- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
141 lines
5.1 KiB
GDScript
141 lines
5.1 KiB
GDScript
# test_mounted_combat_skill_parity.gd —— 40250 军马冲杀与马背技能 1:1 回归测试套件
|
|
extends SceneTree
|
|
|
|
const MountedCombatSkillSystem = preload("res://mounted_combat_skill_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_mounted_combat_skill_parity (Direction 1: Mounted Combat 1:1) ===")
|
|
_test_horse_tiers_and_mounting()
|
|
_test_mounted_basic_attacks()
|
|
_test_military_horse_skills()
|
|
_test_stamina_and_feeding()
|
|
_finish()
|
|
|
|
func _test_horse_tiers_and_mounting() -> void:
|
|
print("\n--- Test 1: Horse Tiers & Mounting Speeds ---")
|
|
var sys := MountedCombatSkillSystem.new()
|
|
|
|
# Normal Horse (lvl 5)
|
|
var r1 = sys.mount_horse(5)
|
|
_assert(r1["tier"] == MountedCombatSkillSystem.HorseTier.NORMAL, "Lvl 5 is NORMAL horse")
|
|
_assert(r1["speed_bonus"] == 30, "Normal horse gives +30 speed")
|
|
sys.unmount_horse()
|
|
|
|
# Combat Horse (lvl 15)
|
|
var r2 = sys.mount_horse(15)
|
|
_assert(r2["tier"] == MountedCombatSkillSystem.HorseTier.COMBAT, "Lvl 15 is COMBAT horse")
|
|
_assert(r2["speed_bonus"] == 40, "Combat horse gives +40 speed")
|
|
sys.unmount_horse()
|
|
|
|
# Military Horse (lvl 25)
|
|
var r3 = sys.mount_horse(25)
|
|
_assert(r3["tier"] == MountedCombatSkillSystem.HorseTier.MILITARY, "Lvl 25 is MILITARY horse")
|
|
_assert(r3["speed_bonus"] == 50, "Military horse gives +50 speed")
|
|
sys.unmount_horse()
|
|
|
|
func _test_mounted_basic_attacks() -> void:
|
|
print("\n--- Test 2: Mounted Basic Attacks ---")
|
|
var sys := MountedCombatSkillSystem.new()
|
|
|
|
# Normal horse cannot attack
|
|
sys.mount_horse(5)
|
|
var fail_atk = sys.execute_mounted_attack(100, ["mob1", "mob2"])
|
|
_assert(fail_atk["ok"] == false and fail_atk["reason"] == "TIER_TOO_LOW", "Normal horse cannot attack")
|
|
sys.unmount_horse()
|
|
|
|
# Combat horse can attack
|
|
sys.mount_horse(15)
|
|
var atk_sig := {"fired": false, "hits": 0, "dmg": 0}
|
|
sys.mounted_attack_executed.connect(func(h: int, d: int):
|
|
atk_sig["fired"] = true
|
|
atk_sig["hits"] = h
|
|
atk_sig["dmg"] = d
|
|
)
|
|
|
|
var res = sys.execute_mounted_attack(500, ["m1", "m2", "m3"])
|
|
_assert(res["ok"] == true, "Combat horse attacked")
|
|
_assert(res["hit_count"] == 3, "Hit 3 targets")
|
|
_assert(res["dmg_per_hit"] == int(500 * 1.2), "Deals 1.2x base attack (600)")
|
|
_assert(res["total_damage"] == 1800, "Total damage is 1800")
|
|
_assert(atk_sig["fired"] == true and atk_sig["dmg"] == 1800, "mounted_attack_executed signal fired")
|
|
_assert(sys.stamina == 99, "Stamina consumed 100 -> 99")
|
|
sys.unmount_horse()
|
|
|
|
func _test_military_horse_skills() -> void:
|
|
print("\n--- Test 3: Military Horse Skills (121, 122, 123) ---")
|
|
var sys := MountedCombatSkillSystem.new()
|
|
sys.mount_horse(25) # Military horse
|
|
|
|
var player := {"sp": 300}
|
|
var skill_sig := {"fired": false, "vnum": 0, "dmg": 0}
|
|
sys.horse_skill_cast.connect(func(v: int, _n: String, d: int, _h: int):
|
|
skill_sig["fired"] = true
|
|
skill_sig["vnum"] = v
|
|
skill_sig["dmg"] = d
|
|
)
|
|
|
|
# Cast 121: Horse Charge (2.5x dmg, SP 60, cd 12.0)
|
|
var c1 = sys.cast_horse_skill(MountedCombatSkillSystem.SKILL_HORSE_CHARGE, 1000, player, ["m1", "m2"])
|
|
_assert(c1["ok"] == true, "Horse Charge cast successfully")
|
|
_assert(c1["damage"] == 2500, "Damage = 1000 * 2.5 = 2500")
|
|
_assert(player["sp"] == 240, "SP deducted (300 - 60 = 240)")
|
|
_assert(skill_sig["fired"] == true and skill_sig["vnum"] == 121, "horse_skill_cast emitted")
|
|
|
|
# Second cast while on cooldown must fail
|
|
var fail_cd = sys.cast_horse_skill(MountedCombatSkillSystem.SKILL_HORSE_CHARGE, 1000, player, ["m1"])
|
|
_assert(fail_cd["ok"] == false and fail_cd["reason"] == "SKILL_ON_COOLDOWN", "Cooldown enforced")
|
|
|
|
# Update 12 seconds
|
|
sys.update(12.5)
|
|
|
|
# Cast 122: Horse Stomp (2.0x dmg, SP 75)
|
|
var c2 = sys.cast_horse_skill(MountedCombatSkillSystem.SKILL_HORSE_STOMP, 1000, player, ["m1", "m2", "m3", "m4"])
|
|
_assert(c2["ok"] == true and c2["damage"] == 2000, "Horse Stomp deals 2.0x (2000)")
|
|
_assert(player["sp"] == 165, "SP is now 240 - 75 = 165")
|
|
|
|
func _test_stamina_and_feeding() -> void:
|
|
print("\n--- Test 4: Stamina Depletion & Feeding ---")
|
|
var sys := MountedCombatSkillSystem.new()
|
|
sys.mount_horse(21)
|
|
sys.stamina = 10 # Low stamina
|
|
|
|
var inv: Array = [
|
|
{"vnum": 50055, "name": "胡萝卜", "count": 2}
|
|
]
|
|
|
|
var feed_sig := {"fired": false, "rec": 0}
|
|
sys.horse_fed.connect(func(_n: String, rec: int):
|
|
feed_sig["fired"] = true
|
|
feed_sig["rec"] = rec
|
|
)
|
|
|
|
var res = sys.feed_horse(50055, inv)
|
|
_assert(res["ok"] == true, "Fed carrot")
|
|
_assert(res["recovered"] == 60, "Carrot restores 60 stamina")
|
|
_assert(sys.stamina == 70, "Stamina is now 10 + 60 = 70")
|
|
_assert(inv[0]["count"] == 1, "Carrot count decreased 2 -> 1")
|
|
_assert(feed_sig["fired"] == true and feed_sig["rec"] == 60, "horse_fed emitted")
|
|
|
|
func _finish() -> void:
|
|
print("\n==========================================")
|
|
print("Mounted Combat Skill 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 Mounted Combat Skill parity tests passed!")
|
|
quit(0)
|