# polymorph_system.gd —— Metin2 40250 怪物变身球与变身术技能系统 1:1 # 对照 40250 服务端 polymorph.cpp, char_item.cpp (Item 70104, Skill 129 Polymorph) class_name PolymorphSystem extends RefCounted signal polymorph_started(mob_vnum: int, duration: float) signal polymorph_ended() const ITEM_POLYMORPH_MARBLE := 70104 const SKILL_POLYMORPH := 129 const AFFECT_POLYMORPH := 220 # 经典变身怪物属性表 (mob_vnum -> 属性加成与基础设定) const MOB_DATA: Dictionary = { 101: {"name": "野猪", "atk": 45, "def": 20, "speed": 120}, 102: {"name": "野狼", "atk": 35, "def": 15, "speed": 135}, 110: {"name": "黑熊", "atk": 60, "def": 35, "speed": 110}, 114: {"name": "白虎", "atk": 85, "def": 40, "speed": 140}, 501: {"name": "流浪汉", "atk": 70, "def": 30, "speed": 125} } var is_polymorphed: bool = false var current_mob_vnum: int = 0 var remaining_time: float = 0.0 var original_race: int = 0 # 0: 战男, 1: 忍女, 2: 修男, 3: 萨女等 var poly_skill_level: int = 1 # 1 ~ 40 (1~19 普通, 20~29 M1~M10, 30~39 G1~G10, 40 P) # 变身属性收益缓存 var bonus_atk: int = 0 var bonus_def: int = 0 var bonus_speed: int = 0 # 计算变身持续时间 (秒) static func get_duration_by_skill(level: int) -> float: if level >= 40: # P 级 return 2100.0 # 35 分钟 elif level >= 30: # G 级 return 1500.0 # 25 分钟 elif level >= 20: # M 级 return 1200.0 # 20 分钟 else: return 600.0 + (level * 20.0) # 10~16 分钟 func can_polymorph(player_dead: bool, player_mounted: bool) -> Dictionary: if player_dead: return {"ok": false, "reason": "PLAYER_DEAD", "msg": "死亡状态下无法变身!"} if player_mounted: return {"ok": false, "reason": "PLAYER_MOUNTED", "msg": "骑马状态下无法变身,请先下马!"} return {"ok": true} # 使用变身球 (Item 70104) 变身 func use_polymorph_marble(item: Dictionary, player_dead: bool, player_mounted: bool, p_race: int = 0) -> Dictionary: var check = can_polymorph(player_dead, player_mounted) if not check["ok"]: return check var vnum: int = int(item.get("vnum", 0)) if vnum != ITEM_POLYMORPH_MARBLE: return {"ok": false, "reason": "NOT_A_MARBLE", "msg": "这不是变身球!"} var mob_vnum: int = int(item.get("mob_vnum", 0)) if mob_vnum == 0: # 从 socket[0] 读 var sockets: Array = item.get("sockets", []) if sockets.size() > 0: mob_vnum = int(sockets[0]) if mob_vnum == 0 or not MOB_DATA.has(mob_vnum): # 默认野猪 101 mob_vnum = 101 original_race = p_race current_mob_vnum = mob_vnum is_polymorphed = true remaining_time = get_duration_by_skill(poly_skill_level) # 结算属性加成 (char_item.cpp) var mdata: Dictionary = MOB_DATA[mob_vnum] var skill_multiplier: float = 1.0 + (float(poly_skill_level) * 0.025) bonus_atk = int(round(float(mdata["atk"]) * skill_multiplier)) bonus_def = int(mdata["def"]) bonus_speed = int(mdata["speed"]) polymorph_started.emit(current_mob_vnum, remaining_time) return { "ok": true, "mob_vnum": current_mob_vnum, "mob_name": mdata["name"], "duration": remaining_time, "bonus_atk": bonus_atk, "bonus_def": bonus_def, "bonus_speed": bonus_speed } # 变身状态限制检查 1:1 func can_use_skill(skill_id: int) -> bool: # 变身中禁止施放人物主动职业技能 if is_polymorphed: # 变身术本身、被动或马术/公共技能除外,职业技能一律拦截 if skill_id != SKILL_POLYMORPH and skill_id < 100: return false return true func can_change_weapon() -> bool: # 变身中禁止切换武器 (equip_rules.gd:316) return not is_polymorphed func can_mount() -> bool: # 变身中禁止骑马 return not is_polymorphed # 逐帧更新倒计时 func update(dt: float) -> void: if not is_polymorphed: return remaining_time -= dt if remaining_time <= 0.0: cancel_polymorph() # 取消/解除变身 func cancel_polymorph() -> void: if not is_polymorphed: return is_polymorphed = false current_mob_vnum = 0 remaining_time = 0.0 bonus_atk = 0 bonus_def = 0 bonus_speed = 0 polymorph_ended.emit() func on_player_death() -> void: if is_polymorphed: cancel_polymorph() func get_state() -> Dictionary: return { "is_polymorphed": is_polymorphed, "mob_vnum": current_mob_vnum, "remaining_time": remaining_time, "poly_skill_level": poly_skill_level, "bonus_atk": bonus_atk, "bonus_def": bonus_def, "bonus_speed": bonus_speed }