Merge branch 'master' of https://github.com/Anuken/Mindustry into 7.0-features

 Conflicts:
	gradle.properties
This commit is contained in:
Anuken
2021-06-28 18:57:27 -04:00
86 changed files with 1926 additions and 1025 deletions

View File

@@ -149,7 +149,16 @@ public abstract class ClientLauncher extends ApplicationCore implements Platform
}
mods.eachClass(Mod::init);
finished = true;
Events.fire(new ClientLoadEvent());
var event = new ClientLoadEvent();
//a temporary measure for compatibility with certain mods
Events.fireWrap(event.getClass(), event, listener -> {
try{
listener.get(event);
}catch(NoSuchFieldError | NoSuchMethodError | NoClassDefFoundError error){
Log.err(error);
}
});
clientLoaded = true;
super.resize(graphics.getWidth(), graphics.getHeight());
app.post(() -> app.post(() -> app.post(() -> app.post(() -> {

View File

@@ -73,8 +73,8 @@ public class Vars implements Loadable{
/** URL to the JSON file containing all the BE servers. Only queried in BE. */
public static final String serverJsonBeURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
/** URL to the JSON file containing all the stable servers. */
//TODO this uses BE servers until full v7 release, there's no point in displaying v6 at all
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_be.json";
//TODO merge with v6 list upon release
public static final String serverJsonURL = "https://raw.githubusercontent.com/Anuken/Mindustry/master/servers_v7.json";
/** URL of the github issue report template.*/
public static final String reportIssueURL = "https://github.com/Anuken/Mindustry/issues/new?labels=bug&template=bug_report.md";
/** list of built-in servers.*/
@@ -148,6 +148,8 @@ public class Vars implements Loadable{
public static int maxTextureSize = 2048;
/** Whether to show the core landing animation. */
public static boolean showLandAnimation = true;
/** Whether to check for memory use before taking screenshots. */
public static boolean checkScreenshotMemory = true;
/** Whether to prompt the user to confirm exiting. */
public static boolean confirmExit = true;
/** if true, UI is not drawn */

View File

@@ -31,7 +31,7 @@ public class MinerAI extends AIController{
//core full of the target item, do nothing
if(targetItem != null && core.acceptStack(targetItem, 1, unit) == 0){
unit.clearItem();
unit.mineTile =null;
unit.mineTile = null;
return;
}
@@ -39,7 +39,7 @@ public class MinerAI extends AIController{
if(unit.stack.amount >= unit.type.itemCapacity || (targetItem != null && !unit.acceptsItem(targetItem))){
mining = false;
}else{
if(timer.get(timerTarget, 60) && targetItem != null){
if(timer.get(timerTarget3, 60) && targetItem != null){
ore = indexer.findClosestOre(unit, targetItem);
}

View File

@@ -24,6 +24,7 @@ public class PhysicsProcess implements AsyncProcess{
@Override
public void begin(){
if(physics == null) return;
boolean local = !Vars.net.client();
//remove stale entities
refs.removeAll(ref -> {
@@ -60,8 +61,9 @@ public class PhysicsProcess implements AsyncProcess{
ref.body.layer =
entity.type.allowLegStep ? layerLegs :
entity.isGrounded() ? layerGround : layerFlying;
ref.x = entity.x();
ref.y = entity.y();
ref.x = entity.x;
ref.y = entity.y;
ref.body.local = local || entity.isLocal();
}
}
@@ -156,6 +158,10 @@ public class PhysicsProcess implements AsyncProcess{
for(int i = 0; i < bodies.size; i++){
PhysicsBody body = bodies.items[i];
//for clients, the only body that collides is the local one; all other physics simulations are handled by the server.
if(!body.local) continue;
body.hitbox(rect);
seq.size = 0;
@@ -174,10 +180,14 @@ public class PhysicsProcess implements AsyncProcess{
float ms = body.mass + other.mass;
float m1 = other.mass / ms, m2 = body.mass / ms;
//first body is always local due to guard check above
body.x += vec.x * m1 / scl;
body.y += vec.y * m1 / scl;
other.x -= vec.x * m2 / scl;
other.y -= vec.y * m2 / scl;
if(other.local){
other.x -= vec.x * m2 / scl;
other.y -= vec.y * m2 / scl;
}
}
}
body.collided = true;
@@ -187,7 +197,7 @@ public class PhysicsProcess implements AsyncProcess{
public static class PhysicsBody implements QuadTreeObject{
public float x, y, radius, mass;
public int layer = 0;
public boolean collided = false;
public boolean collided = false, local = true;
@Override
public void hitbox(Rect out){

View File

@@ -60,7 +60,7 @@ public class Blocks implements ContentList{
cellSynthesisChamber,
//sandbox
powerSource, powerVoid, itemSource, itemVoid, liquidSource, liquidVoid, payloadVoid, payloadSource, illuminator,
powerSource, powerVoid, itemSource, itemVoid, liquidSource, liquidVoid, payloadSource, payloadVoid, illuminator,
//defense
copperWall, copperWallLarge, titaniumWall, titaniumWallLarge, plastaniumWall, plastaniumWallLarge, thoriumWall, thoriumWallLarge, door, doorLarge,
@@ -1178,17 +1178,17 @@ public class Blocks implements ContentList{
//special transport blocks
duct = new Duct("duct"){{
requirements(Category.distribution, with(Items.graphite, 5, Items.metaglass, 2));
requirements(Category.distribution, BuildVisibility.debugOnly, with(Items.graphite, 5, Items.metaglass, 2));
speed = 4f;
}};
ductRouter = new DuctRouter("duct-router"){{
requirements(Category.distribution, with(Items.graphite, 10, Items.metaglass, 4));
requirements(Category.distribution, BuildVisibility.debugOnly, with(Items.graphite, 10, Items.metaglass, 4));
speed = 4f;
}};
ductBridge = new DuctBridge("duct-bridge"){{
requirements(Category.distribution, with(Items.graphite, 20, Items.metaglass, 8));
requirements(Category.distribution, BuildVisibility.debugOnly, with(Items.graphite, 20, Items.metaglass, 8));
speed = 4f;
}};
@@ -1757,13 +1757,13 @@ public class Blocks implements ContentList{
shots = 4;
burstSpacing = 5;
inaccuracy = 10f;
range = 210f;
range = 215f;
xRand = 6f;
size = 2;
health = 300 * size * size;
shootSound = Sounds.missile;
limitRange(2f);
limitRange(5f);
}};
salvo = new ItemTurret("salvo"){{
@@ -1778,7 +1778,7 @@ public class Blocks implements ContentList{
size = 2;
range = 190f;
reloadTime = 34f;
reloadTime = 31f;
restitution = 0.03f;
ammoEjectBack = 3f;
cooldown = 0.03f;
@@ -2147,7 +2147,7 @@ public class Blocks implements ContentList{
}};
repairPoint = new RepairPoint("repair-point"){{
requirements(Category.units, with(Items.lead, 20, Items.copper, 20, Items.silicon, 15));
requirements(Category.units, with(Items.lead, 25, Items.copper, 25, Items.silicon, 20));
repairSpeed = 0.5f;
repairRadius = 65f;
beamWidth = 0.73f;
@@ -2156,16 +2156,16 @@ public class Blocks implements ContentList{
}};
repairTurret = new RepairPoint("repair-turret"){{
requirements(Category.units, with(Items.silicon, 70, Items.thorium, 60, Items.plastanium, 60));
requirements(Category.units, with(Items.silicon, 90, Items.thorium, 80, Items.plastanium, 80));
size = 2;
length = 6f;
repairSpeed = 4f;
repairSpeed = 3.25f;
repairRadius = 140f;
powerUse = 5f;
beamWidth = 1.1f;
pulseRadius = 6.1f;
coolantUse = 0.15f;
coolantMultiplier = 1.7f;
coolantUse = 0.16f;
coolantMultiplier = 1.5f;
acceptCoolant = true;
}};
@@ -2266,12 +2266,12 @@ public class Blocks implements ContentList{
alwaysUnlocked = true;
}};
payloadVoid = new PayloadVoid("payload-void"){{
payloadSource = new PayloadSource("payload-source"){{
requirements(Category.units, BuildVisibility.sandboxOnly, with());
size = 5;
}};
payloadSource = new PayloadSource("payload-source"){{
payloadVoid = new PayloadVoid("payload-void"){{
requirements(Category.units, BuildVisibility.sandboxOnly, with());
size = 5;
}};

View File

@@ -257,7 +257,6 @@ public class Bullets implements ContentList{
width = 8f;
height = 8f;
shrinkY = 0f;
drag = -0.01f;
splashDamageRadius = 30f;
splashDamage = 30f * 1.5f;
ammoMultiplier = 5f;
@@ -274,7 +273,6 @@ public class Bullets implements ContentList{
width = 7f;
height = 8f;
shrinkY = 0f;
drag = -0.01f;
homingPower = 0.08f;
splashDamageRadius = 20f;
splashDamage = 20f * 1.5f;
@@ -288,7 +286,6 @@ public class Bullets implements ContentList{
width = 8f;
height = 8f;
shrinkY = 0f;
drag = -0.01f;
splashDamageRadius = 25f;
splashDamage = 25f * 1.4f;
hitEffect = Fx.blastExplosion;

View File

@@ -854,7 +854,7 @@ public class Fx{
fireRemove = new Effect(70f, e -> {
if(Fire.regions[0] == null) return;
alpha(e.fout());
rect(Fire.regions[((int)(e.rotation + e.fin() * Fire.frames)) % Fire.frames], e.x, e.y);
rect(Fire.regions[((int)(e.rotation + e.fin() * Fire.frames)) % Fire.frames], e.x + Mathf.randomSeedRange((int)e.y, 2), e.y + Mathf.randomSeedRange((int)e.x, 2));
Drawf.light(e.x, e.y, 50f + Mathf.absin(5f, 5f), Pal.lightFlame, 0.6f * e.fout());
}),

View File

@@ -572,7 +572,7 @@ public class UnitTypes implements ContentList{
speed = 1f;
splashDamageRadius = 60f;
instantDisappear = true;
splashDamage = 88f;
splashDamage = 90f;
killShooter = true;
hittable = false;
collidesAir = true;
@@ -581,7 +581,7 @@ public class UnitTypes implements ContentList{
}};
atrax = new UnitType("atrax"){{
speed = 0.5f;
speed = 0.54f;
drag = 0.4f;
hitSize = 13f;
rotateSpeed = 3f;
@@ -621,7 +621,7 @@ public class UnitTypes implements ContentList{
}};
spiroct = new UnitType("spiroct"){{
speed = 0.45f;
speed = 0.48f;
drag = 0.4f;
hitSize = 15f;
rotateSpeed = 3f;
@@ -2113,7 +2113,7 @@ public class UnitTypes implements ContentList{
bullet = new ContinuousLaserBulletType(){{
maxRange = 90f;
damage = 26f;
damage = 27f;
length = 95f;
hitEffect = Fx.hitMeltHeal;
drawSize = 200f;
@@ -2144,7 +2144,7 @@ public class UnitTypes implements ContentList{
x = 70f/4f;
y = -26f/4f;
reload = 70f;
reload = 65f;
shake = 3f;
rotateSpeed = 2f;
shadow = 30f;
@@ -2161,7 +2161,7 @@ public class UnitTypes implements ContentList{
timeIncrease = 3f;
timeDuration = 60f * 20f;
powerDamageScl = 3f;
damage = 50;
damage = 60;
hitColor = lightColor = Pal.heal;
lightRadius = 70f;
clipSize = 250f;
@@ -2177,7 +2177,7 @@ public class UnitTypes implements ContentList{
trailWidth = 6f;
trailColor = Pal.heal;
trailInterval = 3f;
splashDamage = 60f;
splashDamage = 70f;
splashDamageRadius = rad;
hitShake = 4f;
trailRotation = true;

View File

@@ -48,7 +48,7 @@ public class NetServer implements ApplicationListener{
if(state.rules.pvp){
//find team with minimum amount of players and auto-assign player to that.
TeamData re = state.teams.getActive().min(data -> {
if((state.rules.waveTeam == data.team && state.rules.waves) || !data.team.active()) return Integer.MAX_VALUE;
if((state.rules.waveTeam == data.team && state.rules.waves) || !data.team.active() || data.team == Team.derelict) return Integer.MAX_VALUE;
int count = 0;
for(Player other : players){
@@ -472,6 +472,10 @@ public class NetServer implements ApplicationListener{
return;
}
if(!player.dead() && player.unit().isCommanding()){
player.unit().clearCommand();
}
player.getInfo().lastSyncTime = Time.millis();
Call.worldDataBegin(player.con);
netServer.sendWorldData(player);

View File

@@ -11,6 +11,7 @@ import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
import arc.util.async.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.game.EventType.*;
import mindustry.gen.*;
@@ -355,7 +356,7 @@ public class Renderer implements ApplicationListener{
int w = world.width() * tilesize, h = world.height() * tilesize;
int memory = w * h * 4 / 1024 / 1024;
if(memory >= (mobile ? 65 : 120)){
if(Vars.checkScreenshotMemory && memory >= (mobile ? 65 : 120)){
ui.showInfo("@screenshot.invalid");
return;
}

View File

@@ -126,6 +126,10 @@ public abstract class UnlockableContent extends MappableContent{
return true;
}
public boolean logicVisible(){
return !isHidden();
}
/** Makes this piece of content unlocked; if it already unlocked, nothing happens. */
public void unlock(){
if(!unlocked && !alwaysUnlocked){

View File

@@ -52,22 +52,22 @@ public class Puddles{
Puddle p = map.get(tile.pos());
if(p == null){
Puddle puddle = Puddle.create();
puddle.tile(tile);
puddle.liquid(liquid);
puddle.amount(amount);
puddle.generation(generation);
puddle.tile = tile;
puddle.liquid = liquid;
puddle.amount = amount;
puddle.generation = generation;
puddle.set((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f);
puddle.add();
map.put(tile.pos(), puddle);
}else if(p.liquid() == liquid){
p.accepting(Math.max(amount, p.accepting()));
}else if(p.liquid == liquid){
p.accepting = Math.max(amount, p.accepting);
if(generation == 0 && p.lastRipple <= Time.time - 40f && p.amount() >= maxLiquid / 2f){
Fx.ripple.at((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f, 1f, p.liquid().color);
if(generation == 0 && p.lastRipple <= Time.time - 40f && p.amount >= maxLiquid / 2f){
Fx.ripple.at((tile.worldx() + source.worldx()) / 2f, (tile.worldy() + source.worldy()) / 2f, 1f, p.liquid.color);
p.lastRipple = Time.time;
}
}else{
p.amount(p.amount() + reactPuddle(p.liquid(), liquid, amount, p.tile(), (p.x() + source.worldx())/2f, (p.y() + source.worldy())/2f));
p.amount += reactPuddle(p.liquid, liquid, amount, p.tile, (p.x + source.worldx())/2f, (p.y + source.worldy())/2f);
}
}

View File

@@ -297,7 +297,7 @@ public class BulletType extends Content implements Cloneable{
}
}
/** Called when the bullet reaches the end of its lifetime of is destroyed by something external. */
/** Called when the bullet reaches the end of its lifetime or is destroyed by something external. */
public void despawned(Bullet b){
if(despawnHit){
hit(b);

View File

@@ -1,6 +1,7 @@
package mindustry.entities.comp;
import arc.math.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
@@ -24,7 +25,7 @@ abstract class BoundedComp implements Velc, Posc, Healthc, Flyingc{
if(x > world.unitWidth()) dx -= (x - world.unitWidth())/warpDst;
if(y > world.unitHeight()) dy -= (y - world.unitHeight())/warpDst;
velAddNet(dx, dy);
velAddNet(dx * Time.delta, dy * Time.delta);
}
//clamp position if not flying

View File

@@ -28,6 +28,7 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
public static final TextureRegion[] regions = new TextureRegion[frames];
@Import float time, lifetime, x, y;
@Import int id;
Tile tile;
private transient Block block;
@@ -116,7 +117,7 @@ abstract class FireComp implements Timedc, Posc, Syncc, Drawc{
Draw.alpha(Mathf.clamp(warmup / warmupDuration));
Draw.z(Layer.effect);
Draw.rect(regions[Math.min((int)animation, regions.length - 1)], x, y);
Draw.rect(regions[Math.min((int)animation, regions.length - 1)], x + Mathf.randomSeedRange((int)y, 2), y + Mathf.randomSeedRange((int)x, 2));
Draw.reset();
Drawf.light(x, y, 50f + Mathf.absin(5f, 5f), Pal.lightFlame, 0.6f * Mathf.clamp(warmup / warmupDuration));

View File

@@ -1,6 +1,5 @@
package mindustry.entities.comp;
import arc.math.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
@@ -34,7 +33,7 @@ abstract class HealthComp implements Entityc, Posc{
void kill(){
if(dead) return;
health = 0;
health = Math.min(health, 0);
dead = true;
killed();
remove();
@@ -86,7 +85,7 @@ abstract class HealthComp implements Entityc, Posc{
}
void clampHealth(){
health = Mathf.clamp(health, 0, maxHealth);
health = Math.min(health, maxHealth);
}
/** Heals by a flat amount. */

View File

@@ -397,7 +397,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
//move down
elevation -= type.fallSpeed * Time.delta;
if(isGrounded()){
if(isGrounded() || health <= -maxHealth){
Call.unitDestroy(id);
}
}
@@ -528,7 +528,7 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I
@Override
public void killed(){
wasPlayer = isLocal();
health = 0;
health = Math.min(health, 0);
dead = true;
//don't waste time when the unit is already on the ground, just destroy it

View File

@@ -38,7 +38,6 @@ public enum Gamemode{
rules.infiniteResources = true;
rules.editor = true;
rules.waves = false;
rules.enemyCoreBuildRadius = 0f;
rules.waveTimer = false;
});

View File

@@ -165,6 +165,7 @@ public class BlockRenderer{
darkEvents.each(pos -> {
var tile = world.tile(pos);
if(tile == null) return;
float darkness = world.getDarkness(tile.x, tile.y);
//then draw the shadow
Draw.colorl(darkness <= 0f ? 1f : 1f - Math.min((darkness + 0.5f) / 4f, 1f));

View File

@@ -270,6 +270,7 @@ public class MobileInput extends InputHandler implements GestureListener{
b.button(Icon.save, style, this::showSchematicSave).disabled(f -> lastSchematic == null || lastSchematic.file != null);
b.button(Icon.cancel, style, () -> {
selectRequests.clear();
lastSchematic = null;
});
b.row();
b.button(Icon.flipX, style, () -> flipRequests(selectRequests, true));

View File

@@ -70,8 +70,6 @@ public class JsonIO{
json.setElementType(Rules.class, "spawns", SpawnGroup.class);
json.setElementType(Rules.class, "loadout", ItemStack.class);
//TODO this is terrible
json.setSerializer(Sector.class, new Serializer<>(){
@Override
public void write(Json json, Sector object, Class knownType){

View File

@@ -1,20 +1,28 @@
package mindustry.logic;
import arc.*;
import arc.files.*;
import arc.struct.*;
import arc.util.*;
import mindustry.*;
import mindustry.content.*;
import mindustry.ctype.*;
import mindustry.entities.units.*;
import mindustry.logic.LExecutor.*;
import mindustry.type.*;
import mindustry.world.*;
import java.io.*;
/** Stores global constants for logic processors. */
public class GlobalConstants{
public static final int ctrlProcessor = 1, ctrlPlayer = 2, ctrlFormation = 3;
public static final ContentType[] lookableContent = {ContentType.block, ContentType.unit, ContentType.item, ContentType.liquid};
private ObjectIntMap<String> namesToIds = new ObjectIntMap<>();
private Seq<Var> vars = new Seq<>(Var.class);
private UnlockableContent[][] logicIdToContent;
private int[][] contentIdToLogicId;
public void init(){
put("the end", null);
@@ -61,6 +69,49 @@ public class GlobalConstants{
for(UnitCommand cmd : UnitCommand.all){
put("@command" + Strings.capitalize(cmd.name()), cmd);
}
logicIdToContent = new UnlockableContent[ContentType.all.length][];
contentIdToLogicId = new int[ContentType.all.length][];
Fi ids = Core.files.internal("logicids.dat");
if(ids.exists()){
//read logic ID mapping data (generated in ImagePacker)
try(DataInputStream in = new DataInputStream(ids.readByteStream())){
for(ContentType ctype : lookableContent){
short amount = in.readShort();
logicIdToContent[ctype.ordinal()] = new UnlockableContent[amount];
contentIdToLogicId[ctype.ordinal()] = new int[Vars.content.getBy(ctype).size];
//store count constants
put("@" + ctype.name() + "Count", amount);
for(int i = 0; i < amount; i++){
String name = in.readUTF();
UnlockableContent fetched = Vars.content.getByName(ctype, name);
if(fetched != null){
logicIdToContent[ctype.ordinal()][i] = fetched;
contentIdToLogicId[ctype.ordinal()][fetched.id] = i;
}
}
}
}catch(IOException e){
//don't crash?
Log.err("Error reading logic ID mapping", e);
}
}
}
/** @return a piece of content based on its logic ID. This is not equivalent to content ID. */
public @Nullable Content lookupContent(ContentType type, int id){
var arr = logicIdToContent[type.ordinal()];
return arr != null && id >= 0 && id < arr.length ? arr[id] : null;
}
/** @return the integer logic ID of content, or -1 if invalid. */
public int lookupLogicId(UnlockableContent content){
var arr = contentIdToLogicId[content.getContentType().ordinal()];
return arr != null && content.id >= 0 && content.id < arr.length ? arr[content.id] : -1;
}
/** @return a constant ID > 0 if there is a constant with this name, otherwise -1. */

View File

@@ -537,7 +537,7 @@ public class LExecutor{
public void run(LExecutor exec){
Object obj = exec.obj(target);
if(obj instanceof Building b && b.team == exec.team && exec.linkIds.contains(b.id)){
if(type.isObj && exec.var(p1).isobj){ //TODO may break logic?
if(type.isObj && exec.var(p1).isobj){
b.control(type, exec.obj(p1), exec.num(p2), exec.num(p3), exec.num(p4));
}else{
b.control(type, exec.num(p1), exec.num(p2), exec.num(p3), exec.num(p4));
@@ -764,7 +764,6 @@ public class LExecutor{
Var v = exec.var(to);
Var f = exec.var(from);
//TODO error out when the from-value is a constant
if(!v.constant){
if(f.isobj){
v.objval = f.objval;
@@ -1024,6 +1023,26 @@ public class LExecutor{
}
}
public static class LookupI implements LInstruction{
public int dest;
public int from;
public ContentType type;
public LookupI(int dest, int from, ContentType type){
this.dest = dest;
this.from = from;
this.type = type;
}
public LookupI(){
}
@Override
public void run(LExecutor exec){
exec.setobj(dest, constants.lookupContent(type, exec.numi(from)));
}
}
//endregion
}

View File

@@ -7,6 +7,7 @@ import arc.scene.ui.*;
import arc.scene.ui.layout.*;
import mindustry.*;
import mindustry.annotations.Annotations.*;
import mindustry.ctype.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.logic.LCanvas.*;
@@ -662,8 +663,7 @@ public class LStatements{
}
}
//TODO untested
//@RegisterStatement("wait")
@RegisterStatement("wait")
public static class WaitStatement extends LStatement{
public String value = "0.5";
@@ -684,6 +684,42 @@ public class LStatements{
}
}
@RegisterStatement("lookup")
public static class LookupStatement extends LStatement{
public ContentType type = ContentType.item;
public String result = "result", id = "0";
@Override
public void build(Table table){
fields(table, result, str -> result = str);
table.add(" = lookup ");
row(table);
table.button(b -> {
b.label(() -> type.name());
b.clicked(() -> showSelect(b, GlobalConstants.lookableContent, type, o -> {
type = o;
}));
}, Styles.logict, () -> {}).size(64f, 40f).pad(4f).color(table.color);
table.add(" # ");
fields(table, id, str -> id = str);
}
@Override
public Color color(){
return Pal.logicOperations;
}
@Override
public LInstruction build(LAssembler builder){
return new LookupI(builder.var(result), builder.var(id), type);
}
}
@RegisterStatement("end")
public static class EndStatement extends LStatement{
@Override

View File

@@ -11,6 +11,10 @@ public class ModClassLoader extends ClassLoader{
}
};
public ModClassLoader(ClassLoader parent){
super(parent);
}
public void addChild(ClassLoader child){
children.add(child);
}

View File

@@ -39,7 +39,7 @@ public class Mods implements Loadable{
private int totalSprites;
private MultiPacker packer;
private ModClassLoader mainLoader = new ModClassLoader();
private ModClassLoader mainLoader = new ModClassLoader(getClass().getClassLoader());
Seq<LoadedMod> mods = new Seq<>();
private ObjectMap<Class<?>, ModMeta> metas = new ObjectMap<>();

View File

@@ -61,7 +61,6 @@ public class Administration{
});
//block interaction rate limit
//TODO when someone disconnects, a different player is mistakenly kicked for spamming actions
addActionFilter(action -> {
if(action.type != ActionType.breakBlock &&
action.type != ActionType.placeBlock &&

View File

@@ -391,7 +391,15 @@ public class ArcNetProvider implements NetProvider{
//no compression, copy over buffer
if(compression == 0){
buffer.position(0).limit(length);
buffer.put(byteBuffer.array(), byteBuffer.position(), length);
if(byteBuffer.hasArray()){
buffer.put(byteBuffer.array(), byteBuffer.position(), length);
}else{
byte[] readcopy = new byte[length];
int pos = byteBuffer.position();
byteBuffer.get(readcopy);
byteBuffer.position(pos);
buffer.put(readcopy);
}
buffer.position(0);
packet.read(reads.get(), length);
//move read packets forward

View File

@@ -97,7 +97,7 @@ public class Net{
if(e instanceof BufferUnderflowException || e instanceof BufferOverflowException){
error = Core.bundle.get("error.io");
}else if(error.equals("mismatch")){
}else if(error.equals("mismatch") || (e instanceof IndexOutOfBoundsException && e.getStackTrace()[0].getClassName().contains("java.nio"))){
error = Core.bundle.get("error.mismatch");
}else if(error.contains("port out of range") || error.contains("invalid argument") || (error.contains("invalid") && error.contains("address")) || Strings.neatError(e).contains("address associated")){
error = Core.bundle.get("error.invalidaddress");

View File

@@ -664,7 +664,7 @@ public class UnitType extends UnlockableContent{
if(unit.item() != null && unit.itemTime > 0.01f){
float size = (itemSize + Mathf.absin(Time.time, 5f, 1f)) * unit.itemTime;
Draw.mixcol(Pal.accent, Mathf.absin(Time.time, 5f, 0.5f));
Draw.mixcol(Pal.accent, Mathf.absin(Time.time, 5f, 0.1f));
Draw.rect(unit.item().fullIcon,
unit.x + Angles.trnsx(unit.rotation + 180f, itemOffsetY),
unit.y + Angles.trnsy(unit.rotation + 180f, itemOffsetY),

View File

@@ -33,7 +33,6 @@ public class ParticleWeather extends Weather{
region = Core.atlas.find(particleRegion);
//load noise texture
//TODO mod support
if(drawNoise && Core.assets != null){
Core.assets.load("sprites/" + noisePath + ".png", Texture.class);
}

View File

@@ -6,6 +6,7 @@ import arc.graphics.*;
import arc.input.*;
import arc.math.*;
import arc.scene.ui.*;
import arc.scene.ui.TextButton.*;
import arc.scene.ui.layout.*;
import arc.struct.*;
import arc.util.*;
@@ -34,6 +35,7 @@ public class JoinDialog extends BaseDialog{
int totalHosts;
int refreshes;
boolean showHidden;
TextButtonStyle style;
String lastIp;
int lastPort;
@@ -42,6 +44,15 @@ public class JoinDialog extends BaseDialog{
public JoinDialog(){
super("@joingame");
style = new TextButtonStyle(){{
over = Styles.flatOver;
font = Fonts.def;
fontColor = Color.white;
disabledFontColor = Color.gray;
down = Styles.flatOver;
up = Styles.black5;
}};
loadServers();
if(!steam) buttons.add().width(60f);
@@ -119,7 +130,7 @@ public class JoinDialog extends BaseDialog{
//why are java lambdas this bad
TextButton[] buttons = {null};
TextButton button = buttons[0] = remote.button("[accent]" + server.displayIP(), Styles.cleart, () -> {
TextButton button = buttons[0] = remote.button("[accent]" + server.displayIP(), style, () -> {
if(!buttons[0].childrenPressed()){
if(server.lastHost != null){
Events.fire(new ClientPreConnectEvent(server.lastHost));
@@ -405,7 +416,7 @@ public class JoinDialog extends BaseDialog{
float w = targetWidth();
//TODO looks bad
container.button(b -> buildServer(host, b), Styles.cleart, () -> {
container.button(b -> buildServer(host, b), style, () -> {
Events.fire(new ClientPreConnectEvent(host));
if(!Core.settings.getBool("server-disclaimer", false)){
ui.showCustomConfirm("@warning", "@servers.disclaimer", "@ok", "@back", () -> {
@@ -442,7 +453,7 @@ public class JoinDialog extends BaseDialog{
local.row();
local.button(b -> buildServer(host, b), Styles.cleart, () -> {
local.button(b -> buildServer(host, b), style, () -> {
Events.fire(new ClientPreConnectEvent(host));
safeConnect(host.address, host.port, host.version);
}).width(w);

View File

@@ -72,7 +72,6 @@ public class KeybindDialog extends Dialog{
}
if(sections.length != 1){
//TODO toggle style
TextButton button = new TextButton(bundle.get("section." + section.name + ".name", Strings.capitalize(section.name))/*, "toggle"*/);
if(section.equals(this.section))
button.toggle();

View File

@@ -324,7 +324,6 @@ public class ModsDialog extends BaseDialog{
if(showImport) dialog.buttons.button("@mods.browser.reinstall", Icon.download, () -> githubImportMod(mod.getRepo(), mod.isJava()));
}
//TODO improve this menu later
dialog.cont.pane(desc -> {
desc.center();
desc.defaults().padTop(10).left();

View File

@@ -241,7 +241,7 @@ public class PlanetDialog extends BaseDialog implements PlanetInterfaceRenderer{
dialog.add("@sectors.captured");
}
//TODO
//TODO unimplemented, cutscene needed
public void showPlanetLaunch(Sector sector, Cons<Sector> listener){
selected = null;
hovered = null;

View File

@@ -423,12 +423,17 @@ public class SettingsMenuDialog extends Dialog{
}
});
graphics.checkPref("linear", !mobile, b -> {
for(Texture tex : Core.atlas.getTextures()){
TextureFilter filter = b ? TextureFilter.linear : TextureFilter.nearest;
tex.setFilter(filter, filter);
}
});
//iOS (and possibly Android) devices do not support linear filtering well, so disable it
if(!ios){
graphics.checkPref("linear", !mobile, b -> {
for(Texture tex : Core.atlas.getTextures()){
TextureFilter filter = b ? TextureFilter.linear : TextureFilter.nearest;
tex.setFilter(filter, filter);
}
});
}else{
settings.put("linear", false);
}
if(Core.settings.getBool("linear")){
for(Texture tex : Core.atlas.getTextures()){

View File

@@ -267,6 +267,7 @@ public class HudFragment extends Fragment{
//core info
parent.fill(t -> {
t.top();
t.visible(() -> shown);
t.name = "coreinfo";

View File

@@ -774,6 +774,11 @@ public class Block extends UnlockableContent{
return ContentType.block;
}
@Override
public boolean logicVisible(){
return buildVisibility != BuildVisibility.hidden;
}
/** Called after all blocks are created. */
@Override
@CallSuper
@@ -838,7 +843,7 @@ public class Block extends UnlockableContent{
//load specific team regions
teamRegions = new TextureRegion[Team.all.length];
for(Team team : Team.all){
teamRegions[team.id] = teamRegion.found() ? Core.atlas.find(name + "-team-" + team.name, teamRegion) : teamRegion;
teamRegions[team.id] = teamRegion.found() && team.hasPalette ? Core.atlas.find(name + "-team-" + team.name, teamRegion) : teamRegion;
}
if(variants != 0){

View File

@@ -134,24 +134,26 @@ public class Build{
return false;
}
//find closest core, if it doesn't match the team, placing is not legal
if(state.rules.polygonCoreProtection){
float mindst = Float.MAX_VALUE;
CoreBuild closest = null;
for(TeamData data : state.teams.active){
for(CoreBuild tile : data.cores){
float dst = tile.dst2(x * tilesize + type.offset, y * tilesize + type.offset);
if(dst < mindst){
closest = tile;
mindst = dst;
if(!state.rules.editor){
//find closest core, if it doesn't match the team, placing is not legal
if(state.rules.polygonCoreProtection){
float mindst = Float.MAX_VALUE;
CoreBuild closest = null;
for(TeamData data : state.teams.active){
for(CoreBuild tile : data.cores){
float dst = tile.dst2(x * tilesize + type.offset, y * tilesize + type.offset);
if(dst < mindst){
closest = tile;
mindst = dst;
}
}
}
}
if(closest != null && closest.team != team){
if(closest != null && closest.team != team){
return false;
}
}else if(state.teams.eachEnemyCore(team, core -> Mathf.dst(x * tilesize + type.offset, y * tilesize + type.offset, core.x, core.y) < state.rules.enemyCoreBuildRadius + type.size * tilesize / 2f)){
return false;
}
}else if(state.teams.eachEnemyCore(team, core -> Mathf.dst(x * tilesize + type.offset, y * tilesize + type.offset, core.x, core.y) < state.rules.enemyCoreBuildRadius + type.size * tilesize / 2f)){
return false;
}
Tile tile = world.tile(x, y);

View File

@@ -109,10 +109,15 @@ public class Accelerator extends Block{
if(!state.isCampaign() || !consValid()) return;
ui.planet.showPlanetLaunch(state.rules.sector, sector -> {
//TODO cutscene, etc...
consume();
});
//TODO implement
if(true){
ui.showInfo("@indev.campaign");
}else{
ui.planet.showPlanetLaunch(state.rules.sector, sector -> {
//TODO cutscene, etc...
consume();
});
}
Events.fire(Trigger.acceleratorUse);
}

View File

@@ -149,7 +149,7 @@ public class LaunchPad extends Block{
public void display(Table table){
super.display(table);
if(!state.isCampaign()) return;
if(!state.isCampaign() || net.client()) return;
table.row();
table.label(() -> {

View File

@@ -3,8 +3,10 @@ package mindustry.world.blocks.defense;
import arc.graphics.*;
import arc.graphics.g2d.*;
import arc.math.*;
import arc.util.*;
import mindustry.annotations.Annotations.*;
import mindustry.entities.*;
import mindustry.entities.bullet.*;
import mindustry.gen.*;
import mindustry.graphics.*;
import mindustry.world.*;
@@ -18,6 +20,9 @@ public class ShockMine extends Block{
public int length = 10;
public int tendrils = 6;
public Color lightningColor = Pal.lancerLaser;
public int shots = 6;
public float inaccuracy = 0f;
public @Nullable BulletType bullet;
public float teamAlpha = 0.3f;
public @Load("@-team-top") TextureRegion teamRegion;
@@ -46,17 +51,26 @@ public class ShockMine extends Block{
@Override
public void drawCracks(){
//no
}
@Override
public void unitOn(Unit unit){
if(enabled && unit.team != team && timer(timerDamage, cooldown)){
for(int i = 0; i < tendrils; i++){
Lightning.create(team, lightningColor, damage, x, y, Mathf.random(360f), length);
}
triggered();
damage(tileDamage);
}
}
public void triggered(){
for(int i = 0; i < tendrils; i++){
Lightning.create(team, lightningColor, damage, x, y, Mathf.random(360f), length);
}
if(bullet != null){
for(int i = 0; i < shots; i++){
bullet.create(this, x, y, (360f / shots) * i + Mathf.random(inaccuracy));
}
}
}
}
}

View File

@@ -136,6 +136,7 @@ public class ItemTurret extends Turret{
}
BulletType type = ammoTypes.get(item);
if(type == null) return;
totalAmmo += type.ammoMultiplier;
//find ammo entry by type

View File

@@ -100,17 +100,27 @@ public class LiquidTurret extends Turret{
@Override
protected void findTarget(){
if(extinguish && liquids.current().canExtinguish()){
Fire result = null;
float mindst = 0f;
int tr = (int)(range / tilesize);
for(int x = -tr; x <= tr; x++){
for(int y = -tr; y <= tr; y++){
Tile other = world.tileWorld(x + tile.x, y + tile.y);
Tile other = world.tile(x + tile.x, y + tile.y);
var fire = Fires.get(x + tile.x, y + tile.y);
float dst = fire == null ? 0 : dst2(fire);
//do not extinguish fires on other team blocks
if(other != null && Fires.has(x + tile.x, y + tile.y) && (other.build == null || other.team() == team)){
target = Fires.get(x + tile.x, y + tile.y);
return;
if(other != null && fire != null && Fires.has(other.x, other.y) && dst <= range * range && (result == null || dst < mindst) && (other.build == null || other.team() == team)){
result = fire;
mindst = dst;
}
}
}
if(result != null){
target = result;
//don't run standard targeting
return;
}
}
super.findTarget();

View File

@@ -43,6 +43,10 @@ public class PayloadRouter extends PayloadConveyor{
do{
rotation = (rotation + 1) % 4;
onProximityUpdate();
//force update to transfer if necessary
if(next instanceof PayloadConveyorBuild && !(next instanceof PayloadRouterBuild)){
next.updateTile();
}
//this condition intentionally uses "accept from itself" conditions, because payload conveyors only accept during the start
//"accept from self" conditions are for dropped payloads and are less restrictive
}while((blocked || next == null || !next.acceptPayload(next, item)) && ++rotations < 4);

View File

@@ -12,6 +12,7 @@ import mindustry.gen.*;
import mindustry.type.*;
import mindustry.world.*;
import mindustry.world.blocks.*;
import mindustry.world.blocks.storage.*;
import static mindustry.Vars.*;
@@ -69,7 +70,7 @@ public class PayloadSource extends PayloadBlock{
}
public boolean canProduce(Block b){
return b.isVisible() && b.size < size;
return b.isVisible() && b.size < size && !(b instanceof CoreBlock);
}
public boolean canProduce(UnitType t){

View File

@@ -1,6 +1,7 @@
package mindustry.world.blocks.production;
import arc.graphics.g2d.*;
import arc.math.*;
import mindustry.annotations.Annotations.*;
import mindustry.gen.*;
import mindustry.graphics.*;
@@ -67,6 +68,8 @@ public class Fracker extends SolidPump{
super.updateTile();
accumulator += delta() * efficiency();
}else{
warmup = Mathf.lerpDelta(warmup, 0f, 0.02f);
lastPump = 0f;
dumpLiquid(result);
}
}

View File

@@ -8,7 +8,6 @@ import arc.util.*;
import mindustry.world.*;
import mindustry.world.blocks.production.GenericCrafter.*;
//TODO
public class DrawArcSmelter extends DrawBlock{
public TextureRegion top, bottom;
public Color flameColor = Color.valueOf("f58349"), midColor = Color.valueOf("f2d585");

View File

@@ -38,6 +38,6 @@ public class DrawWeave extends DrawBlock{
@Override
public TextureRegion[] icons(Block block){
return new TextureRegion[]{bottom, block.region, weave};
return new TextureRegion[]{bottom, weave, block.region};
}
}