mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-10-20 07:39:42 +00:00
Implement enchanting using enchanting tables (#5953)
Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
This commit is contained in:
211
src/item/enchantment/AvailableEnchantmentRegistry.php
Normal file
211
src/item/enchantment/AvailableEnchantmentRegistry.php
Normal file
@@ -0,0 +1,211 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTagRegistry as TagRegistry;
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTags as Tags;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments as Enchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use function array_filter;
|
||||
use function array_values;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Registry of enchantments that can be applied to items during in-game enchanting (enchanting table, anvil, fishing, etc.).
|
||||
*/
|
||||
final class AvailableEnchantmentRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
/** @var Enchantment[] */
|
||||
private array $enchantments = [];
|
||||
|
||||
/** @var string[][] */
|
||||
private array $primaryItemTags = [];
|
||||
|
||||
/** @var string[][] */
|
||||
private array $secondaryItemTags = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(Enchantments::PROTECTION(), [Tags::ARMOR], []);
|
||||
$this->register(Enchantments::FIRE_PROTECTION(), [Tags::ARMOR], []);
|
||||
$this->register(Enchantments::FEATHER_FALLING(), [Tags::BOOTS], []);
|
||||
$this->register(Enchantments::BLAST_PROTECTION(), [Tags::ARMOR], []);
|
||||
$this->register(Enchantments::PROJECTILE_PROTECTION(), [Tags::ARMOR], []);
|
||||
$this->register(Enchantments::THORNS(), [Tags::CHESTPLATE], [Tags::HELMET, Tags::LEGGINGS, Tags::BOOTS]);
|
||||
$this->register(Enchantments::RESPIRATION(), [Tags::HELMET], []);
|
||||
$this->register(Enchantments::SHARPNESS(), [Tags::SWORD, Tags::AXE], []);
|
||||
$this->register(Enchantments::KNOCKBACK(), [Tags::SWORD], []);
|
||||
$this->register(Enchantments::FIRE_ASPECT(), [Tags::SWORD], []);
|
||||
$this->register(Enchantments::EFFICIENCY(), [Tags::DIG_TOOLS], [Tags::SHEARS]);
|
||||
$this->register(Enchantments::FORTUNE(), [Tags::DIG_TOOLS], []);
|
||||
$this->register(Enchantments::SILK_TOUCH(), [Tags::DIG_TOOLS], [Tags::SHEARS]);
|
||||
$this->register(
|
||||
Enchantments::UNBREAKING(),
|
||||
[Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD],
|
||||
[Tags::SHEARS, Tags::FLINT_AND_STEEL, Tags::SHIELD, Tags::CARROT_ON_STICK, Tags::ELYTRA, Tags::BRUSH]
|
||||
);
|
||||
$this->register(Enchantments::POWER(), [Tags::BOW], []);
|
||||
$this->register(Enchantments::PUNCH(), [Tags::BOW], []);
|
||||
$this->register(Enchantments::FLAME(), [Tags::BOW], []);
|
||||
$this->register(Enchantments::INFINITY(), [Tags::BOW], []);
|
||||
$this->register(
|
||||
Enchantments::MENDING(),
|
||||
[],
|
||||
[Tags::ARMOR, Tags::WEAPONS, Tags::FISHING_ROD,
|
||||
Tags::SHEARS, Tags::FLINT_AND_STEEL, Tags::SHIELD, Tags::CARROT_ON_STICK, Tags::ELYTRA, Tags::BRUSH]
|
||||
);
|
||||
$this->register(Enchantments::VANISHING(), [], [Tags::ALL]);
|
||||
$this->register(Enchantments::SWIFT_SNEAK(), [], [Tags::LEGGINGS]);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $primaryItemTags
|
||||
* @param string[] $secondaryItemTags
|
||||
*/
|
||||
public function register(Enchantment $enchantment, array $primaryItemTags, array $secondaryItemTags) : void{
|
||||
$this->enchantments[spl_object_id($enchantment)] = $enchantment;
|
||||
$this->setPrimaryItemTags($enchantment, $primaryItemTags);
|
||||
$this->setSecondaryItemTags($enchantment, $secondaryItemTags);
|
||||
}
|
||||
|
||||
public function unregister(Enchantment $enchantment) : void{
|
||||
unset($this->enchantments[spl_object_id($enchantment)]);
|
||||
unset($this->primaryItemTags[spl_object_id($enchantment)]);
|
||||
unset($this->secondaryItemTags[spl_object_id($enchantment)]);
|
||||
}
|
||||
|
||||
public function unregisterAll() : void{
|
||||
$this->enchantments = [];
|
||||
$this->primaryItemTags = [];
|
||||
$this->secondaryItemTags = [];
|
||||
}
|
||||
|
||||
public function isRegistered(Enchantment $enchantment) : bool{
|
||||
return isset($this->enchantments[spl_object_id($enchantment)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns primary compatibility tags for the specified enchantment.
|
||||
*
|
||||
* An item matching at least one of these tags (or its descendents) can be:
|
||||
* - Offered this enchantment in an enchanting table
|
||||
* - Enchanted by any means allowed by secondary tags
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getPrimaryItemTags(Enchantment $enchantment) : array{
|
||||
return $this->primaryItemTags[spl_object_id($enchantment)] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $tags
|
||||
*/
|
||||
public function setPrimaryItemTags(Enchantment $enchantment, array $tags) : void{
|
||||
if(!$this->isRegistered($enchantment)){
|
||||
throw new \LogicException("Cannot set primary item tags for non-registered enchantment");
|
||||
}
|
||||
$this->primaryItemTags[spl_object_id($enchantment)] = array_values($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns secondary compatibility tags for the specified enchantment.
|
||||
*
|
||||
* An item matching at least one of these tags (or its descendents) can be:
|
||||
* - Combined with an enchanted book with this enchantment in an anvil
|
||||
* - Obtained as loot with this enchantment, e.g. fishing, treasure chests, mob equipment, etc.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getSecondaryItemTags(Enchantment $enchantment) : array{
|
||||
return $this->secondaryItemTags[spl_object_id($enchantment)] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $tags
|
||||
*/
|
||||
public function setSecondaryItemTags(Enchantment $enchantment, array $tags) : void{
|
||||
if(!$this->isRegistered($enchantment)){
|
||||
throw new \LogicException("Cannot set secondary item tags for non-registered enchantment");
|
||||
}
|
||||
$this->secondaryItemTags[spl_object_id($enchantment)] = array_values($tags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns enchantments that can be applied to the specified item in an enchanting table (primary only).
|
||||
*
|
||||
* @return Enchantment[]
|
||||
*/
|
||||
public function getPrimaryEnchantmentsForItem(Item $item) : array{
|
||||
$itemTags = $item->getEnchantmentTags();
|
||||
if(count($itemTags) === 0 || $item->hasEnchantments()){
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$this->enchantments,
|
||||
fn(Enchantment $e) => TagRegistry::getInstance()->isTagArrayIntersection($this->getPrimaryItemTags($e), $itemTags)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all available enchantments compatible with the item.
|
||||
*
|
||||
* Warning: not suitable for obtaining enchantments for an enchanting table
|
||||
* (use {@link AvailableEnchantmentRegistry::getPrimaryEnchantmentsForItem()} for that).
|
||||
*
|
||||
* @return Enchantment[]
|
||||
*/
|
||||
public function getAllEnchantmentsForItem(Item $item) : array{
|
||||
if(count($item->getEnchantmentTags()) === 0){
|
||||
return [];
|
||||
}
|
||||
|
||||
return array_filter(
|
||||
$this->enchantments,
|
||||
fn(Enchantment $enchantment) => $this->isAvailableForItem($enchantment, $item)
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified enchantment can be applied to the particular item.
|
||||
*
|
||||
* Warning: not suitable for checking the availability of enchantment for an enchanting table.
|
||||
*/
|
||||
public function isAvailableForItem(Enchantment $enchantment, Item $item) : bool{
|
||||
$itemTags = $item->getEnchantmentTags();
|
||||
$tagRegistry = TagRegistry::getInstance();
|
||||
|
||||
return $tagRegistry->isTagArrayIntersection($this->getPrimaryItemTags($enchantment), $itemTags) ||
|
||||
$tagRegistry->isTagArrayIntersection($this->getSecondaryItemTags($enchantment), $itemTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Enchantment[]
|
||||
*/
|
||||
public function getAll() : array{
|
||||
return $this->enchantments;
|
||||
}
|
||||
}
|
66
src/item/enchantment/EnchantOption.php
Normal file
66
src/item/enchantment/EnchantOption.php
Normal file
@@ -0,0 +1,66 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
/**
|
||||
* Represents an option on the enchanting table menu.
|
||||
* If selected, all the enchantments in the option will be applied to the item.
|
||||
*/
|
||||
class EnchantOption{
|
||||
|
||||
/**
|
||||
* @param EnchantmentInstance[] $enchantments
|
||||
*/
|
||||
public function __construct(
|
||||
private int $requiredXpLevel,
|
||||
private string $displayName,
|
||||
private array $enchantments
|
||||
){}
|
||||
|
||||
/**
|
||||
* Returns the minimum amount of XP levels required to select this enchantment option.
|
||||
* It's NOT the number of XP levels that will be subtracted after enchanting.
|
||||
*/
|
||||
public function getRequiredXpLevel() : int{
|
||||
return $this->requiredXpLevel;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the name that will be translated to the 'Standard Galactic Alphabet' client-side.
|
||||
* This can be any arbitrary text string, since the vanilla client cannot read the text anyway.
|
||||
* Example: 'bless creature range free'.
|
||||
*/
|
||||
public function getDisplayName() : string{
|
||||
return $this->displayName;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the enchantments that will be applied to the item when this option is clicked.
|
||||
*
|
||||
* @return EnchantmentInstance[]
|
||||
*/
|
||||
public function getEnchantments() : array{
|
||||
return $this->enchantments;
|
||||
}
|
||||
}
|
@@ -23,9 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
use DaveRandom\CallbackValidator\CallbackType;
|
||||
use DaveRandom\CallbackValidator\ParameterType;
|
||||
use DaveRandom\CallbackValidator\ReturnType;
|
||||
use pocketmine\lang\Translatable;
|
||||
use pocketmine\utils\NotCloneable;
|
||||
use pocketmine\utils\NotSerializable;
|
||||
use pocketmine\utils\Utils;
|
||||
|
||||
/**
|
||||
* Manages enchantment type data.
|
||||
@@ -34,13 +38,32 @@ class Enchantment{
|
||||
use NotCloneable;
|
||||
use NotSerializable;
|
||||
|
||||
/** @var \Closure(int $level) : int $minEnchantingPower */
|
||||
private \Closure $minEnchantingPower;
|
||||
|
||||
/**
|
||||
* @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower
|
||||
*
|
||||
* @param int $primaryItemFlags @deprecated
|
||||
* @param int $secondaryItemFlags @deprecated
|
||||
* @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange)
|
||||
*/
|
||||
public function __construct(
|
||||
private Translatable|string $name,
|
||||
private int $rarity,
|
||||
private int $primaryItemFlags,
|
||||
private int $secondaryItemFlags,
|
||||
private int $maxLevel
|
||||
){}
|
||||
private int $maxLevel,
|
||||
?\Closure $minEnchantingPower = null,
|
||||
private int $enchantingPowerRange = 50
|
||||
){
|
||||
$this->minEnchantingPower = $minEnchantingPower ?? fn(int $level) : int => 1;
|
||||
|
||||
Utils::validateCallableSignature(new CallbackType(
|
||||
new ReturnType("int"),
|
||||
new ParameterType("level", "int")
|
||||
), $this->minEnchantingPower);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a translation key for this enchantment's name.
|
||||
@@ -58,6 +81,8 @@ class Enchantment{
|
||||
|
||||
/**
|
||||
* Returns a bitset indicating what item types can have this item applied from an enchanting table.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function getPrimaryItemFlags() : int{
|
||||
return $this->primaryItemFlags;
|
||||
@@ -66,6 +91,8 @@ class Enchantment{
|
||||
/**
|
||||
* Returns a bitset indicating what item types cannot have this item applied from an enchanting table, but can from
|
||||
* an anvil.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function getSecondaryItemFlags() : int{
|
||||
return $this->secondaryItemFlags;
|
||||
@@ -73,6 +100,8 @@ class Enchantment{
|
||||
|
||||
/**
|
||||
* Returns whether this enchantment can apply to the item type from an enchanting table.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function hasPrimaryItemType(int $flag) : bool{
|
||||
return ($this->primaryItemFlags & $flag) !== 0;
|
||||
@@ -80,6 +109,8 @@ class Enchantment{
|
||||
|
||||
/**
|
||||
* Returns whether this enchantment can apply to the item type from an anvil, if it is not a primary item.
|
||||
*
|
||||
* @deprecated
|
||||
*/
|
||||
public function hasSecondaryItemType(int $flag) : bool{
|
||||
return ($this->secondaryItemFlags & $flag) !== 0;
|
||||
@@ -92,5 +123,34 @@ class Enchantment{
|
||||
return $this->maxLevel;
|
||||
}
|
||||
|
||||
//TODO: methods for min/max XP cost bounds based on enchantment level (not needed yet - enchanting is client-side)
|
||||
/**
|
||||
* Returns whether this enchantment can be applied to the item along with the given enchantment.
|
||||
*/
|
||||
public function isCompatibleWith(Enchantment $other) : bool{
|
||||
return IncompatibleEnchantmentRegistry::getInstance()->areCompatible($this, $other);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the minimum enchanting power value required for the particular level of the enchantment
|
||||
* to be available in an enchanting table.
|
||||
*
|
||||
* Enchanting power is a random value based on the number of bookshelves around an enchanting table
|
||||
* and the enchantability of the item being enchanted. It is only used when determining the available
|
||||
* enchantments for the enchantment options.
|
||||
*/
|
||||
public function getMinEnchantingPower(int $level) : int{
|
||||
return ($this->minEnchantingPower)($level);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the maximum enchanting power value allowed for the particular level of the enchantment
|
||||
* to be available in an enchanting table.
|
||||
*
|
||||
* Enchanting power is a random value based on the number of bookshelves around an enchanting table
|
||||
* and the enchantability of the item being enchanted. It is only used when determining the available
|
||||
* enchantments for the enchantment options.
|
||||
*/
|
||||
public function getMaxEnchantingPower(int $level) : int{
|
||||
return $this->getMinEnchantingPower($level) + $this->enchantingPowerRange;
|
||||
}
|
||||
}
|
||||
|
217
src/item/enchantment/EnchantmentHelper.php
Normal file
217
src/item/enchantment/EnchantmentHelper.php
Normal file
@@ -0,0 +1,217 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
use pocketmine\block\BlockTypeIds;
|
||||
use pocketmine\item\enchantment\AvailableEnchantmentRegistry as EnchantmentRegistry;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemTypeIds;
|
||||
use pocketmine\item\VanillaItems as Items;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\world\Position;
|
||||
use function abs;
|
||||
use function array_filter;
|
||||
use function chr;
|
||||
use function count;
|
||||
use function floor;
|
||||
use function max;
|
||||
use function min;
|
||||
use function ord;
|
||||
use function round;
|
||||
|
||||
final class EnchantmentHelper{
|
||||
private const MAX_BOOKSHELF_COUNT = 15;
|
||||
|
||||
/**
|
||||
* @param EnchantmentInstance[] $enchantments
|
||||
*/
|
||||
public static function enchantItem(Item $item, array $enchantments) : Item{
|
||||
$resultItem = $item->getTypeId() === ItemTypeIds::BOOK ? Items::ENCHANTED_BOOK() : clone $item;
|
||||
|
||||
foreach($enchantments as $enchantment){
|
||||
$resultItem->addEnchantment($enchantment);
|
||||
}
|
||||
|
||||
return $resultItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EnchantOption[]
|
||||
*/
|
||||
public static function getEnchantOptions(Position $tablePos, Item $input, int $seed) : array{
|
||||
if($input->isNull() || $input->hasEnchantments()){
|
||||
return [];
|
||||
}
|
||||
|
||||
$random = new Random($seed);
|
||||
|
||||
$bookshelfCount = self::countBookshelves($tablePos);
|
||||
$baseRequiredLevel = $random->nextRange(1, 8) + ($bookshelfCount >> 1) + $random->nextRange(0, $bookshelfCount);
|
||||
$topRequiredLevel = (int) floor(max($baseRequiredLevel / 3, 1));
|
||||
$middleRequiredLevel = (int) floor($baseRequiredLevel * 2 / 3 + 1);
|
||||
$bottomRequiredLevel = max($baseRequiredLevel, $bookshelfCount * 2);
|
||||
|
||||
return [
|
||||
self::createEnchantOption($random, $input, $topRequiredLevel),
|
||||
self::createEnchantOption($random, $input, $middleRequiredLevel),
|
||||
self::createEnchantOption($random, $input, $bottomRequiredLevel),
|
||||
];
|
||||
}
|
||||
|
||||
private static function countBookshelves(Position $tablePos) : int{
|
||||
$bookshelfCount = 0;
|
||||
$world = $tablePos->getWorld();
|
||||
|
||||
for($x = -2; $x <= 2; $x++){
|
||||
for($z = -2; $z <= 2; $z++){
|
||||
// We only check blocks at a distance of 2 blocks from the enchanting table
|
||||
if(abs($x) !== 2 && abs($z) !== 2){
|
||||
continue;
|
||||
}
|
||||
|
||||
// Ensure the space between the bookshelf stack at this X/Z and the enchanting table is empty
|
||||
for($y = 0; $y <= 1; $y++){
|
||||
// Calculate the coordinates of the space between the bookshelf and the enchanting table
|
||||
$spaceX = max(min($x, 1), -1);
|
||||
$spaceZ = max(min($z, 1), -1);
|
||||
$spaceBlock = $world->getBlock($tablePos->add($spaceX, $y, $spaceZ));
|
||||
if($spaceBlock->getTypeId() !== BlockTypeIds::AIR){
|
||||
continue 2;
|
||||
}
|
||||
}
|
||||
|
||||
// Finally, check the number of bookshelves at the current position
|
||||
for($y = 0; $y <= 1; $y++){
|
||||
$block = $world->getBlock($tablePos->add($x, $y, $z));
|
||||
if($block->getTypeId() === BlockTypeIds::BOOKSHELF){
|
||||
$bookshelfCount++;
|
||||
if($bookshelfCount === self::MAX_BOOKSHELF_COUNT){
|
||||
return $bookshelfCount;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $bookshelfCount;
|
||||
}
|
||||
|
||||
private static function createEnchantOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantOption{
|
||||
$enchantingPower = $requiredXpLevel;
|
||||
|
||||
$enchantability = $inputItem->getEnchantability();
|
||||
$enchantingPower = $enchantingPower + $random->nextRange(0, $enchantability >> 2) + $random->nextRange(0, $enchantability >> 2) + 1;
|
||||
// Random bonus for enchanting power between 0.85 and 1.15
|
||||
$bonus = 1 + ($random->nextFloat() + $random->nextFloat() - 1) * 0.15;
|
||||
$enchantingPower = (int) round($enchantingPower * $bonus);
|
||||
|
||||
$resultEnchantments = [];
|
||||
$availableEnchantments = self::getAvailableEnchantments($enchantingPower, $inputItem);
|
||||
|
||||
$lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments);
|
||||
if($lastEnchantment !== null){
|
||||
$resultEnchantments[] = $lastEnchantment;
|
||||
|
||||
// With probability (power + 1) / 50, continue adding enchantments
|
||||
while($random->nextFloat() <= ($enchantingPower + 1) / 50){
|
||||
// Remove from the list of available enchantments anything that conflicts
|
||||
// with previously-chosen enchantments
|
||||
$availableEnchantments = array_filter(
|
||||
$availableEnchantments,
|
||||
function(EnchantmentInstance $e) use ($lastEnchantment){
|
||||
return $e->getType() !== $lastEnchantment->getType() &&
|
||||
$e->getType()->isCompatibleWith($lastEnchantment->getType());
|
||||
}
|
||||
);
|
||||
|
||||
$lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments);
|
||||
if($lastEnchantment === null){
|
||||
break;
|
||||
}
|
||||
|
||||
$resultEnchantments[] = $lastEnchantment;
|
||||
$enchantingPower >>= 1;
|
||||
}
|
||||
}
|
||||
|
||||
return new EnchantOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return EnchantmentInstance[]
|
||||
*/
|
||||
private static function getAvailableEnchantments(int $enchantingPower, Item $item) : array{
|
||||
$list = [];
|
||||
|
||||
foreach(EnchantmentRegistry::getInstance()->getPrimaryEnchantmentsForItem($item) as $enchantment){
|
||||
for($lvl = $enchantment->getMaxLevel(); $lvl > 0; $lvl--){
|
||||
if($enchantingPower >= $enchantment->getMinEnchantingPower($lvl) &&
|
||||
$enchantingPower <= $enchantment->getMaxEnchantingPower($lvl)
|
||||
){
|
||||
$list[] = new EnchantmentInstance($enchantment, $lvl);
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $list;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param EnchantmentInstance[] $enchantments
|
||||
*/
|
||||
private static function getRandomWeightedEnchantment(Random $random, array $enchantments) : ?EnchantmentInstance{
|
||||
if(count($enchantments) === 0){
|
||||
return null;
|
||||
}
|
||||
|
||||
$totalWeight = 0;
|
||||
foreach($enchantments as $enchantment){
|
||||
$totalWeight += $enchantment->getType()->getRarity();
|
||||
}
|
||||
|
||||
$result = null;
|
||||
$randomWeight = $random->nextRange(1, $totalWeight);
|
||||
|
||||
foreach($enchantments as $enchantment){
|
||||
$randomWeight -= $enchantment->getType()->getRarity();
|
||||
|
||||
if($randomWeight <= 0){
|
||||
$result = $enchantment;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private static function getRandomOptionName(Random $random) : string{
|
||||
$name = "";
|
||||
for($i = $random->nextRange(5, 15); $i > 0; $i--){
|
||||
$name .= chr($random->nextRange(ord("a"), ord("z")));
|
||||
}
|
||||
|
||||
return $name;
|
||||
}
|
||||
}
|
34
src/item/enchantment/IncompatibleEnchantmentGroups.php
Normal file
34
src/item/enchantment/IncompatibleEnchantmentGroups.php
Normal file
@@ -0,0 +1,34 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
/**
|
||||
* Constants for groupings of incompatible enchantments.
|
||||
* Enchantments belonging to the same incompatibility group cannot be applied side-by-side on the same item.
|
||||
*/
|
||||
final class IncompatibleEnchantmentGroups{
|
||||
public const PROTECTION = "protection";
|
||||
public const BOW_INFINITE = "bow_infinite";
|
||||
public const DIG_DROP = "dig_drop";
|
||||
}
|
94
src/item/enchantment/IncompatibleEnchantmentRegistry.php
Normal file
94
src/item/enchantment/IncompatibleEnchantmentRegistry.php
Normal file
@@ -0,0 +1,94 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
use pocketmine\item\enchantment\IncompatibleEnchantmentGroups as Groups;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments as Enchantments;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use function array_intersect_key;
|
||||
use function count;
|
||||
use function spl_object_id;
|
||||
|
||||
/**
|
||||
* Manages which enchantments are incompatible with each other.
|
||||
* Enchantments can be added to groups to make them incompatible with all other enchantments already in that group.
|
||||
*/
|
||||
final class IncompatibleEnchantmentRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @phpstan-var array<int, array<string, true>>
|
||||
* @var true[][]
|
||||
*/
|
||||
private array $incompatibilityMap = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(Groups::PROTECTION, [Enchantments::PROTECTION(), Enchantments::FIRE_PROTECTION(), Enchantments::BLAST_PROTECTION(), Enchantments::PROJECTILE_PROTECTION()]);
|
||||
$this->register(Groups::BOW_INFINITE, [Enchantments::INFINITY(), Enchantments::MENDING()]);
|
||||
$this->register(Groups::DIG_DROP, [Enchantments::FORTUNE(), Enchantments::SILK_TOUCH()]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register incompatibility for an enchantment group.
|
||||
*
|
||||
* All enchantments belonging to the same group are incompatible with each other,
|
||||
* i.e. they cannot be added together on the same item.
|
||||
*
|
||||
* @param Enchantment[] $enchantments
|
||||
*/
|
||||
public function register(string $tag, array $enchantments) : void{
|
||||
foreach($enchantments as $enchantment){
|
||||
$this->incompatibilityMap[spl_object_id($enchantment)][$tag] = true;
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister incompatibility for some enchantments of a particular group.
|
||||
*
|
||||
* @param Enchantment[] $enchantments
|
||||
*/
|
||||
public function unregister(string $tag, array $enchantments) : void{
|
||||
foreach($enchantments as $enchantment){
|
||||
unset($this->incompatibilityMap[spl_object_id($enchantment)][$tag]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Unregister incompatibility for all enchantments of a particular group.
|
||||
*/
|
||||
public function unregisterAll(string $tag) : void{
|
||||
foreach($this->incompatibilityMap as $id => $tags){
|
||||
unset($this->incompatibilityMap[$id][$tag]);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether two enchantments can be applied to the same item.
|
||||
*/
|
||||
public function areCompatible(Enchantment $first, Enchantment $second) : bool{
|
||||
$firstIncompatibilities = $this->incompatibilityMap[spl_object_id($first)] ?? [];
|
||||
$secondIncompatibilities = $this->incompatibilityMap[spl_object_id($second)] ?? [];
|
||||
return count(array_intersect_key($firstIncompatibilities, $secondIncompatibilities)) === 0;
|
||||
}
|
||||
}
|
190
src/item/enchantment/ItemEnchantmentTagRegistry.php
Normal file
190
src/item/enchantment/ItemEnchantmentTagRegistry.php
Normal file
@@ -0,0 +1,190 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
use pocketmine\item\enchantment\ItemEnchantmentTags as Tags;
|
||||
use pocketmine\utils\SingletonTrait;
|
||||
use pocketmine\utils\Utils;
|
||||
use function array_diff;
|
||||
use function array_intersect;
|
||||
use function array_merge;
|
||||
use function array_search;
|
||||
use function array_shift;
|
||||
use function array_unique;
|
||||
use function count;
|
||||
|
||||
/**
|
||||
* Manages known item enchantment tags and the relations between them.
|
||||
* Used to determine which tags belong to which other tags, and to check if lists of tags intersect.
|
||||
*/
|
||||
final class ItemEnchantmentTagRegistry{
|
||||
use SingletonTrait;
|
||||
|
||||
/**
|
||||
* @phpstan-var array<string, list<string>>
|
||||
* @var string[][]
|
||||
*/
|
||||
private array $tagMap = [];
|
||||
|
||||
private function __construct(){
|
||||
$this->register(Tags::ARMOR, [Tags::HELMET, Tags::CHESTPLATE, Tags::LEGGINGS, Tags::BOOTS]);
|
||||
$this->register(Tags::SHIELD);
|
||||
$this->register(Tags::SWORD);
|
||||
$this->register(Tags::TRIDENT);
|
||||
$this->register(Tags::BOW);
|
||||
$this->register(Tags::CROSSBOW);
|
||||
$this->register(Tags::SHEARS);
|
||||
$this->register(Tags::FLINT_AND_STEEL);
|
||||
$this->register(Tags::DIG_TOOLS, [Tags::AXE, Tags::PICKAXE, Tags::SHOVEL, Tags::HOE]);
|
||||
$this->register(Tags::FISHING_ROD);
|
||||
$this->register(Tags::CARROT_ON_STICK);
|
||||
$this->register(Tags::COMPASS);
|
||||
$this->register(Tags::MASK);
|
||||
$this->register(Tags::ELYTRA);
|
||||
$this->register(Tags::BRUSH);
|
||||
$this->register(Tags::WEAPONS, [
|
||||
Tags::SWORD,
|
||||
Tags::TRIDENT,
|
||||
Tags::BOW,
|
||||
Tags::CROSSBOW,
|
||||
Tags::DIG_TOOLS,
|
||||
]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Register tag and its nested tags.
|
||||
*
|
||||
* @param string[] $nestedTags
|
||||
*/
|
||||
public function register(string $tag, array $nestedTags = []) : void{
|
||||
$this->assertNotInternalTag($tag);
|
||||
|
||||
foreach($nestedTags as $nestedTag){
|
||||
if(!isset($this->tagMap[$nestedTag])){
|
||||
$this->register($nestedTag);
|
||||
}
|
||||
$this->tagMap[$tag][] = $nestedTag;
|
||||
}
|
||||
|
||||
if(!isset($this->tagMap[$tag])){
|
||||
$this->tagMap[$tag] = [];
|
||||
$this->tagMap[Tags::ALL][] = $tag;
|
||||
}
|
||||
}
|
||||
|
||||
public function unregister(string $tag) : void{
|
||||
if(!isset($this->tagMap[$tag])){
|
||||
return;
|
||||
}
|
||||
$this->assertNotInternalTag($tag);
|
||||
|
||||
unset($this->tagMap[$tag]);
|
||||
|
||||
foreach(Utils::stringifyKeys($this->tagMap) as $key => $nestedTags){
|
||||
if(($nestedKey = array_search($tag, $nestedTags, true)) !== false){
|
||||
unset($this->tagMap[$key][$nestedKey]);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Remove specified nested tags.
|
||||
*
|
||||
* @param string[] $nestedTags
|
||||
*/
|
||||
public function removeNested(string $tag, array $nestedTags) : void{
|
||||
$this->assertNotInternalTag($tag);
|
||||
$this->tagMap[$tag] = array_diff($this->tagMap[$tag], $nestedTags);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns nested tags of a particular tag.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
public function getNested(string $tag) : array{
|
||||
return $this->tagMap[$tag] ?? [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $firstTags
|
||||
* @param string[] $secondTags
|
||||
*/
|
||||
public function isTagArrayIntersection(array $firstTags, array $secondTags) : bool{
|
||||
if(count($firstTags) === 0 || count($secondTags) === 0){
|
||||
return false;
|
||||
}
|
||||
|
||||
$firstLeafTags = $this->getLeafTagsForArray($firstTags);
|
||||
$secondLeafTags = $this->getLeafTagsForArray($secondTags);
|
||||
|
||||
return count(array_intersect($firstLeafTags, $secondLeafTags)) !== 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags that are recursively nested within each tag in the array and do not have any nested tags.
|
||||
*
|
||||
* @param string[] $tags
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getLeafTagsForArray(array $tags) : array{
|
||||
$leafTagArrays = [];
|
||||
foreach($tags as $tag){
|
||||
$leafTagArrays[] = $this->getLeafTags($tag);
|
||||
}
|
||||
return array_unique(array_merge(...$leafTagArrays));
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns all tags that are recursively nested within the given tag and do not have any nested tags.
|
||||
*
|
||||
* @return string[]
|
||||
*/
|
||||
private function getLeafTags(string $tag) : array{
|
||||
$result = [];
|
||||
$tagsToHandle = [$tag];
|
||||
|
||||
while(count($tagsToHandle) !== 0){
|
||||
$currentTag = array_shift($tagsToHandle);
|
||||
$nestedTags = $this->getNested($currentTag);
|
||||
|
||||
if(count($nestedTags) === 0){
|
||||
$result[] = $currentTag;
|
||||
}else{
|
||||
$tagsToHandle = array_merge($tagsToHandle, $nestedTags);
|
||||
}
|
||||
}
|
||||
|
||||
return $result;
|
||||
}
|
||||
|
||||
private function assertNotInternalTag(string $tag) : void{
|
||||
if($tag === Tags::ALL){
|
||||
throw new \InvalidArgumentException(
|
||||
"Cannot perform any operations on the internal item enchantment tag '$tag'"
|
||||
);
|
||||
}
|
||||
}
|
||||
}
|
57
src/item/enchantment/ItemEnchantmentTags.php
Normal file
57
src/item/enchantment/ItemEnchantmentTags.php
Normal file
@@ -0,0 +1,57 @@
|
||||
<?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\item\enchantment;
|
||||
|
||||
/**
|
||||
* Tags used by items and enchantments to determine which enchantments can be applied to which items.
|
||||
* Some tags may contain other tags.
|
||||
* @see ItemEnchantmentTagRegistry
|
||||
*/
|
||||
final class ItemEnchantmentTags{
|
||||
public const ALL = "all";
|
||||
public const ARMOR = "armor";
|
||||
public const HELMET = "helmet";
|
||||
public const CHESTPLATE = "chestplate";
|
||||
public const LEGGINGS = "leggings";
|
||||
public const BOOTS = "boots";
|
||||
public const SHIELD = "shield";
|
||||
public const SWORD = "sword";
|
||||
public const TRIDENT = "trident";
|
||||
public const BOW = "bow";
|
||||
public const CROSSBOW = "crossbow";
|
||||
public const SHEARS = "shears";
|
||||
public const FLINT_AND_STEEL = "flint_and_steel";
|
||||
public const DIG_TOOLS = "dig_tools";
|
||||
public const AXE = "axe";
|
||||
public const PICKAXE = "pickaxe";
|
||||
public const SHOVEL = "shovel";
|
||||
public const HOE = "hoe";
|
||||
public const FISHING_ROD = "fishing_rod";
|
||||
public const CARROT_ON_STICK = "carrot_on_stick";
|
||||
public const COMPASS = "compass";
|
||||
public const MASK = "mask";
|
||||
public const ELYTRA = "elytra";
|
||||
public const BRUSH = "brush";
|
||||
public const WEAPONS = "weapons";
|
||||
}
|
@@ -23,14 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\item\enchantment;
|
||||
|
||||
/** @deprecated */
|
||||
final class ItemFlags{
|
||||
|
||||
private function __construct(){
|
||||
//NOOP
|
||||
}
|
||||
|
||||
//TODO: this should probably move to protocol
|
||||
|
||||
public const NONE = 0x0;
|
||||
public const ALL = 0xffff;
|
||||
public const ARMOR = self::HEAD | self::TORSO | self::LEGS | self::FEET;
|
||||
|
@@ -36,10 +36,15 @@ class ProtectionEnchantment extends Enchantment{
|
||||
/**
|
||||
* ProtectionEnchantment constructor.
|
||||
*
|
||||
* @phpstan-param null|(\Closure(int $level) : int) $minEnchantingPower
|
||||
*
|
||||
* @param int $primaryItemFlags @deprecated
|
||||
* @param int $secondaryItemFlags @deprecated
|
||||
* @param int[]|null $applicableDamageTypes EntityDamageEvent::CAUSE_* constants which this enchantment type applies to, or null if it applies to all types of damage.
|
||||
* @param int $enchantingPowerRange Value used to calculate the maximum enchanting power (minEnchantingPower + enchantingPowerRange)
|
||||
*/
|
||||
public function __construct(Translatable|string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel, float $typeModifier, ?array $applicableDamageTypes){
|
||||
parent::__construct($name, $rarity, $primaryItemFlags, $secondaryItemFlags, $maxLevel);
|
||||
public function __construct(Translatable|string $name, int $rarity, int $primaryItemFlags, int $secondaryItemFlags, int $maxLevel, float $typeModifier, ?array $applicableDamageTypes, ?\Closure $minEnchantingPower = null, int $enchantingPowerRange = 50){
|
||||
parent::__construct($name, $rarity, $primaryItemFlags, $secondaryItemFlags, $maxLevel, $minEnchantingPower, $enchantingPowerRange);
|
||||
|
||||
$this->typeModifier = $typeModifier;
|
||||
if($applicableDamageTypes !== null){
|
||||
|
@@ -59,47 +59,224 @@ final class VanillaEnchantments{
|
||||
use RegistryTrait;
|
||||
|
||||
protected static function setup() : void{
|
||||
self::register("PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_all(), Rarity::COMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 0.75, null));
|
||||
self::register("FIRE_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_fire(), Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.25, [
|
||||
EntityDamageEvent::CAUSE_FIRE,
|
||||
EntityDamageEvent::CAUSE_FIRE_TICK,
|
||||
EntityDamageEvent::CAUSE_LAVA
|
||||
//TODO: check fireballs
|
||||
]));
|
||||
self::register("FEATHER_FALLING", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_fall(), Rarity::UNCOMMON, ItemFlags::FEET, ItemFlags::NONE, 4, 2.5, [
|
||||
EntityDamageEvent::CAUSE_FALL
|
||||
]));
|
||||
self::register("BLAST_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_explosion(), Rarity::RARE, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.5, [
|
||||
EntityDamageEvent::CAUSE_BLOCK_EXPLOSION,
|
||||
EntityDamageEvent::CAUSE_ENTITY_EXPLOSION
|
||||
]));
|
||||
self::register("PROJECTILE_PROTECTION", new ProtectionEnchantment(KnownTranslationFactory::enchantment_protect_projectile(), Rarity::UNCOMMON, ItemFlags::ARMOR, ItemFlags::NONE, 4, 1.5, [
|
||||
EntityDamageEvent::CAUSE_PROJECTILE
|
||||
]));
|
||||
self::register("THORNS", new Enchantment(KnownTranslationFactory::enchantment_thorns(), Rarity::MYTHIC, ItemFlags::TORSO, ItemFlags::HEAD | ItemFlags::LEGS | ItemFlags::FEET, 3));
|
||||
self::register("RESPIRATION", new Enchantment(KnownTranslationFactory::enchantment_oxygen(), Rarity::RARE, ItemFlags::HEAD, ItemFlags::NONE, 3));
|
||||
self::register("PROTECTION", new ProtectionEnchantment(
|
||||
KnownTranslationFactory::enchantment_protect_all(),
|
||||
Rarity::COMMON,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
0.75,
|
||||
null,
|
||||
fn(int $level) : int => 11 * ($level - 1) + 1,
|
||||
20
|
||||
));
|
||||
self::register("FIRE_PROTECTION", new ProtectionEnchantment(
|
||||
KnownTranslationFactory::enchantment_protect_fire(),
|
||||
Rarity::UNCOMMON,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
1.25,
|
||||
[
|
||||
EntityDamageEvent::CAUSE_FIRE,
|
||||
EntityDamageEvent::CAUSE_FIRE_TICK,
|
||||
EntityDamageEvent::CAUSE_LAVA
|
||||
//TODO: check fireballs
|
||||
],
|
||||
fn(int $level) : int => 8 * ($level - 1) + 10,
|
||||
12
|
||||
));
|
||||
self::register("FEATHER_FALLING", new ProtectionEnchantment(
|
||||
KnownTranslationFactory::enchantment_protect_fall(),
|
||||
Rarity::UNCOMMON,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
2.5,
|
||||
[
|
||||
EntityDamageEvent::CAUSE_FALL
|
||||
],
|
||||
fn(int $level) : int => 6 * ($level - 1) + 5,
|
||||
10
|
||||
));
|
||||
self::register("BLAST_PROTECTION", new ProtectionEnchantment(
|
||||
KnownTranslationFactory::enchantment_protect_explosion(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
1.5,
|
||||
[
|
||||
EntityDamageEvent::CAUSE_BLOCK_EXPLOSION,
|
||||
EntityDamageEvent::CAUSE_ENTITY_EXPLOSION
|
||||
],
|
||||
fn(int $level) : int => 8 * ($level - 1) + 5,
|
||||
12
|
||||
));
|
||||
self::register("PROJECTILE_PROTECTION", new ProtectionEnchantment(
|
||||
KnownTranslationFactory::enchantment_protect_projectile(),
|
||||
Rarity::UNCOMMON,
|
||||
0,
|
||||
0,
|
||||
4,
|
||||
1.5,
|
||||
[
|
||||
EntityDamageEvent::CAUSE_PROJECTILE
|
||||
],
|
||||
fn(int $level) : int => 6 * ($level - 1) + 3,
|
||||
15
|
||||
));
|
||||
self::register("THORNS", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_thorns(),
|
||||
Rarity::MYTHIC,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
fn(int $level) : int => 20 * ($level - 1) + 10,
|
||||
50
|
||||
));
|
||||
self::register("RESPIRATION", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_oxygen(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
fn(int $level) : int => 10 * $level,
|
||||
30
|
||||
));
|
||||
|
||||
self::register("SHARPNESS", new SharpnessEnchantment(KnownTranslationFactory::enchantment_damage_all(), Rarity::COMMON, ItemFlags::SWORD, ItemFlags::AXE, 5));
|
||||
//TODO: smite, bane of arthropods (these don't make sense now because their applicable mobs don't exist yet)
|
||||
self::register("SHARPNESS", new SharpnessEnchantment(
|
||||
KnownTranslationFactory::enchantment_damage_all(),
|
||||
Rarity::COMMON,
|
||||
0,
|
||||
0,
|
||||
5,
|
||||
fn(int $level) : int => 11 * ($level - 1) + 1,
|
||||
20
|
||||
));
|
||||
self::register("KNOCKBACK", new KnockbackEnchantment(
|
||||
KnownTranslationFactory::enchantment_knockback(),
|
||||
Rarity::UNCOMMON,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
fn(int $level) : int => 20 * ($level - 1) + 5,
|
||||
50
|
||||
));
|
||||
self::register("FIRE_ASPECT", new FireAspectEnchantment(
|
||||
KnownTranslationFactory::enchantment_fire(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
fn(int $level) : int => 20 * ($level - 1) + 10,
|
||||
50
|
||||
));
|
||||
//TODO: smite, bane of arthropods, looting (these don't make sense now because their applicable mobs don't exist yet)
|
||||
|
||||
self::register("KNOCKBACK", new KnockbackEnchantment(KnownTranslationFactory::enchantment_knockback(), Rarity::UNCOMMON, ItemFlags::SWORD, ItemFlags::NONE, 2));
|
||||
self::register("FIRE_ASPECT", new FireAspectEnchantment(KnownTranslationFactory::enchantment_fire(), Rarity::RARE, ItemFlags::SWORD, ItemFlags::NONE, 2));
|
||||
self::register("EFFICIENCY", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_digging(),
|
||||
Rarity::COMMON,
|
||||
0,
|
||||
0,
|
||||
5,
|
||||
fn(int $level) : int => 10 * ($level - 1) + 1,
|
||||
50
|
||||
));
|
||||
self::register("FORTUNE", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_lootBonusDigger(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
fn(int $level) : int => 9 * ($level - 1) + 15,
|
||||
50
|
||||
));
|
||||
self::register("SILK_TOUCH", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_untouching(),
|
||||
Rarity::MYTHIC,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
fn(int $level) : int => 15,
|
||||
50
|
||||
));
|
||||
self::register("UNBREAKING", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_durability(),
|
||||
Rarity::UNCOMMON,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
fn(int $level) : int => 8 * ($level - 1) + 5,
|
||||
50
|
||||
));
|
||||
|
||||
self::register("EFFICIENCY", new Enchantment(KnownTranslationFactory::enchantment_digging(), Rarity::COMMON, ItemFlags::DIG, ItemFlags::SHEARS, 5));
|
||||
self::register("FORTUNE", new Enchantment(KnownTranslationFactory::enchantment_lootBonusDigger(), Rarity::RARE, ItemFlags::DIG, ItemFlags::NONE, 3));
|
||||
self::register("SILK_TOUCH", new Enchantment(KnownTranslationFactory::enchantment_untouching(), Rarity::MYTHIC, ItemFlags::DIG, ItemFlags::SHEARS, 1));
|
||||
self::register("UNBREAKING", new Enchantment(KnownTranslationFactory::enchantment_durability(), Rarity::UNCOMMON, ItemFlags::DIG | ItemFlags::ARMOR | ItemFlags::FISHING_ROD | ItemFlags::BOW, ItemFlags::TOOL | ItemFlags::CARROT_STICK | ItemFlags::ELYTRA, 3));
|
||||
self::register("POWER", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_arrowDamage(),
|
||||
Rarity::COMMON,
|
||||
0,
|
||||
0,
|
||||
5,
|
||||
fn(int $level) : int => 10 * ($level - 1) + 1,
|
||||
15
|
||||
));
|
||||
self::register("PUNCH", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_arrowKnockback(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
2,
|
||||
fn(int $level) : int => 20 * ($level - 1) + 12,
|
||||
25
|
||||
));
|
||||
self::register("FLAME", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_arrowFire(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
fn(int $level) : int => 20,
|
||||
30
|
||||
));
|
||||
self::register("INFINITY", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_arrowInfinite(),
|
||||
Rarity::MYTHIC,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
fn(int $level) : int => 20,
|
||||
30
|
||||
));
|
||||
|
||||
self::register("POWER", new Enchantment(KnownTranslationFactory::enchantment_arrowDamage(), Rarity::COMMON, ItemFlags::BOW, ItemFlags::NONE, 5));
|
||||
self::register("PUNCH", new Enchantment(KnownTranslationFactory::enchantment_arrowKnockback(), Rarity::RARE, ItemFlags::BOW, ItemFlags::NONE, 2));
|
||||
self::register("FLAME", new Enchantment(KnownTranslationFactory::enchantment_arrowFire(), Rarity::RARE, ItemFlags::BOW, ItemFlags::NONE, 1));
|
||||
self::register("INFINITY", new Enchantment(KnownTranslationFactory::enchantment_arrowInfinite(), Rarity::MYTHIC, ItemFlags::BOW, ItemFlags::NONE, 1));
|
||||
self::register("MENDING", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_mending(),
|
||||
Rarity::RARE,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
fn(int $level) : int => 25,
|
||||
50
|
||||
));
|
||||
|
||||
self::register("MENDING", new Enchantment(KnownTranslationFactory::enchantment_mending(), Rarity::RARE, ItemFlags::NONE, ItemFlags::ALL, 1));
|
||||
self::register("VANISHING", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_curse_vanishing(),
|
||||
Rarity::MYTHIC,
|
||||
0,
|
||||
0,
|
||||
1,
|
||||
fn(int $level) : int => 25,
|
||||
25
|
||||
));
|
||||
|
||||
self::register("VANISHING", new Enchantment(KnownTranslationFactory::enchantment_curse_vanishing(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::ALL, 1));
|
||||
|
||||
self::register("SWIFT_SNEAK", new Enchantment(KnownTranslationFactory::enchantment_swift_sneak(), Rarity::MYTHIC, ItemFlags::NONE, ItemFlags::LEGS, 3));
|
||||
self::register("SWIFT_SNEAK", new Enchantment(
|
||||
KnownTranslationFactory::enchantment_swift_sneak(),
|
||||
Rarity::MYTHIC,
|
||||
0,
|
||||
0,
|
||||
3,
|
||||
fn(int $level) : int => 10 * $level,
|
||||
5
|
||||
));
|
||||
}
|
||||
|
||||
protected static function register(string $name, Enchantment $member) : void{
|
||||
|
Reference in New Issue
Block a user