From 188530c3efaccb27412834aed3e6d04b08a822f4 Mon Sep 17 00:00:00 2001 From: Sollace Date: Sat, 20 May 2023 11:22:46 +0100 Subject: [PATCH] Added EMI support --- build.gradle | 3 + gradle.properties | 1 + .../spell/crafting/SpellEnhancingRecipe.java | 6 +- .../magic/spell/crafting/SpellbookRecipe.java | 8 +- .../magic/spell/trait/SpellTraits.java | 13 ++ .../ability/magic/spell/trait/Trait.java | 35 +++-- .../client/gui/ItemTraitsTooltipRenderer.java | 15 +- .../client/gui/spellbook/IngredientTree.java | 99 +++++++++++--- .../compat/emi/MagicalShapedEmiRecipe.java | 49 +++++++ .../unicopia/compat/emi/Main.java | 86 ++++++++++++ .../compat/emi/SpellDuplicatingEmiRecipe.java | 62 +++++++++ .../compat/emi/SpellbookEmiRecipe.java | 129 ++++++++++++++++++ .../unicopia/compat/emi/TraitEmiStack.java | 123 +++++++++++++++++ .../unicopia/item/EnchantedStaffItem.java | 6 +- .../resources/assets/unicopia/lang/en_us.json | 1 + src/main/resources/fabric.mod.json | 5 +- 16 files changed, 607 insertions(+), 34 deletions(-) create mode 100644 src/main/java/com/minelittlepony/unicopia/compat/emi/MagicalShapedEmiRecipe.java create mode 100644 src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java create mode 100644 src/main/java/com/minelittlepony/unicopia/compat/emi/SpellDuplicatingEmiRecipe.java create mode 100644 src/main/java/com/minelittlepony/unicopia/compat/emi/SpellbookEmiRecipe.java create mode 100644 src/main/java/com/minelittlepony/unicopia/compat/emi/TraitEmiStack.java diff --git a/build.gradle b/build.gradle index 9395e243..15d181af 100644 --- a/build.gradle +++ b/build.gradle @@ -63,6 +63,9 @@ dependencies { modCompileOnly "com.terraformersmc:modmenu:${project.modmenu_version}" modCompileOnly "dev.emi:trinkets:${project.trinkets_version}" + + //modCompileOnly "dev.emi:emi:${project.emi_version}:api" + modCompileOnly "dev.emi:emi:${project.emi_version}" } processResources { diff --git a/gradle.properties b/gradle.properties index c098ac62..675c481a 100644 --- a/gradle.properties +++ b/gradle.properties @@ -26,3 +26,4 @@ org.gradle.daemon=false kirin_version=1.13.2 reach_attributes_version=2.3.1 trinkets_version=3.5.0 + emi_version=0.7.3+1.19.3 diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java index 4e9879cf..2bd1c445 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellEnhancingRecipe.java @@ -24,9 +24,13 @@ public class SpellEnhancingRecipe implements SpellbookRecipe { this.material = material; } + public IngredientWithSpell getBaseMaterial() { + return material; + } + @Override public void buildCraftingTree(CraftingTreeBuilder builder) { - + builder.input(material.getMatchingStacks()); } @Override diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java index f66e4cd8..89043ce8 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/crafting/SpellbookRecipe.java @@ -32,11 +32,15 @@ public interface SpellbookRecipe extends Recipe { int getPriority(); interface CraftingTreeBuilder { - void input(ItemStack...stack); + void input(ItemStack...stacks); + + void input(Trait...traits); void input(Trait trait, float value); - void mystery(ItemStack...stacks); + default void mystery(ItemStack...stacks) { + input(stacks); + } void result(ItemStack...stack); diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java index 13881e53..ae16b248 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/SpellTraits.java @@ -1,5 +1,6 @@ package com.minelittlepony.unicopia.ability.magic.spell.trait; +import java.util.ArrayList; import java.util.Arrays; import java.util.Collection; import java.util.EnumMap; @@ -39,9 +40,21 @@ public final class SpellTraits implements Iterable> { public static final SpellTraits EMPTY = new SpellTraits(Map.of()); private static Map REGISTRY = new HashMap<>(); + static final Map> ITEMS = new HashMap<>(); public static void load(Map newRegistry) { REGISTRY = new HashMap<>(newRegistry); + ITEMS.clear(); + REGISTRY.forEach((itemId, traits) -> { + Registries.ITEM.getOrEmpty(itemId).ifPresent(item -> { + traits.forEach(entry -> { + List items = ITEMS.computeIfAbsent(entry.getKey(), k -> new ArrayList<>()); + if (!items.contains(item)) { + items.add(item); + } + }); + }); + }); } public static Map all() { diff --git a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java index f3efece9..ee39fa7e 100644 --- a/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java +++ b/src/main/java/com/minelittlepony/unicopia/ability/magic/spell/trait/Trait.java @@ -7,6 +7,7 @@ import java.util.stream.Stream; import com.minelittlepony.unicopia.Unicopia; +import net.minecraft.item.Item; import net.minecraft.item.ItemStack; import net.minecraft.nbt.NbtElement; import net.minecraft.nbt.NbtList; @@ -63,6 +64,7 @@ public enum Trait { private final Text tooltip; private final Text obfuscatedTooltip; + private final List tooltipLines; Trait(TraitGroup group) { this.id = Unicopia.id(name().toLowerCase(Locale.ROOT)); @@ -75,14 +77,17 @@ public enum Trait { ? Formatting.RED : Formatting.WHITE; - MutableText tooltipText = Text.translatable("gui.unicopia.trait.label", - Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".name") - ).formatted(Formatting.YELLOW) - .append(Text.translatable("gui.unicopia.trait.group", getGroup().name().toLowerCase(Locale.ROOT)).formatted(Formatting.ITALIC, Formatting.GRAY)) - .append(Text.literal("\n\n").formatted(Formatting.WHITE) - .append(Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".description").formatted(Formatting.GRAY)) - .append("\n") - .append(Text.translatable("gui.unicopia.trait.corruption", ItemStack.MODIFIER_FORMAT.format(getGroup().getCorruption())).formatted(Formatting.ITALIC, corruptionColor))); + tooltipLines = List.of( + Text.translatable("gui.unicopia.trait.group", getGroup().name().toLowerCase(Locale.ROOT)).formatted(Formatting.ITALIC, Formatting.GRAY), + Text.empty(), + Text.empty(), + Text.translatable("trait." + getId().getNamespace() + "." + getId().getPath() + ".description").formatted(Formatting.GRAY), + Text.empty(), + 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.obfuscatedTooltip = tooltipText.copy().formatted(Formatting.OBFUSCATED); @@ -100,6 +105,16 @@ public enum Trait { 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 getTooltipLines() { + return tooltipLines; + } + public Text getTooltip() { return tooltip; } @@ -108,6 +123,10 @@ public enum Trait { return obfuscatedTooltip; } + public List getItems() { + return SpellTraits.ITEMS.getOrDefault(this, List.of()); + } + public static Collection all() { return IDS.values(); } diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/ItemTraitsTooltipRenderer.java b/src/main/java/com/minelittlepony/unicopia/client/gui/ItemTraitsTooltipRenderer.java index faab1666..0a765c07 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/ItemTraitsTooltipRenderer.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/ItemTraitsTooltipRenderer.java @@ -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) { - 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 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); for (var entry : traits) { - if (isKnown(entry.getKey())) { + if (revealAll || isKnown(entry.getKey())) { ItemTraitsTooltipRenderer.renderTraitIcon(entry.getKey(), entry.getValue() * weight, matrices, x + r * MathHelper.sin(angle), - y + r * MathHelper.cos(angle) + y + r * MathHelper.cos(angle), + revealAll || isKnown(entry.getKey()) ); 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) { + 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; ItemRenderer itemRenderer = MinecraftClient.getInstance().getItemRenderer(); int size = 12; - RenderSystem.setShaderTexture(0, isKnown(trait) ? trait.getSprite() : UNKNOWN); + RenderSystem.setShaderTexture(0, reveal ? trait.getSprite() : UNKNOWN); matrices.push(); matrices.translate(xx, yy, itemRenderer.zOffset + 300.0F); diff --git a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/IngredientTree.java b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/IngredientTree.java index fdeb89e2..132dea4e 100644 --- a/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/IngredientTree.java +++ b/src/main/java/com/minelittlepony/unicopia/client/gui/spellbook/IngredientTree.java @@ -1,6 +1,7 @@ package com.minelittlepony.unicopia.client.gui.spellbook; import java.util.ArrayList; +import java.util.Arrays; import java.util.List; import java.util.Optional; @@ -54,7 +55,14 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder { @Override public void input(ItemStack... stacks) { 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 public void mystery(ItemStack... stacks) { if (stacks.length > 0) { - entries.add(new HiddenStacks(stacks)); + entries.add(Multiple.of(Arrays.stream(stacks).map(HiddenStacks::new).toArray(Entry[]::new))); } } @Override public void result(ItemStack...stacks) { 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 { + + 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); Tooltip getTooltip(); @@ -155,15 +172,37 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder { void onClick(); } - static class Stacks implements IngredientTree.Entry { + static class Multiple implements IngredientTree.Entry { private int ticker; 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) { - this.stacks = stacks; + @Override + 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 @@ -171,25 +210,53 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder { y -= 2; 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); pos.mul(matrices.peek().getPositionMatrix()); drawItem((int)pos.x, (int)pos.y); } protected void drawItem(int x, int y) { - itemRenderer.renderInGui(stacks[index], x, y); + itemRenderer.renderInGui(stack, x, y); } @Override public Tooltip getTooltip() { return () -> { - if (stacks[index].isEmpty()) { + if (stack.isEmpty()) { 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); }); - HiddenStacks(ItemStack[] stacks) { - super(stacks); + HiddenStacks(ItemStack stack) { + super(stack); } @Override 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); RenderSystem.setShaderTexture(0, PlayerScreenHandler.BLOCK_ATLAS_TEXTURE); @@ -231,7 +298,7 @@ class IngredientTree implements SpellbookRecipe.CraftingTreeBuilder { } RenderSystem.disableDepthTest(); 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(); } catch (Exception e) { // Sodium diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/MagicalShapedEmiRecipe.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/MagicalShapedEmiRecipe.java new file mode 100644 index 00000000..06f0ff26 --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/MagicalShapedEmiRecipe.java @@ -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 padIngredients(ShapedRecipe recipe, CustomisedSpellType spellEffect) { + List 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); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java new file mode 100644 index 00000000..f768646e --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/Main.java @@ -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)); + } + }); + } + }); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/SpellDuplicatingEmiRecipe.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/SpellDuplicatingEmiRecipe.java new file mode 100644 index 00000000..ef029add --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/SpellDuplicatingEmiRecipe.java @@ -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 ingredients; + private final int maxCount; + + public CyclingRecipeIngredient(List 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); + } + } + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/SpellbookEmiRecipe.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/SpellbookEmiRecipe.java new file mode 100644 index 00000000..ea7a24fa --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/SpellbookEmiRecipe.java @@ -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 inputs = new ArrayList<>(); + private final List 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 getInputs() { + return inputs; + } + + @Override + public List 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 grid = new ArrayList<>(); + List 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); + } + } +} \ No newline at end of file diff --git a/src/main/java/com/minelittlepony/unicopia/compat/emi/TraitEmiStack.java b/src/main/java/com/minelittlepony/unicopia/compat/emi/TraitEmiStack.java new file mode 100644 index 00000000..62e2de6c --- /dev/null +++ b/src/main/java/com/minelittlepony/unicopia/compat/emi/TraitEmiStack.java @@ -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 getTooltipText() { + return trait.getTooltipLines(); + } + + @Override + public List getTooltip() { + List 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 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); + } +} diff --git a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java index 9244a542..591c7533 100644 --- a/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java +++ b/src/main/java/com/minelittlepony/unicopia/item/EnchantedStaffItem.java @@ -3,6 +3,7 @@ package com.minelittlepony.unicopia.item; import java.util.HashMap; import java.util.List; import java.util.Map; +import java.util.stream.Stream; import javax.annotation.Nullable; @@ -81,7 +82,10 @@ public class EnchantedStaffItem extends StaffItem implements EnchantableItem, Ch @Override public List 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 diff --git a/src/main/resources/assets/unicopia/lang/en_us.json b/src/main/resources/assets/unicopia/lang/en_us.json index 48e0f660..6384f31b 100644 --- a/src/main/resources/assets/unicopia/lang/en_us.json +++ b/src/main/resources/assets/unicopia/lang/en_us.json @@ -24,6 +24,7 @@ "item.unicopia.friendship_bracelet.glowing": "Glowing", "item.unicopia.spellbook": "Spellbook", + "emi.category.unicopia.spellbook": "Spellbook", "item.unicopia.butterfly_spawn_egg": "Butterfly Spawn Egg", "item.unicopia.butterfly": "Butterfly", diff --git a/src/main/resources/fabric.mod.json b/src/main/resources/fabric.mod.json index a1cdf6f6..b5dbb05f 100644 --- a/src/main/resources/fabric.mod.json +++ b/src/main/resources/fabric.mod.json @@ -17,7 +17,7 @@ "accessWidener": "unicopia.aw", "environment": "*", "entrypoints": { - "main": [ + "main": [ "com.minelittlepony.unicopia.Unicopia" ], "client": [ @@ -26,6 +26,9 @@ "modmenu": [ "com.minelittlepony.unicopia.modmenu.UMenuFactory" ], + "emi": [ + "com.minelittlepony.unicopia.compat.emi.Main" + ], "minelittlepony": [ "com.minelittlepony.unicopia.client.minelittlepony.Main" ]