/*
 * Copyright (c) 2026 盐灯水母
 *
 * This Source Code Form is subject to the terms of the Mozilla Public
 * License, v. 2.0. If a copy of the MPL was not distributed with this
 * file, You can obtain one at https://mozilla.org/MPL/2.0/.
 */

package com.remains_cave.dungeon.tomb;

import com.remains_cave.block.ChallengeAltarBlock;
import com.remains_cave.block.ChallengeCageBlock;
import com.remains_cave.block.entity.ChallengeAltarBlockEntity;
import com.remains_cave.dungeon.room.RoomManager;
import com.remains_cave.dungeon.room.RoomState;
import com.remains_cave.effect.ModEffects;
import com.remains_cave.entity.ModEntities;
import com.remains_cave.network.ChallengeDataPacket;
import net.minecraft.core.BlockPos;
import net.minecraft.core.HolderLookup;
import net.minecraft.core.particles.ParticleTypes;
import net.minecraft.nbt.*;
import net.minecraft.network.chat.Component;
import net.minecraft.server.level.ServerBossEvent;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.server.level.ServerPlayer;
import net.minecraft.sounds.SoundEvents;
import net.minecraft.sounds.SoundSource;
import net.minecraft.util.RandomSource;
import net.minecraft.world.BossEvent;
import net.minecraft.world.effect.MobEffectInstance;
import net.minecraft.world.effect.MobEffects;
import net.minecraft.world.entity.Entity;
import net.minecraft.world.entity.EntityType;
import net.minecraft.world.entity.LivingEntity;
import net.minecraft.world.entity.Mob;
import net.minecraft.world.entity.player.Player;
import net.minecraft.world.item.ItemStack;
import net.minecraft.world.level.block.entity.BlockEntity;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.phys.AABB;

import java.util.*;

public class RoomChallenge {
    // 常量
    private static final int[] ELITE_COUNTS = {3, 9, 18};
    private static final int[] NORMAL_COUNTS = {9, 21, 34};
    private static final int[] SPAWN_TIMES = {60, 160, 240};  // 配合 80 tick (4秒) 的刷怪间隔
    private static final int[] WAVE_TIMEOUTS = {300, 480, 600}; // 单位：秒
    private static final int WAVE_INTERVAL_TICKS = 9 * 20; // 9秒
    private static final int START_DELAY_TICKS = 9 * 20;   // 9秒准备阶段
    private static final int SPAWNER_COOLDOWN_TICKS = 3 * 20; // 3秒冷却
    private static final int SPAWN_DELAY_TICKS = 2 * 20;      // 2秒生成延迟
    private static final int ACTIVE_DURATION_TICKS = 3 * 20;   // 3秒后恢复（实际由 scheduleTick 控制）
    private String formatTime(int seconds) {
        int minutes = seconds / 60;
        int secs = seconds % 60;
        if (minutes > 0) {
            return String.format("%d分%d秒", minutes, secs);
        } else {
            return String.format("%d秒", secs);
        }
    }

    // 字段
    private String roomId;
    private BlockPos altarPos;
    private ChallengeType type;
    private boolean active = true;
    private int wave = 1;
    private int remainingElite;
    private int remainingNormal;
    private int toSpawnElite;
    private int toSpawnNormal;
    private long waveStartTime;
    private long lastSpawnTime;
    private final Set<Integer> usedEliteIndices = new HashSet<>();
    private transient ServerBossEvent bossEvent;
    private transient List<BlockPos> eliteSpawners;
    private transient List<BlockPos> normalSpawners;
    private boolean waitingForNextWave = false;
    private long waitStartTime = 0;
    private boolean waitingForStart = true;
    private final Map<BlockPos, Long> spawnerCooldown = new HashMap<>();
    private final List<ScheduledSpawn> pendingSpawns = new ArrayList<>();
    public BlockPos getAltarPos() {
        return altarPos;
    }
    private AABB getRoomArea(ServerLevel level) {
        RoomManager rm = RoomManager.get(level);
        RoomState roomState = rm.getOrCreateRoom(roomId);
        AABB area = roomState.getProtectionArea();
        if (area == null) {
            // 回退：以祭坛为中心 28 格
            area = new AABB(altarPos).inflate(28);
        }
        return area;
    }
    private float lastWaveTimeProgress = 1.0f; // 上一波结束时的剩余时间比例（0-1）

