From cc6d60d044c19c16f6d32abadde791b0fcd51f12 Mon Sep 17 00:00:00 2001 From: megaproxy Date: Sat, 16 May 2026 18:23:22 +0100 Subject: [PATCH] sprint cleanup: corpse_cremated signal, recipe_count, save_system MIME-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit D: Workbench._last_consumed_ingredient transient field captures the carried item before queue_free so cremation_pyre.on_craft_complete can emit corpse_cremated with the real ref. Falls back to proximity scan. Pawn._on_corpse_cremated null-guarded. E: removed redundant r.ingredient_count = 0 from recipe_catalog. Field kept on Recipe for save round-trip compat; nothing reads it functionally. F: save_system._spawn_workbench simplified from 15 lines to 6 — let from_dict do all field restoration. Fixed workbench.from_dict to call _complete() instead of bare _completed=true, which was skipping light enable + beauty register + designation clear. Stale ingredient1/2 buffering comment in job_runner._tick_craft fixed. Co-Authored-By: Claude Opus 4.7 (1M context) --- autoload/save_system.gd | 16 ++++------------ scenes/ai/job_runner.gd | 8 +++++++- scenes/ai/recipe_catalog.gd | 1 - scenes/entities/cremation_pyre.gd | 8 ++++++-- scenes/entities/workbench.gd | 12 +++++++++++- scenes/pawn/pawn.gd | 2 +- 6 files changed, 29 insertions(+), 18 deletions(-) diff --git a/autoload/save_system.gd b/autoload/save_system.gd index 57cb48d..b76bb61 100644 --- a/autoload/save_system.gd +++ b/autoload/save_system.gd @@ -493,18 +493,10 @@ func _spawn_door(world_scene: Node, d: Dictionary) -> void: func _spawn_workbench(world_scene: Node, d: Dictionary) -> void: var ent = _WORKBENCH_SCENE.instantiate() world_scene.add_child(ent) - ent.setup(Vector2i(int(d.get("tile_x", 0)), int(d.get("tile_y", 0)))) - if d.has("label_text"): - ent.label_text = str(d["label_text"]) - if d.has("accepted_skill"): - ent.accepted_skill = StringName(d["accepted_skill"]) - ent.build_progress = int(d.get("build_progress", 0)) - if bool(d.get("completed", false)): - ent.build_progress = ent.BUILD_TICKS - ent.on_build_tick() - # from_dict restores bills + any other Workbench-specific state. - if ent.has_method("from_dict"): - ent.from_dict(d) + # from_dict restores all fields (label_text, accepted_skill, build_progress, + # bills, deposited_inputs) and calls _complete() + setup() internally. + # No manual field assignment needed here. + ent.from_dict(d) func _spawn_crop(world_scene: Node, d: Dictionary) -> void: diff --git a/scenes/ai/job_runner.gd b/scenes/ai/job_runner.gd index ea69c70..b5e0b9a 100644 --- a/scenes/ai/job_runner.gd +++ b/scenes/ai/job_runner.gd @@ -524,10 +524,14 @@ func _tick_craft(t) -> void: # Consume ingredients. # Single-ingredient: pawn.carried_item holds the only input — free it. - # Two-ingredient: pawn.carried_item is ingredient2; ingredient1 was buffered + # Two-ingredient: pawn.carried_item is ingredient (primary); ingredient2 was buffered # in wb.deposited_inputs by _tick_deposit_at_wb — remove it from the buffer. # No-ingredient: carried_item is null; nothing to consume. var ingredient = pawn.carried_item + # Expose the consumed ingredient to on_craft_complete() via a transient field + # so overrides (e.g. CremationPyre) can retrieve it by reference instead of + # relying on a proximity scan. Cleared below after on_craft_complete returns. + wb._last_consumed_ingredient = ingredient pawn.carried_item = null if ingredient != null and is_instance_valid(ingredient): ingredient.queue_free() @@ -551,6 +555,8 @@ func _tick_craft(t) -> void: # Record completion on the bill and reset workbench state. wb.on_craft_complete() + # Clear transient ingredient ref now that on_craft_complete overrides have run. + wb._last_consumed_ingredient = null Audit.log( "job_runner", diff --git a/scenes/ai/recipe_catalog.gd b/scenes/ai/recipe_catalog.gd index 24d84e3..0ed0e4a 100644 --- a/scenes/ai/recipe_catalog.gd +++ b/scenes/ai/recipe_catalog.gd @@ -118,7 +118,6 @@ static func quarry_stone() -> Recipe: r.id = &"quarry_stone" r.label = "Quarry stone" r.ingredient_type = &"" # no input - r.ingredient_count = 0 r.output_type = Item.TYPE_STONE r.work_ticks = 300 r.required_skill = &"manual_labor" diff --git a/scenes/entities/cremation_pyre.gd b/scenes/entities/cremation_pyre.gd index 02e5db9..e8df071 100644 --- a/scenes/entities/cremation_pyre.gd +++ b/scenes/entities/cremation_pyre.gd @@ -64,8 +64,12 @@ func on_craft_complete() -> void: # Base Workbench cleanup (clears current_bill, resets work progress). super.on_craft_complete() - # Find and consume the corpse that was hauled as ingredient. - var consumed_corpse = _find_consumed_corpse() + # Find the corpse that was hauled as ingredient. Prefer the transient + # reference set by JobRunner (reliable regardless of corpse tile position); + # fall back to proximity scan only if the field is somehow unset. + var consumed_corpse = _last_consumed_ingredient if _last_consumed_ingredient != null \ + and is_instance_valid(_last_consumed_ingredient) \ + else _find_consumed_corpse() # Drop 1 ash item on an adjacent walkable tile (same pattern as # Tree.fell() / Workbench output drops elsewhere in Phase 6). diff --git a/scenes/entities/workbench.gd b/scenes/entities/workbench.gd index 02e286a..6f2d683 100644 --- a/scenes/entities/workbench.gd +++ b/scenes/entities/workbench.gd @@ -131,6 +131,12 @@ var _light: PointLight2D = null ## completes or is interrupted. Not stocked by single-ingredient recipes. var deposited_inputs: Dictionary = {} +## Transient: set by JobRunner._tick_craft to the carried ingredient node +## immediately before queue_free() so on_craft_complete() overrides (e.g. +## CremationPyre) can retrieve the consumed entity by reference rather than +## by proximity scan. Cleared by the caller after on_craft_complete returns. +var _last_consumed_ingredient = null + # ── lifecycle ───────────────────────────────────────────────────────────────── @@ -352,7 +358,11 @@ func from_dict(d: Dictionary) -> void: label_text = str(d.get("label_text", "Workbench")) accepted_skill = StringName(d.get("accepted_skill", "crafting")) build_progress = int(d.get("build_progress", 0)) - _completed = bool(d.get("completed", false)) + # Use _complete() when already finished so side effects (light, beauty, + # designation clear) fire correctly on load — matching what on_build_tick() + # does during normal construction. + if bool(d.get("completed", false)): + _complete() current_work_progress = int(d.get("current_work_progress", 0)) # Reconstruct bills from the saved array. Clear first so this is idempotent. bills.clear() diff --git a/scenes/pawn/pawn.gd b/scenes/pawn/pawn.gd index c3cad8d..d0d7036 100644 --- a/scenes/pawn/pawn.gd +++ b/scenes/pawn/pawn.gd @@ -693,7 +693,7 @@ func _on_corpse_buried(corpse, marker) -> void: ## Phase 14 — fires when EventBus.corpse_cremated is emitted (CremationPyre). ## Gives the cremated_friend thought if this pawn was nearby (8 tiles). func _on_corpse_cremated(corpse, pyre) -> void: - if pyre == null: + if corpse == null or pyre == null: return var pyre_tile: Vector2i = pyre.tile if pyre.get("tile") != null else Vector2i(-999, -999) var dist: int = abs(pyre_tile.x - tile.x) + abs(pyre_tile.y - tile.y)