mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Added wind trail particles
This commit is contained in:
parent
e51d82f37e
commit
1545210efa
11 changed files with 164 additions and 14 deletions
|
@ -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
|
||||||
|
);
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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);
|
||||||
|
|
|
@ -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;
|
||||||
|
|
|
@ -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();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
|
@ -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);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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 {
|
||||||
|
|
|
@ -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() {
|
||||||
|
|
|
@ -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());
|
||||||
|
|
||||||
|
|
|
@ -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
|
||||||
|
|
2
src/main/resources/assets/unicopia/particles/wind.json
Normal file
2
src/main/resources/assets/unicopia/particles/wind.json
Normal file
|
@ -0,0 +1,2 @@
|
||||||
|
{
|
||||||
|
}
|
BIN
src/main/resources/assets/unicopia/textures/particle/wind.png
Normal file
BIN
src/main/resources/assets/unicopia/textures/particle/wind.png
Normal file
Binary file not shown.
After Width: | Height: | Size: 8 KiB |
Loading…
Reference in a new issue