From 084feaef53e7d00a6db4346fbc625f3ceaf5bb0b Mon Sep 17 00:00:00 2001 From: ShockedPlot7560 Date: Sun, 23 Mar 2025 17:14:20 +0100 Subject: [PATCH] more work on ItemCombine --- .../AnvilCraftingManagerDataFiller.php | 3 +- src/crafting/ItemCombineRecipe.php | 58 +++++---- src/crafting/ItemDifferentCombineRecipe.php | 41 +++++++ src/crafting/ItemSelfCombineRecipe.php | 44 +++++++ tests/phpunit/crafting/AnvilCraftTest.php | 112 ++++++++++++++++-- .../crafting/WildcardRecipeIngredient.php | 37 ++++++ 6 files changed, 252 insertions(+), 43 deletions(-) create mode 100644 src/crafting/ItemDifferentCombineRecipe.php create mode 100644 src/crafting/ItemSelfCombineRecipe.php create mode 100644 tests/phpunit/crafting/WildcardRecipeIngredient.php diff --git a/src/crafting/AnvilCraftingManagerDataFiller.php b/src/crafting/AnvilCraftingManagerDataFiller.php index ee7abb7ea..d23d7ba1f 100644 --- a/src/crafting/AnvilCraftingManagerDataFiller.php +++ b/src/crafting/AnvilCraftingManagerDataFiller.php @@ -67,8 +67,7 @@ final class AnvilCraftingManagerDataFiller{ foreach(VanillaItems::getAll() as $item){ if($item instanceof Durable){ $itemId = GlobalItemDataHandlers::getSerializer()->serializeType($item)->getName(); - $manager->registerAnvilRecipe(new ItemCombineRecipe( - new MetaWildcardRecipeIngredient($itemId), + $manager->registerAnvilRecipe(new ItemSelfCombineRecipe( new MetaWildcardRecipeIngredient($itemId) )); } diff --git a/src/crafting/ItemCombineRecipe.php b/src/crafting/ItemCombineRecipe.php index 2c90eaed4..52f793152 100644 --- a/src/crafting/ItemCombineRecipe.php +++ b/src/crafting/ItemCombineRecipe.php @@ -34,38 +34,15 @@ use function floor; use function max; use function min; -/** - * Represent a recipe that repair an item with a material in an anvil. - */ -class ItemCombineRecipe implements AnvilRecipe{ - public function __construct( - private RecipeIngredient $input, - private RecipeIngredient $material - ){ } - - public function getInput() : RecipeIngredient{ - return $this->input; - } - - public function getMaterial() : RecipeIngredient{ - return $this->material; - } +abstract class ItemCombineRecipe implements AnvilRecipe{ + abstract protected function validate(Item $input, Item $material) : bool; public function getResultFor(Item $input, Item $material) : ?AnvilCraftResult{ - if($this->input->accepts($input) && $this->material->accepts($material)){ + if($this->validate($input, $material)){ $result = (clone $input); $xpCost = 0; - if($result instanceof Durable && $material instanceof Durable){ - $damage = $result->getDamage(); - if($damage !== 0){ - $baseMaxDurability = $result->getMaxDurability(); - $baseDurability = $baseMaxDurability - $damage; - $materialDurability = $material->getMaxDurability() - $material->getDamage(); - $addDurability = (int) ($baseMaxDurability * 12 / 100); - - $result->setDamage($baseMaxDurability - min($baseMaxDurability, $baseDurability + $materialDurability + $addDurability)); - } - + if($result instanceof Durable && $material instanceof Durable && $this->repair($result, $material)){ + // The two items are compatible for repair $xpCost = 2; } @@ -94,7 +71,7 @@ class ItemCombineRecipe implements AnvilRecipe{ } } - $costAddition = match($enchantment->getRarity()){ + $costAddition = match ($enchantment->getRarity()) { Rarity::COMMON => 1, Rarity::UNCOMMON => 2, Rarity::RARE => 4, @@ -117,9 +94,30 @@ class ItemCombineRecipe implements AnvilRecipe{ ); } - return new AnvilCraftResult($xpCost, $result, null); + if($xpCost !== 0){ + return new AnvilCraftResult($xpCost, $result, null); + } } return null; } + + private function repair(Durable $result, Durable $material) : bool{ + $damage = $result->getDamage(); + if($damage === 0){ + return false; + } + + $baseMaxDurability = $result->getMaxDurability(); + $baseDurability = $baseMaxDurability - $damage; + $materialDurability = $material->getMaxDurability() - $material->getDamage(); + $addDurability = (int) ($baseMaxDurability * 12 / 100); + + $result->setDamage($baseMaxDurability - min( + $baseMaxDurability, + $baseDurability + $materialDurability + $addDurability + )); + + return true; + } } diff --git a/src/crafting/ItemDifferentCombineRecipe.php b/src/crafting/ItemDifferentCombineRecipe.php new file mode 100644 index 000000000..acd7f39c3 --- /dev/null +++ b/src/crafting/ItemDifferentCombineRecipe.php @@ -0,0 +1,41 @@ +base->accepts($input) && $this->material->accepts($material); + } +} diff --git a/src/crafting/ItemSelfCombineRecipe.php b/src/crafting/ItemSelfCombineRecipe.php new file mode 100644 index 000000000..a5f0a8335 --- /dev/null +++ b/src/crafting/ItemSelfCombineRecipe.php @@ -0,0 +1,44 @@ +target->accepts($input) && $this->target->accepts($material); + } +} diff --git a/tests/phpunit/crafting/AnvilCraftTest.php b/tests/phpunit/crafting/AnvilCraftTest.php index b35bfb352..5d5379b93 100644 --- a/tests/phpunit/crafting/AnvilCraftTest.php +++ b/tests/phpunit/crafting/AnvilCraftTest.php @@ -25,12 +25,14 @@ namespace pocketmine\crafting; use Generator; use PHPUnit\Framework\TestCase; +use pocketmine\item\enchantment\EnchantmentInstance; +use pocketmine\item\enchantment\VanillaEnchantments; use pocketmine\item\Item; use pocketmine\item\VanillaItems; use function floor; class AnvilCraftTest extends TestCase{ - public static function materialRepairRecipe() : Generator{ + public static function materialRepairRecipeProvider() : Generator{ yield "No repair available" => [ VanillaItems::DIAMOND_PICKAXE(), VanillaItems::DIAMOND(), @@ -76,29 +78,117 @@ class AnvilCraftTest extends TestCase{ } /** - * @dataProvider materialRepairRecipe + * @dataProvider materialRepairRecipeProvider */ public function testMaterialRepairRecipe(Item $base, Item $material, ?AnvilCraftResult $expected) : void{ $recipe = new MaterialRepairRecipe( - new ExactRecipeIngredient((clone $base)->setCount(1)), - new ExactRecipeIngredient((clone $material)->setCount(1)) + new WildcardRecipeIngredient(), + new WildcardRecipeIngredient() ); - $result = $recipe->getResultFor($base, $material); + self::assertAnvilCraftResultEquals($expected, $recipe->getResultFor($base, $material)); + } + + public static function itemSelfCombineRecipeProvider() : Generator{ + yield "Combine two identical items without damage and enchants" => [ + VanillaItems::DIAMOND_PICKAXE(), + VanillaItems::DIAMOND_PICKAXE(), + null + ]; + + yield "Enchant on base item and no enchants on material with no damage" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + VanillaItems::DIAMOND_PICKAXE(), + null + ]; + + yield "No enchant on base item and one enchant on material" => [ + VanillaItems::DIAMOND_PICKAXE(), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + new AnvilCraftResult(2, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), null) + ]; + + yield "Combine two identical items with damage" => [ + VanillaItems::DIAMOND_PICKAXE()->setDamage(10), + VanillaItems::DIAMOND_PICKAXE(), + new AnvilCraftResult(1, VanillaItems::DIAMOND_PICKAXE()->setDamage(0), null) + ]; + + yield "Combine two identical items with damage for material" => [ + VanillaItems::DIAMOND_PICKAXE(), + VanillaItems::DIAMOND_PICKAXE()->setDamage(10), + null + ]; + + yield "Combine two identical items with different enchantments" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + new AnvilCraftResult(2, VanillaItems::DIAMOND_PICKAXE() + ->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)) + ->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + null) + ]; + + yield "Combine two identical items with different enchantments with damage" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2))->setDamage(10), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + new AnvilCraftResult(4, VanillaItems::DIAMOND_PICKAXE() + ->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)) + ->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + null) + ]; + + yield "Combine two identical items with different enchantments with damage for material" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1))->setDamage(10), + new AnvilCraftResult(2, VanillaItems::DIAMOND_PICKAXE() + ->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)) + ->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), + null) + ]; + + yield "Combine two identical items with same enchantment level" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)), + new AnvilCraftResult(8, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 2)), null) + ]; + + yield "Combine two identical items with same enchantment level and damage" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1))->setDamage(10), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)), + new AnvilCraftResult(10, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 2))->setDamage(0), null) + ]; + + yield "Combine two identical items with same enchantment level and damage for material" => [ + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)), + VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1))->setDamage(10), + new AnvilCraftResult(8, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 2)), null) + ]; + } + + /** + * @dataProvider itemSelfCombineRecipeProvider + */ + public function testItemSelfCombineRecipe(Item $base, Item $combined, ?AnvilCraftResult $expected) : void{ + $recipe = new ItemSelfCombineRecipe(new WildcardRecipeIngredient()); + self::assertAnvilCraftResultEquals($expected, $recipe->getResultFor($base, $combined)); + } + + private static function assertAnvilCraftResultEquals(?AnvilCraftResult $expected, ?AnvilCraftResult $actual) : void{ if($expected === null){ - self::assertNull($result, "Recipe did not match expected result"); + self::assertNull($actual, "Recipe did not match expected result"); return; }else{ - self::assertNotNull($result, "Recipe did not match expected result"); + self::assertNotNull($actual, "Recipe did not match expected result"); } - self::assertEquals($expected->getXpCost(), $result->getXpCost(), "XP cost did not match expected result"); - self::assertTrue($expected->getOutput()->equalsExact($result->getOutput()), "Recipe output did not match expected result"); + self::assertEquals($expected->getXpCost(), $actual->getXpCost(), "XP cost did not match expected result"); + self::assertTrue($expected->getOutput()->equalsExact($actual->getOutput()), "Recipe output did not match expected result"); $sacrificeResult = $expected->getSacrificeResult(); if($sacrificeResult !== null){ - $resultExpected = $result->getSacrificeResult(); + $resultExpected = $actual->getSacrificeResult(); self::assertNotNull($resultExpected, "Recipe sacrifice result did not match expected result"); self::assertTrue($sacrificeResult->equalsExact($resultExpected), "Recipe sacrifice result did not match expected result"); }else{ - self::assertNull($result->getSacrificeResult(), "Recipe sacrifice result did not match expected result"); + self::assertNull($actual->getSacrificeResult(), "Recipe sacrifice result did not match expected result"); } } } diff --git a/tests/phpunit/crafting/WildcardRecipeIngredient.php b/tests/phpunit/crafting/WildcardRecipeIngredient.php new file mode 100644 index 000000000..32642402e --- /dev/null +++ b/tests/phpunit/crafting/WildcardRecipeIngredient.php @@ -0,0 +1,37 @@ +