diff --git a/src/game/engine/scenes/hud/turn_notification.gd b/src/game/engine/scenes/hud/turn_notification.gd index d6825095..0d80dfcf 100644 --- a/src/game/engine/scenes/hud/turn_notification.gd +++ b/src/game/engine/scenes/hud/turn_notification.gd @@ -16,13 +16,13 @@ var _category_colors: Dictionary = {} ## is a meta-bucket that flips every other filter in lockstep. const FILTER_BUCKETS: Dictionary = { "all": [], - "military": ["combat"], + "combat": ["combat"], "research": ["tech"], "city": ["founding", "economy"], "diplomacy": ["event", "magic"], } const FILTER_ORDER: Array[String] = [ - "all", "military", "research", "city", "diplomacy", + "all", "combat", "research", "city", "diplomacy", ] ## Sentinel for "no hex position available" returned by `_extract_hex_pos`. ## Entities lacking a Vector2i position map to this value; callers guard via @@ -40,7 +40,7 @@ var _entries: Array[Dictionary] = [] var _is_processing: bool = false ## Per-bucket visibility. Default: everything shown. var _filter_state: Dictionary = { - "military": true, "research": true, "city": true, "diplomacy": true, + "combat": true, "research": true, "city": true, "diplomacy": true, } var _filter_checks: Dictionary = {} @@ -549,7 +549,7 @@ func _on_unit_destroyed(unit: Variant, killer: Variant) -> void: ## EventBus.combat_resolved(attacker: Variant, defender: Variant, result: Dictionary) -func _on_combat_resolved(attacker: Variant, defender: Variant, _result: Dictionary) -> void: +func _on_combat_resolved(attacker: Variant, defender: Variant, result: Dictionary) -> void: var human_idx: int = _human_player_index() if human_idx < 0: return @@ -559,13 +559,25 @@ func _on_combat_resolved(attacker: Variant, defender: Variant, _result: Dictiona return var att_name: String = _extract_display_name(attacker, "unit") var def_name: String = _extract_display_name(defender, "unit") + # p3-15: log the damage exchanged [attacker vs defender]. City defenders show + # HP loss (+ capture note); unit duels show damage dealt and taken. + var text: String + var city_dmg: int = int(result.get("city_damage", 0)) + if city_dmg > 0: + text = "%s besieges %s: -%d HP" % [att_name, def_name, city_dmg] + if bool(result.get("captured", false)): + text += " (captured)" + else: + var dealt: int = int(result.get("damage_dealt", 0)) + var taken: int = int(result.get("damage_taken", 0)) + text = "%s vs %s: dealt %d, took %d" % [att_name, def_name, dealt, taken] var hex: Vector2i = _extract_hex_pos(defender) if not _has_hex_pos(hex): hex = _extract_hex_pos(attacker) if _has_hex_pos(hex): - _add_entry_now("%s attacked %s" % [att_name, def_name], "combat", hex) + _add_entry_now(text, "combat", hex) else: - _add_entry_now("%s attacked %s" % [att_name, def_name], "combat") + _add_entry_now(text, "combat") ## EventBus.victory_achieved(player_index: int, victory_type: String) diff --git a/src/game/engine/scenes/tests/chronicle_filter_proof.gd b/src/game/engine/scenes/tests/chronicle_filter_proof.gd index 9f34216a..e17707af 100644 --- a/src/game/engine/scenes/tests/chronicle_filter_proof.gd +++ b/src/game/engine/scenes/tests/chronicle_filter_proof.gd @@ -22,7 +22,10 @@ func _ready() -> void: "Research complete: Metallurgy", "tech" ) panel._add_entry_now( - "Erebor has been captured", "combat", Vector2i(11, 7) + "Shield Bearer vs Berserker: dealt 12, took 8", "combat", Vector2i(11, 7) + ) + panel._add_entry_now( + "Catapult Crew besieges Erebor: -47 HP (captured)", "combat", Vector2i(11, 7) ) panel._add_entry_now( "Forge completed in Khazad-dûm", "economy" diff --git a/src/game/engine/tests/unit/test_chronicle_filter_and_click.gd b/src/game/engine/tests/unit/test_chronicle_filter_and_click.gd index d3a6156c..aeec67ae 100644 --- a/src/game/engine/tests/unit/test_chronicle_filter_and_click.gd +++ b/src/game/engine/tests/unit/test_chronicle_filter_and_click.gd @@ -32,7 +32,7 @@ func after_each() -> void: func test_filter_buckets_contain_required_categories() -> void: var buckets: Dictionary = TurnNotificationScript.FILTER_BUCKETS assert_true(buckets.has("all"), "filter must include 'all' meta-bucket") - assert_true(buckets.has("military"), "filter must include 'military'") + assert_true(buckets.has("combat"), "filter must include 'combat'") assert_true(buckets.has("research"), "filter must include 'research'") assert_true(buckets.has("city"), "filter must include 'city'") assert_true(buckets.has("diplomacy"), "filter must include 'diplomacy'") @@ -41,15 +41,15 @@ func test_filter_buckets_contain_required_categories() -> void: func test_filter_order_is_five_checkboxes() -> void: var order: Array[String] = TurnNotificationScript.FILTER_ORDER assert_eq(order.size(), 5, - "FILTER_ORDER must have 5 entries: all + military + research + city + diplomacy") + "FILTER_ORDER must have 5 entries: all + combat + research + city + diplomacy") assert_eq(order[0], "all", "'all' must be the first checkbox") -func test_combat_category_maps_to_military_bucket() -> void: +func test_combat_category_maps_to_combat_bucket() -> void: var buckets: Dictionary = TurnNotificationScript.FILTER_BUCKETS - var military: Array = buckets["military"] as Array - assert_true(military.has("combat"), - "combat-tagged entries belong in the military bucket") + var combat: Array = buckets["combat"] as Array + assert_true(combat.has("combat"), + "combat-tagged entries belong in the combat bucket") func test_tech_category_maps_to_research_bucket() -> void: @@ -65,11 +65,11 @@ func test_tech_category_maps_to_research_bucket() -> void: func test_entry_passes_filter_respects_filter_state() -> void: var panel: CanvasLayer = TurnNotificationScene.instantiate() add_child_autofree(panel) - panel._filter_state["military"] = false + panel._filter_state["combat"] = false var combat_entry: Dictionary = {"text": "kill", "category": "combat"} assert_false(panel._entry_passes_filter(combat_entry), - "combat entry hidden when military filter off") - panel._filter_state["military"] = true + "combat entry hidden when combat filter off") + panel._filter_state["combat"] = true assert_true(panel._entry_passes_filter(combat_entry), "combat entry shown when military filter on")