mirror of
https://github.com/Sollace/Unicopia.git
synced 2024-11-27 15:17:59 +01:00
Fixed unable to place the necromancy spell
This commit is contained in:
parent
82daf178df
commit
9a0041e295
28 changed files with 234 additions and 153 deletions
|
@ -6,6 +6,7 @@ import java.util.UUID;
|
|||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference.EntityValues;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
|
||||
|
@ -26,7 +27,7 @@ public interface WeaklyOwned<E extends Entity> extends Owned<E>, WorldConvertabl
|
|||
|
||||
@Override
|
||||
default Optional<UUID> getMasterId() {
|
||||
return getMasterReference().getId();
|
||||
return getMasterReference().getTarget().map(EntityValues::uuid);
|
||||
}
|
||||
|
||||
interface Mutable<E extends Entity> extends WeaklyOwned<E>, Owned.Mutable<E> {
|
||||
|
|
|
@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.EquinePredicates;
|
|||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.mixin.MixinFallingBlockEntity;
|
||||
|
@ -51,7 +52,7 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
|
|||
player.getEntityWorld().playSound(null, player.getBlockPos(), USounds.ENTITY_PLAYER_CHANGELING_TRANSFORM, SoundCategory.PLAYERS, 1.4F, 0.4F);
|
||||
|
||||
iplayer.getSpellSlot().get(SpellType.CHANGELING_DISGUISE, true)
|
||||
.orElseGet(() -> SpellType.CHANGELING_DISGUISE.withTraits().apply(iplayer))
|
||||
.orElseGet(() -> SpellType.CHANGELING_DISGUISE.withTraits().apply(iplayer, CastingMethod.INNATE))
|
||||
.setDisguise(looked);
|
||||
|
||||
if (!player.isCreative()) {
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.Race;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
|
||||
|
@ -78,7 +79,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
|
|||
|
||||
player.subtractEnergyCost(9);
|
||||
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
|
||||
SpellType.RAINBOOM.withTraits().apply(player);
|
||||
SpellType.RAINBOOM.withTraits().apply(player, CastingMethod.INNATE);
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.*;
|
||||
import com.minelittlepony.unicopia.ability.data.Hit;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.HomingSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||
|
@ -106,7 +107,7 @@ public class UnicornCastingAbility extends AbstractSpellCastingAbility {
|
|||
}, true);
|
||||
player.subtractEnergyCost(removed ? 2 : 4);
|
||||
if (!removed) {
|
||||
Spell s = spell.apply(player);
|
||||
Spell s = spell.apply(player, CastingMethod.GEM);
|
||||
if (s == null) {
|
||||
player.spawnParticles(ParticleTypes.LARGE_SMOKE, 6);
|
||||
player.playSound(USounds.SPELL_CAST_FAIL, 1, 0.5F);
|
||||
|
|
|
@ -45,13 +45,6 @@ public interface Caster<E extends Entity> extends
|
|||
*/
|
||||
boolean subtractEnergyCost(double amount);
|
||||
|
||||
/**
|
||||
* Gets the entity who originally cast the currently active spell.
|
||||
* @return
|
||||
*/
|
||||
@Override
|
||||
LivingEntity getMaster();
|
||||
|
||||
/**
|
||||
* Gets the original caster responsible for this spell.
|
||||
* If none is found, will return itself.
|
||||
|
|
|
@ -9,7 +9,7 @@ public abstract class AbstractAreaEffectSpell extends AbstractSpell {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> source) {
|
||||
return toPlaceable().apply(source);
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return toPlaceable();
|
||||
}
|
||||
}
|
||||
|
|
|
@ -0,0 +1,24 @@
|
|||
package com.minelittlepony.unicopia.ability.magic.spell;
|
||||
|
||||
public enum CastingMethod {
|
||||
/**
|
||||
* Casting from a gem or a unicorn's equipped spell.
|
||||
*/
|
||||
GEM,
|
||||
/**
|
||||
* Casting a projectile form from a gem or unicorn's equipped spell
|
||||
*/
|
||||
GEM_PROJECTILE,
|
||||
/**
|
||||
* Result of a projectile impact
|
||||
*/
|
||||
PROJECTILE,
|
||||
/**
|
||||
* Casting from a magic staff
|
||||
*/
|
||||
STAFF,
|
||||
/**
|
||||
* Result of an entities innate ability
|
||||
*/
|
||||
INNATE
|
||||
}
|
|
@ -8,6 +8,7 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
|
|||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||
import com.minelittlepony.unicopia.entity.CastSpellEntity;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference.EntityValues;
|
||||
import com.minelittlepony.unicopia.entity.UEntities;
|
||||
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
|
||||
import com.minelittlepony.unicopia.particle.ParticleHandle;
|
||||
|
@ -86,8 +87,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
setDirty();
|
||||
}
|
||||
|
||||
castEntity.getId().ifPresentOrElse(
|
||||
id -> checkDetachment(source, id),
|
||||
castEntity.getTarget().ifPresentOrElse(
|
||||
target -> checkDetachment(source, target),
|
||||
() -> spawnPlacedEntity(source)
|
||||
);
|
||||
}
|
||||
|
@ -96,10 +97,13 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
}
|
||||
|
||||
if (situation == Situation.GROUND_ENTITY) {
|
||||
if (!source.isClient() && Ether.get(source.asWorld()).getEntry(getType(), source).isEmpty()) {
|
||||
if (!source.isClient()) {
|
||||
Ether ether = Ether.get(source.asWorld());
|
||||
if (ether.getEntry(getType(), source).isEmpty()) {
|
||||
setDead();
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
if (spell instanceof PlacementDelegate delegate) {
|
||||
delegate.updatePlacement(source, this);
|
||||
|
@ -115,15 +119,15 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
return !isDead();
|
||||
}
|
||||
|
||||
private void checkDetachment(Caster<?> source, UUID id) {
|
||||
if (getWorld(source).map(Ether::get).flatMap(ether -> ether.getEntry(getType(), id)).isEmpty()) {
|
||||
private void checkDetachment(Caster<?> source, EntityValues<?> target) {
|
||||
if (getWorld(source).map(Ether::get).flatMap(ether -> ether.getEntry(getType(), target.uuid())).isEmpty()) {
|
||||
setDead();
|
||||
}
|
||||
}
|
||||
|
||||
private void spawnPlacedEntity(Caster<?> source) {
|
||||
CastSpellEntity entity = UEntities.CAST_SPELL.create(source.asWorld());
|
||||
Vec3d pos = castEntity.getPosition().orElse(position.orElse(source.getOriginVector()));
|
||||
Vec3d pos = getPosition().orElse(position.orElse(source.getOriginVector()));
|
||||
entity.updatePositionAndAngles(pos.x, pos.y, pos.z, source.asEntity().getYaw(), source.asEntity().getPitch());
|
||||
PlaceableSpell copy = spell.toPlaceable();
|
||||
if (spell instanceof PlacementDelegate delegate) {
|
||||
|
@ -162,9 +166,9 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
@Override
|
||||
public void onDestroyed(Caster<?> source) {
|
||||
if (!source.isClient()) {
|
||||
castEntity.getId().ifPresent(id -> {
|
||||
castEntity.getTarget().ifPresent(target -> {
|
||||
getWorld(source).map(Ether::get)
|
||||
.flatMap(ether -> ether.getEntry(getType(), id))
|
||||
.flatMap(ether -> ether.getEntry(getType(), target.uuid()))
|
||||
.ifPresent(Ether.Entry::markDead);
|
||||
});
|
||||
castEntity.set(null);
|
||||
|
@ -184,7 +188,7 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
|
|||
}
|
||||
|
||||
public Optional<Vec3d> getPosition() {
|
||||
return castEntity.getPosition();
|
||||
return castEntity.getTarget().map(EntityValues::pos);
|
||||
}
|
||||
|
||||
public Optional<Attachment> getParticleEffectAttachment(Caster<?> source) {
|
||||
|
|
|
@ -74,6 +74,14 @@ public interface Spell extends NbtSerialisable, Affine {
|
|||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the default form of this spell used to apply to a caster.
|
||||
* @param caster
|
||||
*/
|
||||
default Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to generate this spell's effects.
|
||||
* @param caster The caster currently fueling this spell
|
||||
|
|
|
@ -33,6 +33,11 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
|
|||
return List.of(spell);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> source) {
|
||||
return throwProjectile(source).isPresent();
|
||||
}
|
||||
|
||||
/**
|
||||
* Projects this spell.
|
||||
*
|
||||
|
@ -59,7 +64,7 @@ public final class ThrowableSpell extends AbstractDelegatingSpell {
|
|||
projectile.setPosition(entity.getX(), entity.getEyeY() - 0.1F, entity.getZ());
|
||||
projectile.setOwner(entity);
|
||||
projectile.setItem(UItems.GEMSTONE.getDefaultStack(spell.getType()));
|
||||
projectile.getSpellSlot().put(spell);
|
||||
spell.prepareForCast(caster, CastingMethod.PROJECTILE).apply(projectile);
|
||||
projectile.setVelocity(entity, entity.getPitch(), entity.getYaw(), 0, 1.5F, divergance);
|
||||
projectile.setNoGravity(true);
|
||||
configureProjectile(projectile, caster);
|
||||
|
|
|
@ -45,11 +45,12 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
|
|||
}
|
||||
|
||||
setDirty();
|
||||
|
||||
target.getOrEmpty(caster.asWorld())
|
||||
.filter(entity -> entity.distanceTo(caster.asEntity()) > getDrawDropOffRange(caster))
|
||||
.ifPresent(entity -> {
|
||||
Vec3d pos = caster.getOriginVector();
|
||||
if (target.isPresent(caster.asWorld()) && target.get(caster.asWorld()).distanceTo(caster.asEntity()) > getDrawDropOffRange(caster)) {
|
||||
target.get(caster.asWorld()).requestTeleport(pos.x, pos.y, pos.z);
|
||||
}
|
||||
entity.requestTeleport(pos.x, pos.y, pos.z);
|
||||
});
|
||||
|
||||
return super.tick(caster, situation);
|
||||
}
|
||||
|
@ -145,7 +146,7 @@ public class AttractiveSpell extends ShieldSpell implements HomingSpell, TimedSp
|
|||
public void onImpact(MagicProjectileEntity projectile, EntityHitResult hit) {
|
||||
if (!isDead() && getTraits().get(Trait.CHAOS) > 0) {
|
||||
setDead();
|
||||
Caster.of(hit.getEntity()).ifPresent(getTypeAndTraits()::apply);
|
||||
Caster.of(hit.getEntity()).ifPresent(caster -> getTypeAndTraits().apply(caster, CastingMethod.PROJECTILE));
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -154,7 +154,7 @@ public class BubbleSpell extends AbstractSpell implements TimedSpell,
|
|||
@Override
|
||||
public void onImpact(MagicProjectileEntity projectile, EntityHitResult hit) {
|
||||
Caster.of(hit.getEntity()).ifPresent(caster -> {
|
||||
getTypeAndTraits().apply(caster);
|
||||
getTypeAndTraits().apply(caster, CastingMethod.PROJECTILE);
|
||||
});
|
||||
}
|
||||
|
||||
|
|
|
@ -4,6 +4,7 @@ import org.jetbrains.annotations.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
|
||||
|
@ -32,13 +33,13 @@ public record CustomisedSpellType<T extends Spell> (
|
|||
|
||||
|
||||
@Nullable
|
||||
public T apply(Caster<?> caster) {
|
||||
public T apply(Caster<?> caster, CastingMethod method) {
|
||||
if (isEmpty()) {
|
||||
return null;
|
||||
}
|
||||
|
||||
T spell = create();
|
||||
if (spell != null && spell.apply(caster)) {
|
||||
if (spell != null && spell.prepareForCast(caster, method).apply(caster)) {
|
||||
return spell;
|
||||
}
|
||||
|
||||
|
|
|
@ -5,7 +5,9 @@ import org.jetbrains.annotations.Nullable;
|
|||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.magic.Affine;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.entity.damage.UDamageTypes;
|
||||
|
@ -66,8 +68,8 @@ public class DarkVortexSpell extends AttractiveSpell implements ProjectileDelega
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> source) {
|
||||
return toPlaceable().apply(source);
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return method == CastingMethod.STAFF ? toThrowable() : toPlaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -27,8 +27,8 @@ public class DisplacementSpell extends AbstractSpell implements HomingSpell, Pla
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> caster) {
|
||||
return toPlaceable().apply(caster);
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return toPlaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -57,7 +57,7 @@ public class FireBoltSpell extends AbstractSpell implements HomingSpell,
|
|||
return true;
|
||||
}
|
||||
|
||||
if (getTraits().get(Trait.FOCUS) >= 50 && !target.isPresent(caster.asWorld())) {
|
||||
if (getTraits().get(Trait.FOCUS) >= 50 && target.getOrEmpty(caster.asWorld()).isEmpty()) {
|
||||
target.set(caster.findAllEntitiesInRange(
|
||||
getTraits().get(Trait.FOCUS) - 49,
|
||||
EntityPredicates.VALID_LIVING_ENTITY.and(TargetSelecter.notOwnerOrFriend(this, caster))
|
||||
|
|
|
@ -4,11 +4,13 @@ import java.util.ArrayList;
|
|||
import java.util.List;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference.EntityValues;
|
||||
import com.minelittlepony.unicopia.entity.FairyEntity;
|
||||
import com.minelittlepony.unicopia.entity.UEntities;
|
||||
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
|
||||
|
@ -60,14 +62,14 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD
|
|||
if (lights.isEmpty()) {
|
||||
int size = 2 + caster.asWorld().random.nextInt(2) + (int)(getTraits().get(Trait.LIFE, 10, 20) - 10)/10;
|
||||
while (lights.size() < size) {
|
||||
lights.add(new EntityReference<FairyEntity>());
|
||||
lights.add(new EntityReference<>());
|
||||
}
|
||||
}
|
||||
|
||||
lights.forEach(ref -> {
|
||||
if (!ref.isPresent(caster.asWorld())) {
|
||||
if (ref.getOrEmpty(caster.asWorld()).isEmpty()) {
|
||||
FairyEntity entity = UEntities.TWITTERMITE.create(caster.asWorld());
|
||||
entity.setPosition(ref.getPosition().orElseGet(() -> {
|
||||
entity.setPosition(ref.getTarget().map(EntityValues::pos).orElseGet(() -> {
|
||||
return caster.getOriginVector().add(VecHelper.supply(() -> caster.asWorld().random.nextInt(3) - 1));
|
||||
}));
|
||||
entity.setMaster(caster);
|
||||
|
@ -84,7 +86,7 @@ public class LightSpell extends AbstractSpell implements TimedSpell, ProjectileD
|
|||
|
||||
@Override
|
||||
public void onImpact(MagicProjectileEntity projectile) {
|
||||
Caster.of(projectile.getMaster()).ifPresent(getTypeAndTraits()::apply);
|
||||
Caster.of(projectile.getMaster()).ifPresent(caster -> getTypeAndTraits().apply(caster, CastingMethod.PROJECTILE));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,6 +4,7 @@ import java.util.Optional;
|
|||
|
||||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.entity.EntityReference;
|
||||
import com.minelittlepony.unicopia.entity.behaviour.EntitySwap;
|
||||
|
@ -65,7 +66,7 @@ public class MindSwapSpell extends MimicSpell {
|
|||
|
||||
setDisguise(e);
|
||||
Caster<?> other = Caster.of(e).get();
|
||||
SpellType.MIMIC.withTraits().apply(other).setDisguise(master);
|
||||
SpellType.MIMIC.withTraits().apply(other, CastingMethod.PROJECTILE).setDisguise(master);
|
||||
|
||||
EntitySwap.ALL.accept(master, e);
|
||||
Inventory.swapInventories(
|
||||
|
@ -80,7 +81,7 @@ public class MindSwapSpell extends MimicSpell {
|
|||
});
|
||||
}
|
||||
|
||||
if (counterpart.getId().isPresent() && counterpart.get(caster.asWorld()) == null) {
|
||||
if (counterpart.isSet() && counterpart.get(caster.asWorld()) == null) {
|
||||
caster.getOriginatingCaster().asEntity().damage(caster.asWorld().getDamageSources().magic(), Float.MAX_VALUE);
|
||||
setDead();
|
||||
return false;
|
||||
|
|
|
@ -72,9 +72,15 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
|
|||
super(type);
|
||||
}
|
||||
|
||||
@Override
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return method == CastingMethod.GEM ? toPlaceable() : super.prepareForCast(caster, method);
|
||||
}
|
||||
|
||||
@Override
|
||||
public boolean tick(Caster<?> source, Situation situation) {
|
||||
float radius = source.getLevel().getScaled(4) * 4 + getTraits().get(Trait.POWER);
|
||||
|
||||
float radius = 4 + source.getLevel().getScaled(4) * 4 + getTraits().get(Trait.POWER);
|
||||
|
||||
if (radius <= 0) {
|
||||
return false;
|
||||
|
@ -82,22 +88,22 @@ public class NecromancySpell extends AbstractAreaEffectSpell implements Projecti
|
|||
|
||||
boolean rainy = source.asWorld().hasRain(source.getOrigin());
|
||||
|
||||
if (source.isClient()) {
|
||||
source.spawnParticles(new Sphere(true, radius * 2), rainy ? 98 : 125, pos -> {
|
||||
BlockPos bpos = BlockPos.ofFloored(pos);
|
||||
|
||||
if (!source.asWorld().isAir(bpos.down())) {
|
||||
if (source.asWorld().isAir(bpos) && !source.asWorld().isAir(bpos.down())) {
|
||||
source.addParticle(source.asWorld().hasRain(bpos) ? ParticleTypes.SMOKE : ParticleTypes.FLAME, pos, Vec3d.ZERO);
|
||||
}
|
||||
});
|
||||
return true;
|
||||
}
|
||||
|
||||
if (source.asWorld().getDifficulty() == Difficulty.PEACEFUL) {
|
||||
if (source.isClient() || source.asWorld().getDifficulty() == Difficulty.PEACEFUL) {
|
||||
return true;
|
||||
}
|
||||
|
||||
summonedEntities.removeIf(ref -> ref.getOrEmpty(source.asWorld()).filter(e -> {
|
||||
if (e.isRemoved()) {
|
||||
return false;
|
||||
}
|
||||
if (e.getPos().distanceTo(source.getOriginVector()) > radius * 2) {
|
||||
e.getWorld().sendEntityStatus(e, (byte)60);
|
||||
e.discard();
|
||||
|
|
|
@ -62,10 +62,10 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
if (source.isClient()) {
|
||||
Vec3d origin = source.getOriginVector();
|
||||
|
||||
ParticleEffect effect = teleportationTarget.getPosition()
|
||||
ParticleEffect effect = teleportationTarget.getTarget()
|
||||
.map(target -> {
|
||||
getType();
|
||||
return new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target, 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK);
|
||||
return new FollowingParticleEffect(UParticles.HEALTH_DRAIN, target.pos(), 0.2F).withChild(ParticleTypes.ELECTRIC_SPARK);
|
||||
})
|
||||
.orElse(ParticleTypes.ELECTRIC_SPARK);
|
||||
|
||||
|
@ -73,7 +73,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
source.addParticle(effect, pos, Vec3d.ZERO);
|
||||
});
|
||||
|
||||
teleportationTarget.getPosition().ifPresentOrElse(position -> {
|
||||
teleportationTarget.getTarget().ifPresentOrElse(target -> {
|
||||
particleEffect.update(getUuid(), source, spawner -> {
|
||||
spawner.addParticle(new SphereParticleEffect(UParticles.DISK, getType().getColor(), 0.8F, 1.8F, new Vec3d(pitch, yaw, 0)), source.getOriginVector(), Vec3d.ZERO);
|
||||
});
|
||||
|
@ -81,9 +81,9 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
particleEffect.destroy();
|
||||
});
|
||||
} else {
|
||||
teleportationTarget.getId().ifPresent(id -> {
|
||||
if (Ether.get(source.asWorld()).getEntry(getType(), id).isEmpty()) {
|
||||
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + id);
|
||||
teleportationTarget.getTarget().ifPresent(target -> {
|
||||
if (Ether.get(source.asWorld()).getEntry(getType(), target.uuid()).isEmpty()) {
|
||||
Unicopia.LOGGER.debug("Lost sibling, breaking connection to " + target.uuid());
|
||||
teleportationTarget.set(null);
|
||||
setDirty();
|
||||
source.asWorld().syncWorldEvent(WorldEvents.BLOCK_BROKEN, source.getOrigin(), Block.getRawIdFromState(Blocks.GLASS.getDefaultState()));
|
||||
|
@ -109,12 +109,12 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
|
||||
private void tickWithTargetLink(Caster<?> source, Ether.Entry destination) {
|
||||
|
||||
destination.entity.getPosition().ifPresent(targetPos -> {
|
||||
destination.entity.getTarget().ifPresent(target -> {
|
||||
source.findAllEntitiesInRange(1).forEach(entity -> {
|
||||
if (!entity.hasPortalCooldown() && entity.timeUntilRegen <= 0) {
|
||||
Vec3d offset = entity.getPos().subtract(source.getOriginVector());
|
||||
float yawDifference = pitch < 15 ? (180 - yaw + destination.yaw) : 0;
|
||||
Vec3d dest = targetPos.add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 0);
|
||||
Vec3d dest = target.pos().add(offset.rotateY(yawDifference * MathHelper.RADIANS_PER_DEGREE)).add(0, 0.05, 0);
|
||||
|
||||
entity.resetPortalCooldown();
|
||||
entity.timeUntilRegen = 100;
|
||||
|
@ -145,7 +145,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
Ether ether = Ether.get(source.asWorld());
|
||||
ether.getEntries(getType())
|
||||
.stream()
|
||||
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.getId().isPresent())
|
||||
.filter(entry -> entry.isAvailable() && !entry.entity.referenceEquals(source.asEntity()) && entry.entity.isSet())
|
||||
.findAny()
|
||||
.ifPresent(entry -> {
|
||||
entry.setTaken(true);
|
||||
|
@ -155,7 +155,7 @@ public class PortalSpell extends AbstractSpell implements PlaceableSpell.Placeme
|
|||
}
|
||||
|
||||
private Optional<Ether.Entry> getTarget(Caster<?> source) {
|
||||
return teleportationTarget.getId().flatMap(id -> Ether.get(source.asWorld()).getEntry(getType(), id));
|
||||
return teleportationTarget.getTarget().flatMap(target -> Ether.get(source.asWorld()).getEntry(getType(), target.uuid()));
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -1,7 +1,9 @@
|
|||
package com.minelittlepony.unicopia.ability.magic.spell.effect;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.block.state.StateMaps;
|
||||
|
@ -25,8 +27,8 @@ public class ScorchSpell extends FireSpell implements ProjectileDelegate.Configu
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> source) {
|
||||
return toPlaceable().apply(source);
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return method == CastingMethod.STAFF ? this : toPlaceable();
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -4,7 +4,9 @@ import com.minelittlepony.unicopia.Affinity;
|
|||
import com.minelittlepony.unicopia.USounds;
|
||||
import com.minelittlepony.unicopia.Unicopia;
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
|
@ -47,11 +49,8 @@ public class ShieldSpell extends AbstractSpell {
|
|||
}
|
||||
|
||||
@Override
|
||||
public boolean apply(Caster<?> source) {
|
||||
if (getTraits().get(Trait.GENEROSITY) > 0) {
|
||||
return toPlaceable().apply(source);
|
||||
}
|
||||
return super.apply(source);
|
||||
public Spell prepareForCast(Caster<?> caster, CastingMethod method) {
|
||||
return method == CastingMethod.STAFF || getTraits().get(Trait.GENEROSITY) > 0 ? toPlaceable() : this;
|
||||
}
|
||||
|
||||
@Override
|
||||
|
|
|
@ -43,22 +43,22 @@ public class TargetSelecter {
|
|||
return targets.values().stream().filter(Target::canHurt).count();
|
||||
}
|
||||
|
||||
public static <T extends Entity> Predicate<T> notOwnerOrFriend(Affine spell, Caster<?> source) {
|
||||
return target -> notOwnerOrFriend(spell, source, target);
|
||||
public static <T extends Entity> Predicate<T> notOwnerOrFriend(Affine affine, Caster<?> source) {
|
||||
return target -> notOwnerOrFriend(affine, source, target);
|
||||
}
|
||||
|
||||
public static <T extends Entity> Predicate<T> isOwnerOrFriend(Affine spell, Caster<?> source) {
|
||||
return target -> isOwnerOrFriend(spell, source, target);
|
||||
public static <T extends Entity> Predicate<T> isOwnerOrFriend(Affine affine, Caster<?> source) {
|
||||
return target -> isOwnerOrFriend(affine, source, target);
|
||||
}
|
||||
|
||||
public static <T extends Entity> boolean notOwnerOrFriend(Affine spell, Caster<?> source, Entity target) {
|
||||
return !isOwnerOrFriend(spell, source, target);
|
||||
public static <T extends Entity> boolean notOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
|
||||
return !isOwnerOrFriend(affine, source, target);
|
||||
}
|
||||
|
||||
public static <T extends Entity> boolean isOwnerOrFriend(Affine spell, Caster<?> source, Entity target) {
|
||||
public static <T extends Entity> boolean isOwnerOrFriend(Affine affine, Caster<?> source, Entity target) {
|
||||
Entity owner = source.getMaster();
|
||||
|
||||
if (!(spell.isFriendlyTogether(source) && EquinePredicates.PLAYER_UNICORN.test(owner))) {
|
||||
if (affine.isEnemy(source) || !EquinePredicates.PLAYER_UNICORN.test(owner)) {
|
||||
return FriendshipBraceletItem.isComrade(source, target);
|
||||
}
|
||||
|
||||
|
|
|
@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.command;
|
|||
import java.util.Optional;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
|
@ -112,7 +113,7 @@ public class CastCommand {
|
|||
private static int apply(CommandContext<ServerCommandSource> source, TraitsFunc traits) throws CommandSyntaxException {
|
||||
CustomisedSpellType<?> spellType = getSpell(source, traits);
|
||||
EntityArgumentType.getEntities(source, "targets").forEach(target -> {
|
||||
Caster.of(target).ifPresent(caster -> spellType.apply(caster));
|
||||
Caster.of(target).ifPresent(caster -> spellType.apply(caster, CastingMethod.INNATE));
|
||||
});
|
||||
|
||||
return 0;
|
||||
|
|
|
@ -5,6 +5,7 @@ import java.util.function.Function;
|
|||
import com.minelittlepony.unicopia.EquinePredicates;
|
||||
import com.minelittlepony.unicopia.InteractionManager;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.entity.player.Pony;
|
||||
import com.mojang.authlib.GameProfile;
|
||||
|
@ -83,7 +84,7 @@ public class DisguiseCommand {
|
|||
|
||||
Pony iplayer = Pony.of(player);
|
||||
iplayer.getSpellSlot().get(SpellType.CHANGELING_DISGUISE, true)
|
||||
.orElseGet(() -> SpellType.CHANGELING_DISGUISE.withTraits().apply(iplayer))
|
||||
.orElseGet(() -> SpellType.CHANGELING_DISGUISE.withTraits().apply(iplayer, CastingMethod.INNATE))
|
||||
.setDisguise(entity);
|
||||
|
||||
if (source.getEntity() == player) {
|
||||
|
|
|
@ -88,11 +88,6 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
|
|||
setMaster(caster);
|
||||
}
|
||||
|
||||
@Override
|
||||
public LivingEntity getMaster() {
|
||||
return WeaklyOwned.Mutable.super.getMaster();
|
||||
}
|
||||
|
||||
@Override
|
||||
public LevelStore getLevel() {
|
||||
return level;
|
||||
|
|
|
@ -1,11 +1,15 @@
|
|||
package com.minelittlepony.unicopia.entity;
|
||||
|
||||
import java.lang.ref.WeakReference;
|
||||
import java.util.Objects;
|
||||
import java.util.Optional;
|
||||
import java.util.UUID;
|
||||
import java.util.function.Consumer;
|
||||
|
||||
import org.jetbrains.annotations.Nullable;
|
||||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.Levelled;
|
||||
import com.minelittlepony.unicopia.util.NbtSerialisable;
|
||||
|
||||
import net.minecraft.entity.Entity;
|
||||
|
@ -13,6 +17,7 @@ import net.minecraft.entity.player.PlayerEntity;
|
|||
import net.minecraft.nbt.NbtCompound;
|
||||
import net.minecraft.nbt.NbtElement;
|
||||
import net.minecraft.server.world.ServerWorld;
|
||||
import net.minecraft.util.Util;
|
||||
import net.minecraft.util.math.Vec3d;
|
||||
import net.minecraft.world.World;
|
||||
|
||||
|
@ -21,22 +26,15 @@ import net.minecraft.world.World;
|
|||
* Used to store the 'owner' reference for certain objects that allows them to\
|
||||
* remember who they belong to even when the entity has been unloaded.
|
||||
*
|
||||
* Will also remember the position and certain attributes of the owner.
|
||||
*
|
||||
* @param <T> The type of the entity this reference points to.
|
||||
*/
|
||||
public class EntityReference<T extends Entity> implements NbtSerialisable {
|
||||
@Nullable
|
||||
private EntityValues<T> reference;
|
||||
|
||||
/**
|
||||
* Server UUID of the entity used to look up the entity instance on the server.
|
||||
*/
|
||||
private Optional<UUID> uuid = Optional.empty();
|
||||
/**
|
||||
* Corresponding client id used to look up the entity instance on the client.
|
||||
*/
|
||||
private int clientId;
|
||||
/**
|
||||
* The last-known position of the entity.
|
||||
*/
|
||||
private Optional<Vec3d> pos = Optional.empty();
|
||||
private WeakReference<T> directReference = new WeakReference<>(null);
|
||||
|
||||
public EntityReference() {}
|
||||
|
||||
|
@ -48,48 +46,42 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
|
|||
fromNBT(nbt);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public void copyFrom(EntityReference<? extends T> other) {
|
||||
uuid = other.uuid;
|
||||
clientId = other.clientId;
|
||||
pos = other.pos;
|
||||
this.reference = ((EntityReference<T>)other).reference;
|
||||
this.directReference = new WeakReference<>(other.directReference.get());
|
||||
}
|
||||
|
||||
public void set(@Nullable T entity) {
|
||||
if (entity != null) {
|
||||
uuid = Optional.of(entity.getUuid());
|
||||
clientId = entity.getId();
|
||||
pos = Optional.of(entity.getPos());
|
||||
} else {
|
||||
uuid = Optional.empty();
|
||||
clientId = 0;
|
||||
pos = Optional.empty();
|
||||
}
|
||||
public boolean set(@Nullable T entity) {
|
||||
this.directReference = new WeakReference<>(entity);
|
||||
this.reference = entity == null ? null : new EntityValues<>(entity);
|
||||
return entity != null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the assigned entity's UUID
|
||||
*/
|
||||
public Optional<UUID> getId() {
|
||||
return uuid;
|
||||
public Optional<EntityValues<T>> getTarget() {
|
||||
T value = directReference.get();
|
||||
if (value != null) {
|
||||
set(value);
|
||||
}
|
||||
|
||||
/**
|
||||
* Gets the last known position of the assigned entity.
|
||||
*/
|
||||
public Optional<Vec3d> getPosition() {
|
||||
return pos;
|
||||
return Optional.ofNullable(reference);
|
||||
}
|
||||
|
||||
public boolean isSet() {
|
||||
return getId().isPresent();
|
||||
return reference != null;
|
||||
}
|
||||
|
||||
public boolean referenceEquals(Entity entity) {
|
||||
return entity != null && entity.getUuid().equals(uuid.orElse(null));
|
||||
return entity != null && referenceEquals(entity.getUuid());
|
||||
}
|
||||
|
||||
public boolean isPresent(World world) {
|
||||
return getOrEmpty(world).isPresent();
|
||||
public boolean referenceEquals(UUID uuid) {
|
||||
return (reference == null ? Util.NIL_UUID : reference.uuid()).equals(uuid);
|
||||
}
|
||||
|
||||
public boolean referenceEquals(@Nullable EntityReference<?> other) {
|
||||
final EntityValues<?> st = reference;
|
||||
final EntityValues<?> ot = other == null ? null : other.reference;
|
||||
return st == ot || (st != null && ot != null && Objects.equals(st.uuid(), ot.uuid()));
|
||||
}
|
||||
|
||||
public void ifPresent(World world, Consumer<T> consumer) {
|
||||
|
@ -101,35 +93,74 @@ public class EntityReference<T extends Entity> implements NbtSerialisable {
|
|||
return getOrEmpty(world).orElse(null);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<T> getOrEmpty(World world) {
|
||||
if (uuid.isPresent() && world instanceof ServerWorld serverWorld) {
|
||||
return uuid.map(serverWorld::getEntity).map(e -> (T)e).filter(this::checkReference);
|
||||
}
|
||||
|
||||
if (clientId != 0) {
|
||||
return Optional.ofNullable((T)world.getEntityById(clientId)).filter(this::checkReference);
|
||||
}
|
||||
|
||||
return Optional.empty();
|
||||
}
|
||||
|
||||
private boolean checkReference(Entity e) {
|
||||
pos = Optional.of(e.getPos());
|
||||
return e instanceof PlayerEntity || !e.isRemoved();
|
||||
return Optional.ofNullable(directReference.get())
|
||||
.or(() -> reference == null ? Optional.empty() : reference.resolve(world))
|
||||
.filter(this::set);
|
||||
}
|
||||
|
||||
@Override
|
||||
public void toNBT(NbtCompound tag) {
|
||||
uuid.ifPresent(uuid -> tag.putUuid("uuid", uuid));
|
||||
pos.ifPresent(p -> tag.put("pos", NbtSerialisable.writeVector(p)));
|
||||
tag.putInt("clientId", clientId);
|
||||
getTarget().ifPresent(ref -> ref.toNBT(tag));
|
||||
}
|
||||
|
||||
@Override
|
||||
public void fromNBT(NbtCompound tag) {
|
||||
uuid = tag.containsUuid("uuid") ? Optional.of(tag.getUuid("uuid")) : Optional.empty();
|
||||
pos = tag.contains("pos") ? Optional.ofNullable(NbtSerialisable.readVector(tag.getList("pos", NbtElement.DOUBLE_TYPE))) : Optional.empty();
|
||||
clientId = tag.getInt("clientId");
|
||||
this.reference = tag.contains("uuid") ? new EntityValues<>(tag) : null;
|
||||
}
|
||||
|
||||
@Override
|
||||
public int hashCode() {
|
||||
return getTarget().map(EntityValues::uuid).orElse(Util.NIL_UUID).hashCode();
|
||||
}
|
||||
|
||||
public record EntityValues<T extends Entity>(
|
||||
UUID uuid,
|
||||
Vec3d pos,
|
||||
int clientId,
|
||||
boolean isPlayer,
|
||||
boolean isDead,
|
||||
Levelled.LevelStore level,
|
||||
Levelled.LevelStore corruption) {
|
||||
public EntityValues(Entity entity) {
|
||||
this(
|
||||
entity.getUuid(),
|
||||
entity.getPos(),
|
||||
entity.getId(), entity instanceof PlayerEntity,
|
||||
!entity.isAlive(),
|
||||
Caster.of(entity).map(Caster::getLevel).map(Levelled::copyOf).orElse(Levelled.EMPTY),
|
||||
Caster.of(entity).map(Caster::getCorruption).map(Levelled::copyOf).orElse(Levelled.EMPTY)
|
||||
);
|
||||
}
|
||||
|
||||
public EntityValues(NbtCompound tag) {
|
||||
this(
|
||||
tag.getUuid("uuid"),
|
||||
NbtSerialisable.readVector(tag.getList("pos", NbtElement.DOUBLE_TYPE)),
|
||||
tag.getInt("clientId"),
|
||||
tag.getBoolean("isPlayer"),
|
||||
tag.getBoolean("isDead"),
|
||||
Levelled.fromNbt(tag.getCompound("level")),
|
||||
Levelled.fromNbt(tag.getCompound("corruption"))
|
||||
);
|
||||
}
|
||||
|
||||
@SuppressWarnings("unchecked")
|
||||
public Optional<T> resolve(World world) {
|
||||
if (world instanceof ServerWorld serverWorld) {
|
||||
return Optional.ofNullable((T)serverWorld.getEntity(uuid));
|
||||
}
|
||||
return Optional.ofNullable((T)world.getEntityById(clientId()));
|
||||
}
|
||||
|
||||
public void toNBT(NbtCompound tag) {
|
||||
tag.putUuid("uuid", uuid);
|
||||
tag.put("pos", NbtSerialisable.writeVector(pos));
|
||||
tag.putInt("clientId", clientId);
|
||||
tag.putBoolean("isPlayer", isPlayer);
|
||||
tag.putBoolean("isDead", isDead);
|
||||
tag.put("level", level.toNbt());
|
||||
tag.put("corruption", corruption.toNbt());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -9,6 +9,7 @@ import javax.annotation.Nullable;
|
|||
|
||||
import com.minelittlepony.unicopia.ability.magic.Caster;
|
||||
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.CastingMethod;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
|
||||
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
|
||||
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
|
||||
|
@ -152,7 +153,7 @@ public class EnchantedStaffItem extends StaffItem implements EnchantableItem, Ch
|
|||
if (attacker.isSneaking() && hasCharge(stack)) {
|
||||
stack.damage(50, attacker, p -> p.sendEquipmentBreakStatus(EquipmentSlot.MAINHAND));
|
||||
Caster.of(attacker).ifPresent(c -> c.subtractEnergyCost(4));
|
||||
Caster.of(target).ifPresent(c -> getSpellEffect(stack).create().apply(c));
|
||||
Caster.of(target).ifPresent(c -> getSpellEffect(stack).apply(c, CastingMethod.STAFF));
|
||||
ChargeableItem.consumeEnergy(stack, 1);
|
||||
|
||||
return true;
|
||||
|
@ -182,7 +183,7 @@ public class EnchantedStaffItem extends StaffItem implements EnchantableItem, Ch
|
|||
living.clearActiveItem();
|
||||
living.damage(entity.getDamageSources().magic(), 1);
|
||||
if (EnchantableItem.isEnchanted(stack) && hasCharge(stack)) {
|
||||
Caster.of(entity).ifPresent(c -> getSpellEffect(stack).create().apply(c));
|
||||
Caster.of(entity).ifPresent(c -> getSpellEffect(stack).apply(c, CastingMethod.STAFF));
|
||||
ChargeableItem.consumeEnergy(stack, 1);
|
||||
}
|
||||
}
|
||||
|
|
Loading…
Reference in a new issue