Perform the flattening on the spells

This commit is contained in:
Sollace 2021-03-02 22:10:52 +02:00
parent 51b541091e
commit b3e196c404
20 changed files with 91 additions and 126 deletions

View file

@ -11,8 +11,6 @@ public enum Affinity {
private final int corruption;
private Affinity[] implications;
public static final Affinity[] VALUES = values();
Affinity(Formatting color, int corruption) {
@ -40,20 +38,6 @@ public enum Affinity {
return isNeutral() || other.isNeutral() || this == other;
}
public Affinity[] getImplicators() {
if (implications != null) {
return implications;
}
if (this == NEUTRAL) {
implications = new Affinity[] { GOOD, BAD };
} else {
implications = new Affinity[] { this };
}
return implications;
}
public static Affinity of(int ordinal, Affinity fallback) {
return ordinal < 0 || ordinal >= VALUES.length ? fallback : VALUES[ordinal];
}

View file

@ -7,8 +7,6 @@ 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.Attached;
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;
@ -109,17 +107,17 @@ public class UnicornCastingAbility implements Ability<Hit> {
}
}
} else {
TypedActionResult<Spell> newSpell = getNewSpell(player);
TypedActionResult<SpellType<?>> newSpell = getNewSpell(player);
if (newSpell.getResult() != ActionResult.FAIL) {
@Nullable
Spell spell = newSpell.getValue();
SpellType<?> spell = newSpell.getValue();
if (!player.hasSpell() && spell == null) {
spell = SpellType.SHIELD.create();
spell = SpellType.SHIELD;
}
player.subtractEnergyCost(spell == null ? 2 : 4);
player.setSpell(spell);
player.setSpell(spell == null ? null : spell.create());
}
}
}
@ -139,13 +137,13 @@ public class UnicornCastingAbility implements Ability<Hit> {
return TypedActionResult.pass(stack);
}
private TypedActionResult<Spell> getNewSpell(Pony player) {
private TypedActionResult<SpellType<?>> getNewSpell(Pony player) {
final SpellType<?> current = player.hasSpell() ? player.getSpell(true).getType() : null;
return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, i -> i instanceof Attached))
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), current, SpellType::mayAttach))
.findFirst()
.orElse(TypedActionResult.<Spell>pass(null));
.orElse(TypedActionResult.<SpellType<?>>pass(null));
}
@Override

View file

@ -5,7 +5,6 @@ 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.Thrown;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -63,27 +62,27 @@ public class UnicornProjectileAbility implements Ability<Hit> {
@Override
public void apply(Pony player, Hit data) {
TypedActionResult<Spell> thrown = getNewSpell(player);
TypedActionResult<SpellType<?>> thrown = getNewSpell(player);
if (thrown.getResult() != ActionResult.FAIL) {
@Nullable
Thrown spell = (Thrown)thrown.getValue();
SpellType<?> spell = thrown.getValue();
if (spell == null) {
spell = SpellType.VORTEX.create();
spell = SpellType.VORTEX;
}
player.subtractEnergyCost(getCostEstimate(player));
spell.toss(player);
((Thrown)spell.create()).toss(player);
}
}
private TypedActionResult<Spell> getNewSpell(Pony player) {
private TypedActionResult<SpellType<?>> getNewSpell(Pony player) {
return Streams.stream(player.getMaster().getItemsHand())
.filter(GemstoneItem::isEnchanted)
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), null, i -> i instanceof Thrown))
.map(stack -> GemstoneItem.consumeSpell(stack, player.getMaster(), null, SpellType::mayThrow))
.findFirst()
.orElse(TypedActionResult.<Spell>pass(null));
.orElse(TypedActionResult.<SpellType<?>>pass(null));
}
@Override

View file

