Initial project sync

This commit is contained in:
wxm
2026-07-06 22:55:14 -07:00
commit 8cca00d0da
1066 changed files with 58585 additions and 0 deletions
+146
View File
@@ -0,0 +1,146 @@
extends SceneTree
var failures: Array[String] = []
func _init() -> void:
_run.call_deferred()
func _run() -> void:
await _check_player_death_aligns_and_disappears()
await _check_stage_camera_survives_player_disappearance()
await _check_minion_death_aligns_and_disappears()
await _check_boss_death_aligns_and_disappears()
_finish()
func _check_player_death_aligns_and_disappears() -> void:
var scene: PackedScene = load("res://scenes/characters/player.tscn")
_expect(scene != null, "Player scene should load")
if scene == null:
return
var player := scene.instantiate()
root.add_child(player)
await process_frame
var sprite := player.get_node("Visual/CharacterSprite") as Sprite2D
var baseline := _visible_sprite_bottom_global_y(sprite)
player.get_node("StateMachine").call("set_life_state", &"Dead")
player.call("handle_animations")
_expect_float(_visible_sprite_bottom_global_y(sprite), baseline, "Player first death frame should keep the same visible ground line")
player.call("_tick_manual_animation", 0.55)
_expect_float(_visible_sprite_bottom_global_y(sprite), baseline, "Player fall-back death frame should keep the same visible ground line")
player.call("_tick_manual_animation", 2.0)
await process_frame
_expect_bool(not is_instance_valid(player) or player.is_queued_for_deletion(), true, "Player corpse should disappear after death animation")
func _check_stage_camera_survives_player_disappearance() -> void:
var scene: PackedScene = load("res://scenes/stage/stage.tscn")
_expect(scene != null, "Stage scene should load for player death cleanup")
if scene == null:
return
var stage := scene.instantiate()
root.add_child(stage)
await process_frame
var player := stage.get_node("ActorsContainer/Player")
player.get_node("StateMachine").call("set_life_state", &"Dead")
player.call("handle_animations")
player.call("_tick_manual_animation", 3.0)
await process_frame
await process_frame
stage.call("_update_camera_follow")
stage.call("_update_wipe_center")
_expect_bool(not is_instance_valid(player) or player.is_queued_for_deletion(), true, "Stage player corpse should be gone before camera follow updates")
stage.queue_free()
await process_frame
func _check_minion_death_aligns_and_disappears() -> void:
var scene: PackedScene = load("res://scenes/enemies/minion.tscn")
_expect(scene != null, "Minion scene should load")
if scene == null:
return
var minion := scene.instantiate()
root.add_child(minion)
await process_frame
var sprite := minion.get_node("Visual/CharacterSprite") as Sprite2D
var baseline := _visible_sprite_bottom_global_y(sprite)
minion.get_node("StateMachine").call("set_life_state", &"Dead")
minion.call("handle_animations")
_expect_float(_visible_sprite_bottom_global_y(sprite), baseline, "Minion first death frame should keep the same visible ground line")
var animation_player := minion.get_node("AnimationPlayer") as AnimationPlayer
var animation_name := str(animation_player.current_animation)
animation_player.advance(animation_player.get_animation(animation_name).length + 0.2)
minion.call("handle_animations")
await process_frame
_expect_bool(not is_instance_valid(minion) or minion.is_queued_for_deletion(), true, "Minion corpse should disappear after death animation")
func _check_boss_death_aligns_and_disappears() -> void:
var scene: PackedScene = load("res://scenes/enemies/boss.tscn")
_expect(scene != null, "Boss scene should load")
if scene == null:
return
var boss := scene.instantiate()
root.add_child(boss)
await process_frame
var sprite := boss.get_node("Visual/CharacterSprite") as Sprite2D
var baseline := _visible_sprite_bottom_global_y(sprite)
boss.get_node("StateMachine").call("set_life_state", &"Dead")
boss.call("handle_animations")
_expect_float(_visible_sprite_bottom_global_y(sprite), baseline, "Boss first death frame should keep the same visible ground line")
var animation_player := boss.get_node("AnimationPlayer") as AnimationPlayer
animation_player.advance(animation_player.get_animation("boss_die").length + 0.2)
boss.call("handle_animations")
await process_frame
_expect_bool(not is_instance_valid(boss) or boss.is_queued_for_deletion(), true, "Boss corpse should disappear after death animation")
func _visible_sprite_bottom_global_y(sprite: Sprite2D) -> float:
if sprite == null or sprite.texture == null:
return INF
var image := sprite.texture.get_image()
if image == null or image.is_empty():
return INF
var hframes := maxi(1, sprite.hframes)
var vframes := maxi(1, sprite.vframes)
var frame_width := image.get_width() / hframes
var frame_height := image.get_height() / vframes
var frame_index := clampi(sprite.frame, 0, hframes * vframes - 1)
var column := frame_index % hframes
var row := int(frame_index / hframes)
var bottom := -1
for y: int in range(frame_height):
for x: int in range(frame_width):
var pixel := image.get_pixel(column * frame_width + x, row * frame_height + y)
if pixel.a > 0.01:
bottom = y
if bottom < 0:
return INF
return sprite.to_global(sprite.offset + Vector2(0.0, bottom)).y
func _expect(condition: bool, label: String) -> void:
if not condition:
failures.append(label)
func _expect_bool(actual: bool, expected: bool, label: String) -> void:
if actual != expected:
failures.append("%s: expected %s, got %s" % [label, expected, actual])
func _expect_float(actual: float, expected: float, label: String) -> void:
if absf(actual - expected) > 0.03:
failures.append("%s: expected %.3f, got %.3f" % [label, expected, actual])
func _finish() -> void:
if failures.is_empty():
print("PASS test_death_presentation")
else:
printerr("FAIL test_death_presentation")
for failure: String in failures:
printerr(" - %s" % failure)
quit(failures.size())