Migrate to TLA. Closes #263

This commit is contained in:
Sollace 2024-10-08 00:52:40 +01:00
parent 79030b43c7
commit 3b1fab888f
No known key found for this signature in database
GPG key ID: E52FACE7B5C773DB
16 changed files with 618 additions and 663 deletions

View file

@ -102,6 +102,19 @@ public class ItemTraitsTooltipRenderer implements Text, OrderedText, TooltipComp
}
}
public static void renderStackSingleTrait(Trait trait, float amount, DrawContext context, float x, float y, float weight, float delta, int seed, boolean revealAll) {
float time = MathHelper.cos((MinecraftClient.getInstance().player.age + delta + seed) / 2F) * 0.7F;
float angle = 0.7F + (time / 30F) % MathHelper.TAU;
float r = 9 + 2 * MathHelper.sin(delta / 20F);
ItemTraitsTooltipRenderer.renderTraitIcon(trait, amount * weight, context,
x + r * MathHelper.sin(angle),
y + r * MathHelper.cos(angle),
revealAll || isKnown(trait)
);
}
public static boolean isKnown(Trait trait) {
return MinecraftClient.getInstance().player == null
|| Pony.of(MinecraftClient.getInstance().player).getDiscoveries().isKnown(trait);

View file

@ -1,49 +0,0 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;
import com.minelittlepony.unicopia.ability.magic.spell.effect.CustomisedSpellType;
import com.minelittlepony.unicopia.item.EnchantableItem;
import dev.emi.emi.api.recipe.EmiCraftingRecipe;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.recipe.EmiShapedRecipe;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.CraftingRecipe;
import net.minecraft.recipe.Ingredient;
import net.minecraft.recipe.RecipeEntry;
public class MagicalShapedEmiRecipe extends EmiCraftingRecipe {
public MagicalShapedEmiRecipe(RecipeEntry<? extends CraftingRecipe> recipe, CustomisedSpellType<?> spellEffect, ItemStack output) {
super(padIngredients(recipe, spellEffect), EmiStack.of(output),
recipe.id().withPath(p -> p + "/" + spellEffect.type().getId().getPath()), false);
EmiShapedRecipe.setRemainders(input, recipe.value());
}
private static List<EmiIngredient> padIngredients(RecipeEntry<? extends CraftingRecipe> recipe, CustomisedSpellType<?> spellEffect) {
List<EmiIngredient> list = recipe.value().getIngredients().stream()
.map(ingredient -> remapIngredient(ingredient, spellEffect))
.collect(Collectors.toList());
while (list.size() < 9) {
list.add(EmiStack.EMPTY);
}
return list;
}
private static EmiIngredient remapIngredient(Ingredient ingredient, CustomisedSpellType<?> spellEffect) {
ItemStack[] stacks = ingredient.getMatchingStacks();
for (int i = 0; i < stacks.length; i++) {
if (stacks[i].getItem() instanceof EnchantableItem) {
stacks = Arrays.copyOf(stacks, stacks.length);
stacks[i] = EnchantableItem.enchant(stacks[i].copy(), spellEffect.type());
return EmiIngredient.of(Arrays.stream(stacks).map(EmiStack::of).toList());
}
}
return EmiIngredient.of(ingredient);
}
}

View file

@ -1,149 +0,0 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellDuplicatingRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellEnhancingRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellShapedCraftingRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.block.state.Schematic;
import com.minelittlepony.unicopia.item.EnchantableItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.item.group.MultiItem;
import com.minelittlepony.unicopia.recipe.TransformCropsRecipe;
import com.minelittlepony.unicopia.recipe.URecipes;
import dev.emi.emi.api.EmiPlugin;
import dev.emi.emi.api.EmiRegistry;
import dev.emi.emi.api.recipe.EmiRecipeCategory;
import dev.emi.emi.api.render.EmiTexture;
import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.recipe.EmiStonecuttingRecipe;
import net.minecraft.block.Blocks;
import net.minecraft.item.ItemStack;
import net.minecraft.item.Items;
import net.minecraft.recipe.RecipeType;
import net.minecraft.registry.DynamicRegistryManager;
import net.minecraft.registry.Registries;
import net.minecraft.util.Identifier;
public class Main implements EmiPlugin {
static final EmiStack SPELL_BOOK_STATION = EmiStack.of(UItems.SPELLBOOK);
static final EmiStack CLOUD_SHAPING_STATION = EmiStack.of(UBlocks.SHAPING_BENCH);
static final EmiStack GROWING_STATION = EmiStack.of(Blocks.FARMLAND);
static final EmiStack ALTAR_STATION = EmiStack.of(Blocks.CRYING_OBSIDIAN);
static final EmiRecipeCategory SPELL_BOOK_CATEGORY = new EmiRecipeCategory(Unicopia.id("spellbook"), SPELL_BOOK_STATION, SPELL_BOOK_STATION);
static final EmiRecipeCategory CLOUD_SHAPING_CATEGORY = new EmiRecipeCategory(Unicopia.id("cloud_shaping"), CLOUD_SHAPING_STATION, CLOUD_SHAPING_STATION);
static final EmiRecipeCategory GROWING_CATEGORY = new EmiRecipeCategory(Unicopia.id("growing"), GROWING_STATION, GROWING_STATION);
static final EmiRecipeCategory ALTAR_CATEGORY = new EmiRecipeCategory(Unicopia.id("altar"), ALTAR_STATION, ALTAR_STATION);
static final Identifier WIDGETS = Unicopia.id("textures/gui/widgets.png");
static final EmiTexture EMPTY_ARROW = new EmiTexture(WIDGETS, 44, 0, 24, 17);
@Override
public void register(EmiRegistry registry) {
registry.addCategory(SPELL_BOOK_CATEGORY);
registry.addWorkstation(SPELL_BOOK_CATEGORY, SPELL_BOOK_STATION);
registry.getRecipeManager().listAllOfType(URecipes.SPELLBOOK).forEach(recipe -> {
if (recipe.value() instanceof SpellDuplicatingRecipe) {
registry.addRecipe(new SpellDuplicatingEmiRecipe(recipe));
} else if (recipe.value() instanceof SpellEnhancingRecipe enhancingRecipe) {
Trait.all().forEach(trait -> {
registry.addRecipe(new SpellDuplicatingEmiRecipe(recipe) {
private final Identifier id;
{
id = recipe.id().withPath(p -> p + "/" + trait.getId().getPath());
input(trait);
this.getOutputs().addAll(
Arrays.stream(enhancingRecipe.getBaseMaterial().getMatchingStacks())
.map(stack -> EmiStack.of(SpellTraits.of(stack).add(new SpellTraits.Builder().with(trait, 1).build()).applyTo(stack)).comparison(c -> Comparison.DEFAULT_COMPARISON))
.toList()
);
}
@Nullable
@Override
public Identifier getId() {
return id;
}
});
});
} else {
registry.addRecipe(new SpellbookEmiRecipe(recipe));
}
});
registry.addCategory(CLOUD_SHAPING_CATEGORY);
registry.addWorkstation(CLOUD_SHAPING_CATEGORY, CLOUD_SHAPING_STATION);
registry.getRecipeManager().listAllOfType(URecipes.CLOUD_SHAPING).forEach(recipe -> {
registry.addRecipe(new EmiStonecuttingRecipe(recipe.value()) {
@Override
public EmiRecipeCategory getCategory() {
return CLOUD_SHAPING_CATEGORY;
}
});
});
Stream.of(UItems.GEMSTONE, UItems.BOTCHED_GEM, UItems.MAGIC_STAFF, UItems.FILLED_JAR).forEach(item -> {
registry.setDefaultComparison(item, comparison -> Comparison.compareComponents());
});
DynamicRegistryManager registries = DynamicRegistryManager.of(Registries.REGISTRIES);
registry.getRecipeManager().listAllOfType(RecipeType.CRAFTING).stream()
.filter(recipe -> recipe.value() instanceof SpellShapedCraftingRecipe)
.forEach(recipe -> {
ItemStack output = recipe.value().getResult(registries);
if (output.getItem() instanceof MultiItem multiItem) {
multiItem.getDefaultStacks().forEach(outputVariation -> {
if (EnchantableItem.isEnchanted(outputVariation)) {
registry.addRecipe(new MagicalShapedEmiRecipe(recipe, EnchantableItem.getSpellEffect(outputVariation), outputVariation));
}
});
}
});
registry.addCategory(GROWING_CATEGORY);
registry.addWorkstation(GROWING_CATEGORY, GROWING_STATION);
registry.getRecipeManager().listAllOfType(URecipes.GROWING).forEach(recipe -> {
registry.addRecipe(new StructureInteractionEmiRecipe(
GROWING_CATEGORY,
recipe.id(),
new Schematic.Builder()
.fill(0, 0, 0, 6, 0, 6, recipe.value().getCatalystState())
.set(3, 0, 3, Blocks.FARMLAND.getDefaultState())
.set(3, 1, 3, recipe.value().getTargetState())
.build(),
List.of(EmiStack.of(recipe.value().getTarget()), EmiStack.of(recipe.value().getCatalyst(), TransformCropsRecipe.AREA)),
EmiStack.of(recipe.value().getOutput()),
Unicopia.id("textures/gui/ability/grow.png")
));
});
registry.addCategory(ALTAR_CATEGORY);
registry.addWorkstation(ALTAR_CATEGORY, ALTAR_STATION);
registry.addRecipe(new StructureInteractionEmiRecipe(
ALTAR_CATEGORY,
Unicopia.id("altar/spectral_clock"),
Schematic.ALTAR,
List.of(
EmiStack.of(Items.CLOCK),
EmiStack.of(UItems.SPELLBOOK),
EmiStack.of(Blocks.SOUL_SAND),
EmiStack.of(Blocks.LODESTONE),
EmiStack.of(Blocks.OBSIDIAN, 8 * 4 + 8)
),
EmiStack.of(UItems.SPECTRAL_CLOCK),
Unicopia.id("textures/gui/race/alicorn.png")
));
}
}

View file

@ -1,64 +0,0 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.Arrays;
import java.util.List;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
import dev.emi.emi.EmiPort;
import dev.emi.emi.EmiRenderHelper;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.api.stack.ListEmiIngredient;
import dev.emi.emi.runtime.EmiDrawContext;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.RecipeEntry;
public class SpellDuplicatingEmiRecipe extends SpellbookEmiRecipe {
public SpellDuplicatingEmiRecipe(RecipeEntry<SpellbookRecipe> recipe) {
super(recipe);
}
@Override
public void input(ItemStack... stacks) {
getInputs().add(new CyclingRecipeIngredient(Arrays.stream(stacks).map(EmiStack::of).toList(), 1));
}
@Override
protected EmiIngredient getOutput() {
return new CyclingRecipeIngredient(getOutputs(), 2);
}
static class CyclingRecipeIngredient extends ListEmiIngredient {
private final List<? extends EmiIngredient> ingredients;
private final int maxCount;
public CyclingRecipeIngredient(List<? extends EmiIngredient> ingredients, long amount) {
super(ingredients, amount);
this.ingredients = ingredients;
this.maxCount = ingredients.size();
}
@Override
public void render(DrawContext context, int x, int y, float delta, int flags) {
if (maxCount < 2 || MinecraftClient.getInstance().player == null) {
super.render(context, x, y, delta, flags);
} else {
int tick = (MinecraftClient.getInstance().player.age / 12) % maxCount;
if ((flags & RENDER_AMOUNT) != 0) {
String count = "";
if (getAmount() != 1) {
count += getAmount();
}
EmiRenderHelper.renderAmount(EmiDrawContext.wrap(context), x, y, EmiPort.literal(count));
}
ingredients.get(tick).render(context, x, y, delta, flags & ~RENDER_AMOUNT);
}
}
}
}

View file

@ -1,131 +0,0 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
import com.minelittlepony.unicopia.container.inventory.HexagonalCraftingGrid;
import com.mojang.blaze3d.systems.RenderSystem;
import dev.emi.emi.api.recipe.EmiRecipe;
import dev.emi.emi.api.recipe.EmiRecipeCategory;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.api.widget.TextureWidget;
import dev.emi.emi.api.widget.WidgetHolder;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.RecipeEntry;
import net.minecraft.util.Identifier;
class SpellbookEmiRecipe implements EmiRecipe, SpellbookRecipe.CraftingTreeBuilder {
private final RecipeEntry<SpellbookRecipe> recipe;
private final List<EmiIngredient> inputs = new ArrayList<>();
private final List<EmiStack> outputs = new ArrayList<>();
public SpellbookEmiRecipe(RecipeEntry<SpellbookRecipe> recipe) {
this.recipe = recipe;
recipe.value().buildCraftingTree(this);
}
@Override
public EmiRecipeCategory getCategory() {
return Main.SPELL_BOOK_CATEGORY;
}
@Nullable
@Override
public Identifier getId() {
return recipe.id();
}
@Override
public List<EmiIngredient> getInputs() {
return inputs;
}
@Override
public List<EmiStack> getOutputs() {
return outputs;
}
@Override
public int getDisplayWidth() {
return 150;
}
@Override
public int getDisplayHeight() {
return 75;
}
@Override
public void addWidgets(WidgetHolder widgets) {
widgets.addTexture(SpellbookScreen.TEXTURE, 0, 0, getDisplayWidth(), getDisplayHeight(), 50, 50, 128, 128, 512, 256);
widgets.addTexture(Main.EMPTY_ARROW, 85, 30);
List<HexagonalCraftingGrid.Slot> grid = new ArrayList<>();
List<HexagonalCraftingGrid.Slot> gem = new ArrayList<>();
HexagonalCraftingGrid.create(-34, -5, 3, grid, gem);
int currentInput = 1;
for (int i = 0; i < grid.size(); i++) {
var slot = grid.get(i);
if (currentInput < inputs.size() && slot.weight() == 1) {
widgets.add(new SlotTexture(slot));
widgets.addSlot(inputs.get(currentInput++), slot.left(), slot.top()).drawBack(false);
} else if (slot.weight() == 1) {
widgets.add(new SlotTexture(slot));
widgets.addSlot(slot.left(), slot.top()).drawBack(false);
}
}
widgets.addSlot(inputs.get(0), gem.get(0).left(), gem.get(0).top()).drawBack(false);
widgets.addSlot(getOutput(), 120, 25).large(true).recipeContext(this);
}
protected EmiIngredient getOutput() {
return EmiIngredient.of(outputs);
}
@Override
public void input(ItemStack... stacks) {
inputs.add(EmiIngredient.of(Arrays.stream(stacks).map(EmiStack::of).toList()));
}
@Override
public void input(Trait... traits) {
inputs.add(EmiIngredient.of(Arrays.stream(traits).map(trait -> new TraitEmiStack(trait, 1)).toList()));
}
@Override
public void input(Trait trait, float value) {
inputs.add(new TraitEmiStack(trait, value));
}
@Override
public void result(ItemStack... stack) {
outputs.addAll(Arrays.stream(stack).map(EmiStack::of).toList());
}
static class SlotTexture extends TextureWidget {
public SlotTexture(HexagonalCraftingGrid.Slot slot) {
super(SpellbookScreen.SLOT, slot.left() - 7, slot.top() - 7, 32, 32, 0, 0, 32, 32, 32, 32);
}
@Override
public void render(DrawContext context, int mouseX, int mouseY, float delta) {
RenderSystem.enableBlend();
context.drawTexture(texture, x, y, 0, u, v, width, height, textureWidth, textureHeight);
}
}
}

View file

@ -1,144 +0,0 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.List;
import com.minelittlepony.unicopia.block.state.Schematic;
import dev.emi.emi.api.recipe.EmiRecipe;
import dev.emi.emi.api.recipe.EmiRecipeCategory;
import dev.emi.emi.api.render.EmiTexture;
import dev.emi.emi.api.stack.EmiIngredient;
import dev.emi.emi.api.stack.EmiStack;
import dev.emi.emi.api.widget.WidgetHolder;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.DiffuseLighting;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.VertexConsumerProvider.Immediate;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import net.minecraft.util.math.RotationAxis;
public class StructureInteractionEmiRecipe implements EmiRecipe {
private final EmiRecipeCategory category;
private final Identifier id;
private final Schematic schematic;
private final List<EmiIngredient> inputs;
private final List<EmiStack> output;
private final Identifier processIcon;
private int age;
public StructureInteractionEmiRecipe(EmiRecipeCategory category, Identifier id, Schematic schematic, List<EmiIngredient> inputs, EmiStack output, Identifier processIcon) {
this.category = category;
this.id = id;
this.schematic = schematic;
this.inputs = inputs;
this.output = List.of(output);
this.processIcon = processIcon;
}
@Override
public EmiRecipeCategory getCategory() {
return category;
}
@Override
public Identifier getId() {
return id;
}
@Override
public List<EmiIngredient> getInputs() {
return inputs;
}
@Override
public List<EmiStack> getOutputs() {
return output;
}
@Override
public int getDisplayWidth() {
return 130;
}
@Override
public int getDisplayHeight() {
return schematic.dy() * 8 + 80 + 20 * (inputs.size() - 2);
}
@Override
public void addWidgets(WidgetHolder widgets) {
int y = schematic.dy() * 8;
int row = 0;
age = 0;
widgets.addDrawable(10, y / 2, 100, 100, this::renderSchematic);
int x = 10;
for (int i = 0; i < inputs.size(); i++) {
if (i > 1) {
x -= 40;
row += 20;
}
if (i > 0) {
widgets.addTexture(EmiTexture.PLUS, x + 3, y + 53 + row);
x += 20;
}
widgets.addSlot(inputs.get(i), x, y + 50 + row).catalyst(i > 0);
x += 20;
}
widgets.addTexture(EmiTexture.EMPTY_ARROW, 73, y + 52);
widgets.addSlot(output.get(0), 100, y + 47).large(true).recipeContext(this);
widgets.addTexture(processIcon, 73, y + 45, 13, 13, 0, 0, 16, 16, 16, 16).tooltipText(List.of(Text.translatable(
Util.createTranslationKey("recipe", category.getId()) + "." + "instruction"
)));
}
private void renderSchematic(DrawContext context, int mouseX, int mouseY, float delta) {
if (schematic.volume() == 0) {
return;
}
MatrixStack matrices = context.getMatrices();
Immediate immediate = context.getVertexConsumers();
MinecraftClient client = MinecraftClient.getInstance();
matrices.push();
float minSize = (Math.max(schematic.dz(), Math.max(schematic.dx(), schematic.dy())) + 1) * 16;
float scale = 60 / minSize;
matrices.scale(scale, scale, 1);
matrices.translate(95, 40, 100);
matrices.scale(16, -16, 16);
matrices.peek().getNormalMatrix().scale(1, -1, 1);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(20));
matrices.peek().getPositionMatrix().rotate(RotationAxis.POSITIVE_Y.rotationDegrees(40));
matrices.translate(
(-schematic.dx() - 1) / 2F,
(-schematic.dy() - 1) / 2F,
(-schematic.dz() - 1) / 2F
);
DiffuseLighting.disableGuiDepthLighting();
age++;
for (var entry : schematic.states()) {
int x = entry.x() - schematic.dx() / 2;
int z = entry.z() - schematic.dz() / 2;
int distance = x * x + z * z;
if (age >= distance * 2) {
matrices.push();
matrices.translate(entry.x(), entry.y(), entry.z());
client.getBlockRenderManager().renderBlockAsEntity(entry.state(), matrices, immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, OverlayTexture.DEFAULT_UV);
matrices.pop();
}
}
matrices.pop();
}
}

