Added a health drain particle for changelings, fixed changeling feed having too wide of a radius, and fixed particles not spawning on the clients

This commit is contained in:
Sollace 2021-02-02 12:52:50 +02:00
parent d02cd4226f
commit 6ffff32718
11 changed files with 180 additions and 24 deletions

View file

@ -8,6 +8,7 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit; import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleUtils; import com.minelittlepony.unicopia.particle.ParticleUtils;
import com.minelittlepony.unicopia.particle.UParticles; import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
@ -79,7 +80,7 @@ public class ChangelingFeedAbility implements Ability<Hit> {
} }
protected List<LivingEntity> getTargets(Pony player) { protected List<LivingEntity> getTargets(Pony player) {
List<Entity> list = VecHelper.findInReach(player.getMaster(), 3, this::canDrain); List<Entity> list = VecHelper.findInRange(player.getMaster(), player.getWorld(), player.getOriginVector(), 3, this::canDrain);
RayTraceHelper.<LivingEntity>findEntity(player.getMaster(), 17, 1, RayTraceHelper.<LivingEntity>findEntity(player.getMaster(), 17, 1,
looked -> looked instanceof LivingEntity && !list.contains(looked) && canDrain(looked)) looked -> looked instanceof LivingEntity && !list.contains(looked) && canDrain(looked))
@ -122,6 +123,7 @@ public class ChangelingFeedAbility implements Ability<Hit> {
} }
public float drainFrom(PlayerEntity changeling, LivingEntity living) { public float drainFrom(PlayerEntity changeling, LivingEntity living) {
DamageSource d = MagicalDamageSource.create("feed", changeling); DamageSource d = MagicalDamageSource.create("feed", changeling);
float damage = living.getHealth()/2; float damage = living.getHealth()/2;
@ -131,6 +133,7 @@ public class ChangelingFeedAbility implements Ability<Hit> {
} }
ParticleUtils.spawnParticles(UParticles.CHANGELING_MAGIC, living, 7); ParticleUtils.spawnParticles(UParticles.CHANGELING_MAGIC, living, 7);
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, changeling, 0.2F), living, 1);
if (changeling.hasStatusEffect(StatusEffects.NAUSEA)) { if (changeling.hasStatusEffect(StatusEffects.NAUSEA)) {
StatusEffectInstance effect = changeling.getStatusEffect(StatusEffects.NAUSEA); StatusEffectInstance effect = changeling.getStatusEffect(StatusEffects.NAUSEA);

View file

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.client;
import com.minelittlepony.unicopia.UEntities; import com.minelittlepony.unicopia.UEntities;
import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle; import com.minelittlepony.unicopia.client.particle.ChangelingMagicParticle;
import com.minelittlepony.unicopia.client.particle.DiskParticle; import com.minelittlepony.unicopia.client.particle.DiskParticle;
import com.minelittlepony.unicopia.client.particle.HealthDrainParticle;
import com.minelittlepony.unicopia.client.particle.MagicParticle; import com.minelittlepony.unicopia.client.particle.MagicParticle;
import com.minelittlepony.unicopia.client.particle.RainboomParticle; import com.minelittlepony.unicopia.client.particle.RainboomParticle;
import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle; import com.minelittlepony.unicopia.client.particle.RainbowTrailParticle;
@ -24,6 +25,7 @@ public interface URenderers {
ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.UNICORN_MAGIC, createFactory(MagicParticle::new));
ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new)); ParticleFactoryRegistry.getInstance().register(UParticles.CHANGELING_MAGIC, createFactory(ChangelingMagicParticle::new));
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::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.SPHERE, SphereParticle::new); ParticleFactoryRegistry.getInstance().register(UParticles.SPHERE, SphereParticle::new);
@ -33,10 +35,10 @@ public interface URenderers {
} }
static <T extends ParticleEffect> PendingParticleFactory<T> createFactory(ParticleSupplier<T> supplier) { static <T extends ParticleEffect> PendingParticleFactory<T> createFactory(ParticleSupplier<T> supplier) {
return provider -> (effect, world, x, y, z, dx, dy, dz) -> supplier.get(provider, world, x, y, z, dx, dy, dz); return provider -> (effect, world, x, y, z, dx, dy, dz) -> supplier.get(effect, provider, world, x, y, z, dx, dy, dz);
} }
interface ParticleSupplier<T extends ParticleEffect> { interface ParticleSupplier<T extends ParticleEffect> {
Particle get(SpriteProvider provider, ClientWorld world, double x, double y, double z, double dx, double dy, double dz); Particle get(T effect, SpriteProvider provider, ClientWorld world, double x, double y, double z, double dx, double dy, double dz);
} }
} }

