diff --git a/core/src/io/anuke/mindustry/ai/BlockIndexer.java b/core/src/io/anuke/mindustry/ai/BlockIndexer.java index aa911488ca..76b4a3ac46 100644 --- a/core/src/io/anuke/mindustry/ai/BlockIndexer.java +++ b/core/src/io/anuke/mindustry/ai/BlockIndexer.java @@ -1,10 +1,12 @@ package io.anuke.mindustry.ai; +import com.badlogic.gdx.math.Vector2; import com.badlogic.gdx.utils.Bits; import com.badlogic.gdx.utils.IntMap; import com.badlogic.gdx.utils.ObjectMap; import com.badlogic.gdx.utils.ObjectSet; import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.game.EventType.TileChangeEvent; import io.anuke.mindustry.game.EventType.WorldLoadEvent; import io.anuke.mindustry.game.Team; @@ -13,11 +15,12 @@ import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.meta.BlockFlag; import io.anuke.ucore.core.Events; +import io.anuke.ucore.entities.trait.Entity; +import io.anuke.ucore.function.Predicate; import io.anuke.ucore.util.EnumSet; import io.anuke.ucore.util.Mathf; -import static io.anuke.mindustry.Vars.state; -import static io.anuke.mindustry.Vars.world; +import static io.anuke.mindustry.Vars.*; //TODO consider using quadtrees for finding specific types of blocks within an area //TODO maybe use Arrays instead of ObjectSets? @@ -77,6 +80,12 @@ public class BlockIndexer { } } + for (int x = 0; x < quadWidth(); x++) { + for (int y = 0; y < quadHeight(); y++) { + updateQuadrant(world.tile(x * structQuadrantSize, y * structQuadrantSize)); + } + } + scanOres(); }); } @@ -91,6 +100,36 @@ public class BlockIndexer { return (!state.teams.get(team).ally ? allyMap : enemyMap).get(type, emptyArray); } + public TileEntity findTile(Team team, float x, float y, float range, Predicate pred){ + Entity closest = null; + float dst = 0; + + for(int rx = Math.max((int)((x-range)/tilesize/structQuadrantSize), 0); rx <= (int)((x+range)/tilesize/structQuadrantSize) && rx < quadWidth(); rx ++){ + for(int ry = Math.max((int)((y-range)/tilesize/structQuadrantSize), 0); ry <= (int)((y+range)/tilesize/structQuadrantSize) && ry < quadHeight(); ry ++){ + + if(!getQuad(team, rx, ry)) continue; + + for(int tx = rx * structQuadrantSize; tx < (rx + 1) * structQuadrantSize && tx < world.width(); tx ++){ + for(int ty = ry * structQuadrantSize; ty < (ry + 1) * structQuadrantSize && ty < world.height(); ty ++ ){ + Tile other = world.tile(tx, ty); + + if(other == null || other.entity == null || !pred.test(other)) continue; + + TileEntity e = other.entity; + + float ndst = Vector2.dst(x, y, e.x, e.y); + if(ndst < range && (closest == null || ndst < dst)){ + dst = ndst; + closest = e; + } + } + } + } + } + + return (TileEntity) closest; + } + /**Returns a set of tiles that have ores of the specified type nearby. * While each tile in the set is not guaranteed to have an ore directly on it, * each tile will at least have an ore within {@link #oreQuadrantSize} / 2 blocks of it. @@ -124,7 +163,8 @@ public class BlockIndexer { //this quadrant is now 'dirty', re-scan the whole thing int quadrantX = tile.x / structQuadrantSize; int quadrantY = tile.y / structQuadrantSize; - int index = quadrantX * Mathf.ceil(world.width() / (float)structQuadrantSize) + quadrantY; + int index = quadrantX + quadrantY * quadWidth(); + //Log.info("Updating quadrant: {0} {1}", quadrantX, quadrantY); for(TeamData data : state.teams.getTeams()) { @@ -150,6 +190,19 @@ public class BlockIndexer { } } + private boolean getQuad(Team team, int quadrantX, int quadrantY){ + int index = quadrantX + quadrantY * Mathf.ceil(world.width() / (float)structQuadrantSize); + return structQuadrants[team.ordinal()].get(index); + } + + private int quadWidth(){ + return Mathf.ceil(world.width() / (float)structQuadrantSize); + } + + private int quadHeight(){ + return Mathf.ceil(world.height() / (float)structQuadrantSize); + } + private ObjectMap> getMap(Team team){ if(!state.teams.has(team)) return emptyMap; return state.teams.get(team).ally ? allyMap : enemyMap; diff --git a/core/src/io/anuke/mindustry/ai/WaveSpawner.java b/core/src/io/anuke/mindustry/ai/WaveSpawner.java index e8e39aad34..7252dba755 100644 --- a/core/src/io/anuke/mindustry/ai/WaveSpawner.java +++ b/core/src/io/anuke/mindustry/ai/WaveSpawner.java @@ -105,7 +105,7 @@ public class WaveSpawner { for (int y = quady * quadsize; y < world.height() && y < (quady + 1)*quadsize; y++) { Tile tile = world.tile(x, y); - if(tile.solid() || world.pathfinder().getValueforTeam(Team.red, x, y) == Float.MAX_VALUE){ + if(tile == null || tile.solid() || world.pathfinder().getValueforTeam(Team.red, x, y) == Float.MAX_VALUE){ setQuad(quadx, quady, false); break outer; } diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index 7a893461be..4680cddbd0 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -93,11 +93,11 @@ public class World extends Module{ } public int width(){ - return currentMap.meta.width; + return tiles.length; } public int height(){ - return currentMap.meta.height; + return tiles[0].length; } public Tile tile(int packed){ diff --git a/core/src/io/anuke/mindustry/entities/Player.java b/core/src/io/anuke/mindustry/entities/Player.java index 52bbf7d47d..a85f9daaff 100644 --- a/core/src/io/anuke/mindustry/entities/Player.java +++ b/core/src/io/anuke/mindustry/entities/Player.java @@ -662,7 +662,7 @@ public class Player extends Unit implements BuilderTrait, CarryTrait { if(local){ int index = stream.readByte(); players[index].readSaveSuper(stream); - dead = false; + players[index].dead = false; } } diff --git a/core/src/io/anuke/mindustry/entities/Units.java b/core/src/io/anuke/mindustry/entities/Units.java index d672cab2ff..ad4a81fdc0 100644 --- a/core/src/io/anuke/mindustry/entities/Units.java +++ b/core/src/io/anuke/mindustry/entities/Units.java @@ -10,10 +10,8 @@ import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.ucore.entities.EntityGroup; import io.anuke.ucore.entities.EntityPhysics; -import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.function.Consumer; import io.anuke.ucore.function.Predicate; -import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.*; @@ -91,43 +89,24 @@ public class Units { /**Returns the neareset ally tile in a range.*/ public static TileEntity findAllyTile(Team team, float x, float y, float range, Predicate pred){ - return findTile(x, y, range, tile -> !state.teams.areEnemies(team, tile.getTeam()) && pred.test(tile)); + for(Team enemy : state.teams.alliesOf(team)){ + TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred); + if(entity != null){ + return entity; + } + } + return null; } /**Returns the neareset enemy tile in a range.*/ public static TileEntity findEnemyTile(Team team, float x, float y, float range, Predicate pred){ - return findTile(x, y, range, tile -> state.teams.areEnemies(team, tile.getTeam()) && pred.test(tile)); - } - - //TODO optimize, spatial caching of tiles - /**Returns the neareset tile entity in a range.*/ - public static TileEntity findTile(float x, float y, float range, Predicate pred){ - Entity closest = null; - float dst = 0; - - int rad = (int)(range/tilesize)+1; - int tilex = Mathf.scl2(x, tilesize); - int tiley = Mathf.scl2(y, tilesize); - - for(int rx = -rad; rx <= rad; rx ++){ - for(int ry = -rad; ry <= rad; ry ++){ - Tile other = world.tile(rx+tilex, ry+tiley); - - if(other != null) other = other.target(); - - if(other == null || other.entity == null || !pred.test(other)) continue; - - TileEntity e = other.entity; - - float ndst = Vector2.dst(x, y, e.x, e.y); - if(ndst < range && (closest == null || ndst < dst)){ - dst = ndst; - closest = e; - } + for(Team enemy : state.teams.enemiesOf(team)){ + TileEntity entity = world.indexer().findTile(enemy, x, y, range, pred); + if(entity != null){ + return entity; } } - - return (TileEntity) closest; + return null; } /**Iterates over all units on all teams, including players.*/ diff --git a/core/src/io/anuke/mindustry/entities/units/BaseUnit.java b/core/src/io/anuke/mindustry/entities/units/BaseUnit.java index 2243e14f6e..01e684164a 100644 --- a/core/src/io/anuke/mindustry/entities/units/BaseUnit.java +++ b/core/src/io/anuke/mindustry/entities/units/BaseUnit.java @@ -5,6 +5,7 @@ import io.anuke.annotations.Annotations.Remote; import io.anuke.mindustry.content.fx.ExplosionFx; import io.anuke.mindustry.entities.TileEntity; import io.anuke.mindustry.entities.Unit; +import io.anuke.mindustry.entities.Units; import io.anuke.mindustry.entities.bullet.Bullet; import io.anuke.mindustry.entities.traits.TargetTrait; import io.anuke.mindustry.game.Team; @@ -113,7 +114,7 @@ public abstract class BaseUnit extends Unit{ public void targetClosest(){ if(target != null) return; - //target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange()); + target = Units.getClosestTarget(team, x, y, inventory.getAmmoRange()); } public UnitState getStartState(){ @@ -254,12 +255,14 @@ public abstract class BaseUnit extends Unit{ public void writeSave(DataOutput stream) throws IOException { super.writeSave(stream); stream.writeByte(type.id); + stream.writeBoolean(isWave); } @Override public void readSave(DataInput stream) throws IOException { super.readSave(stream); byte type = stream.readByte(); + this.isWave = stream.readBoolean(); this.type = UnitType.getByID(type); add(); diff --git a/core/src/io/anuke/mindustry/entities/units/GroundUnit.java b/core/src/io/anuke/mindustry/entities/units/GroundUnit.java index df577c613d..5102941d29 100644 --- a/core/src/io/anuke/mindustry/entities/units/GroundUnit.java +++ b/core/src/io/anuke/mindustry/entities/units/GroundUnit.java @@ -53,7 +53,7 @@ public abstract class GroundUnit extends BaseUnit { public void update() { super.update(); - if(target == null){ + if(!velocity.isZero(0.001f) && (target == null || (inventory.hasAmmo() && distanceTo(target) <= inventory.getAmmoRange()))){ rotation = Mathf.lerpDelta(rotation, velocity.angle(), 0.2f); } } @@ -104,7 +104,6 @@ public abstract class GroundUnit extends BaseUnit { super.updateTargeting(); if(Units.invalidateTarget(target, team, x, y, Float.MAX_VALUE)){ - if(target != null) Log.info("Invalidating target {0}", target); target = null; } } diff --git a/core/src/io/anuke/mindustry/game/TeamInfo.java b/core/src/io/anuke/mindustry/game/TeamInfo.java index b55fe91308..33ea3b37c7 100644 --- a/core/src/io/anuke/mindustry/game/TeamInfo.java +++ b/core/src/io/anuke/mindustry/game/TeamInfo.java @@ -76,6 +76,18 @@ public class TeamInfo { return ally ? enemies : allies; } + /**Returns a set of all teams that are allies of this team. + * For teams not active, an empty set is returned.*/ + public ObjectSet alliesOf(Team team) { + boolean ally = allies.contains(team); + boolean enemy = enemies.contains(team); + + //this team isn't even in the game, so target everything! + if(!ally && !enemy) return allTeams; + + return !ally ? enemies : allies; + } + /**Returns a set of all teams that are enemies of this team. * For teams not active, an empty set is returned.*/ public ObjectSet enemyDataOf(Team team) { diff --git a/core/src/io/anuke/mindustry/io/Map.java b/core/src/io/anuke/mindustry/io/Map.java index 6f4c36c929..1963995a4c 100644 --- a/core/src/io/anuke/mindustry/io/Map.java +++ b/core/src/io/anuke/mindustry/io/Map.java @@ -1,6 +1,7 @@ package io.anuke.mindustry.io; import com.badlogic.gdx.graphics.Texture; +import com.badlogic.gdx.utils.ObjectMap; import io.anuke.ucore.function.Supplier; import java.io.InputStream; @@ -24,6 +25,10 @@ public class Map { this.stream = streamSupplier; } + public Map(String unknownName, int width, int height){ + this(unknownName, new MapMeta(0, new ObjectMap<>(), width, height, null), true, () -> null); + } + public String getDisplayName(){ return meta.tags.get("name", name); } diff --git a/core/src/io/anuke/mindustry/io/versions/Save16.java b/core/src/io/anuke/mindustry/io/versions/Save16.java index 4703700755..c241205f1c 100644 --- a/core/src/io/anuke/mindustry/io/versions/Save16.java +++ b/core/src/io/anuke/mindustry/io/versions/Save16.java @@ -9,13 +9,13 @@ import io.anuke.mindustry.entities.traits.TypeTrait; import io.anuke.mindustry.game.Difficulty; import io.anuke.mindustry.game.GameMode; import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.io.Map; import io.anuke.mindustry.io.SaveFileVersion; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.BlockPart; import io.anuke.ucore.entities.Entities; import io.anuke.ucore.entities.EntityGroup; -import io.anuke.ucore.entities.EntityPhysics; import io.anuke.ucore.entities.trait.Entity; import io.anuke.ucore.util.Bits; @@ -23,7 +23,8 @@ import java.io.DataInputStream; import java.io.DataOutputStream; import java.io.IOException; -import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.state; +import static io.anuke.mindustry.Vars.world; public class Save16 extends SaveFileVersion { @@ -39,7 +40,8 @@ public class Save16 extends SaveFileVersion { //general state byte mode = stream.readByte(); String mapname = stream.readUTF(); - world.setMap(world.maps().getByName(mapname)); + Map map = world.maps().getByName(mapname); + world.setMap(map); int wave = stream.readInt(); byte difficulty = stream.readByte(); @@ -55,13 +57,13 @@ public class Save16 extends SaveFileVersion { int blocksize = stream.readInt(); - IntMap map = new IntMap<>(); + IntMap blockMap = new IntMap<>(); for(int i = 0; i < blocksize; i ++){ String name = stream.readUTF(); int id = stream.readShort(); - map.put(id, Block.getByName(name)); + blockMap.put(id, Block.getByName(name)); } //entities @@ -82,7 +84,9 @@ public class Save16 extends SaveFileVersion { short width = stream.readShort(); short height = stream.readShort(); - EntityPhysics.resizeTree(0, 0, width * tilesize, height * tilesize); + if(map == null){ + world.setMap(new Map("unknown", width, height)); + } world.beginMapLoad(); diff --git a/core/src/io/anuke/mindustry/world/mapgen/ProcGen.java b/core/src/io/anuke/mindustry/world/mapgen/ProcGen.java index 2245f40232..5826fee28f 100644 --- a/core/src/io/anuke/mindustry/world/mapgen/ProcGen.java +++ b/core/src/io/anuke/mindustry/world/mapgen/ProcGen.java @@ -41,9 +41,7 @@ public class ProcGen { if(dst < 20){ elevation = 0; - } - - if(r > 0.9){ + }else if(r > 0.9){ marker.floor = (byte)Blocks.water.id; elevation = 0;