View file

@ -1,120 +0,0 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.List;
import org.apache.commons.compress.utils.Lists;
import com.minelittlepony.common.client.gui.Tooltip;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.client.gui.ItemTraitsTooltipRenderer;
import dev.emi.emi.api.render.EmiRender;
import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.gui.tooltip.TooltipComponent;
import net.minecraft.component.ComponentChanges;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.math.MathHelper;
public class TraitEmiStack extends EmiStack {
private final Trait trait;
private final float amount;
private final SpellTraits traits;
public TraitEmiStack(Trait trait, float amount) {
this.trait = trait;
this.amount = amount;
traits = new SpellTraits.Builder().with(trait, amount).build();
}
@Override
public boolean isEmpty() {
return amount == 0;
}
@Override
public ComponentChanges getComponentChanges() {
return ComponentChanges.EMPTY;
}
@Override
public Object getKey() {
return trait;
}
@Override
public Identifier getId() {
return trait.getId();
}
@Override
public List<Text> getTooltipText() {
return trait.getTooltipLines();
}
@Override
public List<TooltipComponent> getTooltip() {
List<TooltipComponent> list = Lists.newArrayList();
if (!isEmpty()) {
for (Text line : Tooltip.of(trait.getTooltip(), 200).getLines()) {
list.add(TooltipComponent.of(line.asOrderedText()));
}
list.addAll(super.getTooltip());
}
return list;
}
@Override
public Text getName() {
return trait.getName();
}
@Override
public void render(DrawContext context, int x, int y, float delta, int flags) {
if ((flags & RENDER_ICON) != 0) {
List<Item> knownItems = trait.getItems();
if (knownItems.isEmpty() || MinecraftClient.getInstance().player == null) {
ItemTraitsTooltipRenderer.renderTraitIcon(trait, amount, context, x, y, true);
} else {
int tick = (MinecraftClient.getInstance().player.age / 12) % knownItems.size();
ItemStack stack = knownItems.get(tick).getDefaultStack();
EmiStack.of(stack).render(context, x, y, delta, flags);
ItemTraitsTooltipRenderer.renderStackTraits(traits, context, x, y, 1, delta, 0, true);
}
}
if ((flags & RENDER_REMAINDER) != 0) {
EmiRender.renderRemainderIcon(this, context, x, y);
}
}
@Override
public EmiStack copy() {
return new TraitEmiStack(trait, amount);
}
@Override
public boolean isEqual(EmiStack stack) {
return super.isEqual(stack) && equalTo(stack);
}
@Override
public boolean isEqual(EmiStack stack, Comparison comparison) {
return super.isEqual(stack, comparison) && equalTo(stack);
}
@Override
public boolean equals(Object obj) {
return super.equals(obj) && obj instanceof EmiStack s && equalTo(s);
}
private boolean equalTo(EmiStack stack) {
return stack instanceof TraitEmiStack t && t.trait == trait && MathHelper.approximatelyEquals(t.amount, amount);
}
}

