part of 'sitemode.dart'; const double fovMaxMovementCost = 10.0; const double fovMaxMemoryDistance = 80.0; extension FOVParts on SiteMode { void fovMaintain() { fovVisible = {}; shadowcast(playerPosition, _fovIsBlocking, (xy) => fovVisible.add(xy)); var oldFovMemory = fovMemory; fovMemory = {}; oldFovMemory.addAll(fovVisible); for (var xy in oldFovMemory) { if (_fovMemorablyClose(xy)) { fovMemory.add(xy); } } fovMovable = {}; for (var r in dijkstra(playerPosition, _fovDijkstraNeighbors)) { if (r.cost > fovMaxMovementCost) { break; } fovMovable.add(r.item); } } bool _fovMemorablyClose(geo.Offset other) { var dx = (other.x - playerPosition.x).toDouble(); var dy = (other.y - playerPosition.y).toDouble(); return math.sqrt(dx * dx + dy * dy) < fovMaxMemoryDistance; } bool _fovKnown(geo.Offset other) { return fovVisible.contains(other) || fovMemory.contains(other); } bool _fovIsBlocking(geo.Offset xy) { switch (level.tiles.get(xy.x, xy.y)) { case null: return true; case LevelTile.exit: return true; case LevelTile.openDoor: case LevelTile.floor: return false; case LevelTile.wall: return true; case LevelTile.closedDoor: return true; } } Iterable> _fovDijkstraNeighbors(geo.Offset xy) sync* { var tile = level.tiles.get(xy.x, xy.y); if (tile == LevelTile.exit || tile == LevelTile.closedDoor) { // can't go anywhere after this return; } for (var (dx, dy) in [(-1, 0), (1, 0), (0, -1), (0, 1)]) { var xy2 = geo.Offset(xy.x + dx, xy.y + dy); // Only return visible or remembered tiles if (!_fovKnown(xy2)) { continue; } var tile = level.tiles.get(xy2.x, xy2.y); if (tile == LevelTile.wall) { continue; } // for now, because exiting isn't implemented if (tile == LevelTile.exit) { continue; } yield Edge(1.0, xy2); } } }