From 625fbdb2d7faac3a0fc3580d933db20f2c1c96bb Mon Sep 17 00:00:00 2001 From: Anuken Date: Sun, 16 Jun 2019 18:50:51 -0400 Subject: [PATCH] Auto-rebuilding enemy drones / GC improvements --- core/assets/maps/saltFlats.msav | Bin 16132 -> 16490 bytes core/src/io/anuke/mindustry/content/Fx.java | 6 +- core/src/io/anuke/mindustry/core/Logic.java | 2 +- .../io/anuke/mindustry/core/NetClient.java | 4 +- .../io/anuke/mindustry/core/NetServer.java | 4 +- .../io/anuke/mindustry/entities/Entities.java | 1 - .../anuke/mindustry/entities/EntityGroup.java | 6 +- .../entities/traits/BuilderMinerTrait.java | 2 +- .../entities/traits/BuilderTrait.java | 40 +++++++------- .../mindustry/entities/type/BaseUnit.java | 6 +- .../mindustry/entities/type/FlyingUnit.java | 8 +-- .../mindustry/entities/type/GroundUnit.java | 4 +- .../anuke/mindustry/entities/type/Player.java | 6 +- .../entities/type/base/BaseDrone.java | 4 +- .../entities/type/base/BuilderDrone.java | 52 ++++++++++++++---- .../entities/type/base/MinerDrone.java | 4 +- .../entities/type/base/RepairDrone.java | 4 +- core/src/io/anuke/mindustry/game/Teams.java | 10 +++- .../anuke/mindustry/input/DesktopInput.java | 4 +- core/src/io/anuke/mindustry/io/JsonIO.java | 25 ++++++++- .../io/anuke/mindustry/io/SaveVersion.java | 36 ++++++++++-- .../ui/dialogs/CustomRulesDialog.java | 1 + .../ui/dialogs/SettingsMenuDialog.java | 2 +- 23 files changed, 159 insertions(+), 72 deletions(-) diff --git a/core/assets/maps/saltFlats.msav b/core/assets/maps/saltFlats.msav index c982e27d4ea64eac40302b494241d90799a33b4d..a0688e922d2f03940c0486b906d134e5b9542587 100644 GIT binary patch literal 16490 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 literal 16132 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 Wqkgk6n { - Fill.circle(e.x + x, e.y + y, e.fout() * 1.5f); + Fill.circle(ex + x, ey + y, fout * 1.5f); }); } @@ -816,8 +817,9 @@ public class Fx implements ContentList{ Draw.color(Color.LIGHT_GRAY); for(int i : Mathf.signs){ + float ex = e.x, ey = e.y, fout = e.fout(); Angles.randLenVectors(e.id, 4, -e.finpow() * 15f, e.rotation + 90f * i, 25f, (x, y) -> { - Fill.circle(e.x + x, e.y + y, e.fout() * 2f); + Fill.circle(ex + x, ey + y, fout * 2f); }); } diff --git a/core/src/io/anuke/mindustry/core/Logic.java b/core/src/io/anuke/mindustry/core/Logic.java index 3d5901dc97..c6eddd548f 100644 --- a/core/src/io/anuke/mindustry/core/Logic.java +++ b/core/src/io/anuke/mindustry/core/Logic.java @@ -61,7 +61,7 @@ public class Logic implements ApplicationListener{ } TeamData data = state.teams.get(tile.getTeam()); - data.brokenBlocks.addFirst(BrokenBlock.get(tile.x, tile.y, block.id, tile.rotation())); + data.brokenBlocks.addFirst(BrokenBlock.get(tile.x, tile.y, tile.rotation(), block.id)); }); } diff --git a/core/src/io/anuke/mindustry/core/NetClient.java b/core/src/io/anuke/mindustry/core/NetClient.java index 658afabd2c..dab0c9b31b 100644 --- a/core/src/io/anuke/mindustry/core/NetClient.java +++ b/core/src/io/anuke/mindustry/core/NetClient.java @@ -360,11 +360,11 @@ public class NetClient implements ApplicationListener{ if(timer.get(0, playerSyncTime)){ BuildRequest[] requests; //limit to 10 to prevent buffer overflows - int usedRequests = Math.min(player.getPlaceQueue().size, 10); + int usedRequests = Math.min(player.buildQueue().size, 10); requests = new BuildRequest[usedRequests]; for(int i = 0; i < usedRequests; i++){ - requests[i] = player.getPlaceQueue().get(i); + requests[i] = player.buildQueue().get(i); } Call.onClientShapshot(lastSent++, player.x, player.y, diff --git a/core/src/io/anuke/mindustry/core/NetServer.java b/core/src/io/anuke/mindustry/core/NetServer.java index 7da3a47a24..ab4eca7e0d 100644 --- a/core/src/io/anuke/mindustry/core/NetServer.java +++ b/core/src/io/anuke/mindustry/core/NetServer.java @@ -282,7 +282,7 @@ public class NetServer implements ApplicationListener{ player.isTyping = chatting; player.isBoosting = boosting; player.isShooting = shooting; - player.getPlaceQueue().clear(); + player.buildQueue().clear(); for(BuildRequest req : requests){ Tile tile = world.tile(req.x, req.y); if(tile == null) continue; @@ -292,7 +292,7 @@ public class NetServer implements ApplicationListener{ }else if(!req.breaking && tile.block() == req.block && (!req.block.rotate || tile.rotation() == req.rotation)){ continue; } - player.getPlaceQueue().addLast(req); + player.buildQueue().addLast(req); } vector.set(x - player.getInterpolator().target.x, y - player.getInterpolator().target.y); diff --git a/core/src/io/anuke/mindustry/entities/Entities.java b/core/src/io/anuke/mindustry/entities/Entities.java index d0d5d16dea..2a6f388ff5 100755 --- a/core/src/io/anuke/mindustry/entities/Entities.java +++ b/core/src/io/anuke/mindustry/entities/Entities.java @@ -13,7 +13,6 @@ import io.anuke.mindustry.entities.traits.Entity; import static io.anuke.mindustry.Vars.collisions; public class Entities{ - public static final int maxLeafObjects = 4; private static final Array> groupArray = new Array<>(); private static final IntMap> groups = new IntMap<>(); private static final Rectangle viewport = new Rectangle(); diff --git a/core/src/io/anuke/mindustry/entities/EntityGroup.java b/core/src/io/anuke/mindustry/entities/EntityGroup.java index 314aa63d76..d3010c84f3 100644 --- a/core/src/io/anuke/mindustry/entities/EntityGroup.java +++ b/core/src/io/anuke/mindustry/entities/EntityGroup.java @@ -17,7 +17,7 @@ public class EntityGroup{ private final Array entitiesToRemove = new Array<>(false, 16); private final Array entitiesToAdd = new Array<>(false, 16); private IntMap map; - private QuadTree tree; + private QuadTree tree; private Consumer removeListener; private Consumer addListener; @@ -27,7 +27,7 @@ public class EntityGroup{ this.type = type; if(useTree){ - tree = new QuadTree<>(Entities.maxLeafObjects, new Rectangle(0, 0, 0, 0)); + tree = new QuadTree<>(new Rectangle(0, 0, 0, 0)); } } @@ -124,7 +124,7 @@ public class EntityGroup{ /** Resizes the internal quadtree, if it is enabled.*/ public void resize(float x, float y, float w, float h){ if(useTree){ - tree = new QuadTree<>(Entities.maxLeafObjects, new Rectangle(x, y, w, h)); + tree = new QuadTree<>(new Rectangle(x, y, w, h)); } } diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java index 253e5c41d0..5fbde29251 100644 --- a/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderMinerTrait.java @@ -7,7 +7,7 @@ public interface BuilderMinerTrait extends MinerTrait, BuilderTrait{ updateBuilding(); //mine only when not building - if(getCurrentRequest() == null){ + if(buildRequest() == null){ updateMining(); } } diff --git a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java index 45cb67da96..27361d61d5 100644 --- a/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java +++ b/core/src/io/anuke/mindustry/entities/traits/BuilderTrait.java @@ -35,21 +35,21 @@ public interface BuilderTrait extends Entity, TeamTrait{ Unit unit = (Unit)this; //remove already completed build requests removal.clear(); - for(BuildRequest req : getPlaceQueue()){ + for(BuildRequest req : buildQueue()){ removal.add(req); } - getPlaceQueue().clear(); + buildQueue().clear(); for(BuildRequest request : removal){ if(!((request.breaking && world.tile(request.x, request.y).block() == Blocks.air) || (!request.breaking && (world.tile(request.x, request.y).rotation() == request.rotation || !request.block.rotate) && world.tile(request.x, request.y).block() == request.block))){ - getPlaceQueue().addLast(request); + buildQueue().addLast(request); } } - BuildRequest current = getCurrentRequest(); + BuildRequest current = buildRequest(); if(current == null){ return; @@ -58,9 +58,9 @@ public interface BuilderTrait extends Entity, TeamTrait{ Tile tile = world.tile(current.x, current.y); if(dst(tile) > finalPlaceDst){ - if(getPlaceQueue().size > 1){ - getPlaceQueue().removeFirst(); - getPlaceQueue().addLast(current); + if(buildQueue().size > 1){ + buildQueue().removeFirst(); + buildQueue().addLast(current); } return; } @@ -71,7 +71,7 @@ public interface BuilderTrait extends Entity, TeamTrait{ }else if(canCreateBlocks() && current.breaking && Build.validBreak(getTeam(), current.x, current.y)){ Call.beginBreak(getTeam(), current.x, current.y); }else{ - getPlaceQueue().removeFirst(); + buildQueue().removeFirst(); return; } } @@ -115,7 +115,7 @@ public interface BuilderTrait extends Entity, TeamTrait{ } /** Returns the queue for storing build requests. */ - Queue getPlaceQueue(); + Queue buildQueue(); /** Build power, can be any float. 1 = builds recipes in normal time, 0 = doesn't build at all. */ float getBuildPower(Tile tile); @@ -126,7 +126,7 @@ public interface BuilderTrait extends Entity, TeamTrait{ } default void writeBuilding(DataOutput output) throws IOException{ - BuildRequest request = getCurrentRequest(); + BuildRequest request = buildRequest(); if(request != null){ output.writeByte(request.breaking ? 1 : 0); @@ -146,7 +146,7 @@ public interface BuilderTrait extends Entity, TeamTrait{ } default void readBuilding(DataInput input, boolean applyChanges) throws IOException{ - if(applyChanges) getPlaceQueue().clear(); + if(applyChanges) buildQueue().clear(); byte type = input.readByte(); if(type != -1){ @@ -165,26 +165,26 @@ public interface BuilderTrait extends Entity, TeamTrait{ request.progress = progress; if(applyChanges){ - getPlaceQueue().addLast(request); + buildQueue().addLast(request); }else if(isBuilding()){ - getCurrentRequest().progress = progress; + buildRequest().progress = progress; } } } /** Return whether this builder's place queue contains items. */ default boolean isBuilding(){ - return getPlaceQueue().size != 0; + return buildQueue().size != 0; } /** Clears the placement queue. */ default void clearBuilding(){ - getPlaceQueue().clear(); + buildQueue().clear(); } /** Add another build requests to the tail of the queue, if it doesn't exist there yet. */ default void addBuildRequest(BuildRequest place){ - for(BuildRequest request : getPlaceQueue()){ + for(BuildRequest request : buildQueue()){ if(request.x == place.x && request.y == place.y){ return; } @@ -193,15 +193,15 @@ public interface BuilderTrait extends Entity, TeamTrait{ if(tile != null && tile.entity instanceof BuildEntity){ place.progress = tile.entity().progress; } - getPlaceQueue().addLast(place); + buildQueue().addLast(place); } /** * Return the build requests currently active, or the one at the top of the queue. * May return null. */ - default BuildRequest getCurrentRequest(){ - return getPlaceQueue().size == 0 ? null : getPlaceQueue().first(); + default BuildRequest buildRequest(){ + return buildQueue().size == 0 ? null : buildQueue().first(); } //due to iOS weirdness, this is apparently required @@ -215,7 +215,7 @@ public interface BuilderTrait extends Entity, TeamTrait{ if(!isBuilding()) return; Unit unit = (Unit)this; - BuildRequest request = getCurrentRequest(); + BuildRequest request = buildRequest(); Tile tile = world.tile(request.x, request.y); if(dst(tile) > placeDistance && !state.isEditor()){ diff --git a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java index 440cab7a2b..de040681cc 100644 --- a/core/src/io/anuke/mindustry/entities/type/BaseUnit.java +++ b/core/src/io/anuke/mindustry/entities/type/BaseUnit.java @@ -112,10 +112,8 @@ public abstract class BaseUnit extends Unit implements ShooterTrait{ this.state.set(state); } - public void retarget(Runnable run){ - if(timer.get(timerTarget, 20)){ - run.run(); - } + public boolean retarget(){ + return timer.get(timerTarget, 20); } /** Only runs when the unit has a target. */ diff --git a/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java b/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java index dc92379241..e0a95b7866 100644 --- a/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java +++ b/core/src/io/anuke/mindustry/entities/type/FlyingUnit.java @@ -32,7 +32,7 @@ public abstract class FlyingUnit extends BaseUnit{ target = null; } - retarget(() -> { + if(retarget()){ targetClosest(); if(target == null) targetClosestEnemyFlag(BlockFlag.producer); @@ -41,7 +41,7 @@ public abstract class FlyingUnit extends BaseUnit{ if(target == null){ setState(patrol); } - }); + }; if(target != null){ attack(type.attackLength); @@ -71,7 +71,7 @@ public abstract class FlyingUnit extends BaseUnit{ }, patrol = new UnitState(){ public void update(){ - retarget(() -> { + if(retarget()){ targetClosest(); targetClosestEnemyFlag(BlockFlag.target); @@ -81,7 +81,7 @@ public abstract class FlyingUnit extends BaseUnit{ } target = getClosestCore(); - }); + }; if(target != null){ circle(60f + Mathf.absin(Time.time() + Mathf.randomSeed(id) * 1200f, 70f, 1200f)); diff --git a/core/src/io/anuke/mindustry/entities/type/GroundUnit.java b/core/src/io/anuke/mindustry/entities/type/GroundUnit.java index 0239f9bdf2..e319063e9d 100644 --- a/core/src/io/anuke/mindustry/entities/type/GroundUnit.java +++ b/core/src/io/anuke/mindustry/entities/type/GroundUnit.java @@ -176,7 +176,9 @@ public abstract class GroundUnit extends BaseUnit{ target = null; } - retarget(this::targetClosest); + if(retarget()){ + targetClosest(); + } } protected void patrol(){ diff --git a/core/src/io/anuke/mindustry/entities/type/Player.java b/core/src/io/anuke/mindustry/entities/type/Player.java index 9389b2d95e..3f1cf60d6d 100644 --- a/core/src/io/anuke/mindustry/entities/type/Player.java +++ b/core/src/io/anuke/mindustry/entities/type/Player.java @@ -241,7 +241,7 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ } @Override - public Queue getPlaceQueue(){ + public Queue buildQueue(){ return placeQueue; } @@ -428,8 +428,8 @@ public class Player extends Unit implements BuilderMinerTrait, ShooterTrait{ /** Draw all current build requests. Does not draw the beam effect, only the positions. */ public void drawBuildRequests(){ BuildRequest last = null; - for(BuildRequest request : getPlaceQueue()){ - if(request.progress > 0.01f || (getCurrentRequest() == request && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue; + for(BuildRequest request : buildQueue()){ + if(request.progress > 0.01f || (buildRequest() == request && (dst(request.x * tilesize, request.y * tilesize) <= placeDistance || state.isEditor()))) continue; if(request.breaking){ Block block = world.ltile(request.x, request.y).block(); diff --git a/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java b/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java index f0bba823af..27ac0c86ff 100644 --- a/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java +++ b/core/src/io/anuke/mindustry/entities/type/base/BaseDrone.java @@ -19,14 +19,14 @@ public abstract class BaseDrone extends FlyingUnit{ if(health >= maxHealth()){ state.set(attack); }else if(!targetHasFlag(BlockFlag.repair)){ - retarget(() -> { + if(retarget()){ Tile repairPoint = Geometry.findClosest(x, y, world.indexer.getAllied(team, BlockFlag.repair)); if(repairPoint != null){ target = repairPoint; }else{ setState(getStartState()); } - }); + } }else{ circle(40f); } diff --git a/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java b/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java index 2477128a88..4b75d6e734 100644 --- a/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java +++ b/core/src/io/anuke/mindustry/entities/type/base/BuilderDrone.java @@ -6,12 +6,15 @@ import io.anuke.arc.collection.IntIntMap; import io.anuke.arc.collection.Queue; import io.anuke.arc.math.Mathf; import io.anuke.arc.util.*; +import io.anuke.mindustry.Vars; import io.anuke.mindustry.entities.EntityGroup; import io.anuke.mindustry.entities.traits.BuilderTrait; import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.entities.type.*; import io.anuke.mindustry.entities.units.UnitState; import io.anuke.mindustry.game.EventType.BuildSelectEvent; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.gen.BrokenBlock; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.BuildBlock; import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; @@ -20,7 +23,6 @@ import java.io.*; import static io.anuke.mindustry.Vars.*; -//TODO follow players public class BuilderDrone extends BaseDrone implements BuilderTrait{ private static final StaticReset reset = new StaticReset(); private static final IntIntMap totals = new IntIntMap(); @@ -43,12 +45,22 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ BuildEntity entity = (BuildEntity)target; TileEntity core = getClosestCore(); - if(entity != null && core != null && (entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid + if(isBuilding() && entity == null && isRebuild()){ + target = world.tile(buildRequest().x, buildRequest().y); + circle(placeDistance * 0.7f); + target = null; + + BuildRequest request = buildRequest(); + + if(world.tile(request.x, request.y).entity instanceof BuildEntity){ + target = world.tile(request.x, request.y).entity; + } + }else if(entity != null && core != null && (entity.progress < 1f || entity.progress > 0f) && entity.tile.block() instanceof BuildBlock){ //building is valid if(!isBuilding() && dst(target) < placeDistance * 0.9f){ //within distance, begin placing if(isBreaking){ - getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y)); + buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y)); }else{ - getPlaceQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock)); + buildQueue().addLast(new BuildRequest(entity.tile.x, entity.tile.y, entity.tile.rotation(), entity.cblock)); } } @@ -58,7 +70,7 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ if(playerTarget == null || playerTarget.getTeam() != team || !playerTarget.isValid()){ playerTarget = null; - retarget(() -> { + if(retarget()){ float minDst = Float.POSITIVE_INFINITY; int minDrones = Integer.MAX_VALUE; @@ -75,7 +87,13 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ } } } - }); + } + + if(getSpawner() != null){ + target = getSpawner(); + circle(40f); + target = null; + } }else{ incDrones(playerTarget); TargetTrait prev = target; @@ -103,7 +121,7 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ BuilderDrone drone = (BuilderDrone)unit; if(drone.isBuilding()){ //stop building if opposite building begins. - BuildRequest req = drone.getCurrentRequest(); + BuildRequest req = drone.buildRequest(); if(req.breaking != event.breaking && req.x == event.tile.x && req.y == event.tile.y){ drone.clearBuilding(); drone.target = null; @@ -131,13 +149,17 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ } } + boolean isRebuild(){ + return Vars.state.rules.enemyCheat && team == waveTeam; + } + @Override public float getBuildPower(Tile tile){ return type.buildPower; } @Override - public Queue getPlaceQueue(){ + public Queue buildQueue(){ return placeQueue; } @@ -147,8 +169,8 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ if(!isBuilding() && timer.get(timerTarget2, 15)){ for(Player player : playerGroup.all()){ - if(player.getTeam() == team && player.getCurrentRequest() != null){ - BuildRequest req = player.getCurrentRequest(); + if(player.getTeam() == team && player.buildRequest() != null){ + BuildRequest req = player.buildRequest(); Tile tile = world.tile(req.x, req.y); if(tile != null && tile.entity instanceof BuildEntity){ BuildEntity b = tile.entity(); @@ -162,6 +184,16 @@ public class BuilderDrone extends BaseDrone implements BuilderTrait{ } } } + + if(isRebuild()){ + TeamData data = Vars.state.teams.get(team); + if(!data.brokenBlocks.isEmpty()){ + long block = data.brokenBlocks.removeLast(); + + placeQueue.addFirst(new BuildRequest(BrokenBlock.x(block), BrokenBlock.y(block), BrokenBlock.rotation(block), content.block(BrokenBlock.block(block)))); + setState(build); + } + } } updateBuilding(); diff --git a/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java b/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java index 605bebb148..9cc5f4f31c 100644 --- a/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java +++ b/core/src/io/anuke/mindustry/entities/type/base/MinerDrone.java @@ -52,13 +52,13 @@ public class MinerDrone extends BaseDrone implements MinerTrait{ return; } - retarget(() -> { + if(retarget()){ findItem(); if(targetItem == null) return; target = world.indexer.findClosestOre(x, y, targetItem); - }); + }; if(target instanceof Tile){ moveTo(type.range / 1.5f); diff --git a/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java b/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java index 9375e6ca8e..e5be4d3d07 100644 --- a/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java +++ b/core/src/io/anuke/mindustry/entities/type/base/RepairDrone.java @@ -19,7 +19,9 @@ public class RepairDrone extends BaseDrone{ public void update(){ - retarget(() -> target = Units.findDamagedTile(team, x, y)); + if(retarget()){ + target = Units.findDamagedTile(team, x, y); + } if(target != null){ if(target.dst(RepairDrone.this) > type.range){ diff --git a/core/src/io/anuke/mindustry/game/Teams.java b/core/src/io/anuke/mindustry/game/Teams.java index 077729ddd0..be4d1a7e2c 100644 --- a/core/src/io/anuke/mindustry/game/Teams.java +++ b/core/src/io/anuke/mindustry/game/Teams.java @@ -42,11 +42,17 @@ public class Teams{ return enemiesOf(team).contains(other); } - public class TeamData{ + /** Allocates a new array with the active teams. + * Never call in the main game loop.*/ + public Array getActive(){ + return Array.select(map, t -> t != null); + } + + public static class TeamData{ public final ObjectSet cores = new ObjectSet<>(); - public final LongQueue brokenBlocks = new LongQueue(); public final EnumSet enemies; public final Team team; + public LongQueue brokenBlocks = new LongQueue(); public TeamData(Team team, EnumSet enemies){ this.team = team; diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 2198f002ef..ebac7e421e 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -214,7 +214,7 @@ public class DesktopInput extends InputHandler{ mode = placing; }else if(selected != null){ //only begin shooting if there's no cursor event - if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.getPlaceQueue().size == 0 && !droppingItem && + if(!tileTapped(selected) && !tryTapPlayer(Core.input.mouseWorld().x, Core.input.mouseWorld().y) && player.buildQueue().size == 0 && !droppingItem && !tryBeginMine(selected) && player.getMineTile() == null && !ui.chatfrag.chatOpen()){ player.isShooting = true; } @@ -222,7 +222,7 @@ public class DesktopInput extends InputHandler{ player.isShooting = true; } }else if(Core.input.keyTap(Binding.deselect) && (block != null || mode != none || player.isBuilding()) && - !(player.getCurrentRequest() != null && player.getCurrentRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){ + !(player.buildRequest() != null && player.buildRequest().breaking && Core.keybinds.get(Binding.deselect) == Core.keybinds.get(Binding.break_block))){ if(block == null){ player.clearBuilding(); } diff --git a/core/src/io/anuke/mindustry/io/JsonIO.java b/core/src/io/anuke/mindustry/io/JsonIO.java index 3c1ce9520c..6cee4ca311 100644 --- a/core/src/io/anuke/mindustry/io/JsonIO.java +++ b/core/src/io/anuke/mindustry/io/JsonIO.java @@ -1,10 +1,12 @@ package io.anuke.mindustry.io; +import io.anuke.arc.collection.EnumSet; +import io.anuke.arc.collection.LongQueue; import io.anuke.arc.util.serialization.Json; import io.anuke.arc.util.serialization.JsonValue; import io.anuke.mindustry.Vars; -import io.anuke.mindustry.game.Rules; -import io.anuke.mindustry.game.SpawnGroup; +import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Teams.TeamData; import io.anuke.mindustry.type.*; @SuppressWarnings("unchecked") @@ -36,6 +38,25 @@ public class JsonIO{ return Vars.content.getByName(ContentType.item, jsonData.asString()); } }); + + setSerializer(TeamData.class, new Serializer(){ + @Override + public void write(Json json, TeamData object, Class knownType){ + json.writeObjectStart(); + json.writeValue("brokenBlocks", object.brokenBlocks.toArray()); + json.writeValue("team", object.team.ordinal()); + json.writeObjectEnd(); + } + + @Override + public TeamData read(Json json, JsonValue jsonData, Class type){ + long[] blocks = jsonData.get("brokenBlocks").asLongArray(); + Team team = Team.all[jsonData.getInt("team", 0)]; + TeamData out = new TeamData(team, EnumSet.of(new Team[]{})); + out.brokenBlocks = new LongQueue(blocks); + return out; + } + }); }}; public static String write(Object object){ diff --git a/core/src/io/anuke/mindustry/io/SaveVersion.java b/core/src/io/anuke/mindustry/io/SaveVersion.java index fee4bf058d..839bed89fe 100644 --- a/core/src/io/anuke/mindustry/io/SaveVersion.java +++ b/core/src/io/anuke/mindustry/io/SaveVersion.java @@ -1,13 +1,14 @@ package io.anuke.mindustry.io; -import io.anuke.arc.collection.Array; -import io.anuke.arc.collection.StringMap; +import io.anuke.arc.collection.*; import io.anuke.arc.util.Time; import io.anuke.arc.util.io.CounterInputStream; import io.anuke.mindustry.entities.Entities; import io.anuke.mindustry.entities.EntityGroup; import io.anuke.mindustry.entities.traits.*; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.gen.BrokenBlock; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.type.ContentType; import io.anuke.mindustry.world.*; @@ -64,6 +65,7 @@ public abstract class SaveVersion extends SaveFileReader{ "wavetime", state.wavetime, "stats", JsonIO.write(state.stats), "rules", JsonIO.write(state.rules), + "teamdata", JsonIO.write(state.teams.getActive().toArray(TeamData.class)), "width", world.width(), "height", world.height() ).merge(tags)); @@ -77,6 +79,13 @@ public abstract class SaveVersion extends SaveFileReader{ state.stats = JsonIO.read(Stats.class, map.get("stats", "{}")); state.rules = JsonIO.read(Rules.class, map.get("rules", "{}")); if(state.rules.spawns.isEmpty()) state.rules.spawns = defaultWaves.get(); + + //only broken blocks are transferred over right now; nothing else + TeamData[] teams = JsonIO.read(TeamData[].class, map.get("teamdata", "[]")); + for(TeamData data : teams){ + state.teams.get(data.team).brokenBlocks = data.brokenBlocks; + } + Map worldmap = world.maps.byName(map.get("mapname", "\\\\\\")); world.setMap(worldmap == null ? new Map(StringMap.of( "name", map.get("mapname", "Unknown"), @@ -92,13 +101,13 @@ public abstract class SaveVersion extends SaveFileReader{ //floor + overlay for(int i = 0; i < world.width() * world.height(); i++){ - Tile tile = world.tile(i % world.width(), i / world.width()); + Tile tile = world.rawTile(i % world.width(), i / world.width()); stream.writeShort(tile.floorID()); stream.writeShort(tile.overlayID()); int consecutives = 0; for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ - Tile nextTile = world.tile(j % world.width(), j / world.width()); + Tile nextTile = world.rawTile(j % world.width(), j / world.width()); if(nextTile.floorID() != tile.floorID() || nextTile.overlayID() != tile.overlayID()){ break; @@ -113,7 +122,7 @@ public abstract class SaveVersion extends SaveFileReader{ //blocks for(int i = 0; i < world.width() * world.height(); i++){ - Tile tile = world.tile(i % world.width(), i / world.width()); + Tile tile = world.rawTile(i % world.width(), i / world.width()); stream.writeShort(tile.blockID()); if(tile.entity != null){ @@ -126,7 +135,7 @@ public abstract class SaveVersion extends SaveFileReader{ int consecutives = 0; for(int j = i + 1; j < world.width() * world.height() && consecutives < 255; j++){ - Tile nextTile = world.tile(j % world.width(), j / world.width()); + Tile nextTile = world.rawTile(j % world.width(), j / world.width()); if(nextTile.blockID() != tile.blockID()){ break; @@ -264,6 +273,8 @@ public abstract class SaveVersion extends SaveFileReader{ } content.setTemporaryMapper(map); + + remapContent(); } public void writeContentHeader(DataOutput stream) throws IOException{ @@ -287,4 +298,17 @@ public abstract class SaveVersion extends SaveFileReader{ } } } + + /** sometimes it's necessary to remap IDs after the content header is read.*/ + public void remapContent(){ + for(Team team : Team.all){ + if(state.teams.isActive(team)){ + LongQueue queue = state.teams.get(team).brokenBlocks; + for(int i = 0; i < queue.size; i++){ + //remap broken block IDs + queue.set(i, BrokenBlock.block(queue.get(i), content.block(BrokenBlock.block(queue.get(i))).id)); + } + } + } + } } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java index 9d7cb84325..588d1410f2 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/CustomRulesDialog.java @@ -69,6 +69,7 @@ public class CustomRulesDialog extends FloatingDialog{ title("$rules.title.enemy"); check("$rules.attack", b -> rules.attackMode = b, () -> rules.attackMode); + check("$rules.enemyCheat", b -> rules.enemyCheat = b, () -> rules.enemyCheat); number("$rules.enemycorebuildradius", f -> rules.enemyCoreBuildRadius = f * tilesize, () -> Math.min(rules.enemyCoreBuildRadius / tilesize, 200)); } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java index ac19e1a480..c47f7524ce 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SettingsMenuDialog.java @@ -123,7 +123,6 @@ public class SettingsMenuDialog extends SettingsDialog{ sound.add("[LIGHT_GRAY]there is no sound implemented in v4 yet"); game.screenshakePref(); - game.checkPref("effects", true); if(mobile){ game.checkPref("autotarget", true); } @@ -212,6 +211,7 @@ public class SettingsMenuDialog extends SettingsDialog{ } } + graphics.checkPref("effects", true); graphics.checkPref("playerchat", true); graphics.checkPref("minimap", !mobile); graphics.checkPref("fps", false);