# 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