    private static class ScheduledSpawn {
        BlockPos pos;
        EntityType<? extends LivingEntity> type;
        boolean isElite;
        long spawnTime; // 计划生成的时间（游戏刻）
    }
    private final Map<BlockPos, Long> activeCageStartTime = new HashMap<>();
    private static final int PARTICLE_DURATION_TICKS = 2 * 20; // 2秒
    public RoomChallenge() {}

    private void sendClearPacket(ServerLevel level) {
        ChallengeDataPacket packet = new ChallengeDataPacket(roomId, type, 0, 0.0f, 0.0f, 0.0f, 3);
        AABB area = getRoomArea(level);
        for (ServerPlayer player : level.players()) {
            if (player.getBoundingBox().intersects(area)) {
                player.connection.send(packet);
            }
        }
    }

    private void clearAllAltars(ServerLevel level) {
        RoomManager roomManager = RoomManager.get(level);
        List<BlockPos> altars = roomManager.getAltars(roomId);
        for (BlockPos pos : altars) {
            if (level.getBlockEntity(pos) instanceof ChallengeAltarBlockEntity altar) {
                altar.setChallengeActive(false, ChallengeType.NONE, null);
            }
        }
    }

    private void sendUpdateToPlayers(ServerLevel level) {
        float monsterProgress;
        float timeProgress;
        float waveProgress;
        int phase = 0; // 默认值，每个分支会重新赋值

        if (waitingForStart) {
            long elapsed = level.getGameTime() - waitStartTime;
            monsterProgress = Math.min(1.0f, (float) elapsed / START_DELAY_TICKS);
            timeProgress = monsterProgress;   // 时间条也从0回满
            waveProgress = 0.0f;
            int sendWave = 0;
            ChallengeDataPacket packet = new ChallengeDataPacket(roomId, type, sendWave, monsterProgress, timeProgress, waveProgress, phase);
            AABB area = getRoomArea(level); // 替换原来的 64 格区域
            for (ServerPlayer player : level.players()) {
                if (player.getBoundingBox().intersects(area)) {
                    player.connection.send(packet);
                }
            }
            return; // 准备阶段发送后直接返回
        } else if (waitingForNextWave) {
            phase = 2;
            long elapsed = level.getGameTime() - waitStartTime;
            float intervalProgress = Math.min(1.0f, (float) elapsed / WAVE_INTERVAL_TICKS);
            monsterProgress = 1.0f; // 触发客户端本地计时，怪物进度从0回满
            // 时间条从上一波剩余时间逐渐回满
            timeProgress = lastWaveTimeProgress + (1.0f - lastWaveTimeProgress) * intervalProgress;
            waveProgress = (float) wave / 3.0f;
        } else {
            phase = 1;
            int total = ELITE_COUNTS[wave-1] + NORMAL_COUNTS[wave-1];
            monsterProgress = (float)(remainingElite + remainingNormal) / total;
            long waveElapsed = (level.getGameTime() - waveStartTime) / 20;
            int timeout = WAVE_TIMEOUTS[wave-1];
            timeProgress = 1.0f - Math.min(1.0f, (float) waveElapsed / timeout);
            waveProgress = (float) wave / 3.0f;
        }

        // 确保值在 [0,1] 范围内
        monsterProgress = Math.max(0.0f, Math.min(1.0f, monsterProgress));
        timeProgress = Math.max(0.0f, Math.min(1.0f, timeProgress));
        waveProgress = Math.max(0.0f, Math.min(1.0f, waveProgress));

// 战斗阶段或波次间隔阶段中，发送数据包的部分
        ChallengeDataPacket packet = new ChallengeDataPacket(roomId, type, wave, monsterProgress, timeProgress, waveProgress, phase);
        AABB area = getRoomArea(level);  // 使用房间区域，而不是固定 64 格
        for (ServerPlayer player : level.players()) {
            if (player.getBoundingBox().intersects(area)) {
                player.connection.send(packet);
            }
        }
    }



