Fixed compile errors / New non-crappy undo system

This commit is contained in:
Anuken
2019-03-11 23:06:41 -04:00
parent 6c3ccb259f
commit 0fd0abc6c0
18 changed files with 392 additions and 316 deletions

View File

@@ -197,8 +197,8 @@ editor.description = Description:
editor.name = Name: editor.name = Name:
editor.teams = Teams editor.teams = Teams
editor.elevation = Elevation editor.elevation = Elevation
editor.errorimageload = Error loading file:\n[accent]{0} editor.errorload = Error loading file:\n[accent]{0}
editor.errorimagesave = Error saving file:\n[accent]{0} editor.errorsave = Error saving file:\n[accent]{0}
editor.generate = Generate editor.generate = Generate
editor.resize = Resize editor.resize = Resize
editor.loadmap = Load Map editor.loadmap = Load Map

View File

@@ -30,13 +30,15 @@ public class DrawOperation{
class TileOpStruct{ class TileOpStruct{
short x; short x;
short y; short y;
/**
* 0: floor
* 1: block
* 2: rotation
*/
byte type; byte type;
byte from; byte from;
byte to; byte to;
} }
public enum OpType{
floor,
block,
rotation,
team
}
} }

View File

@@ -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);
}
}

View File

@@ -8,29 +8,32 @@ import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.world.Block; 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 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{ public enum EditorTool{
pick{ pick{
public void touched(MapEditor editor, int x, int y){ public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, world.width(), world.height())) return; if(!Structs.inBounds(x, y, world.width(), world.height())) return;
byte bf = editor.getMap().read(x, y, DataPosition.floor); Tile tile = editor.tile(x, y);
byte bw = editor.getMap().read(x, y, DataPosition.wall);
byte link = editor.getMap().read(x, y, DataPosition.link); Block floor = tile.floor(), block = tile.block();
byte link = tile.getLinkByte();
if(link != 0){ if(link != 0){
x -= (Pack.leftByte(link) - 8); x -= (Pack.leftByte(link) - 8);
y -= (Pack.rightByte(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.drawBlock = block == Blocks.air ? floor : block;
editor.setDrawBlock(block);
ui.editor.updateSelectedBlock(); ui.editor.updateSelectedBlock();
} }
}, },
@@ -64,7 +67,7 @@ public enum EditorTool{
@Override @Override
public void touched(MapEditor editor, int x, int y){ 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{ line{
@@ -78,56 +81,57 @@ public enum EditorTool{
} }
IntArray stack = new IntArray(); IntArray stack = new IntArray();
int width; Block dest;
byte be, dest; boolean isfloor;
boolean floor; MapEditor data;
MapTileData data;
public void touched(MapEditor editor, int x, int y){ public void touched(MapEditor editor, int x, int y){
if(!Structs.inBounds(x, y, world.width(), world.height())) return; 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 //don't fill multiblocks, thanks
pencil.touched(editor, x, y); pencil.touched(editor, x, y);
return; 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); dest = isfloor ? floor : block;
byte bw = data.read(x, y, DataPosition.wall); Block draw = editor.drawBlock;
boolean synth = editor.getDrawBlock().synthetic();
byte brt = Pack.byteByte((byte) editor.getDrawRotation(), (byte) editor.getDrawTeam().ordinal());
dest = floor ? bf : bw; if(dest == draw || block == Blocks.part || block.isMultiblock()){
byte draw = editor.getDrawBlock().id;
if(dest == draw || content.block(bw) instanceof BlockPart || content.block(bw).isMultiblock()){
return; return;
} }
width = world.width(); int width = world.width();
int height = world.height(); int height = world.height();
IntPositionConsumer writer = (px, py) -> { IntPositionConsumer writer = (px, py) -> {
TileDataMarker prev = editor.getPrev(px, py, false); Tile write = editor.tile(px, py);
if(floor){ if(isfloor){
data.write(px, py, DataPosition.floor, draw); write.setFloor((Floor)draw);
}else{ }else{
data.write(px, py, DataPosition.wall, draw); write.setBlock(draw);
} }
if(synth){ 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()){ if(isAlt()){
//fill all of the same type regardless of borders
for(int cx = 0; cx < width; cx++){ for(int cx = 0; cx < width; cx++){
for(int cy = 0; cy < height; cy++){ for(int cy = 0; cy < height; cy++){
if(eq(cx, cy)){ if(eq(cx, cy)){
@@ -136,28 +140,28 @@ public enum EditorTool{
} }
} }
}else if(isAlt2()){ }else if(isAlt2()){
//fill all teams.
for(int cx = 0; cx < width; cx++){ for(int cx = 0; cx < width; cx++){
for(int cy = 0; cy < height; cy++){ for(int cy = 0; cy < height; cy++){
byte w = data.read(cx, cy, DataPosition.wall); Tile write = editor.tile(cx, cy);
if(content.block(w).synthetic()){ if(write.block().synthetic()){
TileDataMarker prev = editor.getPrev(cx, cy, false); write.setTeam(editor.drawTeam);
data.write(cx, cy, DataPosition.rotationTeam, (byte)editor.getDrawTeam().ordinal());
editor.onWrite(cx, cy, prev);
} }
} }
} }
}else{ }else{
//normal fill
int x1; int x1;
boolean spanAbove, spanBelow; boolean spanAbove, spanBelow;
stack.clear(); stack.clear();
stack.add(asi(x, y)); stack.add(Pos.get(x, y));
while(stack.size > 0){ while(stack.size > 0){
int popped = stack.pop(); int popped = stack.pop();
x = popped % width; x = Pos.x(popped);
y = popped / width; y = Pos.y(popped);
x1 = x; x1 = x;
while(x1 >= 0 && eq(x1, y)) x1--; while(x1 >= 0 && eq(x1, y)) x1--;
@@ -167,14 +171,14 @@ public enum EditorTool{
writer.accept(x1, y); writer.accept(x1, y);
if(!spanAbove && y > 0 && eq(x1, y - 1)){ if(!spanAbove && y > 0 && eq(x1, y - 1)){
stack.add(asi(x1, y - 1)); stack.add(Pos.get(x1, y - 1));
spanAbove = true; spanAbove = true;
}else if(spanAbove && y > 0 && eq(x1, y - 1)){ }else if(spanAbove && eq(x1, y - 1)){
spanAbove = false; spanAbove = false;
} }
if(!spanBelow && y < height - 1 && eq(x1, y + 1)){ if(!spanBelow && y < height - 1 && eq(x1, y + 1)){
stack.add(asi(x1, y + 1)); stack.add(Pos.get(x1, y + 1));
spanBelow = true; spanBelow = true;
}else if(spanBelow && y < height - 1 && eq(x1, y + 1)){ }else if(spanBelow && y < height - 1 && eq(x1, y + 1)){
spanBelow = false; spanBelow = false;
@@ -186,15 +190,9 @@ public enum EditorTool{
} }
boolean eq(int px, int py){ boolean eq(int px, int py){
byte nbf = data.read(px, py, DataPosition.floor); Tile tile = data.tile(px, py);
byte nbw = data.read(px, py, DataPosition.wall);
byte nbe = data.read(px, py, DataPosition.elevation);
return (floor ? nbf : nbw) == dest && nbe == be; return (isfloor ? tile.floor() : tile.block()) == dest;
}
int asi(int x, int y){
return x + y * width;
} }
}, },
zoom; zoom;

View File

@@ -1,19 +1,21 @@
package io.anuke.mindustry.editor; package io.anuke.mindustry.editor;
import io.anuke.arc.collection.GridBits;
import io.anuke.arc.collection.ObjectMap; import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Pack; import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs; import io.anuke.arc.util.Structs;
import io.anuke.mindustry.Vars;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.gen.TileOp; 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.Block;
import io.anuke.mindustry.world.Tile; import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.Floor; 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; import static io.anuke.mindustry.Vars.world;
public class MapEditor{ public class MapEditor{
@@ -24,8 +26,8 @@ public class MapEditor{
private Tile[][] tiles; private Tile[][] tiles;
private OperationStack stack = new OperationStack(); private OperationStack stack = new OperationStack();
private DrawOperation op; private DrawOperation currentOp;
private GridBits used; private boolean loading;
public int brushSize = 1; public int brushSize = 1;
public int rotation; public int rotation;
@@ -36,27 +38,70 @@ public class MapEditor{
return tags; return tags;
} }
public void beginEdit(Tile[][] map, ObjectMap<String, String> tags, boolean clear){ public void beginEdit(int width, int height){
reset();
this.brushSize = 1; loading = true;
this.tags = tags; tiles = createTiles(width, height);
renderer.resize(width(), height());
if(clear){ loading = false;
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; drawBlock = Blocks.stone;
renderer.resize(map.length, map[0].length); tags = new ObjectMap<>();
} }
public Tile[][] tiles(){ public Tile[][] tiles(){
return tiles; return tiles;
} }
public Tile tile(int x, int y){
return tiles[x][y];
}
public int width(){ public int width(){
return tiles.length; return tiles.length;
} }
@@ -74,9 +119,7 @@ public class MapEditor{
} }
public void draw(int x, int y, boolean paint, Block drawBlock, double chance){ public void draw(int x, int y, boolean paint, Block drawBlock, double chance){
byte writeID = drawBlock.id; //byte rotationTeam = Pack.byteByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0);
byte partID = Blocks.part.id;
byte rotationTeam = Pack.byteByte(drawBlock.rotate ? (byte)rotation : 0, drawBlock.synthetic() ? (byte)drawTeam.ordinal() : 0);
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air; boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
@@ -94,52 +137,44 @@ public class MapEditor{
int worldy = dy + offsety + y; int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, world.width(), world.height())){ if(Structs.inBounds(worldx, worldy, world.width(), world.height())){
TileDataMarker prev = getPrev(worldx, worldy, false); Tile tile = tiles[worldx][worldy];
if(i == 1){ if(i == 1){
map.write(worldx, worldy, DataPosition.wall, partID); tile.setLinked((byte)(dx + offsetx), (byte)(dy + offsety));
map.write(worldx, worldy, DataPosition.rotationTeam, rotationTeam);
map.write(worldx, worldy, DataPosition.link, Pack.byteByte((byte) (dx + offsetx + 8), (byte) (dy + offsety + 8)));
}else{ }else{
byte link = map.read(worldx, worldy, DataPosition.link); byte link = tile.getLinkByte();
byte block = map.read(worldx, worldy, DataPosition.wall); Block block = tile.block();
if(link != 0){ if(link != 0){
removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8)); removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8));
}else if(content.block(block).isMultiblock()){ }else if(block.isMultiblock()){
removeLinked(worldx, worldy); removeLinked(worldx, worldy);
} }
} }
onWrite(worldx, worldy, prev);
} }
} }
} }
} }
TileDataMarker prev = getPrev(x, y, false); Tile tile = tiles[x][y];
tile.setBlock(drawBlock);
map.write(x, y, DataPosition.wall, writeID); tile.setTeam(drawTeam);
map.write(x, y, DataPosition.link, (byte) 0);
map.write(x, y, DataPosition.rotationTeam, rotationTeam);
onWrite(x, y, prev);
}else{ }else{
for(int rx = -brushSize; rx <= brushSize; rx++){ for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){ for(int ry = -brushSize; ry <= brushSize; ry++){
if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){ if(Mathf.dst(rx, ry) <= brushSize - 0.5f && (chance >= 0.999 || Mathf.chance(chance))){
int wx = x + rx, wy = y + ry; 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; continue;
} }
TileDataMarker prev = getPrev(wx, wy, true); Tile tile = tiles[wx][wy];
if(!isfloor){ 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); removeLinked(wx, wy);
}else if(link != 0){ }else if(link != 0){
removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8)); removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8));
@@ -147,14 +182,13 @@ public class MapEditor{
} }
if(isfloor){ if(isfloor){
map.write(wx, wy, DataPosition.floor, writeID); tile.setFloor((Floor)drawBlock);
}else{ }else{
map.write(wx, wy, DataPosition.wall, writeID); tile.setBlock(drawBlock);
map.write(wx, wy, DataPosition.link, (byte) 0); if(drawBlock.synthetic()){
map.write(wx, wy, DataPosition.rotationTeam, rotationTeam); tile.setTeam(drawTeam);
}
} }
onWrite(x + rx, y + ry, prev);
} }
} }
} }
@@ -162,62 +196,31 @@ public class MapEditor{
} }
private void removeLinked(int x, int y){ 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 offsetx = -(block.size - 1) / 2;
int offsety = -(block.size - 1) / 2; int offsety = -(block.size - 1) / 2;
for(int dx = 0; dx < block.size; dx++){ for(int dx = 0; dx < block.size; dx++){
for(int dy = 0; dy < block.size; dy++){ for(int dy = 0; dy < block.size; dy++){
int worldx = x + dx + offsetx, worldy = y + dy + offsety; int worldx = x + dx + offsetx, worldy = y + dy + offsety;
if(Structs.inBounds(worldx, worldy, map.width(), map.height())){ if(Structs.inBounds(worldx, worldy, width(), height())){
TileDataMarker prev = getPrev(worldx, worldy, false); tiles[worldx][worldy].setTeam(Team.none);
tiles[worldx][worldy].setBlock(Blocks.air);
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);
} }
} }
} }
} }
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(){ public MapRenderer renderer(){
return renderer; return renderer;
} }
public void resize(int width, int height){ public void resize(int width, int height){
clearOp();
Tile[][] previous = tiles; Tile[][] previous = tiles;
int offsetX = -(width - width())/2, offsetY = -(height - height())/2; int offsetX = -(width - width())/2, offsetY = -(height - height())/2;
loading = true;
tiles = new Tile[width][height]; tiles = new Tile[width][height];
for(int x = 0; x < width; x++){ for(int x = 0; x < width; x++){
@@ -228,17 +231,17 @@ public class MapEditor{
tiles[x][y].x = (short)x; tiles[x][y].x = (short)x;
tiles[x][y].y = (short)y; tiles[x][y].y = (short)y;
}else{ }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);
}
public void changeFloor(int x, int y, Block to){ renderer.resize(width, height);
Block from = tiles[x][y].floor(); loading = false;
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(){ public void undo(){
@@ -253,16 +256,26 @@ public class MapEditor{
} }
} }
public boolean canUndo(){
return stack.canUndo();
}
public boolean canRedo(){
return stack.canRedo();
}
public void flushOp(){
if(currentOp == null || currentOp.isEmpty()) return;
stack.add(currentOp);
currentOp = null;
}
public void addTileOp(long data){ public void addTileOp(long data){
used.set(TileOp.x(data), TileOp.y(data)); if(loading) return;
op.addOperation(data);
}
public boolean checkUsed(short x, short y){ if(currentOp == null) currentOp = new DrawOperation();
if(used == null || used.width() != width() || used.height() != height()){ currentOp.addOperation(data);
used = new GridBits(world.width(), world.height());
}
return used.get(x, y); renderer.updatePoint(TileOp.x(data), TileOp.y(data));
} }
} }

