package game import ( "fmt" "strings" "thehouseoficarus/internal/action" "thehouseoficarus/internal/net" "thehouseoficarus/internal/player" ) func (g *Game) handleShopInput(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.showShopBrowse(sess) return } cmd := parts[0] switch cmd { case "buy": if len(parts) < 2 { sess.WriteLine("Buy what? Use 'browse' to see available items.") } else { g.doShopBuy(sess, strings.Join(parts[1:], " ")) } case "sell": if len(parts) < 2 { sess.WriteLine("Sell what?") } else { g.doShopSell(sess, strings.Join(parts[1:], " ")) } case "browse", "list": g.showShopBrowse(sess) case "leave", "bye", "exit", "quit": g.leaveShop(sess) return default: sess.WriteLine("Commands: buy , sell , browse, leave") } g.writeShopPrompt(sess) } func (g *Game) writeShopPrompt(sess *net.Session) { sess.Write(fmt.Sprintf("\n%s", g.colorize(sess, "dialog", "> "))) } func (g *Game) shopConfig(sess *net.Session) *action.ShopConfig { cfg, _ := sess.Shop.(*action.ShopConfig) return cfg } func (g *Game) showShopBrowse(sess *net.Session) { cfg := g.shopConfig(sess) if cfg == nil { return } if cfg.Message != "" { sess.WriteLine(fmt.Sprintf("\n%s", g.colorize(sess, "dialog", cfg.Message))) } if len(cfg.Items) == 0 { sess.WriteLine("This shop has nothing for sale.") return } unicode := true if p, ok := sess.Player.(*player.Player); ok { unicode = p.OptionBool("unicode") } t := Table{ Title: "Shop Inventory", Columns: []string{"#", "Item", "Buy Price", "Sell Price"}, } for i, si := range cfg.Items { def, _ := g.ItemStore.Load(si.ItemID) itemName := si.ItemID if def != nil { itemName = def.Name } buyStr := fmt.Sprintf("%d cr", si.BuyPrice) sellStr := "\u2014" if si.SellPrice > 0 { sellStr = fmt.Sprintf("%d cr", si.SellPrice) } t.Rows = append(t.Rows, []string{ fmt.Sprintf("%d", i+1), g.colorize(sess, "item", itemName), g.colorize(sess, "credits_pickup", buyStr), g.colorize(sess, "credits_pickup", sellStr), }) } for _, line := range t.Render(unicode) { sess.WriteLine(line) } } func (g *Game) doShopBuy(sess *net.Session, input string) { cfg := g.shopConfig(sess) if cfg == nil { return } idx, err := parseChoiceIndex(input) if err == nil && idx > 0 && idx <= len(cfg.Items) { si := cfg.Items[idx-1] g.buyShopItem(sess, si) return } var match *action.ShopItem for i := range cfg.Items { si := &cfg.Items[i] def, _ := g.ItemStore.Load(si.ItemID) itemName := si.ItemID if def != nil { itemName = def.Name } invMatches := g.collectMatches(input, func(yield func(string, int) bool) { yield(itemName, -1) }) if len(invMatches) > 0 { if match != nil { sess.WriteLine(fmt.Sprintf("That's ambiguous, which one? (use #, e.g. buy %s 1)", si.ItemID)) return } match = si } } if match != nil { g.buyShopItem(sess, *match) return } sess.WriteLine("That item isn't for sale here. Use 'browse' to see shop inventory.") } func (g *Game) buyShopItem(sess *net.Session, si action.ShopItem) { p, _ := sess.Player.(*player.Player) if p.Credits < si.BuyPrice { sess.WriteLine(fmt.Sprintf("You need %d credits to buy that (you have %d).", si.BuyPrice, p.Credits)) return } def, _ := g.ItemStore.Load(si.ItemID) displayName := si.ItemID if def != nil { displayName = def.Name } if def != nil && def.Stackable { for i := 0; i < 28; i++ { slot := p.InvSlot(i) if slot != nil && slot.ItemID == si.ItemID { p.Credits -= si.BuyPrice slot.Quantity++ g.AccountStore.SaveCharacter(p) itemColor := g.itemColorize(sess, def, displayName) sess.WriteLine(fmt.Sprintf("You buy a %s for %s credits.", itemColor, g.colorize(sess, "credits_pickup", fmt.Sprintf("%d", si.BuyPrice)))) return } } } freeSlot := p.FirstFreeSlot() if freeSlot == -1 { sess.WriteLine("Your inventory is full.") return } p.Credits -= si.BuyPrice p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: si.ItemID, Quantity: 1}) g.AccountStore.SaveCharacter(p) itemColor := g.itemColorize(sess, def, displayName) sess.WriteLine(fmt.Sprintf("You buy a %s for %s credits.", itemColor, g.colorize(sess, "credits_pickup", fmt.Sprintf("%d", si.BuyPrice)))) } func (g *Game) doShopSell(sess *net.Session, input string) { cfg := g.shopConfig(sess) if cfg == nil { return } p, _ := sess.Player.(*player.Player) matches := g.findInventoryMatches(input, p) if len(matches) == 0 { sess.WriteLine("You don't have that item.") return } if len(matches) > 1 { sess.WriteLine("That's ambiguous, which one?") return } match := matches[0] var shopItem *action.ShopItem for i := range cfg.Items { si := &cfg.Items[i] if si.ItemID == match.ID && si.SellPrice > 0 { shopItem = si break } } if shopItem == nil { def, _ := g.ItemStore.Load(match.ID) displayName := match.ID if def != nil { displayName = def.Name } sess.WriteLine(fmt.Sprintf("The shop doesn't want to buy %s.", g.colorize(sess, "item", displayName))) return } p.RemoveItem(match.ID, 1) p.Credits += shopItem.SellPrice g.AccountStore.SaveCharacter(p) def, _ := g.ItemStore.Load(match.ID) displayName := match.ID if def != nil { displayName = def.Name } itemColor := g.itemColorize(sess, def, displayName) sess.WriteLine(fmt.Sprintf("You sell a %s for %s credits.", itemColor, g.colorize(sess, "credits_pickup", fmt.Sprintf("%d", shopItem.SellPrice)))) } func (g *Game) leaveShop(sess *net.Session) { sess.Shop = nil p, ok := sess.Player.(*player.Player) if !ok || p == nil || p.Action == nil || p.Action.Type != "talk" { sess.State = net.StateGame if p != nil { g.CancelAction(p) } g.writePrompt(sess) return } behaviorID, _ := p.Action.Data["behavior_id"].(string) nodeKey, _ := p.Action.Data["node"].(string) cfg, err := g.BehaviorStore.LoadTalk(behaviorID) if err != nil { sess.State = net.StateGame g.CancelAction(p) g.writePrompt(sess) return } node, ok := cfg.Nodes[nodeKey] if !ok { sess.State = net.StateGame g.CancelAction(p) g.writePrompt(sess) return } sess.State = net.StateTalk visible := 0 for _, opt := range node.Options { if opt.Condition != nil && !g.checkCondition(sess, opt.Condition) { continue } visible++ sess.WriteLine(fmt.Sprintf(" %d. %s", visible, opt.Text)) } if visible == 0 { sess.State = net.StateGame g.CancelAction(p) g.writePrompt(sess) return } sess.Write("\nChoice: ") }