Improve unit tokens and local tooltips
This commit is contained in:
@@ -68,6 +68,7 @@ const UNIT_STATUS_MARKER_RADIUS := 6.0
|
|||||||
const UNIT_STATUS_MARKER_GAP := 14.0
|
const UNIT_STATUS_MARKER_GAP := 14.0
|
||||||
const UNIT_STATUS_MARKER_OFFSET_Y := 13.0
|
const UNIT_STATUS_MARKER_OFFSET_Y := 13.0
|
||||||
const MAX_UNIT_STATUS_MARKERS := 4
|
const MAX_UNIT_STATUS_MARKERS := 4
|
||||||
|
const UNIT_MAP_SPRITE_TARGET_SIZE := Vector2(46, 48)
|
||||||
const UNIT_ATTACK_ANIMATION_DURATION := 0.30
|
const UNIT_ATTACK_ANIMATION_DURATION := 0.30
|
||||||
const UNIT_ATTACK_LUNGE_DISTANCE := 15.0
|
const UNIT_ATTACK_LUNGE_DISTANCE := 15.0
|
||||||
const UNIT_INFANTRY_ATTACK_DURATION := 0.32
|
const UNIT_INFANTRY_ATTACK_DURATION := 0.32
|
||||||
@@ -456,6 +457,7 @@ var unit_idle_time := 0.0
|
|||||||
|
|
||||||
|
|
||||||
func _ready() -> void:
|
func _ready() -> void:
|
||||||
|
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
|
||||||
_create_hud()
|
_create_hud()
|
||||||
_create_audio()
|
_create_audio()
|
||||||
state.changed.connect(_on_state_changed)
|
state.changed.connect(_on_state_changed)
|
||||||
@@ -799,7 +801,7 @@ func _apply_button_style(button: Button, important: bool = false) -> void:
|
|||||||
func _suppress_local_command_tooltip(button: Button) -> void:
|
func _suppress_local_command_tooltip(button: Button) -> void:
|
||||||
if button == null:
|
if button == null:
|
||||||
return
|
return
|
||||||
button.tooltip_text = ""
|
button.tooltip_text = button.text.strip_edges()
|
||||||
|
|
||||||
|
|
||||||
func _configure_hud_command_button(button: Button, icon_kind: String = "") -> void:
|
func _configure_hud_command_button(button: Button, icon_kind: String = "") -> void:
|
||||||
@@ -835,7 +837,7 @@ func _configure_local_command_button(button: Button, icon_kind: String, label: S
|
|||||||
if button == null:
|
if button == null:
|
||||||
return
|
return
|
||||||
button.text = ""
|
button.text = ""
|
||||||
button.tooltip_text = ""
|
button.tooltip_text = _format_local_command_tooltip(label, hint)
|
||||||
button.custom_minimum_size = minimum_size
|
button.custom_minimum_size = minimum_size
|
||||||
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
button.size_flags_horizontal = Control.SIZE_EXPAND_FILL
|
||||||
button.icon = _make_toolbar_icon(icon_kind)
|
button.icon = _make_toolbar_icon(icon_kind)
|
||||||
@@ -849,6 +851,16 @@ func _configure_local_command_button(button: Button, icon_kind: String, label: S
|
|||||||
button.mouse_exited.connect(_on_local_command_button_mouse_exited)
|
button.mouse_exited.connect(_on_local_command_button_mouse_exited)
|
||||||
|
|
||||||
|
|
||||||
|
func _format_local_command_tooltip(label: String, hint: String) -> String:
|
||||||
|
var normalized_label := label.strip_edges()
|
||||||
|
var normalized_hint := hint.strip_edges()
|
||||||
|
if normalized_hint.is_empty():
|
||||||
|
return normalized_label
|
||||||
|
if normalized_label.is_empty():
|
||||||
|
return normalized_hint
|
||||||
|
return "%s\n%s" % [normalized_label, normalized_hint]
|
||||||
|
|
||||||
|
|
||||||
func _make_toolbar_icon(kind: String) -> Texture2D:
|
func _make_toolbar_icon(kind: String) -> Texture2D:
|
||||||
var image := Image.create(32, 32, false, Image.FORMAT_RGBA8)
|
var image := Image.create(32, 32, false, Image.FORMAT_RGBA8)
|
||||||
image.fill(Color(0, 0, 0, 0))
|
image.fill(Color(0, 0, 0, 0))
|
||||||
@@ -1422,6 +1434,7 @@ func _refresh_screen_backdrop_visibility() -> void:
|
|||||||
|
|
||||||
|
|
||||||
func _create_hud() -> void:
|
func _create_hud() -> void:
|
||||||
|
texture_filter = CanvasItem.TEXTURE_FILTER_LINEAR
|
||||||
var layer := CanvasLayer.new()
|
var layer := CanvasLayer.new()
|
||||||
add_child(layer)
|
add_child(layer)
|
||||||
|
|
||||||
@@ -3713,9 +3726,10 @@ func _draw_units() -> void:
|
|||||||
var acted := bool(unit.get("acted", false))
|
var acted := bool(unit.get("acted", false))
|
||||||
var sprite_modulate := _unit_sprite_modulate(unit, acted)
|
var sprite_modulate := _unit_sprite_modulate(unit, acted)
|
||||||
|
|
||||||
draw_circle(center + Vector2(0, 17), 20, Color(0.0, 0.0, 0.0, 0.30))
|
draw_circle(center + Vector2(0, 18), 22, Color(0.0, 0.0, 0.0, 0.28))
|
||||||
_draw_unit_class_emblem(rect, center, unit, team_color)
|
_draw_unit_class_emblem(rect, center, unit, team_color)
|
||||||
_draw_pixel_unit_sprite(rect, unit, team_color, sprite_modulate, _unit_pixel_facing(unit))
|
if not _draw_high_res_unit_sprite(rect, unit, sprite_modulate, _unit_pixel_facing(unit)):
|
||||||
|
_draw_pixel_unit_sprite(rect, unit, team_color, sprite_modulate, _unit_pixel_facing(unit))
|
||||||
_draw_unit_identity_marks(rect, center, unit, team_color)
|
_draw_unit_identity_marks(rect, center, unit, team_color)
|
||||||
_draw_unit_class_badge(rect, unit, team_color)
|
_draw_unit_class_badge(rect, unit, team_color)
|
||||||
_draw_unit_facing_marker(rect, unit, team_color, sprite_modulate)
|
_draw_unit_facing_marker(rect, unit, team_color, sprite_modulate)
|
||||||
@@ -4097,6 +4111,36 @@ func _unit_sprite_modulate(unit: Dictionary, acted: bool) -> Color:
|
|||||||
return Color.WHITE
|
return Color.WHITE
|
||||||
|
|
||||||
|
|
||||||
|
func _draw_high_res_unit_sprite(rect: Rect2, unit: Dictionary, modulate: Color, facing: int = 1) -> bool:
|
||||||
|
var texture := _load_art_texture(str(unit.get("sprite", "")))
|
||||||
|
if texture == null:
|
||||||
|
return false
|
||||||
|
var sprite_rect := _unit_map_sprite_rect(texture, rect, unit)
|
||||||
|
if sprite_rect.size.x <= 0.0 or sprite_rect.size.y <= 0.0:
|
||||||
|
return false
|
||||||
|
var safe_facing := -1 if facing < 0 else 1
|
||||||
|
if safe_facing < 0:
|
||||||
|
draw_set_transform(Vector2(sprite_rect.position.x * 2.0 + sprite_rect.size.x, 0.0), 0.0, Vector2(-1.0, 1.0))
|
||||||
|
draw_texture_rect(texture, sprite_rect, false, modulate)
|
||||||
|
if safe_facing < 0:
|
||||||
|
draw_set_transform(Vector2.ZERO, 0.0, Vector2.ONE)
|
||||||
|
return true
|
||||||
|
|
||||||
|
|
||||||
|
func _unit_map_sprite_rect(texture: Texture2D, rect: Rect2, unit: Dictionary) -> Rect2:
|
||||||
|
if texture == null:
|
||||||
|
return Rect2()
|
||||||
|
var family := _unit_class_family(unit)
|
||||||
|
var phase := _unit_idle_phase(unit)
|
||||||
|
var intensity := _unit_idle_intensity(unit)
|
||||||
|
var bob := _unit_idle_bob(family, phase, intensity) * 0.42
|
||||||
|
var target := Rect2(
|
||||||
|
rect.position + Vector2((TILE_SIZE - UNIT_MAP_SPRITE_TARGET_SIZE.x) * 0.5, 1.0 + bob),
|
||||||
|
UNIT_MAP_SPRITE_TARGET_SIZE
|
||||||
|
)
|
||||||
|
return _fit_texture_rect(texture, target)
|
||||||
|
|
||||||
|
|
||||||
func _draw_pixel_unit_sprite(rect: Rect2, unit: Dictionary, team_color: Color, modulate: Color, facing: int = 1) -> void:
|
func _draw_pixel_unit_sprite(rect: Rect2, unit: Dictionary, team_color: Color, modulate: Color, facing: int = 1) -> void:
|
||||||
var profile := _pixel_unit_sprite_profile(unit)
|
var profile := _pixel_unit_sprite_profile(unit)
|
||||||
var family := str(profile.get("family", _unit_class_family(unit)))
|
var family := str(profile.get("family", _unit_class_family(unit)))
|
||||||
@@ -10209,10 +10253,11 @@ func _set_local_command_button_state(button: Button, label: String, hint: String
|
|||||||
return
|
return
|
||||||
button.text = ""
|
button.text = ""
|
||||||
button.disabled = disabled
|
button.disabled = disabled
|
||||||
button.tooltip_text = ""
|
|
||||||
button.set_meta("command_label", label)
|
button.set_meta("command_label", label)
|
||||||
button.set_meta("command_hint", hint)
|
button.set_meta("command_hint", hint)
|
||||||
button.set_meta("disabled_hint", disabled_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.tooltip_text = _format_local_command_tooltip(label, tooltip_hint)
|
||||||
|
|
||||||
|
|
||||||
func _refresh_post_move_title(selected: Dictionary = {}) -> void:
|
func _refresh_post_move_title(selected: Dictionary = {}) -> void:
|
||||||
|
|||||||
@@ -107,7 +107,7 @@ func _check_scene_post_move_menu_flow(failures: Array[String]) -> void:
|
|||||||
failures.append("post-move action menu should be visible after moving")
|
failures.append("post-move action menu should be visible after moving")
|
||||||
else:
|
else:
|
||||||
_check_local_panel_position(failures, scene, scene.post_move_menu, Vector2i(2, 3), "post-move action menu")
|
_check_local_panel_position(failures, scene, scene.post_move_menu, Vector2i(2, 3), "post-move action menu")
|
||||||
_check_local_command_button_tooltips_suppressed(failures, [
|
_check_local_command_button_tooltips_present(failures, [
|
||||||
scene.post_move_attack_button,
|
scene.post_move_attack_button,
|
||||||
scene.post_move_tactic_button,
|
scene.post_move_tactic_button,
|
||||||
scene.post_move_item_button,
|
scene.post_move_item_button,
|
||||||
@@ -294,7 +294,7 @@ func _check_scene_post_move_text_fit(failures: Array[String]) -> void:
|
|||||||
failures.append("post-move picker should expose a fitted detail label")
|
failures.append("post-move picker should expose a fitted detail label")
|
||||||
else:
|
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")
|
_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")
|
||||||
_check_local_command_button_tooltips_suppressed(failures, [
|
_check_local_command_button_tooltips_present(failures, [
|
||||||
scene.post_move_picker_back_button,
|
scene.post_move_picker_back_button,
|
||||||
scene.post_move_picker_cancel_move_button,
|
scene.post_move_picker_cancel_move_button,
|
||||||
scene.targeting_hint_back_button,
|
scene.targeting_hint_back_button,
|
||||||
@@ -358,7 +358,7 @@ func _check_scene_post_move_blocked_command_labels(failures: Array[String]) -> v
|
|||||||
failures.append("blocked tactic command should explain itself through local hover metadata")
|
failures.append("blocked tactic command should explain itself through local hover metadata")
|
||||||
if scene.post_move_item_button == null or not scene.post_move_item_button.disabled or scene.post_move_item_button.text != "" or str(scene.post_move_item_button.get_meta("disabled_hint", "")) != "보급 없음":
|
if scene.post_move_item_button == null or not scene.post_move_item_button.disabled or scene.post_move_item_button.text != "" or str(scene.post_move_item_button.get_meta("disabled_hint", "")) != "보급 없음":
|
||||||
failures.append("blocked item command should explain itself through local hover metadata")
|
failures.append("blocked item command should explain itself through local hover metadata")
|
||||||
_check_local_command_button_tooltips_suppressed(failures, [
|
_check_local_command_button_tooltips_present(failures, [
|
||||||
scene.post_move_attack_button,
|
scene.post_move_attack_button,
|
||||||
scene.post_move_tactic_button,
|
scene.post_move_tactic_button,
|
||||||
scene.post_move_item_button
|
scene.post_move_item_button
|
||||||
@@ -768,7 +768,7 @@ func _check_scene_post_move_tactic_picker_flow(failures: Array[String]) -> void:
|
|||||||
failures.append("post-move action menu should hide while the tactic picker is open")
|
failures.append("post-move action menu should hide while the tactic picker is open")
|
||||||
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_suppressed(failures, scene, "post-move tactic picker")
|
_check_post_move_picker_option_tooltips_present(failures, scene, "post-move tactic picker")
|
||||||
|
|
||||||
var escape := InputEventKey.new()
|
var escape := InputEventKey.new()
|
||||||
escape.keycode = KEY_ESCAPE
|
escape.keycode = KEY_ESCAPE
|
||||||
@@ -877,7 +877,7 @@ func _check_scene_post_move_item_picker_flow(failures: Array[String]) -> void:
|
|||||||
failures.append("post-move action menu should hide while the item picker is open")
|
failures.append("post-move action menu should hide while the item picker is open")
|
||||||
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_suppressed(failures, scene, "post-move item picker")
|
_check_post_move_picker_option_tooltips_present(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":
|
||||||
@@ -946,22 +946,22 @@ 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)])
|
failures.append("%s should not cover the moved unit cell: %s over %s" % [label, str(panel_rect), str(cell_rect)])
|
||||||
|
|
||||||
|
|
||||||
func _check_local_command_button_tooltips_suppressed(failures: Array[String], buttons: Array, label: String) -> void:
|
func _check_local_command_button_tooltips_present(failures: Array[String], buttons: Array, label: String) -> void:
|
||||||
for button in buttons:
|
for button in buttons:
|
||||||
if button == null:
|
if button == null:
|
||||||
failures.append("%s should expose local command buttons for tooltip checks" % label)
|
failures.append("%s should expose local command buttons for tooltip checks" % label)
|
||||||
continue
|
continue
|
||||||
if not str(button.tooltip_text).is_empty():
|
if str(button.tooltip_text).strip_edges().is_empty():
|
||||||
failures.append("%s should keep guidance on the map instead of native tooltips: %s" % [label, str(button.tooltip_text)])
|
failures.append("%s should explain icon function through hover tooltip" % label)
|
||||||
|
|
||||||
|
|
||||||
func _check_post_move_picker_option_tooltips_suppressed(failures: Array[String], scene, label: String) -> void:
|
func _check_post_move_picker_option_tooltips_present(failures: Array[String], scene, label: String) -> void:
|
||||||
if scene.post_move_picker_list == null:
|
if scene.post_move_picker_list == null:
|
||||||
failures.append("%s should expose a picker option list" % label)
|
failures.append("%s should expose a picker option list" % label)
|
||||||
return
|
return
|
||||||
for child in scene.post_move_picker_list.get_children():
|
for child in scene.post_move_picker_list.get_children():
|
||||||
if child is Button and not str(child.tooltip_text).is_empty():
|
if child is Button and str(child.tooltip_text).strip_edges().is_empty():
|
||||||
failures.append("%s option should not rely on native tooltip popups: %s" % [label, str(child.tooltip_text)])
|
failures.append("%s option should explain its function through hover tooltip" % 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:
|
||||||
|
|||||||
@@ -3465,8 +3465,8 @@ func _check_local_command_icon_button(failures: Array[String], button: Button, e
|
|||||||
failures.append("local command should carry an immediate visual icon: %s icon=%s meta=%s" % [expected_label, str(button.icon), str(button.get_meta("command_icon", ""))])
|
failures.append("local command should carry an immediate visual icon: %s icon=%s meta=%s" % [expected_label, str(button.icon), str(button.get_meta("command_icon", ""))])
|
||||||
if str(button.get_meta("command_label", "")) != expected_label:
|
if str(button.get_meta("command_label", "")) != expected_label:
|
||||||
failures.append("local command should keep old wording in hover metadata: %s meta=%s" % [expected_label, str(button.get_meta("command_label", ""))])
|
failures.append("local command should keep old wording in hover metadata: %s meta=%s" % [expected_label, str(button.get_meta("command_label", ""))])
|
||||||
if not str(button.tooltip_text).is_empty():
|
if str(button.tooltip_text).strip_edges().is_empty() or not str(button.tooltip_text).contains(expected_label):
|
||||||
failures.append("local command should keep guidance in the local panel instead of native tooltip: %s" % expected_label)
|
failures.append("local command should expose its function through hover tooltip: %s tooltip=%s" % [expected_label, str(button.tooltip_text)])
|
||||||
if button.custom_minimum_size.x <= 0.0 or button.custom_minimum_size.y <= 0.0:
|
if button.custom_minimum_size.x <= 0.0 or button.custom_minimum_size.y <= 0.0:
|
||||||
failures.append("local command should reserve stable icon space: %s" % expected_label)
|
failures.append("local command should reserve stable icon space: %s" % expected_label)
|
||||||
|
|
||||||
@@ -3886,6 +3886,17 @@ func _check_terrain_and_unit_presentation(failures: Array[String]) -> void:
|
|||||||
var acted_modulate: Color = scene._unit_sprite_modulate(generic_enemy, true)
|
var acted_modulate: Color = scene._unit_sprite_modulate(generic_enemy, true)
|
||||||
if acted_modulate.a >= enemy_modulate.a:
|
if acted_modulate.a >= enemy_modulate.a:
|
||||||
failures.append("acted generic enemy sprite should still dim below active tint")
|
failures.append("acted generic enemy sprite should still dim below active tint")
|
||||||
|
var sprite_texture := scene._load_art_texture(str(named_officer.get("sprite", "")))
|
||||||
|
if sprite_texture == null:
|
||||||
|
failures.append("named officer high-res map sprite should load")
|
||||||
|
else:
|
||||||
|
var sprite_rect: Rect2 = scene._unit_map_sprite_rect(sprite_texture, scene._rect_for_cell(Vector2i(1, 6)), named_officer)
|
||||||
|
if sprite_rect.size.x < 24.0 or sprite_rect.size.y < 36.0:
|
||||||
|
failures.append("high-res map sprite should occupy a readable token area: %s" % str(sprite_rect))
|
||||||
|
if sprite_rect.position.x < scene._rect_for_cell(Vector2i(1, 6)).position.x or sprite_rect.end.x > scene._rect_for_cell(Vector2i(1, 6)).end.x:
|
||||||
|
failures.append("high-res map sprite should stay inside its tile horizontally: %s" % str(sprite_rect))
|
||||||
|
if scene.texture_filter != CanvasItem.TEXTURE_FILTER_LINEAR:
|
||||||
|
failures.append("battle scene should use linear texture filtering for smoother high-res map sprites")
|
||||||
scene.free()
|
scene.free()
|
||||||
|
|
||||||
|
|
||||||
|
|||||||
Reference in New Issue
Block a user