# test_world_boss_ai_behavior_parity.gd —— 40250 世界首领动态 AI 行为状态机 1:1 回归测试套件 extends SceneTree const WorldBossAISystem = preload("res://world_boss_ai_system.gd") var _passed := 0 var _failed := 0 func _assert(cond: bool, msg: String) -> void: if cond: _passed += 1 print(" [PASS] %s" % msg) else: _failed += 1 printerr(" [FAIL] %s" % msg) func _init() -> void: print("\n=== Running test_world_boss_ai_behavior_parity (Direction 1: World Boss AI 1:1) ===") _test_boss_initialization() _test_aggro_and_chase() _test_skills_and_attacks() _test_enrage_mechanic() _test_leash_and_reset() _test_death() _finish() func _test_boss_initialization() -> void: print("\n--- Test 1: Boss Initialization & Table Config ---") var ai := WorldBossAISystem.new() var fail = ai.init_boss(99999) _assert(fail["ok"] == false and fail["reason"] == "INVALID_BOSS_VNUM", "Invalid boss vnum rejected") var vnums = [1901, 1304, 2191, 2206] for v in vnums: var init_res = ai.init_boss(v, Vector3(100, 0, 100)) _assert(init_res["ok"] == true, "Boss %d initialized" % v) _assert(ai.current_state == WorldBossAISystem.STATE_IDLE, "Starts in IDLE state") _assert(ai.current_hp == ai.max_hp, "HP at max") func _test_aggro_and_chase() -> void: print("\n--- Test 2: Aggro Acquisition & Chase Mechanics ---") var ai := WorldBossAISystem.new() ai.init_boss(1901, Vector3.ZERO) var aggro_sig := {"fired": false, "id": "", "dist": 0.0} ai.target_acquired.connect(func(tid: String, d: float): aggro_sig["fired"] = true aggro_sig["id"] = tid aggro_sig["dist"] = d ) # Target 25m away (beyond 18m AGGRO_RADIUS) var step1 = ai.update_ai(0.1, Vector3(25, 0, 0), "player", true) _assert(aggro_sig["fired"] == false, "No aggro at 25m") _assert(step1["action"] == "wandering", "Wandering when target is far") # Target enters 15m (within 18m AGGRO_RADIUS) var step2 = ai.update_ai(0.1, Vector3(15, 0, 0), "player", true) _assert(aggro_sig["fired"] == true and aggro_sig["id"] == "player", "Target acquired signal fired at 15m") _assert(step2["state"] == WorldBossAISystem.STATE_CHASE, "Transitions to CHASE state") _assert(ai.current_position.x > 0.0, "Boss stepped forward towards player") func _test_skills_and_attacks() -> void: print("\n--- Test 3: Skill Casting & Basic Attacks ---") var ai := WorldBossAISystem.new() ai.init_boss(1901, Vector3.ZERO) # Nine tails: Atk 850, Def 450, Skill: 暴风雪呼啸, mult 1.8 var skill_sig := {"fired": false, "name": "", "dmg": 0} ai.skill_cast.connect(func(sname: String, dmg: int, _aoe: float): skill_sig["fired"] = true skill_sig["name"] = sname skill_sig["dmg"] = dmg ) # Target at 5.0m (within 9m SKILL_RANGE, but beyond 3.5m ATTACK_RANGE) var skill_act = ai.update_ai(0.1, Vector3(5, 0, 0), "player", true) _assert(skill_act["state"] == WorldBossAISystem.STATE_SKILL, "Casts skill when in skill range") _assert(skill_sig["fired"] == true and skill_sig["name"] == "暴风雪呼啸", "Skill signal emitted with correct name") _assert(skill_sig["dmg"] == int(850 * 1.8), "Skill damage is base_atk * 1.8") # Target closes to 2.0m (within 3.5m ATTACK_RANGE) # Skill is now on cooldown, should perform basic attack var atk_act = ai.update_ai(0.1, Vector3(2, 0, 0), "player", true) _assert(atk_act["state"] == WorldBossAISystem.STATE_ATTACK, "Executes basic attack within melee range") _assert(atk_act["damage"] == 850, "Basic attack damage matches base_atk") func _test_enrage_mechanic() -> void: print("\n--- Test 4: Enrage Mechanic (< 30% HP) ---") var ai := WorldBossAISystem.new() ai.init_boss(1304, Vector3.ZERO) # 1304: HP 320000, Def 480, Atk 920 var enrage_sig := {"fired": false, "mult": 0.0} ai.boss_enraged.connect(func(m: float): enrage_sig["fired"] = true enrage_sig["mult"] = m ) # Deal damage down to ~40% HP (no enrage) # Max HP = 320000. 40% = 128000. Damage needed = 192000 net dmg. # Raw dmg = 192000 + 480 = 192480. var hit1 = ai.receive_damage(192480, "player") _assert(hit1["is_enraged"] == false, "Not enraged at 40% HP") _assert(enrage_sig["fired"] == false, "boss_enraged signal not yet fired") # Deal damage down to 25% HP (< 30% HP trigger) # Need 50000 more net dmg. var hit2 = ai.receive_damage(50000 + 480, "player") _assert(hit2["is_enraged"] == true, "Enraged triggered at <= 30% HP") _assert(enrage_sig["fired"] == true and enrage_sig["mult"] == 1.5, "boss_enraged signal emitted with 1.5x") # Attack while enraged has 1.5x damage # 1. Enraged skill cast var enraged_skill = ai.update_ai(0.1, Vector3(5, 0, 0), "player", true) _assert(enraged_skill["state"] == WorldBossAISystem.STATE_SKILL, "Enraged skill cast triggered") _assert(enraged_skill["damage"] == int(920 * 2.0 * 1.5), "Enraged skill gets 1.5x damage multiplier") # 2. Enraged basic attack (skill is now on cooldown) var enraged_atk = ai.update_ai(0.1, Vector3(2, 0, 0), "player", true) _assert(enraged_atk["state"] == WorldBossAISystem.STATE_ATTACK, "Enraged basic attack triggered") _assert(enraged_atk["damage"] == int(920 * 1.5), "Enraged basic attack gets 1.5x damage") func _test_leash_and_reset() -> void: print("\n--- Test 5: Leash Distance & Full Reset ---") var ai := WorldBossAISystem.new() ai.init_boss(2191, Vector3.ZERO) # Spawn at (0, 0, 0) var leash_start := {"fired": false} var leash_done := {"fired": false} ai.leash_reset_started.connect(func(): leash_start["fired"] = true) ai.leash_reset_completed.connect(func(): leash_done["fired"] = true) # Boss takes damage ai.receive_damage(100000, "player") _assert(ai.current_hp < ai.max_hp, "Boss damaged") # Move boss far beyond 35m LEASH_DISTANCE ai.current_position = Vector3(40, 0, 0) var leash_act = ai.update_ai(0.1, Vector3(45, 0, 0), "player", true) _assert(leash_act["state"] == WorldBossAISystem.STATE_LEASH_RETURN, "Enters LEASH_RETURN state") _assert(leash_start["fired"] == true, "leash_reset_started signal fired") # In leash return, attacks are immune var immune_hit = ai.receive_damage(50000, "player") _assert(immune_hit.get("immune", false) == true, "Attacks during leash return are IMMUNE") # Simulate returning to spawn point while ai.current_state == WorldBossAISystem.STATE_LEASH_RETURN: ai.update_ai(0.5, Vector3(45, 0, 0), "player", true) _assert(leash_done["fired"] == true, "leash_reset_completed signal fired") _assert(ai.current_state == WorldBossAISystem.STATE_IDLE, "Boss back in IDLE") _assert(ai.current_hp == ai.max_hp, "Boss HP fully restored to 100%") _assert(ai.current_position.distance_to(Vector3.ZERO) < 0.1, "Boss back at spawn point") func _test_death() -> void: print("\n--- Test 6: Death State ---") var ai := WorldBossAISystem.new() ai.init_boss(2206, Vector3.ZERO) var fatal = ai.receive_damage(10000000, "player") _assert(fatal["dead"] == true, "Fatal hit flags dead == true") _assert(ai.current_state == WorldBossAISystem.STATE_DEAD, "State becomes DEAD") var after_death = ai.update_ai(0.1, Vector3.ZERO, "player", true) _assert(after_death["state"] == WorldBossAISystem.STATE_DEAD and after_death["action"] == "none", "Dead boss does nothing") func _finish() -> void: print("\n==========================================") print("World Boss AI Parity Tests Finished:") print("Passed: %d, Failed: %d" % [_passed, _failed]) print("==========================================") if _failed > 0: printerr("FAILED: Some tests did not pass.") quit(1) else: print("SUCCESS: All World Boss AI parity tests passed!") quit(0)