diff --git a/core/src/io/anuke/mindustry/core/Control.java b/core/src/io/anuke/mindustry/core/Control.java index b641b1dcb2..58b345c8f7 100644 --- a/core/src/io/anuke/mindustry/core/Control.java +++ b/core/src/io/anuke/mindustry/core/Control.java @@ -18,7 +18,7 @@ import io.anuke.mindustry.gen.Call; import io.anuke.mindustry.input.*; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.net.Net; -import io.anuke.mindustry.type.Item; +import io.anuke.mindustry.type.*; import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.world.Tile; @@ -207,6 +207,23 @@ public class Control implements ApplicationListener{ }); } + public void playZone(Zone zone){ + ui.loadAnd(() -> { + logic.reset(); + state.rules = zone.rules.get(); + state.rules.zone = zone; + world.loadGenerator(zone.generator); + for(Tile core : state.teams.get(defaultTeam).cores){ + for(ItemStack stack : zone.getStartingItems()){ + core.entity.items.add(stack.item, stack.amount); + } + } + state.set(State.playing); + control.saves.zoneSave(); + logic.play(); + }); + } + public boolean isHighScore(){ return hiscore; } diff --git a/core/src/io/anuke/mindustry/core/World.java b/core/src/io/anuke/mindustry/core/World.java index d393e8c0f6..91244682c3 100644 --- a/core/src/io/anuke/mindustry/core/World.java +++ b/core/src/io/anuke/mindustry/core/World.java @@ -202,24 +202,6 @@ public class World implements ApplicationListener{ return state.rules.zone; } - //TODO move to Control - public void playZone(Zone zone){ - ui.loadAnd(() -> { - logic.reset(); - state.rules = zone.rules.get(); - state.rules.zone = zone; - loadGenerator(zone.generator); - for(Tile core : state.teams.get(defaultTeam).cores){ - for(ItemStack stack : zone.getStartingItems()){ - core.entity.items.add(stack.item, stack.amount); - } - } - state.set(State.playing); - control.saves.zoneSave(); - logic.play(); - }); - } - public void loadGenerator(Generator generator){ beginMapLoad(); @@ -231,18 +213,9 @@ public class World implements ApplicationListener{ } public void loadMap(Map map){ - beginMapLoad(); - this.currentMap = map; try{ - createTiles(map.width, map.height); - for(int x = 0; x < map.width; x++){ - for(int y = 0; y < map.height; y++){ - tiles[x][y] = new Tile(x, y); - } - } - MapIO.readTiles(map, tiles); - prepareTiles(tiles); + MapIO.loadMap(map); }catch(Exception e){ Log.err(e); if(!headless){ @@ -254,7 +227,7 @@ public class World implements ApplicationListener{ return; } - endMapLoad(); + this.currentMap = map; invalidMap = false; @@ -503,7 +476,7 @@ public class World implements ApplicationListener{ } } - //update cliffs, occlusion data + //update occlusion data for(int x = 0; x < tiles.length; x++){ for(int y = 0; y < tiles[0].length; y++){ Tile tile = tiles[x][y]; diff --git a/core/src/io/anuke/mindustry/editor/DrawOperation.java b/core/src/io/anuke/mindustry/editor/DrawOperation.java index 62dd831fa1..df4e0374b9 100755 --- a/core/src/io/anuke/mindustry/editor/DrawOperation.java +++ b/core/src/io/anuke/mindustry/editor/DrawOperation.java @@ -60,9 +60,8 @@ public class DrawOperation{ class TileOpStruct{ short x; short y; + short value; byte type; - byte from; - byte to; } public enum OpType{ diff --git a/core/src/io/anuke/mindustry/editor/EditorTile.java b/core/src/io/anuke/mindustry/editor/EditorTile.java index 75a35f8e8a..23f1604c0d 100644 --- a/core/src/io/anuke/mindustry/editor/EditorTile.java +++ b/core/src/io/anuke/mindustry/editor/EditorTile.java @@ -11,10 +11,11 @@ import io.anuke.mindustry.world.modules.*; import static io.anuke.mindustry.Vars.ui; +//TODO somehow remove or replace this class with a more flexible solution public class EditorTile extends Tile{ - public EditorTile(int x, int y, byte floor, byte wall){ - super(x, y, floor, wall); + public EditorTile(int x, int y, short floor, short overlay, short wall){ + super(x, y, floor, overlay, wall); } @Override diff --git a/core/src/io/anuke/mindustry/editor/MapEditor.java b/core/src/io/anuke/mindustry/editor/MapEditor.java index c1253509aa..0390e74de3 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditor.java +++ b/core/src/io/anuke/mindustry/editor/MapEditor.java @@ -1,9 +1,9 @@ package io.anuke.mindustry.editor; import io.anuke.arc.collection.ObjectMap; +import io.anuke.arc.collection.StringMap; import io.anuke.arc.files.FileHandle; import io.anuke.arc.math.Mathf; -import io.anuke.arc.util.Pack; import io.anuke.arc.util.Structs; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.game.Team; @@ -12,16 +12,18 @@ import io.anuke.mindustry.io.MapIO; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.BlockPart; import io.anuke.mindustry.world.blocks.Floor; import java.io.IOException; +import static io.anuke.mindustry.Vars.world; + public class MapEditor{ public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20}; - private ObjectMap tags = new ObjectMap<>(); + private StringMap tags = new StringMap(); private MapRenderer renderer = new MapRenderer(this); - private Tile[][] tiles; private OperationStack stack = new OperationStack(); private DrawOperation currentOp; @@ -32,7 +34,7 @@ public class MapEditor{ public Block drawBlock = Blocks.stone; public Team drawTeam = Team.blue; - public ObjectMap getTags(){ + public StringMap getTags(){ return tags; } @@ -40,7 +42,7 @@ public class MapEditor{ reset(); loading = true; - tiles = createTiles(width, height); + createTiles(width, height); renderer.resize(width(), height()); loading = false; } @@ -49,30 +51,25 @@ public class MapEditor{ reset(); loading = true; - tiles = createTiles(map.width, map.height); + //TODO redundant and does nothing since tiles are overwritten + createTiles(map.width, map.height); tags.putAll(map.tags); - MapIO.readTiles(map, tiles); + //TODO this actually creates the tiles, which are not editor tiles + MapIO.loadMap(map); checkLinkedTiles(); renderer.resize(width(), height()); loading = false; } - public void beginEdit(Tile[][] tiles){ - reset(); - - this.tiles = tiles; - checkLinkedTiles(); - renderer.resize(width(), height()); - } - //adds missing blockparts public void checkLinkedTiles(){ + Tile[][] tiles = world.getTiles(); + //clear block parts first for(int x = 0; x < width(); x++){ for(int y = 0; y < height(); y++){ - if(tiles[x][y].block() == Blocks.part){ + if(tiles[x][y].block() instanceof BlockPart){ tiles[x][y].setBlock(Blocks.air); - tiles[x][y].setLinkByte((byte)0); } } } @@ -80,22 +77,8 @@ public class MapEditor{ //set up missing blockparts for(int x = 0; x < width(); x++){ for(int y = 0; y < height(); y++){ - Block drawBlock = tiles[x][y].block(); - if(drawBlock.isMultiblock()){ - int offsetx = -(drawBlock.size - 1) / 2; - int offsety = -(drawBlock.size - 1) / 2; - for(int dx = 0; dx < drawBlock.size; dx++){ - for(int dy = 0; dy < drawBlock.size; dy++){ - int worldx = dx + offsetx + x; - int worldy = dy + offsety + y; - - if(Structs.inBounds(worldx, worldy, width(), height()) && !(dx + offsetx == 0 && dy + offsety == 0)){ - Tile tile = tiles[worldx][worldy]; - tile.setBlock(Blocks.part); - tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8))); - } - } - } + if(tiles[x][y].block().isMultiblock()){ + world.setBlock(tiles[x][y], tiles[x][y].block(), tiles[x][y].getTeam()); } } } @@ -108,15 +91,14 @@ public class MapEditor{ } /** Creates a 2-D array of EditorTiles with stone as the floor block. */ - public Tile[][] createTiles(int width, int height){ - tiles = new Tile[width][height]; + private void createTiles(int width, int height){ + Tile[][] tiles = world.createTiles(width, height); for(int x = 0; x < width; x++){ for(int y = 0; y < height; y++){ - tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0); + tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0); } } - return tiles; } public Map createMap(FileHandle file){ @@ -131,40 +113,19 @@ public class MapEditor{ } public Tile[][] tiles(){ - return tiles; + return world.getTiles(); } public Tile tile(int x, int y){ - return tiles[x][y]; + return world.rawTile(x, y); } public int width(){ - return tiles.length; + return world.width(); } public int height(){ - return tiles[0].length; - } - - public void updateLinks(Block block, int x, int y){ - int offsetx = -(block.size - 1) / 2; - int offsety = -(block.size - 1) / 2; - - for(int dx = 0; dx < block.size; dx++){ - for(int dy = 0; dy < block.size; dy++){ - int worldx = dx + offsetx + x; - int worldy = dy + offsety + y; - - if(Structs.inBounds(worldx, worldy, width(), height())){ - Tile tile = tiles[worldx][worldy]; - - if(!(worldx == x && worldy == y)){ - tile.setBlock(Blocks.part); - tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8))); - } - } - } - } + return world.height(); } public void draw(int x, int y, boolean paint){ @@ -177,45 +138,34 @@ public class MapEditor{ public void draw(int x, int y, boolean paint, Block drawBlock, double chance){ boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air; + Tile[][] tiles = world.getTiles(); if(drawBlock.isMultiblock()){ - x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1); y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1); int offsetx = -(drawBlock.size - 1) / 2; int offsety = -(drawBlock.size - 1) / 2; - for(int i = 0; i < 2; i++){ - for(int dx = 0; dx < drawBlock.size; dx++){ - for(int dy = 0; dy < drawBlock.size; dy++){ - int worldx = dx + offsetx + x; - int worldy = dy + offsety + y; + for(int dx = 0; dx < drawBlock.size; dx++){ + for(int dy = 0; dy < drawBlock.size; dy++){ + int worldx = dx + offsetx + x; + int worldy = dy + offsety + y; - if(Structs.inBounds(worldx, worldy, width(), height())){ - Tile tile = tiles[worldx][worldy]; + if(Structs.inBounds(worldx, worldy, width(), height())){ + Tile tile = tiles[worldx][worldy]; - if(i == 1){ - tile.setBlock(Blocks.part); - tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8))); - }else{ - byte link = tile.getLinkByte(); - Block block = tile.block(); + Block block = tile.block(); - if(link != 0){ - removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8)); - }else if(block.isMultiblock()){ - removeLinked(worldx, worldy); - } - } + //bail out if there's anything blocking the way + if(block.isMultiblock() || block instanceof BlockPart){ + return; } } } } - Tile tile = tiles[x][y]; - tile.setBlock(drawBlock); - tile.setTeam(drawTeam); + world.setBlock(tiles[x][y], drawBlock, drawTeam); }else{ for(int rx = -brushSize; rx <= brushSize; rx++){ for(int ry = -brushSize; ry <= brushSize; ry++){ @@ -228,14 +178,8 @@ public class MapEditor{ Tile tile = tiles[wx][wy]; - if(!isfloor){ - byte link = tile.getLinkByte(); - - if(tile.block().isMultiblock()){ - removeLinked(wx, wy); - }else if(link != 0 && tiles[wx][wy].block() == Blocks.part){ - removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8)); - } + if(!isfloor && (tile.isLinked() || tile.block().isMultiblock())){ + world.removeBlock(tile.link()); } if(isfloor){ @@ -255,22 +199,6 @@ public class MapEditor{ } } - private void removeLinked(int x, int y){ - Block block = tiles[x][y].block(); - - int offsetx = -(block.size - 1) / 2; - int offsety = -(block.size - 1) / 2; - for(int dx = 0; dx < block.size; dx++){ - for(int dy = 0; dy < block.size; dy++){ - int worldx = x + dx + offsetx, worldy = y + dy + offsety; - if(Structs.inBounds(worldx, worldy, width(), height())){ - tiles[worldx][worldy].setTeam(Team.none); - tiles[worldx][worldy].setBlock(Blocks.air); - } - } - } - } - public MapRenderer renderer(){ return renderer; } @@ -278,11 +206,11 @@ public class MapEditor{ public void resize(int width, int height){ clearOp(); - Tile[][] previous = tiles; + Tile[][] previous = world.getTiles(); int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2; loading = true; - tiles = new Tile[width][height]; + Tile[][] tiles = world.createTiles(width, height); for(int x = 0; x < width; x++){ for(int y = 0; y < height; y++){ int px = offsetX + x, py = offsetY + y; @@ -291,7 +219,7 @@ public class MapEditor{ tiles[x][y].x = (short)x; tiles[x][y].y = (short)y; }else{ - tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0); + tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0); } } } diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java index ef7785e102..905390631c 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -91,7 +91,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> { try{ //TODO what if it's an image? users should be warned for their stupidity - editor.beginEdit(MapIO.readMap(file, true)); + editor.beginEdit(MapIO.createMap(file, true)); }catch(Exception e){ ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false))); Log.err(e); @@ -216,7 +216,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ if(map != null && !map.custom){ ui.showError("$editor.save.overwrite"); }else{ - world.maps.saveMap(editor.getTags(), editor.tiles()); + world.maps.saveMap(editor.getTags()); ui.showInfoFade("$editor.saved"); } } diff --git a/core/src/io/anuke/mindustry/io/MapIO.java b/core/src/io/anuke/mindustry/io/MapIO.java index 2ba9e911dc..59c9aa3f25 100644 --- a/core/src/io/anuke/mindustry/io/MapIO.java +++ b/core/src/io/anuke/mindustry/io/MapIO.java @@ -1,18 +1,23 @@ package io.anuke.mindustry.io; +import io.anuke.arc.collection.StringMap; import io.anuke.arc.files.FileHandle; import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Pixmap; import io.anuke.arc.graphics.Pixmap.Format; import io.anuke.arc.util.Time; +import io.anuke.arc.util.io.CounterInputStream; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.game.Version; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.world.*; import io.anuke.mindustry.world.blocks.Floor; -import java.io.IOException; -import java.io.InputStream; +import java.io.*; +import java.util.zip.InflaterInputStream; + +import static io.anuke.mindustry.Vars.bufferSize; /** Reads and writes map files. */ public class MapIO{ @@ -31,6 +36,21 @@ public class MapIO{ } } + public static Map createMap(FileHandle file, boolean custom) throws IOException{ + try(InputStream is = new InflaterInputStream(file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){ + SaveIO.readHeader(stream); + int version = stream.readInt(); + SaveVersion ver = SaveIO.getSaveWriter(version); + StringMap tags = new StringMap(); + ver.region("meta", stream, counter, in -> tags.putAll(ver.readStringMap(in))); + return new Map(file, tags.getInt("width"), tags.getInt("height"), tags, custom, version, Version.build); + } + } + + public static void loadMap(Map map){ + SaveIO.load(map.file); + } + public static Pixmap generatePreview(Map map) throws IOException{ Time.mark(); Pixmap floors = new Pixmap(map.width, map.height, Format.RGBA8888); diff --git a/core/src/io/anuke/mindustry/io/SaveFileReader.java b/core/src/io/anuke/mindustry/io/SaveFileReader.java index dd1dc6ffd1..437227b16a 100644 --- a/core/src/io/anuke/mindustry/io/SaveFileReader.java +++ b/core/src/io/anuke/mindustry/io/SaveFileReader.java @@ -67,8 +67,12 @@ public abstract class SaveFileReader{ return length; } - /** Skip a chunk completely. */ - public void skipChunk(DataInput input, boolean isByte) throws IOException{ + public void skipRegion(DataInput input) throws IOException{ + skipRegion(input, false); + } + + /** Skip a region completely. */ + public void skipRegion(DataInput input, boolean isByte) throws IOException{ int length = readChunk(input, isByte, t -> {}); int skipped = input.skipBytes(length); if(length != skipped){ diff --git a/core/src/io/anuke/mindustry/io/SaveIO.java b/core/src/io/anuke/mindustry/io/SaveIO.java index 48f82cdb40..1dd209b6e0 100644 --- a/core/src/io/anuke/mindustry/io/SaveIO.java +++ b/core/src/io/anuke/mindustry/io/SaveIO.java @@ -30,6 +30,10 @@ public class SaveIO{ return versionArray.peek(); } + public static SaveVersion getSaveWriter(int version){ + return versions.get(version); + } + public static void saveToSlot(int slot){ FileHandle file = fileFor(slot); boolean exists = file.exists(); diff --git a/core/src/io/anuke/mindustry/io/SaveVersion.java b/core/src/io/anuke/mindustry/io/SaveVersion.java index 782b228fcc..3ea724ddf9 100644 --- a/core/src/io/anuke/mindustry/io/SaveVersion.java +++ b/core/src/io/anuke/mindustry/io/SaveVersion.java @@ -53,7 +53,9 @@ public abstract class SaveVersion extends SaveFileReader{ "wave", state.wave, "wavetime", state.wavetime, "stats", Serialization.writeStatsJson(state.stats), - "rules", Serialization.writeRulesJson(state.rules) + "rules", Serialization.writeRulesJson(state.rules), + "width", world.width(), + "height", world.height() )); } diff --git a/core/src/io/anuke/mindustry/maps/Maps.java b/core/src/io/anuke/mindustry/maps/Maps.java index 015b994174..1db54badb2 100644 --- a/core/src/io/anuke/mindustry/maps/Maps.java +++ b/core/src/io/anuke/mindustry/maps/Maps.java @@ -52,7 +52,7 @@ public class Maps implements Disposable{ FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension); try{ - return MapIO.readMap(file, false); + return MapIO.createMap(file, false); }catch(IOException e){ throw new RuntimeException(e); } @@ -81,13 +81,12 @@ public class Maps implements Disposable{ * Save a custom map to the directory. This updates all values and stored data necessary. * The tags are copied to prevent mutation later. */ - public void saveMap(ObjectMap baseTags, Tile[][] data){ + public void saveMap(ObjectMap baseTags){ try{ ObjectMap tags = new ObjectMap<>(baseTags); String name = tags.get("name"); if(name == null) throw new IllegalArgumentException("Can't save a map with no name. How did this happen?"); - //FileHandle file = customMapDirectory.child(name + "." + mapExtension); FileHandle file; //find map with the same exact display name @@ -106,7 +105,7 @@ public class Maps implements Disposable{ } //create map, write it, etc etc etc - Map map = new Map(file, data.length, data[0].length, tags, true); + Map map = new Map(file, world.width(), world.height(), tags, true); MapIO.writeMap(file, map, data); if(!headless){ @@ -171,7 +170,7 @@ public class Maps implements Disposable{ } private void loadMap(FileHandle file, boolean custom) throws IOException{ - Map map = MapIO.readMap(file, custom); + Map map = MapIO.createMap(file, custom); if(map.name() == null){ throw new IOException("Map name cannot be empty! File: " + file); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java index 7e4015f0fd..1205146ccd 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java @@ -24,7 +24,7 @@ public class MapsDialog extends FloatingDialog{ buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> { Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> { try{ - Map map = MapIO.readMap(file, true); + Map map = MapIO.createMap(file, true); String name = map.tags.get("name"); if(name == null){ ui.showError("$editor.errorname"); diff --git a/settings.gradle b/settings.gradle index 384df3c359..40cab3a003 100644 --- a/settings.gradle +++ b/settings.gradle @@ -27,6 +27,7 @@ if(!hasProperty("release")){ use(':Arc:extensions:freetype', '../Arc/extensions/freetype') use(':Arc:extensions:recorder', '../Arc/extensions/recorder') use(':Arc:extensions:arcnet', '../Arc/extensions/arcnet') + use(':Arc:extensions:packer', '../Arc/extensions/packer') use(':Arc:backends', '../Arc/backends') use(':Arc:backends:backend-lwjgl3', '../Arc/backends/backend-lwjgl3') use(':Arc:backends:backend-android', '../Arc/backends/backend-android')