# alignment_pk_system.gd —— Metin2 40250 善恶值与名望头衔奖惩系统 1:1 # 对照 40250 服务端 char.cpp:1190-1205, char_battle.cpp:UpdateAlignment, root/localeinfo.py:GetAlignmentTitleName class_name AlignmentPkSystem extends RefCounted signal alignment_changed(old_val: int, new_val: int, title: String) signal title_changed(old_title: String, new_title: String) signal items_dropped_on_death(dropped_items: Array) signal zen_bean_consumed(cleared_amount: int, current_alignment: int) # 40250 官方善恶值极值 (内部数值 -200000 ~ +200000, 显示数值 -20000 ~ +20000) const MIN_REAL_ALIGNMENT := -200000 const MAX_REAL_ALIGNMENT := 200000 const VNUM_ZEN_BEAN := 70102 # 禅豆 (消减罪孽值) const ZEN_BEAN_CLEAR_POINTS := 500 # 每次消除 500 显示点数 (5000 内部点数) # 9 大官方经典头衔段位表 (40250 localeinfo.py / locale_game.txt) const TITLES: Array = [ {"threshold": 12000, "name": "崇高骑士", "en_name": "Chivalric", "is_evil": false}, {"threshold": 8000, "name": "尊贵贵族", "en_name": "Noble", "is_evil": false}, {"threshold": 4000, "name": "正义良民", "en_name": "Good", "is_evil": false}, {"threshold": 1000, "name": "友好伙伴", "en_name": "Friendly", "is_evil": false}, {"threshold": 0, "name": "中立行者", "en_name": "Neutral", "is_evil": false}, {"threshold": -3999, "name": "好斗狂徒", "en_name": "Aggressive","is_evil": true}, {"threshold": -7999, "name": "狡诈恶徒", "en_name": "Fraudulent","is_evil": true}, {"threshold": -11999,"name": "险恶凶手", "en_name": "Malicious", "is_evil": true}, {"threshold": -20000,"name": "残忍魔头", "en_name": "Cruel", "is_evil": true} ] var real_alignment: int = 0 # 内部实际值 (-200000 ~ +200000) # 获取玩家当前对外显示的善恶值 (40250: sAlignment = m_iAlignment / 10) func get_alignment() -> int: return int(real_alignment / 10) # 根据善恶值获取官方 1:1 头衔名称 (40250 localeinfo.py:GetAlignmentTitleName) static func get_title_name(alignment: int) -> String: if alignment >= 12000: return "崇高骑士" elif alignment >= 8000: return "尊贵贵族" elif alignment >= 4000: return "正义良民" elif alignment >= 1000: return "友好伙伴" elif alignment >= 0: return "中立行者" elif alignment > -4000: return "好斗狂徒" elif alignment > -8000: return "狡诈恶徒" elif alignment > -12000: return "险恶凶手" else: return "残忍魔头" # 检查当前是否为红名/恶名 func is_evil() -> bool: return get_alignment() < 0 # 更新善恶值 (40250 char_battle.cpp:UpdateAlignment) # amount 为内部值加减 (如传入 100 表示内部 +100) func update_real_alignment(amount: int) -> void: var old_display = get_alignment() var old_title = get_title_name(old_display) real_alignment = clampi(real_alignment + amount, MIN_REAL_ALIGNMENT, MAX_REAL_ALIGNMENT) var new_display = get_alignment() var new_title = get_title_name(new_display) if old_display != new_display: alignment_changed.emit(old_display, new_display, new_title) if old_title != new_title: title_changed.emit(old_title, new_title) # 按显示点数直接调整善恶值 (如传入 +500 相当于内部 +5000) func update_alignment_points(points: int) -> void: update_real_alignment(points * 10) # 在线时间自然恢复善恶值 (40250 char.cpp:1194-1203) # minutes: 挂机分钟数 func recover_alignment_by_time(minutes: int, has_fast_recovery_item: bool = false) -> void: if minutes <= 0: return if real_alignment < 0: var rate_per_min = 120 if has_fast_recovery_item else 60 update_alignment_points(rate_per_min * minutes) else: var rate_per_min = 5 update_alignment_points(rate_per_min * minutes) # 使用禅豆消减罪孽 (40250 Zen Bean 70102) func use_zen_bean(inventory: Array, slot_index: int) -> Dictionary: if slot_index < 0 or slot_index >= inventory.size() or inventory[slot_index] == null: return {"ok": false, "reason": "INVALID_SLOT"} var item: Dictionary = inventory[slot_index] if int(item.get("vnum", 0)) != VNUM_ZEN_BEAN: return {"ok": false, "reason": "NOT_A_ZEN_BEAN", "msg": "此物品不是禅豆!"} var cur_align := get_alignment() if cur_align >= 0: return {"ok": false, "reason": "ALIGNMENT_ALREADY_GOOD", "msg": "当前心境平和,无需使用禅豆消业!"} # 消耗 1 颗禅豆 var cnt := int(item.get("count", 1)) if cnt > 1: item["count"] = cnt - 1 else: inventory[slot_index] = null # 恢复 500 点善恶值 (若剩余负值不足 500 则刚好回满到 0) var clear_amount: int = mini(ZEN_BEAN_CLEAR_POINTS, -cur_align) update_alignment_points(clear_amount) zen_bean_consumed.emit(clear_amount, get_alignment()) return { "ok": true, "cleared": clear_amount, "current_alignment": get_alignment(), "title": get_title_name(get_alignment()), "msg": "服用禅豆,消除 %d 点罪业!当前善恶值:%d (%s)" % [clear_amount, get_alignment(), get_title_name(get_alignment())] } # 击杀和平角色 (恶名惩罚) func record_pk_kill() -> void: update_alignment_points(-2000) # 击破魔石或野外怪物 (正向善名积累) func record_monster_kill(is_metin: bool = false) -> void: var bonus = 50 if is_metin else 10 update_alignment_points(bonus) # 死亡惩罚计算 (40250 红名野外死亡爆装机制) func handle_death_drop_penalty(inventory: Array) -> Array: var cur_align := get_alignment() if cur_align >= 0: return [] # 白名/良民免死掉落 var drop_chance := 0.10 var max_drop_count := 1 if cur_align <= -12000: # 残忍魔头 drop_chance = 0.80 max_drop_count = 3 elif cur_align <= -8000: # 险恶凶手 drop_chance = 0.50 max_drop_count = 2 elif cur_align <= -4000: # 狡诈恶徒 drop_chance = 0.25 max_drop_count = 1 var dropped_items: Array = [] for i in range(inventory.size()): if inventory[i] == null: continue var item: Dictionary = inventory[i] # 灵魂绑定锁定的装备免于死亡掉落 if item.get("is_soulbound", false): continue # 判定概率爆出 if randf() <= drop_chance: dropped_items.append(item.duplicate(true)) inventory[i] = null if dropped_items.size() >= max_drop_count: break if not dropped_items.is_empty(): items_dropped_on_death.emit(dropped_items) return dropped_items # 序列化 func serialize() -> Dictionary: return { "real_alignment": real_alignment } # 反序列化 func deserialize(data: Dictionary) -> void: real_alignment = int(data.get("real_alignment", 0))