Add tactic cast motion effects

This commit is contained in:
2026-06-18 22:34:39 +09:00
parent 3273ec33ec
commit 70a34cf262
4 changed files with 168 additions and 1 deletions

View File

@@ -449,20 +449,34 @@ func _check_attack_motion_profiles(failures: Array[String]) -> void:
_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")
_check_skill_action_profile(failures, scene, "skill_damage", "tactic_damage", "tactic_flare")
_check_skill_action_profile(failures, scene, "skill_heal", "tactic_heal", "healing_sigil")
_check_skill_action_profile(failures, scene, "skill_support", "tactic_support", "order_banner")
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", "charge_dust")
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", "piercing_trace")
scene._on_unit_action_motion_requested("cao_cao", Vector2i(1, 3), Vector2i(3, 3), "skill_damage")
_check_stored_attack_motion(failures, scene, "cao_cao", "tactic_damage", "tactic", "skill_damage", "tactic_flare")
scene._on_unit_action_motion_requested("cao_cao", Vector2i(1, 3), Vector2i(1, 3), "skill_heal")
_check_stored_attack_motion(failures, scene, "cao_cao", "tactic_heal", "tactic", "skill_heal", "healing_sigil")
_check_attack_vfx_signature(failures, scene, "arrow", "piercing_trace")
_check_attack_vfx_signature(failures, scene, "cavalry_charge", "charge_dust")
_check_attack_vfx_signature(failures, scene, "infantry_strike", "shield_spark")
_check_attack_vfx_signature(failures, scene, "command_strike", "command_ring")
_check_attack_vfx_signature(failures, scene, "heavy_strike", "shock_crack")
_check_attack_vfx_signature(failures, scene, "tactic_damage", "tactic_flare")
_check_attack_vfx_signature(failures, scene, "tactic_heal", "healing_sigil")
_check_attack_vfx_signature(failures, scene, "tactic_support", "order_banner")
_check_unique_attack_vfx_signatures(failures, scene)
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_duration("tactic_damage") <= scene._action_motion_duration("command_strike"):
failures.append("tactic cast animation should linger longer than command physical strike")
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_lunge_scale("tactic_damage") >= scene._action_motion_lunge_scale("arrow"):
failures.append("tactic animation should keep the caster planted like a spell cast")
if scene._action_motion_impact_radius("heavy_strike") <= scene._action_motion_impact_radius("infantry_strike"):
failures.append("heavy strike impact radius should exceed infantry strike")
if scene._action_motion_impact_radius("cavalry_charge") <= scene._action_motion_impact_radius("arrow"):
@@ -473,6 +487,8 @@ func _check_attack_motion_profiles(failures: Array[String]) -> void:
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")
if scene._action_motion_sfx("tactic_damage") != "skill_cast":
failures.append("tactic animation should use skill cast SFX")
scene.free()
@@ -496,6 +512,16 @@ func _check_synthetic_attack_profile(failures: Array[String], scene, class_id: S
failures.append("%s synthetic attack profile mismatch: expected %s got %s" % [class_id, expected_profile, profile])
func _check_skill_action_profile(failures: Array[String], scene, action_kind: String, expected_profile: String, expected_signature: String) -> void:
var profile: String = scene._unit_action_motion_profile({"class_id": "hero", "range": 1}, Vector2i(0, 0), Vector2i(0, 0), action_kind)
if profile != expected_profile:
failures.append("%s action profile mismatch: expected %s got %s" % [action_kind, expected_profile, profile])
if not scene._action_motion_allows_same_cell(profile):
failures.append("%s should allow same-cell tactic motion" % profile)
if scene._action_motion_vfx_signature(profile) != expected_signature:
failures.append("%s action VFX mismatch: expected %s got %s" % [action_kind, expected_signature, scene._action_motion_vfx_signature(profile)])
func _check_attack_vfx_signature(failures: Array[String], scene, profile: String, expected_signature: String) -> void:
var signature: String = scene._action_motion_vfx_signature(profile)
if signature != expected_signature:
@@ -569,6 +595,38 @@ func _check_attack_motion_signal(failures: Array[String]) -> void:
return
if not motions.has("cao_cao|attack|6,2|7,2"):
failures.append("attack motion signal missing expected Cao Cao attack: %s" % str(motions))
_check_skill_motion_signal(failures, "spark", "skill_damage", Vector2i(3, 3), func(skill_state) -> void:
var enemy: Dictionary = skill_state.get_unit("yellow_turban_1")
enemy["pos"] = Vector2i(3, 3)
)
_check_skill_motion_signal(failures, "mend", "skill_heal", Vector2i(1, 3), func(skill_state) -> void:
var caster: Dictionary = skill_state.get_unit("cao_cao")
caster["hp"] = max(1, int(caster.get("hp", 1)) - 10)
)
_check_skill_motion_signal(failures, "guard_order", "skill_support", Vector2i(1, 3), func(_skill_state) -> void:
pass
)
func _check_skill_motion_signal(failures: Array[String], skill_id: String, expected_kind: String, target_cell: Vector2i, setup: Callable) -> void:
var state = BattleStateScript.new()
if not state.load_battle("res://data/scenarios/001_yellow_turbans.json"):
failures.append("could not load battle for %s motion" % skill_id)
return
setup.call(state)
var motions: Array[String] = []
state.unit_action_motion_requested.connect(func(unit_id: String, from_cell: Vector2i, to_cell: Vector2i, action_kind: String) -> void:
motions.append("%s|%s|%d,%d|%d,%d" % [unit_id, action_kind, from_cell.x, from_cell.y, to_cell.x, to_cell.y])
)
if not state.select_unit("cao_cao"):
failures.append("could not select Cao Cao for %s motion" % skill_id)
return
if not state.try_cast_selected_skill(skill_id, target_cell):
failures.append("%s should cast successfully for motion check" % skill_id)
return
var expected := "cao_cao|%s|1,3|%d,%d" % [expected_kind, target_cell.x, target_cell.y]
if not motions.has(expected):
failures.append("%s motion signal missing `%s`: %s" % [skill_id, expected, str(motions)])
func _check_camp_conversation_effect(failures: Array[String], conversation: Dictionary, expected_item_id: String, expected_count: int) -> void: