package game import ( "fmt" "strings" "thehouseoficarus/internal/net" "thehouseoficarus/internal/object" "thehouseoficarus/internal/player" ) func (g *Game) executeUse(sess *net.Session, args []string, rawInput string) { if g.tryFinishingBlow(sess, strings.Join(args, " ")) { return } g.doUse(sess, strings.Join(args, " ")) } func (g *Game) doUse(sess *net.Session, input string) { if strings.TrimSpace(input) == "" { g.doHelp(sess, "use") return } p := sess.Player g.cancelAction(p) if g.tryRecharge(sess, p, input) { return } lower := strings.ToLower(input) if lower == "bank" || lower == "bank booth" || strings.HasPrefix(lower, "bank ") { g.doBank(sess) return } sep := "" if strings.Contains(lower, " on ") { sep = " on " } else if strings.Contains(lower, " with ") { sep = " with " } if sep != "" { parts := strings.SplitN(lower, sep, 2) itemA := strings.TrimSpace(parts[0]) itemB := strings.TrimSpace(parts[1]) if itemA == itemB { sess.WriteLine("You can't use that with itself.") return } g.doUseItemOnTarget(sess, p, itemA, itemB) return } if len(strings.Fields(input)) >= 2 { if g.useRoomObject(sess, p, input) { return } g.doUseItemOnTarget(sess, p, strings.Fields(input)[0], strings.Join(strings.Fields(input)[1:], " ")) return } if g.useRoomObject(sess, p, input) { return } g.startAction(sess, "use", input) } func (g *Game) useRoomObject(sess *net.Session, p *player.Player, input string) bool { objInstances := g.World.FindObjInstances(p.RoomID, input) if len(objInstances) == 0 { return false } objSt := &objInstances[0] def, _ := g.ObjectStore.Load(objSt.DefID) if def == nil { return false } recipes := g.stationRecipes(p, objSt.DefID) if len(recipes) > 0 { g.showRecipeMenu(sess, p, recipes, def.Name) return true } bt := def.BehaviorType() if bt != "" { g.startAction(sess, bt, def.Name) return true } for _, ui := range def.UseInteractions { if ui.Item != "" { continue } if ui.Condition != nil && !g.checkCondition(sess, ui.Condition) { continue } if ui.Message != "" { sess.WriteLine(ui.Message) } if ui.Action != nil { g.applyNodeAction(sess, ui.Action) } p.ActionState = &ActionState{Type: ActionUsing, TargetName: def.Name} g.broadcastAction(sess, "\n%s uses the %s.", p.Name, def.Name) return true } if len(def.UseInteractions) > 0 { sess.WriteLine(fmt.Sprintf("Use what on the %s?", def.Name)) return true } sess.WriteLine(fmt.Sprintf("You can't use the %s.", def.Name)) return true } func (g *Game) stationRecipes(p *player.Player, stationDefID string) []*object.ItemDef { items := g.CraftIndex.ByStation(stationDefID) var results []*object.ItemDef for _, item := range items { c := item.FirstCraft() if !craftHasAllItemsQty(c, p.CountItem) { continue } skillName := player.SkillName(c.EffectiveSkill()) if p.Level(skillName) < c.Level { continue } if c.Tool != "" && !g.hasToolType(p, c.Tool) { continue } if stationDefID == "anvil" && !g.hasToolType(p, "hammer") { continue } results = append(results, item) } return results } func (g *Game) showRecipeMenu(sess *net.Session, p *player.Player, items []*object.ItemDef, stationName string) { if len(items) == 0 { sess.WriteLine(fmt.Sprintf("You don't have anything you can make at the %s.", stationName)) return } if len(items) == 1 { g.promptHowMany(sess, items[0].ID) return } needTypes := false for i := range items { for j := i + 1; j < len(items); j++ { if len(items[i].Craft) > 0 && len(items[j].Craft) > 0 && items[i].FirstCraft().Type != items[j].FirstCraft().Type { needTypes = true break } } if needTypes { break } } menu := itemMenuData(itemIDs(items)) sess.PendingMenu = menu sess.State = net.StateRecipeChoice var names []string for _, item := range items { cType := "" if len(item.Craft) > 0 { cType = item.FirstCraft().Type } if cType == "" { cType = "combine" } typeLabel := strings.ToUpper(cType[:1]) + cType[1:] if needTypes { names = append(names, fmt.Sprintf("%s (%s)", g.itemName(sess, item), typeLabel)) } else { names = append(names, g.itemName(sess, item)) } } g.showMenuTable(sess, fmt.Sprintf("What would you like to make at the %s?", stationName), names) } func (g *Game) doUseItemOnTarget(sess *net.Session, p *player.Player, itemAName, itemBName string) { matchesA := g.findInventoryMatches(itemAName, p) if len(matchesA) == 0 { sess.WriteLine(fmt.Sprintf("You don't have any '%s'.", itemAName)) return } uniqueA := uniqueItemNames(matchesA) if len(uniqueA) > 1 { g.showWhichOne(sess, matchesA) return } itemAID := matchesA[0].ID matchesB := g.findInventoryMatches(itemBName, p) objInstances := g.World.FindObjInstances(p.RoomID, itemBName) if len(objInstances) > 0 { objSt := &objInstances[0] def, _ := g.ObjectStore.Load(objSt.DefID) if def == nil { g.startAction(sess, "use", itemBName) return } recipes := g.CraftIndex.FindByStationInput(def.ID, itemAID) if len(recipes) > 0 { cType := "" if len(recipes[0].Craft) > 0 { cType = recipes[0].FirstCraft().Type } if cType == "smelting" { var filtered []*object.ItemDef for _, item := range recipes { c := item.FirstCraft() if c.Type != "smelting" { continue } if !craftHasAllItemsQty(c, p.CountItem) { continue } skillLevel := p.Level(player.SkillName(c.EffectiveSkill())) if skillLevel < c.Level { continue } filtered = append(filtered, item) } if len(filtered) == 0 { sess.WriteLine("You can't smelt that here.") return } if len(filtered) == 1 { g.promptHowMany(sess, filtered[0].ID) return } sess.PendingMenu = itemMenuData(itemIDs(filtered)) sess.State = net.StateRecipeChoice var names []string for _, item := range filtered { names = append(names, g.itemName(sess, item)) } g.showMenuTable(sess, "What would you like to smelt?", names) return } if cType == "smithing" { if !g.hasToolType(p, "hammer") { sess.WriteLine("You need a hammer to smith.") return } g.showSmithTable(sess, p, itemAID) return } if len(recipes) == 1 { g.promptHowMany(sess, recipes[0].ID) return } sess.PendingMenu = itemMenuData(itemIDs(recipes)) sess.State = net.StateRecipeChoice var names []string for _, item := range recipes { names = append(names, g.itemName(sess, item)) } g.showMenuTable(sess, fmt.Sprintf("What would you like to make with %s on the %s?", itemAName, def.Name), names) return } for _, ui := range def.UseInteractions { if ui.Item != itemAID { continue } if ui.Condition != nil && !g.checkCondition(sess, ui.Condition) { continue } if ui.Message != "" { sess.WriteLine(ui.Message) } if ui.Action != nil { g.applyNodeAction(sess, ui.Action) } return } g.startAction(sess, "use", itemBName) return } matchesB = g.findInventoryMatches(itemBName, p) if len(matchesB) > 0 { uniqueB := uniqueItemNames(matchesB) if len(uniqueB) > 1 { sess.WriteLine("Which '" + itemBName + "'?") seen := make(map[string]bool) for _, m := range matchesB { if seen[m.Name] { continue } seen[m.Name] = true def, _ := g.ItemStore.Load(m.ID) sess.WriteLine(fmt.Sprintf(" - %s", g.itemColorize(sess, def, m.Name))) } return } itemBID := matchesB[0].ID craftItems := g.findCraftItems(p, itemAID, itemBID) fletchItems := g.findFletchItems(p, itemAID, itemBID) if len(craftItems) > 0 && len(fletchItems) > 0 { var menu []map[string]string var names []string for _, item := range craftItems { menu = append(menu, map[string]string{"item_id": item.ID}) names = append(names, fmt.Sprintf("%s (Crafting)", item.Name)) } for _, item := range fletchItems { menu = append(menu, map[string]string{"item_id": item.ID}) names = append(names, fmt.Sprintf("%s (Fletching)", item.Name)) } sess.PendingMenu = menu sess.State = net.StateRecipeChoice g.showMenuTable(sess, "What would you like to make?", names) return } if len(craftItems) > 0 { var available []*object.ItemDef for _, item := range craftItems { if g.canDoItem(p, item, player.Crafting) { available = append(available, item) } } if len(available) == 1 { g.startItem(sess, p, available[0], 0, "last_craft") return } if len(available) > 1 { g.showProductionTable(sess, p, available, "Crafting", "crafting", "last_craft", "Craft", false) return } } if len(fletchItems) > 0 { var available []*object.ItemDef for _, item := range fletchItems { if g.canDoFletchItem(p, item) { available = append(available, item) } } if len(available) == 1 { g.startFletchAction(sess, p, available[0], 0) return } if len(available) > 1 { g.showProductionTable(sess, p, available, "Fletching", "fletching", "last_fletch", "Fletch", true) return } } matched := g.CraftIndex.FindByTwoInputs(itemAID, itemBID) var matchable []*object.ItemDef for _, item := range matched { if craftHasAllItemsQty(item.FirstCraft(), p.CountItem) { matchable = append(matchable, item) } } if len(matchable) == 1 { g.promptHowMany(sess, matchable[0].ID) return } if len(matchable) > 1 { sess.PendingMenu = itemMenuData(itemIDs(matchable)) sess.State = net.StateRecipeChoice var names []string for _, item := range matchable { names = append(names, g.itemName(sess, item)) } g.showMenuTable(sess, "What would you like to make?", names) return } if g.isGrimyHerb(itemAID) && itemBID == "vial_of_water" { sess.WriteLine("Don't put a grimy herb in there! Clean it first!") return } sess.WriteLine("You can't combine those items.") return } sess.WriteLine(fmt.Sprintf("There's no '%s' here to use that on.", itemBName)) } func (g *Game) isGrimyHerb(itemID string) bool { items := g.CraftIndex.ByType("clean") for _, item := range items { for _, e := range item.FirstCraft().Consume { for _, id := range e.Items { if id == itemID { return true } } } } return false }