Further rewrites to the magic system

This commit is contained in:
Sollace 2021-03-02 17:58:24 +02:00
parent a58c6a0a44
commit c5fdb9167f
24 changed files with 158 additions and 280 deletions

View file

@ -10,14 +10,5 @@ public interface Attached extends Spell {
* @param source The entity we are currently attached to. * @param source The entity we are currently attached to.
* @return true to keep alive * @return true to keep alive
*/ */
boolean updateOnPerson(Caster<?> caster); boolean onBodyTick(Caster<?> source);
/**
* Called every tick when attached to a living entity.
* Used to apply particle effects.
* Is only called on the client side.
*
* @param source The entity we are currently attached to.
*/
default void renderOnPerson(Caster<?> source) {}
} }

View file

@ -35,19 +35,6 @@ public interface Spell extends NbtSerialisable, Affine {
*/ */
void setDirty(boolean dirty); void setDirty(boolean dirty);
/**
* Gets the highest level this spell can be safely operated at.
* Gems may go higher, however chance of explosion/exhaustion increases with every level.
*/
int getMaxLevelCutOff(Caster<?> caster);
float getMaxExhaustion(Caster<?> caster);
/**
* Gets the chances of this effect turning into an innert gem or exploding.
*/
float getExhaustion(Caster<?> caster);
/** /**
* Called when first attached to a gem. * Called when first attached to a gem.
*/ */
@ -66,29 +53,6 @@ public interface Spell extends NbtSerialisable, Affine {
return false; return false;
} }
/**
* Called every tick when attached to an entity.
* Called on both sides.
*
* @param source The entity we are currently attached to.
*/
boolean update(Caster<?> source);
/**
* Called every tick when attached to an entity to produce particle effects.
* Is only called on the client side.
*
* @param source The entity we are attached to.
*/
void render(Caster<?> source);
/**
* Return true to allow the gem update and move.
*/
default boolean allowAI() {
return false;
}
/** /**
* Returns a new, deep-copied instance of this spell. * Returns a new, deep-copied instance of this spell.
*/ */

View file

@ -22,10 +22,18 @@ import net.minecraft.world.World;
*/ */
public interface Thrown extends Spell, ProjectileDelegate { public interface Thrown extends Spell, ProjectileDelegate {
/**
* Called every tick when attached to an entity.
* Called on both sides.
*
* @param source The entity we are currently attached to.
*/
boolean onThrownTick(Caster<?> source);
@Override @Override
default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) { default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
if (!projectile.isClient()) { if (!projectile.isClient()) {
update(projectile); onThrownTick(projectile);
} }
} }

View file

@ -1,50 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
public abstract class AbstractRangedAreaSpell extends AbstractSpell implements Attached {
protected AbstractRangedAreaSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
}
@Override
public int getMaxLevelCutOff(Caster<?> source) {
return 17;
}
@Override
public float getMaxExhaustion(Caster<?> caster) {
return 1000;
}
@Override
public float getExhaustion(Caster<?> caster) {
float max = getMaxLevelCutOff(caster);
float current = caster.getLevel().get();
if (current > max) {
float maxEc = getMaxExhaustion(caster);
current -= max;
current /= max;
current /= maxEc;
return maxEc - current;
}
return super.getExhaustion(caster);
}
@Override
public boolean updateOnPerson(Caster<?> caster) {
return update(caster);
}
@Override
public void renderOnPerson(Caster<?> caster) {
render(caster);
}
}

View file

