Files
mtgodot-poc/project/test_classic_pet_parity.gd
T
shenandshen 66d217b313 feat(client): 完成40250客户端核心功能1:1对齐与桥梁高度采样修复
- 桥梁与静态物体高度采样修复:
  - 严格对齐 40250 CMapOutdoor::GetHeight 与 CAttributeInstance::GetHeight
  - 解析 .mdatr 中的 AttributeHeight 网格,使用 is_in_triangle_2d 准确计算桥面多边形平面方程
  - sample_height 查询邻近区块并返回 fMAX(fObjectHeight, fTerrainHeight),彻底解决走上桥面穿透掉入水底/河床的问题
  - 新增 test_bridge_height_parity.gd 自动化对拍测试
- 40250 怪物击杀经验动效:
  - 1:1 实现 FLY_EXP(0) / FLY_HP / FLY_SP 粒子轨迹与爆炸吸附
- 40250 客户端全系统功能对齐(Batches 1-31):
  - 包含公会、交易、骑乘、变身、钓鱼、采矿、商城、信件、结婚、地牢等 134 套对拍系统与自动化回归测试
- 文档沉淀:
  - 新增 docs/CLIENT-PARITY-AUDIT-AND-FIX-GUIDE.md 客户端对拍缺陷发现与修复工程指南
2026-09-19 08:51:25 -07:00

147 lines
6.2 KiB
GDScript

