From 153acb7f644d755c45533b7c5311e3ffa47e8973 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 05:31:09 -0400 Subject: fix: harden engine across casino, economy, concurrency, security, and content --- internal/game/cmd_remove.go | 24 +++++++++++++++++++----- 1 file changed, 19 insertions(+), 5 deletions(-) (limited to 'internal/game/cmd_remove.go') diff --git a/internal/game/cmd_remove.go b/internal/game/cmd_remove.go index 349617d..5bc02a7 100644 --- a/internal/game/cmd_remove.go +++ b/internal/game/cmd_remove.go @@ -97,9 +97,12 @@ func (g *Game) removeAmmo(sess *net.Session, p *player.Player, qty int) { removeQty = qty } + quality, maxQuality := g.pickedStackProps(ammoID) for i := 0; i < 28; i++ { s := p.InvSlot(i) - if s != nil && s.ItemID == ammoID { + // Only a stack with the properties a fresh stack gets may absorb the + // equipped stack (see pickedStackProps). + if s != nil && s.ItemID == ammoID && s.Quality == quality && s.MaxQuality == maxQuality { s.Quantity += removeQty p.AmmoQty -= removeQty if p.AmmoQty <= 0 { @@ -123,7 +126,7 @@ func (g *Game) removeAmmo(sess *net.Session, p *player.Player, qty int) { return } - p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: ammoID, Quantity: removeQty}) + p.SetInvSlot(freeSlot, g.newInventorySlot(ammoID, removeQty)) p.AmmoQty -= removeQty if p.AmmoQty <= 0 { delete(p.Equipment, item.SlotAmmo) @@ -193,10 +196,17 @@ func (g *Game) doRemoveAll(sess *net.Session) { g.saveCharacter(p) } +// removeAllAmmo moves the whole equipped ammo stack into the inventory. The +// equipment entry is only cleared once the stack has been merged into an +// existing stack or placed in a free slot, so a full inventory reports a +// failure instead of silently destroying the ammo. func (g *Game) removeAllAmmo(sess *net.Session, p *player.Player, itemID string) { + quality, maxQuality := g.pickedStackProps(itemID) for i := 0; i < 28; i++ { s := p.InvSlot(i) - if s != nil && s.ItemID == itemID { + // Only a stack with the properties a fresh stack gets may absorb the + // equipped stack (see pickedStackProps). + if s != nil && s.ItemID == itemID && s.Quality == quality && s.MaxQuality == maxQuality { s.Quantity += p.AmmoQty delete(p.Equipment, item.SlotAmmo) p.AmmoQty = 0 @@ -210,9 +220,13 @@ func (g *Game) removeAllAmmo(sess *net.Session, p *player.Player, itemID string) } } freeSlot := p.FirstFreeSlot() - if freeSlot >= 0 { - p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: p.AmmoQty}) + if freeSlot < 0 { + // Nowhere to put the equipped stack: leave it equipped and report + // nothing removed. + sess.WriteLine("Nowhere to put that!") + return } + p.SetInvSlot(freeSlot, g.newInventorySlot(itemID, p.AmmoQty)) delete(p.Equipment, item.SlotAmmo) p.AmmoQty = 0 def, _ := g.ItemStore.Load(itemID) -- cgit v1.2.3