- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
114 lines
5.1 KiB
GDScript
114 lines
5.1 KiB
GDScript
# test_belt_system_parity.gd —— 40250 腰带系统与专属药水背包 1:1 纯净回归测试套件
|
|
extends SceneTree
|
|
|
|
const BeltSystem = preload("res://belt_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_belt_system_parity (Direction 2: Belt System 1:1) ===")
|
|
_test_slot_unlock_by_refine_level()
|
|
_test_potion_only_restriction()
|
|
_test_capacity_boundary()
|
|
_test_unequip_and_inventory_transfer()
|
|
_finish()
|
|
|
|
func _test_slot_unlock_by_refine_level() -> void:
|
|
print("\n--- Test 1: Refine-Level Based Slot Unlocking (4 ~ 16 Slots) ---")
|
|
_assert(BeltSystem.calculate_unlocked_slots(0) == 4, "+0 belt unlocks 4 slots")
|
|
_assert(BeltSystem.calculate_unlocked_slots(3) == 8, "+3 belt unlocks 8 slots")
|
|
_assert(BeltSystem.calculate_unlocked_slots(7) == 12, "+7 belt unlocks 12 slots")
|
|
_assert(BeltSystem.calculate_unlocked_slots(9) == 16, "+9 belt unlocks 16 slots (4x4)")
|
|
|
|
var bs := BeltSystem.new()
|
|
_assert(bs.get_unlocked_slot_count() == 0, "0 slots when no belt equipped")
|
|
|
|
bs.equip_belt({"vnum": 18000, "name": "亚麻腰带+0", "refine_level": 0})
|
|
_assert(bs.get_unlocked_slot_count() == 4, "+0 Linen Belt unlocks 4 slots")
|
|
|
|
bs.equip_belt({"vnum": 18039, "name": "王者腰带+9", "refine_level": 9})
|
|
_assert(bs.get_unlocked_slot_count() == 16, "+9 King Belt unlocks 16 slots")
|
|
|
|
func _test_potion_only_restriction() -> void:
|
|
print("\n--- Test 2: Potion-Only Storage Restriction ---")
|
|
var bs := BeltSystem.new()
|
|
bs.equip_belt({"vnum": 18000, "name": "皮腰带+3", "refine_level": 3}) # 8 slots
|
|
|
|
# 1. Weapon rejected
|
|
var r_sword = bs.store_item_in_belt(0, {"vnum": 10, "name": "木剑+0", "item_type": "weapon"})
|
|
_assert(r_sword["ok"] == false and r_sword["reason"] == "NOT_A_POTION", "Weapon rejected from belt")
|
|
|
|
# 2. Armor rejected
|
|
var r_armor = bs.store_item_in_belt(0, {"vnum": 11299, "name": "黑钢甲+9", "item_type": "armor"})
|
|
_assert(r_armor["ok"] == false and r_armor["reason"] == "NOT_A_POTION", "Armor rejected from belt")
|
|
|
|
# 3. Red Potion accepted
|
|
var r_pot = bs.store_item_in_belt(0, {"vnum": 27003, "name": "红药水(大)", "item_type": "potion", "count": 200})
|
|
_assert(r_pot["ok"] == true, "Red Potion accepted in belt")
|
|
_assert(bs.belt_inventory[0]["vnum"] == 27003, "Red potion stored at belt slot 0")
|
|
|
|
# 4. Sacred Dew accepted
|
|
var r_dew = bs.store_item_in_belt(1, {"vnum": 50821, "name": "绿露水", "item_type": "dew", "count": 10})
|
|
_assert(r_dew["ok"] == true, "Dew accepted in belt")
|
|
|
|
# 5. Sun Elixir accepted
|
|
var r_elixir = bs.store_item_in_belt(2, {"vnum": 72723, "name": "太阳之灵药", "item_type": "elixir", "count": 1})
|
|
_assert(r_elixir["ok"] == true, "Elixir accepted in belt")
|
|
|
|
func _test_capacity_boundary() -> void:
|
|
print("\n--- Test 3: Capacity Boundaries on Locked Slots ---")
|
|
var bs := BeltSystem.new()
|
|
bs.equip_belt({"vnum": 18000, "name": "亚麻腰带+0", "refine_level": 0}) # only slots 0..3 open
|
|
|
|
var potion := {"vnum": 27003, "name": "红药水", "item_type": "potion", "count": 10}
|
|
|
|
# Slot 3 (4th slot) is allowed
|
|
var r_ok = bs.store_item_in_belt(3, potion)
|
|
_assert(r_ok["ok"] == true, "Slot 3 allowed on +0 belt")
|
|
|
|
# Slot 4 (5th slot) is locked
|
|
var r_locked = bs.store_item_in_belt(4, potion)
|
|
_assert(r_locked["ok"] == false and r_locked["reason"] == "SLOT_LOCKED_OR_INVALID", "Slot 4 locked on +0 belt")
|
|
|
|
func _test_unequip_and_inventory_transfer() -> void:
|
|
print("\n--- Test 4: Belt Unequip & Potion Eviction to Main Inventory ---")
|
|
var bs := BeltSystem.new()
|
|
bs.equip_belt({"vnum": 18000, "name": "腰带+0", "refine_level": 0})
|
|
|
|
# Store 2 potions in belt
|
|
bs.store_item_in_belt(0, {"vnum": 27003, "name": "红药水", "item_type": "potion", "count": 50})
|
|
bs.store_item_in_belt(1, {"vnum": 27006, "name": "蓝药水", "item_type": "potion", "count": 50})
|
|
|
|
# 1. Main inventory completely full -> unequip blocked
|
|
var full_inv: Array = [{"vnum": 1}, {"vnum": 2}] # no nulls
|
|
var r_block = bs.unequip_belt(full_inv)
|
|
_assert(r_block["ok"] == false and r_block["reason"] == "BELT_NOT_EMPTY", "Unequip blocked when main inventory full")
|
|
_assert(not bs.equipped_belt.is_empty(), "Belt remains equipped")
|
|
|
|
# 2. Main inventory has space -> potions transferred and belt unequipped
|
|
var open_inv: Array = [null, null, null]
|
|
var r_ok = bs.unequip_belt(open_inv)
|
|
_assert(r_ok["ok"] == true, "Unequip succeeds with open inventory")
|
|
_assert(bs.equipped_belt.is_empty(), "Belt unequipped")
|
|
_assert(open_inv[0] != null and open_inv[0]["vnum"] == 27003, "Red potion transferred to main inventory slot 0")
|
|
_assert(open_inv[1] != null and open_inv[1]["vnum"] == 27006, "Blue potion transferred to main inventory slot 1")
|
|
_assert(bs.belt_inventory[0] == null and bs.belt_inventory[1] == null, "Belt inventory cleared")
|
|
|
|
func _finish() -> void:
|
|
print("\n==========================================")
|
|
print("Belt System Parity Test Results: %d Passed, %d Failed" % [_passed, _failed])
|
|
print("==========================================")
|
|
if _failed > 0:
|
|
quit(1)
|
|
else:
|
|
quit(0)
|