    public RoomChallenge(String roomId, BlockPos altarPos, ChallengeType type) {
        this.roomId = roomId;
        this.altarPos = altarPos;
        this.type = type;
    }

    private boolean hasAnyPlayer(ServerLevel level) {
        double radius = 64.0;
        for (ServerPlayer player : level.players()) {
            if (player.distanceToSqr(altarPos.getX() + 0.5, altarPos.getY() + 0.5, altarPos.getZ() + 0.5) <= radius * radius) {
                return true;
            }
        }
        return false;
    }
    public boolean isActive() { return active; }

    public void onMobDeath(boolean isElite) {
        if (isElite) remainingElite--;
        else remainingNormal--;
    }

    public void tick(ServerLevel level) {
        if (!active) return;
        if (waitingForStart) {
            if (waitStartTime == 0) {
                waitStartTime = level.getGameTime();
                bossEvent = new ServerBossEvent(
                        Component.translatable("challenge.preparing"),
                        BossEvent.BossBarColor.RED,
                        BossEvent.BossBarOverlay.PROGRESS
                );
                bossEvent.setProgress(0.0f);
                bossEvent.setVisible(false); // 添加此行
                for (ServerPlayer player : level.players()) {
                    if (isPlayerInRoom(player, roomId)) {
                        bossEvent.addPlayer(player);
                    }
                }
                RoomManager.get(level).activateBoss(roomId, level);
            }
            long now = level.getGameTime();
            long elapsed = now - waitStartTime;
            float progress = Math.min(1.0f, elapsed / (float) START_DELAY_TICKS);
            bossEvent.setProgress(progress);
            if (elapsed >= START_DELAY_TICKS) {
                waitingForStart = false;
                init(level);
            }
            if (level.getGameTime() % 10 == 0) {
                sendUpdateToPlayers(level);
                updatePlayersInRoom(level);
            }
            return;
        }

        if (waitingForNextWave) {
            long now = level.getGameTime();
            long elapsed = now - waitStartTime;
            float progress = Math.min(1.0f, elapsed / (float) WAVE_INTERVAL_TICKS);
            bossEvent.setProgress(progress);
            if (elapsed >= WAVE_INTERVAL_TICKS) {
                waitingForNextWave = false;
                wave++;
                toSpawnElite = ELITE_COUNTS[wave-1];
                toSpawnNormal = NORMAL_COUNTS[wave-1];
                remainingElite = toSpawnElite;
                remainingNormal = toSpawnNormal;
                usedEliteIndices.clear();
                waveStartTime = level.getGameTime();
                lastSpawnTime = waveStartTime;
                bossEvent.setName(Component.translatable("challenge." + type.getSerializedName() + ".title", wave));
                bossEvent.setProgress(1.0f);
            }
            if (level.getGameTime() % 10 == 0) {
                sendUpdateToPlayers(level);
                updatePlayersInRoom(level);
            }
            return;
        }
        long now = level.getGameTime();
        Iterator<Map.Entry<BlockPos, Long>> iterator = activeCageStartTime.entrySet().iterator();
        while (iterator.hasNext()) {
            Map.Entry<BlockPos, Long> entry = iterator.next();
            long startTime = entry.getValue();
            long elapsed = now - startTime;
            if (elapsed < PARTICLE_DURATION_TICKS) {
                if (elapsed % 2 == 0) {
                    BlockPos pos = entry.getKey();
                    double x = pos.getX() + 0.5 + (level.random.nextDouble() - 0.5) * 0.4;
                    double y = pos.getY() + 1.2 + (level.random.nextDouble() - 0.5) * 0.4;
                    double z = pos.getZ() + 0.5 + (level.random.nextDouble() - 0.5) * 0.4;
                    level.sendParticles(ParticleTypes.TRIAL_SPAWNER_DETECTED_PLAYER, x, y, z, 1, 0, 0, 0, 0);
                }
            } else {
                iterator.remove();
            }
        }
        Iterator<ScheduledSpawn> it = pendingSpawns.iterator();
        while (it.hasNext()) {
            ScheduledSpawn s = it.next();
            if (now >= s.spawnTime) {
                executeSpawn(level, s.pos, s.type, s.isElite);
                it.remove();
            }
        }

        if (!active) return;
        if (eliteSpawners == null) init(level);


        long waveElapsed = (now - waveStartTime) / 20;          // 已过去秒数
        int waveIdx = wave - 1;
        int timeoutSeconds = WAVE_TIMEOUTS[waveIdx];           // 当前波次超时秒数

        // 更新 BossBar 显示剩余时间
        int remainingSeconds = Math.max(0, timeoutSeconds - (int)waveElapsed);
        Component timeText = Component.literal(" (剩余 " + formatTime(remainingSeconds) + ")");
        Component waveTitle = Component.translatable("challenge." + type.getSerializedName() + ".title", wave);
        Component fullTitle = waveTitle.copy().append(timeText);
        if (!bossEvent.getName().equals(fullTitle)) {
            bossEvent.setName(fullTitle);
        }



        // 刷怪逻辑
        if (toSpawnElite > 0 || toSpawnNormal > 0) {
            if (waveElapsed < SPAWN_TIMES[waveIdx]) {
                int ticksSinceLast = (int)(now - lastSpawnTime);
                if (ticksSinceLast >= 80) {
                    spawnBatch(level);
                    lastSpawnTime = now;
                }
            } else {
                toSpawnElite = 0;
                toSpawnNormal = 0;
            }
        }

        if (active && level.getGameTime() % 10 == 0) {
            sendUpdateToPlayers(level);
            updatePlayersInRoom(level);
        }
        boolean waveFinished = (remainingElite == 0 && remainingNormal == 0);
        boolean waveTimeout = waveElapsed >= WAVE_TIMEOUTS[wave-1];

        if (waveFinished || waveTimeout) {
            if (waveTimeout) {
                // 超时 → 直接失败
                failChallenge(level);
                active = false;
                bossEvent.removeAllPlayers();
                return;
            }
            // 正常完成波次
            if (wave == 3) {
                grantReward(level);
                active = false;
                bossEvent.removeAllPlayers();
            } else {
                // 记录当前波次剩余时间比例
                long wave_Elapsed = (level.getGameTime() - waveStartTime) / 20;
                int timeout = WAVE_TIMEOUTS[wave-1];
                lastWaveTimeProgress = 1.0f - Math.min(1.0f, (float) wave_Elapsed / timeout);
                // 进入下一波前的等待
                level.playSound(null, altarPos, SoundEvents.TRIAL_SPAWNER_DETECT_PLAYER, SoundSource.BLOCKS, 1.0f, 1.0f);
                waitingForNextWave = true;
                waitStartTime = level.getGameTime();
                bossEvent.setProgress(0.0f);
                toSpawnElite = 0;
                toSpawnNormal = 0;
                bossEvent.setName(Component.translatable("challenge." + type.getSerializedName() + ".title", wave));
            }
        }

        int total = ELITE_COUNTS[wave-1] + NORMAL_COUNTS[wave-1];
        bossEvent.setProgress((float)(remainingElite + remainingNormal) / total);
    }

