/* * Copyright (c) 2015 NOVA, All rights reserved. * This library is free software, licensed under GNU Lesser General Public License version 3 * * This file is part of NOVA. * * NOVA is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * NOVA is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the * GNU General Public License for more details. * * You should have received a copy of the GNU General Public License * along with NOVA. If not, see <http://www.gnu.org/licenses/>. */ package nova.core.recipes.crafting; import nova.core.item.Item; import nova.core.item.ItemFactory; import nova.core.recipes.ingredient.ItemIngredient; import java.util.HashMap; import java.util.Map; import java.util.Objects; import java.util.Optional; /** * @author Stan */ public class ShapelessCraftingRecipe implements CraftingRecipe { private final ItemFactory output; private final RecipeFunction recipeFunction; private final ItemIngredient[] ingredients; public ShapelessCraftingRecipe(ItemFactory output, ItemIngredient... ingredients) { this(output, (crafting, tagged, o) -> Optional.of(o.build()), ingredients); } public ShapelessCraftingRecipe(ItemFactory output, RecipeFunction recipeFunction, ItemIngredient... ingredients) { this.output = output; this.recipeFunction = recipeFunction; this.ingredients = ingredients; } /** * Reorders ingredients to match the given shapeless recipe. * * @param recipe shapeless recipe * @param craftingGrid crafting input * @return reordered inputs, or null if no match was found */ private static RecipeMatching matchShapeless( ItemIngredient[] recipe, CraftingGrid craftingGrid) { if (craftingGrid.countFilledStacks() != recipe.length) { return null; } Item[] matched = new Item[recipe.length]; int[] indices = new int[recipe.length]; outer: for (int i = 0; i < craftingGrid.size(); i++) { Optional<Item> ingredient = craftingGrid.getCrafting(i); if (!ingredient.isPresent()) { continue; } for (int j = 0; j < recipe.length; j++) { if (matched[j] != null) { continue; } if (recipe[j].matches(ingredient.get())) { matched[j] = ingredient.get(); indices[j] = i; continue outer; } } return null; } return new RecipeMatching(matched, indices); } public int size() { return ingredients.length; } public ItemIngredient[] getIngredients() { return ingredients; } @Override public boolean matches(CraftingGrid craftingGrid) { return matchShapeless(ingredients, craftingGrid) != null; } @Override public Optional<Item> getCraftingResult(CraftingGrid craftingGrid) { RecipeMatching matching = matchShapeless(ingredients, craftingGrid); if (matching == null) { return Optional.empty(); } Map<String, Item> map = new HashMap<>(); for (int i = 0; i < ingredients.length; i++) { if (ingredients[i].getTag().isPresent()) { map.put(ingredients[i].getTag().get(), matching.inputs[i]); } } return recipeFunction.doCrafting(craftingGrid, map, output); } @Override public void consumeItems(CraftingGrid craftingGrid) { RecipeMatching matching = matchShapeless(ingredients, craftingGrid); if (matching == null) { return; } for (int i = 0; i < ingredients.length; i++) { ItemIngredient ingredient = ingredients[i]; Optional<Item> consumed = ingredient.consumeOnCrafting(matching.inputs[i], craftingGrid); Objects.requireNonNull(consumed, "The result of 'ItemIngredient.consumeOnCrafting' can't be null"); craftingGrid.setCrafting(matching.indices[i], consumed.filter(item -> item.count() > 0)); } } @Override public Optional<Item> getExampleOutput() { return Optional.of(output.build()); } private static class RecipeMatching { public final Item[] inputs; public final int[] indices; private RecipeMatching(Item[] inputs, int[] indices) { this.inputs = inputs; this.indices = indices; } } }