@ -12,11 +12,8 @@ public abstract class AbstractSpell implements Spell {
private final SpellType<?> type;
private Affinity affinity;
protected AbstractSpell(SpellType<?> type, Affinity affinity) {
protected AbstractSpell(SpellType<?> type) {
this.type = type;
this.affinity = affinity;
}
@Override
@ -46,19 +43,17 @@ public abstract class AbstractSpell implements Spell {
@Override
public Affinity getAffinity() {
return affinity;
return getType().getAffinity();
}
@Override
public void toNBT(CompoundTag compound) {
compound.putBoolean("dead", isDead);
compound.putInt("affinity", affinity.ordinal());
}
@Override
public void fromNBT(CompoundTag compound) {
setDirty(false);
isDead = compound.getBoolean("dead");
affinity = Affinity.of(compound.getInt("affinity"), getType().getAffinity());
}
}

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.entity.player.Pony;
@ -26,8 +25,8 @@ public class AttractiveSpell extends ShieldSpell implements Thrown {
@Nullable
private BlockPos homingPos;
protected AttractiveSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected AttractiveSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.ArrayList;
import java.util.List;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.util.shape.Sphere;
@ -18,8 +17,8 @@ import net.minecraft.util.registry.Registry;
public class AwkwardSpell extends AbstractSpell implements Thrown {
protected AwkwardSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected AwkwardSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -4,7 +4,6 @@ import java.util.Optional;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.FlightType;
import com.minelittlepony.unicopia.Owned;
import com.minelittlepony.unicopia.ability.magic.Attached;
@ -32,8 +31,8 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
private int suppressionCounter;
protected DisguiseSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected DisguiseSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
@ -41,8 +40,8 @@ public class FireSpell extends AbstractSpell implements Thrown, Attached {
private static final Shape VISUAL_EFFECT_RANGE = new Sphere(false, 0.5);
private static final Shape EFFECT_RANGE = new Sphere(false, 4);
protected FireSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected FireSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
@ -30,8 +29,8 @@ public class IceSpell extends AbstractSpell implements Thrown, Attached {
private final int rad = 3;
private final Shape effect_range = new Sphere(false, rad);
protected IceSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected IceSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.util.MagicalDamageSource;
@ -17,8 +16,8 @@ import net.minecraft.world.World;
public class InfernoSpell extends FireSpell {
protected InfernoSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected InfernoSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
@ -30,8 +29,8 @@ public class JoustingSpell extends AbstractSpell implements Attached {
private int age;
protected JoustingSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected JoustingSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List;
import com.google.common.collect.Lists;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.util.WorldEvent;
@ -27,8 +26,8 @@ public class NecromancySpell extends AbstractSpell implements Attached {
EntityType.ZOMBIFIED_PIGLIN
);
protected NecromancySpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected NecromancySpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -1,6 +1,5 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Suppressable;
@ -19,8 +18,8 @@ import net.minecraft.util.math.Vec3d;
public class RevealingSpell extends AbstractSpell implements Attached, Thrown {
private static final Shape AREA = new Sphere(false, 15);
protected RevealingSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected RevealingSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -2,7 +2,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.block.state.StateMaps;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
@ -17,8 +16,8 @@ import net.minecraft.util.math.Vec3d;
public class ScorchSpell extends FireSpell {
protected ScorchSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected ScorchSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -6,7 +6,6 @@ import java.util.TreeMap;
import java.util.UUID;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.EquinePredicates;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.Attached;
@ -40,8 +39,8 @@ public class ShieldSpell extends AbstractSpell implements Attached, Thrown {
private final Map<UUID, Target> targets = new TreeMap<>();
protected ShieldSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected ShieldSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -3,7 +3,6 @@ package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.List;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.magic.Attached;
import com.minelittlepony.unicopia.ability.magic.Caster;
@ -23,8 +22,8 @@ import net.minecraft.util.math.Vec3d;
*/
public class SiphoningSpell extends AbstractSpell implements Attached {
protected SiphoningSpell(SpellType<?> type, Affinity affinity) {
super(type, affinity);
protected SiphoningSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -10,7 +10,9 @@ 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;
@ -21,7 +23,7 @@ import net.minecraft.util.Util;
public class SpellType<T extends Spell> implements Affine {
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, a) -> null);
public static final SpellType<?> EMPTY_KEY = new SpellType<>(EMPTY_ID, Affinity.NEUTRAL, 0xFFFFFF, false, t -> null);
private static final Map<Identifier, SpellType<?>> REGISTRY = new HashMap<>();
private static final Map<Affinity, Set<SpellType<?>>> BY_AFFINITY = new EnumMap<>(Affinity.class);
@ -30,10 +32,13 @@ public class SpellType<T extends Spell> implements Affine {
public static final SpellType<ScorchSpell> SCORCH = register("scorch", Affinity.BAD, 0, true, ScorchSpell::new);
public static final SpellType<FireSpell> FLAME = register("flame", Affinity.GOOD, 0xFF5D00, true, FireSpell::new);
public static final SpellType<InfernoSpell> INFERNAL = register("infernal", Affinity.BAD, 0xF00F00, true, InfernoSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.NEUTRAL, 0x66CDAA, true, ShieldSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.NEUTRAL, 0x4CDEE7, true, AttractiveSpell::new);
public static final SpellType<ShieldSpell> SHIELD = register("shield", Affinity.GOOD, 0x66CDAA, true, ShieldSpell::new);
public static final SpellType<ShieldSpell> REPULSE = register("repulse", Affinity.BAD, 0x66CDAA, true, ShieldSpell::new);
public static final SpellType<AttractiveSpell> VORTEX = register("vortex", Affinity.GOOD, 0x4CDEE7, true, AttractiveSpell::new);
public static final SpellType<AttractiveSpell> SUFFER = register("suffer", Affinity.BAD, 0x4CDEE7, true, AttractiveSpell::new);
public static final SpellType<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0x3A3A3A, true, NecromancySpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.NEUTRAL, 0xe308ab, true, SiphoningSpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphoning", Affinity.GOOD, 0xe308ab, true, SiphoningSpell::new);
public static final SpellType<SiphoningSpell> DRAINING = register("draining", Affinity.BAD, 0xe308ab, true, SiphoningSpell::new);
public static final SpellType<DisguiseSpell> DISGUISE = register("disguise", Affinity.BAD, 0x19E48E, false, DisguiseSpell::new);
public static final SpellType<RevealingSpell> REVEALING = register("reveal", Affinity.GOOD, 0x5CE81F, true, RevealingSpell::new);
public static final SpellType<JoustingSpell> JOUSTING = register("joust", Affinity.GOOD, 0xBDBDF9, false, JoustingSpell::new);
@ -46,7 +51,11 @@ public class SpellType<T extends Spell> implements Affine {
private final Factory<T> factory;
private final Map<Affinity, String> translationKeys = new EnumMap<>(Affinity.class);
private final boolean thrown;
private final boolean attached;
@Nullable
private String translationKey;
SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Factory<T> factory) {
this.id = id;
@ -54,12 +63,24 @@ public class SpellType<T extends Spell> implements Affine {
this.color = color;
this.obtainable = obtainable;
this.factory = factory;
Spell inst = create();
thrown = inst instanceof Thrown;
attached = inst instanceof Attached;
}
public boolean isObtainable() {
return obtainable;
}
public boolean mayThrow() {
return thrown;
}
public boolean mayAttach() {
return attached;
}
public Identifier getId() {
return id;
}
@ -76,23 +97,21 @@ public class SpellType<T extends Spell> implements Affine {
return affinity;
}
public String getTranslationKey(Affinity affinity) {
return translationKeys.computeIfAbsent(affinity, a -> Util.createTranslationKey(a.getTranslationKey(), getId()));
public String getTranslationKey() {
if (translationKey == null) {
translationKey = Util.createTranslationKey(getAffinity().getTranslationKey(), getId());
}
return translationKey;
}
public Text getName(Affinity affinity) {
return new TranslatableText(getTranslationKey(affinity));
public Text getName() {
return new TranslatableText(getTranslationKey());
}
@Nullable
public T create() {
return create(getAffinity());
}
@Nullable
public T create(Affinity affinity) {
try {
return factory.create(this, affinity);
return factory.create(this);
} catch (Exception e) {
e.printStackTrace();
}
@ -102,11 +121,7 @@ public class SpellType<T extends Spell> implements Affine {
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);
for (Affinity i : affinity.getImplicators()) {
byAffinity(i).add(type);
}
byAffinity(affinity).add(type);
REGISTRY.put(id, type);
return type;
}
@ -153,6 +168,6 @@ public class SpellType<T extends Spell> implements Affine {
}
public interface Factory<T extends Spell> {
T create(SpellType<T> type, Affinity affinity);
T create(SpellType<T> type);
}
}

View file

@ -93,7 +93,7 @@ public interface URenderers {
});
FabricModelPredicateProviderRegistry.register(UItems.GEMSTONE, new Identifier("affinity"), (stack, world, entity) -> {
return GemstoneItem.isEnchanted(stack) ? 1 + GemstoneItem.getAffinity(stack).ordinal() : 0;
return GemstoneItem.isEnchanted(stack) ? 1 + GemstoneItem.getSpellKey(stack).getAffinity().ordinal() : 0;
});
ColorProviderRegistry.ITEM.register((stack, i) -> {
return i > 0 || !GemstoneItem.isEnchanted(stack) ? -1 : GemstoneItem.getSpellKey(stack).getColor();

View file

@ -37,9 +37,8 @@ public class GemstoneItem extends Item {
if (isEnchanted(stack)) {
SpellType<?> key = getSpellKey(stack);
Affinity affinity = getAffinity(stack);
MutableText line = new TranslatableText(key.getTranslationKey(affinity) + ".lore").formatted(affinity.getColor());
MutableText line = new TranslatableText(key.getTranslationKey() + ".lore").formatted(key.getAffinity().getColor());
if (!Unicopia.SIDE.getPlayerSpecies().canCast()) {
line = line.formatted(Formatting.OBFUSCATED);
@ -75,26 +74,24 @@ public class GemstoneItem extends Item {
return new TranslatableText(getTranslationKey(stack) + ".obfuscated");
}
return new TranslatableText(getTranslationKey(stack) + ".enchanted", getSpellKey(stack).getName(getAffinity(stack)));
return new TranslatableText(getTranslationKey(stack) + ".enchanted", getSpellKey(stack).getName());
}
return super.getName();
}
public static TypedActionResult<Spell> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable SpellType<?> exclude, Predicate<Spell> test) {
public static TypedActionResult<SpellType<?>> consumeSpell(ItemStack stack, PlayerEntity player, @Nullable SpellType<?> exclude, Predicate<SpellType<?>> test) {
if (!isEnchanted(stack)) {
return TypedActionResult.pass(null);
}
SpellType<Spell> key = GemstoneItem.getSpellKey(stack);
SpellType<Spell> key = getSpellKey(stack);
if (Objects.equals(key, exclude)) {
return TypedActionResult.fail(null);
}
Spell spell = key.create(getAffinity(stack));
if (spell == null || !test.test(spell)) {
if (key == SpellType.EMPTY_KEY || !test.test(key)) {
return TypedActionResult.fail(null);
}
@ -102,13 +99,13 @@ public class GemstoneItem extends Item {
player.swingHand(player.getStackInHand(Hand.OFF_HAND) == stack ? Hand.OFF_HAND : Hand.MAIN_HAND);
if (stack.getCount() == 1) {
GemstoneItem.unenchanted(stack);
unenchanted(stack);
} else {
player.giveItemStack(GemstoneItem.unenchanted(stack.split(1)));
player.giveItemStack(unenchanted(stack.split(1)));
}
}
return TypedActionResult.consume(spell);
return TypedActionResult.consume(key);
}
public static boolean isEnchanted(ItemStack stack) {
@ -121,26 +118,15 @@ public class GemstoneItem extends Item {
public static ItemStack enchanted(ItemStack stack, SpellType<?> type, Affinity affinity) {
stack.getOrCreateTag().putString("spell", type.getId().toString());
stack.getOrCreateTag().putInt("affinity", affinity.ordinal());
return stack;
}
public static ItemStack unenchanted(ItemStack stack) {
stack.removeSubTag("spell");
stack.removeSubTag("affinity");
return stack;
}
public static <T extends Spell> SpellType<T> getSpellKey(ItemStack stack) {
return SpellType.getKey(isEnchanted(stack) ? new Identifier(stack.getTag().getString("spell")) : SpellType.EMPTY_ID);
}
public static Affinity getAffinity(ItemStack stack) {
Affinity fallback = getSpellKey(stack).getAffinity();
if (stack.hasTag() && stack.getTag().contains("affinity")) {
return Affinity.of(stack.getTag().getInt("affinity"), fallback);
}
return fallback;
}
}

View file

@ -108,14 +108,14 @@
"spell.unicopia.shield": "Protective",
"spell.unicopia.shield.lore": "Protection I",
"curse.unicopia.shield": "Repulsive",
"curse.unicopia.shield.lore": "Hostility I",
"curse.unicopia.repulse": "Repulsive",
"curse.unicopia.repulse.lore": "Hostility I",
"spell.unicopia.vortex": "Attractive",
"spell.unicopia.vortex.lore": "Containment I",
"curse.unicopia.vortex": "Suffering",
"curse.unicopia.vortex.lore": "Torture I",
"curse.unicopia.suffer": "Suffering",
"curse.unicopia.suffer.lore": "Torture I",
"curse.unicopia.necromancy": "Resurrection",
"curse.unicopia.necromancy.lore": "Resurrection I",
@ -123,8 +123,8 @@
"spell.unicopia.siphoning": "Siphoning",
"spell.unicopia.siphoning.lore": "Energy II",
"curse.unicopia.siphoning": "Siphoning",
"curse.unicopia.siphoning.lore": "Energy III",
"curse.unicopia.draining": "Siphoning",
"curse.unicopia.draining.lore": "Energy III",
"spell.unicopia.reveal": "Revealing",
"spell.unicopia.reveal.lore": "Discovery II",