    public void init(ServerLevel level) {
        eliteSpawners = RoomManager.get(level).getEliteSpawners(roomId);
        normalSpawners = RoomManager.get(level).getNormalSpawners(roomId);
        if (eliteSpawners == null) eliteSpawners = Collections.emptyList();
        if (normalSpawners == null) normalSpawners = Collections.emptyList();

        toSpawnElite = ELITE_COUNTS[wave-1];
        toSpawnNormal = NORMAL_COUNTS[wave-1];
        remainingElite = toSpawnElite;
        remainingNormal = toSpawnNormal;

        lastWaveTimeProgress = 1.0f; // 新挑战开始时重置

        waveStartTime = level.getGameTime();
        lastSpawnTime = waveStartTime;

        if (bossEvent == null) {
            Component title = Component.translatable("challenge." + type.getSerializedName() + ".title", wave);
            bossEvent = new ServerBossEvent(title, BossEvent.BossBarColor.RED, BossEvent.BossBarOverlay.PROGRESS);
            bossEvent.setVisible(false); // 添加此行
        } else {
            bossEvent.setName(Component.translatable("challenge." + type.getSerializedName() + ".title", wave));
        }
        bossEvent.setProgress(1.0f);
        for (ServerPlayer player : level.players()) {
            if (isPlayerInRoom(player, roomId) && !bossEvent.getPlayers().contains(player)) {
                bossEvent.addPlayer(player);
            }
        }
    }

