From 2978df2a511dad40c544683daa478ce4b6dc7a10 Mon Sep 17 00:00:00 2001 From: Anastasia Calico Date: Mon, 17 Nov 2025 08:53:29 +1100 Subject: [PATCH] feat: Implement REI modifications. --- fabric/build.gradle.kts | 2 +- .../rei/MendermanReiClientPlugin.java | 97 +++++++++++++++++++ .../Mixin_DefaultAnvilCategory.java | 32 ++++++ fabric/src/main/resources/fabric.mod.json | 3 + .../mixin/neoforge/Mixin_AnvilMenu.java | 3 +- .../menderman/api/MendermanRepairHelper.java | 2 +- .../MendermanRecipeViewerHelper.java | 2 +- .../integration/emi/MendermanEmiPlugin.java | 10 +- .../src/main/resources/menderman.mixins.json | 3 +- 9 files changed, 143 insertions(+), 11 deletions(-) create mode 100644 fabric/src/main/java/lgbt/greenhouse/menderman/integration/rei/MendermanReiClientPlugin.java create mode 100644 fabric/src/main/java/lgbt/greenhouse/menderman/mixin/integration/roughlyenoughitems/Mixin_DefaultAnvilCategory.java diff --git a/fabric/build.gradle.kts b/fabric/build.gradle.kts index 03509af..e523e82 100644 --- a/fabric/build.gradle.kts +++ b/fabric/build.gradle.kts @@ -23,7 +23,7 @@ dependencies { // Uncomment to test. CTRL + / will comment the current line. // modLocalRuntime(libs.jei.fabric) -// modLocalRuntime(libs.rei.fabric) + modLocalRuntime(libs.rei.fabric) // modLocalRuntime(libs.emi.fabric) } diff --git a/fabric/src/main/java/lgbt/greenhouse/menderman/integration/rei/MendermanReiClientPlugin.java b/fabric/src/main/java/lgbt/greenhouse/menderman/integration/rei/MendermanReiClientPlugin.java new file mode 100644 index 0000000..18482f0 --- /dev/null +++ b/fabric/src/main/java/lgbt/greenhouse/menderman/integration/rei/MendermanReiClientPlugin.java @@ -0,0 +1,97 @@ +package lgbt.greenhouse.menderman.integration.rei; + +import dev.architectury.event.EventResult; +import lgbt.greenhouse.menderman.api.MendermanRepairHelper; +import lgbt.greenhouse.menderman.api.component.MendermanDataComponents; +import lgbt.greenhouse.menderman.integration.MendermanRecipeViewerHelper; +import me.shedaniel.rei.api.client.plugins.REIClientPlugin; +import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.entry.EntryStack; +import me.shedaniel.rei.api.common.entry.type.VanillaEntryTypes; +import me.shedaniel.rei.api.common.util.EntryIngredients; +import me.shedaniel.rei.plugin.common.BuiltinPlugin; +import me.shedaniel.rei.plugin.common.displays.anvil.DefaultAnvilDisplay; +import net.minecraft.core.registries.BuiltInRegistries; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; + +import java.util.*; +import java.util.function.Consumer; + +public class MendermanReiClientPlugin implements REIClientPlugin { + @Override + public void registerDisplays(DisplayRegistry registry) { + registry.registerVisibilityPredicate((category, display) -> { + if (category.getCategoryIdentifier().equals(BuiltinPlugin.ANVIL) && display instanceof DefaultAnvilDisplay anvilDisplay) { + if (anvilDisplay.getDisplayLocation().isPresent()) { + return EventResult.pass(); + } + + EntryStack inputEntryStack = anvilDisplay.getInputEntries().getFirst().getFirst(); + EntryStack outputEntryStack = anvilDisplay.getOutputEntries().getFirst().getFirst(); + if (inputEntryStack.getType() != VanillaEntryTypes.ITEM || outputEntryStack.getType() != VanillaEntryTypes.ITEM) { + return EventResult.pass(); + } + + ItemStack inputStack = inputEntryStack.castValue(); + ItemStack outputStack = outputEntryStack.castValue(); + + ItemStack inputWithoutDamage = inputStack.copy(); + ItemStack outputWithoutDamage = outputStack.copy(); + + inputWithoutDamage.setDamageValue(0); + outputWithoutDamage.setDamageValue(0); + + if ( + ItemStack.isSameItemSameComponents(inputWithoutDamage, outputWithoutDamage) && + MendermanRecipeViewerHelper.getRepairStacks().stream().anyMatch(entry -> entry.repairable().is(inputStack.getItem())) || + inputStack.has(MendermanDataComponents.REPAIRABLE.get()) + ) { + return EventResult.pass(); + } + } + return EventResult.pass(); + }); + createMaterialRepairRecipes(registry); + } + + private static void createMaterialRepairRecipes(DisplayRegistry registry) { + List repairEntryMap = MendermanRecipeViewerHelper.getRepairStacks(); + + Consumer consumer = registry::add; + + for (MendermanRecipeViewerHelper.RepairEntry entry : repairEntryMap) { + addRepairEntryAnvilRecipes(consumer, entry); + } + } + + private static void addRepairEntryAnvilRecipes(Consumer addRecipeConsumer, MendermanRecipeViewerHelper.RepairEntry entry) { + ResourceLocation itemId = BuiltInRegistries.ITEM.getKey(entry.repairable().getItem()); + if (!entry.materials().isEmpty()) { + for (ItemStack material : entry.materials()) { + ResourceLocation materialId = BuiltInRegistries.ITEM.getKey(material.getItem()); + int maxRepairCount = MendermanRepairHelper.getMaximumItemRepairMaterialCount(entry.repairable(), material); + for (int i = 1; i <= maxRepairCount; ++i) { + ItemStack repairable = entry.repairable().copy(); + repairable.setDamageValue(i == 1 ? repairable.getMaxDamage() - 1 : + repairable.getMaxDamage() - MendermanRepairHelper.getRepairRequirementsRepairAmount(repairable, material.copyWithCount(i - 1)).orElse(0)); + + ItemStack countedMaterial = material.copyWithCount(i); + + ItemStack repaired = entry.repairable().copy(); + repaired.setDamageValue(repairable.getMaxDamage() - MendermanRepairHelper.getRepairRequirementsRepairAmount(repairable, countedMaterial) + .orElse(repairable.getMaxDamage())); + + addRecipeConsumer.accept(new DefaultAnvilDisplay( + List.of(EntryIngredients.of(repairable), EntryIngredients.of(countedMaterial)), + List.of(EntryIngredients.of(repaired)), + Optional.of(ResourceLocation.fromNamespaceAndPath("menderman", "anvil/repair/" + itemId.getNamespace() + "/" + itemId.getPath() + + "/material/" + materialId.getNamespace() + "/" + materialId.getPath() + "/cost/" + i)), + OptionalInt.of(i) + )); + } + } + } + } +} diff --git a/fabric/src/main/java/lgbt/greenhouse/menderman/mixin/integration/roughlyenoughitems/Mixin_DefaultAnvilCategory.java b/fabric/src/main/java/lgbt/greenhouse/menderman/mixin/integration/roughlyenoughitems/Mixin_DefaultAnvilCategory.java new file mode 100644 index 0000000..8afb126 --- /dev/null +++ b/fabric/src/main/java/lgbt/greenhouse/menderman/mixin/integration/roughlyenoughitems/Mixin_DefaultAnvilCategory.java @@ -0,0 +1,32 @@ +package lgbt.greenhouse.menderman.mixin.integration.roughlyenoughitems; + +import com.llamalad7.mixinextras.injector.ModifyExpressionValue; +import com.llamalad7.mixinextras.sugar.Local; +import lgbt.greenhouse.menderman.api.gamerule.MendermanGamerules; +import lgbt.greenhouse.menderman.client.network.MendermanLangHelper; +import me.shedaniel.rei.plugin.client.categories.anvil.DefaultAnvilCategory; +import me.shedaniel.rei.plugin.common.displays.anvil.DefaultAnvilDisplay; +import net.minecraft.client.Minecraft; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.injection.At; +import org.spongepowered.asm.mixin.injection.ModifyArg; + +@SuppressWarnings({"unused", "UnusedMixin"}) // Exists in xplat mixins.json. +@Mixin(value = DefaultAnvilCategory.class, remap = false) +public class Mixin_DefaultAnvilCategory { + @ModifyExpressionValue(method = "setupDisplay(Lme/shedaniel/rei/plugin/common/displays/anvil/DefaultAnvilDisplay;Lme/shedaniel/math/Rectangle;)Ljava/util/List;", at = @At(value = "INVOKE", target = "Ljava/util/OptionalInt;isPresent()Z")) + private boolean menderman$dontDisplayRepairCostWhenZero(boolean original, @Local(argsOnly = true) DefaultAnvilDisplay anvilDisplay) { + assert Minecraft.getInstance().level != null; + return original && (MendermanGamerules.doesRepairingCostExperience(Minecraft.getInstance().level) || anvilDisplay.getOutputEntries().isEmpty() || anvilDisplay.getOutputEntries().getFirst().isEmpty() || + !(anvilDisplay.getOutputEntries().getFirst().getFirst().getValue() instanceof ItemStack stack) || !stack.getEnchantments().isEmpty()); + } + + @ModifyArg(method = "lambda$setupDisplay$0", at = @At(value = "INVOKE", target = "Lnet/minecraft/network/chat/Component;translatable(Ljava/lang/String;[Ljava/lang/Object;)Lnet/minecraft/network/chat/MutableComponent;"), index = 0) + private static String menderman$modifyReiRepairCostMessage(String key, @Local(argsOnly = true) DefaultAnvilDisplay anvilDisplay) { + if ((!(anvilDisplay.getOutputEntries().getFirst().getFirst().getValue() instanceof ItemStack stack) || !stack.getEnchantments().isEmpty()) && MendermanLangHelper.hasLangEntry("container.menderman.repair.cost", key)) { + return "container.menderman.repair.cost"; + } + return key; + } +} diff --git a/fabric/src/main/resources/fabric.mod.json b/fabric/src/main/resources/fabric.mod.json index 8c859dd..2b622a3 100644 --- a/fabric/src/main/resources/fabric.mod.json +++ b/fabric/src/main/resources/fabric.mod.json @@ -22,6 +22,9 @@ "main": [ "lgbt.greenhouse.menderman.MendermanFabric" ], + "rei_client": [ + "lgbt.greenhouse.menderman.integration.rei.MendermanReiClientPlugin" + ], "emi": [ "lgbt.greenhouse.menderman.integration.emi.MendermanEmiPlugin" ] diff --git a/neoforge/src/main/java/lgbt/greenhouse/menderman/mixin/neoforge/Mixin_AnvilMenu.java b/neoforge/src/main/java/lgbt/greenhouse/menderman/mixin/neoforge/Mixin_AnvilMenu.java index 3863d2a..3fbab77 100644 --- a/neoforge/src/main/java/lgbt/greenhouse/menderman/mixin/neoforge/Mixin_AnvilMenu.java +++ b/neoforge/src/main/java/lgbt/greenhouse/menderman/mixin/neoforge/Mixin_AnvilMenu.java @@ -7,13 +7,14 @@ import lgbt.greenhouse.menderman.api.MendermanRepairHelper; import lgbt.greenhouse.menderman.api.component.MendermanDataComponents; import net.minecraft.world.inventory.AnvilMenu; import net.minecraft.world.item.ItemStack; +import org.objectweb.asm.Opcodes; import org.spongepowered.asm.mixin.Mixin; import org.spongepowered.asm.mixin.injection.At; import org.spongepowered.asm.mixin.injection.ModifyVariable; @Mixin(AnvilMenu.class) public class Mixin_AnvilMenu { - @ModifyExpressionValue(method = "lambda$onTake$2", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/player/Abilities;instabuild:Z")) + @ModifyExpressionValue(method = "lambda$onTake$2", at = @At(value = "FIELD", target = "Lnet/minecraft/world/entity/player/Abilities;instabuild:Z", opcode = Opcodes.GETFIELD)) private static boolean menderman$dontDamageAnvil(boolean original) { if (Menderman.shouldDamageAnvil) { Menderman.shouldDamageAnvil = false; diff --git a/xplat/src/main/java/lgbt/greenhouse/menderman/api/MendermanRepairHelper.java b/xplat/src/main/java/lgbt/greenhouse/menderman/api/MendermanRepairHelper.java index 440329d..ceadd52 100644 --- a/xplat/src/main/java/lgbt/greenhouse/menderman/api/MendermanRepairHelper.java +++ b/xplat/src/main/java/lgbt/greenhouse/menderman/api/MendermanRepairHelper.java @@ -14,7 +14,7 @@ public class MendermanRepairHelper { if (stack.has(MendermanDataComponents.REPAIR_REQUIREMENTS.get())) { RepairRequirements requirements = stack.get(MendermanDataComponents.REPAIR_REQUIREMENTS.get()); assert requirements != null; - return OptionalInt.of(stack.getMaxDamage() / getMaximumItemRepairMaterialCount(stack, repairStack) * clampRepairCount(stack, repairStack)); + return OptionalInt.of(Mth.ceil((float)stack.getMaxDamage() / getMaximumItemRepairMaterialCount(stack, repairStack) * clampRepairCount(stack, repairStack))); } return OptionalInt.empty(); } diff --git a/xplat/src/main/java/lgbt/greenhouse/menderman/integration/MendermanRecipeViewerHelper.java b/xplat/src/main/java/lgbt/greenhouse/menderman/integration/MendermanRecipeViewerHelper.java index dcdc4e7..41783e3 100644 --- a/xplat/src/main/java/lgbt/greenhouse/menderman/integration/MendermanRecipeViewerHelper.java +++ b/xplat/src/main/java/lgbt/greenhouse/menderman/integration/MendermanRecipeViewerHelper.java @@ -11,7 +11,7 @@ import net.minecraft.world.item.ItemStack; import java.util.*; public class MendermanRecipeViewerHelper { - private static final Set RECIPE_VIEWERS_WITH_REPAIR_RECIPES = Set.of("emi", "jei"); + private static final Set RECIPE_VIEWERS_WITH_REPAIR_RECIPES = Set.of("emi", "jei", "roughlyenoughitems"); public static final Map> EXPECTED_REPAIRABLE_VALUES = new LinkedHashMap<>(); public static boolean hasRecipeViewer() { diff --git a/xplat/src/main/java/lgbt/greenhouse/menderman/integration/emi/MendermanEmiPlugin.java b/xplat/src/main/java/lgbt/greenhouse/menderman/integration/emi/MendermanEmiPlugin.java index c30e71b..1eb6d17 100644 --- a/xplat/src/main/java/lgbt/greenhouse/menderman/integration/emi/MendermanEmiPlugin.java +++ b/xplat/src/main/java/lgbt/greenhouse/menderman/integration/emi/MendermanEmiPlugin.java @@ -8,6 +8,7 @@ import dev.emi.emi.api.recipe.EmiRecipe; import dev.emi.emi.api.stack.EmiIngredient; import dev.emi.emi.api.stack.EmiStack; import dev.emi.emi.recipe.EmiAnvilRecipe; +import lgbt.greenhouse.menderman.api.component.MendermanDataComponents; import lgbt.greenhouse.menderman.integration.MendermanRecipeViewerHelper; import lgbt.greenhouse.menderman.integration.emi.recipe.MendermanEmiAnvilRecipe; import lgbt.greenhouse.menderman.integration.emi.recipe.MendermanEmiAnvilRepairItemRecipe; @@ -29,7 +30,7 @@ public class MendermanEmiPlugin implements EmiPlugin { if (emiRecipe.getId() != null && emiRecipe.getId().getNamespace().equals("emi") && (emiRecipe.getId().getPath().startsWith("/anvil/repairing/material/") || emiRecipe.getId().getPath().startsWith("/anvil/repairing/tool/") || emiRecipe.getId().getPath().startsWith("/anvil/repairing/grindstone/"))) { ItemStack stack = emiRecipe.getInputs().getFirst().getEmiStacks().getFirst().getItemStack(); - return MendermanRecipeViewerHelper.getRepairStacks().stream().anyMatch(entry -> entry.repairable().is(stack.getItem())); + return MendermanRecipeViewerHelper.getRepairStacks().stream().anyMatch(entry -> entry.repairable().is(stack.getItem())) || stack.has(MendermanDataComponents.REPAIRABLE.get()); } return false; }); @@ -38,14 +39,11 @@ public class MendermanEmiPlugin implements EmiPlugin { } private static void createMaterialRepairRecipes(EmiRegistry emiRegistry) { - Map repairEntryMap = MendermanRecipeViewerHelper.getRepairStacks() - .stream() - .map(repairEntry -> Pair.of(repairEntry.repairable().getItem(), repairEntry)) - .collect(Collectors.toUnmodifiableMap(Pair::getFirst, Pair::getSecond)); + List repairEntryMap = MendermanRecipeViewerHelper.getRepairStacks(); Consumer consumer = emiRegistry::addRecipe; - for (MendermanRecipeViewerHelper.RepairEntry entry : repairEntryMap.values()) { + for (MendermanRecipeViewerHelper.RepairEntry entry : repairEntryMap) { addRepairEntryAnvilRecipes(consumer, entry); addRepairEntryGrindstoneRecipe(consumer, entry); } diff --git a/xplat/src/main/resources/menderman.mixins.json b/xplat/src/main/resources/menderman.mixins.json index e7d45d7..8036b28 100644 --- a/xplat/src/main/resources/menderman.mixins.json +++ b/xplat/src/main/resources/menderman.mixins.json @@ -13,7 +13,8 @@ "integration.emi.Mixin_EmiRecipes", "integration.jei.Mixin_AnvilRecipeCategory", "integration.jei.Mixin_AnvilRecipeMaker", - "integration.jei.Mixin_IRecipeCategory" + "integration.jei.Mixin_IRecipeCategory", + "integration.roughlyenoughitems.Mixin_DefaultAnvilCategory" ], "client": [ "client.Mixin_AnvilScreen", -- 2.51.2