249 lines
14 KiB
Java
249 lines
14 KiB
Java
package net.minecraft.world.level;
|
|
|
|
import org.apache.logging.log4j.LogManager;
|
|
import net.minecraft.world.level.pathfinder.PathComputationType;
|
|
import net.minecraft.tags.FluidTags;
|
|
import net.minecraft.tags.BlockTags;
|
|
import net.minecraft.world.level.material.FluidState;
|
|
import net.minecraft.world.level.levelgen.Heightmap;
|
|
import javax.annotation.Nullable;
|
|
import java.util.List;
|
|
import net.minecraft.util.WeighedRandom;
|
|
import java.util.Random;
|
|
import net.minecraft.world.entity.EntityType;
|
|
import net.minecraft.world.entity.player.Player;
|
|
import net.minecraft.world.entity.SpawnGroupData;
|
|
import net.minecraft.world.level.biome.Biome;
|
|
import net.minecraft.world.level.block.state.BlockState;
|
|
import net.minecraft.world.level.chunk.ChunkGenerator;
|
|
import net.minecraft.nbt.CompoundTag;
|
|
import net.minecraft.world.entity.Entity;
|
|
import net.minecraft.core.Registry;
|
|
import net.minecraft.world.entity.Mob;
|
|
import net.minecraft.world.entity.MobSpawnType;
|
|
import net.minecraft.world.entity.SpawnPlacements;
|
|
import java.util.Objects;
|
|
import net.minecraft.core.Position;
|
|
import net.minecraft.world.phys.Vec3;
|
|
import net.minecraft.util.Mth;
|
|
import net.minecraft.core.BlockPos;
|
|
import net.minecraft.world.level.chunk.LevelChunk;
|
|
import net.minecraft.server.level.ServerLevel;
|
|
import net.minecraft.world.entity.MobCategory;
|
|
import org.apache.logging.log4j.Logger;
|
|
|
|
public final class NaturalSpawner {
|
|
private static final Logger LOGGER;
|
|
|
|
public static void spawnCategoryForChunk(final MobCategory aky, final ServerLevel xd, final LevelChunk cai, final BlockPos fk) {
|
|
final ChunkGenerator<?> bzx5 = xd.getChunkSource().getGenerator();
|
|
int integer6 = 0;
|
|
final BlockPos fk2 = getRandomPosWithin(xd, cai);
|
|
final int integer7 = fk2.getX();
|
|
final int integer8 = fk2.getY();
|
|
final int integer9 = fk2.getZ();
|
|
if (integer8 < 1) {
|
|
return;
|
|
}
|
|
final BlockState byg11 = cai.getBlockState(fk2);
|
|
if (byg11.isRedstoneConductor(cai, fk2)) {
|
|
return;
|
|
}
|
|
final BlockPos.MutableBlockPos a12 = new BlockPos.MutableBlockPos();
|
|
for (int integer10 = 0; integer10 < 3; ++integer10) {
|
|
int integer11 = integer7;
|
|
int integer12 = integer9;
|
|
final int integer13 = 6;
|
|
Biome.SpawnerData e17 = null;
|
|
SpawnGroupData alj18 = null;
|
|
int integer14 = Mth.ceil(Math.random() * 4.0);
|
|
int integer15 = 0;
|
|
for (int integer16 = 0; integer16 < integer14; ++integer16) {
|
|
integer11 += xd.random.nextInt(6) - xd.random.nextInt(6);
|
|
integer12 += xd.random.nextInt(6) - xd.random.nextInt(6);
|
|
a12.set(integer11, integer8, integer12);
|
|
final float float22 = integer11 + 0.5f;
|
|
final float float23 = integer12 + 0.5f;
|
|
final Player ayg24 = xd.getNearestPlayerIgnoreY(float22, float23, -1.0);
|
|
if (ayg24 != null) {
|
|
final double double25 = ayg24.distanceToSqr(float22, integer8, float23);
|
|
if (double25 > 576.0) {
|
|
if (!fk.closerThan(new Vec3(float22, integer8, float23), 24.0)) {
|
|
final ChunkPos bje27 = new ChunkPos(a12);
|
|
if (Objects.equals(bje27, cai.getPos()) || xd.getChunkSource().isEntityTickingChunk(bje27)) {
|
|
if (e17 == null) {
|
|
e17 = getRandomSpawnMobAt(bzx5, aky, xd.random, a12);
|
|
if (e17 == null) {
|
|
break;
|
|
}
|
|
integer14 = e17.minCount + xd.random.nextInt(1 + e17.maxCount - e17.minCount);
|
|
}
|
|
if (e17.type.getCategory() != MobCategory.MISC) {
|
|
if (e17.type.canSpawnFarFromPlayer() || double25 <= 16384.0) {
|
|
final EntityType<?> akr28 = e17.type;
|
|
if (akr28.canSummon()) {
|
|
if (canSpawnMobAt(bzx5, aky, e17, a12)) {
|
|
final SpawnPlacements.Type c29 = SpawnPlacements.getPlacementType(akr28);
|
|
if (isSpawnPositionOk(c29, xd, a12, akr28)) {
|
|
if (SpawnPlacements.checkSpawnRules(akr28, xd, MobSpawnType.NATURAL, a12, xd.random)) {
|
|
if (xd.noCollision(akr28.getAABB(float22, integer8, float23))) {
|
|
Mob akx30;
|
|
try {
|
|
final Entity akn31 = (Entity)akr28.create(xd);
|
|
if (!(akn31 instanceof Mob)) {
|
|
throw new IllegalStateException("Trying to spawn a non-mob: " + Registry.ENTITY_TYPE.getKey(akr28));
|
|
}
|
|
akx30 = (Mob)akn31;
|
|
}
|
|
catch (Exception exception31) {
|
|
NaturalSpawner.LOGGER.warn("Failed to create mob", (Throwable)exception31);
|
|
return;
|
|
}
|
|
akx30.moveTo(float22, integer8, float23, xd.random.nextFloat() * 360.0f, 0.0f);
|
|
if (double25 <= 16384.0 || !akx30.removeWhenFarAway(double25)) {
|
|
if (akx30.checkSpawnRules(xd, MobSpawnType.NATURAL)) {
|
|
if (akx30.checkSpawnObstruction(xd)) {
|
|
alj18 = akx30.finalizeSpawn(xd, xd.getCurrentDifficultyAt(new BlockPos(akx30)), MobSpawnType.NATURAL, alj18, null);
|
|
++integer6;
|
|
++integer15;
|
|
xd.addFreshEntity(akx30);
|
|
if (integer6 >= akx30.getMaxSpawnClusterSize()) {
|
|
return;
|
|
}
|
|
if (akx30.isMaxGroupSizeReached(integer15)) {
|
|
break;
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
@Nullable
|
|
private static Biome.SpawnerData getRandomSpawnMobAt(final ChunkGenerator<?> bzx, final MobCategory aky, final Random random, final BlockPos fk) {
|
|
final List<Biome.SpawnerData> list5 = bzx.getMobsAt(aky, fk);
|
|
if (list5.isEmpty()) {
|
|
return null;
|
|
}
|
|
return WeighedRandom.<Biome.SpawnerData>getRandomItem(random, list5);
|
|
}
|
|
|
|
private static boolean canSpawnMobAt(final ChunkGenerator<?> bzx, final MobCategory aky, final Biome.SpawnerData e, final BlockPos fk) {
|
|
final List<Biome.SpawnerData> list5 = bzx.getMobsAt(aky, fk);
|
|
return !list5.isEmpty() && list5.contains(e);
|
|
}
|
|
|
|
private static BlockPos getRandomPosWithin(final Level bjt, final LevelChunk cai) {
|
|
final ChunkPos bje3 = cai.getPos();
|
|
final int integer4 = bje3.getMinBlockX() + bjt.random.nextInt(16);
|
|
final int integer5 = bje3.getMinBlockZ() + bjt.random.nextInt(16);
|
|
final int integer6 = cai.getHeight(Heightmap.Types.WORLD_SURFACE, integer4, integer5) + 1;
|
|
final int integer7 = bjt.random.nextInt(integer6 + 1);
|
|
return new BlockPos(integer4, integer7, integer5);
|
|
}
|
|
|
|
public static boolean isValidEmptySpawnBlock(final BlockGetter bjd, final BlockPos fk, final BlockState byg, final FluidState cog) {
|
|
return !byg.isCollisionShapeFullBlock(bjd, fk) && !byg.isSignalSource() && cog.isEmpty() && !byg.is(BlockTags.RAILS);
|
|
}
|
|
|
|
public static boolean isSpawnPositionOk(final SpawnPlacements.Type c, final LevelReader bjw, final BlockPos fk, @Nullable final EntityType<?> akr) {
|
|
if (c == SpawnPlacements.Type.NO_RESTRICTIONS) {
|
|
return true;
|
|
}
|
|
if (akr == null || !bjw.getWorldBorder().isWithinBounds(fk)) {
|
|
return false;
|
|
}
|
|
final BlockState byg5 = bjw.getBlockState(fk);
|
|
final FluidState cog6 = bjw.getFluidState(fk);
|
|
final BlockPos fk2 = fk.above();
|
|
final BlockPos fk3 = fk.below();
|
|
switch (c) {
|
|
case IN_WATER: {
|
|
return cog6.is(FluidTags.WATER) && bjw.getFluidState(fk3).is(FluidTags.WATER) && !bjw.getBlockState(fk2).isRedstoneConductor(bjw, fk2);
|
|
}
|
|
default: {
|
|
final BlockState byg6 = bjw.getBlockState(fk3);
|
|
return byg6.isValidSpawn(bjw, fk3, akr) && isValidEmptySpawnBlock(bjw, fk, byg5, cog6) && isValidEmptySpawnBlock(bjw, fk2, bjw.getBlockState(fk2), bjw.getFluidState(fk2));
|
|
}
|
|
}
|
|
}
|
|
|
|
public static void spawnMobsForChunkGeneration(final LevelAccessor bju, final Biome bkq, final int integer3, final int integer4, final Random random) {
|
|
final List<Biome.SpawnerData> list6 = bkq.getMobs(MobCategory.CREATURE);
|
|
if (list6.isEmpty()) {
|
|
return;
|
|
}
|
|
final int integer5 = integer3 << 4;
|
|
final int integer6 = integer4 << 4;
|
|
while (random.nextFloat() < bkq.getCreatureProbability()) {
|
|
final Biome.SpawnerData e9 = WeighedRandom.<Biome.SpawnerData>getRandomItem(random, list6);
|
|
final int integer7 = e9.minCount + random.nextInt(1 + e9.maxCount - e9.minCount);
|
|
SpawnGroupData alj11 = null;
|
|
int integer8 = integer5 + random.nextInt(16);
|
|
int integer9 = integer6 + random.nextInt(16);
|
|
final int integer10 = integer8;
|
|
final int integer11 = integer9;
|
|
for (int integer12 = 0; integer12 < integer7; ++integer12) {
|
|
boolean boolean17 = false;
|
|
for (int integer13 = 0; !boolean17 && integer13 < 4; ++integer13) {
|
|
final BlockPos fk19 = getTopNonCollidingPos(bju, e9.type, integer8, integer9);
|
|
if (e9.type.canSummon() && isSpawnPositionOk(SpawnPlacements.Type.ON_GROUND, bju, fk19, e9.type)) {
|
|
final float float20 = e9.type.getWidth();
|
|
final double double21 = Mth.clamp(integer8, integer5 + (double)float20, integer5 + 16.0 - float20);
|
|
final double double22 = Mth.clamp(integer9, integer6 + (double)float20, integer6 + 16.0 - float20);
|
|
if (!bju.noCollision(e9.type.getAABB(double21, fk19.getY(), double22))) {
|
|
continue;
|
|
}
|
|
if (!SpawnPlacements.<Entity>checkSpawnRules(e9.type, bju, MobSpawnType.CHUNK_GENERATION, new BlockPos(double21, fk19.getY(), double22), bju.getRandom())) {
|
|
continue;
|
|
}
|
|
Entity akn25;
|
|
try {
|
|
akn25 = (Entity)e9.type.create(bju.getLevel());
|
|
}
|
|
catch (Exception exception26) {
|
|
NaturalSpawner.LOGGER.warn("Failed to create mob", (Throwable)exception26);
|
|
continue;
|
|
}
|
|
akn25.moveTo(double21, fk19.getY(), double22, random.nextFloat() * 360.0f, 0.0f);
|
|
if (akn25 instanceof Mob) {
|
|
final Mob akx26 = (Mob)akn25;
|
|
if (akx26.checkSpawnRules(bju, MobSpawnType.CHUNK_GENERATION) && akx26.checkSpawnObstruction(bju)) {
|
|
alj11 = akx26.finalizeSpawn(bju, bju.getCurrentDifficultyAt(new BlockPos(akx26)), MobSpawnType.CHUNK_GENERATION, alj11, null);
|
|
bju.addFreshEntity(akx26);
|
|
boolean17 = true;
|
|
}
|
|
}
|
|
}
|
|
for (integer8 += random.nextInt(5) - random.nextInt(5), integer9 += random.nextInt(5) - random.nextInt(5); integer8 < integer5 || integer8 >= integer5 + 16 || integer9 < integer6 || integer9 >= integer6 + 16; integer8 = integer10 + random.nextInt(5) - random.nextInt(5), integer9 = integer11 + random.nextInt(5) - random.nextInt(5)) {}
|
|
}
|
|
}
|
|
}
|
|
}
|
|
|
|
private static BlockPos getTopNonCollidingPos(final LevelReader bjw, @Nullable final EntityType<?> akr, final int integer3, final int integer4) {
|
|
final BlockPos fk5 = new BlockPos(integer3, bjw.getHeight(SpawnPlacements.getHeightmapType(akr), integer3, integer4), integer4);
|
|
final BlockPos fk6 = fk5.below();
|
|
if (bjw.getBlockState(fk6).isPathfindable(bjw, fk6, PathComputationType.LAND)) {
|
|
return fk6;
|
|
}
|
|
return fk5;
|
|
}
|
|
|
|
static {
|
|
LOGGER = LogManager.getLogger();
|
|
}
|
|
}
|