    private void spawnBatch(ServerLevel level) {
        RandomSource random = level.random;
        int spawned = 0;
        long now = level.getGameTime();
        while (spawned < 1 && (toSpawnElite > 0 || toSpawnNormal > 0)) {
            if (toSpawnElite > 0 && (random.nextBoolean() || toSpawnNormal == 0)) {
                List<Integer> availableElite = new ArrayList<>();
                for (int i = 0; i < eliteSpawners.size(); i++) {
                    BlockPos pos = eliteSpawners.get(i);
                    Long cooldownEnd = spawnerCooldown.get(pos);
                    if (cooldownEnd == null || now >= cooldownEnd) {
                        availableElite.add(i);
                    }
                }
                if (!availableElite.isEmpty()) {
                    int index = availableElite.get(random.nextInt(availableElite.size()));
                    BlockPos pos = eliteSpawners.get(index);
                    setCageActive(level, pos);
                    spawnerCooldown.put(pos, now + SPAWNER_COOLDOWN_TICKS);
                    scheduleSpawn(level, pos, (EntityType<? extends LivingEntity>) type.getEliteEntityType(), true);
                    usedEliteIndices.add(index);
                    toSpawnElite--;
                }
            } else if (toSpawnNormal > 0) {
                List<BlockPos> availableNormal = new ArrayList<>();
                for (BlockPos pos : normalSpawners) {
                    Long cooldownEnd = spawnerCooldown.get(pos);
                    if (cooldownEnd == null || now >= cooldownEnd) {
                        availableNormal.add(pos);
                    }
                }
                if (!availableNormal.isEmpty()) {
                    BlockPos pos = availableNormal.get(random.nextInt(availableNormal.size()));
                    setCageActive(level, pos);
                    spawnerCooldown.put(pos, now + SPAWNER_COOLDOWN_TICKS);
                    scheduleSpawn(level, pos, getRandomNormalType(random), false);
                    toSpawnNormal--;
                }
            }
            spawned++;
        }
    }

    private void setCageActive(ServerLevel level, BlockPos pos) {
        BlockState state = level.getBlockState(pos);
        if (state.getBlock() instanceof ChallengeCageBlock && !state.getValue(ChallengeCageBlock.ACTIVE)) {
            level.setBlock(pos, state.setValue(ChallengeCageBlock.ACTIVE, true), 3);
            level.playSound(null, pos, SoundEvents.TRIAL_SPAWNER_OPEN_SHUTTER, SoundSource.BLOCKS, 1.0f, 1.0f);
            activeCageStartTime.put(pos, level.getGameTime());
            level.scheduleTick(pos, state.getBlock(), ACTIVE_DURATION_TICKS);
        }
    }

    private void scheduleSpawn(ServerLevel level, BlockPos pos, EntityType<? extends LivingEntity> type, boolean isElite) {
        ScheduledSpawn scheduled = new ScheduledSpawn();
        scheduled.pos = pos;
        scheduled.type = type;
        scheduled.isElite = isElite;
        scheduled.spawnTime = level.getGameTime() + SPAWN_DELAY_TICKS;
        pendingSpawns.add(scheduled);
    }

