mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-08 19:02:59 +00:00
Burn meta wildcards from Item, allow more dynamic recipe inputs
this was an obstacle for getting rid of legacy item IDs.
This commit is contained in:
@ -184,7 +184,7 @@ class CraftingManager{
|
||||
public function registerPotionTypeRecipe(PotionTypeRecipe $recipe) : void{
|
||||
$input = $recipe->getInput();
|
||||
$ingredient = $recipe->getIngredient();
|
||||
$this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . ($ingredient->hasAnyDamageValue() ? "?" : $ingredient->getMeta())] = $recipe;
|
||||
$this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] = $recipe;
|
||||
|
||||
foreach($this->recipeRegisteredCallbacks as $callback){
|
||||
$callback();
|
||||
@ -193,7 +193,7 @@ class CraftingManager{
|
||||
|
||||
public function registerPotionContainerChangeRecipe(PotionContainerChangeRecipe $recipe) : void{
|
||||
$ingredient = $recipe->getIngredient();
|
||||
$this->potionContainerChangeRecipes[$recipe->getInputItemId()][$ingredient->getId() . ":" . ($ingredient->hasAnyDamageValue() ? "?" : $ingredient->getMeta())] = $recipe;
|
||||
$this->potionContainerChangeRecipes[$recipe->getInputItemId()][$ingredient->getId() . ":" . $ingredient->getMeta()] = $recipe;
|
||||
|
||||
foreach($this->recipeRegisteredCallbacks as $callback){
|
||||
$callback();
|
||||
@ -253,8 +253,6 @@ class CraftingManager{
|
||||
|
||||
public function matchBrewingRecipe(Item $input, Item $ingredient) : ?BrewingRecipe{
|
||||
return $this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":" . $ingredient->getMeta()] ??
|
||||
$this->potionTypeRecipes[$input->getId() . ":" . $input->getMeta()][$ingredient->getId() . ":?"] ??
|
||||
$this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":" . $ingredient->getMeta()] ??
|
||||
$this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":?"] ?? null;
|
||||
$this->potionContainerChangeRecipes[$input->getId()][$ingredient->getId() . ":" . $ingredient->getMeta()] ?? null;
|
||||
}
|
||||
}
|
||||
|
@ -23,14 +23,17 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\data\bedrock\item\ItemTypeDeserializeException;
|
||||
use pocketmine\item\Durable;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemFactory;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Utils;
|
||||
use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||
use function array_map;
|
||||
use function file_get_contents;
|
||||
use function is_array;
|
||||
use function is_int;
|
||||
use function json_decode;
|
||||
|
||||
final class CraftingManagerFromDataHelper{
|
||||
@ -41,7 +44,7 @@ final class CraftingManagerFromDataHelper{
|
||||
private static function containsUnknownItems(array $items) : bool{
|
||||
$factory = ItemFactory::getInstance();
|
||||
foreach($items as $item){
|
||||
if($item instanceof Durable || $item->hasAnyDamageValue()){
|
||||
if($item instanceof Durable){
|
||||
//TODO: this check is imperfect and might cause problems if meta 0 isn't used for some reason
|
||||
if(!$factory->isRegistered($item->getId())){
|
||||
return true;
|
||||
@ -54,6 +57,30 @@ final class CraftingManagerFromDataHelper{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $data
|
||||
*/
|
||||
private static function deserializeIngredient(array $data) : ?RecipeIngredient{
|
||||
if(!isset($data["id"]) || !is_int($data["id"])){
|
||||
throw new \InvalidArgumentException("Invalid input data, expected int ID");
|
||||
}
|
||||
if(isset($data["damage"]) && $data["damage"] === -1){
|
||||
try{
|
||||
$typeData = GlobalItemDataHandlers::getUpgrader()->upgradeItemTypeDataInt($data["id"], 0, 1, null);
|
||||
}catch(ItemTypeDeserializeException){
|
||||
//probably unknown item
|
||||
return null;
|
||||
}
|
||||
|
||||
return new MetaWildcardRecipeIngredient($typeData->getTypeData()->getName());
|
||||
}
|
||||
|
||||
//TODO: we need to stop using jsonDeserialize for this
|
||||
$item = Item::jsonDeserialize($data);
|
||||
|
||||
return self::containsUnknownItems([$item]) ? null : new ExactRecipeIngredient($item);
|
||||
}
|
||||
|
||||
public static function make(string $filePath) : CraftingManager{
|
||||
$recipes = json_decode(Utils::assumeNotFalse(file_get_contents($filePath), "Missing required resource file"), true);
|
||||
if(!is_array($recipes)){
|
||||
@ -61,6 +88,7 @@ final class CraftingManagerFromDataHelper{
|
||||
}
|
||||
$result = new CraftingManager();
|
||||
|
||||
$ingredientDeserializerFunc = \Closure::fromCallable([self::class, "deserializeIngredient"]);
|
||||
$itemDeserializerFunc = \Closure::fromCallable([Item::class, 'jsonDeserialize']);
|
||||
|
||||
foreach($recipes["shapeless"] as $recipe){
|
||||
@ -73,9 +101,16 @@ final class CraftingManagerFromDataHelper{
|
||||
if($recipeType === null){
|
||||
continue;
|
||||
}
|
||||
$inputs = array_map($itemDeserializerFunc, $recipe["input"]);
|
||||
$inputs = [];
|
||||
foreach($recipe["input"] as $inputData){
|
||||
$input = $ingredientDeserializerFunc($inputData);
|
||||
if($input === null){ //unknown input item
|
||||
continue;
|
||||
}
|
||||
$inputs[] = $input;
|
||||
}
|
||||
$outputs = array_map($itemDeserializerFunc, $recipe["output"]);
|
||||
if(self::containsUnknownItems($inputs) || self::containsUnknownItems($outputs)){
|
||||
if(self::containsUnknownItems($outputs)){
|
||||
continue;
|
||||
}
|
||||
$result->registerShapelessRecipe(new ShapelessRecipe(
|
||||
@ -88,9 +123,16 @@ final class CraftingManagerFromDataHelper{
|
||||
if($recipe["block"] !== "crafting_table"){ //TODO: filter others out for now to avoid breaking economics
|
||||
continue;
|
||||
}
|
||||
$inputs = array_map($itemDeserializerFunc, $recipe["input"]);
|
||||
$inputs = [];
|
||||
foreach($recipe["input"] as $symbol => $inputData){
|
||||
$input = $ingredientDeserializerFunc($inputData);
|
||||
if($input === null){ //unknown input item
|
||||
continue;
|
||||
}
|
||||
$inputs[$symbol] = $input;
|
||||
}
|
||||
$outputs = array_map($itemDeserializerFunc, $recipe["output"]);
|
||||
if(self::containsUnknownItems($inputs) || self::containsUnknownItems($outputs)){
|
||||
if(self::containsUnknownItems($outputs)){
|
||||
continue;
|
||||
}
|
||||
$result->registerShapedRecipe(new ShapedRecipe(
|
||||
@ -111,8 +153,8 @@ final class CraftingManagerFromDataHelper{
|
||||
continue;
|
||||
}
|
||||
$output = Item::jsonDeserialize($recipe["output"]);
|
||||
$input = Item::jsonDeserialize($recipe["input"]);
|
||||
if(self::containsUnknownItems([$output, $input])){
|
||||
$input = self::deserializeIngredient($recipe["input"]);
|
||||
if($input === null || self::containsUnknownItems([$output])){
|
||||
continue;
|
||||
}
|
||||
$result->getFurnaceRecipeManager($furnaceType)->register(new FurnaceRecipe(
|
||||
@ -135,9 +177,9 @@ final class CraftingManagerFromDataHelper{
|
||||
));
|
||||
}
|
||||
foreach($recipes["potion_container_change"] as $recipe){
|
||||
$input = ItemFactory::getInstance()->get($recipe["input_item_id"], -1);
|
||||
$input = ItemFactory::getInstance()->get($recipe["input_item_id"]);
|
||||
$ingredient = Item::jsonDeserialize($recipe["ingredient"]);
|
||||
$output = ItemFactory::getInstance()->get($recipe["output_item_id"], -1);
|
||||
$output = ItemFactory::getInstance()->get($recipe["output_item_id"]);
|
||||
|
||||
if(self::containsUnknownItems([$input, $ingredient, $output])){
|
||||
continue;
|
||||
|
@ -29,7 +29,7 @@ interface CraftingRecipe{
|
||||
/**
|
||||
* Returns a list of items needed to craft this recipe. This MUST NOT include Air items or items with a zero count.
|
||||
*
|
||||
* @return Item[]
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array;
|
||||
|
||||
|
56
src/crafting/ExactRecipeIngredient.php
Normal file
56
src/crafting/ExactRecipeIngredient.php
Normal file
@ -0,0 +1,56 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
/**
|
||||
* Recipe ingredient that matches exactly one item, without wildcards.
|
||||
* Note that recipe inputs cannot require NBT.
|
||||
*/
|
||||
final class ExactRecipeIngredient implements RecipeIngredient{
|
||||
|
||||
public function __construct(private Item $item){
|
||||
if($item->isNull()){
|
||||
throw new \InvalidArgumentException("Recipe ingredients must not be air items");
|
||||
}
|
||||
if($item->getCount() !== 1){
|
||||
throw new \InvalidArgumentException("Recipe ingredients cannot require count");
|
||||
}
|
||||
$this->item = clone $item;
|
||||
}
|
||||
|
||||
public function getItem() : Item{ return clone $this->item; }
|
||||
|
||||
public function accepts(Item $item) : bool{
|
||||
//client-side, recipe inputs can't actually require NBT
|
||||
//but on the PM side, we currently check for it if the input requires it, so we have to continue to do so for
|
||||
//the sake of consistency
|
||||
return $item->getCount() >= 1 && $this->item->equals($item, true, $this->item->hasNamedTag());
|
||||
}
|
||||
|
||||
public function __toString() : string{
|
||||
return "ExactRecipeIngredient(" . $this->item . ")";
|
||||
}
|
||||
}
|
@ -29,14 +29,13 @@ class FurnaceRecipe{
|
||||
|
||||
public function __construct(
|
||||
private Item $result,
|
||||
private Item $ingredient
|
||||
private RecipeIngredient $ingredient
|
||||
){
|
||||
$this->result = clone $result;
|
||||
$this->ingredient = clone $ingredient;
|
||||
}
|
||||
|
||||
public function getInput() : Item{
|
||||
return clone $this->ingredient;
|
||||
public function getInput() : RecipeIngredient{
|
||||
return $this->ingredient;
|
||||
}
|
||||
|
||||
public function getResult() : Item{
|
||||
|
@ -25,11 +25,18 @@ namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\utils\ObjectSet;
|
||||
use function morton2d_encode;
|
||||
|
||||
final class FurnaceRecipeManager{
|
||||
/** @var FurnaceRecipe[] */
|
||||
protected array $furnaceRecipes = [];
|
||||
|
||||
/**
|
||||
* @var FurnaceRecipe[]
|
||||
* @phpstan-var array<int, FurnaceRecipe>
|
||||
*/
|
||||
private array $lookupCache = [];
|
||||
|
||||
/** @phpstan-var ObjectSet<\Closure(FurnaceRecipe) : void> */
|
||||
private ObjectSet $recipeRegisteredCallbacks;
|
||||
|
||||
@ -52,14 +59,27 @@ final class FurnaceRecipeManager{
|
||||
}
|
||||
|
||||
public function register(FurnaceRecipe $recipe) : void{
|
||||
$input = $recipe->getInput();
|
||||
$this->furnaceRecipes[$input->getId() . ":" . ($input->hasAnyDamageValue() ? "?" : $input->getMeta())] = $recipe;
|
||||
$this->furnaceRecipes[] = $recipe;
|
||||
foreach($this->recipeRegisteredCallbacks as $callback){
|
||||
$callback($recipe);
|
||||
}
|
||||
}
|
||||
|
||||
public function match(Item $input) : ?FurnaceRecipe{
|
||||
return $this->furnaceRecipes[$input->getId() . ":" . $input->getMeta()] ?? $this->furnaceRecipes[$input->getId() . ":?"] ?? null;
|
||||
$index = morton2d_encode($input->getId(), $input->getMeta());
|
||||
$simpleRecipe = $this->lookupCache[$index] ?? null;
|
||||
if($simpleRecipe !== null){
|
||||
return $simpleRecipe;
|
||||
}
|
||||
|
||||
foreach($this->furnaceRecipes as $recipe){
|
||||
if($recipe->getInput()->accepts($input)){
|
||||
//remember that this item is accepted by this recipe, so we don't need to bruteforce it again
|
||||
$this->lookupCache[$index] = $recipe;
|
||||
return $recipe;
|
||||
}
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
57
src/crafting/MetaWildcardRecipeIngredient.php
Normal file
57
src/crafting/MetaWildcardRecipeIngredient.php
Normal file
@ -0,0 +1,57 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\world\format\io\GlobalItemDataHandlers;
|
||||
|
||||
/**
|
||||
* Recipe ingredient that matches items by their Minecraft ID only. This is used for things like the crafting table
|
||||
* recipe from planks (multiple types of planks are accepted).
|
||||
*
|
||||
* WARNING: Plugins shouldn't usually use this. This is a hack that relies on internal Minecraft behaviour, which might
|
||||
* change or break at any time.
|
||||
*
|
||||
* @internal
|
||||
*/
|
||||
final class MetaWildcardRecipeIngredient implements RecipeIngredient{
|
||||
|
||||
public function __construct(
|
||||
private string $itemId,
|
||||
){}
|
||||
|
||||
public function getItemId() : string{ return $this->itemId; }
|
||||
|
||||
public function accepts(Item $item) : bool{
|
||||
if($item->getCount() < 1){
|
||||
return false;
|
||||
}
|
||||
|
||||
return GlobalItemDataHandlers::getSerializer()->serializeType($item)->getName() === $this->itemId;
|
||||
}
|
||||
|
||||
public function __toString() : string{
|
||||
return "MetaWildcardRecipeIngredient($this->itemId)";
|
||||
}
|
||||
}
|
31
src/crafting/RecipeIngredient.php
Normal file
31
src/crafting/RecipeIngredient.php
Normal file
@ -0,0 +1,31 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
|
||||
interface RecipeIngredient extends \Stringable{
|
||||
|
||||
public function accepts(Item $item) : bool;
|
||||
}
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\crafting;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_values;
|
||||
use function count;
|
||||
@ -35,7 +34,7 @@ use function strpos;
|
||||
class ShapedRecipe implements CraftingRecipe{
|
||||
/** @var string[] */
|
||||
private array $shape = [];
|
||||
/** @var Item[] char => Item map */
|
||||
/** @var RecipeIngredient[] char => RecipeIngredient map */
|
||||
private array $ingredientList = [];
|
||||
/** @var Item[] */
|
||||
private array $results = [];
|
||||
@ -46,15 +45,15 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
/**
|
||||
* Constructs a ShapedRecipe instance.
|
||||
*
|
||||
* @param string[] $shape <br>
|
||||
* @param string[] $shape <br>
|
||||
* Array of 1, 2, or 3 strings representing the rows of the recipe.
|
||||
* This accepts an array of 1, 2 or 3 strings. Each string should be of the same length and must be at most 3
|
||||
* characters long. Each character represents a unique type of ingredient. Spaces are interpreted as air.
|
||||
* @param Item[] $ingredients <br>
|
||||
* @param RecipeIngredient[] $ingredients <br>
|
||||
* Char => Item map of items to be set into the shape.
|
||||
* This accepts an array of Items, indexed by character. Every unique character (except space) in the shape
|
||||
* array MUST have a corresponding item in this list. Space character is automatically treated as air.
|
||||
* @param Item[] $results List of items that this recipe produces when crafted.
|
||||
* @param Item[] $results List of items that this recipe produces when crafted.
|
||||
*
|
||||
* Note: Recipes **do not** need to be square. Do NOT add padding for empty rows/columns.
|
||||
*/
|
||||
@ -119,7 +118,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[][]
|
||||
* @return (RecipeIngredient|null)[][]
|
||||
*/
|
||||
public function getIngredientMap() : array{
|
||||
$ingredients = [];
|
||||
@ -134,7 +133,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
$ingredients = [];
|
||||
@ -142,7 +141,7 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
for($y = 0; $y < $this->height; ++$y){
|
||||
for($x = 0; $x < $this->width; ++$x){
|
||||
$ingredient = $this->getIngredient($x, $y);
|
||||
if(!$ingredient->isNull()){
|
||||
if($ingredient !== null){
|
||||
$ingredients[] = $ingredient;
|
||||
}
|
||||
}
|
||||
@ -151,9 +150,8 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
return $ingredients;
|
||||
}
|
||||
|
||||
public function getIngredient(int $x, int $y) : Item{
|
||||
$exists = $this->ingredientList[$this->shape[$y][$x]] ?? null;
|
||||
return $exists !== null ? clone $exists : VanillaItems::AIR();
|
||||
public function getIngredient(int $x, int $y) : ?RecipeIngredient{
|
||||
return $this->ingredientList[$this->shape[$y][$x]] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -170,7 +168,12 @@ class ShapedRecipe implements CraftingRecipe{
|
||||
|
||||
$given = $grid->getIngredient($reverse ? $this->width - $x - 1 : $x, $y);
|
||||
$required = $this->getIngredient($x, $y);
|
||||
if(!$required->equals($given, !$required->hasAnyDamageValue(), $required->hasNamedTag()) || $required->getCount() > $given->getCount()){
|
||||
|
||||
if($required === null){
|
||||
if(!$given->isNull()){
|
||||
return false; //hole, such as that in the center of a chest recipe, should not be filled
|
||||
}
|
||||
}elseif(!$required->accepts($given)){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -28,30 +28,24 @@ use pocketmine\utils\Utils;
|
||||
use function count;
|
||||
|
||||
class ShapelessRecipe implements CraftingRecipe{
|
||||
/** @var Item[] */
|
||||
/** @var RecipeIngredient[] */
|
||||
private array $ingredients = [];
|
||||
/** @var Item[] */
|
||||
private array $results;
|
||||
private ShapelessRecipeType $type;
|
||||
|
||||
/**
|
||||
* @param Item[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
|
||||
* @param RecipeIngredient[] $ingredients No more than 9 total. This applies to sum of item stack counts, not count of array.
|
||||
* @param Item[] $results List of result items created by this recipe.
|
||||
* TODO: we'll want to make the type parameter mandatory in PM5
|
||||
*/
|
||||
public function __construct(array $ingredients, array $results, ?ShapelessRecipeType $type = null){
|
||||
$this->type = $type ?? ShapelessRecipeType::CRAFTING();
|
||||
foreach($ingredients as $item){
|
||||
//Ensure they get split up properly
|
||||
if(count($this->ingredients) + $item->getCount() > 9){
|
||||
throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients");
|
||||
}
|
||||
|
||||
while($item->getCount() > 0){
|
||||
$this->ingredients[] = $item->pop();
|
||||
}
|
||||
if(count($ingredients) > 9){
|
||||
throw new \InvalidArgumentException("Shapeless recipes cannot have more than 9 ingredients");
|
||||
}
|
||||
|
||||
$this->ingredients = $ingredients;
|
||||
$this->results = Utils::cloneObjectArray($results);
|
||||
}
|
||||
|
||||
@ -71,28 +65,23 @@ class ShapelessRecipe implements CraftingRecipe{
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item[]
|
||||
* @return RecipeIngredient[]
|
||||
*/
|
||||
public function getIngredientList() : array{
|
||||
return Utils::cloneObjectArray($this->ingredients);
|
||||
return $this->ingredients;
|
||||
}
|
||||
|
||||
public function getIngredientCount() : int{
|
||||
$count = 0;
|
||||
foreach($this->ingredients as $ingredient){
|
||||
$count += $ingredient->getCount();
|
||||
}
|
||||
|
||||
return $count;
|
||||
return count($this->ingredients);
|
||||
}
|
||||
|
||||
public function matchesCraftingGrid(CraftingGrid $grid) : bool{
|
||||
//don't pack the ingredients - shapeless recipes require that each ingredient be in a separate slot
|
||||
$input = $grid->getContents();
|
||||
|
||||
foreach($this->ingredients as $needItem){
|
||||
foreach($this->ingredients as $ingredient){
|
||||
foreach($input as $j => $haveItem){
|
||||
if($haveItem->equals($needItem, !$needItem->hasAnyDamageValue(), $needItem->hasNamedTag()) && $haveItem->getCount() >= $needItem->getCount()){
|
||||
if($ingredient->accepts($haveItem)){
|
||||
unset($input[$j]);
|
||||
continue 2;
|
||||
}
|
||||
|
Reference in New Issue
Block a user