minecraft-source/src/net/minecraft/world/level/NaturalSpawner.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();
}
}