Refined impls

Restricted scoping of DS1Reader constants specific to class impl to reduce confusion
Restructured DS1Reader a bit
Added support for DS1#layers bitsum of active layers
Implemented multiple layers into Chunk#tiles via Chunk#layers
Changed MapDebugger to y-up be north (more logical with level layouts and ds1 coordinates scheme)
This commit is contained in:
Collin Smith 2021-08-03 23:40:53 -07:00
parent 508f08d8ef
commit 8656d50dfa
10 changed files with 159 additions and 89 deletions

View File

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

View File

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

View File

@ -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 {

View File

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

View File

@ -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,

View File

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

View File

@ -55,7 +55,7 @@ public class TileGenerator {
static final class Entry {
int probability;
final Array<Tile> tiles = new Array<>(4);
final Array<Tile> tiles = new Array<>(true, 4, Tile.class);
void add(Tile tile) {
tiles.add(tile);

View File

@ -20,21 +20,30 @@ public final class Zone extends BBox implements Poolable, Disposable {
static final Pool<Chunk> chunkPool = Chunk.pool;
static final Pool<Prefab> 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<Chunk> chunks = new Array<>(256); // TODO: ChunkGrid?
public final Array<Prefab> 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<Chunk> chunks = new Array<>(256); // TODO: ChunkGrid?
public final Array<Prefab> 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) {

View File

@ -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) {

View File

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