- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
170 lines
5.8 KiB
GDScript
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")
|