    private void executeSpawn(ServerLevel level, BlockPos pos, EntityType<? extends LivingEntity> type, boolean isElite) {
        if (type == null) return;
        LivingEntity mob = type.create(level);
        if (mob != null) {
            if (mob instanceof Mob) {
                ((Mob) mob).setPersistenceRequired();
            }
            mob.addEffect(new MobEffectInstance(MobEffects.GLOWING, -1, 0, false, false, true));
            mob.setPos(pos.getX() + 0.5, pos.getY() + 1, pos.getZ() + 0.5);
            CompoundTag data = mob.getPersistentData();
            data.putString("RoomId", roomId);
            data.putBoolean("IsElite", isElite);
            if (isElite) {
                mob.addEffect(new MobEffectInstance(ModEffects.KINGS_AURA, -1, 0, false, false, true));
            }
            level.addFreshEntity(mob);
            level.playSound(null, pos, SoundEvents.TRIAL_SPAWNER_SPAWN_MOB, SoundSource.BLOCKS, 1.0f, 1.0f);
        }
    }

    private EntityType<? extends LivingEntity> getRandomNormalType(RandomSource random) {
        if (type == ChallengeType.WARRIOR) {
            return random.nextBoolean() ? ModEntities.SKELETON_ARCHER.get() : ModEntities.SKELETON_WARLOCK.get();
        } else if (type == ChallengeType.ARCHER) {
            return random.nextBoolean() ? ModEntities.SKELETON_WARRIOR.get() : ModEntities.SKELETON_WARLOCK.get();
        } else {
            return random.nextBoolean() ? ModEntities.SKELETON_WARRIOR.get() : ModEntities.SKELETON_ARCHER.get();
        }
    }

    private int getUnusedEliteIndex(RandomSource random) {
        List<Integer> available = new ArrayList<>();
        for (int i = 0; i < eliteSpawners.size(); i++) {
            if (!usedEliteIndices.contains(i)) available.add(i);
        }
        if (available.isEmpty()) return -1;
        return available.get(random.nextInt(available.size()));
    }

    private void grantReward(ServerLevel level) {
        clearAllAltars(level);

        List<ItemStack> rewards = RewardConfigLoader.getRewards(type);

        if (level.getBlockEntity(altarPos) instanceof ChallengeAltarBlockEntity altar) {
            altar.setCooldown(120 * 60 * 20);
            altar.startRewardSequence(rewards, type);
        }

        level.playSound(null, altarPos, SoundEvents.UI_TOAST_CHALLENGE_COMPLETE, SoundSource.BLOCKS, 1.0f, 1.0f);

        BlockEntity be = level.getBlockEntity(altarPos);
        ChallengeType preset = ChallengeType.NONE;
        if (be instanceof ChallengeAltarBlockEntity altar) {
            preset = altar.getPresetType();
        }
        level.setBlock(altarPos, level.getBlockState(altarPos).setValue(ChallengeAltarBlock.TYPE, preset), 3);

        RoomManager.get(level).deactivateBoss(roomId, level);
        ChallengeManager.get(level).removeChallenge(roomId);

        sendClearPacket(level);
    }

    public void failChallenge(ServerLevel level) {
        clearAllAltars(level);

        // 收集需要删除的实体
        List<Entity> toRemove = new ArrayList<>();
        for (Entity entity : level.getEntities().getAll()) {
            if (entity.getPersistentData().getString("RoomId").equals(roomId)) {
                toRemove.add(entity);
            }
        }
        // 统一删除
        for (Entity entity : toRemove) {
            entity.discard();
        }

        BlockEntity be = level.getBlockEntity(altarPos);
        ChallengeType preset = ChallengeType.NONE;
        if (be instanceof ChallengeAltarBlockEntity altar) {
            preset = altar.getPresetType();
        }
        level.setBlock(altarPos, level.getBlockState(altarPos).setValue(ChallengeAltarBlock.TYPE, preset), 3);
        RoomManager.get(level).deactivateBoss(roomId, level);
        ChallengeManager.get(level).removeChallenge(roomId);

        // 发送清除包
        sendClearPacket(level);
    }

