# cursed_monolith_spire_system.gd —— Metin2 40250 野外魔石破除之【封印魔塔】限时防守挑战 1:1 # 对照 40250 服务端 regen.cpp, dungeon.cpp, char_battle.cpp, item_manager.cpp class_name CursedMonolithSpireSystem extends RefCounted signal spire_emerged(pos: Vector3, time_limit: float) signal wave_spawned(wave_index: int, wave_name: String, mob_count: int) signal trial_completed(success: bool, time_used: float) signal reward_chest_spawned(chest_vnum: int, pos: Vector3) const TRIAL_TIME_LIMIT: float = 180.0 # 3 分钟倒计时 const CHEST_VNUM: int = 50130 # 上古魔塔宝箱 (Ancient Spire Chest) const WAVE_CONFIG: Dictionary = { 1: { "name": "第 1 波:狂暴魔兵突袭", "mob_vnum": 701, # 恶魔士兵 "count": 6 }, 2: { "name": "第 2 波:双生魔将合围", "mob_vnum": 1092, # 恶魔大将 "count": 2 }, 3: { "name": "第 3 波:上古守墓魔王", "mob_vnum": 1093, # 恶魔死神幻影 "count": 1 } } var is_active: bool = false var current_wave: int = 0 var time_remaining: float = 0.0 var spire_pos: Vector3 = Vector3.ZERO var difficulty: int = 50 var active_mobs: Dictionary = {} # mob_id -> { "vnum", "alive" } var next_mob_id: int = 1 var is_victory: bool = false var chest_available: bool = false # 启动封印魔塔挑战 func start_trial(pos: Vector3, diff_level: int = 50) -> Dictionary: if is_active: return {"ok": false, "reason": "TRIAL_ALREADY_ACTIVE", "msg": "封印魔塔挑战正在进行中!"} is_active = true is_victory = false chest_available = false spire_pos = pos difficulty = diff_level time_remaining = TRIAL_TIME_LIMIT current_wave = 0 active_mobs.clear() next_mob_id = 1 spire_emerged.emit(pos, TRIAL_TIME_LIMIT) # 立即召唤第 1 波 _spawn_wave(1) return { "ok": true, "pos": pos, "time_limit": TRIAL_TIME_LIMIT, "msg": "大地剧烈震颤!【诅咒的封印黑石魔塔】破土升起!请在 180 秒内清除所有魔潮!" } # 产生指定阶段怪潮 func _spawn_wave(wave_idx: int) -> void: current_wave = wave_idx var cfg = WAVE_CONFIG[wave_idx] var count: int = cfg["count"] var vnum: int = cfg["mob_vnum"] active_mobs.clear() for i in range(count): var mid := next_mob_id next_mob_id += 1 active_mobs[mid] = { "id": mid, "vnum": vnum, "alive": true } wave_spawned.emit(wave_idx, cfg["name"], count) # 击杀魔潮怪物 func kill_mob(mob_id: int) -> Dictionary: if not is_active: return {"ok": false, "reason": "TRIAL_NOT_ACTIVE"} 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(): if current_wave < 3: # 进入下一波 var next_w = current_wave + 1 _spawn_wave(next_w) return { "ok": true, "wave_cleared": current_wave - 1, "next_wave": next_w, "msg": "当前波次肃清!更加凶险的魔物正在凝聚!" } else: # 第 3 波首领击破 -> 大获全胜! return _finish_trial(true) return { "ok": true, "remaining_in_wave": active_mobs.size() } # 帧心跳更新限时倒计时 func update(delta: float) -> Dictionary: if not is_active: return {"status": "inactive"} time_remaining -= delta if time_remaining <= 0.0: time_remaining = 0.0 return _finish_trial(false) return { "status": "running", "wave": current_wave, "remaining_mobs": active_mobs.size(), "time_remaining": time_remaining } # 结算挑战胜负 func _finish_trial(success: bool) -> Dictionary: is_active = false is_victory = success var time_used = TRIAL_TIME_LIMIT - time_remaining if success: chest_available = true reward_chest_spawned.emit(CHEST_VNUM, spire_pos) trial_completed.emit(true, time_used) return { "ok": true, "victory": true, "time_used": time_used, "msg": "【封印魔塔】封印彻底瓦解!降下【上古魔塔宝箱】!耗时: %.1f 秒" % time_used } else: active_mobs.clear() trial_completed.emit(false, TRIAL_TIME_LIMIT) return { "ok": true, "victory": false, "time_used": TRIAL_TIME_LIMIT, "msg": "时间耗尽!封印黑石魔塔重新沉入深渊地底,挑战失败!" } # 开启上古宝箱获取掉落 func open_reward_chest() -> Array: if not chest_available: return [] chest_available = false var drops: Array = [ {"vnum": 71003, "name": "祝福卷轴", "count": 2}, {"vnum": 50513, "name": "灵魂之石", "count": 1}, {"vnum": 70024, "name": "祝福宝珠", "count": 1}, {"vnum": 1, "name": "金币", "count": 3500000} ] return drops # 取消/中断挑战 func cancel_trial() -> Dictionary: if not is_active: return {"ok": false, "reason": "NOT_ACTIVE"} is_active = false active_mobs.clear() return {"ok": true, "msg": "挑战已提前终止。"} # 获取状态 func get_trial_state() -> Dictionary: return { "is_active": is_active, "is_victory": is_victory, "current_wave": current_wave, "remaining_mobs": active_mobs.size(), "time_remaining": time_remaining, "chest_available": chest_available }