Added wind trail particles

This commit is contained in:
Sollace 2024-04-22 17:21:12 +01:00
parent e51d82f37e
commit 1545210efa
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
11 changed files with 164 additions and 14 deletions

View file

@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.block;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.USounds; 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 com.minelittlepony.unicopia.server.world.WeatherConditions;
import net.minecraft.block.*; import net.minecraft.block.*;
@ -51,6 +53,9 @@ public class WeatherVaneBlock extends BlockWithEntity {
private float clientAngle; private float clientAngle;
private float prevAngle; private float prevAngle;
private float lastAngle;
private Vec3d airflow = Vec3d.ZERO;
public WeatherVane(BlockPos pos, BlockState state) { public WeatherVane(BlockPos pos, BlockState state) {
super(UBlockEntities.WEATHER_VANE, pos, state); super(UBlockEntities.WEATHER_VANE, pos, state);
@ -63,11 +68,14 @@ public class WeatherVaneBlock extends BlockWithEntity {
@Override @Override
public void readNbt(NbtCompound nbt) { public void readNbt(NbtCompound nbt) {
angle = nbt.getFloat("angle"); angle = nbt.getFloat("angle");
airflow = new Vec3d(nbt.getDouble("windX"), 0, nbt.getDouble("windZ"));
} }
@Override @Override
protected void writeNbt(NbtCompound nbt) { protected void writeNbt(NbtCompound nbt) {
nbt.putFloat("angle", angle); nbt.putFloat("angle", angle);
nbt.putDouble("windX", airflow.x);
nbt.putDouble("windZ", airflow.z);
} }
@Override @Override
@ -82,21 +90,24 @@ public class WeatherVaneBlock extends BlockWithEntity {
public static void serverTick(World world, BlockPos pos, BlockState state, WeatherVane entity) { public static void serverTick(World world, BlockPos pos, BlockState state, WeatherVane entity) {
Vec3d airflow = WeatherConditions.get(world).getWindDirection(); Vec3d airflow = WeatherConditions.get(world).getWindDirection();
float angle = (float)Math.atan2(airflow.x, airflow.z) + MathHelper.PI; float angle = (WeatherConditions.get(world).getWindYaw() % MathHelper.PI);
if (Math.signum(entity.angle) != Math.signum(angle)) {
angle = MathHelper.PI - angle;
}
angle %= MathHelper.PI;
entity.lastAngle = entity.prevAngle;
entity.prevAngle = entity.angle;
if (angle != entity.angle) { if (angle != entity.angle) {
entity.angle = angle; entity.angle = angle;
entity.airflow = airflow;
entity.markDirty(); entity.markDirty();
if (world instanceof ServerWorld serverWorld) { if (world instanceof ServerWorld sw) {
serverWorld.getChunkManager().markForUpdate(pos); 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) { 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) { } else if (entity.clientAngle > angle) {
entity.clientAngle -= step; 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
);
}
}
} }
} }
} }

View file

@ -22,6 +22,7 @@ import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle;
import com.minelittlepony.unicopia.client.particle.RaindropsParticle; import com.minelittlepony.unicopia.client.particle.RaindropsParticle;
import com.minelittlepony.unicopia.client.particle.ShockwaveParticle; import com.minelittlepony.unicopia.client.particle.ShockwaveParticle;
import com.minelittlepony.unicopia.client.particle.SphereParticle; 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.*;
import com.minelittlepony.unicopia.client.render.entity.*; import com.minelittlepony.unicopia.client.render.entity.*;
import com.minelittlepony.unicopia.client.render.shader.UShaders; 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.FOOTPRINT, createFactory(FootprintParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_RING, RainboomParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.RAINBOOM_TRAIL, RainbowTrailParticle::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.SHOCKWAVE, ShockwaveParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new);
ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new);

View file

@ -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) { 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); 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); setMaxAge(300);
this.velocityX = velocityX; this.velocityX = velocityX;
this.velocityY = velocityY; this.velocityY = velocityY;

