# pet_companion_system.gd —— Metin2 40250 伴随宠物封印召唤与战力注入系统 1:1 # 对照 40250 服务端 PetSystem.cpp, char_pet.cpp class_name PetCompanionSystem extends RefCounted signal pet_summoned(pet_name: String, vnum: int) signal pet_dismissed(pet_name: String) signal pet_state_changed(state: String) const PET_TABLE: Dictionary = { 53001: { "name": "火凤凰", "mob_vnum": 34001, "bonuses": {"max_hp": 1500, "monster_dmg_pct": 15}, "desc": "最大生命 +1500,对怪物伤害提升 15%" }, 53002: { "name": "幼鹿", "mob_vnum": 34002, "bonuses": {"max_hp": 1000, "exp_pct": 15}, "desc": "最大生命 +1000,获得经验值增加 15%" }, 53003: { "name": "冰凤凰", "mob_vnum": 34003, "bonuses": {"max_hp": 1500, "def_pct": 15}, "desc": "最大生命 +1500,物理防御提升 15%" }, 53005: { "name": "小胖熊猫", "mob_vnum": 34005, "bonuses": {"max_hp": 1000, "critical_pct": 15}, "desc": "最大生命 +1000,致命一击几率提升 15%" }, 53006: { "name": "幼狮", "mob_vnum": 34006, "bonuses": {"max_hp": 1500, "att_pct": 10}, "desc": "最大生命 +1500,物理攻击力提升 10%" } } const FOLLOW_STOP_DIST := 2.0 # 停止距离 const FOLLOW_RUN_DIST := 3.5 # 开始跟随奔跑距离 const TELEPORT_DIST := 25.0 # 瞬间拉回距离 const PET_MOVE_SPEED := 4.5 # 移动速度 (m/s) var is_summoned: bool = false var current_seal_vnum: int = 0 var active_pet: Dictionary = {} var pet_position: Vector3 = Vector3.ZERO var pet_state: String = "wait" # "wait" 或 "run" var remaining_time: float = 0.0 # 召唤 / 收回宠物 (右键点击宠物封印石) func toggle_pet_seal(seal_item: Dictionary, player_pos: Vector3) -> Dictionary: var vnum: int = int(seal_item.get("vnum", 0)) if not PET_TABLE.has(vnum): return {"ok": false, "reason": "NOT_A_PET_SEAL", "msg": "这不是有效的宠物封印!"} # 若当前已召唤同一只宠物,则收回 if is_summoned and current_seal_vnum == vnum: var pet_name = active_pet.get("name", "宠物") dismiss_pet() return { "ok": true, "action": "dismiss", "pet_name": pet_name, "msg": "已将【%s】收回封印石。" % pet_name } # 若当前召唤了不同宠物,先收回 if is_summoned: dismiss_pet() # 召唤新宠物 var cfg: Dictionary = PET_TABLE[vnum] current_seal_vnum = vnum is_summoned = true remaining_time = float(seal_item.get("duration", 86400.0)) # 默认 24 小时 pet_position = player_pos + Vector3(-1.5, 0.0, -1.5) pet_state = "wait" active_pet = { "vnum": vnum, "name": cfg["name"], "mob_vnum": cfg["mob_vnum"], "bonuses": cfg["bonuses"].duplicate() } pet_summoned.emit(cfg["name"], vnum) return { "ok": true, "action": "summon", "pet_name": cfg["name"], "bonuses": cfg["bonuses"], "msg": "伴随宠物【%s】已召唤出战!" % cfg["name"] } # 收回当前宠物 func dismiss_pet() -> void: if not is_summoned: return var pname = active_pet.get("name", "") is_summoned = false current_seal_vnum = 0 active_pet.clear() pet_state = "wait" pet_dismissed.emit(pname) # 获取当前宠物注入的战力属性 func get_active_bonuses() -> Dictionary: if not is_summoned: return {} return active_pet.get("bonuses", {}).duplicate() # 3D 伴随移动逻辑 func update_movement(player_pos: Vector3, dt: float) -> void: if not is_summoned: return var dist: float = pet_position.distance_to(player_pos) # 超远距离瞬间传回身边 if dist >= TELEPORT_DIST: pet_position = player_pos + Vector3(-1.5, 0.0, -1.5) pet_state = "wait" pet_state_changed.emit("wait") return # 跟随靠近 if dist > FOLLOW_STOP_DIST: var dir := (player_pos - pet_position).normalized() pet_position += dir * PET_MOVE_SPEED * dt if pet_state != "run": pet_state = "run" pet_state_changed.emit("run") else: if pet_state != "wait": pet_state = "wait" pet_state_changed.emit("wait") # 逐帧倒计时衰减 func update(dt: float) -> void: if not is_summoned: return remaining_time -= dt if remaining_time <= 0.0: dismiss_pet()