Exit vaults, level type
This commit is contained in:
parent
d489810154
commit
1b4240e430
Binary file not shown.
Before Width: | Height: | Size: 4.9 KiB After Width: | Height: | Size: 18 KiB |
@ -5,4 +5,7 @@ class Palette {
|
|||||||
static const defaultFg = Colors.white;
|
static const defaultFg = Colors.white;
|
||||||
|
|
||||||
static const subtitle = Colors.red;
|
static const subtitle = Colors.red;
|
||||||
|
|
||||||
|
static const demoDoor = Colors.red;
|
||||||
|
static const demoExit = Colors.red;
|
||||||
}
|
}
|
||||||
|
@ -6,6 +6,7 @@ import 'package:dartterm/gen/generator.dart';
|
|||||||
import 'package:dartterm/input.dart';
|
import 'package:dartterm/input.dart';
|
||||||
import 'package:dartterm/skreek.dart';
|
import 'package:dartterm/skreek.dart';
|
||||||
import 'package:dartterm/terminal.dart';
|
import 'package:dartterm/terminal.dart';
|
||||||
|
import 'package:dartterm/world/level.dart';
|
||||||
|
|
||||||
void main() async {
|
void main() async {
|
||||||
Vaults vaults;
|
Vaults vaults;
|
||||||
@ -31,11 +32,12 @@ void main() async {
|
|||||||
|
|
||||||
while (true) {
|
while (true) {
|
||||||
clear();
|
clear();
|
||||||
Vault output = Generator(math.Random(seed), vaults).generate(Requirement(
|
Level output =
|
||||||
|
Generator(math.Random(seed), vaults).generateLevel(Requirement(
|
||||||
16,
|
16,
|
||||||
32,
|
32,
|
||||||
16,
|
16,
|
||||||
24,
|
18,
|
||||||
DirectionSet({
|
DirectionSet({
|
||||||
Direction.up,
|
Direction.up,
|
||||||
Direction.down,
|
Direction.down,
|
||||||
@ -47,23 +49,15 @@ void main() async {
|
|||||||
for (var x = 0; x < w; x++) {
|
for (var x = 0; x < w; x++) {
|
||||||
var cursor = at(x * 2, y * 2).big();
|
var cursor = at(x * 2, y * 2).big();
|
||||||
switch (output.tiles.get(x, y)) {
|
switch (output.tiles.get(x, y)) {
|
||||||
case VaultTile.bspfloor:
|
case LevelTile.floor:
|
||||||
case VaultTile.floor:
|
case LevelTile.openDoor:
|
||||||
cursor.puts(" ");
|
cursor.puts(" ");
|
||||||
case VaultTile.doorpronefloor:
|
case LevelTile.closedDoor:
|
||||||
cursor.puts("-");
|
cursor.fg(Palette.demoDoor).puts("+");
|
||||||
case VaultTile.door:
|
case LevelTile.exit:
|
||||||
cursor.fg(Palette.subtitle).puts("+");
|
cursor.fg(Palette.demoExit).puts("X");
|
||||||
case VaultTile.wall:
|
case LevelTile.wall:
|
||||||
case VaultTile.defaultwall:
|
|
||||||
cursor.puts("#");
|
cursor.puts("#");
|
||||||
case VaultTile.archpronewall:
|
|
||||||
cursor.puts("%");
|
|
||||||
case VaultTile.archwall:
|
|
||||||
cursor.puts("\$");
|
|
||||||
case VaultTile.exit:
|
|
||||||
cursor.puts("X");
|
|
||||||
case VaultTile.meta0:
|
|
||||||
case null:
|
case null:
|
||||||
cursor.puts("?");
|
cursor.puts("?");
|
||||||
}
|
}
|
||||||
|
@ -5,6 +5,7 @@ import 'package:dartterm/algorithms/regionalize.dart';
|
|||||||
import 'package:dartterm/algorithms/kruskal.dart';
|
import 'package:dartterm/algorithms/kruskal.dart';
|
||||||
import 'package:dartterm/bitmap.dart';
|
import 'package:dartterm/bitmap.dart';
|
||||||
import 'package:dartterm/skreek.dart';
|
import 'package:dartterm/skreek.dart';
|
||||||
|
import 'package:dartterm/world/level.dart';
|
||||||
|
|
||||||
part 'direction.dart';
|
part 'direction.dart';
|
||||||
part 'direction_set.dart';
|
part 'direction_set.dart';
|
||||||
@ -22,11 +23,19 @@ class Generator {
|
|||||||
|
|
||||||
Generator(this._random, this._vaults);
|
Generator(this._random, this._vaults);
|
||||||
|
|
||||||
Vault generate(Requirement requirement) {
|
Level generateLevel(Requirement requirement) {
|
||||||
var out = _generateOriented(requirement, false);
|
var out = _generateOriented(requirement, false);
|
||||||
return _finalize(out);
|
return _finalize(out);
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/*
|
||||||
|
Vault generateVault(Requirement requirement) {
|
||||||
|
var out = _generateOriented(requirement, false);
|
||||||
|
var (vault, (_, _)) = _finalize(out);
|
||||||
|
return vault;
|
||||||
|
}
|
||||||
|
*/
|
||||||
|
|
||||||
Vault _generateOriented(Requirement requirement, bool canBeVault) {
|
Vault _generateOriented(Requirement requirement, bool canBeVault) {
|
||||||
if (canBeVault) {
|
if (canBeVault) {
|
||||||
Vault? suggested = _suggest(vaultTries, requirement);
|
Vault? suggested = _suggest(vaultTries, requirement);
|
||||||
@ -248,7 +257,7 @@ class Generator {
|
|||||||
return vault;
|
return vault;
|
||||||
}
|
}
|
||||||
|
|
||||||
Vault _finalize(Vault subj) {
|
Level _finalize(Vault subj) {
|
||||||
var vx = subj.vx, vy = subj.vy;
|
var vx = subj.vx, vy = subj.vy;
|
||||||
|
|
||||||
var orthoOffsets = [(0, -1), (0, 1), (-1, 0), (1, 0)];
|
var orthoOffsets = [(0, -1), (0, 1), (-1, 0), (1, 0)];
|
||||||
@ -303,11 +312,28 @@ class Generator {
|
|||||||
return walkable(subj.tiles.get(x, y));
|
return walkable(subj.tiles.get(x, y));
|
||||||
});
|
});
|
||||||
|
|
||||||
|
// generate one fake region for the exit doors to be in
|
||||||
|
Set<(int, int)> exitRegion = {};
|
||||||
|
for (var x = -2; x < subj.vx + 2; x++) {
|
||||||
|
exitRegion.add((x, -1));
|
||||||
|
exitRegion.add((x, subj.vy));
|
||||||
|
}
|
||||||
|
for (var y = -2; y < subj.vy + 2; y++) {
|
||||||
|
exitRegion.add((-1, y));
|
||||||
|
exitRegion.add((subj.vx, y));
|
||||||
|
}
|
||||||
|
|
||||||
|
int exitRegionId = regions.length;
|
||||||
|
for (var (x, y) in exitRegion) {
|
||||||
|
toRegion[(x, y)] = exitRegionId;
|
||||||
|
}
|
||||||
|
regions.add(Region.fromNonEmptySet(exitRegion));
|
||||||
|
|
||||||
|
// OK: now build the doors
|
||||||
double doorPoints(int x, int y) {
|
double doorPoints(int x, int y) {
|
||||||
return subj.tiles.get(x, y) == VaultTile.doorpronefloor ? 0.5 : 0.0;
|
return subj.tiles.get(x, y) == VaultTile.doorpronefloor ? 0.5 : 0.0;
|
||||||
}
|
}
|
||||||
|
|
||||||
//
|
|
||||||
List<Edge<(int, int)>> possibleDoors = [];
|
List<Edge<(int, int)>> possibleDoors = [];
|
||||||
for (var x = 0; x < subj.vx; x++) {
|
for (var x = 0; x < subj.vx; x++) {
|
||||||
for (var y = 0; y < subj.vy; y++) {
|
for (var y = 0; y < subj.vy; y++) {
|
||||||
@ -348,14 +374,22 @@ class Generator {
|
|||||||
regions[region1].points.length,
|
regions[region1].points.length,
|
||||||
);
|
);
|
||||||
|
|
||||||
possibleDoors.add(Edge(region0, region1, (x, y),
|
possibleDoors.add(Edge(
|
||||||
doorScore(points, roomSize, _random.nextDouble())));
|
region0,
|
||||||
|
region1,
|
||||||
|
(x, y),
|
||||||
|
doorScore(region0 != exitRegionId && region1 != exitRegionId,
|
||||||
|
points, roomSize, _random.nextDouble())));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
List<Edge<(int, int)>> exitDoors = [];
|
||||||
var minimalDoors = kruskal(regions.length, possibleDoors);
|
var minimalDoors = kruskal(regions.length, possibleDoors);
|
||||||
for (var d in minimalDoors) {
|
for (var d in minimalDoors) {
|
||||||
var (x, y) = d.value;
|
var (x, y) = d.value;
|
||||||
subj.tiles.set(x, y, VaultTile.door);
|
subj.tiles.set(x, y, VaultTile.door);
|
||||||
|
if (d.dst == exitRegionId || d.src == exitRegionId) {
|
||||||
|
exitDoors.add(d);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
for (var x = 0; x < subj.vx; x++) {
|
for (var x = 0; x < subj.vx; x++) {
|
||||||
@ -365,19 +399,68 @@ class Generator {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return subj;
|
|
||||||
|
if (exitDoors.length != 1) {
|
||||||
|
throw Exception("should be exactly one exit door");
|
||||||
|
}
|
||||||
|
|
||||||
|
// == Build the exit area ==
|
||||||
|
var (exitX, exitY) = exitDoors[0].value;
|
||||||
|
int exitVaultX, exitVaultY;
|
||||||
|
Vault finalVault;
|
||||||
|
int vaultBlitX, vaultBlitY;
|
||||||
|
if (exitX == 0 || exitX == vx - 1) {
|
||||||
|
finalVault =
|
||||||
|
Vault.blank(vx + 3, vy, VaultTile.defaultwall, DirectionSet({}));
|
||||||
|
vaultBlitX = exitX == 0 ? 3 : 0;
|
||||||
|
vaultBlitY = 0;
|
||||||
|
exitVaultX = exitX == 0 ? 1 : vx + 1;
|
||||||
|
exitVaultY = exitY;
|
||||||
|
} else if (exitY == 0 || exitY == vy - 1) {
|
||||||
|
finalVault =
|
||||||
|
Vault.blank(vx, vy + 3, VaultTile.defaultwall, DirectionSet({}));
|
||||||
|
vaultBlitX = 0;
|
||||||
|
vaultBlitY = exitY == 0 ? 3 : 0;
|
||||||
|
exitVaultX = exitX;
|
||||||
|
exitVaultY = exitY == 0 ? 1 : vy + 1;
|
||||||
|
} else {
|
||||||
|
throw Exception("exit door in invalid position $exitX $exitY $vx $vy");
|
||||||
|
}
|
||||||
|
|
||||||
|
for (var x = exitVaultX - 1; x <= exitVaultX + 1; x++) {
|
||||||
|
for (var y = exitVaultY - 1; y <= exitVaultY + 1; y++) {
|
||||||
|
finalVault.tiles.set(x, y, VaultTile.exit);
|
||||||
|
if (x == exitVaultX && y == exitVaultY ||
|
||||||
|
_manhattan(x, y, vaultBlitX + exitX, vaultBlitY + exitY) == 1) {
|
||||||
|
finalVault.tiles.set(x, y, VaultTile.floor);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
finalVault.blitFrom(subj, vaultBlitX, vaultBlitY);
|
||||||
|
|
||||||
|
return Level(
|
||||||
|
Bitmap.blankWith(finalVault.vx, finalVault.vy,
|
||||||
|
(x, y) => flattenVaultTile(finalVault.tiles.get(x, y)!)),
|
||||||
|
geo.Offset(exitVaultX, exitVaultY));
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
// components:
|
// components:
|
||||||
|
// - is not exit (exit should be placed last so it doesn't get more than one door)
|
||||||
// - points for placement
|
// - points for placement
|
||||||
// - size of the underlying room
|
// - size of the underlying room
|
||||||
// - random factor
|
// - random factor
|
||||||
double doorScore(double pointsForPlacement, int roomSize, double randomFactor) {
|
double doorScore(bool isNotExit, double pointsForPlacement, int roomSize,
|
||||||
|
double randomFactor) {
|
||||||
assert(pointsForPlacement >= 0.0 && pointsForPlacement <= 1.0);
|
assert(pointsForPlacement >= 0.0 && pointsForPlacement <= 1.0);
|
||||||
assert(roomSize >= 0 && roomSize < 100000);
|
assert(roomSize >= 0 && roomSize < 100000);
|
||||||
assert(randomFactor >= 0.0 && randomFactor < 1.0);
|
assert(randomFactor >= 0.0 && randomFactor < 1.0);
|
||||||
return pointsForPlacement * 100000 +
|
return (isNotExit ? 1.0 : 0.0) * 1000000 +
|
||||||
|
pointsForPlacement * 100000 +
|
||||||
(100000 - roomSize).toDouble() +
|
(100000 - roomSize).toDouble() +
|
||||||
randomFactor;
|
randomFactor;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
int _manhattan(int x0, int y0, int x1, int y1) {
|
||||||
|
return (x1 - x0).abs() + (y1 - y0).abs();
|
||||||
|
}
|
||||||
|
@ -52,3 +52,24 @@ VaultTile mergeVaultTile(VaultTile bottom, VaultTile top) {
|
|||||||
}
|
}
|
||||||
return top;
|
return top;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
LevelTile flattenVaultTile(VaultTile vt) {
|
||||||
|
switch (vt) {
|
||||||
|
case VaultTile.meta0:
|
||||||
|
case VaultTile.defaultwall:
|
||||||
|
case VaultTile.archpronewall:
|
||||||
|
case VaultTile.archwall:
|
||||||
|
case VaultTile.wall:
|
||||||
|
return LevelTile.wall;
|
||||||
|
|
||||||
|
case VaultTile.exit:
|
||||||
|
return LevelTile.exit;
|
||||||
|
case VaultTile.door:
|
||||||
|
return LevelTile.closedDoor;
|
||||||
|
|
||||||
|
case VaultTile.doorpronefloor:
|
||||||
|
case VaultTile.bspfloor:
|
||||||
|
case VaultTile.floor:
|
||||||
|
return LevelTile.floor;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
@ -1,3 +1,23 @@
|
|||||||
|
import 'package:dartterm/bitmap.dart';
|
||||||
|
import 'package:dartterm/algorithms/geometry.dart' as geo;
|
||||||
|
|
||||||
class Level {
|
class Level {
|
||||||
Set<(int, int)> openCells = {};
|
Bitmap<LevelTile> tiles;
|
||||||
|
geo.Offset spawn;
|
||||||
|
|
||||||
|
geo.Size get size => tiles.size;
|
||||||
|
|
||||||
|
Level(this.tiles, this.spawn) {
|
||||||
|
assert(tiles.rect.containsPoint(spawn));
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
enum LevelTile {
|
||||||
|
exit,
|
||||||
|
|
||||||
|
floor,
|
||||||
|
wall,
|
||||||
|
|
||||||
|
closedDoor,
|
||||||
|
openDoor,
|
||||||
}
|
}
|
||||||
|
Loading…
Reference in New Issue
Block a user