diff options
Diffstat (limited to 'internal/world')
| -rw-r--r-- | internal/world/mob.go | 46 | ||||
| -rw-r--r-- | internal/world/room.go | 21 | ||||
| -rw-r--r-- | internal/world/world.go | 107 |
3 files changed, 132 insertions, 42 deletions
diff --git a/internal/world/mob.go b/internal/world/mob.go index 9dfec59..59c8a20 100644 --- a/internal/world/mob.go +++ b/internal/world/mob.go @@ -33,8 +33,6 @@ type MobDef struct { Protected bool `yaml:"protected"` Unique bool `yaml:"unique"` RespawnTicks int `yaml:"respawn_ticks"` - WanderRooms []int `yaml:"wander_rooms"` - WanderInterval int `yaml:"wander_interval"` Drops DropTable `yaml:"drops"` } @@ -70,13 +68,24 @@ const ( ) func WordPrefixMatch(input, name string) bool { - lower := strings.ToLower(input) - for _, word := range strings.Fields(name) { - if strings.HasPrefix(strings.ToLower(word), lower) { - return true + inputWords := strings.Fields(strings.ToLower(input)) + if len(inputWords) == 0 { + return false + } + nameWords := strings.Fields(strings.ToLower(name)) + for _, iw := range inputWords { + found := false + for _, nw := range nameWords { + if strings.HasPrefix(nw, iw) || strings.HasPrefix(iw, nw) { + found = true + break + } + } + if !found { + return false } } - return false + return true } func (m *MobInstance) MatchQuality(input string) int { @@ -198,20 +207,19 @@ func (s *MobStore) RollIdleDescription(inst *MobInstance) { return } inst.IdleDescription = pickIdleDescription(def.IdleDescriptions) - inst.WanderRooms = def.WanderRooms - inst.WanderInterval = def.WanderInterval inst.WanderTickCounter = 0 } -func (s *MobStore) SeedMobs(roomID int, mobIDs []string) { +func (s *MobStore) SeedMobs(roomID int, entries []RoomMob) { type defWrapper struct { def *MobDef err error + rm RoomMob } - defs := make([]defWrapper, len(mobIDs)) - for i, defID := range mobIDs { - d, err := s.LoadDef(defID) - defs[i] = defWrapper{d, err} + defs := make([]defWrapper, len(entries)) + for i, rm := range entries { + d, err := s.LoadDef(rm.ID) + defs[i] = defWrapper{d, err, rm} } s.mu.Lock() @@ -220,11 +228,9 @@ func (s *MobStore) SeedMobs(roomID int, mobIDs []string) { if dw.err != nil { continue } - defID := mobIDs[i] + defID := dw.rm.ID instID := fmt.Sprintf("%s_%d_%d", defID, roomID, i) - if inst, exists := s.instances[instID]; exists { - // Already exists — skip (respawn is handled by timers) - _ = inst + if _, exists := s.instances[instID]; exists { continue } inst := &MobInstance{ @@ -242,8 +248,8 @@ func (s *MobStore) SeedMobs(roomID int, mobIDs []string) { Protected: dw.def.Protected, Unique: dw.def.Unique, RespawnTicks: dw.def.RespawnTicks, - WanderRooms: dw.def.WanderRooms, - WanderInterval: dw.def.WanderInterval, + WanderRooms: dw.rm.WanderRooms, + WanderInterval: dw.rm.WanderInterval, RoomID: roomID, HomeRoomID: roomID, Drops: dw.def.Drops, diff --git a/internal/world/room.go b/internal/world/room.go index 58fa82b..46fb876 100644 --- a/internal/world/room.go +++ b/internal/world/room.go @@ -73,10 +73,29 @@ type Room struct { Exits map[ExitDir]ExitDef `yaml:"exits"` Objects []RoomObject `yaml:"objects"` Spawns []SpawnDef `yaml:"spawns"` - Mobs []string `yaml:"mobs"` + Mobs []RoomMob `yaml:"mobs"` OnEnter []EnterStep `yaml:"on_enter"` } +type RoomMob struct { + ID string `yaml:"id"` + WanderRooms []int `yaml:"wander_rooms"` + WanderInterval int `yaml:"wander_interval"` +} + +func (rm *RoomMob) UnmarshalYAML(value *yaml.Node) error { + if value.Kind == yaml.ScalarNode { + var s string + if err := value.Decode(&s); err != nil { + return err + } + rm.ID = s + return nil + } + type raw RoomMob + return value.Decode((*raw)(rm)) +} + type EnterStep struct { Message string `yaml:"message"` Condition *action.Condition `yaml:"condition"` diff --git a/internal/world/world.go b/internal/world/world.go index 71fd518..c8270b1 100644 --- a/internal/world/world.go +++ b/internal/world/world.go @@ -61,6 +61,8 @@ type ObjState struct { WanderRooms []int WanderInterval int WanderCounter int + SharedMax int + SharedTimer int } func (w *World) ObjStateKey(roomID int, defID string, index int) string { @@ -148,6 +150,8 @@ func (w *World) FindObjInstances(roomID int, name string) []ObjState { RoomID: st.RoomID, Depleted: st.Depleted, DepleteTimer: st.DepleteTimer, + SharedMax: st.SharedMax, + SharedTimer: st.SharedTimer, }) _ = key } @@ -161,21 +165,26 @@ func (w *World) FindObjInstances(roomID int, name string) []ObjState { } 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 - } + if WordPrefixMatch(lower, objName) { + return true } - // Check defID both as whole and split by underscores - if strings.HasPrefix(strings.ToLower(defID), lower) { + nameWords := strings.Fields(strings.ToLower(objName)) + inputWords := strings.Fields(strings.ToLower(lower)) + + // Check defID as whole (bidirectional) + if strings.HasPrefix(strings.ToLower(defID), lower) || strings.HasPrefix(lower, strings.ToLower(defID)) { return true } + // Check each defID part (split by underscore) against each input word (bidirectional) for _, part := range strings.Split(defID, "_") { - if strings.HasPrefix(strings.ToLower(part), lower) { - return true + for _, iw := range inputWords { + pl := strings.ToLower(part) + if strings.HasPrefix(pl, iw) || strings.HasPrefix(iw, pl) { + return true + } } } + _ = nameWords return false } @@ -202,6 +211,30 @@ func (w *World) SetObjWander(roomID int, defID string, rooms []int, interval int } } +func (w *World) SetObjSharedDeplete(roomID int, defID string, max int) { + w.mu.Lock() + defer w.mu.Unlock() + for key, st := range w.objStates { + if st.RoomID == roomID && st.DefID == defID && st.SharedMax == 0 { + st.SharedMax = max + st.SharedTimer = max + } + _ = key + } +} + +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 int) { w.mu.Lock() defer w.mu.Unlock() @@ -239,7 +272,7 @@ func (w *World) LoadRoom(id int) (*Room, error) { room.Spawns = make([]SpawnDef, 0) } if room.Mobs == nil { - room.Mobs = make([]string, 0) + room.Mobs = make([]RoomMob, 0) } return &room, nil } @@ -305,6 +338,14 @@ func (w *World) AddReservedGroundItem(roomID int, itemID string, qty int, owner func (w *World) AddGroundItem(roomID int, itemID string, qty int) { w.mu.Lock() defer w.mu.Unlock() + for _, e := range w.groundItems[roomID] { + if e.quantity > 0 && strings.EqualFold(e.itemID, itemID) && + (e.reserveTimer <= 0 || e.reservedFor == "") { + e.quantity += qty + e.despawnTimer = DropDespawnTicks + return + } + } e := &groundEntry{ itemID: itemID, quantity: qty, @@ -395,14 +436,28 @@ func (w *World) SeedGroundItems(roomID int) { defer w.mu.Unlock() for _, s := range room.Spawns { - e := &groundEntry{ - itemID: s.ItemID, - quantity: s.Quantity, - isSpawn: true, - respawnDelay: s.RespawnTicks, - respawnQty: s.Quantity, + merged := false + for _, e := range w.groundItems[roomID] { + if e.quantity > 0 && strings.EqualFold(e.itemID, s.ItemID) && + (e.reserveTimer <= 0 || e.reservedFor == "") { + e.quantity += s.Quantity + e.respawnQty += s.Quantity + e.isSpawn = true + e.respawnDelay = s.RespawnTicks + merged = true + break + } + } + if !merged { + e := &groundEntry{ + itemID: s.ItemID, + quantity: s.Quantity, + isSpawn: true, + respawnDelay: s.RespawnTicks, + respawnQty: s.Quantity, + } + w.groundItems[roomID] = append(w.groundItems[roomID], e) } - w.groundItems[roomID] = append(w.groundItems[roomID], e) } } @@ -412,12 +467,19 @@ func (w *World) Tick() { for _, entries := range w.groundItems { for _, e := range entries { - if e.respawnTimer > 0 { - e.respawnTimer-- - if e.respawnTimer <= 0 { - e.quantity = e.respawnQty + 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 == "") { + e.quantity += other.quantity + other.quantity = 0 + } } } + } if e.despawnTimer > 0 { e.despawnTimer-- if e.despawnTimer <= 0 { @@ -439,6 +501,9 @@ func (w *World) Tick() { if st.DepleteTimer <= 0 { st.Depleted = false st.JustRespawned = true + if st.SharedMax > 0 { + st.SharedTimer = st.SharedMax + } } } } |
