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
+136
View File
@@ -0,0 +1,136 @@
extends SceneTree
const BUFF_ID := &"time_anchor_attack_buff"
var failures: Array[String] = []
func _init() -> void:
_run.call_deferred()
func _run() -> void:
await process_frame
var bus: Node = load("res://autoload/event_bus.gd").new()
bus.name = "EventBus"
root.add_child(bus)
var actor := Node.new()
actor.name = "TestActor"
var container: Node = load("res://scenes/components/effect_container.gd").new()
container.name = "EffectContainer"
actor.add_child(container)
var buff_component: Node = load("res://scenes/components/attack_buff_component.gd").new()
buff_component.name = "AttackBuffComponent"
actor.add_child(buff_component)
root.add_child(actor)
await process_frame
var broadcasts: Array = []
bus.connect("attack_buff_changed", func(stacks: int, max_stacks: int) -> void:
broadcasts.append([stacks, max_stacks])
)
var stat_resolver: Script = load("res://scripts/resolvers/stat_resolver.gd")
var health: Node = load("res://scenes/components/health_component.gd").new()
health.name = "HealthComponent"
health.set("maximum", 1000)
health.set("current", 400)
actor.add_child(health)
await process_frame
# --- Perfect/Good held anchors add stacks; damage formula is 1 + 0.50 x stacks. ---
for _index: int in range(3):
bus.emit_signal("time_anchor_resolved", {}, true, {"label": "perfect"})
_expect_int(int(buff_component.call("attack_buff_stacks")), 3, "three held anchors should give three stacks")
var damage := float(stat_resolver.call("resolve_damage", 100.0, null, {"label": "perfect"}, container, null))
_expect_float(damage, 250.0, "damage should scale by 1 + 0.50 x stacks")
if not broadcasts.is_empty():
_expect_int(int(broadcasts[broadcasts.size() - 1][1]), 7, "broadcast should carry max stacks 7")
# --- Broken anchors do not add stacks. ---
bus.emit_signal("time_anchor_resolved", {}, false, {})
_expect_int(int(buff_component.call("attack_buff_stacks")), 3, "broken anchors must not add stacks")
# --- Bad anchors hold/heal but do not add attack-buff stacks. ---
bus.emit_signal("time_anchor_resolved", {"beat": 15}, true, {"label": "bad"})
_expect_int(int(buff_component.call("attack_buff_stacks")), 3, "bad held anchors must not add attack buff")
# --- Cap at 7; extra Perfect/Good holds keep the cap. ---
for _index: int in range(30):
bus.emit_signal("time_anchor_resolved", {}, true, {"label": "good"})
_expect_int(int(buff_component.call("attack_buff_stacks")), 7, "stacks should cap at 7")
damage = float(stat_resolver.call("resolve_damage", 100.0, null, {"label": "perfect"}, container, null))
_expect_float(damage, 450.0, "capped stacks should give +350% damage")
# --- Each actual phase switch subtracts two stacks, bottoming at zero. ---
var expected_reductions := [5, 3, 1, 0]
for expected: int in expected_reductions:
bus.emit_signal("time_phase_changed", &"past", &"future", &"time_anchor_broken")
_expect_int(int(buff_component.call("attack_buff_stacks")), expected, "phase switch should subtract stacks to %d" % expected)
var active_ids: Array = container.call("active_effect_ids")
_expect_bool(active_ids.has(BUFF_ID), false, "zero stacks should remove the buff effect")
# --- Effect stacks and component report stay equal. ---
bus.emit_signal("time_anchor_resolved", {}, true, {"label": "perfect"})
_expect_int(int(container.call("effect_stacks", BUFF_ID)), int(buff_component.call("attack_buff_stacks")), "effect stacks and component report should match")
# --- First held anchor per 4-beat window heals; low health doubles the heal. ---
health.call("set_values", 400, 1000)
bus.emit_signal("time_anchor_resolved", {"beat": 8}, true, {"label": "perfect"})
_expect_int(int(health.get("current")), 460, "perfect anchor should heal 60 while below half health")
bus.emit_signal("time_anchor_resolved", {"beat": 9}, true, {"label": "good"})
_expect_int(int(health.get("current")), 460, "second held anchor in the same 4-beat window should not heal")
bus.emit_signal("time_anchor_resolved", {"beat": 12}, true, {"label": "bad"})
_expect_int(int(health.get("current")), 480, "bad anchor in a new window should heal 20 while below half health")
health.call("set_values", 980, 1000)
bus.emit_signal("time_anchor_resolved", {"beat": 16}, true, {"label": "perfect"})
_expect_int(int(health.get("current")), 1000, "time-anchor healing should not exceed max health")
# --- chart_reset zeroes the buff. ---
bus.emit_signal("chart_reset", &"test")
_expect_int(int(buff_component.call("attack_buff_stacks")), 0, "chart_reset should zero the buff")
_expect_bool(container.call("active_effect_ids").has(BUFF_ID), false, "chart_reset should remove the buff effect")
# --- Regression: stack-aware aggregation keeps max_stacks = 1 behaviour. ---
var definition: Resource = load("res://resources/effects/effect_definition.gd").new()
definition.set("id", &"test_single_stack_add")
definition.set("duration_type", &"infinite")
definition.set("max_stacks", 1)
var modifier: Resource = load("res://resources/effects/stat_modifier.gd").new()
modifier.set("stat", &"damage_mult")
modifier.set("operation", "add")
modifier.set("value", 0.5)
var stat_modifiers: Array[Resource] = [modifier]
definition.set("stat_modifiers", stat_modifiers)
container.call("add_effect", definition)
container.call("add_effect", definition)
_expect_float(float(container.call("stat_multiplier", &"damage_mult")), 1.5, "max_stacks 1 add modifiers keep the migration baseline")
_finish()
func _expect_int(actual: int, expected: int, label: String) -> void:
if actual != expected:
failures.append("%s: expected %d, got %d" % [label, expected, actual])
func _expect_float(actual: float, expected: float, label: String) -> void:
if not is_equal_approx(actual, expected):
failures.append("%s: expected %.3f, got %.3f" % [label, expected, actual])
func _expect_bool(actual: bool, expected: bool, label: String) -> void:
if actual != expected:
failures.append("%s: expected %s, got %s" % [label, expected, actual])
func _finish() -> void:
if failures.is_empty():
print("PASS attack buff")
quit(0)
else:
for failure: String in failures:
push_error(failure)
quit(1)