From 8ec87872c8b4784ef0f9eebb98a2605f90905f25 Mon Sep 17 00:00:00 2001 From: Anuken Date: Sat, 4 Aug 2018 14:51:33 -0400 Subject: [PATCH] Added outpost schematic system --- .../mindustry/content/blocks/Blocks.java | 1 + .../anuke/mindustry/input/DesktopInput.java | 35 ++++- core/src/io/anuke/mindustry/maps/Sector.java | 2 +- .../maps/generation/FortressGenerator.java | 133 ++++++++++++++---- .../maps/generation/StructureFormat.java | 96 +++++++++++++ .../maps/generation/WorldGenerator.java | 2 +- .../mindustry/ui/dialogs/PausedDialog.java | 2 +- .../mindustry/ui/dialogs/SaveDialog.java | 2 +- .../io/anuke/mindustry/world/ColorMapper.java | 5 +- .../blocks/defense/turrets/ItemTurret.java | 4 + 10 files changed, 242 insertions(+), 40 deletions(-) create mode 100644 core/src/io/anuke/mindustry/maps/generation/StructureFormat.java diff --git a/core/src/io/anuke/mindustry/content/blocks/Blocks.java b/core/src/io/anuke/mindustry/content/blocks/Blocks.java index 1cd89f0305..1e5a793f91 100644 --- a/core/src/io/anuke/mindustry/content/blocks/Blocks.java +++ b/core/src/io/anuke/mindustry/content/blocks/Blocks.java @@ -20,6 +20,7 @@ public class Blocks extends BlockList implements ContentList{ air = new Floor("air"){ { blend = false; + alwaysReplace = true; } //don't draw diff --git a/core/src/io/anuke/mindustry/input/DesktopInput.java b/core/src/io/anuke/mindustry/input/DesktopInput.java index 80a5c9ef35..ec6d5dd58d 100644 --- a/core/src/io/anuke/mindustry/input/DesktopInput.java +++ b/core/src/io/anuke/mindustry/input/DesktopInput.java @@ -9,6 +9,8 @@ import io.anuke.mindustry.entities.Player; import io.anuke.mindustry.graphics.Palette; import io.anuke.mindustry.input.PlaceUtils.NormalizeDrawResult; import io.anuke.mindustry.input.PlaceUtils.NormalizeResult; +import io.anuke.mindustry.maps.generation.StructureFormat; +import io.anuke.mindustry.maps.generation.StructureFormat.StructBlock; import io.anuke.mindustry.net.Net; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; @@ -19,7 +21,9 @@ import io.anuke.ucore.core.KeyBinds; import io.anuke.ucore.core.Settings; import io.anuke.ucore.graphics.Draw; import io.anuke.ucore.graphics.Lines; +import io.anuke.ucore.input.Input; import io.anuke.ucore.scene.ui.layout.Unit; +import io.anuke.ucore.util.Log; import io.anuke.ucore.util.Mathf; import static io.anuke.mindustry.Vars.*; @@ -63,6 +67,23 @@ public class DesktopInput extends InputHandler{ } } + void printArea(NormalizeResult result){ + StructBlock[][] blocks = new StructBlock[Math.abs(result.x2 - result.x) + 1][Math.abs(result.y2 - result.y) + 1]; + + for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ + for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ + int wx = result.x + x; + int wy = result.y + y; + + Block block = world.tile(wx, wy).block(); + + blocks[x][y] = new StructBlock(block == Blocks.blockpart ? Blocks.air : block, world.tile(wx, wy).getRotation()); + } + } + + Log.info(StructureFormat.writeBase64(blocks)); + } + @Override public boolean isDrawing(){ return mode != none || recipe != null; @@ -259,12 +280,16 @@ public class DesktopInput extends InputHandler{ }else if(mode == breaking){ //touch up while breaking, break everything in selection NormalizeResult result = PlaceUtils.normalizeArea(selectX, selectY, cursor.x, cursor.y, rotation, false, maxLength); - for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ - for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ - int wx = selectX + x * Mathf.sign(cursor.x - selectX); - int wy = selectY + y * Mathf.sign(cursor.y - selectY); + if(debug && Inputs.keyDown(Input.CONTROL_LEFT)){ + printArea(result); + }else{ + for(int x = 0; x <= Math.abs(result.x2 - result.x); x++){ + for(int y = 0; y <= Math.abs(result.y2 - result.y); y++){ + int wx = selectX + x * Mathf.sign(cursor.x - selectX); + int wy = selectY + y * Mathf.sign(cursor.y - selectY); - tryBreakBlock(wx, wy); + tryBreakBlock(wx, wy); + } } } } diff --git a/core/src/io/anuke/mindustry/maps/Sector.java b/core/src/io/anuke/mindustry/maps/Sector.java index a320542b48..34f2368135 100644 --- a/core/src/io/anuke/mindustry/maps/Sector.java +++ b/core/src/io/anuke/mindustry/maps/Sector.java @@ -33,7 +33,7 @@ public class Sector{ public transient int difficulty; public Mission currentMission(){ - return missions.get(completedMissions); + return missions.get(Math.min(completedMissions, missions.size - 1)); } public int getSeed(){ diff --git a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java index 1751821a26..1b59950391 100644 --- a/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generation/FortressGenerator.java @@ -1,29 +1,67 @@ package io.anuke.mindustry.maps.generation; -import com.badlogic.gdx.math.GridPoint2; +import com.badlogic.gdx.math.Rectangle; import com.badlogic.gdx.math.Vector2; +import com.badlogic.gdx.utils.Array; import io.anuke.mindustry.content.Items; import io.anuke.mindustry.content.blocks.Blocks; -import io.anuke.mindustry.content.blocks.DefenseBlocks; -import io.anuke.mindustry.content.blocks.ProductionBlocks; -import io.anuke.mindustry.content.blocks.TurretBlocks; import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.maps.generation.StructureFormat.StructBlock; +import io.anuke.mindustry.type.AmmoType; import io.anuke.mindustry.type.Item; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; -import io.anuke.ucore.util.Geometry; +import io.anuke.mindustry.world.blocks.defense.turrets.ItemTurret; +import io.anuke.mindustry.world.blocks.defense.turrets.PowerTurret; +import io.anuke.mindustry.world.blocks.defense.turrets.Turret; + +import static io.anuke.mindustry.Vars.world; public class FortressGenerator{ - private final Block[] turretBlocks = {TurretBlocks.duo, TurretBlocks.hail, TurretBlocks.wave}; - private final Block[] drillBlocks = {ProductionBlocks.tungstenDrill, ProductionBlocks.carbideDrill}; - private final Block[] armorBlocks = {DefenseBlocks.tungstenWall, DefenseBlocks.carbideWall, DefenseBlocks.thoriumWall}; - private final int minCoreDst = 50; + private final static int minCoreDst = 50; + private static Structure[] structures; private int enemyX, enemyY, coreX, coreY; private Team team; private Generation gen; + private static void init(){ + if(structures != null) return; + + String vaults = "BQMADWNhcmJpZGUtZHJpbGwCAA10dW5nc3Rlbi13YWxsAQATdHVuZ3N0ZW4td2FsbC1sYXJnZQAAA2FpcgQABXZhdWx0CQUAAgABAAEAAQABAAIAAAABAAAAAAACAAAAAQAAAAABAQABAgEBAQAAAAIAAgMBAAEAAAICAAAAAAAAAgACAgAABAIAAAIAAgIAAAAAAAACAAICAgMCAwIDAgM="; + + structures = new Structure[]{ + //tiny duo outpost + new Structure(Items.tungsten, "BAMADnR1bmdzdGVuLWRyaWxsAgADZHVvAQANdHVuZ3N0ZW4td2FsbAAAA2FpcgMFAQABAwEDAQMBAAEAAgMDAwIDAQABAAEBAQEBAQEA"), + + //basic outposts with duos + new Structure(Items.tungsten, "BAIAA2R1bwMADWNhcmJpZGUtZHJpbGwBAA10dW5nc3Rlbi13YWxsAAADYWlyBQUAAAEAAQABAAAAAQABAAIAAQABAAEAAgADAwIAAQABAAEAAgABAAEAAAABAAEAAQAAAA=="), + + //more advanced duo outpost + new Structure(Items.lead, "BwYADnR1bmdzdGVuLWRyaWxsAwADZHVvBAAIc3BsaXR0ZXIBAA10dW5nc3Rlbi13YWxsAgATdHVuZ3N0ZW4td2FsbC1sYXJnZQAAA2FpcgUACGNvbnZleW9yCQkAAAAAAQEBAQEBAQEBAgAAAAAAAAICAAEDAAQDAwACAgAAAAABAgACAAABAgUCAQEAAAAAAQABAgMAAQIBAgUCAQEBAQMAAQABAgQCBQMFAwYCBQEFAQQDAQABAgMAAQEBAQUAAQMBAwMAAQABAwICAAMBAQUAAQMCAgADAQMAAAAAAAIDAAQDAwAAAwADAAAAAAAAAQIBAwEDAQMBAwAAAAA="), + + //material storage + new Structure(Items.lead, vaults), + new Structure(Items.coal, vaults), + new Structure(Items.titanium, vaults), + + //salvo outpost + new Structure(Items.tungsten, "BAIABXNhbHZvAwANY2FyYmlkZS1kcmlsbAAAA2FpcgEADGNhcmJpZGUtd2FsbAcHAAAAAAEDAQMBAwEDAAABAwEDAQMCAAAAAQMAAAEAAgAAAAAAAAABAwEDAQAAAAAAAwACAAAAAQIBAAEBAgAAAAAAAAABAgAAAQEAAAAAAQEBAQEBAAABAQEBAQEBAQAAAAA="), + + //advanced laser outpost + new Structure(null, "BQIABmxhbmNlcgEAEmNhcmJpZGUtd2FsbC1sYXJnZQQAEXNvbGFyLXBhbmVsLWxhcmdlAAADYWlyAwALc29sYXItcGFuZWwLCwAAAAAAAAAAAAABAAAAAAAAAAAAAAAAAAAAAAABAAAAAAAAAAEAAAAAAAAAAAABAAAAAAAAAAIAAAAAAAAAAAAAAAAAAAAAAAMAAwAAAAAAAwABAAAAAAABAAAAAgAAAAAAAAAAAAMAAAAAAAAAAAAAAAAAAAAAAAQAAAACAAAAAQAAAAAAAQAAAAMAAAAAAAAAAAAAAAAAAAAAAAAAAAADAAIAAAADAAMAAQAAAAAAAAAAAAEAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAQAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAA"), + + //coal laser outpost + new Structure(null, "BgEADHRob3JpdW0td2FsbAMABmxhbmNlcgUAFGNvbWJ1c3Rpb24tZ2VuZXJhdG9yBAANY2FyYmlkZS1kcmlsbAAAA2FpcgIAC3NvbGFyLXBhbmVsBwcAAAEAAQABAQEBAQEBAAAAAQACAgMAAAACAAEAAAABAAICAAAAAAIAAQAAAAEAAQAEAQUAAQABAAAAAQACAAMBAAMCAAEAAAABAAIAAAMAAwIAAQAAAAEAAQABAwEDAQABAA=="), + + //ultimate laser outpost + new Structure(null, "BgMABmxhbmNlcgIAEmNhcmJpZGUtd2FsbC1sYXJnZQUAEXNvbGFyLXBhbmVsLWxhcmdlAAADYWlyBAALc29sYXItcGFuZWwBAAxjYXJiaWRlLXdhbGwPDwAAAAAAAAAAAAABAwIDAAABAwAAAAAAAAAAAAAAAAAAAAACAwAAAgMAAAAAAAACAwAAAgMAAAAAAAAAAAAAAQMAAAAAAAAAAAMDAAAAAAAAAAAAAAIDAAAAAAAAAgMAAAMDAAAEAwAAAAADAwAAAwMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAIDAAAAAAAAAgMAAAMDAAAAAAUDAAAAAAAAAAAEAwAAAAABAwEDAAAAAAAAAAAAAAAAAAAAAAUDAAADAwAAAgMAAAIDAAADAwAAAAAAAAAABAMAAAAAAAAAAwAAAAAAAAAAAAAAAAAAAAAFAwAAAAAAAAAAAwMAAAIDAAABAwEDAgMAAAQDAAAAAAAAAAAFAwAAAAAAAAAAAAAAAAAAAAAAAAMDAAADAwAAAAAAAAAAAwMAAAIDAAAAAAAAAgMAAAAAAAAAAAAAAwMAAAQDAAAAAAAAAAAAAAAAAAAAAAIDAAACAwAAAAAAAAIDAAACAwAAAQMAAAAAAAABAwAAAAAAAAAAAgMAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAAEDAAAAAAEDAAAAAAAAAAAAAA=="), + }; + } + public void generate(Generation gen, Team team, int coreX, int coreY, int enemyX, int enemyY){ + init(); + this.enemyX = enemyX; this.enemyY = enemyY; this.coreX = coreX; @@ -35,29 +73,39 @@ public class FortressGenerator{ } void genOutposts(){ - int index = 0; - Block turret = turretBlocks[index], drill = drillBlocks[index], armor = armorBlocks[index]; - Item ore = Items.tungsten; + int padding = 10; + Array used = new Array<>(); + Rectangle rect = new Rectangle(); - for(int x = 2; x < gen.width - 2; x++){ - for(int y = 2; y < gen.height - 2; y++){ - if(Vector2.dst(x, y, coreX, coreY) > minCoreDst && - gen.tiles[x][y].floor().dropsItem(ore) && gen.random.chance(0.02)){ - - int elevation = gen.tiles[x][y].getElevation(); - gen.tiles[x][y].setBlock(drill, team); + for(Structure struct : structures){ + for(int x = padding; x < gen.width - padding; x++){ + loop: + for(int y = padding; y < gen.height - padding; y++){ + rect.set(x - struct.layout.length, y - struct.layout[0].length, struct.layout.length, struct.layout[0].length); + if(Vector2.dst(x, y, coreX, coreY) > minCoreDst && Vector2.dst(x, y, enemyX, enemyY) > 30 && + (struct.ore == null || gen.tiles[x][y].floor().dropsItem(struct.ore)) && gen.random.chance(0.03)){ + for(Rectangle other : used){ + if(other.overlaps(rect)){ + continue loop; + } + } + used.add(new Rectangle(rect.x - 1, rect.y - 1, rect.width + 2, rect.height + 2)); + int elevation = world.tile(x, y).getElevation(); + for(int cx = 0; cx < struct.layout.length; cx++){ + for(int cy = 0; cy < struct.layout[0].length; cy++){ + int wx = x + cx - struct.layout.length/2; + int wy = y + cy - struct.layout[0].length/2; + StructBlock block = struct.layout[cx][cy]; + Tile tile = world.tile(wx, wy); + if(block.block != Blocks.air && tile.block().alwaysReplace){ + tile.setElevation(elevation); + tile.setRotation(block.rotation); + tile.setBlock(block.block, team); - for(GridPoint2 point : Geometry.d4){ - gen.tiles[x + point.x][y + point.y].setBlock(turret, team); - gen.tiles[x + point.x][y + point.y].setElevation(elevation); - } - - for(int cx = -2; cx <= 2; cx++){ - for(int cy = -2; cy <= 2; cy++){ - Tile tile = gen.tiles[x + cx][y + cy]; - if(tile.block().alwaysReplace || tile.block() == Blocks.air){ - tile.setElevation(elevation); - tile.setBlock(armor, team); + if(block.block instanceof Turret){ + fillTurret(tile); + } + } } } } @@ -65,4 +113,29 @@ public class FortressGenerator{ } } } + + void setBlock(Block block){ + + } + + void fillTurret(Tile tile){ + Block block = tile.block(); + if(block instanceof PowerTurret){ + tile.entity.power.amount = block.powerCapacity; + }else if(block instanceof ItemTurret){ + ItemTurret turret = (ItemTurret)block; + AmmoType[] type = turret.getAmmoTypes(); + block.handleStack(type[0].item, block.acceptStack(type[0].item, 1000, tile, null), tile, null); + } + } + + static class Structure{ + public final StructBlock[][] layout; + public final Item ore; + + public Structure(Item ore, String encoding){ + this.ore = ore; + this.layout = StructureFormat.read(encoding); + } + } } diff --git a/core/src/io/anuke/mindustry/maps/generation/StructureFormat.java b/core/src/io/anuke/mindustry/maps/generation/StructureFormat.java new file mode 100644 index 0000000000..0826c58311 --- /dev/null +++ b/core/src/io/anuke/mindustry/maps/generation/StructureFormat.java @@ -0,0 +1,96 @@ +package io.anuke.mindustry.maps.generation; + +import com.badlogic.gdx.utils.Base64Coder; +import com.badlogic.gdx.utils.IntMap; +import com.badlogic.gdx.utils.ObjectIntMap; +import com.badlogic.gdx.utils.ObjectIntMap.Entry; +import io.anuke.mindustry.content.blocks.Blocks; +import io.anuke.mindustry.world.Block; + +import java.io.*; + +public class StructureFormat{ + + public static byte[] write(StructBlock[][] blocks){ + try{ + ByteArrayOutputStream out = new ByteArrayOutputStream(); + DataOutputStream stream = new DataOutputStream(out); + + ObjectIntMap mapping = new ObjectIntMap<>(); + int lastid = 1; + mapping.put(Blocks.air, 0); + + for(int x = 0; x < blocks.length; x++){ + for(int y = 0; y < blocks[0].length; y++){ + Block block = blocks[x][y].block; + if(!mapping.containsKey(block)){ + mapping.put(block, lastid++); + } + } + } + + stream.writeByte(mapping.size); + for(Entry entry : mapping){ + stream.writeByte(entry.value); + stream.writeUTF(entry.key.name); + } + + stream.writeByte(blocks.length); + stream.writeByte(blocks[0].length); + + for(int x = 0; x < blocks.length; x++){ + for(int y = 0; y < blocks[0].length; y++){ + StructBlock block = blocks[x][y]; + stream.writeByte(mapping.get(block.block, 0)); + stream.writeByte(block.rotation); + } + } + + return out.toByteArray(); + + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + public static String writeBase64(StructBlock[][] blocks){ + return new String(Base64Coder.encode(write(blocks))); + } + + public static StructBlock[][] read(byte[] bytes){ + try{ + DataInputStream stream = new DataInputStream(new ByteArrayInputStream(bytes)); + byte size = stream.readByte(); + IntMap map = new IntMap<>(); + for(int i = 0; i < size; i++){ + map.put(stream.readByte(), Block.getByName(stream.readUTF())); + } + + byte width = stream.readByte(), height = stream.readByte(); + StructBlock[][] blocks = new StructBlock[width][height]; + for(int x = 0; x < width; x++){ + for(int y = 0; y < height; y++){ + blocks[x][y] = new StructBlock(map.get(stream.readByte()), stream.readByte()); + } + } + + return blocks; + }catch(IOException e){ + throw new RuntimeException(e); + } + } + + public static StructBlock[][] read(String base64){ + return read(Base64Coder.decode(base64)); + } + + public static class StructBlock{ + public final Block block; + public final byte rotation; + + public StructBlock(Block block, byte rotation){ + this.block = block; + this.rotation = rotation; + } + } +} diff --git a/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java b/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java index e3352186fb..3c63c3c84e 100644 --- a/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java +++ b/core/src/io/anuke/mindustry/maps/generation/WorldGenerator.java @@ -129,7 +129,7 @@ public class WorldGenerator{ tile.updateOcclusion(); //fix things on cliffs that shouldn't be - if(tile.block() != Blocks.air && tile.hasCliffs()){ + if(tile.block() != Blocks.air && tile.hasCliffs() && !tile.block().isMultiblock() && tile.block() != Blocks.blockpart){ tile.setBlock(Blocks.air); } } diff --git a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java index a94576412e..efa2a413e8 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/PausedDialog.java @@ -22,7 +22,7 @@ public class PausedDialog extends FloatingDialog{ void rebuild(){ missionTable.clear(); - if(world.getSector() != null){ + if(world.getSector() != null && !world.getSector().complete){ missionTable.add("[LIGHT_GRAY]" + Bundles.format("text.mission", "")); missionTable.row(); missionTable.table(t -> { diff --git a/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java index 338eb5dc3a..26d0096ea8 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/SaveDialog.java @@ -27,7 +27,7 @@ public class SaveDialog extends LoadDialog{ ui.showTextInput("$text.save", "$text.save.newslot", "", text -> { ui.loadAnd("$text.saving", () -> { control.getSaves().addSave(text); - setup(); + threads.runGraphics(() -> threads.run(() -> threads.runGraphics(this::setup))); }); }) ).fillX().margin(10f).minWidth(300f).height(70f).pad(4f).padRight(-4); diff --git a/core/src/io/anuke/mindustry/world/ColorMapper.java b/core/src/io/anuke/mindustry/world/ColorMapper.java index 9bddff9665..20d0067f58 100644 --- a/core/src/io/anuke/mindustry/world/ColorMapper.java +++ b/core/src/io/anuke/mindustry/world/ColorMapper.java @@ -22,7 +22,10 @@ public class ColorMapper implements ContentList{ } public static int colorFor(Block floor, Block wall, Team team, int elevation){ - int color = wall.breakable ? team.intColor : getBlockColor(wall); + if(wall.synthetic()){ + return team.intColor; + } + int color = getBlockColor(wall); if(color == 0) color = ColorMapper.getBlockColor(floor); if(elevation > 0){ if(tmpColors.get() == null) tmpColors.set(new Color()); diff --git a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/ItemTurret.java b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/ItemTurret.java index b982ff5a0b..2972248ac2 100644 --- a/core/src/io/anuke/mindustry/world/blocks/defense/turrets/ItemTurret.java +++ b/core/src/io/anuke/mindustry/world/blocks/defense/turrets/ItemTurret.java @@ -21,6 +21,10 @@ public class ItemTurret extends CooledTurret{ hasItems = true; } + public AmmoType[] getAmmoTypes(){ + return ammoTypes; + } + @Override public void setStats(){ super.setStats();