diff --git a/src/main/java/com/minelittlepony/unicopia/block/WeatherVaneBlock.java b/src/main/java/com/minelittlepony/unicopia/block/WeatherVaneBlock.java index 14d39f7f..39e526bb 100644 --- a/src/main/java/com/minelittlepony/unicopia/block/WeatherVaneBlock.java +++ b/src/main/java/com/minelittlepony/unicopia/block/WeatherVaneBlock.java @@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.block; import org.jetbrains.annotations.Nullable; import com.minelittlepony.unicopia.USounds; +import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; +import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.server.world.WeatherConditions; import net.minecraft.block.*; @@ -51,6 +53,9 @@ public class WeatherVaneBlock extends BlockWithEntity { private float clientAngle; private float prevAngle; + private float lastAngle; + + private Vec3d airflow = Vec3d.ZERO; public WeatherVane(BlockPos pos, BlockState state) { super(UBlockEntities.WEATHER_VANE, pos, state); @@ -63,11 +68,14 @@ public class WeatherVaneBlock extends BlockWithEntity { @Override public void readNbt(NbtCompound nbt) { angle = nbt.getFloat("angle"); + airflow = new Vec3d(nbt.getDouble("windX"), 0, nbt.getDouble("windZ")); } @Override protected void writeNbt(NbtCompound nbt) { nbt.putFloat("angle", angle); + nbt.putDouble("windX", airflow.x); + nbt.putDouble("windZ", airflow.z); } @Override @@ -82,21 +90,24 @@ public class WeatherVaneBlock extends BlockWithEntity { public static void serverTick(World world, BlockPos pos, BlockState state, WeatherVane entity) { Vec3d airflow = WeatherConditions.get(world).getWindDirection(); - float angle = (float)Math.atan2(airflow.x, airflow.z) + MathHelper.PI; - if (Math.signum(entity.angle) != Math.signum(angle)) { - angle = MathHelper.PI - angle; - } - angle %= MathHelper.PI; + float angle = (WeatherConditions.get(world).getWindYaw() % MathHelper.PI); + entity.lastAngle = entity.prevAngle; + entity.prevAngle = entity.angle; if (angle != entity.angle) { entity.angle = angle; + + entity.airflow = airflow; entity.markDirty(); - if (world instanceof ServerWorld serverWorld) { - serverWorld.getChunkManager().markForUpdate(pos); + if (world instanceof ServerWorld sw) { + sw.getChunkManager().markForUpdate(pos); } - world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), USounds.BLOCK_WEATHER_VANE_ROTATE, SoundCategory.BLOCKS, 1, 0.5F + (float)world.random.nextGaussian()); + if (entity.lastAngle == entity.prevAngle) { + world.playSound(null, pos.getX(), pos.getY(), pos.getZ(), USounds.BLOCK_WEATHER_VANE_ROTATE, SoundCategory.BLOCKS, 1, 0.5F + (float)world.random.nextGaussian()); + } } + } public static void clientTick(World world, BlockPos pos, BlockState state, WeatherVane entity) { @@ -111,6 +122,18 @@ public class WeatherVaneBlock extends BlockWithEntity { } else if (entity.clientAngle > angle) { entity.clientAngle -= step; } + + if (world.random.nextInt(3) == 0) { + float radius = 10; + for (int i = 0; i < 5; i++) { + world.addImportantParticle(new TargetBoundParticleEffect(UParticles.WIND, null), + world.getRandom().nextTriangular(pos.getX(), radius), + world.getRandom().nextTriangular(pos.getY(), radius), + world.getRandom().nextTriangular(pos.getZ(), radius), + entity.airflow.x / 10F, 0, entity.airflow.z / 10F + ); + } + } } } } diff --git a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java index b4f09f99..a0d9a21c 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/URenderers.java +++ b/src/main/java/com/minelittlepony/unicopia/client/URenderers.java @@ -22,6 +22,7 @@ import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle; import com.minelittlepony.unicopia.client.particle.RaindropsParticle; import com.minelittlepony.unicopia.client.particle.ShockwaveParticle; import com.minelittlepony.unicopia.client.particle.SphereParticle; +import com.minelittlepony.unicopia.client.particle.WindParticle; import com.minelittlepony.unicopia.client.render.*; import com.minelittlepony.unicopia.client.render.entity.*; import com.minelittlepony.unicopia.client.render.shader.UShaders; @@ -81,6 +82,7 @@ public interface URenderers { ParticleFactoryRegistry.getInstance().register(UParticles.FOOTPRINT, createFactory(FootprintParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_TRAIL, RainbowTrailParticle::new); + ParticleFactoryRegistry.getInstance().register(UParticles.WIND, WindParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SHOCKWAVE, ShockwaveParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new); diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java index 978fede1..4a8d7b26 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/RainbowTrailParticle.java @@ -29,7 +29,7 @@ public class RainbowTrailParticle extends AbstractBillboardParticle { public RainbowTrailParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { super(world, x, y, z, velocityX, velocityY, velocityZ); - trail = new Trail(new Vec3d(x, y, z)); + trail = new Trail(new Vec3d(x, y, z), 1); setMaxAge(300); this.velocityX = velocityX; this.velocityY = velocityY; diff --git a/src/main/java/com/minelittlepony/unicopia/client/particle/WindParticle.java b/src/main/java/com/minelittlepony/unicopia/client/particle/WindParticle.java new file mode 100644 index 00000000..222fdfed --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/client/particle/WindParticle.java @@ -0,0 +1,103 @@ +package com.minelittlepony.unicopia.client.particle; + +import java.util.List; +import org.jetbrains.annotations.Nullable; + +import com.minelittlepony.unicopia.Unicopia; +import com.minelittlepony.unicopia.client.render.bezier.BezierSegment; +import com.minelittlepony.unicopia.client.render.bezier.Trail; +import com.minelittlepony.unicopia.particle.TargetBoundParticleEffect; + +import net.minecraft.client.MinecraftClient; +import net.minecraft.client.render.BufferBuilder; +import net.minecraft.client.render.Tessellator; +import net.minecraft.client.world.ClientWorld; +import net.minecraft.entity.Entity; +import net.minecraft.util.Identifier; +import net.minecraft.util.math.MathHelper; +import net.minecraft.util.math.Vec3d; + +public class WindParticle extends AbstractBillboardParticle { + private static final Identifier TEXTURE = Unicopia.id("textures/particle/wind.png"); + + private final Trail trail; + + @Nullable + private Entity target; + + private int attachmentTicks; + + private final Vec3d offset; + private final boolean passive; + + public WindParticle(TargetBoundParticleEffect effect, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) { + super(world, x, y, z, velocityX, velocityY, velocityZ); + trail = new Trail(new Vec3d(x, y, z), 0.02F); + setMaxAge(300); + this.alpha = 0.15F; + this.velocityX = velocityX; + this.velocityY = velocityY; + this.velocityZ = velocityZ; + this.attachmentTicks = (int)world.random.nextTriangular(15, 12); + this.passive = effect.getTargetId() <= 0; + + if (effect.getTargetId() > 0) { + this.target = world.getEntityById(effect.getTargetId()); + } + offset = target == null ? Vec3d.ZERO : new Vec3d(x, y, z).subtract(target.getPos()); + } + + @Override + protected Identifier getTexture() { + return TEXTURE; + } + + @Override + public boolean isAlive() { + return age < getMaxAge() && (!dead || !trail.getSegments().isEmpty()); + } + + @Override + protected void renderQuads(Tessellator te, BufferBuilder buffer, float x, float y, float z, float tickDelta) { + float alpha = this.alpha * (1 - (float)age / maxAge); + + List segments = trail.getSegments(); + + for (int i = 0; i < segments.size() - 1; i++) { + BezierSegment corners = segments.get(i).getPlane(segments.get(i + 1)); + float scale = getScale(tickDelta); + + corners.forEachCorner(corner -> { + corner.position().mul(scale).add(x, y, z); + }); + + renderQuad(te, buffer, corners.corners(), segments.get(i).getAlpha() * alpha, tickDelta); + } + } + + @Override + public void tick() { + super.tick(); + + float animationFrame = age + MinecraftClient.getInstance().getTickDelta(); + + float sin = MathHelper.sin(animationFrame / 5F) * 0.1F; + float cos = MathHelper.cos(animationFrame / 10F) * 0.2F; + + if (passive) { + trail.update(new Vec3d(x + cos, y + sin, z - cos)); + } else { + if (target != null && target.isAlive()) { + trail.update(target.getPos().add(offset).add(cos, sin, -cos)); + + if (attachmentTicks > 0 && --attachmentTicks <= 0) { + target = null; + } + } + } + + if (trail.tick()) { + markDead(); + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java index f130fbcc..8bfd4c5d 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java +++ b/src/main/java/com/minelittlepony/unicopia/client/render/bezier/Trail.java @@ -12,8 +12,11 @@ public class Trail { public final Vec3d pos; - public Trail(Vec3d pos) { + private final float height; + + public Trail(Vec3d pos, float height) { this.pos = pos; + this.height = height; segments.add(new Segment(pos)); } @@ -61,7 +64,7 @@ public class Trail { } public BezierSegment getPlane(Segment to) { - return new BezierSegment(offset, to.offset, 1); + return new BezierSegment(offset, to.offset, height); } } } 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 0cb1f1b4..de13f11d 100644 --- a/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java +++ b/src/main/java/com/minelittlepony/unicopia/entity/player/PlayerPhysics.java @@ -649,6 +649,14 @@ public class PlayerPhysics extends EntityPhysics implements Tickab velocity.x += - forward * MathHelper.sin(entity.getYaw() * 0.017453292F); velocity.z += forward * MathHelper.cos(entity.getYaw() * 0.017453292F); + if (pony.isClient()) { + float effectChance = 1F - (float)(MathHelper.clamp(velocity.horizontalLengthSquared(), 0, 1)); + + if (entity.getWorld().random.nextInt(1 + (int)(120 * effectChance)) == 0) { + pony.spawnParticles(new TargetBoundParticleEffect(UParticles.WIND, pony.asEntity()), 3); + } + } + if (entity.getWorld().hasRain(entity.getBlockPos())) { applyTurbulance(velocity); } else { @@ -820,6 +828,8 @@ public class PlayerPhysics extends EntityPhysics implements Tickab pony.updateVelocity(); + pony.spawnParticles(new TargetBoundParticleEffect(UParticles.WIND, pony.asEntity()), 4); + if (isFlying()) { playSound(USounds.ENTITY_PLAYER_PEGASUS_DASH, 1, 1); } else { diff --git a/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java b/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java index 15171015..76d84b03 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/TargetBoundParticleEffect.java @@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.particle; import java.util.Locale; +import org.jetbrains.annotations.Nullable; + import com.mojang.brigadier.StringReader; import com.mojang.brigadier.exceptions.CommandSyntaxException; @@ -29,9 +31,9 @@ public class TargetBoundParticleEffect implements ParticleEffect { this.targetId = buf.readInt(); } - public TargetBoundParticleEffect(ParticleType type, Entity target) { + public TargetBoundParticleEffect(ParticleType type, @Nullable Entity target) { this.type = type; - this.targetId = target.getId(); + this.targetId = target == null ? -1 : target.getId(); } public int getTargetId() { diff --git a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java index da302010..0c440ee6 100644 --- a/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java +++ b/src/main/java/com/minelittlepony/unicopia/particle/UParticles.java @@ -19,6 +19,7 @@ public interface UParticles { ParticleType RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); ParticleType RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); + ParticleType WIND = register("wind", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple()); 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 75e0b392..d392b684 100644 --- a/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java +++ b/src/main/java/com/minelittlepony/unicopia/server/world/WeatherConditions.java @@ -114,8 +114,12 @@ public class WeatherConditions extends PersistentState implements Tickable { return MathHelper.lerp(interpolation / (float)maxInterpolation, prevWindYaw, windYaw); } + public int getWindInterpolation() { + return interpolation; + } + public Vec3d getWindDirection() { - return Vec3d.fromPolar(0, windYaw).normalize(); + return Vec3d.fromPolar(0, getWindYaw()).normalize(); } @Override diff --git a/src/main/resources/assets/unicopia/particles/wind.json b/src/main/resources/assets/unicopia/particles/wind.json new file mode 100644 index 00000000..2c63c085 --- /dev/null +++ b/src/main/resources/assets/unicopia/particles/wind.json @@ -0,0 +1,2 @@ +{ +} diff --git a/src/main/resources/assets/unicopia/textures/particle/wind.png b/src/main/resources/assets/unicopia/textures/particle/wind.png new file mode 100644 index 00000000..0926e002 Binary files /dev/null and b/src/main/resources/assets/unicopia/textures/particle/wind.png differ