Added EMI support

This commit is contained in:
Sollace 2023-05-20 11:22:46 +01:00
parent 45d6e2c1f8
commit 188530c3ef
16 changed files with 607 additions and 34 deletions

View file

@ -63,6 +63,9 @@ dependencies {
modCompileOnly "com.terraformersmc:modmenu:${project.modmenu_version}" modCompileOnly "com.terraformersmc:modmenu:${project.modmenu_version}"
modCompileOnly "dev.emi:trinkets:${project.trinkets_version}" modCompileOnly "dev.emi:trinkets:${project.trinkets_version}"
//modCompileOnly "dev.emi:emi:${project.emi_version}:api"
modCompileOnly "dev.emi:emi:${project.emi_version}"
} }
processResources { processResources {

View file

@ -26,3 +26,4 @@ org.gradle.daemon=false
kirin_version=1.13.2 kirin_version=1.13.2
reach_attributes_version=2.3.1 reach_attributes_version=2.3.1
trinkets_version=3.5.0 trinkets_version=3.5.0
emi_version=0.7.3+1.19.3

View file

@ -24,9 +24,13 @@ public class SpellEnhancingRecipe implements SpellbookRecipe {
this.material = material; this.material = material;
} }
public IngredientWithSpell getBaseMaterial() {
return material;
}
@Override @Override
public void buildCraftingTree(CraftingTreeBuilder builder) { public void buildCraftingTree(CraftingTreeBuilder builder) {
builder.input(material.getMatchingStacks());
} }
@Override @Override

View file

@ -32,11 +32,15 @@ public interface SpellbookRecipe extends Recipe<SpellbookInventory> {
int getPriority(); int getPriority();
interface CraftingTreeBuilder { interface CraftingTreeBuilder {
void input(ItemStack...stack); void input(ItemStack...stacks);
void input(Trait...traits);
void input(Trait trait, float value); void input(Trait trait, float value);
void mystery(ItemStack...stacks); default void mystery(ItemStack...stacks) {
input(stacks);
}
void result(ItemStack...stack); void result(ItemStack...stack);

View file

@ -1,5 +1,6 @@
package com.minelittlepony.unicopia.ability.magic.spell.trait; package com.minelittlepony.unicopia.ability.magic.spell.trait;
import java.util.ArrayList;
import java.util.Arrays; import java.util.Arrays;
import java.util.Collection; import java.util.Collection;
import java.util.EnumMap; import java.util.EnumMap;
@ -39,9 +40,21 @@ public final class SpellTraits implements Iterable<Map.Entry<Trait, Float>> {
public static final SpellTraits EMPTY = new SpellTraits(Map.of()); public static final SpellTraits EMPTY = new SpellTraits(Map.of());
private static Map<Identifier, SpellTraits> REGISTRY = new HashMap<>(); private static Map<Identifier, SpellTraits> REGISTRY = new HashMap<>();
static final Map<Trait, List<Item>> ITEMS = new HashMap<>();
public static void load(Map<Identifier, SpellTraits> newRegistry) { public static void load(Map<Identifier, SpellTraits> newRegistry) {
REGISTRY = new HashMap<>(newRegistry); REGISTRY = new HashMap<>(newRegistry);
ITEMS.clear();
REGISTRY.forEach((itemId, traits) -> {
Registries.ITEM.getOrEmpty(itemId).ifPresent(item -> {
traits.forEach(entry -> {
List<Item> items = ITEMS.computeIfAbsent(entry.getKey(), k -> new ArrayList<>());
if (!items.contains(item)) {
items.add(item);
}
});
});
});
} }
public static Map<Identifier, SpellTraits> all() { public static Map<Identifier, SpellTraits> all() {

View file

@ -7,6 +7,7 @@ import java.util.stream.Stream;
import com.minelittlepony.unicopia.Unicopia; import com.minelittlepony.unicopia.Unicopia;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack; import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtElement;
import net.minecraft.nbt.NbtList; import net.minecraft.nbt.NbtList;
@ -63,6 +64,7 @@ public enum Trait {
private final Text tooltip; private final Text tooltip;
private final Text obfuscatedTooltip; private final Text obfuscatedTooltip;
private final List<Text> tooltipLines;
Trait(TraitGroup group) { Trait(TraitGroup group) {
this.id = Unicopia.id(name().toLowerCase(Locale.ROOT)); this.id = Unicopia.id(name().toLowerCase(Locale.ROOT));
@ -75,14 +77,17 @@ public enum Trait {
? Formatting.RED ? Formatting.RED
: Formatting.WHITE; : Formatting.WHITE;
MutableText tooltipText = Text.translatable("gui.unicopia.trait.label", tooltipLines = List.of(
Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".name") Text.translatable("gui.unicopia.trait.group", getGroup().name().toLowerCase(Locale.ROOT)).formatted(Formatting.ITALIC, Formatting.GRAY),
).formatted(Formatting.YELLOW) Text.empty(),
.append(Text.translatable("gui.unicopia.trait.group", getGroup().name().toLowerCase(Locale.ROOT)).formatted(Formatting.ITALIC, Formatting.GRAY)) Text.empty(),
.append(Text.literal("\n\n").formatted(Formatting.WHITE) Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".description").formatted(Formatting.GRAY),
.append(Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".description").formatted(Formatting.GRAY)) Text.empty(),
.append("\n") Text.translatable("gui.unicopia.trait.corruption", ItemStack.MODIFIER_FORMAT.format(getGroup().getCorruption())).formatted(Formatting.ITALIC, corruptionColor)
.append(Text.translatable("gui.unicopia.trait.corruption", ItemStack.MODIFIER_FORMAT.format(getGroup().getCorruption())).formatted(Formatting.ITALIC, corruptionColor))); );
MutableText tooltipText = getName().copy();
tooltipLines.forEach(line -> tooltipText.append(line).append("\n"));
this.tooltip = tooltipText; this.tooltip = tooltipText;
this.obfuscatedTooltip = tooltipText.copy().formatted(Formatting.OBFUSCATED); this.obfuscatedTooltip = tooltipText.copy().formatted(Formatting.OBFUSCATED);
@ -100,6 +105,16 @@ public enum Trait {
return sprite; return sprite;
} }
public Text getName() {
return Text.translatable("gui.unicopia.trait.label",
Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".name")
).formatted(Formatting.YELLOW);
}
public List<Text> getTooltipLines() {
return tooltipLines;
}
public Text getTooltip() { public Text getTooltip() {
return tooltip; return tooltip;
} }
@ -108,6 +123,10 @@ public enum Trait {
return obfuscatedTooltip; return obfuscatedTooltip;
} }
public List<Item> getItems() {
return SpellTraits.ITEMS.getOrDefault(this, List.of());
}
public static Collection<Trait> all() { public static Collection<Trait> all() {
return IDS.values(); return IDS.values();
} }

View file

@ -82,10 +82,10 @@ public class ItemTraitsTooltipRenderer implements Text, OrderedText, TooltipComp
} }
public static void renderStackTraits(ItemStack stack, MatrixStack matrices, float x, float y, float weight, float delta, int seed) { public static void renderStackTraits(ItemStack stack, MatrixStack matrices, float x, float y, float weight, float delta, int seed) {
renderStackTraits(SpellTraits.of(stack), matrices, x, y, weight, delta, seed); renderStackTraits(SpellTraits.of(stack), matrices, x, y, weight, delta, seed, false);
} }
public static void renderStackTraits(SpellTraits traits, MatrixStack matrices, float x, float y, float weight, float delta, int seed) { public static void renderStackTraits(SpellTraits traits, MatrixStack matrices, 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 time = MathHelper.cos((MinecraftClient.getInstance().player.age + delta + seed) / 2F) * 0.7F;
float angle = 0.7F + (time / 30F) % MathHelper.TAU; float angle = 0.7F + (time / 30F) % MathHelper.TAU;
@ -93,10 +93,11 @@ public class ItemTraitsTooltipRenderer implements Text, OrderedText, TooltipComp
float r = 9 + 2 * MathHelper.sin(delta / 20F); float r = 9 + 2 * MathHelper.sin(delta / 20F);
for (var entry : traits) { for (var entry : traits) {
if (isKnown(entry.getKey())) { if (revealAll || isKnown(entry.getKey())) {
ItemTraitsTooltipRenderer.renderTraitIcon(entry.getKey(), entry.getValue() * weight, matrices, ItemTraitsTooltipRenderer.renderTraitIcon(entry.getKey(), entry.getValue() * weight, matrices,
x + r * MathHelper.sin(angle), x + r * MathHelper.sin(angle),
y + r * MathHelper.cos(angle) y + r * MathHelper.cos(angle),
revealAll || isKnown(entry.getKey())
); );
angle += angleIncrement; angle += angleIncrement;
} }
@ -109,12 +110,16 @@ public class ItemTraitsTooltipRenderer implements Text, OrderedText, TooltipComp
} }
public static void renderTraitIcon(Trait trait, float value, MatrixStack matrices, float xx, float yy) { public static void renderTraitIcon(Trait trait, float value, MatrixStack matrices, float xx, float yy) {
renderTraitIcon(trait, value, matrices, xx, yy, isKnown(trait));
}
public static void renderTraitIcon(Trait trait, float value, MatrixStack matrices, float xx, float yy, boolean reveal) {
TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer; TextRenderer textRenderer = MinecraftClient.getInstance().textRenderer;
ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer(); ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
int size = 12; int size = 12;
RenderSystem.setShaderTexture(0, isKnown(trait) ? trait.getSprite() : UNKNOWN); RenderSystem.setShaderTexture(0, reveal ? trait.getSprite() : UNKNOWN);
matrices.push(); matrices.push();
matrices.translate(xx, yy, itemRenderer.zOffset + 300.0F); matrices.translate(xx, yy, itemRenderer.zOffset + 300.0F);

View file

@ -1,6 +1,7 @@
package com.minelittlepony.unicopia.client.gui.spellbook; package com.minelittlepony.unicopia.client.gui.spellbook;
import java.util.ArrayList; import java.util.ArrayList;
import java.util.Arrays;
import java.util.List; import java.util.List;
import java.util.Optional; import java.util.Optional;
@ -54,7 +55,14 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
@Override @Override
public void input(ItemStack... stacks) { public void input(ItemStack... stacks) {
if (stacks.length > 0) { if (stacks.length > 0) {
entries.add(new Stacks(stacks)); entries.add(Entry.of(stacks));
}
}
@Override
public void input(Trait... traits) {
if (traits.length > 0) {
entries.add(Entry.of(1, traits));
} }
} }
@ -68,14 +76,14 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
@Override @Override
public void mystery(ItemStack... stacks) { public void mystery(ItemStack... stacks) {
if (stacks.length > 0) { if (stacks.length > 0) {
entries.add(new HiddenStacks(stacks)); entries.add(Multiple.of(Arrays.stream(stacks).map(HiddenStacks::new).toArray(Entry[]::new)));
} }
} }
@Override @Override
public void result(ItemStack...stacks) { public void result(ItemStack...stacks) {
if (stacks.length > 0) { if (stacks.length > 0) {
result = Optional.of(new Stacks(stacks)); result = Optional.of(Entry.of(stacks));
} }
} }
@ -148,6 +156,15 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
} }
interface Entry { interface Entry {
static Entry of(ItemStack... stacks) {
return Multiple.of(Arrays.stream(stacks).map(Stacks::new).toArray(Entry[]::new));
}
static Entry of(float value, Trait... traits) {
return Multiple.of(Arrays.stream(traits).map(t -> new Traits(t, value)).toArray(Entry[]::new));
}
void render(MatrixStack matrices, int mouseX, int mouseY, float tickDelta); void render(MatrixStack matrices, int mouseX, int mouseY, float tickDelta);
Tooltip getTooltip(); Tooltip getTooltip();
@ -155,15 +172,37 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
void onClick(); void onClick();
} }
static class Stacks implements IngredientTree.Entry { static class Multiple implements IngredientTree.Entry {
private int ticker; private int ticker;
protected int index; protected int index;
protected final ItemStack[] stacks; protected final IngredientTree.Entry[] entries;
protected final ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer(); static final IngredientTree.Entry EMPTY = new IngredientTree.Entry() {
Stacks(ItemStack[] stacks) { @Override
this.stacks = stacks; public void render(MatrixStack matrices, int mouseX, int mouseY, float tickDelta) {}
@Override
public void onClick() { }
@Override
public Tooltip getTooltip() {
return List::of;
}
};
static IngredientTree.Entry of(IngredientTree.Entry... entries) {
if (entries.length == 0) {
return EMPTY;
}
if (entries.length == 1) {
return entries[0];
}
return new Multiple(entries);
}
Multiple(IngredientTree.Entry[] entries) {
this.entries = entries;
} }
@Override @Override
@ -171,25 +210,53 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
y -= 2; y -= 2;
if (ticker++ % 30 == 0) { if (ticker++ % 30 == 0) {
index = (index + 1) % stacks.length; index = (index + 1) % entries.length;
} }
entries[index].render(matrices, x, y, tickDelta);
}
@Override
public Tooltip getTooltip() {
return () -> entries[index].getTooltip().getLines();
}
@Override
public void onClick() {
entries[index].onClick();
}
}
static class Stacks implements IngredientTree.Entry {
protected final ItemStack stack;
protected final ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer();
Stacks(ItemStack stack) {
this.stack = stack;
}
@Override
public void render(MatrixStack matrices, int x, int y, float tickDelta) {
y -= 2;
Vector4f pos = new Vector4f(x, y, 0, 1); Vector4f pos = new Vector4f(x, y, 0, 1);
pos.mul(matrices.peek().getPositionMatrix()); pos.mul(matrices.peek().getPositionMatrix());
drawItem((int)pos.x, (int)pos.y); drawItem((int)pos.x, (int)pos.y);
} }
protected void drawItem(int x, int y) { protected void drawItem(int x, int y) {
itemRenderer.renderInGui(stacks[index], x, y); itemRenderer.renderInGui(stack, x, y);
} }
@Override @Override
public Tooltip getTooltip() { public Tooltip getTooltip() {
return () -> { return () -> {
if (stacks[index].isEmpty()) { if (stack.isEmpty()) {
return List.of(); return List.of();
} }
return stacks[index].getTooltip(MinecraftClient.getInstance().player, TooltipContext.Default.BASIC); return stack.getTooltip(MinecraftClient.getInstance().player, TooltipContext.Default.BASIC);
}; };
} }
@ -204,13 +271,13 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
parent.color(0, 0, 0, a); parent.color(0, 0, 0, a);
}); });
HiddenStacks(ItemStack[] stacks) { HiddenStacks(ItemStack stack) {
super(stacks); super(stack);
} }
@Override @Override
protected void drawItem(int x, int y) { protected void drawItem(int x, int y) {
var model = itemRenderer.getModel(stacks[index], null, null, 0); var model = itemRenderer.getModel(stack, null, null, 0);
MinecraftClient.getInstance().getTextureManager().getTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).setFilter(false, false); MinecraftClient.getInstance().getTextureManager().getTexture(PlayerScreenHandler.BLOCK_ATLAS_TEXTURE).setFilter(false, false);
RenderSystem.setShaderTexture(0, PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); RenderSystem.setShaderTexture(0, PlayerScreenHandler.BLOCK_ATLAS_TEXTURE);
@ -231,7 +298,7 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder {
} }
RenderSystem.disableDepthTest(); RenderSystem.disableDepthTest();
try { try {
itemRenderer.renderItem(stacks[index], ModelTransformation.Mode.GUI, false, new MatrixStack(), layer -> PassThroughVertexConsumer.of(immediate.getBuffer(layer), FIXTURE), 0, OverlayTexture.DEFAULT_UV, model); itemRenderer.renderItem(stack, ModelTransformation.Mode.GUI, false, new MatrixStack(), layer -> PassThroughVertexConsumer.of(immediate.getBuffer(layer), FIXTURE), 0, OverlayTexture.DEFAULT_UV, model);
immediate.draw(); immediate.draw();
} catch (Exception e) { } catch (Exception e) {
// Sodium // Sodium

View file

@ -0,0 +1,49 @@
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.Ingredient;
import net.minecraft.recipe.ShapedRecipe;
import net.minecraft.util.Identifier;
public class MagicalShapedEmiRecipe extends EmiCraftingRecipe {
public MagicalShapedEmiRecipe(ShapedRecipe recipe, CustomisedSpellType<?> spellEffect, ItemStack output) {
super(padIngredients(recipe, spellEffect), EmiStack.of(output),
new Identifier(recipe.getId().getNamespace(), recipe.getId().getPath() + "/" + spellEffect.type().getId().getPath()), false);
EmiShapedRecipe.setRemainders(input, recipe);
}
private static List<EmiIngredient> padIngredients(ShapedRecipe recipe, CustomisedSpellType<?> spellEffect) {
List<EmiIngredient> list = recipe.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 e) {
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

@ -0,0 +1,86 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.Arrays;
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.item.EnchantableItem;
import com.minelittlepony.unicopia.item.UItems;
import com.minelittlepony.unicopia.item.URecipes;
import com.minelittlepony.unicopia.item.group.MultiItem;
import dev.emi.emi.api.EmiPlugin;
import dev.emi.emi.api.EmiRegistry;
import dev.emi.emi.api.recipe.EmiRecipeCategory;
import dev.emi.emi.api.stack.Comparison;
import dev.emi.emi.api.stack.EmiStack;
import net.minecraft.item.ItemStack;
import net.minecraft.recipe.RecipeType;
import net.minecraft.util.Identifier;
public class Main implements EmiPlugin {
static final EmiStack SPELL_BOOK_STATION = EmiStack.of(UItems.SPELLBOOK);
static final EmiRecipeCategory SPELL_BOOK_CATEGORY = new EmiRecipeCategory(Unicopia.id("spellbook"), SPELL_BOOK_STATION, SPELL_BOOK_STATION);
static final Comparison COMPARING_BOTH = Comparison.builder().nbt(true).build();
@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 instanceof SpellDuplicatingRecipe) {
registry.addRecipe(new SpellDuplicatingEmiRecipe(recipe));
} else if (recipe instanceof SpellEnhancingRecipe enhancingRecipe) {
Trait.all().forEach(trait -> {
registry.addRecipe(new SpellDuplicatingEmiRecipe(recipe) {
private final Identifier id;
{
id = recipe.getId().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.builder().nbt(false).build()))
.toList()
);
}
@Nullable
@Override
public Identifier getId() {
return id;
}
});
});
} else {
registry.addRecipe(new SpellbookEmiRecipe(recipe));
}
});
Stream.of(UItems.GEMSTONE, UItems.BOTCHED_GEM, UItems.MAGIC_STAFF, UItems.FILLED_JAR).forEach(item -> {
registry.setDefaultComparison(item, comparison -> COMPARING_BOTH);
});
registry.getRecipeManager().listAllOfType(RecipeType.CRAFTING).stream()
.filter(recipe -> recipe instanceof SpellShapedCraftingRecipe)
.map(SpellShapedCraftingRecipe.class::cast).forEach(recipe -> {
ItemStack output = recipe.getOutput();
if (output.getItem() instanceof MultiItem multiItem && output.getItem() instanceof EnchantableItem enchantable) {
multiItem.getDefaultStacks().forEach(outputVariation -> {
var spellEffect = enchantable.getSpellEffect(outputVariation);
if (!spellEffect.isEmpty()) {
registry.addRecipe(new MagicalShapedEmiRecipe(recipe, spellEffect, outputVariation));
}
});
}
});
}
}

