# class_specialization_system.gd —— Metin2 40250 角色 30 级双流派转职与无损洗髓系统 1:1 # 对照 40250 服务端 quest/skill_group.quest, char_skill.cpp, cmd_general.cpp class_name ClassSpecializationSystem extends RefCounted signal specialization_chosen(job: int, spec_id: int, spec_name: String) signal skill_point_allocated(skill_vnum: int, new_level: int, remaining_points: int) signal specialization_reset(refunded_points: int) const VNUM_RESET_SCROLL := 71100 # 洗髓卷轴 / 重新分配技能书 (Scroll of Redistribution) const REQUIRED_LEVEL := 30 # 转职等级门槛 const SPEC_TREE: Dictionary = { 0: { # 战士 1: { "name": "战狂流派 (Arahan)", "desc": "极度偏向狂暴输出与高速连斩,兼具霸体与破坏力", "skills": { 1: "三连斩", 2: "旋风斩", 3: "狂战士", 4: "剑气", 5: "冲锋" } }, 2: { "name": "圣战流派 (Partisan)", "desc": "坚若磐石的钢铁之躯,以强身术与精神打击粉碎强敌", "skills": { 16: "强身术", 17: "精神打击", 18: "宝剑斩", 19: "震地波", 20: "剑气斩" } } }, 1: { # 刺客 1: { "name": "近战刺客流派 (Dagger)", "desc": "隐身于阴影之中,擅长淬毒与致命近身背刺", "skills": { 31: "伏击", 32: "绝杀", 33: "匕首旋风", 34: "隐身术", 35: "毒刃" } }, 2: { "name": "远程弓箭流派 (Archer)", "desc": "千里之外取敌首级,漫天箭雨与火炎穿透射击", "skills": { 46: "连射", 47: "箭雨", 48: "火炎箭", 49: "疾风步", 50: "毒箭" } } }, 2: { # 修罗 1: { "name": "魔剑修罗流派 (Weapon)", "desc": "附魔之刃附体,吸血噬魂与破魔剑气并存", "skills": { 61: "附魔之刃", 62: "附魔之甲", 63: "惧意", 64: "龙卷风", 65: "手指戳击" } }, 2: { "name": "黑魔法修罗流派 (Black Magic)", "desc": "掌控幽冥暗焰,幽暗之盾抵消伤害,黑暗之球远程轰杀", "skills": { 76: "幽冥一击", 77: "烈焰一击", 78: "黑暗之球", 79: "幽暗之盾", 80: "鬼影步" } } }, 3: { # 萨满 1: { "name": "巨龙神圣辅助流派 (Dragon)", "desc": "掌控巨龙图腾之力,为团队提供巨龙之赐与龙之协助光环", "skills": { 91: "飞龙射击", 92: "巨龙射击", 93: "巨龙咆哮", 94: "巨龙之赐", 95: "龙之协助" } }, 2: { "name": "雷电神圣治疗流派 (Lightning)", "desc": "引动九天雷电轰杀群敌,施展神圣治愈术与疾风迅捷", "skills": { 106: "雷电箭", 107: "召唤雷电", 108: "雷电之爪", 109: "治愈术", 110: "迅捷步" } } } } # 获取职业可选流派 func get_available_specializations(job: int) -> Dictionary: return SPEC_TREE.get(job, {}) # 进行 30 级转职选定流派 func choose_specialization(spec_id: int, player_data: Dictionary) -> Dictionary: var job: int = int(player_data.get("job", 0)) var lvl: int = int(player_data.get("level", 1)) if lvl < REQUIRED_LEVEL: return {"ok": false, "reason": "LEVEL_TOO_LOW", "msg": "转职需要角色等级达到 30 级!当前等级: %d" % lvl} var cur_group: int = int(player_data.get("skill_group", 0)) if cur_group != 0: return {"ok": false, "reason": "ALREADY_SPECIALIZED", "msg": "你已选定了流派!若需变更,请先使用【洗髓卷轴】。"} if not SPEC_TREE.has(job) or not SPEC_TREE[job].has(spec_id): return {"ok": false, "reason": "INVALID_SPEC_ID", "msg": "无效的流派代码!"} var spec = SPEC_TREE[job][spec_id] player_data["skill_group"] = spec_id # 初始化该流派专属 5 大技能 if not player_data.has("skills"): player_data["skills"] = {} for svnum in spec["skills"].keys(): player_data["skills"][svnum] = { "name": spec["skills"][svnum], "level": 0, "master_type": 0 } specialization_chosen.emit(job, spec_id, spec["name"]) return { "ok": true, "job": job, "spec_id": spec_id, "spec_name": spec["name"], "skills_count": spec["skills"].size(), "msg": "恭喜!成功完成 30 级转职,踏入【%s】流派之道!" % spec["name"] } # 学习与加点技能 func allocate_skill_point(skill_vnum: int, player_data: Dictionary) -> Dictionary: var cur_group: int = int(player_data.get("skill_group", 0)) if cur_group == 0: return {"ok": false, "reason": "NOT_SPECIALIZED", "msg": "尚未进行 30 级转职,无法修习流派绝学!"} var pts: int = int(player_data.get("skill_points", 0)) if pts <= 0: return {"ok": false, "reason": "NO_SKILL_POINTS", "msg": "技能点数不足!"} var skills: Dictionary = player_data.get("skills", {}) if not skills.has(skill_vnum): return {"ok": false, "reason": "SKILL_NOT_AVAILABLE", "msg": "此技能不属于当前专精流派!"} var sk = skills[skill_vnum] var cur_lvl: int = int(sk.get("level", 0)) if cur_lvl >= 20: return {"ok": false, "reason": "MAX_NORMAL_LEVEL", "msg": "该技能基础等级已满 (20),进阶需使用技能秘籍!"} sk["level"] = cur_lvl + 1 player_data["skill_points"] = pts - 1 skill_point_allocated.emit(skill_vnum, sk["level"], player_data["skill_points"]) return { "ok": true, "skill_vnum": skill_vnum, "new_level": sk["level"], "remaining_points": player_data["skill_points"], "msg": "【%s】提升至等级 %d!" % [sk.get("name", "技能"), sk["level"]] } # 使用洗髓卷轴 (71100) 无损重置流派与返还技能点 func reset_specialization(inventory: Array, player_data: Dictionary, free_reset: bool = false) -> Dictionary: var cur_group: int = int(player_data.get("skill_group", 0)) if cur_group == 0: return {"ok": false, "reason": "NOT_SPECIALIZED", "msg": "当前尚未选定任何专精流派,无需洗髓!"} # 卷轴消耗检查 var scroll_slot = -1 if not free_reset: for i in range(inventory.size()): if inventory[i] != null and int(inventory[i].get("vnum", 0)) == VNUM_RESET_SCROLL: scroll_slot = i break if scroll_slot == -1: return {"ok": false, "reason": "NEED_RESET_SCROLL", "msg": "需要【洗髓卷轴】(71100) 才能重置专精流派!"} # 统计已投入的所有技能点数 var skills: Dictionary = player_data.get("skills", {}) var total_invested: int = 0 for svnum in skills.keys(): var sk = skills[svnum] total_invested += int(sk.get("level", 0)) # 消耗卷轴 if scroll_slot != -1: var sc_item = inventory[scroll_slot] var sc_cnt = int(sc_item.get("count", 1)) if sc_cnt > 1: sc_item["count"] = sc_cnt - 1 else: inventory[scroll_slot] = null # 全额返还技能点 var cur_pts: int = int(player_data.get("skill_points", 0)) player_data["skill_points"] = cur_pts + total_invested player_data["skill_group"] = 0 player_data["skills"] = {} specialization_reset.emit(total_invested) return { "ok": true, "refunded_points": total_invested, "total_skill_points": player_data["skill_points"], "msg": "洗髓成功!经脉重铸,%d 点技能点数全额返还,可重新选定双分支流派!" % total_invested }