Implement enchanting using enchanting tables (#5953)

Co-authored-by: Dylan K. Taylor <dktapps@pmmp.io>
This commit is contained in:
S3v3Nice
2023-08-15 19:28:26 +03:00
committed by GitHub
parent e48b5b2ec0
commit 39867b97c5
34 changed files with 1892 additions and 128 deletions

View File

@@ -44,8 +44,11 @@ class Armor extends Durable{
protected ?Color $customColor = null;
public function __construct(ItemIdentifier $identifier, string $name, ArmorTypeInfo $info){
parent::__construct($identifier, $name);
/**
* @param string[] $enchantmentTags
*/
public function __construct(ItemIdentifier $identifier, string $name, ArmorTypeInfo $info, array $enchantmentTags = []){
parent::__construct($identifier, $name, $enchantmentTags);
$this->armorInfo = $info;
}
@@ -72,6 +75,14 @@ class Armor extends Durable{
return $this->armorInfo->isFireProof();
}
public function getMaterial() : ArmorMaterial{
return $this->armorInfo->getMaterial();
}
public function getEnchantability() : int{
return $this->armorInfo->getMaterial()->getEnchantability();
}
/**
* Returns the dyed colour of this armour piece. This generally only applies to leather armour.
*/

View File

@@ -0,0 +1,42 @@
<?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;
class ArmorMaterial{
public function __construct(
private readonly int $enchantability
){
}
/**
* Returns the value that defines how enchantable the item is.
*
* The higher an item's enchantability is, the more likely it will be to gain high-level enchantments
* or multiple enchantments upon being enchanted in an enchanting table.
*/
public function getEnchantability() : int{
return $this->enchantability;
}
}

View File

@@ -24,13 +24,18 @@ declare(strict_types=1);
namespace pocketmine\item;
class ArmorTypeInfo{
private ArmorMaterial $material;
public function __construct(
private int $defensePoints,
private int $maxDurability,
private int $armorSlot,
private int $toughness = 0,
private bool $fireProof = false
){}
private bool $fireProof = false,
?ArmorMaterial $material = null
){
$this->material = $material ?? VanillaArmorMaterials::LEATHER();
}
public function getDefensePoints() : int{
return $this->defensePoints;
@@ -51,4 +56,8 @@ class ArmorTypeInfo{
public function isFireProof() : bool{
return $this->fireProof;
}
public function getMaterial() : ArmorMaterial{
return $this->material;
}
}

View File

@@ -0,0 +1,30 @@
<?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;
class EnchantedBook extends Item{
public function getMaxStackSize() : int{
return 1;
}
}

View File

@@ -107,10 +107,13 @@ class Item implements \JsonSerializable{
* NOTE: This should NOT BE USED for creating items to set into an inventory. Use VanillaItems for that
* purpose.
* @see VanillaItems
*
* @param string[] $enchantmentTags
*/
public function __construct(
private ItemIdentifier $identifier,
protected string $name = "Unknown"
protected string $name = "Unknown",
private array $enchantmentTags = []
){
$this->nbt = new CompoundTag();
}
@@ -455,6 +458,29 @@ class Item implements \JsonSerializable{
return $this->name;
}
/**
* Returns tags that represent the type of item being enchanted and are used to determine
* what enchantments can be applied to this item during in-game enchanting (enchanting table, anvil, fishing, etc.).
* @see ItemEnchantmentTags
* @see ItemEnchantmentTagRegistry
* @see AvailableEnchantmentRegistry
*
* @return string[]
*/
public function getEnchantmentTags() : array{
return $this->enchantmentTags;
}
/**
* Returns the value that defines how enchantable the item is.
*
* The higher an item's enchantability is, the more likely it will be to gain high-level enchantments
* or multiple enchantments upon being enchanted in an enchanting table.
*/
public function getEnchantability() : int{
return 1;
}
final public function canBePlaced() : bool{
return $this->getBlock()->canBePlaced();
}

View File

@@ -36,7 +36,7 @@ final class ItemBlock extends Item{
public function __construct(
private Block $block
){
parent::__construct(ItemIdentifier::fromBlock($block), $block->getName());
parent::__construct(ItemIdentifier::fromBlock($block), $block->getName(), $block->getEnchantmentTags());
}
protected function describeState(RuntimeDataDescriber $w) : void{

View File

@@ -303,8 +303,9 @@ final class ItemTypeIds{
public const MANGROVE_BOAT = 20264;
public const GLOW_BERRIES = 20265;
public const CHERRY_SIGN = 20266;
public const ENCHANTED_BOOK = 20267;
public const FIRST_UNUSED_ITEM_ID = 20267;
public const FIRST_UNUSED_ITEM_ID = 20268;
private static int $nextDynamicId = self::FIRST_UNUSED_ITEM_ID;

View File

@@ -1277,6 +1277,7 @@ final class StringToItemParser extends StringToTParser{
$result->register("egg", fn() => Items::EGG());
$result->register("elixir", fn() => Items::MEDICINE()->setType(MedicineType::ELIXIR()));
$result->register("emerald", fn() => Items::EMERALD());
$result->register("enchanted_book", fn() => Items::ENCHANTED_BOOK());
$result->register("enchanted_golden_apple", fn() => Items::ENCHANTED_GOLDEN_APPLE());
$result->register("enchanting_bottle", fn() => Items::EXPERIENCE_BOTTLE());
$result->register("ender_pearl", fn() => Items::ENDER_PEARL());

View File

@@ -26,8 +26,11 @@ namespace pocketmine\item;
abstract class TieredTool extends Tool{
protected ToolTier $tier;
public function __construct(ItemIdentifier $identifier, string $name, ToolTier $tier){
parent::__construct($identifier, $name);
/**
* @param string[] $enchantmentTags
*/
public function __construct(ItemIdentifier $identifier, string $name, ToolTier $tier, array $enchantmentTags = []){
parent::__construct($identifier, $name, $enchantmentTags);
$this->tier = $tier;
}
@@ -43,6 +46,10 @@ abstract class TieredTool extends Tool{
return $this->tier->getBaseEfficiency();
}
public function getEnchantability() : int{
return $this->tier->getEnchantability();
}
public function getFuelTime() : int{
if($this->tier->equals(ToolTier::WOOD())){
return 200;

View File

@@ -45,12 +45,12 @@ final class ToolTier{
protected static function setup() : void{
self::registerAll(
new self("wood", 1, 60, 5, 2),
new self("gold", 2, 33, 5, 12),
new self("stone", 3, 132, 6, 4),
new self("iron", 4, 251, 7, 6),
new self("diamond", 5, 1562, 8, 8),
new self("netherite", 6, 2032, 9, 9)
new self("wood", 1, 60, 5, 2, 15),
new self("gold", 2, 33, 5, 12, 22),
new self("stone", 3, 132, 6, 4, 5),
new self("iron", 4, 251, 7, 6, 14),
new self("diamond", 5, 1562, 8, 8, 10),
new self("netherite", 6, 2032, 9, 9, 15)
);
}
@@ -59,7 +59,8 @@ final class ToolTier{
private int $harvestLevel,
private int $maxDurability,
private int $baseAttackPoints,
private int $baseEfficiency
private int $baseEfficiency,
private int $enchantability
){
$this->Enum___construct($name);
}
@@ -79,4 +80,14 @@ final class ToolTier{
public function getBaseEfficiency() : int{
return $this->baseEfficiency;
}
/**
* Returns the value that defines how enchantable the item is.
*
* The higher an item's enchantability is, the more likely it will be to gain high-level enchantments
* or multiple enchantments upon being enchanted in an enchanting table.
*/
public function getEnchantability() : int{
return $this->enchantability;
}
}

View File

@@ -0,0 +1,73 @@
<?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;
use pocketmine\utils\RegistryTrait;
/**
* This doc-block is generated automatically, do not modify it manually.
* This must be regenerated whenever registry members are added, removed or changed.
* @see build/generate-registry-annotations.php
* @generate-registry-docblock
*
* @method static ArmorMaterial CHAINMAIL()
* @method static ArmorMaterial DIAMOND()
* @method static ArmorMaterial GOLD()
* @method static ArmorMaterial IRON()
* @method static ArmorMaterial LEATHER()
* @method static ArmorMaterial NETHERITE()
* @method static ArmorMaterial TURTLE()
*/
final class VanillaArmorMaterials{
use RegistryTrait;
private function __construct(){
// NOOP
}
protected static function register(string $name, ArmorMaterial $armorMaterial) : void{
self::_registryRegister($name, $armorMaterial);
}
/**
* @return ArmorMaterial[]
* @phpstan-return array<string, ArmorMaterial>
*/
public static function getAll() : array{
// phpstan doesn't support generic traits yet :(
/** @var ArmorMaterial[] $result */
$result = self::_registryGetAll();
return $result;
}
protected static function setup() : void{
self::register("leather", new ArmorMaterial(15));
self::register("chainmail", new ArmorMaterial(12));
self::register("iron", new ArmorMaterial(9));
self::register("turtle", new ArmorMaterial(9));
self::register("gold", new ArmorMaterial(25));
self::register("diamond", new ArmorMaterial(10));
self::register("netherite", new ArmorMaterial(15));
}
}

View File

@@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\block\utils\RecordType;
use pocketmine\block\VanillaBlocks;
use pocketmine\block\VanillaBlocks as Blocks;
use pocketmine\entity\Entity;
use pocketmine\entity\Location;
@@ -32,8 +31,10 @@ use pocketmine\entity\Squid;
use pocketmine\entity\Villager;
use pocketmine\entity\Zombie;
use pocketmine\inventory\ArmorInventory;
use pocketmine\item\enchantment\ItemEnchantmentTags as EnchantmentTags;
use pocketmine\item\ItemIdentifier as IID;
use pocketmine\item\ItemTypeIds as Ids;
use pocketmine\item\VanillaArmorMaterials as ArmorMaterials;
use pocketmine\math\Vector3;
use pocketmine\utils\AssumptionFailedError;
use pocketmine\utils\CloningRegistryTrait;
@@ -151,6 +152,7 @@ use pocketmine\world\World;
* @method static Item ECHO_SHARD()
* @method static Egg EGG()
* @method static Item EMERALD()
* @method static EnchantedBook ENCHANTED_BOOK()
* @method static GoldenAppleEnchanted ENCHANTED_GOLDEN_APPLE()
* @method static EnderPearl ENDER_PEARL()
* @method static ExperienceBottle EXPERIENCE_BOTTLE()
@@ -337,7 +339,7 @@ final class VanillaItems{
self::registerSpawnEggs();
self::registerTierToolItems();
self::register("air", VanillaBlocks::AIR()->asItem()->setCount(0));
self::register("air", Blocks::AIR()->asItem()->setCount(0));
self::register("acacia_sign", new ItemBlockWallOrFloor(new IID(Ids::ACACIA_SIGN), Blocks::ACACIA_SIGN(), Blocks::ACACIA_WALL_SIGN()));
self::register("amethyst_shard", new Item(new IID(Ids::AMETHYST_SHARD), "Amethyst Shard"));
@@ -355,8 +357,8 @@ final class VanillaItems{
self::register("bleach", new Item(new IID(Ids::BLEACH), "Bleach"));
self::register("bone", new Item(new IID(Ids::BONE), "Bone"));
self::register("bone_meal", new Fertilizer(new IID(Ids::BONE_MEAL), "Bone Meal"));
self::register("book", new Book(new IID(Ids::BOOK), "Book"));
self::register("bow", new Bow(new IID(Ids::BOW), "Bow"));
self::register("book", new Book(new IID(Ids::BOOK), "Book", [EnchantmentTags::ALL]));
self::register("bow", new Bow(new IID(Ids::BOW), "Bow", [EnchantmentTags::BOW]));
self::register("bowl", new Bowl(new IID(Ids::BOWL), "Bowl"));
self::register("bread", new Bread(new IID(Ids::BREAD), "Bread"));
self::register("brick", new Item(new IID(Ids::BRICK), "Brick"));
@@ -408,7 +410,7 @@ final class VanillaItems{
self::register("clownfish", new Clownfish(new IID(Ids::CLOWNFISH), "Clownfish"));
self::register("coal", new Coal(new IID(Ids::COAL), "Coal"));
self::register("cocoa_beans", new CocoaBeans(new IID(Ids::COCOA_BEANS), "Cocoa Beans"));
self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass"));
self::register("compass", new Compass(new IID(Ids::COMPASS), "Compass", [EnchantmentTags::COMPASS]));
self::register("cooked_chicken", new CookedChicken(new IID(Ids::COOKED_CHICKEN), "Cooked Chicken"));
self::register("cooked_fish", new CookedFish(new IID(Ids::COOKED_FISH), "Cooked Fish"));
self::register("cooked_mutton", new CookedMutton(new IID(Ids::COOKED_MUTTON), "Cooked Mutton"));
@@ -429,15 +431,16 @@ final class VanillaItems{
self::register("echo_shard", new Item(new IID(Ids::ECHO_SHARD), "Echo Shard"));
self::register("egg", new Egg(new IID(Ids::EGG), "Egg"));
self::register("emerald", new Item(new IID(Ids::EMERALD), "Emerald"));
self::register("enchanted_book", new EnchantedBook(new IID(Ids::ENCHANTED_BOOK), "Enchanted Book"));
self::register("enchanted_golden_apple", new GoldenAppleEnchanted(new IID(Ids::ENCHANTED_GOLDEN_APPLE), "Enchanted Golden Apple"));
self::register("ender_pearl", new EnderPearl(new IID(Ids::ENDER_PEARL), "Ender Pearl"));
self::register("experience_bottle", new ExperienceBottle(new IID(Ids::EXPERIENCE_BOTTLE), "Bottle o' Enchanting"));
self::register("feather", new Item(new IID(Ids::FEATHER), "Feather"));
self::register("fermented_spider_eye", new Item(new IID(Ids::FERMENTED_SPIDER_EYE), "Fermented Spider Eye"));
self::register("fire_charge", new FireCharge(new IID(Ids::FIRE_CHARGE), "Fire Charge"));
self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod"));
self::register("fishing_rod", new FishingRod(new IID(Ids::FISHING_ROD), "Fishing Rod", [EnchantmentTags::FISHING_ROD]));
self::register("flint", new Item(new IID(Ids::FLINT), "Flint"));
self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel"));
self::register("flint_and_steel", new FlintSteel(new IID(Ids::FLINT_AND_STEEL), "Flint and Steel", [EnchantmentTags::FLINT_AND_STEEL]));
self::register("ghast_tear", new Item(new IID(Ids::GHAST_TEAR), "Ghast Tear"));
self::register("glass_bottle", new GlassBottle(new IID(Ids::GLASS_BOTTLE), "Glass Bottle"));
self::register("glistering_melon", new Item(new IID(Ids::GLISTERING_MELON), "Glistering Melon"));
@@ -521,7 +524,7 @@ final class VanillaItems{
self::register("redstone_dust", new Redstone(new IID(Ids::REDSTONE_DUST), "Redstone"));
self::register("rotten_flesh", new RottenFlesh(new IID(Ids::ROTTEN_FLESH), "Rotten Flesh"));
self::register("scute", new Item(new IID(Ids::SCUTE), "Scute"));
self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears"));
self::register("shears", new Shears(new IID(Ids::SHEARS), "Shears", [EnchantmentTags::SHEARS]));
self::register("shulker_shell", new Item(new IID(Ids::SHULKER_SHELL), "Shulker Shell"));
self::register("slimeball", new Item(new IID(Ids::SLIMEBALL), "Slimeball"));
self::register("snowball", new Snowball(new IID(Ids::SNOWBALL), "Snowball"));
@@ -577,67 +580,67 @@ final class VanillaItems{
}
private static function registerTierToolItems() : void{
self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND()));
self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD()));
self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON()));
self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE()));
self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE()));
self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD()));
self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND()));
self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD()));
self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON()));
self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE()));
self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE()));
self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD()));
self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND()));
self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD()));
self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON()));
self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE()));
self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE()));
self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD()));
self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND()));
self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD()));
self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON()));
self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE()));
self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE()));
self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD()));
self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND()));
self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD()));
self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON()));
self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE()));
self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE()));
self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD()));
self::register("diamond_axe", new Axe(new IID(Ids::DIAMOND_AXE), "Diamond Axe", ToolTier::DIAMOND(), [EnchantmentTags::AXE]));
self::register("golden_axe", new Axe(new IID(Ids::GOLDEN_AXE), "Golden Axe", ToolTier::GOLD(), [EnchantmentTags::AXE]));
self::register("iron_axe", new Axe(new IID(Ids::IRON_AXE), "Iron Axe", ToolTier::IRON(), [EnchantmentTags::AXE]));
self::register("netherite_axe", new Axe(new IID(Ids::NETHERITE_AXE), "Netherite Axe", ToolTier::NETHERITE(), [EnchantmentTags::AXE]));
self::register("stone_axe", new Axe(new IID(Ids::STONE_AXE), "Stone Axe", ToolTier::STONE(), [EnchantmentTags::AXE]));
self::register("wooden_axe", new Axe(new IID(Ids::WOODEN_AXE), "Wooden Axe", ToolTier::WOOD(), [EnchantmentTags::AXE]));
self::register("diamond_hoe", new Hoe(new IID(Ids::DIAMOND_HOE), "Diamond Hoe", ToolTier::DIAMOND(), [EnchantmentTags::HOE]));
self::register("golden_hoe", new Hoe(new IID(Ids::GOLDEN_HOE), "Golden Hoe", ToolTier::GOLD(), [EnchantmentTags::HOE]));
self::register("iron_hoe", new Hoe(new IID(Ids::IRON_HOE), "Iron Hoe", ToolTier::IRON(), [EnchantmentTags::HOE]));
self::register("netherite_hoe", new Hoe(new IID(Ids::NETHERITE_HOE), "Netherite Hoe", ToolTier::NETHERITE(), [EnchantmentTags::HOE]));
self::register("stone_hoe", new Hoe(new IID(Ids::STONE_HOE), "Stone Hoe", ToolTier::STONE(), [EnchantmentTags::HOE]));
self::register("wooden_hoe", new Hoe(new IID(Ids::WOODEN_HOE), "Wooden Hoe", ToolTier::WOOD(), [EnchantmentTags::HOE]));
self::register("diamond_pickaxe", new Pickaxe(new IID(Ids::DIAMOND_PICKAXE), "Diamond Pickaxe", ToolTier::DIAMOND(), [EnchantmentTags::PICKAXE]));
self::register("golden_pickaxe", new Pickaxe(new IID(Ids::GOLDEN_PICKAXE), "Golden Pickaxe", ToolTier::GOLD(), [EnchantmentTags::PICKAXE]));
self::register("iron_pickaxe", new Pickaxe(new IID(Ids::IRON_PICKAXE), "Iron Pickaxe", ToolTier::IRON(), [EnchantmentTags::PICKAXE]));
self::register("netherite_pickaxe", new Pickaxe(new IID(Ids::NETHERITE_PICKAXE), "Netherite Pickaxe", ToolTier::NETHERITE(), [EnchantmentTags::PICKAXE]));
self::register("stone_pickaxe", new Pickaxe(new IID(Ids::STONE_PICKAXE), "Stone Pickaxe", ToolTier::STONE(), [EnchantmentTags::PICKAXE]));
self::register("wooden_pickaxe", new Pickaxe(new IID(Ids::WOODEN_PICKAXE), "Wooden Pickaxe", ToolTier::WOOD(), [EnchantmentTags::PICKAXE]));
self::register("diamond_shovel", new Shovel(new IID(Ids::DIAMOND_SHOVEL), "Diamond Shovel", ToolTier::DIAMOND(), [EnchantmentTags::SHOVEL]));
self::register("golden_shovel", new Shovel(new IID(Ids::GOLDEN_SHOVEL), "Golden Shovel", ToolTier::GOLD(), [EnchantmentTags::SHOVEL]));
self::register("iron_shovel", new Shovel(new IID(Ids::IRON_SHOVEL), "Iron Shovel", ToolTier::IRON(), [EnchantmentTags::SHOVEL]));
self::register("netherite_shovel", new Shovel(new IID(Ids::NETHERITE_SHOVEL), "Netherite Shovel", ToolTier::NETHERITE(), [EnchantmentTags::SHOVEL]));
self::register("stone_shovel", new Shovel(new IID(Ids::STONE_SHOVEL), "Stone Shovel", ToolTier::STONE(), [EnchantmentTags::SHOVEL]));
self::register("wooden_shovel", new Shovel(new IID(Ids::WOODEN_SHOVEL), "Wooden Shovel", ToolTier::WOOD(), [EnchantmentTags::SHOVEL]));
self::register("diamond_sword", new Sword(new IID(Ids::DIAMOND_SWORD), "Diamond Sword", ToolTier::DIAMOND(), [EnchantmentTags::SWORD]));
self::register("golden_sword", new Sword(new IID(Ids::GOLDEN_SWORD), "Golden Sword", ToolTier::GOLD(), [EnchantmentTags::SWORD]));
self::register("iron_sword", new Sword(new IID(Ids::IRON_SWORD), "Iron Sword", ToolTier::IRON(), [EnchantmentTags::SWORD]));
self::register("netherite_sword", new Sword(new IID(Ids::NETHERITE_SWORD), "Netherite Sword", ToolTier::NETHERITE(), [EnchantmentTags::SWORD]));
self::register("stone_sword", new Sword(new IID(Ids::STONE_SWORD), "Stone Sword", ToolTier::STONE(), [EnchantmentTags::SWORD]));
self::register("wooden_sword", new Sword(new IID(Ids::WOODEN_SWORD), "Wooden Sword", ToolTier::WOOD(), [EnchantmentTags::SWORD]));
}
private static function registerArmorItems() : void{
self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET)));
self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2)));
self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET)));
self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET)));
self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET)));
self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true)));
self::register("chainmail_boots", new Armor(new IID(Ids::CHAINMAIL_BOOTS), "Chainmail Boots", new ArmorTypeInfo(1, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::BOOTS]));
self::register("diamond_boots", new Armor(new IID(Ids::DIAMOND_BOOTS), "Diamond Boots", new ArmorTypeInfo(3, 430, ArmorInventory::SLOT_FEET, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::BOOTS]));
self::register("golden_boots", new Armor(new IID(Ids::GOLDEN_BOOTS), "Golden Boots", new ArmorTypeInfo(1, 92, ArmorInventory::SLOT_FEET, material: ArmorMaterials::GOLD()), [EnchantmentTags::BOOTS]));
self::register("iron_boots", new Armor(new IID(Ids::IRON_BOOTS), "Iron Boots", new ArmorTypeInfo(2, 196, ArmorInventory::SLOT_FEET, material: ArmorMaterials::IRON()), [EnchantmentTags::BOOTS]));
self::register("leather_boots", new Armor(new IID(Ids::LEATHER_BOOTS), "Leather Boots", new ArmorTypeInfo(1, 66, ArmorInventory::SLOT_FEET, material: ArmorMaterials::LEATHER()), [EnchantmentTags::BOOTS]));
self::register("netherite_boots", new Armor(new IID(Ids::NETHERITE_BOOTS), "Netherite Boots", new ArmorTypeInfo(3, 482, ArmorInventory::SLOT_FEET, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::BOOTS]));
self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST)));
self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2)));
self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST)));
self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST)));
self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST)));
self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true)));
self::register("chainmail_chestplate", new Armor(new IID(Ids::CHAINMAIL_CHESTPLATE), "Chainmail Chestplate", new ArmorTypeInfo(5, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::CHESTPLATE]));
self::register("diamond_chestplate", new Armor(new IID(Ids::DIAMOND_CHESTPLATE), "Diamond Chestplate", new ArmorTypeInfo(8, 529, ArmorInventory::SLOT_CHEST, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::CHESTPLATE]));
self::register("golden_chestplate", new Armor(new IID(Ids::GOLDEN_CHESTPLATE), "Golden Chestplate", new ArmorTypeInfo(5, 113, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::GOLD()), [EnchantmentTags::CHESTPLATE]));
self::register("iron_chestplate", new Armor(new IID(Ids::IRON_CHESTPLATE), "Iron Chestplate", new ArmorTypeInfo(6, 241, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::IRON()), [EnchantmentTags::CHESTPLATE]));
self::register("leather_tunic", new Armor(new IID(Ids::LEATHER_TUNIC), "Leather Tunic", new ArmorTypeInfo(3, 81, ArmorInventory::SLOT_CHEST, material: ArmorMaterials::LEATHER()), [EnchantmentTags::CHESTPLATE]));
self::register("netherite_chestplate", new Armor(new IID(Ids::NETHERITE_CHESTPLATE), "Netherite Chestplate", new ArmorTypeInfo(8, 593, ArmorInventory::SLOT_CHEST, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::CHESTPLATE]));
self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD)));
self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2)));
self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD)));
self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD)));
self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD)));
self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true)));
self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD)));
self::register("chainmail_helmet", new Armor(new IID(Ids::CHAINMAIL_HELMET), "Chainmail Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::HELMET]));
self::register("diamond_helmet", new Armor(new IID(Ids::DIAMOND_HELMET), "Diamond Helmet", new ArmorTypeInfo(3, 364, ArmorInventory::SLOT_HEAD, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::HELMET]));
self::register("golden_helmet", new Armor(new IID(Ids::GOLDEN_HELMET), "Golden Helmet", new ArmorTypeInfo(2, 78, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::GOLD()), [EnchantmentTags::HELMET]));
self::register("iron_helmet", new Armor(new IID(Ids::IRON_HELMET), "Iron Helmet", new ArmorTypeInfo(2, 166, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::IRON()), [EnchantmentTags::HELMET]));
self::register("leather_cap", new Armor(new IID(Ids::LEATHER_CAP), "Leather Cap", new ArmorTypeInfo(1, 56, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::LEATHER()), [EnchantmentTags::HELMET]));
self::register("netherite_helmet", new Armor(new IID(Ids::NETHERITE_HELMET), "Netherite Helmet", new ArmorTypeInfo(3, 408, ArmorInventory::SLOT_HEAD, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::HELMET]));
self::register("turtle_helmet", new TurtleHelmet(new IID(Ids::TURTLE_HELMET), "Turtle Shell", new ArmorTypeInfo(2, 276, ArmorInventory::SLOT_HEAD, material: ArmorMaterials::TURTLE()), [EnchantmentTags::HELMET]));
self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS)));
self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2)));
self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS)));
self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS)));
self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS)));
self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true)));
self::register("chainmail_leggings", new Armor(new IID(Ids::CHAINMAIL_LEGGINGS), "Chainmail Leggings", new ArmorTypeInfo(4, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::CHAINMAIL()), [EnchantmentTags::LEGGINGS]));
self::register("diamond_leggings", new Armor(new IID(Ids::DIAMOND_LEGGINGS), "Diamond Leggings", new ArmorTypeInfo(6, 496, ArmorInventory::SLOT_LEGS, 2, material: ArmorMaterials::DIAMOND()), [EnchantmentTags::LEGGINGS]));
self::register("golden_leggings", new Armor(new IID(Ids::GOLDEN_LEGGINGS), "Golden Leggings", new ArmorTypeInfo(3, 106, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::GOLD()), [EnchantmentTags::LEGGINGS]));
self::register("iron_leggings", new Armor(new IID(Ids::IRON_LEGGINGS), "Iron Leggings", new ArmorTypeInfo(5, 226, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::IRON()), [EnchantmentTags::LEGGINGS]));
self::register("leather_pants", new Armor(new IID(Ids::LEATHER_PANTS), "Leather Pants", new ArmorTypeInfo(2, 76, ArmorInventory::SLOT_LEGS, material: ArmorMaterials::LEATHER()), [EnchantmentTags::LEGGINGS]));
self::register("netherite_leggings", new Armor(new IID(Ids::NETHERITE_LEGGINGS), "Netherite Leggings", new ArmorTypeInfo(6, 556, ArmorInventory::SLOT_LEGS, 3, true, material: ArmorMaterials::NETHERITE()), [EnchantmentTags::LEGGINGS]));
}
}

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

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

View File

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

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

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

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

View 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'"
);
}
}
}

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

View File

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

View File

@@ -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){

View File

@@ -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{