Files
aetherbound-guild/runtime/tests/p10_1/motion_presenter_test.gd
T

75 lines
4.8 KiB
GDScript

extends SceneTree
const MotionPresenter = preload("res://p10_1/motion_presenter.gd")
var failures: Array[String] = []
func _initialize() -> void:
call_deferred("run_test")
func run_test() -> void:
var motion = MotionPresenter.new()
motion.reset(["front", "rear"], 2)
motion.consume({"kind": "wave_entered", "enemy_ids": ["hook"]}, 0.0)
expect(motion.front_visible_enemy() == "hook", "wave entrance exposes one enemy")
var entering: Dictionary = motion.enemy_visual("hook", 0.1, false, false)
expect(float(entering.alpha) > 0.0 and float(entering.alpha) < 1.0 and float(entering.offset.x) > 0.0, "wave enters continuously from the right")
motion.consume({"kind": "healing_denial_attached", "enemy_id": "hook", "target_id": "rear"}, 0.8)
expect(motion.healing_denied_target == "rear" and motion.healing_denial_source == "hook", "healing denial remains bound to its authoritative source and target")
motion.consume({"kind": "indexed_mud_created", "enemy_id": "hook", "target_id": "rear", "crossed_index": 2}, 1.0)
expect(motion.mud_index == 2, "indexed mud remains on its disclosed slot")
var before_order: Array = motion.actor_order.duplicate()
motion.consume({"kind": "pull_committed", "enemy_id": "hook", "target_id": "rear", "order_after": ["rear", "front"]}, 1.2)
expect(motion.actor_order != before_order, "pull commits the new visual order")
var pull_start: Dictionary = motion.actor_visual("rear", 0, -70.0, 1.2, false, false)
var pull_middle: Dictionary = motion.actor_visual("rear", 0, -70.0, 1.49, false, false)
var pull_end: Dictionary = motion.actor_visual("rear", 0, -70.0, 1.9, false, false)
expect(absf(float(pull_start.offset.x)) > absf(float(pull_middle.offset.x)) and absf(float(pull_end.offset.x)) < 0.1, "pull eases from the old slot into the new slot")
expect(float(pull_middle.offset.y) < -5.0, "pull has a visible lifted crossing arc")
motion.consume({"kind": "tide_followed", "safe_index": 3}, 2.0)
var tide_start: Dictionary = motion.actor_visual("rear", 0, -70.0, 2.0, false, false)
var tide_middle: Dictionary = motion.actor_visual("rear", 0, -70.0, 2.36, false, false)
var tide_end: Dictionary = motion.actor_visual("rear", 0, -70.0, 2.8, false, false)
expect(float(tide_start.offset.x) < float(tide_middle.offset.x) and float(tide_middle.offset.x) < float(tide_end.offset.x), "tide translation is continuous and directionally stable")
var reduced_tide: Dictionary = motion.actor_visual("rear", 0, -70.0, 2.8, true, false)
expect(absf(float(reduced_tide.offset.x)) < absf(float(tide_end.offset.x)), "reduced motion preserves state with less travel")
motion.consume({"kind": "allied_basic", "actor_id": "front", "damage": 14}, 3.0)
var anticipation: Dictionary = motion.actor_visual("front", 1, -70.0, 3.08, false, false)
var contact: Dictionary = motion.actor_visual("front", 1, -70.0, 3.34, false, false)
var recovery: Dictionary = motion.actor_visual("front", 1, -70.0, 3.70, false, false)
expect(float(anticipation.offset.x) < float(contact.offset.x) and float(contact.offset.x) > float(recovery.offset.x), "attack has anticipation, contact and recovery")
expect(String(contact.state) == "attack", "contact selects an attack animation state")
motion.consume({"kind": "enemy_basic_damage", "target_id": "rear", "damage": 10}, 3.8)
var hit: Dictionary = motion.actor_visual("rear", 0, -70.0, 4.08, false, false)
expect(String(hit.state) == "hit" and float(hit.offset.x) < float(tide_end.offset.x), "enemy contact produces target recoil")
motion.consume({"kind": "authoritative_damage", "target_id": "rear", "amount": 20, "hp_after": 0}, 4.4)
expect(motion.playback_held(4.43, false) and not motion.playback_held(4.43, true), "local impact pause respects reduced motion")
motion.consume({"kind": "recruit_fallen", "recruit_id": "rear", "permanent": true}, 4.5)
var fallen: Dictionary = motion.actor_visual("rear", 0, -70.0, 5.2, false, false)
expect(float(fallen.alpha) < 0.3 and absf(float(fallen.rotation)) > 1.0 and String(fallen.state) == "fallen", "Fallen settles into a permanent non-looping collapsed state")
for index in range(12):
motion.consume({"kind": "authoritative_damage", "target_id": "front", "amount": index + 1, "hp_after": 10}, 5.3 + index * 0.01)
expect(motion.effects.size() == MotionPresenter.MAX_EFFECTS, "overlapping feedback is capped")
motion.prune(8.0)
expect(motion.transient_resting(8.0), "transient motion returns to rest")
if failures.is_empty():
print("ABG_P10_1_MOTION_PRESENTER_OK wave=enter attack=anticipate,contact,recover pull=eased tide=eased healing_denial=persistent mud=persistent impact=held fallen=collapsed reduced_motion=bounded overlap=8 rest=true authority_mutations=0")
quit(0)
else:
for failure in failures:
push_error(failure)
quit(1)
func expect(condition: bool, message: String) -> void:
if not condition:
failures.append("P10.1 motion presenter: %s" % message)