extends SceneTree const PAST_STRONG := &"effect_tp_past_enemy_strong" const PAST_WEAK := &"effect_tp_past_enemy_weak" var failures: Array[String] = [] func _init() -> void: _run.call_deferred() func _run() -> void: await process_frame # 伤害基线按 easy 难度取值;显式钉住,不依赖残留的 settings.cfg。 var settings := root.get_node_or_null("GameSettings") if settings != null: settings.call("set_difficulty", &"easy") var bus: Node = load("res://autoload/event_bus.gd").new() bus.name = "EventBus" root.add_child(bus) var manager: Node = load("res://autoload/time_phase_manager.gd").new() manager.name = "TimePhaseManager" root.add_child(manager) var actor := Node.new() actor.name = "PhaseActor" var container: Node = load("res://scenes/components/effect_container.gd").new() container.name = "EffectContainer" actor.add_child(container) var adapter: Node = load("res://scenes/components/time_phase_adapter.gd").new() adapter.name = "TimePhaseAdapter" adapter.set("profile", load("res://resources/time_phase/profile_past_strong_enemy.tres")) actor.add_child(adapter) root.add_child(actor) await process_frame # --- Idempotent init applies the current (past) phase effects. --- var ids: Array = container.call("active_effect_ids") _expect_bool(ids.has(PAST_STRONG), true, "past phase should mount the strong effect") _expect_bool(ids.has(PAST_WEAK), false, "past phase should not mount the weak effect") _expect_int(int(container.call("active_count")), 1, "exactly one time phase effect expected after init") # --- Switching phases swaps the effect set. --- manager.call("toggle_time_phase", &"time_anchor_broken") ids = container.call("active_effect_ids") _expect_bool(ids.has(PAST_WEAK), true, "future phase should mount the weak effect") _expect_bool(ids.has(PAST_STRONG), false, "future phase should unmount the strong effect") _expect_int(int(container.call("active_count")), 1, "effect balance: exactly one phase effect after a switch") # --- Switching back restores the original set with no residue. --- manager.call("toggle_time_phase", &"time_anchor_broken") ids = container.call("active_effect_ids") _expect_bool(ids.has(PAST_STRONG), true, "back in past the strong effect should return") _expect_int(int(container.call("active_count")), 1, "effect balance holds across repeated switches") # --- Damage numbers actually flip with the phase. --- # 强势 ×1.4 / 弱势 ×0.7 以 resources/effects/time_phase/ 四个资源现值为准 # (2026-07-04 校准批次;旧口径 ×1.2 已废弃)。 var stat_resolver: Script = load("res://scripts/resolvers/stat_resolver.gd") var past_damage := float(stat_resolver.call("resolve_damage", 100.0, null, {"label": "perfect"}, container, null)) _expect_float(past_damage, 140.0, "past-strong enemy should deal x1.4 damage in past") manager.call("toggle_time_phase", &"time_anchor_broken") var future_damage := float(stat_resolver.call("resolve_damage", 100.0, null, {"label": "perfect"}, container, null)) _expect_float(future_damage, 70.0, "past-strong enemy should deal x0.7 damage in future") # --- Actors without a profile are untouched by phase switches. --- var plain_actor := Node.new() plain_actor.name = "PlainActor" var plain_container: Node = load("res://scenes/components/effect_container.gd").new() plain_container.name = "EffectContainer" plain_actor.add_child(plain_container) var plain_adapter: Node = load("res://scenes/components/time_phase_adapter.gd").new() plain_adapter.name = "TimePhaseAdapter" plain_actor.add_child(plain_adapter) root.add_child(plain_actor) await process_frame manager.call("toggle_time_phase", &"time_anchor_broken") _expect_int(int(plain_container.call("active_count")), 0, "actors without a profile must be unaffected") _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 time phase adapter") quit(0) else: for failure: String in failures: push_error(failure) quit(1)