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

141 lines
5.4 KiB
GDScript

# test_title_trophy_buff_parity.gd —— 40250 真实称号词条战力加成与奖杯成就图鉴 1:1 回归测试套件
extends SceneTree
const TitleTrophyBuffSystem = preload("res://title_trophy_buff_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_title_trophy_buff_parity (Direction 4: Title Trophy Buffs 1:1) ===")
_test_catalog_and_unlock()
_test_title_equip_and_stat_injection()
_test_title_switching_and_unequip()
_test_trophy_milestone_progression()
_finish()
func _test_catalog_and_unlock() -> void:
print("\n--- Test 1: Title Catalog & Unlocking ---")
var sys := TitleTrophyBuffSystem.new()
_assert(TitleTrophyBuffSystem.TITLES_CATALOG.size() == 7, "Catalog has 7 iconic titles")
var unk_res = sys.unlock_title("InvalidTitle")
_assert(unk_res["ok"] == false and unk_res["reason"] == "UNKNOWN_TITLE", "Unknown title rejected")
var u1 = sys.unlock_title("屠龙尊者", 100)
_assert(u1["ok"] == true, "屠龙尊者 unlocked")
_assert(sys.trophy_points == 100, "Trophy points = 100")
_assert("屠龙尊者" in sys.unlocked_titles, "Title in unlocked list")
func _test_title_equip_and_stat_injection() -> void:
print("\n--- Test 2: Equipping Title & Live Stat Injection ---")
var sys := TitleTrophyBuffSystem.new()
sys.unlock_title("屠龙尊者")
var player := {
"name": "战神",
"max_hp": 10000,
"atk": 500,
"def": 300,
"boss_dmg_pct": 0,
"crit_pct": 5
}
var equip_sig := {"fired": false, "title": ""}
sys.title_stat_buffs_applied.connect(func(t: String, _b: Dictionary):
equip_sig["fired"] = true
equip_sig["title"] = t
)
# Try equip locked title
var fail_lock = sys.equip_title("百战名将", player)
_assert(fail_lock["ok"] == false and fail_lock["reason"] == "TITLE_NOT_UNLOCKED", "Cannot equip locked title")
# Equip "屠龙尊者" (boss_dmg_pct: 15, max_hp: 2000, crit_pct: 5)
var res = sys.equip_title("屠龙尊者", player)
_assert(res["ok"] == true, "Title equipped")
_assert(equip_sig["fired"] == true and equip_sig["title"] == "屠龙尊者", "title_stat_buffs_applied emitted")
_assert(player["max_hp"] == 12000, "Max HP increased 10000 -> 12000 (+2000)")
_assert(player["boss_dmg_pct"] == 15, "Boss damage increased 0 -> 15%")
_assert(player["crit_pct"] == 10, "Crit rate increased 5 -> 10% (+5%)")
func _test_title_switching_and_unequip() -> void:
print("\n--- Test 3: Zero-Drift Switching & Unequipping ---")
var sys := TitleTrophyBuffSystem.new()
sys.unlock_title("屠龙尊者")
sys.unlock_title("百战名将")
var player := {
"max_hp": 10000,
"atk": 500,
"def": 300,
"boss_dmg_pct": 0,
"crit_pct": 5
}
# 1. Equip 屠龙尊者
sys.equip_title("屠龙尊者", player)
_assert(player["max_hp"] == 12000, "HP +2000")
# 2. Switch directly to 百战名将 (atk: 80, def: 50, max_hp: 1500)
# 屠龙尊者 stats (-2000 HP, -15% boss dmg, -5% crit) must be deducted first!
sys.equip_title("百战名将", player)
_assert(player["max_hp"] == 11500, "HP is 10000 + 1500 = 11500 (Clean subtraction of 2000, addition of 1500)")
_assert(player["atk"] == 580, "Atk is 500 + 80 = 580")
_assert(player["def"] == 350, "Def is 300 + 50 = 350")
_assert(player["boss_dmg_pct"] == 0, "Boss damage reverted back to 0")
_assert(player["crit_pct"] == 5, "Crit rate reverted back to baseline 5")
# 3. Unequip 百战名将
var unequip_sig := {"fired": false}
sys.title_stat_buffs_removed.connect(func(_t: String): unequip_sig["fired"] = true)
sys.unequip_title(player)
_assert(unequip_sig["fired"] == true, "title_stat_buffs_removed emitted")
_assert(player["max_hp"] == 10000, "HP returned to exact initial 10000")
_assert(player["atk"] == 500, "Atk returned to exact initial 500")
_assert(player["def"] == 300, "Def returned to exact initial 300")
func _test_trophy_milestone_progression() -> void:
print("\n--- Test 4: Trophy Milestones & Cumulative Passives ---")
var sys := TitleTrophyBuffSystem.new()
var milestone_events := []
sys.trophy_milestone_reached.connect(func(tier: int, tname: String, _desc: String):
milestone_events.append({"tier": tier, "name": tname})
)
# Unlock 1 title (50 pts) -> Reaches Tier 1 (50 pts)
sys.unlock_title("屠龙尊者", 50)
_assert(milestone_events.size() == 1 and milestone_events[0]["tier"] == 1, "Reached Tier 1 (青铜试炼奖杯)")
# Unlock 2 more titles (+100 pts -> 150 pts total) -> Reaches Tier 2 (150 pts)
sys.unlock_title("恶魔克星", 50)
sys.unlock_title("魔石终结者", 50)
_assert(milestone_events.size() == 2 and milestone_events[1]["tier"] == 2, "Reached Tier 2 (白银征服奖杯)")
var passives = sys.get_total_trophy_passive_bonuses()
_assert(passives.has("all_stats") and passives["all_stats"] == 3, "Has Tier 1 passive: all_stats +3")
_assert(passives.has("max_hp") and passives["max_hp"] == 1000, "Has Tier 2 passive: max_hp +1000")
_assert(passives.has("drop_rate_pct") and passives["drop_rate_pct"] == 5, "Has Tier 2 passive: drop_rate +5%")
func _finish() -> void:
print("\n==========================================")
print("Title Trophy Buff 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 Title Trophy Buff parity tests passed!")
quit(0)