Add class-specific attack presentation

This commit is contained in:
2026-06-18 17:37:47 +09:00
parent c928bb64b7
commit 84ccead01c
5 changed files with 244 additions and 23 deletions

View File

@@ -9,6 +9,7 @@ func _init() -> void:
_check_battle_visual_data(failures)
_check_scene_texture_loading(failures)
_check_hud_focus_text(failures)
_check_attack_motion_profiles(failures)
_check_attack_motion_signal(failures)
if failures.is_empty():
@@ -119,6 +120,80 @@ func _check_hud_focus_text(failures: Array[String]) -> void:
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"):
failures.append("could not load battle scene state for attack profiles")
scene.free()
return
_check_attack_profile(failures, scene, "cao_cao", Vector2i(1, 3), Vector2i(2, 3), "command_strike")
_check_attack_profile(failures, scene, "xiahou_dun", Vector2i(1, 4), Vector2i(2, 4), "cavalry_charge")
_check_attack_profile(failures, scene, "yellow_turban_1", Vector2i(7, 2), Vector2i(6, 2), "heavy_strike")
_check_attack_profile(failures, scene, "yellow_turban_2", Vector2i(8, 5), Vector2i(7, 5), "infantry_strike")
_check_attack_profile(failures, scene, "yellow_turban_3", Vector2i(7, 6), Vector2i(7, 4), "arrow")
_check_synthetic_attack_profile(failures, scene, "elite_cavalry", "cavalry_charge")
_check_synthetic_attack_profile(failures, scene, "guard_captain", "infantry_strike")
_check_synthetic_attack_profile(failures, scene, "marksman", "arrow", 2)
_check_synthetic_attack_profile(failures, scene, "military_advisor", "command_strike")
_check_synthetic_attack_profile(failures, scene, "commander", "command_strike")
_check_synthetic_attack_profile(failures, scene, "champion", "heavy_strike")
_check_synthetic_attack_profile(failures, scene, "bandit", "heavy_strike")
scene._on_unit_action_motion_requested("xiahou_dun", Vector2i(1, 4), Vector2i(2, 4), "attack")
_check_stored_attack_motion(failures, scene, "xiahou_dun", "cavalry_charge", "melee", "attack")
scene._on_unit_action_motion_requested("yellow_turban_3", Vector2i(7, 6), Vector2i(7, 4), "attack")
_check_stored_attack_motion(failures, scene, "yellow_turban_3", "arrow", "ranged", "attack")
if scene._action_motion_duration("cavalry_charge") <= scene._action_motion_duration("slash"):
failures.append("cavalry charge animation should last longer than default slash")
if scene._action_motion_lunge_scale("arrow") >= scene._action_motion_lunge_scale("slash"):
failures.append("arrow animation should keep the archer mostly planted")
if scene._action_motion_sfx("arrow") != "bow_release":
failures.append("arrow animation should use bow SFX")
if scene._action_motion_sfx("infantry_strike") != "slash":
failures.append("infantry animation should use slash SFX")
if scene._action_motion_sfx("heavy_strike") != "slash":
failures.append("heavy animation should use swing SFX before hit resolution")
scene.free()
func _check_attack_profile(failures: Array[String], scene, unit_id: String, from_cell: Vector2i, to_cell: Vector2i, expected_profile: String) -> void:
var unit: Dictionary = scene.state.get_unit(unit_id)
if unit.is_empty():
failures.append("missing unit for attack profile: %s" % unit_id)
return
var profile: String = scene._unit_action_motion_profile(unit, from_cell, to_cell, "attack")
if profile != expected_profile:
failures.append("%s attack profile mismatch: expected %s got %s" % [unit_id, expected_profile, profile])
func _check_synthetic_attack_profile(failures: Array[String], scene, class_id: String, expected_profile: String, attack_range := 1) -> void:
var unit := {
"class_id": class_id,
"range": attack_range
}
var profile: String = scene._unit_action_motion_profile(unit, Vector2i(0, 0), Vector2i(0, max(1, attack_range)), "attack")
if profile != expected_profile:
failures.append("%s synthetic attack profile mismatch: expected %s got %s" % [class_id, expected_profile, profile])
func _check_stored_attack_motion(failures: Array[String], scene, unit_id: String, expected_profile: String, expected_style: String, expected_kind: String) -> void:
if not scene.unit_action_motion_by_unit.has(unit_id):
failures.append("%s should store an action motion" % unit_id)
return
var motion: Dictionary = scene.unit_action_motion_by_unit[unit_id]
var profile := str(motion.get("profile", ""))
var family := str(motion.get("attack_family", ""))
var style := str(motion.get("style", ""))
var kind := str(motion.get("kind", ""))
if profile != expected_profile or family != expected_profile:
failures.append("%s stored motion profile mismatch: %s" % [unit_id, str(motion)])
if style != expected_style:
failures.append("%s stored motion style mismatch: %s" % [unit_id, str(motion)])
if kind != expected_kind:
failures.append("%s stored motion kind mismatch: %s" % [unit_id, str(motion)])
if float(motion.get("duration", 0.0)) <= 0.0:
failures.append("%s stored motion duration should be positive: %s" % [unit_id, str(motion)])
func _check_attack_motion_signal(failures: Array[String]) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):