View File

@@ -2,7 +2,6 @@ package io.anuke.mindustry.editor;
import io.anuke.arc.Core; import io.anuke.arc.Core;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle; import io.anuke.arc.files.FileHandle;
import io.anuke.arc.function.Consumer; import io.anuke.arc.function.Consumer;
import io.anuke.arc.graphics.Color; 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.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Block; import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Block.Icon; 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.OreBlock;
import io.anuke.mindustry.world.blocks.storage.CoreBlock; import io.anuke.mindustry.world.blocks.storage.CoreBlock;
import java.io.DataInputStream;
import java.io.InputStream;
import static io.anuke.mindustry.Vars.*; import static io.anuke.mindustry.Vars.*;
public class MapEditorDialog extends Dialog implements Disposable{ public class MapEditorDialog extends Dialog implements Disposable{
private MapEditor editor; public final MapEditor editor;
private MapView view; private MapView view;
private MapInfoDialog infoDialog; private MapInfoDialog infoDialog;
private MapLoadDialog loadDialog; private MapLoadDialog loadDialog;
@@ -87,17 +84,12 @@ public class MapEditorDialog extends Dialog implements Disposable{
createDialog("$editor.import", createDialog("$editor.import",
"$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show, "$editor.importmap", "$editor.importmap.description", "icon-load-map", (Runnable) loadDialog::show,
"$editor.importfile", "$editor.importfile.description", "icon-file", (Runnable) () -> "$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{ try{
DataInputStream stream = new DataInputStream(file.read()); //TODO what if it's an image? users should be warned for their stupidity
editor.beginEdit(MapIO.readMap(file, true));
MapMeta meta = MapIO.readMapMeta(stream);
MapTileData data = MapIO.readTileData(stream, meta, false);
editor.beginEdit(data, meta.tags, false);
view.clearStack();
}catch(Exception e){ }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); Log.err(e);
} }
}), true, mapExtension), }), true, mapExtension),
@@ -106,39 +98,32 @@ public class MapEditorDialog extends Dialog implements Disposable{
Platform.instance.showFileChooser("$loadimage", "Image Files", file -> Platform.instance.showFileChooser("$loadimage", "Image Files", file ->
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
MapTileData data = MapIO.readLegacyPixmap(new Pixmap(file)); Pixmap pixmap = new Pixmap(file);
Tile[][] tiles = editor.createTiles(pixmap.getWidth(), pixmap.getHeight());
editor.beginEdit(data, editor.getTags(), false); editor.load(() -> MapIO.readLegacyPixmap(pixmap, tiles));
view.clearStack(); editor.beginEdit(tiles);
}catch (Exception e){ }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); Log.err(e);
} }
}), true, "png") }), true, "png")));
));
t.addImageTextButton("$editor.export", "icon-save-map", isize, () -> createDialog("$editor.export", t.addImageTextButton("$editor.export", "icon-save-map", isize, () ->
"$editor.exportfile", "$editor.exportfile.description", "icon-file", (Runnable) () -> Platform.instance.showFileChooser("$editor.savemap", "Map Files", file -> {
Platform.instance.showFileChooser("$saveimage", "Map Files", file -> {
file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension); file = file.parent().child(file.nameWithoutExtension() + "." + mapExtension);
FileHandle result = file; FileHandle result = file;
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
if(!editor.getTags().containsKey("name")){ if(!editor.getTags().containsKey("name")){
editor.getTags().put("name", result.nameWithoutExtension()); 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){ }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); Log.err(e);
} }
}); });
}, false, mapExtension))); }, false, mapExtension));
t.row();
t.row();
}); });
menu.cont.row(); menu.cont.row();
@@ -149,24 +134,19 @@ public class MapEditorDialog extends Dialog implements Disposable{
}).padTop(-5).size(swidth * 2f + 10, 60f); }).padTop(-5).size(swidth * 2f + 10, 60f);
resizeDialog = new MapResizeDialog(editor, (x, y) -> { resizeDialog = new MapResizeDialog(editor, (x, y) -> {
if(!(world.width() == x && world.height() == y)){ if(!(editor.width() == x && editor.height() == y)){
ui.loadAnd(() -> { ui.loadAnd(() -> {
editor.resize(x, y); editor.resize(x, y);
view.clearStack();
}); });
} }
}); });
loadDialog = new MapLoadDialog(map -> loadDialog = new MapLoadDialog(map ->
ui.loadAnd(() -> { ui.loadAnd(() -> {
try(DataInputStream stream = new DataInputStream(map.stream.get())){ try{
MapMeta meta = MapIO.readMapMeta(stream); editor.beginEdit(map);
MapTileData data = MapIO.readTileData(stream, meta, false);
editor.beginEdit(data, meta.tags, false);
view.clearStack();
}catch(Exception e){ }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); Log.err(e);
} }
})); }));
@@ -198,10 +178,10 @@ public class MapEditorDialog extends Dialog implements Disposable{
shown(() -> { shown(() -> {
saved = true; saved = true;
Platform.instance.beginForceLandscape(); Platform.instance.beginForceLandscape();
view.clearStack(); editor.clearOp();
Core.scene.setScrollFocus(view); Core.scene.setScrollFocus(view);
if(!shownWithMap){ if(!shownWithMap){
editor.beginEdit(new MapTileData(200, 200), new ObjectMap<>(), true); editor.beginEdit(200, 200);
} }
shownWithMap = false; shownWithMap = false;
@@ -209,6 +189,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
}); });
hidden(() -> { hidden(() -> {
editor.clearOp();
Platform.instance.updateRPC(); Platform.instance.updateRPC();
Platform.instance.endForceLandscape(); Platform.instance.endForceLandscape();
}); });
@@ -223,13 +204,14 @@ public class MapEditorDialog extends Dialog implements Disposable{
String name = editor.getTags().get("name", ""); String name = editor.getTags().get("name", "");
if(name.isEmpty()){ if(name.isEmpty()){
ui.showError("$editor.save.noname"); infoDialog.show();
Core.app.post(() -> ui.showError("$editor.save.noname"));
}else{ }else{
Map map = world.maps.getByName(name); Map map = world.maps.all().find(m -> m.name().equals(name));
if(map != null && !map.custom){ if(map != null && !map.custom){
ui.showError("$editor.save.overwrite"); ui.showError("$editor.save.overwrite");
}else{ }else{
world.maps.saveMap(name, editor.getMap(), editor.getTags()); world.maps.saveMap(editor.getTags(), editor.tiles());
ui.showInfoFade("$editor.saved"); ui.showInfoFade("$editor.saved");
} }
} }
@@ -294,12 +276,13 @@ public class MapEditorDialog extends Dialog implements Disposable{
public void beginEditMap(FileHandle file){ public void beginEditMap(FileHandle file){
ui.loadAnd(() -> { ui.loadAnd(() -> {
try{ try{
Map map = MapIO.readMap(file, true);
shownWithMap = true; shownWithMap = true;
editor.beginEdit(MapIO.readTiles(file)); editor.beginEdit(map);
show(); show();
}catch(Exception e){ }catch(Exception e){
Log.err(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(); tools.row();
ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, () -> view.undo()).get(); ImageButton undo = tools.addImageButton("icon-undo", "clear", 16 * 2f, editor::undo).get();
ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, () -> view.redo()).get(); ImageButton redo = tools.addImageButton("icon-redo", "clear", 16 * 2f, editor::redo).get();
addTool.accept(EditorTool.pick); addTool.accept(EditorTool.pick);
tools.row(); tools.row();
undo.setDisabled(() -> !view.getStack().canUndo()); undo.setDisabled(() -> !editor.canUndo());
redo.setDisabled(() -> !view.getStack().canRedo()); redo.setDisabled(() -> !editor.canRedo());
undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE)); undo.update(() -> undo.getImage().setColor(undo.isDisabled() ? Color.GRAY : Color.WHITE));
redo.update(() -> redo.getImage().setColor(redo.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.fill);
addTool.accept(EditorTool.spray); 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().update(() -> {
rotate.getImage().setRotation(editor.getDrawRotation() * 90); rotate.getImage().setRotation(editor.rotation * 90);
rotate.getImage().setOrigin(Align.center); rotate.getImage().setOrigin(Align.center);
}); });
@@ -409,8 +392,8 @@ public class MapEditorDialog extends Dialog implements Disposable{
button.margin(4f); button.margin(4f);
button.getImageCell().grow(); button.getImageCell().grow();
button.getStyle().imageUpColor = team.color; button.getStyle().imageUpColor = team.color;
button.clicked(() -> editor.setDrawTeam(team)); button.clicked(() -> editor.drawTeam = team);
button.update(() -> button.setChecked(editor.getDrawTeam() == team)); button.update(() -> button.setChecked(editor.drawTeam == team));
teamgroup.add(button); teamgroup.add(button);
tools.add(button); tools.add(button);
@@ -423,7 +406,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
mid.table("underline", t -> { mid.table("underline", t -> {
Slider slider = new Slider(0, MapEditor.brushSizes.length - 1, 1, false); 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.top();
t.add("$editor.brush"); t.add("$editor.brush");
@@ -451,21 +434,21 @@ public class MapEditorDialog extends Dialog implements Disposable{
} }
if(Core.input.keyTap(KeyCode.R)){ 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)){ 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) //ctrl keys (undo, redo, save)
if(UIUtils.ctrl()){ if(UIUtils.ctrl()){
if(Core.input.keyTap(KeyCode.Z)){ if(Core.input.keyTap(KeyCode.Z)){
view.undo(); editor.undo();
} }
if(Core.input.keyTap(KeyCode.Y)){ if(Core.input.keyTap(KeyCode.Y)){
view.redo(); editor.redo();
} }
if(Core.input.keyTap(KeyCode.S)){ if(Core.input.keyTap(KeyCode.S)){
@@ -515,9 +498,9 @@ public class MapEditorDialog extends Dialog implements Disposable{
ImageButton button = new ImageButton("white", "clear-toggle"); ImageButton button = new ImageButton("white", "clear-toggle");
button.getStyle().imageUp = new TextureRegionDrawable(region); button.getStyle().imageUp = new TextureRegionDrawable(region);
button.clicked(() -> editor.setDrawBlock(block)); button.clicked(() -> editor.drawBlock = block);
button.resizeImage(8 * 4f); button.resizeImage(8 * 4f);
button.update(() -> button.setChecked(editor.getDrawBlock() == block)); button.update(() -> button.setChecked(editor.drawBlock == block));
group.add(button); group.add(button);
content.add(button).size(50f); content.add(button).size(50f);
@@ -528,7 +511,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
group.getButtons().get(2).setChecked(true); 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.row();
table.add(pane).growY().fillX(); table.add(pane).growY().fillX();
} }

View File

@@ -9,13 +9,12 @@ import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.TextureRegion; import io.anuke.arc.graphics.g2d.TextureRegion;
import io.anuke.arc.math.Mathf; import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Disposable; import io.anuke.arc.util.Disposable;
import io.anuke.arc.util.Pack;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.graphics.IndexedRenderer; import io.anuke.mindustry.graphics.IndexedRenderer;
import io.anuke.mindustry.world.Block; 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; import static io.anuke.mindustry.Vars.tilesize;
public class MapRenderer implements Disposable{ public class MapRenderer implements Disposable{
@@ -101,27 +100,24 @@ public class MapRenderer implements Disposable{
private void render(int wx, int wy){ private void render(int wx, int wy){
int x = wx / chunksize, y = wy / chunksize; int x = wx / chunksize, y = wy / chunksize;
IndexedRenderer mesh = chunks[x][y]; IndexedRenderer mesh = chunks[x][y];
byte bf = editor.getMap().read(wx, wy, DataPosition.floor); Tile tile = editor.tiles()[wx][wy];
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)];
Block floor = content.block(bf); Team team = tile.getTeam();
Block wall = content.block(bw); Block floor = tile.floor();
Block wall = tile.block();
TextureRegion region; TextureRegion region;
int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize; int idxWall = (wx % chunksize) + (wy % chunksize) * chunksize;
int idxDecal = (wx % chunksize) + (wy % chunksize) * chunksize + 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(); region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
if(wall.rotate){ if(wall.rotate){
mesh.draw(idxWall, region, mesh.draw(idxWall, region,
wx * tilesize + wall.offset(), wy * tilesize + wall.offset(), 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{ }else{
mesh.draw(idxWall, region, mesh.draw(idxWall, region,
wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f, wx * tilesize + wall.offset() + (tilesize - region.getWidth() * Draw.scl)/2f,
@@ -139,7 +135,7 @@ public class MapRenderer implements Disposable{
if(wall.update || wall.destructible){ if(wall.update || wall.destructible){
mesh.setColor(team.color); mesh.setColor(team.color);
region = Core.atlas.find("block-border-editor"); 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(); region = !Core.atlas.isFound(wall.editorIcon()) ? Core.atlas.find("clear-editor") : wall.editorIcon();
offsetX = tilesize/2f - region.getWidth()/2f * Draw.scl; offsetX = tilesize/2f - region.getWidth()/2f * Draw.scl;
offsetY = tilesize/2f - region.getHeight()/2f * Draw.scl; offsetY = tilesize/2f - region.getHeight()/2f * Draw.scl;

View File

@@ -24,7 +24,7 @@ public class MapSaveDialog extends FloatingDialog{
shown(() -> { shown(() -> {
cont.clear(); cont.clear();
cont.label(() -> { cont.label(() -> {
Map map = world.maps.getByName(field.getText()); Map map = world.maps.byName(field.getText());
if(map != null){ if(map != null){
if(map.custom){ if(map.custom){
return "$editor.overwrite"; return "$editor.overwrite";
@@ -69,7 +69,7 @@ public class MapSaveDialog extends FloatingDialog{
if(field.getText().isEmpty()){ if(field.getText().isEmpty()){
return true; return true;
} }
Map map = world.maps.getByName(field.getText()); Map map = world.maps.byName(field.getText());
return map != null && !map.custom; return map != null && !map.custom;
} }
} }

View File

@@ -2,7 +2,6 @@ package io.anuke.mindustry.editor;
import io.anuke.arc.Core; import io.anuke.arc.Core;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.GridBits;
import io.anuke.arc.graphics.Color; import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.g2d.Draw; import io.anuke.arc.graphics.g2d.Draw;
import io.anuke.arc.graphics.g2d.Lines; 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.graphics.Pal;
import io.anuke.mindustry.input.Binding; import io.anuke.mindustry.input.Binding;
import io.anuke.mindustry.ui.GridImage; 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{ public class MapView extends Element implements GestureListener{
private MapEditor editor; private MapEditor editor;
private EditorTool tool = EditorTool.pencil; private EditorTool tool = EditorTool.pencil;
//private OperationStack stack = new OperationStack(); //private OperationStack stack = new OperationStack();
//private DrawOperation op; //private DrawOperation op;
private GridBits used; //private GridBits used;
private Bresenham2 br = new Bresenham2(); private Bresenham2 br = new Bresenham2();
private boolean updated = false; private boolean updated = false;
private float offsetx, offsety; private float offsetx, offsety;
@@ -88,13 +87,6 @@ public class MapView extends Element implements GestureListener{
mousex = x; mousex = x;
mousey = y; 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; updated = false;
Point2 p = project(x, y); Point2 p = project(x, y);
@@ -141,12 +133,7 @@ public class MapView extends Element implements GestureListener{
updated = true; updated = true;
} }
if(op != null && updated){ editor.flushOp();
if(!op.isEmpty()){
stack.add(op);
}
op = null;
}
if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){ if(button == KeyCode.MOUSE_MIDDLE && lastTool != null){
tool = lastTool; tool = lastTool;
@@ -196,14 +183,6 @@ public class MapView extends Element implements GestureListener{
this.tool = tool; this.tool = tool;
} }
public void clearStack(){
stack.clear();
}
public OperationStack getStack(){
return stack;
}
public boolean isGrid(){ public boolean isGrid(){
return grid; return grid;
} }
@@ -212,27 +191,6 @@ public class MapView extends Element implements GestureListener{
this.grid = grid; 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 @Override
public void act(float delta){ public void act(float delta){
super.act(delta); super.act(delta);
@@ -266,14 +224,14 @@ public class MapView extends Element implements GestureListener{
} }
private Point2 project(float x, float y){ 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 size = Math.min(width, height);
float sclwidth = size * zoom; float sclwidth = size * zoom;
float sclheight = size * zoom * ratio; float sclheight = size * zoom * ratio;
x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * world.width(); x = (x - getWidth() / 2 + sclwidth / 2 - offsetx * zoom) / sclwidth * editor.width();
y = (y - getHeight() / 2 + sclheight / 2 - offsety * zoom) / sclheight * world.height(); 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)); return Tmp.g1.set((int) (x - 0.5f), (int) (y - 0.5f));
}else{ }else{
return Tmp.g1.set((int) x, (int) y); 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){ 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 size = Math.min(width, height);
float sclwidth = size * zoom; float sclwidth = size * zoom;
float sclheight = size * zoom * ratio; float sclheight = size * zoom * ratio;
float px = ((float) x / world.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2; float px = ((float) x / editor.width()) * sclwidth + offsetx * zoom - sclwidth / 2 + getWidth() / 2;
float py = ((float) (y) / world.height()) * sclheight float py = ((float) (y) / editor.height()) * sclheight
+ offsety * zoom - sclheight / 2 + getHeight() / 2; + offsety * zoom - sclheight / 2 + getHeight() / 2;
return vec.set(px, py); return vec.set(px, py);
} }
@Override @Override
public void draw(){ 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 size = Math.min(width, height);
float sclwidth = size * zoom; float sclwidth = size * zoom;
float sclheight = size * zoom * ratio; float sclheight = size * zoom * ratio;
float centerx = x + width / 2 + offsetx * zoom; float centerx = x + width / 2 + offsetx * zoom;
float centery = y + height / 2 + offsety * 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))){ if(!ScissorStack.pushScissors(rect.set(x, y, width, height))){
return; return;
@@ -325,18 +283,18 @@ public class MapView extends Element implements GestureListener{
int index = 0; int index = 0;
for(int i = 0; i < MapEditor.brushSizes.length; i++){ for(int i = 0; i < MapEditor.brushSizes.length; i++){
if(editor.getBrushSize() == MapEditor.brushSizes[i]){ if(editor.brushSize == MapEditor.brushSizes[i]){
index = i; index = i;
break; break;
} }
} }
float scaling = zoom * Math.min(width, height) / world.width(); float scaling = zoom * Math.min(width, height) / editor.width();
Draw.color(Pal.accent); Draw.color(Pal.accent);
Lines.stroke(Unit.dp.scl(2f)); 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){ if(tool == EditorTool.line && drawing){
Vector2 v1 = unproject(startx, starty).add(x, y); Vector2 v1 = unproject(startx, starty).add(x, y);
float sx = v1.x, sy = v1.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)){ if((tool.edit || tool == EditorTool.line) && (!mobile || drawing)){
Point2 p = project(mousex, mousey); Point2 p = project(mousex, mousey);
Vector2 v = unproject(p.x, p.y).add(x, y); 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( Lines.square(
v.x + scaling / 2f + offset, v.x + scaling / 2f + offset,
v.y + scaling / 2f + offset, v.y + scaling / 2f + offset,
scaling * editor.getDrawBlock().size / 2f); scaling * editor.drawBlock.size / 2f);
} }
} }

View File

@@ -7,12 +7,15 @@ import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap; import io.anuke.arc.graphics.Pixmap;
import io.anuke.arc.graphics.Pixmap.Format; import io.anuke.arc.graphics.Pixmap.Format;
import io.anuke.arc.util.Pack; import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks; import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.MappableContent; import io.anuke.mindustry.game.MappableContent;
import io.anuke.mindustry.game.Team; import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map; import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.Block; 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.Tile;
import io.anuke.mindustry.world.blocks.BlockPart; import io.anuke.mindustry.world.blocks.BlockPart;
@@ -57,16 +60,46 @@ public class MapIO{
return pixmap; return pixmap;
} }
//TODO implement
/**Reads a pixmap in the 3.5 pixmap format.*/ /**Reads a pixmap in the 3.5 pixmap format.*/
public static Tile[][] unfinished_readLegacyPixmap(Pixmap pixmap){ public static void readLegacyPixmap(Pixmap pixmap, Tile[][] tiles){
return null; 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 //TODO implement
/**Reads a pixmap in the old 4.0 .mmap format.*/ /**Reads a pixmap in the old 4.0 .mmap format.*/
private static Tile[][] unfinished_readLegacyMmap(InputStream stream) throws IOException{ private static void readLegacyMmap(FileHandle file, Tile[][] tiles) throws IOException{
return null;
} }
public static int colorFor(Block floor, Block wall, Team team){ 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); 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)){ try(DataOutputStream stream = new DataOutputStream(output)){
stream.writeInt(version); stream.writeInt(version);
stream.writeInt(Version.build); stream.writeInt(Version.build);

View File

@@ -19,7 +19,7 @@ public class SaveMeta{
this.build = build; this.build = build;
this.timestamp = timestamp; this.timestamp = timestamp;
this.timePlayed = timePlayed; this.timePlayed = timePlayed;
this.map = world.maps.getByName(map); this.map = world.maps.all().find(m -> m.fileName().equals(map));
this.wave = wave; this.wave = wave;
this.rules = rules; this.rules = rules;
} }

View File

@@ -1,6 +1,6 @@
package io.anuke.mindustry.io.versions; 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.arc.util.Time;
import io.anuke.mindustry.game.Version; import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.gen.Serialization; import io.anuke.mindustry.gen.Serialization;
@@ -34,8 +34,8 @@ public class Save16 extends SaveFileVersion{
state.rules.spawns = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get().spawns; state.rules.spawns = content.<Zone>getByID(ContentType.zone, state.rules.zone).rules.get().spawns;
} }
String mapname = stream.readUTF(); String mapname = stream.readUTF();
Map map = world.maps.getByName(mapname); Map map = world.maps.all().find(m -> m.fileName().equals(mapname));
if(map == null) map = new Map(Strings.capitalize(mapname), 1, 1); if(map == null) map = new Map(customMapDirectory.child(mapname), 1, 1, new ObjectMap<>(), true);
world.setMap(map); world.setMap(map);
int wave = stream.readInt(); int wave = stream.readInt();
@@ -62,7 +62,7 @@ public class Save16 extends SaveFileVersion{
//--GENERAL STATE-- //--GENERAL STATE--
Serialization.writeRules(stream, state.rules); 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.writeInt(state.wave); //wave
stream.writeFloat(state.wavetime); //wave countdown stream.writeFloat(state.wavetime); //wave countdown

View File

@@ -2,6 +2,7 @@ package io.anuke.mindustry.maps;
import io.anuke.arc.Core; import io.anuke.arc.Core;
import io.anuke.arc.collection.Array; import io.anuke.arc.collection.Array;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.files.FileHandle; import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Texture; import io.anuke.arc.graphics.Texture;
import io.anuke.arc.util.Disposable; import io.anuke.arc.util.Disposable;
@@ -34,6 +35,10 @@ public class Maps implements Disposable{
return maps.select(m -> !m.custom); 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. * 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. * Does not add this map to the map list.
@@ -62,10 +67,32 @@ public class Maps implements Disposable{
loadCustomMaps(); loadCustomMaps();
} }
/** Save a map. This updates all values and stored data necessary. */ /** Save a custom map to the directory. This updates all values and stored data necessary.
public void saveMap(Map map, Tile[][] data){ * The tags are copied to prevent mutation later.*/
public void saveMap(ObjectMap<String, String> baseTags, Tile[][] data){
try{ try{
MapIO.writeMap(map.file.write(false), map, data); ObjectMap<String, String> 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){ if(!headless){
map.texture = new Texture(MapIO.generatePreview(data)); map.texture = new Texture(MapIO.generatePreview(data));
} }

View File

@@ -27,10 +27,10 @@ public class NetworkIO{
try(DataOutputStream stream = new DataOutputStream(os)){ try(DataOutputStream stream = new DataOutputStream(os)){
//--GENERAL STATE-- //--GENERAL STATE--
Serialization.writeRules(stream, state.rules); Serialization.writeRules(stream, state.rules);
stream.writeUTF(world.getMap().name); //map name stream.writeUTF(world.getMap().name()); //map name
//write tags //write tags
ObjectMap<String, String> tags = world.getMap().meta.tags; ObjectMap<String, String> tags = world.getMap().tags;
stream.writeByte(tags.size); stream.writeByte(tags.size);
for(Entry<String, String> entry : tags.entries()){ for(Entry<String, String> entry : tags.entries()){
stream.writeUTF(entry.key); stream.writeUTF(entry.key);
@@ -105,7 +105,7 @@ public class NetworkIO{
//map //map
world.spawner.read(stream); world.spawner.read(stream);
SaveIO.getSaveWriter().readMap(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(); state.teams = new Teams();
@@ -141,7 +141,7 @@ public class NetworkIO{
int maxlen = 32; int maxlen = 32;
String host = (headless ? "Server" : players[0].name); 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)); host = host.substring(0, Math.min(host.length(), maxlen));
map = map.substring(0, Math.min(map.length(), maxlen)); map = map.substring(0, Math.min(map.length(), maxlen));

View File

@@ -106,7 +106,7 @@ public class CustomGameDialog extends FloatingDialog{
image.row(); image.row();
image.add("[accent]" + map.getDisplayName()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center); image.add("[accent]" + map.getDisplayName()).pad(3f).growX().wrap().get().setAlignment(Align.center, Align.center);
image.row(); 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); BorderImage border = new BorderImage(map.texture, 3f);
border.setScaling(Scaling.fit); border.setScaling(Scaling.fit);

View File

@@ -29,12 +29,15 @@ public class MapsDialog extends FloatingDialog{
buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> { buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> {
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> { Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
try{ 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()); 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)); 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", () -> { ui.showConfirm("$confirm", "$editor.overwrite.confirm", () -> {
world.maps.importMap(file, map); world.maps.importMap(file, map);
setup(); setup();
@@ -138,7 +141,7 @@ public class MapsDialog extends FloatingDialog{
table.addImageTextButton("$editor.openin", "icon-load-map", 16 * 2, () -> { table.addImageTextButton("$editor.openin", "icon-load-map", 16 * 2, () -> {
try{ try{
Vars.ui.editor.beginEditMap(map.stream.get()); Vars.ui.editor.beginEditMap(map.file);
dialog.hide(); dialog.hide();
hide(); hide();
}catch(Exception e){ }catch(Exception e){
@@ -148,7 +151,7 @@ public class MapsDialog extends FloatingDialog{
}).fillX().height(54f).marginLeft(10); }).fillX().height(54f).marginLeft(10);
table.addImageTextButton("$delete", "icon-trash-16", 16 * 2, () -> { 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); world.maps.removeMap(map);
dialog.hide(); dialog.hide();
setup(); setup();

View File

@@ -215,6 +215,14 @@ public class Tile implements Position, TargetTrait{
return link != 0; 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. */ /** 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){ public void setLinked(byte dx, byte dy){
setBlock(Blocks.part); setBlock(Blocks.part);

View File

@@ -54,9 +54,9 @@ public class DesktopPlatform extends Platform{
if(world.getMap() == null){ if(world.getMap() == null){
presence.details = "Unknown Map"; presence.details = "Unknown Map";
}else if(!state.rules.waves){ }else if(!state.rules.waves){
presence.details = Strings.capitalize(world.getMap().name); presence.details = Strings.capitalize(world.getMap().getDisplayName());
}else{ }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; presence.largeImageText = "Wave " + state.wave;
} }