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.
This commit is contained in:
megaproxy 2026-05-16 00:29:46 +01:00
parent e5f3693ad9
commit bdd435202d
9 changed files with 551 additions and 10 deletions

View file

@ -206,6 +206,15 @@ func add_bill(b) -> void:
Audit.log("workbench", "%s: bill added — recipe '%s'" % [label_text, b.recipe.id])
## Remove a bill from this workbench's queue. If the bill is currently being
## crafted, the active toil is interrupted cleanly so the pawn re-decides.
func remove_bill(b) -> void:
if current_bill == b:
on_craft_interrupted()
bills.erase(b)
Audit.log("workbench", "%s: bill removed — recipe '%s'" % [label_text, b.recipe.id])
## Return the first bill that is active and whose required_skill matches
## this bench's accepted_skill. Returns null when none qualify.
## CraftingProvider calls this; JobRunner also calls it when the current_bill