diff --git a/client/scripts/main.gd b/client/scripts/main.gd index a03c16e..cb6ee87 100644 --- a/client/scripts/main.gd +++ b/client/scripts/main.gd @@ -866,15 +866,10 @@ func _setup_quest_progress_sync() -> void: Callable(self, "_apply_authority_http_config_to_client"), _quest_reward_delivery_label, _item_defs_client, - Callable(self, "_on_quest_completion_reward_transition") + _inventory_client, + _skill_progression_client ) -func _on_quest_completion_reward_transition(_quest_id: String, _summary: Dictionary) -> void: - _request_inventory_refresh() - var skill_client := _skill_progression_client - if is_instance_valid(skill_client) and skill_client.has_method("request_sync_from_server"): - skill_client.call("request_sync_from_server") - func _request_quest_progress_refresh() -> void: if is_instance_valid(_quest_hud): diff --git a/client/scripts/quest_hud_controller.gd b/client/scripts/quest_hud_controller.gd index fee6a64..e021d05 100644 --- a/client/scripts/quest_hud_controller.gd +++ b/client/scripts/quest_hud_controller.gd @@ -26,7 +26,8 @@ var _previous_status_by_quest: Dictionary = {} var _quest_was_active_in_session: Dictionary = {} var _last_reward_quest_id: String = "" var _last_reward_summary: Dictionary = {} -var _economy_refresh_callback: Callable = Callable() +var _inventory_client: Node = null +var _skill_progression_client: Node = null func setup( @@ -37,15 +38,17 @@ func setup( apply_http_config: Callable, reward_label: Label = null, item_defs_client: Node = null, - economy_refresh_callback: Callable = Callable() + inventory_client: Node = null, + skill_progression_client: Node = null ) -> void: _progress_client = progress_client _defs_client = defs_client _item_defs_client = item_defs_client + _inventory_client = inventory_client + _skill_progression_client = skill_progression_client _progress_label = progress_label _accept_label = accept_label _reward_label = reward_label - _economy_refresh_callback = economy_refresh_callback if apply_http_config.is_valid(): apply_http_config.call(progress_client) apply_http_config.call(defs_client) @@ -213,8 +216,20 @@ func _detect_and_apply_completion_transitions(snapshot: Dictionary) -> void: _last_reward_summary = {} if not _last_reward_quest_id.is_empty() and not _last_reward_summary.is_empty(): quest_completion_reward_transition.emit(_last_reward_quest_id, _last_reward_summary) - if _economy_refresh_callback.is_valid(): - _economy_refresh_callback.call(_last_reward_quest_id, _last_reward_summary) + _refresh_economy_hud() + + +func _refresh_economy_hud() -> void: + if ( + is_instance_valid(_inventory_client) + and _inventory_client.has_method("request_sync_from_server") + ): + _inventory_client.call("request_sync_from_server") + if ( + is_instance_valid(_skill_progression_client) + and _skill_progression_client.has_method("request_sync_from_server") + ): + _skill_progression_client.call("request_sync_from_server") func _quest_ids_in_render_order(snapshot: Dictionary) -> Array: diff --git a/client/test/quest_reward_hud_test.gd b/client/test/quest_reward_hud_test.gd index 96dc810..d134d7b 100644 --- a/client/test/quest_reward_hud_test.gd +++ b/client/test/quest_reward_hud_test.gd @@ -38,6 +38,15 @@ class StubItemDefsClient: return item_id +class MockEconomySyncClient: + extends Node + + var sync_count: int = 0 + + func request_sync_from_server() -> void: + sync_count += 1 + + func _make_defs_client(transport: Node) -> Node: var c: Node = QuestDefinitionsClient.new() c.set("injected_http", transport) @@ -291,15 +300,22 @@ func test_completion_transition_emits_economy_refresh_signal() -> void: func test_completion_transition_invokes_economy_refresh_callback() -> void: # Arrange var controller: Node = QuestHudController.new() - var called := false + var inventory_client := MockEconomySyncClient.new() + var skill_client := MockEconomySyncClient.new() auto_free(controller) + auto_free(inventory_client) + auto_free(skill_client) add_child(controller) - controller.set("_economy_refresh_callback", func(_qid, _summary): called = true) + add_child(inventory_client) + add_child(skill_client) + controller.set("_inventory_client", inventory_client) + controller.set("_skill_progression_client", skill_client) # Act controller.call("_on_progress_received", _active_gather_snapshot()) controller.call("_on_progress_received", _completed_gather_with_summary_snapshot()) # Assert - assert_bool(called).is_true() + assert_int(inventory_client.sync_count).is_equal(1) + assert_int(skill_client.sync_count).is_equal(1) func test_boot_completed_does_not_emit_economy_refresh_signal() -> void: @@ -311,8 +327,6 @@ func test_boot_completed_does_not_emit_economy_refresh_signal() -> void: # Act controller.call("_on_progress_received", _completed_gather_with_summary_snapshot()) # Assert - await ( - assert_signal(controller) - .wait_until(200) - .is_not_emitted("quest_completion_reward_transition") - ) + await (assert_signal(controller).wait_until(200).is_not_emitted( + "quest_completion_reward_transition" + )) diff --git a/docs/plans/NEO-132-implementation-plan.md b/docs/plans/NEO-132-implementation-plan.md index 3fe9d92..fd636d7 100644 --- a/docs/plans/NEO-132-implementation-plan.md +++ b/docs/plans/NEO-132-implementation-plan.md @@ -58,8 +58,8 @@ - **`docs/manual-qa/NEO-132.md`** — four-quest capstone extending NEO-123 with reward HUD + economy verification + idempotency steps. - **`client/README.md`** — **End-to-end quest reward loop (NEO-132)** section with flow table and cross-links. -- **`client/scripts/quest_hud_controller.gd`** — **`quest_completion_reward_transition`** signal on in-session completion with summary. -- **`client/scripts/main.gd`** — connects signal to inventory + skill progression GET refresh (NEO-131 deferred economy HUD gap). +- **`client/scripts/quest_hud_controller.gd`** — **`quest_completion_reward_transition`** signal + **`_refresh_economy_hud()`** on in-session completion (inventory + skill GET). +- **`client/scripts/main.gd`** — passes **`InventoryClient`** + **`SkillProgressionClient`** into quest HUD **`setup`**. - **`docs/manual-qa/NEO-123.md`**, **`docs/manual-qa/NEO-131.md`** — cross-links to NEO-132 capstone. - **`docs/plans/E7M2-prototype-backlog.md`**, **`E7_M2_RewardAndUnlockRouter.md`**, **`documentation_and_implementation_alignment.md`**, **`module_dependency_register.md`** — E7M2-09 landed; E7.M2 **Ready**; Epic 7 Slice 2 client capstone complete.