View file

@ -0,0 +1,65 @@
package com.minelittlepony.unicopia.compat.tla;
import java.util.List;
import com.minelittlepony.unicopia.recipe.URecipes;
import io.github.mattidragon.tlaapi.api.gui.GuiBuilder;
import io.github.mattidragon.tlaapi.api.plugin.PluginContext;
import io.github.mattidragon.tlaapi.api.recipe.TlaIngredient;
import io.github.mattidragon.tlaapi.api.recipe.TlaRecipe;
import io.github.mattidragon.tlaapi.api.recipe.TlaStack;
import net.minecraft.client.MinecraftClient;
import net.minecraft.recipe.RecipeEntry;
import net.minecraft.recipe.StonecuttingRecipe;
import net.minecraft.util.Identifier;
public class CloudShapingTlaRecipe implements TlaRecipe {
private final Identifier id;
private final RecipeCategory category;
private final TlaIngredient input;
private final TlaStack output;
static void generate(RecipeCategory category, PluginContext context) {
context.addRecipeGenerator(URecipes.CLOUD_SHAPING, recipe -> new CloudShapingTlaRecipe(category, recipe));
}
public CloudShapingTlaRecipe(RecipeCategory category, RecipeEntry<StonecuttingRecipe> recipe) {
this.id = recipe.id();
this.category = category;
input = TlaIngredient.ofIngredient(recipe.value().getIngredients().get(0));
output = TlaStack.of(recipe.value().getResult(MinecraftClient.getInstance().world.getRegistryManager()));
}
@Override
public RecipeCategory getCategory() {
return category;
}
@Override
public Identifier getId() {
return id;
}
@Override
public List<TlaIngredient> getInputs() {
return List.of(input);
}
@Override
public List<TlaStack> getOutputs() {
return List.of(output);
}
@Override
public List<TlaIngredient> getCatalysts() {
return List.of();
}
@Override
public void buildGui(GuiBuilder builder) {
builder.addArrow(26, 1, false);
builder.addSlot(input, 0, 0).markInput();
builder.addSlot(output, 58, 0).markOutput();
}
}

