rimlike/scenes/ai/recipe_catalog.gd
megaproxy bdd435202d Workbench bill editor — tap a workbench, see/edit bills
Tap-to-select chain extended to workbenches (pawn always wins on shared
tile). Mutually exclusive with pawn selection via EventBus —
selecting one clears the other.

New WorkbenchPanel (scenes/ui/workbench_panel.gd, ~432 LOC, layer 18,
right-anchored 360 px) mirrors PawnDetailPanel shape. Bill rows expose
recipe name, mode (FOREVER / COUNT / UNTIL_N), target count, completed
progress, pause, and remove. Add-bill popup filters RecipeCatalog.all()
by accepted_skill so a Hearth only offers cooking recipes.

Supporting plumbing:
- EventBus.workbench_selected / workbench_deselected signals.
- Workbench.remove_bill() — interrupts mid-craft cleanly via
  on_craft_interrupted() before erasing.
- RecipeCatalog.all() static enumerator + Recipe.display_name() helper.
- World.workbench_at_tile() lookup.
- i18n keys ui.bill.* and ui.workbench.* in strings.gd.

Closes the deferred Phase 17 "Bill UI for workbenches" item. Player-
built workbenches are now functionally configurable; before this
landed, only world.gd-hardcoded bills worked.
2026-05-16 00:29:46 +01:00

125 lines
4.5 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

class_name RecipeCatalog
## Static registry of all recipes. Each method returns a fresh Recipe instance
## configured for that product. Extend here as new workbenches land.
##
## Phase 6 — two recipes:
## plank() — wood → plank (Carpenter's bench, Crafting)
## stone_block() — stone → stone_block (Smelter, Crafting)
##
## Phase 7 — cooking chain (grain → flour → bread, vegetable → meal):
## flour() — grain → flour (Millstone, Crafting)
## bread() — flour → bread (Hearth, Cooking)
## meal_from_vegetables() — vegetable → meal (Hearth, Cooking)
##
## The full ~22-recipe list per docs/design.md continues in Phase 8+.
static func plank() -> Recipe:
var r := Recipe.new()
r.id = &"plank"
r.label = "Wood plank"
r.ingredient_type = Item.TYPE_WOOD
r.output_type = Item.TYPE_PLANK
r.work_ticks = 60 # ~3 sim seconds at 1× (20 Hz × 3 s = 60 ticks)
r.required_skill = Recipe.SKILL_CRAFTING
r.skill_threshold = 0
return r
static func stone_block() -> Recipe:
var r := Recipe.new()
r.id = &"stone_block"
r.label = "Stone block"
r.ingredient_type = Item.TYPE_STONE
r.output_type = Item.TYPE_STONE_BLOCK
r.work_ticks = 80 # ~4 sim seconds at 1×
r.required_skill = Recipe.SKILL_CRAFTING
r.skill_threshold = 0
return r
# ── Phase 7 — cooking chain ───────────────────────────────────────────────────
static func flour() -> Recipe:
## Step 1 of the grain→flour→bread chain.
## Ingredient: grain. Output: flour. Worked at the Millstone (accepted_skill = crafting).
## Any crafter can do this — no culinary expertise required for grinding.
var r := Recipe.new()
r.id = &"flour"
r.label = "Flour"
r.ingredient_type = Item.TYPE_GRAIN
r.output_type = Item.TYPE_FLOUR
r.work_ticks = 50 # ~2.5 sim seconds at 1× — quick grinding pass
r.required_skill = Recipe.SKILL_CRAFTING
r.skill_threshold = 0
return r
static func bread() -> Recipe:
## Step 2 of the grain→flour→bread chain.
## Ingredient: flour. Output: bread. Worked at the Hearth (accepted_skill = cooking).
var r := Recipe.new()
r.id = &"bread"
r.label = "Bread"
r.ingredient_type = Item.TYPE_FLOUR
r.output_type = Item.TYPE_BREAD
r.work_ticks = 90 # ~4.5 sim seconds at 1× — baking takes longer
r.required_skill = Recipe.SKILL_COOKING
r.skill_threshold = 0
return r
static func meal_from_vegetables() -> Recipe:
## Single-step cooking: vegetable → meal. Worked at the Hearth (accepted_skill = cooking).
## Parallel path so harvested produce (potatoes, etc.) can reach the belly
## without going through the grain chain.
var r := Recipe.new()
r.id = &"meal_veg"
r.label = "Veggie meal"
r.ingredient_type = Item.TYPE_VEGETABLE
r.output_type = Item.TYPE_MEAL
r.work_ticks = 80 # ~4 sim seconds at 1×
r.required_skill = Recipe.SKILL_COOKING
r.skill_threshold = 0
return r
# ── Phase 14 — Death + cremation ─────────────────────────────────────────────
static func cremate_corpse() -> Recipe:
## Cremation recipe. Primary ingredient: 1 corpse (TYPE_CORPSE).
## Secondary ingredient: 5 wood (ingredient2_type / ingredient2_count) — see
## recipe.gd for the Phase 14 extension fields.
## Output: 1 ash item (TYPE_ASH). Work time: 60 ticks (~3 sim seconds at 1×).
## No Quality roll — cremation is binary. accepted_skill = manual_labor;
## any laborer can operate the pyre.
##
## Stub gap: CraftingProvider's ingredient-pickup step only handles the
## primary ingredient (ingredient_type = TYPE_CORPSE). The 5-wood secondary
## requirement is recorded in ingredient2_type/ingredient2_count but is NOT
## enforced at runtime until CraftingProvider is extended (Phase 14 follow-up).
var r := Recipe.new()
r.id = &"cremate_corpse"
r.label = "Cremate corpse"
r.ingredient_type = Item.TYPE_CORPSE
r.ingredient2_type = Item.TYPE_WOOD
r.ingredient2_count = 5
r.output_type = Item.TYPE_ASH
r.work_ticks = 60 # ~3 sim seconds at 1×
r.required_skill = &"manual_labor"
r.skill_threshold = 0
return r
## Returns one fresh instance of every recipe in the catalog. Used by UI
## recipe-pickers to enumerate available bills; callers filter by
## `recipe.required_skill` against the workbench's `accepted_skill`.
static func all() -> Array[Recipe]:
return [
plank(),
stone_block(),
flour(),
bread(),
meal_from_vegetables(),
cremate_corpse(),
]