diff options
| author | historia <[not public]> | 2026-06-27 16:50:41 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-27 16:50:41 -0400 |
| commit | 988b006a5bb9edb6465653566e7bdf8175347b8c (patch) | |
| tree | 3a1fceff2ee80c11b5dafcc49c13ababe021e880 /internal/validate/checks.go | |
| parent | 1cab9ca20e24742f770a676f84e4ed9f1636f297 (diff) | |
| download | thehouseoficarus-988b006a5bb9edb6465653566e7bdf8175347b8c.tar.gz | |
feat: one-way map links, blocked paths on map, map grid startup validation, user colors for maps
Diffstat (limited to 'internal/validate/checks.go')
| -rw-r--r-- | internal/validate/checks.go | 113 |
1 files changed, 112 insertions, 1 deletions
diff --git a/internal/validate/checks.go b/internal/validate/checks.go index f1d6b5e..b2e5449 100644 --- a/internal/validate/checks.go +++ b/internal/validate/checks.go @@ -6,6 +6,7 @@ import ( "strings" "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/color" "thehouseoficarus/internal/world" ) @@ -34,6 +35,13 @@ func validateRooms(s Source) []Issue { Message: fmt.Sprintf("Room %d: has no name", id), }) } + if room.Color != "" && color.Parse(room.Color).Empty() { + issues = append(issues, Issue{ + Level: "WARN", + Type: "reference", + Message: fmt.Sprintf("Room %d: invalid color %q", id, room.Color), + }) + } for dir, exit := range room.Exits { switch dir { @@ -654,7 +662,7 @@ func validateRoomWiring(s Source) []Issue { roomIndex := s.World.RoomIndex() sourceSet := make(map[int]bool) - for _, src := range s.CheckSources { + for _, src := range s.RootRooms { sourceSet[src] = true } @@ -705,6 +713,109 @@ func validateRoomWiring(s Source) []Issue { return issues } +// gridDeltas maps the horizontal exits to their grid movement. Up/Down are +// intentionally excluded: they connect separate horizontal planes rather than +// moving within one. +var gridDeltas = map[world.ExitDir][2]int{ + world.North: {0, -1}, + world.South: {0, 1}, + world.East: {1, 0}, + world.West: {-1, 0}, +} + +// validateRoomGrid lays each reachable horizontal plane on a 2D grid starting +// from the configured root rooms and reports when the exit layout cannot be +// embedded without conflict: +// - overlap: two distinct rooms land on the same grid cell. +// - twist: one room is forced onto two different grid cells. +// +// Up/Down exits don't move on the grid; each leads to a new plane that is laid +// out independently (fresh origin), so one root validates every reachable +// floor. Exit conditions are ignored (geometry is independent of gating), and +// exits to nonexistent rooms are skipped (covered by referential checks). +func validateRoomGrid(s Source) []Issue { + var issues []Issue + roomIndex := s.World.RoomIndex() + + placed := make(map[int]bool) + var seeds []int + for _, r := range s.RootRooms { + if roomIndex[r] { + seeds = append(seeds, r) + } + } + + for len(seeds) > 0 { + origin := seeds[0] + seeds = seeds[1:] + if placed[origin] { + continue + } + + coordOf := map[int][2]int{origin: {0, 0}} + roomAt := map[[2]int]int{{0, 0}: origin} + placed[origin] = true + queue := []int{origin} + + for len(queue) > 0 { + rid := queue[0] + queue = queue[1:] + room, err := s.World.LoadRoom(rid) + if err != nil { + continue + } + c := coordOf[rid] + for _, dir := range world.ExitOrder { + exit, ok := room.Exits[dir] + if !ok || exit.Room <= 0 || !roomIndex[exit.Room] { + continue + } + target := exit.Room + + if dir == world.Up || dir == world.Down { + if !placed[target] { + seeds = append(seeds, target) + } + continue + } + + d := gridDeltas[dir] + want := [2]int{c[0] + d[0], c[1] + d[1]} + + if existing, ok := coordOf[target]; ok { + if existing != want { + issues = append(issues, Issue{ + Level: "ERROR", + Type: "integrity", + Message: fmt.Sprintf( + "Grid twist: room %d (via %d %s) maps to grid (%d,%d) but was already placed at (%d,%d) [plane origin %d]", + target, rid, dir, want[0], want[1], existing[0], existing[1], origin), + }) + } + continue + } + if occupier, ok := roomAt[want]; ok && occupier != target { + issues = append(issues, Issue{ + Level: "ERROR", + Type: "integrity", + Message: fmt.Sprintf( + "Grid overlap: room %d (via %d %s) wants grid (%d,%d), already used by room %d [plane origin %d]", + target, rid, dir, want[0], want[1], occupier, origin), + }) + continue + } + + coordOf[target] = want + roomAt[want] = target + placed[target] = true + queue = append(queue, target) + } + } + } + + return issues +} + func validateTechs(s Source) []Issue { var issues []Issue seen := make(map[string]bool) |
