Volver al índice

src/main/java/ar/com/companeros/horror/Meteorite.java

package ar.com.companeros.horror;

import ar.com.companeros.WorkArea;
import ar.com.companeros.identity.AgentRegistry;
import java.util.*;
import net.minecraft.core.BlockPos;
import net.minecraft.server.level.ServerLevel;
import net.minecraft.world.level.block.Blocks;
import net.minecraft.world.level.block.state.BlockState;
import net.minecraft.world.level.levelgen.Heightmap;

/** Depósito mineral real: sólo ocupa aire y nunca excava ni explota construcciones. */
public final class Meteorite {
    private Meteorite() { }
    public static Map<BlockPos, BlockState> plan(BlockPos center) {
        Map<BlockPos, BlockState> result = new LinkedHashMap<>();
        BlockState[] materials = { Blocks.GRANITE.defaultBlockState(), Blocks.DIORITE.defaultBlockState(),
            Blocks.TUFF.defaultBlockState(), Blocks.RAW_IRON_BLOCK.defaultBlockState(), Blocks.COBBLESTONE.defaultBlockState() };
        for (int y = 0; y <= 3; y++) for (int x = -3; x <= 3; x++) for (int z = -3; z <= 3; z++) {
            if (x * x + z * z + y * y > 10) continue;
            int material = Math.floorMod(x * 31 + z * 17 + y * 7, materials.length);
            result.put(center.offset(x, y, z), materials[material]);
        }
        return Map.copyOf(result);
    }
    public static boolean safeSite(ServerLevel level, BlockPos center, int safeRadius) {
        if (center.getY() < level.getMinBuildHeight() + 2 || center.getY() + 4 >= level.getMaxBuildHeight()) return false;
        if (level.players().stream().anyMatch(player -> player.blockPosition().distSqr(center) < (long) safeRadius * safeRadius)) return false;
        if (AgentRegistry.get(level.getServer()).entries().stream().anyMatch(agent -> agent.camp() != null
                && agent.camp().dimension().equals(level.dimension()) && agent.camp().pos().distSqr(center) < (long) safeRadius * safeRadius)) return false;
        Map<BlockPos, BlockState> plan = plan(center);
        for (BlockPos pos : plan.keySet()) {
            if (!level.hasChunkAt(pos) || !level.getWorldBorder().isWithinBounds(pos)
                    || !level.getBlockState(pos).isAir() || level.getBlockEntity(pos) != null
                    || WorkArea.get(level).contains(pos) || WorkArea.get(level).contains(pos.below())) return false;
        }
        // Un bloque protegido, contenedor o máquina cercano invalida toda la ubicación.
        for (BlockPos pos : BlockPos.betweenClosed(center.offset(-8, -3, -8), center.offset(8, 5, 8))) {
            if (!level.hasChunkAt(pos) || WorkArea.get(level).contains(pos) || level.getBlockEntity(pos) != null) return false;
        }
        return !level.getBlockState(center.below()).getCollisionShape(level, center.below()).isEmpty()
            && level.getFluidState(center.below()).isEmpty();
    }
    public static Optional<BlockPos> findSite(ServerLevel level, int safeRadius) {
        BlockPos origin = level.players().isEmpty() ? level.getSharedSpawnPos()
            : level.players().get(level.random.nextInt(level.players().size())).blockPosition();
        for (int attempt = 0; attempt < 32; attempt++) {
            int x = origin.getX() + level.random.nextInt(385) - 192;
            int z = origin.getZ() + level.random.nextInt(385) - 192;
            BlockPos probe = new BlockPos(x, level.getMinBuildHeight(), z);
            if (!level.hasChunkAt(probe)) continue;
            int y = level.getHeight(Heightmap.Types.MOTION_BLOCKING_NO_LEAVES, x, z);
            BlockPos center = new BlockPos(x, y, z);
            if (safeSite(level, center, safeRadius)) return Optional.of(center);
        }
        return Optional.empty();
    }
    public static boolean land(ServerLevel level, BlockPos center, int safeRadius) {
        if (!safeSite(level, center, safeRadius)) return false;
        Map<BlockPos, BlockState> planned = plan(center);
        List<BlockPos> placed = new ArrayList<>();
        try {
            for (Map.Entry<BlockPos, BlockState> entry : planned.entrySet()) {
                if (!level.getBlockState(entry.getKey()).isAir()
                        || !level.setBlock(entry.getKey(), entry.getValue(), 3)) throw new IllegalStateException("El terreno cambió durante la caída");
                placed.add(entry.getKey());
            }
            return true;
        } catch (RuntimeException failure) {
            for (BlockPos pos : placed) if (level.getBlockState(pos).equals(planned.get(pos)))
                level.setBlock(pos, Blocks.AIR.defaultBlockState(), 3);
            return false;
        }
    }
}