rewrote the system with CraftingManager

This commit is contained in:
ShockedPlot7560 2024-12-14 15:15:15 +01:00
parent 947c8a0621
commit 5b9dc2c275
No known key found for this signature in database
GPG Key ID: D7539B420F1FA86E
18 changed files with 319 additions and 443 deletions

View File

@ -1,63 +0,0 @@
<?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\block\anvil;
use pocketmine\item\Item;
abstract class AnvilAction{
protected int $xpCost = 0;
final public function __construct(
protected Item $base,
protected Item $material,
protected ?string $customName
){ }
/**
* Returns the XP cost requested for this action.
* This XP cost will be summed up to the total XP cost of the anvil operation.
*/
final public function getXpCost() : int{
return $this->xpCost;
}
/**
* If only actions marked as free of repair cost is applied, the result item
* will not have any repair cost increase.
*/
public function isFreeOfRepairCost() : bool {
return false;
}
/**
* Processing an action means applying the changes to the result item
* and updating the XP cost property of the action.
*/
abstract public function process(Item $resultItem) : void;
/**
* Returns whether this action is valid and can be applied.
*/
abstract public function canBeApplied() : bool;
}

View File

@ -1,71 +0,0 @@
<?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\block\anvil;
use pocketmine\item\Item;
use pocketmine\utils\SingletonTrait;
use function is_subclass_of;
final class AnvilActionsFactory{
use SingletonTrait;
/** @var array<class-string<AnvilAction>, true> */
private array $actions = [];
private function __construct(){
$this->register(RenameItemAction::class);
$this->register(CombineEnchantmentsAction::class);
$this->register(RepairWithSacrificeAction::class);
$this->register(RepairWithMaterialAction::class);
}
/**
* @param class-string<AnvilAction> $class
*/
public function register(string $class) : void{
if(!is_subclass_of($class, AnvilAction::class, true)){
throw new \InvalidArgumentException("Class $class is not an AnvilAction");
}
if(isset($this->actions[$class])){
throw new \InvalidArgumentException("Class $class is already registered");
}
$this->actions[$class] = true;
}
/**
* Return all available actions for the given items.
*
* @return AnvilAction[]
*/
public function getActions(Item $base, Item $material, ?string $customName) : array{
$actions = [];
foreach($this->actions as $class => $_){
$action = new $class($base, $material, $customName);
if($action->canBeApplied()){
$actions[] = $action;
}
}
return $actions;
}
}

View File

@ -1,82 +0,0 @@
<?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\block\anvil;
use pocketmine\inventory\transaction\TransactionValidationException;
use pocketmine\item\EnchantedBook;
use pocketmine\item\enchantment\AvailableEnchantmentRegistry;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\Rarity;
use pocketmine\item\Item;
use function floor;
use function max;
use function min;
final class CombineEnchantmentsAction extends AnvilAction{
public function canBeApplied() : bool{
return $this->material->hasEnchantments();
}
public function process(Item $resultItem) : void{
foreach($this->material->getEnchantments() as $instance){
$enchantment = $instance->getType();
$level = $instance->getLevel();
if(!AvailableEnchantmentRegistry::getInstance()->isAvailableForItem($enchantment, $this->base)){
continue;
}
if(($targetEnchantment = $this->base->getEnchantment($enchantment)) !== null){
// Enchant already present on the target item
$targetLevel = $targetEnchantment->getLevel();
$newLevel = ($targetLevel === $level ? $targetLevel + 1 : max($targetLevel, $level));
$level = min($newLevel, $enchantment->getMaxLevel());
$instance = new EnchantmentInstance($enchantment, $level);
}else{
// Check if the enchantment is compatible with the existing enchantments
foreach($this->base->getEnchantments() as $testedInstance){
$testedEnchantment = $testedInstance->getType();
if(!$testedEnchantment->isCompatibleWith($enchantment)){
$this->xpCost++;
continue 2;
}
}
}
$costAddition = match($enchantment->getRarity()){
Rarity::COMMON => 1,
Rarity::UNCOMMON => 2,
Rarity::RARE => 4,
Rarity::MYTHIC => 8,
default => throw new TransactionValidationException("Invalid rarity " . $enchantment->getRarity() . " found")
};
if($this->material instanceof EnchantedBook){
// Enchanted books are half as expensive to combine
$costAddition = max(1, $costAddition / 2);
}
$levelDifference = $instance->getLevel() - $this->base->getEnchantmentLevel($instance->getType());
$this->xpCost += (int) floor($costAddition * $levelDifference);
$resultItem->addEnchantment($instance);
}
}
}

