aboutsummaryrefslogtreecommitdiff
path: root/internal/world/world.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/world/world.go')
-rw-r--r--internal/world/world.go506
1 files changed, 2 insertions, 504 deletions
diff --git a/internal/world/world.go b/internal/world/world.go
index f918293..3094c54 100644
--- a/internal/world/world.go
+++ b/internal/world/world.go
@@ -2,40 +2,14 @@ package world
import (
"fmt"
- "math/rand"
"os"
"path/filepath"
- "sort"
"strings"
"sync"
"gopkg.in/yaml.v3"
)
-const DropDespawnTicks = 1000
-const ReserveTicks = 100
-
-type GroundItemInfo struct {
- ItemID string
- Quantity int
- ReservedFor string
- ReserveTimer int
- DespawnTimer int
- IsSpawn bool
-}
-
-type groundEntry struct {
- itemID string
- quantity int
- isSpawn bool
- respawnTimer int // >0 = counting down to respawn
- respawnQty int
- respawnDelay int
- despawnTimer int // >0 = counting down to despawn (dropped items)
- reservedFor string
- reserveTimer int // >0 = counting down reservation
-}
-
type World struct {
dataDir string
mu sync.Mutex
@@ -45,255 +19,6 @@ type World struct {
objMoves []ObjMove
}
-type ObjMove struct {
- DefID string
- Name string
- FromRoom int
- ToRoom int
-}
-
-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
-}
-
-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()
- 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 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 New(dataDir string) *World {
return &World{
dataDir: dataDir,
@@ -342,129 +67,6 @@ func (w *World) ResolveExit(input string) ExitDir {
return ""
}
-func (w *World) GroundItemsDetailed(roomID int) []GroundItemInfo {
- w.mu.Lock()
- defer w.mu.Unlock()
- var out []GroundItemInfo
- for _, e := range w.groundItems[roomID] {
- if e.quantity <= 0 {
- continue
- }
- info := GroundItemInfo{
- ItemID: e.itemID,
- Quantity: e.quantity,
- DespawnTimer: e.despawnTimer,
- IsSpawn: e.isSpawn,
- }
- if e.reserveTimer > 0 && e.reservedFor != "" {
- info.ReservedFor = e.reservedFor
- info.ReserveTimer = e.reserveTimer
- }
- out = append(out, info)
- }
- return out
-}
-
-func (w *World) GroundItems(roomID int) map[string]int {
- w.mu.Lock()
- defer w.mu.Unlock()
- out := make(map[string]int)
- for _, e := range w.groundItems[roomID] {
- if e.quantity > 0 {
- out[e.itemID] += e.quantity
- }
- }
- return out
-}
-
-func (w *World) AddReservedGroundItem(roomID int, itemID string, qty int, owner string) {
- w.mu.Lock()
- defer w.mu.Unlock()
- e := &groundEntry{
- itemID: itemID,
- quantity: qty,
- despawnTimer: DropDespawnTicks,
- reservedFor: owner,
- reserveTimer: ReserveTicks,
- }
- w.groundItems[roomID] = append(w.groundItems[roomID], e)
-}
-
-func (w *World) AddGroundItem(roomID int, itemID string, qty int) {
- w.mu.Lock()
- defer w.mu.Unlock()
- e := &groundEntry{
- itemID: itemID,
- quantity: qty,
- despawnTimer: DropDespawnTicks,
- }
- w.groundItems[roomID] = append(w.groundItems[roomID], e)
-}
-
-func (w *World) RemoveReservedGroundItem(roomID int, itemID string, qty int, owner string) (int, bool) {
- w.mu.Lock()
- defer w.mu.Unlock()
- removed := 0
- remaining := qty
- for _, e := range w.groundItems[roomID] {
- if remaining <= 0 {
- break
- }
- if !strings.EqualFold(e.itemID, itemID) {
- continue
- }
- if e.quantity <= 0 {
- continue
- }
- if e.reserveTimer > 0 && e.reservedFor != "" && e.reservedFor != owner {
- return removed, false
- }
- take := e.quantity
- if take > remaining {
- take = remaining
- }
- e.quantity -= take
- removed += take
- remaining -= take
- if e.isSpawn && e.quantity <= 0 && e.respawnDelay > 0 {
- e.respawnTimer = e.respawnDelay
- }
- }
- return removed, true
-}
-
-func (w *World) RemoveGroundItem(roomID int, itemID string, qty int) int {
- w.mu.Lock()
- defer w.mu.Unlock()
-
- removed := 0
- remaining := qty
-
- for _, e := range w.groundItems[roomID] {
- if remaining <= 0 {
- break
- }
- if !strings.EqualFold(e.itemID, itemID) {
- continue
- }
- if e.quantity <= 0 {
- continue
- }
- take := e.quantity
- if take > remaining {
- take = remaining
- }
- e.quantity -= take
- removed += take
- remaining -= take
-
- if e.isSpawn && e.quantity <= 0 && e.respawnDelay > 0 {
- e.respawnTimer = e.respawnDelay
- }
- }
- return removed
-}
-
func (w *World) SeedGroundItems(roomID int) {
w.mu.Lock()
if w.seeded[roomID] {
@@ -512,110 +114,6 @@ func (w *World) SeedGroundItems(roomID int) {
func (w *World) Tick() {
w.mu.Lock()
defer w.mu.Unlock()
-
- for _, entries := range w.groundItems {
- for _, e := range entries {
- if e.respawnTimer > 0 {
- e.respawnTimer--
- if e.respawnTimer <= 0 {
- e.quantity = e.respawnQty
- for _, other := range entries {
- if other != e && other.quantity > 0 && strings.EqualFold(other.itemID, e.itemID) &&
- (other.reserveTimer <= 0 || other.reservedFor == "") &&
- other.despawnTimer <= 0 {
- e.quantity += other.quantity
- other.quantity = 0
- }
- }
- }
- }
- if e.despawnTimer > 0 {
- e.despawnTimer--
- if e.despawnTimer <= 0 {
- e.quantity = 0
- }
- }
- if e.reserveTimer > 0 {
- e.reserveTimer--
- if e.reserveTimer <= 0 {
- e.reservedFor = ""
- }
- }
- }
- }
-
- 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)
- }
- }
- }
- }
-}
-
-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
+ w.tickGroundItemsLocked()
+ w.tickObjStatesLocked()
}