From bba9044fd65bf3610a517fc7d1284e937ef04123 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sun, 21 Jun 2026 03:11:03 +0900 Subject: [PATCH] Show combat merit progress in reports --- scripts/core/battle_state.gd | 42 ++++++++++++++++++++++------ scripts/scenes/battle_scene.gd | 40 +++++++++++++++++++++++--- tools/smoke_post_move_action_flow.gd | 4 +-- tools/smoke_visual_assets.gd | 12 +++++++- 4 files changed, 83 insertions(+), 15 deletions(-) diff --git a/scripts/core/battle_state.gd b/scripts/core/battle_state.gd index 3fd1c5b..4475622 100644 --- a/scripts/core/battle_state.gd +++ b/scripts/core/battle_state.gd @@ -3759,8 +3759,9 @@ func _resolve_combat(attacker: Dictionary, target: Dictionary) -> void: var attack_exp := EXP_ATTACK if bool(attack_result.get("hit", false)) else EXP_MISS if bool(attack_result.get("defeated", false)) or battle_status != STATUS_ACTIVE: var final_attack_exp := attack_exp + (EXP_DEFEAT_BONUS if bool(attack_result.get("defeated", false)) else 0) - _grant_experience(attacker, final_attack_exp) - report["attacker_merit"] = final_attack_exp + var final_attack_progress := _grant_experience(attacker, final_attack_exp) + report["attacker_merit"] = int(final_attack_progress.get("amount", 0)) + report["attacker_progress"] = final_attack_progress combat_result_reported.emit(report) return @@ -3770,10 +3771,12 @@ func _resolve_combat(attacker: Dictionary, target: Dictionary) -> void: (report["entries"] as Array).append(counter_result) counter_exp = (EXP_COUNTER if bool(counter_result.get("hit", false)) else EXP_MISS) + (EXP_DEFEAT_BONUS if bool(counter_result.get("defeated", false)) else 0) - _grant_experience(attacker, attack_exp) - report["attacker_merit"] = attack_exp - _grant_experience(target, counter_exp) - report["counter_merit"] = counter_exp + var attack_progress := _grant_experience(attacker, attack_exp) + report["attacker_merit"] = int(attack_progress.get("amount", 0)) + report["attacker_progress"] = attack_progress + var counter_progress := _grant_experience(target, counter_exp) + report["counter_merit"] = int(counter_progress.get("amount", 0)) + report["counter_progress"] = counter_progress combat_result_reported.emit(report) @@ -3785,6 +3788,8 @@ func _make_combat_result_report(attacker: Dictionary, target: Dictionary) -> Dic "target_name": str(target.get("name", "")), "attacker_merit": 0, "counter_merit": 0, + "attacker_progress": {}, + "counter_progress": {}, "entries": [] } @@ -3920,15 +3925,36 @@ func _resolve_skill_heal(caster: Dictionary, target: Dictionary, skill: Dictiona return healed -func _grant_experience(unit: Dictionary, amount: int) -> void: +func _grant_experience(unit: Dictionary, amount: int) -> Dictionary: + var progress := { + "unit_id": str(unit.get("id", "")), + "name": str(unit.get("name", "부대")), + "amount": 0, + "exp_before": int(unit.get("exp", 0)), + "exp_after": int(unit.get("exp", 0)), + "level_before": int(unit.get("level", 1)), + "level_after": int(unit.get("level", 1)), + "class_before": str(unit.get("class", "")), + "class_after": str(unit.get("class", "")), + "leveled": false, + "promoted": false + } if unit.is_empty() or not unit.get("alive", false) or amount <= 0: - return + return progress + var class_id_before := str(unit.get("class_id", "")) unit["exp"] = int(unit.get("exp", 0)) + amount + progress["amount"] = amount _emit_log("%s 공적 +%d." % [unit["name"], amount]) _emit_combat_feedback(unit, "공적 +%d" % amount, "progress") while int(unit["exp"]) >= EXP_LEVEL: unit["exp"] = int(unit["exp"]) - EXP_LEVEL _level_up(unit) + progress["exp_after"] = int(unit.get("exp", 0)) + progress["level_after"] = int(unit.get("level", progress["level_before"])) + progress["class_after"] = str(unit.get("class", progress["class_before"])) + progress["leveled"] = int(progress["level_after"]) > int(progress["level_before"]) + progress["promoted"] = str(unit.get("class_id", "")) != class_id_before + return progress func _level_up(unit: Dictionary) -> void: diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 6b14ea6..7280a9e 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -12952,11 +12952,15 @@ func _format_combat_result_report_lines(report: Dictionary) -> Array[String]: var line := _format_combat_result_entry(entry) if not line.is_empty(): lines.append(line) - if int(report.get("attacker_merit", 0)) > 0: - lines.append("%s 공적 +%d" % [str(report.get("attacker_name", "아군")), int(report.get("attacker_merit", 0))]) - if int(report.get("counter_merit", 0)) > 0: + var attacker_progress_text := _format_combat_progress_line(report.get("attacker_progress", {}), str(report.get("attacker_name", "아군")), int(report.get("attacker_merit", 0))) + if not attacker_progress_text.is_empty(): + lines.append(attacker_progress_text) + var counter_progress = report.get("counter_progress", {}) + if int(report.get("counter_merit", 0)) > 0 or (typeof(counter_progress) == TYPE_DICTIONARY and int((counter_progress as Dictionary).get("amount", 0)) > 0): var counter_name := _combat_result_counter_name(report) - lines.append("%s 공적 +%d" % [counter_name, int(report.get("counter_merit", 0))]) + var counter_progress_text := _format_combat_progress_line(counter_progress, counter_name, int(report.get("counter_merit", 0))) + if not counter_progress_text.is_empty(): + lines.append(counter_progress_text) return lines @@ -13002,6 +13006,34 @@ func _combat_result_counter_name(report: Dictionary) -> String: return str(report.get("target_name", "반격 부대")) +func _format_combat_progress_line(progress_value, fallback_name: String, fallback_amount: int) -> String: + var progress := {} + if typeof(progress_value) == TYPE_DICTIONARY: + progress = (progress_value as Dictionary).duplicate(true) + var amount := int(progress.get("amount", fallback_amount)) + if amount <= 0: + return "" + var name := str(progress.get("name", fallback_name)).strip_edges() + if name.is_empty(): + name = fallback_name + var exp_before := int(progress.get("exp_before", -1)) + var exp_after := int(progress.get("exp_after", -1)) + var parts: Array[String] = ["%s 전공 +%d" % [name, amount]] + if exp_before >= 0 and exp_after >= 0: + parts.append("누적 %d→%d/%d" % [exp_before, exp_after, BattleState.EXP_LEVEL]) + var level_before := int(progress.get("level_before", 0)) + var level_after := int(progress.get("level_after", level_before)) + if level_after > level_before: + parts.append("품계 %d 도달" % level_after) + if bool(progress.get("promoted", false)): + var class_after := str(progress.get("class_after", "")).strip_edges() + if class_after.is_empty(): + parts.append("승급") + else: + parts.append("%s 승급" % class_after) + return _join_strings(parts, " · ") + + func _combat_feedback_sfx_key(text: String, kind: String) -> String: if kind == "reinforcement" or text == "증원": return "footstep" diff --git a/tools/smoke_post_move_action_flow.gd b/tools/smoke_post_move_action_flow.gd index 205f310..0afc1c7 100644 --- a/tools/smoke_post_move_action_flow.gd +++ b/tools/smoke_post_move_action_flow.gd @@ -642,8 +642,8 @@ func _check_scene_post_move_attack_targeting(failures: Array[String]) -> void: failures.append("attack click should show a compact combat result report") else: var result_text := "%s\n%s" % [scene.combat_result_primary_label.text, scene.combat_result_detail_label.text] - if not result_text.contains("공격") or not result_text.contains("피해") or not result_text.contains("공적 +"): - failures.append("combat result report should summarize damage and merit: %s" % result_text) + if not result_text.contains("공격") or not result_text.contains("피해") or not result_text.contains("전공 +") or not result_text.contains("누적"): + failures.append("combat result report should summarize damage, merit, and current progress: %s" % result_text) scene.free() diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 2b28122..645239d 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3900,6 +3900,16 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: "target_name": "황건병", "attacker_merit": 10, "counter_merit": 0, + "attacker_progress": { + "name": "조조", + "amount": 10, + "exp_before": 34, + "exp_after": 44, + "level_before": 1, + "level_after": 1, + "class_before": "지휘관", + "class_after": "지휘관" + }, "entries": [{ "attacker_name": "조조", "target_name": "황건병", @@ -3917,7 +3927,7 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: }) if scene.combat_result_panel == null or not scene.combat_result_panel.visible: failures.append("combat result report should appear after a battle result signal") - elif not scene.combat_result_primary_label.text.contains("피해") or not scene.combat_result_detail_label.text.contains("공적 +10"): + elif not scene.combat_result_primary_label.text.contains("피해") or not scene.combat_result_detail_label.text.contains("전공 +10") or not scene.combat_result_detail_label.text.contains("누적 34→44/100"): failures.append("combat result report should show damage and merit in readable Korean: %s / %s" % [scene.combat_result_primary_label.text, scene.combat_result_detail_label.text]) scene.combat_result_panel.visible = false _check_panel_uses_flat_readable_backing(failures, scene.title_load_panel, "title load readable panel")