Range indication for bells

- Stationary Haunted Bells now show particles at the perimeter of the scanned area, regardless of light level
This commit is contained in:
simibubi 2021-07-07 19:38:32 +02:00
parent efa2bc745f
commit 10b0bdbfcf
6 changed files with 148 additions and 33 deletions

View file

@ -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;

View file

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

View file

@ -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;
@ -48,18 +55,37 @@ public class SoulParticle extends CustomRotationParticle {
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<SoulParticle> {
@Override
public IBasicParticleFactory<SoulParticle> 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<SoulParticle> {
@Override
public IBasicParticleFactory<SoulParticle> 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;
}
}
}

View file

@ -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<List<BlockPos>> 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<BlockPos> spawns = getSoulSpawns(world);
List<BlockPos> 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<BlockPos> getSoulSpawns(World world) {
public List<BlockPos> getPotentialSoulSpawns(World world) {
if (world == null)
return new ArrayList<>();
return getLayer(currentLayerIdx()).map(p -> p.add(pos))
.filter(p -> canSpawnSoulAt(world, p))
.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,9 +96,16 @@ public class SoulPulseEffect {
return;
Vector3d p = Vector3d.of(at);
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<List<BlockPos>> genLayers() {
List<List<BlockPos>> layers = new ArrayList<>();
@ -142,7 +154,8 @@ public class SoulPulseEffect {
public static Stream<BlockPos> getLayer(int idx) {
if (idx < 0 || idx >= MAX_DISTANCE)
return Stream.empty();
return LAYERS.get(idx).stream();
return LAYERS.get(idx)
.stream();
}
}

View file

@ -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"
]
}

View file

@ -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"
]
}