View file

@ -0,0 +1,16 @@
package com.minelittlepony.unicopia.compat.tla;
import com.minelittlepony.unicopia.item.UItems;
import io.github.mattidragon.tlaapi.api.plugin.PluginContext;
import io.github.mattidragon.tlaapi.api.plugin.TlaApiPlugin;
import io.github.mattidragon.tlaapi.api.recipe.TlaStackComparison;
public class Main implements TlaApiPlugin {
@Override
public void register(PluginContext registry) {
RecipeCategory.bootstrap(registry);
registry.getItemComparisons().register(TlaStackComparison.compareComponents(),
UItems.GEMSTONE, UItems.BOTCHED_GEM, UItems.MAGIC_STAFF, UItems.FILLED_JAR
);
}
}

View file

@ -0,0 +1,75 @@
package com.minelittlepony.unicopia.compat.tla;
import java.util.HashMap;
import java.util.Map;
import java.util.function.BiConsumer;
import org.jetbrains.annotations.Nullable;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.block.UBlocks;
import com.minelittlepony.unicopia.item.UItems;
import io.github.mattidragon.tlaapi.api.plugin.PluginContext;
import io.github.mattidragon.tlaapi.api.recipe.CategoryIcon;
import io.github.mattidragon.tlaapi.api.recipe.TlaCategory;
import io.github.mattidragon.tlaapi.api.recipe.TlaIngredient;
import io.github.mattidragon.tlaapi.api.recipe.TlaStack;
import net.minecraft.block.Blocks;
import net.minecraft.item.ItemConvertible;
import net.minecraft.util.Identifier;
public record RecipeCategory(Identifier id, CategoryIcon icon, TlaIngredient stations, int width, int height) implements TlaCategory {
static final Map<RecipeCategory, @Nullable BiConsumer<RecipeCategory, PluginContext>> REGISTRY = new HashMap<>();
static final RecipeCategory SPELL_BOOK = register("spellbook", UItems.SPELLBOOK, 150, 75, SpellbookTlaRecipe::generate);
static final RecipeCategory CLOUD_SHAPING = register("cloud_shaping", UBlocks.SHAPING_BENCH, 76, 18, CloudShapingTlaRecipe::generate);
static final RecipeCategory GROWING = register("growing", Blocks.FARMLAND, 130, 85, StructureInteractionTlaRecipe::generateFarmingRecipes);
static final RecipeCategory ALTAR = register("altar", Blocks.CRYING_OBSIDIAN, 130, 160, StructureInteractionTlaRecipe::generateAltarRecipes);
static RecipeCategory register(String name, ItemConvertible item, int width, int height, @Nullable BiConsumer<RecipeCategory, PluginContext> recipeConstructor) {
return register(new RecipeCategory(Unicopia.id(name), CategoryIcon.item(item), TlaStack.of(item).asIngredient(), width, height), recipeConstructor);
}
static RecipeCategory register(RecipeCategory category, @Nullable BiConsumer<RecipeCategory, PluginContext> recipeConstructor) {
REGISTRY.put(category, recipeConstructor);
return category;
}
static void bootstrap(PluginContext registry) {
REGISTRY.forEach((category, recipeConstructor) -> {
registry.addCategory(category);
registry.addWorkstation(category, category.stations());
try {
if (recipeConstructor != null) {
recipeConstructor.accept(category, registry);
}
} catch (Throwable t) {
Unicopia.LOGGER.fatal("Error occured whilst registering recipes for category " + category.getId(), t);
}
});
}
@Override
public Identifier getId() {
return id;
}
@Override
public int getDisplayHeight() {
return height;
}
@Override
public int getDisplayWidth() {
return width;
}
@Override
public CategoryIcon getIcon() {
return icon;
}
@Override
public CategoryIcon getSimpleIcon() {
return icon;
}
}

