aboutsummaryrefslogtreecommitdiff
path: root/internal/world/ground.go
diff options
context:
space:
mode:
Diffstat (limited to 'internal/world/ground.go')
-rw-r--r--internal/world/ground.go44
1 files changed, 43 insertions, 1 deletions
diff --git a/internal/world/ground.go b/internal/world/ground.go
index 2d5ea7b..6915072 100644
--- a/internal/world/ground.go
+++ b/internal/world/ground.go
@@ -119,7 +119,10 @@ func (w *World) RemoveReservedItem(roomID int, itemID string, qty int, owner str
}
// Pass 2: take. Every entry reachable here is takeable, so no
- // mid-loop abort is possible.
+ // mid-loop abort is possible. The ownership check MUST match pass 1:
+ // without it a player could consume a stack reserved for someone else
+ // when a public stack and a reserved stack coexist (the reserved entry
+ // comes first in the slice, so the take loop would drain it first).
removed := 0
remaining := qty
for _, e := range entries {
@@ -129,6 +132,9 @@ func (w *World) RemoveReservedItem(roomID int, itemID string, qty int, owner str
if e.quantity <= 0 || !strings.EqualFold(e.itemID, itemID) {
continue
}
+ if e.reserveTimer > 0 && e.reservedFor != "" && e.reservedFor != owner {
+ continue
+ }
take := e.quantity
if take > remaining {
take = remaining
@@ -143,6 +149,42 @@ func (w *World) RemoveReservedItem(roomID int, itemID string, qty int, owner str
return removed, true
}
+// RemoveAvailableItem removes up to maxQty of itemID from the room's
+// ground, skipping entries reserved for another player, and returns the
+// quantity actually removed. Unlike RemoveReservedItem it is not
+// all-or-nothing: callers pass a generous upper bound (credit pickups use
+// a sentinel far above any possible stock) and take whatever is accessible
+// at the moment, so a request larger than the available stock still
+// succeeds.
+func (w *World) RemoveAvailableItem(roomID int, itemID string, maxQty int, owner string) int {
+ w.mu.Lock()
+ defer w.mu.Unlock()
+ removed := 0
+ remaining := maxQty
+ for _, e := range w.groundItems[roomID] {
+ if remaining <= 0 {
+ break
+ }
+ if e.quantity <= 0 || !strings.EqualFold(e.itemID, itemID) {
+ continue
+ }
+ if e.reserveTimer > 0 && e.reservedFor != "" && e.reservedFor != owner {
+ 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
+}
+
// AccessibleGround returns the ground items in roomID this owner may take:
// entries reserved for another player are excluded. Pickup flows use this
// to size requests and match names against takeable stock only.