View File

@ -1,58 +0,0 @@
<?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\block\anvil;
use pocketmine\item\Durable;
use pocketmine\item\Item;
use function assert;
use function ceil;
use function floor;
use function max;
use function min;
final class RepairWithMaterialAction extends AnvilAction{
private const COST = 1;
public function canBeApplied() : bool{
return $this->base instanceof Durable &&
$this->base->isValidRepairMaterial($this->material) &&
$this->base->getDamage() > 0;
}
public function process(Item $resultItem) : void{
assert($resultItem instanceof Durable, "Result item must be durable");
assert($this->base instanceof Durable, "Base item must be durable");
$damage = $this->base->getDamage();
$quarter = min($damage, (int) floor($this->base->getMaxDurability() / 4));
$numberRepair = min($this->material->getCount(), (int) ceil($damage / $quarter));
if($numberRepair > 0){
$this->material->pop($numberRepair);
$damage -= $quarter * $numberRepair;
}
$resultItem->setDamage(max(0, $damage));
$this->xpCost = (int) floor($numberRepair * self::COST);
}
}

View File

@ -1,58 +0,0 @@
<?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\block\anvil;
use pocketmine\item\Durable;
use pocketmine\item\Item;
use function assert;
use function min;
final class RepairWithSacrificeAction extends AnvilAction{
private const COST = 2;
public function canBeApplied() : bool{
return $this->base instanceof Durable &&
$this->material instanceof Durable &&
$this->base->getTypeId() === $this->material->getTypeId();
}
public function process(Item $resultItem) : void{
assert($resultItem instanceof Durable, "Result item must be durable");
assert($this->base instanceof Durable, "Base item must be durable");
assert($this->material instanceof Durable, "Material item must be durable");
if($this->base->getDamage() !== 0){
$baseMaxDurability = $this->base->getMaxDurability();
$baseDurability = $baseMaxDurability - $this->base->getDamage();
$materialDurability = $this->material->getMaxDurability() - $this->material->getDamage();
$addDurability = (int) ($baseMaxDurability * 12 / 100);
$newDurability = min($baseMaxDurability, $baseDurability + $materialDurability + $addDurability);
$resultItem->setDamage($baseMaxDurability - $newDurability);
$this->xpCost = self::COST;
}
}
}

View File