View file

@ -2,13 +2,14 @@ package com.minelittlepony.unicopia.client.particle;
import net.minecraft.client.particle.SpriteProvider; import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.particle.ParticleEffect;
public class ChangelingMagicParticle extends MagicParticle { public class ChangelingMagicParticle extends MagicParticle {
private final SpriteProvider provider; private final SpriteProvider provider;
public ChangelingMagicParticle(SpriteProvider provider, ClientWorld world, double x, double y, double z, double dx, double dy, double dz) { public ChangelingMagicParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld world, double x, double y, double z, double dx, double dy, double dz) {
super(provider, world, x, y, z, dx, dy, dz, 1, 1, 1); super(effect, provider, world, x, y, z, dx, dy, dz, 1, 1, 1);
this.provider = provider; this.provider = provider;
float intensity = random.nextFloat() * 0.6F + 0.4F; float intensity = random.nextFloat() * 0.6F + 0.4F;

View file

@ -0,0 +1,48 @@
package com.minelittlepony.unicopia.client.particle;
import com.minelittlepony.unicopia.particle.FollowingParticleEffect;
import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.particle.SpriteBillboardParticle;
import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld;
import net.minecraft.util.math.Vec3d;
public class HealthDrainParticle extends SpriteBillboardParticle {
private final FollowingParticleEffect effect;
public HealthDrainParticle(FollowingParticleEffect effect, SpriteProvider provider, ClientWorld world, double x, double y, double z, double velocityX, double velocityY, double velocityZ) {
super(world, x, y, z, velocityX, velocityY, velocityZ);
setSprite(provider);
setMaxAge(3);
scale(0.125F);
this.effect = effect;
this.collidesWithWorld = false;
}
@Override
public ParticleTextureSheet getType() {
return ParticleTextureSheet.PARTICLE_SHEET_TRANSLUCENT;
}
@Override
public void tick() {
super.tick();
Vec3d target = effect.getTarget(world);
Vec3d pos = new Vec3d(x, y, z);
if (this.scale * 1.5F < 0.5F) {
scale(1.5F);
}
double distance = pos.distanceTo(target);
if (distance > 1) {
age = 0;
}
Vec3d motion = target.subtract(pos).normalize().multiply(Math.min(distance, effect.getSpeed()));
move(motion.x, motion.y, motion.z);
}
}

View file

@ -4,13 +4,14 @@ import net.minecraft.client.particle.ParticleTextureSheet;
import net.minecraft.client.particle.SpriteBillboardParticle; import net.minecraft.client.particle.SpriteBillboardParticle;
import net.minecraft.client.particle.SpriteProvider; import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.particle.ParticleEffect;
public class MagicParticle extends SpriteBillboardParticle { public class MagicParticle extends SpriteBillboardParticle {
private double startX; private double startX;
private double startY; private double startY;
private double startZ; private double startZ;
MagicParticle(SpriteProvider provider, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ, float r, float g, float b) { MagicParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ, float r, float g, float b) {
super(w, x, y, z); super(w, x, y, z);
setSprite(provider); setSprite(provider);
@ -28,8 +29,8 @@ public class MagicParticle extends SpriteBillboardParticle {
colorBlue = b; colorBlue = b;
} }
public MagicParticle(SpriteProvider provider, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) { public MagicParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld w, double x, double y, double z, double vX, double vY, double vZ) {
this(provider, w, x, y, z, vX, vY, vZ, 1, 1, 1); this(effect, provider, w, x, y, z, vX, vY, vZ, 1, 1, 1);
colorAlpha = 0.7F; colorAlpha = 0.7F;
colorGreen *= 0.3F; colorGreen *= 0.3F;

View file

@ -3,10 +3,11 @@ package com.minelittlepony.unicopia.client.particle;
import net.minecraft.client.particle.RainSplashParticle; import net.minecraft.client.particle.RainSplashParticle;
import net.minecraft.client.particle.SpriteProvider; import net.minecraft.client.particle.SpriteProvider;
import net.minecraft.client.world.ClientWorld; import net.minecraft.client.world.ClientWorld;
import net.minecraft.particle.ParticleEffect;
public class RaindropsParticle extends RainSplashParticle { public class RaindropsParticle extends RainSplashParticle {
public RaindropsParticle(SpriteProvider provider, ClientWorld world, double x, double y, double z, double dx, double dy, double dz) { public RaindropsParticle(ParticleEffect effect, SpriteProvider provider, ClientWorld world, double x, double y, double z, double dx, double dy, double dz) {
super(world, x, y, z); super(world, x, y, z);
velocityY = -0.1; velocityY = -0.1;
maxAge += 19; maxAge += 19;

View file

@ -0,0 +1,96 @@
package com.minelittlepony.unicopia.particle;
import java.util.Locale;
import com.mojang.brigadier.StringReader;
import com.mojang.brigadier.exceptions.CommandSyntaxException;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType;
import net.minecraft.entity.Entity;
import net.minecraft.network.PacketByteBuf;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
import net.minecraft.world.World;
public class FollowingParticleEffect implements ParticleEffect {
public static final ParticleEffect.Factory<FollowingParticleEffect> FACTORY = new ParticleEffect.Factory<FollowingParticleEffect>() {
@Override
public FollowingParticleEffect read(ParticleType<FollowingParticleEffect> type, StringReader reader) throws CommandSyntaxException {
reader.expect(' ');
double x = reader.readDouble();
reader.expect(' ');
double y = reader.readDouble();
reader.expect(' ');
double z = reader.readDouble();
reader.expect(' ');
float speed = reader.readFloat();
return new FollowingParticleEffect(type, -1, new Vec3d(x, y, z), speed);
}
@Override
public FollowingParticleEffect read(ParticleType<FollowingParticleEffect> particleType, PacketByteBuf buf) {
return new FollowingParticleEffect(particleType, buf.readInt(), new Vec3d(buf.readDouble(), buf.readDouble(), buf.readDouble()), buf.readFloat());
}
};
private final ParticleType<FollowingParticleEffect> type;
private Vec3d fixedTarget;
private int movingTarget;
private final float followSpeed;
public FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, Vec3d target, float followSpeed) {
this.type = type;
this.fixedTarget = target;
this.movingTarget = -1;
this.followSpeed = followSpeed;
}
public FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, Entity target, float followSpeed) {
this(type, target.getEntityId(), target.getCameraPosVec(1), followSpeed);
}
private FollowingParticleEffect(ParticleType<FollowingParticleEffect> type, int movingTarget, Vec3d fixedTarget, float followSpeed) {
this.type = type;
this.movingTarget = movingTarget;
this.fixedTarget = fixedTarget;
this.followSpeed = followSpeed;
}
public Vec3d getTarget(World world) {
if (movingTarget > -1) {
Entity e = world.getEntityById(movingTarget);
if (e != null) {
fixedTarget = e.getCameraPosVec(1);
} else {
movingTarget = -1;
}
}
return fixedTarget;
}
public float getSpeed() {
return followSpeed;
}
@Override
public ParticleType<?> getType() {
return type;
}
@Override
public void write(PacketByteBuf buf) {
buf.writeInt(movingTarget);
buf.writeDouble(fixedTarget.x);
buf.writeDouble(fixedTarget.y);
buf.writeDouble(fixedTarget.z);
}
@Override
public String asString() {
return String.format(Locale.ROOT, "%s %.2f %.2f %.2f %.2f", Registry.PARTICLE_TYPE.getId(getType()), fixedTarget.x, fixedTarget.y, fixedTarget.z, followSpeed);
}
}

View file

@ -5,6 +5,8 @@ import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.particle.ParticleEffect; import net.minecraft.particle.ParticleEffect;
import net.minecraft.server.world.ServerWorld;
import net.minecraft.world.World;
/** /**
* Utility for spawning particles. * Utility for spawning particles.
@ -18,11 +20,20 @@ public final class ParticleUtils {
Shape shape = new Sphere(false, Math.abs((float)halfDist + entity.getWidth())); Shape shape = new Sphere(false, Math.abs((float)halfDist + entity.getWidth()));
shape.randomPoints(count, entity.world.random).forEach(point -> { shape.randomPoints(count, entity.world.random).forEach(point -> {
entity.world.addParticle(particleId, spawnParticle(entity.world, particleId,
entity.getX() + point.x, entity.getX() + point.x,
middle + point.y, middle + point.y,
entity.getZ() + point.z, entity.getZ() + point.z,
0, 0, 0); 0, 0, 0);
}); });
} }
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);
}
}
} }

View file

@ -19,6 +19,8 @@ public interface UParticles {
ParticleType<SphereParticleEffect> SPHERE = register("sphere", FabricParticleTypes.complex(true, SphereParticleEffect.FACTORY)); ParticleType<SphereParticleEffect> SPHERE = register("sphere", FabricParticleTypes.complex(true, SphereParticleEffect.FACTORY));
ParticleType<DiskParticleEffect> DISK = register("disk", FabricParticleTypes.complex(true, DiskParticleEffect.FACTORY)); ParticleType<DiskParticleEffect> DISK = register("disk", FabricParticleTypes.complex(true, DiskParticleEffect.FACTORY));
ParticleType<FollowingParticleEffect> HEALTH_DRAIN = register("health_drain", FabricParticleTypes.complex(true, FollowingParticleEffect.FACTORY));
static <T extends ParticleType<?>> T register(String name, T type) { static <T extends ParticleType<?>> T register(String name, T type) {
return Registry.register(Registry.PARTICLE_TYPE, new Identifier("unicopia", name), type); return Registry.register(Registry.PARTICLE_TYPE, new Identifier("unicopia", name), type);
} }

View file

@ -5,7 +5,6 @@ import java.util.function.Predicate;
import javax.annotation.Nullable; import javax.annotation.Nullable;
import net.minecraft.entity.Entity; import net.minecraft.entity.Entity;
import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.util.math.Box; import net.minecraft.util.math.Box;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
@ -23,17 +22,4 @@ public interface VecHelper {
static List<Entity> findInRange(@Nullable Entity origin, World w, Vec3d pos, double radius, @Nullable Predicate<Entity> predicate) { static List<Entity> findInRange(@Nullable Entity origin, World w, Vec3d pos, double radius, @Nullable Predicate<Entity> predicate) {
return w.getOtherEntities(origin, Box.method_29968(pos).expand(radius), predicate == null ? inRange(pos, radius) : inRange(pos, radius).and(predicate)); return w.getOtherEntities(origin, Box.method_29968(pos).expand(radius), predicate == null ? inRange(pos, radius) : inRange(pos, radius).and(predicate));
} }
/**
* Gets all entities within a given range from the player.
*/
static List<Entity> findInReach(PlayerEntity player, double reach, @Nullable Predicate<? super Entity> predicate) {
Vec3d look = player.getCameraPosVec(1).multiply(reach);
return player.world.getOtherEntities(player, player
.getBoundingBox()
.expand(look.x, look.y, look.z)
.expand(1, 1, 1), predicate);
}
} }

View file

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