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

163 lines
6.4 KiB
GDScript

# test_biologist_chaegirab_research_parity.gd —— 40250 生物学者车齿木研究链 1:1 回归测试套件
extends SceneTree
const BiologistChaegirabResearchSystem = preload("res://biologist_chaegirab_research_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_biologist_chaegirab_research_parity (Direction 1: Biologist Research 1:1) ===")
_test_stage_progression_and_sample_validation()
_test_sample_completion_and_soul_stone_capstone()
_test_subsequent_stages_and_stat_stacking()
_test_serialization()
_finish()
func _test_stage_progression_and_sample_validation() -> void:
print("\n--- Test 1: Stage Requirements & Sample Item Validations ---")
var sys := BiologistChaegirabResearchSystem.new()
var inv: Array = [
{"vnum": 10, "name": "木剑", "count": 1},
{"vnum": 30006, "name": "兽人牙齿", "count": 5}
]
# 1. Level too low (< 30)
var res_low = sys.submit_sample(inv, 1, 25)
_assert(res_low["ok"] == false, "Rejected: level 25 < 30")
_assert(res_low["reason"] == "PLAYER_LEVEL_TOO_LOW", "Reason is PLAYER_LEVEL_TOO_LOW")
# 2. Wrong sample item
var res_wrong = sys.submit_sample(inv, 0, 35)
_assert(res_wrong["ok"] == false, "Rejected: wrong item (Wood Sword)")
_assert(res_wrong["reason"] == "WRONG_SAMPLE_ITEM", "Reason is WRONG_SAMPLE_ITEM")
# 3. Valid sample submission (force success)
var sub_sig := {"fired": false, "cur": 0}
sys.sample_submitted.connect(func(_n: String, _s: bool, c: int, _t: int):
sub_sig["fired"] = true
sub_sig["cur"] = c
)
var res_succ = sys.submit_sample(inv, 1, 35, true)
_assert(res_succ["ok"] == true, "Orc Tooth submitted successfully")
_assert(res_succ["current"] == 1, "Progress is 1/10")
_assert(inv[1]["count"] == 4, "Orc tooth count decremented 5 -> 4")
_assert(sub_sig["fired"] == true, "sample_submitted signal emitted")
func _test_sample_completion_and_soul_stone_capstone() -> void:
print("\n--- Test 2: Sample Completion & Capstone Soul Stone Delivery ---")
var sys := BiologistChaegirabResearchSystem.new()
var player_stats := {"move_speed": 100}
var inv: Array = [
{"vnum": 30006, "name": "兽人牙齿", "count": 10},
{"vnum": 30220, "name": "兽人灵魂石", "count": 1}
]
# Submit remaining 9 teeth to hit 10/10
for i in range(10):
sys.submit_sample(inv, 0, 35, true)
_assert(sys.current_samples == 10, "Collected all 10 Orc Teeth")
_assert(sys.waiting_for_soul_stone == true, "Now waiting for soul stone")
# Cannot submit more teeth
inv[0] = {"vnum": 30006, "name": "兽人牙齿", "count": 1}
var res_excess = sys.submit_sample(inv, 0, 35, true)
_assert(res_excess["ok"] == false, "Excess sample submission blocked")
_assert(res_excess["reason"] == "WAITING_FOR_SOUL_STONE", "Reason is WAITING_FOR_SOUL_STONE")
var cap_sig := {"fired": false, "stone": ""}
sys.capstone_submitted.connect(func(sn: String, _st: String):
cap_sig["fired"] = true
cap_sig["stone"] = sn
)
# Submit wrong soul stone
inv[0] = {"vnum": 30221, "name": "诅咒灵魂石", "count": 1}
var res_wrong_stone = sys.submit_soul_stone(inv, 0, player_stats)
_assert(res_wrong_stone["ok"] == false, "Wrong soul stone rejected")
_assert(res_wrong_stone["reason"] == "WRONG_SOUL_STONE", "Reason is WRONG_SOUL_STONE")
# Submit valid Orc Soul Stone (slot 1)
var res_stone_succ = sys.submit_soul_stone(inv, 1, player_stats)
_assert(res_stone_succ["ok"] == true, "Orc Soul Stone submitted")
_assert(player_stats["move_speed"] == 110, "Permanent move speed +10 applied (100 -> 110)")
_assert(sys.permanent_bonuses["move_speed"] == 10, "Permanent bonus recorded")
_assert(sys.current_stage_index == 1, "Advanced to Stage 2 (Curse Book)")
_assert(sys.current_samples == 0, "Current samples reset to 0")
_assert(sys.waiting_for_soul_stone == false, "No longer waiting for soul stone")
_assert(cap_sig["fired"] == true, "capstone_submitted signal emitted")
func _test_subsequent_stages_and_stat_stacking() -> void:
print("\n--- Test 3: Subsequent Stages (Curse Book & Demon Keepsake) Stacking ---")
var sys := BiologistChaegirabResearchSystem.new()
var player_stats := {"move_speed": 100, "cast_speed_pct": 0, "defense": 200}
# Stage 1: Complete Orc Tooth (+10 move speed)
var inv1: Array = [
{"vnum": 30006, "name": "兽人牙齿", "count": 10},
{"vnum": 30220, "name": "兽人灵魂石", "count": 1}
]
for i in range(10):
sys.submit_sample(inv1, 0, 30, true)
sys.submit_soul_stone(inv1, 1, player_stats)
# Stage 2: Complete Curse Book (+5% cast speed)
var inv2: Array = [
{"vnum": 30047, "name": "诅咒之书", "count": 15},
{"vnum": 30221, "name": "诅咒灵魂石", "count": 1}
]
for i in range(15):
sys.submit_sample(inv2, 0, 40, true)
sys.submit_soul_stone(inv2, 1, player_stats)
# Stage 3: Complete Demon Keepsake (+60 def)
var inv3: Array = [
{"vnum": 30015, "name": "恶魔纪念物", "count": 15},
{"vnum": 30222, "name": "恶魔灵魂石", "count": 1}
]
for i in range(15):
sys.submit_sample(inv3, 0, 50, true)
sys.submit_soul_stone(inv3, 1, player_stats)
_assert(player_stats["move_speed"] == 110, "Stage 1 bonus active (+10)")
_assert(player_stats["cast_speed_pct"] == 5, "Stage 2 bonus active (+5%)")
_assert(player_stats["defense"] == 260, "Stage 3 bonus active (+60)")
_assert(sys.completed_stages.size() == 3, "3 stages completed in total")
func _test_serialization() -> void:
print("\n--- Test 4: Serialization & Restoration ---")
var sys1 := BiologistChaegirabResearchSystem.new()
sys1.current_stage_index = 4 # Stage 5: Zelkova Branch
sys1.current_samples = 18
sys1.waiting_for_soul_stone = false
sys1.completed_stages = [1, 2, 3, 4]
sys1.permanent_bonuses = {"move_speed": 10, "cast_speed_pct": 5, "defense": 60, "attack_power": 50}
var data = sys1.serialize()
var sys2 := BiologistChaegirabResearchSystem.new()
sys2.deserialize(data)
_assert(sys2.current_stage_index == 4, "Restored stage index 4")
_assert(sys2.current_samples == 18, "Restored samples 18")
_assert(sys2.completed_stages.size() == 4, "Restored 4 completed stages")
_assert(sys2.permanent_bonuses["attack_power"] == 50, "Restored attack bonus 50")
func _finish() -> void:
print("\n=======================================================")
print("Biologist Research Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)