diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index e7a79215f7..e35de501f8 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -352,6 +352,7 @@ zone.desolateRift.name = Desolate Rift zone.nuclearComplex.name = Nuclear Production Complex zone.overgrowth.name = Overgrowth zone.tarFields.name = Tar Fields +zone.saltFlats.name = Salt Flats settings.language = Language settings.reset = Reset to Defaults diff --git a/core/assets/maps/craters.msav b/core/assets/maps/craters.msav index b3e555a7d2..01738f45b2 100644 Binary files a/core/assets/maps/craters.msav and b/core/assets/maps/craters.msav differ diff --git a/core/src/io/anuke/mindustry/content/TechTree.java b/core/src/io/anuke/mindustry/content/TechTree.java index caf1ebeb4b..2c3ee5d1b9 100644 --- a/core/src/io/anuke/mindustry/content/TechTree.java +++ b/core/src/io/anuke/mindustry/content/TechTree.java @@ -246,8 +246,28 @@ public class TechTree implements ContentList{ }); }); - node(spiritFactory, () -> { - node(phantomFactory); + node(draugFactory, () -> { + node(spiritFactory, () -> { + node(phantomFactory); + }); + + node(daggerFactory, () -> { + node(crawlerFactory, () -> { + node(titanFactory, () -> { + node(fortressFactory, () -> { + + }); + }); + }); + + node(wraithFactory, () -> { + node(spiritFactory, () -> { + node(revenantFactory, () -> { + + }); + }); + }); + }); }); node(dartPad, () -> { diff --git a/core/src/io/anuke/mindustry/content/Zones.java b/core/src/io/anuke/mindustry/content/Zones.java index abad869d04..95adae396a 100644 --- a/core/src/io/anuke/mindustry/content/Zones.java +++ b/core/src/io/anuke/mindustry/content/Zones.java @@ -84,6 +84,7 @@ public class Zones implements ContentList{ launchPeriod = 5; loadout = Loadouts.basicFoundation; zoneRequirements = ZoneRequirement.with(desertWastes, 60); + blockRequirements = new Block[]{Blocks.daggerFactory, Blocks.draugFactory}; resources = new Item[]{Items.copper, Items.scrap, Items.lead, Items.coal, Items.sand}; }}; diff --git a/core/src/io/anuke/mindustry/core/Logic.java b/core/src/io/anuke/mindustry/core/Logic.java index 1904b713b5..3d5901dc97 100644 --- a/core/src/io/anuke/mindustry/core/Logic.java +++ b/core/src/io/anuke/mindustry/core/Logic.java @@ -6,17 +6,21 @@ import io.anuke.arc.ApplicationListener; import io.anuke.arc.Events; import io.anuke.arc.collection.ObjectSet.ObjectSetIterator; import io.anuke.arc.util.Time; -import io.anuke.mindustry.content.Fx; -import io.anuke.mindustry.content.Items; +import io.anuke.mindustry.content.*; import io.anuke.mindustry.core.GameState.State; import io.anuke.mindustry.entities.*; import io.anuke.mindustry.entities.type.Player; import io.anuke.mindustry.entities.type.TileEntity; import io.anuke.mindustry.game.EventType.*; import io.anuke.mindustry.game.*; +import io.anuke.mindustry.game.Teams.TeamData; +import io.anuke.mindustry.gen.BrokenBlock; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.BuildBlock; +import io.anuke.mindustry.world.blocks.BuildBlock.BuildEntity; import static io.anuke.mindustry.Vars.*; @@ -39,13 +43,25 @@ public class Logic implements ApplicationListener{ p.respawns = state.rules.respawns; } }); - } - @Override - public void init(){ - collisions.setCollider(tilesize, (x, y) -> { - Tile tile = world.tile(x, y); - return tile != null && tile.solid(); + Events.on(BlockDestroyEvent.class, event -> { + //blocks that get broken are appended to the team's broken block queue + Tile tile = event.tile; + Block block = tile.block(); + if(block instanceof BuildBlock){ + BuildEntity entity = tile.entity(); + + //update block to reflect the fact that something was being constructed + if(entity.cblock != null && entity.cblock.synthetic()){ + block = entity.cblock; + }else{ + //otherwise this was a deconstruction that was interrupted, don't want to rebuild that + return; + } + } + + TeamData data = state.teams.get(tile.getTeam()); + data.brokenBlocks.addFirst(BrokenBlock.get(tile.x, tile.y, block.id, tile.rotation())); }); } @@ -197,7 +213,6 @@ public class Logic implements ApplicationListener{ } collisions.collideGroups(bulletGroup, playerGroup); - collisions.collideGroups(playerGroup, playerGroup); } world.pathfinder.update(); diff --git a/core/src/io/anuke/mindustry/entities/EntityCollisions.java b/core/src/io/anuke/mindustry/entities/EntityCollisions.java index 3d98f80d35..808f8e4fc8 100644 --- a/core/src/io/anuke/mindustry/entities/EntityCollisions.java +++ b/core/src/io/anuke/mindustry/entities/EntityCollisions.java @@ -1,11 +1,14 @@ package io.anuke.mindustry.entities; import io.anuke.arc.collection.Array; -import io.anuke.arc.collection.IntSet; import io.anuke.arc.math.Mathf; import io.anuke.arc.math.geom.*; import io.anuke.mindustry.entities.traits.Entity; import io.anuke.mindustry.entities.traits.SolidTrait; +import io.anuke.mindustry.world.Tile; + +import static io.anuke.mindustry.Vars.tilesize; +import static io.anuke.mindustry.Vars.world; public class EntityCollisions{ //range for tile collision scanning @@ -14,29 +17,15 @@ public class EntityCollisions{ private static final float seg = 1f; //tile collisions - private float tilesize; private Rectangle tmp = new Rectangle(); - private TileCollider collider; - private TileHitboxProvider hitboxProvider; private Vector2 vector = new Vector2(); private Vector2 l1 = new Vector2(); private Rectangle r1 = new Rectangle(); private Rectangle r2 = new Rectangle(); //entity collisions - private IntSet collided = new IntSet(); private Array arrOut = new Array<>(); - public void setCollider(float tilesize, TileCollider collider, TileHitboxProvider hitbox){ - this.tilesize = tilesize; - this.collider = collider; - this.hitboxProvider = hitbox; - } - - public void setCollider(float tilesize, TileCollider collider){ - setCollider(tilesize, collider, (x, y, out) -> out.setSize(tilesize).setCenter(x * tilesize, y * tilesize)); - } - public void move(SolidTrait entity, float deltax, float deltay){ boolean movedx = false; @@ -67,8 +56,6 @@ public class EntityCollisions{ } public void moveDelta(SolidTrait entity, float deltax, float deltay, boolean x){ - if(collider == null) - throw new IllegalArgumentException("No tile collider specified! Call setCollider() first."); Rectangle rect = r1; entity.hitboxTile(rect); @@ -81,9 +68,8 @@ public class EntityCollisions{ for(int dx = -r; dx <= r; dx++){ for(int dy = -r; dy <= r; dy++){ int wx = dx + tilex, wy = dy + tiley; - if(collider.solid(wx, wy) && entity.collidesGrid(wx, wy)){ - - hitboxProvider.getHitbox(wx, wy, tmp); + if(solid(wx, wy) && entity.collidesGrid(wx, wy)){ + tmp.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize); if(tmp.overlaps(rect)){ Vector2 v = Geometry.overlap(rect, tmp, x); @@ -99,9 +85,6 @@ public class EntityCollisions{ } public boolean overlapsTile(Rectangle rect){ - if(collider == null) - throw new IllegalArgumentException("No tile collider specified! Call setCollider() first."); - rect.getCenter(vector); int r = 1; @@ -112,8 +95,8 @@ public class EntityCollisions{ for(int dx = -r; dx <= r; dx++){ for(int dy = -r; dy <= r; dy++){ int wx = dx + tilex, wy = dy + tiley; - if(collider.solid(wx, wy)){ - hitboxProvider.getHitbox(wx, wy, r2); + if(solid(wx, wy)){ + r2.setSize(tilesize).setCenter(wx * tilesize, wy * tilesize); if(r2.overlaps(rect)){ return true; @@ -126,7 +109,6 @@ public class EntityCollisions{ @SuppressWarnings("unchecked") public void updatePhysics(EntityGroup group){ - collided.clear(); QuadTree tree = group.tree(); tree.clear(); @@ -140,6 +122,11 @@ public class EntityCollisions{ } } + private static boolean solid(int x, int y){ + Tile tile = world.tile(x, y); + return tile != null && tile.solid(); + } + private void checkCollide(Entity entity, Entity other){ SolidTrait a = (SolidTrait)entity; @@ -221,10 +208,9 @@ public class EntityCollisions{ @SuppressWarnings("unchecked") public void collideGroups(EntityGroup groupa, EntityGroup groupb){ - collided.clear(); for(Entity entity : groupa.all()){ - if(!(entity instanceof SolidTrait) || collided.contains(entity.getID())) + if(!(entity instanceof SolidTrait)) continue; SolidTrait solid = (SolidTrait)entity; @@ -241,20 +227,10 @@ public class EntityCollisions{ for(SolidTrait sc : arrOut){ sc.hitbox(r1); - if(r2.overlaps(r1) && !collided.contains(sc.getID())){ + if(r2.overlaps(r1)){ checkCollide(entity, sc); } } - - collided.add(entity.getID()); } } - - public interface TileCollider{ - boolean solid(int x, int y); - } - - public interface TileHitboxProvider{ - void getHitbox(int x, int y, Rectangle out); - } } diff --git a/core/src/io/anuke/mindustry/game/EventType.java b/core/src/io/anuke/mindustry/game/EventType.java index 872f74a1c1..de0a286c66 100644 --- a/core/src/io/anuke/mindustry/game/EventType.java +++ b/core/src/io/anuke/mindustry/game/EventType.java @@ -129,6 +129,8 @@ public class EventType{ } } + /** Called right before a block is destroyed. + * The tile entity of the tile in this event cannot be null when this happens.*/ public static class BlockDestroyEvent{ public final Tile tile; diff --git a/core/src/io/anuke/mindustry/game/Teams.java b/core/src/io/anuke/mindustry/game/Teams.java index bf328255ca..077729ddd0 100644 --- a/core/src/io/anuke/mindustry/game/Teams.java +++ b/core/src/io/anuke/mindustry/game/Teams.java @@ -1,5 +1,6 @@ package io.anuke.mindustry.game; +import io.anuke.annotations.Annotations.Struct; import io.anuke.arc.collection.*; import io.anuke.mindustry.Vars; import io.anuke.mindustry.world.Tile; @@ -43,6 +44,7 @@ public class Teams{ public class TeamData{ public final ObjectSet cores = new ObjectSet<>(); + public final LongQueue brokenBlocks = new LongQueue(); public final EnumSet enemies; public final Team team; @@ -51,4 +53,11 @@ public class Teams{ this.enemies = enemies; } } + + /** Represents a block made by this team that was destroyed somewhere on the map. + * This does not include deconstructed blocks.*/ + @Struct + public class BrokenBlockStruct{ + public short x, y, rotation, block; + } } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/TechTreeDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/TechTreeDialog.java index 601de99218..1521eb7128 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/TechTreeDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/TechTreeDialog.java @@ -246,44 +246,51 @@ public class TechTreeDialog extends FloatingDialog{ } }); - infoTable.background("content-background"); infoTable.update(() -> infoTable.setPosition(button.getX() + button.getWidth(), button.getY() + button.getHeight(), Align.topLeft)); - infoTable.margin(0).left().defaults().left(); + infoTable.left(); - infoTable.addImageButton("icon-info", "node", 14 * 2, () -> ui.content.show(node.block)).growY().width(50f); + infoTable.table("content-background", b -> { + b.margin(0).left().defaults().left(); - infoTable.add().grow(); + b.addImageButton("icon-info", "node", 14 * 2, () -> ui.content.show(node.block)).growY().width(50f); + b.add().grow(); + b.table(desc -> { + desc.left().defaults().left(); + desc.add(node.block.localizedName); + desc.row(); + if(locked(node)){ + desc.table(t -> { + t.left(); + for(ItemStack req : node.requirements){ + t.table(list -> { + list.left(); + list.addImage(req.item.getContentIcon()).size(8 * 3).padRight(3); + list.add(req.item.localizedName()).color(Color.LIGHT_GRAY); + list.label(() -> " " + Math.min(data.getItem(req.item), req.amount) + " / " + req.amount) + .update(l -> l.setColor(data.has(req.item, req.amount) ? Color.LIGHT_GRAY : Color.SCARLET)); + }).fillX().left(); + t.row(); + } + }); + }else{ + desc.add("$completed"); + } + }).pad(9); - infoTable.table(desc -> { - desc.left().defaults().left(); - desc.add(node.block.localizedName); - desc.row(); - if(locked(node)){ - desc.table(t -> { - t.left(); - for(ItemStack req : node.requirements){ - t.table(list -> { - list.left(); - list.addImage(req.item.getContentIcon()).size(8 * 3).padRight(3); - list.add(req.item.localizedName()).color(Color.LIGHT_GRAY); - list.label(() -> " " + Math.min(data.getItem(req.item), req.amount) + " / " + req.amount) - .update(l -> l.setColor(data.has(req.item, req.amount) ? Color.LIGHT_GRAY : Color.SCARLET)); - }).fillX().left(); - t.row(); - } - }); - }else{ - desc.add("$completed"); + if(mobile && locked(node)){ + b.row(); + b.addImageTextButton("$research", "icon-check", "node", 16 * 2, () -> unlock(node)) + .disabled(i -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3); } - }).pad(9); + }); - if(mobile && locked(node)){ - infoTable.row(); - infoTable.addImageTextButton("$research", "icon-check", "node", 16 * 2, () -> unlock(node)) - .disabled(b -> !data.hasItems(node.requirements)).growX().height(44f).colspan(3); + infoTable.row(); + if(node.block.description != null){ + infoTable.table("dialogDim", t -> t.margin(3f).left().labelWrap(node.block.description).color(Color.LIGHT_GRAY).growX()).fillX(); } + addChild(infoTable); infoTable.pack(); infoTable.act(Core.graphics.getDeltaTime()); diff --git a/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java b/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java index f1fb275894..a896f8583b 100644 --- a/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java +++ b/core/src/io/anuke/mindustry/world/blocks/BuildBlock.java @@ -170,7 +170,7 @@ public class BuildBlock extends Block{ * The recipe of the block that is being constructed. * If there is no recipe for this block, as is the case with rocks, 'previous' is used. */ - public Block cblock; + public @Nullable Block cblock; public float progress = 0; public float buildCost; diff --git a/tools/src/io/anuke/mindustry/Generators.java b/tools/src/io/anuke/mindustry/Generators.java index 443f81a883..423e4eacbd 100644 --- a/tools/src/io/anuke/mindustry/Generators.java +++ b/tools/src/io/anuke/mindustry/Generators.java @@ -70,7 +70,7 @@ public class Generators{ ImagePacker.generate("block-icons", () -> { Image colors = new Image(content.blocks().size, 1); - Color outlineColor = Color.valueOf("4d4e58"); + Color outlineColor = Color.valueOf("404049"); for(Block block : content.blocks()){ TextureRegion[] regions = block.getGeneratedIcons();