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")));