diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 448cfd0..2bfaf72 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -81,6 +81,10 @@ const DIALOGUE_SPEAKER_FONT_SIZE := 18 const DIALOGUE_SPEAKER_MIN_FONT_SIZE := 15 const DIALOGUE_TEXT_FONT_SIZE := 17 const DIALOGUE_TEXT_MIN_FONT_SIZE := 14 +const LOCAL_COMMAND_TITLE_FONT_SIZE := 14 +const LOCAL_COMMAND_TITLE_MIN_FONT_SIZE := 10 +const LOCAL_COMMAND_DETAIL_FONT_SIZE := 12 +const LOCAL_COMMAND_DETAIL_MIN_FONT_SIZE := 10 const BRIEFING_CAMP_THUMBNAIL_SIZE := Vector2(188, 86) const CAMP_TALK_PORTRAIT_SIZE := Vector2(58, 58) const SHOP_MERCHANT_BADGE_SIZE := Vector2(50, 50) @@ -88,8 +92,13 @@ const SHOP_ITEM_ICON_SIZE := Vector2(48, 48) const HUD_UNIT_PORTRAIT_SIZE := Vector2(92, 104) const HUD_UNIT_PORTRAIT_STACK_SIZE := Vector2(88, 100) const POST_MOVE_MENU_SIZE := Vector2(240, 146) +const POST_MOVE_TITLE_SIZE := Vector2(196, 22) const POST_MOVE_PICKER_PANEL_SIZE := Vector2(336, 236) +const POST_MOVE_PICKER_TITLE_SIZE := Vector2(296, 22) +const POST_MOVE_PICKER_DETAIL_SIZE := Vector2(296, 32) const TARGETING_HINT_PANEL_SIZE := Vector2(254, 118) +const TARGETING_HINT_TITLE_SIZE := Vector2(220, 22) +const TARGETING_HINT_DETAIL_SIZE := Vector2(220, 28) const LOCAL_COMMAND_PANEL_GAP := 8.0 const LOCAL_COMMAND_PANEL_BOARD_PADDING := 4.0 const UI_RICE_PAPER := Color(0.58, 0.45, 0.25, 0.98) @@ -759,6 +768,13 @@ func _fit_label_font_size_to_text(label: Label, text: String, base_size: int, mi return font_size +func _set_fitted_label_text(label: Label, text: String, base_size: int, min_size: int, available_size: Vector2 = Vector2.ZERO) -> void: + if label == null: + return + label.text = text + _fit_label_font_size_to_text(label, text, base_size, min_size, available_size) + + func _fit_font_size_for_text(text: String, base_size: int, min_size: int, available_size: Vector2) -> int: if text.strip_edges().is_empty(): return base_size @@ -1440,8 +1456,9 @@ func _create_hud() -> void: post_move_title_label = Label.new() post_move_title_label.text = "군령 선택" + post_move_title_label.custom_minimum_size = POST_MOVE_TITLE_SIZE post_move_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _apply_label_style(post_move_title_label, UI_OLD_BRONZE, 14) + _apply_label_style(post_move_title_label, UI_OLD_BRONZE, LOCAL_COMMAND_TITLE_FONT_SIZE) post_move_column.add_child(post_move_title_label) var post_move_grid := GridContainer.new() @@ -1495,14 +1512,15 @@ func _create_hud() -> void: post_move_picker_title_label = Label.new() post_move_picker_title_label.text = "군령 선택" + post_move_picker_title_label.custom_minimum_size = POST_MOVE_PICKER_TITLE_SIZE post_move_picker_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _apply_label_style(post_move_picker_title_label, UI_OLD_BRONZE, 14) + _apply_label_style(post_move_picker_title_label, UI_OLD_BRONZE, LOCAL_COMMAND_TITLE_FONT_SIZE) picker_column.add_child(post_move_picker_title_label) post_move_picker_detail_label = Label.new() post_move_picker_detail_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - post_move_picker_detail_label.custom_minimum_size = Vector2(296, 32) - _apply_label_style(post_move_picker_detail_label, UI_PARCHMENT_TEXT) + post_move_picker_detail_label.custom_minimum_size = POST_MOVE_PICKER_DETAIL_SIZE + _apply_label_style(post_move_picker_detail_label, UI_PARCHMENT_TEXT, LOCAL_COMMAND_DETAIL_FONT_SIZE) picker_column.add_child(post_move_picker_detail_label) var picker_scroll := ScrollContainer.new() @@ -1546,14 +1564,15 @@ func _create_hud() -> void: targeting_hint_title_label = Label.new() targeting_hint_title_label.text = "적장 지목" + targeting_hint_title_label.custom_minimum_size = TARGETING_HINT_TITLE_SIZE targeting_hint_title_label.horizontal_alignment = HORIZONTAL_ALIGNMENT_CENTER - _apply_label_style(targeting_hint_title_label, UI_OLD_BRONZE, 14) + _apply_label_style(targeting_hint_title_label, UI_OLD_BRONZE, LOCAL_COMMAND_TITLE_FONT_SIZE) targeting_column.add_child(targeting_hint_title_label) targeting_hint_detail_label = Label.new() targeting_hint_detail_label.autowrap_mode = TextServer.AUTOWRAP_WORD_SMART - targeting_hint_detail_label.custom_minimum_size = Vector2(220, 28) - _apply_label_style(targeting_hint_detail_label, UI_PARCHMENT_TEXT) + targeting_hint_detail_label.custom_minimum_size = TARGETING_HINT_DETAIL_SIZE + _apply_label_style(targeting_hint_detail_label, UI_PARCHMENT_TEXT, LOCAL_COMMAND_DETAIL_FONT_SIZE) targeting_column.add_child(targeting_hint_detail_label) var targeting_button_row := HBoxContainer.new() @@ -8329,7 +8348,13 @@ func _update_post_move_menu() -> void: return _position_post_move_menu() if post_move_title_label != null: - post_move_title_label.text = "%s: 군령 선택" % str(selected.get("name", "부대")) + _set_fitted_label_text( + post_move_title_label, + "%s: 군령 선택" % str(selected.get("name", "부대")), + LOCAL_COMMAND_TITLE_FONT_SIZE, + LOCAL_COMMAND_TITLE_MIN_FONT_SIZE, + POST_MOVE_TITLE_SIZE + ) if post_move_attack_button != null: var attack_blocked := not _has_basic_attack_target(pending_move_unit_id) post_move_attack_button.disabled = attack_blocked @@ -8449,9 +8474,21 @@ func _rebuild_post_move_picker(selected: Dictionary) -> void: _clear_post_move_picker_list() _position_post_move_picker() if post_move_picker_title_label != null: - post_move_picker_title_label.text = _post_move_picker_title_text(selected) + _set_fitted_label_text( + post_move_picker_title_label, + _post_move_picker_title_text(selected), + LOCAL_COMMAND_TITLE_FONT_SIZE, + LOCAL_COMMAND_TITLE_MIN_FONT_SIZE, + POST_MOVE_PICKER_TITLE_SIZE + ) if post_move_picker_detail_label != null: - post_move_picker_detail_label.text = _post_move_picker_detail_text() + _set_fitted_label_text( + post_move_picker_detail_label, + _post_move_picker_detail_text(), + LOCAL_COMMAND_DETAIL_FONT_SIZE, + LOCAL_COMMAND_DETAIL_MIN_FONT_SIZE, + POST_MOVE_PICKER_DETAIL_SIZE + ) if post_move_picker_mode == "tactic": _rebuild_post_move_tactic_picker(selected) elif post_move_picker_mode == "item": @@ -8723,9 +8760,21 @@ func _update_targeting_hint_panel() -> void: return _position_targeting_hint_panel(selected) if targeting_hint_title_label != null: - targeting_hint_title_label.text = _targeting_hint_title_text() + _set_fitted_label_text( + targeting_hint_title_label, + _targeting_hint_title_text(), + LOCAL_COMMAND_TITLE_FONT_SIZE, + LOCAL_COMMAND_TITLE_MIN_FONT_SIZE, + TARGETING_HINT_TITLE_SIZE + ) if targeting_hint_detail_label != null: - targeting_hint_detail_label.text = _targeting_hint_detail_text() + _set_fitted_label_text( + targeting_hint_detail_label, + _targeting_hint_detail_text(), + LOCAL_COMMAND_DETAIL_FONT_SIZE, + LOCAL_COMMAND_DETAIL_MIN_FONT_SIZE, + TARGETING_HINT_DETAIL_SIZE + ) if targeting_hint_back_button != null: targeting_hint_back_button.text = "죽간 거두기" targeting_hint_back_button.tooltip_text = "군령 선택으로 되돌립니다." if _has_pending_move() else "지목을 거둡니다." diff --git a/tools/smoke_post_move_action_flow.gd b/tools/smoke_post_move_action_flow.gd index 6facca3..ce7684c 100644 --- a/tools/smoke_post_move_action_flow.gd +++ b/tools/smoke_post_move_action_flow.gd @@ -9,6 +9,7 @@ func _init() -> void: _check_deferred_move_events(failures) _check_scene_post_move_menu_flow(failures) _check_scene_post_move_edge_positioning(failures) + _check_scene_post_move_text_fit(failures) _check_scene_post_move_tactic_picker_flow(failures) _check_scene_post_move_item_picker_flow(failures) @@ -166,6 +167,64 @@ func _check_scene_post_move_edge_positioning(failures: Array[String]) -> void: scene.free() +func _check_scene_post_move_text_fit(failures: Array[String]) -> void: + var scene = BattleSceneScript.new() + scene._create_hud() + if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"): + failures.append("could not load opening battle for post-move text fit") + scene.free() + return + scene.battle_started = true + scene.campaign_complete_screen = false + scene.briefing_panel.visible = false + scene.result_panel.visible = false + scene._clear_pending_move_state() + + if not scene.state.select_unit("cao_cao"): + failures.append("could not select Cao Cao for post-move text fit") + scene.free() + return + var cao_cao: Dictionary = scene.state.get_unit("cao_cao") + cao_cao["name"] = "매우 긴 이름의 임시 선봉장" + cao_cao["pos"] = Vector2i(2, 3) + cao_cao["moved"] = true + cao_cao["acted"] = false + scene.pending_move_unit_id = "cao_cao" + scene.pending_move_from_cell = Vector2i(1, 3) + scene.pending_move_to_cell = Vector2i(2, 3) + scene._show_post_move_menu() + + if scene.post_move_title_label == null: + failures.append("post-move menu should expose a fitted title label") + else: + _check_fitted_label_font(failures, scene.post_move_title_label, BattleSceneScript.LOCAL_COMMAND_TITLE_FONT_SIZE, BattleSceneScript.LOCAL_COMMAND_TITLE_MIN_FONT_SIZE, "post-move long unit title") + + scene._show_post_move_picker("tactic") + if scene.post_move_picker_title_label == null: + failures.append("post-move picker should expose a fitted title label") + else: + _check_fitted_label_font(failures, scene.post_move_picker_title_label, BattleSceneScript.LOCAL_COMMAND_TITLE_FONT_SIZE, BattleSceneScript.LOCAL_COMMAND_TITLE_MIN_FONT_SIZE, "post-move picker long unit title") + if scene.post_move_picker_detail_label == null: + failures.append("post-move picker should expose a fitted detail label") + else: + _check_fitted_label_font(failures, scene.post_move_picker_detail_label, BattleSceneScript.LOCAL_COMMAND_DETAIL_FONT_SIZE, BattleSceneScript.LOCAL_COMMAND_DETAIL_MIN_FONT_SIZE, "post-move picker detail") + + scene.selected_item_id = "very_long_supply_order_marker_name" + scene.selected_skill_id = "" + scene.basic_attack_targeting = false + scene._update_targeting_hint_panel() + if scene.targeting_hint_title_label == null: + failures.append("targeting hint should expose a fitted title label") + else: + _check_fitted_label_font(failures, scene.targeting_hint_title_label, BattleSceneScript.LOCAL_COMMAND_TITLE_FONT_SIZE, BattleSceneScript.LOCAL_COMMAND_TITLE_MIN_FONT_SIZE, "targeting long item title") + if scene.targeting_hint_detail_label == null: + failures.append("targeting hint should expose a fitted detail label") + else: + _check_fitted_label_font(failures, scene.targeting_hint_detail_label, BattleSceneScript.LOCAL_COMMAND_DETAIL_FONT_SIZE, BattleSceneScript.LOCAL_COMMAND_DETAIL_MIN_FONT_SIZE, "targeting detail") + + scene.free() + + func _check_scene_post_move_attack_targeting(failures: Array[String]) -> void: var scene = BattleSceneScript.new() scene._create_hud() @@ -475,5 +534,13 @@ func _check_local_panel_position(failures: Array[String], scene, panel: Control, failures.append("%s should not cover the moved unit cell: %s over %s" % [label, str(panel_rect), str(cell_rect)]) +func _check_fitted_label_font(failures: Array[String], label: Label, base_size: int, min_size: int, label_name: String) -> void: + var font_size := label.get_theme_font_size("font_size") + if font_size < min_size or font_size > base_size: + failures.append("%s font size should stay inside fitted bounds %d..%d, got %d" % [label_name, min_size, base_size, font_size]) + if label.custom_minimum_size.x <= 0.0 or label.custom_minimum_size.y <= 0.0: + failures.append("%s should reserve stable label space" % label_name) + + func _screen_for_cell(cell: Vector2i) -> Vector2: return BattleSceneScript.BOARD_OFFSET + Vector2(cell.x, cell.y) * BattleSceneScript.TILE_SIZE + Vector2.ONE * (BattleSceneScript.TILE_SIZE * 0.5)