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;
}
}
}