diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index a0bb94a..de55e20 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -195,8 +195,8 @@ const OPENING_STORY_PROGRESS_LEDGER_SIZE := Vector2(326, 48) const OPENING_STORY_PROGRESS_SEAL_SIZE := Vector2(34, 34) const COMMAND_NOTICE_ICON_SIZE := Vector2(32, 32) const COMMAND_NOTICE_MAX_ICONS := 5 -const COMBAT_RESULT_PANEL_POSITION := Vector2(838, 430) -const COMBAT_RESULT_PANEL_SIZE := Vector2(396, 128) +const COMBAT_RESULT_PANEL_POSITION := Vector2(838, 402) +const COMBAT_RESULT_PANEL_SIZE := Vector2(396, 164) const CHAPTER_SEAL_BADGE_SIZE := Vector2(54, 54) const CHAPTER_SCENARIO_THUMBNAIL_SIZE := Vector2(72, 50) const CHAPTER_INFO_BADGE_SIZE := Vector2(86, 22) @@ -489,6 +489,8 @@ var objective_notice_label: Label var combat_result_panel: PanelContainer var combat_result_title_label: Label var combat_result_primary_label: Label +var combat_result_growth_label: Label +var combat_result_merit_bar: ProgressBar var combat_result_detail_label: Label var ui_root_control: Control var screen_backdrop: ColorRect @@ -3753,8 +3755,26 @@ func _create_hud() -> void: _apply_label_style(combat_result_primary_label, UI_RICE_PAPER_LIGHT, 15) combat_result_column.add_child(combat_result_primary_label) + combat_result_growth_label = Label.new() + combat_result_growth_label.name = "CombatResultGrowth" + combat_result_growth_label.custom_minimum_size = Vector2(360, 20) + combat_result_growth_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER + combat_result_growth_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT + _apply_label_style(combat_result_growth_label, UI_PARCHMENT_TEXT, 13) + combat_result_column.add_child(combat_result_growth_label) + + combat_result_merit_bar = ProgressBar.new() + combat_result_merit_bar.name = "CombatResultMeritBar" + combat_result_merit_bar.custom_minimum_size = Vector2(360, 14) + combat_result_merit_bar.min_value = 0.0 + combat_result_merit_bar.max_value = BattleState.EXP_LEVEL + combat_result_merit_bar.show_percentage = false + combat_result_merit_bar.add_theme_stylebox_override("background", _make_panel_style(Color(0.050, 0.029, 0.018, 0.96), UI_LACQUER_EDGE, 1, 0, 0, 0)) + combat_result_merit_bar.add_theme_stylebox_override("fill", _make_panel_style(Color(0.72, 0.50, 0.16, 1.0), Color(0.92, 0.80, 0.42, 1.0), 1, 0, 0, 0)) + combat_result_column.add_child(combat_result_merit_bar) + combat_result_detail_label = Label.new() - combat_result_detail_label.custom_minimum_size = Vector2(360, 44) + combat_result_detail_label.custom_minimum_size = Vector2(360, 38) combat_result_detail_label.vertical_alignment = VERTICAL_ALIGNMENT_TOP combat_result_detail_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART _apply_label_style(combat_result_detail_label, UI_PARCHMENT_TEXT, 13) @@ -13033,6 +13053,7 @@ func _show_combat_result_report(report: Dictionary) -> void: combat_result_detail_label.text = _join_strings(lines.slice(1), "\n") combat_result_detail_label.tooltip_text = combat_result_primary_label.tooltip_text combat_result_panel.tooltip_text = combat_result_primary_label.tooltip_text + _update_combat_result_growth_widgets(report, combat_result_primary_label.tooltip_text) combat_result_panel.visible = true combat_result_notice_timer = COMBAT_RESULT_NOTICE_DURATION combat_result_panel.move_to_front() @@ -13068,6 +13089,58 @@ func _format_combat_result_report_lines(report: Dictionary) -> Array[String]: return lines +func _update_combat_result_growth_widgets(report: Dictionary, tooltip_text: String) -> void: + var progress := _primary_combat_progress(report) + var amount := int(progress.get("amount", 0)) + var has_progress := amount > 0 + if combat_result_growth_label != null: + combat_result_growth_label.visible = has_progress + combat_result_growth_label.text = _format_combat_growth_summary(progress) if has_progress else "" + combat_result_growth_label.tooltip_text = tooltip_text + if combat_result_merit_bar != null: + combat_result_merit_bar.visible = has_progress + combat_result_merit_bar.max_value = BattleState.EXP_LEVEL + combat_result_merit_bar.value = clampi(int(progress.get("exp_after", 0)), 0, BattleState.EXP_LEVEL) if has_progress else 0 + combat_result_merit_bar.tooltip_text = tooltip_text + + +func _primary_combat_progress(report: Dictionary) -> Dictionary: + var attacker_progress := _as_dictionary(report.get("attacker_progress", {})) + var counter_progress := _as_dictionary(report.get("counter_progress", {})) + if int(attacker_progress.get("amount", 0)) > 0: + return attacker_progress + if int(counter_progress.get("amount", 0)) > 0: + return counter_progress + return {} + + +func _as_dictionary(value) -> Dictionary: + if typeof(value) == TYPE_DICTIONARY: + return (value as Dictionary).duplicate(true) + return {} + + +func _format_combat_growth_summary(progress: Dictionary) -> String: + if progress.is_empty(): + return "" + var name := str(progress.get("name", "부대")).strip_edges() + if name.is_empty(): + name = "부대" + var amount := int(progress.get("amount", 0)) + var exp_after := clampi(int(progress.get("exp_after", 0)), 0, BattleState.EXP_LEVEL) + var next_needed := BattleState.EXP_LEVEL - exp_after + var level_before := int(progress.get("level_before", 0)) + var level_after := int(progress.get("level_after", level_before)) + var parts: Array[String] = ["%s 전공 +%d" % [name, amount]] + 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() + parts.append("%s 승급" % class_after if not class_after.is_empty() else "승급") + parts.append("다음 등급까지 %d" % next_needed) + return _join_strings(parts, " · ") + + func _format_combat_result_entry(entry: Dictionary) -> String: var attacker_name := str(entry.get("attacker_name", "부대")) var target_name := str(entry.get("target_name", "적")) diff --git a/tools/smoke_post_move_action_flow.gd b/tools/smoke_post_move_action_flow.gd index 3480d8e..a8dfa4e 100644 --- a/tools/smoke_post_move_action_flow.gd +++ b/tools/smoke_post_move_action_flow.gd @@ -644,6 +644,10 @@ func _check_scene_post_move_attack_targeting(failures: Array[String]) -> void: 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("전공 +") or not result_text.contains("누적"): failures.append("combat result report should summarize damage, merit, and current progress: %s" % result_text) + if scene.combat_result_growth_label == null or not scene.combat_result_growth_label.visible or not scene.combat_result_growth_label.text.contains("전공 +"): + failures.append("combat result report should surface visible merit gain outside dense detail text: %s" % ("" if scene.combat_result_growth_label == null else scene.combat_result_growth_label.text)) + if scene.combat_result_merit_bar == null or not scene.combat_result_merit_bar.visible or scene.combat_result_merit_bar.value <= 0.0: + failures.append("combat result report should show a visible merit progress bar") scene.free() diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 99bf936..ca5b853 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3999,6 +3999,14 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: 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") 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]) + if scene.combat_result_growth_label == null or not scene.combat_result_growth_label.visible: + failures.append("combat result report should expose a visible growth summary") + elif not scene.combat_result_growth_label.text.contains("조조 전공 +10") or not scene.combat_result_growth_label.text.contains("다음 등급까지 56"): + failures.append("combat result growth summary should make merit gain and next level clear: %s" % scene.combat_result_growth_label.text) + if scene.combat_result_merit_bar == null or not scene.combat_result_merit_bar.visible: + failures.append("combat result report should expose a visible merit progress bar") + elif int(scene.combat_result_merit_bar.value) != 44 or int(scene.combat_result_merit_bar.max_value) != BattleStateScript.EXP_LEVEL: + failures.append("combat result merit bar should show current progress 44/100: %s/%s" % [str(scene.combat_result_merit_bar.value), str(scene.combat_result_merit_bar.max_value)]) scene.combat_result_panel.visible = false _check_panel_uses_flat_readable_backing(failures, scene.title_load_panel, "title load readable panel") _check_panel_uses_flat_readable_backing(failures, scene.title_settings_panel, "title settings readable panel")