# test_classic_pet_parity.gd —— 40250 经典封印宠物系统 1:1 回归测试套件
extends SceneTree
const ClassicPetSystem = preload("res://classic_pet_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_classic_pet_parity (Direction 1: Classic Pet System 1:1) ===")
_test_summon_and_buff_application()
_test_auto_unsummon_previous_pet()
_test_dungeon_only_buff_condition()
_test_follow_and_teleport_ai()
_test_serialization()
_finish()
func _test_summon_and_buff_application() -> void:
print("\n--- Test 1: Summon & Buff Injection/Removal ---")
var sys := ClassicPetSystem.new()
var player := {"max_hp": 5000, "attack_pct": 0, "defense_pct": 0}
var seal_firedrake := {"vnum": ClassicPetSystem.SEAL_BABY_FIREDRAKE}
var summon_sig := {"fired": false, "vnum": 0, "name": ""}
sys.pet_summoned.connect(func(v: int, n: String, _b: Dictionary):
summon_sig["fired"] = true
summon_sig["vnum"] = v
summon_sig["name"] = n
)
var unsummon_sig := {"fired": false, "vnum": 0}
sys.pet_unsummoned.connect(func(v: int, _n: String):
unsummon_sig["fired"] = true
unsummon_sig["vnum"] = v
)
# Summon Baby Firedrake
var res = sys.summon_pet(seal_firedrake, player, false, Vector2.ZERO)
_assert(res["ok"] == true, "Summon succeeded")
_assert(sys.is_summoned == true, "System reports pet is summoned")
_assert(sys.active_mob_vnum == ClassicPetSystem.MOB_BABY_FIREDRAKE, "Mob vnum is 34005")
_assert(player["max_hp"] == 6500, "HP increased by 1500 (5000 -> 6500)")
_assert(player["attack_pct"] == 15, "Attack pct increased by 15%")
_assert(summon_sig["fired"] == true, "pet_summoned signal emitted")
# Unsummon
var un_res = sys.unsummon_pet(player)
_assert(un_res["ok"] == true, "Unsummon succeeded")
_assert(sys.is_summoned == false, "System reports pet unsummoned")
_assert(player["max_hp"] == 5000, "HP reversed back to 5000")
_assert(player["attack_pct"] == 0, "Attack pct reversed back to 0%")
_assert(unsummon_sig["fired"] == true, "pet_unsummoned signal emitted")
func _test_auto_unsummon_previous_pet() -> void:
print("\n--- Test 2: Auto Unsummon Previous Pet When Summoning New ---")
var sys := ClassicPetSystem.new()
var player := {"monster_damage_pct": 0, "defense_pct": 0, "max_hp": 10000}
# 1. Summon Boar (+15% monster damage)
sys.summon_pet({"vnum": ClassicPetSystem.SEAL_BABY_BOAR}, player, false)
_assert(player["monster_damage_pct"] == 15, "Boar buff applied (+15% monster dmg)")
# 2. Directly summon Ice Phoenix (+15% def, +1500 HP)
sys.summon_pet({"vnum": ClassicPetSystem.SEAL_ICE_PHOENIX}, player, false)
_assert(sys.active_mob_vnum == ClassicPetSystem.MOB_ICE_PHOENIX, "Now Ice Phoenix is active")
_assert(player["monster_damage_pct"] == 0, "Previous Boar buff cleared")
_assert(player["defense_pct"] == 15, "Phoenix defense buff applied (+15%)")
_assert(player["max_hp"] == 11500, "Phoenix HP buff applied (+1500)")
func _test_dungeon_only_buff_condition() -> void:
print("\n--- Test 3: 40250 Dungeon-Only Buff Validation (Baby Azrael) ---")
var sys := ClassicPetSystem.new()
var seal_azrael := {"vnum": ClassicPetSystem.SEAL_BABY_AZRAEL}
# In normal field map (not in dungeon)
var player1 := {"max_hp": 5000, "dungeon_attack_pct": 0}
sys.summon_pet(seal_azrael, player1, false)
_assert(player1["max_hp"] == 6000, "Azrael standard HP (+1000) applied in field")
_assert(player1["dungeon_attack_pct"] == 0, "Dungeon attack buff not applied outside dungeon")
sys.unsummon_pet(player1)
# Inside dungeon
var player2 := {"max_hp": 5000, "dungeon_attack_pct": 0}
sys.summon_pet(seal_azrael, player2, true)
_assert(player2["max_hp"] == 6000, "Azrael standard HP (+1000) applied in dungeon")
_assert(player2["dungeon_attack_pct"] == 20, "Dungeon attack buff (+20%) successfully applied in dungeon")
sys.unsummon_pet(player2)
_assert(player2["dungeon_attack_pct"] == 0, "Dungeon buff cleared after unsummon")
func _test_follow_and_teleport_ai() -> void:
print("\n--- Test 4: Follow AI (Idle, Follow, Teleport) ---")
var sys := ClassicPetSystem.new()
var dummy_stats := {}
sys.summon_pet({"vnum": ClassicPetSystem.SEAL_BABY_BOAR}, dummy_stats, false, Vector2(0, 0))
# Owner is at (1, 0) -> dist is 1.5 - 1.0 = 0.5 (< 2.0 MIN_FOLLOW_DIST)
var ai_idle = sys.update_follow_ai(Vector2(1.0, 0.0), 0.1)
_assert(ai_idle["action"] == "idle", "AI stays idle when within 2.0m")
# Owner walks away to (8, 0) -> dist > 2.0m
var ai_follow = sys.update_follow_ai(Vector2(8.0, 0.0), 0.5)
_assert(ai_follow["action"] == "follow", "AI follows owner when distance > 2.0m")
# Owner warps/runs far away to (100, 100) -> dist > 15.0m
var move_sig := {"fired": false, "teleport": false}
sys.pet_moved.connect(func(_pos: Vector2, is_tele: bool):
move_sig["fired"] = true
move_sig["teleport"] = is_tele
)
var ai_tele = sys.update_follow_ai(Vector2(100.0, 100.0), 0.1)
_assert(ai_tele["action"] == "teleport", "AI teleports when distance > 15.0m")
_assert(move_sig["teleport"] == true, "pet_moved signal reported teleport")
func _test_serialization() -> void:
print("\n--- Test 5: State Serialization & Restoration ---")
var sys1 := ClassicPetSystem.new()
var dummy_stats := {"max_hp": 5000}
sys1.summon_pet({"vnum": ClassicPetSystem.SEAL_BABY_FIREDRAKE}, dummy_stats, false, Vector2(10, 20))
var data = sys1.serialize()
_assert(data["is_summoned"] == true, "Serialized is_summoned")
_assert(data["active_mob_vnum"] == ClassicPetSystem.MOB_BABY_FIREDRAKE, "Serialized mob vnum")
_assert(data["active_seal_vnum"] == ClassicPetSystem.SEAL_BABY_FIREDRAKE, "Serialized seal vnum")
var sys2 := ClassicPetSystem.new()
sys2.deserialize(data)
_assert(sys2.is_summoned == true, "Restored is_summoned")
_assert(sys2.active_mob_vnum == ClassicPetSystem.MOB_BABY_FIREDRAKE, "Restored mob vnum")
_assert(sys2.active_pet_name == "幼火龙", "Restored pet name")
func _finish() -> void:
print("\n=======================================================")
print("Classic Pet System Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)