Allow spells to animate post-removal

This commit is contained in:
Sollace 2024-01-25 19:09:57 +00:00
parent 5356c5c7ec
commit a19637a8e4
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
8 changed files with 95 additions and 6 deletions

View file

@ -7,6 +7,7 @@ import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.SpellContainer.Operation;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -92,7 +93,11 @@ public class UnicornDispellAbility implements Ability<Pos> {
public boolean apply(Pony player, Pos data) {
player.setAnimation(Animation.WOLOLO, Animation.Recipient.ANYONE);
Caster.stream(VecHelper.findInRange(player.asEntity(), player.asWorld(), data.vec(), 3, EquinePredicates.IS_PLACED_SPELL).stream()).forEach(target -> {
target.getSpellSlot().clear();
target.getSpellSlot().forEach(spell -> {
spell.setDead();
spell.tickDying(target);
return Operation.ofBoolean(!spell.isDead());
}, true);
});
return true;
}

View file

@ -69,11 +69,20 @@ public abstract class AbstractDelegatingSpell implements Spell,
getDelegates().forEach(Spell::setDead);
}
@Override
public void tickDying(Caster<?> caster) {
}
@Override
public boolean isDead() {
return getDelegates().isEmpty() || getDelegates().stream().allMatch(Spell::isDead);
}
@Override
public boolean isDying() {
return false;
}
@Override
public boolean isDirty() {
return dirty || getDelegates().stream().anyMatch(Spell::isDirty);
@ -110,7 +119,13 @@ public abstract class AbstractDelegatingSpell implements Spell,
@Override
public boolean tick(Caster<?> source, Situation situation) {
return execute(getDelegates().stream(), a -> a.tick(source, situation));
return execute(getDelegates().stream(), a -> {
if (a.isDying()) {
a.tickDying(source);
return !a.isDead();
}
return a.tick(source, situation);
});
}
@Override

View file

@ -59,6 +59,10 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
private int prevAge;
private int age;
private boolean dead;
private int prevDeathTicks;
private int deathTicks;
private Optional<Vec3d> position = Optional.empty();
public PlaceableSpell(CustomisedSpellType<?> type) {
@ -74,6 +78,29 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return MathHelper.lerp(tickDelta, prevAge, age);
}
public float getScale(float tickDelta) {
float add = MathHelper.clamp(getAge(tickDelta) / 25F, 0, 1);
float subtract = dead ? 1 - (MathHelper.lerp(tickDelta, prevDeathTicks, deathTicks) / 20F) : 0;
return MathHelper.clamp(add - subtract, 0, 1);
}
@Override
public boolean isDying() {
return dead && deathTicks > 0;
}
@Override
public void setDead() {
super.setDead();
dead = true;
deathTicks = 20;
}
@Override
public boolean isDead() {
return dead && deathTicks <= 0;
}
@Override
public Collection<Spell> getDelegates() {
return List.of(spell);
@ -119,6 +146,12 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
return !isDead();
}
@Override
public void tickDying(Caster<?> caster) {
prevDeathTicks = deathTicks;
deathTicks--;
}
private void checkDetachment(Caster<?> source, EntityValues<?> target) {
if (getWorld(source).map(Ether::get).map(ether -> ether.get(getType(), target, placedSpellId)).isEmpty()) {
setDead();
@ -198,6 +231,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putBoolean("dead", dead);
compound.putInt("deathTicks", deathTicks);
compound.putInt("age", age);
compound.putFloat("pitch", pitch);
compound.putFloat("yaw", yaw);
@ -217,6 +252,8 @@ public class PlaceableSpell extends AbstractDelegatingSpell implements OrientedS
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
dead = compound.getBoolean("dead");
deathTicks = compound.getInt("deathTicks");
age = compound.getInt("age");
pitch = compound.getFloat("pitch");
yaw = compound.getFloat("yaw");

View file

@ -61,6 +61,8 @@ public interface Spell extends NbtSerialisable, Affine {
*/
boolean isDead();
boolean isDying();
/**
* Returns true if this effect has changes that need to be sent to the client.
*/
@ -68,6 +70,7 @@ public interface Spell extends NbtSerialisable, Affine {
/**
* Applies this spell to the supplied caster.
* @param caster The caster to apply the spell to
*/
default boolean apply(Caster<?> caster) {
caster.getSpellSlot().put(this);
@ -76,7 +79,7 @@ public interface Spell extends NbtSerialisable, Affine {
/**
* Gets the default form of this spell used to apply to a caster.
* @param caster
* @param caster The caster currently fueling this spell
*/
default Spell prepareForCast(Caster<?> caster, CastingMethod method) {
return this;
@ -89,6 +92,12 @@ public interface Spell extends NbtSerialisable, Affine {
*/
boolean tick(Caster<?> caster, Situation situation);
/**
* Called on spells that are actively dying to update any post-death animations before removal.
* @param caster The caster currently fueling this spell
*/
void tickDying(Caster<?> caster);
/**
* Marks this effect as dirty.
*/

View file

@ -12,6 +12,7 @@ import net.minecraft.nbt.NbtCompound;
public abstract class AbstractSpell implements Spell {
private boolean dead;
private boolean dying;
private boolean dirty;
private boolean hidden;
private boolean destroyed;
@ -45,7 +46,7 @@ public abstract class AbstractSpell implements Spell {
@Override
public final void setDead() {
dead = true;
dying = true;
setDirty();
}
@ -54,6 +55,11 @@ public abstract class AbstractSpell implements Spell {
return dead;
}
@Override
public final boolean isDying() {
return dying;
}
@Override
public final boolean isDirty() {
return dirty;
@ -82,6 +88,11 @@ public abstract class AbstractSpell implements Spell {
protected void onDestroyed(Caster<?> caster) {
}
@Override
public void tickDying(Caster<?> caster) {
dead = true;
}
@Override
public final void destroy(Caster<?> caster) {
if (destroyed) {
@ -94,6 +105,7 @@ public abstract class AbstractSpell implements Spell {
@Override
public void toNBT(NbtCompound compound) {
compound.putBoolean("dying", dying);
compound.putBoolean("dead", dead);
compound.putBoolean("hidden", hidden);
compound.putUuid("uuid", uuid);
@ -106,6 +118,7 @@ public abstract class AbstractSpell implements Spell {
if (compound.contains("uuid")) {
uuid = compound.getUuid("uuid");
}
dying = compound.getBoolean("dying");
dead = compound.getBoolean("dead");
hidden = compound.getBoolean("hidden");
if (compound.contains("traits")) {

View file

@ -57,7 +57,7 @@ public class PlacedSpellRenderer extends SpellRenderer<PlaceableSpell> {
matrices.multiply(RotationAxis.POSITIVE_Y.rotationDegrees(180 - spell.yaw));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90));
float scale = (spell.getAge(tickDelta) / 25F) * 3;
float scale = spell.getScale(tickDelta) * 3;
matrices.scale(scale, scale, scale);
float angle = (animationProgress / 9F) % 360;

View file

@ -72,6 +72,10 @@ public class CastSpellEntity extends LightEmittingEntity implements Caster<CastS
}
}
protected void updatePostDeath() {
}
@Override
public EntityReference<LivingEntity> getMasterReference() {
return owner;

View file

@ -44,7 +44,13 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
}
public boolean tick(Situation situation) {
return tick(spell -> Operation.ofBoolean(spell.tick(owner, situation)));
return tick(spell -> {
if (spell.isDying()) {
spell.tickDying(owner);
return Operation.ofBoolean(!spell.isDead());
}
return Operation.ofBoolean(spell.tick(owner, situation));
});
}
public boolean tick(Function<Spell, Operation> tickAction) {