diff options
| author | historia <[not public]> | 2026-07-07 00:22:32 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-07-07 00:22:32 -0400 |
| commit | a51f7a53aa2c487ebf94ba0363038574ae8bb590 (patch) | |
| tree | b393a8f9e4732b40f6fe8058d086bd631537ad3b /internal/game/exit_migrate.go | |
| parent | 9292634f2f8d71a53879ff07e1330c671b207d51 (diff) | |
| download | thehouseoficarus-a51f7a53aa2c487ebf94ba0363038574ae8bb590.tar.gz | |
feat: hidden exits (and related flags) work with commands that change room ids. exit code simplified and unified between impassable and blocked exits.
Diffstat (limited to 'internal/game/exit_migrate.go')
| -rw-r--r-- | internal/game/exit_migrate.go | 150 |
1 files changed, 150 insertions, 0 deletions
diff --git a/internal/game/exit_migrate.go b/internal/game/exit_migrate.go new file mode 100644 index 0000000..845a6b2 --- /dev/null +++ b/internal/game/exit_migrate.go @@ -0,0 +1,150 @@ +package game + +import ( + "strconv" + "strings" + + "thehouseoficarus/internal/player" +) + +// rewritePlayerRoomIDs remaps room-ID-embedded state in a single player per +// idMap (oldID -> newID): discovered-exit player flag keys +// (hidden_exit_<id>_<dir>), current RoomID, EnterSeqRoom, MapSymbols keys, and +// Stats.RoomsVisited keys. Returns whether any field was changed. +// +// idMap is assumed to be a bijection (see Room.RewriteRoomIDs); the flag-key +// and int-map rekeys use delete-all-then-set-all so a swap (A<->B) cannot +// clobber an entry mid-rewrite. +func rewritePlayerRoomIDs(p *player.Player, idMap map[int]int) bool { + if p == nil || len(idMap) == 0 { + return false + } + changed := false + if remapScalar(idMap, &p.RoomID) { + changed = true + } + if p.EnterSeqRoom != 0 && remapScalar(idMap, &p.EnterSeqRoom) { + changed = true + } + if len(p.Flags) > 0 && rewriteDiscoveredExitFlags(p.Flags, idMap) { + changed = true + } + if len(p.MapSymbols) > 0 && rekeyIntMap(p.MapSymbols, idMap) { + changed = true + } + if len(p.Stats.RoomsVisited) > 0 && rekeyIntMap(p.Stats.RoomsVisited, idMap) { + changed = true + } + return changed +} + +func remapScalar(idMap map[int]int, n *int) bool { + if newID, ok := idMap[*n]; ok && newID != *n { + *n = newID + return true + } + return false +} + +// rewriteDiscoveredExitFlags rewrites hidden_exit_<roomID>_<dir> flag keys, +// remapping the embedded roomID per idMap. Flag values are preserved. +func rewriteDiscoveredExitFlags(flags map[string]any, idMap map[int]int) bool { + type move struct { + oldKey, newKey string + val any + } + var moves []move + for k, v := range flags { + if !strings.HasPrefix(k, hiddenExitFlagPrefix) { + continue + } + rest := k[len(hiddenExitFlagPrefix):] // "<roomID>_<dir>" + idx := strings.IndexByte(rest, '_') + if idx <= 0 { + continue + } + n, err := strconv.Atoi(rest[:idx]) + if err != nil { + continue + } + newID, ok := idMap[n] + if !ok || newID == n { + continue + } + moves = append(moves, move{k, hiddenExitFlagPrefix + strconv.Itoa(newID) + "_" + rest[idx+1:], v}) + } + if len(moves) == 0 { + return false + } + for _, m := range moves { + delete(flags, m.oldKey) + } + for _, m := range moves { + flags[m.newKey] = m.val + } + return true +} + +// rekeyIntMap rekeys a map[int]V per idMap (oldID -> newID), preserving values. +// Uses delete-all-then-set-all so a swap cannot clobber an entry. +func rekeyIntMap[V any](m map[int]V, idMap map[int]int) bool { + type entry struct { + key int + val V + } + var saved []entry + for k, v := range m { + if newID, ok := idMap[k]; ok && newID != k { + saved = append(saved, entry{k, v}) + } + } + if len(saved) == 0 { + return false + } + for _, e := range saved { + delete(m, e.key) + } + for _, e := range saved { + m[idMap[e.key]] = e.val + } + return true +} + +// RewriteRoomIDs is the shared entry point called by every room-ID change path +// (the swapid command and the admin web GUI rename) to migrate room-ID-embedded +// state across all characters. Online players are updated in memory (and +// re-saved); offline characters are loaded, rewritten, and saved. It follows +// the same cross-goroutine mutation precedent as cmd_undig/cmd_summon. +func (g *Game) RewriteRoomIDs(idMap map[int]int) { + if len(idMap) == 0 { + return + } + online := make(map[string]bool) + if g.Hub != nil { + for _, sess := range g.Hub.AllSessions() { + if sess.Player == nil { + continue + } + if rewritePlayerRoomIDs(sess.Player, idMap) { + g.AccountStore.SaveCharacter(sess.Player) + } + online[sess.Player.Name] = true + } + } + names, err := g.AccountStore.ListCharacterNames() + if err != nil { + return + } + for _, name := range names { + if online[name] { + continue + } + p, err := g.AccountStore.LoadCharacter(name) + if err != nil { + continue + } + if rewritePlayerRoomIDs(p, idMap) { + g.AccountStore.SaveCharacter(p) + } + } +} |
