49    private const MAX_BOOKSHELF_COUNT = 15;
 
   51    private function __construct(){
 
   66        $resultItem = $item->getTypeId() === 
ItemTypeIds::BOOK ? Items::ENCHANTED_BOOK() : clone $item;
 
   68        foreach($enchantments as $enchantment){
 
   69            $resultItem->addEnchantment($enchantment);
 
 
   79        if($input->isNull() || $input->hasEnchantments()){
 
   83        $random = 
new Random($seed);
 
   85        $bookshelfCount = self::countBookshelves($tablePos);
 
   86        $baseRequiredLevel = $random->nextRange(1, 8) + ($bookshelfCount >> 1) + $random->nextRange(0, $bookshelfCount);
 
   87        $topRequiredLevel = (int) floor(max($baseRequiredLevel / 3, 1));
 
   88        $middleRequiredLevel = (int) floor($baseRequiredLevel * 2 / 3 + 1);
 
   89        $bottomRequiredLevel = max($baseRequiredLevel, $bookshelfCount * 2);
 
   92            self::createOption($random, $input, $topRequiredLevel),
 
   93            self::createOption($random, $input, $middleRequiredLevel),
 
   94            self::createOption($random, $input, $bottomRequiredLevel),
 
 
   98    private static function countBookshelves(
Position $tablePos) : int{
 
  102        for($x = -2; $x <= 2; $x++){
 
  103            for($z = -2; $z <= 2; $z++){
 
  105                if(abs($x) !== 2 && abs($z) !== 2){
 
  110                for($y = 0; $y <= 1; $y++){
 
  112                    $spaceX = max(min($x, 1), -1);
 
  113                    $spaceZ = max(min($z, 1), -1);
 
  114                    $spaceBlock = $world->getBlock($tablePos->add($spaceX, $y, $spaceZ));
 
  115                    if($spaceBlock->getTypeId() !== BlockTypeIds::AIR){
 
  121                for($y = 0; $y <= 1; $y++){
 
  122                    $block = $world->getBlock($tablePos->add($x, $y, $z));
 
  123                    if($block->getTypeId() === BlockTypeIds::BOOKSHELF){
 
  125                        if($bookshelfCount === self::MAX_BOOKSHELF_COUNT){
 
  126                            return $bookshelfCount;
 
  133        return $bookshelfCount;
 
  136    private static function createOption(Random $random, Item $inputItem, 
int $requiredXpLevel) : EnchantingOption{
 
  137        $enchantingPower = $requiredXpLevel;
 
  139        $enchantability = $inputItem->getEnchantability();
 
  140        $enchantingPower = $enchantingPower + $random->nextRange(0, $enchantability >> 2) + $random->nextRange(0, $enchantability >> 2) + 1;
 
  142        $bonus = 1 + ($random->nextFloat() + $random->nextFloat() - 1) * 0.15;
 
  143        $enchantingPower = (int) round($enchantingPower * $bonus);
 
  145        $resultEnchantments = [];
 
  146        $availableEnchantments = self::getAvailableEnchantments($enchantingPower, $inputItem);
 
  148        $lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments);
 
  149        if($lastEnchantment !== 
null){
 
  150            $resultEnchantments[] = $lastEnchantment;
 
  153            while($random->nextFloat() <= ($enchantingPower + 1) / 50){
 
  156                $availableEnchantments = array_filter(
 
  157                    $availableEnchantments,
 
  158                    function(EnchantmentInstance $e) use ($lastEnchantment){
 
  159                        return $e->getType() !== $lastEnchantment->getType() &&
 
  160                            $e->getType()->isCompatibleWith($lastEnchantment->getType());
 
  164                $lastEnchantment = self::getRandomWeightedEnchantment($random, $availableEnchantments);
 
  165                if($lastEnchantment === 
null){
 
  169                $resultEnchantments[] = $lastEnchantment;
 
  170                $enchantingPower >>= 1;
 
  174        return new EnchantingOption($requiredXpLevel, self::getRandomOptionName($random), $resultEnchantments);
 
  180    private static function getAvailableEnchantments(
int $enchantingPower, Item $item) : array{
 
  183        foreach(EnchantmentRegistry::getInstance()->getPrimaryEnchantmentsForItem($item) as $enchantment){
 
  184            for($lvl = $enchantment->getMaxLevel(); $lvl > 0; $lvl--){
 
  185                if($enchantingPower >= $enchantment->getMinEnchantingPower($lvl) &&
 
  186                    $enchantingPower <= $enchantment->getMaxEnchantingPower($lvl)
 
  188                    $list[] = 
new EnchantmentInstance($enchantment, $lvl);
 
  200    private static function getRandomWeightedEnchantment(Random $random, array $enchantments) : ?EnchantmentInstance{
 
  201        if(count($enchantments) === 0){
 
  206        foreach($enchantments as $enchantment){
 
  207            $totalWeight += $enchantment->getType()->getRarity();
 
  211        $randomWeight = $random->nextRange(1, $totalWeight);
 
  213        foreach($enchantments as $enchantment){
 
  214            $randomWeight -= $enchantment->getType()->getRarity();
 
  216            if($randomWeight <= 0){
 
  217                $result = $enchantment;
 
  225    private static function getRandomOptionName(Random $random) : string{
 
  227        for($i = $random->nextRange(5, 15); $i > 0; $i--){
 
  228            $name .= chr($random->nextRange(ord(
"a"), ord(
"z")));