package game import ( "fmt" "sort" "strconv" "strings" "thehouseoficarus/internal/behavior" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" "thehouseoficarus/internal/ui" ) func (g *Game) executeBank(sess *net.Session, args []string, rawInput string) { g.doBank(sess) } func (g *Game) handleBankInput(sess *net.Session, input string) { if sess.Player == nil { sess.State = net.StateGame g.writePrompt(sess) return } input = strings.TrimSpace(input) parts := strings.Fields(strings.ToLower(input)) if len(parts) == 0 { g.showBankBrowse(sess) return } cmd := parts[0] switch cmd { case "deposit": if len(parts) < 2 { sess.WriteLine("Deposit what?") } else if parts[1] == "credits" { g.doBankDepositCredits(sess, parts) } else if parts[1] == "all" { g.doBankDepositAll(sess) } else { g.doBankDeposit(sess, strings.Join(parts[1:], " ")) } case "withdraw": if len(parts) < 2 { sess.WriteLine("Withdraw what?") } else if parts[1] == "credits" { g.doBankWithdrawCredits(sess, parts) } else { g.doBankWithdraw(sess, strings.Join(parts[1:], " ")) } case "browse", "list": g.showBankBrowse(sess) case "leave", "bye", "exit", "quit": g.leaveBank(sess) return default: sess.WriteLine("Commands: deposit , deposit credits , deposit all, withdraw , withdraw credits , browse, leave") } g.writeBankPrompt(sess) } func (g *Game) writeBankPrompt(sess *net.Session) { sess.WritePrompt(g.colorize(sess, "dialog", "Bank> ")) } func (g *Game) showBankBrowse(sess *net.Session) { p := sess.Player unicode := p.OptionBool("unicode") t := ui.Table{ Title: "Bank Contents", Columns: []string{"#", "Item", "Quantity"}, } type bankEntry struct { slot int name string id string qty int } var entries []bankEntry for _, i := range p.BankSlots() { slot := p.BankSlot(i) if slot == nil { continue } def, _ := g.ItemStore.Load(slot.ItemID) itemName := slot.ItemID if def != nil { itemName = def.Name } entries = append(entries, bankEntry{slot: i, name: itemName, id: slot.ItemID, qty: slot.Quantity}) } if len(entries) == 0 && p.BankedCredits <= 0 { sess.WriteLine("Your bank is empty.") return } sort.Slice(entries, func(i, j int) bool { return entries[i].slot < entries[j].slot }) for _, e := range entries { def, _ := g.ItemStore.Load(e.id) t.Rows = append(t.Rows, []string{ fmt.Sprintf("%d", e.slot+1), g.itemColorize(sess, def, e.name), fmt.Sprintf("%d", e.qty), }) } for _, line := range t.Render(unicode, p.OptionInt("wrap_width")) { sess.WriteLine(line) } if p.BankedCredits > 0 { sess.WriteLine(g.colorize(sess, "currency_pickup", fmt.Sprintf("Banked credits: %s", formatInt(p.BankedCredits)))) } } func (g *Game) doBankDeposit(sess *net.Session, input string) { p := sess.Player matches := g.findInventoryMatches(input, p) if len(matches) == 0 { sess.WriteLine("You don't have that item.") return } if len(matches) > 1 { g.showWhichOne(sess, matches) return } match := matches[0] slot := p.InvSlot(match.Slot) if slot == nil { return } itemID := slot.ItemID qty := slot.Quantity def, _ := g.ItemStore.Load(itemID) p.SetInvSlot(match.Slot, nil) bankIdx := p.NextBankSlot() p.SetBankSlot(bankIdx, &player.InventorySlot{ ItemID: itemID, Quantity: qty, Quality: slot.Quality, }) g.AccountStore.SaveCharacter(p) displayName := itemID if def != nil { displayName = def.Name } itemColor := g.itemColorize(sess, def, displayName) if qty > 1 { sess.WriteLine(fmt.Sprintf("You deposit %d %ss.", qty, itemColor)) } else { sess.WriteLine(fmt.Sprintf("You deposit a %s.", itemColor)) } } func (g *Game) doBankDepositAll(sess *net.Session) { p := sess.Player var deposited int for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot == nil { continue } bankIdx := p.NextBankSlot() p.SetBankSlot(bankIdx, &player.InventorySlot{ ItemID: slot.ItemID, Quantity: slot.Quantity, Quality: slot.Quality, }) p.SetInvSlot(i, nil) deposited++ } if deposited == 0 { sess.WriteLine("Your inventory is already empty.") return } g.AccountStore.SaveCharacter(p) sess.WriteLine(fmt.Sprintf("You deposit %d item%s.", deposited, plural(deposited))) } func (g *Game) doBankWithdraw(sess *net.Session, input string) { p := sess.Player freeSlot := p.FirstFreeSlot() if freeSlot == -1 { sess.WriteLine("Your inventory is full.") return } var entries []struct { slot int name string id string qty int } for _, i := range p.BankSlots() { bs := p.BankSlot(i) if bs == nil { continue } def, _ := g.ItemStore.Load(bs.ItemID) itemName := bs.ItemID if def != nil { itemName = def.Name } entries = append(entries, struct { slot int name string id string qty int }{slot: i, name: itemName, id: bs.ItemID, qty: bs.Quantity}) } idx, err := parseChoiceIndex(input) if err == nil && idx > 0 && idx <= len(entries) { e := entries[idx-1] g.withdrawBankItem(sess, p, e.slot) return } var matches []struct { slot int name string id string qty int } lower := strings.ToLower(input) for _, e := range entries { if behavior.WordPrefixMatch(e.name, lower) { matches = append(matches, e) } } if len(matches) == 1 { g.withdrawBankItem(sess, p, matches[0].slot) return } for _, e := range entries { def, _ := g.ItemStore.Load(e.id) if def != nil && def.MatchesName(input) { matches = append(matches, e) } } if len(matches) == 0 { sess.WriteLine("That item isn't in your bank. Use 'browse' to see your bank contents.") return } if len(matches) > 1 { var names []string for _, m := range matches { names = append(names, m.name) } sess.WriteLine("That's ambiguous, which one?") for _, n := range names { sess.WriteLine(fmt.Sprintf(" - %s", n)) } return } g.withdrawBankItem(sess, p, matches[0].slot) } func (g *Game) withdrawBankItem(sess *net.Session, p *player.Player, bankSlotIdx int) { bs := p.BankSlot(bankSlotIdx) if bs == nil { return } freeSlot := p.FirstFreeSlot() if freeSlot == -1 { sess.WriteLine("Your inventory is full.") return } itemID := bs.ItemID qty := bs.Quantity def, _ := g.ItemStore.Load(itemID) if def != nil && def.Stackable { p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: qty}) } else { p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: itemID, Quantity: 1}) if qty > 1 { bs.Quantity-- g.AccountStore.SaveCharacter(p) displayName := itemID if def != nil { displayName = def.Name } itemColor := g.itemColorize(sess, def, displayName) sess.WriteLine(fmt.Sprintf("You withdraw a %s.", itemColor)) return } } p.SetBankSlot(bankSlotIdx, nil) g.AccountStore.SaveCharacter(p) displayName := itemID if def != nil { displayName = def.Name } itemColor := g.itemColorize(sess, def, displayName) if qty > 1 { sess.WriteLine(fmt.Sprintf("You withdraw %d %ss.", qty, itemColor)) } else { sess.WriteLine(fmt.Sprintf("You withdraw a %s.", itemColor)) } } func (g *Game) doBankDepositCredits(sess *net.Session, parts []string) { p := sess.Player if len(parts) < 3 { sess.WriteLine("Usage: deposit credits ") return } amount, err := strconv.Atoi(parts[2]) if err != nil || amount <= 0 { sess.WriteLine("Invalid amount.") return } if p.Credits < amount { sess.WriteLine("You don't have that many credits.") return } p.Credits -= amount p.BankedCredits += amount g.AccountStore.SaveCharacter(p) sess.WriteLine(g.colorize(sess, "currency_pickup", fmt.Sprintf("You deposit %s credits. (Banked: %s)", formatInt(amount), formatInt(p.BankedCredits)))) g.showBankBrowse(sess) } func (g *Game) doBankWithdrawCredits(sess *net.Session, parts []string) { p := sess.Player if len(parts) < 3 { sess.WriteLine("Usage: withdraw credits ") return } amount, err := strconv.Atoi(parts[2]) if err != nil || amount <= 0 { sess.WriteLine("Invalid amount.") return } if p.BankedCredits < amount { sess.WriteLine("You don't have that many banked credits.") return } p.BankedCredits -= amount p.Credits += amount g.AccountStore.SaveCharacter(p) sess.WriteLine(g.colorize(sess, "currency_pickup", fmt.Sprintf("You withdraw %s credits.", formatInt(amount)))) g.showBankBrowse(sess) } func (g *Game) canUseBank(p *player.Player) bool { instances := g.World.FindObjInstances(p.RoomID, "bank booth") return len(instances) > 0 } func (g *Game) doBank(sess *net.Session) { p := sess.Player if !g.canUseBank(p) { sess.WriteLine("There is no bank here.") return } sess.State = net.StateBank sess.WriteLine(g.colorize(sess, "dialog", "You access the bank terminal.")) g.showBankBrowse(sess) g.writeBankPrompt(sess) } func (g *Game) leaveBank(sess *net.Session) { sess.State = net.StateGame g.writePrompt(sess) }