Improve battle marker readability
This commit is contained in:
@@ -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)
|
||||
|
||||
@@ -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"):
|
||||
|
||||
Reference in New Issue
Block a user