Skip to content

Commit

Permalink
first look at anvil actions
Browse files Browse the repository at this point in the history
  • Loading branch information
ShockedPlot7560 committed Aug 19, 2024
1 parent 1cc809c commit 7cfb6ee
Show file tree
Hide file tree
Showing 7 changed files with 384 additions and 138 deletions.
52 changes: 52 additions & 0 deletions src/block/anvil/AnvilAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,52 @@
<?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;

public function __construct(
protected Item $base,
protected Item $material,
protected ?string $customName
){ }

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;
}

abstract public function process(Item $resultItem) : void;

abstract public function canBeApplied() : bool;
}
71 changes: 71 additions & 0 deletions src/block/anvil/AnvilActionsFactory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,71 @@
<?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;
}
}
81 changes: 81 additions & 0 deletions src/block/anvil/CombineEnchantmentsAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,81 @@
<?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 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 += $costAddition * $levelDifference;

Check failure on line 77 in src/block/anvil/CombineEnchantmentsAction.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Property pocketmine\block\anvil\AnvilAction::$xpCost (int) does not accept float|int.

Check failure on line 77 in src/block/anvil/CombineEnchantmentsAction.php

View workflow job for this annotation

GitHub Actions / PHP 8.1 / PHPStan analysis

Property pocketmine\block\anvil\AnvilAction::$xpCost (int) does not accept float|int.

Check failure on line 77 in src/block/anvil/CombineEnchantmentsAction.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Property pocketmine\block\anvil\AnvilAction::$xpCost (int) does not accept float|int.

Check failure on line 77 in src/block/anvil/CombineEnchantmentsAction.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Property pocketmine\block\anvil\AnvilAction::$xpCost (int) does not accept float|int.

Check failure on line 77 in src/block/anvil/CombineEnchantmentsAction.php

View workflow job for this annotation

GitHub Actions / PHP 8.2 / PHPStan analysis

Property pocketmine\block\anvil\AnvilAction::$xpCost (int) does not accept float|int.

Check failure on line 77 in src/block/anvil/CombineEnchantmentsAction.php

View workflow job for this annotation

GitHub Actions / PHP 8.3 / PHPStan analysis

Property pocketmine\block\anvil\AnvilAction::$xpCost (int) does not accept float|int.
$resultItem->addEnchantment($instance);
}
}
}
49 changes: 49 additions & 0 deletions src/block/anvil/RenameItemAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,49 @@
<?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 function strlen;

final class RenameItemAction extends AnvilAction{
private const COST = 1;

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);
}
}
}
}
58 changes: 58 additions & 0 deletions src/block/anvil/RepairWithMaterialAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?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 = $numberRepair * self::COST;
}
}
58 changes: 58 additions & 0 deletions src/block/anvil/RepairWithSacrificeAction.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,58 @@
<?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;
}
}
}
Loading

0 comments on commit 7cfb6ee

Please sign in to comment.