extends SceneTree ## Regression for the first Level 1 contact: body blocking must not leave the ## opening melee minion outside the player's basic attack hit window. var failures: Array[String] = [] func _init() -> void: _run.call_deferred() func _run() -> void: await process_frame var rhythm := root.get_node_or_null("RhythmManager") if rhythm != null and rhythm.has_method("stop_manager"): rhythm.call("stop_manager") await _check_inactive_boss_is_not_front_area_body_wall() await _check_body_blocked_contact_hits() await _check_opening_sequence_can_reach_minion() _finish() func _check_inactive_boss_is_not_front_area_body_wall() -> void: var stage := await _make_stage() if stage == null: return var boss := stage.get_node("ActorsContainer/Boss") as CharacterBody2D var receiver := boss.get_node("DamageReceiver") as Area2D _expect( (int(boss.collision_layer) & (1 << 6)) == 0, "Inactive Level 1 Boss should not occupy the enemy_body layer before boss-room entry" ) _expect( not receiver.monitorable, "Inactive Level 1 Boss should not expose a damage receiver before boss-room entry" ) boss.set("combat_enabled", true) await physics_frame _expect( (int(boss.collision_layer) & (1 << 6)) != 0, "Boss should restore enemy_body collision when boss combat unlocks" ) _expect(receiver.monitorable, "Boss damage receiver should restore when boss combat unlocks") stage.free() func _check_body_blocked_contact_hits() -> void: var stage := await _make_stage() if stage == null: return var container := stage.get_node("ActorsContainer") var player := container.get_node("Player") as Node2D var minion := container.get_node("JinZhanMinion") as Node2D var behavior := minion.get_node_or_null("MinionBehavior") if behavior != null: behavior.set("enabled", false) var health := minion.get_node("HealthComponent") player.global_position = Vector2(minion.global_position.x - 20.0, minion.global_position.y) player.set("heading", Vector2.RIGHT) await physics_frame var start_health := int(health.get("current")) player.call("submit_combo_input", "D", "perfect") await create_timer(0.8).timeout await physics_frame _expect( int(health.get("current")) < start_health, "Body-blocked Level 1 melee contact should still be hit by D basic attack" ) stage.free() func _check_opening_sequence_can_reach_minion() -> void: var stage := await _make_stage() if stage == null: return var container := stage.get_node("ActorsContainer") var player := container.get_node("Player") as Node2D var minion := container.get_node("JinZhanMinion") as Node2D var health := minion.get_node("HealthComponent") var start_health := int(health.get("current")) # new2 出生点改到地图最左侧后,玩家需要先推进一段路才遇敌;这里直接把 # 玩家放到近战怪的追击停步距离(120px)上,验证接敌后的连招能够命中。 player.global_position = Vector2(minion.global_position.x - 120.0, minion.global_position.y) player.set("heading", Vector2.RIGHT) await physics_frame for _index: int in range(5): player.call("submit_combo_input", "D", "perfect") await create_timer(0.65).timeout await physics_frame _expect( int(health.get("current")) < start_health, "Approach-range Level 1 sequence should be able to damage the first minion" ) stage.free() func _make_stage() -> Node: var stage_scene: PackedScene = load("res://scenes/stage/stage.tscn") _expect(stage_scene != null, "stage.tscn should load") if stage_scene == null: return null var stage: Node = stage_scene.instantiate() root.add_child(stage) await process_frame await physics_frame return stage func _expect(condition: bool, label: String) -> void: if not condition: failures.append(label) func _finish() -> void: if failures.is_empty(): print("PASS level1 melee contact") quit(0) else: for failure: String in failures: push_error(failure) quit(1)