@ -1,15 +1,14 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.Spell;
import net.minecraft.nbt.CompoundTag; import net.minecraft.nbt.CompoundTag;
public abstract class AbstractSpell implements Spell { public abstract class AbstractSpell implements Spell {
protected boolean isDead; private boolean isDead;
protected boolean isDirty; private boolean isDirty;
private final SpellType<?> type; private final SpellType<?> type;
@ -45,21 +44,6 @@ public abstract class AbstractSpell implements Spell {
isDirty = dirty; isDirty = dirty;
} }
@Override
public int getMaxLevelCutOff(Caster<?> source) {
return 1;
}
@Override
public float getMaxExhaustion(Caster<?> caster) {
return 1;
}
@Override
public float getExhaustion(Caster<?> caster) {
return 0;
}
@Override @Override
public Affinity getAffinity() { public Affinity getAffinity() {
return affinity; return affinity;

View file

@ -31,7 +31,7 @@ public class AttractiveSpell extends ShieldSpell implements Thrown {
} }
@Override @Override
public void render(Caster<?> source) { public void generateParticles(Caster<?> source) {
int range = 4 + (source.getLevel().get() * 2); int range = 4 + (source.getLevel().get() * 2);
Vec3d pos = source.getOriginVector(); Vec3d pos = source.getOriginVector();

View file

@ -23,27 +23,26 @@ public class AwkwardSpell extends AbstractSpell implements Thrown {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onThrownTick(Caster<?> source) {
if (source.isClient()) {
source.spawnParticles(new Sphere(false, (1 + source.getLevel().get()) * 8), 10, pos -> {
List<Identifier> names = new ArrayList<>(Registry.PARTICLE_TYPE.getIds());
int index = (int)MathHelper.nextDouble(source.getWorld().random, 0, names.size());
Identifier id = names.get(index);
ParticleType<?> type = Registry.PARTICLE_TYPE.get(id);
if (type instanceof ParticleEffect && shouldSpawnParticle(type)) {
source.addParticle((ParticleEffect)type, pos, Vec3d.ZERO);
}
});
}
return true; return true;
} }
@Override
public void render(Caster<?> source) {
source.spawnParticles(new Sphere(false, (1 + source.getLevel().get()) * 8), 10, pos -> {
List<Identifier> names = new ArrayList<>(Registry.PARTICLE_TYPE.getIds());
int index = (int)MathHelper.nextDouble(source.getWorld().random, 0, names.size());
Identifier id = names.get(index);
ParticleType<?> type = Registry.PARTICLE_TYPE.get(id);
if (type instanceof ParticleEffect && shouldSpawnParticle(type)) {
source.addParticle((ParticleEffect)type, pos, Vec3d.ZERO);
}
});
}
protected boolean shouldSpawnParticle(ParticleType<?> type) { protected boolean shouldSpawnParticle(ParticleType<?> type) {
return type != ParticleTypes.BARRIER return type != ParticleTypes.BARRIER
&& type != ParticleTypes.SMOKE && type != ParticleTypes.SMOKE

View file

@ -78,17 +78,21 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
} }
@Override @Override
public boolean updateOnPerson(Caster<?> caster) { public boolean onBodyTick(Caster<?> source) {
return update(caster);
}
@Override
public boolean update(Caster<?> source) {
return update(source, true); return update(source, true);
} }
@SuppressWarnings("unchecked") @SuppressWarnings("unchecked")
public boolean update(Caster<?> source, boolean tick) { public boolean update(Caster<?> source, boolean tick) {
if (source.isClient()) {
if (isSuppressed()) {
source.spawnParticles(MagicParticleEffect.UNICORN, 5);
source.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
} else if (source.getWorld().random.nextInt(30) == 0) {
source.spawnParticles(UParticles.CHANGELING_MAGIC, 2);
}
}
LivingEntity owner = source.getMaster(); LivingEntity owner = source.getMaster();
Entity entity = disguise.getAppearance(); Entity entity = disguise.getAppearance();
@ -168,16 +172,6 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
disguise.remove(); disguise.remove();
} }
@Override
public void render(Caster<?> source) {
if (isSuppressed()) {
source.spawnParticles(MagicParticleEffect.UNICORN, 5);
source.spawnParticles(UParticles.CHANGELING_MAGIC, 5);
} else if (source.getWorld().random.nextInt(30) == 0) {
source.spawnParticles(UParticles.CHANGELING_MAGIC, 2);
}
}
@Override @Override
public void toNBT(CompoundTag compound) { public void toNBT(CompoundTag compound) {
super.toNBT(compound); super.toNBT(compound);

View file

@ -2,6 +2,7 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates; import com.minelittlepony.unicopia.EquinePredicates;
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.ability.magic.Magical; import com.minelittlepony.unicopia.ability.magic.Magical;
import com.minelittlepony.unicopia.ability.magic.Thrown; import com.minelittlepony.unicopia.ability.magic.Thrown;
@ -35,7 +36,7 @@ import net.minecraft.world.explosion.Explosion.DestructionType;
/** /**
* Simple fire spell that triggers an effect when used on a block. * Simple fire spell that triggers an effect when used on a block.
*/ */
public class FireSpell extends AbstractRangedAreaSpell implements Thrown { public class FireSpell extends AbstractSpell implements Thrown, Attached {
private static final Shape VISUAL_EFFECT_RANGE = new Sphere(false, 0.5); private static final Shape VISUAL_EFFECT_RANGE = new Sphere(false, 0.5);
private static final Shape EFFECT_RANGE = new Sphere(false, 4); private static final Shape EFFECT_RANGE = new Sphere(false, 4);
@ -52,15 +53,23 @@ public class FireSpell extends AbstractRangedAreaSpell implements Thrown {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onBodyTick(Caster<?> source) {
return onThrownTick(source);
}
@Override
public boolean onThrownTick(Caster<?> source) {
if (source.isClient()) {
generateParticles(source);
}
return PosHelper.getAllInRegionMutable(source.getOrigin(), EFFECT_RANGE).reduce(false, return PosHelper.getAllInRegionMutable(source.getOrigin(), EFFECT_RANGE).reduce(false,
(r, i) -> applyBlocks(source.getWorld(), i), (r, i) -> applyBlocks(source.getWorld(), i),
(a, b) -> a || b) (a, b) -> a || b)
|| applyEntities(null, source.getWorld(), source.getOriginVector()); || applyEntities(null, source.getWorld(), source.getOriginVector());
} }
@Override protected void generateParticles(Caster<?> source) {
public void render(Caster<?> source) {
source.spawnParticles(VISUAL_EFFECT_RANGE, source.getLevel().get() * 6, pos -> { source.spawnParticles(VISUAL_EFFECT_RANGE, source.getLevel().get() * 6, pos -> {
source.addParticle(ParticleTypes.LARGE_SMOKE, pos, Vec3d.ZERO); source.addParticle(ParticleTypes.LARGE_SMOKE, pos, Vec3d.ZERO);
}); });

View file

@ -1,22 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
public class GenericSpell extends AbstractSpell {
protected GenericSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
}
@Override
public boolean update(Caster<?> source) {
return true;
}
@Override
public void render(Caster<?> source) {
source.spawnParticles(new MagicParticleEffect(getType().getColor()), 1);
}
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
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.ability.magic.Thrown; import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.block.state.StateMaps; import com.minelittlepony.unicopia.block.state.StateMaps;
@ -24,7 +25,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
public class IceSpell extends AbstractRangedAreaSpell implements Thrown { public class IceSpell extends AbstractSpell implements Thrown, Attached {
private final int rad = 3; private final int rad = 3;
private final Shape effect_range = new Sphere(false, rad); private final Shape effect_range = new Sphere(false, rad);
@ -34,7 +35,12 @@ public class IceSpell extends AbstractRangedAreaSpell implements Thrown {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onBodyTick(Caster<?> source) {
return onThrownTick(source);
}
@Override
public boolean onThrownTick(Caster<?> source) {
LivingEntity owner = source.getMaster(); LivingEntity owner = source.getMaster();
PosHelper.getAllInRegionMutable(source.getOrigin(), effect_range) PosHelper.getAllInRegionMutable(source.getOrigin(), effect_range)
@ -43,10 +49,6 @@ public class IceSpell extends AbstractRangedAreaSpell implements Thrown {
return applyEntities(source.getMaster(), source.getWorld(), source.getOriginVector()); return applyEntities(source.getMaster(), source.getWorld(), source.getOriginVector());
} }
@Override
public void render(Caster<?> source) {
}
protected boolean applyEntities(LivingEntity owner, World world, Vec3d pos) { protected boolean applyEntities(LivingEntity owner, World world, Vec3d pos) {
return !VecHelper.findInRange(owner, world, pos, 3, i -> applyEntitySingle(owner, i)).isEmpty(); return !VecHelper.findInRange(owner, world, pos, 3, i -> applyEntitySingle(owner, i)).isEmpty();
} }
@ -85,13 +87,13 @@ public class IceSpell extends AbstractRangedAreaSpell implements Thrown {
world.addParticle(ParticleTypes.SPLASH, pos.getX() + world.random.nextFloat(), pos.getY() + 1, pos.getZ() + world.random.nextFloat(), 0, 0, 0); world.addParticle(ParticleTypes.SPLASH, pos.getX() + world.random.nextFloat(), pos.getY() + 1, pos.getZ() + world.random.nextFloat(), 0, 0, 0);
} }
public static boolean isSurroundedByIce(World w, BlockPos pos) { private static boolean isSurroundedByIce(World w, BlockPos pos) {
return !PosHelper.adjacentNeighbours(pos).anyMatch(i -> return !PosHelper.adjacentNeighbours(pos).anyMatch(i ->
w.getBlockState(i).getMaterial() == Material.ICE w.getBlockState(i).getMaterial() == Material.ICE
); );
} }
private void incrementIce(World world, BlockPos pos) { private static void incrementIce(World world, BlockPos pos) {
BlockState state = world.getBlockState(pos); BlockState state = world.getBlockState(pos);
Block id = state.getBlock(); Block id = state.getBlock();

View file

@ -22,7 +22,7 @@ public class InfernoSpell extends FireSpell {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onThrownTick(Caster<?> source) {
World w = source.getWorld(); World w = source.getWorld();
if (!w.isClient) { if (!w.isClient) {

View file

@ -2,8 +2,8 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.UTags; import com.minelittlepony.unicopia.UTags;
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.ability.magic.Thrown;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect; import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
import com.minelittlepony.unicopia.particle.ParticleHandle; import com.minelittlepony.unicopia.particle.ParticleHandle;
@ -21,7 +21,7 @@ import net.minecraft.util.math.BlockPos;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.GameRules; import net.minecraft.world.GameRules;
public class JoustingSpell extends AbstractRangedAreaSpell implements Thrown { public class JoustingSpell extends AbstractSpell implements Attached {
private final int rad = 5; private final int rad = 5;
private final Shape effect_range = new Sphere(false, rad); private final Shape effect_range = new Sphere(false, rad);
@ -41,7 +41,14 @@ public class JoustingSpell extends AbstractRangedAreaSpell implements Thrown {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onBodyTick(Caster<?> source) {
if (source.isClient()) {
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO);
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> p.attach(source));
}
LivingEntity owner = source.getMaster(); LivingEntity owner = source.getMaster();
source.findAllEntitiesInRange(rad).forEach(e -> { source.findAllEntitiesInRange(rad).forEach(e -> {
@ -78,14 +85,6 @@ public class JoustingSpell extends AbstractRangedAreaSpell implements Thrown {
return entity.world.getGameRules().getBoolean(GameRules.DO_MOB_GRIEFING); return entity.world.getGameRules().getBoolean(GameRules.DO_MOB_GRIEFING);
} }
@Override
public void render(Caster<?> source) {
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(UParticles.RAINBOOM_TRAIL, source.getOriginVector(), Vec3d.ZERO);
spawner.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, source.getPhysics().getMotionAngle()), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> p.attach(source));
}
@Override @Override
public void toNBT(CompoundTag compound) { public void toNBT(CompoundTag compound) {
super.toNBT(compound); super.toNBT(compound);

View file

@ -4,6 +4,7 @@ import java.util.List;
import com.google.common.collect.Lists; import com.google.common.collect.Lists;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
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 +19,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 AbstractRangedAreaSpell { public class NecromancySpell extends AbstractSpell implements Attached {
private final List<EntityType<? extends LivingEntity>> spawns = Lists.newArrayList( private final List<EntityType<? extends LivingEntity>> spawns = Lists.newArrayList(
EntityType.ZOMBIE, EntityType.ZOMBIE,
@ -31,16 +32,28 @@ public class NecromancySpell extends AbstractRangedAreaSpell {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onBodyTick(Caster<?> source) {
if (source.getWorld().isClient || source.getWorld().getDifficulty() == Difficulty.PEACEFUL) { int radius = source.getLevel().get() + 1;
if (source.isClient()) {
Shape affectRegion = new Sphere(false, radius * 4);
source.spawnParticles(affectRegion, 5, pos -> {
if (!source.getWorld().isAir(new BlockPos(pos).down())) {
source.addParticle(ParticleTypes.FLAME, pos, Vec3d.ZERO);
}
});
return true;
}
if (source.getWorld().getDifficulty() == Difficulty.PEACEFUL) {
return true; return true;
} }
float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty(); float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty();
int radius = source.getLevel().get() + 1;
Shape affectRegion = new Sphere(false, radius * 4); Shape affectRegion = new Sphere(false, radius * 4);
if (source.getWorld().random.nextInt(100) != 0) { if (source.getWorld().random.nextInt(100) != 0) {
@ -80,15 +93,4 @@ public class NecromancySpell extends AbstractRangedAreaSpell {
source.getWorld().spawnEntity(zombie); source.getWorld().spawnEntity(zombie);
} }
@Override
public void render(Caster<?> source) {
Shape affectRegion = new Sphere(false, (1 + source.getLevel().get()) * 4);
source.spawnParticles(affectRegion, 5, pos -> {
if (!source.getWorld().isAir(new BlockPos(pos).down())) {
source.addParticle(ParticleTypes.FLAME, pos, Vec3d.ZERO);
}
});
}
} }

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability.magic.spell; package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
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.ability.magic.Suppressable; import com.minelittlepony.unicopia.ability.magic.Suppressable;
import com.minelittlepony.unicopia.ability.magic.Thrown; import com.minelittlepony.unicopia.ability.magic.Thrown;
@ -15,7 +16,7 @@ import net.minecraft.util.math.Vec3d;
/** /**
* A spell for revealing changelings. * A spell for revealing changelings.
*/ */
public class RevealingSpell extends AbstractSpell implements Thrown { public class RevealingSpell extends AbstractSpell implements Attached, Thrown {
private static final Shape AREA = new Sphere(false, 15); private static final Shape AREA = new Sphere(false, 15);
protected RevealingSpell(SpellType<?> type, Affinity affinity) { protected RevealingSpell(SpellType<?> type, Affinity affinity) {
@ -28,7 +29,22 @@ public class RevealingSpell extends AbstractSpell implements Thrown {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onBodyTick(Caster<?> source) {
return onThrownTick(source);
}
@Override
public boolean onThrownTick(Caster<?> source) {
if (source.isClient()) {
MagicParticleEffect effect = new MagicParticleEffect(getType().getColor());
source.spawnParticles(AREA, 5, pos -> {
source.addParticle(effect, pos, Vec3d.ZERO);
});
source.spawnParticles(effect, 5);
}
source.findAllSpellsInRange(15).forEach(e -> { source.findAllSpellsInRange(15).forEach(e -> {
Suppressable spell = e.getSpell(Suppressable.class, false); Suppressable spell = e.getSpell(Suppressable.class, false);
@ -40,14 +56,4 @@ public class RevealingSpell extends AbstractSpell implements Thrown {
return true; return true;
} }
@Override
public void render(Caster<?> source) {
MagicParticleEffect effect = new MagicParticleEffect(getType().getColor());
source.spawnParticles(AREA, 5, pos -> {
source.addParticle(effect, pos, Vec3d.ZERO);
});
source.spawnParticles(effect, 5);
}
} }

View file

@ -22,7 +22,7 @@ public class ScorchSpell extends FireSpell {
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onThrownTick(Caster<?> source) {
BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum()); BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum());
@ -41,7 +41,7 @@ public class ScorchSpell extends FireSpell {
} }
@Override @Override
public void render(Caster<?> source) { protected void generateParticles(Caster<?> source) {
source.addParticle(ParticleTypes.END_ROD, source.getOriginVector(), Vec3d.ZERO); source.addParticle(ParticleTypes.END_ROD, source.getOriginVector(), Vec3d.ZERO);
source.spawnParticles(ParticleTypes.FLAME, 3); source.spawnParticles(ParticleTypes.FLAME, 3);
source.spawnParticles(new MagicParticleEffect(getType().getColor()), 3); source.spawnParticles(new MagicParticleEffect(getType().getColor()), 3);

View file

@ -11,6 +11,7 @@ import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Attached; 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.ability.magic.Thrown;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.FriendshipBraceletItem; import com.minelittlepony.unicopia.item.FriendshipBraceletItem;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments; import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
@ -33,7 +34,7 @@ import net.minecraft.entity.vehicle.BoatEntity;
import net.minecraft.sound.SoundEvents; import net.minecraft.sound.SoundEvents;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public class ShieldSpell extends AbstractRangedAreaSpell implements Attached { public class ShieldSpell extends AbstractSpell implements Attached, Thrown {
private final ParticleHandle particlEffect = new ParticleHandle(); private final ParticleHandle particlEffect = new ParticleHandle();
@ -49,8 +50,7 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
particlEffect.destroy(); particlEffect.destroy();
} }
@Override protected void generateParticles(Caster<?> source) {
public void render(Caster<?> source) {
float radius = (float)getDrawDropOffRange(source); float radius = (float)getDrawDropOffRange(source);
source.spawnParticles(new Sphere(true, radius), (int)(radius * 6), pos -> { source.spawnParticles(new Sphere(true, radius), (int)(radius * 6), pos -> {
@ -67,7 +67,21 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
} }
@Override @Override
public boolean updateOnPerson(Caster<?> source) { public boolean onThrownTick(Caster<?> source) {
if (source.isClient()) {
generateParticles(source);
}
applyEntities(source);
return true;
}
@Override
public boolean onBodyTick(Caster<?> source) {
if (source.isClient()) {
generateParticles(source);
}
long costMultiplier = applyEntities(source); long costMultiplier = applyEntities(source);
if (costMultiplier > 0) { if (costMultiplier > 0) {
double cost = 2 + source.getLevel().get(); double cost = 2 + source.getLevel().get();
@ -91,12 +105,6 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
return (4 + (source.getLevel().get() * 2)) / multiplier; return (4 + (source.getLevel().get() * 2)) / multiplier;
} }
@Override
public boolean update(Caster<?> source) {
applyEntities(source);
return true;
}
protected List<Entity> getTargets(Caster<?> source, double radius) { protected List<Entity> getTargets(Caster<?> source, double radius) {
Entity owner = source.getMaster(); Entity owner = source.getMaster();

View file

@ -5,8 +5,8 @@ import java.util.stream.Collectors;
import com.minelittlepony.unicopia.Affinity; import com.minelittlepony.unicopia.Affinity;
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.ability.magic.Thrown;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource; import com.minelittlepony.unicopia.util.MagicalDamageSource;
import com.minelittlepony.unicopia.util.shape.Sphere; import com.minelittlepony.unicopia.util.shape.Sphere;
@ -21,17 +21,31 @@ 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 AbstractRangedAreaSpell implements Thrown { public class SiphoningSpell extends AbstractSpell implements Attached {
protected SiphoningSpell(SpellType<?> type, Affinity affinity) { protected SiphoningSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity); super(type, affinity);
} }
@Override @Override
public boolean update(Caster<?> source) { public boolean onBodyTick(Caster<?> source) {
int radius = 4 + source.getLevel().get(); int radius = 4 + source.getLevel().get();
if (source.isClient()) {
Vec3d origin = source.getOriginVector();
int direction = !isEnemy(source) ? 1 : -1;
source.spawnParticles(new Sphere(true, radius, 1, 0, 1), 1, pos -> {
if (!source.getWorld().isAir(new BlockPos(pos).down())) {
double dist = pos.distanceTo(origin);
Vec3d velocity = pos.subtract(origin).normalize().multiply(direction * dist);
source.addParticle(direction == 1 ? ParticleTypes.HEART : ParticleTypes.ANGRY_VILLAGER, pos, velocity);
}
});
}
LivingEntity owner = source.getMaster(); LivingEntity owner = source.getMaster();
List<LivingEntity> target = source.findAllEntitiesInRange(radius) List<LivingEntity> target = source.findAllEntitiesInRange(radius)
@ -39,7 +53,7 @@ public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
.map(e -> (LivingEntity)e) .map(e -> (LivingEntity)e)
.collect(Collectors.toList()); .collect(Collectors.toList());
DamageSource damage = damageSource(owner); DamageSource damage = MagicalDamageSource.create("drain", owner);
if (!isFriendlyTogether(source)) { if (!isFriendlyTogether(source)) {
if (owner != null) { if (owner != null) {
@ -94,30 +108,4 @@ public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
return false; return false;
} }
protected DamageSource damageSource(LivingEntity actor) {
if (actor == null) {
return MagicalDamageSource.create("drain");
}
return MagicalDamageSource.create("drain", actor);
}
@Override
public void render(Caster<?> source) {
int radius = 4 + source.getLevel().get();
Vec3d origin = source.getOriginVector();
int direction = !isEnemy(source) ? 1 : -1;
source.spawnParticles(new Sphere(true, radius, 1, 0, 1), 1, pos -> {
if (!source.getWorld().isAir(new BlockPos(pos).down())) {
double dist = pos.distanceTo(origin);
Vec3d velocity = pos.subtract(origin).normalize().multiply(direction * dist);
source.addParticle(direction == 1 ? ParticleTypes.HEART : ParticleTypes.ANGRY_VILLAGER, pos, velocity);
}
});
}
} }

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.entity; package com.minelittlepony.unicopia.entity;
import com.minelittlepony.unicopia.ability.magic.Magical;
import com.minelittlepony.unicopia.item.UItems; import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel; import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgSpawnProjectile; import com.minelittlepony.unicopia.network.MsgSpawnProjectile;
@ -19,7 +20,7 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World; import net.minecraft.world.World;
public class FloatingArtefactEntity extends Entity { public class FloatingArtefactEntity extends Entity implements Magical {
private static final TrackedData<ItemStack> ITEM = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.ITEM_STACK); private static final TrackedData<ItemStack> ITEM = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.ITEM_STACK);
private static final TrackedData<Byte> STATE = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.BYTE); private static final TrackedData<Byte> STATE = DataTracker.registerData(FloatingArtefactEntity.class, TrackedDataHandlerRegistry.BYTE);

View file

@ -83,11 +83,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
Attached effect = getSpell(Attached.class, true); Attached effect = getSpell(Attached.class, true);
if (effect != null) { if (effect != null) {
if (entity.getEntityWorld().isClient()) { if (!effect.onBodyTick(this)) {
effect.renderOnPerson(this);
}
if (!effect.updateOnPerson(this)) {
setSpell(null); setSpell(null);
} }
} }

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.entity.behaviour; package com.minelittlepony.unicopia.entity.behaviour;
import java.util.function.BiFunction; import java.util.function.BiFunction;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;

View file

@ -4,6 +4,7 @@ import java.util.Optional;
import com.minelittlepony.common.util.animation.MotionCompositor; import com.minelittlepony.common.util.animation.MotionCompositor;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell; import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import net.minecraft.util.math.Vec3d; import net.minecraft.util.math.Vec3d;
public class PlayerCamera extends MotionCompositor { public class PlayerCamera extends MotionCompositor {

View file

@ -6,6 +6,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Levelled; import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.Magical; import com.minelittlepony.unicopia.ability.magic.Magical;
import com.minelittlepony.unicopia.ability.magic.Spell; import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType; import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.EntityPhysics; import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.Physics; import com.minelittlepony.unicopia.entity.Physics;
@ -162,16 +163,12 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
lastBlockPos = getBlockPos(); lastBlockPos = getBlockPos();
} }
Spell spell = getSpell(true); Thrown spell = getSpell(Thrown.class, true);
if (spell.isDead()) { if (spell.isDead()) {
remove(); remove();
} else { } else {
spell.update(this); spell.onThrownTick(this);
if (world.isClient()) {
spell.render(this);
}
} }
} }

View file

@ -36,7 +36,7 @@ public class MagicalDamageSource extends EntityDamageSource {
this(type, null, direct, unblockable); this(type, null, direct, unblockable);
} }
protected MagicalDamageSource(String type, Entity source, boolean direct, boolean unblockable) { protected MagicalDamageSource(String type, @Nullable Entity source, boolean direct, boolean unblockable) {
super(type, source); super(type, source);
setUsesMagic(); setUsesMagic();
if (direct) { if (direct) {