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

208 lines
7.7 KiB
GDScript
Raw Blame History

This file contains ambiguous Unicode characters
This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.
# test_fish_clam_roast_parity.gd —— Metin2 40250 活鱼宰杀、珍珠蚌开珠与篝火炙烤盛宴 1:1 测试套件
extends SceneTree
const FishClamRoastSystem = preload("res://fish_clam_roast_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_fish_clam_roast_parity (Direction 1: Fish Gutting & Clam Pearls 1:1) ===")
test_live_fish_gutting()
test_clam_pearl_opening()
test_campfire_grilling()
test_cooked_fish_buffs()
test_serialization()
print("\n=======================================================")
print("Fish & Clam Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)
func test_live_fish_gutting() -> void:
print("\n--- Test 1: Live Fish Gutting (40250 1:1) ---")
var system = FishClamRoastSystem.new()
var inv: Array = [
{"vnum": 27806, "count": 1}, # 活鲤鱼 (Sazan)
{"vnum": 27808, "count": 1}, # 活草鱼 (Ot Sazanı)
{"vnum": 27803, "count": 1}, # 活鲈鱼
{"vnum": 27803, "count": 1}, # 活鲈鱼
{"vnum": 27803, "count": 1} # 活鲈鱼
]
# 1. 产生死鱼 (roll >= 4001)
var res = system.gut_live_fish(inv, 0, 5000)
_assert(res["ok"] == true, "Gut fish succeeded")
_assert(res["result"] == "dead_fish", "Result is dead_fish")
_assert(res["vnum"] == 27836, "Spawned dead Sazan (27836)")
# 2. 产生鱼骨 (roll 2001~4000)
res = system.gut_live_fish(inv, 1, 3000)
_assert(res["ok"] == true, "Gut fish succeeded for bone")
_assert(res["result"] == "fish_bone", "Result is fish_bone")
_assert(res["vnum"] == 27995, "Spawned Fish Bone (27995)")
# 3. 产生珍珠蚌 (roll <= 1000)
res = system.gut_live_fish(inv, 2, 500)
_assert(res["ok"] == true, "Found clam inside fish!")
_assert(res["result"] == "clam", "Result is clam")
_assert(res["vnum"] == 27987, "Spawned Clam (27987)")
# 4. 产生蚯蚓 (roll 1001~1500)
res = system.gut_live_fish(inv, 3, 1200)
_assert(res["ok"] == true, "Found earthworm")
_assert(res["result"] == "earthworm", "Result is earthworm")
_assert(res["vnum"] == 27801, "Spawned Earthworm (27801)")
# 5. 滑入水中溜走 (roll > 1500)
res = system.gut_live_fish(inv, 4, 1800)
_assert(res["ok"] == true, "Escaped")
_assert(res["result"] == "escape", "Fish escaped back to water")
func test_clam_pearl_opening() -> void:
print("\n--- Test 2: Clam Opening & 3 Pearls (40250 1:1) ---")
var system = FishClamRoastSystem.new()
var inv: Array = [
{"vnum": 27987, "count": 1}, # 蚌 1: 碎石
{"vnum": 27987, "count": 1}, # 蚌 2: 空
{"vnum": 27987, "count": 1}, # 蚌 3: 白珍珠
{"vnum": 27987, "count": 1}, # 蚌 4: 蓝珍珠
{"vnum": 27987, "count": 1} # 蚌 5: 血红珍珠
]
# 1. 碎石 (branch roll <= 50)
var res = system.open_clam(inv, 0, 30)
_assert(res["ok"] == true, "Opened clam for stone")
_assert(res["result"] == "stone", "Result is stone")
_assert(res["vnum"] == 27990, "Vnum is 27990 (Stone Piece)")
# 2. 空蚌 (branch roll > 50, pearl roll <= 80)
res = system.open_clam(inv, 1, 70, 50)
_assert(res["ok"] == true, "Opened empty clam")
_assert(res["result"] == "empty", "Result is empty")
# 3. 白珍珠 (pearl roll 81~90)
res = system.open_clam(inv, 2, 70, 85)
_assert(res["ok"] == true, "Opened White Pearl")
_assert(res["result"] == "white_pearl", "Result is white_pearl")
_assert(res["vnum"] == 27992, "Vnum is 27992 (White Pearl)")
# 4. 蓝珍珠 (pearl roll 91~97)
res = system.open_clam(inv, 3, 70, 95)
_assert(res["ok"] == true, "Opened Blue Pearl")
_assert(res["result"] == "blue_pearl", "Result is blue_pearl")
_assert(res["vnum"] == 27993, "Vnum is 27993 (Blue Pearl)")
# 5. 血红珍珠 (pearl roll >= 98)
res = system.open_clam(inv, 4, 70, 99)
_assert(res["ok"] == true, "Opened Blood Pearl")
_assert(res["result"] == "blood_pearl", "Result is blood_pearl")
_assert(res["vnum"] == 27994, "Vnum is 27994 (Blood Pearl)")
func test_campfire_grilling() -> void:
print("\n--- Test 3: Campfire Grilling (40250 1:1) ---")
var system = FishClamRoastSystem.new()
var inv: Array = [
{"vnum": 27836, "count": 3}, # 3 条死鲤鱼
{"vnum": 27838, "count": 1} # 1 条死草鱼
]
# 未靠近营火拒绝烤制
var res = system.grill_fish(inv, 0, false)
_assert(res["ok"] == false, "Rejected grilling without campfire")
_assert(res["reason"] == "NO_CAMPFIRE", "Reason is NO_CAMPFIRE")
# 点燃营火
system.place_campfire(600.0)
_assert(system.is_near_active_campfire == true, "Campfire is active")
# 烤制 3 条死鲤鱼 (27836 -> 27866 烤鲤鱼)
res = system.grill_fish(inv, 0)
_assert(res["ok"] == true, "Grilling Sazan succeeded")
_assert(res["cooked_vnum"] == 27866, "Cooked Sazan vnum is 27866")
_assert(res["count"] == 3, "Preserved count 3")
_assert(inv[0] != null and inv[0]["vnum"] == 27866 and inv[0]["count"] == 3, "Slot holds 3 cooked fish")
func test_cooked_fish_buffs() -> void:
print("\n--- Test 4: Cooked Fish Consumption & Buffs (40250 1:1) ---")
var system = FishClamRoastSystem.new()
var inv: Array = [
{"vnum": 27866, "count": 1}, # 烤鲤鱼: 移速 +20 (600s)
{"vnum": 27868, "count": 1}, # 烤草鱼: 攻速 +20 (600s)
{"vnum": 27877, "count": 1}, # 烤泥鳅: 隐身 (300s)
{"vnum": 27874, "count": 1}, # 烤海鲈鱼: 治愈中毒
{"vnum": 27863, "count": 1} # 烤鲈鱼: 恢复 180 HP
]
# 1. 食用烤鲤鱼
var res = system.eat_cooked_fish(inv, 0)
_assert(res["ok"] == true, "Ate cooked Sazan")
_assert(res["effect"] == "move_speed", "Effect is move_speed")
_assert(res["value"] == 20, "Value is 20")
_assert(res["duration"] == 600.0, "Duration is 600s")
# 2. 食用烤草鱼
res = system.eat_cooked_fish(inv, 1)
_assert(res["ok"] == true, "Ate cooked Ot Sazani")
_assert(res["effect"] == "attack_speed", "Effect is attack_speed")
_assert(res["value"] == 20, "Value is 20")
# 3. 食用烤泥鳅
res = system.eat_cooked_fish(inv, 2)
_assert(res["ok"] == true, "Ate cooked Copra")
_assert(res["effect"] == "invisibility", "Effect is invisibility")
_assert(res["duration"] == 300.0, "Duration is 300s")
# 4. 食用解毒鱼与瞬间恢复鱼
res = system.eat_cooked_fish(inv, 3)
_assert(res["effect"] == "cure_poison", "Effect is cure_poison")
res = system.eat_cooked_fish(inv, 4)
_assert(res["effect"] == "instant_hp" and res["value"] == 180, "Instant HP restored 180")
# 检查增益状态列表
var buffs = system.get_active_buffs()
_assert(buffs.has("move_speed"), "Active buff has move_speed")
_assert(buffs.has("attack_speed"), "Active buff has attack_speed")
_assert(buffs.has("invisibility"), "Active buff has invisibility")
# 时间推移 100 秒
system.update(100.0)
_assert(buffs["invisibility"]["time_remaining"] <= 200.0, "Invisibility ticked down")
# 时间推移 250 秒 (隐身 300s 到期清除)
system.update(250.0)
_assert(not system.get_active_buffs().has("invisibility"), "Invisibility expired and removed")
_assert(system.get_active_buffs().has("move_speed"), "Move speed still active")
func test_serialization() -> void:
print("\n--- Test 5: Buff Serialization & Restoration ---")
var system = FishClamRoastSystem.new()
system.place_campfire(500.0)
var inv: Array = [{"vnum": 27866, "count": 1}]
system.eat_cooked_fish(inv, 0)
var data = system.serialize()
_assert(data.has("active_buffs") and data["active_buffs"].has("move_speed"), "Serialized move_speed buff")
var restored = FishClamRoastSystem.new()
restored.deserialize(data)
_assert(restored.is_near_active_campfire == true, "Restored active campfire")
_assert(restored.get_active_buffs().has("move_speed"), "Restored move_speed buff")