Improve pixel map unit silhouettes

This commit is contained in:
2026-06-19 16:01:01 +09:00
parent 806b375313
commit 19af1359d1
2 changed files with 246 additions and 51 deletions

View File

@@ -3199,11 +3199,12 @@ func _unit_sprite_modulate(unit: Dictionary, acted: bool) -> Color:
func _draw_pixel_unit_sprite(rect: Rect2, unit: Dictionary, team_color: Color, modulate: Color) -> void: func _draw_pixel_unit_sprite(rect: Rect2, unit: Dictionary, team_color: Color, modulate: Color) -> void:
var family := _unit_class_family(unit) var profile := _pixel_unit_sprite_profile(unit)
var family := str(profile.get("family", _unit_class_family(unit)))
var phase := _unit_idle_phase(unit) var phase := _unit_idle_phase(unit)
var bob := roundf(sin(phase) * 1.25) var bob := _unit_idle_bob(family, phase)
var step := 1.0 if sin(phase * 1.65) >= 0.0 else -1.0 var step := _unit_idle_step(family, phase)
var pixel := 3.0 var pixel := float(profile.get("pixel", 3.0))
var origin := Vector2(roundf(rect.position.x + 12.0), roundf(rect.position.y + 5.0 + bob)) var origin := Vector2(roundf(rect.position.x + 12.0), roundf(rect.position.y + 5.0 + bob))
var outline := _unit_pixel_color(Color(0.035, 0.026, 0.018, 1.0), modulate) var outline := _unit_pixel_color(Color(0.035, 0.026, 0.018, 1.0), modulate)
var skin := _unit_pixel_color(Color(0.86, 0.62, 0.38, 1.0), modulate) var skin := _unit_pixel_color(Color(0.86, 0.62, 0.38, 1.0), modulate)
@@ -3233,6 +3234,84 @@ func _unit_idle_phase(unit: Dictionary) -> float:
return float(Time.get_ticks_msec()) * 0.0024 + float(seed % 360) * 0.01745 return float(Time.get_ticks_msec()) * 0.0024 + float(seed % 360) * 0.01745
func _unit_idle_bob(family: String, phase: float) -> float:
if family == "cavalry":
return roundf(sin(phase * 1.55) * 0.75)
if family == "ranged":
return roundf(sin(phase * 1.10) * 0.75)
if family == "tactic":
return roundf(sin(phase * 0.82) * 0.65)
if family == "heavy":
return roundf(sin(phase * 0.72) * 0.45)
return roundf(sin(phase) * 1.15)
func _unit_idle_step(family: String, phase: float) -> float:
var rate := 1.65
if family == "cavalry":
rate = 2.15
elif family == "ranged":
rate = 1.25
elif family == "tactic":
rate = 0.95
elif family == "heavy":
rate = 0.78
return 1.0 if sin(phase * rate) >= 0.0 else -1.0
func _pixel_unit_sprite_profile(unit: Dictionary) -> Dictionary:
var family := _unit_class_family(unit)
if family == "cavalry":
return {
"family": family,
"pixel": 3.0,
"min_x": 0.0,
"max_x": 15.0,
"max_y": 14.0,
"weapon": "banner",
"idle": "hoof_cycle"
}
if family == "ranged":
return {
"family": family,
"pixel": 3.0,
"min_x": 1.0,
"max_x": 14.0,
"max_y": 13.0,
"weapon": "bow",
"idle": "bow_sway"
}
if family == "tactic":
return {
"family": family,
"pixel": 3.0,
"min_x": 1.0,
"max_x": 13.0,
"max_y": 14.0,
"weapon": "staff",
"idle": "robe_pulse"
}
if family == "heavy":
return {
"family": family,
"pixel": 3.0,
"min_x": 1.0,
"max_x": 14.0,
"max_y": 14.0,
"weapon": "heavy_blade",
"idle": "weight_shift"
}
return {
"family": family,
"pixel": 3.0,
"min_x": 1.0,
"max_x": 12.0,
"max_y": 13.0,
"weapon": "shield_spear",
"idle": "march_step"
}
func _unit_pixel_color(color: Color, modulate: Color) -> Color: func _unit_pixel_color(color: Color, modulate: Color) -> Color:
return Color( return Color(
color.r * modulate.r, color.r * modulate.r,
@@ -3246,74 +3325,136 @@ func _draw_pixel_rect(origin: Vector2, x: float, y: float, width: float, height:
draw_rect(Rect2(origin + Vector2(x, y) * pixel, Vector2(width, height) * pixel), color) draw_rect(Rect2(origin + Vector2(x, y) * pixel, Vector2(width, height) * pixel), color)
func _draw_pixel_shield(origin: Vector2, x: float, y: float, pixel: float, outline: Color, face: Color, trim: Color) -> void:
_draw_pixel_rect(origin, x + 1, y, 2, 1, pixel, outline)
_draw_pixel_rect(origin, x, y + 1, 4, 3, pixel, outline)
_draw_pixel_rect(origin, x + 1, y + 4, 2, 1, pixel, outline)
_draw_pixel_rect(origin, x + 1, y + 1, 2, 3, pixel, face)
_draw_pixel_rect(origin, x + 2, y + 1, 1, 3, pixel, trim)
func _draw_pixel_spear(origin: Vector2, x: float, y: float, pixel: float, shaft: Color, blade: Color) -> void:
_draw_pixel_rect(origin, x, y, 1, 9, pixel, shaft)
_draw_pixel_rect(origin, x - 1, y - 1, 3, 1, pixel, blade)
_draw_pixel_rect(origin, x, y - 2, 1, 1, pixel, blade.lightened(0.20))
func _draw_pixel_bow(origin: Vector2, x: float, y: float, pixel: float, wood: Color, string_color: Color, arrow: Color) -> void:
_draw_pixel_rect(origin, x + 1, y, 2, 1, pixel, wood)
_draw_pixel_rect(origin, x + 3, y + 1, 1, 2, pixel, wood)
_draw_pixel_rect(origin, x + 4, y + 3, 1, 3, pixel, wood)
_draw_pixel_rect(origin, x + 3, y + 6, 1, 2, pixel, wood)
_draw_pixel_rect(origin, x + 1, y + 8, 2, 1, pixel, wood)
_draw_pixel_rect(origin, x + 1, y + 1, 1, 7, pixel, string_color)
_draw_pixel_rect(origin, x - 3, y + 4, 6, 1, pixel, arrow)
_draw_pixel_rect(origin, x + 3, y + 3, 1, 3, pixel, arrow.lightened(0.10))
func _draw_pixel_horse_tack(origin: Vector2, pixel: float, cloth: Color, trim: Color) -> void:
_draw_pixel_rect(origin, 4, 6, 4, 1, pixel, trim)
_draw_pixel_rect(origin, 4, 7, 4, 2, pixel, cloth)
_draw_pixel_rect(origin, 8, 7, 1, 2, pixel, trim)
func _draw_pixel_banner(origin: Vector2, x: float, y: float, pixel: float, pole: Color, cloth: Color, trim: Color) -> void:
_draw_pixel_rect(origin, x, y, 1, 9, pixel, pole)
_draw_pixel_rect(origin, x + 1, y, 3, 2, pixel, cloth)
_draw_pixel_rect(origin, x + 1, y + 2, 2, 1, pixel, cloth.darkened(0.12))
_draw_pixel_rect(origin, x + 3, y + 2, 1, 1, pixel, trim)
func _draw_pixel_staff(origin: Vector2, x: float, y: float, pixel: float, staff: Color, tassel: Color) -> void:
_draw_pixel_rect(origin, x, y, 1, 9, pixel, staff)
_draw_pixel_rect(origin, x - 1, y, 3, 1, pixel, tassel)
_draw_pixel_rect(origin, x, y - 1, 1, 1, pixel, tassel.lightened(0.18))
_draw_pixel_rect(origin, x + 1, y + 2, 1, 2, pixel, tassel.darkened(0.18))
func _draw_pixel_heavy_blade(origin: Vector2, x: float, y: float, pixel: float, handle: Color, blade: Color) -> void:
_draw_pixel_rect(origin, x, y + 1, 1, 7, pixel, handle)
_draw_pixel_rect(origin, x - 1, y, 3, 1, pixel, handle.darkened(0.10))
_draw_pixel_rect(origin, x - 2, y - 1, 3, 2, pixel, blade)
_draw_pixel_rect(origin, x - 3, y, 1, 2, pixel, blade.darkened(0.10))
_draw_pixel_rect(origin, x - 1, y + 1, 2, 1, pixel, blade.lightened(0.16))
func _draw_pixel_infantry(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void: func _draw_pixel_infantry(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void:
_draw_pixel_rect(origin, 4, 1, 4, 2, pixel, outline) _draw_pixel_rect(origin, 4, 1, 4, 1, pixel, accent)
_draw_pixel_rect(origin, 5, 2, 2, 2, pixel, skin) _draw_pixel_rect(origin, 4, 2, 4, 2, pixel, outline)
_draw_pixel_rect(origin, 3, 4, 6, 5, pixel, outline) _draw_pixel_rect(origin, 5, 3, 2, 2, pixel, skin)
_draw_pixel_rect(origin, 4, 4, 4, 4, pixel, cloth) _draw_pixel_rect(origin, 3, 5, 6, 5, pixel, outline)
_draw_pixel_rect(origin, 3, 6, 2, 3, pixel, class_color) _draw_pixel_rect(origin, 4, 5, 4, 4, pixel, cloth)
_draw_pixel_rect(origin, 7, 6, 2, 3, pixel, cloth_dark) _draw_pixel_rect(origin, 5, 6, 2, 3, pixel, class_color)
_draw_pixel_rect(origin, 4 + step, 9, 2, 3, pixel, outline) _draw_pixel_shield(origin, 1, 5, pixel, outline, class_color.darkened(0.12), accent)
_draw_pixel_rect(origin, 7 - step, 9, 2, 3, pixel, outline) _draw_pixel_spear(origin, 10 + step, 2, pixel, accent.darkened(0.12), accent.lightened(0.18))
_draw_pixel_rect(origin, 1, 4, 1, 8, pixel, accent.lightened(0.15)) _draw_pixel_rect(origin, 4 + step, 10, 2, 3, pixel, outline)
_draw_pixel_rect(origin, 0, 3, 3, 1, pixel, accent) _draw_pixel_rect(origin, 7 - step, 10, 2, 3, pixel, outline)
_draw_pixel_rect(origin, 4 + step, 10, 2, 1, pixel, cloth_dark)
_draw_pixel_rect(origin, 7 - step, 10, 2, 1, pixel, cloth_dark)
func _draw_pixel_archer(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void: func _draw_pixel_archer(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void:
_draw_pixel_rect(origin, 4, 1, 4, 2, pixel, outline) _draw_pixel_rect(origin, 4, 1, 4, 2, pixel, outline)
_draw_pixel_rect(origin, 5, 2, 2, 2, pixel, skin) _draw_pixel_rect(origin, 4, 2, 4, 1, pixel, cloth_dark)
_draw_pixel_rect(origin, 3, 4, 6, 5, pixel, outline) _draw_pixel_rect(origin, 5, 3, 2, 2, pixel, skin)
_draw_pixel_rect(origin, 4, 4, 4, 4, pixel, cloth) _draw_pixel_rect(origin, 3, 5, 6, 5, pixel, outline)
_draw_pixel_rect(origin, 5, 5, 2, 4, pixel, class_color) _draw_pixel_rect(origin, 4, 5, 4, 4, pixel, cloth)
_draw_pixel_rect(origin, 4 + step, 9, 2, 3, pixel, outline) _draw_pixel_rect(origin, 2, 6, 2, 2, pixel, cloth_dark)
_draw_pixel_rect(origin, 7 - step, 9, 2, 3, pixel, outline) _draw_pixel_rect(origin, 5, 6, 2, 3, pixel, class_color)
draw_line(origin + Vector2(9, 3) * pixel, origin + Vector2(11, 7) * pixel, accent, 2.0) _draw_pixel_rect(origin, 4 + step, 10, 2, 3, pixel, outline)
draw_line(origin + Vector2(11, 7) * pixel, origin + Vector2(9, 11) * pixel, accent, 2.0) _draw_pixel_rect(origin, 7 - step, 10, 2, 3, pixel, outline)
draw_line(origin + Vector2(4, 6) * pixel, origin + Vector2(11, 7) * pixel, accent.lightened(0.15), 1.3) _draw_pixel_rect(origin, 1, 4, 1, 5, pixel, accent.darkened(0.22))
_draw_pixel_rect(origin, 2, 5, 2, 1, pixel, cloth_dark) _draw_pixel_rect(origin, 1, 5, 2, 1, pixel, accent)
_draw_pixel_bow(origin, 8 + step, 2, pixel, accent, outline.lightened(0.28), accent.lightened(0.16))
func _draw_pixel_cavalry(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void: func _draw_pixel_cavalry(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void:
var horse := _unit_pixel_color(Color(0.33, 0.20, 0.11, 1.0), Color(1.0, 1.0, 1.0, outline.a)) var horse := _unit_pixel_color(Color(0.33, 0.20, 0.11, 1.0), Color(1.0, 1.0, 1.0, outline.a))
var mane := _unit_pixel_color(Color(0.12, 0.075, 0.04, 1.0), Color(1.0, 1.0, 1.0, outline.a)) var mane := _unit_pixel_color(Color(0.12, 0.075, 0.04, 1.0), Color(1.0, 1.0, 1.0, outline.a))
_draw_pixel_rect(origin, 1, 7, 9, 3, pixel, outline) _draw_pixel_rect(origin, 0, 8, 10, 3, pixel, outline)
_draw_pixel_rect(origin, 2, 7, 7, 2, pixel, horse) _draw_pixel_rect(origin, 1, 8, 8, 2, pixel, horse)
_draw_pixel_rect(origin, 8, 5, 3, 3, pixel, outline) _draw_pixel_rect(origin, 8, 6, 4, 3, pixel, outline)
_draw_pixel_rect(origin, 8, 6, 2, 2, pixel, horse) _draw_pixel_rect(origin, 8, 7, 3, 2, pixel, horse)
_draw_pixel_rect(origin, 7, 5, 1, 3, pixel, mane) _draw_pixel_rect(origin, 7, 6, 1, 3, pixel, mane)
_draw_pixel_rect(origin, 2 + step, 10, 2, 3, pixel, outline) _draw_pixel_rect(origin, 0, 7, 2, 1, pixel, mane)
_draw_pixel_rect(origin, 7 - step, 10, 2, 3, pixel, outline) _draw_pixel_rect(origin, 1 + step, 11, 2, 3, pixel, outline)
_draw_pixel_rect(origin, 4 - step, 11, 2, 3, pixel, outline)
_draw_pixel_rect(origin, 7 + step, 11, 2, 3, pixel, outline)
_draw_pixel_horse_tack(origin, pixel, class_color, accent)
_draw_pixel_rect(origin, 4, 2, 4, 2, pixel, outline) _draw_pixel_rect(origin, 4, 2, 4, 2, pixel, outline)
_draw_pixel_rect(origin, 5, 3, 2, 2, pixel, skin) _draw_pixel_rect(origin, 5, 3, 2, 2, pixel, skin)
_draw_pixel_rect(origin, 4, 5, 4, 3, pixel, cloth) _draw_pixel_rect(origin, 4, 5, 4, 3, pixel, cloth)
_draw_pixel_rect(origin, 5, 5, 2, 2, pixel, class_color) _draw_pixel_rect(origin, 5, 5, 2, 2, pixel, class_color)
draw_line(origin + Vector2(4, 4) * pixel, origin + Vector2(2, 1) * pixel, accent, 2.0) _draw_pixel_banner(origin, 11, 0, pixel, accent.darkened(0.22), class_color, accent)
func _draw_pixel_strategist(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void: func _draw_pixel_strategist(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void:
_draw_pixel_rect(origin, 4, 1, 4, 2, pixel, outline) _draw_pixel_rect(origin, 4, 0, 4, 1, pixel, class_color.lightened(0.20))
_draw_pixel_rect(origin, 5, 2, 2, 2, pixel, skin) _draw_pixel_rect(origin, 3, 1, 6, 2, pixel, outline)
_draw_pixel_rect(origin, 3, 4, 6, 7, pixel, outline) _draw_pixel_rect(origin, 5, 3, 2, 2, pixel, skin)
_draw_pixel_rect(origin, 4, 4, 4, 6, pixel, cloth) _draw_pixel_rect(origin, 3, 5, 6, 7, pixel, outline)
_draw_pixel_rect(origin, 3, 6, 2, 3, pixel, class_color) _draw_pixel_rect(origin, 4, 5, 4, 6, pixel, cloth)
_draw_pixel_rect(origin, 7, 6, 2, 3, pixel, class_color) _draw_pixel_rect(origin, 3, 7, 2, 3, pixel, class_color)
_draw_pixel_rect(origin, 4 + step, 11, 2, 2, pixel, outline) _draw_pixel_rect(origin, 7, 7, 2, 3, pixel, class_color.darkened(0.08))
_draw_pixel_rect(origin, 7 - step, 11, 2, 2, pixel, outline) _draw_pixel_rect(origin, 4 + step, 12, 2, 2, pixel, outline)
_draw_pixel_rect(origin, 8, 3, 3, 1, pixel, accent) _draw_pixel_rect(origin, 7 - step, 12, 2, 2, pixel, outline)
_draw_pixel_rect(origin, 9, 4, 2, 1, pixel, accent) _draw_pixel_rect(origin, 1, 6, 2, 4, pixel, cloth_dark)
_draw_pixel_rect(origin, 2, 5, 1, 5, pixel, cloth_dark) _draw_pixel_rect(origin, 2, 10, 3, 1, pixel, accent.lightened(0.10))
_draw_pixel_staff(origin, 10 + step, 3, pixel, accent.darkened(0.22), accent)
func _draw_pixel_heavy(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void: func _draw_pixel_heavy(origin: Vector2, pixel: float, outline: Color, skin: Color, cloth: Color, cloth_dark: Color, class_color: Color, accent: Color, step: float) -> void:
_draw_pixel_rect(origin, 4, 1, 4, 2, pixel, outline) _draw_pixel_rect(origin, 4, 1, 4, 2, pixel, outline)
_draw_pixel_rect(origin, 5, 2, 2, 2, pixel, skin) _draw_pixel_rect(origin, 3, 2, 6, 1, pixel, accent.darkened(0.08))
_draw_pixel_rect(origin, 2, 4, 8, 6, pixel, outline) _draw_pixel_rect(origin, 5, 3, 2, 2, pixel, skin)
_draw_pixel_rect(origin, 3, 4, 6, 5, pixel, cloth) _draw_pixel_rect(origin, 2, 5, 8, 6, pixel, outline)
_draw_pixel_rect(origin, 4, 5, 4, 4, pixel, class_color) _draw_pixel_rect(origin, 3, 5, 6, 5, pixel, cloth)
_draw_pixel_rect(origin, 3 + step, 10, 2, 3, pixel, outline) _draw_pixel_rect(origin, 4, 6, 4, 4, pixel, class_color)
_draw_pixel_rect(origin, 8 - step, 10, 2, 3, pixel, outline) _draw_pixel_rect(origin, 1, 6, 2, 3, pixel, outline)
draw_line(origin + Vector2(9, 2) * pixel, origin + Vector2(2, 10) * pixel, accent, 3.0) _draw_pixel_rect(origin, 9, 6, 2, 3, pixel, outline)
_draw_pixel_rect(origin, 8, 1, 3, 2, pixel, accent.lightened(0.20)) _draw_pixel_rect(origin, 3 + step, 11, 2, 3, pixel, outline)
_draw_pixel_rect(origin, 8 - step, 11, 2, 3, pixel, outline)
_draw_pixel_heavy_blade(origin, 11 + step, 2, pixel, accent.darkened(0.22), accent.lightened(0.12))
func _draw_unit_identity_marks(rect: Rect2, center: Vector2, unit: Dictionary, team_color: Color) -> void: func _draw_unit_identity_marks(rect: Rect2, center: Vector2, unit: Dictionary, team_color: Color) -> void:

View File

@@ -381,6 +381,15 @@ func _check_pixel_unit_helpers(failures: Array[String]) -> void:
var scene = BattleSceneScript.new() var scene = BattleSceneScript.new()
for method_name in [ for method_name in [
"_draw_pixel_unit_sprite", "_draw_pixel_unit_sprite",
"_pixel_unit_sprite_profile",
"_unit_idle_bob",
"_unit_idle_step",
"_draw_pixel_shield",
"_draw_pixel_spear",
"_draw_pixel_bow",
"_draw_pixel_banner",
"_draw_pixel_staff",
"_draw_pixel_heavy_blade",
"_draw_pixel_infantry", "_draw_pixel_infantry",
"_draw_pixel_archer", "_draw_pixel_archer",
"_draw_pixel_cavalry", "_draw_pixel_cavalry",
@@ -389,4 +398,49 @@ func _check_pixel_unit_helpers(failures: Array[String]) -> void:
]: ]:
if not scene.has_method(method_name): if not scene.has_method(method_name):
failures.append("missing pixel-map unit helper: %s" % method_name) failures.append("missing pixel-map unit helper: %s" % method_name)
_check_pixel_unit_profiles(scene, failures)
scene.free() scene.free()
func _check_pixel_unit_profiles(scene, failures: Array[String]) -> void:
var profiles := {
"infantry": scene._pixel_unit_sprite_profile({"class_id": "infantry"}),
"ranged": scene._pixel_unit_sprite_profile({"class_id": "archer"}),
"cavalry": scene._pixel_unit_sprite_profile({"class_id": "cavalry"}),
"tactic": scene._pixel_unit_sprite_profile({"class_id": "strategist"}),
"heavy": scene._pixel_unit_sprite_profile({"class_id": "bandit"})
}
for family in profiles.keys():
var profile: Dictionary = profiles[family]
if str(profile.get("family", "")) != str(family):
failures.append("pixel unit profile should map %s class to %s family: %s" % [family, family, str(profile)])
if absf(float(profile.get("pixel", 0.0)) - 3.0) > 0.001:
failures.append("pixel unit profile should use a stable 3px grid for %s" % family)
if float(profile.get("min_x", -1.0)) < 0.0 or float(profile.get("max_x", 99.0)) > 16.0:
failures.append("pixel unit profile should stay inside tile width for %s: %s" % [family, str(profile)])
var bottom_px := 5.0 + 1.0 + float(profile.get("max_y", 99.0)) * float(profile.get("pixel", 3.0))
if bottom_px >= 56.0:
failures.append("pixel unit profile should stay above HP bar for %s: %.1f" % [family, bottom_px])
if float((profiles["cavalry"] as Dictionary).get("max_x", 0.0)) <= float((profiles["infantry"] as Dictionary).get("max_x", 0.0)):
failures.append("cavalry pixel profile should read wider than infantry")
if float((profiles["heavy"] as Dictionary).get("max_x", 0.0)) <= float((profiles["infantry"] as Dictionary).get("max_x", 0.0)):
failures.append("heavy pixel profile should read wider than infantry")
if float((profiles["tactic"] as Dictionary).get("max_y", 0.0)) <= float((profiles["infantry"] as Dictionary).get("max_y", 0.0)):
failures.append("tactic pixel profile should read taller or more robed than infantry")
var seen_weapons := {}
var seen_idles := {}
for family in profiles.keys():
var profile: Dictionary = profiles[family]
var weapon := str(profile.get("weapon", ""))
var idle := str(profile.get("idle", ""))
if weapon.is_empty() or seen_weapons.has(weapon):
failures.append("pixel unit profile should expose a distinct weapon silhouette for %s" % family)
seen_weapons[weapon] = true
if idle.is_empty() or seen_idles.has(idle):
failures.append("pixel unit profile should expose a distinct idle profile for %s" % family)
seen_idles[idle] = true
if str((profiles["ranged"] as Dictionary).get("weapon", "")) != "bow":
failures.append("ranged pixel profile should keep an explicit bow silhouette")
if str((profiles["infantry"] as Dictionary).get("weapon", "")) != "shield_spear":
failures.append("infantry pixel profile should keep shield and spear silhouette")