diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java index 74dfa609..486e0316 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -54,6 +54,7 @@ import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtCompound; import net.minecraft.particle.ParticleTypes; import net.minecraft.registry.tag.DamageTypeTags; +import net.minecraft.registry.tag.FluidTags; import net.minecraft.server.network.ServerPlayerEntity; import net.minecraft.server.world.ServerWorld; import net.minecraft.text.Text; @@ -340,11 +341,12 @@ public class Pony extends Living implements Copyable, Update } public void onSpawn() { - if (entity.getWorld() instanceof ServerWorld sw - && getObservedSpecies() == Race.BAT - && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE - && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin())) { - SpawnLocator.selectSpawnPosition(sw, entity); + if (entity.getWorld() instanceof ServerWorld sw && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE) { + boolean mustAvoidSun = getObservedSpecies() == Race.BAT && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin()); + boolean mustAvoidAir = getCompositeRace().includes(Race.SEAPONY) && !sw.getFluidState(getOrigin()).isIn(FluidTags.WATER); + if (mustAvoidSun || mustAvoidAir) { + SpawnLocator.selectSpawnPosition(sw, entity, mustAvoidAir, mustAvoidSun); + } } ticksSunImmunity = INITIAL_SUN_IMMUNITY; } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java index 4c49f017..13d30bf3 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java @@ -1,37 +1,68 @@ package com.minelittlepony.unicopia.entity.player; -import java.util.Optional; - import com.minelittlepony.unicopia.util.MeteorlogicalUtil; -import net.minecraft.entity.ai.FuzzyPositions; +import net.minecraft.block.Block; +import net.minecraft.block.BlockState; import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.fluid.FluidState; +import net.minecraft.registry.tag.FluidTags; import net.minecraft.server.network.SpawnLocating; import net.minecraft.server.world.ServerWorld; import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; +import net.minecraft.world.Heightmap; +import net.minecraft.world.chunk.WorldChunk; public class SpawnLocator extends SpawnLocating { - - public static BlockPos findSafeSpawnLocation(ServerWorld world, int x, int z) { - return SpawnLocating.findOverworldSpawn(world, x, z); - } - - public static Optional fuzz(ServerWorld world, BlockPos pos, int horizontal, int vertical) { - - for (int attempt = 0; attempt < 6; attempt++) { - BlockPos target = FuzzyPositions.localFuzz(world.random, horizontal, vertical); - target = findSafeSpawnLocation(world, target.getX(), target.getZ()); - - if (target != null) { - return Optional.of(target); - } + private static BlockPos findSafeSpawnLocation(ServerWorld world, int x, int z, boolean avoidAir) { + if (!avoidAir) { + return findOverworldSpawn(world, x, z); } - return Optional.empty(); + boolean hasCeiling = world.getDimension().hasCeiling(); + WorldChunk chunk = world.getChunk(ChunkSectionPos.getSectionCoord(x), ChunkSectionPos.getSectionCoord(z)); + int startHeight = hasCeiling + ? world.getChunkManager().getChunkGenerator().getSpawnHeight(world) + : chunk.sampleHeightmap(Heightmap.Type.MOTION_BLOCKING, x & 0xF, z & 0xF); + if (startHeight < world.getBottomY()) { + return null; + } + + int terrainHeight = chunk.sampleHeightmap(Heightmap.Type.WORLD_SURFACE, x & 0xF, z & 0xF); + if (terrainHeight <= startHeight && terrainHeight > chunk.sampleHeightmap(Heightmap.Type.OCEAN_FLOOR, x & 0xF, z & 0xF)) { + return null; + } + + BlockPos.Mutable mutable = new BlockPos.Mutable(); + for (int y = startHeight + 1; y >= world.getBottomY(); --y) { + mutable.set(x, y, z); + BlockState state = world.getBlockState(mutable); + FluidState fluid = state.getFluidState(); + if (fluid.isEmpty()) { + continue; + } + if (!fluid.isIn(FluidTags.WATER)) { + break; + } + if (!Block.isFaceFullSquare(state.getCollisionShape(world, mutable), Direction.UP)) { + continue; + } + + return mutable.up().toImmutable(); + } + return null; } - public static void selectSpawnPosition(ServerWorld world, PlayerEntity entity) { + private static boolean checkAtmosphere(ServerWorld world, BlockPos pos, boolean avoidAir) { + if (avoidAir) { + return world.getFluidState(pos).isIn(FluidTags.WATER); + + } + return world.getFluidState(pos).isEmpty(); + } + + public static void selectSpawnPosition(ServerWorld world, PlayerEntity entity, boolean avoidAir, boolean avoidSun) { BlockPos spawnPos = world.getSpawnPos(); int spawnRadius = Math.min( MathHelper.floor(world.getWorldBorder().getDistanceInsideBorder(spawnPos.getX(), spawnPos.getZ())), @@ -53,7 +84,8 @@ public class SpawnLocator extends SpawnLocating { BlockPos candidatePos = findSafeSpawnLocation(world, spawnPos.getX() + x - spawnRadius, - spawnPos.getZ() + z - spawnRadius + spawnPos.getZ() + z - spawnRadius, + avoidAir ); if (candidatePos == null) { @@ -70,7 +102,7 @@ public class SpawnLocator extends SpawnLocating { mutable.move(0, -1, 0); } - if (!world.getFluidState(mutable).isEmpty()) { + if (!checkAtmosphere(world, mutable, avoidAir)) { continue; } @@ -78,13 +110,17 @@ public class SpawnLocator extends SpawnLocating { mutable.move(0, 1, 0); } - if (!world.getFluidState(mutable).isEmpty()) { + if (!checkAtmosphere(world, mutable, avoidAir)) { continue; } entity.refreshPositionAndAngles(mutable, 0, 0); - if (!world.isSpaceEmpty(entity) || MeteorlogicalUtil.isPositionExposedToSun(world, mutable)) { + if (!world.isSpaceEmpty(entity)) { + continue; + } + + if (avoidSun && MeteorlogicalUtil.isPositionExposedToSun(world, mutable)) { continue; }