aboutsummaryrefslogtreecommitdiff
path: root/internal/world/world.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-06-10 01:48:28 -0400
committerhistoria <[not public]>2026-06-10 01:48:28 -0400
commita226d72e51eecb768b13600303f73483118d9104 (patch)
tree458d15ef049732c15dacc591a830d3beddbedfde /internal/world/world.go
parent6a0f3d7a252de4b1741cfe5e1c561412c602becc (diff)
downloadthehouseoficarus-a226d72e51eecb768b13600303f73483118d9104.tar.gz
feat: implemented janky object interaction model
Diffstat (limited to 'internal/world/world.go')
-rw-r--r--internal/world/world.go250
1 files changed, 246 insertions, 4 deletions
diff --git a/internal/world/world.go b/internal/world/world.go
index 1429081..71fd518 100644
--- a/internal/world/world.go
+++ b/internal/world/world.go
@@ -2,8 +2,10 @@ package world
import (
"fmt"
+ "math/rand"
"os"
"path/filepath"
+ "sort"
"strings"
"sync"
@@ -37,6 +39,177 @@ type World struct {
mu sync.Mutex
groundItems map[int][]*groundEntry
seeded map[int]bool
+ objStates map[string]*ObjState
+ objMoves []ObjMove
+}
+
+type ObjMove struct {
+ DefID string
+ Name string
+ FromRoom int
+ ToRoom int
+}
+
+type ObjState struct {
+ Depleted bool
+ DepleteTimer int
+ DefID string
+ Name string
+ Index int
+ RoomID int
+ JustRespawned bool
+ WanderRooms []int
+ WanderInterval int
+ WanderCounter int
+}
+
+func (w *World) ObjStateKey(roomID int, defID string, index int) string {
+ return fmt.Sprintf("%d:%s:%d", roomID, defID, index)
+}
+
+func (w *World) objStateKey(roomID int, defID string, index int) string {
+ return w.ObjStateKey(roomID, defID, index)
+}
+
+func (w *World) EnsureObjectStates(roomID int, defIDs []string) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if w.objStates == nil {
+ w.objStates = make(map[string]*ObjState)
+ }
+ counts := make(map[string]int)
+ for _, defID := range defIDs {
+ idx := counts[defID]
+ counts[defID]++
+ key := w.objStateKey(roomID, defID, idx)
+ if _, exists := w.objStates[key]; !exists {
+ w.objStates[key] = &ObjState{
+ DefID: defID,
+ Name: defID,
+ Index: idx,
+ RoomID: roomID,
+ }
+ }
+ }
+}
+
+func (w *World) GetObjState(roomID int, defID string, index int) *ObjState {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if w.objStates == nil {
+ return nil
+ }
+ return w.objStates[w.objStateKey(roomID, defID, index)]
+}
+
+func (w *World) GetObjStateByKey(key string) *ObjState {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ if w.objStates == nil {
+ return nil
+ }
+ return w.objStates[key]
+}
+
+func (w *World) AllObjInstances(roomID int) []ObjState {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ var out []ObjState
+ for _, st := range w.objStates {
+ if st.RoomID == roomID {
+ out = append(out, *st)
+ }
+ }
+ sort.Slice(out, func(i, j int) bool {
+ if out[i].DefID != out[j].DefID {
+ return out[i].DefID < out[j].DefID
+ }
+ return out[i].Index < out[j].Index
+ })
+ return out
+}
+
+func (w *World) FindObjInstances(roomID int, name string) []ObjState {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ var out []ObjState
+ lower := strings.ToLower(name)
+ for key, st := range w.objStates {
+ if st.RoomID != roomID {
+ continue
+ }
+ if !wordMatchesObj(lower, st.DefID, st.Name) {
+ continue
+ }
+ out = append(out, ObjState{
+ DefID: st.DefID,
+ Name: st.Name,
+ Index: st.Index,
+ RoomID: st.RoomID,
+ Depleted: st.Depleted,
+ DepleteTimer: st.DepleteTimer,
+ })
+ _ = key
+ }
+ sort.Slice(out, func(i, j int) bool {
+ if out[i].DefID != out[j].DefID {
+ return out[i].DefID < out[j].DefID
+ }
+ return out[i].Index < out[j].Index
+ })
+ return out
+}
+
+func wordMatchesObj(lower, defID, objName string) bool {
+ // Check against display name (space-separated words)
+ for _, word := range strings.Fields(objName) {
+ if strings.HasPrefix(strings.ToLower(word), lower) {
+ return true
+ }
+ }
+ // Check defID both as whole and split by underscores
+ if strings.HasPrefix(strings.ToLower(defID), lower) {
+ return true
+ }
+ for _, part := range strings.Split(defID, "_") {
+ if strings.HasPrefix(strings.ToLower(part), lower) {
+ return true
+ }
+ }
+ return false
+}
+
+func (w *World) SetObjName(roomID int, defID string, name string) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ for key, st := range w.objStates {
+ if st.RoomID == roomID && st.DefID == defID {
+ st.Name = name
+ }
+ _ = key
+ }
+}
+
+func (w *World) SetObjWander(roomID int, defID string, rooms []int, interval int) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ for key, st := range w.objStates {
+ if st.RoomID == roomID && st.DefID == defID {
+ st.WanderRooms = rooms
+ st.WanderInterval = interval
+ }
+ _ = key
+ }
+}
+
+func (w *World) SetObjDepleted(roomID int, defID string, index int, delay int) {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ key := w.objStateKey(roomID, defID, index)
+ if st, ok := w.objStates[key]; ok {
+ st.Depleted = true
+ st.DepleteTimer = delay
+ }
}
func New(dataDir string) *World {
@@ -44,6 +217,7 @@ func New(dataDir string) *World {
dataDir: dataDir,
groundItems: make(map[int][]*groundEntry),
seeded: make(map[int]bool),
+ objStates: make(map[string]*ObjState),
}
}
@@ -59,10 +233,7 @@ func (w *World) LoadRoom(id int) (*Room, error) {
}
room.ID = id
if room.Exits == nil {
- room.Exits = make(map[ExitDir]int)
- }
- if room.Objects == nil {
- room.Objects = make([]string, 0)
+ room.Exits = make(map[ExitDir]ExitDef)
}
if room.Spawns == nil {
room.Spawns = make([]SpawnDef, 0)
@@ -261,4 +432,75 @@ func (w *World) Tick() {
}
}
}
+
+ for _, st := range w.objStates {
+ if st.Depleted && st.DepleteTimer > 0 {
+ st.DepleteTimer--
+ if st.DepleteTimer <= 0 {
+ st.Depleted = false
+ st.JustRespawned = true
+ }
+ }
+ }
+}
+
+func (w *World) FlushObjRespawns() []ObjState {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ var out []ObjState
+ for _, st := range w.objStates {
+ if st.JustRespawned {
+ out = append(out, *st)
+ st.JustRespawned = false
+ }
+ }
+ return out
+}
+
+func (w *World) TickObjWander() {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ var moves []struct {
+ st *ObjState
+ toRoom int
+ }
+ for _, st := range w.objStates {
+ if len(st.WanderRooms) == 0 || st.WanderInterval <= 0 {
+ continue
+ }
+ st.WanderCounter++
+ if st.WanderCounter >= st.WanderInterval {
+ st.WanderCounter = 0
+ toRoom := st.WanderRooms[rand.Intn(len(st.WanderRooms))]
+ if toRoom == st.RoomID {
+ continue
+ }
+ moves = append(moves, struct {
+ st *ObjState
+ toRoom int
+ }{st, toRoom})
+ }
+ }
+ for _, m := range moves {
+ oldKey := w.objStateKey(m.st.RoomID, m.st.DefID, m.st.Index)
+ fromRoom := m.st.RoomID
+ m.st.RoomID = m.toRoom
+ newKey := w.objStateKey(m.st.RoomID, m.st.DefID, m.st.Index)
+ delete(w.objStates, oldKey)
+ w.objStates[newKey] = m.st
+ w.objMoves = append(w.objMoves, ObjMove{
+ DefID: m.st.DefID,
+ Name: m.st.Name,
+ FromRoom: fromRoom,
+ ToRoom: m.toRoom,
+ })
+ }
+}
+
+func (w *World) FlushObjMoves() []ObjMove {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ out := w.objMoves
+ w.objMoves = nil
+ return out
}