diff --git a/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java b/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java index eb33b2b6..776812ce 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java @@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.UTags; +import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.trinkets.TrinketsDelegate; import net.minecraft.entity.Entity; @@ -14,7 +15,9 @@ import net.minecraft.entity.effect.StatusEffect; import net.minecraft.entity.effect.StatusEffectInstance; import net.minecraft.entity.effect.StatusEffectCategory; import net.minecraft.entity.effect.StatusEffects; +import net.minecraft.util.math.BlockPos; import net.minecraft.world.LightType; +import net.minecraft.world.World; public class SunBlindnessStatusEffect extends StatusEffect { public static final int MAX_DURATION = 250; @@ -27,7 +30,7 @@ public class SunBlindnessStatusEffect extends StatusEffect { public void applyUpdateEffect(LivingEntity entity, int amplifier) { StatusEffectInstance state = entity.getStatusEffect(this); - if (state == null) { + if (state == null || isSunImmune(entity)) { return; } @@ -50,9 +53,15 @@ public class SunBlindnessStatusEffect extends StatusEffect { return duration > 0; } + public static boolean isSunImmune(LivingEntity entity) { + return entity.hasStatusEffect(StatusEffects.BLINDNESS) + || entity.hasPortalCooldown() + || Pony.of(entity).map(pony -> pony.isSunImmune()).orElse(false); + } + public static boolean hasSunExposure(LivingEntity entity) { - if (entity.hasPortalCooldown()) { + if (isSunImmune(entity)) { return false; } @@ -64,21 +73,21 @@ public class SunBlindnessStatusEffect extends StatusEffect { return true; } - - if (entity.getEquippedStack(EquipmentSlot.HEAD).isIn(UTags.SHADES)) { + if (entity.getEquippedStack(EquipmentSlot.HEAD).isIn(UTags.SHADES) + || TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.FACE).anyMatch(i -> i.isIn(UTags.SHADES)) + || entity.isSubmergedInWater()) { return false; } - if (TrinketsDelegate.getInstance().getEquipped(entity, TrinketsDelegate.FACE).anyMatch(i -> i.isIn(UTags.SHADES))) { - return false; + return isPositionExposedToSun(entity.world, entity.getBlockPos()); + + } + + public static boolean isPositionExposedToSun(World world, BlockPos pos) { + if (world.isClient) { + world.calculateAmbientDarkness(); } - if (entity.world.isClient) { - entity.world.calculateAmbientDarkness(); - } - - int light = entity.world.getLightLevel(LightType.SKY, entity.getBlockPos()); - - return !(entity.isSubmergedInWater() || light < 12 || entity.world.isRaining() || entity.world.isThundering() || !entity.world.isDay()); + return world.getDimension().hasSkyLight() && world.getLightLevel(LightType.SKY, pos) >= 12 && !world.isRaining() && !world.isThundering() && world.isDay(); } } 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 fe0b3e0c..445cfc8f 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -59,6 +59,7 @@ import net.minecraft.sound.SoundEvents; import net.minecraft.text.Text; import net.minecraft.util.Hand; import net.minecraft.util.math.*; +import net.minecraft.world.GameMode; public class Pony extends Living implements Transmittable, Copieable, UpdateCallback { @@ -107,6 +108,7 @@ public class Pony extends Living implements Transmittable, Copieab private int ticksInSun; private boolean hasShades; + private int ticksSunImmunity = 20; private Animation animation = Animation.NONE; private int animationMaxDuration; @@ -221,6 +223,10 @@ public class Pony extends Living implements Transmittable, Copieab this.invisible = invisible; } + public boolean isSunImmune() { + return ticksSunImmunity > 0; + } + @Override public Affinity getAffinity() { return getSpecies().getAffinity(); @@ -270,6 +276,15 @@ public class Pony extends Living implements Transmittable, Copieab return interpolator; } + public void onSpawn() { + if (entity.world instanceof ServerWorld sw + && getSpecies() == Race.BAT + && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE + && SunBlindnessStatusEffect.isPositionExposedToSun(sw, getOrigin())) { + SpawnLocator.selectSpawnPosition(sw, entity); + } + } + @Override public boolean beforeUpdate() { @@ -355,6 +370,10 @@ public class Pony extends Living implements Transmittable, Copieab @Override public void tick() { + if (ticksSunImmunity > 0) { + ticksSunImmunity--; + } + if (animationDuration >= 0 && --animationDuration <= 0) { setAnimation(Animation.NONE); } @@ -618,6 +637,7 @@ public class Pony extends Living implements Transmittable, Copieab mana.getXp().set(oldPlayer.getMagicalReserves().getXp().get()); advancementProgress.putAll(oldPlayer.getAdvancementProgress()); setDirty(); + onSpawn(); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java new file mode 100644 index 00000000..86fb9993 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java @@ -0,0 +1,68 @@ +package com.minelittlepony.unicopia.entity.player; + +import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect; + +import net.minecraft.entity.player.PlayerEntity; +import net.minecraft.server.network.SpawnLocating; +import net.minecraft.server.world.ServerWorld; +import net.minecraft.util.math.*; +import net.minecraft.util.math.random.Random; + +public class SpawnLocator extends SpawnLocating { + + public static void selectSpawnPosition(ServerWorld world, PlayerEntity entity) { + BlockPos spawnPos = world.getSpawnPos(); + int spawnRadius = Math.min( + MathHelper.floor(world.getWorldBorder().getDistanceInsideBorder(spawnPos.getX(), spawnPos.getZ())), + Math.max(0, world.getServer().getSpawnRadius(world)) + ); + + long l = spawnRadius * 2 + 1; + long m = l * l; + int spawnArea = m > Integer.MAX_VALUE ? Integer.MAX_VALUE : (int)m; + int offsetMultiplier = spawnArea <= 16 ? spawnArea - 1 : 17; + int rng = Random.create().nextInt(spawnArea); + + BlockPos.Mutable mutable = new BlockPos.Mutable(); + + for (int attempt = 0; attempt < spawnArea; attempt++) { + int tile = (rng + offsetMultiplier * attempt) % spawnArea; + int x = tile % (spawnRadius * 2 + 1); + int z = tile / (spawnRadius * 2 + 1); + + BlockPos candidatePos = findOverworldSpawn(world, + spawnPos.getX() + x - spawnRadius, + spawnPos.getZ() + z - spawnRadius + ); + + if (candidatePos == null) { + continue; + } + + mutable.set(candidatePos); + mutable.move(0, -1, 0); + + while (!world.isAir(mutable) && mutable.getY() >= spawnPos.getY() - spawnRadius * 2 && !world.isOutOfHeightLimit(mutable)) { + mutable.move(0, -1, 0); + } + while (world.isAir(mutable) && mutable.getY() >= spawnPos.getY() - spawnRadius * 2 && !world.isOutOfHeightLimit(mutable)) { + mutable.move(0, -1, 0); + } + if (!world.isAir(mutable)) { + mutable.move(0, 1, 0); + } + + if (!world.getFluidState(mutable).isEmpty()) { + continue; + } + + entity.refreshPositionAndAngles(mutable, 0, 0); + + if (!world.isSpaceEmpty(entity) || SunBlindnessStatusEffect.isPositionExposedToSun(world, mutable)) { + continue; + } + + break; + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java b/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java index 24ca0d46..40bec613 100644 --- a/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/mixin/MixinServerPlayerEntity.java @@ -9,7 +9,6 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfo; import com.minelittlepony.unicopia.entity.PonyContainer; import com.minelittlepony.unicopia.entity.duck.ServerPlayerEntityDuck; import com.minelittlepony.unicopia.entity.player.Pony; - import net.minecraft.entity.player.PlayerEntity; import net.minecraft.screen.ScreenHandlerListener; import net.minecraft.server.network.ServerPlayerEntity; diff --git a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java index b244e6e7..fd583928 100644 --- a/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java +++ b/src/main/java/com/minelittlepony/unicopia/network/MsgRequestSpeciesChange.java @@ -47,6 +47,10 @@ public class MsgRequestSpeciesChange implements Packet { if (force || player.getActualSpecies().isDefault() || (player.getActualSpecies() == worldDefaultRace && !player.isSpeciesPersisted())) { player.setSpecies(newRace.isPermitted(sender) ? newRace : worldDefaultRace); + + if (force) { + player.onSpawn(); + } } Channel.SERVER_PLAYER_CAPABILITIES.send(sender, new MsgPlayerCapabilities(true, player));