View file

@ -0,0 +1,17 @@
package com.minelittlepony.unicopia.compat.tla;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
import io.github.mattidragon.tlaapi.api.recipe.TlaIngredient;
import net.minecraft.recipe.RecipeEntry;
class SpellDuplicatingTlaRecipe extends SpellbookTlaRecipe {
public SpellDuplicatingTlaRecipe(RecipeEntry<SpellbookRecipe> recipe) {
super(recipe);
}
@Override
protected TlaIngredient getOutput() {
return super.getOutput().withAmount(2);
}
}

View file

@ -0,0 +1,162 @@
package com.minelittlepony.unicopia.compat.tla;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.List;
import java.util.function.Supplier;
import java.util.stream.Stream;
import org.jetbrains.annotations.Nullable;
import com.google.common.base.Suppliers;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellDuplicatingRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellEnhancingRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.crafting.SpellbookRecipe;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
import com.minelittlepony.unicopia.container.inventory.HexagonalCraftingGrid;
import com.minelittlepony.unicopia.recipe.URecipes;
import io.github.mattidragon.tlaapi.api.gui.GuiBuilder;
import io.github.mattidragon.tlaapi.api.gui.TextureConfig;
import io.github.mattidragon.tlaapi.api.gui.TlaBounds;
import io.github.mattidragon.tlaapi.api.plugin.PluginContext;
import io.github.mattidragon.tlaapi.api.recipe.TlaIngredient;
import io.github.mattidragon.tlaapi.api.recipe.TlaRecipe;
import io.github.mattidragon.tlaapi.api.recipe.TlaStack;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.RecipeEntry;
import net.minecraft.util.Identifier;
class SpellbookTlaRecipe implements TlaRecipe, SpellbookRecipe.CraftingTreeBuilder {
static final Identifier WIDGETS = Unicopia.id("textures/gui/widgets.png");
static final TextureConfig EMPTY_ARROW = TextureConfig.builder().texture(WIDGETS).uv(44, 0).size(24, 17).build();
private final RecipeEntry<SpellbookRecipe> recipe;
protected final List<TraitedTlaIngredient> inputs = new ArrayList<>();
private final List<TlaStack> outputs = new ArrayList<>();
private final Supplier<List<TlaIngredient>> ingredients;
static void generate(RecipeCategory category, PluginContext context) {
context.addGenerator(client -> client.world.getRecipeManager().listAllOfType(URecipes.SPELLBOOK).stream().flatMap(recipe -> {
if (recipe.value() instanceof SpellDuplicatingRecipe) {
return Stream.of(new SpellDuplicatingTlaRecipe(recipe));
}
if (recipe.value() instanceof SpellEnhancingRecipe enhancingRecipe) {
return Trait.all().stream().map(trait -> {
return new SpellDuplicatingTlaRecipe(recipe) {
private final Identifier id = recipe.id().withPath(p -> p + "/" + trait.getId().getPath());
{
input(trait);
getOutputs().addAll(
Arrays.stream(enhancingRecipe.getBaseMaterial().getMatchingStacks())
.map(stack -> TlaStack.of(SpellTraits.of(stack).add(new SpellTraits.Builder().with(trait, 1).build()).applyTo(stack)))
.toList()
);
}
@Nullable
@Override
public Identifier getId() {
return id;
}
};
});
}
return Stream.of((TlaRecipe)new SpellbookTlaRecipe(recipe));
}).toList());
}
public SpellbookTlaRecipe(RecipeEntry<SpellbookRecipe> recipe) {
this.recipe = recipe;
this.ingredients = Suppliers.memoize(() -> inputs.stream().map(TraitedTlaIngredient::ingredient).toList());
recipe.value().buildCraftingTree(this);
}
@Override
public RecipeCategory getCategory() {
return RecipeCategory.SPELL_BOOK;
}
@Nullable
@Override
public Identifier getId() {
return recipe.id();
}
@Override
public List<TlaIngredient> getInputs() {
return ingredients.get();
}
@Override
public List<TlaStack> getOutputs() {
return outputs;
}
@Override
public List<TlaIngredient> getCatalysts() {
return List.of();
}
@Override
public void buildGui(GuiBuilder builder) {
TlaBounds bounds = builder.getBounds();
builder.addTexture(TextureConfig.builder().texture(SpellbookScreen.TEXTURE)
.size(bounds.width(), bounds.height())
.uv(50, 50)
.regionSize(128, 128)
.textureSize(512, 256)
.build(), 0, 0);
builder.addTexture(EMPTY_ARROW, 85, 30);
List<HexagonalCraftingGrid.Slot> grid = new ArrayList<>();
List<HexagonalCraftingGrid.Slot> gem = new ArrayList<>();
HexagonalCraftingGrid.create(-34, -5, 3, grid, gem);
int currentInput = 1;
for (int i = 0; i < grid.size(); i++) {
var slot = grid.get(i);
if (currentInput < inputs.size() && slot.weight() == 1) {
inputs.get(currentInput++).buildGui(slot, builder);
} else if (slot.weight() == 1) {
inputs.get(0).buildGui(TlaIngredient.EMPTY, slot, builder);
}
}
inputs.get(0).buildGui(gem.get(0), builder);
builder.addSlot(getOutput(), 120, 25).makeLarge().markOutput();
}
protected TlaIngredient getOutput() {
return TlaIngredient.ofStacks(outputs);
}
@Override
public void input(ItemStack... stacks) {
inputs.add(TraitedTlaIngredient.of(TlaIngredient.ofStacks(Arrays.stream(stacks).map(TlaStack::of).toList())));
}
@Override
public void input(Trait... traits) {
inputs.add(TraitedTlaIngredient.of(List.of(traits), 1));
}
@Override
public void input(Trait trait, float value) {
inputs.add(TraitedTlaIngredient.of(trait, value));
}
@Override
public void result(ItemStack... stack) {
outputs.addAll(Arrays.stream(stack).map(TlaStack::of).toList());
}
}

