diff --git a/android/src/mindustry/android/AndroidLauncher.java b/android/src/mindustry/android/AndroidLauncher.java index 5084cbc67d..1c9ee8f144 100644 --- a/android/src/mindustry/android/AndroidLauncher.java +++ b/android/src/mindustry/android/AndroidLauncher.java @@ -45,6 +45,7 @@ public class AndroidLauncher extends AndroidApplication{ handler.uncaughtException(thread, error); }else{ error.printStackTrace(); + Log.err(error); System.exit(1); } }); diff --git a/core/assets/contributors b/core/assets/contributors index e11fc8b808..59f40b3ef2 100644 --- a/core/assets/contributors +++ b/core/assets/contributors @@ -112,3 +112,4 @@ Catchears younggam simba-fs RedRadiation +Marko Zajc diff --git a/core/src/mindustry/ai/BaseAI.java b/core/src/mindustry/ai/BaseAI.java index 2a81d1535d..85e6656d28 100644 --- a/core/src/mindustry/ai/BaseAI.java +++ b/core/src/mindustry/ai/BaseAI.java @@ -32,6 +32,7 @@ public class BaseAI{ private static final Seq tmpTiles = new Seq<>(); private static int correct = 0, incorrect = 0; + private static boolean anyDrills; private int lastX, lastY, lastW, lastH; private boolean triedWalls, foundPath; @@ -200,7 +201,7 @@ public class BaseAI{ int cx = x - (int)rotator.x; int cy = y - (int)rotator.y; - //chekc valid placeability + //check valid placeability for(Stile tile : result.tiles){ int realX = tile.x + cx, realY = tile.y + cy; if(!Build.validPlace(tile.block, data.team, realX, realY, tile.rotation)){ @@ -217,16 +218,18 @@ public class BaseAI{ //make sure at least X% of resource requirements are met correct = incorrect = 0; + anyDrills = false; if(part.required instanceof Item){ for(Stile tile : result.tiles){ if(tile.block instanceof Drill){ + anyDrills = true; tile.block.iterateTaken(tile.x + cx, tile.y + cy, (ex, ey) -> { Tile res = world.rawTile(ex, ey); if(res.drop() == part.required){ correct ++; - }else{ + }else if(res.drop() != null){ incorrect ++; } }); @@ -235,7 +238,7 @@ public class BaseAI{ } //fail if not enough fit requirements - if((float)correct / incorrect < correctPercent){ + if(anyDrills && (incorrect != 0 || correct == 0)){ return false; } diff --git a/core/src/mindustry/core/Renderer.java b/core/src/mindustry/core/Renderer.java index 712f894155..8f5423cbc1 100644 --- a/core/src/mindustry/core/Renderer.java +++ b/core/src/mindustry/core/Renderer.java @@ -20,6 +20,9 @@ import static arc.Core.*; import static mindustry.Vars.*; public class Renderer implements ApplicationListener{ + /** These are global variables, for headless access. Cached. */ + public static float laserOpacity = 0.5f, bridgeOpacity = 0.75f; + public final BlockRenderer blocks = new BlockRenderer(); public final MinimapRenderer minimap = new MinimapRenderer(); public final OverlayRenderer overlays = new OverlayRenderer(); @@ -29,7 +32,6 @@ public class Renderer implements ApplicationListener{ public @Nullable Bloom bloom; public FrameBuffer effectBuffer = new FrameBuffer(); - public float laserOpacity = 1f; public boolean animateShields, drawWeather = true; /** minZoom = zooming out, maxZoom = zooming in */ public float minZoom = 1.5f, maxZoom = 6f; @@ -71,6 +73,7 @@ public class Renderer implements ApplicationListener{ camerascale = Mathf.lerpDelta(camerascale, dest, 0.1f); if(Mathf.equal(camerascale, dest, 0.001f)) camerascale = dest; laserOpacity = settings.getInt("lasersopacity") / 100f; + bridgeOpacity = settings.getInt("bridgeopacity") / 100f; animateShields = settings.getBool("animatedshields"); if(landTime > 0){ diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index 5b065ef666..a5c55485ec 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -108,7 +108,7 @@ abstract class BuilderComp implements Posc, Teamc, Rotc{ plans.removeFirst(); return; } - }else if((tile.team() != team && tile.team() != Team.derelict) || (!current.breaking && cb.cblock != current.block)){ + }else if((tile.team() != team && tile.team() != Team.derelict) || (!current.breaking && (cb.cblock != current.block || cb.tile != current.tile()))){ plans.removeFirst(); return; } diff --git a/core/src/mindustry/entities/comp/PayloadComp.java b/core/src/mindustry/entities/comp/PayloadComp.java index f29d988d74..8db8656d88 100644 --- a/core/src/mindustry/entities/comp/PayloadComp.java +++ b/core/src/mindustry/entities/comp/PayloadComp.java @@ -136,7 +136,9 @@ abstract class PayloadComp implements Posc, Rotc, Hitboxc, Unitc{ int rot = (int)((rotation + 45f) / 90f) % 4; payload.place(on, rot); - if(isPlayer()) payload.build.lastAccessed = getPlayer().name; + if(getControllerName() != null){ + payload.build.lastAccessed = getControllerName(); + } Fx.unitDrop.at(tile); Fx.placeBlock.at(on.drawx(), on.drawy(), on.block().size); diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 004ae4a196..81a604e455 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -445,6 +445,15 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I remove(); } + /** @return name of direct or indirect player controller. */ + @Override + public @Nullable String getControllerName(){ + if(isPlayer()) return getPlayer().name; + if(controller instanceof LogicAI ai && ai.controller != null) return ai.controller.lastAccessed; + if(controller instanceof FormationAI ai && ai.leader != null && ai.leader.isPlayer()) return ai.leader.getPlayer().name; + return null; + } + @Override public void display(Table table){ type.display(self(), table); diff --git a/core/src/mindustry/logic/LStatements.java b/core/src/mindustry/logic/LStatements.java index d20c4477f4..d8fce354ad 100644 --- a/core/src/mindustry/logic/LStatements.java +++ b/core/src/mindustry/logic/LStatements.java @@ -387,15 +387,13 @@ public class LStatements{ public static class RadarStatement extends LStatement{ public RadarTarget target1 = RadarTarget.enemy, target2 = RadarTarget.any, target3 = RadarTarget.any; public RadarSort sort = RadarSort.distance; - public String radar = "0", sortOrder = "1", output = "result"; + public String radar = "turret1", sortOrder = "1", output = "result"; @Override public void build(Table table){ table.defaults().left(); if(buildFrom()){ - radar = "turret1"; - table.add(" from "); fields(table, radar, v -> radar = v); diff --git a/core/src/mindustry/logic/LogicDialog.java b/core/src/mindustry/logic/LogicDialog.java index 51b6468c77..dd12b9cca3 100644 --- a/core/src/mindustry/logic/LogicDialog.java +++ b/core/src/mindustry/logic/LogicDialog.java @@ -3,6 +3,7 @@ package mindustry.logic; import arc.*; import arc.func.*; import arc.scene.ui.TextButton.*; +import arc.util.*; import mindustry.gen.*; import mindustry.logic.LStatements.*; import mindustry.ui.*; @@ -90,16 +91,20 @@ public class LogicDialog extends BaseDialog{ onResize(() -> canvas.rebuild()); } - public void show(String code, Cons consumer){ + public void show(String code, Cons modified){ canvas.statements.clearChildren(); canvas.rebuild(); try{ canvas.load(code); }catch(Throwable t){ - t.printStackTrace(); + Log.err(t); canvas.load(""); } - this.consumer = consumer; + this.consumer = result -> { + if(!result.equals(code)){ + modified.get(result); + } + }; show(); } diff --git a/core/src/mindustry/net/CrashSender.java b/core/src/mindustry/net/CrashSender.java index 0d2204890c..4ca4f3714e 100644 --- a/core/src/mindustry/net/CrashSender.java +++ b/core/src/mindustry/net/CrashSender.java @@ -47,7 +47,12 @@ public class CrashSender{ public static void send(Throwable exception, Cons writeListener){ try{ - exception.printStackTrace(); + try{ + //log to file + Log.err(exception); + }catch(Throwable no){ + exception.printStackTrace(); + } //try saving game data try{ diff --git a/core/src/mindustry/ui/dialogs/JoinDialog.java b/core/src/mindustry/ui/dialogs/JoinDialog.java index 2d9f565c7b..4f858a67ff 100644 --- a/core/src/mindustry/ui/dialogs/JoinDialog.java +++ b/core/src/mindustry/ui/dialogs/JoinDialog.java @@ -563,25 +563,32 @@ public class JoinDialog extends BaseDialog{ transient Host lastHost; void setIP(String ip){ - - //parse ip:port, if unsuccessful, use default values - if(ip.lastIndexOf(':') != -1 && ip.lastIndexOf(':') != ip.length() - 1){ - try{ + try{ + boolean isIpv6 = Strings.count(ip, ':') > 1; + if(isIpv6 && ip.lastIndexOf("]:") != -1 && ip.lastIndexOf("]:") != ip.length() - 1){ + int idx = ip.indexOf("]:"); + this.ip = ip.substring(1, idx); + this.port = Integer.parseInt(ip.substring(idx + 2, ip.length())); + }else if(!isIpv6 && ip.lastIndexOf(':') != -1 && ip.lastIndexOf(':') != ip.length() - 1){ int idx = ip.lastIndexOf(':'); this.ip = ip.substring(0, idx); this.port = Integer.parseInt(ip.substring(idx + 1)); - }catch(Exception e){ + }else{ this.ip = ip; this.port = Vars.port; } - }else{ + }catch(Exception e){ this.ip = ip; this.port = Vars.port; } } String displayIP(){ - return ip + (port != Vars.port ? ":" + port : ""); + if(Strings.count(ip, ':') > 1){ + return port != Vars.port ? "[" + ip + "]:" + port : ip; + }else{ + return ip + (port != Vars.port ? ":" + port : ""); + } } public Server(){ diff --git a/core/src/mindustry/world/Build.java b/core/src/mindustry/world/Build.java index ee868942a3..b450509c7b 100644 --- a/core/src/mindustry/world/Build.java +++ b/core/src/mindustry/world/Build.java @@ -45,7 +45,10 @@ public class Build{ build.setDeconstruct(previous); build.prevBuild = prevBuild; tile.build.health = tile.build.maxHealth * prevPercent; - if(unit != null && unit.isPlayer()) tile.build.lastAccessed = unit.getPlayer().name; + + if(unit != null && unit.getControllerName() != null){ + tile.build.lastAccessed = unit.getControllerName(); + } Core.app.post(() -> Events.fire(new BlockBuildBeginEvent(tile, team, unit, true))); } @@ -64,7 +67,7 @@ public class Build{ //auto-rotate the block to the correct orientation and bail out if(tile.team() == team && tile.block == result && tile.build != null && tile.block.quickRotate){ - if(unit != null && unit.isPlayer()) tile.build.lastAccessed = unit.getPlayer().name; + if(unit != null && unit.getControllerName() != null) tile.build.lastAccessed = unit.getControllerName(); tile.build.rotation = Mathf.mod(rotation, 4); tile.build.updateProximity(); tile.build.noSleep(); @@ -90,7 +93,7 @@ public class Build{ build.setConstruct(previous.size == sub.size ? previous : Blocks.air, result); build.prevBuild = prevBuild; - if(unit != null && unit.isPlayer()) build.lastAccessed = unit.getPlayer().name; + if(unit != null && unit.getControllerName() != null) build.lastAccessed = unit.getControllerName(); result.placeBegan(tile, previous); diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index 9e6696263c..f2ebd161dd 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -78,8 +78,8 @@ public class ConstructBlock extends Block{ tile.build.overwrote(prev); } - if(builder != null && builder.isPlayer()){ - tile.build.lastAccessed = builder.getPlayer().name; + if(builder != null && builder.getControllerName() != null){ + tile.build.lastAccessed = builder.getControllerName(); } } diff --git a/core/src/mindustry/world/blocks/distribution/ExtendingItemBridge.java b/core/src/mindustry/world/blocks/distribution/ExtendingItemBridge.java index 18bc444eec..98b48e043a 100644 --- a/core/src/mindustry/world/blocks/distribution/ExtendingItemBridge.java +++ b/core/src/mindustry/world/blocks/distribution/ExtendingItemBridge.java @@ -1,9 +1,9 @@ package mindustry.world.blocks.distribution; -import arc.*; import arc.graphics.g2d.*; import arc.math.*; import arc.math.geom.*; +import mindustry.core.*; import mindustry.graphics.*; import mindustry.world.*; @@ -36,9 +36,8 @@ public class ExtendingItemBridge extends ItemBridge{ ex *= uptime; ey *= uptime; - float opacity = Core.settings.getInt("bridgeopacity") / 100f; - if(Mathf.zero(opacity)) return; - Draw.alpha(opacity); + if(Mathf.zero(Renderer.bridgeOpacity)) return; + Draw.alpha(Renderer.bridgeOpacity); Lines.stroke(8f); Lines.line(bridgeRegion, @@ -59,7 +58,7 @@ public class ExtendingItemBridge extends ItemBridge{ Draw.color(); for(int a = 0; a < arrows; a++){ - Draw.alpha(Mathf.absin(a / (float)arrows - time / 100f, 0.1f, 1f) * uptime * opacity); + Draw.alpha(Mathf.absin(a / (float)arrows - time / 100f, 0.1f, 1f) * uptime * Renderer.bridgeOpacity); Draw.rect(arrowRegion, x + Geometry.d4(i).x * (tilesize / 2f + a * 6f + 2) * uptime, y + Geometry.d4(i).y * (tilesize / 2f + a * 6f + 2) * uptime, diff --git a/core/src/mindustry/world/blocks/distribution/ItemBridge.java b/core/src/mindustry/world/blocks/distribution/ItemBridge.java index 604cefce4a..2ab0c9b047 100644 --- a/core/src/mindustry/world/blocks/distribution/ItemBridge.java +++ b/core/src/mindustry/world/blocks/distribution/ItemBridge.java @@ -10,6 +10,7 @@ import arc.struct.IntSet.*; import arc.util.*; import arc.util.io.*; import mindustry.annotations.Annotations.*; +import mindustry.core.*; import mindustry.entities.units.*; import mindustry.gen.*; import mindustry.graphics.*; @@ -70,6 +71,9 @@ public class ItemBridge extends Block{ } public void drawBridge(BuildPlan req, float ox, float oy, float flip){ + if(Mathf.zero(Renderer.bridgeOpacity)) return; + Draw.alpha(Renderer.bridgeOpacity); + Lines.stroke(8f); Tmp.v1.set(ox, oy).sub(req.drawx(), req.drawy()).setLength(tilesize/2f); @@ -84,6 +88,8 @@ public class ItemBridge extends Block{ Draw.rect(arrowRegion, (req.drawx() + ox) / 2f, (req.drawy() + oy) / 2f, Angles.angle(req.drawx(), req.drawy(), ox, oy) + flip); + + Draw.reset(); } @Override @@ -321,13 +327,12 @@ public class ItemBridge extends Block{ Tile other = world.tile(link); if(!linkValid(tile, other)) return; - float opacity = Core.settings.getInt("bridgeopacity") / 100f; - if(Mathf.zero(opacity)) return; + if(Mathf.zero(Renderer.bridgeOpacity)) return; int i = relativeTo(other.x, other.y); Draw.color(Color.white, Color.black, Mathf.absin(Time.time, 6f, 0.07f)); - Draw.alpha(Math.max(uptime, 0.25f) * opacity); + Draw.alpha(Math.max(uptime, 0.25f) * Renderer.bridgeOpacity); Draw.rect(endRegion, x, y, i * 90 + 90); Draw.rect(endRegion, other.drawx(), other.drawy(), i * 90 + 270); @@ -350,7 +355,7 @@ public class ItemBridge extends Block{ Draw.color(); for(int a = 0; a < arrows; a++){ - Draw.alpha(Mathf.absin(a / (float)arrows - time / 100f, 0.1f, 1f) * uptime * opacity); + Draw.alpha(Mathf.absin(a / (float)arrows - time / 100f, 0.1f, 1f) * uptime * Renderer.bridgeOpacity); Draw.rect(arrowRegion, x + Geometry.d4(i).x * (tilesize / 2f + a * 4f + time % 4f), y + Geometry.d4(i).y * (tilesize / 2f + a * 4f + time % 4f), i * 90f); diff --git a/core/src/mindustry/world/blocks/distribution/StackConveyor.java b/core/src/mindustry/world/blocks/distribution/StackConveyor.java index 34da509f96..41b0736181 100644 --- a/core/src/mindustry/world/blocks/distribution/StackConveyor.java +++ b/core/src/mindustry/world/blocks/distribution/StackConveyor.java @@ -167,6 +167,16 @@ public class StackConveyor extends Block implements Autotiler{ } } + //cannot load when facing + if(state == stateLoad){ + for(Building near : proximity){ + if(near instanceof StackConveyorBuild && near.front() == this){ + state = stateMove; + break; + } + } + } + //update other conveyor state when this conveyor's state changes if(state != lastState){ proxUpdating = true; diff --git a/core/src/mindustry/world/blocks/logic/LogicBlock.java b/core/src/mindustry/world/blocks/logic/LogicBlock.java index b9fc38bf8b..21d3534273 100644 --- a/core/src/mindustry/world/blocks/logic/LogicBlock.java +++ b/core/src/mindustry/world/blocks/logic/LogicBlock.java @@ -477,9 +477,7 @@ public class LogicBlock extends Block{ @Override public void buildConfiguration(Table table){ table.button(Icon.pencil, Styles.clearTransi, () -> { - Vars.ui.logic.show(code, code -> { - configure(compress(code, relativeConnections())); - }); + Vars.ui.logic.show(code, code -> configure(compress(code, relativeConnections()))); }).size(40); } diff --git a/core/src/mindustry/world/blocks/power/PowerNode.java b/core/src/mindustry/world/blocks/power/PowerNode.java index 33ac673c92..f5801d98a7 100644 --- a/core/src/mindustry/world/blocks/power/PowerNode.java +++ b/core/src/mindustry/world/blocks/power/PowerNode.java @@ -159,7 +159,7 @@ public class PowerNode extends PowerBlock{ protected void setupColor(float satisfaction){ Draw.color(laserColor1, laserColor2, (1f - satisfaction) * 0.86f + Mathf.absin(3f, 0.1f)); - Draw.alpha(renderer == null ? 0.5f : renderer.laserOpacity); + Draw.alpha(Renderer.laserOpacity); } protected void drawLaser(Team team, float x1, float y1, float x2, float y2, int size1, int size2){ @@ -391,7 +391,7 @@ public class PowerNode extends PowerBlock{ public void draw(){ super.draw(); - if(Mathf.zero(renderer.laserOpacity)) return; + if(Mathf.zero(Renderer.laserOpacity)) return; Draw.z(Layer.power); setupColor(power.graph.getSatisfaction()); diff --git a/gradle.properties b/gradle.properties index 86554206c5..f42373473e 100644 --- a/gradle.properties +++ b/gradle.properties @@ -1,3 +1,3 @@ org.gradle.daemon=true org.gradle.jvmargs=-Xms256m -Xmx1024m -archash=47b5f71c468d3cec66b6721aeb7f7602ddb468d7 +archash=5e9176c3feefa5ee2f14b08f82da1f01f790e64a diff --git a/ios/src/mindustry/ios/IOSLauncher.java b/ios/src/mindustry/ios/IOSLauncher.java index 818ea50a0f..edf279d912 100644 --- a/ios/src/mindustry/ios/IOSLauncher.java +++ b/ios/src/mindustry/ios/IOSLauncher.java @@ -255,6 +255,7 @@ public class IOSLauncher extends IOSApplication.Delegate{ }catch(Throwable t){ //attempt to log the exception CrashSender.log(t); + Log.err(t); //rethrow the exception so it actually crashes throw t; }