# world_boss_system.gd —— Metin2 40250 野外四大世界 Boss 巡回战与专属宝箱 1:1 # 对照 40250 服务端 char_battle.cpp, regen.cpp, mob_drop_item.txt, special_item_group.txt class_name WorldBossSystem extends RefCounted signal boss_spawned(boss_vnum: int, boss_name: String, position: Vector3) signal boss_hp_changed(current_hp: int, max_hp: int) signal minions_summoned(boss_vnum: int, minion_vnum: int, count: int) signal boss_enraged(boss_vnum: int) signal boss_killed(boss_vnum: int, killer_pos: Vector3, drops: Array) const BOSS_TABLE: Dictionary = { 1901: { "name": "九尾妖狐", "zone": "冰山", "max_hp": 280000, "base_atk": 850, "base_def": 450, "chest_vnum": 50006, "chest_name": "九尾妖狐宝箱", "minion_vnum": 2204, # 冰山雪怪 "minion_count": 4, "gold_min": 50000, "gold_max": 100000 }, 2191: { "name": "巨型沙漠龟", "zone": "永壁沙漠", "max_hp": 320000, "base_atk": 920, "base_def": 520, "chest_vnum": 50007, "chest_name": "沙漠龟宝箱", "minion_vnum": 2108, # 沙漠强盗 "minion_count": 4, "gold_min": 60000, "gold_max": 120000 }, 1304: { "name": "黄虎之魂", "zone": "黄龙神殿", "max_hp": 350000, "base_atk": 1050, "base_def": 480, "chest_vnum": 50008, "chest_name": "黄虎宝箱", "minion_vnum": 1302, # 狂暴神殿狂热信徒 "minion_count": 4, "gold_min": 70000, "gold_max": 130000 }, 2206: { "name": "烈焰领主", "zone": "烈焰原野", "max_hp": 400000, "base_atk": 1200, "base_def": 500, "chest_vnum": 50009, "chest_name": "烈焰宝箱", "minion_vnum": 2202, # 烈焰幽灵 "minion_count": 4, "gold_min": 80000, "gold_max": 150000 } } # 宝箱开启奖池 (40250 special_item_group.txt 1:1) const CHEST_LOOT_TABLE: Dictionary = { 50006: [ {"vnum": 50513, "name": "灵魂之石", "count": 1, "weight": 30}, {"vnum": 25040, "name": "祝福卷轴", "count": 1, "weight": 25}, {"vnum": 169, "name": "战神之剑+9", "count": 1, "weight": 15}, {"vnum": 11299, "name": "黑钢甲+9", "count": 1, "weight": 10}, {"vnum": 71084, "name": "高级附魔石", "count": 3, "weight": 20} ], 50007: [ {"vnum": 50513, "name": "灵魂之石", "count": 1, "weight": 30}, {"vnum": 25040, "name": "祝福卷轴", "count": 1, "weight": 25}, {"vnum": 3159, "name": "鬼牙刃+9", "count": 1, "weight": 15}, {"vnum": 11499, "name": "黑风甲+9", "count": 1, "weight": 10}, {"vnum": 71084, "name": "高级附魔石", "count": 3, "weight": 20} ], 50008: [ {"vnum": 50513, "name": "灵魂之石", "count": 1, "weight": 30}, {"vnum": 25040, "name": "祝福卷轴", "count": 1, "weight": 25}, {"vnum": 1109, "name": "党人斧+9", "count": 1, "weight": 15}, {"vnum": 11699, "name": "黑仙袍+9", "count": 1, "weight": 10}, {"vnum": 71084, "name": "高级附魔石", "count": 3, "weight": 20} ], 50009: [ {"vnum": 50513, "name": "灵魂之石", "count": 1, "weight": 30}, {"vnum": 25040, "name": "祝福卷轴", "count": 1, "weight": 25}, {"vnum": 189, "name": "天狼剑+9", "count": 1, "weight": 15}, {"vnum": 11899, "name": "黑龙袍+9", "count": 1, "weight": 10}, {"vnum": 71084, "name": "高级附魔石", "count": 3, "weight": 20} ] } # 当前活跃的世界 Boss var active_boss: Dictionary = {} var is_boss_alive: bool = false var respawn_timers: Dictionary = {} # mob_vnum -> remaining_cooldown func spawn_boss(mob_vnum: int, pos: Vector3 = Vector3.ZERO) -> Dictionary: if not BOSS_TABLE.has(mob_vnum): return {"ok": false, "reason": "INVALID_BOSS_VNUM", "msg": "未知的世界Boss!"} var cfg: Dictionary = BOSS_TABLE[mob_vnum] active_boss = { "vnum": mob_vnum, "name": cfg["name"], "zone": cfg["zone"], "max_hp": cfg["max_hp"], "current_hp": cfg["max_hp"], "base_atk": cfg["base_atk"], "current_atk": cfg["base_atk"], "base_def": cfg["base_def"], "current_def": cfg["base_def"], "chest_vnum": cfg["chest_vnum"], "chest_name": cfg["chest_name"], "minion_vnum": cfg["minion_vnum"], "minion_count": cfg["minion_count"], "position": pos, "is_enraged": false, "phase_75_triggered": false, "phase_50_triggered": false, "phase_25_triggered": false, "summoned_minions": [] } is_boss_alive = true boss_spawned.emit(mob_vnum, cfg["name"], pos) return { "ok": true, "boss": active_boss.duplicate(), "msg": "【世界Boss】%s 已在 %s 现身!" % [cfg["name"], cfg["zone"]] } # 承受伤害与阶段机制判定 func apply_damage(raw_dmg: int) -> Dictionary: if not is_boss_alive or active_boss.is_empty(): return {"ok": false, "reason": "NO_ACTIVE_BOSS", "msg": "当前没有存活的世界Boss!"} var def: int = active_boss["current_def"] var net_dmg: int = max(1, raw_dmg - def) active_boss["current_hp"] = max(0, active_boss["current_hp"] - net_dmg) var cur_hp: int = active_boss["current_hp"] var max_hp: int = active_boss["max_hp"] var hp_ratio: float = float(cur_hp) / float(max_hp) boss_hp_changed.emit(cur_hp, max_hp) var triggered_events: Array = [] # 1. 阶段小怪召唤机制 (75%, 50%, 25%) if hp_ratio <= 0.75 and not active_boss["phase_75_triggered"]: active_boss["phase_75_triggered"] = true _summon_minions(active_boss["minion_vnum"], active_boss["minion_count"]) triggered_events.append("PHASE_75_MINIONS") if hp_ratio <= 0.50 and not active_boss["phase_50_triggered"]: active_boss["phase_50_triggered"] = true _summon_minions(active_boss["minion_vnum"], active_boss["minion_count"]) triggered_events.append("PHASE_50_MINIONS") if hp_ratio <= 0.25 and not active_boss["phase_25_triggered"]: active_boss["phase_25_triggered"] = true _summon_minions(active_boss["minion_vnum"], active_boss["minion_count"]) triggered_events.append("PHASE_25_MINIONS") # 2. 残血狂暴机制 (<= 30% HP) if hp_ratio <= 0.30 and not active_boss["is_enraged"]: active_boss["is_enraged"] = true active_boss["current_atk"] = int(active_boss["base_atk"] * 1.5) boss_enraged.emit(active_boss["vnum"]) triggered_events.append("BOSS_ENRAGED") # 3. 击杀判定 if cur_hp <= 0: is_boss_alive = false var drops: Array = _generate_boss_drops(active_boss["vnum"]) var death_pos: Vector3 = active_boss["position"] var boss_name: String = active_boss["name"] var boss_vnum: int = active_boss["vnum"] boss_killed.emit(boss_vnum, death_pos, drops) return { "ok": true, "killed": true, "net_dmg": net_dmg, "remaining_hp": 0, "drops": drops, "triggered_events": triggered_events, "msg": "【世界Boss】%s 已被击杀!掉落专属宝箱!" % boss_name } return { "ok": true, "killed": false, "net_dmg": net_dmg, "remaining_hp": cur_hp, "hp_ratio": hp_ratio, "is_enraged": active_boss["is_enraged"], "current_atk": active_boss["current_atk"], "triggered_events": triggered_events } # 召唤护卫小怪 func _summon_minions(minion_vnum: int, count: int) -> void: for i in range(count): var offset := Vector3(randf_range(-3.0, 3.0), 0.0, randf_range(-3.0, 3.0)) active_boss["summoned_minions"].append({ "vnum": minion_vnum, "pos": active_boss["position"] + offset }) minions_summoned.emit(active_boss["vnum"], minion_vnum, count) # 生成 Boss 击杀掉落物 func _generate_boss_drops(mob_vnum: int) -> Array: var cfg: Dictionary = BOSS_TABLE[mob_vnum] var drops: Array = [] # 1. 必掉专属宝箱 drops.append({ "vnum": cfg["chest_vnum"], "name": cfg["chest_name"], "count": 1 }) # 2. 必掉灵魂之石 (50513) drops.append({ "vnum": 50513, "name": "灵魂之石", "count": 1 }) # 3. 必掉祝福卷轴 (25040) drops.append({ "vnum": 25040, "name": "祝福卷轴", "count": 1 }) # 4. 大额金币掉落 (散落地上) var gold_amount: int = randi_range(cfg["gold_min"], cfg["gold_max"]) drops.append({ "vnum": 1, # 金币 "name": "金币", "count": gold_amount }) return drops # 开启世界 Boss 宝箱 func open_boss_chest(chest_slot: int, inventory: Array) -> Dictionary: if chest_slot < 0 or chest_slot >= inventory.size(): return {"ok": false, "reason": "INVALID_SLOT", "msg": "无效的背包格子!"} var item = inventory[chest_slot] if item == null: return {"ok": false, "reason": "EMPTY_SLOT", "msg": "该格子上没有物品!"} var chest_vnum: int = int(item.get("vnum", 0)) if not CHEST_LOOT_TABLE.has(chest_vnum): return {"ok": false, "reason": "NOT_A_BOSS_CHEST", "msg": "这不是世界 Boss 宝箱!"} var pool: Array = CHEST_LOOT_TABLE[chest_vnum] # 权重抽取奖励 var total_weight: int = 0 for entry in pool: total_weight += int(entry.get("weight", 1)) var roll := randi_range(1, total_weight) var accumulated := 0 var chosen_reward: Dictionary = {} for entry in pool: accumulated += int(entry.get("weight", 1)) if roll <= accumulated: chosen_reward = entry.duplicate() break if chosen_reward.is_empty(): chosen_reward = pool[0].duplicate() # 扣除宝箱 inventory[chest_slot] = null # 放入抽中的奖励 var stored_slot := -1 for i in range(inventory.size()): if inventory[i] == null: inventory[i] = { "vnum": chosen_reward["vnum"], "count": chosen_reward.get("count", 1), "name": chosen_reward["name"] } stored_slot = i break return { "ok": true, "reward": chosen_reward, "stored_slot": stored_slot, "msg": "打开宝箱获得了【%s】!" % chosen_reward["name"] }