diff options
| author | historia <[not public]> | 2026-06-27 21:25:20 -0400 |
|---|---|---|
| committer | historia <[not public]> | 2026-06-27 21:25:20 -0400 |
| commit | 87dacfbb3dd16e7f55a82361eace98f9a39d75c8 (patch) | |
| tree | d548f75a37b5b8e960c5e5d111237e172650bb59 /internal/world/room.go | |
| parent | 83f8f5447ec3baf85314ce7343f0e339d28bcc15 (diff) | |
| download | thehouseoficarus-87dacfbb3dd16e7f55a82361eace98f9a39d75c8.tar.gz | |
feat: room-specific objects can be defined inline in room yaml
Diffstat (limited to 'internal/world/room.go')
| -rw-r--r-- | internal/world/room.go | 55 |
1 files changed, 52 insertions, 3 deletions
diff --git a/internal/world/room.go b/internal/world/room.go index 8f13b82..8b8830e 100644 --- a/internal/world/room.go +++ b/internal/world/room.go @@ -1,8 +1,11 @@ package world import ( + "strings" + "gopkg.in/yaml.v3" "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/object" ) type ExitDir string @@ -125,8 +128,54 @@ func (e EnterStep) IsTimed() bool { e.TakeItem != "" || e.Teleport != 0 || e.Heal != 0 } +// RoomObject is either a reference to a file-backed object definition (only +// `id`/wander fields set) or a fully inline object definition (Inline != nil). +// An entry is treated as inline when it carries any passive content field +// (name, description, aliases, inroom_description, color, hidden, on_look). +// Inline objects are room-scoped: their identity (ID, used as the ObjState +// DefID) is derived from the normalized name; `id:` is reserved for references. type RoomObject struct { - ID string `yaml:"id"` - WanderRooms []int `yaml:"wander_rooms"` - WanderInterval float64 `yaml:"wander_interval"` + ID string + WanderRooms []int + WanderInterval float64 + Inline *object.ObjectDef +} + +func (ro *RoomObject) UnmarshalYAML(value *yaml.Node) error { + type rawRef struct { + ID string `yaml:"id"` + WanderRooms []int `yaml:"wander_rooms"` + WanderInterval float64 `yaml:"wander_interval"` + } + var ref rawRef + if err := value.Decode(&ref); err != nil { + return err + } + ro.WanderRooms = ref.WanderRooms + ro.WanderInterval = ref.WanderInterval + + var def object.ObjectDef + if err := value.Decode(&def); err != nil { + return err + } + inline := def.Name != "" || len(def.Description) > 0 || def.InRoomDescription != "" || + len(def.Aliases) > 0 || def.Color != "" || def.Hidden || def.OnLook != nil + if inline { + // Identity derives from the name. def.ID still holds any explicit `id:` + // the builder wrote; it is left in place only so validation can flag it + // as a stray field (runtime resolution overrides the copy's ID anyway). + ro.ID = NormalizeObjectName(def.Name) + ro.Inline = &def + return nil + } + ro.ID = ref.ID + return nil +} + +// NormalizeObjectName lowercases, trims, and collapses internal whitespace, +// keeping spaces (not underscores) so the word-prefix matcher treats the name +// as a single token in its `_`-split fallback. It is the canonical derivation +// of an inline object's ObjState DefID from its name. +func NormalizeObjectName(name string) string { + return strings.Join(strings.Fields(strings.ToLower(name)), " ") } |
