Add visual post-move picker rows
This commit is contained in:
@@ -35,6 +35,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 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.
|
- 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.
|
||||||
- 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.
|
- 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.
|
||||||
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons` and generated lacquer/jade/cinnabar button surfaces under `art/ui/buttons`, with procedural icon drawing and flat button styles kept only as fallbacks.
|
- Battle, top-toolbar, and pre-battle command buttons now prefer generated high-resolution bronze/parchment icon art under `art/ui/icons` and generated lacquer/jade/cinnabar button surfaces under `art/ui/buttons`, with procedural icon drawing and flat button styles kept only as fallbacks.
|
||||||
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop, with generated transparent feature overlays for road connections, water, villages, castles, and wasteland accents.
|
- Battle maps blend generated terrain texture tiles for plains, forests, hills, wasteland, roads, water, villages, and castles over the high-resolution battlefield backdrop, with generated transparent feature overlays for road connections, water, villages, castles, and wasteland accents.
|
||||||
- Core UI panels now prefer generated high-resolution lacquer, scroll, jade, and command-seal frame textures under `art/ui/panels`, with the calmer ink/wood/jade flat styles kept as fallbacks instead of the earlier yellow, hard-edged frame treatment.
|
- Core UI panels now prefer generated high-resolution lacquer, scroll, jade, and command-seal frame textures under `art/ui/panels`, with the calmer ink/wood/jade flat styles kept as fallbacks instead of the earlier yellow, hard-edged frame treatment.
|
||||||
|
|||||||
@@ -183,6 +183,8 @@ const POST_MOVE_TITLE_SIZE := Vector2(196, 22)
|
|||||||
const POST_MOVE_PICKER_PANEL_SIZE := Vector2(336, 236)
|
const POST_MOVE_PICKER_PANEL_SIZE := Vector2(336, 236)
|
||||||
const POST_MOVE_PICKER_TITLE_SIZE := Vector2(296, 22)
|
const POST_MOVE_PICKER_TITLE_SIZE := Vector2(296, 22)
|
||||||
const POST_MOVE_PICKER_DETAIL_SIZE := Vector2(296, 32)
|
const POST_MOVE_PICKER_DETAIL_SIZE := Vector2(296, 32)
|
||||||
|
const POST_MOVE_PICKER_OPTION_SIZE := Vector2(296, 46)
|
||||||
|
const POST_MOVE_PICKER_OPTION_ICON_MAX_WIDTH := 34
|
||||||
const TARGETING_HINT_PANEL_SIZE := Vector2(254, 118)
|
const TARGETING_HINT_PANEL_SIZE := Vector2(254, 118)
|
||||||
const TARGETING_HINT_TITLE_SIZE := Vector2(220, 22)
|
const TARGETING_HINT_TITLE_SIZE := Vector2(220, 22)
|
||||||
const TARGETING_HINT_DETAIL_SIZE := Vector2(220, 28)
|
const TARGETING_HINT_DETAIL_SIZE := Vector2(220, 28)
|
||||||
@@ -14910,12 +14912,8 @@ func _rebuild_post_move_tactic_picker(selected: Dictionary) -> void:
|
|||||||
for skill_id_value in skill_ids:
|
for skill_id_value in skill_ids:
|
||||||
var skill_id := str(skill_id_value)
|
var skill_id := str(skill_id_value)
|
||||||
var skill := state.get_skill_def(skill_id)
|
var skill := state.get_skill_def(skill_id)
|
||||||
var skill_button := Button.new()
|
|
||||||
skill_button.text = _format_post_move_tactic_button_text(skill_id, skill, selected)
|
|
||||||
var disabled_reason := _post_move_tactic_disabled_reason(selected, skill_id, skill)
|
var disabled_reason := _post_move_tactic_disabled_reason(selected, skill_id, skill)
|
||||||
skill_button.disabled = not disabled_reason.is_empty()
|
var skill_button := _make_post_move_tactic_option_button(skill_id, skill, disabled_reason)
|
||||||
_suppress_local_command_tooltip(skill_button)
|
|
||||||
_apply_button_style(skill_button)
|
|
||||||
skill_button.pressed.connect(_on_post_move_tactic_option_pressed.bind(skill_id))
|
skill_button.pressed.connect(_on_post_move_tactic_option_pressed.bind(skill_id))
|
||||||
post_move_picker_list.add_child(skill_button)
|
post_move_picker_list.add_child(skill_button)
|
||||||
|
|
||||||
@@ -14933,17 +14931,115 @@ func _rebuild_post_move_item_picker(selected: Dictionary) -> void:
|
|||||||
var item_id := str(item_id_value)
|
var item_id := str(item_id_value)
|
||||||
var item := state.get_item_def(item_id)
|
var item := state.get_item_def(item_id)
|
||||||
var count := int(inventory.get(item_id, 0))
|
var count := int(inventory.get(item_id, 0))
|
||||||
var item_button := Button.new()
|
|
||||||
item_button.text = _format_post_move_item_button_text(item_id, item, count)
|
|
||||||
_apply_item_button_icon(item_button, item)
|
|
||||||
var disabled_reason := _post_move_item_disabled_reason(selected, item_id, count)
|
var disabled_reason := _post_move_item_disabled_reason(selected, item_id, count)
|
||||||
item_button.disabled = not disabled_reason.is_empty()
|
var item_button := _make_post_move_item_option_button(item_id, item, count, disabled_reason)
|
||||||
_suppress_local_command_tooltip(item_button)
|
|
||||||
_apply_button_style(item_button)
|
|
||||||
item_button.pressed.connect(_on_post_move_item_option_pressed.bind(item_id))
|
item_button.pressed.connect(_on_post_move_item_option_pressed.bind(item_id))
|
||||||
post_move_picker_list.add_child(item_button)
|
post_move_picker_list.add_child(item_button)
|
||||||
|
|
||||||
|
|
||||||
|
func _make_post_move_tactic_option_button(skill_id: String, skill: Dictionary, disabled_reason: String) -> Button:
|
||||||
|
var skill_name := str(skill.get("name", skill_id))
|
||||||
|
var mp_cost := int(skill.get("mp_cost", 0))
|
||||||
|
var kind_text := _skill_kind_text(str(skill.get("kind", "skill")))
|
||||||
|
var range_text := _format_tactic_range_text(skill)
|
||||||
|
var effect_text := _format_tactic_effect_text(skill)
|
||||||
|
var detail_text := "%s · 거리 %s" % [kind_text, range_text]
|
||||||
|
if not effect_text.is_empty():
|
||||||
|
detail_text = "%s · %s" % [detail_text, effect_text]
|
||||||
|
if not disabled_reason.is_empty():
|
||||||
|
detail_text = "%s · %s" % [detail_text, _post_move_disabled_summary(disabled_reason)]
|
||||||
|
|
||||||
|
var button := Button.new()
|
||||||
|
button.text = "%s 기력 %d\n%s" % [skill_name, mp_cost, detail_text]
|
||||||
|
button.tooltip_text = _format_post_move_tactic_option_tooltip(skill_name, mp_cost, kind_text, range_text, effect_text, disabled_reason)
|
||||||
|
button.disabled = not disabled_reason.is_empty()
|
||||||
|
button.custom_minimum_size = POST_MOVE_PICKER_OPTION_SIZE
|
||||||
|
button.icon = _post_move_tactic_icon_texture(skill)
|
||||||
|
button.expand_icon = false
|
||||||
|
button.add_theme_constant_override("icon_max_width", POST_MOVE_PICKER_OPTION_ICON_MAX_WIDTH)
|
||||||
|
button.set_meta("picker_icon", _post_move_tactic_icon_kind(skill))
|
||||||
|
button.set_meta("picker_label", skill_name)
|
||||||
|
_apply_button_style(button)
|
||||||
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
func _make_post_move_item_option_button(item_id: String, item: Dictionary, count: int, disabled_reason: String) -> Button:
|
||||||
|
var item_name := _item_display_name(item_id)
|
||||||
|
var effect_text := _format_post_move_item_effect_text(item)
|
||||||
|
var detail_text := "잔량 %d · %s" % [count, effect_text]
|
||||||
|
if not disabled_reason.is_empty():
|
||||||
|
detail_text = "%s · %s" % [detail_text, _post_move_disabled_summary(disabled_reason)]
|
||||||
|
|
||||||
|
var button := Button.new()
|
||||||
|
button.text = "%s\n%s" % [item_name, detail_text]
|
||||||
|
button.tooltip_text = _format_post_move_item_option_tooltip(item_name, count, effect_text, disabled_reason)
|
||||||
|
button.disabled = not disabled_reason.is_empty()
|
||||||
|
button.custom_minimum_size = POST_MOVE_PICKER_OPTION_SIZE
|
||||||
|
button.icon = _post_move_item_icon_texture(item)
|
||||||
|
button.expand_icon = false
|
||||||
|
button.add_theme_constant_override("icon_max_width", POST_MOVE_PICKER_OPTION_ICON_MAX_WIDTH)
|
||||||
|
button.set_meta("picker_icon", "item")
|
||||||
|
button.set_meta("picker_label", item_name)
|
||||||
|
_apply_button_style(button)
|
||||||
|
return button
|
||||||
|
|
||||||
|
|
||||||
|
func _post_move_tactic_icon_kind(skill: Dictionary) -> String:
|
||||||
|
var kind := str(skill.get("kind", "skill")).strip_edges().to_lower()
|
||||||
|
if kind == "damage":
|
||||||
|
return "attack"
|
||||||
|
if kind == "heal":
|
||||||
|
return "item"
|
||||||
|
if kind == "support":
|
||||||
|
return "status"
|
||||||
|
return "tactic"
|
||||||
|
|
||||||
|
|
||||||
|
func _post_move_tactic_icon_texture(skill: Dictionary) -> Texture2D:
|
||||||
|
return _make_toolbar_icon(_post_move_tactic_icon_kind(skill))
|
||||||
|
|
||||||
|
|
||||||
|
func _post_move_item_icon_texture(item: Dictionary) -> Texture2D:
|
||||||
|
var icon := _load_art_texture(str(item.get("icon", "")))
|
||||||
|
if icon != null:
|
||||||
|
return icon
|
||||||
|
return _make_toolbar_icon("item")
|
||||||
|
|
||||||
|
|
||||||
|
func _format_post_move_tactic_option_tooltip(skill_name: String, mp_cost: int, kind_text: String, range_text: String, effect_text: String, disabled_reason: String) -> String:
|
||||||
|
var lines: Array[String] = [
|
||||||
|
skill_name,
|
||||||
|
"기력 %d · %s · 사거리 %s" % [mp_cost, kind_text, range_text]
|
||||||
|
]
|
||||||
|
if not effect_text.is_empty():
|
||||||
|
lines.append(effect_text)
|
||||||
|
if not disabled_reason.is_empty():
|
||||||
|
lines.append(disabled_reason)
|
||||||
|
return _join_strings(lines, "\n")
|
||||||
|
|
||||||
|
|
||||||
|
func _format_post_move_item_option_tooltip(item_name: String, count: int, effect_text: String, disabled_reason: String) -> String:
|
||||||
|
var lines: Array[String] = [
|
||||||
|
item_name,
|
||||||
|
"잔량 %d · %s" % [count, effect_text]
|
||||||
|
]
|
||||||
|
if not disabled_reason.is_empty():
|
||||||
|
lines.append(disabled_reason)
|
||||||
|
return _join_strings(lines, "\n")
|
||||||
|
|
||||||
|
|
||||||
|
func _post_move_disabled_summary(disabled_reason: String) -> String:
|
||||||
|
if disabled_reason.contains("기력"):
|
||||||
|
return "기력 부족"
|
||||||
|
if disabled_reason.contains("봉인"):
|
||||||
|
return "봉인"
|
||||||
|
if disabled_reason.contains("대상"):
|
||||||
|
return "표식 없음"
|
||||||
|
if disabled_reason.contains("보급") or disabled_reason.contains("없습니다"):
|
||||||
|
return "잔량 없음"
|
||||||
|
return "불가"
|
||||||
|
|
||||||
|
|
||||||
func _format_post_move_tactic_button_text(skill_id: String, skill: Dictionary, selected: Dictionary) -> String:
|
func _format_post_move_tactic_button_text(skill_id: String, skill: Dictionary, selected: Dictionary) -> String:
|
||||||
var marker := "> " if selected_skill_id == skill_id else ""
|
var marker := "> " if selected_skill_id == skill_id else ""
|
||||||
var skill_name := str(skill.get("name", skill_id))
|
var skill_name := str(skill.get("name", skill_id))
|
||||||
|
|||||||
@@ -832,6 +832,7 @@ func _check_scene_post_move_tactic_picker_flow(failures: Array[String]) -> void:
|
|||||||
if scene.tactic_menu != null and scene.tactic_menu.visible:
|
if scene.tactic_menu != null and scene.tactic_menu.visible:
|
||||||
failures.append("post-move tactic shortcut should not use the side tactic menu")
|
failures.append("post-move tactic shortcut should not use the side tactic menu")
|
||||||
_check_post_move_picker_option_tooltips_present(failures, scene, "post-move tactic picker")
|
_check_post_move_picker_option_tooltips_present(failures, scene, "post-move tactic picker")
|
||||||
|
_check_post_move_picker_option_visuals(failures, scene, "post-move tactic picker")
|
||||||
|
|
||||||
var escape := InputEventKey.new()
|
var escape := InputEventKey.new()
|
||||||
escape.keycode = KEY_ESCAPE
|
escape.keycode = KEY_ESCAPE
|
||||||
@@ -941,6 +942,7 @@ func _check_scene_post_move_item_picker_flow(failures: Array[String]) -> void:
|
|||||||
if scene.item_menu != null and scene.item_menu.visible:
|
if scene.item_menu != null and scene.item_menu.visible:
|
||||||
failures.append("post-move item shortcut should not use the side item menu")
|
failures.append("post-move item shortcut should not use the side item menu")
|
||||||
_check_post_move_picker_option_tooltips_present(failures, scene, "post-move item picker")
|
_check_post_move_picker_option_tooltips_present(failures, scene, "post-move item picker")
|
||||||
|
_check_post_move_picker_option_visuals(failures, scene, "post-move item picker")
|
||||||
|
|
||||||
scene._on_post_move_item_option_pressed("bean")
|
scene._on_post_move_item_option_pressed("bean")
|
||||||
if scene.selected_item_id != "bean":
|
if scene.selected_item_id != "bean":
|
||||||
@@ -1027,6 +1029,28 @@ func _check_post_move_picker_option_tooltips_present(failures: Array[String], sc
|
|||||||
failures.append("%s option should explain its function through hover tooltip" % label)
|
failures.append("%s option should explain its function through hover tooltip" % label)
|
||||||
|
|
||||||
|
|
||||||
|
func _check_post_move_picker_option_visuals(failures: Array[String], scene, label: String) -> void:
|
||||||
|
if scene.post_move_picker_list == null:
|
||||||
|
failures.append("%s should expose a visual picker option list" % label)
|
||||||
|
return
|
||||||
|
var saw_button := false
|
||||||
|
for child in scene.post_move_picker_list.get_children():
|
||||||
|
if not child is Button:
|
||||||
|
continue
|
||||||
|
saw_button = true
|
||||||
|
var button := child as Button
|
||||||
|
if button.icon == null or str(button.get_meta("picker_icon", "")).strip_edges().is_empty():
|
||||||
|
failures.append("%s option should carry an immediate generated icon" % label)
|
||||||
|
if button.custom_minimum_size.x < BattleSceneScript.POST_MOVE_PICKER_OPTION_SIZE.x or button.custom_minimum_size.y < BattleSceneScript.POST_MOVE_PICKER_OPTION_SIZE.y:
|
||||||
|
failures.append("%s option should reserve stable visual row space: %s" % [label, str(button.custom_minimum_size)])
|
||||||
|
if not button.text.contains("\n"):
|
||||||
|
failures.append("%s option should use compact two-line visual text: %s" % [label, button.text])
|
||||||
|
if button.get_theme_constant("icon_max_width") < BattleSceneScript.POST_MOVE_PICKER_OPTION_ICON_MAX_WIDTH:
|
||||||
|
failures.append("%s option should reserve readable icon width" % label)
|
||||||
|
if not saw_button:
|
||||||
|
failures.append("%s should create at least one visual option button" % label)
|
||||||
|
|
||||||
|
|
||||||
func _check_fitted_label_font(failures: Array[String], label: Label, base_size: int, min_size: int, label_name: String) -> void:
|
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")
|
var font_size := label.get_theme_font_size("font_size")
|
||||||
if font_size < min_size or font_size > base_size:
|
if font_size < min_size or font_size > base_size:
|
||||||
|
|||||||
Reference in New Issue
Block a user