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

190 lines
7.0 KiB
GDScript

# test_spider_dungeon_key_parity.gd —— 40250 蜘蛛洞穴通行证与蛛后巢穴密匙系统 1:1 回归测试套件
extends SceneTree
const SpiderDungeonKeySystem = preload("res://spider_dungeon_key_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_spider_dungeon_key_parity (Direction 4: Spider Dungeon Key 1:1) ===")
_test_passage_ticket_entry()
_test_arachnid_key_crafting()
_test_baroness_lair_and_boss_fight()
_test_lair_timeout()
_test_serialization()
_finish()
func _test_passage_ticket_entry() -> void:
print("\n--- Test 1: Passage Ticket (71095) Entry to SD2 & SD3 ---")
var sys := SpiderDungeonKeySystem.new()
var inv: Array = [
{"vnum": SpiderDungeonKeySystem.VNUM_PASSAGE_TICKET, "name": "蛛洞通行证", "count": 2}
]
# 1. Level too low (< 40 for SD2)
var res_low = sys.enter_spider_dungeon(2, 35, inv)
_assert(res_low["ok"] == false, "Entry rejected: level 35 < 40")
_assert(res_low["reason"] == "LEVEL_TOO_LOW", "Reason is LEVEL_TOO_LOW")
var entry_sig := {"fired": false, "floor": 0, "map": ""}
sys.ticket_consumed_entry_granted.connect(func(f: int, m: String):
entry_sig["fired"] = true
entry_sig["floor"] = f
entry_sig["map"] = m
)
# 2. Enter SD2 (Lv 45)
var res_sd2 = sys.enter_spider_dungeon(2, 45, inv)
_assert(res_sd2["ok"] == true, "Entered SD2 successfully")
_assert(inv[0]["count"] == 1, "Ticket count decremented 2 -> 1")
_assert(entry_sig["fired"] == true, "ticket_consumed_entry_granted signal emitted")
_assert(entry_sig["floor"] == 2 and entry_sig["map"] == "Spider_Dungeon_2", "Floor 2 reported")
# 3. Enter SD3 (Lv 55)
var res_sd3 = sys.enter_spider_dungeon(3, 55, inv)
_assert(res_sd3["ok"] == true, "Entered SD3 successfully")
_assert(inv[0] == null, "Last ticket consumed completely")
_assert(entry_sig["floor"] == 3 and entry_sig["map"] == "Spider_Dungeon_3", "Floor 3 reported")
# 4. Attempt enter without ticket
var res_no_ticket = sys.enter_spider_dungeon(2, 50, inv)
_assert(res_no_ticket["ok"] == false, "Entry rejected without ticket")
_assert(res_no_ticket["reason"] == "NO_PASSAGE_TICKET", "Reason is NO_PASSAGE_TICKET")
func _test_arachnid_key_crafting() -> void:
print("\n--- Test 2: Crafting Arachnid Key (30324) at Scholar Uriel ---")
var sys := SpiderDungeonKeySystem.new()
var player_gold := {"gold": 100000}
var inv: Array = [
{"vnum": SpiderDungeonKeySystem.VNUM_SPIDER_POISON_SACK, "name": "蜘蛛毒囊", "count": 1},
{"vnum": SpiderDungeonKeySystem.VNUM_SPIDER_EGG_SACK, "name": "蜘蛛卵囊", "count": 1},
{"vnum": SpiderDungeonKeySystem.VNUM_SPIDER_EYES, "name": "蜘蛛眼睛", "count": 1},
{"vnum": SpiderDungeonKeySystem.VNUM_SPIDER_LEGS, "name": "蜘蛛腿", "count": 1}
]
var craft_sig := {"fired": false, "count": 0}
sys.arachnid_key_crafted.connect(func(c: int):
craft_sig["fired"] = true
craft_sig["count"] = c
)
# 1. Missing Gold test
player_gold["gold"] = 1000
var res_no_gold = sys.craft_arachnid_key(inv, player_gold)
_assert(res_no_gold["ok"] == false, "Crafting rejected: gold 1000 < 50000")
_assert(res_no_gold["reason"] == "NOT_ENOUGH_GOLD", "Reason is NOT_ENOUGH_GOLD")
# 2. Successful craft with 50,000 gold
player_gold["gold"] = 80000
var res_craft = sys.craft_arachnid_key(inv, player_gold)
_assert(res_craft["ok"] == true, "Crafted Arachnid Key successfully")
_assert(player_gold["gold"] == 30000, "50,000 gold deducted (80000 -> 30000)")
_assert(craft_sig["fired"] == true, "arachnid_key_crafted signal emitted")
# Verify key exists in inventory
var key_found := false
for it in inv:
if it != null and int(it.get("vnum", 0)) == SpiderDungeonKeySystem.VNUM_ARACHNID_KEY:
key_found = true
break
_assert(key_found == true, "Arachnid Key (30324) generated in inventory")
func _test_baroness_lair_and_boss_fight() -> void:
print("\n--- Test 3: Baroness Lair Entry, Boss Slay & Rewards ---")
var sys := SpiderDungeonKeySystem.new()
var inv: Array = [
{"vnum": SpiderDungeonKeySystem.VNUM_ARACHNID_KEY, "name": "蛛网密匙", "count": 1},
null, null, null
]
var open_sig := {"fired": false, "dur": 0.0}
sys.baroness_lair_opened.connect(func(d: float):
open_sig["fired"] = true
open_sig["dur"] = d
)
var slay_sig := {"fired": false, "drops": []}
sys.baroness_slain.connect(func(d: Array):
slay_sig["fired"] = true
slay_sig["drops"] = d
)
# 1. Open Lair with key
var res_open = sys.open_baroness_lair(55, inv)
_assert(res_open["ok"] == true, "Baroness Lair opened")
_assert(sys.is_lair_active == true, "Lair is active")
_assert(sys.baroness_alive == true, "Spider Baroness is alive")
_assert(inv[0] == null, "Arachnid key consumed")
_assert(open_sig["fired"] == true, "baroness_lair_opened signal emitted")
_assert(open_sig["dur"] == 1200.0, "Lair duration is 1200s (20 min)")
# 2. Slay Baroness
var res_slay = sys.slay_baroness(inv)
_assert(res_slay["ok"] == true, "Baroness defeated")
_assert(sys.is_lair_active == false, "Lair closed after victory")
_assert(slay_sig["fired"] == true, "baroness_slain signal emitted")
# Check drops in inventory
var chest_found := false
for it in inv:
if it != null and int(it.get("vnum", 0)) == SpiderDungeonKeySystem.VNUM_CHEST_BARONESS:
chest_found = true
break
_assert(chest_found == true, "King Spider Chest (50181) received in inventory")
func _test_lair_timeout() -> void:
print("\n--- Test 4: Lair Timeout Failure ---")
var sys := SpiderDungeonKeySystem.new()
var inv: Array = [
{"vnum": SpiderDungeonKeySystem.VNUM_ARACHNID_KEY, "name": "蛛网密匙", "count": 1}
]
sys.open_baroness_lair(55, inv)
var close_sig := {"fired": false, "succ": true}
sys.lair_closed.connect(func(s: bool):
close_sig["fired"] = true
close_sig["succ"] = s
)
# Advance time by 1200s
sys.update(1200.0)
_assert(sys.is_lair_active == false, "Lair closed upon timeout")
_assert(close_sig["fired"] == true, "lair_closed signal emitted")
_assert(close_sig["succ"] == false, "Failed challenge due to timeout")
func _test_serialization() -> void:
print("\n--- Test 5: Serialization & Restoration ---")
var sys1 := SpiderDungeonKeySystem.new()
var inv: Array = [
{"vnum": SpiderDungeonKeySystem.VNUM_ARACHNID_KEY, "name": "蛛网密匙", "count": 1}
]
sys1.open_baroness_lair(55, inv)
sys1.update(300.0) # 900s left
var data = sys1.serialize()
_assert(data["is_lair_active"] == true, "Serialized is_lair_active")
var sys2 := SpiderDungeonKeySystem.new()
sys2.deserialize(data)
_assert(sys2.is_lair_active == true, "Restored is_lair_active")
_assert(sys2.baroness_alive == true, "Restored baroness_alive")
_assert(is_equal_approx(sys2.lair_time_remaining, 900.0), "Restored time remaining 900s")
func _finish() -> void:
print("\n=======================================================")
print("Spider Dungeon Key Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)