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

159 lines
5.7 KiB
GDScript

# test_cube_crafting_parity.gd —— Metin2 40250 官方魔方炼金合成台 1:1 测试套件
extends SceneTree
const CubeCraftingSystem = preload("res://cube_crafting_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_cube_crafting_parity (Direction 3: Cube Crafting System 1:1) ===")
test_baek_go_herb_crafting()
test_huahn_so_dew_crafting()
test_jae_seon_kim_triton_sword()
test_recipe_and_npc_mismatch()
test_history_and_serialization()
print("\n=======================================================")
print("Cube Crafting Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)
func test_baek_go_herb_crafting() -> void:
print("\n--- Test 1: Baek-Go Herb Juice Crafting (40250 1:1) ---")
var system = CubeCraftingSystem.new()
var opened_emitted := []
system.cube_opened.connect(func(npc, name):
opened_emitted.append({"npc": npc, "name": name})
)
# 打开白高魔方界面
_assert(system.open_cube(20018) == true, "Opened cube at Baek-Go (20018)")
_assert(opened_emitted.size() == 1, "cube_opened emitted")
# 放入 1 朵桃花 (50721)
var slots: Array = [{"vnum": 50721, "count": 1}]
var inv: Array = []
var res = system.craft_item(slots, inv, 0)
_assert(res["ok"] == true and res["success"] == true, "Crafted Peach Flower Juice")
_assert(res["reward_vnum"] == 50801, "Reward vnum 50801")
_assert(slots[0] == null, "Cube slot material consumed")
_assert(inv.size() == 1 and inv[0]["vnum"] == 50801, "Inventory received juice")
# 放入 1 朵紫丁香 (50723) -> 批量产出 100 瓶药水 (50803)
slots = [{"vnum": 50723, "count": 1}]
res = system.craft_item(slots, inv, 0)
_assert(res["ok"] == true, "Crafted Lilac Juice batch")
_assert(res["reward_count"] == 100, "Yielded 100 count")
system.close_cube()
_assert(system.is_cube_open == false, "Closed cube")
func test_huahn_so_dew_crafting() -> void:
print("\n--- Test 2: Huahn-So Dew Crafting & Gold Fee (40250 1:1) ---")
var system = CubeCraftingSystem.new()
system.open_cube(20022) # 幻素
# 材料: 桃花汁 x10 (50801) + 空瓶 x10 (70031)
var slots: Array = [
{"vnum": 50801, "count": 10},
{"vnum": 70031, "count": 10}
]
var inv: Array = []
# 金币不足拦截 (需要 10,000 金币,玩家只有 5,000)
var res = system.craft_item(slots, inv, 5000)
_assert(res["ok"] == false, "Rejected due to insufficient gold")
_assert(res["reason"] == "NOT_ENOUGH_GOLD", "Reason is NOT_ENOUGH_GOLD")
_assert(slots[0]["count"] == 10, "Materials not consumed on gold error")
# 金币充足,成功合成
res = system.craft_item(slots, inv, 20000)
_assert(res["ok"] == true and res["success"] == true, "Crafted Peach Flower Dew (50821)")
_assert(res["remaining_gold"] == 10000, "Deducted 10,000 gold fee")
_assert(res["reward_vnum"] == 50821, "Received Dew item 50821")
func test_jae_seon_kim_triton_sword() -> void:
print("\n--- Test 3: Jae-Seon Kim Triton Sword Crafting (40250 1:1) ---")
var system = CubeCraftingSystem.new()
system.open_cube(20383) # 金在善
# 1. 战神之剑+5 (145) + 30500 x10 + 30505 x10 (成功率 5%)
var slots: Array = [
{"vnum": 145, "count": 1},
{"vnum": 30500, "count": 10},
{"vnum": 30505, "count": 10}
]
var inv: Array = []
# 模拟失败 (roll = 50 > 5)
var res = system.craft_item(slots, inv, 150000, 50)
_assert(res["ok"] == true and res["success"] == false, "Low tier Triton craft failed")
_assert(res["remaining_gold"] == 50000, "Fee deducted even on failure")
_assert(inv.is_empty(), "No reward given on failure")
# 2. 战神之剑+9 (149) + 30500 x10 + 30505 x10 (保底 100% 成功)
slots = [
{"vnum": 149, "count": 1},
{"vnum": 30500, "count": 10},
{"vnum": 30505, "count": 10}
]
res = system.craft_item(slots, inv, 100000, 10)
_assert(res["ok"] == true and res["success"] == true, "Master tier +9 Triton craft succeeded 100%")
_assert(res["reward_vnum"] == 460, "Reward is Triton Sword+0 (460)")
_assert(inv.size() == 1 and inv[0]["vnum"] == 460, "Triton Sword added to inventory")
func test_recipe_and_npc_mismatch() -> void:
print("\n--- Test 4: Recipe & NPC Mismatch Handling ---")
var system = CubeCraftingSystem.new()
system.open_cube(20018) # 白高
# 放入海神之剑的材料到白高面前 -> 配方不匹配
var slots: Array = [
{"vnum": 149, "count": 1},
{"vnum": 30500, "count": 10},
{"vnum": 30505, "count": 10}
]
var inv: Array = []
var res = system.craft_item(slots, inv, 100000)
_assert(res["ok"] == false, "Baek-Go cannot craft Triton Sword")
_assert(res["reason"] == "NO_MATCHING_RECIPE", "Reason is NO_MATCHING_RECIPE")
# 窗口关闭时调用 -> 拦截
system.close_cube()
res = system.craft_item(slots, inv, 100000)
_assert(res["ok"] == false, "Rejected when cube closed")
_assert(res["reason"] == "CUBE_NOT_OPEN", "Reason is CUBE_NOT_OPEN")
func test_history_and_serialization() -> void:
print("\n--- Test 5: Crafting History & Serialization ---")
var system = CubeCraftingSystem.new()
system.open_cube(20018)
var slots: Array = [{"vnum": 50721, "count": 1}]
var inv: Array = []
system.craft_item(slots, inv, 0)
var hist = system.get_history()
_assert(hist.size() == 1, "Recorded 1 crafting history")
_assert(hist[0]["recipe_id"] == "herb_peach_flower", "Recorded recipe herb_peach_flower")
var data = system.serialize()
var restored = CubeCraftingSystem.new()
restored.deserialize(data)
_assert(restored.get_history().size() == 1, "Restored 1 history record")
system.clear_history()
_assert(system.get_history().is_empty(), "Cleared history")