- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
124 lines
5.5 KiB
GDScript
124 lines
5.5 KiB
GDScript
# test_mount_horse_parity.gd —— 战马与骑乘系统 1:1 回归测试套件
|
|
# 运行方式:
|
|
# /opt/homebrew/bin/godot --headless --path metin2-client/project --script test_mount_horse_parity.gd
|
|
extends SceneTree
|
|
|
|
const HorseSystem = preload("res://horse_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_mount_horse_parity (Direction 3: Horse & Mount System 1:1) ===")
|
|
_test_horse_tiers_and_food()
|
|
_test_summoning()
|
|
_test_ctrl_h_ride()
|
|
_test_ctrl_b_back()
|
|
_test_ctrl_g_ride()
|
|
_test_ctrl_f_feed()
|
|
_test_mounted_combat_ranges()
|
|
_finish()
|
|
|
|
func _test_horse_tiers_and_food() -> void:
|
|
print("\n--- Test 1: Horse Tiers & Foods ---")
|
|
_assert(HorseSystem.get_horse_tier_from_item(50051) == HorseSystem.HORSE_TIER_BEGINNER, "50051 is Beginner Horse")
|
|
_assert(HorseSystem.get_horse_tier_from_item(50052) == HorseSystem.HORSE_TIER_COMBAT, "50052 is Combat Horse")
|
|
_assert(HorseSystem.get_horse_tier_from_item(50053) == HorseSystem.HORSE_TIER_MILITARY, "50053 is Military Horse")
|
|
|
|
_assert(HorseSystem.get_required_food(HorseSystem.HORSE_TIER_BEGINNER) == HorseSystem.ITEM_FOOD_HAY, "Beginner eats Hay (50057)")
|
|
_assert(HorseSystem.get_required_food(HorseSystem.HORSE_TIER_COMBAT) == HorseSystem.ITEM_FOOD_CARROT, "Combat horse eats Carrot (50054)")
|
|
_assert(HorseSystem.get_required_food(HorseSystem.HORSE_TIER_MILITARY) == HorseSystem.ITEM_FOOD_GINSENG, "Military horse eats Ginseng (50058)")
|
|
|
|
func _test_summoning() -> void:
|
|
print("\n--- Test 2: Summoning & SP Costs ---")
|
|
# Rejects if insufficient SP
|
|
var r1 := HorseSystem.try_summon_horse(HorseSystem.HORSE_TIER_BEGINNER, 50, 0)
|
|
_assert(r1["success"] == false and r1["err"] == "insufficient_sp", "Rejects summon when SP < 100")
|
|
|
|
# Roll test
|
|
var r_fail := HorseSystem.try_summon_horse(HorseSystem.HORSE_TIER_BEGINNER, 150, 0, 50)
|
|
_assert(r_fail["success"] == false, "Roll 50 fails for 10% base chance")
|
|
|
|
var r_succ := HorseSystem.try_summon_horse(HorseSystem.HORSE_TIER_BEGINNER, 150, 0, 5)
|
|
_assert(r_succ["success"] == true, "Roll 5 succeeds for 10% base chance")
|
|
|
|
# Higher summon skill adds +10% per level
|
|
var chance_lv5 := HorseSystem.calculate_summon_chance(HorseSystem.HORSE_TIER_BEGINNER, 5)
|
|
_assert(chance_lv5 == 60, "Skill Lv 5 adds +50% chance (10 + 50 = 60%)")
|
|
|
|
func _test_ctrl_h_ride() -> void:
|
|
print("\n--- Test 3: Ctrl+H (/user_horse_ride) State Machine ---")
|
|
# 1. Horse not summoned -> reject
|
|
var r1 := HorseSystem.handle_user_horse_ride(false, false, false, false)
|
|
_assert(r1["action"] == "none" and r1["err"] == "horse_not_summoned", "Cannot ride if horse is not summoned")
|
|
|
|
# 2. Horse summoned -> mount
|
|
var r2 := HorseSystem.handle_user_horse_ride(false, true, false, false)
|
|
_assert(r2["action"] == "start_riding" and r2["is_riding"] == true, "Starts riding when horse is summoned")
|
|
|
|
# 3. Already riding -> dismount
|
|
var r3 := HorseSystem.handle_user_horse_ride(true, true, false, false)
|
|
_assert(r3["action"] == "stop_riding" and r3["is_riding"] == false, "Stops riding when already mounted")
|
|
|
|
# 4. Dead/stun -> no action
|
|
var r4 := HorseSystem.handle_user_horse_ride(false, true, false, true)
|
|
_assert(r4["action"] == "none" and r4["err"] == "dead_or_stun", "No action while dead or stunned")
|
|
|
|
func _test_ctrl_b_back() -> void:
|
|
print("\n--- Test 4: Ctrl+B (/user_horse_back) State Machine ---")
|
|
# 1. Currently riding -> must dismount first
|
|
var r1 := HorseSystem.handle_user_horse_back(true, true)
|
|
_assert(r1["action"] == "none" and r1["err"] == "must_dismount_first", "Must dismount before sending horse back")
|
|
|
|
# 2. Horse not summoned -> cannot send back
|
|
var r2 := HorseSystem.handle_user_horse_back(false, false)
|
|
_assert(r2["action"] == "none" and r2["err"] == "horse_not_summoned", "Cannot send back if not summoned")
|
|
|
|
# 3. Summoned & unmounted -> unsummon
|
|
var r3 := HorseSystem.handle_user_horse_back(false, true)
|
|
_assert(r3["action"] == "unsummon" and r3["is_horse_summoned"] == false, "Successfully unsummons horse")
|
|
|
|
func _test_ctrl_g_ride() -> void:
|
|
print("\n--- Test 5: Ctrl+G (/ride) State Machine ---")
|
|
# 1. Dismount if already riding
|
|
var r1 := HorseSystem.handle_ride(true, true, false)
|
|
_assert(r1["action"] == "stop_riding", "/ride dismounts if riding")
|
|
|
|
# 2. Mount summoned horse
|
|
var r2 := HorseSystem.handle_ride(false, true, false)
|
|
_assert(r2["action"] == "start_riding", "/ride mounts summoned horse")
|
|
|
|
# 3. Mount item from inventory if horse not summoned
|
|
var r3 := HorseSystem.handle_ride(false, false, true)
|
|
_assert(r3["action"] == "mount_item", "/ride mounts ride item from inventory")
|
|
|
|
func _test_ctrl_f_feed() -> void:
|
|
print("\n--- Test 6: Ctrl+F (/user_horse_feed) ---")
|
|
var f1 := HorseSystem.handle_user_horse_feed(HorseSystem.HORSE_TIER_COMBAT, HorseSystem.ITEM_FOOD_HAY)
|
|
_assert(f1["success"] == false, "Combat horse rejects Hay")
|
|
|
|
var f2 := HorseSystem.handle_user_horse_feed(HorseSystem.HORSE_TIER_COMBAT, HorseSystem.ITEM_FOOD_CARROT)
|
|
_assert(f2["success"] == true, "Combat horse accepts Carrot")
|
|
|
|
func _test_mounted_combat_ranges() -> void:
|
|
print("\n--- Test 7: Combat & Pickup Ranges (3.0m vs 5.0m) ---")
|
|
_assert(HorseSystem.WALKING_ATTACK_RANGE == 3.0, "Walking attack range is 3.0m (300cm)")
|
|
_assert(HorseSystem.MOUNTED_ATTACK_RANGE == 5.0, "Mounted attack range is 5.0m (500cm)")
|
|
|
|
func _finish() -> void:
|
|
print("\n=== SUMMARY: %d passed, %d failed ===" % [_passed, _failed])
|
|
if _failed == 0:
|
|
print("ALL TESTS PASSED!\n")
|
|
quit(0)
|
|
else:
|
|
printerr("SOME TESTS FAILED!\n")
|
|
quit(1)
|