aboutsummaryrefslogtreecommitdiff
path: root/default-plugin/src/main/java/me/shedaniel
diff options
context:
space:
mode:
Diffstat (limited to 'default-plugin/src/main/java/me/shedaniel')
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/DefaultClientPlugin.java64
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ArmorDyeRecipeFiller.java89
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BannerDuplicateRecipeFiller.java71
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BookCloningRecipeFiller.java101
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/CraftingRecipeFiller.java93
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/FireworkRocketRecipeFiller.java79
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapCloningRecipeFiller.java53
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapExtendingRecipeFiller.java86
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ShieldDecorationRecipeFiller.java112
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ShulkerBoxColoringFiller.java61
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/SuspiciousStewRecipeFiller.java64
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/TippedArrowRecipeFiller.java72
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/DefaultPlugin.java1
-rw-r--r--default-plugin/src/main/java/me/shedaniel/rei/plugin/common/displays/DefaultSmithingDisplay.java90
14 files changed, 964 insertions, 72 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 f1c4267ac..013b7deb0 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
@@ -29,14 +29,13 @@ import dev.architectury.event.EventResult;
import dev.architectury.networking.NetworkManager;
import dev.architectury.platform.Platform;
import it.unimi.dsi.fastutil.objects.Reference2ObjectOpenHashMap;
-import it.unimi.dsi.fastutil.objects.ReferenceOpenHashSet;
-import it.unimi.dsi.fastutil.objects.ReferenceSet;
import me.shedaniel.math.Rectangle;
import me.shedaniel.rei.api.client.favorites.FavoriteEntry;
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;
@@ -44,6 +43,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;
@@ -55,6 +55,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;
@@ -73,7 +74,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;
@@ -116,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");
}
@@ -181,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(),
@@ -231,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)) {
@@ -275,7 +310,7 @@ public class DefaultClientPlugin implements REIClientPlugin, BuiltinClientPlugin
registry.registerRecipeFiller(CampfireCookingRecipe.class, RecipeType.CAMPFIRE_COOKING, DefaultCampfireDisplay::new);
registry.registerRecipeFiller(StonecutterRecipe.class, RecipeType.STONECUTTING, DefaultStoneCuttingDisplay::new);
registry.registerRecipeFiller(SmithingTransformRecipe.class, RecipeType.SMITHING, DefaultSmithingDisplay::ofTransforming);
- registry.registerRecipeFiller(SmithingTrimRecipe.class, RecipeType.SMITHING, DefaultSmithingDisplay::ofTrimming);
+ registry.registerRecipesFiller(SmithingTrimRecipe.class, RecipeType.SMITHING, DefaultSmithingDisplay::fromTrimming);
registry.registerFiller(AnvilRecipe.class, DefaultAnvilDisplay::new);
registry.registerFiller(BrewingRecipe.class, DefaultBrewingDisplay::new);
registry.registerFiller(TagKey.class, tagKey -> {
@@ -292,24 +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();
- PotionContents potionContents = itemStack.getOrDefault(DataComponents.POTION_CONTENTS, PotionContents.EMPTY);
- if (potionContents.potion().isPresent() && registeredPotions.add(potionContents.potion().get().value())) {
- 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);
- outputStack.set(DataComponents.POTION_CONTENTS, potionContents);
- 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..39315bab8
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/ArmorDyeRecipeFiller.java
@@ -0,0 +1,89 @@
+/*
+ * 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.tags.ItemTags;
+import net.minecraft.world.item.DyeColor;
+import net.minecraft.world.item.DyeItem;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.component.DyedItemColor;
+import net.minecraft.world.item.crafting.ArmorDyeRecipe;
+import net.minecraft.world.item.crafting.RecipeHolder;
+
+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(RecipeHolder<ArmorDyeRecipe> recipe) {
+ List<Display> displays = new ArrayList<>();
+ List<EntryStack<?>> toDye = EntryRegistry.getInstance().getEntryStacks().filter(entry -> entry.getValueType() == ItemStack.class && entry.<ItemStack>castValue().is(ItemTags.DYEABLE)).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 = DyedItemColor.applyDyes(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 = DyedItemColor.applyDyes(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..f28eb8f43
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BannerDuplicateRecipeFiller.java
@@ -0,0 +1,71 @@
+/*
+ * 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 net.minecraft.world.item.crafting.RecipeHolder;
+
+import java.util.*;
+
+public class BannerDuplicateRecipeFiller implements CraftingRecipeFiller<BannerDuplicateRecipe> {
+ @Override
+ public Collection<Display> apply(RecipeHolder<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..4a58cf71f
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/BookCloningRecipeFiller.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.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.core.component.DataComponents;
+import net.minecraft.server.network.Filterable;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.component.WrittenBookContent;
+import net.minecraft.world.item.crafting.BookCloningRecipe;
+import net.minecraft.world.item.crafting.RecipeHolder;
+
+import java.util.*;
+
+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(RecipeHolder<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.update(DataComponents.WRITTEN_BOOK_CONTENT, WrittenBookContent.EMPTY, WrittenBookContent::tryCraftCopy);
+ 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.set(DataComponents.WRITTEN_BOOK_CONTENT, new WrittenBookContent(new Filterable<>(TITLES[new Random().nextInt(TITLES.length)], Optional.empty()),
+ AUTHORS[new Random().nextInt(AUTHORS.length)], 0, List.of(), true));
+ 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..8670ed6fc
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/CraftingRecipeFiller.java
@@ -0,0 +1,93 @@
+/*
+ * 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.RecipeHolder;
+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<RecipeHolder<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..4511c5408
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/FireworkRocketRecipeFiller.java
@@ -0,0 +1,79 @@
+/*
+ * 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.core.component.DataComponents;
+import net.minecraft.network.chat.Component;
+import net.minecraft.world.item.ItemStack;
+import net.minecraft.world.item.Items;
+import net.minecraft.world.item.component.Fireworks;
+import net.minecraft.world.item.crafting.FireworkRocketRecipe;
+import net.minecraft.world.item.crafting.RecipeHolder;
+
+import java.util.ArrayList;
+import java.util.Collection;
+import java.util.List;
+
+public class FireworkRocketRecipeFiller implements CraftingRecipeFiller<FireworkRocketRecipe> {
+ @Override
+ public Collection<Display> apply(RecipeHolder<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));
+ outputs[i].getValue().set(DataComponents.FIREWORKS, new Fireworks(i + 1, List.of()));
+ }
+ 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..e692668ca
--- /dev/null
+++ b/default-plugin/src/main/java/me/shedaniel/rei/plugin/client/categories/crafting/filler/MapCloningRecipeFiller.java
@@ -0,0 +1,53 @@
+/*
+ * 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