View file

@ -0,0 +1,198 @@
package com.minelittlepony.unicopia.compat.tla;
import java.util.List;
import com.minelittlepony.unicopia.Unicopia;
import com.minelittlepony.unicopia.block.state.Schematic;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.recipe.TransformCropsRecipe;
import com.minelittlepony.unicopia.recipe.URecipes;
import io.github.mattidragon.tlaapi.api.gui.GuiBuilder;
import io.github.mattidragon.tlaapi.api.gui.TextureConfig;
import io.github.mattidragon.tlaapi.api.plugin.PluginContext;
import io.github.mattidragon.tlaapi.api.recipe.TlaIngredient;
import io.github.mattidragon.tlaapi.api.recipe.TlaRecipe;
import io.github.mattidragon.tlaapi.api.recipe.TlaStack;
import net.fabricmc.fabric.api.transfer.v1.item.ItemVariant;
import net.minecraft.block.Blocks;
import net.minecraft.client.MinecraftClient;
import net.minecraft.client.gui.DrawContext;
import net.minecraft.client.render.DiffuseLighting;
import net.minecraft.client.render.LightmapTextureManager;
import net.minecraft.client.render.OverlayTexture;
import net.minecraft.client.render.VertexConsumerProvider.Immediate;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.Items;
import net.minecraft.text.Text;
import net.minecraft.util.Identifier;
import net.minecraft.util.Util;
import net.minecraft.util.math.RotationAxis;
public class StructureInteractionTlaRecipe implements TlaRecipe {
static final TextureConfig PLUS = TextureConfig.builder().texture(Identifier.of("emi", "textures/gui/widgets.png")).size(13, 13).uv(82, 0).build();
private final RecipeCategory category;
private final Identifier id;
private final Schematic schematic;
private final List<TlaIngredient> allInputs;
private final List<TlaIngredient> inputs;
private final List<TlaIngredient> catalysts;
private final List<TlaStack> output;
private final TextureConfig processIcon;
private int age;
static void generateFarmingRecipes(RecipeCategory category, PluginContext context) {
context.addRecipeGenerator(URecipes.GROWING, recipe -> {
return new StructureInteractionTlaRecipe(
category,
recipe.id(),
new Schematic.Builder()
.fill(0, 0, 0, 6, 0, 6, recipe.value().getCatalystState())
.set(3, 0, 3, Blocks.FARMLAND.getDefaultState())
.set(3, 1, 3, recipe.value().getTargetState())
.build(),
List.of(
TlaStack.of(recipe.value().getTarget()).asIngredient(),
TlaStack.of(ItemVariant.of(recipe.value().getCatalyst()), TransformCropsRecipe.AREA).asIngredient()
),
TlaStack.of(recipe.value().getOutput()),
Unicopia.id("textures/gui/ability/grow.png")
);
});
}
static void generateAltarRecipes(RecipeCategory category, PluginContext context) {
context.addGenerator(client -> List.of(new StructureInteractionTlaRecipe(
category,
Unicopia.id("altar/spectral_clock"),
Schematic.ALTAR,
List.of(
TlaStack.of(Items.CLOCK).asIngredient(),
TlaStack.of(UItems.SPELLBOOK).asIngredient(),
TlaStack.of(Blocks.SOUL_SAND).asIngredient(),
TlaStack.of(Blocks.LODESTONE).asIngredient(),
TlaStack.of(Blocks.OBSIDIAN, 8 * 4 + 8).asIngredient()
),
TlaStack.of(UItems.SPECTRAL_CLOCK),
Unicopia.id("textures/gui/race/alicorn.png")
)));
}
public StructureInteractionTlaRecipe(RecipeCategory category, Identifier id, Schematic schematic, List<TlaIngredient> inputs, TlaStack output, Identifier processIcon) {
this.category = category;
this.id = id;
this.schematic = schematic;
this.allInputs = inputs;
this.inputs = inputs.stream().limit(1).toList();
this.catalysts = inputs.stream().skip(1).toList();
this.output = List.of(output);
this.processIcon = TextureConfig.builder().texture(processIcon).size(13, 13).regionSize(16, 16).textureSize(16, 16).build();
}
@Override
public RecipeCategory getCategory() {
return category;
}
@Override
public Identifier getId() {
return id;
}
@Override
public List<TlaIngredient> getInputs() {
return inputs;
}
@Override
public List<TlaStack> getOutputs() {
return output;
}
@Override
public List<TlaIngredient> getCatalysts() {
return catalysts;
}
/*@Override
public int getDisplayHeight() {
return schematic.dy() * 8 + 80 + 20 * (inputs.size() - 2);
}*/
@Override
public void buildGui(GuiBuilder widgets) {
int y = schematic.dy() * 8;
int row = 0;
age = 0;
widgets.addCustomWidget(10, y / 2, 100, 100, this::renderSchematic);
int x = 10;
for (int i = 0; i < allInputs.size(); i++) {
if (i > 1) {
x -= 40;
row += 20;
}
if (i > 0) {
widgets.addTexture(PLUS, x + 3, y + 53 + row);
x += 20;
}
var slot = widgets.addSlot(allInputs.get(i), x, y + 50 + row);
if (i > 0) {
slot.markCatalyst();
}
x += 20;
}
widgets.addArrow(73, y + 52, false);
widgets.addSlot(output.get(0), 100, y + 47).makeLarge().markOutput();
widgets.addTexture(processIcon, 73, y + 45).addTooltip(List.of(Text.translatable(
Util.createTranslationKey("recipe", category.getId()) + "." + "instruction"
)));
}
private void renderSchematic(DrawContext context, int mouseX, int mouseY, float delta) {
if (schematic.volume() == 0) {
return;
}
MatrixStack matrices = context.getMatrices();
Immediate immediate = context.getVertexConsumers();
MinecraftClient client = MinecraftClient.getInstance();
matrices.push();
float minSize = (Math.max(schematic.dz(), Math.max(schematic.dx(), schematic.dy())) + 1) * 16;
float scale = 60 / minSize;
matrices.scale(scale, scale, 1);
matrices.translate(95, 40, 100);
matrices.scale(16, -16, 16);
matrices.peek().getNormalMatrix().scale(1, -1, 1);
matrices.multiply(RotationAxis.POSITIVE_X.rotationDegrees(20));
matrices.peek().getPositionMatrix().rotate(RotationAxis.POSITIVE_Y.rotationDegrees(40));
matrices.translate(
(-schematic.dx() - 1) / 2F,
(-schematic.dy() - 1) / 2F,
(-schematic.dz() - 1) / 2F
);
DiffuseLighting.disableGuiDepthLighting();
age++;
for (var entry : schematic.states()) {
int x = entry.x() - schematic.dx() / 2;
int z = entry.z() - schematic.dz() / 2;
int distance = x * x + z * z;
if (age >= distance * 2) {
matrices.push();
matrices.translate(entry.x(), entry.y(), entry.z());
client.getBlockRenderManager().renderBlockAsEntity(entry.state(), matrices, immediate, LightmapTextureManager.MAX_LIGHT_COORDINATE, OverlayTexture.DEFAULT_UV);
matrices.pop();
}
}
matrices.pop();
}
}

