Added sticky keys option. Fixes #322

This commit is contained in:
Sollace 2024-04-08 22:13:08 +01:00
parent 4cb06bcc92
commit a8f0796ad7
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
9 changed files with 117 additions and 62 deletions

View file

@ -34,6 +34,9 @@ public class Config extends com.minelittlepony.common.util.settings.Config {
.addComment("If true Mine Little Pony will not be considered when determining the race to use") .addComment("If true Mine Little Pony will not be considered when determining the race to use")
.addComment("The result will always be what is set by this config file."); .addComment("The result will always be what is set by this config file.");
public final Setting<Boolean> toggleAbilityKeys = value("client", "toggleAbilityKeys", false)
.addComment("If true the ability keybinds will function as toggle keys rather than hold keys");
public final Setting<Integer> hudPage = value("client", "hudActivePage", 0) public final Setting<Integer> hudPage = value("client", "hudActivePage", 0)
.addComment("The page of abilities currently visible in the HUD. You can change this in-game using the PG_UP and PG_DWN keys (configurable)"); .addComment("The page of abilities currently visible in the HUD. You can change this in-game using the PG_UP and PG_DWN keys (configurable)");

View file

@ -73,7 +73,7 @@ public record Race (
.abilities(Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW) .abilities(Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW)
); );
public static final Race UNICORN = register("unicorn", new Builder().foraging().magic() public static final Race UNICORN = register("unicorn", new Builder().foraging().magic()
.abilities(Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL) .abilities(Abilities.TELEPORT, Abilities.CAST, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL)
); );
public static final Race PEGASUS = register("pegasus", new Builder().foraging().flight(FlightType.AVIAN).weatherMagic().cloudMagic() public static final Race PEGASUS = register("pegasus", new Builder().foraging().flight(FlightType.AVIAN).weatherMagic().cloudMagic()
.abilities(Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY) .abilities(Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY)
@ -83,7 +83,7 @@ public record Race (
); );
public static final Race ALICORN = register("alicorn", new Builder().foraging().availability(Availability.COMMANDS).flight(FlightType.AVIAN).earth().magic().weatherMagic().cloudMagic() public static final Race ALICORN = register("alicorn", new Builder().foraging().availability(Availability.COMMANDS).flight(FlightType.AVIAN).earth().magic().weatherMagic().cloudMagic()
.abilities( .abilities(
Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.SHOOT, Abilities.DISPELL, Abilities.TELEPORT, Abilities.GROUP_TELEPORT, Abilities.CAST, Abilities.SHOOT, Abilities.DISPELL,
Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY, Abilities.TOGGLE_FLIGHT, Abilities.RAINBOOM, Abilities.CAPTURE_CLOUD, Abilities.CARRY,
Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW, Abilities.HUG, Abilities.STOMP, Abilities.KICK, Abilities.GROW,
Abilities.TIME Abilities.TIME

View file

@ -18,7 +18,9 @@ import com.minelittlepony.unicopia.entity.player.Pony;
import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper; import net.fabricmc.fabric.api.client.keybinding.v1.KeyBindingHelper;
import net.minecraft.client.MinecraftClient; import net.minecraft.client.MinecraftClient;
import net.minecraft.client.option.KeyBinding; import net.minecraft.client.option.KeyBinding;
import net.minecraft.client.option.StickyKeyBinding;
import net.minecraft.client.sound.PositionedSoundInstance; import net.minecraft.client.sound.PositionedSoundInstance;
import net.minecraft.client.util.InputUtil;
import net.minecraft.text.Text; import net.minecraft.text.Text;
import net.minecraft.util.math.MathHelper; import net.minecraft.util.math.MathHelper;
@ -35,6 +37,10 @@ public class KeyBindingsHandler {
private final Binding pageDown = register(GLFW.GLFW_KEY_PAGE_DOWN, "hud_page_dn"); private final Binding pageDown = register(GLFW.GLFW_KEY_PAGE_DOWN, "hud_page_dn");
private final Binding pageUp = register(GLFW.GLFW_KEY_PAGE_UP, "hud_page_up"); private final Binding pageUp = register(GLFW.GLFW_KEY_PAGE_UP, "hud_page_up");
private final Binding singleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_tap");
private final Binding doubleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_double_tap");
private final Binding tripleTapModifier = register(InputUtil.UNKNOWN_KEY.getCode(), "ability_modifier_triple_tap");
private final Set<KeyBinding> pressed = new HashSet<>(); private final Set<KeyBinding> pressed = new HashSet<>();
public KeyBindingsHandler() { public KeyBindingsHandler() {
@ -47,8 +53,12 @@ public class KeyBindingsHandler {
return reverse.get(slot); return reverse.get(slot);
} }
public boolean isToggleMode() {
return Unicopia.getConfig().toggleAbilityKeys.get();
}
public void addKeybind(int code, AbilitySlot slot) { public void addKeybind(int code, AbilitySlot slot) {
Binding binding = register(code, slot.name().toLowerCase()); Binding binding = new Binding(KeyBindingHelper.registerKeyBinding(new StickyKeyBinding("key.unicopia." + slot.name().toLowerCase(), code, KEY_CATEGORY, this::isToggleMode)));
reverse.put(slot, binding); reverse.put(slot, binding);
keys.put(binding, slot); keys.put(binding, slot);
} }
@ -104,6 +114,7 @@ public class KeyBindingsHandler {
int page = Unicopia.getConfig().hudPage.get(); int page = Unicopia.getConfig().hudPage.get();
page += sigma; page += sigma;
Unicopia.getConfig().hudPage.set(page); Unicopia.getConfig().hudPage.set(page);
Unicopia.getConfig().save();
client.getSoundManager().play(PositionedSoundInstance.master(USounds.Vanilla.UI_BUTTON_CLICK, 1.75F + (0.25F * sigma))); client.getSoundManager().play(PositionedSoundInstance.master(USounds.Vanilla.UI_BUTTON_CLICK, 1.75F + (0.25F * sigma)));
UHud.INSTANCE.setMessage(Text.translatable("gui.unicopia.page_num", page + 1, max + 1)); UHud.INSTANCE.setMessage(Text.translatable("gui.unicopia.page_num", page + 1, max + 1));
} }
@ -141,6 +152,27 @@ public class KeyBindingsHandler {
} }
public ActivationType getType() { public ActivationType getType() {
if (binding.isPressed() && binding instanceof StickyKeyBinding) {
if (singleTapModifier.binding.isPressed()) {
KeyBinding.untoggleStickyKeys();
return ActivationType.TAP;
}
if (doubleTapModifier.binding.isPressed()) {
KeyBinding.untoggleStickyKeys();
return ActivationType.DOUBLE_TAP;
}
if (tripleTapModifier.binding.isPressed()) {
KeyBinding.untoggleStickyKeys();
return ActivationType.TRIPLE_TAP;
}
if (isToggleMode()) {
return ActivationType.NONE;
}
}
long now = System.currentTimeMillis(); long now = System.currentTimeMillis();
if (type != ActivationType.NONE && now > nextPhaseTime - 70) { if (type != ActivationType.NONE && now > nextPhaseTime - 70) {
ActivationType t = type; ActivationType t = type;

View file

@ -68,6 +68,10 @@ public class SettingsScreen extends GameGui {
}) })
.getStyle().setText("unicopia.options.ignore_mine_lp"); .getStyle().setText("unicopia.options.ignore_mine_lp");
content.addButton(new Toggle(LEFT, row += 20, config.toggleAbilityKeys.get()))
.onChange(config.toggleAbilityKeys)
.getStyle().setText("unicopia.options.toggle_ability_keys");
mineLpStatus = content.addButton(new Label(LEFT, row += 10)).getStyle().setText(getMineLPStatus()); mineLpStatus = content.addButton(new Label(LEFT, row += 10)).getStyle().setText(getMineLPStatus());
RegistryIndexer<Race> races = RegistryIndexer.of(Race.REGISTRY); RegistryIndexer<Race> races = RegistryIndexer.of(Race.REGISTRY);

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.client.gui; package com.minelittlepony.unicopia.client.gui;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.AbilityDispatcher; import com.minelittlepony.unicopia.ability.AbilityDispatcher;
import com.minelittlepony.unicopia.ability.AbilitySlot; import com.minelittlepony.unicopia.ability.AbilitySlot;
import com.minelittlepony.unicopia.client.KeyBindingsHandler; import com.minelittlepony.unicopia.client.KeyBindingsHandler;
@ -82,6 +83,12 @@ class Slot {
bSwap &= abilities.isFilled(bSlot); bSwap &= abilities.isFilled(bSlot);
} }
AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot);
if (stat.getAbility(Unicopia.getConfig().hudPage.get()).isEmpty()) {
return;
}
RenderSystem.setShaderColor(1, 1, 1, 1); RenderSystem.setShaderColor(1, 1, 1, 1);
RenderSystem.enableBlend(); RenderSystem.enableBlend();
MatrixStack matrices = context.getMatrices(); MatrixStack matrices = context.getMatrices();
@ -91,7 +98,7 @@ class Slot {
// background // background
context.drawTexture(UHud.HUD_TEXTURE, 0, 0, backgroundU, backgroundV, size, size, 128, 128); context.drawTexture(UHud.HUD_TEXTURE, 0, 0, backgroundU, backgroundV, size, size, 128, 128);
AbilityDispatcher.Stat stat = abilities.getStat(bSwap ? bSlot : aSlot);
int iconPosition = ((size - iconSize + slotPadding + 1) / 2); int iconPosition = ((size - iconSize + slotPadding + 1) / 2);
int sz = iconSize - slotPadding; int sz = iconSize - slotPadding;
@ -122,6 +129,11 @@ class Slot {
} }
void renderLabel(DrawContext context, AbilityDispatcher abilities, float tickDelta) { void renderLabel(DrawContext context, AbilityDispatcher abilities, float tickDelta) {
if (abilities.getStat(aSlot).getAbility(Unicopia.getConfig().hudPage.get()).isEmpty()) {
return;
}
Text label = KeyBindingsHandler.INSTANCE.getBinding(aSlot).getLabel(); Text label = KeyBindingsHandler.INSTANCE.getBinding(aSlot).getLabel();
MatrixStack matrices = context.getMatrices(); MatrixStack matrices = context.getMatrices();

View file

@ -48,8 +48,8 @@ public class UHud {
private final List<Slot> slots = List.of( private final List<Slot> slots = List.of(
new ManaRingSlot(this, AbilitySlot.PRIMARY, AbilitySlot.PASSIVE, 0, 0), new ManaRingSlot(this, AbilitySlot.PRIMARY, AbilitySlot.PASSIVE, 0, 0),
new Slot(this, AbilitySlot.SECONDARY, AbilitySlot.SECONDARY, 26, -5), new Slot(this, AbilitySlot.SECONDARY, AbilitySlot.SECONDARY, 30, -8),
new Slot(this, AbilitySlot.TERTIARY, AbilitySlot.TERTIARY, 36, 19) new Slot(this, AbilitySlot.TERTIARY, AbilitySlot.TERTIARY, 40, 18)
); );
@Nullable @Nullable
@ -68,10 +68,6 @@ public class UHud {
private SpellType<?> focusedType = SpellType.empty(); private SpellType<?> focusedType = SpellType.empty();
public void render(InGameHud hud, DrawContext context, float tickDelta) { public void render(InGameHud hud, DrawContext context, float tickDelta) {
// TODO: Check this when backporting!
// InGameHud#renderHotbar line 460
// context.getMatrices().translate(0.0f, 0.0f, -90.0f);
final int hotbarZ = -90; final int hotbarZ = -90;
if (client.player == null) { if (client.player == null) {
@ -96,7 +92,6 @@ public class UHud {
font = client.textRenderer; font = client.textRenderer;
xDirection = client.player.getMainArm() == Arm.LEFT ? -1 : 1; xDirection = client.player.getMainArm() == Arm.LEFT ? -1 : 1;
matrices.push(); matrices.push();
matrices.translate(scaledWidth / 2, scaledHeight / 2, 0); matrices.translate(scaledWidth / 2, scaledHeight / 2, 0);
@ -109,7 +104,7 @@ public class UHud {
matrices.pop(); matrices.pop();
matrices.push(); matrices.push();
int hudX = ((scaledWidth - 50) / 2) + (104 * xDirection); int hudX = ((scaledWidth - 50) / 2) + (109 * xDirection);
int hudY = scaledHeight - 50; int hudY = scaledHeight - 50;
int hudZ = hotbarZ; int hudZ = hotbarZ;
@ -139,14 +134,13 @@ public class UHud {
slots.forEach(slot -> slot.renderBackground(context, abilities, swap, tickDelta)); slots.forEach(slot -> slot.renderBackground(context, abilities, swap, tickDelta));
boolean canCast = Abilities.CAST.canUse(pony.getCompositeRace()) || Abilities.KIRIN_CAST.canUse(pony.getCompositeRace());
if (canCast) {
Ability<?> ability = pony.getAbilities().getStat(AbilitySlot.PRIMARY) Ability<?> ability = pony.getAbilities().getStat(AbilitySlot.PRIMARY)
.getAbility(Unicopia.getConfig().hudPage.get()) .getAbility(Unicopia.getConfig().hudPage.get())
.orElse(null); .orElse(null);
boolean canCast = ability == Abilities.CAST || ability == Abilities.KIRIN_CAST || ability == Abilities.SHOOT;
if (ability == Abilities.CAST || ability == Abilities.SHOOT) { if (canCast) {
matrices.push(); matrices.push();
matrices.translate(PRIMARY_SLOT_SIZE / 2F, PRIMARY_SLOT_SIZE / 2F, 0); matrices.translate(PRIMARY_SLOT_SIZE / 2F, PRIMARY_SLOT_SIZE / 2F, 0);
boolean first = !pony.asEntity().isSneaking(); boolean first = !pony.asEntity().isSneaking();
@ -158,7 +152,9 @@ public class UHud {
prevReplacing = replacing; prevReplacing = replacing;
setMessage(ability.getName(pony)); setMessage(ability.getName(pony));
} }
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(first ? 37 : 63)); int baseAngle = xDirection < 0 ? 100 : 0;
int secondAngleDif = xDirection * 30;
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(baseAngle + 37 + (first ? 0 : secondAngleDif)));
matrices.translate(-23, 0, 0); matrices.translate(-23, 0, 0);
matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-26)); matrices.multiply(RotationAxis.POSITIVE_Z.rotationDegrees(-26));
matrices.scale(0.8F, 0.8F, 1); matrices.scale(0.8F, 0.8F, 1);
@ -166,15 +162,19 @@ public class UHud {
context.drawTexture(HUD_TEXTURE, 0, 0, u, 120, 13, 7, 128, 128); context.drawTexture(HUD_TEXTURE, 0, 0, u, 120, 13, 7, 128, 128);
matrices.pop(); matrices.pop();
} }
}
slots.forEach(slot -> slot.renderLabel(context, abilities, tickDelta)); slots.forEach(slot -> slot.renderLabel(context, abilities, tickDelta));
matrices.pop(); matrices.pop();
if (canCast) { if (canCast) {
matrices.push();
if (xDirection < 0) {
hudX += PRIMARY_SLOT_SIZE / 2F - 8;
}
SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 10 - xDirection * 13, hudY + 2, EQUIPPED_GEMSTONE_SCALE); SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.MAIN_HAND), hudX + 10 - xDirection * 13, hudY + 2, EQUIPPED_GEMSTONE_SCALE);
SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 8 - xDirection * 2, hudY - 6, EQUIPPED_GEMSTONE_SCALE); SpellIconRenderer.renderSpell(context, pony.getCharms().getEquippedSpell(Hand.OFF_HAND), hudX + 8 - xDirection * 2, hudY - 6, EQUIPPED_GEMSTONE_SCALE);
matrices.pop();
} }
RenderSystem.disableBlend(); RenderSystem.disableBlend();

View file

@ -11,7 +11,6 @@ import java.util.stream.Collectors;
import org.jetbrains.annotations.Nullable; import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.USounds; import com.minelittlepony.unicopia.USounds;
import com.minelittlepony.unicopia.UTags;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.item.ButterflyItem; import com.minelittlepony.unicopia.item.ButterflyItem;
import com.minelittlepony.unicopia.util.NbtSerialisable; import com.minelittlepony.unicopia.util.NbtSerialisable;
@ -74,7 +73,7 @@ public class ButterflyEntity extends AmbientEntity {
} }
public static boolean canSpawn(EntityType<? extends ButterflyEntity> type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) { public static boolean canSpawn(EntityType<? extends ButterflyEntity> type, WorldAccess world, SpawnReason spawnReason, BlockPos pos, Random random) {
return world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON); return true;//world.getBlockState(pos.down()).isIn(UTags.Blocks.BUTTERFLIES_SPAWNABLE_ON);
} }
@Override @Override

View file

@ -26,7 +26,8 @@ import net.minecraft.world.Heightmap.Type;
public interface UEntities { public interface UEntities {
EntityType<ButterflyEntity> BUTTERFLY = register("butterfly", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.AMBIENT).entityFactory(ButterflyEntity::new) EntityType<ButterflyEntity> BUTTERFLY = register("butterfly", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.AMBIENT).entityFactory(ButterflyEntity::new)
.spawnRestriction(Location.NO_RESTRICTIONS, Type.WORLD_SURFACE_WG, ButterflyEntity::canSpawn) .spawnRestriction(Location.NO_RESTRICTIONS, Type.MOTION_BLOCKING_NO_LEAVES, ButterflyEntity::canSpawn)
.spawnableFarFromPlayer()
.dimensions(EntityDimensions.fixed(0.25F, 0.25F))); .dimensions(EntityDimensions.fixed(0.25F, 0.25F)));
EntityType<MagicProjectileEntity> THROWN_ITEM = register("thrown_item", FabricEntityTypeBuilder.<MagicProjectileEntity>create(SpawnGroup.MISC, MagicProjectileEntity::new) EntityType<MagicProjectileEntity> THROWN_ITEM = register("thrown_item", FabricEntityTypeBuilder.<MagicProjectileEntity>create(SpawnGroup.MISC, MagicProjectileEntity::new)
.trackRangeBlocks(100) .trackRangeBlocks(100)
@ -83,7 +84,7 @@ public interface UEntities {
.trackRangeChunks(8) .trackRangeChunks(8)
.dimensions(EntityDimensions.fixed(3, 2))); .dimensions(EntityDimensions.fixed(3, 2)));
EntityType<SpecterEntity> SPECTER = register("specter", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.MONSTER).entityFactory(SpecterEntity::new) EntityType<SpecterEntity> SPECTER = register("specter", FabricEntityTypeBuilder.createMob().spawnGroup(SpawnGroup.MONSTER).entityFactory(SpecterEntity::new)
.spawnRestriction(Location.ON_GROUND, Type.WORLD_SURFACE, HostileEntity::canSpawnInDark) .spawnRestriction(Location.ON_GROUND, Type.MOTION_BLOCKING_NO_LEAVES, HostileEntity::canSpawnIgnoreLightLevel)
.fireImmune() .fireImmune()
.spawnableFarFromPlayer() .spawnableFarFromPlayer()
.dimensions(EntityDimensions.fixed(1, 2))); .dimensions(EntityDimensions.fixed(1, 2)));

View file

@ -1329,6 +1329,9 @@
"key.unicopia.secondary": "Secondary Ability", "key.unicopia.secondary": "Secondary Ability",
"key.unicopia.tertiary": "Tertiary Ability", "key.unicopia.tertiary": "Tertiary Ability",
"key.unicopia.passive": "Passive Ability", "key.unicopia.passive": "Passive Ability",
"key.unicopia.ability_modifier_tap": "VR Ability Modifier (1-TAP)",
"key.unicopia.ability_modifier_double_tap": "VR Ability Modifier (2-TAP)",
"key.unicopia.ability_modifier_triple_tap": "VR Ability Modifier (3-TAP)",
"key.unicopia.hud_page_dn": "Hud Previous Page", "key.unicopia.hud_page_dn": "Hud Previous Page",
"key.unicopia.hud_page_up": "Hud Next Page", "key.unicopia.hud_page_up": "Hud Next Page",
@ -1408,6 +1411,7 @@
"commands.gravity.set.multiple": "Updated %s entities", "commands.gravity.set.multiple": "Updated %s entities",
"unicopia.options.title": "Unicopia Options", "unicopia.options.title": "Unicopia Options",
"unicopia.options.toggle_ability_keys": "Sticky Ability Keys",
"unicopia.options.ignore_mine_lp": "Ignore Mine Little Pony", "unicopia.options.ignore_mine_lp": "Ignore Mine Little Pony",
"unicopia.options.ignore_mine_lp.missing": "* Mine Little Pony is not installed", "unicopia.options.ignore_mine_lp.missing": "* Mine Little Pony is not installed",
"unicopia.options.ignore_mine_lp.detected": "* Your detected race is %s", "unicopia.options.ignore_mine_lp.detected": "* Your detected race is %s",
@ -1427,28 +1431,28 @@
"command.unicopia.config.list": "[Config] Property (%s) contains (%s) entries: ", "command.unicopia.config.list": "[Config] Property (%s) contains (%s) entries: ",
"command.unicopia.config.clear": "[Config] Cleared all values from property %s", "command.unicopia.config.clear": "[Config] Cleared all values from property %s",
"unicopia.race.unset": "Unset", "race.unicopia.unset": "Unset",
"unicopia.race.unset.alt": "Unset", "race.unicopia.unset.alt": "Unset",
"unicopia.race.human": "Human", "race.unicopia.human": "Human",
"unicopia.race.human.alt": "Humans", "race.unicopia.human.alt": "Humans",
"unicopia.race.earth": "Earth Pony", "race.unicopia.earth": "Earth Pony",
"unicopia.race.earth.alt": "Earth Ponies", "race.unicopia.earth.alt": "Earth Ponies",
"unicopia.race.unicorn": "Unicorn", "race.unicopia.unicorn": "Unicorn",
"unicopia.race.unicorn.alt": "Unicorns", "race.unicopia.unicorn.alt": "Unicorns",
"unicopia.race.pegasus": "Pegasus", "race.unicopia.pegasus": "Pegasus",
"unicopia.race.pegasus.alt": "Pegasi", "race.unicopia.pegasus.alt": "Pegasi",
"unicopia.race.alicorn": "Alicorn", "race.unicopia.alicorn": "Alicorn",
"unicopia.race.alicorn.alt": "Alicorns", "race.unicopia.alicorn.alt": "Alicorns",
"unicopia.race.changeling": "Changeling", "race.unicopia.changeling": "Changeling",
"unicopia.race.changeling.alt": "Changelings", "race.unicopia.changeling.alt": "Changelings",
"unicopia.race.bat": "Bat Pony", "race.unicopia.bat": "Bat Pony",
"unicopia.race.bat.alt": "Bat Ponies", "race.unicopia.bat.alt": "Bat Ponies",
"unicopia.race.kirin": "Kirin", "race.unicopia.kirin": "Kirin",
"unicopia.race.kirin.alt": "Kirins", "race.unicopia.kirin.alt": "Kirins",
"unicopia.race.hippogriff": "Hippogriff", "race.unicopia.hippogriff": "Hippogriff",
"unicopia.race.hippogriff.alt": "Hippogriffs", "race.unicopia.hippogriff.alt": "Hippogriffs",
"unicopia.race.seapony": "Sea Pony", "race.unicopia.seapony": "Sea Pony",
"unicopia.race.seapony.alt": "Sea Ponies", "race.unicopia.seapony.alt": "Sea Ponies",
"death.attack.unicopia.generic.and_also": "%1$s and %2$s", "death.attack.unicopia.generic.and_also": "%1$s and %2$s",
"death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying", "death.attack.unicopia.generic.whilst_flying": "%1$s whilst flying",