Files
mtgodot-poc/project/test_mercenary_companion_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

157 lines
6.4 KiB
GDScript

# test_mercenary_companion_parity.gd —— 40250 单人单机假人/陪练佣兵助战 1:1 回归测试套件
extends SceneTree
const MercenaryCompanionSystem = preload("res://mercenary_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_mercenary_companion_parity (Direction 2: Mercenary Companion 1:1) ===")
_test_summon_and_stats()
_test_buff_and_healing()
_test_assist_combat_and_stances()
_test_teleport_and_movement()
_test_downed_and_revival()
_finish()
func _test_summon_and_stats() -> void:
print("\n--- Test 1: Summoning & Level Scaling ---")
var merc := MercenaryCompanionSystem.new()
var summon_sig := {"fired": false, "type": 0, "name": ""}
merc.mercenary_summoned.connect(func(t: int, n: String):
summon_sig["fired"] = true
summon_sig["type"] = t
summon_sig["name"] = n
)
# Level 50 Shaman
var res = merc.summon_mercenary(MercenaryCompanionSystem.MercenaryType.SHAMAN_HEALER, Vector3(10, 0, 10), 50)
_assert(res["ok"] == true, "Shaman summoned")
_assert(summon_sig["fired"] == true and summon_sig["type"] == 1, "mercenary_summoned signal emitted")
_assert(merc.is_active == true and merc.is_alive == true, "Merc is active and alive")
_assert(merc.level == 50, "Mercenary level is 50")
# Level 50 growth: 1 + 49 * 0.05 = 3.45x base_hp (8000 * 3.45 = 27600)
_assert(merc.max_hp == int(8000 * 3.45), "HP properly scaled by level")
# Dismiss
var dis_sig := {"fired": false}
merc.mercenary_dismissed.connect(func(): dis_sig["fired"] = true)
var dis_res = merc.dismiss_mercenary()
_assert(dis_res["ok"] == true, "Merc dismissed")
_assert(dis_sig["fired"] == true, "mercenary_dismissed signal emitted")
_assert(merc.is_active == false, "Merc is inactive")
func _test_buff_and_healing() -> void:
print("\n--- Test 2: Shaman Buffs & Smart Emergency Healing ---")
var merc := MercenaryCompanionSystem.new()
merc.summon_mercenary(MercenaryCompanionSystem.MercenaryType.SHAMAN_HEALER, Vector3.ZERO, 30)
var buffs_received := []
merc.buff_applied_to_player.connect(func(bname: String, dur: float, val: float):
buffs_received.append({"name": bname, "duration": dur, "value": val})
)
var heal_received := {"fired": false, "amount": 0}
merc.heal_applied_to_player.connect(func(amt: int):
heal_received["fired"] = true
heal_received["amount"] = amt
)
# Update when player is healthy (10000 / 10000)
var up1 = merc.update_mercenary(0.1, Vector3.ZERO, 10000, 10000)
_assert(buffs_received.size() == 2, "Shaman applied 2 buffs (Blessing & Dragon's Aid)")
_assert(heal_received["fired"] == false, "No heal cast when player is healthy")
# Player HP drops to 40% (4000 / 10000 < 60%)
var up2 = merc.update_mercenary(0.1, Vector3.ZERO, 4000, 10000)
_assert(up2["status"] == "cast_heal", "Shaman triggered emergency heal")
_assert(heal_received["fired"] == true, "heal_applied_to_player signal fired")
_assert(heal_received["amount"] == 3500, "Healed 35% of max HP (3500)")
# Second hit while heal is on cooldown should not retrigger heal
heal_received["fired"] = false
var up3 = merc.update_mercenary(0.1, Vector3.ZERO, 3000, 10000)
_assert(heal_received["fired"] == false, "Heal respects cooldown")
func _test_assist_combat_and_stances() -> void:
print("\n--- Test 3: Stances & Warrior Assist Combat ---")
var merc := MercenaryCompanionSystem.new()
merc.summon_mercenary(MercenaryCompanionSystem.MercenaryType.WARRIOR_TANK, Vector3.ZERO, 50)
# Check stance change
var st_res = merc.set_stance(MercenaryCompanionSystem.Stance.FOLLOW)
_assert(st_res["ok"] == true and merc.current_stance == MercenaryCompanionSystem.Stance.FOLLOW, "Stance set to FOLLOW")
# Change to ASSIST
merc.set_stance(MercenaryCompanionSystem.Stance.ASSIST)
_assert(merc.current_stance == MercenaryCompanionSystem.Stance.ASSIST, "Stance set to ASSIST")
var atk_sig := {"fired": false, "tgt": "", "dmg": 0}
merc.mercenary_attacked.connect(func(tgt: String, dmg: int):
atk_sig["fired"] = true
atk_sig["tgt"] = tgt
atk_sig["dmg"] = dmg
)
# Target at (1.5, 0, 0) within melee range
merc.pos = Vector3.ZERO
var act = merc.update_mercenary(0.1, Vector3.ZERO, 10000, 10000, "mob_101", Vector3(1.5, 0, 0))
_assert(act["status"] == "attacked_target", "Warrior attacked target")
_assert(atk_sig["fired"] == true and atk_sig["tgt"] == "mob_101", "mercenary_attacked signal fired")
_assert(atk_sig["dmg"] == merc.atk, "Damage matches warrior's ATK")
func _test_teleport_and_movement() -> void:
print("\n--- Test 4: Distance Teleport Catch-up ---")
var merc := MercenaryCompanionSystem.new()
merc.summon_mercenary(MercenaryCompanionSystem.MercenaryType.SHAMAN_HEALER, Vector3.ZERO, 10)
# Place mercenary 30 meters away (> 25m TELEPORT_DISTANCE)
merc.pos = Vector3(30, 0, 0)
var tp_act = merc.update_mercenary(0.1, Vector3.ZERO, 5000, 5000)
_assert(tp_act["status"] == "teleported_to_player", "Mercenary teleported back to player")
_assert(merc.pos.distance_to(Vector3.ZERO) < 3.0, "Position is now close to player")
func _test_downed_and_revival() -> void:
print("\n--- Test 5: Downed State & Revival ---")
var merc := MercenaryCompanionSystem.new()
merc.summon_mercenary(MercenaryCompanionSystem.MercenaryType.WARRIOR_TANK, Vector3.ZERO, 10)
var down_sig := {"fired": false}
var rev_sig := {"fired": false}
merc.mercenary_downed.connect(func(): down_sig["fired"] = true)
merc.mercenary_revived.connect(func(): rev_sig["fired"] = true)
# Take fatal damage
var hit = merc.receive_damage(999999)
_assert(hit["ok"] == true and hit["downed"] == true, "Mercenary downed")
_assert(down_sig["fired"] == true, "mercenary_downed signal fired")
_assert(merc.is_alive == false, "is_alive is false")
# Instant revive
var rev_res = merc.revive(true)
_assert(rev_res["ok"] == true, "Instant revive succeeded")
_assert(rev_sig["fired"] == true, "mercenary_revived signal fired")
_assert(merc.is_alive == true and merc.current_hp == merc.max_hp, "HP restored to 100% on instant revive")
func _finish() -> void:
print("\n==========================================")
print("Mercenary Companion 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 Mercenary Companion parity tests passed!")
quit(0)