From 0ea4eec5dd2122c6704216971eb1249276297867 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 19 Jun 2026 18:20:26 -0400 Subject: shop fixes, 'toggle' completely removed, movement speed increased --- internal/world/objects.go | 332 ++++++++++++++++++++++++++++++++++++++++++++++ 1 file changed, 332 insertions(+) create mode 100644 internal/world/objects.go (limited to 'internal/world/objects.go') diff --git a/internal/world/objects.go b/internal/world/objects.go new file mode 100644 index 0000000..568ea40 --- /dev/null +++ b/internal/world/objects.go @@ -0,0 +1,332 @@ +package world + +import ( + "fmt" + "math/rand" + "sort" + "strings" + + "thehouseoficarus/internal/action" +) + +type ObjState struct { + Depleted bool + DepleteTimer float64 + DefID string + Name string + Index int + RoomID int + JustRespawned bool + WanderRooms []int + WanderInterval float64 + WanderCounter int + SharedMax float64 + SharedTimer int + Quality float64 +} + +type ObjMove struct { + DefID string + Name string + FromRoom int + ToRoom int +} + +func (w *World) ObjStateKey(roomID int, defID string, index int) string { + return fmt.Sprintf("%d:%s:%d", roomID, defID, index) +} + +func (w *World) EnsureObjectStates(roomID int, defIDs []string) { + w.mu.Lock() + defer w.mu.Unlock() + w.ensureObjectStatesLocked(roomID, defIDs) +} + +func (w *World) ensureObjectStatesLocked(roomID int, defIDs []string) { + 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) AddObjInstance(roomID int, defID string, quality float64) { + w.mu.Lock() + defer w.mu.Unlock() + if w.objStates == nil { + w.objStates = make(map[string]*ObjState) + } + idx := 0 + for { + key := w.ObjStateKey(roomID, defID, idx) + if _, exists := w.objStates[key]; !exists { + w.objStates[key] = &ObjState{ + DefID: defID, + Name: defID, + Index: idx, + RoomID: roomID, + Quality: quality, + } + return + } + idx++ + } +} + +func (w *World) RemoveObjInstance(roomID int, defID string) { + w.mu.Lock() + defer w.mu.Unlock() + idx := 0 + for { + key := w.ObjStateKey(roomID, defID, idx) + if _, exists := w.objStates[key]; !exists { + return + } + delete(w.objStates, key) + idx++ + } +} + +func (w *World) AllFireStates() []*ObjState { + w.mu.Lock() + defer w.mu.Unlock() + var out []*ObjState + for _, st := range w.objStates { + if st.DefID == "fire" && st.Quality > 0 { + out = append(out, st) + } + } + return out +} + +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 _, 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, + SharedMax: st.SharedMax, + SharedTimer: st.SharedTimer, + Quality: st.Quality, + }) + } + 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 { + if action.WordPrefixMatch(lower, objName) { + return true + } + inputWords := strings.Fields(strings.ToLower(lower)) + + if strings.HasPrefix(strings.ToLower(defID), lower) || strings.HasPrefix(lower, strings.ToLower(defID)) { + return true + } + for _, part := range strings.Split(defID, "_") { + for _, iw := range inputWords { + pl := strings.ToLower(part) + if strings.HasPrefix(pl, iw) || strings.HasPrefix(iw, pl) { + return true + } + } + } + return false +} + +func (w *World) SetObjName(roomID int, defID string, name string) { + w.mu.Lock() + defer w.mu.Unlock() + for _, st := range w.objStates { + if st.RoomID == roomID && st.DefID == defID { + st.Name = name + } + } +} + +func (w *World) SetObjWander(roomID int, defID string, rooms []int, interval float64) { + w.mu.Lock() + defer w.mu.Unlock() + for _, st := range w.objStates { + if st.RoomID == roomID && st.DefID == defID { + st.WanderRooms = rooms + st.WanderInterval = interval + } + } +} + +func (w *World) SetObjDepleteTimer(roomID int, defID string, max float64) { + w.mu.Lock() + defer w.mu.Unlock() + for _, st := range w.objStates { + if st.RoomID == roomID && st.DefID == defID && st.SharedMax == 0 { + st.SharedMax = max + st.SharedTimer = int(max) + } + } +} + +func (w *World) AllSharedObjStates() []*ObjState { + w.mu.Lock() + defer w.mu.Unlock() + var out []*ObjState + for _, st := range w.objStates { + if st.SharedMax > 0 { + out = append(out, st) + } + } + return out +} + +func (w *World) SetObjDepleted(roomID int, defID string, index int, delay float64) { + 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 (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 float64(st.WanderCounter) < st.WanderInterval { + continue + } + 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 +} + +func (w *World) tickObjStatesLocked() { + for _, st := range w.objStates { + if st.Depleted && st.DepleteTimer > 0 { + st.DepleteTimer-- + if st.DepleteTimer <= 0 { + st.Depleted = false + st.JustRespawned = true + if st.SharedMax > 0 { + st.SharedTimer = int(st.SharedMax) + } + } + } + } +} -- cgit v1.2.3