- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
224 lines
8.1 KiB
GDScript
224 lines
8.1 KiB
GDScript
# test_devils_catacomb_parity.gd —— 40250 地下墓穴与冥王阿兹瑞尔 1:1 纯净回归测试套件
|
|
extends SceneTree
|
|
|
|
const DevilsCatacombSystem = preload("res://devils_catacomb_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_devils_catacomb_parity (Direction 1: Devil's Catacomb 1:1) ===")
|
|
_test_entry_requirements()
|
|
_test_floor_1_to_4_progression()
|
|
_test_floor_5_tartaros_and_obelisk()
|
|
_test_floor_6_charon()
|
|
_test_floor_7_azrael_battle_and_drops()
|
|
_test_catacomb_timeout()
|
|
_finish()
|
|
|
|
func _test_entry_requirements() -> void:
|
|
print("\n--- Test 1: Entry Requirements (Lv75 & Soul Key 30311) ---")
|
|
var dcs := DevilsCatacombSystem.new()
|
|
var inv: Array = [
|
|
{"vnum": 30311, "name": "灵魂水晶钥匙", "count": 1}
|
|
]
|
|
|
|
# Level < 75 rejected
|
|
var r1 = dcs.enter_catacomb(70, inv)
|
|
_assert(r1["ok"] == false and r1["reason"] == "LEVEL_TOO_LOW", "Level < 75 rejected")
|
|
|
|
# Missing key
|
|
var r2 = dcs.enter_catacomb(75, [null])
|
|
_assert(r2["ok"] == false and r2["reason"] == "MISSING_SOUL_KEY", "Missing soul crystal key rejected")
|
|
|
|
# Valid entry
|
|
var r3 = dcs.enter_catacomb(75, inv)
|
|
_assert(r3["ok"] == true, "Entry succeeds with Lv 75 and Soul Crystal Key")
|
|
_assert(dcs.is_active == true, "Catacomb is active")
|
|
_assert(dcs.current_floor == 1, "Starts on Floor 1")
|
|
_assert(dcs.remaining_time == 3600.0, "60-minute countdown initialized")
|
|
|
|
func _test_floor_1_to_4_progression() -> void:
|
|
print("\n--- Test 2: Floor 1 to 4 Progression (Key, Maze, Real Metin) ---")
|
|
var dcs := DevilsCatacombSystem.new()
|
|
var inv: Array = [
|
|
{"vnum": 30311, "name": "灵魂水晶钥匙", "count": 1}
|
|
]
|
|
dcs.enter_catacomb(75, inv)
|
|
|
|
var floor_signal := {"fired": false, "floor": 0}
|
|
dcs.floor_advanced.connect(func(f: int):
|
|
floor_signal["fired"] = true
|
|
floor_signal["floor"] = f
|
|
)
|
|
|
|
# 1. Floor 1: Unlock seal using 30311 key
|
|
var r1 = dcs.unlock_floor_1(inv)
|
|
_assert(r1["ok"] == true and r1["floor"] == 2, "Unlocked Floor 1 -> Advanced to Floor 2")
|
|
_assert(inv[0] == null, "Soul crystal key consumed")
|
|
_assert(floor_signal["fired"] == true and floor_signal["floor"] == 2, "floor_advanced signal fired for floor 2")
|
|
|
|
# 2. Floor 2: Complete maze
|
|
var r2 = dcs.complete_floor_2()
|
|
_assert(r2["ok"] == true and r2["floor"] == 3, "Maze completed -> Advanced to Floor 3")
|
|
|
|
# 3. Floor 3: Break fake vs real Metin (8035)
|
|
dcs.correct_metin_id = 4 # Force correct ID to 4 for deterministic test
|
|
|
|
var metin_signal := {"fired": false, "correct": false}
|
|
dcs.metin_destroyed.connect(func(corr: bool):
|
|
metin_signal["fired"] = true
|
|
metin_signal["correct"] = corr
|
|
)
|
|
|
|
# Break fake metin 1
|
|
var r_fake = dcs.break_floor_3_metin(1)
|
|
_assert(r_fake["ok"] == true and r_fake["correct"] == false, "Fake metin detected")
|
|
_assert(dcs.current_floor == 3, "Stays on floor 3 after fake metin")
|
|
_assert(metin_signal["fired"] == true and metin_signal["correct"] == false, "metin_destroyed signal fired false")
|
|
|
|
# Break real metin 4
|
|
var r_real = dcs.break_floor_3_metin(4)
|
|
_assert(r_real["ok"] == true and r_real["correct"] == true, "Real metin destroyed")
|
|
_assert(dcs.current_floor == 4, "Advanced to Floor 4")
|
|
_assert(metin_signal["correct"] == true, "metin_destroyed signal fired true")
|
|
|
|
# 4. Floor 4: Complete pillar maze
|
|
var r4 = dcs.complete_floor_4()
|
|
_assert(r4["ok"] == true and r4["floor"] == 5, "Floor 4 completed -> Advanced to Floor 5")
|
|
|
|
func _test_floor_5_tartaros_and_obelisk() -> void:
|
|
print("\n--- Test 3: Floor 5 Tartaros Defeat & Obelisk Activation ---")
|
|
var dcs := DevilsCatacombSystem.new()
|
|
dcs.enter_catacomb(75, [{"vnum": 30311}])
|
|
dcs.current_floor = 5 # Jump to floor 5
|
|
|
|
var inv: Array = [null]
|
|
var tartaros_signal := {"fired": false}
|
|
dcs.tartaros_killed.connect(func(_drop: Dictionary):
|
|
tartaros_signal["fired"] = true
|
|
)
|
|
|
|
# Defeat Tartaros
|
|
var r_tar = dcs.defeat_tartaros(inv)
|
|
_assert(r_tar["ok"] == true, "Tartaros defeated")
|
|
_assert(inv[0] != null and inv[0]["vnum"] == 30312, "Grim Soul Stone (30312) dropped into inventory")
|
|
_assert(tartaros_signal["fired"] == true, "tartaros_killed signal fired")
|
|
|
|
# Insert stone into obelisk
|
|
var obelisk_signal := {"fired": false}
|
|
dcs.obelisk_activated.connect(func():
|
|
obelisk_signal["fired"] = true
|
|
)
|
|
|
|
var r_ob = dcs.insert_grim_stone_to_obelisk(inv)
|
|
_assert(r_ob["ok"] == true and r_ob["floor"] == 6, "Obelisk activated -> Advanced to Floor 6")
|
|
_assert(inv[0] == null, "Grim Soul Stone consumed by obelisk")
|
|
_assert(obelisk_signal["fired"] == true, "obelisk_activated signal fired")
|
|
|
|
func _test_floor_6_charon() -> void:
|
|
print("\n--- Test 4: Floor 6 Charon Defeat ---")
|
|
var dcs := DevilsCatacombSystem.new()
|
|
dcs.enter_catacomb(75, [{"vnum": 30311}])
|
|
dcs.current_floor = 6
|
|
|
|
var charon_signal := {"fired": false}
|
|
dcs.charon_killed.connect(func(): charon_signal["fired"] = true)
|
|
|
|
var r = dcs.defeat_charon()
|
|
_assert(r["ok"] == true and r["floor"] == 7, "Charon defeated -> Advanced to Floor 7")
|
|
_assert(charon_signal["fired"] == true, "charon_killed signal fired")
|
|
_assert(dcs.is_azrael_alive == true, "Azrael is now active on floor 7")
|
|
_assert(dcs.azrael_hp == 900000, "Azrael initial HP is 900,000")
|
|
|
|
func _test_floor_7_azrael_battle_and_drops() -> void:
|
|
print("\n--- Test 5: Floor 7 Azrael Battle, Minion Summon & Drops ---")
|
|
var dcs := DevilsCatacombSystem.new()
|
|
dcs.enter_catacomb(75, [{"vnum": 30311}])
|
|
dcs.current_floor = 6
|
|
dcs.defeat_charon() # Transitions to floor 7
|
|
|
|
var azrael_hp_signal := {"fired": false, "hp": 0}
|
|
dcs.azrael_hp_changed.connect(func(hp: int, _max: int):
|
|
azrael_hp_signal["fired"] = true
|
|
azrael_hp_signal["hp"] = hp
|
|
)
|
|
|
|
# 1. Normal damage: Raw 1680 - DEF 680 = 1000 net dmg
|
|
var r1 = dcs.attack_azrael(1680)
|
|
_assert(r1["ok"] == true and r1["net_dmg"] == 1000, "Raw 1680 - 680 DEF = 1000 net dmg")
|
|
_assert(dcs.azrael_hp == 899000, "Azrael HP reduced to 899,000")
|
|
_assert(azrael_hp_signal["fired"] == true, "azrael_hp_changed signal fired")
|
|
|
|
# 2. Cross 50% HP threshold (450,000 HP) -> summons minions
|
|
# Current HP: 899,000. Deal 450,000 net dmg -> raw 450,680
|
|
var r2 = dcs.attack_azrael(450680)
|
|
_assert(dcs.azrael_hp == 449000, "Azrael HP below 50% (449,000)")
|
|
_assert(r2["summoned_minions"] == true, "Azrael summoned undead minions at <= 50% HP")
|
|
|
|
# 3. Lethal damage kills Azrael
|
|
var clear_signal := {"fired": false, "drops": []}
|
|
dcs.catacomb_cleared.connect(func(drops: Array):
|
|
clear_signal["fired"] = true
|
|
clear_signal["drops"] = drops
|
|
)
|
|
|
|
var r3 = dcs.attack_azrael(1000000)
|
|
_assert(r3["ok"] == true and r3["killed"] == true, "Azrael killed")
|
|
_assert(dcs.is_active == false, "Catacomb completed")
|
|
_assert(clear_signal["fired"] == true, "catacomb_cleared signal fired")
|
|
|
|
# Verify drops
|
|
var drops: Array = r3["drops"]
|
|
var has_chest := false
|
|
var has_soul_stone := false
|
|
var has_shield := false
|
|
var has_gold := false
|
|
|
|
for d in drops:
|
|
if d["vnum"] == 50187: # Azrael Chest
|
|
has_chest = true
|
|
elif d["vnum"] == 50513: # Soul Stone
|
|
has_soul_stone = true
|
|
elif d["vnum"] == 13149: # Titan Shield
|
|
has_shield = true
|
|
elif d["vnum"] == 1:
|
|
has_gold = true
|
|
|
|
_assert(has_chest, "Guaranteed Azrael Chest (50187) dropped")
|
|
_assert(has_soul_stone, "Guaranteed Soul Stone (50513) dropped")
|
|
_assert(has_shield, "Titan Shield (13149) dropped")
|
|
_assert(has_gold, "Yang dropped")
|
|
|
|
func _test_catacomb_timeout() -> void:
|
|
print("\n--- Test 6: Catacomb Timeout Failure ---")
|
|
var dcs := DevilsCatacombSystem.new()
|
|
dcs.enter_catacomb(75, [{"vnum": 30311}])
|
|
|
|
var fail_signal := {"fired": false, "reason": ""}
|
|
dcs.catacomb_failed.connect(func(r: String):
|
|
fail_signal["fired"] = true
|
|
fail_signal["reason"] = r
|
|
)
|
|
|
|
dcs.update(3601.0)
|
|
_assert(fail_signal["fired"] == true and fail_signal["reason"] == "TIME_OUT", "Timeout failure emitted")
|
|
_assert(dcs.is_active == false, "Catacomb deactivated on timeout")
|
|
|
|
func _finish() -> void:
|
|
print("\n==========================================")
|
|
print("Devil's Catacomb Parity Test Results: %d Passed, %d Failed" % [_passed, _failed])
|
|
print("==========================================")
|
|
if _failed > 0:
|
|
quit(1)
|
|
else:
|
|
quit(0)
|