Add spiral particle (wip)

This commit is contained in:
Sollace 2024-04-23 14:41:21 +01:00
parent 1545210efa
commit 03f4b4004e
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
8 changed files with 180 additions and 15 deletions

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.SpiralParticle;
import com.minelittlepony.unicopia.client.particle.WindParticle; 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.*;
@ -80,6 +81,7 @@ public interface URenderers {
ParticleFactoryRegistry.getInstance().register(UParticles.RAIN_DROPS, createFactory(RaindropsParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.RAIN_DROPS, createFactory(RaindropsParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create)); ParticleFactoryRegistry.getInstance().register(UParticles.HEALTH_DRAIN, createFactory(HealthDrainParticle::create));
ParticleFactoryRegistry.getInstance().register(UParticles.FOOTPRINT, createFactory(FootprintParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.FOOTPRINT, createFactory(FootprintParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.SPIRAL, createFactory(SpiralParticle::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.WIND, WindParticle::new);

View file

@ -0,0 +1,81 @@
package com.minelittlepony.unicopia.client.particle;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.particle.SpiralParticleEffect;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.particle.NoRenderParticle;
import net.minecraft.client.particle.Particle;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class SpiralParticle extends NoRenderParticle {
private final SpiralParticleEffect parameters;
@Nullable
private final Particle particle;
private float scale;
public SpiralParticle(SpiralParticleEffect parameters, SpriteProvider provider, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
this.scale = 0.1f * (random.nextFloat() * 0.5f + 0.5f) * 2.0f;
setMaxAge(3);
scale(0.125F);
this.parameters = parameters;
this.collidesWithWorld = false;
this.particle = MinecraftClient.getInstance().particleManager.addParticle(parameters.effect(), x, y, z, velocityX, velocityY, velocityZ);
this.particle.setMaxAge(1000);
this.gravityStrength = 0;
}
@Override
public Particle scale(float scale) {
this.scale *= scale;
super.scale(scale);
return this;
}
@Override
public void move(double dx, double dy, double dz) {
super.move(dx, dy, dz);
if (particle != null) {
particle.setPos(x, y, z);
}
}
@Override
public void tick() {
if (particle == null || !particle.isAlive()) {
markDead();
}
super.tick();
Vec3d target = parameters.centerPoint().getPosition(world);
Vec3d pos = new Vec3d(x, y, z);
if (scale * 1.5F < 0.5F) {
scale(1.5F);
}
double distance = pos.distanceTo(target);
if (distance > 0) {
age = 0;
}
Vec3d radial = target.subtract(pos).normalize();
Vec3d tangent = radial.rotateY(MathHelper.HALF_PI).multiply(parameters.angularVelocity() * 0.9F);
Vec3d motion = radial.multiply(parameters.angularVelocity() * 0.1F).add(tangent);
move(motion.x, motion.y, motion.z);
}
@Override
public String toString() {
return super.toString() + ", Angular Velocity " + parameters.angularVelocity() + ", Target (" + parameters.centerPoint() + ") Sub-Particle (" + particle + ")";
}
}

View file

@ -26,10 +26,7 @@ public record FollowingParticleEffect (
this(type, this(type,
new WeakTarget(reader), new WeakTarget(reader),
ParticleFactoryHelper.readFloat(reader), ParticleFactoryHelper.readFloat(reader),
ParticleFactoryHelper.readOptional(reader, r -> { ParticleFactoryHelper.readOptional(reader, r -> ParticleFactoryHelper.read(r)));
r.expect(' ');
return ParticleFactoryHelper.read(r);
}));
} }
protected FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, PacketByteBuf buf) { protected FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, PacketByteBuf buf) {

View file

@ -14,17 +14,11 @@ import net.minecraft.registry.Registries;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public interface ParticleFactoryHelper { public interface ParticleFactoryHelper {
@SuppressWarnings("deprecation") @SuppressWarnings({ "deprecation", "unchecked", "rawtypes" })
PacketCodec<ParticleEffect> PARTICLE_EFFECT_CODEC = new PacketCodec<>( PacketCodec<ParticleEffect> PARTICLE_EFFECT_CODEC = PacketCodec.ofRegistry(Registries.PARTICLE_TYPE).andThen(
buf -> { (buf, type) -> type.getParametersFactory().read((ParticleType) type, buf),
@SuppressWarnings("unchecked") ParticleEffect::getType,
ParticleType<ParticleEffect> type = (ParticleType<ParticleEffect>)Registries.PARTICLE_TYPE.get(buf.readInt()); (buf, effect) -> effect.write(buf)
return type.getParametersFactory().read(type, buf);
},
(buf, effect) -> {
buf.writeInt(Registries.PARTICLE_TYPE.getRawId(effect.getType()));
effect.write(buf);
}
); );
PacketCodec<Optional<ParticleEffect>> OPTIONAL_PARTICLE_EFFECT_CODEC = PARTICLE_EFFECT_CODEC.asOptional(); PacketCodec<Optional<ParticleEffect>> OPTIONAL_PARTICLE_EFFECT_CODEC = PARTICLE_EFFECT_CODEC.asOptional();
PacketCodec<Vec3d> VECTOR_CODEC = new PacketCodec<>( PacketCodec<Vec3d> VECTOR_CODEC = new PacketCodec<>(
@ -39,6 +33,7 @@ public interface ParticleFactoryHelper {
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
static <T extends ParticleEffect> T read(StringReader reader) throws CommandSyntaxException { static <T extends ParticleEffect> T read(StringReader reader) throws CommandSyntaxException {
reader.expect(' ');
return (T)ParticleEffectArgumentType.readParameters(reader, Registries.PARTICLE_TYPE.getReadOnlyWrapper()); return (T)ParticleEffectArgumentType.readParameters(reader, Registries.PARTICLE_TYPE.getReadOnlyWrapper());
} }

View file

@ -0,0 +1,48 @@
package com.minelittlepony.unicopia.particle;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType;
public record SpiralParticleEffect(
WeakTarget centerPoint,
float angularVelocity,
ParticleEffect effect
) implements ParticleEffect {
@SuppressWarnings("deprecation")
public static final Factory<SpiralParticleEffect> FACTORY = ParticleFactoryHelper.of(SpiralParticleEffect::new, SpiralParticleEffect::new);
protected SpiralParticleEffect(ParticleType<SpiralParticleEffect> type, StringReader reader) throws CommandSyntaxException {
this(new WeakTarget(reader),
ParticleFactoryHelper.readFloat(reader),
ParticleFactoryHelper.read(reader)
);
}
protected SpiralParticleEffect(ParticleType<SpiralParticleEffect> type, PacketByteBuf buf) {
this(new WeakTarget(buf),
buf.readFloat(),
ParticleFactoryHelper.PARTICLE_EFFECT_CODEC.read(buf)
);
}
@Override
public ParticleType<?> getType() {
return UParticles.SPIRAL;
}
@Override
public void write(PacketByteBuf buffer) {
centerPoint.write(buffer);
buffer.writeFloat(angularVelocity);
ParticleFactoryHelper.PARTICLE_EFFECT_CODEC.write(buffer, effect);
}
@Override
public String asString() {
return null;
}
}

View file

@ -27,6 +27,7 @@ public interface UParticles {
ParticleType<SphereParticleEffect> DISK = register("disk", FabricParticleTypes.complex(true, SphereParticleEffect.FACTORY)); ParticleType<SphereParticleEffect> DISK = register("disk", FabricParticleTypes.complex(true, SphereParticleEffect.FACTORY));
ParticleType<FollowingParticleEffect> HEALTH_DRAIN = register("health_drain", FabricParticleTypes.complex(true, FollowingParticleEffect.FACTORY)); ParticleType<FollowingParticleEffect> HEALTH_DRAIN = register("health_drain", FabricParticleTypes.complex(true, FollowingParticleEffect.FACTORY));
ParticleType<SpiralParticleEffect> SPIRAL = register("spiral", FabricParticleTypes.complex(true, SpiralParticleEffect.FACTORY));
DefaultParticleType GROUND_POUND = register("ground_pound", FabricParticleTypes.simple()); DefaultParticleType GROUND_POUND = register("ground_pound", FabricParticleTypes.simple());
DefaultParticleType CLOUDS_ESCAPING = register("clouds_escaping", FabricParticleTypes.simple(true)); DefaultParticleType CLOUDS_ESCAPING = register("clouds_escaping", FabricParticleTypes.simple(true));

View file

@ -1,10 +1,33 @@
package com.minelittlepony.unicopia.util.serialization; package com.minelittlepony.unicopia.util.serialization;
import java.util.Optional; import java.util.Optional;
import java.util.function.BiConsumer;
import java.util.function.BiFunction;
import java.util.function.Function;
import java.util.function.Supplier;
import net.minecraft.network.PacketByteBuf; import net.minecraft.network.PacketByteBuf;
import net.minecraft.registry.Registry;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
public record PacketCodec<T>(PacketByteBuf.PacketReader<T> reader, PacketByteBuf.PacketWriter<T> writer) { public record PacketCodec<T>(PacketByteBuf.PacketReader<T> reader, PacketByteBuf.PacketWriter<T> writer) {
public static final PacketCodec<Float> FLOAT = new PacketCodec<>(PacketByteBuf::readFloat, PacketByteBuf::writeFloat);
public static final PacketCodec<Integer> INT = new PacketCodec<>(PacketByteBuf::readInt, PacketByteBuf::writeInt);
public static final PacketCodec<Byte> BYTE = new PacketCodec<>(PacketByteBuf::readByte, (b, v) -> b.writeByte(v));
public static final PacketCodec<Long> LONG = new PacketCodec<>(PacketByteBuf::readLong, PacketByteBuf::writeLong);
public static final PacketCodec<String> STRING = new PacketCodec<>(PacketByteBuf::readString, PacketByteBuf::writeString);
public static final PacketCodec<Identifier> IDENTIFIER = STRING.xMap(Identifier::new, Identifier::toString);
public static final <T> PacketCodec<T> ofRegistry(Registry<T> registry) {
return INT.xMap(registry::get, registry::getRawId);
}
public static final <T extends Enum<T>> PacketCodec<T> ofEnum(Supplier<T[]> valuesGetter) {
final T[] values = valuesGetter.get();
return INT.xMap(id -> values[MathHelper.clamp(id, 0, values.length)], Enum::ordinal);
}
public T read(PacketByteBuf buf) { public T read(PacketByteBuf buf) {
return reader().apply(buf); return reader().apply(buf);
@ -17,4 +40,17 @@ public record PacketCodec<T>(PacketByteBuf.PacketReader<T> reader, PacketByteBuf
public PacketCodec<Optional<T>> asOptional() { public PacketCodec<Optional<T>> asOptional() {
return new PacketCodec<>(buf -> buf.readOptional(reader), (buf, v) -> buf.writeOptional(v, writer)); return new PacketCodec<>(buf -> buf.readOptional(reader), (buf, v) -> buf.writeOptional(v, writer));
} }
public <X> PacketCodec<X> xMap(Function<T, X> to, Function<X, T> from) {
return new PacketCodec<>(buf -> to.apply(reader.apply(buf)), (buf, v) -> writer.accept(buf, from.apply(v)));
}
public <X> PacketCodec<X> andThen(BiFunction<PacketByteBuf, T, X> to, Function<X, T> from, BiConsumer<PacketByteBuf, X> write) {
return new PacketCodec<>(buf -> {
return to.apply(buf, reader.apply(buf));
}, (buf, v) -> {
writer.accept(buf, from.apply(v));
write.accept(buf, v);
});
}
} }

View file

@ -0,0 +1,5 @@
{
"textures": [
"minecraft:heart"
]
}