getTypeId() === ItemTypeIds::BOOK ? Items::ENCHANTED_BOOK() : clone $item; foreach($enchantments as $enchantment){ $resultItem->addEnchantment($enchantment); } return $resultItem; } /** * @return EnchantingOption[] */ public static function generateOptions(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::createOption($random, $input, $topRequiredLevel), self::createOption($random, $input, $middleRequiredLevel), self::createOption($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 createOption(Random $random, Item $inputItem, int $requiredXpLevel) : EnchantingOption{ $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 EnchantingOption($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; } }