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.
* @return true to keep alive
*/
boolean updateOnPerson(Caster<?> caster);
/**
* 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) {}
boolean onBodyTick(Caster<?> source);
}

View file

@ -35,19 +35,6 @@ public interface Spell extends NbtSerialisable, Affine {
*/
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.
*/
@ -66,29 +53,6 @@ public interface Spell extends NbtSerialisable, Affine {
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.
*/

View file

@ -22,10 +22,18 @@ import net.minecraft.world.World;
*/
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
default void onImpact(MagicProjectileEntity projectile, BlockPos pos, BlockState state) {
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;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import net.minecraft.nbt.CompoundTag;
public abstract class AbstractSpell implements Spell {
protected boolean isDead;
protected boolean isDirty;
private boolean isDead;
private boolean isDirty;
private final SpellType<?> type;
@ -45,21 +44,6 @@ public abstract class AbstractSpell implements Spell {
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
public Affinity getAffinity() {
return affinity;

View file

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

View file

@ -23,27 +23,26 @@ public class AwkwardSpell extends AbstractSpell implements Thrown {
}
@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;
}
@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) {
return type != ParticleTypes.BARRIER
&& type != ParticleTypes.SMOKE

View file

@ -78,17 +78,21 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
}
@Override
public boolean updateOnPerson(Caster<?> caster) {
return update(caster);
}
@Override
public boolean update(Caster<?> source) {
public boolean onBodyTick(Caster<?> source) {
return update(source, true);
}
@SuppressWarnings("unchecked")
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();
Entity entity = disguise.getAppearance();
@ -168,16 +172,6 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
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
public void toNBT(CompoundTag 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.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Magical;
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.
*/
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 EFFECT_RANGE = new Sphere(false, 4);
@ -52,15 +53,23 @@ public class FireSpell extends AbstractRangedAreaSpell implements Thrown {
}
@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,
(r, i) -> applyBlocks(source.getWorld(), i),
(a, b) -> a || b)
|| applyEntities(null, source.getWorld(), source.getOriginVector());
}
@Override
public void render(Caster<?> source) {
protected void generateParticles(Caster<?> source) {
source.spawnParticles(VISUAL_EFFECT_RANGE, source.getLevel().get() * 6, pos -> {
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;
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.Thrown;
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.world.World;
public class IceSpell extends AbstractRangedAreaSpell implements Thrown {
public class IceSpell extends AbstractSpell implements Thrown, Attached {
private final int rad = 3;
private final Shape effect_range = new Sphere(false, rad);
@ -34,7 +35,12 @@ public class IceSpell extends AbstractRangedAreaSpell implements Thrown {
}
@Override
public boolean update(Caster<?> source) {
public boolean onBodyTick(Caster<?> source) {
return onThrownTick(source);
}
@Override
public boolean onThrownTick(Caster<?> source) {
LivingEntity owner = source.getMaster();
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());
}
@Override
public void render(Caster<?> source) {
}
protected boolean applyEntities(LivingEntity owner, World world, Vec3d pos) {
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);
}
public static boolean isSurroundedByIce(World w, BlockPos pos) {
private static boolean isSurroundedByIce(World w, BlockPos pos) {
return !PosHelper.adjacentNeighbours(pos).anyMatch(i ->
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);
Block id = state.getBlock();

View file

@ -22,7 +22,7 @@ public class InfernoSpell extends FireSpell {
}
@Override
public boolean update(Caster<?> source) {
public boolean onThrownTick(Caster<?> source) {
World w = source.getWorld();
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.UTags;
import com.minelittlepony.unicopia.ability.magic.Attached;
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.particle.OrientedBillboardParticleEffect;
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.world.GameRules;
public class JoustingSpell extends AbstractRangedAreaSpell implements Thrown {
public class JoustingSpell extends AbstractSpell implements Attached {
private final int rad = 5;
private final Shape effect_range = new Sphere(false, rad);
@ -41,7 +41,14 @@ public class JoustingSpell extends AbstractRangedAreaSpell implements Thrown {
}
@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();
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);
}
@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
public void toNBT(CompoundTag compound) {
super.toNBT(compound);

View file

@ -4,6 +4,7 @@ import java.util.List;
import com.google.common.collect.Lists;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.util.WorldEvent;
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.world.Difficulty;
public class NecromancySpell extends AbstractRangedAreaSpell {
public class NecromancySpell extends AbstractSpell implements Attached {
private final List<EntityType<? extends LivingEntity>> spawns = Lists.newArrayList(
EntityType.ZOMBIE,
@ -31,16 +32,28 @@ public class NecromancySpell extends AbstractRangedAreaSpell {
}
@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;
}
float additional = source.getWorld().getLocalDifficulty(source.getOrigin()).getLocalDifficulty();
int radius = source.getLevel().get() + 1;
Shape affectRegion = new Sphere(false, radius * 4);
if (source.getWorld().random.nextInt(100) != 0) {
@ -80,15 +93,4 @@ public class NecromancySpell extends AbstractRangedAreaSpell {
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;
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.Suppressable;
import com.minelittlepony.unicopia.ability.magic.Thrown;
@ -15,7 +16,7 @@ import net.minecraft.util.math.Vec3d;
/**
* 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);
protected RevealingSpell(SpellType<?> type, Affinity affinity) {
@ -28,7 +29,22 @@ public class RevealingSpell extends AbstractSpell implements Thrown {
}
@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 -> {
Suppressable spell = e.getSpell(Suppressable.class, false);
@ -40,14 +56,4 @@ public class RevealingSpell extends AbstractSpell implements Thrown {
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
public boolean update(Caster<?> source) {
public boolean onThrownTick(Caster<?> source) {
BlockPos pos = PosHelper.findSolidGroundAt(source.getWorld(), source.getOrigin(), source.getPhysics().getGravitySignum());
@ -41,7 +41,7 @@ public class ScorchSpell extends FireSpell {
}
@Override
public void render(Caster<?> source) {
protected void generateParticles(Caster<?> source) {
source.addParticle(ParticleTypes.END_ROD, source.getOriginVector(), Vec3d.ZERO);
source.spawnParticles(ParticleTypes.FLAME, 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.ability.magic.Attached;
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.item.FriendshipBraceletItem;
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.util.math.Vec3d;
public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
public class ShieldSpell extends AbstractSpell implements Attached, Thrown {
private final ParticleHandle particlEffect = new ParticleHandle();
@ -49,8 +50,7 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
particlEffect.destroy();
}
@Override
public void render(Caster<?> source) {
protected void generateParticles(Caster<?> source) {
float radius = (float)getDrawDropOffRange(source);
source.spawnParticles(new Sphere(true, radius), (int)(radius * 6), pos -> {
@ -67,7 +67,21 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
}
@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);
if (costMultiplier > 0) {
double cost = 2 + source.getLevel().get();
@ -91,12 +105,6 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
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) {
Entity owner = source.getMaster();

View file

@ -5,8 +5,8 @@ import java.util.stream.Collectors;
import com.minelittlepony.unicopia.Affinity;
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.Thrown;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
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.
*/
public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
public class SiphoningSpell extends AbstractSpell implements Attached {
protected SiphoningSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
}
@Override
public boolean update(Caster<?> source) {
public boolean onBodyTick(Caster<?> source) {
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();
List<LivingEntity> target = source.findAllEntitiesInRange(radius)
@ -39,7 +53,7 @@ public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
.map(e -> (LivingEntity)e)
.collect(Collectors.toList());
DamageSource damage = damageSource(owner);
DamageSource damage = MagicalDamageSource.create("drain", owner);
if (!isFriendlyTogether(source)) {
if (owner != null) {
@ -94,30 +108,4 @@ public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
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;
import com.minelittlepony.unicopia.ability.magic.Magical;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
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.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<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);
if (effect != null) {
if (entity.getEntityWorld().isClient()) {
effect.renderOnPerson(this);
}
if (!effect.updateOnPerson(this)) {
if (!effect.onBodyTick(this)) {
setSpell(null);
}
}

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.entity.behaviour;
import java.util.function.BiFunction;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
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.unicopia.ability.magic.spell.DisguiseSpell;
import net.minecraft.util.math.Vec3d;
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.Magical;
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.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.Physics;
@ -162,16 +163,12 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
lastBlockPos = getBlockPos();
}
Spell spell = getSpell(true);
Thrown spell = getSpell(Thrown.class, true);
if (spell.isDead()) {
remove();
} else {
spell.update(this);
if (world.isClient()) {
spell.render(this);
}
spell.onThrownTick(this);
}
}

View file

@ -36,7 +36,7 @@ public class MagicalDamageSource extends EntityDamageSource {
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);
setUsesMagic();
if (direct) {