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 0ed77c13..2ef2627d 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/effect/SunBlindnessStatusEffect.java @@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.entity.Living; import com.minelittlepony.unicopia.entity.damage.UDamageTypes; +import com.minelittlepony.unicopia.entity.player.MeteorlogicalUtil; import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.trinkets.TrinketsDelegate; @@ -16,9 +17,6 @@ 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; @@ -80,14 +78,6 @@ public class SunBlindnessStatusEffect extends StatusEffect { return false; } - return isPositionExposedToSun(entity.getWorld(), entity.getBlockPos()); - } - - public static boolean isPositionExposedToSun(World world, BlockPos pos) { - if (world.isClient) { - world.calculateAmbientDarkness(); - } - - return world.getDimension().hasSkyLight() && world.getLightLevel(LightType.SKY, pos) >= 12 && !world.isRaining() && !world.isThundering() && world.isDay(); + return MeteorlogicalUtil.isPositionExposedToSun(entity.getWorld(), entity.getBlockPos()); } } diff --git a/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java b/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java index 7ee274d7..298e41e4 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/MeteorlogicalUtil.java @@ -2,7 +2,9 @@ package com.minelittlepony.unicopia.entity.player; import net.minecraft.entity.Entity; import net.minecraft.util.hit.HitResult.Type; +import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.MathHelper; +import net.minecraft.world.LightType; import net.minecraft.world.World; public interface MeteorlogicalUtil { @@ -19,8 +21,8 @@ public interface MeteorlogicalUtil { return false; } - // we translate sun angle to a scale of 0-1 (0=sunrise, 1=sunset, >1 nighttime) - final float skyAngle = ((entity.getWorld().getSkyAngle(1) + 0.25F) % 1F) * 2; + + final float skyAngle = getSkyAngle(entity.getWorld()); float playerYaw = MathHelper.wrapDegrees(entity.getHeadYaw()); float playerAngle = (-entity.getPitch(1) / 90F) / 2F; @@ -38,4 +40,40 @@ public interface MeteorlogicalUtil { && playerAngle > (skyAngle - 0.04F) && playerAngle < (skyAngle + 0.04F) && entity.raycast(100, 1, true).getType() == Type.MISS; } + + static float getSunIntensity(World world) { + float skyAngle = getSkyAngle(world); + if (skyAngle > 1) { + return 0; + } + + // intensity (0-1) has a peak at 0.5 (midday) + float intensity = MathHelper.cos((skyAngle - 0.5F) * MathHelper.PI); + + if (world.isRaining()) { + intensity *= 0.5; + } + if (world.isThundering()) { + intensity *= 0.5; + } + + return intensity; + } + + // we translate sun angle to a scale of 0-1 (0=sunrise, 1=sunset, >1 nighttime) + static float getSkyAngle(World world) { + return ((world.getSkyAngle(1) + 0.25F) % 1F) * 2; + } + + static boolean isPositionExposedToSun(World world, BlockPos pos) { + if (world.isClient) { + world.calculateAmbientDarkness(); + } + + 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/PlayerPhysics.java b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java index 03230c70..38b60422 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -553,7 +553,10 @@ public class PlayerPhysics extends EntityPhysics implements Tickab if (entity.getWorld().hasRain(entity.getBlockPos())) { applyTurbulance(velocity); } else { - descentRate -= WeatherConditions.getUpdraft(new BlockPos.Mutable().set(entity.getBlockPos()), entity.getWorld()) / 3F; + double updraft = WeatherConditions.getUpdraft(new BlockPos.Mutable().set(entity.getBlockPos()), entity.getWorld()) / 3F; + updraft *= 1 + motion; + velocity.y += updraft; + descentRate -= updraft; } descentRate += 0.001F; 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 cd9ad5e4..c83ca701 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/Pony.java @@ -327,7 +327,7 @@ public class Pony extends Living implements Copyable, Update if (entity.getWorld() instanceof ServerWorld sw && getObservedSpecies() == Race.BAT && sw.getServer().getSaveProperties().getGameMode() != GameMode.ADVENTURE - && SunBlindnessStatusEffect.isPositionExposedToSun(sw, getOrigin())) { + && MeteorlogicalUtil.isPositionExposedToSun(sw, getOrigin())) { SpawnLocator.selectSpawnPosition(sw, entity); } 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 dabb4533..f69e48ac 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/SpawnLocator.java @@ -2,8 +2,6 @@ package com.minelittlepony.unicopia.entity.player; import java.util.Optional; -import com.minelittlepony.unicopia.entity.effect.SunBlindnessStatusEffect; - import net.minecraft.entity.ai.FuzzyPositions; import net.minecraft.entity.player.PlayerEntity; import net.minecraft.server.network.SpawnLocating; @@ -84,7 +82,7 @@ public class SpawnLocator extends SpawnLocating { entity.refreshPositionAndAngles(mutable, 0, 0); - if (!world.isSpaceEmpty(entity) || SunBlindnessStatusEffect.isPositionExposedToSun(world, mutable)) { + if (!world.isSpaceEmpty(entity) || MeteorlogicalUtil.isPositionExposedToSun(world, mutable)) { continue; } diff --git a/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java b/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java index a58cff3e..535e3c66 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java @@ -1,12 +1,14 @@ package com.minelittlepony.unicopia.server.world; import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.entity.player.MeteorlogicalUtil; import com.minelittlepony.unicopia.util.Tickable; import net.minecraft.block.BlockState; import net.minecraft.block.Blocks; import net.minecraft.nbt.NbtCompound; import net.minecraft.registry.tag.BlockTags; +import net.minecraft.registry.tag.FluidTags; import net.minecraft.util.Identifier; import net.minecraft.util.math.*; import net.minecraft.util.math.random.Random; @@ -15,23 +17,23 @@ import net.minecraft.world.PersistentState; import net.minecraft.world.World; public class WeatherConditions extends PersistentState implements Tickable { - public static final TwoDimensionalField HEIGHT_MAP_FIELD = (world, pos) -> { - return world.getTopY(Type.WORLD_SURFACE_WG, pos.getX(), pos.getZ()); - }; - public static final TwoDimensionalField THERMAL_FIELD = (world, pos) -> { - return world.getBiome(pos).value().getTemperature() + (float)getUpdraft(pos, world); - }; - public static final TwoDimensionalField LOCAL_ALTITUDE_FIELD = (world, pos) -> { + public static final Plane HEIGHT_MAP_FIELD = (world, pos) -> world.getTopY(Type.WORLD_SURFACE_WG, pos.getX(), pos.getZ()); + public static final Plane THERMAL_FIELD = (world, pos) -> (float)getUpdraft(pos, world); + public static final Plane LOCAL_ALTITUDE_FIELD = (world, pos) -> { if (!world.isAir(pos)) { return 0; } - return pos.getY() - getSurfaceBelow(pos, world); + int y = pos.getY(); + do { + pos.move(Direction.DOWN); + } while (world.isAir(pos) && world.isInBuildLimit(pos)); + return y - pos.getY(); }; - public static final double FIRE_UPDRAFT = 0.3; - public static final double SAND_UPDRAFT = 0.13; - public static final double SOUL_SAND_UPDRAFT = -0.13; - public static final double ICE_UPDRAFT = -0.10; + public static final double FIRE_UPDRAFT = 0.13; + public static final double SAND_UPDRAFT = 0.03; + public static final double SOUL_SAND_UPDRAFT = -0.03; + public static final double ICE_UPDRAFT = 0; public static final double VOID_UPDRAFT = -0.23; public static final float MAX_UPDRAFT_HEIGHT = 20; @@ -108,47 +110,57 @@ public class WeatherConditions extends PersistentState implements Tickable { public static Vec3d getAirflow(BlockPos pos, World world) { BlockPos.Mutable probedPosition = new BlockPos.Mutable(); - final float localAltitude = LOCAL_ALTITUDE_FIELD.getValue(world, probedPosition.set(pos)); - final float terrainFactor = Math.min(MAX_TERRAIN_HEIGHT, localAltitude) / MAX_TERRAIN_HEIGHT; - final float windFactor = Math.min(MAX_WIND_HEIGHT, localAltitude) / MAX_WIND_HEIGHT; + final float terrainFactor = getScaledDistanceFromTerrain(probedPosition.set(pos), world, MAX_TERRAIN_HEIGHT); + final float windFactor = getScaledDistanceFromTerrain(probedPosition.set(pos), world, MAX_WIND_HEIGHT); Vec3d terrainGradient = LOCAL_ALTITUDE_FIELD.computeAverage(world, pos, probedPosition).multiply(1 - terrainFactor); - Vec3d thermalGradient = THERMAL_FIELD.computeAverage(world, pos, probedPosition).multiply(terrainFactor); - Vec3d wind = get(world).getWindDirection().multiply(1 - windFactor); + Vec3d thermalGradient = THERMAL_FIELD.computeAverage(world, pos, probedPosition).multiply(1 - terrainFactor); + Vec3d wind = get(world).getWindDirection().multiply(windFactor); return terrainGradient .add(thermalGradient) .add(wind) .normalize() - .add(0, getUpdraft(probedPosition.set(pos), world), 0) - .multiply(localAltitude / MAX_WIND_HEIGHT); + .multiply(windFactor); } public static double getUpdraft(BlockPos.Mutable pos, World world) { - final float ratio = 1 - Math.min(MAX_UPDRAFT_HEIGHT, pos.getY() - getSurfaceBelow(pos, world)) / MAX_UPDRAFT_HEIGHT; + double factor = 1 - getScaledDistanceFromTerrain(pos, world, MAX_UPDRAFT_HEIGHT); + return factor * getMaterialSurfaceTemperature(pos, world); + } + private static float getScaledDistanceFromTerrain(BlockPos.Mutable pos, World world, float maxDistance) { + return Math.min(maxDistance, LOCAL_ALTITUDE_FIELD.getValue(world, pos)) / maxDistance; + } + + private static double getMaterialSurfaceTemperature(BlockPos.Mutable pos, World world) { BlockState state = world.getBlockState(pos); + if (state.isAir()) { - return VOID_UPDRAFT * ratio; + return VOID_UPDRAFT; } if (state.isOf(Blocks.SOUL_SAND) || state.isOf(Blocks.SOUL_SOIL)) { - return SOUL_SAND_UPDRAFT * ratio; + return SOUL_SAND_UPDRAFT; } if (state.isOf(Blocks.LAVA) || state.isOf(Blocks.LAVA_CAULDRON) || state.isIn(BlockTags.FIRE) || state.isIn(BlockTags.CAMPFIRES) || state.isOf(Blocks.MAGMA_BLOCK)) { - return FIRE_UPDRAFT * ratio; + return FIRE_UPDRAFT; } if (state.isIn(BlockTags.SAND)) { - return SAND_UPDRAFT * ratio; + return SAND_UPDRAFT * MeteorlogicalUtil.getSunIntensity(world); } if (state.isIn(BlockTags.SNOW) || state.isIn(BlockTags.ICE)) { - return ICE_UPDRAFT * ratio; + return ICE_UPDRAFT * MeteorlogicalUtil.getSunIntensity(world); + } + + if (state.getFluidState().isIn(FluidTags.WATER)) { + return MeteorlogicalUtil.getSunIntensity(world); } return 0; @@ -182,14 +194,7 @@ public class WeatherConditions extends PersistentState implements Tickable { return Random.create(posLong + time); } - private static int getSurfaceBelow(BlockPos.Mutable pos, World world) { - do { - pos.move(Direction.DOWN); - } while (world.isAir(pos) && world.isInBuildLimit(pos)); - return pos.getY(); - } - - public interface TwoDimensionalField { + public interface Plane { float getValue(World world, BlockPos.Mutable pos); default Vec3d computedAverage(World world, BlockPos pos) {