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

159 lines
4.6 KiB
GDScript

# pk_penalty_system.gd —— Metin2 40250 恶名红名掉落惩罚与露西之戒保护 1:1
# 对照 40250 服务端 char_battle.cpp, char.cpp, Item 70043, 70102, 70048
class_name PkPenaltySystem
extends RefCounted
signal lucy_ring_protected()
signal item_dropped_on_death(item: Dictionary)
signal alignment_restored(new_alignment: int)
const MIN_PENALTY_LEVEL := 50
const ITEM_LUCY_RING := 70043 # 露西之戒
const ITEM_ZEN_BEAN := 70102 # 禅豆 (+500 善恶值洗恶名)
const ITEM_CLOAK_OF_PEACE := 70048 # 和平披风 (隐匿红名)
# 负善恶值掉落几率表 (40250 char_battle.cpp 1:1)
static func get_death_drop_chance(alignment: int) -> float:
if alignment >= 0:
return 0.0
elif alignment >= -3999: # 凶恶
return 0.05
elif alignment >= -7999: # 歹毒
return 0.10
elif alignment >= -11999: # 恶贯满盈
return 0.15
else: # <= -12000 嗜血魔王
return 0.25
# 死亡掉落综合判定
func process_death_penalty(
player_level: int,
alignment: int,
equipment: Dictionary,
inventory: Array,
forced_roll: float = -1.0
) -> Dictionary:
# 1. 50 级以下或非红名(>=0) 免除一切死亡掉落
if player_level < MIN_PENALTY_LEVEL or alignment >= 0:
return {
"dropped": false,
"reason": "SAFE_LEVEL_OR_POSITIVE_KARMA",
"msg": "受新手保护或和平声望庇佑,免于死亡惩罚。"
}
# 2. 检查是否佩戴露西之戒 (70043)
var has_lucy_ring := false
var ring_slot_key = ""
for eq_key in equipment.keys():
var eq = equipment[eq_key]
if eq != null and int(eq.get("vnum", 0)) == ITEM_LUCY_RING:
has_lucy_ring = true
ring_slot_key = eq_key
break
if has_lucy_ring:
# 露西之戒替死碎裂,阻断掉落
equipment[ring_slot_key] = null
lucy_ring_protected.emit()
return {
"dropped": false,
"ring_shattered": true,
"msg": "【露西之戒】替你承受了死亡的厄运,戒指粉碎瓦解,身上物品完好无损!"
}
# 3. 计算掉落几率
var chance := get_death_drop_chance(alignment)
var roll := forced_roll if forced_roll >= 0.0 else randf()
if roll > chance:
return {
"dropped": false,
"reason": "LUCKY_ROLL",
"msg": "幸运未触发掉落惩罚。"
}
# 4. 寻找可掉落的物品 (已锁定的物品 is_locked 严禁掉落)
var droppable_candidates: Array = []
# 从未锁定装备中搜寻
for eq_key in equipment.keys():
var it = equipment[eq_key]
if it != null and not it.get("is_locked", false):
droppable_candidates.append({"type": "equip", "key": eq_key, "item": it})
# 从未锁定背包中搜寻
for i in range(inventory.size()):
var it = inventory[i]
if it != null and not it.get("is_locked", false):
droppable_candidates.append({"type": "inv", "slot": i, "item": it})
if droppable_candidates.is_empty():
return {
"dropped": false,
"reason": "ALL_ITEMS_PROTECTED",
"msg": "身上的所有贵重物品均处于安全加锁保护中,未发生掉落!"
}
# 掉落一件物品
var chosen = droppable_candidates[randi() % droppable_candidates.size()]
var dropped_item: Dictionary = chosen["item"].duplicate(true)
if chosen["type"] == "equip":
equipment[chosen["key"]] = null
else:
inventory[chosen["slot"]] = null
item_dropped_on_death.emit(dropped_item)
return {
"dropped": true,
"dropped_item": dropped_item,
"msg": "恶名昭彰!你被击杀,珍贵物品【%s】散落到了地面!" % dropped_item.get("name", "物品")
}
# 使用禅豆 (70102) 消除 500 点恶名
func use_zen_bean(
slot_idx: int,
inventory: Array,
current_alignment: int
) -> Dictionary:
if slot_idx < 0 or slot_idx >= inventory.size():
return {"ok": false, "reason": "INVALID_SLOT"}
var it = inventory[slot_idx]
if it == null or int(it.get("vnum", 0)) != ITEM_ZEN_BEAN:
return {"ok": false, "reason": "NOT_ZEN_BEAN", "msg": "这不是禅豆!"}
if current_alignment >= 0:
return {
"ok": false,
"reason": "NOT_EVIL",
"msg": "你的名声本就光明清白,无需服用禅豆洗去恶名。"
}
# 消耗禅豆
var cnt: int = int(it.get("count", 1))
if cnt <= 1:
inventory[slot_idx] = null
else:
it["count"] = cnt - 1
# 回复 500 点善恶值 (上限为 0)
var new_alignment: int = mini(0, current_alignment + 500)
alignment_restored.emit(new_alignment)
return {
"ok": true,
"old_alignment": current_alignment,
"new_alignment": new_alignment,
"restored": 500,
"msg": "服用了禅豆,心灵得到了净化,恶名值减少了 500 点!"
}
# 判定和平披风 (70048) 是否隐匿红名
func is_red_name_hidden(equipment: Dictionary) -> bool:
for eq in equipment.values():
if eq != null and int(eq.get("vnum", 0)) == ITEM_CLOAK_OF_PEACE:
return true
return false