diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 83892e1..dffaebf 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -55,10 +55,10 @@ const UNIT_ARROW_ATTACK_DURATION := 0.36 const UNIT_COMMAND_ATTACK_DURATION := 0.38 const UNIT_HEAVY_ATTACK_DURATION := 0.34 const UNIT_TACTIC_CAST_DURATION := 0.42 -const DIALOGUE_PANEL_POSITION := Vector2(72, 396) -const DIALOGUE_PANEL_SIZE := Vector2(1100, 260) -const DIALOGUE_PORTRAIT_SIZE := Vector2(184, 224) -const DIALOGUE_COLUMN_SIZE := Vector2(850, 226) +const DIALOGUE_PANEL_POSITION := Vector2(72, 356) +const DIALOGUE_PANEL_SIZE := Vector2(1100, 304) +const DIALOGUE_PORTRAIT_SIZE := Vector2(184, 262) +const DIALOGUE_COLUMN_SIZE := Vector2(850, 266) const DIALOGUE_TEXT_SIZE := Vector2(830, 112) const BRIEFING_CAMP_THUMBNAIL_SIZE := Vector2(138, 72) const CAMP_TALK_PORTRAIT_SIZE := Vector2(58, 58) @@ -341,9 +341,12 @@ func _age_panel_style(style: StyleBoxFlat, variant: String, border_width: int) - style.set_border_width(SIDE_RIGHT, border_width + 1) style.content_margin_top += 2 style.content_margin_bottom += 2 - "edict", "speech_scroll", "bamboo_slips": + "edict", "notice_edict", "speech_scroll", "bamboo_slips": style.set_border_width(SIDE_TOP, border_width + 2) style.set_border_width(SIDE_BOTTOM, border_width + 2) + "edict_compact": + style.set_border_width(SIDE_TOP, border_width + 1) + style.set_border_width(SIDE_BOTTOM, border_width + 1) "seal", "speaker_seal", "caption": style.set_border_width(SIDE_LEFT, border_width + 2) style.set_border_width(SIDE_RIGHT, border_width + 2) @@ -362,17 +365,29 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void: var shadow_size := 6 match variant: "paper": - fill = UI_SCROLL_CENTER - border = UI_CHARCOAL_WASH - border_width = 7 - margin = 20 - shadow_size = 16 + fill = Color(0.68, 0.50, 0.25, 0.997) + border = UI_BAMBOO_DARK + border_width = 9 + margin = 22 + shadow_size = 18 "edict": - fill = Color(0.55, 0.36, 0.15, 0.995) - border = UI_INK_WASH + fill = UI_BONE_PAPER + border = UI_BAMBOO_DARK + border_width = 6 + margin = 16 + shadow_size = 7 + "edict_compact": + fill = UI_BONE_PAPER + border = UI_BAMBOO_DARK + border_width = 3 + margin = 6 + shadow_size = 2 + "notice_edict": + fill = UI_BONE_PAPER + border = UI_BAMBOO_DARK border_width = 5 - margin = 15 - shadow_size = 8 + margin = 12 + shadow_size = 10 "seal": fill = Color(0.23, 0.018, 0.010, 1.0) border = UI_OLD_BRONZE @@ -389,16 +404,16 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void: border_width = 5 margin = 3 "dialogue": - fill = Color(0.036, 0.012, 0.006, 0.996) - border = UI_SEAL_RED_DARK - border_width = 7 - margin = 18 + fill = Color(0.070, 0.018, 0.010, 0.997) + border = UI_OLD_BRONZE + border_width = 8 + margin = 19 shadow_size = 18 "speech_scroll": - fill = Color(0.67, 0.50, 0.27, 0.996) - border = UI_INK_WASH - border_width = 5 - margin = 14 + fill = UI_BONE_PAPER + border = UI_BAMBOO_DARK + border_width = 6 + margin = 15 shadow_size = 4 "notice": fill = Color(0.070, 0.020, 0.014, 0.982) @@ -429,10 +444,10 @@ func _apply_panel_style(panel: PanelContainer, variant: String) -> void: margin = 5 shadow_size = 2 "result_tablet": - fill = Color(0.48, 0.34, 0.17, 0.994) - border = UI_SOOT_EDGE - border_width = 7 - margin = 18 + fill = Color(0.62, 0.44, 0.20, 0.996) + border = UI_BAMBOO_DARK + border_width = 8 + margin = 19 shadow_size = 12 _: pass @@ -577,6 +592,69 @@ func _make_scroll_rod(width: float = 0.0, height: float = 4.0) -> HBoxContainer: return row +func _make_tablet_binding(width: float = 0.0, height: float = 18.0) -> HBoxContainer: + var row := HBoxContainer.new() + row.custom_minimum_size = Vector2(width, height) + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.mouse_filter = Control.MOUSE_FILTER_IGNORE + row.add_theme_constant_override("separation", 3) + row.add_child(_make_binding_end_cap(height)) + row.add_child(_make_binding_strip(height, UI_BAMBOO_DARK, 14.0)) + row.add_child(_make_binding_strip(height, UI_TARNISHED_BRONZE, 0.0)) + row.add_child(_make_seal_knot(height)) + row.add_child(_make_binding_strip(height, UI_TARNISHED_BRONZE, 0.0)) + row.add_child(_make_binding_strip(height, UI_BAMBOO_DARK, 14.0)) + row.add_child(_make_binding_end_cap(height)) + return row + + +func _make_binding_end_cap(size: float) -> ColorRect: + var cap := ColorRect.new() + cap.color = UI_LACQUER_EDGE + cap.custom_minimum_size = Vector2(size * 1.15, size) + cap.mouse_filter = Control.MOUSE_FILTER_IGNORE + return cap + + +func _make_binding_strip(height: float, color: Color, width: float) -> ColorRect: + var strip := ColorRect.new() + strip.color = color + strip.custom_minimum_size = Vector2(width, height) + strip.size_flags_horizontal = Control.SIZE_EXPAND_FILL if width <= 0.0 else Control.SIZE_SHRINK_CENTER + strip.mouse_filter = Control.MOUSE_FILTER_IGNORE + return strip + + +func _make_seal_knot(height: float) -> HBoxContainer: + var knot := HBoxContainer.new() + knot.custom_minimum_size = Vector2(height * 2.0 + 3.0, height) + knot.mouse_filter = Control.MOUSE_FILTER_IGNORE + knot.add_theme_constant_override("separation", 3) + for index in range(2): + var tile := ColorRect.new() + tile.color = UI_SEAL_RED if index == 0 else UI_SEAL_RED_DARK + tile.custom_minimum_size = Vector2(height, height) + tile.mouse_filter = Control.MOUSE_FILTER_IGNORE + knot.add_child(tile) + return knot + + +func _make_ink_wash_rule(width: float = 0.0, height: float = 7.0) -> HBoxContainer: + var row := HBoxContainer.new() + row.custom_minimum_size = Vector2(width, height) + row.size_flags_horizontal = Control.SIZE_EXPAND_FILL + row.mouse_filter = Control.MOUSE_FILTER_IGNORE + row.add_theme_constant_override("separation", 2) + for index in range(5): + var strip := ColorRect.new() + strip.custom_minimum_size = Vector2(10 if index != 2 else width, height) + strip.size_flags_horizontal = Control.SIZE_EXPAND_FILL if index == 2 else Control.SIZE_SHRINK_CENTER + strip.color = UI_INK_WASH if index == 2 else Color(UI_SEAL_RED_DARK.r, UI_SEAL_RED_DARK.g, UI_SEAL_RED_DARK.b, 0.96) + strip.mouse_filter = Control.MOUSE_FILTER_IGNORE + row.add_child(strip) + return row + + func _make_seal_ribbon(width: float = 0.0, height: float = 18.0) -> HBoxContainer: var row := HBoxContainer.new() row.custom_minimum_size = Vector2(width, height) @@ -687,28 +765,40 @@ func _create_hud() -> void: layer.add_child(root) var top_bar := PanelContainer.new() - top_bar.position = Vector2(24, 16) - top_bar.size = Vector2(1120, 64) + top_bar.position = Vector2(24, 14) + top_bar.size = Vector2(1192, 72) _apply_panel_style(top_bar, "compact") root.add_child(top_bar) var top_row := HBoxContainer.new() - top_row.add_theme_constant_override("separation", 12) + top_row.add_theme_constant_override("separation", 8) top_bar.add_child(top_row) status_label = Label.new() - status_label.custom_minimum_size = Vector2(180, 40) + status_label.custom_minimum_size = Vector2(154, 48) _apply_label_style(status_label, UI_PARCHMENT_TEXT) top_row.add_child(status_label) + var objective_top_panel := PanelContainer.new() + objective_top_panel.custom_minimum_size = Vector2(460, 54) + _apply_panel_style(objective_top_panel, "edict_compact") + top_row.add_child(objective_top_panel) + + var objective_top_row := HBoxContainer.new() + objective_top_row.add_theme_constant_override("separation", 7) + objective_top_panel.add_child(objective_top_row) + objective_top_row.add_child(_make_bamboo_gutter(14, 36)) + objective_label = Label.new() objective_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - objective_label.custom_minimum_size = Vector2(360, 48) - _apply_label_style(objective_label, UI_PARCHMENT_TEXT) - top_row.add_child(objective_label) + objective_label.custom_minimum_size = Vector2(382, 36) + objective_label.size_flags_horizontal = Control.SIZE_EXPAND_FILL + _apply_label_style(objective_label, UI_AGED_INK, 13) + objective_top_row.add_child(objective_label) + objective_top_row.add_child(_make_bamboo_gutter(14, 36)) campaign_status_label = Label.new() - campaign_status_label.custom_minimum_size = Vector2(300, 40) + campaign_status_label.custom_minimum_size = Vector2(260, 48) _apply_label_style(campaign_status_label, UI_PARCHMENT_TEXT) top_row.add_child(campaign_status_label) @@ -724,19 +814,26 @@ func _create_hud() -> void: objective_notice_panel = PanelContainer.new() objective_notice_panel.visible = false - objective_notice_panel.position = Vector2(360, 84) - objective_notice_panel.size = Vector2(560, 64) + objective_notice_panel.position = Vector2(338, 88) + objective_notice_panel.size = Vector2(604, 128) objective_notice_panel.mouse_filter = Control.MOUSE_FILTER_IGNORE - _apply_panel_style(objective_notice_panel, "notice") + _apply_panel_style(objective_notice_panel, "notice_edict") root.add_child(objective_notice_panel) + var objective_notice_column := VBoxContainer.new() + objective_notice_column.add_theme_constant_override("separation", 5) + objective_notice_panel.add_child(objective_notice_column) + objective_notice_column.add_child(_make_scroll_rod(548, 4)) + objective_notice_column.add_child(_make_seal_ribbon(548, 14)) + objective_notice_label = Label.new() objective_notice_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER objective_notice_label.vertical_alignment = VERTICAL_ALIGNMENT_CENTER objective_notice_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - objective_notice_label.custom_minimum_size = Vector2(520, 48) - _apply_label_style(objective_notice_label, UI_PARCHMENT_TEXT, 16) - objective_notice_panel.add_child(objective_notice_label) + objective_notice_label.custom_minimum_size = Vector2(548, 54) + _apply_label_style(objective_notice_label, UI_AGED_INK, 15) + objective_notice_column.add_child(objective_notice_label) + objective_notice_column.add_child(_make_scroll_rod(548, 3)) var side_panel := PanelContainer.new() side_panel.position = Vector2(760, 104) @@ -1063,8 +1160,9 @@ func _create_hud() -> void: root.add_child(briefing_panel) var briefing_column := VBoxContainer.new() - briefing_column.add_theme_constant_override("separation", 8) + briefing_column.add_theme_constant_override("separation", 7) briefing_panel.add_child(briefing_column) + briefing_column.add_child(_make_tablet_binding(680, 16)) var briefing_title_panel := PanelContainer.new() briefing_title_panel.custom_minimum_size = Vector2(680, 46) @@ -1111,6 +1209,7 @@ func _create_hud() -> void: briefing_objective_row.add_child(briefing_objective_label) briefing_objective_row.add_child(_make_bamboo_gutter(18, 84)) + briefing_column.add_child(_make_ink_wash_rule(680, 7)) briefing_column.add_child(_make_section_caption("비단 전장도 · 군막 장부", 680)) briefing_camp_overview_row = HBoxContainer.new() @@ -1161,6 +1260,8 @@ func _create_hud() -> void: _apply_label_style(briefing_label, UI_AGED_INK, 14) briefing_scroll.add_child(briefing_label) + briefing_column.add_child(_make_tablet_binding(680, 14)) + var prep_button_row := HBoxContainer.new() prep_button_row.add_theme_constant_override("separation", 8) briefing_column.add_child(prep_button_row) @@ -1402,9 +1503,11 @@ func _create_hud() -> void: dialogue_column = VBoxContainer.new() dialogue_column.custom_minimum_size = DIALOGUE_COLUMN_SIZE dialogue_column.size_flags_horizontal = Control.SIZE_EXPAND_FILL - dialogue_column.add_theme_constant_override("separation", 8) + dialogue_column.add_theme_constant_override("separation", 7) dialogue_row.add_child(dialogue_column) + dialogue_column.add_child(_make_tablet_binding(DIALOGUE_TEXT_SIZE.x, 13)) + dialogue_seal_ribbon = _make_seal_ribbon(DIALOGUE_TEXT_SIZE.x, 18) dialogue_column.add_child(dialogue_seal_ribbon) @@ -1440,6 +1543,7 @@ func _create_hud() -> void: dialogue_text_row.add_child(_make_bamboo_gutter(18, DIALOGUE_TEXT_SIZE.y - 20)) dialogue_column.add_child(_make_scroll_rod(DIALOGUE_TEXT_SIZE.x, 3)) + dialogue_column.add_child(_make_ink_wash_rule(DIALOGUE_TEXT_SIZE.x, 6)) var dialogue_control_row := HBoxContainer.new() dialogue_control_row.add_theme_constant_override("separation", 8) @@ -1476,10 +1580,12 @@ func _create_hud() -> void: result_column.alignment = BoxContainer.ALIGNMENT_CENTER result_panel.add_child(result_column) + result_column.add_child(_make_tablet_binding(520, 16)) result_column.add_child(_make_scroll_rod(520, 4)) result_seal_ribbon = _make_seal_ribbon(520, 18) result_column.add_child(result_seal_ribbon) + result_column.add_child(_make_section_caption("戰功 조서", 520)) result_label = Label.new() result_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_LEFT @@ -2054,6 +2160,16 @@ func _draw_silk_map_frame(board_rect: Rect2) -> void: for rod in [top_rod, bottom_rod, left_rod, right_rod]: draw_rect(rod, UI_BAMBOO_DARK) draw_rect(rod, Color(0.62, 0.43, 0.18, 0.80), false, 1.0) + var hanger_y := top_rod.position.y - 8.0 + draw_line( + Vector2(top_rod.position.x + 32.0, hanger_y), + Vector2(top_rod.end.x - 32.0, hanger_y), + Color(UI_TARNISHED_BRONZE.r, UI_TARNISHED_BRONZE.g, UI_TARNISHED_BRONZE.b, 0.82), + 2.0 + ) + for x in [top_rod.position.x + 54.0, top_rod.end.x - 54.0]: + draw_line(Vector2(x, hanger_y), Vector2(x, top_rod.position.y + 7.0), Color(UI_BAMBOO_DARK.r, UI_BAMBOO_DARK.g, UI_BAMBOO_DARK.b, 0.88), 2.0) + draw_circle(Vector2(x, hanger_y), 4.0, UI_SEAL_RED_DARK) for corner in [ top_rod.position - Vector2(3, 3), Vector2(top_rod.end.x - 11.0, top_rod.position.y - 3.0), @@ -2064,6 +2180,11 @@ func _draw_silk_map_frame(board_rect: Rect2) -> void: draw_rect(Rect2(corner + Vector2(2, 2), Vector2(10, 10)), UI_OLD_BRONZE, false, 1.0) draw_rect(board_rect.grow(4.0), UI_LACQUER_EDGE, false, 5.0) draw_rect(board_rect.grow(1.0), UI_OLD_BRONZE, false, 2.0) + var seal_rect := Rect2(board_rect.end - Vector2(58, 54), Vector2(34, 34)) + draw_rect(seal_rect, Color(UI_SEAL_RED.r, UI_SEAL_RED.g, UI_SEAL_RED.b, 0.34)) + draw_rect(seal_rect.grow(-5.0), Color(UI_SEAL_RED_DARK.r, UI_SEAL_RED_DARK.g, UI_SEAL_RED_DARK.b, 0.68), false, 2.0) + draw_line(seal_rect.position + Vector2(8, seal_rect.size.y * 0.5), seal_rect.end - Vector2(8, seal_rect.size.y * 0.5), Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.42), 1.2) + draw_line(seal_rect.position + Vector2(seal_rect.size.x * 0.5, 8), seal_rect.end - Vector2(seal_rect.size.x * 0.5, 8), Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.34), 1.2) func _current_battle_background_texture() -> Texture2D: @@ -2108,7 +2229,7 @@ func _terrain_fill_color(cell: Vector2i, terrain_key: String, has_background: bo func _map_grid_color(has_background: bool) -> Color: var color := GRID_COLOR - color.a = 0.23 if has_background else 0.52 + color.a = 0.14 if has_background else 0.38 return color @@ -4708,27 +4829,27 @@ func _format_briefing_objectives() -> String: var text := "" var victory := str(state.objectives.get("victory", "")) if not victory.is_empty(): - text = "승리 조서: %s" % victory + text = "승리 조서:\n %s" % victory var defeat := str(state.objectives.get("defeat", "")) if not defeat.is_empty(): if not text.is_empty(): text += "\n" - text += "패배 흉조: %s" % defeat + text += "패배 흉조:\n %s" % defeat var progress_lines := state.get_objective_progress_lines(false, false) if not progress_lines.is_empty(): if not text.is_empty(): text += "\n" - text += "전공 죽간: %s" % _join_strings(progress_lines, ", ") + text += "전공 죽간:\n %s" % _join_strings(progress_lines, ", ") var risk_lines := state.get_defeat_progress_lines() if not risk_lines.is_empty(): if not text.is_empty(): text += "\n" - text += "흉조 징후: %s" % _join_strings(risk_lines, ", ") + text += "흉조 징후:\n %s" % _join_strings(risk_lines, ", ") var rewards_text := _format_briefing_rewards() if not rewards_text.is_empty(): if not text.is_empty(): text += "\n" - text += "전리품 봉인: %s" % rewards_text + text += "전리품 봉인:\n %s" % rewards_text return text diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 49c88fa..6cab14d 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -2646,15 +2646,15 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: for expected in ["승리 조서", "패배 흉조", "전공 죽간", "흉조 징후"]: if not mission_text.contains(expected): failures.append("mission panel should use old campaign wording `%s`: %s" % [expected, mission_text]) - _check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.74, 0.58, 0.32, 0.996)) - _check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.045, 0.026, 0.016, 1.0), 8) - _check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.55, 0.36, 0.15, 0.995)) - _check_panel_style_frame(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.10, 0.065, 0.040, 1.0), 5) - _check_panel_style_fill(failures, scene.dialogue_panel, "dialogue panel", Color(0.036, 0.012, 0.006, 0.996)) - _check_panel_style_frame(failures, scene.dialogue_panel, "dialogue panel", Color(0.28, 0.020, 0.014, 1.0), 8) + _check_panel_style_fill(failures, scene.briefing_panel, "briefing panel", Color(0.68, 0.50, 0.25, 0.997)) + _check_panel_style_frame(failures, scene.briefing_panel, "briefing panel", Color(0.20, 0.11, 0.045, 1.0), 10) + _check_panel_style_fill(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.66, 0.52, 0.31, 0.995)) + _check_panel_style_frame(failures, scene.briefing_objective_panel, "briefing objective edict panel", Color(0.20, 0.11, 0.045, 1.0), 6) + _check_panel_style_fill(failures, scene.dialogue_panel, "dialogue panel", Color(0.070, 0.018, 0.010, 0.997)) + _check_panel_style_frame(failures, scene.dialogue_panel, "dialogue panel", Color(0.78, 0.58, 0.27, 1.0), 9) _check_panel_style_fill(failures, scene.dialogue_portrait_panel, "dialogue portrait panel", Color(0.035, 0.014, 0.008, 1.0)) _check_panel_style_fill(failures, scene.dialogue_speaker_panel, "dialogue speaker seal panel", Color(0.28, 0.020, 0.014, 1.0)) - _check_panel_style_fill(failures, scene.dialogue_text_panel, "dialogue text scroll panel", Color(0.67, 0.50, 0.27, 0.996)) + _check_panel_style_fill(failures, scene.dialogue_text_panel, "dialogue text scroll panel", Color(0.66, 0.52, 0.31, 0.995)) _check_seal_ribbon(failures, scene.briefing_seal_ribbon, "briefing seal ribbon") _check_seal_ribbon(failures, scene.dialogue_seal_ribbon, "dialogue seal ribbon") _check_seal_ribbon(failures, scene.result_seal_ribbon, "result seal ribbon") @@ -2700,7 +2700,7 @@ func _check_ancient_ui_theme(failures: Array[String]) -> void: scene._update_briefing_camp_overview(scene.state.get_briefing(), false) if scene.briefing_camp_overview_fallback_label == null or scene.briefing_camp_overview_fallback_label.text != "비단 전장도": failures.append("briefing map fallback should read as an old campaign map") - _check_panel_style_fill(failures, scene.result_panel, "result panel", Color(0.48, 0.34, 0.17, 0.994)) + _check_panel_style_fill(failures, scene.result_panel, "result panel", Color(0.62, 0.44, 0.20, 0.996)) if not scene._format_victory_result_text().contains("승전 조서"): failures.append("victory result should read like a bronze proclamation") if not scene._format_defeat_result_text().contains("패전 비문"):