package game import ( "fmt" "strings" "thehouseoficarus/internal/net" ) func (g *Game) doDropAll(sess *net.Session) { p := sess.Player g.cancelAction(p) count := 0 for _, slot := range p.Inventory { if slot != nil && slot.Quantity > 0 { count++ } } if count == 0 { sess.WriteLine("You have nothing to drop.") return } sess.SetState(net.StateDropAllConfirm) sess.WriteLine(fmt.Sprintf("Drop all %d items? [y/N]", count)) } func (g *Game) doDropAllNamed(sess *net.Session, input string) { p := sess.Player g.cancelAction(p) matches := g.findInventoryMatches(input, p) if len(matches) == 0 { sess.WriteLine(fmt.Sprintf("You don't have any '%s'.", input)) return } unique := uniqueItemNames(matches) if len(unique) > 1 { g.showWhichOne(sess, matches) return } itemID := matches[0].ID def, _ := g.ItemStore.Load(itemID) name := itemID if def != nil { name = def.Name } coloredName := g.itemColorize(sess, def, name) if def != nil && def.Stackable { // Drop every matching stack, not just the first: multiple stacks of // one stackable item are normal (trigger/gather/steal placement does // not always merge), so "drop all " must span them. total := 0 for _, match := range matches { slot := p.InvSlot(match.Slot) if slot == nil || slot.ItemID != itemID || slot.Quantity <= 0 { continue } total += slot.Quantity p.SetInvSlot(match.Slot, nil) g.World.AddGroundItem(p.RoomID, itemID, slot.Quantity) } g.saveCharacter(p) if total == 1 { sess.WriteLine(fmt.Sprintf("You drop a %s.", coloredName)) } else { sess.WriteLine(fmt.Sprintf("You drop %d x %s.", total, coloredName)) } return } total := 0 for _, match := range matches { slot := p.InvSlot(match.Slot) if slot == nil || slot.ItemID != itemID || slot.Quantity <= 0 { continue } total += slot.Quantity p.SetInvSlot(match.Slot, nil) g.World.AddGroundItem(p.RoomID, itemID, slot.Quantity) } g.saveCharacter(p) if total == 1 { sess.WriteLine(fmt.Sprintf("You drop a %s.", coloredName)) } else { sess.WriteLine(fmt.Sprintf("You drop %d x %s.", total, coloredName)) } } func (g *Game) doDrop(sess *net.Session, input string) { p := sess.Player g.cancelAction(p) qty, itemName := parseQty(input) matches := g.findInventoryMatches(itemName, p) if len(matches) == 0 { sess.WriteLine(fmt.Sprintf("You don't have any '%s'.", itemName)) return } unique := uniqueItemNames(matches) if len(unique) > 1 { g.showWhichOne(sess, matches) return } itemID := matches[0].ID def, _ := g.ItemStore.Load(itemID) name := itemID if def != nil { name = def.Name } if qty == 0 { if def != nil && def.Stackable { // "drop " means the whole item: sum every matching stack. for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot != nil && slot.ItemID == itemID && slot.Quantity > 0 { qty += slot.Quantity } } } else { qty = 1 } } if qty <= 0 { sess.WriteLine(fmt.Sprintf("You don't have any %s.", name)) return } if def != nil && def.Stackable { // Consume the requested quantity across every matching stack, not just // the first one. remaining := qty for _, match := range matches { if remaining <= 0 { break } slot := p.InvSlot(match.Slot) if slot == nil || slot.ItemID != itemID || slot.Quantity <= 0 { continue } take := slot.Quantity if take > remaining { take = remaining } quality, maxQuality := slot.Quality, slot.MaxQuality if slot.Quantity > take { slot.Quantity -= take } else { p.SetInvSlot(match.Slot, nil) } g.World.AddGroundItemQ(p.RoomID, itemID, take, quality, maxQuality) remaining -= take } dropped := qty - remaining g.saveCharacter(p) if dropped == 1 { sess.WriteLine(fmt.Sprintf("You drop a %s.", g.itemColorize(sess, def, name))) } else { sess.WriteLine(fmt.Sprintf("You drop %d x %s.", dropped, g.itemColorize(sess, def, name))) } return } remaining := qty for _, match := range matches { if remaining <= 0 { break } slot := p.InvSlot(match.Slot) if slot == nil || slot.ItemID != itemID || slot.Quantity <= 0 { continue } // Drop as much of the requested quantity as this slot holds, not // just one: a non-stackable slot holding several units must honour // "drop 3 ". take := slot.Quantity if take > remaining { take = remaining } quality, maxQuality := slot.Quality, slot.MaxQuality if slot.Quantity > take { slot.Quantity -= take } else { p.SetInvSlot(match.Slot, nil) } g.World.AddGroundItemQ(p.RoomID, itemID, take, quality, maxQuality) remaining -= take } dropped := qty - remaining g.saveCharacter(p) if dropped == 0 { sess.WriteLine(fmt.Sprintf("You don't have any %s.", g.itemColorize(sess, def, name))) } else if dropped == 1 { sess.WriteLine(fmt.Sprintf("You drop a %s.", g.itemColorize(sess, def, name))) } else { sess.WriteLine(fmt.Sprintf("You drop %d x %s.", dropped, g.itemColorize(sess, def, name))) } } func (g *Game) executeDrop(sess *net.Session, args []string, rawInput string) { if len(args) == 0 { sess.WriteLine("Drop what?") } else if args[0] == "all" { if len(args) == 1 { g.doDropAll(sess) } else { g.doDropAllNamed(sess, strings.Join(args[1:], " ")) } } else { g.doDrop(sess, strings.Join(args, " ")) } } func (g *Game) handleDropAll(sess *net.Session, input string) { p := sess.Player if p == nil { sess.SetState(net.StateGame) g.writePrompt(sess) return } g.cancelAction(p) input = strings.TrimSpace(strings.ToLower(input)) if input != "y" && input != "yes" { sess.SetState(net.StateGame) g.writePrompt(sess) return } var dropped []string for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot == nil || slot.Quantity <= 0 { continue } g.World.AddGroundItemQ(p.RoomID, slot.ItemID, slot.Quantity, slot.Quality, slot.MaxQuality) def, _ := g.ItemStore.Load(slot.ItemID) name := slot.ItemID if def != nil { name = def.Name } dropped = append(dropped, g.itemColorize(sess, def, name)) p.SetInvSlot(i, nil) } g.saveCharacter(p) sess.SetState(net.StateGame) if len(dropped) == 0 { sess.WriteLine("You have nothing to drop.") } else { sess.Write("You drop your ") formatPickupList(sess, dropped) } g.writePrompt(sess) }