diff --git a/src/main/java/com/simibubi/create/AllParticleTypes.java b/src/main/java/com/simibubi/create/AllParticleTypes.java index 23b3fdf0a..a7cd1df79 100644 --- a/src/main/java/com/simibubi/create/AllParticleTypes.java +++ b/src/main/java/com/simibubi/create/AllParticleTypes.java @@ -35,7 +35,9 @@ public enum AllParticleTypes { BASIN_FLUID(FluidParticleData::new), FLUID_DRIP(FluidParticleData::new), SOUL(SoulParticle.Data::new), - SOUL_BASE(SoulBaseParticle.Data::new) + SOUL_BASE(SoulBaseParticle.Data::new), + SOUL_PERIMETER(SoulParticle.PerimeterData::new), + SOUL_EXPANDING_PERIMETER(SoulParticle.ExpandingPerimeterData::new) ; private ParticleEntry entry; diff --git a/src/main/java/com/simibubi/create/content/curiosities/bell/SoulBaseParticle.java b/src/main/java/com/simibubi/create/content/curiosities/bell/SoulBaseParticle.java index fd17c7d16..1036f0064 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/bell/SoulBaseParticle.java +++ b/src/main/java/com/simibubi/create/content/curiosities/bell/SoulBaseParticle.java @@ -31,7 +31,7 @@ public class SoulBaseParticle extends CustomRotationParticle { selectSpriteLoopingWithAge(animatedSprite); BlockPos pos = new BlockPos(posX, posY, posZ); - if (age++ >= maxAge || !SoulPulseEffect.canSpawnSoulAt(world, pos)) + if (age++ >= maxAge || !SoulPulseEffect.canSpawnSoulAt(world, pos, false)) setExpired(); } diff --git a/src/main/java/com/simibubi/create/content/curiosities/bell/SoulParticle.java b/src/main/java/com/simibubi/create/content/curiosities/bell/SoulParticle.java index 302ecc398..5c514da9f 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/bell/SoulParticle.java +++ b/src/main/java/com/simibubi/create/content/curiosities/bell/SoulParticle.java @@ -1,13 +1,16 @@ package com.simibubi.create.content.curiosities.bell; +import com.mojang.blaze3d.vertex.IVertexBuilder; import com.simibubi.create.AllParticleTypes; import net.minecraft.client.particle.IAnimatedSprite; import net.minecraft.client.renderer.ActiveRenderInfo; import net.minecraft.client.world.ClientWorld; +import net.minecraft.particles.IParticleData; import net.minecraft.particles.ParticleType; import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.vector.Quaternion; +import net.minecraft.util.math.vector.Vector3f; public class SoulParticle extends CustomRotationParticle { @@ -26,14 +29,18 @@ public class SoulParticle extends CustomRotationParticle { protected int firstEndFrame = 33; protected int endFrames = 20; - protected int totalFrames = 53; - - protected int ticksPerFrame = 2; - protected AnimationStage animationStage; + protected int totalFrames = 53; + protected int ticksPerFrame = 2; + + protected boolean isPerimeter = false; + protected boolean isExpandingPerimeter = false; + protected boolean isVisible = true; + protected int perimeterFrames = 8; + public SoulParticle(ClientWorld worldIn, double x, double y, double z, double vx, double vy, double vz, - IAnimatedSprite spriteSet) { + IAnimatedSprite spriteSet, IParticleData data) { super(worldIn, x, y, z, spriteSet, 0); this.animatedSprite = spriteSet; this.particleScale = 0.5f; @@ -42,24 +49,43 @@ public class SoulParticle extends CustomRotationParticle { this.loopLength = loopFrames + (int) (this.rand.nextFloat() * 5f - 4f); this.startTicks = startFrames + (int) (this.rand.nextFloat() * 5f - 4f); this.endTicks = endFrames + (int) (this.rand.nextFloat() * 5f - 4f); - this.numLoops = (int)(1f + this.rand.nextFloat() * 2f); + this.numLoops = (int) (1f + this.rand.nextFloat() * 2f); this.setFrame(0); this.field_21507 = true; // disable movement this.mirror = this.rand.nextBoolean(); - this.animationStage = new StartAnimation(this); + this.isPerimeter = data instanceof PerimeterData; + this.isExpandingPerimeter = data instanceof ExpandingPerimeterData; + this.animationStage = !isPerimeter ? new StartAnimation(this) : new PerimeterAnimation(this); + if (isPerimeter) { + prevPosY = posY -= .5f - 1 / 128f; + totalFrames = perimeterFrames; + isVisible = false; + } } @Override public void tick() { animationStage.tick(); - animationStage = animationStage.getNext(); BlockPos pos = new BlockPos(posX, posY, posZ); - if (animationStage == null || !SoulPulseEffect.canSpawnSoulAt(world, pos)) + if (animationStage == null) setExpired(); + if (!SoulPulseEffect.canSpawnSoulAt(world, pos, false)) { + isVisible = true; + if (!isPerimeter) + setExpired(); + } else if (isPerimeter) + isVisible = false; + } + + @Override + public void buildGeometry(IVertexBuilder builder, ActiveRenderInfo camera, float partialTicks) { + if (!isVisible) + return; + super.buildGeometry(builder, camera, partialTicks); } public void setFrame(int frame) { @@ -69,20 +95,44 @@ public class SoulParticle extends CustomRotationParticle { @Override public Quaternion getCustomRotation(ActiveRenderInfo camera, float partialTicks) { + if (isPerimeter) + return Vector3f.POSITIVE_X.getDegreesQuaternion(90); return new Quaternion(0, -camera.getYaw(), 0, true); } public static class Data extends BasicParticleData { @Override public IBasicParticleFactory getBasicFactory() { - return SoulParticle::new; + return (worldIn, x, y, z, vx, vy, vz, spriteSet) -> new SoulParticle(worldIn, x, y, z, vx, vy, vz, + spriteSet, this); } + @Override public ParticleType getType() { return AllParticleTypes.SOUL.get(); } } + public static class PerimeterData extends BasicParticleData { + @Override + public IBasicParticleFactory getBasicFactory() { + return (worldIn, x, y, z, vx, vy, vz, spriteSet) -> new SoulParticle(worldIn, x, y, z, vx, vy, vz, + spriteSet, this); + } + + @Override + public ParticleType getType() { + return AllParticleTypes.SOUL_PERIMETER.get(); + } + } + + public static class ExpandingPerimeterData extends PerimeterData { + @Override + public ParticleType getType() { + return AllParticleTypes.SOUL_EXPANDING_PERIMETER.get(); + } + } + public static abstract class AnimationStage { protected final SoulParticle particle; @@ -118,7 +168,8 @@ public class SoulParticle extends CustomRotationParticle { public void tick() { super.tick(); - particle.setFrame(particle.firstStartFrame + (int) (getAnimAge() / (float) particle.startTicks * particle.startFrames)); + particle.setFrame( + particle.firstStartFrame + (int) (getAnimAge() / (float) particle.startTicks * particle.startFrames)); } @Override @@ -144,9 +195,11 @@ public class SoulParticle extends CustomRotationParticle { int loopTick = getLoopTick(); - if (loopTick == 0) loops++; + if (loopTick == 0) + loops++; - particle.setFrame(particle.firstLoopFrame + loopTick);//(int) (((float) loopTick / (float) particle.loopLength) * particle.loopFrames)); + particle.setFrame(particle.firstLoopFrame + loopTick);// (int) (((float) loopTick / (float) + // particle.loopLength) * particle.loopFrames)); } @@ -173,7 +226,8 @@ public class SoulParticle extends CustomRotationParticle { public void tick() { super.tick(); - particle.setFrame(particle.firstEndFrame + (int) ((getAnimAge() / (float) particle.endTicks) * particle.endFrames)); + particle.setFrame( + particle.firstEndFrame + (int) ((getAnimAge() / (float) particle.endTicks) * particle.endFrames)); } @@ -185,4 +239,26 @@ public class SoulParticle extends CustomRotationParticle { return null; } } + + public static class PerimeterAnimation extends AnimationStage { + + public PerimeterAnimation(SoulParticle particle) { + super(particle); + } + + @Override + public void tick() { + super.tick(); + particle.setFrame((int) getAnimAge() % particle.perimeterFrames); + } + + @Override + public AnimationStage getNext() { + if (animAge < (particle.isExpandingPerimeter ? 8 + : particle.startTicks + particle.endTicks + particle.numLoops * particle.loopLength)) + return this; + else + return null; + } + } } diff --git a/src/main/java/com/simibubi/create/content/curiosities/bell/SoulPulseEffect.java b/src/main/java/com/simibubi/create/content/curiosities/bell/SoulPulseEffect.java index 7fbacdffa..a1dc8c640 100644 --- a/src/main/java/com/simibubi/create/content/curiosities/bell/SoulPulseEffect.java +++ b/src/main/java/com/simibubi/create/content/curiosities/bell/SoulPulseEffect.java @@ -5,6 +5,9 @@ import java.util.List; import java.util.stream.Collectors; import java.util.stream.Stream; +import com.simibubi.create.content.curiosities.bell.SoulParticle.ExpandingPerimeterData; +import com.simibubi.create.foundation.utility.VecHelper; + import net.minecraft.entity.EntitySpawnPlacementRegistry; import net.minecraft.entity.EntityType; import net.minecraft.util.math.AxisAlignedBB; @@ -17,7 +20,7 @@ import net.minecraft.world.spawner.WorldEntitySpawner; public class SoulPulseEffect { - public static final int MAX_DISTANCE = 10; + public static final int MAX_DISTANCE = 11; private static final List> LAYERS = genLayers(); private static final int WAITING_TICKS = 100; @@ -50,10 +53,10 @@ public class SoulPulseEffect { if (ticks < 0 || ticks % TICKS_PER_LAYER != 0) return null; - List spawns = getSoulSpawns(world); + List spawns = getPotentialSoulSpawns(world); while (spawns.isEmpty() && ticks > 0) { ticks -= TICKS_PER_LAYER; - spawns.addAll(getSoulSpawns(world)); + spawns.addAll(getPotentialSoulSpawns(world)); } return spawns; } @@ -62,28 +65,30 @@ public class SoulPulseEffect { return distance - ticks / TICKS_PER_LAYER - 1; } - public List getSoulSpawns(World world) { + public List getPotentialSoulSpawns(World world) { if (world == null) return new ArrayList<>(); return getLayer(currentLayerIdx()).map(p -> p.add(pos)) - .filter(p -> canSpawnSoulAt(world, p)) - .collect(Collectors.toList()); + .filter(p -> canSpawnSoulAt(world, p, true)) + .collect(Collectors.toList()); } - public static boolean canSpawnSoulAt(World world, BlockPos at) { + public static boolean canSpawnSoulAt(World world, BlockPos at, boolean ignoreLight) { EntityType dummy = EntityType.ZOMBIE; double dummyWidth = 0.2, dummyHeight = 0.75; double w2 = dummyWidth / 2; return world != null - && WorldEntitySpawner.canCreatureTypeSpawnAtLocation( - EntitySpawnPlacementRegistry.PlacementType.ON_GROUND, world, at, dummy) - && world.getLightLevel(LightType.BLOCK, at) < 8 - && world.getBlockCollisions(null, new AxisAlignedBB( - at.getX() + 0.5 - w2, at.getY(), at.getZ() + 0.5 - w2, - at.getX() + 0.5 + w2, at.getY() + dummyHeight, at.getZ() + 0.5 + w2 - ), (a,b) -> true).allMatch(VoxelShape::isEmpty); + && WorldEntitySpawner + .canCreatureTypeSpawnAtLocation(EntitySpawnPlacementRegistry.PlacementType.ON_GROUND, world, at, dummy) + && (ignoreLight || world.getLightLevel(LightType.BLOCK, at) < 8) + && world + .getBlockCollisions(null, + new AxisAlignedBB(at.getX() + 0.5 - w2, at.getY(), at.getZ() + 0.5 - w2, at.getX() + 0.5 + w2, + at.getY() + dummyHeight, at.getZ() + 0.5 + w2), + (a, b) -> true) + .allMatch(VoxelShape::isEmpty); } public void spawnParticles(World world, BlockPos at) { @@ -91,8 +96,15 @@ public class SoulPulseEffect { return; Vector3d p = Vector3d.of(at); - world.addOptionalParticle(new SoulParticle.Data(), p.x + 0.5, p.y + 0.5, p.z + 0.5, 0, 0, 0); - world.addParticle(new SoulBaseParticle.Data(), p.x + 0.5, p.y + 0.01, p.z + 0.5, 0, 0, 0); + if (canOverlap()) + world.addOptionalParticle(((int) Math.round(VecHelper.getCenterOf(pos) + .distanceTo(VecHelper.getCenterOf(at)))) >= distance ? new SoulParticle.PerimeterData() + : new ExpandingPerimeterData(), + p.x + 0.5, p.y + 0.5, p.z + 0.5, 0, 0, 0); + if (world.getLightLevel(LightType.BLOCK, at) < 8) { + world.addOptionalParticle(new SoulParticle.Data(), p.x + 0.5, p.y + 0.5, p.z + 0.5, 0, 0, 0); + world.addParticle(new SoulBaseParticle.Data(), p.x + 0.5, p.y + 0.01, p.z + 0.5, 0, 0, 0); + } } private static List> genLayers() { @@ -142,7 +154,8 @@ public class SoulPulseEffect { public static Stream getLayer(int idx) { if (idx < 0 || idx >= MAX_DISTANCE) return Stream.empty(); - return LAYERS.get(idx).stream(); + return LAYERS.get(idx) + .stream(); } } diff --git a/src/main/resources/assets/create/particles/soul_expanding_perimeter.json b/src/main/resources/assets/create/particles/soul_expanding_perimeter.json new file mode 100644 index 000000000..3274e5921 --- /dev/null +++ b/src/main/resources/assets/create/particles/soul_expanding_perimeter.json @@ -0,0 +1,12 @@ +{ + "textures": [ + "create:soul_base_0", + "create:soul_base_1", + "create:soul_base_2", + "create:soul_base_3", + "create:soul_base_2", + "create:soul_base_1", + "create:soul_base_0", + "create:soul_base_0" + ] +} diff --git a/src/main/resources/assets/create/particles/soul_perimeter.json b/src/main/resources/assets/create/particles/soul_perimeter.json new file mode 100644 index 000000000..92b9f65be --- /dev/null +++ b/src/main/resources/assets/create/particles/soul_perimeter.json @@ -0,0 +1,12 @@ +{ + "textures": [ + "create:soul_base_0", + "create:soul_base_1", + "create:soul_base_2", + "create:soul_base_3", + "create:soul_base_4", + "create:soul_base_5", + "create:soul_base_6", + "create:soul_base_7" + ] +}