From abd612c15799f604e671e83dc7c410ed2b44185f Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Thu, 25 Jun 2026 15:40:48 -0400 Subject: slop refactor --- internal/game/core_production.go | 779 +-------------------------------------- 1 file changed, 12 insertions(+), 767 deletions(-) (limited to 'internal/game/core_production.go') diff --git a/internal/game/core_production.go b/internal/game/core_production.go index 56d1ccc..8a38f36 100644 --- a/internal/game/core_production.go +++ b/internal/game/core_production.go @@ -1,622 +1,14 @@ package game import ( - "fmt" - "math/rand" - "sort" - "strconv" - "strings" - - "thehouseoficarus/internal/action" - "thehouseoficarus/internal/color" - "thehouseoficarus/internal/engine" - "thehouseoficarus/internal/net" - "thehouseoficarus/internal/object" + "thehouseoficarus/internal/behavior" + "thehouseoficarus/internal/item" "thehouseoficarus/internal/player" ) -func (g *Game) resolveCraftVar(sess *net.Session, varSpec string, craft *object.CraftDef, outputID string, byproducts []string, hasItem func(string) bool) string { - if !strings.Contains(varSpec, "%") { - return varSpec - } - - outDef, _ := g.ItemStore.Load(outputID) - outputName := outputID - if outDef != nil { - outputName = g.itemColorize(sess, outDef, outDef.Name) - } - varSpec = strings.ReplaceAll(varSpec, "%n", outputName) - - for i, e := range craft.Consume { - key := fmt.Sprintf("%%i%d", i+1) - if !strings.Contains(varSpec, key) { - continue - } - var matchedID string - for _, id := range e.Items { - if hasItem(id) { - matchedID = id - break - } - } - if matchedID == "" && len(e.Items) > 0 { - matchedID = e.Items[0] - } - if def, err := g.ItemStore.Load(matchedID); err == nil { - varSpec = strings.ReplaceAll(varSpec, key, g.itemColorize(sess, def, def.Name)) - } else { - varSpec = strings.ReplaceAll(varSpec, key, matchedID) - } - } - - for i, bpID := range byproducts { - key := fmt.Sprintf("%%b%d", i+1) - if !strings.Contains(varSpec, key) { - continue - } - if def, err := g.ItemStore.Load(bpID); err == nil { - varSpec = strings.ReplaceAll(varSpec, key, g.itemColorize(sess, def, def.Name)) - } else { - varSpec = strings.ReplaceAll(varSpec, key, bpID) - } - } - - return color.ExpandTags(g.colorMode(sess), varSpec) -} - -func (g *Game) resolveCraftMessage(sess *net.Session, itemMsg, defaultMsg string, craft *object.CraftDef, outputID string, byproducts []string, hasItem func(string) bool) string { - msg := itemMsg - if msg == "" { - msg = defaultMsg - } - if msg == "" { - return "" - } - return g.resolveCraftVar(sess, msg, craft, outputID, byproducts, hasItem) -} - -func (g *Game) startProduction(sess *net.Session, p *player.Player, item *object.ItemDef, craft *object.CraftDef, actionType action.ActionType, displayVerb, startMsg, endMsg string, count int) { - g.cancelAction(p) - g.cancelBgAction(p) - - skill := craft.EffectiveSkill() - if skill != "" { - skillLevel := p.Level(player.SkillName(skill)) - if skillLevel < craft.Level { - sess.WriteLine(fmt.Sprintf("You need level %d %s to do that.", craft.Level, skill)) - g.reprompt(sess) - return - } - } - - if !craftHasAllItemsQty(craft, p.CountItem) { - sess.WriteLine("You don't have the required materials.") - g.reprompt(sess) - return - } - - outDef, _ := g.ItemStore.Load(item.ID) - - wait := craft.Wait - if wait <= 0 { - wait = 4 - } - - outputName := item.ID - if outDef != nil { - outputName = outDef.Name - } - - p.Action = &action.Action{ - Type: actionType, - TargetID: item.ID, - TargetName: outputName, - Data: &action.ProductionData{ - ItemID: item.ID, - Phase: 0, - Wait: wait, - StartMsg: startMsg, - EndMsg: endMsg, - Remaining: count, - }, - WaitLeft: engine.ToTicks(1), - } - - g.broadcastAction(sess, "%s starts %s.", p.Name, displayVerb) -} - -func (g *Game) startProductionFromItem(sess *net.Session, p *player.Player, item *object.ItemDef, count int) { - craft := item.FirstCraft() - if craft == nil { - return - } - - _, stationName := g.findStation(p.RoomID, craft.Station) - if stationName == "" { - stationName = "inventory" - } - - info, ok := productionTypes[craft.Type] - if !ok { - info = productionTypeInfo{ - ActionType: action.ActionType(craft.Type), - DisplayVerb: craft.Type, - StartMessage: "You start " + craft.Type + " %i1.", - SuccessMessage: "You produce %n.", - EndMessage: "You've finished " + craft.Type + ".", - } - } - - startMsg := g.resolveCraftMessage(sess, craft.StartMessage, info.StartMessage, craft, item.ID, nil, p.HasItem) - if stationName != "inventory" { - startMsg += " on the " + stationName + "." - } - - endMsg := g.resolveCraftMessage(sess, craft.EndMessage, info.EndMessage, craft, item.ID, nil, p.HasItem) - - g.startProduction(sess, p, item, craft, info.ActionType, info.DisplayVerb, startMsg, endMsg, count) -} - -func (g *Game) loadCraftItem(itemID string) *object.ItemDef { - item, err := g.ItemStore.Load(itemID) - if err != nil || len(item.Craft) == 0 { - return nil - } - return item -} - -func (g *Game) advanceProduction(sess *net.Session, p *player.Player) bool { - d, ok := p.Action.Data.(*action.ProductionData) - if !ok { - g.cancelAction(p) - return false - } - itemID := d.ItemID - phase := d.Phase - wait := d.Wait - startMsg := d.StartMsg - endMsg := d.EndMsg - remaining := d.Remaining - - item := g.loadCraftItem(itemID) - if item == nil { - g.cancelAction(p) - return false - } - craft := item.FirstCraft() - - if phase == 0 { - if startMsg != "" { - sess.WriteLine(startMsg) - } - if len(craft.Steps) > 0 { - d.NextStepIndex = 0 - d.StepsAccumulatedTicks = 0 - } - d.Phase = 1 - p.Action.WaitLeft = engine.ToTicks(wait) - return true - } - - if stepsIdx := d.NextStepIndex; stepsIdx < len(craft.Steps) { - accTicks := d.StepsAccumulatedTicks - accTicks += wait - d.StepsAccumulatedTicks = accTicks - for i := stepsIdx; i < len(craft.Steps); i++ { - if accTicks >= craft.Steps[i].Tick { - sess.WriteLine(g.resolveCraftVar(sess, craft.Steps[i].Message, craft, item.ID, nil, p.HasItem)) - d.NextStepIndex = i + 1 - } - } - } - - skill := craft.EffectiveSkill() - skillLevel := p.Level(player.SkillName(skill)) - chance := 1.0 - if craft.Success != nil { - chance = action.SuccessChance(action.SuccessFormula(*craft.Success), skillLevel, craft.Level) - } - - info, _ := productionTypes[craft.Type] - - if rand.Float64() < chance { - outputQty := craft.OutputQty - if outputQty <= 0 { - outputQty = 1 - } - - byproducts := g.collectByproducts(p, item) - - placed := false - outDef, _ := g.ItemStore.Load(item.ID) - if outDef != nil && outDef.Stackable { - for i := 0; i < 28; i++ { - slot := p.InvSlot(i) - if slot != nil && slot.ItemID == item.ID { - craftConsumeAll(craft, p.HasItem, p.RemoveItem) - slot.Quantity += outputQty - placed = true - break - } - } - } - if !placed { - craftConsumeAll(craft, p.HasItem, p.RemoveItem) - freeSlot := p.FirstFreeSlot() - if freeSlot == -1 { - sess.WriteLine("Your inventory is too full!") - g.cancelAction(p) - return false - } - p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: item.ID, Quantity: outputQty}) - } - - for _, bp := range byproducts { - if slot := p.FirstFreeSlot(); slot >= 0 { - p.SetInvSlot(slot, &player.InventorySlot{ItemID: bp, Quantity: 1}) - } - } - - if craft.XP > 0 { - if newLevel := p.AddSkillXP(player.SkillName(skill), craft.XP); newLevel > 0 { - sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d %s! ***", newLevel, skill))) - } - } - g.AccountStore.SaveCharacter(p) - - var defaultSuccess string - if info.SuccessMessage != "" { - defaultSuccess = info.SuccessMessage - } else { - defaultSuccess = "You produce %n." - } - msg := g.resolveCraftMessage(sess, craft.Message, defaultSuccess, craft, item.ID, byproducts, p.HasItem) - if p.OptionBool("xp_drops") && craft.XP > 0 { - msg += g.formatXpDropSingle(sess, p, player.SkillName(skill), craft.XP) - } - sess.WriteLine(msg) - } else { - craftConsumeAll(craft, p.HasItem, p.RemoveItem) - - if craft.Fail != "" { - freeSlot := p.FirstFreeSlot() - if freeSlot >= 0 { - p.SetInvSlot(freeSlot, &player.InventorySlot{ItemID: craft.Fail, Quantity: 1}) - } - } - g.AccountStore.SaveCharacter(p) - - msg := g.resolveCraftMessage(sess, craft.FailMessage, info.FailMessage, craft, item.ID, nil, p.HasItem) - if msg != "" { - sess.WriteLine(g.colorize(sess, "damage_taken", msg)) - } - } - - if remaining > 0 { - remaining-- - d.Remaining = remaining - if remaining <= 0 { - if endMsg != "" { - sess.WriteLine(endMsg) - } - g.cancelAction(p) - return false - } - } - - if g.canContinueProduction(p, item) { - p.Action.WaitLeft = engine.ToTicks(wait) - return true - } - - if endMsg != "" { - sess.WriteLine(endMsg) - } - g.cancelAction(p) - return false -} - -func (g *Game) collectByproducts(p *player.Player, item *object.ItemDef) []string { - craft := item.FirstCraft() - if craft == nil { - return nil - } - var byproducts []string - for _, e := range craft.Consume { - if len(e.Byproducts) == 0 { - continue - } - for i, id := range e.Items { - if p.HasItem(id) && i < len(e.Byproducts) && e.Byproducts[i] != "" { - byproducts = append(byproducts, e.Byproducts[i]) - break - } - } - } - return byproducts -} - -func (g *Game) canContinueProduction(p *player.Player, item *object.ItemDef) bool { - craft := item.FirstCraft() - if craft == nil { - return false - } - if !craftHasAllItemsQty(craft, p.CountItem) { - return false - } - outputQty := craft.OutputQty - if outputQty <= 0 { - outputQty = 1 - } - outDef, _ := g.ItemStore.Load(item.ID) - if outDef != nil && outDef.Stackable { - for i := 0; i < 28; i++ { - slot := p.InvSlot(i) - if slot != nil && slot.ItemID == item.ID { - return true - } - } - } - return p.FirstFreeSlot() >= 0 -} - -func (g *Game) handleRecipeChoice(sess *net.Session, input string) { - input = strings.TrimSpace(input) - - menuData := sess.PendingMenu - if len(menuData) == 0 { - sess.State = net.StateGame - g.reprompt(sess) - return - } - - if input == "" { - sess.PendingMenu = nil - sess.State = net.StateGame - sess.WriteLine("Never mind.") - g.reprompt(sess) - return - } - - if choice, err := strconv.Atoi(input); err == nil { - sess.PendingMenu = nil - sess.State = net.StateGame - if choice <= 0 || choice > len(menuData) { - sess.WriteLine("Never mind.") - g.reprompt(sess) - return - } - g.dispatchMenuEntry(sess, menuData[choice-1]) - return - } - - lower := strings.ToLower(input) - matchIdx := -1 - for i, entry := range menuData { - name := g.menuEntryName(entry) - if name == "" { - continue - } - if strings.ToLower(name) == lower || object.WordPrefixMatch(input, name) { - if matchIdx >= 0 { - sess.WriteLine("That's ambiguous.") - return - } - matchIdx = i - } - } - if matchIdx >= 0 { - sess.PendingMenu = nil - sess.State = net.StateGame - g.dispatchMenuEntry(sess, menuData[matchIdx]) - return - } - - sess.PendingMenu = nil - sess.State = net.StateGame - sess.WriteLine("Never mind.") - g.reprompt(sess) -} - -func (g *Game) menuEntryName(entry map[string]string) string { - if iid, ok := entry["item_id"]; ok { - if def, _ := g.ItemStore.Load(iid); def != nil { - return def.Name - } - return iid - } - if barID, ok := entry["bar_id"]; ok { - if def, _ := g.ItemStore.Load(barID); def != nil { - return def.Name - } - return barID - } - return "" -} - -func (g *Game) dispatchMenuEntry(sess *net.Session, entry map[string]string) { - if iid, ok := entry["item_id"]; ok { - g.promptHowMany(sess, iid) - return - } - if barID, ok := entry["bar_id"]; ok { - g.showSmithTable(sess, sess.Player, barID) - return - } - g.reprompt(sess) -} - -func (g *Game) formatMaterials(sess *net.Session, item *object.ItemDef) string { - craft := item.FirstCraft() - if craft == nil { - return "" - } - var parts []string - for _, e := range craft.Consume { - itemName := e.Items[0] - if def, err := g.ItemStore.Load(e.Items[0]); err == nil { - itemName = g.itemColorize(sess, def, def.Name) - } - if e.Quantity > 1 { - parts = append(parts, fmt.Sprintf("%s x%d", itemName, e.Quantity)) - } else { - parts = append(parts, itemName) - } - } - return strings.Join(parts, ", ") -} - -func (g *Game) showProductionTable(sess *net.Session, p *player.Player, items []*object.ItemDef, title, skill, lastFlagKey, promptVerb string, background bool) { - sort.Slice(items, func(i, j int) bool { - return items[i].FirstCraft().Level < items[j].FirstCraft().Level - }) - - mode := g.colorMode(sess) - skillLevel := p.Level(player.SkillName(skill)) - dimSpec := color.Parse("240") - - tbl := &Table{ - Title: title, - Columns: []string{"#", "Product", "Materials", "Level"}, - } - - for i, item := range items { - craft := item.FirstCraft() - outDef, _ := g.ItemStore.Load(item.ID) - productName := item.ID - if outDef != nil { - productName = outDef.Name - } - - outputQty := craft.OutputQty - if outputQty <= 0 { - outputQty = 1 - } - if outDef != nil && outDef.Stackable && outputQty > 1 { - productName = fmt.Sprintf("%s x%d", productName, outputQty) - } - - matStr := g.formatMaterials(sess, item) - levelStr := fmt.Sprint(craft.Level) - numStr := fmt.Sprintf("%d", i+1) - - canMake := skillLevel >= craft.Level && craftHasAllItemsQty(craft, p.CountItem) - if canMake { - productName = g.itemColorize(sess, outDef, productName) - } else { - productName = color.Render(mode, dimSpec, productName) - matStr = color.Render(mode, dimSpec, matStr) - levelStr = color.Render(mode, dimSpec, levelStr) - numStr = color.Render(mode, dimSpec, numStr) - } - - tbl.Rows = append(tbl.Rows, []string{numStr, productName, matStr, levelStr}) - } - - unicode := p.OptionBool("unicode") - sess.WriteLine("") - for _, line := range tbl.Render(unicode) { - sess.WriteLine(line) - } - - lastItemID, _ := p.Flags[lastFlagKey].(string) - hint := "" - if lastItemID != "" { - for _, item := range items { - if item.ID == lastItemID { - if outDef, err := g.ItemStore.Load(item.ID); err == nil { - hint = outDef.Name - } - break - } - } - } - - if hint != "" { - sess.Write(fmt.Sprintf("%s what (enter for all %s): ", promptVerb, hint)) - } else { - sess.Write(fmt.Sprintf("%s what: ", promptVerb)) - } - - sess.PendingMenu = itemMenuData(itemIDs(items)) - sess.PendingSkill = skill - sess.PendingLastFlag = lastFlagKey - sess.PendingBackground = background - sess.State = net.StateProductChoice -} - -func (g *Game) handleProductChoice(sess *net.Session, input string) { - p := sess.Player - menuData := sess.PendingMenu - skill := sess.PendingSkill - lastFlagKey := sess.PendingLastFlag - background := sess.PendingBackground - sess.PendingMenu = nil - sess.PendingSkill = "" - sess.PendingLastFlag = "" - sess.PendingBackground = false - sess.State = net.StateGame - - input = strings.TrimSpace(input) - - var items []*object.ItemDef - for _, entry := range menuData { - iid := entry["item_id"] - if item := g.loadCraftItem(iid); item != nil { - items = append(items, item) - } - } - - if len(items) == 0 { - g.reprompt(sess) - return - } - - sort.Slice(items, func(i, j int) bool { - return items[i].FirstCraft().Level < items[j].FirstCraft().Level - }) - - lastItemID, _ := p.Flags[lastFlagKey].(string) - - sel, errMsg := g.resolveCraftByInput(input, items, lastItemID) - switch errMsg { - case "ambiguous": - sess.WriteLine("That's ambiguous.") - g.reprompt(sess) - return - case "never_mind": - sess.WriteLine("Never mind.") - g.reprompt(sess) - return - } - - craft := sel.Item.FirstCraft() - skillLevel := p.Level(player.SkillName(skill)) - if skillLevel < craft.Level { - sess.WriteLine(fmt.Sprintf("You need level %d %s to make that.", craft.Level, skill)) - g.reprompt(sess) - return - } - if !craftHasAllItemsQty(craft, p.CountItem) { - sess.WriteLine("You don't have the materials for that.") - g.reprompt(sess) - return - } - - p.EnsureFlags() - p.Flags[lastFlagKey] = sel.Item.ID - g.AccountStore.SaveCharacter(p) - - if background { - g.startFletchAction(sess, p, sel.Item, sel.Count) - } else { - g.startProductionFromItem(sess, p, sel.Item, sel.Count) - } -} - func (g *Game) findTool(p *player.Player, toolTypes []string) (toolSpeed float64, toolName string, found bool) { toolSpeed = -1 - if itemID, ok := p.Equipment[object.SlotMainHand]; ok { + if itemID, ok := p.Equipment[item.SlotMainHand]; ok { if def, err := g.ItemStore.Load(itemID); err == nil { for _, t := range toolTypes { if def.ToolType == t { @@ -649,7 +41,7 @@ func (g *Game) hasToolType(p *player.Player, toolType string) bool { } type productionTypeInfo struct { - ActionType action.ActionType + ActionType behavior.ActionType DisplayVerb string StartMessage string SuccessMessage string @@ -659,7 +51,7 @@ type productionTypeInfo struct { var productionTypes = map[string]productionTypeInfo{ "cooking": { - ActionType: action.TypeCook, + ActionType: behavior.TypeCook, DisplayVerb: "cooking", StartMessage: "You start cooking %i1.", SuccessMessage: "Cooked to perfection. %n looks great!", @@ -667,7 +59,7 @@ var productionTypes = map[string]productionTypeInfo{ FailMessage: "You accidentally burn the %i1.", }, "smelting": { - ActionType: action.TypeSmelt, + ActionType: behavior.TypeSmelt, DisplayVerb: "smelting", StartMessage: "You place the %i1 into the furnace.", SuccessMessage: "You remove a white hot %n!", @@ -675,42 +67,42 @@ var productionTypes = map[string]productionTypeInfo{ FailMessage: "The %i1 is consumed by the flames.", }, "smithing": { - ActionType: action.TypeSmith, + ActionType: behavior.TypeSmith, DisplayVerb: "smithing", StartMessage: "You begin smithing %i1.", SuccessMessage: "You smith a %n.", EndMessage: "You've finished smithing.", }, "crafting": { - ActionType: action.TypeCraft, + ActionType: behavior.TypeCraft, DisplayVerb: "crafting", StartMessage: "You begin crafting %i1.", SuccessMessage: "You craft a %n.", EndMessage: "You've finished crafting.", }, "combine": { - ActionType: action.TypeCombine, + ActionType: behavior.TypeCombine, DisplayVerb: "combining", StartMessage: "You start combining %i1.", SuccessMessage: "You produce %n.", EndMessage: "You've finished combining.", }, "fletching": { - ActionType: action.TypeFletch, + ActionType: behavior.TypeFletch, DisplayVerb: "fletching", StartMessage: "You begin fletching %i1.", SuccessMessage: "You fletch a %n.", EndMessage: "You've finished fletching.", }, "pharmacy": { - ActionType: action.TypeMix, + ActionType: behavior.TypeMix, DisplayVerb: "mixing", StartMessage: "You start mixing %i1.", SuccessMessage: "You mix a %n.", EndMessage: "You've finished mixing.", }, "construction": { - ActionType: action.TypeConstruct, + ActionType: behavior.TypeConstruct, DisplayVerb: "constructing", StartMessage: "You begin constructing %i1.", SuccessMessage: "You construct a %n.", @@ -726,150 +118,3 @@ func init() { productionActionTypes[string(pt.ActionType)] = true } } - -func itemMenuData(itemIDs []string) []map[string]string { - out := make([]map[string]string, len(itemIDs)) - for i, id := range itemIDs { - out[i] = map[string]string{"item_id": id} - } - return out -} - -func itemIDs(defs []*object.ItemDef) []string { - ids := make([]string, len(defs)) - for i, d := range defs { - ids[i] = d.ID - } - return ids -} - -func (g *Game) promptHowMany(sess *net.Session, itemID string) { - sess.PendingItemID = itemID - sess.State = net.StateHowMany - sess.Write("How many (return for all)?: ") -} - -type craftSelection struct { - Item *object.ItemDef - Count int -} - -func (g *Game) resolveCraftByInput(input string, items []*object.ItemDef, lastItemID string) (sel craftSelection, errMsg string) { - if input == "" { - if lastItemID == "" { - return sel, "never_mind" - } - for i := range items { - if items[i].ID == lastItemID { - sel.Item = items[i] - return sel, "" - } - } - return sel, "never_mind" - } - - qty, productName := parseQty(input) - productName = strings.TrimSpace(productName) - - if idx, err := strconv.Atoi(productName); err == nil && idx > 0 && idx <= len(items) { - sel.Item = items[idx-1] - sel.Count = qty - return sel, "" - } - - matchCount := 0 - for i := range items { - outDef, _ := g.ItemStore.Load(items[i].ID) - name := items[i].ID - if outDef != nil { - name = outDef.Name - } - if object.WordPrefixMatch(productName, name) { - if matchCount == 0 { - sel.Item = items[i] - sel.Count = qty - } - matchCount++ - } - } - if matchCount > 1 { - return sel, "ambiguous" - } - if sel.Item == nil { - return sel, "never_mind" - } - return sel, "" -} - -func (g *Game) showMenuTable(sess *net.Session, title string, names []string) { - p := sess.Player - tbl := &Table{Title: title} - for i, name := range names { - tbl.Rows = append(tbl.Rows, []string{fmt.Sprintf("%d", i+1), name}) - } - unicode := p.OptionBool("unicode") - sess.WriteLine("") - for _, line := range tbl.Render(unicode) { - sess.WriteLine(line) - } -} - -func (g *Game) itemName(sess *net.Session, item *object.ItemDef) string { - if item != nil { - if def, err := g.ItemStore.Load(item.ID); err == nil { - return g.itemColorize(sess, def, def.Name) - } - return item.ID - } - return "" -} - -func (g *Game) showRecipeChoice(sess *net.Session, p *player.Player, items []*object.ItemDef, emptyMsg, title string, autoOption string) { - if len(items) == 0 { - sess.WriteLine(emptyMsg) - return - } - if len(items) == 1 { - if autoOption != "" && p.OptionBool(autoOption) { - g.startProductionFromItem(sess, p, items[0], 0) - return - } - g.promptHowMany(sess, items[0].ID) - return - } - sess.State = net.StateRecipeChoice - sess.PendingMenu = itemMenuData(itemIDs(items)) - var names []string - for _, item := range items { - names = append(names, g.itemName(sess, item)) - } - g.showMenuTable(sess, title, names) -} - -func (g *Game) handleHowMany(sess *net.Session, input string) { - p := sess.Player - itemID := sess.PendingItemID - sess.PendingItemID = "" - sess.State = net.StateGame - - input = strings.TrimSpace(input) - - var count int - if input == "" { - count = 0 - } else if n, err := strconv.Atoi(input); err == nil && n > 0 { - count = n - } else { - sess.WriteLine("Never mind.") - g.reprompt(sess) - return - } - - item := g.loadCraftItem(itemID) - if item == nil { - g.reprompt(sess) - return - } - - g.startProductionFromItem(sess, p, item, count) -} -- cgit v1.2.3