import 'dart:math' as math; import 'package:dartterm/algorithms/regionalize.dart'; import 'package:dartterm/bitmap.dart'; import 'package:dartterm/skreek.dart'; part 'direction.dart'; part 'direction_set.dart'; part 'orientation.dart'; part 'requirement.dart'; part 'vault.dart'; part 'vaults.dart'; const vaultTries = 10; class Generator { final math.Random _random; final Vaults _vaults; List _queue = []; Generator(this._random, this._vaults); Vault generate(Requirement requirement) { Vault? suggested = _suggest(vaultTries, requirement); if (suggested != null) { return suggested; } // 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 = unReorientRequirement(requirement, orientation); if (req2.vy > (req2.vx - 2) * 3 / 2) { orientation = (orientation + 2) % 8; // rotate once more } req2 = unReorientRequirement(requirement, orientation); var out2 = _generate(req2); var out1 = reorientVault(out2, 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(Requirement req) { var vx = req.vx; var vy = req.vy; var v = Vault.blank(vx, vy, req.smooth, VaultTile.wall); if (vx < 2 || vy < 2) { } else if (vx < 9 || (vx - 2) * (vy - 2) < 12) { var v2 = Vault.blank(vx - 2, vy - 2, req.smooth, VaultTile.bspfloor); v.blitFrom(v2, 1, 1); } else { // pick a split point var splitVx = _random.nextInt(vx - 8) + 4; 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(reqLeft); var vaultRight = generate(reqRight); v.blitFrom(vaultLeft, 0, 0); v.blitFrom(vaultRight, splitVx - 1, 0); } return v; } Vault? _suggest(int tries, Requirement req) { for (var i = 0; i < tries; i++) { var sugg = _popSuggestion(); if (sugg == null) { return null; } sugg = reorientVault(sugg, randomOrientation(_random)); sugg = _tidy(sugg, req); if (sugg != null) { return sugg; } } return null; } Vault? _popSuggestion() { if (_queue.isEmpty) { _queue = _vaults.randomFlight(_random); } if (_queue.isEmpty) { return null; } return _queue.removeLast(); } Vault? _tidy(Vault vault, Requirement req) { if (vault.vx > req.vx || vault.vy > req.vy) { return null; } if (vault.vx < req.vx / 2 || vault.vy < req.vy / 2) { return null; } if (!vault.smooth.directions.containsAll(req.smooth.directions)) { return null; } var rsd = req.smooth.directions; bool mustFillX = rsd.contains(Direction.left) && rsd.contains(Direction.right); if (vault.vx != req.vx && mustFillX) { return null; } bool mustFillY = rsd.contains(Direction.up) && rsd.contains(Direction.down); if (vault.vy != req.vy && mustFillY) { return null; } Vault full = Vault.blank(req.vx, req.vy, req.smooth, VaultTile.wall); int vx = vault.vx; int dx; if (rsd.contains(Direction.left)) { dx = 0; } else if (rsd.contains(Direction.right)) { dx = req.vx - vx; } else { dx = _random.nextInt(req.vx - vx); } int vy = vault.vy; int dy; if (rsd.contains(Direction.up)) { dy = 0; } else if (rsd.contains(Direction.down)) { dy = req.vy - vy; } else { dy = _random.nextInt(req.vy - vy); } full.blitFrom(vault, dx, dy); return full; } }