Improve battle marker readability

This commit is contained in:
2026-06-19 13:06:05 +09:00
parent 8ba83cc985
commit 761e571901
2 changed files with 121 additions and 4 deletions

View File

@@ -50,9 +50,9 @@ const OBJECTIVE_NOTICE_DURATION := 2.6
const UNIT_MOVE_ANIMATION_DURATION := 0.18
const TARGET_PREVIEW_BADGE_SIZE := Vector2(92, 22)
const LOW_HP_WARNING_RATIO := 0.35
const UNIT_STATUS_MARKER_RADIUS := 4.0
const UNIT_STATUS_MARKER_GAP := 9.0
const UNIT_STATUS_MARKER_OFFSET_Y := 14.0
const UNIT_STATUS_MARKER_RADIUS := 6.0
const UNIT_STATUS_MARKER_GAP := 14.0
const UNIT_STATUS_MARKER_OFFSET_Y := 13.0
const MAX_UNIT_STATUS_MARKERS := 4
const UNIT_ATTACK_ANIMATION_DURATION := 0.30
const UNIT_ATTACK_LUNGE_DISTANCE := 15.0
@@ -401,6 +401,7 @@ func _draw() -> void:
_draw_map()
_draw_overlays()
_draw_units()
_draw_objective_foreground_markers()
_draw_target_selection_markers()
_draw_attack_effects()
_draw_target_preview_badge()
@@ -629,6 +630,14 @@ func _draw_ui_font(bold: bool = false) -> Font:
return ThemeDB.fallback_font
func _draw_ink_text(font: Font, position: Vector2, text: String, alignment: HorizontalAlignment, width: float, font_size: int, color: Color, outline_color := Color(0.02, 0.012, 0.006, 0.95)) -> void:
if text.is_empty():
return
for offset in [Vector2(-1, 0), Vector2(1, 0), Vector2(0, -1), Vector2(0, 1), Vector2(1, 1)]:
draw_string(font, position + offset, text, alignment, width, font_size, outline_color)
draw_string(font, position, text, alignment, width, font_size, color)
func _apply_control_font(control: Control, bold: bool = false) -> void:
if control == null:
return
@@ -2831,6 +2840,45 @@ func _draw_objective_seal_marker(rect: Rect2) -> void:
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 _draw_objective_foreground_markers() -> void:
var font := _draw_ui_font(true)
for cell in state.get_objective_cells():
if not state.is_inside(cell):
continue
var rect := _rect_for_cell(cell)
_draw_objective_corner_brackets(rect)
var label_rect := Rect2(rect.position + Vector2(rect.size.x - 20.0, 3.0), Vector2(17.0, 16.0))
draw_rect(label_rect, Color(UI_SEAL_RED_DARK.r, UI_SEAL_RED_DARK.g, UI_SEAL_RED_DARK.b, 0.86))
draw_rect(label_rect, Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.88), false, 1.2)
_draw_ink_text(font, label_rect.position + Vector2(0, 12), "", HORIZONTAL_ALIGNMENT_CENTER, label_rect.size.x, 11, UI_PARCHMENT_TEXT)
func _draw_objective_corner_brackets(rect: Rect2) -> void:
var bracket := 12.0
var inset := 4.0
var line_color := Color(UI_OLD_BRONZE.r, UI_OLD_BRONZE.g, UI_OLD_BRONZE.b, 0.95)
var shadow := Color(0.02, 0.012, 0.006, 0.88)
var corners := [
rect.position + Vector2(inset, inset),
Vector2(rect.end.x - inset, rect.position.y + inset),
rect.end - Vector2(inset, inset),
Vector2(rect.position.x + inset, rect.end.y - inset)
]
_draw_objective_corner(corners[0], Vector2.RIGHT, Vector2.DOWN, bracket, shadow, 4.0)
_draw_objective_corner(corners[1], Vector2.LEFT, Vector2.DOWN, bracket, shadow, 4.0)
_draw_objective_corner(corners[2], Vector2.LEFT, Vector2.UP, bracket, shadow, 4.0)
_draw_objective_corner(corners[3], Vector2.RIGHT, Vector2.UP, bracket, shadow, 4.0)
_draw_objective_corner(corners[0], Vector2.RIGHT, Vector2.DOWN, bracket, line_color, 2.0)
_draw_objective_corner(corners[1], Vector2.LEFT, Vector2.DOWN, bracket, line_color, 2.0)
_draw_objective_corner(corners[2], Vector2.LEFT, Vector2.UP, bracket, line_color, 2.0)
_draw_objective_corner(corners[3], Vector2.RIGHT, Vector2.UP, bracket, line_color, 2.0)
func _draw_objective_corner(origin: Vector2, x_dir: Vector2, y_dir: Vector2, length: float, color: Color, width: float) -> void:
draw_line(origin, origin + x_dir * length, color, width)
draw_line(origin, origin + y_dir * length, color, width)
func _draw_overlays() -> void:
for cell in state.get_objective_cells():
_draw_objective_seal_marker(_rect_for_cell(cell))
@@ -3192,10 +3240,22 @@ func _draw_unit_status_markers(center: Vector2, hp_ratio: float, unit: Dictionar
return
var start_x := -float(marker_kinds.size() - 1) * UNIT_STATUS_MARKER_GAP * 0.5
var font := _draw_ui_font(true)
for index in range(marker_kinds.size()):
var marker_kind := marker_kinds[index]
var marker_center := center + Vector2(start_x + float(index) * UNIT_STATUS_MARKER_GAP, UNIT_STATUS_MARKER_OFFSET_Y)
draw_circle(marker_center, UNIT_STATUS_MARKER_RADIUS + 1.5, Color(0.02, 0.022, 0.028, 0.88))
draw_circle(marker_center, UNIT_STATUS_MARKER_RADIUS, _unit_status_marker_color(marker_kinds[index]))
draw_circle(marker_center, UNIT_STATUS_MARKER_RADIUS, _unit_status_marker_color(marker_kind))
_draw_ink_text(
font,
marker_center + Vector2(-UNIT_STATUS_MARKER_RADIUS, 3.0),
_unit_status_marker_glyph(marker_kind),
HORIZONTAL_ALIGNMENT_CENTER,
UNIT_STATUS_MARKER_RADIUS * 2.0,
8,
_unit_status_marker_text_color(marker_kind),
Color(0.02, 0.012, 0.006, 0.80)
)
func _unit_status_marker_kinds(unit: Dictionary) -> Array[String]:
@@ -3252,6 +3312,28 @@ func _unit_status_marker_color(marker_kind: String) -> Color:
return Color(0.72, 0.86, 1.0)
func _unit_status_marker_glyph(marker_kind: String) -> String:
if marker_kind == "support":
return ""
if marker_kind == "debuff":
return ""
if marker_kind == "poison":
return ""
if marker_kind == "seal":
return ""
if marker_kind == "snare":
return ""
if marker_kind == "disarm":
return ""
return ""
func _unit_status_marker_text_color(marker_kind: String) -> Color:
if marker_kind == "poison" or marker_kind == "support":
return UI_AGED_INK
return UI_PARCHMENT_TEXT
func _visual_rect_for_unit(unit: Dictionary) -> Rect2:
var cell: Vector2i = unit.get("pos", Vector2i(-1, -1))
var rect := _rect_for_cell(cell)

View File

@@ -97,6 +97,7 @@ func _init() -> void:
_check_hover_intent_badges(failures)
_check_action_button_disabled_reasons(failures)
_check_terrain_and_unit_presentation(failures)
_check_objective_and_status_marker_helpers(failures)
_check_attack_motion_profiles(failures)
_check_attack_motion_signal(failures)
@@ -3596,6 +3597,40 @@ func _check_terrain_and_unit_presentation(failures: Array[String]) -> void:
scene.free()
func _check_objective_and_status_marker_helpers(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("could not load battle scene state for objective/status markers")
scene.free()
return
if scene.state.get_objective_cells().is_empty():
failures.append("opening battle should expose objective cells for foreground markers")
var glyphs := {
"support": "",
"debuff": "",
"poison": "",
"seal": "",
"snare": "",
"disarm": ""
}
for marker_kind in glyphs.keys():
if scene._unit_status_marker_glyph(str(marker_kind)) != str(glyphs[marker_kind]):
failures.append("status marker glyph mismatch for %s" % str(marker_kind))
var unit := {
"status_effects": [
{"type": "stat_bonus", "stat": "def", "amount": 2, "remaining_phases": 2},
{"type": "stat_bonus", "stat": "def", "amount": -2, "remaining_phases": 2},
{"type": "damage_over_time", "status": "poison", "amount": 4, "remaining_phases": 2},
{"type": "action_lock", "status": "seal", "action": "skill", "remaining_phases": 2}
]
}
var marker_kinds := scene._unit_status_marker_kinds(unit)
for expected in ["support", "debuff", "poison", "seal"]:
if not marker_kinds.has(expected):
failures.append("status marker kinds should include %s: %s" % [expected, str(marker_kinds)])
scene.free()
func _check_attack_motion_profiles(failures: Array[String]) -> void:
var scene = BattleSceneScript.new()
if not scene.state.load_battle("res://data/scenarios/001_yellow_turbans.json"):