package me.hammerle.supersnuvi.gamelogic; import java.io.File; import java.io.FileInputStream; import java.io.FileOutputStream; import java.io.IOException; import java.util.HashMap; import java.util.Map.Entry; public class LevelData { private final static int STRING_ID = 1; private final static int INTEGER_ID = 2; // container for all custom data private final HashMap data; // layers up to bIndex (including) are considered background // the next layer is the entity spawn layer // every layer after that is foreground private int bIndex; private int width; private int height; private int layers; // storage for tiles // [layer][width][height] private int[][][] tiles; // the file of the level for saving and loading private final File file; public LevelData(File file) { this.file = file; this.data = new HashMap<>(); this.bIndex = 0; this.width = 0; this.height = 0; this.layers = 0; this.tiles = null; } public LevelData(File file, int bIndex, int layer, int width, int height) { this.file = file; this.data = new HashMap<>(); this.bIndex = bIndex; this.width = width; this.height = height; this.layers = layer; this.tiles = new int[layer][width][height]; for(int l = 0; l < layer; l++) { for(int x = 0; x < width; x++) { for(int y = 0; y < height; y++) { tiles[l][x][y] = -1; } } } } // ------------------------------------------------------------------------- // file writing / reading // ------------------------------------------------------------------------- private void writeString(FileOutputStream out, String s, boolean identifier) throws IOException { // writing type identifier if(identifier) { out.write(STRING_ID); } // writing length of string byte[] b = s.getBytes(); writeInt(out, b.length, false); // writing the bytes of the string out.write(b); } private String readString(FileInputStream in) throws IOException { int length = readInt(in); byte[] b = new byte[length]; in.read(b); if(length > 1024 * 1024) { throw new IOException("invalid level file"); } return new String(b); } private void writeInt(FileOutputStream out, int i, boolean identifier) throws IOException { // writing type identifier if(identifier) { out.write(INTEGER_ID); } // writing the int per byte out.write(i & 0xFF); out.write((i >> 8) & 0xFF); out.write((i >> 16) & 0xFF); out.write((i >> 24) & 0xFF); } private int readInt(FileInputStream in) throws IOException { int i = 0; i |= in.read(); i |= in.read() << 8; i |= in.read() << 16; i |= in.read() << 24; return i; } private void writeRepeated(int count, int currentTile, FileOutputStream out) throws IOException { while(true) { if(count <= 0) { break; } else if(count >= 255) { count -= 255; out.write(255); writeInt(out, currentTile, false); } else { out.write(count); writeInt(out, currentTile, false); break; } } } public boolean save() { if(file == null) { return false; } try { try(FileOutputStream out = new FileOutputStream(file)) { // writing number of data fields writeInt(out, data.size(), false); // writing keys and data, data varies, key are always strings for(Entry e : data.entrySet()) { writeString(out, e.getKey(), false); if(e.getValue() instanceof Integer) { writeInt(out, (Integer) e.getValue(), true); } else { writeString(out, e.getValue().toString(), true); } } // write background index writeInt(out, bIndex, false); // write number of layers writeInt(out, layers, false); // write layer width writeInt(out, width, false); // write layer height writeInt(out, height, false); // write tiles if(layers > 0 && width > 0 && height > 0) { int count = 0; int currentTile = tiles[0][0][0]; for(int x = 0; x < layers; x++) { for(int y = 0; y < width; y++) { for(int z = 0; z < height; z++) { if(currentTile == tiles[x][y][z]) { count++; } else { writeRepeated(count, currentTile, out); count = 1; currentTile = tiles[x][y][z]; } } } } writeRepeated(count, currentTile, out); } out.flush(); out.close(); } return true; } catch(IOException ex) { ex.printStackTrace(); return false; } } public boolean load() { if(file == null) { return false; } try { try (FileInputStream in = new FileInputStream(file)) { // getting the number of data fields int fields = readInt(in); // getting keys and data, data varies, key are always strings for(; fields > 0; fields--) { String key = readString(in); int id = in.read(); switch(id) { case STRING_ID: data.put(key, readString(in)); break; case INTEGER_ID: data.put(key, readInt(in)); break; default: System.err.println("fucked up level data"); return false; } } //data.forEach((k, v) -> System.out.println(k + " " + v + " " + v.getClass())); // getting background index bIndex = readInt(in); // getting number of layers layers = readInt(in); // getting layer width width = readInt(in); // getting layer height height = readInt(in); //System.out.println(layers + " " + width + " " + height); // init tiles tiles = new int[layers][width][height]; // getting tiles if(layers > 0 && width > 0 && height > 0) { int count = 0; int currentTile = -1; for(int x = 0; x < layers; x++) { for(int y = 0; y < width; y++) { for(int z = 0; z < height; z++) { if(count == 0) { count = in.read(); currentTile = readInt(in); //System.out.println(count + " " + currentTile); } tiles[x][y][z] = currentTile; count--; } } } } in.close(); //save(); } int x = bIndex + 1; for(int y = 0; y < width; y++) { for(int z = 0; z < height; z++) { if(tiles[x][y][z] == -1) { tiles[x][y][z] = 0; } } } return true; } catch(IOException ex) { ex.printStackTrace(); return false; } } // ------------------------------------------------------------------------- // variable level data // ------------------------------------------------------------------------- public void setData(String name, int value) { data.put(name, value); } public void setData(String name, String value) { data.put(name, value); } public int getInt(String name, int error) { Object o = data.get(name); if(o == null || !(o instanceof Integer)) { return error; } return (Integer) o; } public String getString(String name, String error) { Object o = data.get(name); if(o == null || !(o instanceof String)) { return error; } return (String) o; } // ------------------------------------------------------------------------- // level data getter / setter // ------------------------------------------------------------------------- public int getBackgroundIndex() { return bIndex; } public void setBackgroundIndex(int bIndex) { this.bIndex = bIndex; } public int getLayers() { return layers; } public int getWidth() { return width; } public int getHeight() { return height; } // ------------------------------------------------------------------------- // layer stuff // ------------------------------------------------------------------------- public void clearLayer(int index) { if(index >= 0 && index < layers) { for(int x = 0; x < width; x++) { for(int y = 0; y < height; y++) { tiles[index][x][y] = -1; } } } } public void copyLayer(int fromIndex, int toIndex) { if(fromIndex >= 0 && fromIndex < layers && toIndex >= 0 && toIndex < layers) { for(int x = 0; x < width; x++) { System.arraycopy(tiles[fromIndex][x], 0, tiles[toIndex][x], 0, height); } } } public void addLayer() { int[][][] newLayers = new int[layers + 1][][]; System.arraycopy(tiles, 0, newLayers, 0, layers); newLayers[layers] = new int[width][height]; for(int x = 0; x < width; x++) { for(int y = 0; y < height; y++) { newLayers[layers][x][y] = -1; } } layers++; tiles = newLayers; } public void addLayer(int index) { if(index >= layers) { addLayer(); return; } int[][][] newLayers = new int[layers + 1][][]; System.arraycopy(tiles, 0, newLayers, 0, index); newLayers[index] = new int[width][height]; for(int x = 0; x < width; x++) { for(int y = 0; y < height; y++) { newLayers[index][x][y] = -1; } } System.arraycopy(tiles, index, newLayers, index + 1, layers - index); layers++; tiles = newLayers; } public void removeLayer(int index) { if(layers <= 1) { layers = 0; tiles = new int[0][0][0]; return; } int[][][] newLayers = new int[layers - 1][][]; if(index > 0) { System.arraycopy(tiles, 0, newLayers, 0, index); } int left = layers - index - 1; if(left > 0) { System.arraycopy(tiles, index + 1, newLayers, index, left); } layers--; tiles = newLayers; } public void setLayerSize(int w, int h) { int[][][] newLayers = new int[layers][w][h]; int cw = Math.min(w, width); int ch = Math.min(h, height); for(int l = 0; l < layers; l++) { for(int x = 0; x < cw; x++) { System.arraycopy(tiles[l][x], 0, newLayers[l][x], 0, ch); } } width = w; height = h; tiles = newLayers; } // ------------------------------------------------------------------------- // tile getter / setter // ------------------------------------------------------------------------- public int getInteractionTile(int x, int y) { if(x < 0 || y < 0 || x >= width || y >= height || bIndex >= layers) { return -1; } return tiles[bIndex][x][y]; } public int getTile(int layer, int x, int y) { return tiles[layer][x][y]; } public void setTile(int layer, int x, int y, int value) { tiles[layer][x][y] = value; } // ------------------------------------------------------------------------- // entity layer // ------------------------------------------------------------------------- public void deactivateEntity(int x, int y) { tiles[bIndex + 1][x][y] = -Math.abs(tiles[bIndex + 1][x][y]); } public void activateEntities() { int[][] ent = tiles[bIndex + 1]; forEachEntity((x, y, tile) -> { if(tile < 0) { ent[x][y] = Math.abs(ent[x][y]); } }, 0, width, 0, height); } // ------------------------------------------------------------------------- // tile iterators // ------------------------------------------------------------------------- public void forEachBackground(TileConsumer c, int sx, int ex, int sy, int ey) { int max = Math.min(layers - 1, bIndex); sx = Math.max(0, sx); sy = Math.max(0, sy); ex = Math.min(width, ex); ey = Math.min(height, ey); for(int l = 0; l <= max; l++) { for(int x = sx; x < ex; x++) { for(int y = sy; y < ey; y++) { c.consume(x, y, tiles[l][x][y]); } } } } public void forEachInteractTile(TileConsumer c) { if(bIndex < layers) { for(int y = 0; y < width; y++) { for(int z = 0; z < height; z++) { c.consume(y, z, tiles[bIndex][y][z]); } } } } public void forEachEntity(TileConsumer c, int sx, int ex, int sy, int ey) { int l = bIndex + 1; if(l < layers) { sx = Math.max(0, sx); sy = Math.max(0, sy); ex = Math.min(width, ex); ey = Math.min(height, ey); int[][] ent = tiles[l]; for(int x = sx; x < ex; x++) { for(int y = sy; y < ey; y++) { c.consume(x, y, ent[x][y]); } } } } public void forEachForeground(TileConsumer c, int sx, int ex, int sy, int ey) { sx = Math.max(0, sx); sy = Math.max(0, sy); ex = Math.min(width, ex); ey = Math.min(height, ey); for(int l = bIndex + 2; l < layers; l++) { for(int x = sx; x < ex; x++) { for(int y = sy; y < ey; y++) { c.consume(x, y, tiles[l][x][y]); } } } } }