From 527be41e3299028e2819fe7534ef09c9afbcdaee Mon Sep 17 00:00:00 2001 From: Anuken Date: Tue, 17 Nov 2020 19:40:18 -0500 Subject: [PATCH] Merged Unit & Builder components / Cleanup --- .../src/main/resources/classids.properties | 5 +++ .../src/main/resources/revisions/alpha/0.json | 1 + .../main/resources/revisions/arkyid/0.json | 1 + .../src/main/resources/revisions/beta/0.json | 1 + .../src/main/resources/revisions/block/4.json | 1 + .../main/resources/revisions/corvus/4.json | 1 + .../src/main/resources/revisions/flare/4.json | 1 + .../src/main/resources/revisions/gamma/0.json | 1 + .../src/main/resources/revisions/mace/4.json | 1 + .../src/main/resources/revisions/mono/3.json | 1 + .../main/resources/revisions/pulsar/0.json | 1 + .../main/resources/revisions/quasar/0.json | 1 + .../src/main/resources/revisions/risso/4.json | 1 + .../main/resources/revisions/toxopid/0.json | 1 + core/assets/bundles/bundle.properties | 1 + core/src/mindustry/ai/types/BuilderAI.java | 29 +++++++------- core/src/mindustry/ai/types/FormationAI.java | 16 ++++---- core/src/mindustry/ai/types/LogicAI.java | 4 +- core/src/mindustry/ai/types/MinerAI.java | 14 +++---- core/src/mindustry/content/UnitTypes.java | 37 ++++++++++-------- core/src/mindustry/core/NetClient.java | 8 ++-- core/src/mindustry/core/NetServer.java | 6 +-- .../mindustry/entities/comp/BuilderComp.java | 19 ++++++--- .../mindustry/entities/comp/BuildingComp.java | 2 +- .../mindustry/entities/comp/MinerComp.java | 6 ++- .../mindustry/entities/comp/PlayerComp.java | 6 +-- .../src/mindustry/entities/comp/UnitComp.java | 6 +-- .../mindustry/graphics/OverlayRenderer.java | 2 +- core/src/mindustry/input/DesktopInput.java | 16 ++++---- core/src/mindustry/input/InputHandler.java | 24 ++++++------ core/src/mindustry/input/MobileInput.java | 14 +++---- core/src/mindustry/logic/LExecutor.java | 31 ++++++--------- core/src/mindustry/type/UnitType.java | 4 +- .../ui/fragments/PlacementFragment.java | 2 +- .../world/blocks/ConstructBlock.java | 2 +- .../world/blocks/payloads/Payload.java | 1 + tests/src/test/java/ApplicationTests.java | 9 +++++ tests/src/test/resources/114.msav | Bin 0 -> 36280 bytes 38 files changed, 152 insertions(+), 125 deletions(-) create mode 100644 annotations/src/main/resources/revisions/alpha/0.json create mode 100644 annotations/src/main/resources/revisions/arkyid/0.json create mode 100644 annotations/src/main/resources/revisions/beta/0.json create mode 100644 annotations/src/main/resources/revisions/block/4.json create mode 100644 annotations/src/main/resources/revisions/corvus/4.json create mode 100644 annotations/src/main/resources/revisions/flare/4.json create mode 100644 annotations/src/main/resources/revisions/gamma/0.json create mode 100644 annotations/src/main/resources/revisions/mace/4.json create mode 100644 annotations/src/main/resources/revisions/mono/3.json create mode 100644 annotations/src/main/resources/revisions/pulsar/0.json create mode 100644 annotations/src/main/resources/revisions/quasar/0.json create mode 100644 annotations/src/main/resources/revisions/risso/4.json create mode 100644 annotations/src/main/resources/revisions/toxopid/0.json create mode 100644 tests/src/test/resources/114.msav diff --git a/annotations/src/main/resources/classids.properties b/annotations/src/main/resources/classids.properties index 43a3517ff0..293373a9df 100644 --- a/annotations/src/main/resources/classids.properties +++ b/annotations/src/main/resources/classids.properties @@ -1,10 +1,13 @@ #Maps entity names to IDs. Autogenerated. alpha=0 +arkyid=29 atrax=1 +beta=30 block=2 corvus=24 flare=3 +gamma=31 mace=4 mega=5 mindustry.entities.comp.BuildingComp=6 @@ -26,6 +29,8 @@ oct=26 poly=18 pulsar=19 quad=23 +quasar=32 risso=20 spiroct=21 +toxopid=33 vela=25 \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/alpha/0.json b/annotations/src/main/resources/revisions/alpha/0.json new file mode 100644 index 0000000000..eff5fa651d --- /dev/null +++ b/annotations/src/main/resources/revisions/alpha/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/arkyid/0.json b/annotations/src/main/resources/revisions/arkyid/0.json new file mode 100644 index 0000000000..eff5fa651d --- /dev/null +++ b/annotations/src/main/resources/revisions/arkyid/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/beta/0.json b/annotations/src/main/resources/revisions/beta/0.json new file mode 100644 index 0000000000..eff5fa651d --- /dev/null +++ b/annotations/src/main/resources/revisions/beta/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/block/4.json b/annotations/src/main/resources/revisions/block/4.json new file mode 100644 index 0000000000..7503c106b6 --- /dev/null +++ b/annotations/src/main/resources/revisions/block/4.json @@ -0,0 +1 @@ +{version:4,fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/corvus/4.json b/annotations/src/main/resources/revisions/corvus/4.json new file mode 100644 index 0000000000..7503c106b6 --- /dev/null +++ b/annotations/src/main/resources/revisions/corvus/4.json @@ -0,0 +1 @@ +{version:4,fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/flare/4.json b/annotations/src/main/resources/revisions/flare/4.json new file mode 100644 index 0000000000..7503c106b6 --- /dev/null +++ b/annotations/src/main/resources/revisions/flare/4.json @@ -0,0 +1 @@ +{version:4,fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/gamma/0.json b/annotations/src/main/resources/revisions/gamma/0.json new file mode 100644 index 0000000000..eff5fa651d --- /dev/null +++ b/annotations/src/main/resources/revisions/gamma/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/mace/4.json b/annotations/src/main/resources/revisions/mace/4.json new file mode 100644 index 0000000000..786a64a7e2 --- /dev/null +++ b/annotations/src/main/resources/revisions/mace/4.json @@ -0,0 +1 @@ +{version:4,fields:[{name:ammo,type:float},{name:armor,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/mono/3.json b/annotations/src/main/resources/revisions/mono/3.json new file mode 100644 index 0000000000..364fdca813 --- /dev/null +++ b/annotations/src/main/resources/revisions/mono/3.json @@ -0,0 +1 @@ +{version:3,fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/pulsar/0.json b/annotations/src/main/resources/revisions/pulsar/0.json new file mode 100644 index 0000000000..b2a9e3161a --- /dev/null +++ b/annotations/src/main/resources/revisions/pulsar/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/quasar/0.json b/annotations/src/main/resources/revisions/quasar/0.json new file mode 100644 index 0000000000..b2a9e3161a --- /dev/null +++ b/annotations/src/main/resources/revisions/quasar/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:baseRotation,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/risso/4.json b/annotations/src/main/resources/revisions/risso/4.json new file mode 100644 index 0000000000..7503c106b6 --- /dev/null +++ b/annotations/src/main/resources/revisions/risso/4.json @@ -0,0 +1 @@ +{version:4,fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/annotations/src/main/resources/revisions/toxopid/0.json b/annotations/src/main/resources/revisions/toxopid/0.json new file mode 100644 index 0000000000..eff5fa651d --- /dev/null +++ b/annotations/src/main/resources/revisions/toxopid/0.json @@ -0,0 +1 @@ +{fields:[{name:ammo,type:float},{name:armor,type:float},{name:controller,type:mindustry.entities.units.UnitController},{name:elevation,type:float},{name:flag,type:double},{name:health,type:float},{name:isShooting,type:boolean},{name:mineTile,type:mindustry.world.Tile},{name:mounts,type:"mindustry.entities.units.WeaponMount[]"},{name:plans,type:arc.struct.Queue},{name:rotation,type:float},{name:shield,type:float},{name:spawnedByCore,type:boolean},{name:stack,type:mindustry.type.ItemStack},{name:statuses,type:arc.struct.Seq},{name:team,type:mindustry.game.Team},{name:type,type:mindustry.type.UnitType},{name:x,type:float},{name:y,type:float}]} \ No newline at end of file diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 06533c4bcd..e20bd856c8 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -1279,6 +1279,7 @@ liquid.slag.description = Refined in separators into constituent metals, or spra liquid.oil.description = Used in advanced material production and as incendiary ammunition. liquid.cryofluid.description = Used as coolant in reactors, turrets and factories. +block.resupply-point.description = Resupplies nearby units with copper ammunition. Not compatible with units that require battery power. block.armored-conveyor.description = Moves items forward. Does not accept inputs from the sides. block.illuminator.description = Emits light. block.message.description = Stores a message for communication between allies. diff --git a/core/src/mindustry/ai/types/BuilderAI.java b/core/src/mindustry/ai/types/BuilderAI.java index 76200981ac..acb7464b9d 100644 --- a/core/src/mindustry/ai/types/BuilderAI.java +++ b/core/src/mindustry/ai/types/BuilderAI.java @@ -14,21 +14,20 @@ import static mindustry.Vars.*; public class BuilderAI extends AIController{ float buildRadius = 1500; boolean found = false; - @Nullable Builderc following; + @Nullable Unit following; @Override public void updateMovement(){ - Builderc builder = (Builderc)unit; - if(builder.moving()){ - builder.lookAt(builder.vel().angle()); + if(unit.moving()){ + unit.lookAt(unit.vel.angle()); } if(target != null && shouldShoot()){ unit.lookAt(target); } - builder.updateBuilding(true); + unit.updateBuilding = true; if(following != null){ //try to follow and mimic someone @@ -36,18 +35,18 @@ public class BuilderAI extends AIController{ //validate follower if(!following.isValid() || !following.activelyBuilding()){ following = null; - builder.plans().clear(); + unit.plans.clear(); return; } //set to follower's first build plan, whatever that is - builder.plans().clear(); - builder.plans().addFirst(following.buildPlan()); + unit.plans.clear(); + unit.plans.addFirst(following.buildPlan()); } - if(builder.buildPlan() != null){ + if(unit.buildPlan() != null){ //approach request if building - BuildPlan req = builder.buildPlan(); + BuildPlan req = unit.buildPlan(); boolean valid = (req.tile().build instanceof ConstructBuild && req.tile().bc().cblock == req.block) || @@ -60,7 +59,7 @@ public class BuilderAI extends AIController{ moveTo(req.tile(), buildingRange - 20f); }else{ //discard invalid request - builder.plans().removeFirst(); + unit.plans.removeFirst(); } }else{ @@ -71,8 +70,8 @@ public class BuilderAI extends AIController{ Units.nearby(unit.team, unit.x, unit.y, buildRadius, u -> { if(found) return; - if(u instanceof Builderc b && u != unit && b.activelyBuilding()){ - BuildPlan plan = b.buildPlan(); + if(u.canBuild() && u != unit && u.activelyBuilding()){ + BuildPlan plan = u.buildPlan(); Building build = world.build(plan.x, plan.y); if(build instanceof ConstructBuild cons){ @@ -80,7 +79,7 @@ public class BuilderAI extends AIController{ //make sure you can reach the request in time if(dist / unit.speed() < cons.buildCost * 0.9f){ - following = b; + following = u; found = true; } } @@ -98,7 +97,7 @@ public class BuilderAI extends AIController{ blocks.removeFirst(); }else if(Build.validPlace(content.block(block.block), unit.team(), block.x, block.y, block.rotation)){ //it's valid. //add build request. - builder.addBuild(new BuildPlan(block.x, block.y, block.rotation, content.block(block.block), block.config)); + unit.addBuild(new BuildPlan(block.x, block.y, block.rotation, content.block(block.block), block.config)); //shift build plan to tail so next unit builds something else. blocks.addLast(blocks.removeFirst()); }else{ diff --git a/core/src/mindustry/ai/types/FormationAI.java b/core/src/mindustry/ai/types/FormationAI.java index 98f4874779..6c6d3c127d 100644 --- a/core/src/mindustry/ai/types/FormationAI.java +++ b/core/src/mindustry/ai/types/FormationAI.java @@ -59,13 +59,13 @@ public class FormationAI extends AIController implements FormationMember{ unit.moveAt(realtarget.sub(unit).limit(speed)); } - if(unit instanceof Minerc mine && leader instanceof Minerc com){ - if(com.mineTile() != null && mine.validMine(com.mineTile())){ - mine.mineTile(com.mineTile()); + if(unit.canMine() && leader.canMine()){ + if(leader.mineTile != null && unit.validMine(leader.mineTile)){ + unit.mineTile(leader.mineTile); CoreBuild core = unit.team.core(); - if(core != null && com.mineTile().drop() != null && unit.within(core, unit.type.range) && !unit.acceptsItem(com.mineTile().drop())){ + if(core != null && leader.mineTile.drop() != null && unit.within(core, unit.type.range) && !unit.acceptsItem(leader.mineTile.drop())){ if(core.acceptStack(unit.stack.item, unit.stack.amount, unit) > 0){ Call.transferItemTo(unit.stack.item, unit.stack.amount, unit.x, unit.y, core); @@ -73,13 +73,13 @@ public class FormationAI extends AIController implements FormationMember{ } } }else{ - mine.mineTile(null); + unit.mineTile(null); } } - if(unit instanceof Builderc build && leader instanceof Builderc com && com.activelyBuilding()){ - build.clearBuilding(); - build.addBuild(com.buildPlan()); + if(unit.canBuild() && leader.canBuild() && leader.activelyBuilding()){ + unit.clearBuilding(); + leader.addBuild(unit.buildPlan()); } } diff --git a/core/src/mindustry/ai/types/LogicAI.java b/core/src/mindustry/ai/types/LogicAI.java index 20650efe53..d2f759150b 100644 --- a/core/src/mindustry/ai/types/LogicAI.java +++ b/core/src/mindustry/ai/types/LogicAI.java @@ -92,9 +92,7 @@ public class LogicAI extends AIController{ } } case stop -> { - if(unit instanceof Builderc build){ - build.clearBuilding(); - } + unit.clearBuilding(); } } diff --git a/core/src/mindustry/ai/types/MinerAI.java b/core/src/mindustry/ai/types/MinerAI.java index ae18e8085b..97d8198bb2 100644 --- a/core/src/mindustry/ai/types/MinerAI.java +++ b/core/src/mindustry/ai/types/MinerAI.java @@ -17,21 +17,21 @@ public class MinerAI extends AIController{ protected void updateMovement(){ Building core = unit.closestCore(); - if(!(unit instanceof Minerc miner) || core == null) return; + if(!(unit.canMine()) || core == null) return; - if(miner.mineTile() != null && !miner.mineTile().within(unit, unit.type.range)){ - miner.mineTile(null); + if(unit.mineTile != null && !unit.mineTile.within(unit, unit.type.range)){ + unit.mineTile(null); } if(mining){ if(timer.get(timerTarget2, 60 * 4) || targetItem == null){ - targetItem = unit.team.data().mineItems.min(i -> indexer.hasOre(i) && miner.canMine(i), i -> core.items.get(i)); + targetItem = unit.team.data().mineItems.min(i -> indexer.hasOre(i) && unit.canMine(i), i -> core.items.get(i)); } //core full of the target item, do nothing if(targetItem != null && core.acceptStack(targetItem, 1, unit) == 0){ unit.clearItem(); - miner.mineTile(null); + unit.mineTile(null); return; } @@ -47,7 +47,7 @@ public class MinerAI extends AIController{ moveTo(ore, unit.type.range / 2f, 20f); if(unit.within(ore, unit.type.range)){ - miner.mineTile(ore); + unit.mineTile = ore; } if(ore.block() != Blocks.air){ @@ -56,7 +56,7 @@ public class MinerAI extends AIController{ } } }else{ - miner.mineTile(null); + unit.mineTile = null; if(unit.stack.amount == 0){ mining = true; diff --git a/core/src/mindustry/content/UnitTypes.java b/core/src/mindustry/content/UnitTypes.java index b9a0ee1d09..19606cf7f7 100644 --- a/core/src/mindustry/content/UnitTypes.java +++ b/core/src/mindustry/content/UnitTypes.java @@ -17,11 +17,13 @@ import static mindustry.Vars.*; public class UnitTypes implements ContentList{ //region definitions + //(the wall of shame - should fix the legacy stuff eventually...) + //mech public static @EntityDef({Unitc.class, Mechc.class}) UnitType mace, dagger, crawler, fortress, scepter, reign; - //mech + builder + miner - public static @EntityDef({Unitc.class, Mechc.class, Builderc.class}) UnitType nova, pulsar, quasar; + //mech + public static @EntityDef(value = {Unitc.class, Mechc.class}, legacy = true) UnitType nova, pulsar, quasar; //mech public static @EntityDef({Unitc.class, Mechc.class}) UnitType vela; @@ -29,29 +31,29 @@ public class UnitTypes implements ContentList{ //legs public static @EntityDef({Unitc.class, Legsc.class}) UnitType corvus, atrax; - //legs + building - public static @EntityDef({Unitc.class, Legsc.class, Builderc.class}) UnitType spiroct, arkyid, toxopid; + //legs + public static @EntityDef(value = {Unitc.class, Legsc.class}, legacy = true) UnitType spiroct, arkyid, toxopid; - //air (no special traits) + //air public static @EntityDef({Unitc.class}) UnitType flare, eclipse, horizon, zenith, antumbra; - //air, legacy mining + //air public static @EntityDef(value = {Unitc.class}, legacy = true) UnitType mono; - //air + building + mining - public static @EntityDef({Unitc.class, Builderc.class}) UnitType poly; + //air + public static @EntityDef(value = {Unitc.class}, legacy = true) UnitType poly; - //air + building + mining + payload - public static @EntityDef({Unitc.class, Builderc.class, Payloadc.class}) UnitType mega; + //air + payload + public static @EntityDef({Unitc.class, Payloadc.class}) UnitType mega; - //air + building + payload - public static @EntityDef(value = {Unitc.class, Builderc.class, Payloadc.class}, legacy = true) UnitType quad; + //air + payload + public static @EntityDef(value = {Unitc.class, Payloadc.class}, legacy = true) UnitType quad; - //air + building + payload - public static @EntityDef({Unitc.class, Builderc.class, Payloadc.class, AmmoDistributec.class}) UnitType oct; + //air + payload + ammo distribution + public static @EntityDef({Unitc.class, Payloadc.class, AmmoDistributec.class}) UnitType oct; - //air + building + mining - public static @EntityDef({Unitc.class, Builderc.class}) UnitType alpha, beta, gamma; + //air + public static @EntityDef(value = {Unitc.class}, legacy = true) UnitType alpha, beta, gamma; //water public static @EntityDef({Unitc.class, WaterMovec.class}) UnitType risso, minke, bryde, sei, omura; @@ -470,7 +472,6 @@ public class UnitTypes implements ContentList{ mineTier = 1; hitSize = 29f; health = 18000f; - buildSpeed = 1.7f; armor = 9f; landShake = 1.5f; rotateSpeed = 1.5f; @@ -700,6 +701,7 @@ public class UnitTypes implements ContentList{ rippleScale = 2f; legSpeed = 0.2f; ammoType = AmmoTypes.power; + buildSpeed = 1f; legSplashDamage = 32; legSplashRange = 30; @@ -802,6 +804,7 @@ public class UnitTypes implements ContentList{ rippleScale = 3f; legSpeed = 0.19f; ammoType = AmmoTypes.powerHigh; + buildSpeed = 1f; legSplashDamage = 80; legSplashRange = 60; diff --git a/core/src/mindustry/core/NetClient.java b/core/src/mindustry/core/NetClient.java index 9fff2051e1..f2cc3d6353 100644 --- a/core/src/mindustry/core/NetClient.java +++ b/core/src/mindustry/core/NetClient.java @@ -571,13 +571,13 @@ public class NetClient implements ApplicationListener{ BuildPlan[] requests = null; if(player.isBuilder()){ //limit to 10 to prevent buffer overflows - int usedRequests = Math.min(player.builder().plans().size, 10); + int usedRequests = Math.min(player.unit().plans().size, 10); int totalLength = 0; //prevent buffer overflow by checking config length for(int i = 0; i < usedRequests; i++){ - BuildPlan plan = player.builder().plans().get(i); + BuildPlan plan = player.unit().plans().get(i); if(plan.config instanceof byte[] b){ int length = b.length; totalLength += length; @@ -591,7 +591,7 @@ public class NetClient implements ApplicationListener{ requests = new BuildPlan[usedRequests]; for(int i = 0; i < usedRequests; i++){ - requests[i] = player.builder().plans().get(i); + requests[i] = player.unit().plans().get(i); } } @@ -607,7 +607,7 @@ public class NetClient implements ApplicationListener{ unit.rotation, unit instanceof Mechc m ? m.baseRotation() : 0, unit.vel.x, unit.vel.y, - player.unit().mineTile(), + player.unit().mineTile, player.boosting, player.shooting, ui.chatfrag.shown(), control.input.isBuilding, requests, Core.camera.position.x, Core.camera.position.y, diff --git a/core/src/mindustry/core/NetServer.java b/core/src/mindustry/core/NetServer.java index a3340f510b..959874e82d 100644 --- a/core/src/mindustry/core/NetServer.java +++ b/core/src/mindustry/core/NetServer.java @@ -600,8 +600,8 @@ public class NetServer implements ApplicationListener{ player.unit().aim(pointerX, pointerY); if(player.isBuilder()){ - player.builder().clearBuilding(); - player.builder().updateBuilding(building); + player.unit().clearBuilding(); + player.unit().updateBuilding(building); if(requests != null){ for(BuildPlan req : requests){ @@ -625,7 +625,7 @@ public class NetServer implements ApplicationListener{ con.rejectedRequests.add(req); continue; } - player.builder().plans().addLast(req); + player.unit().plans().addLast(req); } } } diff --git a/core/src/mindustry/entities/comp/BuilderComp.java b/core/src/mindustry/entities/comp/BuilderComp.java index 4d9f81fa65..12fd9e8029 100644 --- a/core/src/mindustry/entities/comp/BuilderComp.java +++ b/core/src/mindustry/entities/comp/BuilderComp.java @@ -13,6 +13,7 @@ import mindustry.entities.units.*; import mindustry.game.EventType.*; import mindustry.gen.*; import mindustry.graphics.*; +import mindustry.type.*; import mindustry.world.*; import mindustry.world.blocks.*; import mindustry.world.blocks.ConstructBlock.*; @@ -22,17 +23,22 @@ import java.util.*; import static mindustry.Vars.*; @Component -abstract class BuilderComp implements Unitc{ +abstract class BuilderComp implements Posc, Teamc, Rotc{ static final Vec2[] vecs = new Vec2[]{new Vec2(), new Vec2(), new Vec2(), new Vec2()}; @Import float x, y, rotation; + @Import UnitType type; @SyncLocal Queue plans = new Queue<>(1); @SyncLocal transient boolean updateBuilding = true; + public boolean canBuild(){ + return type.buildSpeed > 0; + } + @Override public void update(){ - if(!updateBuilding) return; + if(!updateBuilding || !canBuild()) return; float finalPlaceDst = state.rules.infiniteResources ? Float.MAX_VALUE : buildingRange; boolean infinite = state.rules.infiniteResources || team().rules().infiniteResources; @@ -102,9 +108,9 @@ abstract class BuilderComp implements Unitc{ ConstructBuild entity = tile.bc(); if(current.breaking){ - entity.deconstruct(self(), core, 1f / entity.buildCost * Time.delta * type().buildSpeed * state.rules.buildSpeedMultiplier); + entity.deconstruct(self(), core, 1f / entity.buildCost * Time.delta * type.buildSpeed * state.rules.buildSpeedMultiplier); }else{ - entity.construct(self(), core, 1f / entity.buildCost * Time.delta * type().buildSpeed * state.rules.buildSpeedMultiplier, current.config); + entity.construct(self(), core, 1f / entity.buildCost * Time.delta * type.buildSpeed * state.rules.buildSpeedMultiplier, current.config); } current.stuck = Mathf.equal(current.progress, entity.progress); @@ -161,6 +167,8 @@ abstract class BuilderComp implements Unitc{ /** Add another build requests to the queue, if it doesn't exist there yet. */ void addBuild(BuildPlan place, boolean tail){ + if(!canBuild()) return; + BuildPlan replace = null; for(BuildPlan request : plans){ if(request.x == place.x && request.y == place.y){ @@ -196,9 +204,8 @@ abstract class BuilderComp implements Unitc{ return plans.size == 0 ? null : plans.first(); } - @Override public void draw(){ - if(!isBuilding() || !updateBuilding) return; + if(!isBuilding() || !updateBuilding || !canBuild()) return; //TODO check correctness Draw.z(Layer.flyingUnit); diff --git a/core/src/mindustry/entities/comp/BuildingComp.java b/core/src/mindustry/entities/comp/BuildingComp.java index 2dc21c8c66..2f63bd1063 100644 --- a/core/src/mindustry/entities/comp/BuildingComp.java +++ b/core/src/mindustry/entities/comp/BuildingComp.java @@ -1122,7 +1122,7 @@ abstract class BuildingComp implements Posc, Teamc, Healthc, Buildingc, Timerc, /** Returns whether or not a hand cursor should be shown over this block. */ public Cursor getCursor(){ - return block.configurable ? SystemCursor.hand : SystemCursor.arrow; + return block.configurable && team == player.team() ? SystemCursor.hand : SystemCursor.arrow; } /** diff --git a/core/src/mindustry/entities/comp/MinerComp.java b/core/src/mindustry/entities/comp/MinerComp.java index 8a55a19c1f..eb5802711e 100644 --- a/core/src/mindustry/entities/comp/MinerComp.java +++ b/core/src/mindustry/entities/comp/MinerComp.java @@ -32,7 +32,7 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc{ } boolean mining(){ - return mineTile != null && !(((Object)this) instanceof Builderc b && b.activelyBuilding()); + return mineTile != null && !this.self().activelyBuilding(); } public boolean validMine(Tile tile, boolean checkDst){ @@ -44,6 +44,10 @@ abstract class MinerComp implements Itemsc, Posc, Teamc, Rotc, Drawc{ return validMine(tile, true); } + public boolean canMine(){ + return type.mineSpeed > 0; + } + @Override public void update(){ Building core = closestCore(); diff --git a/core/src/mindustry/entities/comp/PlayerComp.java b/core/src/mindustry/entities/comp/PlayerComp.java index bde8712e36..0f8236377c 100644 --- a/core/src/mindustry/entities/comp/PlayerComp.java +++ b/core/src/mindustry/entities/comp/PlayerComp.java @@ -48,7 +48,7 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra transient float textFadeTime; public boolean isBuilder(){ - return unit instanceof Builderc; + return unit.canBuild(); } public @Nullable CoreBuild closestCore(){ @@ -160,10 +160,6 @@ abstract class PlayerComp implements UnitController, Entityc, Syncc, Timerc, Dra return unit; } - public Builderc builder(){ - return !(unit instanceof Builderc) ? Nulls.builder : (Builderc)unit; - } - public void unit(Unit unit){ if(unit == null) throw new IllegalArgumentException("Unit cannot be null. Use clearUnit() instead."); if(this.unit == unit) return; diff --git a/core/src/mindustry/entities/comp/UnitComp.java b/core/src/mindustry/entities/comp/UnitComp.java index 097cce0130..aa02f25022 100644 --- a/core/src/mindustry/entities/comp/UnitComp.java +++ b/core/src/mindustry/entities/comp/UnitComp.java @@ -31,7 +31,7 @@ import mindustry.world.blocks.payloads.*; import static mindustry.Vars.*; @Component(base = true) -abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Commanderc, Displayable, Senseable, Ranged, Minerc{ +abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, Itemsc, Rotc, Unitc, Weaponsc, Drawc, Boundedc, Syncc, Shieldc, Commanderc, Displayable, Senseable, Ranged, Minerc, Builderc{ @Import boolean hovering, dead; @Import float x, y, rotation, elevation, maxHealth, drag, armor, hitSize, health, ammo, minFormationSpeed; @@ -88,8 +88,8 @@ abstract class UnitComp implements Healthc, Physicsc, Hitboxc, Statusc, Teamc, I /** @return where the unit wants to look at. */ public float prefRotation(){ - if(this instanceof Builderc builder && builder.activelyBuilding()){ - return angleTo(builder.buildPlan()); + if(activelyBuilding()){ + return angleTo(buildPlan()); }else if(mineTile() != null){ return angleTo(mineTile()); }else if(moving()){ diff --git a/core/src/mindustry/graphics/OverlayRenderer.java b/core/src/mindustry/graphics/OverlayRenderer.java index d2a3cb3700..6723f81c66 100644 --- a/core/src/mindustry/graphics/OverlayRenderer.java +++ b/core/src/mindustry/graphics/OverlayRenderer.java @@ -28,7 +28,7 @@ public class OverlayRenderer{ if(player.dead()) return; if(player.isBuilder()){ - player.builder().drawBuildRequests(); + player.unit().drawBuildRequests(); } input.drawBottom(); diff --git a/core/src/mindustry/input/DesktopInput.java b/core/src/mindustry/input/DesktopInput.java index 611c9b5305..b35778c881 100644 --- a/core/src/mindustry/input/DesktopInput.java +++ b/core/src/mindustry/input/DesktopInput.java @@ -59,7 +59,7 @@ public class DesktopInput extends InputHandler{ group.fill(t -> { t.bottom(); t.visible(() -> { - t.color.a = Mathf.lerpDelta(t.color.a, player.builder().isBuilding() ? 1f : 0f, 0.15f); + t.color.a = Mathf.lerpDelta(t.color.a, player.unit().isBuilding() ? 1f : 0f, 0.15f); return ui.hudfrag.shown && Core.settings.getBool("hints") && selectRequests.isEmpty() && t.color.a > 0.01f; }); @@ -290,7 +290,7 @@ public class DesktopInput extends InputHandler{ cursorType = cursor.build.getCursor(); } - if(isPlacing() || !selectRequests.isEmpty()){ + if((isPlacing() && player.isBuilder()) || !selectRequests.isEmpty()){ cursorType = SystemCursor.hand; } @@ -366,7 +366,7 @@ public class DesktopInput extends InputHandler{ int rawCursorX = World.toTile(Core.input.mouseWorld().x), rawCursorY = World.toTile(Core.input.mouseWorld().y); // automatically pause building if the current build queue is empty - if(Core.settings.getBool("buildautopause") && isBuilding && !player.builder().isBuilding()){ + if(Core.settings.getBool("buildautopause") && isBuilding && !player.unit().isBuilding()){ isBuilding = false; buildWasAutoPaused = true; } @@ -388,7 +388,7 @@ public class DesktopInput extends InputHandler{ } if(Core.input.keyTap(Binding.clear_building)){ - player.builder().clearBuilding(); + player.unit().clearBuilding(); } if(Core.input.keyTap(Binding.schematic_select) && !Core.scene.hasKeyboard() && mode != breaking){ @@ -480,8 +480,8 @@ public class DesktopInput extends InputHandler{ deleting = true; }else if(selected != null){ //only begin shooting if there's no cursor event - if(!tileTapped(selected.build) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && !player.builder().activelyBuilding() && !droppingItem && - !tryBeginMine(selected) && player.unit().mineTile() == null && !Core.scene.hasKeyboard()){ + if(!tileTapped(selected.build) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && !player.unit().activelyBuilding() && !droppingItem && + !tryBeginMine(selected) && player.unit().mineTile == null && !Core.scene.hasKeyboard()){ player.shooting = shouldShoot; } }else if(!Core.scene.hasKeyboard()){ //if it's out of bounds, shooting is just fine @@ -506,7 +506,7 @@ public class DesktopInput extends InputHandler{ if(Core.input.keyDown(Binding.select) && mode == none && !isPlacing() && deleting){ BuildPlan req = getRequest(cursorX, cursorY); if(req != null && req.breaking){ - player.builder().plans().remove(req); + player.unit().plans().remove(req); } }else{ deleting = false; @@ -547,7 +547,7 @@ public class DesktopInput extends InputHandler{ if(sreq != null){ if(getRequest(sreq.x, sreq.y, sreq.block.size, sreq) != null){ - player.builder().plans().remove(sreq, true); + player.unit().plans().remove(sreq, true); } sreq = null; } diff --git a/core/src/mindustry/input/InputHandler.java b/core/src/mindustry/input/InputHandler.java index bd72655e18..ed219c026e 100644 --- a/core/src/mindustry/input/InputHandler.java +++ b/core/src/mindustry/input/InputHandler.java @@ -169,7 +169,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ @Remote(variants = Variant.one) public static void removeQueueBlock(int x, int y, boolean breaking){ - player.builder().removeBuild(x, y, breaking); + player.unit().removeBuild(x, y, breaking); } @Remote(targets = Loc.both, called = Loc.server) @@ -383,7 +383,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public Eachable allRequests(){ return cons -> { - for(BuildPlan request : player.builder().plans()) cons.get(request); + for(BuildPlan request : player.unit().plans()) cons.get(request); for(BuildPlan request : selectRequests) cons.get(request); for(BuildPlan request : lineRequests) cons.get(request); }; @@ -401,7 +401,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ player.typing = ui.chatfrag.shown(); if(player.isBuilder()){ - player.builder().updateBuilding(isBuilding); + player.unit().updateBuilding(isBuilding); } if(player.shooting && !wasShooting && player.unit().hasWeapons() && state.rules.unitAmmo && player.unit().ammo <= 0){ @@ -653,7 +653,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ return r2.overlaps(r1); }; - for(BuildPlan req : player.builder().plans()){ + for(BuildPlan req : player.unit().plans()){ if(test.get(req)) return req; } @@ -678,7 +678,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ Draw.color(Pal.remove); Lines.stroke(1f); - for(BuildPlan req : player.builder().plans()){ + for(BuildPlan req : player.unit().plans()){ if(req.breaking) continue; if(req.bounds(Tmp.r2).overlaps(Tmp.r1)){ drawBreaking(req); @@ -740,7 +740,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ for(BuildPlan req : requests){ if(req.block != null && validPlace(req.x, req.y, req.block, req.rotation)){ BuildPlan copy = req.copy(); - player.builder().addBuild(copy); + player.unit().addBuild(copy); } } } @@ -804,7 +804,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ //remove build requests Tmp.r1.set(result.x * tilesize, result.y * tilesize, (result.x2 - result.x) * tilesize, (result.y2 - result.y) * tilesize); - Iterator it = player.builder().plans().iterator(); + Iterator it = player.unit().plans().iterator(); while(it.hasNext()){ BuildPlan req = it.next(); if(!req.breaking && req.bounds(Tmp.r2).overlaps(Tmp.r1)){ @@ -1044,7 +1044,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } public boolean canShoot(){ - return block == null && !onConfigurable() && !isDroppingItem() && !player.builder().activelyBuilding() && + return block == null && !onConfigurable() && !isDroppingItem() && !player.unit().activelyBuilding() && !(player.unit() instanceof Mechc && player.unit().isFlying()); } @@ -1090,7 +1090,7 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ } public boolean validPlace(int x, int y, Block type, int rotation, BuildPlan ignore){ - for(BuildPlan req : player.builder().plans()){ + for(BuildPlan req : player.unit().plans()){ if(req != ignore && !req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).overlaps(type.bounds(x, y, Tmp.r2)) @@ -1108,15 +1108,15 @@ public abstract class InputHandler implements InputProcessor, GestureListener{ public void placeBlock(int x, int y, Block block, int rotation){ BuildPlan req = getRequest(x, y); if(req != null){ - player.builder().plans().remove(req); + player.unit().plans().remove(req); } - player.builder().addBuild(new BuildPlan(x, y, rotation, block, block.nextConfig())); + player.unit().addBuild(new BuildPlan(x, y, rotation, block, block.nextConfig())); } public void breakBlock(int x, int y){ Tile tile = world.tile(x, y); if(tile != null && tile.build != null) tile = tile.build.tile; - player.builder().addBuild(new BuildPlan(tile.x, tile.y)); + player.unit().addBuild(new BuildPlan(tile.x, tile.y)); } public void drawArrow(Block block, int x, int y, int rotation){ diff --git a/core/src/mindustry/input/MobileInput.java b/core/src/mindustry/input/MobileInput.java index 6edea2d80a..fa39ad5492 100644 --- a/core/src/mindustry/input/MobileInput.java +++ b/core/src/mindustry/input/MobileInput.java @@ -114,7 +114,7 @@ public class MobileInput extends InputHandler implements GestureListener{ } } - for(BuildPlan req : player.builder().plans()){ + for(BuildPlan req : player.unit().plans()){ Tile other = world.tile(req.x, req.y); if(other == null || req.breaking) continue; @@ -219,10 +219,10 @@ public class MobileInput extends InputHandler implements GestureListener{ BuildPlan copy = request.copy(); if(other == null){ - player.builder().addBuild(copy); + player.unit().addBuild(copy); }else if(!other.breaking && other.x == request.x && other.y == request.y && other.block.size == request.block.size){ - player.builder().plans().remove(other); - player.builder().addBuild(copy); + player.unit().plans().remove(other); + player.unit().addBuild(copy); } } @@ -245,10 +245,10 @@ public class MobileInput extends InputHandler implements GestureListener{ Boolp schem = () -> lastSchematic != null && !selectRequests.isEmpty(); group.fill(t -> { - t.visible(() -> (player.builder().isBuilding() || block != null || mode == breaking || !selectRequests.isEmpty()) && !schem.get()); + t.visible(() -> (player.unit().isBuilding() || block != null || mode == breaking || !selectRequests.isEmpty()) && !schem.get()); t.bottom().left(); t.button("@cancel", Icon.cancel, () -> { - player.builder().clearBuilding(); + player.unit().clearBuilding(); selectRequests.clear(); mode = none; block = null; @@ -914,7 +914,7 @@ public class MobileInput extends InputHandler implements GestureListener{ } //update shooting if not building + not mining - if(!player.builder().isBuilding() && player.unit().mineTile() == null){ + if(!player.unit().isBuilding() && player.unit().mineTile == null){ //autofire targeting if(manualShooting){ diff --git a/core/src/mindustry/logic/LExecutor.java b/core/src/mindustry/logic/LExecutor.java index d4c20eaf5e..244746ef81 100644 --- a/core/src/mindustry/logic/LExecutor.java +++ b/core/src/mindustry/logic/LExecutor.java @@ -321,13 +321,8 @@ public class LExecutor{ ((LogicAI)unit.controller()).controller = exec.building(varThis); //clear old state - if(unit instanceof Minerc miner){ - miner.mineTile(null); - } - - if(unit instanceof Builderc builder){ - builder.clearBuilding(); - } + unit.mineTile = null; + unit.clearBuilding(); return (LogicAI)unit.controller(); } @@ -357,12 +352,8 @@ public class LExecutor{ //stop mining/building if(type == LUnitControl.stop){ - if(unit instanceof Minerc miner){ - miner.mineTile(null); - } - if(unit instanceof Builderc build){ - build.clearBuilding(); - } + unit.mineTile = null; + unit.clearBuilding(); } } case within -> { @@ -390,8 +381,8 @@ public class LExecutor{ } case mine -> { Tile tile = world.tileWorld(x1, y1); - if(unit instanceof Minerc miner){ - miner.mineTile(miner.validMine(tile) ? tile : null); + if(unit.canMine()){ + unit.mineTile = unit.validMine(tile) ? tile : null; } } case payDrop -> { @@ -432,12 +423,12 @@ public class LExecutor{ } } case build -> { - if(unit instanceof Builderc builder && exec.obj(p3) instanceof Block block){ + if(unit.canBuild() && exec.obj(p3) instanceof Block block){ int x = World.toTile(x1), y = World.toTile(y1); int rot = exec.numi(p4); //reset state of last request when necessary - if(ai.plan.x != x || ai.plan.y != y || ai.plan.block != block || builder.plans().isEmpty()){ + if(ai.plan.x != x || ai.plan.y != y || ai.plan.block != block || unit.plans.isEmpty()){ ai.plan.progress = 0; ai.plan.initialized = false; ai.plan.stuck = false; @@ -446,11 +437,11 @@ public class LExecutor{ ai.plan.set(x, y, rot, block); ai.plan.config = exec.obj(p5) instanceof Content c ? c : null; - builder.clearBuilding(); + unit.clearBuilding(); if(ai.plan.tile() != null){ - builder.updateBuilding(true); - builder.addBuild(ai.plan); + unit.updateBuilding = true; + unit.addBuild(ai.plan); } } } diff --git a/core/src/mindustry/type/UnitType.java b/core/src/mindustry/type/UnitType.java index 2a5be98056..6da3a75ec0 100644 --- a/core/src/mindustry/type/UnitType.java +++ b/core/src/mindustry/type/UnitType.java @@ -80,7 +80,7 @@ public class UnitType extends UnlockableContent{ public int ammoCapacity = -1; public AmmoType ammoType = AmmoTypes.copper; public int mineTier = -1; - public float buildSpeed = 1f, mineSpeed = 1f; + public float buildSpeed = -1f, mineSpeed = 1f; public Sound mineSound = Sounds.minebeam; public float mineSoundVolume = 0.6f; @@ -225,7 +225,7 @@ public class UnitType extends UnlockableContent{ stats.addPercent(Stat.mineSpeed, mineSpeed); stats.add(Stat.mineTier, new BlockFilterValue(b -> b instanceof Floor f && f.itemDrop != null && f.itemDrop.hardness <= mineTier && !f.playerUnmineable)); } - if(inst instanceof Builderc){ + if(buildSpeed > 0){ stats.addPercent(Stat.buildSpeed, buildSpeed); } if(inst instanceof Payloadc){ diff --git a/core/src/mindustry/ui/fragments/PlacementFragment.java b/core/src/mindustry/ui/fragments/PlacementFragment.java index 4c268e5dcd..9115b16785 100644 --- a/core/src/mindustry/ui/fragments/PlacementFragment.java +++ b/core/src/mindustry/ui/fragments/PlacementFragment.java @@ -102,7 +102,7 @@ public class PlacementFragment extends Fragment{ Block tryRecipe = tile == null ? null : tile.block instanceof ConstructBlock ? ((ConstructBuild)tile).cblock : tile.block; Object tryConfig = tile == null ? null : tile.config(); - for(BuildPlan req : player.builder().plans()){ + for(BuildPlan req : player.unit().plans()){ if(!req.breaking && req.block.bounds(req.x, req.y, Tmp.r1).contains(Core.input.mouseWorld())){ tryRecipe = req.block; tryConfig = req.config; diff --git a/core/src/mindustry/world/blocks/ConstructBlock.java b/core/src/mindustry/world/blocks/ConstructBlock.java index ce37e80dde..34532df8b0 100644 --- a/core/src/mindustry/world/blocks/ConstructBlock.java +++ b/core/src/mindustry/world/blocks/ConstructBlock.java @@ -179,7 +179,7 @@ public class ConstructBlock extends Block{ if(control.input.buildWasAutoPaused && !control.input.isBuilding && player.isBuilder()){ control.input.isBuilding = true; } - player.builder().addBuild(new BuildPlan(tile.x, tile.y, rotation, cblock, lastConfig), false); + player.unit().addBuild(new BuildPlan(tile.x, tile.y, rotation, cblock, lastConfig), false); } } diff --git a/core/src/mindustry/world/blocks/payloads/Payload.java b/core/src/mindustry/world/blocks/payloads/Payload.java index 0e4773538f..ec174f238d 100644 --- a/core/src/mindustry/world/blocks/payloads/Payload.java +++ b/core/src/mindustry/world/blocks/payloads/Payload.java @@ -57,6 +57,7 @@ public interface Payload{ return (T)payload; }else if(type == payloadUnit){ byte id = read.b(); + if(EntityMapping.map(id) == null) throw new RuntimeException("No type with ID " + id + " found."); Unit unit = (Unit)EntityMapping.map(id).get(); unit.read(read); return (T)new UnitPayload(unit); diff --git a/tests/src/test/java/ApplicationTests.java b/tests/src/test/java/ApplicationTests.java index 72393306ad..5895b7fe10 100644 --- a/tests/src/test/java/ApplicationTests.java +++ b/tests/src/test/java/ApplicationTests.java @@ -433,6 +433,15 @@ public class ApplicationTests{ assertEquals(256, world.height()); } + @Test + void load114Save(){ + resetWorld(); + SaveIO.load(Core.files.internal("114.msav")); + + assertEquals(500, world.width()); + assertEquals(500, world.height()); + } + @Test void arrayIterators(){ Seq arr = Seq.with("a", "b" , "c", "d", "e", "f"); diff --git a/tests/src/test/resources/114.msav b/tests/src/test/resources/114.msav new file mode 100644 index 0000000000000000000000000000000000000000..e0c9277c7713517ee79dff4994bb85d7fd2a6131 GIT binary patch literal 36280 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%0D1Q@UHuMb#23+2hwYI~{+$_gT`tr`d*4*JCH_ z_e%Hq6YyNCB<9T#y}0|!nfFI5{_=lr(VCU>g{q>C&k+9qwd}6`+x_7M8TI>Kl+@|O zRo`@fde83T`K}8yMC2wv>hHd!=HJb$ex!eIUf#)K_ou&EEAyP@NsHh0@%%l1TJ_%A zyi=Q_e#cBnKmPP)ZPIhijf>BD$4r0zOXm0A#E*wLkM9T<{wcladu>x{(4W5@M;cc< zEf4=w7xSei?8Qr&u*h2l{U;37e(YJ(ex$$FzEJgGxY!?Nr~IITuh#40B}AE%x3vfx91PmUp%Mmv|Q!aP4%}6bZh2K{x;LD-s=0FUnjSGuve?IRy}|HsNTQI z`_J`N&huNZJ$n57y#IwCb=SZ5UR0m!{(Na{#=2Fet!*`OlfP|`f3j2M?UN}l-tXy7 zzUFw)T%qJ^rR?QZwf>#5Qclm8-1yq`AIWo@cOfJ+K0g1RtwjE%|B|TQnsc` zr(Nhx+`H3#U)KnC@^62-dENb&LicSHw|&{d$F){@(Q2EunIGpfFDjP(|K(|hT=|^y z_7fg_K5wILXRd8h8dGtgaNmmC8|~I6Z+ZM_&T;$M5ALX2WOw!$HwFLRIP1sKb)hc= z|Jy&@u`Ds{MP>CJVXMtezHj5F|8AFg?zM|G+S|@w{f5oj@3C*JxAf1y)f~3}f`wkh zUcc|$viG-{Pck!9{3Ru1FX#W_XIY?7q_ooFOP6C7=pTIX_`;4Kx5Zn+qW_ehDdbaF zYO7WMe97Kde>$&u+Qia{-5usKZ#WTUM=sNoaI$~%#iPO)Wp|Hzder^KDGR)t8Vg3 z_I1Qzzt6_r!ME2fuXIkadphHAHtzw;YZKIty1T`kd1!NXvE{70PM??kc(>@pi^s=Y zm;d}*wBh-Z$9uldI8hr@vhqvG-4onR54Swt@nYu%w`t9GYO;^lUAkmvv-Ii4yG3gp zU)~a(|Mo_a*An9nFmgwUdrtf4a}SrGJ}$e))a&7ZL$aZC^gBIwpGY za%|(tU+2>W^y$SEvTd!OD<^0{UHHxy45HG6t**@+6mwWuDqwH)M&x#i@v8KnJlf#vN9531sXuR?k^?$`}+jo|;&OCf^W8myd(Nf5{$&!&yy&ygxtCEBQhV0+TghJ5ZJ(p?tD@&)?(vT);fJ*Q z7Fx_-yr(2C;PEAgL#wrNx5>ScuRJPaeC%uUr=0aSUidyVef%h5+nf&p^LCwEq*A!Z z`Oy^*q5S@?^QK4qe!D(@{`B{DnQXoGqvECe+^@U6-m|Xnd5~P_!;3E;&H8b(<+$s? z519`yzAX9cDYIE-!uyi@jpzPbryt*OYPFKpo6{$YBI|zlJf7F;cYJI0xx(7u`a|B& zPd|!E{&d0gzBB8dbsYQ7bLE}Yj=JPldd~J*L|5gDKhye3f3>z>oBgj?JbCvkx61Mz zmC5t>wWOUieE!0}z@Ky8YNgxJPSMvFp0QYGoujhlx9sydr#Sg#KQFOS6P7dmc-Oh_ z^9gpL_^WnnUs|Yd7GE!S{9~>CBZj|Qb^cyja^mgn*DWq-?+X4E_4=*QuQ-0Y z=e4mfviE*ik?d#pv?Rv!*IcFTm*$tOsXV@OLF${MpKjDfUybq1cJH;#cr6pX{=!Uw zxi6|z=iZm#H7eY@@%;Qf{XL!eXDqJ;f2t|bpMOsGx8>8qi@ob2rNkuJTKd0jjf$2l zj}cz<#q@v3KHCpFEw)|xcj|V&&y$Xw_ScPm?AUDoaqDLP1?x+<=q+4dd(BY%_{7rX zf32Q#^mm@!>Q+$2|7%xX!G)vyoS#48>$(%iFMP;3`}@B0S9i_0<|QU9|JL_c_q4}W zi>F=QYj3#CPc^srmf>~zvNQJnob#4FxU!Xh*Svk+KYF~XvpV-JyHh`H;??^42(8rn zaq~Op8@0`wB46dt9yBM(@`*+0i~sXPzFFP5v}{4Joq7yU@vbd_H>Vjf-}xauF*Z`; zh~APXb%+06n%ZwH5WerL{Qvy|>Rf`ySOfhvxP@$+D{t zTckxCi(CJr7_R+i1LSwHdb@A8}bmvdg#W_+`N zg^AaznAJ(fH^ln=jZ8jP`6Z(EePL_Z=QTfwEUG{IH+HvVdPz39V=+TKU6i$0u*Uy^S1 zUEh26|Jw7VH~;D^`xE}|`TrR{H+8pJsGZf?+Ub`nGx?oj{&eBa*c$Va8-KfdR{Z<@ zep3B?ao@20+uuF7{lz|U_P>2Qoq6^Ze+qwlP^JGe+&kdJpl!x2q1h+dd&ta@lX8uuJ*`UvP;FU4s9qzL0XYYF? zh*u|^{9E_(+5e>Rf8Kb#{vP|aTLEsa3(~`hxScK=Uw=M4Y60M+l^OU1+N_#TDrUC?X)p((8X@^ZGP``<4Pds@HUsajks z_Wfzj+Q05|cYmw$dl9|=n|t7kQ{_tT=g;2vds5N%;_Ll1@n7$M%h{C9{&KQu$Kt1} z5BHtE%x|5y-bQcbR5|0qH~zciJEC-U9``Kyxc{yDjo<0-!nu@u=4-Q?pZs^Y_5b@{ z?{~i7I66&k*U##iOY3>(XC9dJ`=P(U1Gbo(<|kv{-#bU`EqZ9Y3_mh$`^Xm?pukc z>Gw+L|G9QNu;k^|`3!7$>mySnSVd@qcf`{ z^-~z?mhE`pF0(vU!df}kuJ2&-?C<|&OLRWU6~*{fhp@F;rIyru|FKz&!^Zoa*y9i1 zO*{T@wk5B9&@Z`F|Fny(R^IDz z>V7q+^#7HSG3x31qAOz5_m*6YK3zCzjUlz-M_ZhsQH zd%xC|^W`~fe&xIC^40OL+-FntIQsrn?iVVjr+)YzIpwVH%zX_p{(tsf&MZ4|^6~ll z`=Wn8efV!F9C~Scd(Kjxo$t>kw@vsP{_}p#jWvJl7asn(_foLS^}qj3muD;Q$ls!A zx2*bv?Ll*y^NKZ-y1sMA*nU2@{_&N)PVBBrZ5LIAeq8W)*5!Xrw+i3h+5F-9Pnp=` z>z^l05&EaK|K0bCwjQz%^9}!}YjnS!^!|0<k6T-)Vp1-(UTk_TQ&4mG9y*efOcSUR)-l;iPgIbH-)tHZPu~#hMt*JYcbPwbJkV zyY~yG{+n0cqno*6yNvnHtWbla>$?@cBy>>OD|qg{jh?GYnLsSoqf9m!^CGD<$Y`uv!Eq?#|y8;>2Yr+{q|lF7}BKMe=1QiO6O<(o%^~g z!oKYO^mV_?Yl(mM!LxV#dw*AbeZ-Z#f17hsentDeTw3}_{x?gi2>+X%Ym{}Eo_+n- z*?2YoUmcT9yw^`{f6b*Sw@y2~xUZ$Lv|_=`r|Z6oGVHB<@YtVkOX25uHHYt7bN*Zj z=2B-W>p$dqHFTYB%B{bPnJ(!6@!$WM=jh}~+4ue&W_fiFl1+25|qtlLj3*8QGf`g+HKJKIxj-*}1pymm^qJ>w#N z>UTuJosR_x|Nj`PL|?VPxnkR+FFP()?0Ic?{OLbC)^Df3yB7x?Pfn~nb7jw$OO-3< zPucO8`FsC7!@}SG>lhYlT@JSUlNWkc>hFrj9V>if534iGFS1!$x;=TvqQHoYH~ydA zUvstl*O8tF{p&>*tUb!Vv?RJiZuvZ}yCRAAC+xp9YgxIf#{Uz4^QO)FlQ(U}pS+y? zvoAJQ~FE#+CA#7TUKj5$=|41Gyl=e*yG!| zR~(2p6HOyP?^;_aeq z-f7-^bieGsMR(l(z1q1{0{qTz9g=fEq_P6$} zdvN`3t$NG3hVJES=alLkdD*%Aw@mrN_Z`A7&Rw?rxn{ZZ+>^HJ)wk$z{F6N%ylIvm zU+W zD@qCu69ncOfB${Y^1XTfn~zFYCa(For~JL;^Lf?p%EFRwKjHQKbJqRLE%6_16|&ER ztB+e%q?}j#)uP=LF1bu_+EUqRcaJp7y!rj$W{%STX)V@kBJJNC+}-*(pl#ONJ@;Qp z{_y)0WE0_Y15d{bFU<7NjDtS2{_h!j$ini z!Eu?y-Vgbjyo~1?R4PvGS-3(Zb$4fhseJPXpX%O5w}T-|zA*ebsb8DhvhvOPRqFyd zOw0w+tbf)D+I0JxCYB0K6ImvEfUr+T|LnGXFm2+a-X)*? zF0Jgjx2yiP%ooRLF2{B6Ix7E`b*`ErqZq$c{MFm7GI^CGYOmx|-NzkO9lxZc9A zjp2g(wN#dRm>*yL#++SHd(xESt8?_OC$5R$bo#V(RnV!TMLSeeeK%X~vE8xu+Wfhe zmGTFrc6!O|>B(q~GqlxQxQ73$S;e6fyDZx(1do1I_??-(b#1Dq4P%^i-8t||IX^XA!f{FZ#P9Hz~a_} z@VVP0?RM=@Y=6$PtVq0k$?At2|0u_Fo=OdqKJkLXc`xs(gXXhYo)l=r?Nx2hmYBC$ z`C4Is%;rz$wh6`aPO;x(vo81Zyl0ngiC<@#6(q-ZK7YyWBHsmTwZE9h?O|J$Z@hi~ zsbj_qd`-naZ_x}`Y3)9_Y)_Yg{dT3fd^2WlzkJywf1{;#|6(ThNjz)omiH?MmLAKi zFnbWzx5Uo8%UIykPvd=mQkFdTpLHiy=8)Gq|7t6Z%+k^sJ#rO-imH+3M&CEbKbL&t zo_%iOKdVdY7i}n=!xXD)RrE!BrAw%Q+Rc}5pLcDRIQ+@d&YxB7oY$f+ySCmxrM;y_ z?(n5~srA)$9Vx8_5r3a=Q+xD6?0oT+<+BfZRh&9C@lJsGq2Tk`D_B=-S;BwyvdfE) z@AS&J&n2imo^dWgaNgMoTc!CA8V5=|IQ!ug!>N?Zv!pkz(lRS5+y22JOl|vR-k&^e zX4iFYZz~IX{wc=$#LkIpxaMsR-+55kJw)0iF72?TpGen-XZAm9(=V9W|GAtmIInqo zYTCBTKUR5c{$#2do@;zO{PaiOr^lAhNjUu6#xAnx-0UMq7H%ujS~}r2m;diL-tu@E z9_QF23;o1gAMWvteEm~8RqLBs{WI;E^FuI>&s%%0nxc=j`BJ#~d`xqw=!0(Bh>#elLAoR%B)YuQa?94{=0hA#?P{^C3E_cj(VNCe)ogc z(_>7jd2&nlXbO4Ud=<8E%c5`Bie(lZd^y|l<+IzzU%qd8&9(jWtd%+@Tjx04eY)u1 zyPOZ6?N$8mo2)zht$tfogc%CYv|iBjFWu00sm4^6%Rjc7FUW6FpD^iWuI2en+~*6U zs!mU+w=&y$aPqt3k2br#f9J40*FUJ$>-^O+xr}$sf7YGvd6xR&P57dlE*aqqc8VtZ znC;y5W8T{n7ytav`R&8=@?m5_n4-n8roI=P(b{%_ws4&Rk$Hk~_ur|h#{ zpq;_0KYz@M*G)96v?pdLdTm~(+Z5fj#CdD5lVM$Q0I%uxB=<>695mc7y^K&k z{Y4>h)wPhC<-CWA*_q=$FV=kU_NmBy&id0`a z*Rk}{v97R$#gFwW_By>@9;IHIZXR2;OWHGT(#77mQ`Q@Lb6-7Jx}j=k)%&*T9BUhv z|CqnO^suv$E$`y#SF#m(zqQCQX2eP)UzvKjkbh6giY0sg@x0tHb<>2T#X&-g7SB{) z_j22{g5&SgZ&yEPE1Y`5PFuHovcUn(%=UE0(5;TUcWVpXu1lHkXBM@7$Mue#YlJ#Y zn?Lldep|4gKlIis$4l+`TyAq6*G*bn;Ja_r&U3A&AMhU7yq+su-bQ)%!%eHdqXdI@#Z^_*AlCIC&%dr`cLo3wcbvWemJ$d?d1EX9#LwMTS78(7>^d3?y;J3 zFxt^hp4a4AVm#A#%g#1O-Me)aqH3Y)!D+vp__i56+qTiSCD!qV89Ud4;HBw5nNIH5 zF>i}(tB&udT#eSdOy^eya(_Snq9I)Pwc5F}Z^RQz308C=-bY152w!e+i>jVn@dy9T>M_r6 z3f~bRx@p>lx}KsE%RRH^Y^`U0`hu%ss)y~@wg1B|6>8r3?)!c}>%#d#kJl;Q-*#ZL z^bOx7;`2Hq#cx<^TCZBLur|hE+o9n9C#J9EuKOdW$h%5PKYq5}j>!k2KGjM;EtkEm z+ZwLBwf1yg;@=Nl&wD?ds-OE|$qLp-p=D3bEikt5k@)5F=laet&hJkvbrYBDz4$e< zU2(qVd4v60CxWDJOVplzuqtcu`FpokEy>=rXOc;blt!BT$cm`b z=t}IG&C&KGz~Y6_26pQ?1yY$^_G10=kw2g`0s|A@b{v;c*gKs{X+9D zD%cOmzKs~u7g?rAqgy3a5RyY0@`woz)$ zl)RcXFW&1ubq?;^$8?)(+S`fOSt1U5Ja5+Wyy36^$8hal$1NwtuRcqxow(m~S<=<@ ztc>mR^e>(d3T2$sd1vzjZg<}2Im%n^O((1Mh9Veh4q+o^tM* zwrg(7et92Fa~GMD8hge(1YxPBm)?^APFXmg0*jq8u1{;hjD!>p+8O}cpHe#Unx zW?~<%EZP#a_uXvM3EYkE9kYM#=8Kb!2#t;I@V>fW^@F+#X8UiSwEtP&UB7EKFXPd_ zQO^^c8*;PjIDeWk*>B&fe>*n8{Xk&k)J6jsDN@ zuR<(0qj`RwnlAOU?LlagNU-9(6;)G3ycbV-Kha_Ty!W*=+f57RWZjpj(7asuNwxg! zggYK7VeeYrhnt<->$pyu`5fz%t2X(wi!7oo{4)Krc1-uYC&lD1z7g3$A!%zT3@|(>zf$aryaeE$^eAHLN#Zpe1J(q?f(md*8RWQwq|L z^T@6|zwE=@2idk;3+#ee&u(1wc=>tfTZ^@rd&M2EPP?2gZK1w6Ak_DMwaNWYbJjG3 zhexv8e_y*^%T~+tlvUmKJiZ?f{^&l5S~17@e?TbP^^72EiM;52Pxz)PJrE6D$$7aazpuZ_)i~EuMUzcDa#ibQZH4#E?^Qcir*S8m2p%h} zU}^Koar=JuO*M1k*0!nXwZ~6=x$65{?Auinbp_xZr7hH zyy1Jm=CLspHIKza7%GdLGSn3%j)f!zcbk!+BP>aRC*or zPP_9(cJG?5FS(|BC*`^c8|%H6-n`ZK|JeL2<=^Ac|4YJNkGuAb%XSS9?(1QD-(S11 zZ{}N5e$kzFyVhk!;`sw@4A(*8ny@8y>j}WvdQOfF8@xBI_r3b_1CLp>aUihc=?2&qX`*5Cv$*I5i1^0hj zVz(phhsDck5igl1uajH;h6Hcz{}i$>{$;MqtkB!SOJDBk-&4)fRp9e^W6S5{&p`+6 zF6rGlU%J}k0l!e#o8A3;`fGd5^B>-+WB6AU?KM-_$MxCG@BEwN_x(+k$rj%qzWe*< za?{&y)%U!+ux{$xTPLcwzHXP^ckj@*_m3rVWy<1CY}zpC?QZAaVcVwLuX2skd{t<$ zC0||k*yitT->MjI)aWEs{|%e+Cfd^fee3=Z-C1k;vzOa%F}ImL@!Zx3|LqHX>Low? zQaGC$b-U_r-+{x-Jbhc-e^30T)V#Utk93=RtvB=X>9dOV?K9g`wvNLJ#_qkA z5!o8|PbNJndoBG?zfbLlo#y0SDT}6lsJ^*f|G>0u)r?P;PS4mSxAfLDkyl|-A9uE7 z*Mv&WO0|6Pe9s=PE^e0D8ZbjaJ zNyls7TxLIian*_Qd8b}`?O49|9NX@P?=?TEu5Ilu-Vkzo{oL#uDVdh3sg(_>zwhUL zx}~EQY-w?+q%?q!XM5JanP$PyJNCAe9aL`GEHj7cj_}&vO)E1lr)um?df#$Ac8-dE zmFDS{FXAS#H-|3B;Zt-g{XMr#-9{o=iGB6d`LE|)7fdN)SYpjmN$F1AQ zq5aAE$K09!M^<%*FJoSQVa&Pysf~FDL+8dc zZ{}VnDO7*ptJ*Hp>&#P{kG5Q&yg2%6VyO7-2XFa4>2S|^<(<9mY5&%S`8Q?rC2hD5 zu4+hDyuEe$!G(qqzh5;ReinZ-+NWFVCg=UFx7!x1exWRL zF#q($bytpSPP%`&n%n=$*0r;Q8C&yTIJ_&g=^*wfu} z|J;}wt?&Kd-s`IYEemy&m+qKV^!W1Zdv)0|Qt{Dz(`ziHE1&P0`M>{z^r-{knjI3$ z@A?0_wP1JOV~NJ)xA$>o_OIr$cvWoi>ft=cs~>jz=>PE*DLf&R@?E6WU3+f6V!LbzQ6X)FzttO>Q>##{Mkj1SGNW|sLrbS9TQnl(#Bb? zY_p_#f=&O|ve}CH^RMLDKMY;?r7zaEQq11^VPbyol%p2cm?p8h?c#c>F5&ozp>u~; zak5C>$@T(M))=njfF0%aH|B>G?oswAZ|dOQSr~diSClI<_}b*6ieHDKCY#TF_>!;2 zC`#Sp`s#<}2@I~;tp!_IVs1096Z!o%toCkyi?>|8+U47VUw3On#VGYXvn+U_XD3x* z(DdZ*dYyFVDfv$?WVGL39K{r~w*KJNTdTO{bC{c8@zitM}P->tdZpRv$?!*P#z zw_R368@5k;CspxG>!6&=CadoUH?KBLUi0E_=HvN11z-JMb9~ad*;Vj1Z&|s!vQ6EA zlq%(iegb~hfA(IJd~og1UKx{iuX&aX_1$K{W{a0?*C$*nV#NJ%-yAxxZ%4&AHCD|GdZf<;9B) z5A=Llr*ayHD#_yZfx8C2MRaBv3yqalV z1Vh+&UFmSQn=hZ(gilgGHoNsryl{bL#!k7VyVzdN6IY(=daK*&;jaUU3tr4MHDkVQ zd-;jtwBwPC+kahOc;&I}qbqma${i~AhD;8>wOqXDuYP;p zE>y-J^M8%bbOpXtp56_sA4;p31zz^#EWRV%x3^&Ky}G)64FyYnn^~Nay6I9W-LzaO zxPJA2QwKYSeaCa!_J>X}4Y8VKZK3zNo*^?nQuddol9Q^di0*{<>3ib5tIQ8;cioEV zIduR3`lol^B-qdXU%IpI_Cu>b)|&Fy>N<+I^%?E5`(pfW*7Av89+$V)U22?h=GMGj zhCS;c+1~r_1*sUzVY_E6nEL=|BU~(RR**8$zHmdwO_wwer0~b{k2neGVbUt z>B&CxW1HF=#p;{)pItC&xF)pyRq`qBwaYE0FD>Y)m$XrS()?!g+}De()`;I^2+)QZyUeK@O|{Qb3Uejv+8a2=DG3Se-2yz z*tOdA$G7e8R4ygISQq1Lr{7Y2cGlX@T08FhGd~TxShi}P(B_wXhrdtkaDVR+&3@^; z>yOeH{>838GJCHbRE~R9Y;o(st`%$*O%qHlWz3b{Ui4mgCZVqUO-uSm(VU+=B?sSc z^qRd{kNx&Li*4+(PG6MDXTIgxwA13kUC9J*_nmTaFODbZKmJjkw8rAxv4+J~4>GJ+ z`%bW^({aGzF6*B3m2Q+tgf79EmuB^BC;<*EdW0dE5R4=OWZu|NF7lVJr7rRMw{nDf4 z`inC26y<(;uW50AaZSPX){CC*9gEJ{h3z=?c#>bCv`w_9*Gg-*xl7hhmGWp0tjY%xj+hG9;wy zQn|+d=r8LpL~jz#yMKqlHMoLhb-^2#u1P|CcPCG^3jdz@vdEhGt@NogtvQ-y_D26* zZn`{mR*}=nsuun}jqS(nHO3#*^!GRAe_tN`z|MJ7Zg%|Cw+YJESKpfv+o-;azvKNT z`99^F{5;#TIdzB9hec7AO*|IIfx+~wg90_FO@oO<@YK=>g?c(P#M>(Z(1jz`~F zJ@|FEY~A(N4?2Cj-IJHs)e+0D-N<9n%Yx#pmeL2Vx-`UY3H`|6_pzPd9$^+$o%KxS4G#>*{@lD&Y|2m zm8A`_zq7di+|!K_WxVci;NiC1=kdJf7dNLC+^l)q^!v;0eCx|$hm=NGcG5_Q`BbylUV~R4LtOKWHr5{lI zz2xs+j(PLEZZdcks{D{@zPIBO_vE@W*LHo`y7E)|XYHkXFa5q>+E*of`&W!}%~ZyC zRhwBp|H7;$U!VBzsijTxlj^G9JJ#*BY=0b8ab$0S$PPad&b@VQr?#XwWM@`R*t)Ob z{Iw!tSZ>6^;8 zi#+_jcbE7+|2q-|FXqTL-Hu)RKJQUP5tmF=chuyyl5dg*WOjT-nQoG*E6Mt2+eci?7Otrw}Ow=#`nq5Nd|J( z=f6)nF6iI*ft^Y2r&osUsq`BuZ>JXQo_}xer^DuzhIYP__FdeNH~ZnY4yHxEXBO8; zKQN1#tefgT!FK259X-#SQwucy|99G9r~1=SRpyeYNXO}v*HJt6tvT>fN4f9yO}$4K zqVomsEK~c@Refn@#^+kzOZUX?Z)>X8yq>a~{^0r-*XS&}V$Kiwf1Tg+D{aDLw=z{5hq`NPZ$AI`+srqubK~{? zr9bL6H9toz=ghB}d4FP8w@K73 z3rx9x;QO+uxQ~%{@(ccp-LU>!Gk4F+rQ4@E>{#(Lr77B4`dv-kGRAeMYeO1Wy=m3; zPy28EC8*HnF~6^Q?eb!iuYdn)9y7UHaczdyvg0i)UwyYJ&f7D~RloAUc#?*8u1 zjPvb2^Q2i^POou-udRvs9ro`w_TBeRy=nE{mbSkx)A^;$lb;3WiUaz>Lyh;W`t?M* za7ArY+WqNTBL4dxn_GJo`1|metus7(pze5n*qNqSW8UA-RzCY4)1=)Ke>qv||HG2Y z(e@v^g*1Zy|D0O$_rb~6<^N*&oLOIO7GL+P^4sY{=S+?nd+9$+6W< zd$aWf?K>|Yn&(>n_RN)syX_BN4ZZwlhF$ORh^Z#cTTp))hT=rWWBXX{{QJ^ zD_2U-JE&T_Ldt$-Mc}FX!s_Y1*H_C|+}8OdwP}6*C*_;x=hyRue_y)ljPaABZjt9T zPgI}1>$^F@ELC4m-{;}t=*ttNFF#jxHWQpzeL}wO^BP6*P4oX8KezJD4_VJORUFTD z-v3+uQY3e#^t&U)`}e1c-8uH=a(#{E?_2)&`s`o3T|E--xB1g58*TfKhgmjfnVktb z6#sYQ;mtEH&f2@54fr z>e1e8t3BtLM?e3u(!u}RN!P_UpZ|Xp_GWgy?zfZ6!|Uo_ZQh=*+jwRA-`8ue1~<>X z#AmQ^DbL-TRdbTqEk4bT6uv+2Z~6b_%Qlt%F1%AVb$#K|X#wAF{Mb2n@A;Zn>6<^4 zrN!4@wfDPfTVcR?=kv?YmnAi*{3VQ_s`9%J^%K3Y}CA}KhtksmaRXad+f(c&O6rg?3c40 z_doyRq_z9)&l_fcxBKu|J~YAL_ws)G$#Z|a-1|IMVSd${cW;F4tC@Z;_uQ_Vnij38 z80)^~eRp;HJ*z-1MOP>BwD>*$pT1e2|L4rjyZUN&|0c?xowV2KTWfr+cJ;3d)6WS# aTYm3risX#=n&jkf8}lFjXQ