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

137 lines
5.4 KiB
GDScript

# test_costume_attr_transfer_parity.gd —— Metin2 40250 时装属性转移与增益附魔 1:1 测试套件
extends SceneTree
const CostumeAttrTransferSystem = preload("res://costume_attr_transfer_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_costume_attr_transfer_parity (Direction 3: Costume Attr Transfer 1:1) ===")
test_costume_transform_70063()
test_costume_enchant_70064()
test_costume_attribute_transfer()
test_rejections_and_safety()
print("\n=======================================================")
print("Costume Attr Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)
func test_costume_transform_70063() -> void:
print("\n--- Test 1: Costume Transform 70063 (40250 1:1) ---")
var system = CostumeAttrTransferSystem.new()
var transformed_signals := []
system.costume_transformed.connect(func(vnum, attrs):
transformed_signals.append({"vnum": vnum, "attrs": attrs})
)
var inv: Array = [
{"vnum": 70063, "count": 2}, # 重铸卷轴 x2 (slot 0)
{"vnum": 41001, "costume_subtype": "body", "attrs": []} # 基础时装铠甲 (slot 1)
]
# 重铸时装,强制赋予 3 条属性
var custom_attrs = [
{"type": "max_hp", "name": "生命上限", "value": 2000},
{"type": "crit_pct", "name": "暴击几率", "value": 10},
{"type": "att_speed", "name": "攻击速度", "value": 8}
]
var res = system.transform_costume(inv, 0, 1, 3, custom_attrs)
_assert(res["ok"] == true, "Transformed costume successfully")
_assert(res["attrs_count"] == 3, "Acquired 3 attributes")
_assert(inv[1]["attrs"].size() == 3, "Costume holds 3 attributes in inventory")
_assert(inv[1]["attrs"][0]["value"] == 2000, "First attribute is 2000 HP")
_assert(inv[0]["count"] == 1, "Scroll count decremented from 2 to 1")
_assert(transformed_signals.size() == 1, "costume_transformed signal fired")
func test_costume_enchant_70064() -> void:
print("\n--- Test 2: Costume Enchant 70064 (40250 1:1) ---")
var system = CostumeAttrTransferSystem.new()
var inv: Array = [
{"vnum": 70064, "count": 1}, # 附魔石 (slot 0)
{"vnum": 41001, "costume_subtype": "body", "attrs": [
{"type": "max_hp", "value": 500},
{"type": "max_sp", "value": 200}
]}, # 带有 2 条词条的时装 (slot 1)
{"vnum": 41002, "costume_subtype": "body", "attrs": []} # 0 词条时装 (slot 2)
]
# 1. 尝试对 0 词条时装附魔 -> 拦截
var res = system.enchant_costume(inv, 0, 2)
_assert(res["ok"] == false, "Rejected enchanting costume with 0 attributes")
_assert(res["reason"] == "NO_ATTRS_TO_ENCHANT", "Reason is NO_ATTRS_TO_ENCHANT")
# 2. 对 2 词条时装附魔 -> 成功保留 2 词条结构并刷新
var new_custom = [
{"type": "crit_pct", "name": "暴击几率", "value": 10},
{"type": "penetrate_pct", "name": "穿刺伤害几率", "value": 10}
]
res = system.enchant_costume(inv, 0, 1, new_custom)
_assert(res["ok"] == true, "Enchanted costume successfully")
_assert(res["attrs_count"] == 2, "Preserved 2 attributes count")
_assert(inv[1]["attrs"][0]["type"] == "crit_pct", "New first attribute is crit_pct")
_assert(inv[0] == null, "Consumed enchant scroll to null")
func test_costume_attribute_transfer() -> void:
print("\n--- Test 3: Costume Attribute Transfer 72325 (40250 1:1) ---")
var system = CostumeAttrTransferSystem.new()
var inv: Array = [
{"vnum": 72325, "count": 1}, # 转移符 (slot 0)
{"vnum": 41001, "costume_subtype": "body", "attrs": [
{"type": "max_hp", "value": 2000},
{"type": "crit_pct", "value": 10}
]}, # 旧时装铠甲 (slot 1)
{"vnum": 41050, "costume_subtype": "body", "attrs": []}, # 崭新神圣铠甲 (slot 2)
{"vnum": 45001, "costume_subtype": "hair", "attrs": []} # 时装发型 (slot 3, 异类)
]
# 1. 铠甲转移给发型 -> 异类拦截
var res = system.transfer_costume_attributes(inv, 0, 1, 3)
_assert(res["ok"] == false, "Rejected mismatch transfer (body to hair)")
_assert(res["reason"] == "TYPE_MISMATCH", "Reason is TYPE_MISMATCH")
# 2. 铠甲转移给铠甲 -> 成功
res = system.transfer_costume_attributes(inv, 0, 1, 2)
_assert(res["ok"] == true, "Transfer body to body succeeded")
_assert(inv[2]["attrs"].size() == 2, "Destination costume received 2 attributes")
_assert(inv[2]["attrs"][0]["value"] == 2000, "Transferred 2000 HP")
_assert(inv[1]["attrs"].is_empty(), "Source costume attributes cleared")
_assert(inv[0] == null, "Consumed transfer scroll")
func test_rejections_and_safety() -> void:
print("\n--- Test 4: Safety Rejections (Equipped & Non-costume) ---")
var system = CostumeAttrTransferSystem.new()
var inv: Array = [
{"vnum": 70063, "count": 1},
{"vnum": 149, "attrs": []}, # 普通武器战神之剑+9 (slot 1, 非时装)
{"vnum": 41001, "costume_subtype": "body", "is_equipped": true, "attrs": []} # 穿戴中时装 (slot 2)
]
# 1. 非时装物品 -> 拦截
var res = system.transform_costume(inv, 0, 1)
_assert(res["ok"] == false, "Rejected non-costume item")
_assert(res["reason"] == "NOT_COSTUME", "Reason is NOT_COSTUME")
# 2. 穿戴中时装 -> 拦截
res = system.transform_costume(inv, 0, 2)
_assert(res["ok"] == false, "Rejected equipped costume")
_assert(res["reason"] == "EQUIPPED_ITEM", "Reason is EQUIPPED_ITEM")