diff --git a/core/build.gradle b/core/build.gradle index 86ee5f17..55a798a1 100644 --- a/core/build.gradle +++ b/core/build.gradle @@ -26,6 +26,11 @@ idea.module.generatedSourceDirs += vcsGeneratedSourceDir project.ext.flatbuffersSourceDirs = file('src/main/flatbuffers/') +// Java +dependencies { + implementation 'javax.annotation:javax.annotation-api:1.3.2' +} + // LibGDX dependencies { api "com.badlogicgames.gdx:gdx:$gdxVersion" diff --git a/core/src/main/java/com/riiablo/map2/Chunk.java b/core/src/main/java/com/riiablo/map2/Chunk.java index 7574b022..363923ef 100644 --- a/core/src/main/java/com/riiablo/map2/Chunk.java +++ b/core/src/main/java/com/riiablo/map2/Chunk.java @@ -10,7 +10,9 @@ import com.badlogic.gdx.utils.Pools; import com.riiablo.codec.util.BBox; import com.riiablo.map2.DT1.Tile; 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.DT1.Tile.SUBTILE_SIZE; /** acts as a dt1 Tile cache chunk of zone */ @@ -20,7 +22,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.tiles = tilePools.obtain(width * height / SUBTILE_SIZE); + chunk.numTiles = width * height / SUBTILE_SIZE; + chunk.tiles = tilePools.obtain(chunk.numTiles); return chunk; } @@ -30,6 +33,7 @@ public class Chunk extends BBox implements Poolable, Disposable { @Override public void dispose() { + numTiles = 0; tilePools.free(tiles); pool.free(this); } @@ -44,16 +48,44 @@ public class Chunk extends BBox implements Poolable, Disposable { .build(); // public Zone zone; + public int numTiles; public Tile[] tiles; 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); + void drawDebug(DebugMode mode, Pixmap pixmap, int x, int y) { + switch (mode) { + case CHUNK: + pixmap.setColor(color); + pixmap.drawRectangle( + x + xMin, + y + yMin, + width, + height); + break; + case SUBTILE: + pixmap.setColor(RED); + drawDebugSubtiles(pixmap, x, y); + break; + } + } + + void drawDebugSubtiles(Pixmap pixmap, int x, int y) { + Tile[] tiles = this.tiles; + for (int i = 0, s = numTiles; i < s; i++) { + Tile tile = tiles[i]; + if (tile == null) continue; + drawDebugSubtile(tile, pixmap, x, y); + } + } + + void drawDebugSubtile(Tile tile, Pixmap pixmap, int x, int y) { + // TODO: apply tile offset + subtile offset of flags id + byte[] flags = tile.flags; + for (int i = 0; i < Tile.NUM_SUBTILES; i++) { + byte flag = flags[i]; + if (flag == 0) continue; + pixmap.drawPixel(x + i % 25, y + i / 25); + } } } diff --git a/core/src/main/java/com/riiablo/map2/MapGenerator.java b/core/src/main/java/com/riiablo/map2/MapGenerator.java index c79ddc21..56ea62d5 100644 --- a/core/src/main/java/com/riiablo/map2/MapGenerator.java +++ b/core/src/main/java/com/riiablo/map2/MapGenerator.java @@ -8,7 +8,7 @@ import com.riiablo.map2.util.ZoneGraph; import static com.riiablo.map2.DT1.Tile.SUBTILE_SIZE; public class MapGenerator { - TileGenerator tileGenerator = new TileGenerator(); + public TileGenerator tileGenerator = new TileGenerator(); final Random random = new Random(); public void generate(DS1 ds1) { @@ -23,6 +23,7 @@ public class MapGenerator { ds1.height); Chunk chunk = zone.get(0, 0); copyPrefab(chunk, ds1); + zone.prefab(ds1.fileName, 0, 0, 0, ds1.width, ds1.height); } public void generate(Seed seed, int diff) { @@ -30,8 +31,8 @@ public class MapGenerator { } void copyPrefab(Chunk chunk, DS1 ds1) { - if (ds1.width != chunk.width) throw new IllegalArgumentException("ds1 width != chunk width"); - if (ds1.height != chunk.height) throw new IllegalArgumentException("ds1 height != chunk height"); + 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 + ")"); int[] cells = ds1.floors; Tile[] tiles = chunk.tiles; for (int i = 0, s = ds1.floorLen; i < s; i++) { @@ -41,6 +42,16 @@ public class MapGenerator { 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 d74196b4..9bbdae60 100644 --- a/core/src/main/java/com/riiablo/map2/Prefab.java +++ b/core/src/main/java/com/riiablo/map2/Prefab.java @@ -1,8 +1,21 @@ package com.riiablo.map2; +import com.badlogic.gdx.utils.Pool; +import com.badlogic.gdx.utils.Pool.Poolable; +import com.badlogic.gdx.utils.Pools; + import com.riiablo.codec.util.BBox; -public class Prefab extends BBox { +public class Prefab extends BBox implements Poolable { + static final Pool pool = Pools.get(Prefab.class); + public String name; public int group; + + @Override + public void reset() { + super.reset(); + name = null; + group = 0; + } } diff --git a/core/src/main/java/com/riiablo/map2/TileGenerator.java b/core/src/main/java/com/riiablo/map2/TileGenerator.java index e0525bd7..1957e7de 100644 --- a/core/src/main/java/com/riiablo/map2/TileGenerator.java +++ b/core/src/main/java/com/riiablo/map2/TileGenerator.java @@ -8,8 +8,8 @@ import com.riiablo.map2.DT1.Tile; import com.riiablo.map2.random.Random; public class TileGenerator { - ObjectSet dt1s; - IntMap entries; + final ObjectSet dt1s = new ObjectSet<>(); + final IntMap entries = new IntMap<>(); final Random random = new Random(); diff --git a/core/src/main/java/com/riiablo/map2/Zone.java b/core/src/main/java/com/riiablo/map2/Zone.java index 355a1692..61499d9f 100644 --- a/core/src/main/java/com/riiablo/map2/Zone.java +++ b/core/src/main/java/com/riiablo/map2/Zone.java @@ -12,10 +12,13 @@ import com.riiablo.codec.util.BBox; import com.riiablo.map2.util.DebugMode; import static com.riiablo.map2.DT1.Tile.SUBTILE_SIZE; +import static com.riiablo.map2.util.DebugMode.CHUNK; +import static com.riiablo.map2.util.DebugMode.SUBTILE; public final class Zone extends BBox implements Poolable, Disposable { public static final Pool pool = Pools.get(Zone.class, 16); 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) { assert (width / SUBTILE_SIZE) % chunkWidth == 0 @@ -62,6 +65,8 @@ public final class Zone extends BBox implements Poolable, Disposable { @Override public void reset() { chunkPool.freeAll(chunks); + chunks.clear(); + prefabs.clear(); } @Override @@ -74,6 +79,7 @@ public final class Zone extends BBox implements Poolable, Disposable { 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); @@ -81,6 +87,14 @@ public final class Zone extends BBox implements Poolable, Disposable { int color = MathUtils.random.nextInt() | 0xff; + public Prefab prefab(String name, int group, int x, int y, int width, int height) { + Prefab prefab = prefabPool.obtain(); + prefab.asBox(x, y, width, height); + prefab.name = name; + prefab.group = group; + return prefab; + } + public void drawDebug(DebugMode mode, Pixmap pixmap, int x, int y) { switch (mode) { case CHUNK: @@ -100,7 +114,7 @@ public final class Zone extends BBox implements Poolable, Disposable { } void drawDebugChunk(Pixmap pixmap, int x, int y) { - for (Chunk chunk : chunks) chunk.drawDebug(pixmap, x, y); + for (Chunk chunk : chunks) chunk.drawDebug(CHUNK, pixmap, x, y); } void drawDebugPrefab(Pixmap pixmap, int x, int y) { @@ -117,6 +131,7 @@ public final class Zone extends BBox implements Poolable, Disposable { } void drawDebugSubtile(Pixmap pixmap, int x, int y) { + for (Chunk chunk : chunks) chunk.drawDebug(SUBTILE, pixmap, x, y); pixmap.setColor(color); pixmap.drawRectangle(x + xMin, y + yMin, width, height); } 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 8684d376..41e1efdf 100644 --- a/tools/map-debugger/src/main/java/com/riiablo/MapDebugger.java +++ b/tools/map-debugger/src/main/java/com/riiablo/MapDebugger.java @@ -1,23 +1,40 @@ package com.riiablo; +import org.apache.commons.cli.CommandLine; +import org.apache.commons.cli.Option; +import org.apache.commons.cli.Options; + import com.badlogic.gdx.Application; import com.badlogic.gdx.Gdx; 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.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; + import com.riiablo.camera.OrthographicCamera; +import com.riiablo.io.ByteInput; import com.riiablo.logger.Level; import com.riiablo.logger.LogManager; import com.riiablo.logger.Logger; +import com.riiablo.map2.DS1; +import com.riiablo.map2.DS1Reader; +import com.riiablo.map2.DT1; +import com.riiablo.map2.DT1Reader; +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; @@ -33,6 +50,48 @@ public class MapDebugger extends Tool { .start(); } + @Override + protected void createCliOptions(Options options) { + super.createCliOptions(options); + + options.addOption(Option + .builder("d") + .longOpt("d2") + .desc("directory containing D2 MPQ files") + .hasArg() + .argName("path") + .build()); + } + + @Override + protected void handleCliOptions(String cmd, Options options, CommandLine cli) { + super.handleCliOptions(cmd, options, cli); + + final InstallationFinder finder = InstallationFinder.getInstance(); + + final FileHandle d2Home; + if (cli.hasOption("d2")) { + d2Home = new FileHandle(cli.getOptionValue("d2")); + if (!InstallationFinder.isD2Home(d2Home)) { + throw new GdxRuntimeException("'d2' does not refer to a valid D2 installation: " + d2Home); + } + } else { + log.trace("Locating D2 installations..."); + Array homeDirs = finder.getHomeDirs(); + log.trace("D2 installations: {}", homeDirs); + if (homeDirs.size > 0) { + d2Home = homeDirs.first(); + } else { + System.err.println("Unable to locate any D2 installation!"); + printHelp(cmd, options); + System.exit(0); + return; + } + } + log.debug("d2Home: {}", d2Home); + Riiablo.home = d2Home; + } + OrthographicCamera camera; Batch batch; ZoneGraph tree; @@ -43,6 +102,9 @@ public class MapDebugger extends Tool { Gdx.app.setLogLevel(Application.LOG_DEBUG); LogManager.setLevel(MapDebugger.class.getName(), Level.DEBUG); + Riiablo.home = Gdx.files.absolute(Riiablo.home.path()); + Riiablo.mpqs = new MPQFileHandleResolver(); + camera = new OrthographicCamera(); batch = new SpriteBatch(); Gdx.input.setInputProcessor(new InputAdapter() { @@ -91,14 +153,39 @@ public class MapDebugger extends Tool { // coords above are in tiles, need to be in subtiles tree = new ZoneGraph(); - claim1(tree, -56, 0, 56, 40); - claim2(tree, 0, -40, 80, 80, 8, 8); + + 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 + + // 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); } + DS1 readDs1(DS1Reader ds1Reader, String fileName) { + byte[] bytes = Riiablo.mpqs.resolve(fileName).readBytes(); + ByteInput in = ByteInput.wrap(bytes); + return ds1Reader.readDs1(fileName, in); + } + + DT1 addDt1(TileGenerator generator, DT1Reader dt1Reader, String fileName) { + byte[] bytes = Riiablo.mpqs.resolve(fileName).readBytes(); + ByteInput in = ByteInput.wrap(bytes); + DT1 dt1 = dt1Reader.readDt1(fileName, in); + generator.add(dt1); + 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(