View file

@ -0,0 +1,66 @@
package com.minelittlepony.unicopia.compat.tla;
import java.util.List;
import java.util.Optional;
import com.minelittlepony.unicopia.ability.magic.spell.trait.SpellTraits;
import com.minelittlepony.unicopia.ability.magic.spell.trait.Trait;
import com.minelittlepony.unicopia.client.gui.ItemTraitsTooltipRenderer;
import com.minelittlepony.unicopia.client.gui.spellbook.SpellbookScreen;
import com.minelittlepony.unicopia.container.inventory.HexagonalCraftingGrid;
import com.mojang.blaze3d.systems.RenderSystem;
import io.github.mattidragon.tlaapi.api.gui.GuiBuilder;
import io.github.mattidragon.tlaapi.api.gui.TlaBounds;
import io.github.mattidragon.tlaapi.api.recipe.TlaIngredient;
import io.github.mattidragon.tlaapi.impl.SimpleCustomTlaWidget;
import net.minecraft.client.MinecraftClient;
public record TraitedTlaIngredient(Optional<TraitEntry> trait, TlaIngredient ingredient) {
public static TraitedTlaIngredient of(Trait trait, float amount) {
return of(List.of(trait), amount);
}
public static TraitedTlaIngredient of(List<Trait> traits, float amount) {
return new TraitedTlaIngredient(Optional.of(new TraitEntry(traits, amount)),
TlaIngredient.ofItems(traits.stream().flatMap(SpellTraits::getItems).distinct().toList())
);
}
public static TraitedTlaIngredient of(TlaIngredient ingredient) {
return new TraitedTlaIngredient(Optional.empty(), ingredient);
}
public void buildGui(HexagonalCraftingGrid.Slot slot, GuiBuilder builder) {
buildGui(ingredient(), slot, builder);
}
public void buildGui(TlaIngredient ingredientOverride, HexagonalCraftingGrid.Slot slot, GuiBuilder builder) {
builder.addCustomWidget(new SlotTexture(slot));
builder.addSlot(ingredientOverride, slot.left(), slot.top()).disableBackground();
trait.ifPresent(traitEntry -> {
builder.addCustomWidget(slot.left(), slot.top(), 16, 16, (context, mouseX, mouseY, delta) -> {
int tick = (MinecraftClient.getInstance().player.age / 12) % traitEntry.traits().size();
Trait currentDisplayedTrait = traitEntry.traits().get(tick);
if (currentDisplayedTrait.getItems().isEmpty() || MinecraftClient.getInstance().player == null) {
ItemTraitsTooltipRenderer.renderTraitIcon(currentDisplayedTrait, traitEntry.amount(), context, 0, 0, true);
} else {
ItemTraitsTooltipRenderer.renderStackSingleTrait(currentDisplayedTrait, traitEntry.amount(), context, 0, 0, 1, delta, 0, true);
}
});
});
}
record TraitEntry(List<Trait> traits, float amount) { }
static class SlotTexture extends SimpleCustomTlaWidget {
public SlotTexture(HexagonalCraftingGrid.Slot slot) {
super((context, mouseX, mouseY, delta) -> {
RenderSystem.enableBlend();
context.drawTexture(SpellbookScreen.SLOT, 0, 0, 0, 0, 0, 32, 32, 32, 32);
RenderSystem.disableBlend();
}, new TlaBounds(slot.left() - 7, slot.top() - 7, 32, 32));
}
}
}

View file

@ -46,10 +46,10 @@
"item.unicopia.spellbook": "Spellbook",
"item.unicopia.spectral_clock": "Spectral Clock",
"emi.category.unicopia.spellbook": "Spellbook",
"emi.category.unicopia.cloud_shaping": "Shaping",
"emi.category.unicopia.growing": "Growing",
"emi.category.unicopia.altar": "Dark Ritual",
"tla.category.unicopia.spellbook": "Spellbook",
"tla.category.unicopia.cloud_shaping": "Shaping",
"tla.category.unicopia.growing": "Growing",
"tla.category.unicopia.altar": "Dark Ritual",
"recipe.unicopia.altar.instruction": "Cast item into flames",
"recipe.unicopia.growing.instruction": "Focus Earth Pony Magic",

View file

@ -30,8 +30,8 @@
"modmenu": [
"com.minelittlepony.unicopia.modmenu.UMenuFactory"
],
"emi": [
"com.minelittlepony.unicopia.compat.emi.Main"
"tla-api": [
"com.minelittlepony.unicopia.compat.tla.Main"
],
"minelittlepony": [
"com.minelittlepony.unicopia.client.minelittlepony.Main"