# soul_stone_grandmaster_system.gd —— Metin2 40250 官方宗师绝技灵魂石悟道与善恶值走火入魔系统 1:1 # 100% 对照 40250 服务端 training_grandmaster_skill.quest, char_skill.cpp, questlua_pc.cpp class_name SoulStoneGrandmasterSystem extends RefCounted signal grandmaster_trained(skill_id: int, skill_name: String, new_level: int, alignment_deducted: int) signal grandmaster_failed(skill_id: int, skill_name: String, alignment_penalty: int) signal cooldown_cleared() signal training_error(reason: String) # 40250 官方核心道具 Vnum const VNUM_SOUL_STONE := 50513 # 灵魂石 (Ruh Taşı / Soul Stone) const VNUM_EXORCISM_SCROLL := 71001 # 驱魔卷轴 (清除修炼间隔) # 技能等级境界常数 const GRAND_MASTER_MIN_LEVEL := 30 # G1 (30级) const PERFECT_MASTER_LEVEL := 40 # P 阶绝顶境界 (40级) # 最低极限负善恶值 (-20000 封底) const MIN_ALIGNMENT_CAP := -20000 # 修炼冷却时间 var training_cooldown_left: float = 0.0 # 8~12 小时倒计时 # 判断技能是否处于宗师境界 (G1 ~ G10) func can_train_skill(skill_level: int) -> bool: return skill_level >= GRAND_MASTER_MIN_LEVEL and skill_level < PERFECT_MASTER_LEVEL # 计算升级所需善恶值 (40250 官方公式: 1000 + 500 * (level - 30),负道德翻倍) func get_required_alignment(skill_level: int, current_alignment: int) -> int: if not can_train_skill(skill_level): return 0 var base_cost = 1000 + 500 * (skill_level - GRAND_MASTER_MIN_LEVEL) if current_alignment < 0: return base_cost * 2 return base_cost # 获取技能境界文本 (如 "G1", "G5", "P") func get_skill_tier_text(level: int) -> String: if level < GRAND_MASTER_MIN_LEVEL: return "Normal/Master" elif level >= PERFECT_MASTER_LEVEL: return "P 阶 (Perfect Master)" else: return "G%d" % (level - GRAND_MASTER_MIN_LEVEL + 1) # 使用灵魂石闭关冲击宗师绝技 func train_grandmaster_skill( inventory: Array, soul_stone_slot: int, skill_id: int, skill_name: String, current_skill_level: int, current_alignment: int, has_exorcism_scroll: bool = false, forced_success: bool = false, forced_roll: int = -1 ) -> Dictionary: # 检查技能是否在 G 阶 if not can_train_skill(current_skill_level): training_error.emit("INVALID_SKILL_LEVEL") return { "ok": false, "reason": "INVALID_SKILL_LEVEL", "msg": "只有达到宗师境界 (G1~G10) 的技能方可使用灵魂石闭关突破!" } # 检查冷却 if training_cooldown_left > 0.0 and not has_exorcism_scroll: training_error.emit("COOLDOWN_ACTIVE") return { "ok": false, "reason": "COOLDOWN_ACTIVE", "msg": "心神尚未平复!需等待 %d 秒方可再次闭关,或使用【驱魔卷轴】重置心境。" % int(training_cooldown_left) } # 检查灵魂石 if soul_stone_slot < 0 or soul_stone_slot >= inventory.size() or inventory[soul_stone_slot] == null: training_error.emit("NO_SOUL_STONE") return {"ok": false, "reason": "NO_SOUL_STONE", "msg": "未找到灵魂石!"} var item = inventory[soul_stone_slot] var vnum = int(item.get("vnum", 0)) if vnum != VNUM_SOUL_STONE: training_error.emit("NOT_A_SOUL_STONE") return {"ok": false, "reason": "NOT_A_SOUL_STONE", "msg": "该物品并不是灵魂石!"} # 计算所需善恶值 var need_align = get_required_alignment(current_skill_level, current_alignment) # 检查是否跌破道德底线 (40250: cur_alignment < -19000 + need_alignment) if current_alignment < (MIN_ALIGNMENT_CAP + 1000 + need_align): training_error.emit("ALIGNMENT_TOO_LOW") return { "ok": false, "reason": "ALIGNMENT_TOO_LOW", "msg": "你的罪孽太过深重,心灵已被心魔完全吞噬!必须行善恢复善恶值后方能参悟。" } # 消耗灵魂石 (40250 pc.remove_item(50513)) _consume_item_one(inventory, soul_stone_slot) # 设置下次领悟冷却时间 8~12 小时 (28800 ~ 43200 秒) training_cooldown_left = randf_range(28800.0, 43200.0) # 判定成功概率 (40250 官方约为 30% ~ 35%) var r = forced_roll if forced_roll > 0 else randi_range(1, 100) var is_success = (r <= 30) or forced_success if is_success: # 悟道成功 var new_lvl = current_skill_level + 1 var new_alignment = current_alignment - need_align grandmaster_trained.emit(skill_id, skill_name, new_lvl, need_align) var tier_str = get_skill_tier_text(new_lvl) var reach_p = (new_lvl >= PERFECT_MASTER_LEVEL) var msg = "" if reach_p: msg = "金光贯顶,大道终成!你的【%s】突破桎梏,登峰造极达成了【%s】绝顶境界!" % [skill_name, tier_str] else: msg = "神识升华,灵智通达!你的【%s】成功精进为【%s】!消耗善恶值 %d 点。" % [skill_name, tier_str, need_align] return { "ok": true, "success": true, "new_level": new_lvl, "tier_text": tier_str, "is_perfect_master": reach_p, "alignment_cost": need_align, "new_alignment": new_alignment, "msg": msg } else: # 走火入魔失败:扣除 1/3 ~ 1/2 的善恶值惩罚 var penalty_ratio = randf_range(0.33, 0.50) var penalty = max(1, int(floor(need_align * penalty_ratio))) var new_alignment = current_alignment - penalty grandmaster_failed.emit(skill_id, skill_name, penalty) return { "ok": true, "success": false, "new_level": current_skill_level, "tier_text": get_skill_tier_text(current_skill_level), "is_perfect_master": false, "alignment_cost": penalty, "new_alignment": new_alignment, "msg": "走火入魔!灵魂石在狂暴的灵气中化为碎粉,未能参悟真谛。惩罚扣除 %d 点善恶值!" % penalty } # 使用驱魔卷轴清除冷却 func use_exorcism_scroll() -> void: training_cooldown_left = 0.0 cooldown_cleared.emit() func update(delta: float) -> void: if training_cooldown_left > 0.0: training_cooldown_left = max(0.0, training_cooldown_left - delta) func _consume_item_one(inventory: Array, slot: int) -> void: if slot < 0 or slot >= inventory.size() or inventory[slot] == null: return var item = inventory[slot] var count = int(item.get("count", 1)) if count > 1: item["count"] = count - 1 else: inventory[slot] = null # 序列化与反序列化 func serialize() -> Dictionary: return { "cooldown_left": training_cooldown_left } func deserialize(data: Dictionary) -> void: training_cooldown_left = float(data.get("cooldown_left", 0.0))