Skip to content

Commit 084feae

Browse files
more work on ItemCombine
1 parent eaab4b3 commit 084feae

6 files changed

+252
-43
lines changed

src/crafting/AnvilCraftingManagerDataFiller.php

+1-2
Original file line numberDiff line numberDiff line change
@@ -67,8 +67,7 @@ public static function fillData(CraftingManager $manager) : CraftingManager{
6767
foreach(VanillaItems::getAll() as $item){
6868
if($item instanceof Durable){
6969
$itemId = GlobalItemDataHandlers::getSerializer()->serializeType($item)->getName();
70-
$manager->registerAnvilRecipe(new ItemCombineRecipe(
71-
new MetaWildcardRecipeIngredient($itemId),
70+
$manager->registerAnvilRecipe(new ItemSelfCombineRecipe(
7271
new MetaWildcardRecipeIngredient($itemId)
7372
));
7473
}

src/crafting/ItemCombineRecipe.php

+28-30
Original file line numberDiff line numberDiff line change
@@ -34,38 +34,15 @@
3434
use function max;
3535
use function min;
3636

37-
/**
38-
* Represent a recipe that repair an item with a material in an anvil.
39-
*/
40-
class ItemCombineRecipe implements AnvilRecipe{
41-
public function __construct(
42-
private RecipeIngredient $input,
43-
private RecipeIngredient $material
44-
){ }
45-
46-
public function getInput() : RecipeIngredient{
47-
return $this->input;
48-
}
49-
50-
public function getMaterial() : RecipeIngredient{
51-
return $this->material;
52-
}
37+
abstract class ItemCombineRecipe implements AnvilRecipe{
38+
abstract protected function validate(Item $input, Item $material) : bool;
5339

5440
public function getResultFor(Item $input, Item $material) : ?AnvilCraftResult{
55-
if($this->input->accepts($input) && $this->material->accepts($material)){
41+
if($this->validate($input, $material)){
5642
$result = (clone $input);
5743
$xpCost = 0;
58-
if($result instanceof Durable && $material instanceof Durable){
59-
$damage = $result->getDamage();
60-
if($damage !== 0){
61-
$baseMaxDurability = $result->getMaxDurability();
62-
$baseDurability = $baseMaxDurability - $damage;
63-
$materialDurability = $material->getMaxDurability() - $material->getDamage();
64-
$addDurability = (int) ($baseMaxDurability * 12 / 100);
65-
66-
$result->setDamage($baseMaxDurability - min($baseMaxDurability, $baseDurability + $materialDurability + $addDurability));
67-
}
68-
44+
if($result instanceof Durable && $material instanceof Durable && $this->repair($result, $material)){
45+
// The two items are compatible for repair
6946
$xpCost = 2;
7047
}
7148

@@ -94,7 +71,7 @@ public function getResultFor(Item $input, Item $material) : ?AnvilCraftResult{
9471
}
9572
}
9673

97-
$costAddition = match($enchantment->getRarity()){
74+
$costAddition = match ($enchantment->getRarity()) {
9875
Rarity::COMMON => 1,
9976
Rarity::UNCOMMON => 2,
10077
Rarity::RARE => 4,
@@ -117,9 +94,30 @@ public function getResultFor(Item $input, Item $material) : ?AnvilCraftResult{
11794
);
11895
}
11996

120-
return new AnvilCraftResult($xpCost, $result, null);
97+
if($xpCost !== 0){
98+
return new AnvilCraftResult($xpCost, $result, null);
99+
}
121100
}
122101

123102
return null;
124103
}
104+
105+
private function repair(Durable $result, Durable $material) : bool{
106+
$damage = $result->getDamage();
107+
if($damage === 0){
108+
return false;
109+
}
110+
111+
$baseMaxDurability = $result->getMaxDurability();
112+
$baseDurability = $baseMaxDurability - $damage;
113+
$materialDurability = $material->getMaxDurability() - $material->getDamage();
114+
$addDurability = (int) ($baseMaxDurability * 12 / 100);
115+
116+
$result->setDamage($baseMaxDurability - min(
117+
$baseMaxDurability,
118+
$baseDurability + $materialDurability + $addDurability
119+
));
120+
121+
return true;
122+
}
125123
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,41 @@
1+
<?php
2+
3+
/*
4+
*
5+
* ____ _ _ __ __ _ __ __ ____
6+
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7+
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8+
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9+
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Lesser General Public License as published by
13+
* the Free Software Foundation, either version 3 of the License, or
14+
* (at your option) any later version.
15+
*
16+
* @author PocketMine Team
17+
* @link http://www.pocketmine.net/
18+
*
19+
*
20+
*/
21+
22+
declare(strict_types=1);
23+
24+
namespace pocketmine\crafting;
25+
26+
use pocketmine\item\Item;
27+
28+
/**
29+
* Represent a recipe that repair an item with a material in an anvil.
30+
*/
31+
class ItemDifferentCombineRecipe extends ItemCombineRecipe{
32+
public function __construct(
33+
private RecipeIngredient $base,
34+
private RecipeIngredient $material
35+
){
36+
}
37+
38+
protected function validate(Item $input, Item $material) : bool{
39+
return $this->base->accepts($input) && $this->material->accepts($material);
40+
}
41+
}
+44
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
<?php
2+
3+
/*
4+
*
5+
* ____ _ _ __ __ _ __ __ ____
6+
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7+
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8+
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9+
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Lesser General Public License as published by
13+
* the Free Software Foundation, either version 3 of the License, or
14+
* (at your option) any later version.
15+
*
16+
* @author PocketMine Team
17+
* @link http://www.pocketmine.net/
18+
*
19+
*
20+
*/
21+
22+
declare(strict_types=1);
23+
24+
namespace pocketmine\crafting;
25+
26+
use pocketmine\item\Item;
27+
28+
/**
29+
* Represent a recipe that repair an item with a material in an anvil.
30+
*/
31+
class ItemSelfCombineRecipe extends ItemCombineRecipe{
32+
/**
33+
* @param RecipeIngredient $target The item that will be concerned by the combinaison.
34+
* The input and material have to be accepted by this ingredient to be able to combine them.
35+
*/
36+
public function __construct(
37+
private RecipeIngredient $target
38+
){
39+
}
40+
41+
protected function validate(Item $input, Item $material) : bool{
42+
return $this->target->accepts($input) && $this->target->accepts($material);
43+
}
44+
}

tests/phpunit/crafting/AnvilCraftTest.php

+101-11
Original file line numberDiff line numberDiff line change
@@ -25,12 +25,14 @@
2525

2626
use Generator;
2727
use PHPUnit\Framework\TestCase;
28+
use pocketmine\item\enchantment\EnchantmentInstance;
29+
use pocketmine\item\enchantment\VanillaEnchantments;
2830
use pocketmine\item\Item;
2931
use pocketmine\item\VanillaItems;
3032
use function floor;
3133

3234
class AnvilCraftTest extends TestCase{
33-
public static function materialRepairRecipe() : Generator{
35+
public static function materialRepairRecipeProvider() : Generator{
3436
yield "No repair available" => [
3537
VanillaItems::DIAMOND_PICKAXE(),
3638
VanillaItems::DIAMOND(),
@@ -76,29 +78,117 @@ public static function materialRepairRecipe() : Generator{
7678
}
7779

7880
/**
79-
* @dataProvider materialRepairRecipe
81+
* @dataProvider materialRepairRecipeProvider
8082
*/
8183
public function testMaterialRepairRecipe(Item $base, Item $material, ?AnvilCraftResult $expected) : void{
8284
$recipe = new MaterialRepairRecipe(
83-
new ExactRecipeIngredient((clone $base)->setCount(1)),
84-
new ExactRecipeIngredient((clone $material)->setCount(1))
85+
new WildcardRecipeIngredient(),
86+
new WildcardRecipeIngredient()
8587
);
86-
$result = $recipe->getResultFor($base, $material);
88+
self::assertAnvilCraftResultEquals($expected, $recipe->getResultFor($base, $material));
89+
}
90+
91+
public static function itemSelfCombineRecipeProvider() : Generator{
92+
yield "Combine two identical items without damage and enchants" => [
93+
VanillaItems::DIAMOND_PICKAXE(),
94+
VanillaItems::DIAMOND_PICKAXE(),
95+
null
96+
];
97+
98+
yield "Enchant on base item and no enchants on material with no damage" => [
99+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
100+
VanillaItems::DIAMOND_PICKAXE(),
101+
null
102+
];
103+
104+
yield "No enchant on base item and one enchant on material" => [
105+
VanillaItems::DIAMOND_PICKAXE(),
106+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
107+
new AnvilCraftResult(2, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)), null)
108+
];
109+
110+
yield "Combine two identical items with damage" => [
111+
VanillaItems::DIAMOND_PICKAXE()->setDamage(10),
112+
VanillaItems::DIAMOND_PICKAXE(),
113+
new AnvilCraftResult(1, VanillaItems::DIAMOND_PICKAXE()->setDamage(0), null)
114+
];
115+
116+
yield "Combine two identical items with damage for material" => [
117+
VanillaItems::DIAMOND_PICKAXE(),
118+
VanillaItems::DIAMOND_PICKAXE()->setDamage(10),
119+
null
120+
];
121+
122+
yield "Combine two identical items with different enchantments" => [
123+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)),
124+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
125+
new AnvilCraftResult(2, VanillaItems::DIAMOND_PICKAXE()
126+
->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2))
127+
->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
128+
null)
129+
];
130+
131+
yield "Combine two identical items with different enchantments with damage" => [
132+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2))->setDamage(10),
133+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
134+
new AnvilCraftResult(4, VanillaItems::DIAMOND_PICKAXE()
135+
->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2))
136+
->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
137+
null)
138+
];
139+
140+
yield "Combine two identical items with different enchantments with damage for material" => [
141+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2)),
142+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1))->setDamage(10),
143+
new AnvilCraftResult(2, VanillaItems::DIAMOND_PICKAXE()
144+
->addEnchantment(new EnchantmentInstance(VanillaEnchantments::EFFICIENCY(), 2))
145+
->addEnchantment(new EnchantmentInstance(VanillaEnchantments::UNBREAKING(), 1)),
146+
null)
147+
];
148+
149+
yield "Combine two identical items with same enchantment level" => [
150+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)),
151+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)),
152+
new AnvilCraftResult(8, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 2)), null)
153+
];
154+
155+
yield "Combine two identical items with same enchantment level and damage" => [
156+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1))->setDamage(10),
157+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)),
158+
new AnvilCraftResult(10, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 2))->setDamage(0), null)
159+
];
160+
161+
yield "Combine two identical items with same enchantment level and damage for material" => [
162+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1)),
163+
VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 1))->setDamage(10),
164+
new AnvilCraftResult(8, VanillaItems::DIAMOND_PICKAXE()->addEnchantment(new EnchantmentInstance(VanillaEnchantments::FORTUNE(), 2)), null)
165+
];
166+
}
167+
168+
/**
169+
* @dataProvider itemSelfCombineRecipeProvider
170+
*/
171+
public function testItemSelfCombineRecipe(Item $base, Item $combined, ?AnvilCraftResult $expected) : void{
172+
$recipe = new ItemSelfCombineRecipe(new WildcardRecipeIngredient());
173+
self::assertAnvilCraftResultEquals($expected, $recipe->getResultFor($base, $combined));
174+
}
175+
176+
private static function assertAnvilCraftResultEquals(?AnvilCraftResult $expected, ?AnvilCraftResult $actual) : void{
87177
if($expected === null){
88-
self::assertNull($result, "Recipe did not match expected result");
178+
self::assertNull($actual, "Recipe did not match expected result");
89179
return;
90180
}else{
91-
self::assertNotNull($result, "Recipe did not match expected result");
181+
self::assertNotNull($actual, "Recipe did not match expected result");
92182
}
93-
self::assertEquals($expected->getXpCost(), $result->getXpCost(), "XP cost did not match expected result");
94-
self::assertTrue($expected->getOutput()->equalsExact($result->getOutput()), "Recipe output did not match expected result");
183+
self::assertEquals($expected->getXpCost(), $actual->getXpCost(), "XP cost did not match expected result");
184+
self::assertTrue($expected->getOutput()->equalsExact($actual->getOutput()), "Recipe output did not match expected result");
95185
$sacrificeResult = $expected->getSacrificeResult();
96186
if($sacrificeResult !== null){
97-
$resultExpected = $result->getSacrificeResult();
187+
$resultExpected = $actual->getSacrificeResult();
98188
self::assertNotNull($resultExpected, "Recipe sacrifice result did not match expected result");
99189
self::assertTrue($sacrificeResult->equalsExact($resultExpected), "Recipe sacrifice result did not match expected result");
100190
}else{
101-
self::assertNull($result->getSacrificeResult(), "Recipe sacrifice result did not match expected result");
191+
self::assertNull($actual->getSacrificeResult(), "Recipe sacrifice result did not match expected result");
102192
}
103193
}
104194
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,37 @@
1+
<?php
2+
3+
/*
4+
*
5+
* ____ _ _ __ __ _ __ __ ____
6+
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7+
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8+
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9+
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10+
*
11+
* This program is free software: you can redistribute it and/or modify
12+
* it under the terms of the GNU Lesser General Public License as published by
13+
* the Free Software Foundation, either version 3 of the License, or
14+
* (at your option) any later version.
15+
*
16+
* @author PocketMine Team
17+
* @link http://www.pocketmine.net/
18+
*
19+
*
20+
*/
21+
22+
declare(strict_types=1);
23+
24+
namespace pocketmine\crafting;
25+
26+
use pocketmine\item\Item;
27+
28+
final class WildcardRecipeIngredient implements RecipeIngredient{
29+
30+
public function __toString() : string{
31+
return "WildcardRecipeIngredient()";
32+
}
33+
34+
public function accepts(Item $item) : bool{
35+
return true;
36+
}
37+
}

0 commit comments

Comments
 (0)