package game import ( "fmt" "strings" "thehouseoficarus/internal/action" "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.showStationRecipeMenu(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} if g.Hub != nil { for _, other := range g.Hub.PlayersInRoom(p.RoomID) { if other != sess && other.Player != nil { other.WriteLine(g.colorize(other, "broadcast", fmt.Sprintf("\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) []action.RecipeDef { allRecipes, err := g.RecipeStore.LoadAll() if err != nil { return nil } var results []action.RecipeDef for _, r := range allRecipes { if len(r.Station) == 0 { continue } stationMatch := false for _, sid := range r.Station { if sid == stationDefID { stationMatch = true break } } if !stationMatch { continue } if !r.HasAllItemsQty(p.CountItem) { continue } skillName := player.SkillName(r.EffectiveSkill()) if p.Level(skillName) < r.Level { continue } if r.Tool != "" && !g.hasToolType(p, r.Tool) { continue } if stationDefID == "anvil" && !g.hasToolType(p, "hammer") { continue } results = append(results, r) } return results } func (g *Game) showStationRecipeMenu(sess *net.Session, p *player.Player, recipes []action.RecipeDef, stationName string) { if len(recipes) == 0 { sess.WriteLine(fmt.Sprintf("You don't have anything you can make at the %s.", stationName)) return } if len(recipes) == 1 { g.promptHowMany(sess, recipes[0].ID) return } needTypes := false for i := range recipes { for j := i + 1; j < len(recipes); j++ { if recipes[i].Type != recipes[j].Type { needTypes = true break } } if needTypes { break } } menu := recipeMenuData(recipeDefIDs(recipes)) sess.PendingMenu = menu sess.State = net.StateRecipeChoice var names []string for _, r := range recipes { name := g.recipeName(sess, &r) if needTypes { typeLabel := strings.ToUpper(r.Type[:1]) + r.Type[1:] names = append(names, fmt.Sprintf("%s (%s)", name, typeLabel)) } else { names = append(names, name) } } 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 } stationIDs := []string{def.ID} recipes, err := g.RecipeStore.FindByStation(itemAID, stationIDs, "") if err == nil && len(recipes) > 0 { if recipes[0].Type == "smelting" { filtered := recipes[:0:0] for _, r := range recipes { if r.Type != "smelting" { continue } if !r.HasAllItemsQty(p.CountItem) { continue } skillLevel := p.Level(player.SkillName(r.EffectiveSkill())) if skillLevel < r.Level { continue } filtered = append(filtered, r) } 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 = recipeMenuData(recipeDefIDs(filtered)) sess.State = net.StateRecipeChoice var names []string for _, r := range filtered { names = append(names, g.recipeName(sess, &r)) } g.showMenuTable(sess, "What would you like to smelt?", names) return } if recipes[0].Type == "smithing" { if !g.hasToolType(p, "hammer") { sess.WriteLine("You need a hammer to smith.") return } allRecipes, _ := g.RecipeStore.LoadAll() g.showSmithTable(sess, p, itemAID, allRecipes) return } if len(recipes) == 1 { g.promptHowMany(sess, recipes[0].ID) return } sess.PendingMenu = recipeMenuData(recipeDefIDs(recipes)) sess.State = net.StateRecipeChoice var names []string for _, r := range recipes { names = append(names, g.recipeName(sess, &r)) } 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 craftRecipes := g.findCraftRecipesForItems(p, itemAID, itemBID) fletchRecipes := g.findFletchRecipesForItems(p, itemAID, itemBID) if len(craftRecipes) > 0 && len(fletchRecipes) > 0 { var menu []map[string]string var names []string for _, r := range craftRecipes { menu = append(menu, map[string]string{"recipe_id": r.ID}) outDef, _ := g.ItemStore.Load(r.Output) name := r.Output if outDef != nil { name = outDef.Name } names = append(names, fmt.Sprintf("%s (Crafting)", name)) } for _, r := range fletchRecipes { menu = append(menu, map[string]string{"fletch_recipe_id": r.ID}) outDef, _ := g.ItemStore.Load(r.Output) name := r.Output if outDef != nil { name = outDef.Name } names = append(names, fmt.Sprintf("%s (Fletching)", name)) } sess.PendingMenu = menu sess.State = net.StateRecipeChoice g.showMenuTable(sess, "What would you like to make?", names) return } if len(craftRecipes) > 0 { var available []action.RecipeDef for _, r := range craftRecipes { if g.canDoGenericRecipe(p, &r, player.Crafting) { available = append(available, r) } } if len(available) == 1 { g.startGenericRecipe(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(fletchRecipes) > 0 { var available []action.RecipeDef for _, r := range fletchRecipes { if g.canDoFletchRecipe(p, &r) { available = append(available, r) } } 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.findCombineResults(sess, p, itemAID, itemBID) if len(matched) == 1 { g.promptHowMany(sess, "combine_"+matched[0].ID) return } if len(matched) > 1 { sess.PendingMenu = combineMenuData(matched) sess.State = net.StateRecipeChoice var names []string for _, def := range matched { names = append(names, g.itemColorize(sess, def, def.Name)) } g.showMenuTable(sess, "What would you like to make?", names) 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) findCombineResults(sess *net.Session, p *player.Player, itemAID, itemBID string) []*object.ItemDef { items, err := g.ItemStore.LoadAll() if err != nil { return nil } var matched []*object.ItemDef for _, def := range items { if len(def.MadeFrom) == 0 { continue } aEntry := -1 bEntry := -1 for ei, entry := range def.MadeFrom { for _, id := range entry.Items { if id == itemAID && aEntry < 0 { aEntry = ei } if id == itemBID && bEntry < 0 { bEntry = ei } } } if aEntry >= 0 && bEntry >= 0 && aEntry != bEntry && madeFromItemsAvailable(p, def.MadeFrom) { matched = append(matched, def) } } return matched } func combineMenuData(defs []*object.ItemDef) []map[string]string { out := make([]map[string]string, len(defs)) for i, d := range defs { out[i] = map[string]string{"combine_item": d.ID} } return out } func madeFromItemsAvailable(p *player.Player, madeFrom []object.MadeFromEntry) bool { for _, entry := range madeFrom { found := false for _, id := range entry.Items { if p.HasItem(id) { found = true break } } if !found { return false } } return true }