From a51f7a53aa2c487ebf94ba0363038574ae8bb590 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Tue, 7 Jul 2026 00:22:32 -0400 Subject: feat: hidden exits (and related flags) work with commands that change room ids. exit code simplified and unified between impassable and blocked exits. --- internal/admin/api_rooms.go | 50 ++++++++++++++++++++++++++------------------- 1 file changed, 29 insertions(+), 21 deletions(-) (limited to 'internal/admin/api_rooms.go') diff --git a/internal/admin/api_rooms.go b/internal/admin/api_rooms.go index 75f4779..124b093 100644 --- a/internal/admin/api_rooms.go +++ b/internal/admin/api_rooms.go @@ -821,37 +821,40 @@ func (s *AdminServer) handleRoomRename(w http.ResponseWriter, r *http.Request) { s.world.RebuildRoomIndex(s.dataDir) s.world.ClearRoomState(body.ID) + // Remap every genuine room-ID reference (exit targets, trigger/on-enter + // room+teleport+despawn_rooms, mob wander_rooms) from the old ID to the + // new one across all rooms — including the renamed room itself, so a + // self-loop exit is corrected too. Only rooms whose references actually + // moved are re-marshaled (preserving untouched files' formatting). + idMap := map[int]int{body.ID: body.NewID} allIDs, _ := listRoomIDs(s.dataDir) - var exitUpdates int + var refUpdates int for _, otherID := range allIDs { - if otherID == body.NewID { - continue - } otherRoom, loadErr := s.world.LoadRoom(otherID) if loadErr != nil { continue } - changed := false - for _, dir := range world.ExitOrder { - exit, exists := otherRoom.Exits[dir] - if !exists || exit.Room != body.ID { - continue - } - exit.Room = body.NewID - otherRoom.Exits[dir] = exit - changed = true - } - if changed { - otherPath, pathOk := s.world.GetRoomPath(otherID) - if pathOk { - writeYAMLFile(otherPath, otherRoom) - exitUpdates++ - } + if !otherRoom.RewriteRoomIDs(idMap) { + continue + } + otherPath, pathOk := s.world.GetRoomPath(otherID) + if !pathOk { + continue } + writeYAMLFile(otherPath, otherRoom) + refUpdates++ + } + + // Migrate room-ID-embedded state across all characters (discovered-exit + // player flags, RoomID, EnterSeqRoom, MapSymbols, RoomsVisited) so a + // rename does not strand players or leave stale hidden_exit__ + // flags pointing at the old room ID. + if s.rewriteRoomIDs != nil { + s.rewriteRoomIDs(idMap) } s.undoStack.Push(ChangeDesc{ - Description: fmt.Sprintf("rename room %d to %d (updated %d exits)", body.ID, body.NewID, exitUpdates), + Description: fmt.Sprintf("rename room %d to %d (updated %d refs)", body.ID, body.NewID, refUpdates), FilePath: oldPath, NewFilePath: newPath, OldContent: data, @@ -941,6 +944,11 @@ func (s *AdminServer) handleResolveDuplicate(w http.ResponseWriter, r *http.Requ writeJSON(w, map[string]any{"error": "yaml parse failed: " + err.Error()}) return } + // A duplicate file was never in the room index (only one of the pair + // is indexed), so no player has visited it and no other room points at + // it. Renaming it to an unused ID therefore needs no exit/trigger/wander + // reference rewrite and no player-state migration — only its own id + // field and filename change. m["id"] = body.NewID newContent, err := writeMapAsYAML(newRel, m) -- cgit v1.2.3