diff --git a/core/assets/bundles/bundle.properties b/core/assets/bundles/bundle.properties index 81b71ebf88..7379296bf3 100644 --- a/core/assets/bundles/bundle.properties +++ b/core/assets/bundles/bundle.properties @@ -197,8 +197,8 @@ editor.description = Description: editor.name = Name: editor.teams = Teams editor.elevation = Elevation -editor.errorimageload = Error loading file:\n[accent]{0} -editor.errorimagesave = Error saving file:\n[accent]{0} +editor.errorload = Error loading file:\n[accent]{0} +editor.errorsave = Error saving file:\n[accent]{0} editor.generate = Generate editor.resize = Resize editor.loadmap = Load Map diff --git a/core/src/io/anuke/mindustry/editor/DrawOperation.java b/core/src/io/anuke/mindustry/editor/DrawOperation.java index bfa6b25673..ef73a00ad0 100755 --- a/core/src/io/anuke/mindustry/editor/DrawOperation.java +++ b/core/src/io/anuke/mindustry/editor/DrawOperation.java @@ -30,13 +30,15 @@ public class DrawOperation{ class TileOpStruct{ short x; short y; - /** - * 0: floor - * 1: block - * 2: rotation - */ byte type; byte from; byte to; } + + public enum OpType{ + floor, + block, + rotation, + team + } } diff --git a/core/src/io/anuke/mindustry/editor/EditorTile.java b/core/src/io/anuke/mindustry/editor/EditorTile.java new file mode 100644 index 0000000000..8659969fb2 --- /dev/null +++ b/core/src/io/anuke/mindustry/editor/EditorTile.java @@ -0,0 +1,53 @@ +package io.anuke.mindustry.editor; + +import io.anuke.mindustry.editor.DrawOperation.OpType; +import io.anuke.mindustry.game.Team; +import io.anuke.mindustry.gen.TileOp; +import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; +import io.anuke.mindustry.world.blocks.Floor; + +import static io.anuke.mindustry.Vars.ui; + +public class EditorTile extends Tile{ + + public EditorTile(int x, int y, byte floor, byte wall){ + super(x, y, floor, wall); + } + + @Override + public void setFloor(Floor type){ + Block previous = floor(); + if(previous == type) return; + super.setFloor(type); + op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous.id, type.id)); + } + + @Override + public void setBlock(Block type){ + Block previous = block(); + if(previous == type) return; + super.setBlock(type); + op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous.id, type.id)); + } + + @Override + public void setTeam(Team team){ + byte previous = getTeamID(); + if(previous == team.ordinal()) return; + super.setTeam(team); + op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous, (byte)team.ordinal())); + } + + @Override + public void setRotation(byte rotation){ + byte previous = getRotation(); + if(previous == rotation) return; + super.setRotation(rotation); + op(TileOp.get(x, y, (byte)OpType.floor.ordinal(), previous, rotation)); + } + + private static void op(long op){ + ui.editor.editor.addTileOp(op); + } +} diff --git a/core/src/io/anuke/mindustry/editor/EditorTool.java b/core/src/io/anuke/mindustry/editor/EditorTool.java index c15b7d9e64..690e193474 100644 --- a/core/src/io/anuke/mindustry/editor/EditorTool.java +++ b/core/src/io/anuke/mindustry/editor/EditorTool.java @@ -8,29 +8,32 @@ import io.anuke.arc.util.Pack; import io.anuke.arc.util.Structs; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.world.Block; -import io.anuke.mindustry.world.blocks.BlockPart; +import io.anuke.mindustry.world.Pos; +import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.Floor; -import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.ui; +import static io.anuke.mindustry.Vars.world; public enum EditorTool{ pick{ public void touched(MapEditor editor, int x, int y){ if(!Structs.inBounds(x, y, world.width(), world.height())) return; - byte bf = editor.getMap().read(x, y, DataPosition.floor); - byte bw = editor.getMap().read(x, y, DataPosition.wall); - byte link = editor.getMap().read(x, y, DataPosition.link); + Tile tile = editor.tile(x, y); + + Block floor = tile.floor(), block = tile.block(); + byte link = tile.getLinkByte(); if(link != 0){ x -= (Pack.leftByte(link) - 8); y -= (Pack.rightByte(link) - 8); - bf = editor.getMap().read(x, y, DataPosition.floor); - bw = editor.getMap().read(x, y, DataPosition.wall); + + tile = editor.tile(x, y); + block = tile.block(); } - Block block = content.block(bw == 0 ? bf : bw); - editor.setDrawBlock(block); + editor.drawBlock = block == Blocks.air ? floor : block; ui.editor.updateSelectedBlock(); } }, @@ -64,7 +67,7 @@ public enum EditorTool{ @Override public void touched(MapEditor editor, int x, int y){ - editor.draw(x, y, isPaint(), editor.getDrawBlock(), 0.012); + editor.draw(x, y, isPaint(), editor.drawBlock, 0.012); } }, line{ @@ -78,56 +81,57 @@ public enum EditorTool{ } IntArray stack = new IntArray(); - int width; - byte be, dest; - boolean floor; - MapTileData data; + Block dest; + boolean isfloor; + MapEditor data; public void touched(MapEditor editor, int x, int y){ if(!Structs.inBounds(x, y, world.width(), world.height())) return; + Tile tile = editor.tile(x, y); - if(editor.getDrawBlock().isMultiblock()){ + if(editor.drawBlock.isMultiblock()){ //don't fill multiblocks, thanks pencil.touched(editor, x, y); return; } - data = editor.getMap(); + data = editor; + isfloor = editor.drawBlock instanceof Floor; - floor = editor.getDrawBlock() instanceof Floor; + Block floor = tile.floor(); + Block block = tile.block(); + boolean synth = editor.drawBlock.synthetic(); - byte bf = data.read(x, y, DataPosition.floor); - byte bw = data.read(x, y, DataPosition.wall); - boolean synth = editor.getDrawBlock().synthetic(); - byte brt = Pack.byteByte((byte) editor.getDrawRotation(), (byte) editor.getDrawTeam().ordinal()); + dest = isfloor ? floor : block; + Block draw = editor.drawBlock; - dest = floor ? bf : bw; - byte draw = editor.getDrawBlock().id; - - if(dest == draw || content.block(bw) instanceof BlockPart || content.block(bw).isMultiblock()){ + if(dest == draw || block == Blocks.part || block.isMultiblock()){ return; } - width = world.width(); + int width = world.width(); int height = world.height(); IntPositionConsumer writer = (px, py) -> { - TileDataMarker prev = editor.getPrev(px, py, false); + Tile write = editor.tile(px, py); - if(floor){ - data.write(px, py, DataPosition.floor, draw); + if(isfloor){ + write.setFloor((Floor)draw); }else{ - data.write(px, py, DataPosition.wall, draw); + write.setBlock(draw); } if(synth){ - data.write(px, py, DataPosition.rotationTeam, brt); + write.setTeam(editor.drawTeam); } - editor.onWrite(px, py, prev); + if(draw.rotate){ + write.setRotation((byte)editor.rotation); + } }; if(isAlt()){ + //fill all of the same type regardless of borders for(int cx = 0; cx < width; cx++){ for(int cy = 0; cy < height; cy++){ if(eq(cx, cy)){ @@ -136,28 +140,28 @@ public enum EditorTool{ } } }else if(isAlt2()){ + //fill all teams. for(int cx = 0; cx < width; cx++){ for(int cy = 0; cy < height; cy++){ - byte w = data.read(cx, cy, DataPosition.wall); - if(content.block(w).synthetic()){ - TileDataMarker prev = editor.getPrev(cx, cy, false); - data.write(cx, cy, DataPosition.rotationTeam, (byte)editor.getDrawTeam().ordinal()); - editor.onWrite(cx, cy, prev); + Tile write = editor.tile(cx, cy); + if(write.block().synthetic()){ + write.setTeam(editor.drawTeam); } } } }else{ + //normal fill int x1; boolean spanAbove, spanBelow; stack.clear(); - stack.add(asi(x, y)); + stack.add(Pos.get(x, y)); while(stack.size > 0){ int popped = stack.pop(); - x = popped % width; - y = popped / width; + x = Pos.x(popped); + y = Pos.y(popped); x1 = x; while(x1 >= 0 && eq(x1, y)) x1--; @@ -167,14 +171,14 @@ public enum EditorTool{ writer.accept(x1, y); if(!spanAbove && y > 0 && eq(x1, y - 1)){ - stack.add(asi(x1, y - 1)); + stack.add(Pos.get(x1, y - 1)); spanAbove = true; - }else if(spanAbove && y > 0 && eq(x1, y - 1)){ + }else if(spanAbove && eq(x1, y - 1)){ spanAbove = false; } if(!spanBelow && y < height - 1 && eq(x1, y + 1)){ - stack.add(asi(x1, y + 1)); + stack.add(Pos.get(x1, y + 1)); spanBelow = true; }else if(spanBelow && y < height - 1 && eq(x1, y + 1)){ spanBelow = false; @@ -186,15 +190,9 @@ public enum EditorTool{ } boolean eq(int px, int py){ - byte nbf = data.read(px, py, DataPosition.floor); - byte nbw = data.read(px, py, DataPosition.wall); - byte nbe = data.read(px, py, DataPosition.elevation); + Tile tile = data.tile(px, py); - return (floor ? nbf : nbw) == dest && nbe == be; - } - - int asi(int x, int y){ - return x + y * width; + return (isfloor ? tile.floor() : tile.block()) == dest; } }, zoom; diff --git a/core/src/io/anuke/mindustry/editor/MapEditor.java b/core/src/io/anuke/mindustry/editor/MapEditor.java index 9a2b2d427b..1d03393992 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditor.java +++ b/core/src/io/anuke/mindustry/editor/MapEditor.java @@ -1,19 +1,21 @@ package io.anuke.mindustry.editor; -import io.anuke.arc.collection.GridBits; import io.anuke.arc.collection.ObjectMap; +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.Vars; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.gen.TileOp; +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.Floor; -import static io.anuke.mindustry.Vars.content; +import java.io.IOException; + import static io.anuke.mindustry.Vars.world; public class MapEditor{ @@ -24,8 +26,8 @@ public class MapEditor{ private Tile[][] tiles; private OperationStack stack = new OperationStack(); - private DrawOperation op; - private GridBits used; + private DrawOperation currentOp; + private boolean loading; public int brushSize = 1; public int rotation; @@ -36,27 +38,70 @@ public class MapEditor{ return tags; } - public void beginEdit(Tile[][] map, ObjectMap tags, boolean clear){ + public void beginEdit(int width, int height){ + reset(); - this.brushSize = 1; - this.tags = tags; + loading = true; + tiles = createTiles(width, height); + renderer.resize(width(), height()); + loading = false; + } - if(clear){ - for(int x = 0; x < map.length; x++){ - for(int y = 0; y < map[0].length; y++){ - map[x][y].setFloor((Floor)Blocks.stone); - } + public void beginEdit(Map map) throws IOException{ + reset(); + + loading = true; + tiles = createTiles(map.width, map.height); + tags.putAll(map.tags); + MapIO.readTiles(map, tiles); + renderer.resize(width(), height()); + loading = false; + } + + public void beginEdit(Tile[][] tiles) throws IOException{ + reset(); + + this.tiles = tiles; + renderer.resize(width(), height()); + } + + public void load(Runnable r){ + loading = true; + r.run(); + loading = false; + } + + /**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]; + + 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); } } + return tiles; + } + public Map createMap(FileHandle file){ + return new Map(file, width(), height(), new ObjectMap<>(tags), true); + } + + private void reset(){ + clearOp(); + brushSize = 1; drawBlock = Blocks.stone; - renderer.resize(map.length, map[0].length); + tags = new ObjectMap<>(); } public Tile[][] tiles(){ return tiles; } + public Tile tile(int x, int y){ + return tiles[x][y]; + } + public int width(){ return tiles.length; } @@ -74,9 +119,7 @@ public class MapEditor{ } public void draw(int x, int y, boolean paint, Block drawBlock, double chance){ - byte writeID = drawBlock.id; - byte partID = Blocks.part.id; - byte rotationTeam = Pack.byteByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0); + //byte rotationTeam = Pack.byteByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0); boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air; @@ -94,52 +137,44 @@ public class MapEditor{ int worldy = dy + offsety + y; if(Structs.inBounds(worldx, worldy, world.width(), world.height())){ - TileDataMarker prev = getPrev(worldx, worldy, false); + Tile tile = tiles[worldx][worldy]; if(i == 1){ - map.write(worldx, worldy, DataPosition.wall, partID); - map.write(worldx, worldy, DataPosition.rotationTeam, rotationTeam); - map.write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8))); + tile.setLinked((byte)(dx + offsetx), (byte)(dy + offsety)); }else{ - byte link = map.read(worldx, worldy, DataPosition.link); - byte block = map.read(worldx, worldy, DataPosition.wall); + byte link = tile.getLinkByte(); + Block block = tile.block(); if(link != 0){ removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8)); - }else if(content.block(block).isMultiblock()){ + }else if(block.isMultiblock()){ removeLinked(worldx, worldy); } } - - onWrite(worldx, worldy, prev); } } } } - TileDataMarker prev = getPrev(x, y, false); - - map.write(x, y, DataPosition.wall, writeID); - map.write(x, y, DataPosition.link, (byte) 0); - map.write(x, y, DataPosition.rotationTeam, rotationTeam); - - onWrite(x, y, prev); + Tile tile = tiles[x][y]; + tile.setBlock(drawBlock); + tile.setTeam(drawTeam); }else{ for(int rx = -brushSize; rx <= brushSize; rx++){ for(int ry = -brushSize; ry <= brushSize; ry++){ if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){ int wx = x + rx, wy = y + ry; - if(wx < 0 || wy < 0 || wx >= map.width() || wy >= map.height() || (paint && !isfloor && content.block(map.read(wx, wy, DataPosition.wall)) == Blocks.air)){ + if(wx < 0 || wy < 0 || wx >= width() || wy >= height() || (paint && !isfloor && tiles[wx][wy].block() == Blocks.air)){ continue; } - TileDataMarker prev = getPrev(wx, wy, true); + Tile tile = tiles[wx][wy]; if(!isfloor){ - byte link = map.read(wx, wy, DataPosition.link); + byte link = tile.getLinkByte(); - if(content.block(map.read(wx, wy, DataPosition.wall)).isMultiblock()){ + if(tile.block().isMultiblock()){ removeLinked(wx, wy); }else if(link != 0){ removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8)); @@ -147,14 +182,13 @@ public class MapEditor{ } if(isfloor){ - map.write(wx, wy, DataPosition.floor, writeID); + tile.setFloor((Floor)drawBlock); }else{ - map.write(wx, wy, DataPosition.wall, writeID); - map.write(wx, wy, DataPosition.link, (byte) 0); - map.write(wx, wy, DataPosition.rotationTeam, rotationTeam); + tile.setBlock(drawBlock); + if(drawBlock.synthetic()){ + tile.setTeam(drawTeam); + } } - - onWrite(x + rx, y + ry, prev); } } } @@ -162,62 +196,31 @@ public class MapEditor{ } private void removeLinked(int x, int y){ - Block block = content.block(map.read(x, y, DataPosition.wall)); + 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, map.width(), map.height())){ - TileDataMarker prev = getPrev(worldx, worldy, false); - - map.write(worldx, worldy, DataPosition.link, (byte) 0); - map.write(worldx, worldy, DataPosition.rotationTeam, (byte) 0); - map.write(worldx, worldy, DataPosition.wall, (byte) 0); - - onWrite(worldx, worldy, prev); + if(Structs.inBounds(worldx, worldy, width(), height())){ + tiles[worldx][worldy].setTeam(Team.none); + tiles[worldx][worldy].setBlock(Blocks.air); } } } } - boolean checkDupes(int x, int y){ - return Vars.ui.editor.getView().checkForDuplicates((short) x, (short) y); - } - - void onWrite(int x, int y, TileDataMarker previous){ - if(previous == null){ - renderer.updatePoint(x, y); - return; - } - - TileDataMarker current = map.new TileDataMarker(); - map.position(x, y); - map.read(current); - - Vars.ui.editor.getView().addTileOp(new TileOperation((short) x, (short) y, previous, current)); - renderer.updatePoint(x, y); - } - - TileDataMarker getPrev(int x, int y, boolean checkDupes){ - if(checkDupes && checkDupes(x, y)){ - return null; - }else{ - TileDataMarker marker = map.newDataMarker(); - map.position(x, y); - map.read(marker); - return marker; - } - } - public MapRenderer renderer(){ return renderer; } public void resize(int width, int height){ + clearOp(); + Tile[][] previous = tiles; int offsetX = -(width - width())/2, offsetY = -(height - height())/2; + loading = true; tiles = new Tile[width][height]; for(int x = 0; x < width; x++){ @@ -228,17 +231,17 @@ public class MapEditor{ tiles[x][y].x = (short)x; tiles[x][y].y = (short)y; }else{ - tiles[x][y] = new Tile(x, y, Blocks.stone.id, (byte)0); + tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0); } } } + renderer.resize(width, height); + loading = false; } - public void changeFloor(int x, int y, Block to){ - Block from = tiles[x][y].floor(); - tiles[x][y].setFloor((Floor)to); - addTileOp(TileOp.get((short)x, (short)y, (byte)0, from.id, to.id)); + public void clearOp(){ + stack.clear(); } public void undo(){ @@ -253,16 +256,26 @@ public class MapEditor{ } } - public void addTileOp(long data){ - used.set(TileOp.x(data), TileOp.y(data)); - op.addOperation(data); + public boolean canUndo(){ + return stack.canUndo(); } - public boolean checkUsed(short x, short y){ - if(used == null || used.width() != width() || used.height() != height()){ - used = new GridBits(world.width(), world.height()); - } + public boolean canRedo(){ + return stack.canRedo(); + } - return used.get(x, y); + public void flushOp(){ + if(currentOp == null || currentOp.isEmpty()) return; + stack.add(currentOp); + currentOp = null; + } + + public void addTileOp(long data){ + if(loading) return; + + if(currentOp == null) currentOp = new DrawOperation(); + currentOp.addOperation(data); + + renderer.updatePoint(TileOp.x(data), TileOp.y(data)); } } diff --git a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java index 6ff2b6cbff..5f4cda2fc6 100644 --- a/core/src/io/anuke/mindustry/editor/MapEditorDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapEditorDialog.java @@ -2,7 +2,6 @@ package io.anuke.mindustry.editor; import io.anuke.arc.Core; import io.anuke.arc.collection.Array; -import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.files.FileHandle; import io.anuke.arc.function.Consumer; import io.anuke.arc.graphics.Color; @@ -26,16 +25,14 @@ import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.ui.dialogs.FloatingDialog; import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block.Icon; +import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.OreBlock; import io.anuke.mindustry.world.blocks.storage.CoreBlock; -import java.io.DataInputStream; -import java.io.InputStream; - import static io.anuke.mindustry.Vars.*; public class MapEditorDialog extends Dialog implements Disposable{ - private MapEditor editor; + public final MapEditor editor; private MapView view; private MapInfoDialog infoDialog; private MapLoadDialog loadDialog; @@ -87,17 +84,12 @@ public class MapEditorDialog extends Dialog implements Disposable{ createDialog("$editor.import", "$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show, "$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable) () -> - Platform.instance.showFileChooser("$loadimage", "Map Files", file -> ui.loadAnd(() -> { + Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> { try{ - DataInputStream stream = new DataInputStream(file.read()); - - MapMeta meta = MapIO.readMapMeta(stream); - MapTileData data = MapIO.readTileData(stream, meta, false); - - editor.beginEdit(data, meta.tags, false); - view.clearStack(); + //TODO what if it's an image? users should be warned for their stupidity + editor.beginEdit(MapIO.readMap(file, true)); }catch(Exception e){ - ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false))); Log.err(e); } }), true, mapExtension), @@ -106,39 +98,32 @@ public class MapEditorDialog extends Dialog implements Disposable{ Platform.instance.showFileChooser("$loadimage", "Image Files", file -> ui.loadAnd(() -> { try{ - MapTileData data = MapIO.readLegacyPixmap(new Pixmap(file)); - - editor.beginEdit(data, editor.getTags(), false); - view.clearStack(); + Pixmap pixmap = new Pixmap(file); + Tile[][] tiles = editor.createTiles(pixmap.getWidth(), pixmap.getHeight()); + editor.load(() -> MapIO.readLegacyPixmap(pixmap, tiles)); + editor.beginEdit(tiles); }catch (Exception e){ - ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false))); Log.err(e); } - }), true, "png") - )); + }), true, "png"))); - t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> createDialog("$editor.export", - "$editor.exportfile", "$editor.exportfile.description", "icon-file", (Runnable) () -> - Platform.instance.showFileChooser("$saveimage", "Map Files", file -> { + t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> + Platform.instance.showFileChooser("$editor.savemap", "Map Files", file -> { file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); FileHandle result = file; ui.loadAnd(() -> { - try{ if(!editor.getTags().containsKey("name")){ editor.getTags().put("name", result.nameWithoutExtension()); } - MapIO.writeMap(result.write(false), editor.getTags(), editor.getMap()); + MapIO.writeMap(result, editor.createMap(result), editor.tiles()); }catch(Exception e){ - ui.showError(Core.bundle.format("editor.errorimagesave", Strings.parseException(e, false))); + ui.showError(Core.bundle.format("editor.errorsave", Strings.parseException(e, false))); Log.err(e); } }); - }, false, mapExtension))); - - t.row(); - - t.row(); + }, false, mapExtension)); }); menu.cont.row(); @@ -149,24 +134,19 @@ public class MapEditorDialog extends Dialog implements Disposable{ }).padTop(-5).size(swidth * 2f + 10, 60f); resizeDialog = new MapResizeDialog(editor, (x, y) -> { - if(!(world.width() == x && world.height() == y)){ + if(!(editor.width() == x && editor.height() == y)){ ui.loadAnd(() -> { editor.resize(x, y); - view.clearStack(); }); } }); loadDialog = new MapLoadDialog(map -> ui.loadAnd(() -> { - try(DataInputStream stream = new DataInputStream(map.stream.get())){ - MapMeta meta = MapIO.readMapMeta(stream); - MapTileData data = MapIO.readTileData(stream, meta, false); - - editor.beginEdit(data, meta.tags, false); - view.clearStack(); + try{ + editor.beginEdit(map); }catch(Exception e){ - ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false))); Log.err(e); } })); @@ -198,10 +178,10 @@ public class MapEditorDialog extends Dialog implements Disposable{ shown(() -> { saved = true; Platform.instance.beginForceLandscape(); - view.clearStack(); + editor.clearOp(); Core.scene.setScrollFocus(view); if(!shownWithMap){ - editor.beginEdit(new MapTileData(200, 200), new ObjectMap<>(), true); + editor.beginEdit(200, 200); } shownWithMap = false; @@ -209,6 +189,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ }); hidden(() -> { + editor.clearOp(); Platform.instance.updateRPC(); Platform.instance.endForceLandscape(); }); @@ -223,13 +204,14 @@ public class MapEditorDialog extends Dialog implements Disposable{ String name = editor.getTags().get("name", ""); if(name.isEmpty()){ - ui.showError("$editor.save.noname"); + infoDialog.show(); + Core.app.post(() -> ui.showError("$editor.save.noname")); }else{ - Map map = world.maps.getByName(name); + Map map = world.maps.all().find(m -> m.name().equals(name)); if(map != null && !map.custom){ ui.showError("$editor.save.overwrite"); }else{ - world.maps.saveMap(name, editor.getMap(), editor.getTags()); + world.maps.saveMap(editor.getTags(), editor.tiles()); ui.showInfoFade("$editor.saved"); } } @@ -294,12 +276,13 @@ public class MapEditorDialog extends Dialog implements Disposable{ public void beginEditMap(FileHandle file){ ui.loadAnd(() -> { try{ + Map map = MapIO.readMap(file, true); shownWithMap = true; - editor.beginEdit(MapIO.readTiles(file)); + editor.beginEdit(map); show(); }catch(Exception e){ Log.err(e); - ui.showError(Core.bundle.format("editor.errorimageload", Strings.parseException(e, false))); + ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false))); } }); } @@ -364,15 +347,15 @@ public class MapEditorDialog extends Dialog implements Disposable{ tools.row(); - ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, () -> view.undo()).get(); - ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, () -> view.redo()).get(); + ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, editor::undo).get(); + ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, editor::redo).get(); addTool.accept(EditorTool.pick); tools.row(); - undo.setDisabled(() -> !view.getStack().canUndo()); - redo.setDisabled(() -> !view.getStack().canRedo()); + undo.setDisabled(() -> !editor.canUndo()); + redo.setDisabled(() -> !editor.canRedo()); undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE)); redo.update(() -> redo.getImage().setColor(redo.isDisabled() ? Color.GRAY : Color.WHITE)); @@ -387,9 +370,9 @@ public class MapEditorDialog extends Dialog implements Disposable{ addTool.accept(EditorTool.fill); addTool.accept(EditorTool.spray); - ImageButton rotate = tools.addImageButton("icon-arrow-16", "clear", 16 * 2f, () -> editor.setDrawRotation((editor.getDrawRotation() + 1) % 4)).get(); + ImageButton rotate = tools.addImageButton("icon-arrow-16", "clear", 16 * 2f, () -> editor.rotation = (editor.rotation + 1) % 4).get(); rotate.getImage().update(() -> { - rotate.getImage().setRotation(editor.getDrawRotation() * 90); + rotate.getImage().setRotation(editor.rotation * 90); rotate.getImage().setOrigin(Align.center); }); @@ -409,8 +392,8 @@ public class MapEditorDialog extends Dialog implements Disposable{ button.margin(4f); button.getImageCell().grow(); button.getStyle().imageUpColor = team.color; - button.clicked(() -> editor.setDrawTeam(team)); - button.update(() -> button.setChecked(editor.getDrawTeam() == team)); + button.clicked(() -> editor.drawTeam = team); + button.update(() -> button.setChecked(editor.drawTeam == team)); teamgroup.add(button); tools.add(button); @@ -423,7 +406,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ mid.table("underline", t -> { Slider slider = new Slider(0, MapEditor.brushSizes.length - 1, 1, false); - slider.moved(f -> editor.setBrushSize(MapEditor.brushSizes[(int) (float) f])); + slider.moved(f -> editor.brushSize = MapEditor.brushSizes[(int) (float) f]); t.top(); t.add("$editor.brush"); @@ -451,21 +434,21 @@ public class MapEditorDialog extends Dialog implements Disposable{ } if(Core.input.keyTap(KeyCode.R)){ - editor.setDrawRotation((editor.getDrawRotation() + 1) % 4); + editor.rotation = Mathf.mod(editor.rotation + 1, 4); } if(Core.input.keyTap(KeyCode.E)){ - editor.setDrawRotation(Mathf.mod((editor.getDrawRotation() + 1), 4)); + editor.rotation = Mathf.mod(editor.rotation - 1, 4); } //ctrl keys (undo, redo, save) if(UIUtils.ctrl()){ if(Core.input.keyTap(KeyCode.Z)){ - view.undo(); + editor.undo(); } if(Core.input.keyTap(KeyCode.Y)){ - view.redo(); + editor.redo(); } if(Core.input.keyTap(KeyCode.S)){ @@ -515,9 +498,9 @@ public class MapEditorDialog extends Dialog implements Disposable{ ImageButton button = new ImageButton("white", "clear-toggle"); button.getStyle().imageUp = new TextureRegionDrawable(region); - button.clicked(() -> editor.setDrawBlock(block)); + button.clicked(() -> editor.drawBlock = block); button.resizeImage(8 * 4f); - button.update(() -> button.setChecked(editor.getDrawBlock() == block)); + button.update(() -> button.setChecked(editor.drawBlock == block)); group.add(button); content.add(button).size(50f); @@ -528,7 +511,7 @@ public class MapEditorDialog extends Dialog implements Disposable{ group.getButtons().get(2).setChecked(true); - table.table("underline", extra -> extra.labelWrap(() -> editor.getDrawBlock().localizedName).width(200f).center()).growX(); + table.table("underline", extra -> extra.labelWrap(() -> editor.drawBlock.localizedName).width(200f).center()).growX(); table.row(); table.add(pane).growY().fillX(); } diff --git a/core/src/io/anuke/mindustry/editor/MapRenderer.java b/core/src/io/anuke/mindustry/editor/MapRenderer.java index d0da1def5b..9a56571f93 100644 --- a/core/src/io/anuke/mindustry/editor/MapRenderer.java +++ b/core/src/io/anuke/mindustry/editor/MapRenderer.java @@ -9,13 +9,12 @@ import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.TextureRegion; import io.anuke.arc.math.Mathf; import io.anuke.arc.util.Disposable; -import io.anuke.arc.util.Pack; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.graphics.IndexedRenderer; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.Tile; -import static io.anuke.mindustry.Vars.content; import static io.anuke.mindustry.Vars.tilesize; public class MapRenderer implements Disposable{ @@ -101,27 +100,24 @@ public class MapRenderer implements Disposable{ private void render(int wx, int wy){ int x = wx / chunksize, y = wy / chunksize; IndexedRenderer mesh = chunks[x][y]; - byte bf = editor.getMap().read(wx, wy, DataPosition.floor); - byte bw = editor.getMap().read(wx, wy, DataPosition.wall); - byte btr = editor.getMap().read(wx, wy, DataPosition.rotationTeam); - byte rotation = Pack.leftByte(btr); - Team team = Team.all[Pack.rightByte(btr)]; + Tile tile = editor.tiles()[wx][wy]; - Block floor = content.block(bf); - Block wall = content.block(bw); + Team team = tile.getTeam(); + Block floor = tile.floor(); + Block wall = tile.block(); TextureRegion region; int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize; int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + chunksize * chunksize; - if(bw != 0 && (wall.synthetic() || wall == Blocks.part)){ + if(wall != Blocks.air && (wall.synthetic() || wall == Blocks.part)){ region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon(); if(wall.rotate){ mesh.draw(idxWall, region, wx * tilesize + wall.offset(), wy * tilesize + wall.offset(), - region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, rotation * 90 - 90); + region.getWidth() * Draw.scl, region.getHeight() * Draw.scl, tile.getRotation() * 90 - 90); }else{ mesh.draw(idxWall, region, wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f, @@ -139,7 +135,7 @@ public class MapRenderer implements Disposable{ if(wall.update || wall.destructible){ mesh.setColor(team.color); region = Core.atlas.find("block-border-editor"); - }else if(!wall.synthetic() && bw != 0){ + }else if(!wall.synthetic() && wall != Blocks.air){ region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon(); offsetX = tilesize/2f - region.getWidth()/2f * Draw.scl; offsetY = tilesize/2f - region.getHeight()/2f * Draw.scl; diff --git a/core/src/io/anuke/mindustry/editor/MapSaveDialog.java b/core/src/io/anuke/mindustry/editor/MapSaveDialog.java index 7d00f33d22..bcac2914e9 100644 --- a/core/src/io/anuke/mindustry/editor/MapSaveDialog.java +++ b/core/src/io/anuke/mindustry/editor/MapSaveDialog.java @@ -24,7 +24,7 @@ public class MapSaveDialog extends FloatingDialog{ shown(() -> { cont.clear(); cont.label(() -> { - Map map = world.maps.getByName(field.getText()); + Map map = world.maps.byName(field.getText()); if(map != null){ if(map.custom){ return "$editor.overwrite"; @@ -69,7 +69,7 @@ public class MapSaveDialog extends FloatingDialog{ if(field.getText().isEmpty()){ return true; } - Map map = world.maps.getByName(field.getText()); + Map map = world.maps.byName(field.getText()); return map != null && !map.custom; } } diff --git a/core/src/io/anuke/mindustry/editor/MapView.java b/core/src/io/anuke/mindustry/editor/MapView.java index 18fe4b48cf..bf115b9f8c 100644 --- a/core/src/io/anuke/mindustry/editor/MapView.java +++ b/core/src/io/anuke/mindustry/editor/MapView.java @@ -2,7 +2,6 @@ package io.anuke.mindustry.editor; import io.anuke.arc.Core; import io.anuke.arc.collection.Array; -import io.anuke.arc.collection.GridBits; import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.Lines; @@ -22,16 +21,16 @@ import io.anuke.arc.util.Tmp; import io.anuke.mindustry.graphics.Pal; import io.anuke.mindustry.input.Binding; import io.anuke.mindustry.ui.GridImage; -import io.anuke.mindustry.world.Pos; -import static io.anuke.mindustry.Vars.*; +import static io.anuke.mindustry.Vars.mobile; +import static io.anuke.mindustry.Vars.ui; public class MapView extends Element implements GestureListener{ private MapEditor editor; private EditorTool tool = EditorTool.pencil; //private OperationStack stack = new OperationStack(); //private DrawOperation op; - private GridBits used; + //private GridBits used; private Bresenham2 br = new Bresenham2(); private boolean updated = false; private float offsetx, offsety; @@ -88,13 +87,6 @@ public class MapView extends Element implements GestureListener{ mousex = x; mousey = y; - op = new DrawOperation(); - if(used == null || used.width() != world.width() || used.height() != world.height()){ - used = new GridBits(world.width(), world.height()); - }else{ - used.clear(); - } - updated = false; Point2 p = project(x, y); @@ -141,12 +133,7 @@ public class MapView extends Element implements GestureListener{ updated = true; } - if(op != null && updated){ - if(!op.isEmpty()){ - stack.add(op); - } - op = null; - } + editor.flushOp(); if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){ tool = lastTool; @@ -196,14 +183,6 @@ public class MapView extends Element implements GestureListener{ this.tool = tool; } - public void clearStack(){ - stack.clear(); - } - - public OperationStack getStack(){ - return stack; - } - public boolean isGrid(){ return grid; } @@ -212,27 +191,6 @@ public class MapView extends Element implements GestureListener{ this.grid = grid; } - public void undo(){ - if(stack.canUndo()){ - stack.undo(editor); - } - } - - public void redo(){ - if(stack.canRedo()){ - stack.redo(editor); - } - } - - public void addTileOp(int xy, byte type, byte from, byte to){ - used.set(Pos.x(xy), Pos.y(xy)); - op.addOperation(xy, type, from, to); - } - - public boolean checkForDuplicates(short x, short y){ - return used.get(x, y); - } - @Override public void act(float delta){ super.act(delta); @@ -266,14 +224,14 @@ public class MapView extends Element implements GestureListener{ } private Point2 project(float x, float y){ - float ratio = 1f / ((float) world.width() / world.height()); + float ratio = 1f / ((float) editor.width() / editor.height()); float size = Math.min(width, height); float sclwidth = size * zoom; float sclheight = size * zoom * ratio; - x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * world.width(); - y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * world.height(); + x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.width(); + y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * editor.height(); - if(editor.getDrawBlock().size % 2 == 0 && tool != EditorTool.eraser){ + if(editor.drawBlock.size % 2 == 0 && tool != EditorTool.eraser){ return Tmp.g1.set((int) (x - 0.5f), (int) (y - 0.5f)); }else{ return Tmp.g1.set((int) x, (int) y); @@ -281,26 +239,26 @@ public class MapView extends Element implements GestureListener{ } private Vector2 unproject(int x, int y){ - float ratio = 1f / ((float) world.width() / world.height()); + float ratio = 1f / ((float) editor.width() / editor.height()); float size = Math.min(width, height); float sclwidth = size * zoom; float sclheight = size * zoom * ratio; - float px = ((float) x / world.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2; - float py = ((float) (y) / world.height()) * sclheight + float px = ((float) x / editor.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2; + float py = ((float) (y) / editor.height()) * sclheight + offsety * zoom - sclheight / 2 + getHeight() / 2; return vec.set(px, py); } @Override public void draw(){ - float ratio = 1f / ((float) world.width() / world.height()); + float ratio = 1f / ((float) editor.width() / editor.height()); float size = Math.min(width, height); float sclwidth = size * zoom; float sclheight = size * zoom * ratio; float centerx = x + width / 2 + offsetx * zoom; float centery = y + height / 2 + offsety * zoom; - image.setImageSize(world.width(), world.height()); + image.setImageSize(editor.width(), editor.height()); if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){ return; @@ -325,18 +283,18 @@ public class MapView extends Element implements GestureListener{ int index = 0; for(int i = 0; i < MapEditor.brushSizes.length; i++){ - if(editor.getBrushSize() == MapEditor.brushSizes[i]){ + if(editor.brushSize == MapEditor.brushSizes[i]){ index = i; break; } } - float scaling = zoom * Math.min(width, height) / world.width(); + float scaling = zoom * Math.min(width, height) / editor.width(); Draw.color(Pal.accent); Lines.stroke(Unit.dp.scl(2f)); - if((!editor.getDrawBlock().isMultiblock() || tool == EditorTool.eraser) && tool != EditorTool.fill){ + if((!editor.drawBlock.isMultiblock() || tool == EditorTool.eraser) && tool != EditorTool.fill){ if(tool == EditorTool.line && drawing){ Vector2 v1 = unproject(startx, starty).add(x, y); float sx = v1.x, sy = v1.y; @@ -355,11 +313,11 @@ public class MapView extends Element implements GestureListener{ if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){ Point2 p = project(mousex, mousey); Vector2 v = unproject(p.x, p.y).add(x, y); - float offset = (editor.getDrawBlock().size % 2 == 0 ? scaling / 2f : 0f); + float offset = (editor.drawBlock.size % 2 == 0 ? scaling / 2f : 0f); Lines.square( v.x + scaling / 2f + offset, v.y + scaling / 2f + offset, - scaling * editor.getDrawBlock().size / 2f); + scaling * editor.drawBlock.size / 2f); } } diff --git a/core/src/io/anuke/mindustry/io/MapIO.java b/core/src/io/anuke/mindustry/io/MapIO.java index 1fc8901943..fdc6738c09 100644 --- a/core/src/io/anuke/mindustry/io/MapIO.java +++ b/core/src/io/anuke/mindustry/io/MapIO.java @@ -7,12 +7,15 @@ import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Pixmap; import io.anuke.arc.graphics.Pixmap.Format; import io.anuke.arc.util.Pack; +import io.anuke.arc.util.Structs; import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.game.MappableContent; import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Version; import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.world.Block; +import io.anuke.mindustry.world.LegacyColorMapper; +import io.anuke.mindustry.world.LegacyColorMapper.LegacyBlock; import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.blocks.BlockPart; @@ -57,16 +60,46 @@ public class MapIO{ return pixmap; } - //TODO implement /**Reads a pixmap in the 3.5 pixmap format.*/ - public static Tile[][] unfinished_readLegacyPixmap(Pixmap pixmap){ - return null; + public static void readLegacyPixmap(Pixmap pixmap, Tile[][] tiles){ + for(int x = 0; x < pixmap.getWidth(); x++){ + for(int y = 0; y < pixmap.getHeight(); y++){ + int color = pixmap.getPixel(x, pixmap.getHeight() - 1 - y); + LegacyBlock block = LegacyColorMapper.get(color); + Tile tile = tiles[x][y]; + + tile.setFloor(block.floor); + tile.setBlock(block.wall); + + //place core + if(color == Color.rgba8888(Color.GREEN)){ + for(int dx = 0; dx < 3; dx++){ + for(int dy = 0; dy < 3; dy++){ + int worldx = dx - 1 + x; + int worldy = dy - 1 + y; + + //multiblock parts + if(Structs.inBounds(worldx, worldy, pixmap.getWidth(), pixmap.getHeight())){ + Tile write = tiles[worldx][worldy]; + write.setBlock(Blocks.part); + write.setTeam(Team.blue); + write.setLinkByte(Pack.byteByte((byte) (dx - 1 + 8), (byte) (dy - 1 + 8))); + } + } + } + + //actual core parts + tile.setBlock(Blocks.coreShard); + tile.setTeam(Team.blue); + } + } + } } //TODO implement /**Reads a pixmap in the old 4.0 .mmap format.*/ - private static Tile[][] unfinished_readLegacyMmap(InputStream stream) throws IOException{ - return null; + private static void readLegacyMmap(FileHandle file, Tile[][] tiles) throws IOException{ + } public static int colorFor(Block floor, Block wall, Team team){ @@ -76,7 +109,9 @@ public class MapIO{ return Color.rgba8888(wall.solid ? wall.color : floor.color); } - public static void writeMap(OutputStream output, Map map, Tile[][] tiles) throws IOException{ + public static void writeMap(FileHandle file, Map map, Tile[][] tiles) throws IOException{ + OutputStream output = file.write(false); + try(DataOutputStream stream = new DataOutputStream(output)){ stream.writeInt(version); stream.writeInt(Version.build); diff --git a/core/src/io/anuke/mindustry/io/SaveMeta.java b/core/src/io/anuke/mindustry/io/SaveMeta.java index cfe0be8231..d7a97510dc 100644 --- a/core/src/io/anuke/mindustry/io/SaveMeta.java +++ b/core/src/io/anuke/mindustry/io/SaveMeta.java @@ -19,7 +19,7 @@ public class SaveMeta{ this.build = build; this.timestamp = timestamp; this.timePlayed = timePlayed; - this.map = world.maps.getByName(map); + this.map = world.maps.all().find(m -> m.fileName().equals(map)); this.wave = wave; this.rules = rules; } diff --git a/core/src/io/anuke/mindustry/io/versions/Save16.java b/core/src/io/anuke/mindustry/io/versions/Save16.java index 40c2c2f371..c1452a21ca 100644 --- a/core/src/io/anuke/mindustry/io/versions/Save16.java +++ b/core/src/io/anuke/mindustry/io/versions/Save16.java @@ -1,6 +1,6 @@ package io.anuke.mindustry.io.versions; -import io.anuke.arc.util.Strings; +import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.util.Time; import io.anuke.mindustry.game.Version; import io.anuke.mindustry.gen.Serialization; @@ -34,8 +34,8 @@ public class Save16 extends SaveFileVersion{ state.rules.spawns = content.getByID(ContentType.zone, state.rules.zone).rules.get().spawns; } String mapname = stream.readUTF(); - Map map = world.maps.getByName(mapname); - if(map == null) map = new Map(Strings.capitalize(mapname), 1, 1); + Map map = world.maps.all().find(m -> m.fileName().equals(mapname)); + if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true); world.setMap(map); int wave = stream.readInt(); @@ -62,7 +62,7 @@ public class Save16 extends SaveFileVersion{ //--GENERAL STATE-- Serialization.writeRules(stream, state.rules); - stream.writeUTF(world.getMap().name); //map name + stream.writeUTF(world.getMap().fileName()); //map name stream.writeInt(state.wave); //wave stream.writeFloat(state.wavetime); //wave countdown diff --git a/core/src/io/anuke/mindustry/maps/Maps.java b/core/src/io/anuke/mindustry/maps/Maps.java index 355f151658..ff2c7dd30f 100644 --- a/core/src/io/anuke/mindustry/maps/Maps.java +++ b/core/src/io/anuke/mindustry/maps/Maps.java @@ -2,6 +2,7 @@ package io.anuke.mindustry.maps; import io.anuke.arc.Core; import io.anuke.arc.collection.Array; +import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.files.FileHandle; import io.anuke.arc.graphics.Texture; import io.anuke.arc.util.Disposable; @@ -34,6 +35,10 @@ public class Maps implements Disposable{ return maps.select(m -> !m.custom); } + public Map byName(String name){ + return maps.find(m -> m.name().equals(name)); + } + /** * Loads a map from the map folder and returns it. Should only be used for zone maps. * Does not add this map to the map list. @@ -62,10 +67,32 @@ public class Maps implements Disposable{ loadCustomMaps(); } - /** Save a map. This updates all values and stored data necessary. */ - public void saveMap(Map map, Tile[][] data){ + /** 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){ + try{ - MapIO.writeMap(map.file.write(false), map, data); + 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); + + //find map with the same exact display name + Map other = maps.find(m -> m.getDisplayName().equals(name)); + + if(other != null){ + //dispose of map if it's already there + if(other.texture != null){ + other.texture.dispose(); + other.texture = null; + } + maps.remove(other); + } + + //create map, write it, etc etc etc + Map map = new Map(file, data.length, data[0].length, tags, true); + MapIO.writeMap(file, map, data); + if(!headless){ map.texture = new Texture(MapIO.generatePreview(data)); } diff --git a/core/src/io/anuke/mindustry/net/NetworkIO.java b/core/src/io/anuke/mindustry/net/NetworkIO.java index 8e26a4a701..45e46aae3b 100644 --- a/core/src/io/anuke/mindustry/net/NetworkIO.java +++ b/core/src/io/anuke/mindustry/net/NetworkIO.java @@ -27,10 +27,10 @@ public class NetworkIO{ try(DataOutputStream stream = new DataOutputStream(os)){ //--GENERAL STATE-- Serialization.writeRules(stream, state.rules); - stream.writeUTF(world.getMap().name); //map name + stream.writeUTF(world.getMap().name()); //map name //write tags - ObjectMap tags = world.getMap().meta.tags; + ObjectMap tags = world.getMap().tags; stream.writeByte(tags.size); for(Entry entry : tags.entries()){ stream.writeUTF(entry.key); @@ -105,7 +105,7 @@ public class NetworkIO{ //map world.spawner.read(stream); SaveIO.getSaveWriter().readMap(stream); - world.setMap(new Map(map, 0, 0)); + world.setMap(new Map(customMapDirectory.child(map), 0, 0, new ObjectMap<>(), true)); state.teams = new Teams(); @@ -141,7 +141,7 @@ public class NetworkIO{ int maxlen = 32; String host = (headless ? "Server" : players[0].name); - String map = world.getMap() == null ? "None" : world.getMap().name; + String map = world.getMap() == null ? "None" : world.getMap().name(); host = host.substring(0, Math.min(host.length(), maxlen)); map = map.substring(0, Math.min(map.length(), maxlen)); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java index c068eae79d..bfc1d4902d 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/CustomGameDialog.java @@ -106,7 +106,7 @@ public class CustomGameDialog extends FloatingDialog{ image.row(); image.add("[accent]" + map.getDisplayName()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center); image.row(); - image.label((() -> Core.bundle.format("level.highscore", Core.settings.getInt("hiscore" + map.name, 0)))).pad(3f); + image.label((() -> Core.bundle.format("level.highscore", Core.settings.getInt("hiscore" + map.fileName(), 0)))).pad(3f); BorderImage border = new BorderImage(map.texture, 3f); border.setScaling(Scaling.fit); diff --git a/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java b/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java index 6ce8745e39..40ad3b8405 100644 --- a/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java +++ b/core/src/io/anuke/mindustry/ui/dialogs/MapsDialog.java @@ -29,12 +29,15 @@ 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.read(), file.name(), true, file::read); + Map map = MapIO.readMap(file, true); String name = map.tags.get("name", file.nameWithoutExtension()); - if(world.maps.getByName(name) != null && !world.maps.getByName(name).custom){ + //TODO filename conflict, erasure + Map conflict = world.maps.byName(name); + + if(conflict != null && !conflict.custom){ ui.showError(Core.bundle.format("editor.import.exists", name)); - }else if(world.maps.getByName(name) != null){ + }else if(conflict != null){ ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> { world.maps.importMap(file, map); setup(); @@ -138,7 +141,7 @@ public class MapsDialog extends FloatingDialog{ table.addImageTextButton("$editor.openin", "icon-load-map", 16 * 2, () -> { try{ - Vars.ui.editor.beginEditMap(map.stream.get()); + Vars.ui.editor.beginEditMap(map.file); dialog.hide(); hide(); }catch(Exception e){ @@ -148,7 +151,7 @@ public class MapsDialog extends FloatingDialog{ }).fillX().height(54f).marginLeft(10); table.addImageTextButton("$delete", "icon-trash-16", 16 * 2, () -> { - ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name), () -> { + ui.showConfirm("$confirm", Core.bundle.format("map.delete", map.name()), () -> { world.maps.removeMap(map); dialog.hide(); setup(); diff --git a/core/src/io/anuke/mindustry/world/Tile.java b/core/src/io/anuke/mindustry/world/Tile.java index b60897e1e6..d0147acda0 100644 --- a/core/src/io/anuke/mindustry/world/Tile.java +++ b/core/src/io/anuke/mindustry/world/Tile.java @@ -215,6 +215,14 @@ public class Tile implements Position, TargetTrait{ return link != 0; } + public byte getLinkByte(){ + return link; + } + + public void setLinkByte(byte b){ + this.link = 0; + } + /** Sets this to a linked tile, which sets the block to a part. dx and dy can only be -8-7. */ public void setLinked(byte dx, byte dy){ setBlock(Blocks.part); diff --git a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java index 24c8be9015..b262f854ea 100644 --- a/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java +++ b/desktop/src/io/anuke/mindustry/desktop/DesktopPlatform.java @@ -54,9 +54,9 @@ public class DesktopPlatform extends Platform{ if(world.getMap() == null){ presence.details = "Unknown Map"; }else if(!state.rules.waves){ - presence.details = Strings.capitalize(world.getMap().name); + presence.details = Strings.capitalize(world.getMap().getDisplayName()); }else{ - presence.details = Strings.capitalize(world.getMap().name) + " | Wave " + state.wave; + presence.details = Strings.capitalize(world.getMap().getDisplayName()) + " | Wave " + state.wave; presence.largeImageText = "Wave " + state.wave; }