Spells can now be obtained via gemstones

This commit is contained in:
Sollace 2021-03-01 12:56:30 +02:00
parent 782bb86f0c
commit b9679a3706
34 changed files with 406 additions and 520 deletions

View file

@ -5,6 +5,7 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.DisguiseSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.behaviour.Disguise;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.UParticles;
@ -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(() -> {
DisguiseSpell disc = new DisguiseSpell();
DisguiseSpell disc = SpellType.DISGUISE.create();
iplayer.setSpell(disc);
return disc;

View file

@ -4,7 +4,7 @@ import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.spell.JoustingSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import com.minelittlepony.unicopia.particle.OrientedBillboardParticleEffect;
@ -67,7 +67,7 @@ public class PegasusRainboomAbility implements Ability<Hit> {
if (player.getPhysics().isFlying() && !player.hasSpell()) {
player.getMagicalReserves().getMana().multiply(0.1F);
player.addParticle(new OrientedBillboardParticleEffect(UParticles.RAINBOOM_RING, player.getPhysics().getMotionAngle()), player.getOriginVector(), Vec3d.ZERO);
player.setSpell(new JoustingSpell());
player.setSpell(SpellType.JOUSTING.create());
}
}

View file

@ -10,10 +10,10 @@ 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.ShieldSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.AmuletItem;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import net.minecraft.item.ItemStack;
@ -79,7 +79,7 @@ public class UnicornCastingAbility implements Ability<Hit> {
Optional<Spell> newSpell = getNewSpell(player);
@Nullable
Spell spell = player.hasSpell() ? newSpell.orElse(null) : newSpell.orElseGet(ShieldSpell::new);
Spell spell = player.hasSpell() ? newSpell.orElse(null) : newSpell.orElseGet(SpellType.SHIELD::create);
player.subtractEnergyCost(spell == null ? 2 : 4);
player.setSpell(spell);
@ -104,11 +104,11 @@ public class UnicornCastingAbility implements Ability<Hit> {
}
private Optional<Spell> getNewSpell(Pony player) {
final String current = player.hasSpell() ? player.getSpell(true).getName() : null;
final SpellType<?> current = player.hasSpell() ? player.getSpell(true).getType() : null;
return Streams.stream(player.getMaster().getItemsHand())
.map(SpellRegistry::getKeyFromStack)
.map(GemstoneItem::getSpellKey)
.filter(i -> !Objects.equals(i, current))
.map(SpellRegistry.instance()::getSpellFromName)
.map(SpellType::create)
.filter(Objects::nonNull)
.findFirst();
}

View file

@ -6,9 +6,9 @@ import com.google.common.collect.Streams;
import com.minelittlepony.unicopia.Race;
import com.minelittlepony.unicopia.ability.data.Hit;
import com.minelittlepony.unicopia.ability.magic.Thrown;
import com.minelittlepony.unicopia.ability.magic.spell.AttractiveSpell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.player.Pony;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
import net.minecraft.util.Identifier;
@ -61,12 +61,13 @@ public class UnicornProjectileAbility implements Ability<Hit> {
@Override
public void apply(Pony player, Hit data) {
player.subtractEnergyCost(getCostEstimate(player));
getThrown(player).orElseGet(AttractiveSpell::new).toss(player);
getThrown(player).orElseGet(SpellType.VORTEX::create).toss(player);
}
private Optional<Thrown> getThrown(Pony player) {
return Streams.stream(player.getMaster().getItemsHand())
.map(SpellRegistry.instance()::getSpellFrom)
.map(GemstoneItem::getSpellKey)
.map(SpellType::create)
.filter(i -> i != null && i instanceof Thrown)
.map(Thrown.class::cast)
.findFirst();

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.ability.magic;
import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.util.NbtSerialisable;
import net.minecraft.entity.projectile.ProjectileEntity;
@ -11,14 +12,9 @@ import net.minecraft.entity.projectile.ProjectileEntity;
public interface Spell extends NbtSerialisable, Affine {
/**
* Gets the name used to identify this effect.
* Returns the registered type of this spell.
*/
String getName();
/**
* Gets the tint for this spell when applied to a gem.
*/
int getTint();
SpellType<?> getType();
/**
* Sets this effect as dead.
@ -87,6 +83,11 @@ public interface Spell extends NbtSerialisable, Affine {
*/
void render(Caster<?> source);
@Override
default Affinity getAffinity() {
return getType().getAffinity();
}
/**
* Return true to allow the gem update and move.
*/
@ -98,6 +99,6 @@ public interface Spell extends NbtSerialisable, Affine {
* Returns a new, deep-copied instance of this spell.
*/
default Spell copy() {
return SpellRegistry.instance().copyInstance(this);
return SpellType.copy(this);
}
}

View file

@ -2,17 +2,15 @@ package com.minelittlepony.unicopia.ability.magic;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.spell.SpellRegistry;
import com.minelittlepony.unicopia.item.GemstoneItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.projectile.MagicProjectileEntity;
import com.minelittlepony.unicopia.projectile.ProjectileDelegate;
import net.minecraft.block.BlockState;
import net.minecraft.entity.Entity;
import net.minecraft.entity.LivingEntity;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.sound.SoundCategory;
import net.minecraft.sound.SoundEvent;
import net.minecraft.sound.SoundEvents;
@ -54,9 +52,7 @@ public interface Thrown extends Spell, ProjectileDelegate {
* Gets the appearance to be used when projecting this spell.
*/
default ItemStack getCastAppearance(Caster<?> caster) {
Item item = getAffinity() == Affinity.BAD ? Items.MAGMA_CREAM : Items.SNOWBALL;
return SpellRegistry.instance().enchantStack(new ItemStack(item), getName());
return GemstoneItem.enchanted(UItems.GEMSTONE.getDefaultStack(), getType());
}
/**

View file

@ -5,6 +5,10 @@ import com.minelittlepony.unicopia.ability.magic.Caster;
public abstract class AbstractRangedAreaSpell extends AbstractSpell implements Attached {
protected AbstractRangedAreaSpell(SpellType<?> type) {
super(type);
}
@Override
public int getMaxLevelCutOff(Caster<?> source) {
return 17;

View file

@ -10,6 +10,17 @@ public abstract class AbstractSpell implements Spell {
protected boolean isDead;
protected boolean isDirty;
private final SpellType<?> type;
protected AbstractSpell(SpellType<?> type) {
this.type = type;
}
@Override
public SpellType<?> getType() {
return type;
}
@Override
public void setDead() {
isDead = true;

View file

@ -26,19 +26,8 @@ public class AttractiveSpell extends ShieldSpell implements Thrown {
@Nullable
private BlockPos homingPos;
@Override
public String getName() {
return "vortex";
}
@Override
public int getTint() {
return 0x4CDEE7;
}
@Override
public Affinity getAffinity() {
return Affinity.NEUTRAL;
protected AttractiveSpell(SpellType<?> type) {
super(type);
}
@Override
@ -47,7 +36,7 @@ public class AttractiveSpell extends ShieldSpell implements Thrown {
Vec3d pos = source.getOriginVector();
source.spawnParticles(new Sphere(false, range), range * 9, p -> {
source.addParticle(new MagicParticleEffect(getTint()), p, p.subtract(pos));
source.addParticle(new MagicParticleEffect(getType().getColor()), p, p.subtract(pos));
});
}

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;
@ -20,19 +19,8 @@ import net.minecraft.util.registry.Registry;
public class AwkwardSpell extends AbstractSpell implements Thrown {
@Override
public String getName() {
return "awkward";
}
@Override
public Affinity getAffinity() {
return Affinity.NEUTRAL;
}
@Override
public int getTint() {
return 0xE1239C;
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,19 +31,8 @@ public class DisguiseSpell extends AbstractSpell implements Attached, Suppressab
private int suppressionCounter;
@Override
public String getName() {
return "disguise";
}
@Override
public Affinity getAffinity() {
return Affinity.BAD;
}
@Override
public int getTint() {
return 0x19E48E;
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.Caster;
import com.minelittlepony.unicopia.ability.magic.Magical;
@ -40,19 +39,8 @@ public class FireSpell extends AbstractRangedAreaSpell implements Thrown {
private static final Shape VISUAL_EFFECT_RANGE = new Sphere(false, 0.5);
private static final Shape EFFECT_RANGE = new Sphere(false, 4);
@Override
public String getName() {
return "fire";
}
@Override
public Affinity getAffinity() {
return Affinity.GOOD;
}
@Override
public int getTint() {
return 0xFF5D00;
protected FireSpell(SpellType<?> type) {
super(type);
}
@Override

View file

@ -1,38 +1,12 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.function.Supplier;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Caster;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.particle.MagicParticleEffect;
public class GenericSpell extends AbstractSpell {
private final String name;
private final int tint;
private final Affinity affinity;
static Supplier<Spell> factory(String name, int tint, Affinity affinity) {
return () -> new GenericSpell(name, tint, affinity);
}
public GenericSpell(String name, int tint, Affinity affinity) {
this.name = name;
this.tint = tint;
this.affinity = affinity;
}
@Override
public String getName() {
return name;
}
@Override
public int getTint() {
return tint;
protected GenericSpell(SpellType<?> type) {
super(type);
}
@Override
@ -42,11 +16,6 @@ public class GenericSpell extends AbstractSpell {
@Override
public void render(Caster<?> source) {
source.spawnParticles(new MagicParticleEffect(getTint()), 1);
}
@Override
public Affinity getAffinity() {
return affinity;
source.spawnParticles(new MagicParticleEffect(getType().getColor()), 1);
}
}

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.ability.magic.Thrown;
import com.minelittlepony.unicopia.block.state.StateMaps;
@ -29,19 +28,8 @@ public class IceSpell extends AbstractRangedAreaSpell implements Thrown {
private final int rad = 3;
private final Shape effect_range = new Sphere(false, rad);
@Override
public String getName() {
return "ice";
}
@Override
public Affinity getAffinity() {
return Affinity.GOOD;
}
@Override
public int getTint() {
return 0xBDBDF9;
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;
@ -16,19 +15,9 @@ import net.minecraft.util.math.Vec3d;
import net.minecraft.world.World;
public class InfernoSpell extends FireSpell {
@Override
public String getName() {
return "inferno";
}
@Override
public Affinity getAffinity() {
return Affinity.BAD;
}
@Override
public int getTint() {
return 0xF00F00;
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.Caster;
import com.minelittlepony.unicopia.ability.magic.Thrown;
@ -30,19 +29,9 @@ public class JoustingSpell extends AbstractRangedAreaSpell implements Thrown {
private int age;
@Override
public String getName() {
return "joust";
}
@Override
public Affinity getAffinity() {
return Affinity.GOOD;
}
@Override
public int getTint() {
return 0xBDBDF9;
protected JoustingSpell(SpellType<?> type) {
super(type);
// TODO Auto-generated constructor stub
}
@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.Caster;
import com.minelittlepony.unicopia.util.WorldEvent;
import com.minelittlepony.unicopia.util.shape.Shape;
@ -26,19 +25,8 @@ public class NecromancySpell extends AbstractRangedAreaSpell {
EntityType.ZOMBIFIED_PIGLIN
);
@Override
public String getName() {
return "necromancy";
}
@Override
public Affinity getAffinity() {
return Affinity.BAD;
}
@Override
public int getTint() {
return 0x3A3A3A;
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.Caster;
import com.minelittlepony.unicopia.ability.magic.Suppressable;
import com.minelittlepony.unicopia.ability.magic.Thrown;
@ -17,14 +16,8 @@ import net.minecraft.util.math.Vec3d;
*/
public class RevealingSpell extends AbstractSpell implements Thrown {
@Override
public String getName() {
return "reveal";
}
@Override
public int getTint() {
return 0x5CE81F;
protected RevealingSpell(SpellType<?> type) {
super(type);
}
@Override
@ -50,17 +43,11 @@ public class RevealingSpell extends AbstractSpell implements Thrown {
public void render(Caster<?> source) {
Shape area = new Sphere(false, 15);
MagicParticleEffect effect = new MagicParticleEffect(getTint());
MagicParticleEffect effect = new MagicParticleEffect(getType().getColor());
source.spawnParticles(area, 5, pos -> {
source.addParticle(effect, pos, Vec3d.ZERO);
});
source.spawnParticles(effect, 5);
}
@Override
public Affinity getAffinity() {
return Affinity.GOOD;
}
}

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,14 +16,8 @@ import net.minecraft.util.math.Vec3d;
public class ScorchSpell extends FireSpell {
@Override
public String getName() {
return "scorch";
}
@Override
public int getTint() {
return 0;
protected ScorchSpell(SpellType<?> type) {
super(type);
}
@Override
@ -50,12 +43,7 @@ public class ScorchSpell extends FireSpell {
public void render(Caster<?> source) {
source.addParticle(ParticleTypes.END_ROD, source.getOriginVector(), Vec3d.ZERO);
source.spawnParticles(ParticleTypes.FLAME, 3);
source.spawnParticles(new MagicParticleEffect(getTint()), 3);
}
@Override
public Affinity getAffinity() {
return Affinity.BAD;
source.spawnParticles(new MagicParticleEffect(getType().getColor()), 3);
}
@Override

View file

@ -39,19 +39,8 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
private final Map<UUID, Target> targets = new TreeMap<>();
@Override
public String getName() {
return "shield";
}
@Override
public Affinity getAffinity() {
return Affinity.NEUTRAL;
}
@Override
public int getTint() {
return 0x66CDAA;
protected ShieldSpell(SpellType<?> type) {
super(type);
}
@Override
@ -65,15 +54,15 @@ public class ShieldSpell extends AbstractRangedAreaSpell implements Attached {
float radius = (float)getDrawDropOffRange(source);
source.spawnParticles(new Sphere(true, radius), (int)(radius * 6), pos -> {
source.addParticle(new MagicParticleEffect(getTint()), pos, Vec3d.ZERO);
source.addParticle(new MagicParticleEffect(getType().getColor()), pos, Vec3d.ZERO);
});
particlEffect.ifAbsent(source, spawner -> {
spawner.addParticle(new SphereParticleEffect(getTint(), 0.3F, radius), source.getOriginVector(), Vec3d.ZERO);
spawner.addParticle(new SphereParticleEffect(getType().getColor(), 0.3F, radius), source.getOriginVector(), Vec3d.ZERO);
}).ifPresent(p -> {
p.attach(source);
p.setAttribute(0, radius);
p.setAttribute(1, getTint());
p.setAttribute(1, getType().getColor());
});
}

View file

@ -23,14 +23,8 @@ import net.minecraft.util.math.Vec3d;
*/
public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
@Override
public String getName() {
return "siphon";
}
@Override
public int getTint() {
return 0xe308ab;
protected SiphoningSpell(SpellType<?> type) {
super(type);
}
@Override
@ -126,9 +120,4 @@ public class SiphoningSpell extends AbstractRangedAreaSpell implements Thrown {
}
});
}
@Override
public Affinity getAffinity() {
return Affinity.NEUTRAL;
}
}

View file

@ -1,178 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Supplier;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import javax.annotation.concurrent.Immutable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Spell;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.CompoundTag;
public class SpellRegistry {
private static final SpellRegistry instance = new SpellRegistry();
public static SpellRegistry instance() {
return instance;
}
private final Map<String, Entry<?>> entries = new HashMap<>();
private final Map<Affinity, Set<String>> keysByAffinity = new HashMap<>();
private SpellRegistry() {
register(ShieldSpell::new);
register(FireSpell::new);
register(AttractiveSpell::new);
register(NecromancySpell::new);
register(SiphoningSpell::new);
register(IceSpell::new);
register(AwkwardSpell::new);
register(InfernoSpell::new);
register(RevealingSpell::new);
register(ScorchSpell::new);
register(DisguiseSpell::new);
register(JoustingSpell::new);
}
@Nullable
public Spell getSpellFromName(String name) {
if (entries.containsKey(name)) {
return entries.get(name).create();
}
return null;
}
@SuppressWarnings("unchecked")
public <T extends Spell> T copyInstance(T effect) {
return (T)createEffectFromNBT(toNBT(effect));
}
@Nullable
public Spell createEffectFromNBT(CompoundTag compound) {
if (compound.contains("effect_id")) {
Spell effect = getSpellFromName(compound.getString("effect_id"));
if (effect != null) {
effect.fromNBT(compound);
}
return effect;
}
return null;
}
public static CompoundTag toNBT(Spell effect) {
CompoundTag compound = effect.toNBT();
compound.putString("effect_id", effect.getName());
return compound;
}
@Nullable
public Spell getSpellFrom(ItemStack stack) {
return getSpellFromName(getKeyFromStack(stack));
}
public <T extends Spell> void register(Supplier<T> factory) {
try {
new Entry<>(factory);
} catch (Exception e) {
e.printStackTrace();
}
}
public ItemStack disenchantStack(ItemStack stack) {
if (stackHasEnchantment(stack)) {
stack.getTag().remove("spell");
if (stack.getTag().isEmpty()) {
stack.setTag(null);
}
}
return stack;
}
public ItemStack enchantStack(ItemStack stack, ItemStack from) {
return enchantStack(stack, getKeyFromStack(from));
}
public ItemStack enchantStack(ItemStack stack, String name) {
stack.getOrCreateTag().putString("spell", name);
return stack;
}
public static boolean stackHasEnchantment(ItemStack stack) {
return !stack.isEmpty() && stack.hasTag() && stack.getTag().contains("spell");
}
@Nonnull
public static String getKeyFromStack(ItemStack stack) {
if (stackHasEnchantment(stack)) {
return stack.getTag().getString("spell");
}
return "";
}
public int getSpellTintFromStack(ItemStack stack) {
return getSpellTint(getKeyFromStack(stack));
}
public int getSpellTint(String key) {
if (entries.containsKey(key)) {
return entries.get(key).color;
}
return 0xffffff;
}
public Set<String> getAllNames(Affinity affinity) {
return keysByAffinity.get(affinity);
}
@Immutable
class Entry<T extends Spell> {
final Supplier<T> factory;
final int color;
final Affinity affinity;
Entry(Supplier<T> factory) throws Exception {
T inst = factory.get();
this.factory = factory;
this.color = inst.getTint();
this.affinity = inst.getAffinity();
for (Affinity affinity : affinity.getImplicators()) {
keysByAffinity.computeIfAbsent(affinity, a -> new HashSet<>()).add(inst.getName());
}
entries.put(inst.getName(), this);
}
T create() {
try {
return factory.get();
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
}
}

View file

@ -0,0 +1,151 @@
package com.minelittlepony.unicopia.ability.magic.spell;
import java.util.EnumMap;
import java.util.HashMap;
import java.util.HashSet;
import java.util.Map;
import java.util.Set;
import java.util.function.Function;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Spell;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
public class SpellType<T extends Spell> {
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);
private static final Map<Identifier, SpellType<?>> REGISTRY = new HashMap<>();
private static final Map<Affinity, Set<SpellType<?>>> BY_AFFINITY = new EnumMap<>(Affinity.class);
public static final SpellType<IceSpell> ICE = register("ice", Affinity.GOOD, 0xBDBDF9, true, IceSpell::new);
public static final SpellType<FireSpell> FIRE = register("fire", Affinity.GOOD, 0xFF5D00, true, FireSpell::new);
public static final SpellType<InfernoSpell> INFERNO = register("inferno", Affinity.BAD, 0xF00F00, true, InfernoSpell::new);
public static final SpellType<ScorchSpell> SCORCH = register("scorch", Affinity.BAD, 0, true, ScorchSpell::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<NecromancySpell> NECROMANCY = register("necromancy", Affinity.BAD, 0x3A3A3A, true, NecromancySpell::new);
public static final SpellType<SiphoningSpell> SIPHONING = register("siphon", Affinity.NEUTRAL, 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);
public static final SpellType<AwkwardSpell> AWKWARD = register("awkward", Affinity.NEUTRAL, 0xE1239C, true, AwkwardSpell::new);
final Identifier id;
final Affinity affinity;
final int color;
final boolean obtainable;
final Function<SpellType<?>, T> factory;
@Nullable
private String translationKey;
SpellType(Identifier id, Affinity affinity, int color, boolean obtainable, Function<SpellType<?>, T> factory) {
this.id = id;
this.affinity = affinity;
this.color = color;
this.obtainable = obtainable;
this.factory = factory;
}
public boolean isObtainable() {
return obtainable;
}
public Identifier getId() {
return id;
}
/**
* Gets the tint for this spell when applied to a gem.
*/
public int getColor() {
return color;
}
public Affinity getAffinity() {
return affinity;
}
public String getTranslationKey() {
if (translationKey == null) {
translationKey = Util.createTranslationKey("spell", getId());
}
return translationKey;
}
public Text getName() {
return new TranslatableText(getTranslationKey());
}
@Nullable
public T create() {
try {
return factory.apply(this);
} catch (Exception e) {
e.printStackTrace();
}
return null;
}
public static <T extends Spell> SpellType<T> register(Identifier id, Affinity affinity, int color, boolean obtainable, Function<SpellType<?>, T> factory) {
SpellType<T> type = new SpellType<>(id, affinity, color, obtainable, factory);
for (Affinity i : affinity.getImplicators()) {
BY_AFFINITY.computeIfAbsent(i, a -> new HashSet<>()).add(type);
}
REGISTRY.put(id, type);
return type;
}
public static <T extends Spell> SpellType<T> register(String name, Affinity affinity, int color, boolean obtainable, Function<SpellType<?>, T> factory) {
return register(new Identifier("unicopia", name), affinity, color, obtainable, factory);
}
@SuppressWarnings("unchecked")
public static <T extends Spell> SpellType<T> getKey(Identifier id) {
return (SpellType<T>)REGISTRY.getOrDefault(id, EMPTY_KEY);
}
public static Set<SpellType<?>> byAffinity(Affinity affinity) {
return BY_AFFINITY.get(affinity);
}
@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")) {
Spell effect = getKey(new Identifier(compound.getString("effect_id"))).create();
if (effect != null) {
effect.fromNBT(compound);
}
return effect;
}
return null;
}
public static CompoundTag toNBT(Spell effect) {
CompoundTag compound = effect.toNBT();
compound.putString("effect_id", effect.getType().getId().toString());
return compound;
}
}

View file

@ -1 +0,0 @@
package com.minelittlepony.unicopia.ability.magic.spell;

View file

@ -3,6 +3,7 @@ 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;
import com.mojang.brigadier.builder.LiteralArgumentBuilder;
@ -63,7 +64,7 @@ public class DisguiseCommand {
DisguiseSpell effect = iplayer.getSpell(DisguiseSpell.class, true);
if (effect == null) {
iplayer.setSpell(new DisguiseSpell().setDisguise(entity));
iplayer.setSpell(SpellType.DISGUISE.create().setDisguise(entity));
} else {
effect.setDisguise(entity);
}

View file

@ -5,7 +5,7 @@ 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.SpellRegistry;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.ai.BreakHeartGoal;
import com.minelittlepony.unicopia.entity.ai.DynamicTargetGoal;
import com.minelittlepony.unicopia.entity.ai.WantItTakeItGoal;
@ -92,7 +92,7 @@ public class Creature extends Living<LivingEntity> {
Spell effect = getSpell(true);
if (effect != null) {
compound.put("effect", SpellRegistry.toNBT(effect));
compound.put("effect", SpellType.toNBT(effect));
}
physics.toNBT(compound);
}
@ -101,7 +101,7 @@ public class Creature extends Living<LivingEntity> {
public void fromNBT(CompoundTag compound) {
super.fromNBT(compound);
if (compound.contains("effect")) {
setSpell(SpellRegistry.instance().createEffectFromNBT(compound.getCompound("effect")));
setSpell(SpellType.fromNBT(compound.getCompound("effect")));
}
physics.fromNBT(compound);
}

View file

@ -15,7 +15,7 @@ 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.SpellRegistry;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.entity.PonyContainer;
import com.minelittlepony.unicopia.entity.Living;
@ -387,7 +387,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
Spell effect = getSpell(true);
if (effect != null) {
compound.put("effect", SpellRegistry.toNBT(effect));
compound.put("effect", SpellType.toNBT(effect));
}
}
@ -403,7 +403,7 @@ public class Pony extends Living<PlayerEntity> implements Transmittable, Copieab
magicExhaustion = compound.getFloat("magicExhaustion");
if (compound.contains("effect")) {
getPrimarySpellSlot().set(SpellRegistry.instance().createEffectFromNBT(compound.getCompound("effect")));
getPrimarySpellSlot().set(SpellType.fromNBT(compound.getCompound("effect")));
}
}

View file

@ -0,0 +1,79 @@
package com.minelittlepony.unicopia.item;
import java.util.List;
import javax.annotation.Nullable;
import com.minelittlepony.unicopia.Affinity;
import com.minelittlepony.unicopia.ability.magic.Spell;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import net.minecraft.client.item.TooltipContext;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.text.TranslatableText;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.world.World;
public class GemstoneItem extends Item {
public GemstoneItem(Settings settings) {
super(settings);
}
@Override
public void appendTooltip(ItemStack stack, @Nullable World world, List<Text> list, TooltipContext tooltipContext) {
}
@Override
public void appendStacks(ItemGroup tab, DefaultedList<ItemStack> items) {
super.appendStacks(tab, items);
if (isIn(tab)) {
SpellType.byAffinity(Affinity.GOOD).forEach(type -> {
if (type.isObtainable()) {
items.add(enchanted(getDefaultStack(), type));
}
});
}
}
@Override
public String getTranslationKey(ItemStack stack) {
return getTranslationKey();
}
@Override
public Text getName(ItemStack stack) {
if (isEnchanted(stack)) {
return new TranslatableText(getTranslationKey(stack) + ".enchanted", getSpellKey(stack).getName());
}
return super.getName();
}
public static boolean isEnchanted(ItemStack stack) {
return !stack.isEmpty() && stack.hasTag() && stack.getTag().contains("spell");
}
public static ItemStack enchanted(ItemStack stack, SpellType<?> type) {
stack.getOrCreateTag().putString("spell", type.getId().toString());
return stack;
}
public ItemStack unenchanted(ItemStack stack) {
if (isEnchanted(stack)) {
stack.getTag().remove("spell");
if (stack.getTag().isEmpty()) {
stack.setTag(null);
}
}
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);
}
}

View file

@ -0,0 +1,34 @@
package com.minelittlepony.unicopia.item;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.item.toxin.ToxicHolder;
import net.minecraft.item.Item;
import net.minecraft.item.ItemGroup;
import net.minecraft.item.ItemStack;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.minecraft.util.Identifier;
import net.minecraft.util.collection.DefaultedList;
import net.minecraft.util.registry.Registry;
public interface UItemGroups {
ItemGroup ALL_ITEMS = FabricItemGroupBuilder.create(new Identifier("unicopia", "items")).appendItems(list -> {
list.addAll(VanillaOverrides.REGISTRY.stream().map(Item::getDefaultStack).collect(Collectors.toList()));
DefaultedList<ItemStack> defs = DefaultedList.of();
UItems.ITEMS.stream()
.filter(item -> !(item instanceof ChameleonItem) || ((ChameleonItem)item).isFullyDisguised())
.forEach(item -> {
item.appendStacks(ItemGroup.SEARCH, defs);
});
list.addAll(defs);
}).icon(UItems.EMPTY_JAR::getDefaultStack).build();
ItemGroup HORSE_FEED = FabricItemGroupBuilder.create(new Identifier("unicopia", "horsefeed")).appendItems(list -> {
list.addAll(Registry.ITEM.stream()
.filter(item -> item instanceof ToxicHolder && ((ToxicHolder)item).getToxic().isPresent())
.map(Item::getDefaultStack)
.collect(Collectors.toList()));
}).icon(UItems.ZAP_APPLE::getDefaultStack).build();
static void bootstrap() {}
}

View file

@ -2,18 +2,14 @@ package com.minelittlepony.unicopia.item;
import java.util.ArrayList;
import java.util.List;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.item.enchantment.UEnchantments;
import com.minelittlepony.unicopia.item.toxin.ToxicHolder;
import com.minelittlepony.unicopia.item.toxin.Toxics;
import com.minelittlepony.unicopia.item.toxin.UFoodComponents;
import net.minecraft.item.Item;
import net.minecraft.item.Item.Settings;
import net.minecraft.item.ItemGroup;
import net.fabricmc.fabric.api.client.itemgroup.FabricItemGroupBuilder;
import net.fabricmc.fabric.api.item.v1.FabricItemSettings;
import net.minecraft.item.BlockItem;
import net.minecraft.item.FoodComponents;
@ -57,7 +53,7 @@ public interface UItems {
Item CRYSTAL_HEART = register("crystal_heart", new CrystalHeartItem(new Item.Settings().group(ItemGroup.DECORATIONS).maxCount(1)));
Item CRYSTAL_SHARD = register("crystal_shard", new Item(new Item.Settings().group(ItemGroup.MATERIALS)));
Item GEMSTONE = register("gemstone", new Item(new Item.Settings().group(ItemGroup.MATERIALS)));
Item GEMSTONE = register("gemstone", new GemstoneItem(new Item.Settings().group(ItemGroup.MATERIALS)));
Item PEGASUS_FEATHER = register("pegasus_feather", new Item(new Item.Settings().group(ItemGroup.MATERIALS)));
Item GRYPHON_FEATHER = register("gryphon_feather", new Item(new Item.Settings().group(ItemGroup.MATERIALS)));
@ -94,21 +90,6 @@ public interface UItems {
Toxics.bootstrap();
UEnchantments.bootstrap();
URecipes.bootstrap();
FabricItemGroupBuilder.create(new Identifier("unicopia", "items")).appendItems(list -> {
list.addAll(VanillaOverrides.REGISTRY.stream().map(Item::getDefaultStack).collect(Collectors.toList()));
list.addAll(ITEMS.stream()
.filter(item -> !(item instanceof ChameleonItem) || ((ChameleonItem)item).isFullyDisguised())
.map(Item::getDefaultStack)
.collect(Collectors.toList())
);
}).icon(EMPTY_JAR::getDefaultStack).build();
FabricItemGroupBuilder.create(new Identifier("unicopia", "horsefeed")).appendItems(list -> {
list.addAll(Registry.ITEM.stream()
.filter(item -> item instanceof ToxicHolder && ((ToxicHolder)item).getToxic().isPresent())
.map(Item::getDefaultStack)
.collect(Collectors.toList()));
}).icon(ZAP_APPLE::getDefaultStack).build();
UItemGroups.bootstrap();
}
}

View file

@ -6,10 +6,11 @@ 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.SpellRegistry;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import net.minecraft.entity.data.TrackedData;
import net.minecraft.nbt.CompoundTag;
import net.minecraft.util.Identifier;
/**
* Synchronisation class for spell effects.
@ -35,30 +36,6 @@ public class EffectSync {
this.param = param;
}
public boolean has() {
CompoundTag comp = owned.getEntity().getDataTracker().get(param);
if (comp == null || !comp.contains("effect_id")) {
if (effect != null) {
effect.setDead();
effect = null;
}
} else {
String id = comp.getString("effect_id");
if (effect == null || !effect.getName().contentEquals(id)) {
if (effect != null) {
effect.setDead();
}
effect = SpellRegistry.instance().createEffectFromNBT(comp);
} else if (!owned.getEntity().world.isClient() && effect.isDirty()) {
set(effect);
}
}
return effect != null;
}
public <T extends Spell> Optional<T> getOrEmpty(Class<T> type, boolean update) {
T effect = get(type, update);
@ -72,7 +49,7 @@ public class EffectSync {
@SuppressWarnings("unchecked")
public <E extends Spell> E get(Class<E> type, boolean update) {
if (update) {
sync();
sync(true);
}
if (effect == null || type == null || type.isAssignableFrom(effect.getClass())) {
@ -82,7 +59,12 @@ public class EffectSync {
return null;
}
private void sync() {
public boolean has() {
sync(false);
return effect != null;
}
private void sync(boolean force) {
CompoundTag comp = owned.getEntity().getDataTracker().get(param);
if (comp == null || !comp.contains("effect_id")) {
@ -92,19 +74,17 @@ public class EffectSync {
}
return;
} else {
String id = comp.getString("effect_id");
if (effect == null || !effect.getName().contentEquals(id)) {
if (effect == null || !effect.getType().getId().equals(new Identifier(comp.getString("effect_id")))) {
if (effect != null) {
effect.setDead();
}
effect = SpellRegistry.instance().createEffectFromNBT(comp);
effect = SpellType.fromNBT(comp);
} else if (owned.getEntity().world.isClient()) {
if (lastValue != comp || !(comp == null || comp.equals(lastValue))) {
lastValue = comp;
effect.fromNBT(comp);
}
} else if (effect.isDirty()) {
} else if ((force || !owned.getEntity().world.isClient()) && effect.isDirty()) {
set(effect);
}
}
@ -119,7 +99,7 @@ public class EffectSync {
if (effect == null) {
owned.getEntity().getDataTracker().set(param, new CompoundTag());
} else {
owned.getEntity().getDataTracker().set(param, SpellRegistry.toNBT(effect));
owned.getEntity().getDataTracker().set(param, SpellType.toNBT(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.SpellType;
import com.minelittlepony.unicopia.entity.Equine;
import net.fabricmc.api.EnvType;
@ -59,13 +60,13 @@ public class ParticleHandle {
public static final class Link {
private Optional<Caster<?>> caster = Optional.empty();
private String effect;
private SpellType<?> effect;
private boolean linked;
public void attach(Caster<?> caster) {
this.linked = true;
this.caster = Optional.of(caster);
this.effect = caster.getSpell(false).getName();
this.effect = caster.getSpell(false).getType();
}
public void detach() {
@ -83,7 +84,7 @@ public class ParticleHandle {
return Equine.of(e) == c
&& c.hasSpell()
&& c.getSpell(false).getName().equals(effect)
&& c.getSpell(false).getType().equals(effect)
&& e != null
&& c.getWorld().getEntityById(e.getEntityId()) != null;
});

View file

@ -6,7 +6,7 @@ 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.spell.SpellRegistry;
import com.minelittlepony.unicopia.ability.magic.spell.SpellType;
import com.minelittlepony.unicopia.entity.EntityPhysics;
import com.minelittlepony.unicopia.entity.Physics;
import com.minelittlepony.unicopia.item.UItems;
@ -220,7 +220,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
super.readCustomDataFromTag(compound);
physics.fromNBT(compound);
if (compound.contains("effect")) {
setSpell(SpellRegistry.instance().createEffectFromNBT(compound.getCompound("effect")));
setSpell(SpellType.fromNBT(compound.getCompound("effect")));
}
}
@ -230,7 +230,7 @@ public class MagicProjectileEntity extends ThrownItemEntity implements Magical,
physics.toNBT(compound);
if (hasSpell()) {
compound.put("effect", SpellRegistry.toNBT(getSpell(true)));
compound.put("effect", SpellType.toNBT(getSpell(true)));
}
}

View file

@ -31,6 +31,7 @@
"item.unicopia.crystal_heart": "Crystal Heart",
"item.unicopia.crystal_shard": "Crystal Shard",
"item.unicopia.gemstone": "Gemstone",
"item.unicopia.gemstone.enchanted": "%s Gem",
"item.unicopia.pegasus_feather": "Pegasus Feather",
"item.unicopia.gryphon_feather": "Gryphon Feather",
@ -95,62 +96,56 @@
"affinity.neutral": "Neutral",
"affinity.curse": "Corrupt",
"spell.shield": "Defense",
"spell.shield.tagline": "Protection I",
"spell.unicopia.shield": "Protective",
"spell.unicopia.shield.tagline": "Protection I",
"spell.awkward": "Awkwardness",
"spell.awkward.tagline": "*Derp*",
"spell.unicopia.awkward": "Unstable",
"spell.unicopia.awkward.tagline": "*Derp*",
"spell.portal": "Transportation",
"spell.portal.tagline": "Teleportation I",
"spell.unicopia.light": "Illumination",
"spell.unicopia.light.tagline": "Discovery I",
"spell.light": "Illumination",
"spell.light.tagline": "Discovery I",
"spell.unicopia.reveal": "Revealing",
"spell.unicopia.reveal.tagline": "Discovery II",
"spell.reveal": "Revealing",
"spell.reveal.tagline": "Discovery II",
"spell.unicopia.flame": "Burning",
"spell.unicopia.flame.tagline": "Fire I",
"spell.flame": "Burning",
"spell.flame.tagline": "Fire I",
"spell.unicopia.fire": "Flaming",
"spell.unicopia.fire.tagline": "Fire II",
"spell.fire": "Flame",
"spell.fire.tagline": "Fire II",
"spell.unicopia.vortex": "Retention",
"spell.unicopia.vortex.tagline": "Containment I",
"spell.vortex": "Retention",
"spell.vortex.tagline": "Containment I",
"spell.unicopia.siphon": "Siphoning",
"spell.unicopia.siphon.tagline": "Energy II",
"spell.charge": "Channeling",
"spell.charge.tagline": "Energy I",
"spell.unicopia.ice": "Frost",
"spell.unicopia.ice.tagline": "Ice I",
"spell.siphon": "Pilon",
"spell.siphon.tagline": "Energy II",
"curse.unicopia.shield": "Repulsive",
"curse.unicopia.shield.tagline": "Hostility I",
"spell.ice": "Frost",
"spell.ice.tagline": "Ice I",
"curse.unicopia.darkness": "Dark",
"curse.unicopia.darkness.tagline": "Golomancy I/Resurrection II",
"curse.shield": "Repulsion",
"curse.shield.tagline": "Hostility I",
"curse.unicopia.harm": "Harmful",
"curse.unicopia.harm.tagline": "Hostility II",
"curse.darkness": "Darkness",
"curse.darkness.tagline": "Golomancy I/Resurrection II",
"curse.unicopia.awkward": "Awkward",
"curse.unicopia.awkward.tagline": "*Derp*",
"curse.harm": "Hurting",
"curse.harm.tagline": "Hostility II",
"curse.unicopia.vortex": "Suffering",
"curse.unicopia.vortex.tagline": "Torture I",
"curse.awkward": "Awkwardness",
"curse.awkward.tagline": "*Derp*",
"curse.unicopia.necromancy": "Resurrection",
"curse.unicopia.necromancy.tagline": "Resurrection I",
"curse.vortex": "Suffering",
"curse.vortex.tagline": "Torture I",
"curse.unicopia.inferno": "Inferno",
"curse.unicopia.inferno.tagline": "Fire III",
"curse.necromancy": "Necromancy",
"curse.necromancy.tagline": "Resurrection I",
"curse.inferno": "Inferno",
"curse.inferno.tagline": "Fire III",
"curse.siphon": "Siphoning",
"curse.siphon.tagline": "Energy III",
"curse.unicopia.siphon": "Siphoning",
"curse.unicopia.siphon.tagline": "Energy III",
"toxicity.safe.name": "Safe",
"toxicity.mild.name": "Mildly Toxic",