From 9666676766bf21364f4fc4d0d1d336e72f2a91b4 Mon Sep 17 00:00:00 2001 From: historia <[not public]> Date: Tue, 6 Oct 2026 12:57:23 -0400 Subject: refactor: split casino.go, dedup helpers, remove audit docs --- internal/game/production_engine.go | 43 +++++++++++++++++++++++++------------- 1 file changed, 29 insertions(+), 14 deletions(-) (limited to 'internal/game/production_engine.go') diff --git a/internal/game/production_engine.go b/internal/game/production_engine.go index 4717fbf..682cea3 100644 --- a/internal/game/production_engine.go +++ b/internal/game/production_engine.go @@ -76,14 +76,12 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, item *item.I 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 - } + skill := recipeSkill(craft) + skillLevel := p.Level(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) { @@ -92,6 +90,23 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, item *item.I return } + // Enforce station and tool requirements here, at the single execution + // chokepoint. Recipes can also start via `use X with Y` and trigger + // transports that bypass the crafting menus; without this, a recipe can + // be executed anywhere with no station or tool. + if len(craft.Station) > 0 { + if stID, _ := g.findStation(p.RoomID, craft.Station); stID == "" { + sess.WriteLine("You are not near the required station.") + g.reprompt(sess) + return + } + } + if craft.Tool != "" && !g.hasToolType(p, craft.Tool) { + sess.WriteLine("You do not have the required tool.") + g.reprompt(sess) + return + } + outDef, _ := g.ItemStore.Load(item.ID) wait := craft.TicksPerCycle @@ -129,8 +144,8 @@ func (g *Game) startProduction(sess *net.Session, p *player.Player, item *item.I func (g *Game) usableCraftIndex(p *player.Player, item *item.ItemDef) int { for i := range item.Craft { c := &item.Craft[i] - skill := c.EffectiveSkill() - if skill != "" && p.Level(player.SkillName(skill)) < c.Level { + skill := recipeSkill(c) + if p.Level(skill) < c.Level { continue } if !craftHasAllItemsQty(c, p.CountItem) { @@ -248,8 +263,8 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) { return } - skill := craft.EffectiveSkill() - skillLevel := p.Level(player.SkillName(skill)) + skill := recipeSkill(craft) + skillLevel := p.Level(skill) chance := 1.0 if craft.Success != nil { chance = behavior.SuccessChance(behavior.SuccessFormula(*craft.Success), skillLevel, craft.Level) @@ -313,7 +328,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) { } if craft.XP > 0 { - if newLevel := p.AddSkillXP(player.SkillName(skill), craft.XP); newLevel > 0 { + if newLevel := p.AddSkillXP(skill, craft.XP); newLevel > 0 { sess.WriteLine(g.colorize(sess, "level_up", fmt.Sprintf("*** You are now level %d %s! ***", newLevel, skill))) } } @@ -327,7 +342,7 @@ func (g *Game) advanceProduction(sess *net.Session, p *player.Player) { } msg := g.resolveCraftMessage(sess, craft.SuccessMessage, defaultSuccess, craft, item.ID, byproducts, hasItem) if p.OptionBool("xp_drops") && craft.XP > 0 { - msg += g.formatXpDropSingle(sess, p, player.SkillName(skill), craft.XP) + msg += g.formatXpDropSingle(sess, p, skill, craft.XP) } sess.WriteLine(msg) } else { -- cgit v1.2.3