# test_pet_companion_parity.gd —— 40250 伴随宠物系统 1:1 纯净回归测试套件 extends SceneTree const PetCompanionSystem = preload("res://pet_companion_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_pet_companion_parity (Direction 2: Pet Companion System 1:1) ===") _test_pet_table_and_validity() _test_summon_and_bonuses() _test_dismiss_and_toggle() _test_3d_movement_and_states() _test_time_expiry() _finish() func _test_pet_table_and_validity() -> void: print("\n--- Test 1: Pet Table Configuration & Seal Validation ---") var pet_sys := PetCompanionSystem.new() # Invalid seal item var invalid_res = pet_sys.toggle_pet_seal({"vnum": 99999}, Vector3.ZERO) _assert(invalid_res["ok"] == false and invalid_res["reason"] == "NOT_A_PET_SEAL", "Invalid seal rejected") # Check configured pets in PET_TABLE _assert(PetCompanionSystem.PET_TABLE.has(53001), "Fire Phoenix (53001) in table") _assert(PetCompanionSystem.PET_TABLE.has(53002), "Reindeer (53002) in table") _assert(PetCompanionSystem.PET_TABLE.has(53003), "Ice Phoenix (53003) in table") _assert(PetCompanionSystem.PET_TABLE.has(53005), "Bao Bao (53005) in table") _assert(PetCompanionSystem.PET_TABLE.has(53006), "Leonidas (53006) in table") var f_phoenix = PetCompanionSystem.PET_TABLE[53001] _assert(f_phoenix["mob_vnum"] == 34001, "Fire Phoenix mob vnum is 34001") _assert(f_phoenix["bonuses"]["max_hp"] == 1500, "Fire Phoenix bonus Max HP is 1500") _assert(f_phoenix["bonuses"]["monster_dmg_pct"] == 15, "Fire Phoenix bonus monster dmg is 15%") func _test_summon_and_bonuses() -> void: print("\n--- Test 2: Summoning & Stat Injection ---") var pet_sys := PetCompanionSystem.new() var signal_data := { "fired": false, "name": "", "vnum": 0 } pet_sys.pet_summoned.connect(func(pname: String, vnum: int): signal_data["fired"] = true signal_data["name"] = pname signal_data["vnum"] = vnum ) var player_pos := Vector3(100.0, 0.0, 200.0) var res = pet_sys.toggle_pet_seal({"vnum": 53001, "duration": 3600.0}, player_pos) _assert(res["ok"] == true, "Fire Phoenix summon succeeded") _assert(res["action"] == "summon", "Action is summon") _assert(pet_sys.is_summoned == true, "is_summoned is true") _assert(pet_sys.current_seal_vnum == 53001, "current_seal_vnum is 53001") _assert(signal_data["fired"] == true, "pet_summoned signal emitted") _assert(signal_data["name"] == "火凤凰", "Summoned pet name is 火凤凰") _assert(signal_data["vnum"] == 53001, "Summoned pet vnum is 53001") var bonuses = pet_sys.get_active_bonuses() _assert(bonuses["max_hp"] == 1500, "Active bonus max_hp is 1500") _assert(bonuses["monster_dmg_pct"] == 15, "Active bonus monster_dmg_pct is 15%") func _test_dismiss_and_toggle() -> void: print("\n--- Test 3: Dismiss & Switch Pets ---") var pet_sys := PetCompanionSystem.new() var dismiss_data := { "fired": false, "name": "" } pet_sys.pet_dismissed.connect(func(pname: String): dismiss_data["fired"] = true dismiss_data["name"] = pname ) var player_pos := Vector3.ZERO # Summon 53005 (Bao Bao) pet_sys.toggle_pet_seal({"vnum": 53005}, player_pos) _assert(pet_sys.is_summoned == true, "Bao Bao summoned") _assert(pet_sys.get_active_bonuses()["critical_pct"] == 15, "Bao Bao grants 15% crit") # Toggle 53005 again -> dismiss var res = pet_sys.toggle_pet_seal({"vnum": 53005}, player_pos) _assert(res["ok"] == true and res["action"] == "dismiss", "Toggling same seal dismisses pet") _assert(pet_sys.is_summoned == false, "Pet is no longer summoned") _assert(dismiss_data["fired"] == true, "pet_dismissed signal emitted") _assert(dismiss_data["name"] == "小胖熊猫", "Dismissed name matches") _assert(pet_sys.get_active_bonuses().is_empty(), "No bonuses active when pet dismissed") # Summon 53006 (Leonidas) and then summon 53003 (Ice Phoenix) to switch pet_sys.toggle_pet_seal({"vnum": 53006}, player_pos) _assert(pet_sys.current_seal_vnum == 53006, "Leonidas active") _assert(pet_sys.get_active_bonuses()["att_pct"] == 10, "Leonidas grants 10% att") # Switch directly to 53003 var switch_res = pet_sys.toggle_pet_seal({"vnum": 53003}, player_pos) _assert(switch_res["ok"] == true and switch_res["action"] == "summon", "Direct switch to Ice Phoenix succeeded") _assert(pet_sys.current_seal_vnum == 53003, "Active pet is now Ice Phoenix") _assert(pet_sys.get_active_bonuses()["def_pct"] == 15, "Ice Phoenix grants 15% def") func _test_3d_movement_and_states() -> void: print("\n--- Test 4: 3D Movement & State Machine ---") var pet_sys := PetCompanionSystem.new() var player_pos := Vector3(0.0, 0.0, 0.0) pet_sys.toggle_pet_seal({"vnum": 53001}, player_pos) # 1. When close (dist <= 2.0), state is wait pet_sys.pet_position = Vector3(1.0, 0.0, 0.0) # dist = 1.0m <= 2.0m pet_sys.update_movement(player_pos, 0.1) _assert(pet_sys.pet_state == "wait", "Pet waits within 2.0m") # 2. When player moves to (10, 0, 0), dist is 9.0m (> 2.0m) player_pos = Vector3(10.0, 0.0, 0.0) var prev_pos = pet_sys.pet_position pet_sys.update_movement(player_pos, 0.5) # dt = 0.5s -> move speed 4.5m/s -> move 2.25m towards player _assert(pet_sys.pet_state == "run", "Pet state is run when following player") _assert(pet_sys.pet_position.x > prev_pos.x, "Pet moved towards player") # 3. When player teleports far away (> 25.0m) player_pos = Vector3(100.0, 0.0, 100.0) pet_sys.update_movement(player_pos, 0.1) var new_dist = pet_sys.pet_position.distance_to(player_pos) _assert(new_dist < 5.0, "Pet instantly teleports near player when dist >= 25m") _assert(pet_sys.pet_state == "wait", "Pet returns to wait state after teleport") func _test_time_expiry() -> void: print("\n--- Test 5: Pet Duration Expiration ---") var pet_sys := PetCompanionSystem.new() pet_sys.toggle_pet_seal({"vnum": 53001, "duration": 10.0}, Vector3.ZERO) _assert(pet_sys.is_summoned == true, "Pet summoned with 10s duration") pet_sys.update(4.0) _assert(pet_sys.is_summoned == true, "Pet still active at 4s elapsed") _assert(is_equal_approx(pet_sys.remaining_time, 6.0), "6s remaining") pet_sys.update(7.0) # Exceeds remaining _assert(pet_sys.is_summoned == false, "Pet dismissed automatically when duration expires") _assert(pet_sys.get_active_bonuses().is_empty(), "No bonuses after expiration") func _finish() -> void: print("\n==========================================") print("Pet Companion Parity Test Results: %d Passed, %d Failed" % [_passed, _failed]) print("==========================================") if _failed > 0: quit(1) else: quit(0)