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

161 lines
6.4 KiB
GDScript

# test_horse_combat_parity.gd —— 40250 战马骑乘战斗与马术技能 1:1 纯净回归测试套件
extends SceneTree
const HorseCombatSystem = preload("res://horse_combat_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_horse_combat_parity (Direction 3: Mounted Combat & Horse Skills 1:1) ===")
_test_mounted_state_and_basic_attack()
_test_horse_skills_137_to_140()
_test_cooldowns_and_sp_validation()
_test_stamina_and_feeding()
_finish()
func _test_mounted_state_and_basic_attack() -> void:
print("\n--- Test 1: Mounted State & 5.0m Attack Sweep Range ---")
var hcs := HorseCombatSystem.new()
var targets: Array = [
{"id": 1, "distance": 2.5}, # In range
{"id": 2, "distance": 4.8}, # In range (<= 5.0m)
{"id": 3, "distance": 5.2} # Out of range (> 5.0m)
]
# 1. Not riding
var r1 = hcs.perform_mounted_basic_attack(500, targets)
_assert(r1["ok"] == false and r1["reason"] == "NOT_RIDING", "Attack blocked when not riding")
# 2. Beginner horse (Tier 1) cannot fight
hcs.set_mounted_state(true, HorseCombatSystem.HORSE_TIER_BEGINNER)
var r2 = hcs.perform_mounted_basic_attack(500, targets)
_assert(r2["ok"] == false and r2["reason"] == "HORSE_TIER_TOO_LOW", "Attack blocked on beginner horse")
# 3. Combat horse (Tier 2) can fight
hcs.set_mounted_state(true, HorseCombatSystem.HORSE_TIER_COMBAT)
var attack_data := {"fired": false, "targets": 0, "dmg": 0}
hcs.horse_attack_performed.connect(func(cnt: int, dmg: int):
attack_data["fired"] = true
attack_data["targets"] = cnt
attack_data["dmg"] = dmg
)
var r3 = hcs.perform_mounted_basic_attack(500, targets)
_assert(r3["ok"] == true, "Attack succeeds on combat horse")
_assert(r3["attack_range"] == 5.0, "Mounted attack range is 5.0m")
_assert(r3["hit_count"] == 2, "Hit 2 targets within 5.0m (2.5m and 4.8m)")
_assert(r3["damage_per_hit"] == 600, "1.2x damage multiplier (500 * 1.2 = 600)")
_assert(r3["total_damage"] == 1200, "Total damage is 1200 for 2 targets")
_assert(attack_data["fired"] == true and attack_data["targets"] == 2, "horse_attack_performed signal fired")
func _test_horse_skills_137_to_140() -> void:
print("\n--- Test 2: 4 Military Horse Skills (137-140) ---")
var hcs := HorseCombatSystem.new()
# Combat horse (Tier 2) cannot use skills
hcs.set_mounted_state(true, HorseCombatSystem.HORSE_TIER_COMBAT)
var r1 = hcs.use_horse_skill(137, 100, 1000)
_assert(r1["ok"] == false and r1["reason"] == "REQUIRES_MILITARY_HORSE", "Skills blocked on tier 2 horse")
# Switch to Military Horse (Tier 3)
hcs.set_mounted_state(true, HorseCombatSystem.HORSE_TIER_MILITARY)
var skill_signal := {"fired": false, "id": 0, "name": ""}
hcs.horse_skill_used.connect(func(sid: int, sname: String, _sp: int):
skill_signal["fired"] = true
skill_signal["id"] = sid
skill_signal["name"] = sname
)
# 1. Skill 137: 战马砍击 (Horse Slash)
var s137 = hcs.use_horse_skill(137, 100, 1000)
_assert(s137["ok"] == true, "Skill 137 used successfully")
_assert(s137["damage"] == 2500, "Skill 137 deals 250% damage (2500)")
_assert(s137["effect"] == "KNOCKBACK", "Skill 137 inflicts KNOCKBACK")
_assert(skill_signal["fired"] == true and skill_signal["id"] == 137, "horse_skill_used signal fired for 137")
# 2. Skill 138: 战马突刺 (Horse Charge)
var s138 = hcs.use_horse_skill(138, 100, 1000)
_assert(s138["ok"] == true, "Skill 138 used successfully")
_assert(s138["damage"] == 2000, "Skill 138 deals 200% damage (2000)")
_assert(s138["effect"] == "STUMBLE", "Skill 138 inflicts STUMBLE")
# 3. Skill 139: 战马震击 (Horse Stomp)
var s139 = hcs.use_horse_skill(139, 100, 1000)
_assert(s139["ok"] == true, "Skill 139 used successfully")
_assert(s139["damage"] == 3000, "Skill 139 deals 300% damage (3000)")
_assert(s139["effect"] == "AOE_STUN", "Skill 139 inflicts AOE_STUN")
# 4. Skill 140: 破浪击 (Power Wave)
var s140 = hcs.use_horse_skill(140, 100, 1000)
_assert(s140["ok"] == true, "Skill 140 used successfully")
_assert(s140["damage"] == 2200, "Skill 140 deals 220% damage (2200)")
_assert(s140["range"] == 12.0, "Skill 140 has 12.0m ranged reach")
func _test_cooldowns_and_sp_validation() -> void:
print("\n--- Test 3: Skill Cooldowns & SP Verification ---")
var hcs := HorseCombatSystem.new()
hcs.set_mounted_state(true, HorseCombatSystem.HORSE_TIER_MILITARY)
# Cast 137
hcs.use_horse_skill(137, 100, 1000)
# Cast again immediately -> on cooldown
var cd_res = hcs.use_horse_skill(137, 100, 1000)
_assert(cd_res["ok"] == false and cd_res["reason"] == "SKILL_ON_COOLDOWN", "Skill 137 is on cooldown")
# Update 10s -> still on cooldown
hcs.update(10.0)
_assert(hcs.skill_cooldowns[137] == 5.0, "5.0s remaining cooldown on 137")
# Update 6s -> ready
hcs.update(6.0)
_assert(hcs.skill_cooldowns[137] == 0.0, "Cooldown expired")
# Try with insufficient SP (< 30)
var low_sp_res = hcs.use_horse_skill(137, 20, 1000)
_assert(low_sp_res["ok"] == false and low_sp_res["reason"] == "INSUFFICIENT_SP", "Skill blocked with low SP")
func _test_stamina_and_feeding() -> void:
print("\n--- Test 4: Horse Stamina & Feeding (Carrot / Ginseng) ---")
var hcs := HorseCombatSystem.new()
hcs.set_mounted_state(true, HorseCombatSystem.HORSE_TIER_MILITARY)
# Drain stamina manually to test exhaustion
hcs.horse_stamina = 2.0
var ex_res = hcs.use_horse_skill(137, 100, 1000)
_assert(ex_res["ok"] == false and ex_res["reason"] == "HORSE_EXHAUSTED", "Skill blocked when stamina < 5")
# Feed invalid food
var r_bad = hcs.feed_horse(99999)
_assert(r_bad["ok"] == false and r_bad["reason"] == "WRONG_FOOD", "Invalid food rejected")
# Feed Carrot (50054) -> +40 stamina (2 + 40 = 42)
var r_carrot = hcs.feed_horse(50054)
_assert(r_carrot["ok"] == true, "Fed carrot successfully")
_assert(hcs.horse_stamina == 42.0, "Stamina increased by 40 to 42.0")
# Feed Ginseng (50058) -> +80 stamina (42 + 80 = 122 -> capped at 100.0)
var r_ginseng = hcs.feed_horse(50058)
_assert(r_ginseng["ok"] == true, "Fed ginseng successfully")
_assert(hcs.horse_stamina == 100.0, "Stamina capped at 100.0")
func _finish() -> void:
print("\n==========================================")
print("Horse Combat Parity Test Results: %d Passed, %d Failed" % [_passed, _failed])
print("==========================================")
if _failed > 0:
quit(1)
else:
quit(0)