diff --git a/core/src/main/java/com/riiablo/map2/Chunk.java b/core/src/main/java/com/riiablo/map2/Chunk.java index 363923ef..13c30c89 100644 --- a/core/src/main/java/com/riiablo/map2/Chunk.java +++ b/core/src/main/java/com/riiablo/map2/Chunk.java @@ -13,6 +13,8 @@ import com.riiablo.map2.util.BucketPool; import com.riiablo.map2.util.DebugMode; import static com.badlogic.gdx.graphics.Color.RED; +import static com.riiablo.map2.DS1.MAX_LAYERS; +import static com.riiablo.map2.DT1.Tile.NUM_SUBTILES; import static com.riiablo.map2.DT1.Tile.SUBTILE_SIZE; /** acts as a dt1 Tile cache chunk of zone */ @@ -22,8 +24,8 @@ public class Chunk extends BBox implements Poolable, Disposable { public static Chunk obtain(int x, int y, int width, int height) { Chunk chunk = pool.obtain(); chunk.asBox(x, y, width, height); + chunk.layers = 0; chunk.numTiles = width * height / SUBTILE_SIZE; - chunk.tiles = tilePools.obtain(chunk.numTiles); return chunk; } @@ -33,8 +35,14 @@ public class Chunk extends BBox implements Poolable, Disposable { @Override public void dispose() { + layers = 0; numTiles = 0; - tilePools.free(tiles); + for (int i = 0, s = tiles.length; i < s; i++) { + if (tiles[i] != null) { + tilePools.free(tiles[i]); + tiles[i] = null; + } + } pool.free(this); } @@ -48,8 +56,27 @@ public class Chunk extends BBox implements Poolable, Disposable { .build(); // public Zone zone; + public int layers; public int numTiles; - public Tile[] tiles; + public final Tile[][] tiles = new Tile[MAX_LAYERS][]; + + Chunk init(int layers) { + if (this.layers != 0) throw new IllegalStateException("chunk already initialized"); + this.layers = layers; + for (int i = 0; i < MAX_LAYERS; i++) { + if ((layers & (1 << i)) != 0) { + tiles[i] = tilePools.obtain(numTiles); + } + } + + return this; + } + + Tile[] tiles(int layer) { + Tile[] tiles = this.tiles[layer]; + if (tiles == null) throw new IllegalArgumentException("layer(" + layer + ") does not exist!"); + return tiles; + } int color = MathUtils.random.nextInt() | 0xff; @@ -65,27 +92,32 @@ public class Chunk extends BBox implements Poolable, Disposable { break; case SUBTILE: pixmap.setColor(RED); - drawDebugSubtiles(pixmap, x, y); + drawDebugSubtiles(pixmap, x + xMin, y + yMin); break; } } void drawDebugSubtiles(Pixmap pixmap, int x, int y) { - Tile[] tiles = this.tiles; + int chunkWidth = width / SUBTILE_SIZE; + Tile[] tiles = this.tiles[4]; + if (tiles == null) return; // TODO: remove when above is corrected for (int i = 0, s = numTiles; i < s; i++) { Tile tile = tiles[i]; if (tile == null) continue; - drawDebugSubtile(tile, pixmap, x, y); + drawDebugSubtile(tile, pixmap, + x + (i % chunkWidth) * SUBTILE_SIZE, + y + (i / chunkWidth) * SUBTILE_SIZE); } } void drawDebugSubtile(Tile tile, Pixmap pixmap, int x, int y) { + // pixmap.drawPixel(x, y); // TODO: apply tile offset + subtile offset of flags id byte[] flags = tile.flags; - for (int i = 0; i < Tile.NUM_SUBTILES; i++) { + for (int i = 0; i < NUM_SUBTILES; i++) { byte flag = flags[i]; if (flag == 0) continue; - pixmap.drawPixel(x + i % 25, y + i / 25); + pixmap.drawPixel(x + (i % SUBTILE_SIZE), y + (i / SUBTILE_SIZE)); } } } diff --git a/core/src/main/java/com/riiablo/map2/DS1.java b/core/src/main/java/com/riiablo/map2/DS1.java index 4c5c7782..0ea267b4 100644 --- a/core/src/main/java/com/riiablo/map2/DS1.java +++ b/core/src/main/java/com/riiablo/map2/DS1.java @@ -13,6 +13,7 @@ public class DS1 { static final int MAX_FLOORS = 2; static final int MAX_SHADOWS = 1; static final int MAX_TAGS = 1; + static final int MAX_LAYERS = MAX_WALLS + MAX_FLOORS + MAX_SHADOWS + MAX_TAGS; String fileName; @@ -30,6 +31,7 @@ public class DS1 { int numFloors; int numTags; int numShadows; + int layers; int wallRun, wallLen; int[] walls; diff --git a/core/src/main/java/com/riiablo/map2/DS1Reader.java b/core/src/main/java/com/riiablo/map2/DS1Reader.java index b298fcbb..edb7a7ea 100644 --- a/core/src/main/java/com/riiablo/map2/DS1Reader.java +++ b/core/src/main/java/com/riiablo/map2/DS1Reader.java @@ -38,12 +38,12 @@ import static com.riiablo.map2.Orientation.UNKNOWN_20; public class DS1Reader { private static final Logger log = LogManager.getLogger(DS1Reader.class); - static final int WALL_OFFSET = 0; - static final int ORIENTATION_OFFSET = WALL_OFFSET + DS1.MAX_WALLS; - static final int FLOOR_OFFSET = ORIENTATION_OFFSET + DS1.MAX_WALLS; - static final int SHADOW_OFFSET = FLOOR_OFFSET + DS1.MAX_FLOORS; - static final int TAG_OFFSET = SHADOW_OFFSET + DS1.MAX_SHADOWS; - static final int MAX_LAYERS = TAG_OFFSET + DS1.MAX_TAGS; + private static final int WALL_OFFSET = 0; + private static final int ORIENTATION_OFFSET = WALL_OFFSET + DS1.MAX_WALLS; + private static final int FLOOR_OFFSET = ORIENTATION_OFFSET + DS1.MAX_WALLS; + private static final int SHADOW_OFFSET = FLOOR_OFFSET + DS1.MAX_FLOORS; + private static final int TAG_OFFSET = SHADOW_OFFSET + DS1.MAX_SHADOWS; + private static final int MAX_LAYERS = TAG_OFFSET + DS1.MAX_TAGS; static final int WALL_LAYER_0 = 0; static final int WALL_LAYER_1 = 1; @@ -157,18 +157,25 @@ public class DS1Reader { if (version < 4) { ds1.numWalls = 1; ds1.numFloors = 1; - ds1.numTags = 1; ds1.numShadows = 1; + ds1.numTags = 1; } else { ds1.numWalls = in.readSafe32u(); ds1.numFloors = version < 16 ? 1 : in.readSafe32u(); - ds1.numTags = version >= 10 && (ds1.tagType == 1 || ds1.tagType == 2) ? 1 : 0; ds1.numShadows = 1; + ds1.numTags = version >= 10 && (ds1.tagType == 1 || ds1.tagType == 2) ? 1 : 0; } log.trace("layers: {} walls (+{} orients) + {} floors + {} shadows + {} tags", ds1.numWalls, ds1.numWalls, ds1.numFloors, ds1.numShadows, ds1.numTags); + ds1.layers |= ((1 << ds1.numWalls) - 1); + ds1.layers |= ((1 << ds1.numFloors) - 1) << (DS1.MAX_WALLS); + ds1.layers |= ((1 << ds1.numShadows) - 1) << (DS1.MAX_WALLS + DS1.MAX_FLOORS); + ds1.layers |= ((1 << ds1.numTags) - 1) << (DS1.MAX_WALLS + DS1.MAX_FLOORS + DS1.MAX_SHADOWS); + + log.tracef("layer flags: %08x", ds1.layers); + ds1.specialTiles = new IntMap<>(8); try { diff --git a/core/src/main/java/com/riiablo/map2/Map.java b/core/src/main/java/com/riiablo/map2/Map.java index c3f60a31..f8ef5cfa 100644 --- a/core/src/main/java/com/riiablo/map2/Map.java +++ b/core/src/main/java/com/riiablo/map2/Map.java @@ -1,11 +1,12 @@ package com.riiablo.map2; import com.badlogic.gdx.utils.Array; +import com.badlogic.gdx.utils.Disposable; import com.badlogic.gdx.utils.IntMap; import com.riiablo.map2.util.ZoneGraph; -public class Map { +public class Map implements Disposable { /** * LevelType.Id -> TileGenerator * I.e., procedurally generates a random tile using its specifier @@ -15,6 +16,15 @@ public class Map { final ZoneGraph zones = new ZoneGraph(); final Array chunks = new Array<>(); + @Override + public void dispose() { + zones.dispose(); + } + + public ZoneGraph zones() { + return zones; + } + void addZone(int x, int y, int width, int height) { // Zone zone = zones.claim(x, y, width, height); } diff --git a/core/src/main/java/com/riiablo/map2/MapGenerator.java b/core/src/main/java/com/riiablo/map2/MapGenerator.java index 56ea62d5..0a8210c7 100644 --- a/core/src/main/java/com/riiablo/map2/MapGenerator.java +++ b/core/src/main/java/com/riiablo/map2/MapGenerator.java @@ -11,10 +11,11 @@ public class MapGenerator { public TileGenerator tileGenerator = new TileGenerator(); final Random random = new Random(); - public void generate(DS1 ds1) { + public Map generate(DS1 ds1) { Map map = new Map(); ZoneGraph zones = map.zones; Zone zone = zones.claim( + ds1.fileName, 0, 0, ds1.width * SUBTILE_SIZE, @@ -22,8 +23,9 @@ public class MapGenerator { ds1.width, ds1.height); Chunk chunk = zone.get(0, 0); + zone.prefab(ds1.fileName, 0, 0, 0, ds1.width * SUBTILE_SIZE, ds1.height * SUBTILE_SIZE); copyPrefab(chunk, ds1); - zone.prefab(ds1.fileName, 0, 0, 0, ds1.width, ds1.height); + return map; } public void generate(Seed seed, int diff) { @@ -33,8 +35,9 @@ public class MapGenerator { void copyPrefab(Chunk chunk, DS1 ds1) { if (ds1.width != chunk.width / SUBTILE_SIZE) throw new IllegalArgumentException("ds1 width(" + ds1.width + ") != chunk width(" + chunk.width + ")"); if (ds1.height != chunk.height / SUBTILE_SIZE) throw new IllegalArgumentException("ds1 height(" + ds1.height + ") != chunk height(" + chunk.height + ")"); + chunk.init(ds1.layers); int[] cells = ds1.floors; - Tile[] tiles = chunk.tiles; + Tile[] tiles = chunk.tiles(4); for (int i = 0, s = ds1.floorLen; i < s; i++) { int cell = cells[i]; tiles[i] = tileGenerator.next( @@ -43,15 +46,15 @@ public class MapGenerator { DS1.Cell.subIndex(cell)); } - cells = ds1.walls; - int[] orientations = ds1.orientations; - for (int i = 0, s = ds1.wallLen; i < s; i++) { - int cell = cells[i]; - tiles[i] = tileGenerator.next( - orientations[i], - DS1.Cell.mainIndex(cell), - DS1.Cell.subIndex(cell)); - } + // cells = ds1.walls; + // int[] orientations = ds1.orientations; + // for (int i = 0, s = ds1.wallLen; i < s; i++) { + // int cell = cells[i]; + // tiles[i] = tileGenerator.next( + // orientations[i], + // DS1.Cell.mainIndex(cell), + // DS1.Cell.subIndex(cell)); + // } } void copyPrefab(Chunk chunk, DS1 ds1, diff --git a/core/src/main/java/com/riiablo/map2/Prefab.java b/core/src/main/java/com/riiablo/map2/Prefab.java index 9bbdae60..df393674 100644 --- a/core/src/main/java/com/riiablo/map2/Prefab.java +++ b/core/src/main/java/com/riiablo/map2/Prefab.java @@ -1,5 +1,7 @@ package com.riiablo.map2; +import com.badlogic.gdx.graphics.Pixmap; +import com.badlogic.gdx.math.MathUtils; import com.badlogic.gdx.utils.Pool; import com.badlogic.gdx.utils.Pool.Poolable; import com.badlogic.gdx.utils.Pools; @@ -18,4 +20,15 @@ public class Prefab extends BBox implements Poolable { name = null; group = 0; } + + int color = MathUtils.random.nextInt() | 0xff; + + void drawDebug(Pixmap pixmap, int x, int y) { + pixmap.setColor(color); + pixmap.drawRectangle( + x + xMin, + y + yMin, + width, + height); + } } diff --git a/core/src/main/java/com/riiablo/map2/TileGenerator.java b/core/src/main/java/com/riiablo/map2/TileGenerator.java index 1957e7de..a4d8570a 100644 --- a/core/src/main/java/com/riiablo/map2/TileGenerator.java +++ b/core/src/main/java/com/riiablo/map2/TileGenerator.java @@ -55,7 +55,7 @@ public class TileGenerator { static final class Entry { int probability; - final Array tiles = new Array<>(4); + final Array tiles = new Array<>(true, 4, Tile.class); void add(Tile tile) { tiles.add(tile); diff --git a/core/src/main/java/com/riiablo/map2/Zone.java b/core/src/main/java/com/riiablo/map2/Zone.java index 61499d9f..8139ae04 100644 --- a/core/src/main/java/com/riiablo/map2/Zone.java +++ b/core/src/main/java/com/riiablo/map2/Zone.java @@ -20,21 +20,30 @@ public final class Zone extends BBox implements Poolable, Disposable { static final Pool chunkPool = Chunk.pool; static final Pool prefabPool = Prefab.pool; - public static Zone obtain(int x, int y, int width, int height, int chunkWidth, int chunkHeight) { + public String name; + public int chunkWidth; + public int chunkHeight; + public int xChunks; + public int yChunks; + public final Array chunks = new Array<>(256); // TODO: ChunkGrid? + public final Array prefabs = new Array<>(); + + public static Zone obtain(String name, int x, int y, int width, int height, int chunkWidth, int chunkHeight) { assert (width / SUBTILE_SIZE) % chunkWidth == 0 : "width(" + width + ") / SUBTILE_SIZE(" + SUBTILE_SIZE + ") is not evenly divisible by chunkWidth(" + chunkWidth + ")"; assert (height / SUBTILE_SIZE) % chunkHeight == 0 : "height(" + height + ") / SUBTILE_SIZE(" + SUBTILE_SIZE + ") is not evenly divisible by chunkHeight(" + chunkHeight + ")"; Zone zone = pool.obtain(); zone.asBox(x, y, width, height); - zone.chunksX = width / SUBTILE_SIZE / chunkWidth; - zone.chunksY = height / SUBTILE_SIZE / chunkHeight; + zone.name = name; + zone.xChunks = width / SUBTILE_SIZE / chunkWidth; + zone.yChunks = height / SUBTILE_SIZE / chunkHeight; zone.chunkWidth = chunkWidth; zone.chunkHeight = chunkHeight; obtainChunks( zone.xMin, zone.yMin, zone.chunks, - zone.chunksX, zone.chunksY, + zone.xChunks, zone.yChunks, zone.chunkWidth * SUBTILE_SIZE, zone.chunkHeight * SUBTILE_SIZE); return zone; } @@ -66,6 +75,7 @@ public final class Zone extends BBox implements Poolable, Disposable { public void reset() { chunkPool.freeAll(chunks); chunks.clear(); + prefabPool.freeAll(prefabs); prefabs.clear(); } @@ -74,17 +84,14 @@ public final class Zone extends BBox implements Poolable, Disposable { pool.free(this); } - public int chunkHeight; - public int chunkWidth; - public int chunksX; - public int chunksY; - public final Array chunks = new Array<>(256); // TODO: ChunkGrid? - public final Array prefabs = new Array<>(); - public Chunk get(int x, int y) { return chunks.get(y * chunkWidth + x); } + public Chunk init(int x, int y, int layers) { + return get(x, y).init(layers); + } + int color = MathUtils.random.nextInt() | 0xff; public Prefab prefab(String name, int group, int x, int y, int width, int height) { @@ -92,6 +99,7 @@ public final class Zone extends BBox implements Poolable, Disposable { prefab.asBox(x, y, width, height); prefab.name = name; prefab.group = group; + prefabs.add(prefab); return prefab; } @@ -118,7 +126,7 @@ public final class Zone extends BBox implements Poolable, Disposable { } void drawDebugPrefab(Pixmap pixmap, int x, int y) { - // TODO: implement + for (Prefab prefab : prefabs) prefab.drawDebug(pixmap, x, y); } void drawDebugTile(Pixmap pixmap, int x, int y) { diff --git a/core/src/main/java/com/riiablo/map2/util/ZoneGraph.java b/core/src/main/java/com/riiablo/map2/util/ZoneGraph.java index 368e51d3..7d2ed943 100644 --- a/core/src/main/java/com/riiablo/map2/util/ZoneGraph.java +++ b/core/src/main/java/com/riiablo/map2/util/ZoneGraph.java @@ -45,8 +45,8 @@ public class ZoneGraph implements Disposable { } } - public Zone claim(int x, int y, int width, int height, int chunkWidth, int chunkHeight) { - Zone element = Zone.obtain(x, y, width, height, chunkWidth, chunkHeight); + public Zone claim(String name, int x, int y, int width, int height, int chunkWidth, int chunkHeight) { + Zone element = Zone.obtain(name, x, y, width, height, chunkWidth, chunkHeight); Node n = Node.wrap(element); nodes.add(n); mode = UNSET; @@ -73,7 +73,7 @@ public class ZoneGraph implements Disposable { if (nodes.isEmpty()) return; Texture texture = prepareTexture(mode); if (texture == null) return; - batch.draw(texture, box.xMin, box.yMin); + batch.draw(texture, box.xMin, -box.yMax); } Texture prepareTexture(DebugMode mode) { diff --git a/tools/map-debugger/src/main/java/com/riiablo/MapDebugger.java b/tools/map-debugger/src/main/java/com/riiablo/MapDebugger.java index 41e1efdf..01af59fe 100644 --- a/tools/map-debugger/src/main/java/com/riiablo/MapDebugger.java +++ b/tools/map-debugger/src/main/java/com/riiablo/MapDebugger.java @@ -10,9 +10,9 @@ import com.badlogic.gdx.InputAdapter; import com.badlogic.gdx.files.FileHandle; import com.badlogic.gdx.graphics.GL20; import com.badlogic.gdx.graphics.g2d.Batch; +import com.badlogic.gdx.graphics.g2d.BitmapFont; import com.badlogic.gdx.graphics.g2d.SpriteBatch; import com.badlogic.gdx.math.Vector2; - import com.badlogic.gdx.math.Vector3; import com.badlogic.gdx.utils.Array; import com.badlogic.gdx.utils.GdxRuntimeException; @@ -26,18 +26,16 @@ import com.riiablo.map2.DS1; import com.riiablo.map2.DS1Reader; import com.riiablo.map2.DT1; import com.riiablo.map2.DT1Reader; +import com.riiablo.map2.Map; import com.riiablo.map2.MapGenerator; import com.riiablo.map2.TileGenerator; -import com.riiablo.map2.Zone; import com.riiablo.map2.util.DebugMode; -import com.riiablo.map2.util.ZoneGraph; import com.riiablo.mpq.MPQFileHandleResolver; import com.riiablo.tool.LwjglTool; import com.riiablo.tool.Tool; import com.riiablo.util.InstallationFinder; import static com.badlogic.gdx.Input.Keys.GRAVE; -import static com.riiablo.map2.DT1.Tile.SUBTILE_SIZE; import static com.riiablo.map2.util.DebugMode.TILE; public class MapDebugger extends Tool { @@ -92,9 +90,12 @@ public class MapDebugger extends Tool { Riiablo.home = d2Home; } + OrthographicCamera ui; + BitmapFont font; + OrthographicCamera camera; Batch batch; - ZoneGraph tree; + Map map; DebugMode debugMode = TILE; @Override @@ -105,6 +106,9 @@ public class MapDebugger extends Tool { Riiablo.home = Gdx.files.absolute(Riiablo.home.path()); Riiablo.mpqs = new MPQFileHandleResolver(); + ui = new OrthographicCamera(); + font = new BitmapFont(); + camera = new OrthographicCamera(); batch = new SpriteBatch(); Gdx.input.setInputProcessor(new InputAdapter() { @@ -151,25 +155,26 @@ public class MapDebugger extends Tool { } }); - // coords above are in tiles, need to be in subtiles - tree = new ZoneGraph(); - DS1Reader ds1Reader = new DS1Reader(); DS1 townN1 = readDs1(ds1Reader, "data\\global\\tiles\\act1\\town\\townn1.ds1"); DT1Reader dt1Reader = new DT1Reader(); MapGenerator generator = new MapGenerator(); - // addDt1(generator.tileGenerator, dt1Reader, ""); - generator.generate(townN1); - // TODO: need --d2 to set Riiablo.home and init Riiablo.mpq to be able to read DS1 files + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\town\\floor.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\town\\objects.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\town\\fence.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\outdoors\\river.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\outdoors\\stonewall.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\town\\trees.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\outdoors\\objects.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\outdoors\\treegroups.dt1"); + addDt1(generator.tileGenerator, dt1Reader, "data\\global\\tiles\\act1\\outdoors\\bridge.dt1"); + map = generator.generate(townN1); + map.zones().claim("BloodMoor", -120, -400, 400, 400, 8, 8); // to test layout - // claim1(tree, -56, 0, 56, 40); - // claim2(tree, 0, -40, 80, 80, 8, 8); - - // tree.claim(-25, 0, 25, 25); - // tree.claim(0, 0, 50, 50); - // tree.claim(0, -75, 75, 75); - // tree.claim(-100, -100, 100, 100); + // map = new Map(); + // map.zones().claim("TownN1", 0, 0, 280, 200, 1, 1); + // map.zones().claim("BloodMoor", -120, -400, 400, 400, 8, 8); } DS1 readDs1(DS1Reader ds1Reader, String fileName) { @@ -186,33 +191,13 @@ public class MapDebugger extends Tool { return dt1; } - /** pseudo-ds1 claim /w grid size = zone size */ - Zone claim1(ZoneGraph tree, int tileX, int tileY, int width, int height) { - return tree.claim( - tileX * SUBTILE_SIZE, - tileY * SUBTILE_SIZE, - width * SUBTILE_SIZE, - height * SUBTILE_SIZE, - width, - height); - } - - Zone claim2(ZoneGraph tree, int tileX, int tileY, int width, int height, int chunkWidth, int chunkHeight) { - return tree.claim( - tileX * SUBTILE_SIZE, - tileY * SUBTILE_SIZE, - width * SUBTILE_SIZE, - height * SUBTILE_SIZE, - chunkWidth, - chunkHeight); - } - @Override public void resize(int width, int height) { Vector3 tmp = camera.position.cpy(); - camera.setToOrtho(false, width, height); + camera.setToOrtho(false, width, height); // y-down to make north-up camera.position.set(tmp); - camera.update(); + camera.update(); // required due to camera.position.set(Vector3) call + ui.setToOrtho(false, width, height); } @Override @@ -220,15 +205,25 @@ public class MapDebugger extends Tool { Gdx.gl.glClearColor(0.0f, 0.0f, 0.0f, 1.0f); Gdx.gl.glClear(GL20.GL_COLOR_BUFFER_BIT); - batch.setProjectionMatrix(camera.combined); batch.begin(); - tree.drawDebug(batch, debugMode); + + batch.setProjectionMatrix(camera.combined); + map.zones().drawDebug(batch, debugMode); + + batch.setProjectionMatrix(ui.combined); + font.draw( + batch, + "FPS: " + Gdx.graphics.getFramesPerSecond() + "\nDebugMode: " + debugMode, + 0, + camera.viewportHeight); + batch.end(); } @Override public void dispose() { batch.dispose(); - tree.dispose(); + map.dispose(); + font.dispose(); } }