@ -23,10 +23,10 @@ declare(strict_types=1);
namespace pocketmine\block\utils;
use pocketmine\block\anvil\AnvilActionsFactory;
use pocketmine\crafting\AnvilCraftResult;
use pocketmine\item\Item;
use pocketmine\player\Player;
use function max;
use pocketmine\Server;
final class AnvilHelper{
private const COST_LIMIT = 39;
@ -36,31 +36,29 @@ final class AnvilHelper{
*
* Returns null if the operation can't do anything.
*/
public static function calculateResult(Player $player, Item $base, Item $material, ?string $customName = null) : ?AnvilResult {
$xpCost = 0;
$resultItem = clone $base;
public static function calculateResult(Player $player, Item $base, Item $material, ?string $customName = null) : ?AnvilCraftResult {
$additionnalRepairCost = 0;
foreach(AnvilActionsFactory::getInstance()->getActions($base, $material, $customName) as $action){
$action->process($resultItem);
if(!$action->isFreeOfRepairCost() && $action->getXpCost() > 0){
// Repair cost increment if the item has been processed
// and any of the action is not free of repair cost
$additionnalRepairCost = 1;
$recipe = Server::getInstance()->getCraftingManager()->matchAnvilRecipe($base, $material);
$result = $recipe->getResultFor($base, $material);
if($result !== null){
$resultItem = $result->getResult();
$xpCost = $result->getXpCost();
if(($customName === null || $customName === "") && $resultItem->hasCustomName()){
$xpCost++;
$resultItem->clearCustomName();
}elseif($resultItem->getCustomName() !== $customName){
$xpCost++;
$resultItem->setCustomName($customName);
}
$xpCost += $action->getXpCost();
$result = new AnvilCraftResult($xpCost, $resultItem);
}
$xpCost += 2 ** $resultItem->getAnvilRepairCost() - 1;
$xpCost += 2 ** $material->getAnvilRepairCost() - 1;
$resultItem->setAnvilRepairCost(
max($resultItem->getAnvilRepairCost(), $material->getAnvilRepairCost()) + $additionnalRepairCost
);
if($xpCost <= 0 || ($xpCost > self::COST_LIMIT && !$player->isCreative())){
if($result === null || $result->getXpCost() <= 0 || ($result->getXpCost() > self::COST_LIMIT && !$player->isCreative())){
return null;
}
return new AnvilResult($xpCost, $resultItem);
return $result;
}
}

View File

@ -21,21 +21,21 @@
declare(strict_types=1);
namespace pocketmine\block\utils;
namespace pocketmine\crafting;
use pocketmine\item\Item;
class AnvilResult{
class AnvilCraftResult{
public function __construct(
private int $repairCost,
private ?Item $result,
private int $xpCost,
private Item $result,
){}
public function getRepairCost() : int{
return $this->repairCost;
public function getXpCost() : int{
return $this->xpCost;
}
public function getResult() : ?Item{
public function getResult() : Item{
return $this->result;
}
}

View File

@ -21,29 +21,12 @@
declare(strict_types=1);
namespace pocketmine\block\anvil;
namespace pocketmine\crafting;
use pocketmine\item\Item;
use function strlen;
final class RenameItemAction extends AnvilAction{
private const COST = 1;
interface AnvilRecipe{
public function getXpCost() : int;
public function canBeApplied() : bool{
return true;
}
public function process(Item $resultItem) : void{
if($this->customName === null || strlen($this->customName) === 0){
if($this->base->hasCustomName()){
$this->xpCost += self::COST;
$resultItem->clearCustomName();
}
}else{
if($this->base->getCustomName() !== $this->customName){
$this->xpCost += self::COST;
$resultItem->setCustomName($this->customName);
}
}
}
public function getResultFor(Item $input, Item $material) : ?AnvilCraftResult;
}

View File

@ -76,6 +76,18 @@ class CraftingManager{
*/
private array $brewingRecipeCache = [];
/**
* @var AnvilRecipe[]
* @phpstan-var list<AnvilRecipe>
*/
private array $anvilRecipes = [];
/**
* @var AnvilRecipe[][]
* @phpstan-var array<int, array<int, AnvilRecipe>>
*/
private array $anvilRecipeCache = [];
/** @phpstan-var ObjectSet<\Closure() : void> */
private ObjectSet $recipeRegisteredCallbacks;
@ -197,6 +209,14 @@ class CraftingManager{
return $this->potionContainerChangeRecipes;
}
/**
* @return AnvilRecipe[][]
* @phpstan-return list<AnvilRecipe>
*/
public function getAnvilRecipes() : array{
return $this->anvilRecipes;
}
public function registerShapedRecipe(ShapedRecipe $recipe) : void{
$this->shapedRecipes[self::hashOutputs($recipe->getResults())][] = $recipe;
$this->craftingRecipeIndex[] = $recipe;
@ -231,6 +251,14 @@ class CraftingManager{
}
}
public function registerAnvilRecipe(AnvilRecipe $recipe) : void{
$this->anvilRecipes[] = $recipe;
foreach($this->recipeRegisteredCallbacks as $callback){
$callback();
}
}
/**
* @param Item[] $outputs
*/
@ -304,4 +332,21 @@ class CraftingManager{
return null;
}
public function matchAnvilRecipe(Item $input, Item $material) : ?AnvilRecipe{
$inputHash = $input->getStateId();
$materialHash = $material->getStateId();
$cached = $this->anvilRecipeCache[$inputHash][$materialHash] ?? null;
if($cached !== null){
return $cached;
}
foreach($this->anvilRecipes as $recipe){
if($recipe->getResultFor($input, $material) !== null){
return $this->anvilRecipeCache[$inputHash][$materialHash] = $recipe;
}
}
return null;
}
}

View File

@ -38,6 +38,7 @@ use pocketmine\data\bedrock\item\SavedItemStackData;
use pocketmine\data\SavedDataLoadingException;
use pocketmine\errorhandler\ErrorToExceptionHandler;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
use pocketmine\nbt\LittleEndianNbtSerializer;
use pocketmine\utils\Filesystem;
use pocketmine\utils\Utils;
@ -333,6 +334,18 @@ final class CraftingManagerFromDataHelper{
));
}
$result->registerAnvilRecipe(new MaterialRepairRecipe(
new ExactRecipeIngredient(VanillaItems::DIAMOND_PICKAXE()),
new ExactRecipeIngredient(VanillaItems::DIAMOND()),
VanillaItems::DIAMOND_PICKAXE()
));
$result->registerAnvilRecipe(new ItemCombineRecipe(
new ExactRecipeIngredient(VanillaItems::DIAMOND_PICKAXE()),
new ExactRecipeIngredient(VanillaItems::DIAMOND_PICKAXE()),
VanillaItems::DIAMOND_PICKAXE()
));
//TODO: smithing
return $result;

View File

@ -0,0 +1,139 @@
<?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\inventory\transaction\TransactionValidationException;
use pocketmine\item\Durable;
use pocketmine\item\EnchantedBook;
use pocketmine\item\enchantment\AvailableEnchantmentRegistry;
use pocketmine\item\enchantment\EnchantmentInstance;
use pocketmine\item\enchantment\Rarity;
use pocketmine\item\Item;
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,
private Item $result
){ }
public function getInput() : RecipeIngredient{
return $this->input;
}
public function getMaterial() : RecipeIngredient{
return $this->material;
}
public function getResult() : Item{
return clone $this->result;
}
public function getXpCost() : int{
return 2;
}
public function getResultFor(Item $input, Item $material) : ?AnvilCraftResult{
if($input->equals($this->input->getItem()) && $material->equals($this->material->getItem())){
$result = $this->getResult();
$xpCost = 0;
if($input instanceof Durable && $material instanceof Durable){
$damage = $input->getDamage();
if($damage !== 0){
$baseMaxDurability = $input->getMaxDurability();
$baseDurability = $baseMaxDurability - $damage;
$materialDurability = $material->getMaxDurability() - $material->getDamage();
$addDurability = (int) ($baseMaxDurability * 12 / 100);
$result->setDamage($baseMaxDurability - min($baseMaxDurability, $baseDurability + $materialDurability + $addDurability));
}
$xpCost = 2;
}
// setting base enchantments to result
foreach($input->getEnchantments() as $enchantment){
$result->addEnchantment($enchantment);
}
// combining enchantments
foreach($material->getEnchantments() as $instance){
$enchantment = $instance->getType();
$level = $instance->getLevel();
if(!AvailableEnchantmentRegistry::getInstance()->isAvailableForItem($enchantment, $input)){
continue;
}
if(($targetEnchantment = $input->getEnchantment($enchantment)) !== null){
// Enchant already present on the target item
$targetLevel = $targetEnchantment->getLevel();
$newLevel = ($targetLevel === $level ? $targetLevel + 1 : max($targetLevel, $level));
$level = min($newLevel, $enchantment->getMaxLevel());
$instance = new EnchantmentInstance($enchantment, $level);
}else{
// Check if the enchantment is compatible with the existing enchantments
foreach($input->getEnchantments() as $testedInstance){
$testedEnchantment = $testedInstance->getType();
if(!$testedEnchantment->isCompatibleWith($enchantment)){
$xpCost++;
//TODO: XP COST
continue 2;
}
}
}
$costAddition = match($enchantment->getRarity()){
Rarity::COMMON => 1,
Rarity::UNCOMMON => 2,
Rarity::RARE => 4,
Rarity::MYTHIC => 8,
default => throw new TransactionValidationException("Invalid rarity " . $enchantment->getRarity() . " found")
};
if($material instanceof EnchantedBook){
// Enchanted books are half as expensive to combine
$costAddition = max(1, $costAddition / 2);
}
$levelDifference = $instance->getLevel() - $input->getEnchantmentLevel($instance->getType());
$xpCost += (int) floor($costAddition * $levelDifference);
$result->addEnchantment($instance);
$xpCost += 2 ** $input->getAnvilRepairCost() - 1;
$xpCost += 2 ** $material->getAnvilRepairCost() - 1;
$result->setAnvilRepairCost(
max($result->getAnvilRepairCost(), $material->getAnvilRepairCost()) + 1
);
}
return new AnvilCraftResult($xpCost, $result);
}
return null;
}
}

View File

@ -0,0 +1,74 @@
<?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 function ceil;
use function floor;
use function max;
use function min;
/**
* Represent a recipe that repair an item with a material in an anvil.
*/
class MaterialRepairRecipe implements AnvilRecipe{
public function __construct(
private RecipeIngredient $input,
private RecipeIngredient $material,
private Item $result
){ }
public function getInput() : RecipeIngredient{
return $this->input;
}
public function getMaterial() : RecipeIngredient{
return $this->material;
}
public function getResult() : Item{
return clone $this->result;
}
public function getXpCost() : int{
return 1;
}
public function getResultFor(Item $input, Item $material) : ?Item{
if($this->input->accepts($input) && $this->material->accepts($material)){
$damage = $input->getDamage();
if($damage !== 0){
$quarter = min($damage, (int) floor($input->getMaxDurability() / 4));
$numberRepair = min($material->getCount(), (int) ceil($damage / $quarter));
if($numberRepair > 0){
// TODO: remove the material
$damage -= $quarter * $numberRepair;
}
return $this->getResult()->setDamage(max(0, $damage));
}
}
return null;
}
}

View File

@ -26,8 +26,8 @@ namespace pocketmine\inventory\transaction;
use pocketmine\block\Anvil;
use pocketmine\block\inventory\AnvilInventory;
use pocketmine\block\utils\AnvilHelper;
use pocketmine\block\utils\AnvilResult;
use pocketmine\block\VanillaBlocks;
use pocketmine\crafting\AnvilCraftResult;
use pocketmine\event\player\PlayerUseAnvilEvent;
use pocketmine\item\Item;
use pocketmine\item\VanillaItems;
@ -44,14 +44,14 @@ class AnvilTransaction extends InventoryTransaction{
public function __construct(
Player $source,
private readonly AnvilResult $expectedResult,
private readonly AnvilCraftResult $expectedResult,
private readonly ?string $customName
) {
parent::__construct($source);
}
private function validateFiniteResources(int $xpSpent) : void{
$expectedXpCost = $this->expectedResult->getRepairCost();
$expectedXpCost = $this->expectedResult->getXpCost();
if($xpSpent !== $expectedXpCost){
throw new TransactionValidationException("Expected the amount of xp spent to be $expectedXpCost, but received $xpSpent");
}
@ -62,20 +62,20 @@ class AnvilTransaction extends InventoryTransaction{
}
}
private function validateInputs(Item $base, Item $material, Item $expectedOutput) : ?AnvilResult {
private function validateInputs(Item $base, Item $material, Item $expectedOutput) : ?int {
$calculAttempt = AnvilHelper::calculateResult($this->source, $base, $material, $this->customName);
if($calculAttempt === null){
return null;
}
$result = $calculAttempt->getResult();
if($result === null || !$result->equalsExact($expectedOutput)){
if(!$result->equalsExact($expectedOutput)){
return null;
}
$this->baseItem = $base;
$this->materialItem = $material;
return $calculAttempt;
return $calculAttempt->getXpCost();
}
public function validate() : void{
@ -102,16 +102,16 @@ class AnvilTransaction extends InventoryTransaction{
}
if(count($inputs) < 2){
$attempt = $this->validateInputs($inputs[0], VanillaItems::AIR(), $outputItem) ??
$xpCost = $this->validateInputs($inputs[0], VanillaItems::AIR(), $outputItem) ??
throw new TransactionValidationException("Inputs do not match expected result");
}else{
$attempt = $this->validateInputs($inputs[0], $inputs[1], $outputItem) ??
$xpCost = $this->validateInputs($inputs[0], $inputs[1], $outputItem) ??
$this->validateInputs($inputs[1], $inputs[0], $outputItem) ??
throw new TransactionValidationException("Inputs do not match expected result");
}
if($this->source->hasFiniteResources()){
$this->validateFiniteResources($attempt->getRepairCost());
$this->validateFiniteResources($xpCost);
}
}
@ -150,7 +150,7 @@ class AnvilTransaction extends InventoryTransaction{
$ev = new PlayerUseAnvilEvent($this->source, $this->baseItem, $this->materialItem, $this->expectedResult->getResult() ?? throw new \AssertionError(
"Expected that the expected result is not null"
), $this->customName, $this->expectedResult->getRepairCost());
), $this->customName, $this->expectedResult->getXpCost());
$ev->call();
return !$ev->isCancelled();
}

View File

@ -33,7 +33,6 @@ use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\player\Player;
use pocketmine\utils\Binary;
use function in_array;
use function lcg_value;
use function mt_rand;
@ -173,8 +172,4 @@ class Armor extends Durable{
$tag->setInt(self::TAG_CUSTOM_COLOR, Binary::signInt($this->customColor->toARGB())) :
$tag->removeTag(self::TAG_CUSTOM_COLOR);
}
public function isValidRepairMaterial(Item $material) : bool{
return in_array($material->getTypeId(), $this->armorInfo->getMaterial()->getRepairMaterials(), true);
}
}

View File

@ -118,10 +118,6 @@ abstract class Durable extends Item{
return $this->damage >= $this->getMaxDurability() || $this->isNull();
}
public function isValidRepairMaterial(Item $material) : bool {
return false;
}
protected function deserializeCompoundTag(CompoundTag $tag) : void{
parent::deserializeCompoundTag($tag);
$this->unbreakable = $tag->getByte("Unbreakable", 0) !== 0;

View File

@ -23,8 +23,6 @@ declare(strict_types=1);
namespace pocketmine\item;
use function in_array;
abstract class TieredTool extends Tool{
protected ToolTier $tier;
@ -63,8 +61,4 @@ abstract class TieredTool extends Tool{
public function isFireProof() : bool{
return $this->tier === ToolTier::NETHERITE;
}
public function isValidRepairMaterial(Item $material) : bool{
return in_array($material->getTypeId(), $this->tier->getRepairMaterials(), true);
}
}

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\block\BlockTypeIds;
use pocketmine\utils\LegacyEnumShimTrait;
/**
@ -51,11 +50,10 @@ enum ToolTier{
/**
* This function exists only to permit the use of named arguments and to make the code easier to read in PhpStorm.
* @param int[] $repairMaterials The typeId of the items that can be used to repair this tool in the anvil.
* @phpstan-return TMetadata
*/
private static function meta(int $harvestLevel, int $maxDurability, int $baseAttackPoints, int $baseEfficiency, int $enchantability, array $repairMaterials = []) : array{
return [$harvestLevel, $maxDurability, $baseAttackPoints, $baseEfficiency, $enchantability, $repairMaterials];
private static function meta(int $harvestLevel, int $maxDurability, int $baseAttackPoints, int $baseEfficiency, int $enchantability) : array{
return [$harvestLevel, $maxDurability, $baseAttackPoints, $baseEfficiency, $enchantability];
}
/**
@ -63,26 +61,12 @@ enum ToolTier{
*/
private function getMetadata() : array{
return match($this){
self::WOOD => self::meta(1, 60, 5, 2, 15, [
ItemTypeIds::fromBlockTypeId(BlockTypeIds::OAK_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::SPRUCE_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::BIRCH_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::JUNGLE_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::ACACIA_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::DARK_OAK_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::CRIMSON_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::WARPED_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::CHERRY_PLANKS),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::MANGROVE_PLANKS)
]),
self::GOLD => self::meta(2, 33, 5, 12, 22, [ItemTypeIds::GOLD_INGOT]),
self::STONE => self::meta(3, 132, 6, 4, 5, [
ItemTypeIds::fromBlockTypeId(BlockTypeIds::COBBLESTONE),
ItemTypeIds::fromBlockTypeId(BlockTypeIds::COBBLED_DEEPSLATE)
]),
self::IRON => self::meta(4, 251, 7, 6, 14, [ItemTypeIds::IRON_INGOT]),
self::DIAMOND => self::meta(5, 1562, 8, 8, 10, [ItemTypeIds::DIAMOND]),
self::NETHERITE => self::meta(6, 2032, 9, 9, 15, [ItemTypeIds::NETHERITE_INGOT])
self::WOOD => self::meta(1, 60, 5, 2, 15),
self::GOLD => self::meta(2, 33, 5, 12, 22),
self::STONE => self::meta(3, 132, 6, 4, 5),
self::IRON => self::meta(4, 251, 7, 6, 14),
self::DIAMOND => self::meta(5, 1562, 8, 8, 10),
self::NETHERITE => self::meta(6, 2032, 9, 9, 15)
};
}
@ -111,13 +95,4 @@ enum ToolTier{
public function getEnchantability() : int{
return $this->getMetadata()[4];
}
/**
* Returns the typeId of items that can be used to repair this tool in the anvil.
*
* @return int[]
*/
public function getRepairMaterials() : array{
return $this->getMetadata()[5];
}
}

View File

@ -38,8 +38,4 @@ class TurtleHelmet extends Armor{
return false;
}
public function isValidRepairMaterial(Item $material) : bool{
return $material->getTypeId() === ItemTypeIds::SCUTE;
}
}