Editor<->world data merging

This commit is contained in:
Anuken
2019-05-06 17:03:53 -04:00
parent 51f9ad5a2c
commit 3035d569cc
13 changed files with 108 additions and 160 deletions

View File

@@ -18,7 +18,7 @@ import io.anuke.mindustry.gen.Call;
import io.anuke.mindustry.input.*;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.net.Net;
import io.anuke.mindustry.type.Item;
import io.anuke.mindustry.type.*;
import io.anuke.mindustry.ui.dialogs.FloatingDialog;
import io.anuke.mindustry.world.Tile;
@@ -207,6 +207,23 @@ public class Control implements ApplicationListener{
});
}
public void playZone(Zone zone){
ui.loadAnd(() -> {
logic.reset();
state.rules = zone.rules.get();
state.rules.zone = zone;
world.loadGenerator(zone.generator);
for(Tile core : state.teams.get(defaultTeam).cores){
for(ItemStack stack : zone.getStartingItems()){
core.entity.items.add(stack.item, stack.amount);
}
}
state.set(State.playing);
control.saves.zoneSave();
logic.play();
});
}
public boolean isHighScore(){
return hiscore;
}

View File

@@ -202,24 +202,6 @@ public class World implements ApplicationListener{
return state.rules.zone;
}
//TODO move to Control
public void playZone(Zone zone){
ui.loadAnd(() -> {
logic.reset();
state.rules = zone.rules.get();
state.rules.zone = zone;
loadGenerator(zone.generator);
for(Tile core : state.teams.get(defaultTeam).cores){
for(ItemStack stack : zone.getStartingItems()){
core.entity.items.add(stack.item, stack.amount);
}
}
state.set(State.playing);
control.saves.zoneSave();
logic.play();
});
}
public void loadGenerator(Generator generator){
beginMapLoad();
@@ -231,18 +213,9 @@ public class World implements ApplicationListener{
}
public void loadMap(Map map){
beginMapLoad();
this.currentMap = map;
try{
createTiles(map.width, map.height);
for(int x = 0; x < map.width; x++){
for(int y = 0; y < map.height; y++){
tiles[x][y] = new Tile(x, y);
}
}
MapIO.readTiles(map, tiles);
prepareTiles(tiles);
MapIO.loadMap(map);
}catch(Exception e){
Log.err(e);
if(!headless){
@@ -254,7 +227,7 @@ public class World implements ApplicationListener{
return;
}
endMapLoad();
this.currentMap = map;
invalidMap = false;
@@ -503,7 +476,7 @@ public class World implements ApplicationListener{
}
}
//update cliffs, occlusion data
//update occlusion data
for(int x = 0; x < tiles.length; x++){
for(int y = 0; y < tiles[0].length; y++){
Tile tile = tiles[x][y];

View File

@@ -60,9 +60,8 @@ public class DrawOperation{
class TileOpStruct{
short x;
short y;
short value;
byte type;
byte from;
byte to;
}
public enum OpType{

View File

@@ -11,10 +11,11 @@ import io.anuke.mindustry.world.modules.*;
import static io.anuke.mindustry.Vars.ui;
//TODO somehow remove or replace this class with a more flexible solution
public class EditorTile extends Tile{
public EditorTile(int x, int y, byte floor, byte wall){
super(x, y, floor, wall);
public EditorTile(int x, int y, short floor, short overlay, short wall){
super(x, y, floor, overlay, wall);
}
@Override

View File

@@ -1,9 +1,9 @@
package io.anuke.mindustry.editor;
import io.anuke.arc.collection.ObjectMap;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.math.Mathf;
import io.anuke.arc.util.Pack;
import io.anuke.arc.util.Structs;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team;
@@ -12,16 +12,18 @@ import io.anuke.mindustry.io.MapIO;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.Block;
import io.anuke.mindustry.world.Tile;
import io.anuke.mindustry.world.blocks.BlockPart;
import io.anuke.mindustry.world.blocks.Floor;
import java.io.IOException;
import static io.anuke.mindustry.Vars.world;
public class MapEditor{
public static final int[] brushSizes = {1, 2, 3, 4, 5, 9, 15, 20};
private ObjectMap<String, String> tags = new ObjectMap<>();
private StringMap tags = new StringMap();
private MapRenderer renderer = new MapRenderer(this);
private Tile[][] tiles;
private OperationStack stack = new OperationStack();
private DrawOperation currentOp;
@@ -32,7 +34,7 @@ public class MapEditor{
public Block drawBlock = Blocks.stone;
public Team drawTeam = Team.blue;
public ObjectMap<String, String> getTags(){
public StringMap getTags(){
return tags;
}
@@ -40,7 +42,7 @@ public class MapEditor{
reset();
loading = true;
tiles = createTiles(width, height);
createTiles(width, height);
renderer.resize(width(), height());
loading = false;
}
@@ -49,30 +51,25 @@ public class MapEditor{
reset();
loading = true;
tiles = createTiles(map.width, map.height);
//TODO redundant and does nothing since tiles are overwritten
createTiles(map.width, map.height);
tags.putAll(map.tags);
MapIO.readTiles(map, tiles);
//TODO this actually creates the tiles, which are not editor tiles
MapIO.loadMap(map);
checkLinkedTiles();
renderer.resize(width(), height());
loading = false;
}
public void beginEdit(Tile[][] tiles){
reset();
this.tiles = tiles;
checkLinkedTiles();
renderer.resize(width(), height());
}
//adds missing blockparts
public void checkLinkedTiles(){
Tile[][] tiles = world.getTiles();
//clear block parts first
for(int x = 0; x < width(); x++){
for(int y = 0; y < height(); y++){
if(tiles[x][y].block() == Blocks.part){
if(tiles[x][y].block() instanceof BlockPart){
tiles[x][y].setBlock(Blocks.air);
tiles[x][y].setLinkByte((byte)0);
}
}
}
@@ -80,22 +77,8 @@ public class MapEditor{
//set up missing blockparts
for(int x = 0; x < width(); x++){
for(int y = 0; y < height(); y++){
Block drawBlock = tiles[x][y].block();
if(drawBlock.isMultiblock()){
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, width(), height()) && !(dx + offsetx == 0 && dy + offsety == 0)){
Tile tile = tiles[worldx][worldy];
tile.setBlock(Blocks.part);
tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8)));
}
}
}
if(tiles[x][y].block().isMultiblock()){
world.setBlock(tiles[x][y], tiles[x][y].block(), tiles[x][y].getTeam());
}
}
}
@@ -108,15 +91,14 @@ public class MapEditor{
}
/** Creates a 2-D array of EditorTiles with stone as the floor block. */
public Tile[][] createTiles(int width, int height){
tiles = new Tile[width][height];
private void createTiles(int width, int height){
Tile[][] tiles = world.createTiles(width, height);
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
}
}
return tiles;
}
public Map createMap(FileHandle file){
@@ -131,40 +113,19 @@ public class MapEditor{
}
public Tile[][] tiles(){
return tiles;
return world.getTiles();
}
public Tile tile(int x, int y){
return tiles[x][y];
return world.rawTile(x, y);
}
public int width(){
return tiles.length;
return world.width();
}
public int height(){
return tiles[0].length;
}
public void updateLinks(Block block, int x, int y){
int offsetx = -(block.size - 1) / 2;
int offsety = -(block.size - 1) / 2;
for(int dx = 0; dx < block.size; dx++){
for(int dy = 0; dy < block.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, width(), height())){
Tile tile = tiles[worldx][worldy];
if(!(worldx == x && worldy == y)){
tile.setBlock(Blocks.part);
tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8)));
}
}
}
}
return world.height();
}
public void draw(int x, int y, boolean paint){
@@ -177,45 +138,34 @@ public class MapEditor{
public void draw(int x, int y, boolean paint, Block drawBlock, double chance){
boolean isfloor = drawBlock instanceof Floor && drawBlock != Blocks.air;
Tile[][] tiles = world.getTiles();
if(drawBlock.isMultiblock()){
x = Mathf.clamp(x, (drawBlock.size - 1) / 2, width() - drawBlock.size / 2 - 1);
y = Mathf.clamp(y, (drawBlock.size - 1) / 2, height() - drawBlock.size / 2 - 1);
int offsetx = -(drawBlock.size - 1) / 2;
int offsety = -(drawBlock.size - 1) / 2;
for(int i = 0; i < 2; i++){
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
for(int dx = 0; dx < drawBlock.size; dx++){
for(int dy = 0; dy < drawBlock.size; dy++){
int worldx = dx + offsetx + x;
int worldy = dy + offsety + y;
if(Structs.inBounds(worldx, worldy, width(), height())){
Tile tile = tiles[worldx][worldy];
if(Structs.inBounds(worldx, worldy, width(), height())){
Tile tile = tiles[worldx][worldy];
if(i == 1){
tile.setBlock(Blocks.part);
tile.setLinkByte(Pack.byteByte((byte)(dx + offsetx + 8), (byte)(dy + offsety + 8)));
}else{
byte link = tile.getLinkByte();
Block block = tile.block();
Block block = tile.block();
if(link != 0){
removeLinked(worldx - (Pack.leftByte(link) - 8), worldy - (Pack.rightByte(link) - 8));
}else if(block.isMultiblock()){
removeLinked(worldx, worldy);
}
}
//bail out if there's anything blocking the way
if(block.isMultiblock() || block instanceof BlockPart){
return;
}
}
}
}
Tile tile = tiles[x][y];
tile.setBlock(drawBlock);
tile.setTeam(drawTeam);
world.setBlock(tiles[x][y], drawBlock, drawTeam);
}else{
for(int rx = -brushSize; rx <= brushSize; rx++){
for(int ry = -brushSize; ry <= brushSize; ry++){
@@ -228,14 +178,8 @@ public class MapEditor{
Tile tile = tiles[wx][wy];
if(!isfloor){
byte link = tile.getLinkByte();
if(tile.block().isMultiblock()){
removeLinked(wx, wy);
}else if(link != 0 && tiles[wx][wy].block() == Blocks.part){
removeLinked(wx - (Pack.leftByte(link) - 8), wy - (Pack.rightByte(link) - 8));
}
if(!isfloor && (tile.isLinked() || tile.block().isMultiblock())){
world.removeBlock(tile.link());
}
if(isfloor){
@@ -255,22 +199,6 @@ public class MapEditor{
}
}
private void removeLinked(int x, int y){
Block block = tiles[x][y].block();
int offsetx = -(block.size - 1) / 2;
int offsety = -(block.size - 1) / 2;
for(int dx = 0; dx < block.size; dx++){
for(int dy = 0; dy < block.size; dy++){
int worldx = x + dx + offsetx, worldy = y + dy + offsety;
if(Structs.inBounds(worldx, worldy, width(), height())){
tiles[worldx][worldy].setTeam(Team.none);
tiles[worldx][worldy].setBlock(Blocks.air);
}
}
}
}
public MapRenderer renderer(){
return renderer;
}
@@ -278,11 +206,11 @@ public class MapEditor{
public void resize(int width, int height){
clearOp();
Tile[][] previous = tiles;
Tile[][] previous = world.getTiles();
int offsetX = -(width - width()) / 2, offsetY = -(height - height()) / 2;
loading = true;
tiles = new Tile[width][height];
Tile[][] tiles = world.createTiles(width, height);
for(int x = 0; x < width; x++){
for(int y = 0; y < height; y++){
int px = offsetX + x, py = offsetY + y;
@@ -291,7 +219,7 @@ public class MapEditor{
tiles[x][y].x = (short)x;
tiles[x][y].y = (short)y;
}else{
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (byte)0);
tiles[x][y] = new EditorTile(x, y, Blocks.stone.id, (short)0, (short)0);
}
}
}

View File

@@ -91,7 +91,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
Platform.instance.showFileChooser("$editor.loadmap", "Map Files", file -> ui.loadAnd(() -> {
try{
//TODO what if it's an image? users should be warned for their stupidity
editor.beginEdit(MapIO.readMap(file, true));
editor.beginEdit(MapIO.createMap(file, true));
}catch(Exception e){
ui.showError(Core.bundle.format("editor.errorload", Strings.parseException(e, false)));
Log.err(e);
@@ -216,7 +216,7 @@ public class MapEditorDialog extends Dialog implements Disposable{
if(map != null && !map.custom){
ui.showError("$editor.save.overwrite");
}else{
world.maps.saveMap(editor.getTags(), editor.tiles());
world.maps.saveMap(editor.getTags());
ui.showInfoFade("$editor.saved");
}
}

View File

@@ -1,18 +1,23 @@
package io.anuke.mindustry.io;
import io.anuke.arc.collection.StringMap;
import io.anuke.arc.files.FileHandle;
import io.anuke.arc.graphics.Color;
import io.anuke.arc.graphics.Pixmap;
import io.anuke.arc.graphics.Pixmap.Format;
import io.anuke.arc.util.Time;
import io.anuke.arc.util.io.CounterInputStream;
import io.anuke.mindustry.content.Blocks;
import io.anuke.mindustry.game.Team;
import io.anuke.mindustry.game.Version;
import io.anuke.mindustry.maps.Map;
import io.anuke.mindustry.world.*;
import io.anuke.mindustry.world.blocks.Floor;
import java.io.IOException;
import java.io.InputStream;
import java.io.*;
import java.util.zip.InflaterInputStream;
import static io.anuke.mindustry.Vars.bufferSize;
/** Reads and writes map files. */
public class MapIO{
@@ -31,6 +36,21 @@ public class MapIO{
}
}
public static Map createMap(FileHandle file, boolean custom) throws IOException{
try(InputStream is = new InflaterInputStream(file.read(bufferSize)); CounterInputStream counter = new CounterInputStream(is); DataInputStream stream = new DataInputStream(counter)){
SaveIO.readHeader(stream);
int version = stream.readInt();
SaveVersion ver = SaveIO.getSaveWriter(version);
StringMap tags = new StringMap();
ver.region("meta", stream, counter, in -> tags.putAll(ver.readStringMap(in)));
return new Map(file, tags.getInt("width"), tags.getInt("height"), tags, custom, version, Version.build);
}
}
public static void loadMap(Map map){
SaveIO.load(map.file);
}
public static Pixmap generatePreview(Map map) throws IOException{
Time.mark();
Pixmap floors = new Pixmap(map.width, map.height, Format.RGBA8888);

View File

@@ -67,8 +67,12 @@ public abstract class SaveFileReader{
return length;
}
/** Skip a chunk completely. */
public void skipChunk(DataInput input, boolean isByte) throws IOException{
public void skipRegion(DataInput input) throws IOException{
skipRegion(input, false);
}
/** Skip a region completely. */
public void skipRegion(DataInput input, boolean isByte) throws IOException{
int length = readChunk(input, isByte, t -> {});
int skipped = input.skipBytes(length);
if(length != skipped){

View File

@@ -30,6 +30,10 @@ public class SaveIO{
return versionArray.peek();
}
public static SaveVersion getSaveWriter(int version){
return versions.get(version);
}
public static void saveToSlot(int slot){
FileHandle file = fileFor(slot);
boolean exists = file.exists();

View File

@@ -53,7 +53,9 @@ public abstract class SaveVersion extends SaveFileReader{
"wave", state.wave,
"wavetime", state.wavetime,
"stats", Serialization.writeStatsJson(state.stats),
"rules", Serialization.writeRulesJson(state.rules)
"rules", Serialization.writeRulesJson(state.rules),
"width", world.width(),
"height", world.height()
));
}

View File

@@ -52,7 +52,7 @@ public class Maps implements Disposable{
FileHandle file = Core.files.internal("maps/" + name + "." + mapExtension);
try{
return MapIO.readMap(file, false);
return MapIO.createMap(file, false);
}catch(IOException e){
throw new RuntimeException(e);
}
@@ -81,13 +81,12 @@ public class Maps implements Disposable{
* Save a custom map to the directory. This updates all values and stored data necessary.
* The tags are copied to prevent mutation later.
*/
public void saveMap(ObjectMap<String, String> baseTags, Tile[][] data){
public void saveMap(ObjectMap<String, String> baseTags){
try{
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);
FileHandle file;
//find map with the same exact display name
@@ -106,7 +105,7 @@ public class Maps implements Disposable{
}
//create map, write it, etc etc etc
Map map = new Map(file, data.length, data[0].length, tags, true);
Map map = new Map(file, world.width(), world.height(), tags, true);
MapIO.writeMap(file, map, data);
if(!headless){
@@ -171,7 +170,7 @@ public class Maps implements Disposable{
}
private void loadMap(FileHandle file, boolean custom) throws IOException{
Map map = MapIO.readMap(file, custom);
Map map = MapIO.createMap(file, custom);
if(map.name() == null){
throw new IOException("Map name cannot be empty! File: " + file);

View File

@@ -24,7 +24,7 @@ public class MapsDialog extends FloatingDialog{
buttons.addImageTextButton("$editor.importmap", "icon-add", 14 * 2, () -> {
Platform.instance.showFileChooser("$editor.importmap", "Map File", file -> {
try{
Map map = MapIO.readMap(file, true);
Map map = MapIO.createMap(file, true);
String name = map.tags.get("name");
if(name == null){
ui.showError("$editor.errorname");