diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index b12ef46..329d93f 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -286,7 +286,7 @@ const UI_OLD_SILK := Color(0.48, 0.44, 0.36, 0.997) const UI_BURNT_PAPER := Color(0.22, 0.18, 0.13, 0.996) const UI_BRUSH_RULE := Color(0.065, 0.060, 0.052, 0.98) const UI_TEXT_DARK_OUTLINE := Color(0.018, 0.012, 0.007, 0.88) -const UI_TEXT_LIGHT_OUTLINE := Color(0.96, 0.88, 0.66, 0.56) +const UI_TEXT_LIGHT_OUTLINE := Color(0.98, 0.92, 0.76, 0.76) const UI_TEXT_SHADOW := Color(0.010, 0.007, 0.004, 0.66) const UI_TITLE_TEXT := Color(1.0, 0.96, 0.82, 1.0) const UI_TITLE_MUTED_TEXT := Color(0.92, 0.84, 0.66, 1.0) @@ -881,7 +881,8 @@ func _panel_variant_prefers_flat_text_surface(variant: String) -> bool: "cinematic_caption", "story_caption", "cinematic_text_plate", - "briefing_text_plate" + "briefing_text_plate", + "opening_ledger" ] @@ -1075,6 +1076,13 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void: radius = 8 margin = 14 shadow_size = 10 + "opening_ledger": + fill = Color(0.010, 0.010, 0.009, 0.994) + border = Color(0.45, 0.37, 0.23, 0.96) + border_width = 2 + radius = 8 + margin = 8 + shadow_size = 10 "briefing_text_plate": fill = Color(0.80, 0.79, 0.71, 0.999) border = Color(0.19, 0.16, 0.11, 1.0) @@ -1342,7 +1350,7 @@ func _color_luminance(color: Color) -> float: func _text_outline_color_for(font_color: Color) -> Color: if _color_luminance(font_color) >= 0.42: return UI_TEXT_DARK_OUTLINE - return Color(UI_TEXT_LIGHT_OUTLINE.r, UI_TEXT_LIGHT_OUTLINE.g, UI_TEXT_LIGHT_OUTLINE.b, 0.42) + return UI_TEXT_LIGHT_OUTLINE func _apply_text_legibility(control: Control, font_color: Color, font_size: int = 0) -> void: @@ -1983,6 +1991,9 @@ func _command_hint_panel_position(button: Button) -> Vector2: var x := button_rect.position.x if button_rect.get_center().x > viewport_size.x * 0.5: x = button_rect.end.x - panel_size.x + var parent := button.get_parent() + if parent != null and parent.name == "TopToolBar": + x = minf(x, SIDE_PANEL_POSITION.x - panel_size.x - gap) var y := button_rect.end.y + gap if y + panel_size.y > viewport_size.y - 10.0: y = button_rect.position.y - panel_size.y - gap @@ -2981,13 +2992,13 @@ func _make_opening_story_beat_strip() -> PanelContainer: panel.custom_minimum_size = OPENING_STORY_PROGRESS_LEDGER_SIZE panel.modulate = Color(1.0, 1.0, 1.0, 0.96) panel.tooltip_text = "제1장 서문에서 지금 펼쳐진 기록입니다." - _apply_panel_style(panel, "transparent_overlay") + _apply_panel_style(panel, "opening_ledger") var panel_style := panel.get_theme_stylebox("panel") if panel_style != null: - panel_style.content_margin_left = 0 - panel_style.content_margin_right = 0 - panel_style.content_margin_top = 0 - panel_style.content_margin_bottom = 0 + panel_style.content_margin_left = 7 + panel_style.content_margin_right = 7 + panel_style.content_margin_top = 4 + panel_style.content_margin_bottom = 4 var rail_row := HBoxContainer.new() rail_row.name = "OpeningStoryProgressRail" @@ -4170,7 +4181,7 @@ func _create_hud() -> void: var opening_top_dim := ColorRect.new() opening_top_dim.name = "OpeningTopInkShade" - opening_top_dim.color = Color(0.020, 0.017, 0.014, 0.13) + opening_top_dim.color = Color(0.020, 0.017, 0.014, 0.40) opening_top_dim.mouse_filter = Control.MOUSE_FILTER_IGNORE opening_top_dim.position = Vector2(0, 0) opening_top_dim.size = Vector2(1280, 92) @@ -4361,7 +4372,7 @@ func _create_hud() -> void: var briefing_objective_header := HBoxContainer.new() briefing_objective_header.add_theme_constant_override("separation", 8) briefing_column.add_child(briefing_objective_header) - briefing_objective_header.add_child(_make_section_caption("승리와 패배", 548)) + briefing_objective_header.add_child(_make_section_caption("목표", 548)) briefing_objective_toggle_button = Button.new() briefing_objective_toggle_button.text = "" @@ -4391,7 +4402,7 @@ func _create_hud() -> void: _apply_label_style(briefing_objective_label, UI_AGED_INK, BRIEFING_OBJECTIVE_FONT_SIZE) briefing_objective_row.add_child(briefing_objective_label) - briefing_column.add_child(_make_section_caption("전장도와 군막 보고", 680)) + briefing_column.add_child(_make_section_caption("전장 보고", 680)) briefing_camp_overview_panel = PanelContainer.new() briefing_camp_overview_panel.custom_minimum_size = Vector2(680, 114) @@ -10152,8 +10163,8 @@ func _update_hud() -> void: var hovered_unit := _hovered_unit_for_hud_portrait() _update_hud_unit_portrait(hovered_unit) if hovered_unit.is_empty(): - selected_label.text = "전장 관망" - selected_label.tooltip_text = "마우스를 부대 위에 올리면 상세 군세가 표시됩니다." + selected_label.text = _format_empty_hud_focus_summary_text() + selected_label.tooltip_text = _format_empty_hud_focus_detail_text() _update_hud_unit_badges({}, selected_label.tooltip_text) _update_hud_unit_resource_bars({}, selected_label.tooltip_text) else: @@ -10389,6 +10400,18 @@ func _format_unit_focus_summary_text(unit: Dictionary, prefix: String) -> String ] +func _format_empty_hud_focus_summary_text() -> String: + if battle_started and state.battle_status == BattleState.STATUS_ACTIVE: + return "장수 선택 대기\n아군을 눌러 행군을 시작합니다." + return "전투 준비 대기\n군막 명령을 선택합니다." + + +func _format_empty_hud_focus_detail_text() -> String: + if battle_started and state.battle_status == BattleState.STATUS_ACTIVE: + return "아군 장수를 선택하면 병력, 기력, 이동 범위와 명령 아이콘이 표시됩니다.\n상단 군세 아이콘으로 아군과 적군 목록을 볼 수 있습니다." + return "전투 시작 전에는 군막에서 장수, 물자, 진형을 정비합니다." + + func _update_hud_unit_resource_bars(unit: Dictionary, detail_text: String) -> void: if unit.is_empty(): _set_hud_unit_resource_bar(hud_unit_hp_bar, 0, 1, "병력", "", false) @@ -17239,6 +17262,8 @@ func _format_progression_events(events) -> String: func _format_inventory_status_text() -> String: + if _should_show_selection_inventory_hint(): + return "장수 선택 대기\n아군 장수를 선택하면 병력, 기력, 병장, 도구가 표시됩니다.\n상단 군세 아이콘으로 아군과 적군 목록을 확인할 수 있습니다." var inventory := state.get_inventory_snapshot() if campaign_complete_screen or state.battle_status != BattleState.STATUS_ACTIVE: inventory = campaign_state.get_inventory_snapshot() @@ -17246,6 +17271,8 @@ func _format_inventory_status_text() -> String: func _format_inventory_status_summary_text() -> String: + if _should_show_selection_inventory_hint(): + return "장수 선택 후 군수 확인" var inventory := state.get_inventory_snapshot() if campaign_complete_screen or state.battle_status != BattleState.STATUS_ACTIVE: inventory = campaign_state.get_inventory_snapshot() @@ -17277,6 +17304,14 @@ func _format_inventory_status_summary_text() -> String: return _join_strings(parts, " · ") +func _should_show_selection_inventory_hint() -> bool: + return ( + battle_started + and state.battle_status == BattleState.STATUS_ACTIVE + and state.get_selected_unit().is_empty() + ) + + func _format_reward_items(items: Array) -> String: var counts := {} for item_id in items: diff --git a/tools/capture_readability_frames.gd b/tools/capture_readability_frames.gd index bf2d217..6df7336 100644 --- a/tools/capture_readability_frames.gd +++ b/tools/capture_readability_frames.gd @@ -96,10 +96,14 @@ func _capture_viewport_set(spec: Dictionary, output_dir: String, captures: Array await _settle() if scene.objective_notice_panel != null: scene.objective_notice_panel.visible = false + scene._update_hud() + await _settle() + await _capture_frame(scene, output_dir, label, "06_battle_idle", captures, failures) + scene._update_threat_button() scene._on_command_hint_button_mouse_entered(scene.threat_button) await _settle() - await _capture_frame(scene, output_dir, label, "06_battle_threat_hint", captures, failures) + await _capture_frame(scene, output_dir, label, "07_battle_threat_hint", captures, failures) scene.queue_free() await _settle() diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 1b53840..57926f5 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3549,6 +3549,20 @@ func _check_hud_focus_text(failures: Array[String]) -> void: failures.append("inventory HUD summary should stay compact and leave details to tooltip: %s" % ("" if scene.inventory_label == null else scene.inventory_label.text)) elif not scene.inventory_label.tooltip_text.contains("군수:"): failures.append("inventory HUD tooltip should preserve full inventory detail: %s" % scene.inventory_label.tooltip_text) + scene.state.selected_unit_id = "" + scene.hover_cell = Vector2i(-1, -1) + scene._update_hud() + if scene.selected_label == null or not scene.selected_label.text.contains("장수 선택 대기"): + failures.append("empty battle HUD should give a readable unit-selection prompt: %s" % ("" if scene.selected_label == null else scene.selected_label.text)) + if scene.selected_label != null and not scene.selected_label.tooltip_text.contains("군세"): + failures.append("empty battle HUD tooltip should point players to the unit list: %s" % scene.selected_label.tooltip_text) + if scene.inventory_label == null or scene.inventory_label.text.contains("군수 없음") or not scene.inventory_label.text.contains("장수 선택"): + failures.append("empty battle inventory line should not show noisy stock text: %s" % ("" if scene.inventory_label == null else scene.inventory_label.text)) + if scene.inventory_label != null and (not scene.inventory_label.tooltip_text.contains("장수 선택") or not scene.inventory_label.tooltip_text.contains("도구")): + failures.append("empty battle inventory tooltip should explain where readable item detail appears: %s" % scene.inventory_label.tooltip_text) + scene.state.selected_unit_id = "cao_cao" + scene.hover_cell = Vector2i(1, 6) + scene._update_hud() if scene.hud_unit_badge_row == null or not scene.hud_unit_badge_row.visible: failures.append("selected unit HUD should expose compact hover badges") else: @@ -4099,6 +4113,8 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: if opening_ledger == null: failures.append("opening prologue should use a story ledger instead of numbered progress cards") else: + if str(opening_ledger.get_meta("panel_style_variant", "")) != "opening_ledger" or bool(opening_ledger.get_meta("generated_panel_texture", true)): + failures.append("opening story ledger should use a flat dark backing so chapter text stays readable over sky art") if opening_ledger.position.x < 300.0 or opening_ledger.position.x > 680.0: failures.append("opening story ledger should sit beside the chapter seal instead of the upper-right corner: %s" % str(opening_ledger.position)) if opening_ledger.custom_minimum_size.x > 340.0 or opening_ledger.custom_minimum_size.y > 54.0: @@ -4849,6 +4865,9 @@ func _check_hud_command_grid_layout(failures: Array[String]) -> void: var threat_panel_tooltip: String = "" if scene.command_hint_panel == null else scene.command_hint_panel.tooltip_text if not threat_panel_tooltip.contains("초병 유인 반경") or not threat_panel_tooltip.contains("거점 경계권"): failures.append("threat command hint should preserve the full detailed tooltip: %s" % threat_panel_tooltip) + var hint_rect := Rect2(scene.command_hint_panel.position, BattleSceneScript.COMMAND_HINT_PANEL_SIZE) + if hint_rect.end.x > BattleSceneScript.SIDE_PANEL_POSITION.x - 2.0: + failures.append("top command hint should not cover the right readable battle panel: %s" % str(hint_rect)) scene.free()