For spells with a timer, display the timer progress to the user

This commit is contained in:
Sollace 2022-09-16 12:47:32 +02:00
parent 082e5e37f2
commit 638a136d6d
8 changed files with 158 additions and 59 deletions

View file

@ -2,10 +2,7 @@ package com.minelittlepony.unicopia.ability.magic;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.PlaceableSpell;
import com.minelittlepony.unicopia.ability.magic.spell.ProjectileSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.ability.magic.spell.effect.ShieldSpell;
import net.minecraft.entity.Entity;
@ -16,8 +13,9 @@ public interface SpellPredicate<T extends Spell> extends Predicate<Spell> {
SpellPredicate<ProjectileSpell> HAS_PROJECTILE_EVENTS = s -> s instanceof ProjectileSpell;
SpellPredicate<AbstractDisguiseSpell> IS_DISGUISE = s -> s instanceof AbstractDisguiseSpell;
SpellPredicate<ShieldSpell> IS_SHIELD_LIKE = spell -> spell instanceof ShieldSpell;
SpellPredicate<TimedSpell> IS_TIMED = spell -> spell instanceof TimedSpell;
default SpellPredicate<T> and(SpellPredicate<T> predicate) {
default <Q extends Spell> SpellPredicate<Q> and(SpellPredicate<Q> predicate) {
SpellPredicate<T> self = this;
return s -> {
return self.test(s) && predicate.test(s);

View file

@ -0,0 +1,49 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.MathHelper;
/**
* A magic effect with a set duration capable of reporting how long it has until it runs out.
*/
public interface TimedSpell extends Spell {
Timer getTimer();
class Timer implements NbtSerialisable {
public int maxDuration;
public int duration;
public int prevDuration;
public Timer(int initial) {
maxDuration = initial;
duration = initial;
}
public void tick() {
prevDuration = duration;
duration--;
}
public float getPercentTimeRemaining(float tickDelta) {
return MathHelper.lerp(tickDelta, prevDuration, duration) / maxDuration;
}
public int getTicksRemaining() {
return duration;
}
@Override
public void toNBT(NbtCompound compound) {
compound.putInt("duration", duration);
compound.putInt("maxDuration", maxDuration);
}
@Override
public void fromNBT(NbtCompound compound) {
duration = compound.getInt("duration");
maxDuration = compound.getInt("maxDuration");
}
}
}

View file

@ -17,30 +17,32 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class AttractiveSpell extends ShieldSpell implements ProjectileSpell, HomingSpell {
public class AttractiveSpell extends ShieldSpell implements ProjectileSpell, HomingSpell, TimedSpell {
private final EntityReference<Entity> target = new EntityReference<>();
private int age;
private int duration;
private final Timer timer;
protected AttractiveSpell(CustomisedSpellType<?> type) {
super(type);
duration = 120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19);
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
}
@Override
public Timer getTimer() {
return timer;
}
@Override
public boolean tick(Caster<?> caster, Situation situation) {
age++;
timer.tick();
if (age % 20 == 0) {
duration--;
}
if (duration <= 0) {
if (timer.duration <= 0) {
return false;
}
setDirty();
Vec3d pos = caster.getOriginVector();
if (target.isPresent(caster.getReferenceWorld()) && target.get(caster.getReferenceWorld()).distanceTo(caster.getEntity()) > getDrawDropOffRange(caster)) {
target.get(caster.getReferenceWorld()).requestTeleport(pos.x, pos.y, pos.z);
@ -147,15 +149,13 @@ public class AttractiveSpell extends ShieldSpell implements ProjectileSpell, Hom
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.put("target", target.toNBT());
compound.putInt("age", age);
compound.putInt("duration", duration);
timer.toNBT(compound);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
target.fromNBT(compound.getCompound("target"));
age = compound.getInt("age");
duration = compound.getInt("duration");
timer.fromNBT(compound);
}
}

View file

@ -5,8 +5,10 @@ import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.Situation;
import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell;
import com.minelittlepony.unicopia.util.shape.Sphere;
import net.minecraft.nbt.NbtCompound;
import net.minecraft.particle.ParticleEffect;
import net.minecraft.particle.ParticleType;
import net.minecraft.particle.ParticleTypes;
@ -15,14 +17,31 @@ import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
import net.minecraft.util.registry.Registry;
public class AwkwardSpell extends AbstractSpell {
public class AwkwardSpell extends AbstractSpell implements TimedSpell {
private final Timer timer;
protected AwkwardSpell(CustomisedSpellType<?> type) {
super(type);
timer = new Timer(20);
}
@Override
public Timer getTimer() {
return timer;
}
@Override
public boolean tick(Caster<?> source, Situation situation) {
if (situation != Situation.PROJECTILE) {
timer.tick();
if (timer.duration <= 0) {
return false;
}
}
if (source.isClient()) {
source.spawnParticles(new Sphere(false, (1 + source.getLevel().getScaled(8)) * 8), 10, pos -> {
@ -49,4 +68,16 @@ public class AwkwardSpell extends AbstractSpell {
&& type != ParticleTypes.EXPLOSION_EMITTER
&& type != ParticleTypes.AMBIENT_ENTITY_EFFECT;
}
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
timer.toNBT(compound);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
timer.fromNBT(compound);
}
}

View file

@ -5,6 +5,7 @@ import java.util.stream.Stream;
import com.minelittlepony.unicopia.ability.magic.Caster;
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.item.FriendshipBraceletItem;
@ -17,7 +18,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.util.math.MathHelper;
import net.minecraft.util.math.Vec3d;
public class FeatherFallSpell extends AbstractSpell {
public class FeatherFallSpell extends AbstractSpell implements TimedSpell {
private static final int MIN_RANGE = 1;
private static final int MAX_RANGE = 20;
private static final int MIN_TARGETS = 1;
@ -35,20 +36,28 @@ public class FeatherFallSpell extends AbstractSpell {
.with(Trait.ORDER, 15)
.build();
private int duration;
private final Timer timer;
protected FeatherFallSpell(CustomisedSpellType<?> type) {
super(type);
duration = (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19);
timer = new Timer(10 + (int)(getTraits().get(Trait.FOCUS, 0, 160)));
}
@Override
public Timer getTimer() {
return timer;
}
@Override
public boolean tick(Caster<?> caster, Situation situation) {
timer.tick();
if (duration-- <= 0) {
if (timer.duration <= 0) {
return false;
}
setDirty();
List<Entity> targets = getTargets(caster).toList();
if (targets.isEmpty()) {
@ -82,7 +91,7 @@ public class FeatherFallSpell extends AbstractSpell {
ParticleUtils.spawnParticles(new MagicParticleEffect(getType().getColor()), target, 7);
});
return caster.subtractEnergyCost(duration % 50 == 0 ? getCostPerEntity() * targets.size() : 0);
return caster.subtractEnergyCost(timer.duration % 50 == 0 ? getCostPerEntity() * targets.size() : 0);
}
protected double getCostPerEntity() {
@ -117,12 +126,12 @@ public class FeatherFallSpell extends AbstractSpell {
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("duration", duration);
timer.toNBT(compound);
}
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
duration = compound.getInt("duration");
timer.fromNBT(compound);
}
}

View file

@ -5,6 +5,7 @@ import java.util.List;
import com.minelittlepony.unicopia.ability.magic.Caster;
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;
@ -16,7 +17,7 @@ import net.minecraft.nbt.NbtCompound;
import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList;
public class LightSpell extends AbstractSpell {
public class LightSpell extends AbstractSpell implements TimedSpell {
public static final SpellTraits DEFAULT_TRAITS = new SpellTraits.Builder()
.with(Trait.LIFE, 10)
.with(Trait.AIR, 0.3F)
@ -24,14 +25,18 @@ public class LightSpell extends AbstractSpell {
.with(Trait.ORDER, 25)
.build();
private int age;
private int duration;
private final Timer timer;
private final List<EntityReference<FairyEntity>> lights = new ArrayList<>();
protected LightSpell(CustomisedSpellType<?> type) {
super(type);
duration = 120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19);
timer = new Timer((120 + (int)(getTraits().get(Trait.FOCUS, 0, 160) * 19)) * 20);
}
@Override
public Timer getTimer() {
return timer;
}
@Override
@ -41,16 +46,14 @@ public class LightSpell extends AbstractSpell {
return false;
}
age++;
timer.tick();
if (age % 20 == 0) {
duration--;
}
if (duration <= 0) {
if (timer.duration <= 0) {
return false;
}
setDirty();
if (!caster.isClient()) {
if (lights.isEmpty()) {
int size = 2 + caster.getReferenceWorld().random.nextInt(2) + (int)(getTraits().get(Trait.LIFE, 10, 20) - 10)/10;
@ -93,8 +96,7 @@ public class LightSpell extends AbstractSpell {
@Override
public void toNBT(NbtCompound compound) {
super.toNBT(compound);
compound.putInt("age", age);
compound.putInt("duration", duration);
timer.toNBT(compound);
if (!lights.isEmpty()) {
NbtList list = new NbtList();
lights.forEach(light -> {
@ -107,8 +109,7 @@ public class LightSpell extends AbstractSpell {
@Override
public void fromNBT(NbtCompound compound) {
super.fromNBT(compound);
age = compound.getInt("age");
duration = compound.getInt("duration");
timer.fromNBT(compound);
lights.clear();
if (compound.contains("lights", NbtElement.LIST_TYPE)) {
compound.getList("lights", NbtElement.COMPOUND_TYPE).forEach(nbt -> {

View file

@ -4,11 +4,11 @@ import java.util.ArrayList;
import java.util.List;
import com.minelittlepony.common.client.gui.GameGui;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDelegatingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.*;
import com.minelittlepony.unicopia.client.FlowingText;
import com.minelittlepony.unicopia.client.particle.SphereModel;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.Channel;
import com.minelittlepony.unicopia.network.MsgRemoveSpell;
@ -23,6 +23,7 @@ import net.minecraft.screen.ScreenTexts;
import net.minecraft.sound.SoundEvents;
import net.minecraft.text.MutableText;
import net.minecraft.text.Text;
import net.minecraft.util.StringHelper;
import net.minecraft.util.math.Vector4f;
public class DismissSpellScreen extends GameGui {
@ -88,8 +89,6 @@ public class DismissSpellScreen extends GameGui {
class Entry extends Vector4f implements Element, Drawable, Selectable {
private final List<Text> tooltip = new ArrayList<>();
private final Spell spell;
private final Spell actualSpell;
@ -103,19 +102,6 @@ public class DismissSpellScreen extends GameGui {
SphereModel.convertToCartesianCoord(this, radius, azimuth, azimuth);
add(0, -(float)radius / 2F, 0, 0);
MutableText name = actualSpell.getType().getName().copy();
int color = actualSpell.getType().getColor();
name.setStyle(name.getStyle().withColor(color == 0 ? 0xFFAAAAAA : color));
tooltip.add(Text.translatable("Spell Type: %s", name));
actualSpell.getType().getTraits().appendTooltip(tooltip);
tooltip.add(ScreenTexts.EMPTY);
tooltip.add(Text.translatable("Affinity: %s", actualSpell.getAffinity().name()).formatted(actualSpell.getAffinity().getColor()));
tooltip.add(ScreenTexts.EMPTY);
tooltip.addAll(FlowingText.wrap(Text.translatable(actualSpell.getType().getTranslationKey() + ".lore").formatted(actualSpell.getAffinity().getColor()), 180).toList());
tooltip.add(ScreenTexts.EMPTY);
tooltip.add(Text.translatable("[Click to Discard]"));
}
private Spell getActualSpell() {
@ -147,7 +133,9 @@ public class DismissSpellScreen extends GameGui {
copy.set(getX(), getY(), getZ(), getW());
copy.transform(matrices.peek().getPositionMatrix());
DrawableUtil.renderItemIcon(actualSpell.getType().getDefualtStack(),
var type = actualSpell.getType().withTraits(actualSpell.getTraits());
DrawableUtil.renderItemIcon(actualSpell.isDead() ? UItems.BOTCHED_GEM.getDefaultStack() : type.getDefaultStack(),
copy.getX() - 8 + (copy.getX() - mouseX - 5) / 60D,
copy.getY() - 8 + (copy.getY() - mouseY - 5) / 60D,
1
@ -162,6 +150,24 @@ public class DismissSpellScreen extends GameGui {
if (isMouseOver(relativeMouseX, relativeMouseY)) {
DrawableUtil.drawArc(matrices, 0, 8, 0, DrawableUtil.TAU, color | 0x000000FF, false);
List<Text> tooltip = new ArrayList<>();
MutableText name = actualSpell.getType().getName().copy();
color = actualSpell.getType().getColor();
name.setStyle(name.getStyle().withColor(color == 0 ? 0xFFAAAAAA : color));
tooltip.add(Text.translatable("Spell Type: %s", name));
actualSpell.getType().getTraits().appendTooltip(tooltip);
tooltip.add(ScreenTexts.EMPTY);
tooltip.add(Text.translatable("Affinity: %s", actualSpell.getAffinity().name()).formatted(actualSpell.getAffinity().getColor()));
tooltip.add(ScreenTexts.EMPTY);
tooltip.addAll(FlowingText.wrap(Text.translatable(actualSpell.getType().getTranslationKey() + ".lore").formatted(actualSpell.getAffinity().getColor()), 180).toList());
if (spell instanceof TimedSpell timed) {
tooltip.add(ScreenTexts.EMPTY);
tooltip.add(Text.translatable("Time Left: %s", StringHelper.formatTicks(timed.getTimer().getTicksRemaining())));
}
tooltip.add(ScreenTexts.EMPTY);
tooltip.add(Text.translatable("[Click to Discard]"));
renderTooltip(matrices, tooltip, 0, 0);
if (!lastMouseOver) {

View file

@ -7,7 +7,9 @@ import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.*;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.ability.magic.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.AbstractDisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.TimedSpell;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.ability.magic.spell.effect.SpellType;
import com.minelittlepony.unicopia.client.KeyBindingsHandler;
@ -168,6 +170,9 @@ public class UHud extends DrawableHelper {
DrawableUtil.drawArc(modelStack, radius, radius + 3, 0, DrawableUtil.TAU, color & 0xFFFFFF2F, false);
DrawableUtil.drawArc(modelStack, radius + 3, radius + 4, 0, DrawableUtil.TAU, color & 0xFFFFFFAF, false);
pony.getSpellSlot().get(spell.and(SpellPredicate.IS_TIMED), false).map(TimedSpell::getTimer).ifPresent(timer -> {
DrawableUtil.drawArc(modelStack, radius, radius + 3, 0, DrawableUtil.TAU * timer.getPercentTimeRemaining(client.getTickDelta()), 0xFFFFFFFF, false);
});
modelStack.pop();
}