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

158 lines
5.6 KiB
GDScript

# test_cursed_monolith_spire_parity.gd —— 40250 封印魔塔限时防守挑战 1:1 回归测试套件
extends SceneTree
const CursedMonolithSpireSystem = preload("res://cursed_monolith_spire_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_cursed_monolith_spire_parity (Direction 2: Cursed Monolith Spire 1:1) ===")
_test_start_trial_and_waves()
_test_full_victory_and_chest()
_test_timeout_defeat()
_finish()
func _test_start_trial_and_waves() -> void:
print("\n--- Test 1: Spire Emergence & Multi-Wave Progression ---")
var spire := CursedMonolithSpireSystem.new()
var emerge_sig := {"fired": false, "time": 0.0}
spire.spire_emerged.connect(func(_p: Vector3, t: float):
emerge_sig["fired"] = true
emerge_sig["time"] = t
)
var wave_sig := {"fired": false, "wave": 0, "count": 0}
spire.wave_spawned.connect(func(w: int, _n: String, c: int):
wave_sig["fired"] = true
wave_sig["wave"] = w
wave_sig["count"] = c
)
var res = spire.start_trial(Vector3(100, 0, 100), 50)
_assert(res["ok"] == true, "Trial started")
_assert(spire.is_active == true, "Spire trial is active")
_assert(emerge_sig["fired"] == true and emerge_sig["time"] == 180.0, "spire_emerged signal emitted with 180s")
_assert(wave_sig["fired"] == true and wave_sig["wave"] == 1 and wave_sig["count"] == 6, "Wave 1 spawned 6 mobs")
_assert(spire.active_mobs.size() == 6, "6 active mobs in wave 1")
# Starting another trial while active must fail
var fail_dup = spire.start_trial(Vector3.ZERO)
_assert(fail_dup["ok"] == false and fail_dup["reason"] == "TRIAL_ALREADY_ACTIVE", "Cannot start duplicate trial")
# Clear wave 1 (kill all 6 mobs)
var mob_ids = spire.active_mobs.keys().duplicate()
for i in range(mob_ids.size()):
spire.kill_mob(mob_ids[i])
# Wave 2 should have automatically triggered (2 mobs)
_assert(spire.current_wave == 2, "Transitioned to Wave 2")
_assert(spire.active_mobs.size() == 2, "Wave 2 has 2 mobs")
func _test_full_victory_and_chest() -> void:
print("\n--- Test 2: Full Victory & Ancient Chest Looting ---")
var spire := CursedMonolithSpireSystem.new()
spire.start_trial(Vector3.ZERO, 60)
var victory_sig := {"fired": false, "success": false, "time": 0.0}
spire.trial_completed.connect(func(s: bool, t: float):
victory_sig["fired"] = true
victory_sig["success"] = s
victory_sig["time"] = t
)
var chest_sig := {"fired": false, "vnum": 0}
spire.reward_chest_spawned.connect(func(v: int, _p: Vector3):
chest_sig["fired"] = true
chest_sig["vnum"] = v
)
# Clear wave 1
var w1_ids = spire.active_mobs.keys().duplicate()
for mid in w1_ids:
spire.kill_mob(mid)
# Clear wave 2
var w2_ids = spire.active_mobs.keys().duplicate()
for mid in w2_ids:
spire.kill_mob(mid)
_assert(spire.current_wave == 3, "Reached Wave 3 (Ancient Guardian)")
_assert(spire.active_mobs.size() == 1, "Wave 3 has 1 boss")
# Advance 20 seconds of game time
spire.update(20.0)
# Kill final boss
var boss_id = spire.active_mobs.keys()[0]
var boss_kill = spire.kill_mob(boss_id)
_assert(boss_kill["ok"] == true and boss_kill["victory"] == true, "Boss defeated, victory achieved")
_assert(spire.is_active == false and spire.is_victory == true, "Trial finished in victory")
_assert(victory_sig["fired"] == true and victory_sig["success"] == true, "trial_completed emitted victory")
_assert(chest_sig["fired"] == true and chest_sig["vnum"] == 50130, "reward_chest_spawned emitted 50130")
_assert(spire.chest_available == true, "Chest available for looting")
# Open Chest
var drops = spire.open_reward_chest()
_assert(drops.size() == 4, "Chest gave 4 rewards")
_assert(spire.chest_available == false, "Chest emptied")
var has_scroll := false
var has_soul_stone := false
var has_marble := false
var has_gold := false
for d in drops:
if d["vnum"] == 71003: has_scroll = true
elif d["vnum"] == 50513: has_soul_stone = true
elif d["vnum"] == 70024: has_marble = true
elif d["vnum"] == 1 and d["count"] >= 3000000: has_gold = true
_assert(has_scroll, "Reward contains Blessing Scroll (71003)")
_assert(has_soul_stone, "Reward contains Soul Stone (50513)")
_assert(has_marble, "Reward contains Blessing Marble (70024)")
_assert(has_gold, "Reward contains 3.5M gold")
# Second open attempt gives nothing
var empty = spire.open_reward_chest()
_assert(empty.is_empty(), "Cannot open chest twice")
func _test_timeout_defeat() -> void:
print("\n--- Test 3: Timeout Defeat (180s expired) ---")
var spire := CursedMonolithSpireSystem.new()
spire.start_trial(Vector3.ZERO, 50)
var defeat_sig := {"fired": false, "success": true}
spire.trial_completed.connect(func(s: bool, _t: float):
defeat_sig["fired"] = true
defeat_sig["success"] = s
)
# Simulate 185 seconds passing without clearing mobs
var tick = spire.update(185.0)
_assert(tick["victory"] == false, "Defeat on timeout")
_assert(spire.is_active == false and spire.is_victory == false, "Trial marked as inactive defeat")
_assert(defeat_sig["fired"] == true and defeat_sig["success"] == false, "trial_completed emitted defeat")
_assert(spire.chest_available == false, "No chest awarded on defeat")
func _finish() -> void:
print("\n==========================================")
print("Cursed Monolith Spire Parity Tests Finished:")
print("Passed: %d, Failed: %d" % [_passed, _failed])
print("==========================================")
if _failed > 0:
printerr("FAILED: Some tests did not pass.")
quit(1)
else:
print("SUCCESS: All Cursed Monolith Spire parity tests passed!")
quit(0)