Further cleanup to the magic system

This commit is contained in:
Sollace 2021-03-03 11:33:23 +02:00
parent b3e196c404
commit 269db7bdc9
25 changed files with 160 additions and 224 deletions

View file

@ -2,8 +2,10 @@ package com.minelittlepony.unicopia;
import java.util.function.Predicate;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
@ -28,7 +30,7 @@ public interface EquinePredicates {
Predicate<LivingEntity> HAS_WANT_IT_NEED_IT = e -> EnchantmentHelper.getEquipmentLevel(UEnchantments.WANT_IT_NEED_IT, e) > 0;
static Predicate<Entity> carryingSpell(Class<? extends Spell> type) {
return IS_PLAYER.and(entity -> Pony.of((PlayerEntity)entity).getSpellOrEmpty(type, false).isPresent());
static Predicate<Entity> carryingSpell(@Nullable SpellType<?> type) {
return IS_PLAYER.and(entity -> Pony.of((PlayerEntity)entity).getSpellSlot().get(type, false).isPresent());
}
}

View file

@ -8,7 +8,7 @@ import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.ShieldSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
@ -23,7 +23,7 @@ import net.minecraft.util.math.Vec3d;
*/
public class BatEeeeAbility implements Ability<Hit> {
private static final Predicate<Entity> HAS_SHIELD = EquinePredicates.carryingSpell(ShieldSpell.class);
private static final Predicate<Entity> HAS_SHIELD = EquinePredicates.carryingSpell(SpellType.SHIELD);
@Override
public int getWarmupTime(Pony player) {

View file

@ -44,7 +44,8 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
Entity looked = trace.getEntity().map(e -> {
return e instanceof PlayerEntity ? Pony.of((PlayerEntity)e)
.getSpellOrEmpty(DisguiseSpell.class)
.getSpellSlot()
.get(SpellType.DISGUISE, true)
.map(DisguiseSpell::getDisguise)
.map(Disguise::getAppearance)
.orElse(e) : e;
@ -57,7 +58,7 @@ public class ChangelingDisguiseAbility extends ChangelingFeedAbility {
player.getEntityWorld().playSound(null, player.getBlockPos(), SoundEvents.ENTITY_PARROT_IMITATE_RAVAGER, SoundCategory.PLAYERS, 1.4F, 0.4F);
iplayer.getSpellOrEmpty(DisguiseSpell.class).orElseGet(() -> {
iplayer.getSpellSlot().get(SpellType.DISGUISE, true).orElseGet(() -> {
DisguiseSpell disc = SpellType.DISGUISE.create();
iplayer.setSpell(disc);

View file

@ -40,7 +40,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
return null;
}
if (player.getPhysics().isFlying() && !player.hasSpell()) {
if (player.getPhysics().isFlying() && !player.getSpellSlot().isPresent()) {
return Hit.INSTANCE;
}
@ -64,7 +64,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
return;
}
if (player.getPhysics().isFlying() && !player.hasSpell()) {
if (player.getPhysics().isFlying() && !player.getSpellSlot().isPresent()) {
player.getMagicalReserves().getMana().multiply(0.1F);
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
player.setSpell(SpellType.JOUSTING.create());

View file

@ -7,6 +7,7 @@ import javax.annotation.Nullable;
import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.AmuletItem;
@ -60,7 +61,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
return Hit.of(spell != ActionResult.FAIL && manaLevel > 4F);
}
return Hit.of(manaLevel > (player.hasSpell() ? 2F : 4F));
return Hit.of(manaLevel > (player.getSpellSlot().isPresent() ? 2F : 4F));
}
@Override
@ -82,7 +83,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
return 4F;
}
if (player.hasSpell()) {
if (player.getSpellSlot().isPresent()) {
return 2F;
}
@ -112,7 +113,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
if (newSpell.getResult() != ActionResult.FAIL) {
@Nullable
SpellType<?> spell = newSpell.getValue();
if (!player.hasSpell() && spell == null) {
if (!player.getSpellSlot().isPresent() && spell == null) {
spell = SpellType.SHIELD;
}
@ -138,7 +139,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
}
private TypedActionResult<SpellType<?>> getNewSpell(Pony player) {
final SpellType<?> current = player.hasSpell() ? player.getSpell(true).getType() : null;
final SpellType<?> current = player.getSpellSlot().get(true).map(Spell::getType).orElse(null);
return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, SpellType::mayAttach))

View file

@ -27,48 +27,10 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
Physics getPhysics();
EffectSync getPrimarySpellSlot();
EffectSync getSpellSlot();
default void setSpell(@Nullable Spell spell) {
getPrimarySpellSlot().set(spell);
}
/**
* Gets the active effect for this caster.
*/
@Nullable
default Spell getSpell(boolean update) {
return getSpell(null, update);
}
/**
* Gets the active effect for the matching given type.
* Returns null if no such effect exists for this caster.
*/
@Nullable
default <T extends Spell> T getSpell(@Nullable Class<T> type, boolean update) {
return getPrimarySpellSlot().get(type, update);
}
/**
* Gets the active effect for this caster updating it if needed.
*/
default <T extends Spell> Optional<T> getSpellOrEmpty(Class<T> type, boolean update) {
return getPrimarySpellSlot().getOrEmpty(type, update);
}
/**
* Gets the active effect for this caster updating it if needed.
*/
default <T extends Spell> Optional<T> getSpellOrEmpty(Class<T> type) {
return getSpellOrEmpty(type, true);
}
/**
* Returns true if this caster has an active effect attached to it.
*/
default boolean hasSpell() {
return getPrimarySpellSlot().has();
getSpellSlot().put(spell);
}
/**
@ -80,7 +42,7 @@ public interface Caster<E extends LivingEntity> extends Owned<E>, Levelled, Affi
}
/**
* gets the minecraft world
* Gets the minecraft world
*/
@Override
default World getWorld() {

View file

@ -52,11 +52,4 @@ public interface Spell extends NbtSerialisable, Affine {
default boolean handleProjectileImpact(ProjectileEntity projectile) {
return false;
}
/**
* Returns a new, deep-copied instance of this spell.
*/
default Spell copy() {
return SpellType.copy(this);
}
}

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
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;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.util.shape.Shape;
@ -45,12 +44,12 @@ public class RevealingSpell extends AbstractSpell implements Attached, Thrown {
}
source.findAllSpellsInRange(15).forEach(e -> {
Suppressable spell = e.getSpell(Suppressable.class, false);
if (spell != null && spell.isVulnerable(source, this)) {
e.getSpellSlot().get(SpellPredicate.IS_SUPPRESSABLE, false)
.filter(spell -> spell.isVulnerable(source, this))
.ifPresent(spell -> {
spell.onSuppressed(source);
source.getWorld().playSound(null, e.getOrigin(), SoundEvents.ENTITY_ZOMBIE_VILLAGER_CURE, SoundCategory.PLAYERS, 0.2F, 0.5F);
}
});
});
return true;

View file

@ -0,0 +1,14 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.function.Predicate;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.Suppressable;
import com.minelittlepony.unicopia.ability.magic.Thrown;
public interface SpellPredicate<T> extends Predicate<Spell> {
SpellPredicate<Thrown> IS_THROWN = s -> s instanceof Thrown;
SpellPredicate<Attached> IS_ATTACHED = s -> s instanceof Attached;
SpellPredicate<Suppressable> IS_SUPPRESSABLE = s -> s instanceof Suppressable;
}

View file

@ -10,9 +10,7 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.text.Text;
@ -20,7 +18,7 @@ import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
public class SpellType<T extends Spell> implements Affine {
public final class SpellType<T extends Spell> implements Affine, SpellPredicate<T> {
public static final Identifier EMPTY_ID = new Identifier("unicopia", "null");
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, t -> null);
@ -57,7 +55,7 @@ public class SpellType<T extends Spell> implements Affine {
@Nullable
private String translationKey;
SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
private SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
this.id = id;
this.affinity = affinity;
this.color = color;
@ -65,8 +63,8 @@ public class SpellType<T extends Spell> implements Affine {
this.factory = factory;
Spell inst = create();
thrown = inst instanceof Thrown;
attached = inst instanceof Attached;
thrown = SpellPredicate.IS_THROWN.test(inst);
attached = SpellPredicate.IS_ATTACHED.test(inst);
}
public boolean isObtainable() {
@ -119,6 +117,11 @@ public class SpellType<T extends Spell> implements Affine {
return null;
}
@Override
public boolean test(@Nullable Spell spell) {
return spell != null && spell.getType() == this;
}
public static <T extends Spell> SpellType<T> register(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
SpellType<T> type = new SpellType<>(id, affinity, color, obtainable, factory);
byAffinity(affinity).add(type);
@ -132,18 +135,13 @@ public class SpellType<T extends Spell> implements Affine {
@SuppressWarnings("unchecked")
public static <T extends Spell> SpellType<T> getKey(Identifier id) {
return (SpellType<T>)REGISTRY.getOrDefault(id, EMPTY_KEY);
return (SpellType<T>)(EMPTY_ID.equals(id) ? EMPTY_KEY : REGISTRY.getOrDefault(id, EMPTY_KEY));
}
public static Set<SpellType<?>> byAffinity(Affinity affinity) {
return BY_AFFINITY.computeIfAbsent(affinity, a -> new HashSet<>());
}
@SuppressWarnings("unchecked")
public static <T extends Spell> T copy(T effect) {
return (T)fromNBT(toNBT(effect));
}
@Nullable
public static Spell fromNBT(CompoundTag compound) {
if (compound.contains("effect_id")) {

View file

@ -9,6 +9,7 @@ import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.client.KeyBindingsHandler;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -88,7 +89,7 @@ public class UHud extends DrawableHelper {
matrices.pop();
if (pony.getSpecies() == Race.CHANGELING && !client.player.isSneaking()) {
pony.getSpellOrEmpty(DisguiseSpell.class, false).map(DisguiseSpell::getDisguise)
pony.getSpellSlot().get(SpellType.DISGUISE, false).map(DisguiseSpell::getDisguise)
.map(Disguise::getAppearance)
.ifPresent(appearance -> {

View file

@ -3,7 +3,7 @@ package com.minelittlepony.unicopia.client.render;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.ItemImpl;
import com.minelittlepony.unicopia.entity.Living;
@ -83,7 +83,7 @@ public class WorldRenderDelegate {
int fireTicks = pony.getMaster().doesRenderOnFire() ? 1 : 0;
return ((Caster<?>)pony).getSpellOrEmpty(DisguiseSpell.class, true).map(effect -> {
return ((Caster<?>)pony).getSpellSlot().get(SpellType.DISGUISE, true).map(effect -> {
effect.update(pony, false);
Disguise ve = effect.getDisguise();

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.command;
import java.util.function.Function;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.mojang.brigadier.CommandDispatcher;
@ -61,13 +60,10 @@ public class DisguiseCommand {
throw FAILED_EXCEPTION.create();
}
DisguiseSpell effect = iplayer.getSpell(DisguiseSpell.class, true);
if (effect == null) {
iplayer.setSpell(SpellType.DISGUISE.create().setDisguise(entity));
} else {
effect.setDisguise(entity);
}
iplayer.getSpellSlot()
.get(SpellType.DISGUISE, true)
.orElseGet(SpellType.DISGUISE::create)
.setDisguise(entity);
if (!isSelf) {
source.sendFeedback(new TranslatableText("commands.disguise.success.other", player.getName(), entity.getName()), true);
@ -83,7 +79,7 @@ public class DisguiseCommand {
static int reveal(ServerCommandSource source, PlayerEntity player) {
Pony iplayer = Pony.of(player);
iplayer.getSpellOrEmpty(DisguiseSpell.class).ifPresent(disguise -> {
iplayer.getSpellSlot().get(SpellType.DISGUISE, true).ifPresent(disguise -> {
disguise.onDestroyed(iplayer);
});

View file

@ -4,7 +4,6 @@ import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.Affine;
import com.minelittlepony.unicopia.ability.magic.Levelled;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.ai.BreakHeartGoal;
import com.minelittlepony.unicopia.entity.ai.DynamicTargetGoal;
@ -89,11 +88,9 @@ public class Creature extends Living<LivingEntity> {
@Override
public void toNBT(CompoundTag compound) {
super.toNBT(compound);
Spell effect = getSpell(true);
if (effect != null) {
getSpellSlot().get(true).ifPresent(effect -> {
compound.put("effect", SpellType.toNBT(effect));
}
});
physics.toNBT(compound);
}

View file

@ -5,10 +5,9 @@ import java.util.stream.Stream;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.network.EffectSync;
@ -60,7 +59,7 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
}
@Override
public EffectSync getPrimarySpellSlot() {
public EffectSync getSpellSlot() {
return effectDelegate;
}
@ -79,15 +78,11 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override
public void tick() {
if (hasSpell()) {
Attached effect = getSpell(Attached.class, true);
if (effect != null) {
getSpellSlot().get(SpellPredicate.IS_ATTACHED, true).ifPresent(effect -> {
if (!effect.onBodyTick(this)) {
setSpell(null);
}
}
}
});
if (invinsibilityTicks > 0) {
invinsibilityTicks--;
@ -142,18 +137,13 @@ public abstract class Living<T extends LivingEntity> implements Equine<T>, Caste
@Override
public boolean onProjectileImpact(ProjectileEntity projectile) {
if (hasSpell()) {
Spell effect = getSpell(true);
if (!effect.isDead() && effect.handleProjectileImpact(projectile)) {
return true;
}
}
return false;
return getSpellSlot().get(true)
.filter(effect -> !effect.isDead() && effect.handleProjectileImpact(projectile))
.isPresent();
}
protected void handleFall(float distance, float damageMultiplier) {
getSpellOrEmpty(DisguiseSpell.class, false).ifPresent(spell -> {
getSpellSlot().get(SpellType.DISGUISE, false).ifPresent(spell -> {
spell.getDisguise().onImpact(this, distance, damageMultiplier);
});
}

View file

@ -14,7 +14,7 @@ import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.InteractionManager;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.PlayerAttributes;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.projectile.ProjectileUtil;
@ -345,7 +345,7 @@ public class Disguise implements NbtSerialisable {
VoxelShape entityShape = VoxelShapes.cuboid(box.expand(1.0E-6D));
world.getOtherEntities(entity, box.expand(0.5), predicate.and(e -> {
Caster.of(e).flatMap(c -> c.getSpellOrEmpty(DisguiseSpell.class, false)).ifPresent(p -> {
Caster.of(e).flatMap(c -> c.getSpellSlot().get(SpellType.DISGUISE, false)).ifPresent(p -> {
p.getDisguise().getCollissionShapes(ctx, shape -> {
if (!shape.isEmpty() && VoxelShapes.matchesAnywhere(shape, entityShape, BooleanBiFunction.AND)) {
shapes.add(shape);

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 com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import net.minecraft.util.math.Vec3d;
@ -46,7 +47,8 @@ public class PlayerCamera extends MotionCompositor {
}
public Optional<Double> calculateDistance(double distance) {
return player.getSpellOrEmpty(DisguiseSpell.class, false)
return player.getSpellSlot()
.get(SpellType.DISGUISE, false)
.map(DisguiseSpell::getDisguise)
.flatMap(d -> d.getDistance(player))
.map(d -> distance * d);

View file

@ -5,7 +5,6 @@ import java.util.Optional;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Spell;
import net.minecraft.entity.EntityDimensions;
@ -72,13 +71,9 @@ public final class PlayerDimensions {
}
Optional<Provider> getPredicate() {
if (pony.hasSpell()) {
Spell effect = pony.getSpell(true);
if (!effect.isDead() && effect instanceof Provider) {
return Optional.of(((Provider)effect));
}
}
return Optional.empty();
return pony.getSpellSlot().get(true)
.filter(effect -> !effect.isDead() && effect instanceof Provider)
.map(effect -> (Provider)effect);
}
public interface Provider {

View file

@ -3,8 +3,7 @@ package com.minelittlepony.unicopia.entity.player;
import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.JoustingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.Jumper;
@ -78,7 +77,7 @@ public class PlayerPhysics extends EntityPhysics<Pony> implements Tickable, Moti
@Override
public boolean isRainbooming() {
return pony.getSpellOrEmpty(JoustingSpell.class).isPresent();
return pony.getSpellSlot().get(SpellType.JOUSTING, true).isPresent();
}
@Override
@ -448,14 +447,10 @@ public class PlayerPhysics extends EntityPhysics<Pony> implements Tickable, Moti
return FlightType.ARTIFICIAL;
}
if (pony.hasSpell()) {
Spell effect = pony.getSpell(true);
if (!effect.isDead() && effect instanceof FlightType.Provider) {
return ((FlightType.Provider)effect).getFlightType(pony);
}
}
return pony.getSpecies().getFlightType();
return pony.getSpellSlot().get(true)
.filter(effect -> !effect.isDead() && effect instanceof FlightType.Provider)
.map(effect -> ((FlightType.Provider)effect).getFlightType(pony))
.orElse(pony.getSpecies().getFlightType());
}
public void updateFlightStat(boolean flying) {

View file

@ -14,7 +14,6 @@ import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.WorldTribeManager;
import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.ShieldSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.PonyContainer;
@ -134,7 +133,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
@Override
public boolean isInvisible() {
return invisible && hasSpell();
return invisible && getSpellSlot().isPresent();
}
public boolean isSpeciesPersisted() {
@ -289,7 +288,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
float g = gravity.getGravityModifier();
boolean extraProtection = getSpell(ShieldSpell.class, false) != null;
boolean extraProtection = getSpellSlot().get(SpellType.SHIELD, false).isPresent();
if (g != 1 || extraProtection || getSpecies().canFly() && !entity.isCreative() && !entity.isSpectator()) {
@ -384,11 +383,9 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
compound.put("powers", powers.toNBT());
compound.put("gravity", gravity.toNBT());
Spell effect = getSpell(true);
if (effect != null) {
getSpellSlot().get(true).ifPresent(effect ->{
compound.put("effect", SpellType.toNBT(effect));
}
});
}
@Override
@ -403,7 +400,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
magicExhaustion = compound.getFloat("magicExhaustion");
if (compound.contains("effect")) {
getPrimarySpellSlot().set(SpellType.fromNBT(compound.getCompound("effect")));
getSpellSlot().put(SpellType.fromNBT(compound.getCompound("effect")));
}
}
@ -411,7 +408,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
public void copyFrom(Pony oldPlayer) {
speciesPersisted = oldPlayer.speciesPersisted;
if (!oldPlayer.getEntity().removed) {
setSpell(oldPlayer.getSpell(true));
setSpell(oldPlayer.getSpellSlot().get(true).orElse(null));
}
setSpecies(oldPlayer.getSpecies());
setDirty();

View file

@ -14,6 +14,7 @@ import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Creature;
import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
@ -82,7 +83,7 @@ abstract class MixinLivingEntity extends Entity implements PonyContainer<Equine<
@Inject(method = "isClimbing()Z", at = @At("HEAD"), cancellable = true)
public void onIsClimbing(CallbackInfoReturnable<Boolean> info) {
if (get() instanceof Pony && horizontalCollision) {
((Pony)get()).getSpellOrEmpty(DisguiseSpell.class, false)
((Pony)get()).getSpellSlot().get(SpellType.DISGUISE, false)
.map(DisguiseSpell::getDisguise)
.filter(Disguise::canClimbWalls)
.ifPresent(v -> {
@ -95,7 +96,7 @@ abstract class MixinLivingEntity extends Entity implements PonyContainer<Equine<
@Inject(method = "isPushable()Z", at = @At("HEAD"), cancellable = true)
private void onIsPushable(CallbackInfoReturnable<Boolean> info) {
Caster.of(this)
.flatMap(c -> c.getSpellOrEmpty(DisguiseSpell.class, false))
.flatMap(c -> c.getSpellSlot().get(SpellType.DISGUISE, false))
.map(DisguiseSpell::getDisguise)
.map(Disguise::getAppearance)
.filter(Entity::isPushable)

View file

@ -7,7 +7,7 @@ import org.spongepowered.asm.mixin.injection.At;
import org.spongepowered.asm.mixin.injection.Inject;
import org.spongepowered.asm.mixin.injection.callback.CallbackInfoReturnable;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -20,7 +20,7 @@ abstract class MixinTargetPredicate {
public void onTest(@Nullable LivingEntity baseEntity, LivingEntity targetEntity, CallbackInfoReturnable<Boolean> info) {
Equine<?> eq = Equine.of(targetEntity);
if (eq instanceof Pony) {
((Pony)eq).getSpellOrEmpty(DisguiseSpell.class).ifPresent(spell -> {
((Pony)eq).getSpellSlot().get(SpellType.DISGUISE, true).ifPresent(spell -> {
if (spell.getDisguise().getAppearance() == baseEntity) {
info.setReturnValue(false);
}

View file

@ -6,6 +6,7 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import net.minecraft.entity.data.TrackedData;
@ -13,7 +14,7 @@ import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Identifier;
/**
* Synchronisation class for spell effects.
* Synchronisation class for spells.
* Since we can't have our own serializers, we have to intelligently
* determine whether to update it from an nbt tag.
*
@ -21,85 +22,83 @@ import net.minecraft.util.Identifier;
*/
public class EffectSync {
@Nullable
private Spell effect;
private Optional<Spell> spell = Optional.empty();
private final Caster<?> owned;
private final Caster<?> owner;
private final TrackedData<CompoundTag> param;
@Nullable
private CompoundTag lastValue;
public EffectSync(Caster<?> owned, TrackedData<CompoundTag> param) {
this.owned = owned;
public EffectSync(Caster<?> owner, TrackedData<CompoundTag> param) {
this.owner = owner;
this.param = param;
}
public <T extends Spell> Optional<T> getOrEmpty(Class<T> type, boolean update) {
T effect = get(type, update);
if (effect == null || effect.isDead()) {
return Optional.empty();
}
return Optional.of(effect);
/**
* Gets the active effect for this caster updating it if needed.
*/
public <T extends Spell> Optional<T> get(boolean update) {
return get(null, update);
}
/**
* Gets the active effect for this caster updating it if needed.
*/
@SuppressWarnings("unchecked")
public <E extends Spell> E get(Class<E> type, boolean update) {
public <T extends Spell> Optional<T> get(@Nullable SpellPredicate<T> type, boolean update) {
if (update) {
sync(true);
}
if (effect == null || type == null || type.isAssignableFrom(effect.getClass())) {
return (E)effect;
if (checkReference() && (type == null || type.test(spell.get()))) {
return (Optional<T>)spell;
}
return null;
return Optional.empty();
}
public boolean has() {
/**
* Returns true if this caster has an active effect attached to it.
*/
public boolean isPresent() {
sync(false);
return effect != null;
return checkReference();
}
private boolean checkReference() {
return spell.isPresent() && !spell.get().isDead();
}
private void sync(boolean force) {
CompoundTag comp = owned.getEntity().getDataTracker().get(param);
CompoundTag comp = owner.getEntity().getDataTracker().get(param);
Spell effect = spell.orElse(null);
if (comp == null || !comp.contains("effect_id")) {
if (effect != null) {
effect.setDead();
effect = null;
}
return;
} else {
if (effect == null || !effect.getType().getId().equals(new Identifier(comp.getString("effect_id")))) {
if (effect != null) {
effect.setDead();
}
effect = SpellType.fromNBT(comp);
} else if (owned.getEntity().world.isClient()) {
updateReference(null);
} else if (!checkReference() || !effect.getType().getId().equals(new Identifier(comp.getString("effect_id")))) {
updateReference(SpellType.fromNBT(comp));
} else if (owner.getEntity().world.isClient()) {
if (lastValue != comp || !(comp == null || comp.equals(lastValue))) {
lastValue = comp;
effect.fromNBT(comp);
}
} else if ((force || !owned.getEntity().world.isClient()) && effect.isDirty()) {
set(effect);
}
} else if ((force || !owner.isClient()) && effect.isDirty()) {
put(effect);
}
}
public void set(@Nullable Spell effect) {
if (this.effect != null && this.effect != effect) {
this.effect.setDead();
public void put(@Nullable Spell effect) {
updateReference(effect);
owner.getEntity().getDataTracker().set(param, effect == null ? new CompoundTag() : SpellType.toNBT(effect));
}
this.effect = effect;
if (effect == null) {
owned.getEntity().getDataTracker().set(param, new CompoundTag());
} else {
owned.getEntity().getDataTracker().set(param, SpellType.toNBT(effect));
private void updateReference(@Nullable Spell effect) {
if (spell.isPresent() && spell.get() != effect) {
spell.get().setDead();
}
spell = Optional.ofNullable(effect);
}
}

View file

@ -4,6 +4,7 @@ import java.util.Optional;
import java.util.function.Consumer;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
@ -66,7 +67,7 @@ public class ParticleHandle {
public void attach(Caster<?> caster) {
this.linked = true;
this.caster = Optional.of(caster);
this.effect = caster.getSpell(false).getType();
this.effect = caster.getSpellSlot().get(false).map(Spell::getType).orElse(null);
}
public void detach() {
@ -81,10 +82,10 @@ public class ParticleHandle {
caster = caster.filter(c -> {
Entity e = c.getEntity();
return Equine.of(e) == c
&& c.hasSpell()
&& c.getSpell(false).getType().equals(effect)
&& c.getSpellSlot().get(false)
.filter(s -> s.getType() == effect)
.isPresent()
&& e != null
&& c.getWorld().getEntityById(e.getEntityId()) != null;
});

View file

@ -2,11 +2,12 @@ package com.minelittlepony.unicopia.projectile;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.UEntities;
import com.minelittlepony.unicopia.ability.magic.Affine;
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.SpellPredicate;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.Physics;
@ -77,7 +78,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
@Override
protected Item getDefaultItem() {
switch (getSpellOrEmpty(Spell.class, false).map(Spell::getAffinity).orElse(Affinity.NEUTRAL)) {
switch (getSpellSlot().get(false).map(Spell::getAffinity).orElse(Affinity.NEUTRAL)) {
case GOOD: return Items.SNOWBALL;
case BAD: return Items.MAGMA_CREAM;
default: return Items.AIR;
@ -111,11 +112,11 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
@Override
public Affinity getAffinity() {
return hasSpell() ? getSpell(true).getAffinity() : Affinity.NEUTRAL;
return getSpellSlot().get(true).map(Affine::getAffinity).orElse(Affinity.NEUTRAL);
}
@Override
public EffectSync getPrimarySpellSlot() {
public EffectSync getSpellSlot() {
return effectDelegate;
}
@ -158,17 +159,13 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
setNoGravity(false);
}
if (hasSpell()) {
if (getSpellSlot().isPresent()) {
if (lastBlockPos == null || !lastBlockPos.equals(getBlockPos())) {
lastBlockPos = getBlockPos();
}
Thrown spell = getSpell(Thrown.class, true);
if (spell.isDead()) {
if (!getSpellSlot().get(SpellPredicate.IS_THROWN, true).filter(spell -> spell.onThrownTick(this)).isPresent()) {
remove();
} else {
spell.onThrownTick(this);
}
}
@ -228,10 +225,9 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
public void writeCustomDataToTag(CompoundTag compound) {
super.writeCustomDataToTag(compound);
physics.toNBT(compound);
if (hasSpell()) {
compound.put("effect", SpellType.toNBT(getSpell(true)));
}
getSpellSlot().get(true).ifPresent(effect -> {
compound.put("effect", SpellType.toNBT(effect));
});
}
@Override
@ -249,13 +245,9 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
@Override
protected void onBlockHit(BlockHitResult hit) {
if (hasSpell()) {
Spell effect = getSpell(true);
if (effect instanceof ProjectileDelegate) {
((ProjectileDelegate)effect).onImpact(this, hit.getBlockPos(), world.getBlockState(hit.getBlockPos()));
}
}
getSpellSlot().get(SpellPredicate.IS_THROWN, true).ifPresent(effect -> {
effect.onImpact(this, hit.getBlockPos(), world.getBlockState(hit.getBlockPos()));
});
if (getItem().getItem() instanceof ProjectileDelegate) {
((ProjectileDelegate)getItem().getItem()).onImpact(this, hit.getBlockPos(), world.getBlockState(hit.getBlockPos()));