mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-23 21:38:00 +01:00
Add a particle effect for dropping a cloud in a bottle
This commit is contained in:
parent
82bc317bc1
commit
fc5b50a975
10 changed files with 323 additions and 66 deletions
|
@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.client;
|
|||
|
||||
import com.minelittlepony.unicopia.UEntities;
|
||||
import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle;
|
||||
import com.minelittlepony.unicopia.client.particle.CloudsEscapingParticle;
|
||||
import com.minelittlepony.unicopia.client.particle.DiskParticle;
|
||||
import com.minelittlepony.unicopia.client.particle.GroundPoundParticle;
|
||||
import com.minelittlepony.unicopia.client.particle.HealthDrainParticle;
|
||||
|
@ -43,6 +44,7 @@ public interface URenderers {
|
|||
ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new);
|
||||
ParticleFactoryRegistry.getInstance().register(UParticles.DISK, DiskParticle::new);
|
||||
ParticleFactoryRegistry.getInstance().register(UParticles.GROUND_POUND, GroundPoundParticle::new);
|
||||
ParticleFactoryRegistry.getInstance().register(UParticles.CLOUDS_ESCAPING, CloudsEscapingParticle::new);
|
||||
|
||||
EntityRendererRegistry.INSTANCE.register(UEntities.THROWN_ITEM, (manager, context) -> new FlyingItemEntityRenderer<>(manager, context.getItemRenderer()));
|
||||
EntityRendererRegistry.INSTANCE.register(UEntities.FLOATING_ARTEFACT, FloatingArtefactEntityRenderer::new);
|
||||
|
|
|
@ -0,0 +1,58 @@
|
|||
package com.minelittlepony.unicopia.client.particle;
|
||||
|
||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||
|
||||
import net.minecraft.client.world.ClientWorld;
|
||||
import net.minecraft.particle.DefaultParticleType;
|
||||
import net.minecraft.particle.ParticleTypes;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class CloudsEscapingParticle extends GroundPoundParticle {
|
||||
|
||||
public CloudsEscapingParticle(DefaultParticleType effect, ClientWorld world, double x, double y, double z, double dX, double dY, double dZ) {
|
||||
super(effect, world, x, y, z, dX, dY, dZ);
|
||||
maxAge = 200;
|
||||
collidesWithWorld = false;
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
this.prevPosX = this.x;
|
||||
this.prevPosY = this.y;
|
||||
this.prevPosZ = this.z;
|
||||
if (this.age++ >= this.maxAge) {
|
||||
this.markDead();
|
||||
} else {
|
||||
this.velocityY -= 0.04D * this.gravityStrength;
|
||||
this.move(this.velocityX, this.velocityY, this.velocityZ);
|
||||
this.velocityX *= 0.9800000190734863D;
|
||||
this.velocityY *= 0.9800000190734863D;
|
||||
this.velocityZ *= 0.9800000190734863D;
|
||||
if (this.onGround) {
|
||||
this.velocityX *= 0.699999988079071D;
|
||||
this.velocityZ *= 0.699999988079071D;
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
this.velocityY += 0.125;
|
||||
|
||||
Vec3d center = getPos();
|
||||
|
||||
double variance = age / maxAge;
|
||||
Vec3d vel = new Vec3d(
|
||||
(world.random.nextFloat() - 0.5) * (0.125 + variance),
|
||||
velocityY * 0.2,
|
||||
(world.random.nextFloat() - 0.5) * (0.125 + variance)
|
||||
);
|
||||
|
||||
double columnHeight = 1 + age / 30;
|
||||
new Sphere(true, columnHeight, 1, 1, 1)
|
||||
.offset(center)
|
||||
.randomPoints(random)
|
||||
.forEach(point -> {
|
||||
ParticleUtils.spawnParticle(ParticleTypes.CLOUD, world, point, vel);
|
||||
});
|
||||
}
|
||||
}
|
|
@ -1,6 +1,6 @@
|
|||
package com.minelittlepony.unicopia.client.particle;
|
||||
|
||||
import com.minelittlepony.unicopia.util.shape.Shape;
|
||||
import com.minelittlepony.unicopia.particle.ParticleUtils;
|
||||
import com.minelittlepony.unicopia.util.shape.Sphere;
|
||||
|
||||
import net.minecraft.block.BlockState;
|
||||
|
@ -32,27 +32,18 @@ public class GroundPoundParticle extends Particle {
|
|||
public void buildGeometry(VertexConsumer vertexConsumer, Camera camera, float f) {
|
||||
}
|
||||
|
||||
protected Vec3d getPos() {
|
||||
return new Vec3d(x, y, z);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void tick() {
|
||||
super.tick();
|
||||
|
||||
spawnParticleRing(age, 1);
|
||||
}
|
||||
Vec3d vel = new Vec3d(0, (0.5 + (Math.sin(age) * 2.5)) * 5, 0);
|
||||
|
||||
|
||||
private void spawnParticleRing(int timeDiff, double yVel) {
|
||||
|
||||
Shape shape = new Sphere(true, timeDiff, 1, 0, 1);
|
||||
|
||||
double y = 0.5 + (Math.sin(timeDiff) * 2.5);
|
||||
|
||||
yVel *= y * 5;
|
||||
|
||||
Vec3d center = new Vec3d(x, this.y, z);
|
||||
for (int i = 0; i < shape.getVolumeOfSpawnableSpace(); i++) {
|
||||
Vec3d point = shape.computePoint(random).add(center);
|
||||
|
||||
BlockPos pos = new BlockPos(point.x, center.y - 1, point.z);
|
||||
new Sphere(true, age, 1, 0, 1).offset(getPos()).randomPoints(random).forEach(point -> {
|
||||
BlockPos pos = new BlockPos(point).down();
|
||||
|
||||
BlockState state = world.getBlockState(pos);
|
||||
if (state.isAir()) {
|
||||
|
@ -62,13 +53,7 @@ public class GroundPoundParticle extends Particle {
|
|||
}
|
||||
}
|
||||
|
||||
world.addParticle(new BlockStateParticleEffect(ParticleTypes.BLOCK, state),
|
||||
point.x,
|
||||
point.y,
|
||||
point.z,
|
||||
0, yVel, 0
|
||||
);
|
||||
}
|
||||
ParticleUtils.spawnParticle(new BlockStateParticleEffect(ParticleTypes.BLOCK, state), world, point, vel);
|
||||
});
|
||||
}
|
||||
|
||||
}
|
||||
|
|
|
@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.item;
|
|||
|
||||
import com.minelittlepony.unicopia.entity.IItemEntity;
|
||||
import com.minelittlepony.unicopia.entity.ItemImpl;
|
||||
import com.minelittlepony.unicopia.particle.UParticles;
|
||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
|
||||
import com.minelittlepony.unicopia.util.WorldEvent;
|
||||
|
@ -138,6 +139,15 @@ public class JarItem extends Item implements ProjectileDelegate, ItemImpl.Tickab
|
|||
|
||||
if (rain || thunder) {
|
||||
projectile.world.syncWorldEvent(WorldEvent.PROJECTILE_HIT, projectile.getBlockPos(), thunder ? 0x888888 : 0xF8F8F8);
|
||||
|
||||
for (int i = projectile.world.random.nextInt(3) + 1; i >= 0; i--) {
|
||||
projectile.world.addParticle(UParticles.CLOUDS_ESCAPING, true,
|
||||
projectile.getX(), projectile.getY(), projectile.getZ(),
|
||||
projectile.world.random.nextFloat() - 0.5,
|
||||
0,
|
||||
projectile.world.random.nextFloat() - 0.5
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
WorldEvent.play(WorldEvent.DESTROY_BLOCK, projectile.world, projectile.getBlockPos(), Blocks.GLASS.getDefaultState());
|
||||
|
|
|
@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
|
|||
import net.minecraft.entity.Entity;
|
||||
import net.minecraft.particle.ParticleEffect;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
/**
|
||||
|
@ -28,12 +29,15 @@ public final class ParticleUtils {
|
|||
});
|
||||
}
|
||||
|
||||
public static void spawnParticle(ParticleEffect particleId, World world, Vec3d pos, Vec3d vel) {
|
||||
spawnParticle(world, particleId, pos.x, pos.y, pos.z, vel.x, vel.y, vel.z);
|
||||
}
|
||||
|
||||
public static void spawnParticle(World world, ParticleEffect effect, double x, double y, double z, double vX, double vY, double vZ) {
|
||||
if (world instanceof ServerWorld) {
|
||||
((ServerWorld)world).spawnParticles(effect, x, y, z, 1, vX, vY, vZ, 0);
|
||||
} else {
|
||||
world.addParticle(effect, x, y, z, vX, vY, vZ);
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
|
|
|
@ -22,6 +22,7 @@ public interface UParticles {
|
|||
ParticleType<FollowingParticleEffect> HEALTH_DRAIN = register("health_drain", FabricParticleTypes.complex(true, FollowingParticleEffect.FACTORY));
|
||||
|
||||
DefaultParticleType GROUND_POUND = register("ground_pound", FabricParticleTypes.simple());
|
||||
DefaultParticleType CLOUDS_ESCAPING = register("clouds_escaping", FabricParticleTypes.simple(true));
|
||||
|
||||
static <T extends ParticleType<?>> T register(String name, T type) {
|
||||
return Registry.register(Registry.PARTICLE_TYPE, new Identifier("unicopia", name), type);
|
||||
|
|
|
@ -0,0 +1,130 @@
|
|||
package com.minelittlepony.unicopia.util.shape;
|
||||
|
||||
import java.util.Random;
|
||||
|
||||
import net.minecraft.util.math.MathHelper;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
public class Cylinder implements Shape {
|
||||
|
||||
private final boolean hollow;
|
||||
|
||||
private final double stretchX;
|
||||
private final double stretchZ;
|
||||
|
||||
private final double height;
|
||||
private final double rad;
|
||||
|
||||
private float yaw = 0;
|
||||
private float pitch = 0;
|
||||
|
||||
private final double volume;
|
||||
|
||||
/**
|
||||
* Creates a uniform cylinder.
|
||||
*
|
||||
* @param hollow True if this shape must be hollow.
|
||||
* @param height Cylinder height
|
||||
* @param radius Cylinder radius
|
||||
*
|
||||
*/
|
||||
public Cylinder(boolean hollow, double height, double radius) {
|
||||
this(hollow, height, radius, 1, 1);
|
||||
}
|
||||
|
||||
/**
|
||||
* Creates a cylinder of arbitrary dimensions.
|
||||
* <p>
|
||||
* Can be used to create a flat circle by setting the height to 0.
|
||||
*
|
||||
* @param hollow True if this shape must be hollow.
|
||||
* @param height Cylinder height
|
||||
* @param radius Cylinder radius
|
||||
* @param stretchX Warp this shape's X-axis
|
||||
* @param stretchZ Warp this shape's Z-axis
|
||||
*/
|
||||
public Cylinder(boolean hollow, double height, double radius, double stretchX, double stretchZ) {
|
||||
this.hollow = hollow;
|
||||
this.height = height;
|
||||
rad = radius;
|
||||
this.stretchX = stretchX;
|
||||
this.stretchZ = stretchZ;
|
||||
volume = computeSpawnableSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getVolumeOfSpawnableSpace() {
|
||||
return volume;
|
||||
}
|
||||
|
||||
private double computeSpawnableSpace() {
|
||||
if (hollow) {
|
||||
if (stretchX != stretchZ) {
|
||||
double result = 3 * (stretchX + stretchZ);
|
||||
result -= Math.sqrt((10 * stretchX * stretchZ) + 3 * ((stretchX * stretchX) + (stretchZ * stretchZ)));
|
||||
return Math.PI * result;
|
||||
}
|
||||
return 2 * Math.PI * rad * stretchX * height;
|
||||
}
|
||||
return Math.PI * (stretchX * rad * stretchZ * rad) * height;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getXOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getYOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public double getZOffset() {
|
||||
return 0;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d computePoint(Random rand) {
|
||||
double y = MathHelper.nextDouble(rand, 0, height);
|
||||
double pheta = MathHelper.nextDouble(rand, 0, Math.PI * 2);
|
||||
double rho = hollow && Math.abs(y) != height/2 ? rad : MathHelper.nextDouble(rand, 0, rad);
|
||||
|
||||
return new Vec3d(rho * Math.cos(pheta) * stretchX, y, rho * Math.sin(pheta) * stretchZ)
|
||||
.rotateY(yaw)
|
||||
.rotateX(pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Cylinder setRotation(float u, float v) {
|
||||
yaw = u;
|
||||
pitch = v;
|
||||
return this;
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean isPointInside(Vec3d point) {
|
||||
point = point.rotateY(-yaw).rotateX(-pitch);
|
||||
point = new Vec3d(point.x / stretchX, point.y, point.z / stretchZ);
|
||||
double y = Math.abs(point.y);
|
||||
if (y < height/2) {
|
||||
double r = Math.sqrt((point.x * point.x) + (point.z * point.z));
|
||||
return hollow ? r == rad : r <= rad;
|
||||
}
|
||||
return y == height/2;
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d getLowerBound() {
|
||||
return new Vec3d(-rad * stretchX, 0, -rad * stretchZ)
|
||||
.rotateY(yaw)
|
||||
.rotateX(pitch);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d getUpperBound() {
|
||||
return new Vec3d(-rad * stretchX, height, -rad * stretchZ)
|
||||
.rotateY(yaw)
|
||||
.rotateX(pitch);
|
||||
}
|
||||
}
|
|
@ -0,0 +1,104 @@
|
|||
package com.minelittlepony.unicopia.util.shape;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators.AbstractSpliterator;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import com.google.common.collect.Streams;
|
||||
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
/**
|
||||
*
|
||||
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
|
||||
*/
|
||||
public interface PointGenerator {
|
||||
|
||||
/**
|
||||
* Get the volume of space filled by this shape, or the surface area if hollow.
|
||||
*
|
||||
* @return double volume
|
||||
*/
|
||||
double getVolumeOfSpawnableSpace();
|
||||
|
||||
/**
|
||||
* Computes a random coordinate that falls within this shape's designated area.
|
||||
*/
|
||||
Vec3d computePoint(Random rand);
|
||||
|
||||
/**
|
||||
* Returns a sequence of random points dealed out to uniformly fill this shape's area.
|
||||
*/
|
||||
default Stream<Vec3d> randomPoints(Random rand) {
|
||||
return randomPoints((int)getVolumeOfSpawnableSpace(), rand);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a sequence of N random points.
|
||||
*/
|
||||
default Stream<Vec3d> randomPoints(int n, Random rand) {
|
||||
AtomicInteger atom = new AtomicInteger(n);
|
||||
return StreamSupport.stream(new AbstractSpliterator<Vec3d>(n, Spliterator.SIZED) {
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Vec3d> consumer) {
|
||||
|
||||
if (atom.decrementAndGet() >= 0) {
|
||||
consumer.accept(computePoint(rand));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
|
||||
default PointGenerator union(PointGenerator other) {
|
||||
final PointGenerator source = this;
|
||||
return new PointGenerator() {
|
||||
|
||||
@Override
|
||||
public double getVolumeOfSpawnableSpace() {
|
||||
return source.getVolumeOfSpawnableSpace() + other.getVolumeOfSpawnableSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d computePoint(Random rand) {
|
||||
return source.computePoint(rand);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Stream<Vec3d> randomPoints(int n, Random rand) {
|
||||
double total = this.getVolumeOfSpawnableSpace();
|
||||
|
||||
return Streams.concat(
|
||||
source.randomPoints((int)(n * (source.getVolumeOfSpawnableSpace() / total)), rand),
|
||||
other.randomPoints((int)(n * (other.getVolumeOfSpawnableSpace() / total)), rand)
|
||||
);
|
||||
}
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a new point generator where all of its points are offset by the specified amount.
|
||||
*/
|
||||
default PointGenerator offset(Vec3d offset) {
|
||||
final PointGenerator source = this;
|
||||
return new PointGenerator() {
|
||||
|
||||
@Override
|
||||
public double getVolumeOfSpawnableSpace() {
|
||||
return source.getVolumeOfSpawnableSpace();
|
||||
}
|
||||
|
||||
@Override
|
||||
public Vec3d computePoint(Random rand) {
|
||||
return source.computePoint(rand).add(offset);
|
||||
}
|
||||
|
||||
};
|
||||
}
|
||||
}
|
|
@ -1,20 +1,12 @@
|
|||
package com.minelittlepony.unicopia.util.shape;
|
||||
|
||||
import java.util.Random;
|
||||
import java.util.Spliterator;
|
||||
import java.util.Spliterators.AbstractSpliterator;
|
||||
import java.util.concurrent.atomic.AtomicInteger;
|
||||
import java.util.function.Consumer;
|
||||
import java.util.stream.Stream;
|
||||
import java.util.stream.StreamSupport;
|
||||
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
|
||||
/**
|
||||
*
|
||||
*Interface for a 3d shape, used for spawning particles in a designated area (or anything else you need shapes for).
|
||||
*/
|
||||
public interface Shape {
|
||||
public interface Shape extends PointGenerator {
|
||||
|
||||
/**
|
||||
* Rotates this shape around it's center.
|
||||
|
@ -26,13 +18,6 @@ public interface Shape {
|
|||
*/
|
||||
Shape setRotation(float u, float v);
|
||||
|
||||
/**
|
||||
* Get the volume of space filled by this shape, or the surface area if hollow.
|
||||
*
|
||||
* @return double volume
|
||||
*/
|
||||
double getVolumeOfSpawnableSpace();
|
||||
|
||||
/**
|
||||
* X offset from the shape's origin.
|
||||
*
|
||||
|
@ -64,33 +49,9 @@ public interface Shape {
|
|||
*/
|
||||
Vec3d getUpperBound();
|
||||
|
||||
/**
|
||||
* Computes a random coordinate that falls within this shape's designated area.
|
||||
*/
|
||||
Vec3d computePoint(Random rand);
|
||||
|
||||
/**
|
||||
* Checks if the given point is on the edge, or if not hollow the inside, of this shape.
|
||||
* @return
|
||||
*/
|
||||
boolean isPointInside(Vec3d point);
|
||||
|
||||
/**
|
||||
* Returns a sequence of N random points.
|
||||
*/
|
||||
default Stream<Vec3d> randomPoints(int n, Random rand) {
|
||||
AtomicInteger atom = new AtomicInteger(n);
|
||||
return StreamSupport.stream(new AbstractSpliterator<Vec3d>(n, Spliterator.SIZED) {
|
||||
@Override
|
||||
public boolean tryAdvance(Consumer<? super Vec3d> consumer) {
|
||||
|
||||
if (atom.decrementAndGet() >= 0) {
|
||||
consumer.accept(computePoint(rand));
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}, false);
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,2 @@
|
|||
{
|
||||
}
|
Loading…
Reference in a new issue