aboutsummaryrefslogtreecommitdiff
path: root/internal/game/cmd_get.go
diff options
context:
space:
mode:
authorhistoria <[not public]>2026-09-18 17:48:26 -0400
committerhistoria <[not public]>2026-09-18 17:48:26 -0400
commit8fe9e3c3fef0049df82281a62afe35dda516dfe2 (patch)
treedd269f2c0710f334e19f00a191260ebc46284a90 /internal/game/cmd_get.go
parent194164a77eb4f2479b94c19e6f0e1278ab608781 (diff)
downloadthehouseoficarus-8fe9e3c3fef0049df82281a62afe35dda516dfe2.tar.gz
fix: close combat ghost, admin traversal, login throttle, journal double-pay, and quality/economy gaps
Diffstat (limited to 'internal/game/cmd_get.go')
-rw-r--r--internal/game/cmd_get.go54
1 files changed, 39 insertions, 15 deletions
diff --git a/internal/game/cmd_get.go b/internal/game/cmd_get.go
index a2ad137..33f55de 100644
--- a/internal/game/cmd_get.go
+++ b/internal/game/cmd_get.go
@@ -75,7 +75,7 @@ func (g *Game) doGet(sess *net.Session, input string) {
// doGetAllNamed). See TestPickupMergesIntoFullInventory.
if def != nil && def.Stackable {
- quality, maxQuality := g.pickedStackProps(itemID)
+ quality, maxQuality := g.groundStackProps(p, itemID)
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
if slot == nil || slot.ItemID != itemID || slot.Quality != quality || slot.MaxQuality != maxQuality {
@@ -96,11 +96,12 @@ func (g *Game) doGet(sess *net.Session, input string) {
sess.WriteLine("Your inventory is full.")
return
}
- if _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, qty, p.Name); !ok {
+ removed, q, mq, ok := g.World.RemoveReservedItemQ(p.RoomID, itemID, qty, p.Name)
+ if !ok {
sess.WriteLine("That's not yours!")
return
}
- p.SetInvSlot(freeSlot, g.newInventorySlot(itemID, qty))
+ p.SetInvSlot(freeSlot, g.groundSlot(itemID, removed, q, mq))
g.saveCharacter(p)
if qty == 1 {
sess.WriteLine(fmt.Sprintf("You pick up a %s.", g.itemColorize(sess, def, def.Name)))
@@ -116,10 +117,11 @@ func (g *Game) doGet(sess *net.Session, input string) {
if fs == -1 {
break
}
- if _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, 1, p.Name); !ok {
+ _, q, mq, ok := g.World.RemoveReservedItemQ(p.RoomID, itemID, 1, p.Name)
+ if !ok {
break
}
- p.SetInvSlot(fs, g.newInventorySlot(itemID, 1))
+ p.SetInvSlot(fs, g.groundSlot(itemID, 1, q, mq))
picked++
}
@@ -195,7 +197,7 @@ func (g *Game) doGetAllNamed(sess *net.Session, input string) {
coloredName := g.itemColorize(sess, def, name)
if def != nil && def.Stackable {
- quality, maxQuality := g.pickedStackProps(itemID)
+ quality, maxQuality := g.groundStackProps(p, itemID)
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
if slot == nil || slot.ItemID != itemID || slot.Quality != quality || slot.MaxQuality != maxQuality {
@@ -216,12 +218,12 @@ func (g *Game) doGetAllNamed(sess *net.Session, input string) {
sess.WriteLine("Your inventory is full.")
return
}
- removed, ok := g.World.RemoveReservedItem(p.RoomID, itemID, wanted, p.Name)
+ removed, q, mq, ok := g.World.RemoveReservedItemQ(p.RoomID, itemID, wanted, p.Name)
if !ok {
sess.WriteLine("That's not yours!")
return
}
- p.SetInvSlot(freeSlot, g.newInventorySlot(itemID, removed))
+ p.SetInvSlot(freeSlot, g.groundSlot(itemID, removed, q, mq))
g.saveCharacter(p)
if removed == 1 {
sess.WriteLine(fmt.Sprintf("You pick up a %s.", coloredName))
@@ -237,11 +239,11 @@ func (g *Game) doGetAllNamed(sess *net.Session, input string) {
if fs == -1 {
break
}
- _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, 1, p.Name)
+ _, q, mq, ok := g.World.RemoveReservedItemQ(p.RoomID, itemID, 1, p.Name)
if !ok {
break
}
- p.SetInvSlot(fs, g.newInventorySlot(itemID, 1))
+ p.SetInvSlot(fs, g.groundSlot(itemID, 1, q, mq))
picked++
}
@@ -303,7 +305,7 @@ func (g *Game) doGetAll(sess *net.Session) {
for qty > 0 {
if def != nil && def.Stackable {
stacked := false
- quality, maxQuality := g.pickedStackProps(itemID)
+ quality, maxQuality := g.groundStackProps(p, itemID)
for i := 0; i < 28; i++ {
slot := p.InvSlot(i)
if slot == nil || slot.ItemID != itemID || slot.Quality != quality || slot.MaxQuality != maxQuality {
@@ -335,11 +337,11 @@ func (g *Game) doGetAll(sess *net.Session) {
g.saveCharacter(p)
return
}
- _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, qty, p.Name)
+ removed, q, mq, ok := g.World.RemoveReservedItemQ(p.RoomID, itemID, qty, p.Name)
if !ok {
break
}
- p.SetInvSlot(freeSlot, g.newInventorySlot(itemID, qty))
+ p.SetInvSlot(freeSlot, g.groundSlot(itemID, removed, q, mq))
name := itemID
if def != nil {
name = def.Name
@@ -361,11 +363,11 @@ func (g *Game) doGetAll(sess *net.Session) {
}
take := 1
- _, ok := g.World.RemoveReservedItem(p.RoomID, itemID, take, p.Name)
+ _, q, mq, ok := g.World.RemoveReservedItemQ(p.RoomID, itemID, take, p.Name)
if !ok {
break
}
- p.SetInvSlot(freeSlot, g.newInventorySlot(itemID, take))
+ p.SetInvSlot(freeSlot, g.groundSlot(itemID, take, q, mq))
name := itemID
if def != nil {
name = def.Name
@@ -433,6 +435,28 @@ func (g *Game) pickedStackProps(itemID string) (quality, maxQuality int) {
return stackProps(def)
}
+// groundStackProps is pickedStackProps for a ground pickup: it prefers the
+// quality carried by the ground entry so a partially-used item is not
+// re-created at full quality (and a pickup only merges into a slot with the
+// same properties).
+func (g *Game) groundStackProps(p *player.Player, itemID string) (quality, maxQuality int) {
+ if q, mq, ok := g.World.AccessibleGroundQuality(p.RoomID, itemID, p.Name); ok && mq > 0 {
+ return q, mq
+ }
+ return g.pickedStackProps(itemID)
+}
+
+// groundSlot builds an inventory slot for a ground pickup, preserving the
+// quality carried by the ground entry. A ground entry with no quality record
+// (loot/trigger/spawn drops) falls back to the item's full quality, matching
+// newInventorySlot.
+func (g *Game) groundSlot(itemID string, qty, quality, maxQuality int) *player.InventorySlot {
+ if maxQuality > 0 {
+ return &player.InventorySlot{ItemID: itemID, Quantity: qty, Quality: quality, MaxQuality: maxQuality}
+ }
+ return g.newInventorySlot(itemID, qty)
+}
+
func (g *Game) collectMatches(input string, each func(func(itemID string, slot int) bool)) []itemMatch {
var matches []itemMatch
each(func(itemID string, slot int) bool {