From 7eab582d8a8ee2af1a6baae7baf0696c7005de2e Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Fri, 18 Sep 2026 15:16:44 -0400 Subject: fix: harden triggers, economy, casino, world data, and admin/net paths --- internal/game/cmd_trigger_utility.go | 62 ++++++++++++++++++++++-------------- 1 file changed, 38 insertions(+), 24 deletions(-) (limited to 'internal/game/cmd_trigger_utility.go') diff --git a/internal/game/cmd_trigger_utility.go b/internal/game/cmd_trigger_utility.go index b5a168b..f9e3692 100644 --- a/internal/game/cmd_trigger_utility.go +++ b/internal/game/cmd_trigger_utility.go @@ -107,12 +107,9 @@ func (g *Game) triggerEmGrab(sess *net.Session, p *player.Player, mod *ModDef, t return } - if p.FirstFreeSlot() < 0 { - sess.WriteLine("Your inventory is full.") - return - } - - groundItems := g.World.GroundItems(p.RoomID) + // Reservation-aware: only items this player may take are considered, so + // em grab cannot magnetize kill loot reserved for another player. + groundItems := g.World.AccessibleGround(p.RoomID, p.Name) var matchedID string for itemID := range groundItems { def, _ := g.ItemStore.Load(itemID) @@ -143,41 +140,58 @@ func (g *Game) triggerEmGrab(sess *net.Session, p *player.Player, mod *ModDef, t } // Credits are currency, not an inventory item: collect them the same way - // the get command does. + // the get command does, respecting the balance cap so the excess stays on + // the ground instead of being removed and clamped away. if matchedID == "credits" { - g.World.RemoveGroundItem(p.RoomID, matchedID, qty) - g.grantCredits(p, qty) + room := creditHeadroom(p) + if room <= 0 { + sess.WriteLine(g.colorize(sess, "currency_pickup", "Your purse can't hold any more credits.")) + return + } + taken := g.World.RemoveAvailableItem(p.RoomID, matchedID, room, p.Name) + if taken <= 0 { + return + } + g.grantCredits(p, taken) + g.saveCharacter(p) g.triggerModReward(sess, p, mod, 1.0) - sess.WriteLine(fmt.Sprintf("You magnetically pull %d credits toward you.", qty)) + sess.WriteLine(fmt.Sprintf("You magnetically pull %d credits toward you.", taken)) return } if def != nil && def.Stackable { - g.World.RemoveGroundItem(p.RoomID, matchedID, qty) - if !placeCraftOutput(p, matchedID, def, qty) { - g.World.AddGroundItem(p.RoomID, matchedID, qty) + taken := g.World.RemoveAvailableItem(p.RoomID, matchedID, qty, p.Name) + if taken <= 0 { + sess.WriteLine("You don't see that here.") + return + } + if !placeCraftOutput(p, matchedID, def, taken) { + g.World.AddGroundItem(p.RoomID, matchedID, taken) sess.WriteLine("Your inventory is full.") return } } else { // Non-stackable items take one slot each; leave any remainder on the - // ground instead of cramming N items into a single slot. - taken := 0 - for taken < qty { + // ground instead of cramming N items into a single slot. Remove from + // the ground first (reservation-aware) so a placement failure cannot + // duplicate stock. + taken, ok := g.World.RemoveReservedItem(p.RoomID, matchedID, qty, p.Name) + if !ok || taken == 0 { + sess.WriteLine("Your inventory is full.") + return + } + placed := 0 + for placed < taken { freeSlot := p.FirstFreeSlot() if freeSlot < 0 { break } p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: matchedID, Quantity: 1}) - taken++ - } - if taken == 0 { - sess.WriteLine("Your inventory is full.") - return + placed++ } - g.World.RemoveGroundItem(p.RoomID, matchedID, taken) - if taken < qty { - sess.WriteLine(fmt.Sprintf("Your inventory fills up; %d %s remain on the ground.", qty-taken, name)) + if placed < taken { + g.World.AddGroundItem(p.RoomID, matchedID, taken-placed) + sess.WriteLine(fmt.Sprintf("Your inventory fills up; %d %s remain on the ground.", taken-placed, name)) } } -- cgit v1.2.3