- 桥梁与静态物体高度采样修复: - 严格对齐 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 客户端对拍缺陷发现与修复工程指南
141 lines
5.3 KiB
GDScript
141 lines
5.3 KiB
GDScript
# test_combat_replay_snapshot_parity.gd —— 40250 战斗高光战报与首领击杀快照 1:1 回归测试套件
|
|
extends SceneTree
|
|
|
|
const CombatReplaySnapshotSystem = preload("res://combat_replay_snapshot_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_combat_replay_snapshot_parity (Direction 4: Combat Highlights 1:1) ===")
|
|
_test_combat_session_tracking()
|
|
_test_highlight_card_generation_and_rating()
|
|
_test_personal_bests_and_records()
|
|
_test_history_buffer_cap()
|
|
_finish()
|
|
|
|
func _test_combat_session_tracking() -> void:
|
|
print("\n--- Test 1: Session Tracking & Hit Metrics ---")
|
|
var cr := CombatReplaySnapshotSystem.new()
|
|
|
|
var start_sig := {"fired": false, "name": ""}
|
|
cr.combat_session_started.connect(func(n: String):
|
|
start_sig["fired"] = true
|
|
start_sig["name"] = n
|
|
)
|
|
|
|
var s_res = cr.start_combat_session(1901, "九尾妖狐", 280000)
|
|
_assert(s_res["ok"] == true, "Session started")
|
|
_assert(cr.is_in_session == true, "is_in_session is true")
|
|
_assert(start_sig["fired"] == true and start_sig["name"] == "九尾妖狐", "combat_session_started emitted")
|
|
|
|
# Record attacks
|
|
cr.record_hit(1500, false)
|
|
cr.record_hit(3000, true) # Crit
|
|
cr.record_hit(8500, true, "三连斩") # Skill crit, highest hit so far
|
|
cr.record_damage_taken(650)
|
|
|
|
_assert(cr.total_damage_dealt == 13000, "Total damage dealt is 13000 (1500 + 3000 + 8500)")
|
|
_assert(cr.total_attacks == 3, "Total attacks is 3")
|
|
_assert(cr.critical_hits_count == 2, "2 critical hits")
|
|
_assert(cr.total_skills_used == 1, "1 skill used")
|
|
_assert(cr.max_single_hit == 8500, "Highest single hit tracked as 8500")
|
|
_assert(cr.total_damage_taken == 650, "Damage taken tracked as 650")
|
|
|
|
func _test_highlight_card_generation_and_rating() -> void:
|
|
print("\n--- Test 2: Highlight Card Generation & S-Rank Rating ---")
|
|
var cr := CombatReplaySnapshotSystem.new()
|
|
cr.start_combat_session(1304, "黄虎领主", 320000)
|
|
|
|
var card_sig := {"fired": false, "rank": ""}
|
|
cr.combat_highlight_generated.connect(func(c: Dictionary):
|
|
card_sig["fired"] = true
|
|
card_sig["rank"] = c["rank"]
|
|
)
|
|
|
|
# Simulate 10 seconds of combat with 50,000 total damage (DPS = 5000 -> S Rank)
|
|
cr.update(10.0)
|
|
cr.record_hit(20000, true, "旋风斩")
|
|
cr.record_hit(30000, true, "剑气斩")
|
|
|
|
var drops: Array = [{"vnum": 50513, "name": "灵魂之石", "count": 1}]
|
|
var card = cr.end_combat_session(true, drops)
|
|
|
|
_assert(cr.is_in_session == false, "Session ended")
|
|
_assert(card["target_name"] == "黄虎领主", "Card target name matches")
|
|
_assert(card["total_damage"] == 50000, "Card total damage is 50000")
|
|
_assert(card["dps"] == 5000, "DPS is 5000 (50000 / 10s)")
|
|
_assert(card["rank"] == "S", "Awarded S Rank (DPS >= 4000)")
|
|
_assert(card["drops"].size() == 1, "Drops included in highlight card")
|
|
_assert(card_sig["fired"] == true and card_sig["rank"] == "S", "combat_highlight_generated signal fired")
|
|
|
|
func _test_personal_bests_and_records() -> void:
|
|
print("\n--- Test 3: Personal Best Records & Signals ---")
|
|
var cr := CombatReplaySnapshotSystem.new()
|
|
|
|
var record_sig := {"fired": false, "type": "", "val": 0}
|
|
cr.new_personal_record.connect(func(_t: String, rtype: String, val: int):
|
|
record_sig["fired"] = true
|
|
record_sig["type"] = rtype
|
|
record_sig["val"] = val
|
|
)
|
|
|
|
# 1st Kill: Nine Tails, 10s, 30000 dmg, DPS = 3000, max hit = 10000
|
|
cr.start_combat_session(1901, "九尾妖狐", 280000)
|
|
cr.update(10.0)
|
|
cr.record_hit(10000, true)
|
|
cr.record_hit(20000, true)
|
|
cr.end_combat_session(true)
|
|
|
|
_assert(record_sig["fired"] == true and record_sig["type"] == "FIRST_KILL", "First kill record emitted")
|
|
var pb1 = cr.get_personal_best("九尾妖狐")
|
|
_assert(pb1["best_dps"] == 3000, "Recorded best DPS 3000")
|
|
_assert(pb1["highest_hit"] == 20000, "Recorded highest hit 20000")
|
|
|
|
# 2nd Kill: Nine Tails, faster kill (5s), DPS = 8000, max hit = 35000
|
|
record_sig["fired"] = false
|
|
cr.start_combat_session(1901, "九尾妖狐", 280000)
|
|
cr.update(5.0)
|
|
cr.record_hit(35000, true, "大绝招")
|
|
cr.record_hit(5000, false)
|
|
cr.end_combat_session(true)
|
|
|
|
_assert(record_sig["fired"] == true, "New personal record emitted")
|
|
var pb2 = cr.get_personal_best("九尾妖狐")
|
|
_assert(pb2["best_dps"] == 8000, "Best DPS updated to 8000")
|
|
_assert(pb2["highest_hit"] == 35000, "Highest hit updated to 35000")
|
|
_assert(pb2["fastest_time"] == 5.0, "Fastest time updated to 5.0s")
|
|
|
|
func _test_history_buffer_cap() -> void:
|
|
print("\n--- Test 4: History Buffer Cap (Max 20 Cards) ---")
|
|
var cr := CombatReplaySnapshotSystem.new()
|
|
for i in range(25):
|
|
cr.start_combat_session(1, "Mob_%d" % i, 100)
|
|
cr.update(1.0)
|
|
cr.record_hit(100, false)
|
|
cr.end_combat_session(true)
|
|
|
|
var recents = cr.get_recent_highlights()
|
|
_assert(recents.size() == CombatReplaySnapshotSystem.MAX_STORED_CARDS, "History capped at max 20 cards")
|
|
_assert(recents[0]["target_name"] == "Mob_24", "Most recent card is at front")
|
|
|
|
func _finish() -> void:
|
|
print("\n==========================================")
|
|
print("Combat Replay Snapshot 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 Combat Replay Snapshot parity tests passed!")
|
|
quit(0)
|