Skip to content

Commit 7cfb6ee

Browse files
first look at anvil actions
1 parent 1cc809c commit 7cfb6ee

7 files changed

+384
-138
lines changed

src/block/anvil/AnvilAction.php

+52
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,52 @@
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\block\anvil;
25+
26+
use pocketmine\item\Item;
27+
28+
abstract class AnvilAction{
29+
protected int $xpCost = 0;
30+
31+
public function __construct(
32+
protected Item $base,
33+
protected Item $material,
34+
protected ?string $customName
35+
){ }
36+
37+
final public function getXpCost() : int{
38+
return $this->xpCost;
39+
}
40+
41+
/**
42+
* If only actions marked as free of repair cost is applied, the result item
43+
* will not have any repair cost increase.
44+
*/
45+
public function isFreeOfRepairCost() : bool {
46+
return false;
47+
}
48+
49+
abstract public function process(Item $resultItem) : void;
50+
51+
abstract public function canBeApplied() : bool;
52+
}
+71
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
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\block\anvil;
25+
26+
use pocketmine\item\Item;
27+
use pocketmine\utils\SingletonTrait;
28+
use function is_subclass_of;
29+
30+
final class AnvilActionsFactory{
31+
use SingletonTrait;
32+
33+
/** @var array<class-string<AnvilAction>, true> */
34+
private array $actions = [];
35+
36+
private function __construct(){
37+
$this->register(RenameItemAction::class);
38+
$this->register(CombineEnchantmentsAction::class);
39+
$this->register(RepairWithSacrificeAction::class);
40+
$this->register(RepairWithMaterialAction::class);
41+
}
42+
43+
/**
44+
* @param class-string<AnvilAction> $class
45+
*/
46+
public function register(string $class) : void{
47+
if(!is_subclass_of($class, AnvilAction::class, true)){
48+
throw new \InvalidArgumentException("Class $class is not an AnvilAction");
49+
}
50+
if(isset($this->actions[$class])){
51+
throw new \InvalidArgumentException("Class $class is already registered");
52+
}
53+
$this->actions[$class] = true;
54+
}
55+
56+
/**
57+
* Return all available actions for the given items.
58+
*
59+
* @return AnvilAction[]
60+
*/
61+
public function getActions(Item $base, Item $material, ?string $customName) : array{
62+
$actions = [];
63+
foreach($this->actions as $class => $_){
64+
$action = new $class($base, $material, $customName);
65+
if($action->canBeApplied()){
66+
$actions[] = $action;
67+
}
68+
}
69+
return $actions;
70+
}
71+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,81 @@
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\block\anvil;
25+
26+
use pocketmine\inventory\transaction\TransactionValidationException;
27+
use pocketmine\item\EnchantedBook;
28+
use pocketmine\item\enchantment\AvailableEnchantmentRegistry;
29+
use pocketmine\item\enchantment\EnchantmentInstance;
30+
use pocketmine\item\enchantment\Rarity;
31+
use pocketmine\item\Item;
32+
use function max;
33+
use function min;
34+
35+
final class CombineEnchantmentsAction extends AnvilAction{
36+
public function canBeApplied() : bool{
37+
return $this->material->hasEnchantments();
38+
}
39+
40+
public function process(Item $resultItem) : void{
41+
foreach($this->material->getEnchantments() as $instance){
42+
$enchantment = $instance->getType();
43+
$level = $instance->getLevel();
44+
if(!AvailableEnchantmentRegistry::getInstance()->isAvailableForItem($enchantment, $this->base)){
45+
continue;
46+
}
47+
if(($targetEnchantment = $this->base->getEnchantment($enchantment)) !== null){
48+
// Enchant already present on the target item
49+
$targetLevel = $targetEnchantment->getLevel();
50+
$newLevel = ($targetLevel === $level ? $targetLevel + 1 : max($targetLevel, $level));
51+
$level = min($newLevel, $enchantment->getMaxLevel());
52+
$instance = new EnchantmentInstance($enchantment, $level);
53+
}else{
54+
// Check if the enchantment is compatible with the existing enchantments
55+
foreach($this->base->getEnchantments() as $testedInstance){
56+
$testedEnchantment = $testedInstance->getType();
57+
if(!$testedEnchantment->isCompatibleWith($enchantment)){
58+
$this->xpCost++;
59+
continue 2;
60+
}
61+
}
62+
}
63+
64+
$costAddition = match($enchantment->getRarity()){
65+
Rarity::COMMON => 1,
66+
Rarity::UNCOMMON => 2,
67+
Rarity::RARE => 4,
68+
Rarity::MYTHIC => 8,
69+
default => throw new TransactionValidationException("Invalid rarity " . $enchantment->getRarity() . " found")
70+
};
71+
72+
if($this->material instanceof EnchantedBook){
73+
// Enchanted books are half as expensive to combine
74+
$costAddition = max(1, $costAddition / 2);
75+
}
76+
$levelDifference = $instance->getLevel() - $this->base->getEnchantmentLevel($instance->getType());
77+
$this->xpCost += $costAddition * $levelDifference;
78+
$resultItem->addEnchantment($instance);
79+
}
80+
}
81+
}

src/block/anvil/RenameItemAction.php

+49
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,49 @@
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\block\anvil;
25+
26+
use pocketmine\item\Item;
27+
use function strlen;
28+
29+
final class RenameItemAction extends AnvilAction{
30+
private const COST = 1;
31+
32+
public function canBeApplied() : bool{
33+
return true;
34+
}
35+
36+
public function process(Item $resultItem) : void{
37+
if($this->customName === null || strlen($this->customName) === 0){
38+
if($this->base->hasCustomName()){
39+
$this->xpCost += self::COST;
40+
$resultItem->clearCustomName();
41+
}
42+
}else{
43+
if($this->base->getCustomName() !== $this->customName){
44+
$this->xpCost += self::COST;
45+
$resultItem->setCustomName($this->customName);
46+
}
47+
}
48+
}
49+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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\block\anvil;
25+
26+
use pocketmine\item\Durable;
27+
use pocketmine\item\Item;
28+
use function assert;
29+
use function ceil;
30+
use function floor;
31+
use function max;
32+
use function min;
33+
34+
final class RepairWithMaterialAction extends AnvilAction{
35+
private const COST = 1;
36+
37+
public function canBeApplied() : bool{
38+
return $this->base instanceof Durable &&
39+
$this->base->isValidRepairMaterial($this->material) &&
40+
$this->base->getDamage() > 0;
41+
}
42+
43+
public function process(Item $resultItem) : void{
44+
assert($resultItem instanceof Durable, "Result item must be durable");
45+
assert($this->base instanceof Durable, "Base item must be durable");
46+
47+
$damage = $this->base->getDamage();
48+
$quarter = min($damage, (int) floor($this->base->getMaxDurability() / 4));
49+
$numberRepair = min($this->material->getCount(), (int) ceil($damage / $quarter));
50+
if($numberRepair > 0){
51+
$this->material->pop($numberRepair);
52+
$damage -= $quarter * $numberRepair;
53+
}
54+
$resultItem->setDamage(max(0, $damage));
55+
56+
$this->xpCost = $numberRepair * self::COST;
57+
}
58+
}
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,58 @@
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\block\anvil;
25+
26+
use pocketmine\item\Durable;
27+
use pocketmine\item\Item;
28+
use function assert;
29+
use function min;
30+
31+
final class RepairWithSacrificeAction extends AnvilAction{
32+
private const COST = 2;
33+
34+
public function canBeApplied() : bool{
35+
return $this->base instanceof Durable &&
36+
$this->material instanceof Durable &&
37+
$this->base->getTypeId() === $this->material->getTypeId();
38+
}
39+
40+
public function process(Item $resultItem) : void{
41+
assert($resultItem instanceof Durable, "Result item must be durable");
42+
assert($this->base instanceof Durable, "Base item must be durable");
43+
assert($this->material instanceof Durable, "Material item must be durable");
44+
45+
if($this->base->getDamage() !== 0){
46+
$baseMaxDurability = $this->base->getMaxDurability();
47+
$baseDurability = $baseMaxDurability - $this->base->getDamage();
48+
$materialDurability = $this->material->getMaxDurability() - $this->material->getDamage();
49+
$addDurability = (int) ($baseMaxDurability * 12 / 100);
50+
51+
$newDurability = min($baseMaxDurability, $baseDurability + $materialDurability + $addDurability);
52+
53+
$resultItem->setDamage($baseMaxDurability - $newDurability);
54+
55+
$this->xpCost = self::COST;
56+
}
57+
}
58+
}

0 commit comments

Comments
 (0)