From fead876200401456eb823426f52d5dc96d5baf38 Mon Sep 17 00:00:00 2001 From: Wickedness Date: Sat, 20 Jun 2026 16:07:28 +0900 Subject: [PATCH] Polish story controls and command guidance --- README.md | 4 +- scripts/scenes/battle_scene.gd | 151 ++++++++++++++++++++------- tools/smoke_post_move_action_flow.gd | 49 +++++++++ tools/smoke_title_menu.gd | 8 +- tools/smoke_visual_assets.gd | 8 +- 5 files changed, 179 insertions(+), 41 deletions(-) diff --git a/README.md b/README.md index b4320ab..8eda980 100644 --- a/README.md +++ b/README.md @@ -36,7 +36,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr - Pre-battle Formation lets deployed officers swap starting positions inside scenario-defined cells, with portrait rows, readable zone badges, and hover placement instructions. - Pre-battle briefing shows the campaign chapter, chapter battle number, location, victory/defeat summary, first-clear reward preview, generated battlefield thumbnail, allied portrait/enemy unit force markers, and image-first tactical marker cards for lure lines and supply points with compact tactical summaries plus full hover hints. - Pre-battle prep commands use compact icon buttons with active selection state and Korean hover labels for chapters, shop, talks, armory, roster, formation, and save records. -- Post-move tactic and item pickers use generated command/item icons, compact two-line option rows, and hover details instead of dense single-line text. +- Post-move command boards, tactic pickers, and item pickers use generated command/item icons, compact option rows, and richer hover guidance for move-attack, stratagem, supply, wait, and cancel decisions instead of dense inline text. - Target preview badges, minimap hover badges, and battlefield target markers include generated jade panel surfaces plus action icons for move, attack, threat, support, objective, terrain, and item outcomes. - Story, briefing, dialogue, mission-detail, and result scroll/ribbon/binding/bamboo/ink/tassel ornaments use generated seal/panel textures instead of flat color boxes. - Battlefield hover info badges use generated jade panel texture plus terrain, supply, tactic, objective, and threat icons, keeping raw grid coordinates out of visible play text. @@ -132,7 +132,7 @@ Godot 4 tactical RPG prototype inspired by classic turn-based Romance of the Thr - Campaign completion screen. - New campaign save reset. - Manual campaign checkpoint save/load. -- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop, Cao Cao portrait art, and a compact left-side command board with `처음부터`, `로드하기`, and `환경설정`; its generated command board, title buttons, and opening-story controls keep safer source-art proportions plus linear filtering for QHD fullscreen scaling. New campaigns then move into a generated four-panel Yellow Turban rebellion, Cao Cao resolve, Xiahou Dun meeting, and Yingchuan contact prologue with full-screen story artwork, a chapter seal, a compact chapter-side story-ledger strip instead of exposed numeric cards, QHD-safe record/camp buttons, and a bottom scroll caption panel. Title and in-game settings expose clearer background-music, combat-SFX, edge-scroll, and fullscreen display controls, include one-click audio recovery, and persist preferences in `user://heros_settings.json`. +- Cinematic title screen uses a dedicated ancient-war-camp dawn backdrop, Cao Cao portrait art, and a compact left-side command board with `처음부터`, `로드하기`, and `환경설정`; its generated command board, title buttons, and opening-story controls keep safer source-art proportions, linear filtering, and tile-fit 9-slice surfaces for QHD fullscreen scaling. New campaigns then move into a generated four-panel Yellow Turban rebellion, Cao Cao resolve, Xiahou Dun meeting, and Yingchuan contact prologue with full-screen story artwork, a chapter seal, a compact chapter-side story-ledger strip instead of exposed numeric cards, `기록 넘기기`/`군막으로 바로` story controls, and a bottom scroll caption panel. Title and in-game settings expose clearer background-music, combat-SFX, edge-scroll, and fullscreen display controls, include one-click audio recovery, and persist preferences in `user://heros_settings.json`. - Victory and defeat result overlay. - Dialogue portrait slot stays fixed across speaker and narration lines, with officer default image paths, optional per-line overrides, and speaker-initial or record fallback. - Initial AI-generated photorealistic officer portraits for Cao Cao, Xiahou Dun, Xiahou Yuan, Cao Ren, Dian Wei, Guo Jia, and Zhang He. diff --git a/scripts/scenes/battle_scene.gd b/scripts/scenes/battle_scene.gd index 21ca45d..0225a9c 100644 --- a/scripts/scenes/battle_scene.gd +++ b/scripts/scenes/battle_scene.gd @@ -1063,6 +1063,7 @@ func _make_button_texture_style(state_key: String, content_margin: int = 10, tex var style := StyleBoxTexture.new() style.texture = texture style.draw_center = true + _configure_texture_style_tiling(style) style.texture_margin_left = texture_margin.x style.texture_margin_right = texture_margin.x style.texture_margin_top = texture_margin.y @@ -1082,6 +1083,7 @@ func _make_icon_button_texture_style(state_key: String, content_margin: int = 6, var style := StyleBoxTexture.new() style.texture = texture style.draw_center = true + _configure_texture_style_tiling(style) style.texture_margin_left = texture_margin.x style.texture_margin_right = texture_margin.x style.texture_margin_top = texture_margin.y @@ -1094,6 +1096,13 @@ func _make_icon_button_texture_style(state_key: String, content_margin: int = 6, return style +func _configure_texture_style_tiling(style: StyleBoxTexture) -> void: + if style == null: + return + style.axis_stretch_horizontal = StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT + style.axis_stretch_vertical = StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT + + func _control_texture_target_size(control: Control, fallback := Vector2(96.0, 42.0)) -> Vector2: if control == null: return fallback @@ -1113,8 +1122,8 @@ func _text_button_texture_margin_for(button: Button, title_button: bool, opening var target := _control_texture_target_size(button, Vector2(148.0, 44.0)) var max_x := maxi(8, int(floor(target.x * 0.5)) - 3) var max_y := maxi(6, int(floor(target.y * 0.5)) - 3) - var desired_x := int(floor(target.x * (0.16 if opening_story_button else (0.12 if title_button else 0.14)))) - var desired_y := int(floor(target.y * (0.24 if opening_story_button else 0.22))) + var desired_x := int(floor(target.x * (0.22 if opening_story_button else (0.12 if title_button else 0.14)))) + var desired_y := int(floor(target.y * (0.25 if opening_story_button else 0.22))) var cap_x := 38 if opening_story_button else (60 if title_button else 72) var cap_y := 14 if opening_story_button else (18 if title_button else 24) return Vector2i( @@ -1393,11 +1402,11 @@ func _configure_prep_menu_button(button: Button, node_name: String, icon_kind: S button.set_meta("command_hint", hint) -func _configure_local_command_button(button: Button, icon_kind: String, label: String, hint: String, minimum_size := LOCAL_COMMAND_BUTTON_SIZE) -> void: +func _configure_local_command_button(button: Button, icon_kind: String, label: String, hint: String, minimum_size := LOCAL_COMMAND_BUTTON_SIZE, detail: String = "") -> void: if button == null: return button.text = "" - button.tooltip_text = _format_local_command_tooltip(label, hint) + button.tooltip_text = _format_local_command_tooltip(label, detail if not detail.strip_edges().is_empty() else hint) button.custom_minimum_size = minimum_size button.size_flags_horizontal = Control.SIZE_EXPAND_FILL button.icon = _make_toolbar_icon(icon_kind) @@ -1407,6 +1416,7 @@ func _configure_local_command_button(button: Button, icon_kind: String, label: S button.set_meta("command_icon", icon_kind) button.set_meta("command_label", label) button.set_meta("command_hint", hint) + button.set_meta("command_hint_detail", detail) button.mouse_entered.connect(_on_local_command_button_mouse_entered.bind(button)) button.mouse_exited.connect(_on_local_command_button_mouse_exited) @@ -2764,9 +2774,9 @@ func _make_opening_story_beat_strip() -> PanelContainer: opening_prologue_story_strip = HBoxContainer.new() opening_prologue_story_strip.name = "OpeningStoryLedgerMarks" - opening_prologue_story_strip.custom_minimum_size = Vector2(74, 18) + opening_prologue_story_strip.custom_minimum_size = Vector2(82, 24) opening_prologue_story_strip.size_flags_vertical = Control.SIZE_SHRINK_CENTER - opening_prologue_story_strip.add_theme_constant_override("separation", 5) + opening_prologue_story_strip.add_theme_constant_override("separation", 4) opening_prologue_story_strip.tooltip_text = panel.tooltip_text rail_row.add_child(opening_prologue_story_strip) return panel @@ -2787,18 +2797,23 @@ func _rebuild_opening_story_beat_strip() -> void: opening_prologue_progress_label.text = tooltip opening_prologue_progress_label.tooltip_text = "현재 기록: %s" % tooltip for index in range(page_count): - var tick := ColorRect.new() + var is_current := index == opening_prologue_index + var tick := _make_generated_ornament_panel( + "OpeningStoryLedgerTick%d" % (index + 1), + Vector2(18, 14) if is_current else Vector2(12, 10), + "command_seal" + ) tick.name = "OpeningStoryLedgerTick%d" % (index + 1) - tick.custom_minimum_size = Vector2(20, 5) if index == opening_prologue_index else Vector2(12, 4) tick.size_flags_vertical = Control.SIZE_SHRINK_CENTER - tick.color = ( + tick.modulate = ( Color(UI_PARCHMENT_TEXT.r, UI_PARCHMENT_TEXT.g, UI_PARCHMENT_TEXT.b, 0.92) - if index == opening_prologue_index + if is_current else Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.42) ) tick.tooltip_text = _opening_story_record_text(index, page_count) tick.set_meta("story_progress_tick", true) - tick.set_meta("current_story_beat", index == opening_prologue_index) + tick.set_meta("current_story_beat", is_current) + tick.set_meta("quiet_story_ledger_mark", true) opening_prologue_story_strip.add_child(tick) @@ -3686,27 +3701,62 @@ func _create_hud() -> void: post_move_column.add_child(post_move_grid) post_move_attack_button = Button.new() - _configure_local_command_button(post_move_attack_button, "attack", "타격령", "무기 공격") + _configure_local_command_button( + post_move_attack_button, + "attack", + "타격령", + "적 표식 지목", + LOCAL_COMMAND_BUTTON_SIZE, + "공격 가능한 적에게 붉은 표식이 뜹니다. 표식을 눌러 이동과 타격을 한 군령으로 끝냅니다." + ) post_move_attack_button.pressed.connect(_on_post_move_attack_pressed) post_move_grid.add_child(post_move_attack_button) post_move_tactic_button = Button.new() - _configure_local_command_button(post_move_tactic_button, "tactic", "책략첩", "책략 표식") + _configure_local_command_button( + post_move_tactic_button, + "tactic", + "책략첩", + "죽간 펼침", + LOCAL_COMMAND_BUTTON_SIZE, + "책략 죽간을 펼친 뒤 빛나는 표식 칸을 지목합니다." + ) post_move_tactic_button.pressed.connect(_on_post_move_tactic_pressed) post_move_grid.add_child(post_move_tactic_button) post_move_item_button = Button.new() - _configure_local_command_button(post_move_item_button, "item", "보급첩", "보급 사용") + _configure_local_command_button( + post_move_item_button, + "item", + "보급첩", + "보급 지목", + LOCAL_COMMAND_BUTTON_SIZE, + "보급첩을 펼친 뒤 회복이나 도구를 받을 부대를 지목합니다." + ) post_move_item_button.pressed.connect(_on_post_move_item_pressed) post_move_grid.add_child(post_move_item_button) post_move_wait_button = Button.new() - _configure_local_command_button(post_move_wait_button, "wait", "대기령", "행동 종료") + _configure_local_command_button( + post_move_wait_button, + "wait", + "대기령", + "그 자리 대기", + LOCAL_COMMAND_BUTTON_SIZE, + "이동한 자리에서 군령을 마치고 다음 부대를 기다립니다." + ) post_move_wait_button.pressed.connect(_on_post_move_wait_pressed) post_move_grid.add_child(post_move_wait_button) post_move_cancel_button = Button.new() - _configure_local_command_button(post_move_cancel_button, "cancel", "발걸음 거두기", "이동 취소", LOCAL_COMMAND_CANCEL_BUTTON_SIZE) + _configure_local_command_button( + post_move_cancel_button, + "cancel", + "발걸음 거두기", + "이동 취소", + LOCAL_COMMAND_CANCEL_BUTTON_SIZE, + "방금 이동을 거두고 원래 자리에서 다시 군령을 고릅니다." + ) post_move_cancel_button.pressed.connect(_on_post_move_cancel_pressed) post_move_column.add_child(post_move_cancel_button) @@ -3946,25 +3996,25 @@ func _create_hud() -> void: opening_caption_row.add_child(opening_button_column) opening_prologue_skip_button = Button.new() - opening_prologue_skip_button.text = "서문 접기" + opening_prologue_skip_button.text = "군막으로 바로" opening_prologue_skip_button.icon = _make_toolbar_icon("campaign") - opening_prologue_skip_button.custom_minimum_size = Vector2(184, 44) - opening_prologue_skip_button.tooltip_text = "서문을 접고 군막으로 이동합니다." + opening_prologue_skip_button.custom_minimum_size = Vector2(168, 52) + opening_prologue_skip_button.tooltip_text = "서문을 덮고 군막으로 바로 이동합니다." opening_prologue_skip_button.expand_icon = false opening_prologue_skip_button.add_theme_constant_override("icon_max_width", 22) opening_prologue_skip_button.add_theme_constant_override("h_separation", 8) opening_prologue_skip_button.icon_alignment = HORIZONTAL_ALIGNMENT_LEFT opening_prologue_skip_button.vertical_icon_alignment = VERTICAL_ALIGNMENT_CENTER - opening_prologue_skip_button.add_theme_font_size_override("font_size", 15) + opening_prologue_skip_button.add_theme_font_size_override("font_size", 14) opening_prologue_skip_button.set_meta("opening_story_button", true) _apply_button_style(opening_prologue_skip_button) opening_prologue_skip_button.pressed.connect(_on_opening_prologue_skip_pressed) opening_prologue_next_button = Button.new() - opening_prologue_next_button.text = "다음 기록" + opening_prologue_next_button.text = "기록 넘기기" opening_prologue_next_button.icon = _make_toolbar_icon("move") - opening_prologue_next_button.custom_minimum_size = Vector2(184, 50) - opening_prologue_next_button.tooltip_text = "다음 기록을 펼칩니다." + opening_prologue_next_button.custom_minimum_size = Vector2(168, 52) + opening_prologue_next_button.tooltip_text = "다음 기록으로 장면을 넘깁니다." opening_prologue_next_button.expand_icon = false opening_prologue_next_button.add_theme_constant_override("icon_max_width", 22) opening_prologue_next_button.add_theme_constant_override("h_separation", 8) @@ -14522,9 +14572,9 @@ func _update_opening_prologue_page() -> void: _apply_opening_caption_text_style(opening_prologue_step_label, UI_OPENING_CAPTION_MUTED) if opening_prologue_next_button != null: var is_last_page := opening_prologue_index >= page_count - 1 - opening_prologue_next_button.text = "군막으로" if is_last_page else "다음 기록" + opening_prologue_next_button.text = "군막으로" if is_last_page else "기록 넘기기" opening_prologue_next_button.icon = _make_toolbar_icon("campaign" if is_last_page else "move") - opening_prologue_next_button.tooltip_text = "군막으로 이동합니다." if is_last_page else "다음 기록을 펼칩니다." + opening_prologue_next_button.tooltip_text = "군막으로 이동합니다." if is_last_page else "다음 기록으로 장면을 넘깁니다." _rebuild_opening_story_beat_strip() @@ -16913,35 +16963,52 @@ func _update_post_move_menu() -> void: _set_local_command_button_state( post_move_attack_button, "타격령", - "무기 공격", + "적 표식 지목", attack_blocked, - "타격 없음" + "타격 없음", + "공격 가능한 적에게 붉은 표식이 뜹니다. 표식을 눌러 이동과 타격을 한 군령으로 끝냅니다." ) if post_move_tactic_button != null: var skill_blocked := not _unit_has_usable_skill_target(pending_move_unit_id) _set_local_command_button_state( post_move_tactic_button, "책략첩", - "책략 표식", + "죽간 펼침", skill_blocked, - "책략 없음" + "책략 없음", + "책략 죽간을 펼친 뒤 빛나는 표식 칸을 지목합니다." ) if post_move_item_button != null: var item_blocked := not _unit_has_usable_item_target(pending_move_unit_id) _set_local_command_button_state( post_move_item_button, "보급첩", - "보급 사용", + "보급 지목", item_blocked, - "보급 없음" + "보급 없음", + "보급첩을 펼친 뒤 회복이나 도구를 받을 부대를 지목합니다." ) if post_move_wait_button != null: - _set_local_command_button_state(post_move_wait_button, "대기령", "행동 종료", false, "") + _set_local_command_button_state( + post_move_wait_button, + "대기령", + "그 자리 대기", + false, + "", + "이동한 자리에서 군령을 마치고 다음 부대를 기다립니다." + ) if post_move_cancel_button != null: - _set_local_command_button_state(post_move_cancel_button, "발걸음 거두기", "이동 취소", false, "") + _set_local_command_button_state( + post_move_cancel_button, + "발걸음 거두기", + "이동 취소", + false, + "", + "방금 이동을 거두고 원래 자리에서 다시 군령을 고릅니다." + ) -func _set_local_command_button_state(button: Button, label: String, hint: String, disabled: bool, disabled_hint: String) -> void: +func _set_local_command_button_state(button: Button, label: String, hint: String, disabled: bool, disabled_hint: String, detail: String = "") -> void: if button == null: return button.text = "" @@ -16949,10 +17016,24 @@ func _set_local_command_button_state(button: Button, label: String, hint: String button.set_meta("command_label", label) button.set_meta("command_hint", hint) button.set_meta("disabled_hint", disabled_hint) - var tooltip_hint := disabled_hint if disabled and not disabled_hint.strip_edges().is_empty() else hint + button.set_meta("command_hint_detail", detail) + var tooltip_hint := _local_command_tooltip_hint(hint, detail, disabled, disabled_hint) button.tooltip_text = _format_local_command_tooltip(label, tooltip_hint) +func _local_command_tooltip_hint(hint: String, detail: String, disabled: bool, disabled_hint: String) -> String: + var normalized_hint := hint.strip_edges() + var normalized_detail := detail.strip_edges() + var normalized_disabled := disabled_hint.strip_edges() + if disabled and not normalized_disabled.is_empty(): + if normalized_detail.is_empty(): + return normalized_disabled + return "%s\n%s" % [normalized_disabled, normalized_detail] + if not normalized_detail.is_empty(): + return normalized_detail + return normalized_hint + + func _refresh_post_move_title(selected: Dictionary = {}) -> void: if post_move_title_label == null: return diff --git a/tools/smoke_post_move_action_flow.gd b/tools/smoke_post_move_action_flow.gd index 30cbd56..c3ac85b 100644 --- a/tools/smoke_post_move_action_flow.gd +++ b/tools/smoke_post_move_action_flow.gd @@ -226,6 +226,7 @@ func _check_scene_post_move_menu_flow(failures: Array[String]) -> void: scene.post_move_wait_button, scene.post_move_cancel_button ], "post-move action menu") + _check_post_move_command_guidance(failures, scene) if cao_cao.get("pos", Vector2i.ZERO) != Vector2i(2, 3) or not bool(cao_cao.get("moved", false)) or bool(cao_cao.get("acted", false)): failures.append("scene move should be pending with action still available") if scene.state.get_selected_unit().is_empty(): @@ -1138,6 +1139,54 @@ func _check_local_command_button_tooltips_present(failures: Array[String], butto failures.append("%s should explain icon function through hover tooltip" % label) +func _check_post_move_command_guidance(failures: Array[String], scene) -> void: + var expectations := [ + { + "button": scene.post_move_attack_button, + "label": "타격령", + "hint": "적 표식 지목", + "detail": "이동과 타격을 한 군령" + }, + { + "button": scene.post_move_tactic_button, + "label": "책략첩", + "hint": "죽간 펼침", + "detail": "빛나는 표식 칸" + }, + { + "button": scene.post_move_item_button, + "label": "보급첩", + "hint": "보급 지목", + "detail": "회복이나 도구를 받을 부대" + }, + { + "button": scene.post_move_wait_button, + "label": "대기령", + "hint": "그 자리 대기", + "detail": "군령을 마치고" + }, + { + "button": scene.post_move_cancel_button, + "label": "발걸음 거두기", + "hint": "이동 취소", + "detail": "원래 자리에서 다시 군령" + } + ] + for expectation in expectations: + var button: Button = expectation["button"] + if button == null: + failures.append("post-move command guidance button missing: %s" % str(expectation["label"])) + continue + if str(button.get_meta("command_label", "")) != str(expectation["label"]): + failures.append("post-move command should keep label metadata: %s" % str(button.get_meta("command_label", ""))) + if str(button.get_meta("command_hint", "")) != str(expectation["hint"]): + failures.append("post-move command should keep short hover hint `%s`: %s" % [str(expectation["hint"]), str(button.get_meta("command_hint", ""))]) + if not str(button.get_meta("command_hint_detail", "")).contains(str(expectation["detail"])): + failures.append("post-move command should keep rich hover detail `%s`: %s" % [str(expectation["detail"]), str(button.get_meta("command_hint_detail", ""))]) + if not str(button.tooltip_text).contains(str(expectation["detail"])): + failures.append("post-move command tooltip should explain `%s`: %s" % [str(expectation["detail"]), str(button.tooltip_text)]) + + func _check_post_move_picker_option_tooltips_present(failures: Array[String], scene, label: String) -> void: if scene.post_move_picker_list == null: failures.append("%s should expose a picker option list" % label) diff --git a/tools/smoke_title_menu.gd b/tools/smoke_title_menu.gd index 00a1e1f..ab98f73 100644 --- a/tools/smoke_title_menu.gd +++ b/tools/smoke_title_menu.gd @@ -307,11 +307,11 @@ func _check_title_menu_start_load_and_settings(failures: Array[String]) -> void: if opening_story_strip.custom_minimum_size.x > 340.0 or opening_story_strip.custom_minimum_size.y > 54.0: failures.append("opening story ledger should stay compact beside the chapter seal: %s" % str(opening_story_strip.custom_minimum_size)) _check_control_inside_design_view(failures, opening_story_strip as Control, "opening story ledger") - if scene.opening_prologue_next_button == null or scene.opening_prologue_next_button.text != "다음 기록": + if scene.opening_prologue_next_button == null or scene.opening_prologue_next_button.text != "기록 넘기기": failures.append("opening prologue next button should read as a story action") else: _check_opening_story_button_art(failures, scene.opening_prologue_next_button, "opening next") - if scene.opening_prologue_skip_button == null or scene.opening_prologue_skip_button.text != "서문 접기": + if scene.opening_prologue_skip_button == null or scene.opening_prologue_skip_button.text != "군막으로 바로": failures.append("opening prologue skip button should read as a contextual camp shortcut") else: _check_opening_story_button_art(failures, scene.opening_prologue_skip_button, "opening skip") @@ -537,6 +537,8 @@ func _check_title_menu_button_art(failures: Array[String], button: Button, expec failures.append("%s generated button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()]) if style.texture_margin_top > 24 or style.texture_margin_bottom > 24: failures.append("%s generated button slice should stay shallow enough for the 720p title menu: %d/%d" % [context, style.texture_margin_top, style.texture_margin_bottom]) + if style.axis_stretch_horizontal != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT or style.axis_stretch_vertical != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT: + failures.append("%s generated button surface should tile-fit its 9-slice center so QHD fullscreen does not smear the ornament" % context) _check_button_texture_slice_fit(failures, button, context) if str(button.tooltip_text).strip_edges().is_empty(): failures.append("%s should describe its command through a tooltip" % context) @@ -561,6 +563,8 @@ func _check_opening_story_button_art(failures: Array[String], button: Button, co var style := stylebox as StyleBoxTexture if style.texture_margin_left < 24 or style.texture_margin_top < 10: failures.append("%s generated button slices should preserve the ornamental frame at QHD: %d/%d" % [context, style.texture_margin_left, style.texture_margin_top]) + if style.axis_stretch_horizontal != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT or style.axis_stretch_vertical != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT: + failures.append("%s generated button surface should tile-fit its 9-slice center at QHD" % context) _check_button_texture_slice_fit(failures, button, context) if button.get_theme_font_size("font_size") > 17: failures.append("%s should stay visually quiet in the story caption" % context) diff --git a/tools/smoke_visual_assets.gd b/tools/smoke_visual_assets.gd index 7abbe7c..bd21094 100644 --- a/tools/smoke_visual_assets.gd +++ b/tools/smoke_visual_assets.gd @@ -3981,7 +3981,7 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: if scene.opening_prologue_next_button == null: failures.append("opening prologue should expose a next button") else: - if scene.opening_prologue_next_button.text != "다음 기록": + if scene.opening_prologue_next_button.text != "기록 넘기기": failures.append("opening next button should use story-fluent Korean text") if scene.opening_prologue_next_button.icon == null: failures.append("opening next button should carry generated icon art") @@ -3991,7 +3991,7 @@ func _check_opening_prologue_presentation(failures: Array[String]) -> void: if scene.opening_prologue_skip_button == null: failures.append("opening prologue should expose a skip button") else: - if scene.opening_prologue_skip_button.text != "서문 접기": + if scene.opening_prologue_skip_button.text != "군막으로 바로": failures.append("opening skip button should avoid modern skip-button wording") if scene.opening_prologue_skip_button.icon == null: failures.append("opening skip button should carry generated icon art") @@ -4441,6 +4441,8 @@ func _check_button_uses_generated_surface(failures: Array[String], button: Butto failures.append("%s generated icon button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()]) if abs(style.texture.get_width() - style.texture.get_height()) > 4: failures.append("%s generated icon button texture should be square or near-square: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()]) + if style.axis_stretch_horizontal != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT or style.axis_stretch_vertical != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT: + failures.append("%s generated icon button surface should tile-fit its center for QHD fullscreen" % context) _check_button_texture_slice_for_control(failures, button, style, context, 10, 10) if style.content_margin_left < 5 or style.content_margin_top < 5: failures.append("%s generated icon button texture should reserve icon content margins" % context) @@ -4449,6 +4451,8 @@ func _check_button_uses_generated_surface(failures: Array[String], button: Butto failures.append("%s text button should keep the wide generated button surface" % context) if style.texture.get_width() < 512 or style.texture.get_height() < 192: failures.append("%s generated button texture should keep high-resolution source pixels: %dx%d" % [context, style.texture.get_width(), style.texture.get_height()]) + if style.axis_stretch_horizontal != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT or style.axis_stretch_vertical != StyleBoxTexture.AXIS_STRETCH_MODE_TILE_FIT: + failures.append("%s generated button surface should tile-fit its 9-slice center so QHD fullscreen does not smear the ornament" % context) var min_top_margin := 10 if bool(button.get_meta("opening_story_button", false)) else 10 _check_button_texture_slice_for_control(failures, button, style, context, 16, min_top_margin) var min_content_margin := 6 if bool(button.get_meta("opening_story_button", false)) else 8