View file

@ -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<Trail.Segment> 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();
}
}
}

View file

@ -12,8 +12,11 @@ public class Trail {
public final Vec3d pos; public final Vec3d pos;
public Trail(Vec3d pos) { private final float height;
public Trail(Vec3d pos, float height) {
this.pos = pos; this.pos = pos;
this.height = height;
segments.add(new Segment(pos)); segments.add(new Segment(pos));
} }
@ -61,7 +64,7 @@ public class Trail {
} }
public BezierSegment getPlane(Segment to) { public BezierSegment getPlane(Segment to) {
return new BezierSegment(offset, to.offset, 1); return new BezierSegment(offset, to.offset, height);
} }
} }
} }

View file

@ -649,6 +649,14 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
velocity.x += - forward * MathHelper.sin(entity.getYaw() * 0.017453292F); velocity.x += - forward * MathHelper.sin(entity.getYaw() * 0.017453292F);
velocity.z += forward * MathHelper.cos(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())) { if (entity.getWorld().hasRain(entity.getBlockPos())) {
applyTurbulance(velocity); applyTurbulance(velocity);
} else { } else {
@ -820,6 +828,8 @@ public class PlayerPhysics extends EntityPhysics<PlayerEntity> implements Tickab
pony.updateVelocity(); pony.updateVelocity();
pony.spawnParticles(new TargetBoundParticleEffect(UParticles.WIND, pony.asEntity()), 4);
if (isFlying()) { if (isFlying()) {
playSound(USounds.ENTITY_PLAYER_PEGASUS_DASH, 1, 1); playSound(USounds.ENTITY_PLAYER_PEGASUS_DASH, 1, 1);
} else { } else {

View file

@ -3,6 +3,8 @@ package com.minelittlepony.unicopia.particle;
import java.util.Locale; import java.util.Locale;
import org.jetbrains.annotations.Nullable;
import com.mojang.brigadier.StringReader; import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException; import com.mojang.brigadier.exceptions.CommandSyntaxException;
@ -29,9 +31,9 @@ public class TargetBoundParticleEffect implements ParticleEffect {
this.targetId = buf.readInt(); this.targetId = buf.readInt();
} }
public TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, Entity target) { public TargetBoundParticleEffect(ParticleType<TargetBoundParticleEffect> type, @Nullable Entity target) {
this.type = type; this.type = type;
this.targetId = target.getId(); this.targetId = target == null ? -1 : target.getId();
} }
public int getTargetId() { public int getTargetId() {

View file

@ -19,6 +19,7 @@ public interface UParticles {
ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY)); ParticleType<OrientedBillboardParticleEffect> RAINBOOM_RING = register("rainboom_ring", FabricParticleTypes.complex(OrientedBillboardParticleEffect.FACTORY));
ParticleType<TargetBoundParticleEffect> RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY)); ParticleType<TargetBoundParticleEffect> RAINBOOM_TRAIL = register("rainboom_trail", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY));
ParticleType<TargetBoundParticleEffect> WIND = register("wind", FabricParticleTypes.complex(TargetBoundParticleEffect.FACTORY));
DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple()); DefaultParticleType RAIN_DROPS = register("rain_drops", FabricParticleTypes.simple());

View file

@ -114,8 +114,12 @@ public class WeatherConditions extends PersistentState implements Tickable {
return MathHelper.lerp(interpolation / (float)maxInterpolation, prevWindYaw, windYaw); return MathHelper.lerp(interpolation / (float)maxInterpolation, prevWindYaw, windYaw);
} }
public int getWindInterpolation() {
return interpolation;
}
public Vec3d getWindDirection() { public Vec3d getWindDirection() {
return Vec3d.fromPolar(0, windYaw).normalize(); return Vec3d.fromPolar(0, getWindYaw()).normalize();
} }
@Override @Override

View file

@ -0,0 +1,2 @@
{
}

Binary file not shown.

After

Width:  |  Height:  |  Size: 8 KiB