    private boolean isPlayerInRoom(Player player, String roomId) {
        if (player.level().isClientSide) return false;
        ServerLevel level = (ServerLevel) player.level();
        AABB area = getRoomArea(level);
        return area != null && area.contains(player.getX(), player.getY(), player.getZ());
    }

    public void save(CompoundTag tag) {
        tag.putString("roomId", roomId);
        tag.putLong("altarPos", altarPos.asLong());
        tag.putString("type", type.getSerializedName());
        tag.putBoolean("active", active);
        tag.putInt("wave", wave);
        tag.putInt("remainingElite", remainingElite);
        tag.putInt("remainingNormal", remainingNormal);
        tag.putInt("toSpawnElite", toSpawnElite);
        tag.putInt("toSpawnNormal", toSpawnNormal);
        tag.putLong("waveStartTime", waveStartTime);
        tag.putLong("lastSpawnTime", lastSpawnTime);
        ListTag usedList = new ListTag();
        for (int i : usedEliteIndices) {
            usedList.add(IntTag.valueOf(i));
        }
        tag.put("usedEliteIndices", usedList);
        tag.putFloat("lastWaveTimeProgress", lastWaveTimeProgress);
    }

    public void loadFromNbt(CompoundTag tag, HolderLookup.Provider registries) {
        roomId = tag.getString("roomId");
        altarPos = BlockPos.of(tag.getLong("altarPos"));
        String typeName = tag.getString("type");
        type = ChallengeType.valueOf(typeName.toUpperCase(Locale.ROOT));  // 关键修改
        active = tag.getBoolean("active");
        wave = tag.getInt("wave");
        remainingElite = tag.getInt("remainingElite");
        remainingNormal = tag.getInt("remainingNormal");
        toSpawnElite = tag.getInt("toSpawnElite");
        toSpawnNormal = tag.getInt("toSpawnNormal");
        waveStartTime = tag.getLong("waveStartTime");
        lastSpawnTime = tag.getLong("lastSpawnTime");
        ListTag usedList = tag.getList("usedEliteIndices", Tag.TAG_INT);
        usedEliteIndices.clear();
        for (int i = 0; i < usedList.size(); i++) {
            usedEliteIndices.add(usedList.getInt(i));
        }
        if (tag.contains("lastWaveTimeProgress")) {
            lastWaveTimeProgress = tag.getFloat("lastWaveTimeProgress");
        } else {
            lastWaveTimeProgress = 1.0f;
        }
    }
    private void updatePlayersInRoom(ServerLevel level) {
        if (bossEvent == null) return;
        AABB area = getRoomArea(level);
        if (area == null) return;

        Set<ServerPlayer> playersInRoom = new HashSet<>();
        for (ServerPlayer player : level.players()) {
            if (area.contains(player.getX(), player.getY(), player.getZ())) {
                playersInRoom.add(player);
            }
        }

        List<ServerPlayer> currentPlayers = new ArrayList<>(bossEvent.getPlayers());
        for (ServerPlayer player : currentPlayers) {
            if (!playersInRoom.contains(player)) {
                bossEvent.removePlayer(player);
                // 发送清除包给离开的玩家，让客户端隐藏 UI
                ChallengeDataPacket clearPacket = new ChallengeDataPacket(roomId, type, 0, 0.0f, 0.0f, 0.0f, 3);
                player.connection.send(clearPacket);
            }
        }

        for (ServerPlayer player : playersInRoom) {
            if (!bossEvent.getPlayers().contains(player)) {
                bossEvent.addPlayer(player);
            }
        }
    }
    public void load(CompoundTag tag, HolderLookup.Provider registries) {
    }
}