- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
135 lines
5.0 KiB
GDScript
135 lines
5.0 KiB
GDScript
# test_spider_dungeon_parity.gd —— 40250 蜘蛛男爵夫人巢穴 1:1 纯净回归测试套件
|
|
extends SceneTree
|
|
|
|
const SpiderDungeonSystem = preload("res://spider_dungeon_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_parity (Direction 1: Spider Dungeon 3 & Baroness 1:1) ===")
|
|
_test_entry_and_key_consumption()
|
|
_test_egg_frenzy_mechanics()
|
|
_test_combat_and_minions_summon()
|
|
_test_baroness_kill_and_drops()
|
|
_test_timeout_failure()
|
|
_finish()
|
|
|
|
func _test_entry_and_key_consumption() -> void:
|
|
print("\n--- Test 1: Entry Level & Arachnid Key (30327) ---")
|
|
var sds := SpiderDungeonSystem.new()
|
|
var inv: Array = [
|
|
{"vnum": 30327}, # Arachnid Key
|
|
null
|
|
]
|
|
|
|
# Level 40 rejected (requires 50)
|
|
var res = sds.enter_dungeon(40, inv)
|
|
_assert(res["ok"] == false and res["reason"] == "LEVEL_TOO_LOW", "Level < 50 rejected")
|
|
|
|
# Missing key rejected
|
|
res = sds.enter_dungeon(50, [null])
|
|
_assert(res["ok"] == false and res["reason"] == "MISSING_KEY", "Missing Arachnid Key rejected")
|
|
|
|
# Valid entry succeeds
|
|
res = sds.enter_dungeon(55, inv)
|
|
_assert(res["ok"] == true, "Entry succeeds with Level 55 and key")
|
|
_assert(inv[0] == null, "Arachnid key consumed from inventory")
|
|
_assert(sds.is_active == true, "Dungeon is now active")
|
|
_assert(sds.remaining_time == 1200.0, "20-minute countdown initialized")
|
|
_assert(sds.eggs_remaining == 4, "4 spider eggs present")
|
|
|
|
func _test_egg_frenzy_mechanics() -> void:
|
|
print("\n--- Test 2: Egg Frenzy Mechanics (-DEF, +ATK) ---")
|
|
var sds := SpiderDungeonSystem.new()
|
|
sds.enter_dungeon(60, [{"vnum": 30327}])
|
|
|
|
_assert(sds.baroness_def == 400, "Initial Baroness DEF is 400")
|
|
_assert(sds.baroness_atk == 800, "Initial Baroness ATK is 800")
|
|
|
|
# Destroy 1st egg: -15% DEF (340), +20% ATK (960)
|
|
var res = sds.destroy_egg()
|
|
_assert(res["ok"] == true, "1st egg destroyed")
|
|
_assert(sds.baroness_def == 340, "Baroness DEF reduced to 340 (-15%)")
|
|
_assert(sds.baroness_atk == 960, "Baroness ATK increased to 960 (+20%)")
|
|
|
|
# Destroy 2nd egg: -30% DEF (280), +40% ATK (1120)
|
|
sds.destroy_egg()
|
|
_assert(sds.baroness_def == 280, "2 eggs broken: DEF 280")
|
|
_assert(sds.baroness_atk == 1120, "2 eggs broken: ATK 1120")
|
|
|
|
# Destroy 3rd egg: -45% DEF (220), +60% ATK (1280)
|
|
sds.destroy_egg()
|
|
_assert(sds.baroness_def == 220, "3 eggs broken: DEF 220")
|
|
|
|
# Destroy 4th egg: -60% DEF (160), +80% ATK (1440)
|
|
sds.destroy_egg()
|
|
_assert(sds.baroness_def == 160, "All 4 eggs broken: DEF down to 160 (-60%)")
|
|
_assert(sds.baroness_atk == 1440, "All 4 eggs broken: ATK up to 1440 (+80%)")
|
|
|
|
# 5th egg attempt rejected
|
|
res = sds.destroy_egg()
|
|
_assert(res["ok"] == false and res["reason"] == "NO_EGGS_LEFT", "Cannot destroy nonexistent egg")
|
|
|
|
func _test_combat_and_minions_summon() -> void:
|
|
print("\n--- Test 3: Combat Damage & 50% HP Minions Wave ---")
|
|
var sds := SpiderDungeonSystem.new()
|
|
sds.enter_dungeon(60, [{"vnum": 30327}])
|
|
# Break 4 eggs so defense is 160
|
|
for i in range(4): sds.destroy_egg()
|
|
|
|
# Deal 50,000 raw damage -> effective damage is 50000 - 160 = 49840
|
|
var res = sds.damage_baroness(50000)
|
|
_assert(res["ok"] == true, "Damaged Baroness")
|
|
_assert(res["damage_dealt"] == 49840, "Effective damage calculated after 160 DEF")
|
|
_assert(sds.minions_spawned == false, "Minions not yet spawned above 50% HP")
|
|
|
|
# Deal another 80,000 damage -> drops below 50% (125,000)
|
|
res = sds.damage_baroness(80000)
|
|
_assert(sds.baroness_hp <= 125000, "HP dropped below 50%")
|
|
_assert(sds.minions_spawned == true, "50% HP triggered minions wave")
|
|
_assert(res["minions_summoned"] == true, "Minions summon event reported")
|
|
|
|
func _test_baroness_kill_and_drops() -> void:
|
|
print("\n--- Test 4: Baroness Kill & Baroness Chest Drop ---")
|
|
var sds := SpiderDungeonSystem.new()
|
|
sds.enter_dungeon(60, [{"vnum": 30327}])
|
|
|
|
# Deal lethal damage
|
|
var res = sds.damage_baroness(300000)
|
|
_assert(res["ok"] == true and res["dead"] == true, "Baroness slain")
|
|
_assert(sds.dungeon_completed == true, "Dungeon cleared")
|
|
_assert(sds.is_active == false, "Dungeon deactivated")
|
|
|
|
var drops: Array[Dictionary] = res["drops"]
|
|
_assert(drops.size() == 2, "2 items dropped")
|
|
_assert(drops[0]["vnum"] == SpiderDungeonSystem.ITEM_BARONESS_CHEST, "Dropped Baroness Chest (50157)")
|
|
_assert(drops[1]["vnum"] == SpiderDungeonSystem.ITEM_TITAN_SHIELD, "Dropped Titan Shield (13129)")
|
|
|
|
func _test_timeout_failure() -> void:
|
|
print("\n--- Test 5: Timeout Failure ---")
|
|
var sds := SpiderDungeonSystem.new()
|
|
sds.enter_dungeon(60, [{"vnum": 30327}])
|
|
|
|
# Update 1201s (past 1200s limit)
|
|
var res = sds.update(1201.0)
|
|
_assert(res["failed"] == true and res["reason"] == "TIME_OUT", "Dungeon failed on timeout")
|
|
_assert(sds.is_active == false, "Dungeon marked inactive")
|
|
|
|
func _finish() -> void:
|
|
print("\n=== SUMMARY: %d passed, %d failed ===" % [_passed, _failed])
|
|
if _failed == 0:
|
|
print("ALL TESTS PASSED!\n")
|
|
quit(0)
|
|
else:
|
|
printerr("SOME TESTS FAILED!\n")
|
|
quit(1)
|