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 plan(BlockPos center) { Map 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 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 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 planned = plan(center); List placed = new ArrayList<>(); try { for (Map.Entry 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; } } }