aboutsummaryrefslogtreecommitdiff
path: root/default-plugin/src/main/java
diff options
context:
space:
mode:
authorshedaniel <daniel@shedaniel.me>2024-04-16 01:42:49 +0900
committershedaniel <daniel@shedaniel.me>2024-04-16 01:42:49 +0900
commit9bdb9cb7c0a03accb6f86bcedff4b22f38f1346f (patch)
treebc7557eeebd32609fc8b26db28f613e5fd576366 /default-plugin/src/main/java
parentf59653ff2a7c3641624928478a3622e6ffd32889 (diff)
parent8981a2f730942e169ba5efa2f25ad3066dc0f797 (diff)
downloadRoughlyEnoughItems-9bdb9cb7c0a03accb6f86bcedff4b22f38f1346f.tar.gz
RoughlyEnoughItems-9bdb9cb7c0a03accb6f86bcedff4b22f38f1346f.tar.bz2
RoughlyEnoughItems-9bdb9cb7c0a03accb6f86bcedff4b22f38f1346f.zip
Merge branch 'feature/12.1' into feature/13.1
# Conflicts: # gradle.properties # runtime/src/main/java/me/shedaniel/rei/impl/client/gui/changelog/ChangelogLoader.java # runtime/src/main/java/me/shedaniel/rei/impl/client/gui/screen/UncertainDisplayViewingScreen.java # runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/EntryWidget.java # runtime/src/main/java/me/shedaniel/rei/impl/client/gui/widget/OverflowWidget.java
Diffstat (limited to 'default-plugin/src/main/java')
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java62
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ArmorDyeRecipeFiller.java87
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BannerDuplicateRecipeFiller.java70
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BookCloningRecipeFiller.java104
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/CraftingRecipeFiller.java92
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/FireworkRocketRecipeFiller.java78
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapCloningRecipeFiller.java52
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapExtendingRecipeFiller.java85
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ShieldDecorationRecipeFiller.java110
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ShulkerBoxColoringFiller.java60
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/SuspiciousStewRecipeFiller.java63
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/TippedArrowRecipeFiller.java75
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/DefaultPlugin.java1
13 files changed, 919 insertions, 20 deletions
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 ea61c622f..2764ca379 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
@@ -27,6 +27,7 @@ import com.google.common.collect.*;
import dev.architectury.event.EventResult;
import dev.architectury.networking.NetworkManager;
import dev.architectury.platform.Platform;
+import it.unimi.dsi.fastutil.objects.Object2FloatMap;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
import it.unimi.dsi.fastutil.objects.ReferenceSet;
@@ -36,6 +37,7 @@ import me.shedaniel.rei.api.client.favorites.FavoriteEntryType;
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;
+import me.shedaniel.rei.api.client.registry.entry.CollapsibleEntryRegistry;
import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
import me.shedaniel.rei.api.client.registry.screen.ExclusionZones;
import me.shedaniel.rei.api.client.registry.screen.ScreenRegistry;
@@ -43,6 +45,7 @@ import me.shedaniel.rei.api.client.registry.transfer.TransferHandlerRegistry;
import me.shedaniel.rei.api.client.registry.transfer.simple.SimpleTransferHandler;
import me.shedaniel.rei.api.common.entry.EntryIngredient;
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.api.common.util.EntryStacks;
import me.shedaniel.rei.impl.ClientInternals;
@@ -54,6 +57,7 @@ import me.shedaniel.rei.plugin.client.categories.beacon.DefaultBeaconBaseCategor
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.crafting.filler.*;
import me.shedaniel.rei.plugin.client.categories.tag.DefaultTagCategory;
import me.shedaniel.rei.plugin.client.exclusionzones.DefaultPotionEffectExclusionZones;
import me.shedaniel.rei.plugin.client.exclusionzones.DefaultRecipeBookExclusionZones;
@@ -72,7 +76,6 @@ import me.shedaniel.rei.plugin.common.displays.cooking.DefaultBlastingDisplay;
import me.shedaniel.rei.plugin.common.displays.cooking.DefaultSmeltingDisplay;
import me.shedaniel.rei.plugin.common.displays.cooking.DefaultSmokingDisplay;
import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay;
-import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomDisplay;
import me.shedaniel.rei.plugin.common.displays.tag.DefaultTagDisplay;
import me.shedaniel.rei.plugin.common.displays.tag.TagNodes;
import net.fabricmc.api.EnvType;
@@ -113,6 +116,19 @@ import java.util.stream.Stream;
@Environment(EnvType.CLIENT)
@ApiStatus.Internal
public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin {
+ private static final CraftingRecipeFiller<?>[] CRAFTING_RECIPE_FILLERS = new CraftingRecipeFiller[]{
+ new TippedArrowRecipeFiller(),
+ new ShulkerBoxColoringFiller(),
+ new BannerDuplicateRecipeFiller(),
+ new ShieldDecorationRecipeFiller(),
+ new SuspiciousStewRecipeFiller(),
+ new BookCloningRecipeFiller(),
+ new FireworkRocketRecipeFiller(),
+ new ArmorDyeRecipeFiller(),
+ new MapCloningRecipeFiller(),
+ new MapExtendingRecipeFiller()
+ };
+
public DefaultClientPlugin() {
ClientInternals.attachInstance((Supplier<Object>) () -> this, "builtinClientPlugin");
}
@@ -178,6 +194,24 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
}
@Override
+ public void registerCollapsibleEntries(CollapsibleEntryRegistry registry) {
+ registry.group(new ResourceLocation("roughlyenoughitems", "enchanted_book"), Component.translatable("item.minecraft.enchanted_book"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().is(Items.ENCHANTED_BOOK));
+ registry.group(new ResourceLocation("roughlyenoughitems", "potion"), Component.translatable("item.minecraft.potion"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().is(Items.POTION));
+ registry.group(new ResourceLocation("roughlyenoughitems", "splash_potion"), Component.translatable("item.minecraft.splash_potion"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().is(Items.SPLASH_POTION));
+ registry.group(new ResourceLocation("roughlyenoughitems", "lingering_potion"), Component.translatable("item.minecraft.lingering_potion"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().is(Items.LINGERING_POTION));
+ registry.group(new ResourceLocation("roughlyenoughitems", "spawn_egg"), Component.translatable("text.rei.spawn_egg"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().getItem() instanceof SpawnEggItem);
+ registry.group(new ResourceLocation("roughlyenoughitems", "tipped_arrow"), Component.translatable("item.minecraft.tipped_arrow"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().is(Items.TIPPED_ARROW));
+ registry.group(new ResourceLocation("roughlyenoughitems", "music_disc"), Component.translatable("text.rei.music_disc"),
+ stack -> stack.getType() == VanillaEntryTypes.ITEM && stack.<ItemStack>castValue().getItem() instanceof RecordItem);
+ }
+
+ @Override
public void registerCategories(CategoryRegistry registry) {
registry.add(
new DefaultCraftingCategory(),
@@ -228,6 +262,10 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
return EventResult.pass();
});
+ for (CraftingRecipeFiller<?> filler : CRAFTING_RECIPE_FILLERS) {
+ filler.registerCategories(registry);
+ }
+
Set<Item> axes = Sets.newHashSet(), hoes = Sets.newHashSet(), shovels = Sets.newHashSet();
EntryRegistry.getInstance().getEntryStacks().filter(stack -> stack.getValueType() == ItemStack.class).map(stack -> ((ItemStack) stack.getValue()).getItem()).forEach(item -> {
if (item instanceof AxeItem && axes.add(item)) {
@@ -289,25 +327,9 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
for (Map.Entry<Item, Integer> entry : AbstractFurnaceBlockEntity.getFuel().entrySet()) {
registry.add(new DefaultFuelDisplay(Collections.singletonList(EntryIngredients.of(entry.getKey())), Collections.emptyList(), entry.getValue()));
}
- EntryIngredient arrowStack = EntryIngredient.of(EntryStacks.of(Items.ARROW));
- ReferenceSet<Potion> registeredPotions = new ReferenceOpenHashSet<>();
- EntryRegistry.getInstance().getEntryStacks().filter(entry -> entry.getValueType() == ItemStack.class && entry.<ItemStack>castValue().getItem() == Items.LINGERING_POTION).forEach(entry -> {
- ItemStack itemStack = (ItemStack) entry.getValue();
- Potion potion = PotionUtils.getPotion(itemStack);
- if (registeredPotions.add(potion)) {
- List<EntryIngredient> input = new ArrayList<>();
- for (int i = 0; i < 4; i++)
- input.add(arrowStack);
- input.add(EntryIngredients.of(itemStack));
- for (int i = 0; i < 4; i++)
- input.add(arrowStack);
- ItemStack outputStack = new ItemStack(Items.TIPPED_ARROW, 8);
- PotionUtils.setPotion(outputStack, potion);
- PotionUtils.setCustomEffects(outputStack, PotionUtils.getCustomEffects(itemStack));
- EntryIngredient output = EntryIngredients.of(outputStack);
- registry.add(new DefaultCustomDisplay(null, input, Collections.singletonList(output)));
- }
- });
+ for (CraftingRecipeFiller<?> filler : CRAFTING_RECIPE_FILLERS) {
+ filler.registerDisplays(registry);
+ }
if (ComposterBlock.COMPOSTABLES.isEmpty()) {
ComposterBlock.bootStrap();
}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ArmorDyeRecipeFiller.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ArmorDyeRecipeFiller.java
new file mode 100644
index 000000000..2582beb51
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ArmorDyeRecipeFiller.java
@@ -0,0 +1,87 @@
+/*
+ * 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.crafting.filler;
+
+import me.shedaniel.rei.api.client.registry.entry.EntryRegistry;
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.EntryIngredients;
+import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomShapelessDisplay;
+import net.minecraft.world.item.DyeColor;
+import net.minecraft.world.item.DyeItem;
+import net.minecraft.world.item.DyeableLeatherItem;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.ArmorDyeRecipe;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+public class ArmorDyeRecipeFiller implements CraftingRecipeFiller<ArmorDyeRecipe> {
+ @Override
+ public Collection<Display> apply(ArmorDyeRecipe recipe) {
+ List<Display> displays = new ArrayList<>();
+ List<EntryStack<?>> toDye = EntryRegistry.getInstance().getEntryStacks().filter(entry -> entry.getValueType() == ItemStack.class && entry.<ItemStack>castValue().getItem() instanceof DyeableLeatherItem).toList();
+ DyeColor[] colors = DyeColor.values();
+
+ for (EntryStack<?> armor : toDye) {
+ ItemStack armorStack = armor.castValue();
+ for (DyeColor color : colors) {
+ ItemStack output = armorStack.copy();
+ DyeItem dyeItem = DyeItem.byColor(color);
+ output = DyeableLeatherItem.dyeArmor(output, List.of(dyeItem));
+ displays.add(new DefaultCustomShapelessDisplay(recipe,
+ List.of(EntryIngredient.of(armor.copy()),
+ EntryIngredients.of(dyeItem)),
+ List.of(EntryIngredients.of(output))));
+ }
+
+ for (int i = 0; i < 9; i++) {
+ int dyes = new Random().nextInt(2) + 2;
+ List<EntryIngredient> inputs = new ArrayList<>();
+ List<DyeItem> dyeItems = new ArrayList<>();
+ inputs.add(EntryIngredient.of(armor.copy()));
+ for (int j = 0; j < dyes; j++) {
+ DyeColor color = colors[new Random().nextInt(colors.length)];
+ DyeItem dyeItem = DyeItem.byColor(color);
+ dyeItems.add(dyeItem);
+ inputs.add(EntryIngredients.of(dyeItem));
+ }
+ ItemStack output = armorStack.copy();
+ output = DyeableLeatherItem.dyeArmor(output, dyeItems);
+ displays.add(new DefaultCustomShapelessDisplay(recipe,
+ inputs, List.of(EntryIngredients.of(output))));
+ }
+ }
+
+ return displays;
+ }
+
+ @Override
+ public Class<ArmorDyeRecipe> getRecipeClass() {
+ return ArmorDyeRecipe.class;
+ }
+}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BannerDuplicateRecipeFiller.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BannerDuplicateRecipeFiller.java
new file mode 100644
index 000000000..127355293
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BannerDuplicateRecipeFiller.java
@@ -0,0 +1,70 @@
+/*
+ * 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.crafting.filler;
+
+import com.mojang.datafixers.util.Pair;
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomShapelessDisplay;
+import net.minecraft.core.registries.BuiltInRegistries;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.DyeColor;
+import net.minecraft.world.item.Item;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.crafting.BannerDuplicateRecipe;
+
+import java.util.*;
+
+public class BannerDuplicateRecipeFiller implements CraftingRecipeFiller<BannerDuplicateRecipe> {
+ @Override
+ public Collection<Display> apply(BannerDuplicateRecipe recipe) {
+ List<Display> displays = new ArrayList<>();
+ Map<DyeColor, Pair<EntryIngredient.Builder, EntryStack<?>>> displayMap = new HashMap<>();
+
+ for (Pair<DyeColor, ItemStack> pair : ShieldDecorationRecipeFiller.randomizeBanners()) {
+ Optional<Item> bannerOptional = BuiltInRegistries.ITEM.getOptional(new ResourceLocation(pair.getFirst().getName() + "_banner"));
+ if (bannerOptional.isEmpty()) continue;
+ Pair<EntryIngredient.Builder, EntryStack<?>> builderPair = displayMap.computeIfAbsent(pair.getFirst(), color -> Pair.of(EntryIngredient.builder(), EntryStacks.of(bannerOptional.get())));
+ builderPair.getFirst().add(EntryStacks.of(pair.getSecond()));
+ }
+
+ for (Pair<EntryIngredient.Builder, EntryStack<?>> pair : displayMap.values()) {
+ EntryIngredient inputsFirst = pair.getFirst().build();
+ EntryStack<?> inputsSecond = pair.getSecond();
+ EntryIngredient.unifyFocuses(inputsFirst);
+ displays.add(new DefaultCustomShapelessDisplay(recipe,
+ List.of(inputsFirst, EntryIngredient.of(inputsSecond)),
+ List.of(inputsFirst)));
+ }
+
+ return displays;
+ }
+
+ @Override
+ public Class<BannerDuplicateRecipe> getRecipeClass() {
+ return BannerDuplicateRecipe.class;
+ }
+}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BookCloningRecipeFiller.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BookCloningRecipeFiller.java
new file mode 100644
index 000000000..74b3c0101
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BookCloningRecipeFiller.java
@@ -0,0 +1,104 @@
+/*
+ * 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.crafting.filler;
+
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.api.common.util.CollectionUtils;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomShapelessDisplay;
+import net.minecraft.nbt.IntTag;
+import net.minecraft.nbt.StringTag;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.WrittenBookItem;
+import net.minecraft.world.item.crafting.BookCloningRecipe;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+import java.util.Random;
+
+public class BookCloningRecipeFiller implements CraftingRecipeFiller<BookCloningRecipe> {
+ private static final String[] TITLES = new String[]{
+ "Adventurer's Dreams", "Adventurer's Diary", "The Lost Journal",
+ "The Lost Diary", "The Lost Book", "The Lost Tome", "The Lost Codex",
+ "The Last Journal", "The Last Diary", "The Last Book", "The Last Tome",
+ "Secrets of the World", "Secrets of the Universe", "Secrets of the Cosmos",
+ "Myths of the World", "Myths of the Universe", "Myths of the Cosmos",
+ "Old Tales of the World", "Old Tales of the Universe", "Old Tales of the Cosmos",
+ "The World of the Legends", "The Universe of the Heroes", "The Cosmos of the Gods",
+ "Diary of a Villager", "Diary of a Farmer", "Diary of a Fisherman",
+ "Dungeon Journal", "Dungeon Diary", "Dungeon Book", "Dungeon Tome",
+ "Dunk Memes", "Top 10 Memes of 2019", "Top 10 Memes of 2020",
+ "Plastic Memories", "Kizumonogatari"
+ };
+ private static final String[] AUTHORS = new String[]{
+ "shedaniel", "Steve", "Alex", "Notch", "Herobrine", "God",
+ "Santa Claus", "The Easter Bunny", "The Tooth Fairy"
+ };
+
+ @Override
+ public Collection<Display> apply(BookCloningRecipe recipe) {
+ List<Display> displays = new ArrayList<>();
+
+ for (int i = 1; i <= 8; i++) {
+ EntryIngredient.Builder[] inputs = new EntryIngredient.Builder[9];
+ for (int j = 0; j < 9; j++) {
+ inputs[j] = EntryIngredient.builder();
+ }
+ EntryIngredient.Builder output = EntryIngredient.builder();
+ for (int j = 0; j < 10; j++) {
+ ItemStack writtenBook = generateBook();
+ ItemStack bookAndQuill = new ItemStack(Items.WRITABLE_BOOK);
+ inputs[0].add(EntryStacks.of(writtenBook));
+ for (int k = 0; k < i; k++) {
+ inputs[k + 1].add(EntryStacks.of(bookAndQuill));
+ }
+ ItemStack cloned = writtenBook.copy();
+ cloned.addTagElement(WrittenBookItem.TAG_GENERATION, IntTag.valueOf(1));
+ cloned.setCount(i);
+ output.add(EntryStacks.of(cloned));
+ }
+ displays.add(new DefaultCustomShapelessDisplay(recipe,
+ CollectionUtils.map(inputs, EntryIngredient.Builder::build),
+ List.of(output.build())));
+ }
+
+ return displays;
+ }
+
+ private ItemStack generateBook() {
+ ItemStack stack = new ItemStack(Items.WRITTEN_BOOK);
+ stack.addTagElement(WrittenBookItem.TAG_AUTHOR, StringTag.valueOf(AUTHORS[new Random().nextInt(AUTHORS.length)]));
+ stack.addTagElement(WrittenBookItem.TAG_TITLE, StringTag.valueOf(TITLES[new Random().nextInt(TITLES.length)]));
+ stack.addTagElement(WrittenBookItem.TAG_GENERATION, IntTag.valueOf(0));
+ return stack;
+ }
+
+ @Override
+ public Class<BookCloningRecipe> getRecipeClass() {
+ return BookCloningRecipe.class;
+ }
+}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/CraftingRecipeFiller.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/CraftingRecipeFiller.java
new file mode 100644
index 000000000..ac575045b
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/CraftingRecipeFiller.java
@@ -0,0 +1,92 @@
+/*
+ * 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.crafting.filler;
+
+import me.shedaniel.math.Point;
+import me.shedaniel.math.Rectangle;
+import me.shedaniel.rei.api.client.gui.DisplayRenderer;
+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.category.CategoryRegistry;
+import me.shedaniel.rei.api.client.registry.display.DisplayCategoryView;
+import me.shedaniel.rei.api.client.registry.display.DisplayRegistry;
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.plugin.common.BuiltinPlugin;
+import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCraftingDisplay;
+import net.minecraft.network.chat.Component;
+import net.minecraft.resources.ResourceLocation;
+import net.minecraft.world.item.crafting.CraftingRecipe;
+import net.minecraft.world.item.crafting.RecipeType;
+
+import java.util.Collection;
+import java.util.List;
+import java.util.function.Function;
+
+public interface CraftingRecipeFiller<T extends CraftingRecipe> extends Function<T, Collection<Display>> {
+ default void registerCategories(CategoryRegistry registry) {
+ }
+
+ default void registerExtension(CategoryRegistry registry, ExtensionCallback callback) {
+ registry.get(BuiltinPlugin.CRAFTING).registerExtension((display, category, lastView) -> {
+ if (getRecipeClass().isInstance(display.getOptionalRecipe().orElse(null))) {
+ return new DisplayCategoryView<>() {
+ @Override
+ public DisplayRenderer getDisplayRenderer(DefaultCraftingDisplay<?> display) {
+ return lastView.getDisplayRenderer(display);
+ }
+
+ @Override
+ public List<Widget> setupDisplay(DefaultCraftingDisplay<?> display, Rectangle bounds) {
+ List<Widget> widgets = lastView.setupDisplay(display, bounds);
+ callback.accept(bounds, widgets, display);
+ return widgets;
+ }
+ };
+ } else {
+ return lastView;
+ }
+ });
+ }
+
+ default void registerDisplays(DisplayRegistry registry) {
+ registry.registerRecipesFiller(getRecipeClass(), RecipeType.CRAFTING, this::apply);
+ }
+
+ default Widget createInfoWidget(Rectangle rectangle, DefaultCraftingDisplay<?> display, Component... texts) {
+ Point point = new Point(rectangle.getMaxX() - 4, rectangle.y + 4);
+ Rectangle bounds = new Rectangle(point.getX() - 9, point.getY() + 1, 8, 8);
+ if (display.isShapeless()) {
+ bounds.x -= 10;
+ }
+ Widget widget = Widgets.createTexturedWidget(new ResourceLocation("roughlyenoughitems:textures/gui/info.png"), bounds.getX(), bounds.getY(), 0, 0, bounds.getWidth(), bounds.getHeight(), 1, 1, 1, 1);
+ return Widgets.withTooltip(Widgets.withBounds(widget, bounds), texts);
+ }
+
+ Class<T> getRecipeClass();
+
+ @FunctionalInterface
+ interface ExtensionCallback {
+ void accept(Rectangle bounds, List<Widget> widgets, DefaultCraftingDisplay<?> display);
+ }
+}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/FireworkRocketRecipeFiller.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/FireworkRocketRecipeFiller.java
new file mode 100644
index 000000000..d4a689df1
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/FireworkRocketRecipeFiller.java
@@ -0,0 +1,78 @@
+/*
+ * 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.crafting.filler;
+
+import me.shedaniel.rei.api.client.registry.category.CategoryRegistry;
+import me.shedaniel.rei.api.common.display.Display;
+import me.shedaniel.rei.api.common.entry.EntryIngredient;
+import me.shedaniel.rei.api.common.entry.EntryStack;
+import me.shedaniel.rei.api.common.util.EntryIngredients;
+import me.shedaniel.rei.api.common.util.EntryStacks;
+import me.shedaniel.rei.plugin.common.displays.crafting.DefaultCustomShapelessDisplay;
+import net.minecraft.nbt.CompoundTag;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.crafting.FireworkRocketRecipe;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class FireworkRocketRecipeFiller implements CraftingRecipeFiller<FireworkRocketRecipe> {
+ @Override
+ public Collection<Display> apply(FireworkRocketRecipe recipe) {
+ List<Display> displays = new ArrayList<>();
+ {
+ EntryIngredient[] inputs = new EntryIngredient[4];
+ inputs[0] = EntryIngredients.of(Items.GUNPOWDER);
+ inputs[1] = EntryIngredients.of(Items.PAPER);
+ inputs[2] = EntryIngredient.of(EntryStack.empty().cast(), EntryStacks.of(Items.GUNPOWDER), EntryStacks.of(Items.GUNPOWDER));
+ inputs[3] = EntryIngredient.of(EntryStack.empty().cast(), EntryStack.empty().cast(), EntryStacks.of(Items.GUNPOWDER));
+ EntryStack<ItemStack>[] outputs = new EntryStack[3];
+ for (int i = 0; i < 3; i++) {
+ outputs[i] = EntryStacks.of(new ItemStack(Items.FIREWORK_ROCKET, 3));
+ CompoundTag tag = outputs[i].getValue().getOrCreateTagElement("Fireworks");
+ tag.putByte("Flight", (byte) (i + 1));
+ }
+ displays.add(new DefaultCustomShapelessDisplay(recipe,
+ List.of(inputs),
+ List.of(EntryIngredient.of(outputs))));
+ }
+
+ return displays;
+ }
+
+ @Override
+ public Class<FireworkRocketRecipe> getRecipeClass() {
+ return FireworkRocketRecipe.class;
+ }
+
+ @Override
+ public void registerCategories(CategoryRegistry registry) {
+ registerExtension(registry, (bounds, widgets, display) -> {
+ widgets.add(createInfoWidget(bounds, display, Component.translatable("text.rei.crafting.firework.gunpowder.amount")));
+ });
+ }
+}
diff --git a/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapCloningRecipeFiller.java b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapCloningRecipeFiller.java
new file mode 100644
index 000000000..e36a6fd8f
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapCloningRecipeFiller.java
@@ -0,0 +1,52 @@
+/*
+ * This file is licensed under the MIT License, part of Roughly Enough Ite