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.Race;
import com.minelittlepony.unicopia.ability.data.Pos; import com.minelittlepony.unicopia.ability.data.Pos;
import com.minelittlepony.unicopia.ability.magic.Caster; 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.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation; import com.minelittlepony.unicopia.client.render.PlayerPoser.Animation;
import com.minelittlepony.unicopia.entity.player.Pony; import com.minelittlepony.unicopia.entity.player.Pony;
@ -92,7 +93,11 @@ public class UnicornDispellAbility implements Ability<Pos> {
public boolean apply(Pony player, Pos data) { public boolean apply(Pony player, Pos data) {
player.setAnimation(Animation.WOLOLO, Animation.Recipient.ANYONE); 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 -> { 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; return true;
} }

View file

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

View file

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

View file

@ -61,6 +61,8 @@ public interface Spell extends NbtSerialisable, Affine {
*/ */
boolean isDead(); boolean isDead();
boolean isDying();
/** /**
* Returns true if this effect has changes that need to be sent to the client. * 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. * Applies this spell to the supplied caster.
* @param caster The caster to apply the spell to
*/ */
default boolean apply(Caster<?> caster) { default boolean apply(Caster<?> caster) {
caster.getSpellSlot().put(this); 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. * 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) { default Spell prepareForCast(Caster<?> caster, CastingMethod method) {
return this; return this;
@ -89,6 +92,12 @@ public interface Spell extends NbtSerialisable, Affine {
*/ */
boolean tick(Caster<?> caster, Situation situation); 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. * Marks this effect as dirty.
*/ */

View file

@ -12,6 +12,7 @@ import net.minecraft.nbt.NbtCompound;
public abstract class AbstractSpell implements Spell { public abstract class AbstractSpell implements Spell {
private boolean dead; private boolean dead;
private boolean dying;
private boolean dirty; private boolean dirty;
private boolean hidden; private boolean hidden;
private boolean destroyed; private boolean destroyed;
@ -45,7 +46,7 @@ public abstract class AbstractSpell implements Spell {
@Override @Override
public final void setDead() { public final void setDead() {
dead = true; dying = true;
setDirty(); setDirty();
} }
@ -54,6 +55,11 @@ public abstract class AbstractSpell implements Spell {
return dead; return dead;
} }
@Override
public final boolean isDying() {
return dying;
}
@Override @Override
public final boolean isDirty() { public final boolean isDirty() {
return dirty; return dirty;
@ -82,6 +88,11 @@ public abstract class AbstractSpell implements Spell {
protected void onDestroyed(Caster<?> caster) { protected void onDestroyed(Caster<?> caster) {
} }
@Override
public void tickDying(Caster<?> caster) {
dead = true;
}
@Override @Override
public final void destroy(Caster<?> caster) { public final void destroy(Caster<?> caster) {
if (destroyed) { if (destroyed) {
@ -94,6 +105,7 @@ public abstract class AbstractSpell implements Spell {
@Override @Override
public void toNBT(NbtCompound compound) { public void toNBT(NbtCompound compound) {
compound.putBoolean("dying", dying);
compound.putBoolean("dead", dead); compound.putBoolean("dead", dead);
compound.putBoolean("hidden", hidden); compound.putBoolean("hidden", hidden);
compound.putUuid("uuid", uuid); compound.putUuid("uuid", uuid);
@ -106,6 +118,7 @@ public abstract class AbstractSpell implements Spell {
if (compound.contains("uuid")) { if (compound.contains("uuid")) {
uuid = compound.getUuid("uuid"); uuid = compound.getUuid("uuid");
} }
dying = compound.getBoolean("dying");
dead = compound.getBoolean("dead"); dead = compound.getBoolean("dead");
hidden = compound.getBoolean("hidden"); hidden = compound.getBoolean("hidden");
if (compound.contains("traits")) { 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_Y.rotationDegrees(180 - spell.yaw));
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(90)); 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); matrices.scale(scale, scale, scale);
float angle = (animationProgress / 9F) % 360; float angle = (animationProgress / 9F) % 360;

View file

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

View file

@ -44,7 +44,13 @@ public class EffectSync implements SpellContainer, NbtSerialisable {
} }
public boolean tick(Situation situation) { 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) { public boolean tick(Function<Spell, Operation> tickAction) {