View file

@ -0,0 +1,62 @@
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 net.minecraft.client.MinecraftClient;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
public class SpellDuplicatingEmiRecipe extends SpellbookEmiRecipe {
public SpellDuplicatingEmiRecipe(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(MatrixStack matrices, int x, int y, float delta, int flags) {
if (maxCount < 2 || MinecraftClient.getInstance().player == null) {
super.render(matrices, 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(matrices, x, y, EmiPort.literal(count));
}
ingredients.get(tick).render(matrices, x, y, delta, flags & ~RENDER_AMOUNT);
}
}
}
}

View file

@ -0,0 +1,129 @@
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.render.EmiTexture;
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.util.math.MatrixStack;
import net.minecraft.item.ItemStack;
import net.minecraft.util.Identifier;
class SpellbookEmiRecipe implements EmiRecipe, SpellbookRecipe.CraftingTreeBuilder {
private final SpellbookRecipe recipe;
private final List<EmiIngredient> inputs = new ArrayList<>();
private final List<EmiStack> outputs = new ArrayList<>();
public SpellbookEmiRecipe(SpellbookRecipe recipe) {
this.recipe = recipe;
recipe.buildCraftingTree(this);
}
@Override
public EmiRecipeCategory getCategory() {
return Main.SPELL_BOOK_CATEGORY;
}
@Nullable
@Override
public Identifier getId() {
return recipe.getId();
}
@Override
public List<EmiIngredient> getInputs() {
return inputs;
}
@Override
public List<EmiStack> getOutputs() {
return outputs;
}
@Override
public int getDisplayWidth() {
return 220;
}
@Override
public int getDisplayHeight() {
return 145;
}
@Override
public void addWidgets(WidgetHolder widgets) {
widgets.addTexture(SpellbookScreen.TEXTURE, 0, 0, getDisplayWidth(), getDisplayHeight(), 50, 50, 128, 128, 512, 256);
widgets.addTexture(EmiTexture.EMPTY_ARROW, 160, 65);
List<HexagonalCraftingGrid.Slot> grid = new ArrayList<>();
List<HexagonalCraftingGrid.Slot> gem = new ArrayList<>();
HexagonalCraftingGrid.create(4, 35, 3, grid, gem);
int currentInput = 1;
for (int i = 0; i < grid.size(); i++) {
widgets.add(new SlotTexture(grid.get(i)));
if (currentInput < inputs.size() && grid.get(i).weight() == 1) {
widgets.addSlot(inputs.get(currentInput++), grid.get(i).left(), grid.get(i).top()).drawBack(false);
} else {
widgets.addSlot(grid.get(i).left(), grid.get(i).top()).drawBack(false);
}
}
widgets.addSlot(inputs.get(0), gem.get(0).left(), gem.get(0).top()).drawBack(false);
widgets.addSlot(getOutput(), 190, 60).output(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(MatrixStack matrices, int mouseX, int mouseY, float delta) {
RenderSystem.enableBlend();
super.render(matrices, mouseX, mouseY, delta);
}
}
}

View file

@ -0,0 +1,123 @@
package com.minelittlepony.unicopia.compat.emi;
import java.util.List;
import org.apache.commons.compress.utils.Lists;
import org.jetbrains.annotations.Nullable;
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.tooltip.TooltipComponent;
import net.minecraft.client.util.math.MatrixStack;
import net.minecraft.item.Item;
import net.minecraft.item.ItemStack;
import net.minecraft.nbt.NbtCompound;
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;
}
@Nullable
@Override
public NbtCompound getNbt() {
return null;
}
@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(MatrixStack matrices, 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, matrices, x, y, true);
} else {
int tick = (MinecraftClient.getInstance().player.age / 12) % knownItems.size();
ItemStack stack = knownItems.get(tick).getDefaultStack();
EmiStack.of(stack).render(matrices, x, y, delta, flags);
ItemTraitsTooltipRenderer.renderStackTraits(traits, matrices, x, y, 1, delta, 0, true);
}
}
if ((flags & RENDER_REMAINDER) != 0) {
EmiRender.renderRemainderIcon(this, matrices, 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

@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.item;
import java.util.HashMap; import java.util.HashMap;
import java.util.List; import java.util.List;
import java.util.Map; import java.util.Map;
import java.util.stream.Stream;
import javax.annotation.Nullable; import javax.annotation.Nullable;
@ -81,7 +82,10 @@ public class EnchantedStaffItem extends StaffItem implements EnchantableItem, Ch
@Override @Override
public List<ItemStack> getDefaultStacks() { public List<ItemStack> getDefaultStacks() {
return ENTITY_TYPE_TO_SPELL.values().stream().distinct().map(type -> EnchantableItem.enchant(getDefaultStack(), type)).toList(); return Stream.concat(
Stream.of(getDefaultStack()),
ENTITY_TYPE_TO_SPELL.values().stream().distinct().map(type -> EnchantableItem.enchant(getDefaultStack(), type))
).toList();
} }
@Override @Override

View file

@ -24,6 +24,7 @@
"item.unicopia.friendship_bracelet.glowing": "Glowing", "item.unicopia.friendship_bracelet.glowing": "Glowing",
"item.unicopia.spellbook": "Spellbook", "item.unicopia.spellbook": "Spellbook",
"emi.category.unicopia.spellbook": "Spellbook",
"item.unicopia.butterfly_spawn_egg": "Butterfly Spawn Egg", "item.unicopia.butterfly_spawn_egg": "Butterfly Spawn Egg",
"item.unicopia.butterfly": "Butterfly", "item.unicopia.butterfly": "Butterfly",

View file

@ -26,6 +26,9 @@
"modmenu": [ "modmenu": [
"com.minelittlepony.unicopia.modmenu.UMenuFactory" "com.minelittlepony.unicopia.modmenu.UMenuFactory"
], ],
"emi": [
"com.minelittlepony.unicopia.compat.emi.Main"
],
"minelittlepony": [ "minelittlepony": [
"com.minelittlepony.unicopia.client.minelittlepony.Main" "com.minelittlepony.unicopia.client.minelittlepony.Main"
] ]