diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java index 4ea379854..92d11a74b 100644 --- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java @@ -30,8 +30,10 @@ import dev.architectury.platform.Platform; import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap; import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.entry.type.BuiltinClientEntryTypes; import me.shedaniel.rei.api.client.favorites.FavoriteEntry; import me.shedaniel.rei.api.client.favorites.FavoriteEntryType; +import me.shedaniel.rei.api.client.gui.Renderer; import me.shedaniel.rei.api.client.plugins.REIClientPlugin; import me.shedaniel.rei.api.client.registry.category.CategoryRegistry; import me.shedaniel.rei.api.client.registry.display.DisplayRegistry; @@ -56,6 +58,7 @@ import me.shedaniel.rei.plugin.client.categories.beacon.DefaultBeaconPaymentCategory; import me.shedaniel.rei.plugin.client.categories.cooking.DefaultCookingCategory; import me.shedaniel.rei.plugin.client.categories.crafting.DefaultCraftingCategory; +import me.shedaniel.rei.plugin.client.categories.grindstone.DefaultGrindstoneCategory; import me.shedaniel.rei.plugin.client.categories.tag.DefaultTagCategory; import me.shedaniel.rei.plugin.client.displays.ClientsidedCookingDisplay; import me.shedaniel.rei.plugin.client.displays.ClientsidedCraftingDisplay; @@ -72,6 +75,8 @@ import me.shedaniel.rei.plugin.common.displays.beacon.DefaultBeaconPaymentDisplay; import me.shedaniel.rei.plugin.common.displays.brewing.BrewingRecipe; import me.shedaniel.rei.plugin.common.displays.brewing.DefaultBrewingDisplay; +import me.shedaniel.rei.plugin.common.displays.grindstone.DefaultGrindstoneDisplay; +import me.shedaniel.rei.plugin.common.displays.grindstone.GrindstoneRecipe; import me.shedaniel.rei.plugin.common.displays.tag.DefaultTagDisplay; import me.shedaniel.rei.plugin.common.displays.tag.TagNodes; import net.fabricmc.api.EnvType; @@ -220,6 +225,7 @@ public void registerCategories(CategoryRegistry registry) { new DefaultStrippingCategory(), new DefaultSmithingCategory(), new DefaultAnvilCategory(), + new DefaultGrindstoneCategory(), new DefaultBeaconBaseCategory(), new DefaultBeaconPaymentCategory(), new DefaultTillingCategory(), @@ -238,6 +244,7 @@ public void registerCategories(CategoryRegistry registry) { registry.addWorkstations(FUEL, EntryStacks.of(Items.FURNACE), EntryStacks.of(Items.SMOKER), EntryStacks.of(Items.BLAST_FURNACE)); registry.addWorkstations(BREWING, EntryStacks.of(Items.BREWING_STAND)); registry.addWorkstations(ANVIL, EntryStacks.of(Items.ANVIL)); + registry.addWorkstations(GRINDSTONE, EntryStacks.of(Items.GRINDSTONE)); registry.addWorkstations(STONE_CUTTING, EntryStacks.of(Items.STONECUTTER)); registry.addWorkstations(COMPOSTING, EntryStacks.of(Items.COMPOSTER)); registry.addWorkstations(SMITHING, EntryStacks.of(Items.SMITHING_TABLE)); @@ -313,6 +320,8 @@ public void registerDisplays(DisplayRegistry registry) { .fill(ClientsidedCookingDisplay.Blasting::new); registry.beginFiller(AnvilRecipe.class) .fill(DefaultAnvilDisplay::new); + registry.beginFiller(GrindstoneRecipe.class) + .fill(DefaultGrindstoneDisplay::new); registry.beginFiller(BrewingRecipe.class) .fill(DefaultBrewingDisplay::new); registry.beginFiller(TagKey.class) @@ -446,7 +455,7 @@ public void registerDisplays(DisplayRegistry registry) { EntryRegistry.getInstance().getEntryStacks().forEach(stack -> { if (stack.getType() != VanillaEntryTypes.ITEM) return; ItemStack itemStack = stack.castValue(); - if (!itemStack.isEnchantable()) return; + if (!itemStack.isEnchantable() && !itemStack.is(Items.ELYTRA) && !itemStack.is(Items.SHIELD)) return; for (Pair pair : enchantmentBooks) { if (!pair.getKey().enchantment().value().canEnchant(itemStack)) continue; Optional> output = DefaultAnvilDisplay.calculateOutput(itemStack, pair.getValue()); @@ -455,7 +464,45 @@ public void registerDisplays(DisplayRegistry registry) { Collections.singletonList(EntryIngredients.of(output.get().getLeft())), Optional.empty(), OptionalInt.of(output.get().getRight()))); } }); - + + // grindstone combining recipes + BuiltInRegistries.ITEM.forEach(item -> { + ItemStack base = item.getDefaultInstance(); + if (!base.isDamageableItem() || !item.components().has(DataComponents.REPAIRABLE)) return; + ItemStack a = base.copy(); + a.setDamageValue(base.getMaxDamage() - 1); + ItemStack b = a.copy(); + Optional output = DefaultGrindstoneDisplay.calculateOutput(a, b); + if (output.isEmpty()) return; + Renderer renderer = EntryStack.of(VanillaEntryTypes.ITEM.getDefinition(), output.get().itemStack()); + registry.add(new DefaultGrindstoneDisplay( + List.of(EntryIngredients.of(a), EntryIngredients.of(b)), + List.of(EntryIngredient.of(EntryStack.of(BuiltinClientEntryTypes.RENDERING, renderer))), + Optional.empty(), + OptionalDouble.of(output.get().averageExp()) + )); + }); + + // grindstone disenchanting recipes + EntryRegistry.getInstance().getEntryStacks().forEach(stack -> { + if (stack.getType() != VanillaEntryTypes.ITEM) return; + ItemStack itemStack = stack.castValue(); + if (!itemStack.isEnchantable() && !itemStack.is(Items.ELYTRA) && !itemStack.is(Items.SHIELD)) return; + for (Pair pair : enchantmentBooks) { + if (!pair.getKey().enchantment().value().canEnchant(itemStack)) continue; + Optional> input = DefaultAnvilDisplay.calculateOutput(itemStack, pair.getValue()); + if (input.isEmpty()) continue; + Optional output = DefaultGrindstoneDisplay.calculateOutput(input.get().getLeft(), ItemStack.EMPTY); + if (output.isEmpty()) continue; + Renderer renderer = EntryStack.of(VanillaEntryTypes.ITEM.getDefinition(), output.get().itemStack()); + registry.add(new DefaultGrindstoneDisplay( + List.of(EntryIngredients.of(input.get().getLeft()), EntryIngredient.empty()), + List.of(EntryIngredient.of(EntryStack.of(BuiltinClientEntryTypes.RENDERING, renderer))), + Optional.empty(), + OptionalDouble.of(output.get().averageExp()))); + } + }); + for (Registry reg : BuiltInRegistries.REGISTRY) { reg.getTags().forEach(tagPair -> tagPair.unwrap().ifLeft(registry::add)); } diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/grindstone/DefaultGrindstoneCategory.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/grindstone/DefaultGrindstoneCategory.java new file mode 100644 index 000000000..65a8be227 --- /dev/null +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/grindstone/DefaultGrindstoneCategory.java @@ -0,0 +1,101 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.plugin.client.categories.grindstone; + +import com.google.common.collect.Lists; +import me.shedaniel.math.Point; +import me.shedaniel.math.Rectangle; +import me.shedaniel.rei.api.client.gui.Renderer; +import me.shedaniel.rei.api.client.gui.widgets.Widget; +import me.shedaniel.rei.api.client.gui.widgets.Widgets; +import me.shedaniel.rei.api.client.registry.display.DisplayCategory; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.util.EntryStacks; +import me.shedaniel.rei.plugin.common.BuiltinPlugin; +import me.shedaniel.rei.plugin.common.displays.grindstone.DefaultGrindstoneDisplay; +import net.minecraft.client.Minecraft; +import net.minecraft.client.gui.Font; +import net.minecraft.network.chat.Component; +import net.minecraft.world.level.block.Blocks; + +import java.text.DecimalFormat; +import java.util.List; + +public class DefaultGrindstoneCategory implements DisplayCategory { + @Override + public CategoryIdentifier getCategoryIdentifier() { + return BuiltinPlugin.GRINDSTONE; + } + + @Override + public Component getTitle() { + return Component.translatable("block.minecraft.grindstone"); + } + + @Override + public Renderer getIcon() { + return EntryStacks.of(Blocks.GRINDSTONE); + } + + @Override + public List setupDisplay(DefaultGrindstoneDisplay display, Rectangle bounds) { + Point startPoint = new Point(bounds.getCenterX() - 31, bounds.getCenterY() - 20); + List widgets = Lists.newArrayList(); + widgets.add(Widgets.createRecipeBase(bounds)); + widgets.add(Widgets.createArrow(new Point(startPoint.x - 6, startPoint.y + 12))); + widgets.add(Widgets.createResultSlotBackground(new Point(startPoint.x + 30, startPoint.y + 13))); + widgets.add(Widgets.createSlot(new Point(startPoint.x - 30, startPoint.y + 2)).entries(display.getInputEntries().get(0)).markInput()); + widgets.add(Widgets.createSlot(new Point(startPoint.x - 30, startPoint.y + 23)).entries(display.getInputEntries().get(1)).markInput()); + widgets.add(Widgets.createSlot(new Point(startPoint.x + 30, startPoint.y + 13)).entries(display.getOutputEntries().get(0)).disableBackground().markOutput()); + if (display.getAverageXpReward().isPresent()) { + widgets.add(Widgets.createDrawableWidget((graphics, mouseX, mouseY, delta) -> { + Font font = Minecraft.getInstance().font; + DecimalFormat format = new DecimalFormat("0.#"); + Component componentXp = Component.translatable("category.rei.grindstone.xp", format.format(display.getAverageXpReward().getAsDouble())); + Component componentAverage = Component.translatable("category.rei.grindstone.average"); + int textboxLeftPos = startPoint.x + 102 - font.width(componentXp) - 2; + int textboxTopPos = startPoint.y + 14 - font.lineHeight / 2; + int xpLeftPos = textboxLeftPos - 2; + int xpTopPos = textboxTopPos; + int xpRightPos = startPoint.x + 102; + graphics.drawString(font, componentXp, textboxLeftPos, textboxTopPos + 2, 0x80ff20); + textboxLeftPos += font.width(componentXp) / 2 - font.width(componentAverage) / 2; + textboxTopPos += font.lineHeight + 3; + graphics.fill( + Math.min(xpLeftPos, textboxLeftPos - 2), + xpTopPos - 2, + Math.max(xpRightPos, + font.width(componentAverage)), + textboxTopPos + 12, + 0x4f000000); + graphics.drawString(font, componentAverage, textboxLeftPos, textboxTopPos + 2, 0x80ff20); + })); + } + return widgets; + } + + @Override + public int getDisplayHeight() { + return 47; + } +} diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/BuiltinPlugin.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/BuiltinPlugin.java index 5a7b3c4af..d57fee0a7 100644 --- a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/BuiltinPlugin.java +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/BuiltinPlugin.java @@ -31,6 +31,7 @@ import me.shedaniel.rei.plugin.common.displays.brewing.DefaultBrewingDisplay; import me.shedaniel.rei.plugin.common.displays.cooking.CookingDisplay; import me.shedaniel.rei.plugin.common.displays.crafting.CraftingDisplay; +import me.shedaniel.rei.plugin.common.displays.grindstone.DefaultGrindstoneDisplay; import me.shedaniel.rei.plugin.common.displays.tag.DefaultTagDisplay; import org.jetbrains.annotations.ApiStatus; @@ -47,6 +48,7 @@ public interface BuiltinPlugin { CategoryIdentifier FUEL = CategoryIdentifier.of("minecraft", "plugins/fuel"); CategoryIdentifier SMITHING = CategoryIdentifier.of("minecraft", "plugins/smithing"); CategoryIdentifier ANVIL = CategoryIdentifier.of("minecraft", "plugins/anvil"); + CategoryIdentifier GRINDSTONE = CategoryIdentifier.of("minecraft", "plugins/grindstone"); CategoryIdentifier BEACON_BASE = CategoryIdentifier.of("minecraft", "plugins/beacon_base"); CategoryIdentifier BEACON_PAYMENT = CategoryIdentifier.of("minecraft", "plugins/beacon_payment"); CategoryIdentifier TILLING = CategoryIdentifier.of("minecraft", "plugins/tilling"); diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/DefaultGrindstoneDisplay.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/DefaultGrindstoneDisplay.java new file mode 100644 index 000000000..c971d3132 --- /dev/null +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/DefaultGrindstoneDisplay.java @@ -0,0 +1,147 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.plugin.common.displays.grindstone; + +import com.mojang.serialization.Codec; +import com.mojang.serialization.codecs.RecordCodecBuilder; +import me.shedaniel.rei.api.common.category.CategoryIdentifier; +import me.shedaniel.rei.api.common.display.Display; +import me.shedaniel.rei.api.common.display.DisplaySerializer; +import me.shedaniel.rei.api.common.display.basic.BasicDisplay; +import me.shedaniel.rei.api.common.entry.EntryIngredient; +import me.shedaniel.rei.api.common.util.EntryIngredients; +import me.shedaniel.rei.plugin.common.BuiltinPlugin; +import net.fabricmc.api.EnvType; +import net.fabricmc.api.Environment; +import net.minecraft.client.Minecraft; +import net.minecraft.nbt.CompoundTag; +import net.minecraft.network.codec.ByteBufCodecs; +import net.minecraft.network.codec.StreamCodec; +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.entity.EntityEquipment; +import net.minecraft.world.entity.player.Inventory; +import net.minecraft.world.inventory.GrindstoneMenu; +import net.minecraft.world.inventory.Slot; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.ApiStatus; + +import java.util.*; + +public class DefaultGrindstoneDisplay extends BasicDisplay { + public static final DisplaySerializer SERIALIZER = DisplaySerializer.of( + RecordCodecBuilder.mapCodec(instance -> instance.group( + EntryIngredient.codec().listOf().fieldOf("inputs").forGetter(DefaultGrindstoneDisplay::getInputEntries), + EntryIngredient.codec().listOf().fieldOf("outputs").forGetter(DefaultGrindstoneDisplay::getOutputEntries), + ResourceLocation.CODEC.optionalFieldOf("location").forGetter(DefaultGrindstoneDisplay::getDisplayLocation), + Codec.DOUBLE.optionalFieldOf("average_xp_reward").forGetter(d -> d.averageXpReward.stream().boxed().findFirst()) + ).apply(instance, (inputs, outputs, location, averageXpReward) -> new DefaultGrindstoneDisplay(inputs, outputs, location, averageXpReward.stream().mapToDouble(d -> d).findFirst()))), + StreamCodec.composite( + EntryIngredient.streamCodec().apply(ByteBufCodecs.list()), + DefaultGrindstoneDisplay::getInputEntries, + EntryIngredient.streamCodec().apply(ByteBufCodecs.list()), + DefaultGrindstoneDisplay::getOutputEntries, + ByteBufCodecs.optional(ResourceLocation.STREAM_CODEC), + DefaultGrindstoneDisplay::getDisplayLocation, + ByteBufCodecs.optional(ByteBufCodecs.DOUBLE), + d -> d.averageXpReward.stream().boxed().findFirst(), + (inputs, outputs, location, averageXpReward) -> new DefaultGrindstoneDisplay(inputs, outputs, location, averageXpReward.stream().mapToDouble(d -> d).findFirst()) + )); + // cannot get the function immediately, since it is located in an anonymous class, so it is lazily initialized with mixins + private final OptionalDouble averageXpReward; + + public DefaultGrindstoneDisplay(GrindstoneRecipe recipe) { + this( + Arrays.asList( + EntryIngredients.ofItemStacks(recipe.getTopInput()), + EntryIngredients.ofItemStacks(recipe.getBottomInput()) + ), + Collections.singletonList(EntryIngredients.ofItemStacks(recipe.getOutputs())), + Optional.ofNullable(recipe.getId()), + recipe.getAverageXpReward() + ); + } + + public DefaultGrindstoneDisplay(List inputs, List outputs, Optional location) { + this(inputs, outputs, location, OptionalDouble.empty()); + } + + public DefaultGrindstoneDisplay(List inputs, List outputs, Optional location, CompoundTag tag) { + this(inputs, outputs, location, extractXpReward(tag)); + } + + private static OptionalDouble extractXpReward(CompoundTag tag) { + if (tag.contains("average_xp_reward")) { + Optional value = tag.getDouble("average_xp_reward"); + return value.map(OptionalDouble::of).orElseGet(OptionalDouble::empty); + } + return OptionalDouble.empty(); + } + + + public DefaultGrindstoneDisplay(List inputs, List outputs, Optional location, OptionalDouble averageXpReward) { + super(inputs, outputs, location); + this.averageXpReward = averageXpReward; + } + + @Override + public CategoryIdentifier getCategoryIdentifier() { + return BuiltinPlugin.GRINDSTONE; + } + + @Override + public DisplaySerializer getSerializer() { + return SERIALIZER; + } + + public OptionalDouble getAverageXpReward() { + return averageXpReward; + } + + @ApiStatus.Experimental + @ApiStatus.Internal + @Environment(EnvType.CLIENT) + public static Optional calculateOutput(ItemStack top, ItemStack bottom) { + try { + if (Minecraft.getInstance().player == null) return Optional.empty(); + GrindstoneMenu menu = new GrindstoneMenu(0, new Inventory(Minecraft.getInstance().player, new EntityEquipment())); + menu.setItem(0, menu.incrementStateId(), top); + menu.setItem(1, menu.incrementStateId(), bottom); + Slot outputSlot = menu.getSlot(2); + GrindstoneResultSlotAccessor outputSlotAccessor = (GrindstoneResultSlotAccessor) outputSlot; + int expTop = outputSlotAccessor.invokeGetExperienceFromItem(top); + int expBottom = outputSlotAccessor.invokeGetExperienceFromItem(bottom); + int maxExp = expTop + expBottom; + ItemStack outputStack = outputSlot.getItem().copy(); + if (!outputStack.isEmpty()) { + return Optional.of(new GrindStoneRecipeResult(outputStack, maxExp * 0.75)); // vanilla randomizes between 0.5 and 1.0 of max exp + } else { + return Optional.empty(); + } + } catch (Throwable ignored) { + return Optional.empty(); + } + } + + public record GrindStoneRecipeResult(ItemStack itemStack, double averageExp) {} +} diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/GrindstoneRecipe.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/GrindstoneRecipe.java new file mode 100644 index 000000000..2fce5f6f3 --- /dev/null +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/GrindstoneRecipe.java @@ -0,0 +1,72 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.plugin.common.displays.grindstone; + +import net.minecraft.resources.ResourceLocation; +import net.minecraft.world.item.ItemStack; +import org.jetbrains.annotations.Nullable; + +import java.util.List; +import java.util.OptionalDouble; + +public class GrindstoneRecipe { + @Nullable + private final ResourceLocation id; + private final List topInput; + private final List bottomInput; + private final List outputs; + private final OptionalDouble averageXpReward; + + public GrindstoneRecipe(@Nullable ResourceLocation id, List topInput, List bottomInput, List outputs) { + this(id, topInput, bottomInput, outputs, OptionalDouble.empty()); + } + + public GrindstoneRecipe(@Nullable ResourceLocation id, List topInput, List bottomInput, List outputs, OptionalDouble averageXpReward) { + this.id = id; + this.topInput = topInput; + this.bottomInput = bottomInput; + this.outputs = outputs; + this.averageXpReward = averageXpReward; + } + + public @Nullable ResourceLocation getId() { + return id; + } + + public List getTopInput() { + return topInput; + } + + public List getBottomInput() { + return bottomInput; + } + + public List getOutputs() { + return outputs; + } + + public OptionalDouble getAverageXpReward() { + return averageXpReward; + } +} diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/GrindstoneResultSlotAccessor.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/GrindstoneResultSlotAccessor.java new file mode 100644 index 000000000..9ed895593 --- /dev/null +++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/grindstone/GrindstoneResultSlotAccessor.java @@ -0,0 +1,7 @@ +package me.shedaniel.rei.plugin.common.displays.grindstone; + +import net.minecraft.world.item.ItemStack; + +public interface GrindstoneResultSlotAccessor { + int invokeGetExperienceFromItem(ItemStack stack); +} \ No newline at end of file diff --git a/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinGrindstoneResultSlotAccessor.java b/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinGrindstoneResultSlotAccessor.java new file mode 100644 index 000000000..df3f6c451 --- /dev/null +++ b/fabric/src/main/java/me/shedaniel/rei/mixin/fabric/MixinGrindstoneResultSlotAccessor.java @@ -0,0 +1,35 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.mixin.fabric; + +import me.shedaniel.rei.plugin.common.displays.grindstone.GrindstoneResultSlotAccessor; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(targets = "net.minecraft.world.inventory.GrindstoneMenu$4") +public interface MixinGrindstoneResultSlotAccessor extends GrindstoneResultSlotAccessor { + @Invoker("getExperienceFromItem") + int invokeGetExperienceFromItem(ItemStack stack); +} \ No newline at end of file diff --git a/fabric/src/main/resources/rei.mixins.json b/fabric/src/main/resources/rei.mixins.json index 85be50e95..542148d49 100644 --- a/fabric/src/main/resources/rei.mixins.json +++ b/fabric/src/main/resources/rei.mixins.json @@ -6,6 +6,7 @@ "client": [ "MixinClientPacketListener", "MixinEffectsInInventory", + "MixinGrindstoneResultSlotAccessor", "MixinInputConstants", "MixinInputConstantsKey", "MixinRecipeToast", diff --git a/fabric/src/main/resources/roughlyenoughitems.accessWidener b/fabric/src/main/resources/roughlyenoughitems.accessWidener index 84e1facb8..20dd7f94d 100644 --- a/fabric/src/main/resources/roughlyenoughitems.accessWidener +++ b/fabric/src/main/resources/roughlyenoughitems.accessWidener @@ -46,4 +46,4 @@ accessible field net/minecraft/client/gui/GuiGraphics scissorStack Lnet/minecraf mutable field net/minecraft/client/gui/GuiGraphics scissorStack Lnet/minecraft/client/gui/GuiGraphics$ScissorStack; accessible class net/minecraft/client/gui/GuiGraphics$ScissorStack accessible field net/minecraft/client/gui/GuiGraphics$ScissorStack stack Ljava/util/Deque; -accessible method net/minecraft/client/gui/GuiGraphics$ScissorStack ()V +accessible method net/minecraft/client/gui/GuiGraphics$ScissorStack ()V \ No newline at end of file diff --git a/forge/src/main/resources/META-INF/accesstransformer.cfg b/forge/src/main/resources/META-INF/accesstransformer.cfg index 6ec98af95..77c32b949 100644 --- a/forge/src/main/resources/META-INF/accesstransformer.cfg +++ b/forge/src/main/resources/META-INF/accesstransformer.cfg @@ -50,4 +50,4 @@ public net.minecraft.world.item.CreativeModeTab f_256824_ # displayItemsGenerato public net.minecraft.world.item.CreativeModeTab$ItemDisplayBuilder public net.minecraft.client.multiplayer.ClientLevel f_104561_ # connection public net.minecraft.client.multiplayer.MultiPlayerGameMode f_105190_ # connection -public net.minecraft.client.gui.GuiGraphics f_279627_ # bufferSource +public net.minecraft.client.gui.GuiGraphics f_279627_ # bufferSource \ No newline at end of file diff --git a/neoforge/src/main/java/me/shedaniel/rei/mixin/forge/MixinGrindstoneResultSlotAccessor.java b/neoforge/src/main/java/me/shedaniel/rei/mixin/forge/MixinGrindstoneResultSlotAccessor.java new file mode 100644 index 000000000..9f3bb1c50 --- /dev/null +++ b/neoforge/src/main/java/me/shedaniel/rei/mixin/forge/MixinGrindstoneResultSlotAccessor.java @@ -0,0 +1,35 @@ +/* + * This file is licensed under the MIT License, part of Roughly Enough Items. + * Copyright (c) 2018, 2019, 2020, 2021, 2022, 2023 shedaniel + * + * Permission is hereby granted, free of charge, to any person obtaining a copy + * of this software and associated documentation files (the "Software"), to deal + * in the Software without restriction, including without limitation the rights + * to use, copy, modify, merge, publish, distribute, sublicense, and/or sell + * copies of the Software, and to permit persons to whom the Software is + * furnished to do so, subject to the following conditions: + * + * The above copyright notice and this permission notice shall be included in all + * copies or substantial portions of the Software. + * + * THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, EXPRESS OR + * IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF MERCHANTABILITY, + * FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. IN NO EVENT SHALL THE + * AUTHORS OR COPYRIGHT HOLDERS BE LIABLE FOR ANY CLAIM, DAMAGES OR OTHER + * LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, ARISING FROM, + * OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR OTHER DEALINGS IN THE + * SOFTWARE. + */ + +package me.shedaniel.rei.mixin.forge; + +import me.shedaniel.rei.plugin.common.displays.grindstone.GrindstoneResultSlotAccessor; +import net.minecraft.world.item.ItemStack; +import org.spongepowered.asm.mixin.Mixin; +import org.spongepowered.asm.mixin.gen.Invoker; + +@Mixin(targets = "net.minecraft.world.inventory.GrindstoneMenu$4") +public interface MixinGrindstoneResultSlotAccessor extends GrindstoneResultSlotAccessor { + @Invoker("getExperienceFromItem") + int invokeGetExperienceFromItem(ItemStack stack); +} diff --git a/neoforge/src/main/resources/META-INF/accesstransformer.cfg b/neoforge/src/main/resources/META-INF/accesstransformer.cfg index 387e2bce5..139b112a6 100644 --- a/neoforge/src/main/resources/META-INF/accesstransformer.cfg +++ b/neoforge/src/main/resources/META-INF/accesstransformer.cfg @@ -55,4 +55,4 @@ public net.minecraft.client.gui.GuiGraphics bufferSource public-f net.minecraft.client.gui.GuiGraphics scissorStack public net.minecraft.client.gui.GuiGraphics$ScissorStack public net.minecraft.client.gui.GuiGraphics$ScissorStack stack -public net.minecraft.client.gui.GuiGraphics$ScissorStack ()V +public net.minecraft.client.gui.GuiGraphics$ScissorStack ()V \ No newline at end of file diff --git a/neoforge/src/main/resources/rei.mixins.json b/neoforge/src/main/resources/rei.mixins.json index 65be5712a..931a51dcb 100644 --- a/neoforge/src/main/resources/rei.mixins.json +++ b/neoforge/src/main/resources/rei.mixins.json @@ -6,6 +6,7 @@ "MixinClientPacketListener", "MixinEffectsInInventory", "MixinFontSet", + "MixinGrindstoneResultSlotAccessor", "MixinInputConstants", "MixinInputConstantsKey", "MixinRecipeToast" diff --git a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json index d6c807650..9e1807b8a 100755 --- a/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json +++ b/runtime/src/main/resources/assets/roughlyenoughitems/lang/en_us.json @@ -55,6 +55,8 @@ "category.rei.campfire": "Campfire", "category.rei.campfire.time": "%s sec", "category.rei.stone_cutting": "Stone Cutting", + "category.rei.grindstone.xp": "+%f XP", + "category.rei.grindstone.average": "avg.", "category.rei.brewing": "Brewing", "category.rei.brewing.input": "Original Potion", "category.rei.brewing.reactant": "Ingredient",