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

170 lines
5.8 KiB
GDScript

# test_fishing_rod_reel_parity.gd —— Metin2 40250 钓鱼抛竿拉杆与浮漂反应小游戏 1:1 测试套件
extends SceneTree
const FishingRodReelSystem = preload("res://fishing_rod_reel_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_fishing_rod_reel_parity (Direction 3: Fishing Rod & Reel 1:1) ===")
test_bait_attachment_and_cast()
test_premature_pull()
test_bite_and_successful_catch()
test_reaction_timeout()
test_rod_refine_at_fisherman()
test_serialization()
print("\n=======================================================")
print("Fishing Rod & Reel Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)
func test_bait_attachment_and_cast() -> void:
print("\n--- Test 1: Bait Attachment & Line Casting (40250 1:1) ---")
var system = FishingRodReelSystem.new()
system.equip_rod(0)
var inv: Array = [
{"vnum": 27801, "count": 2}, # 蚯蚓 x2 (slot 0)
{"vnum": 50027, "count": 1} # 彩票 (slot 1, 非鱼饵)
]
# 未挂饵尝试抛竿 -> 拦截
var res = system.cast_line()
_assert(res["ok"] == false, "Rejected casting without bait")
_assert(res["reason"] == "NO_BAIT", "Reason is NO_BAIT")
# 非鱼饵道具挂饵 -> 拦截
res = system.attach_bait(inv, 1)
_assert(res["ok"] == false, "Rejected invalid bait item")
_assert(res["reason"] == "NOT_BAIT", "Reason is NOT_BAIT")
# 挂上蚯蚓
res = system.attach_bait(inv, 0)
_assert(res["ok"] == true, "Attached earthworm bait")
_assert(system.has_bait == true, "System has bait")
_assert(inv[0]["count"] == 1, "Consumed 1 earthworm")
# 抛竿
res = system.cast_line(3.0)
_assert(res["ok"] == true, "Cast fishing line")
_assert(system.is_fishing == true, "State is fishing")
_assert(system.has_bait == false, "Bait consumed on cast")
func test_premature_pull() -> void:
print("\n--- Test 2: Premature Rod Pull (40250 1:1) ---")
var system = FishingRodReelSystem.new()
system.equip_rod(0, 0)
var inv: Array = [{"vnum": 27801, "count": 1}]
system.attach_bait(inv, 0)
system.cast_line(5.0) # 5 秒后咬钩
# 仅过 1 秒便起竿 (尚未咬钩)
system.update(1.0)
var pull_res = system.pull_rod(inv)
_assert(pull_res["ok"] == false, "Premature pull failed to catch fish")
_assert(pull_res["reason"] == "PULLED_TOO_EARLY", "Reason is PULLED_TOO_EARLY")
_assert(system.rod_practice_points == 1, "Practiced +1 point even on premature pull")
_assert(system.is_fishing == false, "Fishing session ended")
func test_bite_and_successful_catch() -> void:
print("\n--- Test 3: Fish Bite & Successful Catch (40250 1:1) ---")
var system = FishingRodReelSystem.new()
system.equip_rod(2, 5) # 钓竿+2,已有 5 点熟练度
var bite_signals := []
system.fish_bite.connect(func(window):
bite_signals.append(window)
)
var inv: Array = [
{"vnum": 27801, "count": 1}
]
system.attach_bait(inv, 0)
system.cast_line(2.0)
# 推进 2 秒,触发鱼咬钩
system.update(2.0)
_assert(system.is_bite_active == true, "Fish is biting!")
_assert(bite_signals.size() == 1, "fish_bite signal emitted")
# 提竿并强制捕获草鱼 (CATCH_POOL 索引 2)
var pull_res = system.pull_rod(inv, 2, true)
_assert(pull_res["ok"] == true, "Successfully caught fish")
_assert(pull_res["fish_vnum"] == 27808, "Caught Ot Sazani (27808)")
_assert(pull_res["practice_points"] == 6, "Practice points increased from 5 to 6")
# 检查背包是否新增了草鱼
var has_fish := false
for it in inv:
if it != null and it.get("vnum", 0) == 27808:
has_fish = true
_assert(has_fish == true, "Caught fish inserted into inventory")
func test_reaction_timeout() -> void:
print("\n--- Test 4: Reaction Window Timeout (40250 1:1) ---")
var system = FishingRodReelSystem.new()
system.equip_rod(0, 0)
var inv: Array = [{"vnum": 27801, "count": 1}]
system.attach_bait(inv, 0)
system.cast_line(1.0)
system.update(1.0) # 咬钩
_assert(system.is_bite_active == true, "Bite active")
# 反应窗口为 3 秒,推进 3.5 秒超时
system.update(3.5)
_assert(system.is_fishing == false, "Fishing ended due to timeout")
_assert(system.rod_practice_points == 1, "Practice point +1 on timeout")
func test_rod_refine_at_fisherman() -> void:
print("\n--- Test 5: Rod Refine at Fisherman (40250 1:1) ---")
var system = FishingRodReelSystem.new()
# 设定钓竿+0,需要 10 点满熟练度
system.equip_rod(0, 8)
# 未满点数尝试精炼 -> 拦截
var res = system.refine_rod_at_fisherman()
_assert(res["ok"] == false, "Rejected refine with 8/10 points")
_assert(res["reason"] == "PRACTICE_NOT_MAXED", "Reason is PRACTICE_NOT_MAXED")
# 补满点数 10/10
system.equip_rod(0, 10)
_assert(system.can_refine_rod() == true, "Rod is ready to refine")
# 成功升级为钓鱼竿+1 (chance 100%)
res = system.refine_rod_at_fisherman(50)
_assert(res["ok"] == true and res["success"] == true, "Upgraded to Rod+1")
_assert(system.rod_level == 1, "New rod level is 1")
_assert(system.rod_practice_points == 0, "Practice points reset to 0")
# 模拟失败降级: 设定钓竿+1 满点数 (20 点),强制失败 roll 95 > 90
system.equip_rod(1, 20)
res = system.refine_rod_at_fisherman(95)
_assert(res["ok"] == true and res["success"] == false, "Refine failed and degraded")
_assert(system.rod_level == 0, "Degraded back to Rod+0")
func test_serialization() -> void:
print("\n--- Test 6: Serialization & Restoration ---")
var system = FishingRodReelSystem.new()
system.equip_rod(3, 35)
var data = system.serialize()
var restored = FishingRodReelSystem.new()
restored.deserialize(data)
_assert(restored.rod_level == 3, "Restored rod level 3")
_assert(restored.rod_practice_points == 35, "Restored practice points 35")