From e783fa5441f68ca2f53d0764df8c1f1d21d948ab Mon Sep 17 00:00:00 2001 From: VinPropane Date: Sun, 28 Jun 2026 17:58:04 -0400 Subject: [PATCH] NEO-153: refresh faction standing on completion and show issue busy copy. Match quest HUD completion refresh and accept-busy feedback for duplicate Shift+C presses. --- client/scripts/contract_hud_controller.gd | 11 ++++++ client/test/contract_hud_controller_test.gd | 41 +++++++++++++++++++++ 2 files changed, 52 insertions(+) diff --git a/client/scripts/contract_hud_controller.gd b/client/scripts/contract_hud_controller.gd index 21e87d0..dc30b11 100644 --- a/client/scripts/contract_hud_controller.gd +++ b/client/scripts/contract_hud_controller.gd @@ -9,6 +9,7 @@ const PROTOTYPE_CONTRACT_TEMPLATE_ID := "prototype_contract_clear_combat_pocket" const PROTOTYPE_CONTRACT_SEED_BUCKET := "prototype_contract_dev_seed" const ISSUE_IDLE_HINT := "Contract issue: — (Shift+C prototype)" const ISSUE_SENDING_HINT := "Contract issue: sending…" +const ISSUE_BUSY_HINT := "Contract issue: busy — try again" const REWARD_HEADER := "Contract rewards:" const ISSUE_DENY_COPY := { "active_contract_exists": "Contract issue: denied — active contract already issued", @@ -101,6 +102,7 @@ func try_issue_key_input(event: InputEvent) -> bool: ): return false if bool(_contract_client.call("is_issue_busy")): + _render_issue_feedback_label(ISSUE_BUSY_HINT) return true _render_issue_feedback_label(ISSUE_SENDING_HINT) var queued: bool = bool( @@ -204,6 +206,7 @@ func _detect_and_apply_completion_transitions(snapshot: Dictionary) -> void: _last_reward_instance_id = iid _last_reward_summary = summary.duplicate(true) _refresh_economy_hud() + _refresh_faction_standing() _previous_status_by_instance[iid] = new_status @@ -235,6 +238,14 @@ func _refresh_economy_hud() -> void: _skill_progression_client.call("request_sync_from_server") +func _refresh_faction_standing() -> void: + if ( + is_instance_valid(_faction_standing_client) + and _faction_standing_client.has_method("request_sync_from_server") + ): + _faction_standing_client.call("request_sync_from_server") + + func _render_active_label() -> void: if not is_instance_valid(_active_label): return diff --git a/client/test/contract_hud_controller_test.gd b/client/test/contract_hud_controller_test.gd index a28e043..34d1b75 100644 --- a/client/test/contract_hud_controller_test.gd +++ b/client/test/contract_hud_controller_test.gd @@ -62,6 +62,14 @@ class PendingIssueHttpTransport: return OK +class TrackingFactionClient: + extends Node + var sync_count: int = 0 + + func request_sync_from_server() -> void: + sync_count += 1 + + static func _active_snapshot() -> Dictionary: return { "schemaVersion": 1, @@ -169,6 +177,23 @@ func test_try_issue_key_input_handles_shift_c() -> void: assert_str(built["issue_label"].text).contains("sending") +func test_try_issue_key_input_shows_busy_while_issue_in_flight() -> void: + # Arrange + var built: Dictionary = await _build_controller( + NoopHttpTransport.new(), PendingIssueHttpTransport.new() + ) + var event := InputEventKey.new() + event.pressed = true + event.shift_pressed = true + event.keycode = KEY_C + built["controller"].call("try_issue_key_input", event) + # Act + var handled: bool = bool(built["controller"].call("try_issue_key_input", event)) + # Assert + assert_bool(handled).is_true() + assert_str(built["issue_label"].text).contains("busy") + + func test_active_label_shows_encounter_objective_id() -> void: # Arrange var built: Dictionary = await _build_controller( @@ -220,6 +245,22 @@ func test_reward_label_populates_on_active_to_completed_transition() -> void: assert_str(built["reward_label"].text).contains("Salvage +15 XP") +func test_completion_transition_refreshes_faction_standing() -> void: + # Arrange + var built: Dictionary = await _build_controller( + NoopHttpTransport.new(), NoopHttpTransport.new() + ) + var faction_client := TrackingFactionClient.new() + auto_free(faction_client) + add_child(faction_client) + built["controller"].set("_faction_standing_client", faction_client) + built["controller"].call("_on_contracts_received", _active_snapshot()) + # Act + built["controller"].call("_on_contracts_received", _completed_snapshot()) + # Assert + assert_int(faction_client.sync_count).is_equal(1) + + func test_reward_label_populates_when_first_get_is_already_completed() -> void: # Arrange — issue marks session active; encounter completes before GET returns active var built: Dictionary = await _build_controller(