import 'dart:developer'; import 'dart:math' as math; import 'package:dartterm/world/level.dart'; enum Direction { up, left, down, right, } class Vaults { List _primitive = []; static Future load(String filename) async { // TODO return Vaults(); } Vault generateBoxed(math.Random random, Requirement req) { var vx = req.vx; var vy = req.vy; var tiles = [ for (var y = 0; y < vy; y++) for (var x = 0; x < vx; x++) LevelTile.wall ]; var v = Vault(tiles, vx, vy, req.smooth); if (req.vx < 2 || req.vy < 2) { return v; } var req2 = Requirement(vx - 2, vy - 2, req.smooth); var inner = generate(random, req2); v.blitFrom(inner, 1, 1); return v; } Vault generate(math.Random random, Requirement requirement) { // TODO: Pick a relevant vault from the vaults file if possible // // First of all: randomize orientation. // This way we only have to consider one kind of spilt var orientation = randomOrientation(random); // Try to make vx the long axis if possible var req2 = requirement.unReorient(orientation); if (req2.vy > (req2.vx - 2) * 3 / 2) { orientation = (orientation + 2) % 8; // rotate once more } req2 = requirement.unReorient(orientation); var out2 = _generate(random, req2); var out1 = out2.reorient(orientation); log("$orientation ${requirement.vx} ${requirement.vy} ${req2.vx} ${req2.vy} ${out2.vx} ${out2.vy} ${out1.vx} ${out1.vy}"); assert(out1.vx == requirement.vx); assert(out1.vy == requirement.vy); return out1; } Vault _generate(math.Random random, Requirement req) { var vx = req.vx; var vy = req.vy; var tiles = [ for (var y = 0; y < vy; y++) for (var x = 0; x < vx; x++) LevelTile.wall ]; var v = Vault(tiles, vx, vy, req.smooth); if (vx < 3 || vx * vy < 10) { v.clear(LevelTile.floor); } else { // pick a split point var splitVx = random.nextInt(vx - 2) + 1; var reqLeft = Requirement(splitVx, vy, req.smooth.clone()); reqLeft.smooth.directions.add(Direction.right); var reqRight = Requirement((vx - splitVx - 1), vy, req.smooth.clone()); reqRight.smooth.directions.add(Direction.left); var vaultLeft = generate(random, reqLeft); var vaultRight = generate(random, reqRight); v.blitFrom(vaultLeft, 0, 0); v.blitFrom(vaultRight, splitVx + 1, 0); } return v; } } // TODO: There are many more efficient ways to do this class DirectionSet { final Set directions; DirectionSet(this.directions); DirectionSet flip() { var ds2 = DirectionSet({}); for (var i in directions) { switch (i) { case Direction.up: ds2.directions.add(Direction.up); case Direction.left: ds2.directions.add(Direction.right); case Direction.down: ds2.directions.add(Direction.down); case Direction.right: ds2.directions.add(Direction.left); } } return ds2; } DirectionSet rotateLeft() { var ds2 = DirectionSet({}); for (var i in directions) { switch (i) { case Direction.up: ds2.directions.add(Direction.left); case Direction.left: ds2.directions.add(Direction.down); case Direction.down: ds2.directions.add(Direction.right); case Direction.right: ds2.directions.add(Direction.up); } } return ds2; } DirectionSet rotateRight() { var ds2 = DirectionSet({}); for (var i in directions) { switch (i) { case Direction.up: ds2.directions.add(Direction.right); case Direction.right: ds2.directions.add(Direction.down); case Direction.down: ds2.directions.add(Direction.left); case Direction.left: ds2.directions.add(Direction.up); } } return ds2; } DirectionSet clone() { var ds2 = DirectionSet({}); ds2.directions.addAll(directions); return ds2; } } class Requirement { final int vx, vy; final DirectionSet smooth; Requirement(this.vx, this.vy, this.smooth); Requirement flip() { return Requirement(vx, vy, smooth.flip()); } Requirement rotateLeft() { return Requirement(vy, vx, smooth.rotateLeft()); } Requirement rotateRight() { return Requirement(vy, vx, smooth.rotateRight()); } Requirement unReorient(int r) { assert(r >= 0 && r < 8); Requirement o = this; if (r % 2 == 1) { o = o.flip(); r -= 1; } while (r >= 2) { o = o.rotateLeft(); r -= 2; } return o; } } class Vault { final List tiles; final int vx, vy; final DirectionSet smooth; Vault(this.tiles, this.vx, this.vy, this.smooth) { assert(tiles.length == vx * vy); } // TODO: We should be assessing this based on whether the pattern in the input // PNG had right-angled borders on this side, not based on the literal // presence or absence of walls // // In other words, this is wrong. static Vault fromVaultData(List tiles, int vx, int vy) { assert(tiles.length == vx * vy); var smooth = { Direction.up, Direction.left, Direction.down, Direction.right }; for (var x = 0; x < vx; x++) { if (tiles[x + 0 * vx] == LevelTile.wall) { smooth.remove(Direction.up); break; } } for (var x = 0; x < vx; x++) { if (tiles[x + (vy - 1) * vx] == LevelTile.wall) { smooth.remove(Direction.down); break; } } for (var y = 0; y < vy; y++) { if (tiles[0 + y * vx] == LevelTile.wall) { smooth.remove(Direction.left); break; } } for (var y = 0; y < vy; y++) { if (tiles[vx - 1 + y * vx] == LevelTile.wall) { smooth.remove(Direction.right); break; } } return Vault(tiles, vx, vy, DirectionSet(smooth)); } void clear(LevelTile lt) { for (var y = 0; y < vy; y++) { for (var x = 0; x < vx; x++) { tiles[y * vx + x] = lt; } } } void blitFrom(Vault other, int dx, int dy) { assert(dx >= 0); assert(dy >= 0); assert(dx + other.vx <= vx); assert(dy + other.vy <= vy); for (var x = 0; x < other.vx; x++) { for (var y = 0; y < other.vy; y++) { tiles[(y + dy) * vx + x + dx] = other.tiles[y * other.vx + x]; } } } Vault flip() { List tiles2 = [ for (var y = 0; y < vy; y++) for (var x = vx - 1; x >= 0; x--) tiles[y * vx + x] ]; return Vault(tiles2, vx, vy, smooth.flip()); } // TODO: Actually test this logic. // It feels right in my head but that doesn't mean it's right Vault rotateRight() { List tiles2 = [ for (var x = 0; x < vx; x++) for (var y = 0; y < vy; y++) tiles[y * vx + x] ]; return Vault(tiles2, vy, vx, smooth.rotateRight()); } Vault rotateLeft() { List tiles2 = [ for (var x = vx - 1; x >= 0; x++) for (var y = vy - 1; y >= 0; y++) tiles[y * vx + x] ]; return Vault(tiles2, vy, vx, smooth.rotateLeft()); } Vault reorient(int r) { assert(r >= 0 && r < 8); Vault o = this; while (r >= 2) { o = o.rotateRight(); r -= 2; } if (r == 1) { o = o.flip(); r -= 1; } return o; } } int randomOrientation(math.Random random) { return random.nextInt(8); }