110 public static function matchIngredients(array $providedItems, array $recipeIngredients,
int $expectedIterations) : void{
111 if(count($recipeIngredients) === 0){
114 if(count($providedItems) === 0){
118 $packedProvidedItems = self::packItems(Utils::cloneObjectArray($providedItems));
119 $packedProvidedItemMatches = array_fill_keys(array_keys($packedProvidedItems), 0);
121 $recipeIngredientMatches = [];
123 foreach($recipeIngredients as $ingredientIndex => $recipeIngredient){
125 foreach($packedProvidedItems as $itemIndex => $packedItem){
126 if($recipeIngredient->accepts($packedItem)){
127 $packedProvidedItemMatches[$itemIndex]++;
128 $acceptedItems[$itemIndex] = $itemIndex;
132 if(count($acceptedItems) === 0){
133 throw new TransactionValidationException(
"No provided items satisfy ingredient requirement $recipeIngredient");
136 $recipeIngredientMatches[$ingredientIndex] = $acceptedItems;
139 foreach($packedProvidedItemMatches as $itemIndex => $itemMatchCount){
140 if($itemMatchCount === 0){
141 $item = $packedProvidedItems[$itemIndex];
142 throw new TransactionValidationException(
"Provided item $item is not accepted by any recipe ingredient");
149 uasort($recipeIngredientMatches, fn(array $a, array $b) => count($a) <=> count($b));
151 foreach($recipeIngredientMatches as $ingredientIndex => $acceptedItems){
152 $needed = $expectedIterations;
154 foreach($packedProvidedItems as $itemIndex => $item){
155 if(!isset($acceptedItems[$itemIndex])){
159 $taken = min($needed, $item->getCount());
161 $item->setCount($item->getCount() - $taken);
163 if($item->getCount() === 0){
164 unset($packedProvidedItems[$itemIndex]);
173 $recipeIngredient = $recipeIngredients[$ingredientIndex];
174 $actualIterations = $expectedIterations - $needed;
175 throw new TransactionValidationException(
"Not enough items to satisfy recipe ingredient $recipeIngredient for $expectedIterations (only have enough items for $actualIterations iterations)");
178 if(count($packedProvidedItems) > 0){
179 throw new TransactionValidationException(
"Not all provided items were used");
192 protected function matchOutputs(array $txItems, array $recipeItems) : int{
193 if(count($recipeItems) === 0){
196 if(count($txItems) === 0){
201 while(count($recipeItems) > 0){
203 $recipeItem = array_pop($recipeItems);
204 $needCount = $recipeItem->getCount();
205 foreach($recipeItems as $i => $otherRecipeItem){
206 if($otherRecipeItem->canStackWith($recipeItem)){
207 $needCount += $otherRecipeItem->getCount();
208 unset($recipeItems[$i]);
213 foreach($txItems as $j => $txItem){
214 if($txItem->canStackWith($recipeItem)){
215 $haveCount += $txItem->getCount();
220 if($haveCount % $needCount !== 0){
222 throw new TransactionValidationException(
"Expected an exact multiple of required $recipeItem (given: $haveCount, needed: $needCount)");
225 $multiplier = intdiv($haveCount, $needCount);
227 throw new TransactionValidationException(
"Expected more than zero items matching $recipeItem (given: $haveCount, needed: $needCount)");
229 if($iterations === 0){
230 $iterations = $multiplier;
231 }elseif($multiplier !== $iterations){
233 throw new TransactionValidationException(
"Expected $recipeItem x$iterations, but found x$multiplier");
237 if(count($txItems) > 0){
239 throw new TransactionValidationException(
"Expected 0 items left over, have " . count($txItems));
245 private function validateRecipe(CraftingRecipe $recipe, ?
int $expectedRepetitions) : int{
258 $this->squashDuplicateSlotChanges();
259 if(count($this->actions) < 1){
263 $this->matchItems($this->outputs, $this->inputs);
265 if($this->recipe ===
null){
267 foreach($this->craftingManager->matchRecipeByOutputs($this->outputs) as $recipe){
270 $this->repetitions = $this->matchOutputs($this->outputs, $recipe->getResultsFor($this->source->getCraftingGrid()));
272 self::matchIngredients($this->inputs, $recipe->getIngredientList(), $this->repetitions);
275 $this->recipe = $recipe;
283 if($this->recipe ===
null){
284 throw new TransactionValidationException(
"Unable to match a recipe to transaction (tried to match against $failed recipes)");
287 $this->repetitions = $this->validateRecipe($this->recipe, $this->repetitions);
292 $ev = new CraftItemEvent($this, $this->recipe, $this->repetitions, $this->inputs, $this->outputs);