diff options
Diffstat (limited to 'internal/game/cmd_shop.go')
| -rw-r--r-- | internal/game/cmd_shop.go | 295 |
1 files changed, 295 insertions, 0 deletions
diff --git a/internal/game/cmd_shop.go b/internal/game/cmd_shop.go new file mode 100644 index 0000000..a3f7613 --- /dev/null +++ b/internal/game/cmd_shop.go @@ -0,0 +1,295 @@ +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 <item>, sell <item>, 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: ") +} |
