# endless_spire_dungeon_system.gd —— Metin2 40250 单人单机迷宫地牢【无尽天关】爬塔挑战 1:1 # 对照 40250 服务端 dungeon.cpp, dungeon_info.cpp, questmanager.cpp class_name EndlessSpireDungeonSystem extends RefCounted signal floor_started(floor_num: int, is_boss_floor: bool, mob_count: int) signal floor_cleared(floor_num: int, points_earned: int, time_used: float) signal checkpoint_saved(checkpoint_floor: int) signal spire_trial_completed(highest_floor: int, total_points: int) signal spire_trial_failed(floor_num: int, reason: String) const MAX_FLOORS := 50 const FLOOR_TIME_LIMIT := 120.0 # 每层 120 秒限时 const MILESTONE_BOSSES: Dictionary = { 5: {"name": "半兽人首领", "vnum": 691, "hp": 50000}, 10: {"name": "蜘蛛女王", "vnum": 2091, "hp": 80000}, 15: {"name": "沙漠巨怪", "vnum": 2191, "hp": 120000}, 20: {"name": "恶魔死神", "vnum": 1093, "hp": 180000}, 25: {"name": "烈焰领主", "vnum": 2206, "hp": 220000}, 30: {"name": "九尾妖狐", "vnum": 1901, "hp": 280000}, 35: {"name": "黄虎之魂", "vnum": 1304, "hp": 320000}, 40: {"name": "冥王阿兹瑞尔", "vnum": 2598, "hp": 400000}, 45: {"name": "冰龙贝兰-塞陶", "vnum": 2493, "hp": 500000}, 50: {"name": "混沌魔神·阿萨泽尔", "vnum": 8027, "hp": 800000} } var is_in_trial: bool = false var current_floor: int = 1 var highest_checkpoint: int = 1 var total_points: int = 0 var floor_time_remaining: float = 0.0 var active_mobs: Dictionary = {} # mob_id -> { "vnum", "alive" } var next_mob_id: int = 1 var personal_best_floor: int = 0 # 开启爬塔挑战 func start_trial(from_checkpoint: bool = false) -> Dictionary: if is_in_trial: return {"ok": false, "reason": "ALREADY_IN_TRIAL"} is_in_trial = true current_floor = highest_checkpoint if from_checkpoint else 1 total_points = 0 next_mob_id = 1 _start_floor(current_floor) return { "ok": true, "starting_floor": current_floor, "checkpoint": highest_checkpoint, "msg": "踏入【无尽天关】第 %d 层!" % current_floor } # 开启指定层数 func _start_floor(floor_num: int) -> void: current_floor = floor_num floor_time_remaining = FLOOR_TIME_LIMIT active_mobs.clear() var is_boss = MILESTONE_BOSSES.has(floor_num) if is_boss: var b_cfg = MILESTONE_BOSSES[floor_num] var mid = next_mob_id next_mob_id += 1 active_mobs[mid] = { "id": mid, "vnum": b_cfg["vnum"], "name": b_cfg["name"], "is_boss": true, "alive": true } floor_started.emit(floor_num, true, 1) else: # 普通层小怪 var mob_count = 4 + (floor_num % 5) * 2 for i in range(mob_count): var mid = next_mob_id next_mob_id += 1 active_mobs[mid] = { "id": mid, "vnum": 101 + (floor_num % 10), "name": "天关守卫", "is_boss": false, "alive": true } floor_started.emit(floor_num, false, mob_count) # 击杀当前层怪物 func kill_mob(mob_id: int) -> Dictionary: if not is_in_trial: return {"ok": false, "reason": "NOT_IN_TRIAL"} if not active_mobs.has(mob_id) or not active_mobs[mob_id]["alive"]: return {"ok": false, "reason": "MOB_NOT_FOUND"} active_mobs[mob_id]["alive"] = false active_mobs.erase(mob_id) # 检查本层是否清空 if active_mobs.is_empty(): return _on_floor_cleared() return { "ok": true, "remaining_mobs": active_mobs.size() } # 本层通关结算 func _on_floor_cleared() -> Dictionary: var time_used = FLOOR_TIME_LIMIT - floor_time_remaining var points_awarded = current_floor * 10 total_points += points_awarded if current_floor > personal_best_floor: personal_best_floor = current_floor floor_cleared.emit(current_floor, points_awarded, time_used) # 检查点保存判定 (逢 5 层通关) if current_floor % 5 == 0: var next_cp = current_floor + 1 if next_cp > highest_checkpoint and next_cp <= MAX_FLOORS: highest_checkpoint = next_cp checkpoint_saved.emit(highest_checkpoint) # 终极 50 层通关判定 if current_floor >= MAX_FLOORS: is_in_trial = false spire_trial_completed.emit(MAX_FLOORS, total_points) return { "ok": true, "trial_completed": true, "highest_floor": MAX_FLOORS, "total_points": total_points, "msg": "登峰造极!你已彻底通关【无尽天关】第 50 层巅峰!" } # 进阶下一层 var next_f = current_floor + 1 _start_floor(next_f) return { "ok": true, "floor_cleared": current_floor - 1, "next_floor": next_f, "points": total_points, "msg": "第 %d 层顺利通关!进入第 %d 层!" % [current_floor - 1, next_f] } # 帧心跳更新限时 func update(delta: float) -> Dictionary: if not is_in_trial: return {"status": "inactive"} floor_time_remaining -= delta if floor_time_remaining <= 0.0: floor_time_remaining = 0.0 is_in_trial = false spire_trial_failed.emit(current_floor, "TIME_EXPIRED") return { "status": "failed", "reason": "TIME_EXPIRED", "floor": current_floor, "msg": "时间耗尽!被传送出天关。" } return { "status": "active", "floor": current_floor, "remaining_time": floor_time_remaining, "remaining_mobs": active_mobs.size() } # 中途离开挑战 func exit_trial() -> Dictionary: if not is_in_trial: return {"ok": false, "reason": "NOT_IN_TRIAL"} is_in_trial = false active_mobs.clear() return {"ok": true, "msg": "已主动退出天关,检查点进度已保存。"} # 获取挑战概览 func get_spire_status() -> Dictionary: return { "is_in_trial": is_in_trial, "current_floor": current_floor, "highest_checkpoint": highest_checkpoint, "personal_best_floor": personal_best_floor, "total_points": total_points, "remaining_time": floor_time_remaining }