diff --git a/src/main/java/com/minelittlepony/unicopia/block/data/WeatherConditions.java b/src/main/java/com/minelittlepony/unicopia/block/data/WeatherConditions.java new file mode 100644 index 00000000..dcae7ed2 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/block/data/WeatherConditions.java @@ -0,0 +1,129 @@ +package com.minelittlepony.unicopia.block.data; + +import net.minecraft.block.BlockState; +import net.minecraft.block.Blocks; +import net.minecraft.tag.BlockTags; +import net.minecraft.util.math.*; +import net.minecraft.world.Heightmap.Type; +import net.minecraft.world.World; + +public class WeatherConditions { + 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) -> { + if (!world.isAir(pos)) { + return 0; + } + return pos.getY() - getSurfaceBelow(pos, world); + }; + + 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 VOID_UPDRAFT = -0.23; + + public static final float MAX_UPDRAFT_HEIGHT = 20; + public static final float MAX_TERRAIN_HEIGHT = 50; + + public static Vec3d getAirflow(BlockPos pos, World world) { + BlockPos.Mutable probedPosition = new BlockPos.Mutable(); + + final float terrainFactor = Math.min(MAX_TERRAIN_HEIGHT, LOCAL_ALTITUDE_FIELD.getValue(world, probedPosition.set(pos))) / MAX_TERRAIN_HEIGHT; + + Vec3d terrainGradient = LOCAL_ALTITUDE_FIELD.computeAverage(world, pos, probedPosition).multiply(1 - terrainFactor); + Vec3d thermalGradient = THERMAL_FIELD.computeAverage(world, pos, probedPosition).multiply(terrainFactor); + + return terrainGradient.add(thermalGradient).normalize().add(0, getUpdraft(probedPosition.set(pos), world), 0); + } + + 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; + + BlockState state = world.getBlockState(pos); + if (state.isAir()) { + return VOID_UPDRAFT * ratio; + } + + if (state.isOf(Blocks.SOUL_SAND) || state.isOf(Blocks.SOUL_SOIL)) { + return SOUL_SAND_UPDRAFT * ratio; + } + + 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; + } + + if (state.isIn(BlockTags.SAND)) { + return SAND_UPDRAFT * ratio; + } + + if (state.isIn(BlockTags.SNOW) || state.isIn(BlockTags.ICE)) { + return ICE_UPDRAFT * ratio; + } + + return 0; + } + + 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 { + float getValue(World world, BlockPos.Mutable pos); + + default Vec3d computedAverage(World world, BlockPos pos) { + return computeAverage(world, pos, new BlockPos.Mutable()); + } + + default Vec3d computeAverage(World world, BlockPos pos, BlockPos.Mutable probedPosition) { + + float e = getValue(world, probedPosition.set(pos)); + + // A(-1,-1) B( 0,-1) C( 1,-1) + // D(-1, 0) E( 0, 0) F( 1, 0) + // G(-1, 1) H( 0, 1) I( 1, 1) + + int projectionDistance = 4; + + // DEF + Vec3d def = new Vec3d(-average( + getValue(world, probedPosition.set(pos.getX() - projectionDistance, pos.getY(), pos.getZ())) - e, + e - getValue(world, probedPosition.set(pos.getX() + projectionDistance, pos.getY(), pos.getZ())) + ), 0, 0).normalize(); + // BEH + Vec3d beh = new Vec3d(0, 0, -average( + getValue(world, probedPosition.set(pos.getX(), pos.getY(), pos.getZ() - projectionDistance)) - e, + e - getValue(world, probedPosition.set(pos.getX(), pos.getY(), pos.getZ() + projectionDistance)) + )).normalize(); + + // AEI + double diagMag = average( + getValue(world, probedPosition.set(pos.getX() - projectionDistance, pos.getY(), pos.getZ() - projectionDistance)) - e, + e - getValue(world, probedPosition.set(pos.getX() + projectionDistance, pos.getY(), pos.getZ() + projectionDistance)) + ); + Vec3d aei = new Vec3d(0.5 * diagMag, 0, 0.5 * diagMag).normalize(); + // GEC + diagMag = average( + getValue(world, probedPosition.set(pos.getX() - projectionDistance, pos.getY(), pos.getZ() + projectionDistance)) - e, + e - getValue(world, probedPosition.set(pos.getX() + projectionDistance, pos.getY(), pos.getZ() - projectionDistance)) + ); + Vec3d gec = new Vec3d(0.5 * diagMag, 0, 0.5 * diagMag).normalize(); + + return beh.add(def).add(aei).add(gec).normalize(); + } + + private static float average(float a, float b) { + return (a + b) / 2F; + } + } +} 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 7af4f5ba..bf595600 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -5,6 +5,7 @@ import com.minelittlepony.unicopia.ability.magic.SpellPredicate; import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType; import com.minelittlepony.unicopia.advancement.UCriteria; import com.minelittlepony.unicopia.block.data.ModificationType; +import com.minelittlepony.unicopia.block.data.WeatherConditions; import com.minelittlepony.unicopia.client.minelittlepony.MineLPDelegate; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.entity.*; @@ -32,6 +33,7 @@ import net.minecraft.particle.ParticleTypes; import net.minecraft.sound.SoundCategory; import net.minecraft.sound.SoundEvents; import net.minecraft.util.math.*; +import net.minecraft.util.math.random.Random; public class PlayerPhysics extends EntityPhysics implements Tickable, Motion, NbtSerialisable { private static final int MAX_WALL_HIT_CALLDOWN = 30; @@ -336,6 +338,8 @@ public class PlayerPhysics extends EntityPhysics implements Tickab if (entity.world.hasRain(entity.getBlockPos())) { applyTurbulance(velocity); + } else { + velocity.y += WeatherConditions.getUpdraft(new BlockPos.Mutable().set(entity.getBlockPos()), entity.world); } if (type.isAvian()) { @@ -551,8 +555,16 @@ public class PlayerPhysics extends EntityPhysics implements Tickab } private void applyTurbulance(MutableVector velocity) { - float glance = 360 * entity.world.random.nextFloat(); - float forward = 0.015F * entity.world.random.nextFloat() * entity.world.getRainGradient(1); + + Vec3d wind = WeatherConditions.getAirflow(entity.getBlockPos(), entity.world).multiply(0.04); + velocity.x += wind.x; + velocity.y += wind.y; + velocity.z += wind.z; + + Random rng = Random.create(entity.world.getTime()); + + float glance = 360 * rng.nextFloat(); + float forward = 0.015F * rng.nextFloat() * entity.world.getRainGradient(1); if (entity.world.random.nextInt(30) == 0) { forward *= 10; @@ -564,12 +576,12 @@ public class PlayerPhysics extends EntityPhysics implements Tickab forward *= 100; } - if (entity.world.isThundering() && entity.world.random.nextInt(60) == 0) { + if (entity.world.isThundering() && rng.nextInt(60) == 0) { velocity.y += forward * 3 * getGravitySignum(); } if (forward >= 1) { - entity.world.playSound(null, entity.getBlockPos(), USounds.AMBIENT_WIND_GUST, SoundCategory.AMBIENT, 3, 1); + SoundEmitter.playSoundAt(entity, USounds.AMBIENT_WIND_GUST, SoundCategory.AMBIENT, 3, 1); } forward = Math.min(forward, 7); diff --git a/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java b/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java index ea37535f..b35735fc 100644 --- a/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java +++ b/src/main/java/com/minelittlepony/unicopia/projectile/MagicProjectileEntity.java @@ -25,7 +25,6 @@ import com.minelittlepony.unicopia.network.datasync.EffectSync; import net.minecraft.entity.Entity; import net.minecraft.entity.EntityType; import net.minecraft.entity.LivingEntity; -import net.minecraft.entity.Entity.RemovalReason; import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.data.DataTracker; import net.minecraft.entity.data.TrackedData;