- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
109 lines
4.8 KiB
GDScript
109 lines
4.8 KiB
GDScript
# test_dungeon_ranking_parity.gd —— 40250 地牢信息与最速榜单 1:1 纯净回归测试套件
|
|
extends SceneTree
|
|
|
|
const DungeonRankingSystem = preload("res://dungeon_ranking_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_dungeon_ranking_parity (Direction 1: Dungeon Ranking & Info 1:1) ===")
|
|
_test_dungeon_registry_configurations()
|
|
_test_clear_time_and_records()
|
|
_test_cooldown_tracking()
|
|
_test_summaries_list()
|
|
_finish()
|
|
|
|
func _test_dungeon_registry_configurations() -> void:
|
|
print("\n--- Test 1: Dungeon Registry Configurations ---")
|
|
var drs := DungeonRankingSystem.new()
|
|
_assert(DungeonRankingSystem.DUNGEON_REGISTRY.has("demon_tower"), "Demon Tower defined")
|
|
_assert(DungeonRankingSystem.DUNGEON_REGISTRY.has("spider_dungeon_3"), "Spider Dungeon 3 defined")
|
|
_assert(DungeonRankingSystem.DUNGEON_REGISTRY.has("dragon_lair"), "Dragon Lair defined")
|
|
_assert(DungeonRankingSystem.DUNGEON_REGISTRY.has("devils_catacomb"), "Devil's Catacomb defined")
|
|
|
|
var dl = DungeonRankingSystem.DUNGEON_REGISTRY["dragon_lair"]
|
|
_assert(dl["min_level"] == 75, "Dragon Lair requires Lv 75")
|
|
_assert(dl["ticket_vnum"] == 30179 and dl["ticket_count"] == 3, "Requires 3x Twisted Key (30179)")
|
|
|
|
var sd3 = DungeonRankingSystem.DUNGEON_REGISTRY["spider_dungeon_3"]
|
|
_assert(sd3["min_level"] == 50, "Spider Dungeon 3 requires Lv 50")
|
|
_assert(sd3["ticket_vnum"] == 30327 and sd3["ticket_count"] == 1, "Requires Arachnid Key (30327)")
|
|
|
|
func _test_clear_time_and_records() -> void:
|
|
print("\n--- Test 2: Clear Time Tracking & Personal Best Record ---")
|
|
var drs := DungeonRankingSystem.new()
|
|
|
|
var record_signal := {"fired": false, "time": 0.0}
|
|
drs.new_fastest_record.connect(func(_d: String, t: float, _old: float):
|
|
record_signal["fired"] = true
|
|
record_signal["time"] = t
|
|
)
|
|
|
|
# 1. First clear of Dragon Lair (600.0s, peak dmg 8500)
|
|
var r1 = drs.record_clear("dragon_lair", 600.0, 8500, 1000.0)
|
|
_assert(r1["ok"] == true, "Clear recorded")
|
|
_assert(r1["is_new_record"] == true, "First clear is a new personal best")
|
|
_assert(r1["fastest_time"] == 600.0, "Fastest time is 600.0s")
|
|
_assert(r1["total_clears"] == 1, "Total clears is 1")
|
|
_assert(r1["highest_peak_damage"] == 8500, "Highest peak damage is 8500")
|
|
_assert(record_signal["fired"] == true and record_signal["time"] == 600.0, "new_fastest_record signal fired")
|
|
|
|
# 2. Slower clear (700.0s) -> doesn't replace 600.0s
|
|
record_signal["fired"] = false
|
|
var r2 = drs.record_clear("dragon_lair", 700.0, 9000, 2000.0)
|
|
_assert(r2["is_new_record"] == false, "Slower clear is not a new record")
|
|
_assert(r2["fastest_time"] == 600.0, "Fastest time remains 600.0s")
|
|
_assert(r2["total_clears"] == 2, "Total clears incremented to 2")
|
|
_assert(r2["highest_peak_damage"] == 9000, "Peak damage updated to 9000")
|
|
_assert(record_signal["fired"] == false, "new_fastest_record signal did NOT fire")
|
|
|
|
# 3. Faster clear (450.0s) -> replaces record!
|
|
var r3 = drs.record_clear("dragon_lair", 450.0, 9200, 3000.0)
|
|
_assert(r3["is_new_record"] == true, "Faster clear sets new record")
|
|
_assert(r3["fastest_time"] == 450.0, "Fastest time updated to 450.0s")
|
|
_assert(r3["total_clears"] == 3, "Total clears is 3")
|
|
_assert(record_signal["fired"] == true and record_signal["time"] == 450.0, "new_fastest_record signal fired for 450.0s")
|
|
|
|
func _test_cooldown_tracking() -> void:
|
|
print("\n--- Test 3: Dungeon Cooldown Tracking ---")
|
|
var drs := DungeonRankingSystem.new()
|
|
|
|
# Spider dungeon cooldown is 1800s
|
|
drs.record_clear("spider_dungeon_3", 300.0, 5000, 1000.0) # cleared at time 1000.0
|
|
|
|
# 1. Query at timestamp 1500.0 (elapsed 500s < 1800s) -> on cooldown
|
|
var info_cd = drs.get_dungeon_info("spider_dungeon_3", 1500.0)
|
|
_assert(info_cd["is_on_cooldown"] == true, "Dungeon is on cooldown")
|
|
_assert(info_cd["remaining_cooldown"] == 1300.0, "1300.0s remaining cooldown")
|
|
|
|
# 2. Query at timestamp 3000.0 (elapsed 2000s > 1800s) -> ready
|
|
var info_ready = drs.get_dungeon_info("spider_dungeon_3", 3000.0)
|
|
_assert(info_ready["is_on_cooldown"] == false, "Dungeon is ready")
|
|
_assert(info_ready["remaining_cooldown"] == 0.0, "0.0s remaining cooldown")
|
|
|
|
func _test_summaries_list() -> void:
|
|
print("\n--- Test 4: All Dungeon Summaries List ---")
|
|
var drs := DungeonRankingSystem.new()
|
|
var list = drs.get_all_dungeon_summaries()
|
|
_assert(list.size() == 4, "Summaries list contains all 4 dungeons")
|
|
_assert(list[0]["dungeon_id"] == "demon_tower", "Contains Demon Tower")
|
|
_assert(list[1]["dungeon_id"] == "spider_dungeon_3", "Contains Spider Dungeon 3")
|
|
|
|
func _finish() -> void:
|
|
print("\n==========================================")
|
|
print("Dungeon Ranking Parity Test Results: %d Passed, %d Failed" % [_passed, _failed])
|
|
print("==========================================")
|
|
if _failed > 0:
|
|
quit(1)
|
|
else:
|
|
quit(0)
|