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

150 lines
6.4 KiB
GDScript

# test_alignment_pk_parity.gd —— 40250 善恶值与名望头衔奖惩系统 1:1 回归测试套件
extends SceneTree
const AlignmentPkSystem = preload("res://alignment_pk_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_alignment_pk_parity (Direction 3: Alignment PK & Titles 1:1) ===")
_test_title_tiers_and_thresholds()
_test_zen_bean_clearing()
_test_safe_zone_time_recovery()
_test_red_name_death_drop_penalty()
_test_serialization()
_finish()
func _test_title_tiers_and_thresholds() -> void:
print("\n--- Test 1: Title Tiers & Alignment Thresholds (40250 1:1) ---")
var sys := AlignmentPkSystem.new()
_assert(AlignmentPkSystem.get_title_name(15000) == "崇高骑士", ">= 12000 is Chivalric (崇高骑士)")
_assert(AlignmentPkSystem.get_title_name(9000) == "尊贵贵族", ">= 8000 is Noble (尊贵贵族)")
_assert(AlignmentPkSystem.get_title_name(5000) == "正义良民", ">= 4000 is Good (正义良民)")
_assert(AlignmentPkSystem.get_title_name(1500) == "友好伙伴", ">= 1000 is Friendly (友好伙伴)")
_assert(AlignmentPkSystem.get_title_name(500) == "中立行者", "0 ~ 999 is Neutral (中立行者)")
_assert(AlignmentPkSystem.get_title_name(-1000) == "好斗狂徒", "-1 ~ -3999 is Aggressive (好斗狂徒)")
_assert(AlignmentPkSystem.get_title_name(-5000) == "狡诈恶徒", "-4000 ~ -7999 is Fraudulent (狡诈恶徒)")
_assert(AlignmentPkSystem.get_title_name(-9000) == "险恶凶手", "-8000 ~ -11999 is Malicious (险恶凶手)")
_assert(AlignmentPkSystem.get_title_name(-15000)== "残忍魔头", "<= -12000 is Cruel (残忍魔头)")
# Test clamping bounds (-200000 to +200000 real alignment)
sys.update_real_alignment(500000)
_assert(sys.real_alignment == 200000, "Clamped at MAX_REAL_ALIGNMENT (+200000)")
_assert(sys.get_alignment() == 20000, "Display alignment clamped at +20000")
sys.update_real_alignment(-900000)
_assert(sys.real_alignment == -200000, "Clamped at MIN_REAL_ALIGNMENT (-200000)")
_assert(sys.get_alignment() == -20000, "Display alignment clamped at -20000")
func _test_zen_bean_clearing() -> void:
print("\n--- Test 2: Zen Bean (70102) Sin Clearing ---")
var sys := AlignmentPkSystem.new()
var inv: Array = [
{"vnum": AlignmentPkSystem.VNUM_ZEN_BEAN, "name": "禅豆", "count": 3}
]
# Set alignment to positive
sys.real_alignment = 5000 # +500
var res_pos = sys.use_zen_bean(inv, 0)
_assert(res_pos["ok"] == false, "Zen bean rejected when alignment is positive")
_assert(res_pos["reason"] == "ALIGNMENT_ALREADY_GOOD", "Reason is ALREADY_GOOD")
_assert(inv[0]["count"] == 3, "Zen bean not consumed")
# Set alignment to -1200 (-12000 real)
sys.real_alignment = -12000
var bean_sig := {"fired": false, "cleared": 0, "align": 0}
sys.zen_bean_consumed.connect(func(c: int, a: int):
bean_sig["fired"] = true
bean_sig["cleared"] = c
bean_sig["align"] = a
)
var res_neg = sys.use_zen_bean(inv, 0)
_assert(res_neg["ok"] == true, "Zen bean successfully used")
_assert(res_neg["cleared"] == 500, "Cleared 500 display points")
_assert(sys.get_alignment() == -700, "New alignment is -700 (-1200 + 500)")
_assert(inv[0]["count"] == 2, "Zen bean count decremented to 2")
_assert(bean_sig["fired"] == true, "zen_bean_consumed signal emitted")
# Test capping clear to 0 when remaining evil < 500 (e.g. -200)
sys.real_alignment = -2000 # -200 display
inv[0]["count"] = 1
var res_cap = sys.use_zen_bean(inv, 0)
_assert(res_cap["ok"] == true, "Zen bean used")
_assert(res_cap["cleared"] == 200, "Only cleared remaining 200 points")
_assert(sys.get_alignment() == 0, "Alignment restored to 0 (Neutral)")
_assert(inv[0] == null, "Last Zen bean consumed completely")
func _test_safe_zone_time_recovery() -> void:
print("\n--- Test 3: Safe Zone Time-Based Recovery (40250 char.cpp) ---")
var sys := AlignmentPkSystem.new()
# Evil player: recovers 60 pts/min without item
sys.real_alignment = -10000 # -1000 display
sys.recover_alignment_by_time(5, false) # +300 pts
_assert(sys.get_alignment() == -700, "Recovered 300 points over 5 minutes (60/min)")
# Evil player: recovers 120 pts/min with item
sys.recover_alignment_by_time(5, true) # +600 pts
_assert(sys.get_alignment() == -100, "Recovered 600 points over 5 minutes with item (120/min)")
# Good player: recovers 5 pts/min
sys.real_alignment = 10000 # +1000 display
sys.recover_alignment_by_time(10, false) # +50 pts
_assert(sys.get_alignment() == 1050, "Recovered 50 points over 10 minutes (5/min)")
func _test_red_name_death_drop_penalty() -> void:
print("\n--- Test 4: Red Name Death Drop Penalty & Soulbound Exemption ---")
var sys := AlignmentPkSystem.new()
var inv: Array = [
{"vnum": 189, "name": "毒剑+9", "is_soulbound": false},
{"vnum": 11299, "name": "黑铁甲+9", "is_soulbound": true}, # Soulbound protected!
{"vnum": 50300, "name": "技能书", "is_soulbound": false}
]
# 1. White name (Alignment >= 0): 0 drops
sys.real_alignment = 50000
var drops_good = sys.handle_death_drop_penalty(inv)
_assert(drops_good.is_empty(), "Good player never drops items on death")
_assert(inv[0] != null and inv[1] != null and inv[2] != null, "Inventory remains intact")
# 2. Cruel Red name (Alignment <= -12000): guaranteed drop with 80% chance
sys.real_alignment = -150000 # -15000 (Cruel)
# Seed random for deterministic test
seed(42)
var drops_evil = sys.handle_death_drop_penalty(inv)
_assert(not drops_evil.is_empty(), "Cruel red name dropped items on death")
_assert(inv[1] != null, "Soulbound Black Steel Armor was NOT dropped (immune)")
func _test_serialization() -> void:
print("\n--- Test 5: Serialization & Restoration ---")
var sys1 := AlignmentPkSystem.new()
sys1.real_alignment = -85400 # -8540 display (Malicious)
var data = sys1.serialize()
var sys2 := AlignmentPkSystem.new()
sys2.deserialize(data)
_assert(sys2.real_alignment == -85400, "Restored real alignment")
_assert(sys2.get_alignment() == -8540, "Restored display alignment")
_assert(sys2.get_title_name(sys2.get_alignment()) == "险恶凶手", "Restored Malicious title")
func _finish() -> void:
print("\n=======================================================")
print("Alignment PK System Parity Tests: %d Passed, %d Failed" % [_passed, _failed])
print("=======================================================\n")
if _failed > 0:
quit(1)
else:
quit(0)