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

123 lines
4.8 KiB
GDScript

# test_pk_penalty_parity.gd —— 40250 恶名死亡惩罚与露西之戒保护 1:1 纯净回归测试套件
extends SceneTree
const PkPenaltySystem = preload("res://pk_penalty_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_pk_penalty_parity (Direction 3: PK Penalty & Lucy Ring 1:1) ===")
_test_safe_conditions_no_drop()
_test_lucy_ring_protection()
_test_negative_karma_drop_and_item_lock()
_test_zen_bean_and_cloak()
_finish()
func _test_safe_conditions_no_drop() -> void:
print("\n--- Test 1: Safe Conditions (Level < 50 or Positive Karma) ---")
var pks := PkPenaltySystem.new()
var eq: Dictionary = {"weapon": {"vnum": 169, "name": "战神之剑+9"}}
var inv: Array = [{"vnum": 10, "name": "木剑"}]
# Level < 50 with negative karma
var r1 = pks.process_death_penalty(45, -5000, eq, inv, 0.01)
_assert(r1["dropped"] == false, "No drop when player level < 50")
# Positive karma at Level 75
var r2 = pks.process_death_penalty(75, 8000, eq, inv, 0.01)
_assert(r2["dropped"] == false, "No drop when alignment >= 0 (Noble)")
func _test_lucy_ring_protection() -> void:
print("\n--- Test 2: Lucy's Ring (70043) Shatter Protection ---")
var pks := PkPenaltySystem.new()
var eq: Dictionary = {
"weapon": {"vnum": 169, "name": "战神之剑+9"},
"ring1": {"vnum": PkPenaltySystem.ITEM_LUCY_RING, "name": "露西之戒"}
}
var inv: Array = [{"vnum": 11299, "name": "黑钢甲+9"}]
var ring_signal := {"fired": false}
pks.lucy_ring_protected.connect(func(): ring_signal["fired"] = true)
# Level 75, Cruel (-15,000 karma)
var res = pks.process_death_penalty(75, -15000, eq, inv, 0.01)
_assert(res["dropped"] == false, "No items dropped")
_assert(res["ring_shattered"] == true, "Lucy's Ring shattered")
_assert(eq["ring1"] == null, "Lucy's Ring consumed from equipment slot")
_assert(eq["weapon"] != null, "Valuable weapon preserved")
_assert(inv[0] != null, "Valuable armor in inventory preserved")
_assert(ring_signal["fired"] == true, "lucy_ring_protected signal fired")
func _test_negative_karma_drop_and_item_lock() -> void:
print("\n--- Test 3: Drop Mechanics & Item Lock Immunity ---")
var pks := PkPenaltySystem.new()
var eq: Dictionary = {
"weapon": {"vnum": 169, "name": "战神之剑+9", "is_locked": true} # Locked!
}
var inv: Array = [
{"vnum": 11299, "name": "黑钢甲+9", "is_locked": true}, # Locked!
{"vnum": 50054, "name": "胡萝卜", "is_locked": false} # Unlocked, can drop!
]
var drop_signal := {"fired": false, "name": ""}
pks.item_dropped_on_death.connect(func(it: Dictionary):
drop_signal["fired"] = true
drop_signal["name"] = it.get("name", "")
)
# Level 75, Malicious (-5000 karma, 10% chance). Forced roll 0.05 <= 0.10
var res = pks.process_death_penalty(75, -5000, eq, inv, 0.05)
_assert(res["dropped"] == true, "Death drop triggered")
_assert(res["dropped_item"]["vnum"] == 50054, "Unlocked carrot dropped")
_assert(inv[1] == null, "Carrot removed from inventory")
_assert(eq["weapon"] != null, "Locked weapon was NOT dropped")
_assert(inv[0] != null, "Locked armor was NOT dropped")
_assert(drop_signal["fired"] == true and drop_signal["name"] == "胡萝卜", "item_dropped_on_death signal fired")
func _test_zen_bean_and_cloak() -> void:
print("\n--- Test 4: Zen Bean (70102) & Peace Cloak (70048) ---")
var pks := PkPenaltySystem.new()
var inv: Array = [
{"vnum": PkPenaltySystem.ITEM_ZEN_BEAN, "name": "禅豆", "count": 2}
]
# 1. Use on positive karma rejected
var r_bad = pks.use_zen_bean(0, inv, 1000)
_assert(r_bad["ok"] == false and r_bad["reason"] == "NOT_EVIL", "Cannot use Zen Bean if alignment >= 0")
# 2. Use on -1200 karma -> -700 karma
var r1 = pks.use_zen_bean(0, inv, -1200)
_assert(r1["ok"] == true, "Zen bean consumed")
_assert(r1["new_alignment"] == -700, "Alignment restored by +500 to -700")
_assert(inv[0]["count"] == 1, "1 Zen bean remaining in stack")
# 3. Use on -200 karma -> capped at 0
var r2 = pks.use_zen_bean(0, inv, -200)
_assert(r2["ok"] == true and r2["new_alignment"] == 0, "Alignment capped at 0 (neutral)")
_assert(inv[0] == null, "Stack consumed")
# 4. Peace Cloak hides red name
var eq_with_cloak := {"body": null, "cloak": {"vnum": PkPenaltySystem.ITEM_CLOAK_OF_PEACE}}
_assert(pks.is_red_name_hidden(eq_with_cloak) == true, "Cloak of Peace hides red name")
var eq_no_cloak := {"body": null}
_assert(pks.is_red_name_hidden(eq_no_cloak) == false, "No cloak reveals red name")
func _finish() -> void:
print("\n==========================================")
print("PK Penalty Parity Test Results: %d Passed, %d Failed" % [_passed, _failed])
print("==========================================")
if _failed > 0:
quit(1)
else:
quit(0)