# devils_catacomb_system.gd —— Metin2 40250 地下墓穴 7 层与冥王阿兹瑞尔挑战副本 1:1 # 对照 40250 服务端 devils_catacomb.quest, dungeon.cpp, char_battle.cpp, mob 2598, 2597, 2596 class_name DevilsCatacombSystem extends RefCounted signal floor_advanced(floor: int) signal metin_destroyed(is_correct: bool) signal tartaros_killed(drop_item: Dictionary) signal obelisk_activated() signal charon_killed() signal azrael_hp_changed(current_hp: int, max_hp: int) signal catacomb_cleared(drops: Array) signal catacomb_failed(reason: String) const NPC_CATACOMB_GUARD := 20367 const ITEM_SOUL_CRYSTAL_KEY := 30311 # 灵魂水晶钥匙 (进入与 1 层解锁) const ITEM_GRIM_SOUL_STONE := 30312 # 冷酷之石 (5 层塔耳塔洛斯掉落) const MIN_LEVEL := 75 const TIME_LIMIT := 3600.0 # 60 分钟限时 # 首领属性 const MOB_TARTAROS := 2597 # 5 层守卫 塔耳塔洛斯 const MOB_CHARON := 2596 # 6 层将军 卡隆 const MOB_AZRAEL := 2598 # 7 层冥界之王 阿兹瑞尔 const AZRAEL_MAX_HP := 900000 const AZRAEL_BASE_ATK := 2200 const AZRAEL_BASE_DEF := 680 var is_active: bool = false var current_floor: int = 0 var remaining_time: float = 0.0 # 3 层复仇魔石状态 var correct_metin_id: int = 3 var metins_broken: int = 0 # 5 层方尖碑状态 var has_grim_stone: bool = false var is_obelisk_unlocked: bool = false # 7 层阿兹瑞尔状态 var azrael_hp: int = 0 var azrael_max_hp: int = AZRAEL_MAX_HP var is_azrael_alive: bool = false var azrael_minions_summoned: bool = false # 进入地下墓穴 (通过 NPC 20367) func enter_catacomb(player_level: int, inventory: Array) -> Dictionary: if player_level < MIN_LEVEL: return { "ok": false, "reason": "LEVEL_TOO_LOW", "msg": "你的等级不足 75 级,无法承受地下墓穴的死灵死气!" } # 检查 1 层必需的灵魂水晶钥匙 (30311) var key_slot := -1 for i in range(inventory.size()): var item = inventory[i] if item != null and int(item.get("vnum", 0)) == ITEM_SOUL_CRYSTAL_KEY: key_slot = i break if key_slot == -1: return { "ok": false, "reason": "MISSING_SOUL_KEY", "msg": "进入地下墓穴需要持有【灵魂水晶钥匙】!" } # 初始化副本状态进入 1 层 is_active = true current_floor = 1 remaining_time = TIME_LIMIT correct_metin_id = randi_range(1, 5) metins_broken = 0 has_grim_stone = false is_obelisk_unlocked = false is_azrael_alive = false floor_advanced.emit(1) return { "ok": true, "floor": 1, "time_limit": remaining_time, "msg": "成功踏入地下墓穴一层!限时 60 分钟!" } # 第 1 层:使用灵魂水晶钥匙开启石门封印进入 2 层 func unlock_floor_1(inventory: Array) -> Dictionary: if not is_active or current_floor != 1: return {"ok": false, "reason": "WRONG_FLOOR"} var key_slot := -1 for i in range(inventory.size()): var item = inventory[i] if item != null and int(item.get("vnum", 0)) == ITEM_SOUL_CRYSTAL_KEY: key_slot = i break if key_slot == -1: return {"ok": false, "reason": "MISSING_KEY", "msg": "背包中缺少灵魂水晶钥匙!"} # 消耗钥匙 inventory[key_slot] = null current_floor = 2 floor_advanced.emit(2) return { "ok": true, "floor": 2, "msg": "灵魂水晶钥匙已插入封印,石门轰然开启!进入地下墓穴二层!" } # 第 2 层:突破恶灵迷宫进入 3 层 func complete_floor_2() -> Dictionary: if not is_active or current_floor != 2: return {"ok": false, "reason": "WRONG_FLOOR"} current_floor = 3 floor_advanced.emit(3) return { "ok": true, "floor": 3, "msg": "已突破恶灵迷宫!进入地下墓穴三层!" } # 第 3 层:击碎复仇魔石 (8035) func break_floor_3_metin(metin_idx: int) -> Dictionary: if not is_active or current_floor != 3: return {"ok": false, "reason": "WRONG_FLOOR"} metins_broken += 1 if metin_idx == correct_metin_id: current_floor = 4 metin_destroyed.emit(true) floor_advanced.emit(4) return { "ok": true, "correct": true, "floor": 4, "msg": "击碎了真正的复仇魔石!封印瓦解,传送至地下墓穴四层!" } else: metin_destroyed.emit(false) return { "ok": true, "correct": false, "floor": 3, "msg": "这是虚假的伪装魔石!周围唤醒了冥界诅咒之魂!" } # 第 4 层:玄武岩石柱迷宫寻路进入 5 层 func complete_floor_4() -> Dictionary: if not is_active or current_floor != 4: return {"ok": false, "reason": "WRONG_FLOOR"} current_floor = 5 floor_advanced.emit(5) return { "ok": true, "floor": 5, "msg": "成功找到玄武岩迷宫真出口!进入地下墓穴五层!" } # 第 5 层:击败塔耳塔洛斯 (2597) 并获得冷酷之石 (30312) func defeat_tartaros(inventory: Array) -> Dictionary: if not is_active or current_floor != 5: return {"ok": false, "reason": "WRONG_FLOOR"} # 放入背包 var empty_slot := -1 for i in range(inventory.size()): if inventory[i] == null: empty_slot = i break if empty_slot == -1: return {"ok": false, "reason": "INVENTORY_FULL", "msg": "背包已满,无法拾取冷酷之石!"} var grim_stone = {"vnum": ITEM_GRIM_SOUL_STONE, "name": "冷酷之石", "count": 1} inventory[empty_slot] = grim_stone has_grim_stone = true tartaros_killed.emit(grim_stone) return { "ok": true, "drop": grim_stone, "msg": "击败了守卫【塔耳塔洛斯】,获得了【冷酷之石】!请前往方尖碑镶嵌!" } # 第 5 层:将冷酷之石插入玄武岩方尖碑,开启 6 层通道 func insert_grim_stone_to_obelisk(inventory: Array) -> Dictionary: if not is_active or current_floor != 5: return {"ok": false, "reason": "WRONG_FLOOR"} var stone_slot := -1 for i in range(inventory.size()): var item = inventory[i] if item != null and int(item.get("vnum", 0)) == ITEM_GRIM_SOUL_STONE: stone_slot = i break if stone_slot == -1: return {"ok": false, "reason": "MISSING_STONE", "msg": "背包中缺少冷酷之石!"} inventory[stone_slot] = null is_obelisk_unlocked = true current_floor = 6 obelisk_activated.emit() floor_advanced.emit(6) return { "ok": true, "floor": 6, "msg": "冷酷之石完美嵌入方尖碑!冥界封印开启,进入地下墓穴六层!" } # 第 6 层:击败冥界将军卡隆 (2596) 进入 7 层阿兹瑞尔王座 func defeat_charon() -> Dictionary: if not is_active or current_floor != 6: return {"ok": false, "reason": "WRONG_FLOOR"} current_floor = 7 # 初始化 7 层 Boss 阿兹瑞尔 azrael_hp = AZRAEL_MAX_HP azrael_max_hp = AZRAEL_MAX_HP is_azrael_alive = true azrael_minions_summoned = false charon_killed.emit() floor_advanced.emit(7) return { "ok": true, "floor": 7, "boss_hp": azrael_hp, "msg": "冥界将军【卡隆】已被斩灭!传送至七层【冥王阿兹瑞尔王座】!" } # 第 7 层:对冥王阿兹瑞尔造成伤害 func attack_azrael(raw_dmg: int) -> Dictionary: if not is_active or current_floor != 7 or not is_azrael_alive: return {"ok": false, "reason": "BOSS_NOT_AVAILABLE"} var net_dmg: int = max(1, raw_dmg - AZRAEL_BASE_DEF) azrael_hp = max(0, azrael_hp - net_dmg) azrael_hp_changed.emit(azrael_hp, azrael_max_hp) # 半血召唤死灵护卫 var triggered_minions := false if azrael_hp <= int(AZRAEL_MAX_HP * 0.5) and not azrael_minions_summoned: azrael_minions_summoned = true triggered_minions = true # 击败阿兹瑞尔 if azrael_hp <= 0: is_azrael_alive = false is_active = false var drops: Array = _generate_azrael_drops() catacomb_cleared.emit(drops) return { "ok": true, "killed": true, "net_dmg": net_dmg, "remaining_hp": 0, "drops": drops, "msg": "【冥王阿兹瑞尔】轰然倒下!地下墓穴已被彻底征服!" } return { "ok": true, "killed": false, "net_dmg": net_dmg, "remaining_hp": azrael_hp, "summoned_minions": triggered_minions } # 逐帧倒计时 func update(dt: float) -> void: if not is_active: return remaining_time -= dt if remaining_time <= 0.0: is_active = false is_azrael_alive = false catacomb_failed.emit("TIME_OUT") # 生成阿兹瑞尔掉落物 func _generate_azrael_drops() -> Array: var drops: Array = [] # 1. 必掉阿兹瑞尔专属宝箱 (50187) drops.append({ "vnum": 50187, "name": "阿兹瑞尔宝箱", "count": 1 }) # 2. 必掉灵魂之石 (50513) drops.append({ "vnum": 50513, "name": "灵魂之石", "count": 1 }) # 3. 终极泰坦之盾 (13149) 概率掉落 drops.append({ "vnum": 13149, "name": "泰坦之盾+0", "count": 1 }) # 4. 大额冥界金币 drops.append({ "vnum": 1, "name": "金币", "count": randi_range(200000, 400000) }) return drops