diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/ContentApiPlugin.java b/src/main/java/io/github/kiber2009/plugin/contentapi/ContentApiPlugin.java index c9276e9..9625932 100644 --- a/src/main/java/io/github/kiber2009/plugin/contentapi/ContentApiPlugin.java +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/ContentApiPlugin.java @@ -21,6 +21,7 @@ public void onEnable() { instance = this; getServer().getPluginManager().registerEvents(new ItemsManager(), this); + getServer().getPluginManager().registerEvents(new RecipesManager(), this); getLifecycleManager().registerEventHandler(LifecycleEvents.COMMANDS, commands -> commands.registrar().register(Commands.literal("contentapi") diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/RecipesManager.java b/src/main/java/io/github/kiber2009/plugin/contentapi/RecipesManager.java new file mode 100644 index 0000000..1ca2b46 --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/RecipesManager.java @@ -0,0 +1,19 @@ +package io.github.kiber2009.plugin.contentapi; + +import io.github.kiber2009.plugin.contentapi.api.recipe.CustomCraftingRecipe; +import io.github.kiber2009.plugin.contentapi.registry.recipe.RecipeGlobalRegistry; +import org.bukkit.event.EventHandler; +import org.bukkit.event.Listener; +import org.bukkit.event.inventory.PrepareItemCraftEvent; + +final class RecipesManager implements Listener { + @EventHandler + private static void onPrepareItemCraft(final PrepareItemCraftEvent event) { + if (event.isRepair()) + return; + + final CustomCraftingRecipe recipe = RecipeGlobalRegistry.CRAFTING.get(event.getInventory().getMatrix()); + if (recipe != null) + event.getInventory().setResult(recipe.getResult()); + } +} \ No newline at end of file diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/CustomCraftingRecipe.java b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/CustomCraftingRecipe.java new file mode 100644 index 0000000..fa86203 --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/CustomCraftingRecipe.java @@ -0,0 +1,9 @@ +package io.github.kiber2009.plugin.contentapi.api.recipe; + +import org.bukkit.inventory.ItemStack; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.function.Predicate; + +public interface CustomCraftingRecipe extends CustomRecipe, Predicate<@Nullable ItemStack @NonNull []> {} diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/CustomRecipe.java b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/CustomRecipe.java new file mode 100644 index 0000000..25b4299 --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/CustomRecipe.java @@ -0,0 +1,10 @@ +package io.github.kiber2009.plugin.contentapi.api.recipe; + +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; + +public interface CustomRecipe { + @Contract(pure = true) + @NonNull ItemStack getResult(); +} diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/choice/CustomItemRecipeChoice.java b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/choice/CustomItemRecipeChoice.java new file mode 100644 index 0000000..6ee6c07 --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/choice/CustomItemRecipeChoice.java @@ -0,0 +1,23 @@ +package io.github.kiber2009.plugin.contentapi.api.recipe.choice; + +import io.github.kiber2009.plugin.contentapi.api.item.CustomItem; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.function.Predicate; + +public class CustomItemRecipeChoice implements Predicate<@Nullable ItemStack> { + private final CustomItem item; + + public CustomItemRecipeChoice(final @NonNull CustomItem item) { + this.item = item; + } + + @Override + @Contract(value = "null -> false", pure = true) + public boolean test(final @Nullable ItemStack stack) { + return item.match(stack); + } +} diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/crafting/CustomShapedCraftingRecipe.java b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/crafting/CustomShapedCraftingRecipe.java new file mode 100644 index 0000000..71e4bc2 --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/crafting/CustomShapedCraftingRecipe.java @@ -0,0 +1,59 @@ +package io.github.kiber2009.plugin.contentapi.api.recipe.crafting; + +import io.github.kiber2009.plugin.contentapi.api.recipe.CustomCraftingRecipe; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.function.Predicate; + +public class CustomShapedCraftingRecipe implements CustomCraftingRecipe { + private static final byte[][] OFFSETS = {{0, 0}, {0, 1}, {1, 0}, {1, 1}}; + + private final ItemStack result; + private final Predicate[] matrix; + + public CustomShapedCraftingRecipe(final @NonNull Predicate<@Nullable ItemStack> @NonNull [] matrix, + final @NonNull ItemStack result) { + if (matrix.length != 4 && matrix.length != 9) + throw new IllegalArgumentException("Invalid matrix size"); + this.matrix = matrix; + this.result = result; + } + + @Override + @Contract(pure = true) + public @NonNull ItemStack getResult() { + return result.clone(); + } + + @Override + @Contract(pure = true) + public boolean test(final @Nullable ItemStack @NonNull [] matrix) { + if (matrix.length == 4 && this.matrix.length == 9) + return false; + + if (matrix.length == this.matrix.length) { + for (int i = 0; i < matrix.length; i++) + if (!this.matrix[i].test(matrix[i])) + return false; + return true; + } + + for (final byte[] off : OFFSETS) { + boolean fits = true; + + for (int i = 0; i < 2 && fits; i++) + for (int j = 0; j < 2; j++) + if (!this.matrix[(off[0] + i) * 3 + off[1] + j].test(matrix[i * 2 + j])) { + fits = false; + break; + } + + if (fits) + return true; + } + return false; + } +} diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/crafting/CustomShapelessCraftingRecipe.java b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/crafting/CustomShapelessCraftingRecipe.java new file mode 100644 index 0000000..24361df --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/api/recipe/crafting/CustomShapelessCraftingRecipe.java @@ -0,0 +1,58 @@ +package io.github.kiber2009.plugin.contentapi.api.recipe.crafting; + +import io.github.kiber2009.plugin.contentapi.api.recipe.CustomCraftingRecipe; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +import java.util.function.Predicate; + +public class CustomShapelessCraftingRecipe implements CustomCraftingRecipe { + private final ItemStack result; + private final Predicate[] ingredients; + + @SafeVarargs + public CustomShapelessCraftingRecipe(final @NonNull ItemStack result, + final @NonNull Predicate<@NonNull ItemStack> @NonNull ... ingredients) { + if (ingredients.length > 9) + throw new IllegalArgumentException("Too many ingredients"); + this.result = result; + this.ingredients = ingredients; + } + + @Override + @Contract(pure = true) + public @NonNull ItemStack getResult() { + return result.clone(); + } + + @Override + @Contract(pure = true) + public boolean test(final @Nullable ItemStack @NonNull [] matrix) { + if (matrix.length < ingredients.length) + return false; + + final boolean[] used = new boolean[ingredients.length]; + int usedCount = 0; + + for (final ItemStack elem : matrix) { + if (elem == null) + continue; + + boolean found = false; + for (int i = 0; i < ingredients.length; i++) + if (!used[i] && ingredients[i].test(elem)) { + used[i] = true; + usedCount++; + found = true; + break; + } + + if (!found) + return false; + } + + return usedCount == ingredients.length; + } +} diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/registry/GlobalRegistry.java b/src/main/java/io/github/kiber2009/plugin/contentapi/registry/GlobalRegistry.java index 0207854..55cb928 100644 --- a/src/main/java/io/github/kiber2009/plugin/contentapi/registry/GlobalRegistry.java +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/registry/GlobalRegistry.java @@ -11,11 +11,11 @@ import java.util.Objects; import java.util.Set; -public sealed class GlobalRegistry permits ItemGlobalRegistry { +public class GlobalRegistry { @SuppressWarnings("StaticInitializerReferencesSubClass") public static final ItemGlobalRegistry ITEMS = new ItemGlobalRegistry(); - private final Map map = new HashMap<>(); + protected final Map map = new HashMap<>(); protected void registerItem(final @NonNull NamespacedKey key, final @NonNull T value) { if (map.containsKey(key)) @@ -23,7 +23,7 @@ protected void registerItem(final @NonNull NamespacedKey key, final @NonNull T v map.put(key, value); } - public void register(final @NonNull LocalRegistry registry) { + public void register(final @NonNull LocalRegistry registry) { for (final NamespacedKey key : registry.keySet()) registerItem(key, Objects.requireNonNull(registry.get(key))); } diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/registry/recipe/CraftingRecipeGlobalRegistry.java b/src/main/java/io/github/kiber2009/plugin/contentapi/registry/recipe/CraftingRecipeGlobalRegistry.java new file mode 100644 index 0000000..f496951 --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/registry/recipe/CraftingRecipeGlobalRegistry.java @@ -0,0 +1,20 @@ +package io.github.kiber2009.plugin.contentapi.registry.recipe; + +import io.github.kiber2009.plugin.contentapi.api.recipe.CustomCraftingRecipe; +import org.bukkit.inventory.ItemStack; +import org.jetbrains.annotations.Contract; +import org.jspecify.annotations.NonNull; +import org.jspecify.annotations.Nullable; + +public final class CraftingRecipeGlobalRegistry extends RecipeGlobalRegistry { + CraftingRecipeGlobalRegistry() { + } + + @Contract(pure = true) + public @Nullable CustomCraftingRecipe get(final @Nullable ItemStack @NonNull [] matrix) { + for (final CustomCraftingRecipe recipe : map.values()) + if (recipe.test(matrix)) + return recipe; + return null; + } +} diff --git a/src/main/java/io/github/kiber2009/plugin/contentapi/registry/recipe/RecipeGlobalRegistry.java b/src/main/java/io/github/kiber2009/plugin/contentapi/registry/recipe/RecipeGlobalRegistry.java new file mode 100644 index 0000000..46f58be --- /dev/null +++ b/src/main/java/io/github/kiber2009/plugin/contentapi/registry/recipe/RecipeGlobalRegistry.java @@ -0,0 +1,13 @@ +package io.github.kiber2009.plugin.contentapi.registry.recipe; + +import io.github.kiber2009.plugin.contentapi.api.recipe.CustomRecipe; +import io.github.kiber2009.plugin.contentapi.registry.GlobalRegistry; + +public sealed class RecipeGlobalRegistry extends GlobalRegistry + permits CraftingRecipeGlobalRegistry { + @SuppressWarnings("StaticInitializerReferencesSubClass") + public static final CraftingRecipeGlobalRegistry CRAFTING = new CraftingRecipeGlobalRegistry(); + + RecipeGlobalRegistry() { + } +}