Polish up the siphoning and necro spells

This commit is contained in:
Sollace 2021-03-03 20:04:40 +02:00
parent 3bb4d36a7e
commit c9670a3d3c
6 changed files with 208 additions and 90 deletions

View file

@ -18,6 +18,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
import net.minecraft.entity.damage.DamageSource; import net.minecraft.entity.damage.DamageSource;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
/** /**
@ -77,7 +78,15 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
} }
default Stream<Entity> findAllEntitiesInRange(double radius, @Nullable Predicate<Entity> test) { default Stream<Entity> findAllEntitiesInRange(double radius, @Nullable Predicate<Entity> test) {
return VecHelper.findInRange(getEntity(), getWorld(), getOriginVector(), radius, test).stream(); return findAllEntitiesInRange(getOriginVector(), radius, test);
}
default Stream<Entity> findAllEntitiesInRange(Vec3d origin, double radius, @Nullable Predicate<Entity> test) {
return VecHelper.findInRange(getEntity(), getWorld(), origin, radius, test).stream();
}
default Stream<Entity> findAllEntitiesInRange(Vec3d origin, double radius) {
return findAllEntitiesInRange(origin, radius, null);
} }
default Stream<Entity> findAllEntitiesInRange(double radius) { default Stream<Entity> findAllEntitiesInRange(double radius) {

View file

@ -0,0 +1,93 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle;
import com.minelittlepony.unicopia.particle.UParticles;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d;
public abstract class AbstractPlacedSpell extends AbstractSpell implements Attached {
@Nullable
protected Vec3d origin;
@Nullable
protected BlockPos placement;
@Nullable
private Identifier dimension;
private final ParticleHandle particlEffect = new ParticleHandle();
protected AbstractPlacedSpell(SpellType<?> type) {
super(type);
}
@Override
public void setDead() {
super.setDead();
particlEffect.destroy();
}
@Override
public boolean onBodyTick(Caster<?> source) {
if (origin == null) {
origin = source.getOriginVector();
placement = source.getOrigin();
dimension = source.getWorld().getRegistryKey().getValue();
}
if (!source.getWorld().getRegistryKey().getValue().equals(dimension)) {
return false;
}
if (source.isClient()) {
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.MAGIC_RUNES, 90, 0), origin, Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(1, getType().getColor());
});
}
return onGroundTick(source);
}
protected abstract boolean onGroundTick(Caster<?> source);
@Override
public void toNBT(CompoundTag compound) {
super.toNBT(compound);
if (placement != null) {
compound.put("placement", NbtHelper.fromBlockPos(placement));
}
if (origin != null) {
compound.put("origin", NbtSerialisable.writeVector(origin));
}
if (dimension != null) {
compound.putString("dimension", dimension.toString());
}
}
@Override
public void fromNBT(CompoundTag compound) {
super.fromNBT(compound);
if (compound.contains("placement")) {
placement = NbtHelper.toBlockPos(compound.getCompound("placement"));
}
if (compound.contains("origin")) {
origin = NbtSerialisable.readVector(compound.getList("origin", 6));
}
if (compound.contains("dimension")) {
dimension = new Identifier(compound.getString("dimension"));
}
}
}

View file

@ -8,7 +8,6 @@ import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect; import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import com.minelittlepony.unicopia.util.VecHelper; import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
@ -16,6 +15,7 @@ import net.minecraft.entity.Entity;
import net.minecraft.entity.ItemEntity; import net.minecraft.entity.ItemEntity;
import net.minecraft.entity.player.PlayerEntity; import net.minecraft.entity.player.PlayerEntity;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.nbt.NbtHelper;
import net.minecraft.util.math.BlockPos; import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
@ -91,7 +91,7 @@ public class AttractiveSpell extends ShieldSpell implements Thrown {
public void toNBT(CompoundTag compound) { public void toNBT(CompoundTag compound) {
super.toNBT(compound); super.toNBT(compound);
if (homingPos != null) { if (homingPos != null) {
compound.put("homingPos", NbtSerialisable.writeBlockPos(homingPos)); compound.put("homingPos", NbtHelper.fromBlockPos(homingPos));
} }
} }
@ -99,7 +99,7 @@ public class AttractiveSpell extends ShieldSpell implements Thrown {
public void fromNBT(CompoundTag compound) { public void fromNBT(CompoundTag compound) {
super.fromNBT(compound); super.fromNBT(compound);
if (compound.contains("homingPos")) { if (compound.contains("homingPos")) {
homingPos = NbtSerialisable.readBlockPos(compound.getCompound("homingPos")); homingPos = NbtHelper.toBlockPos(compound.getCompound("homingPos"));
} }
} }

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List; import java.util.List;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.util.WorldEvent; import com.minelittlepony.unicopia.util.WorldEvent;
import com.minelittlepony.unicopia.util.shape.Shape; import com.minelittlepony.unicopia.util.shape.Shape;
@ -18,7 +17,7 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.Difficulty; import net.minecraft.world.Difficulty;
public class NecromancySpell extends AbstractSpell implements Attached { public class NecromancySpell extends AbstractPlacedSpell {
private final List<EntityType<? extends LivingEntity>> spawns = Lists.newArrayList( private final List<EntityType<? extends LivingEntity>> spawns = Lists.newArrayList(
EntityType.ZOMBIE, EntityType.ZOMBIE,
@ -31,19 +30,16 @@ public class NecromancySpell extends AbstractSpell implements Attached {
} }
@Override @Override
public boolean onBodyTick(Caster<?> source) { public boolean onGroundTick(Caster<?> source) {
int radius = source.getLevel().get() + 1; int radius = (source.getLevel().get() + 1) * 4;
if (source.isClient()) { if (source.isClient()) {
Shape affectRegion = new Sphere(false, radius * 4); source.spawnParticles(origin, new Sphere(false, radius), 5, pos -> {
source.spawnParticles(affectRegion, 5, pos -> {
if (!source.getWorld().isAir(new BlockPos(pos).down())) { if (!source.getWorld().isAir(new BlockPos(pos).down())) {
source.addParticle(ParticleTypes.FLAME, pos, Vec3d.ZERO); source.addParticle(ParticleTypes.FLAME, pos, Vec3d.ZERO);
} }
}); });
return true; return true;
} }
@ -51,18 +47,16 @@ public class NecromancySpell extends AbstractSpell implements Attached {
return true; return true;
} }
float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty(); float additional = source.getWorld().getLocalDifficulty(placement).getLocalDifficulty();
Shape affectRegion = new Sphere(false, radius * 4); Shape affectRegion = new Sphere(false, radius);
if (source.getWorld().random.nextInt(100) != 0) { if (source.getWorld().random.nextInt(100) != 0) {
return true; return true;
} }
Vec3d origin = source.getOriginVector(); if (source.findAllEntitiesInRange(radius, e -> e instanceof ZombieEntity).count() >= 10 * (1 + additional)) {
return false;
if (source.findAllEntitiesInRange(radius * 4, e -> e instanceof ZombieEntity).count() >= 10 * (1 + additional)) {
return true;
} }
for (int i = 0; i < 10; i++) { for (int i = 0; i < 10; i++) {
@ -72,12 +66,9 @@ public class NecromancySpell extends AbstractSpell implements Attached {
if (source.getWorld().isAir(loc.up()) && !source.getWorld().isAir(loc)) { if (source.getWorld().isAir(loc.up()) && !source.getWorld().isAir(loc)) {
spawnMonster(source, pos); spawnMonster(source, pos);
return false;
} }
} }
return true; return true;
} }
@ -85,6 +76,8 @@ public class NecromancySpell extends AbstractSpell implements Attached {
int index = (int)MathHelper.nextDouble(source.getWorld().random, 0, spawns.size()); int index = (int)MathHelper.nextDouble(source.getWorld().random, 0, spawns.size());
LivingEntity zombie = spawns.get(index).create(source.getWorld()); LivingEntity zombie = spawns.get(index).create(source.getWorld());
source.subtractEnergyCost(3);
zombie.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0); zombie.updatePositionAndAngles(pos.x, pos.y, pos.z, 0, 0);
zombie.setVelocity(0, 0.3, 0); zombie.setVelocity(0, 0.3, 0);

View file

@ -2,12 +2,16 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List; import java.util.List;
import java.util.stream.Collectors; import java.util.stream.Collectors;
import java.util.stream.Stream;
import com.minelittlepony.unicopia.Race; import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster; import com.minelittlepony.unicopia.ability.magic.Caster;
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.UParticles;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.VecHelper;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.entity.LivingEntity; import net.minecraft.entity.LivingEntity;
@ -20,21 +24,20 @@ import net.minecraft.util.math.Vec3d;
/** /**
* A spell that pulls health from other entities and delivers it to the caster. * A spell that pulls health from other entities and delivers it to the caster.
*/ */
public class SiphoningSpell extends AbstractSpell implements Attached { public class SiphoningSpell extends AbstractPlacedSpell {
protected SiphoningSpell(SpellType<?> type) { protected SiphoningSpell(SpellType<?> type) {
super(type); super(type);
} }
@Override @Override
public boolean onBodyTick(Caster<?> source) { public boolean onGroundTick(Caster<?> source) {
int radius = 4 + source.getLevel().get();
if (source.isClient()) { if (source.isClient()) {
Vec3d origin = source.getOriginVector(); int radius = 4 + source.getLevel().get();
int direction = !isEnemy(source) ? 1 : -1; int direction = isFriendlyTogether(source) ? 1 : -1;
source.spawnParticles(new Sphere(true, radius, 1, 0, 1), 1, pos -> { source.spawnParticles(origin, new Sphere(true, radius, 1, 0, 1), 1, pos -> {
if (!source.getWorld().isAir(new BlockPos(pos).down())) { if (!source.getWorld().isAir(new BlockPos(pos).down())) {
double dist = pos.distanceTo(origin); double dist = pos.distanceTo(origin);
@ -45,24 +48,67 @@ public class SiphoningSpell extends AbstractSpell implements Attached {
}); });
} }
LivingEntity owner = source.getMaster(); if (source.getWorld().getTime() % 10 != 0) {
return true;
}
List<LivingEntity> target = source.findAllEntitiesInRange(radius) if (isFriendlyTogether(source)) {
.filter(e -> e instanceof LivingEntity) distributeHealth(source);
.map(e -> (LivingEntity)e) } else {
.collect(Collectors.toList()); collectHealth(source);
}
return true;
}
private Stream<LivingEntity> getTargets(Caster<?> source) {
return VecHelper.findInRange(null, source.getWorld(), origin, 4 + source.getLevel().get(), e -> e instanceof LivingEntity)
.stream()
.map(e -> (LivingEntity)e);
}
private void distributeHealth(Caster<?> source) {
LivingEntity owner = source.getMaster();
DamageSource damage = MagicalDamageSource.create("drain", owner);
getTargets(source).forEach(e -> {
float maxHealthGain = e.getMaxHealth() - e.getHealth();
source.subtractEnergyCost(0.2F);
if (maxHealthGain <= 0) {
if (source.getWorld().random.nextInt(30) == 0) {
onDestroyed(source);
} else {
e.damage(damage, e.getHealth() / 4);
}
} else {
e.heal((float)Math.min(0.5F * (1 + source.getLevel().get()), maxHealthGain * 0.6));
ParticleUtils.spawnParticle(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, e, 0.2F), e.world, e.getPos(), Vec3d.ZERO);
}
});
}
private void collectHealth(Caster<?> source) {
LivingEntity owner = source.getMaster();
float maxHealthGain = owner.getMaxHealth() - owner.getHealth();
if (maxHealthGain == 0) {
return;
}
List<LivingEntity> targets = getTargets(source).collect(Collectors.toList());
if (targets.isEmpty()) {
return;
}
float attackAmount = Math.max(maxHealthGain / targets.size(), 0.5F);
DamageSource damage = MagicalDamageSource.create("drain", owner); DamageSource damage = MagicalDamageSource.create("drain", owner);
if (!isFriendlyTogether(source)) {
if (owner != null) {
float healthGain = 0; float healthGain = 0;
float maxHealthGain = owner.getMaxHealth() - owner.getHealth();
if (maxHealthGain > 0) { for (LivingEntity e : targets) {
float attackAmount = Math.max(maxHealthGain / target.size(), 0.5F);
for (LivingEntity e : target) {
if (!e.equals(owner)) { if (!e.equals(owner)) {
float dealt = Math.min(e.getHealth(), attackAmount); float dealt = Math.min(e.getHealth(), attackAmount);
@ -80,31 +126,12 @@ public class SiphoningSpell extends AbstractSpell implements Attached {
} }
e.damage(damage, dealt); e.damage(damage, dealt);
ParticleUtils.spawnParticles(new FollowingParticleEffect(UParticles.HEALTH_DRAIN, owner, 0.2F), e, 1);
healthGain += dealt; healthGain += dealt;
} }
} }
}
owner.heal(healthGain); owner.heal(healthGain);
} }
} else {
target.forEach(e -> {
float maxHealthGain = e.getMaxHealth() - e.getHealth();
if (maxHealthGain <= 0) {
if (source.getWorld().random.nextInt(30) == 0) {
onDestroyed(source);
} else {
e.damage(damage, e.getHealth() / 4);
}
} else {
e.heal((float)Math.min(0.5F * (1 + source.getLevel().get()), maxHealthGain * 0.6));
}
});
}
return false;
}
} }

View file

@ -1,7 +1,9 @@
package com.minelittlepony.unicopia.util; package com.minelittlepony.unicopia.util;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.math.BlockPos; import net.minecraft.nbt.DoubleTag;
import net.minecraft.nbt.ListTag;
import net.minecraft.util.math.Vec3d;
public interface NbtSerialisable { public interface NbtSerialisable {
/** /**
@ -28,21 +30,15 @@ public interface NbtSerialisable {
return compound; return compound;
} }
static CompoundTag writeBlockPos(BlockPos pos) { static ListTag writeVector(Vec3d vector) {
CompoundTag dest = new CompoundTag(); ListTag list = new ListTag();
list.add(DoubleTag.of(vector.getX()));
dest.putInt("X", pos.getX()); list.add(DoubleTag.of(vector.getY()));
dest.putInt("Y", pos.getY()); list.add(DoubleTag.of(vector.getZ()));
dest.putInt("Z", pos.getZ()); return list;
return dest;
} }
static BlockPos readBlockPos(CompoundTag compound) { static Vec3d readVector(ListTag list) {
return new BlockPos( return new Vec3d(list.getDouble(0), list.getDouble(1), list.getDouble(2));
compound.getInt("X"),
compound.getInt("Y"),
compound.getInt("Z")
);
} }
} }