From f98cebbd6258f65c25fb6362cb66b9c00afa45ba Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sun, 24 Nov 2024 17:58:58 +0000 Subject: [PATCH] Separate hotbar from player inventory this allows this functionality to be used with any type of inventory, and also makes it a little nicer to use in many cases. --- src/command/defaults/EnchantCommand.php | 4 +- src/entity/ExperienceManager.php | 4 +- src/entity/Human.php | 32 +++-- src/inventory/Hotbar.php | 122 ++++++++++++++++++ src/inventory/PlayerInventory.php | 86 ------------ src/item/Armor.php | 2 +- src/network/mcpe/InventoryManager.php | 6 +- .../mcpe/StandardEntityEventBroadcaster.php | 8 +- .../mcpe/handler/InGamePacketHandler.php | 4 +- src/player/Player.php | 56 ++++---- src/player/SurvivalBlockBreakHandler.php | 2 +- 11 files changed, 185 insertions(+), 141 deletions(-) create mode 100644 src/inventory/Hotbar.php diff --git a/src/command/defaults/EnchantCommand.php b/src/command/defaults/EnchantCommand.php index 191a146b0..6b73aad6c 100644 --- a/src/command/defaults/EnchantCommand.php +++ b/src/command/defaults/EnchantCommand.php @@ -56,7 +56,7 @@ class EnchantCommand extends VanillaCommand{ return true; } - $item = $player->getInventory()->getItemInHand(); + $item = $player->getHotbar()->getHeldItem(); if($item->isNull()){ $sender->sendMessage(KnownTranslationFactory::commands_enchant_noItem()); @@ -79,7 +79,7 @@ class EnchantCommand extends VanillaCommand{ //this is necessary to deal with enchanted books, which are a different item type than regular books $enchantedItem = EnchantingHelper::enchantItem($item, [new EnchantmentInstance($enchantment, $level)]); - $player->getInventory()->setItemInHand($enchantedItem); + $player->getHotbar()->setHeldItem($enchantedItem); self::broadcastCommandMessage($sender, KnownTranslationFactory::commands_enchant_success($player->getName())); return true; diff --git a/src/entity/ExperienceManager.php b/src/entity/ExperienceManager.php index 9cff48f33..5e04728f8 100644 --- a/src/entity/ExperienceManager.php +++ b/src/entity/ExperienceManager.php @@ -243,7 +243,7 @@ class ExperienceManager{ //TODO: replace this with a more generic equipment getting/setting interface $equipment = []; - if(($item = $this->entity->getInventory()->getItemInHand()) instanceof Durable && $item->hasEnchantment(VanillaEnchantments::MENDING())){ + if(($item = $this->entity->getHotbar()->getHeldItem()) instanceof Durable && $item->hasEnchantment(VanillaEnchantments::MENDING())){ $equipment[$mainHandIndex] = $item; } if(($item = $this->entity->getOffHandInventory()->getItem(0)) instanceof Durable && $item->hasEnchantment(VanillaEnchantments::MENDING())){ @@ -263,7 +263,7 @@ class ExperienceManager{ $xpValue -= (int) ceil($repairAmount / 2); if($k === $mainHandIndex){ - $this->entity->getInventory()->setItemInHand($repairItem); + $this->entity->getHotbar()->setHeldItem($repairItem); }elseif($k === $offHandIndex){ $this->entity->getOffHandInventory()->setItem(0, $repairItem); }else{ diff --git a/src/entity/Human.php b/src/entity/Human.php index 006bf6df8..8b446660a 100644 --- a/src/entity/Human.php +++ b/src/entity/Human.php @@ -32,6 +32,7 @@ use pocketmine\entity\projectile\ProjectileSource; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\inventory\CallbackInventoryListener; +use pocketmine\inventory\Hotbar; use pocketmine\inventory\Inventory; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerEnderInventory; @@ -100,6 +101,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ public function getNetworkTypeId() : string{ return EntityIds::PLAYER; } + protected Hotbar $hotbar; protected PlayerInventory $inventory; protected PlayerOffHandInventory $offHandInventory; protected PlayerEnderInventory $enderInventory; @@ -228,6 +230,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ return min(100, 7 * $this->xpManager->getXpLevel()); } + public function getHotbar() : Hotbar{ + return $this->hotbar; + } + public function getInventory() : PlayerInventory{ return $this->inventory; } @@ -266,18 +272,20 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->xpManager = new ExperienceManager($this); $this->inventory = new PlayerInventory($this); + $this->hotbar = new Hotbar($this->inventory); + $syncHeldItem = fn() => NetworkBroadcastUtils::broadcastEntityEvent( $this->getViewers(), fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this) ); $this->inventory->getListeners()->add(new CallbackInventoryListener( function(Inventory $unused, int $slot, Item $unused2) use ($syncHeldItem) : void{ - if($slot === $this->inventory->getHeldItemIndex()){ + if($slot === $this->hotbar->getSelectedIndex()){ $syncHeldItem(); } }, function(Inventory $unused, array $oldItems) use ($syncHeldItem) : void{ - if(array_key_exists($this->inventory->getHeldItemIndex(), $oldItems)){ + if(array_key_exists($this->hotbar->getSelectedIndex(), $oldItems)){ $syncHeldItem(); } } @@ -326,8 +334,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ self::populateInventoryFromListTag($this->enderInventory, $enderChestInventoryItems); } - $this->inventory->setHeldItemIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0)); - $this->inventory->getHeldItemIndexChangeListeners()->add(fn() => NetworkBroadcastUtils::broadcastEntityEvent( + $this->hotbar->setSelectedIndex($nbt->getInt(self::TAG_SELECTED_INVENTORY_SLOT, 0)); + $this->hotbar->getSelectedIndexChangeListeners()->add(fn() => NetworkBroadcastUtils::broadcastEntityEvent( $this->getViewers(), fn(EntityEventBroadcaster $broadcaster, array $recipients) => $broadcaster->onMobMainHandItemChange($recipients, $this) )); @@ -367,7 +375,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $type = $source->getCause(); if($type !== EntityDamageEvent::CAUSE_SUICIDE && $type !== EntityDamageEvent::CAUSE_VOID - && ($this->inventory->getItemInHand() instanceof Totem || $this->offHandInventory->getItem(0) instanceof Totem)){ + && ($this->hotbar->getHeldItem() instanceof Totem || $this->offHandInventory->getItem(0) instanceof Totem)){ $compensation = $this->getHealth() - $source->getFinalDamage() - 1; if($compensation <= -1){ @@ -389,10 +397,10 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->broadcastAnimation(new TotemUseAnimation($this)); $this->broadcastSound(new TotemUseSound()); - $hand = $this->inventory->getItemInHand(); + $hand = $this->hotbar->getHeldItem(); if($hand instanceof Totem){ $hand->pop(); //Plugins could alter max stack size - $this->inventory->setItemInHand($hand); + $this->hotbar->setHeldItem($hand); }elseif(($offHand = $this->offHandInventory->getItem(0)) instanceof Totem){ $offHand->pop(); $this->offHandInventory->setItem(0, $offHand); @@ -425,8 +433,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $nbt->setTag(self::TAG_INVENTORY, $inventoryTag); //Normal inventory - $slotCount = $this->inventory->getSize() + $this->inventory->getHotbarSize(); - for($slot = $this->inventory->getHotbarSize(); $slot < $slotCount; ++$slot){ + $slotCount = $this->inventory->getSize() + $this->hotbar->getSize(); + for($slot = $this->hotbar->getSize(); $slot < $slotCount; ++$slot){ $item = $this->inventory->getItem($slot - 9); if(!$item->isNull()){ $inventoryTag->push($item->nbtSerialize($slot)); @@ -441,7 +449,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } } - $nbt->setInt(self::TAG_SELECTED_INVENTORY_SLOT, $this->inventory->getHeldItemIndex()); + $nbt->setInt(self::TAG_SELECTED_INVENTORY_SLOT, $this->hotbar->getSelectedIndex()); $offHandItem = $this->offHandInventory->getItem(0); if(!$offHandItem->isNull()){ @@ -495,7 +503,7 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ $this->location->pitch, $this->location->yaw, $this->location->yaw, //TODO: head yaw - ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($this->getInventory()->getItemInHand())), + ItemStackWrapper::legacy($typeConverter->coreItemStackToNet($this->hotbar->getHeldItem())), GameMode::SURVIVAL, $this->getAllNetworkData(), new PropertySyncData([], []), @@ -529,8 +537,8 @@ class Human extends Living implements ProjectileSource, InventoryHolder{ } protected function onDispose() : void{ + $this->hotbar->getSelectedIndexChangeListeners()->clear(); $this->inventory->removeAllViewers(); - $this->inventory->getHeldItemIndexChangeListeners()->clear(); $this->offHandInventory->removeAllViewers(); $this->enderInventory->removeAllViewers(); parent::onDispose(); diff --git a/src/inventory/Hotbar.php b/src/inventory/Hotbar.php new file mode 100644 index 000000000..a98a868d4 --- /dev/null +++ b/src/inventory/Hotbar.php @@ -0,0 +1,122 @@ + + */ + protected ObjectSet $selectedIndexChangeListeners; + + public function __construct( + private Inventory $inventory, + private int $size = 9 + ){ + if($this->inventory->getSize() < $this->size){ + throw new \InvalidArgumentException("Inventory size must be at least $this->size"); + } + $this->selectedIndexChangeListeners = new ObjectSet(); + } + + public function isHotbarSlot(int $slot) : bool{ + return $slot >= 0 && $slot < $this->getSize(); + } + + /** + * @throws \InvalidArgumentException + */ + private function throwIfNotHotbarSlot(int $slot) : void{ + if(!$this->isHotbarSlot($slot)){ + throw new \InvalidArgumentException("$slot is not a valid hotbar slot index (expected 0 - " . ($this->getSize() - 1) . ")"); + } + } + + /** + * Returns the item in the specified hotbar slot. + * + * @throws \InvalidArgumentException if the hotbar slot index is out of range + */ + public function getHotbarSlotItem(int $hotbarSlot) : Item{ + $this->throwIfNotHotbarSlot($hotbarSlot); + return $this->inventory->getItem($hotbarSlot); + } + + /** + * Returns the hotbar slot number the holder is currently holding. + */ + public function getSelectedIndex() : int{ + return $this->selectedIndex; + } + + /** + * Sets which hotbar slot the player is currently loading. + * + * @param int $hotbarSlot 0-8 index of the hotbar slot to hold + * + * @throws \InvalidArgumentException if the hotbar slot is out of range + */ + public function setSelectedIndex(int $hotbarSlot) : void{ + $this->throwIfNotHotbarSlot($hotbarSlot); + + $oldIndex = $this->selectedIndex; + $this->selectedIndex = $hotbarSlot; + + foreach($this->selectedIndexChangeListeners as $callback){ + $callback($oldIndex); + } + } + + /** + * @return \Closure[]|ObjectSet + * @phpstan-return ObjectSet<\Closure(int $oldIndex) : void> + */ + public function getSelectedIndexChangeListeners() : ObjectSet{ return $this->selectedIndexChangeListeners; } + + /** + * Returns the currently-held item. + */ + public function getHeldItem() : Item{ + return $this->getHotbarSlotItem($this->selectedIndex); + } + + /** + * Sets the item in the currently-held slot to the specified item. + */ + public function setHeldItem(Item $item) : void{ + $this->inventory->setItem($this->getSelectedIndex(), $item); + } + + /** + * Returns the number of slots in the hotbar. + */ + public function getSize() : int{ + return $this->size; + } +} diff --git a/src/inventory/PlayerInventory.php b/src/inventory/PlayerInventory.php index fdaa0adff..a2e9e9252 100644 --- a/src/inventory/PlayerInventory.php +++ b/src/inventory/PlayerInventory.php @@ -24,102 +24,16 @@ declare(strict_types=1); namespace pocketmine\inventory; use pocketmine\entity\Human; -use pocketmine\item\Item; -use pocketmine\player\Player; -use pocketmine\utils\ObjectSet; class PlayerInventory extends SimpleInventory{ protected Human $holder; - protected int $itemInHandIndex = 0; - - /** - * @var \Closure[]|ObjectSet - * @phpstan-var ObjectSet<\Closure(int $oldIndex) : void> - */ - protected ObjectSet $heldItemIndexChangeListeners; public function __construct(Human $player){ $this->holder = $player; - $this->heldItemIndexChangeListeners = new ObjectSet(); parent::__construct(36); } - public function isHotbarSlot(int $slot) : bool{ - return $slot >= 0 && $slot < $this->getHotbarSize(); - } - - /** - * @throws \InvalidArgumentException - */ - private function throwIfNotHotbarSlot(int $slot) : void{ - if(!$this->isHotbarSlot($slot)){ - throw new \InvalidArgumentException("$slot is not a valid hotbar slot index (expected 0 - " . ($this->getHotbarSize() - 1) . ")"); - } - } - - /** - * Returns the item in the specified hotbar slot. - * - * @throws \InvalidArgumentException if the hotbar slot index is out of range - */ - public function getHotbarSlotItem(int $hotbarSlot) : Item{ - $this->throwIfNotHotbarSlot($hotbarSlot); - return $this->getItem($hotbarSlot); - } - - /** - * Returns the hotbar slot number the holder is currently holding. - */ - public function getHeldItemIndex() : int{ - return $this->itemInHandIndex; - } - - /** - * Sets which hotbar slot the player is currently loading. - * - * @param int $hotbarSlot 0-8 index of the hotbar slot to hold - * - * @throws \InvalidArgumentException if the hotbar slot is out of range - */ - public function setHeldItemIndex(int $hotbarSlot) : void{ - $this->throwIfNotHotbarSlot($hotbarSlot); - - $oldIndex = $this->itemInHandIndex; - $this->itemInHandIndex = $hotbarSlot; - - foreach($this->heldItemIndexChangeListeners as $callback){ - $callback($oldIndex); - } - } - - /** - * @return \Closure[]|ObjectSet - * @phpstan-return ObjectSet<\Closure(int $oldIndex) : void> - */ - public function getHeldItemIndexChangeListeners() : ObjectSet{ return $this->heldItemIndexChangeListeners; } - - /** - * Returns the currently-held item. - */ - public function getItemInHand() : Item{ - return $this->getHotbarSlotItem($this->itemInHandIndex); - } - - /** - * Sets the item in the currently-held slot to the specified item. - */ - public function setItemInHand(Item $item) : void{ - $this->setItem($this->getHeldItemIndex(), $item); - } - - /** - * Returns the number of slots in the hotbar. - */ - public function getHotbarSize() : int{ - return 9; - } - public function getHolder() : Human{ return $this->holder; } diff --git a/src/item/Armor.php b/src/item/Armor.php index 417c57f75..9e1046d96 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -145,7 +145,7 @@ class Armor extends Durable{ $thisCopy = clone $this; $new = $thisCopy->pop(); $player->getArmorInventory()->setItem($this->getArmorSlot(), $new); - $player->getInventory()->setItemInHand($existing); + $player->getHotbar()->setHeldItem($existing); $sound = $new->getMaterial()->getEquipSound(); if($sound !== null){ $player->broadcastSound($sound); diff --git a/src/network/mcpe/InventoryManager.php b/src/network/mcpe/InventoryManager.php index e4c303121..85c0dc0fb 100644 --- a/src/network/mcpe/InventoryManager.php +++ b/src/network/mcpe/InventoryManager.php @@ -132,7 +132,7 @@ class InventoryManager{ $this->addComplex(UIInventorySlotOffset::CURSOR, $this->player->getCursorInventory()); $this->addComplex(UIInventorySlotOffset::CRAFTING2X2_INPUT, $this->player->getCraftingGrid()); - $this->player->getInventory()->getHeldItemIndexChangeListeners()->add($this->syncSelectedHotbarSlot(...)); + $this->player->getHotbar()->getSelectedIndexChangeListeners()->add($this->syncSelectedHotbarSlot(...)); } private function associateIdWithInventory(int $id, Inventory $inventory) : void{ @@ -668,7 +668,7 @@ class InventoryManager{ public function syncSelectedHotbarSlot() : void{ $playerInventory = $this->player->getInventory(); - $selected = $playerInventory->getHeldItemIndex(); + $selected = $this->player->getHotbar()->getSelectedIndex(); if($selected !== $this->clientSelectedHotbarSlot){ $inventoryEntry = $this->inventories[spl_object_id($playerInventory)] ?? null; if($inventoryEntry === null){ @@ -681,7 +681,7 @@ class InventoryManager{ $this->session->sendDataPacket(MobEquipmentPacket::create( $this->player->getId(), - new ItemStackWrapper($itemStackInfo->getStackId(), $this->session->getTypeConverter()->coreItemStackToNet($playerInventory->getItemInHand())), + new ItemStackWrapper($itemStackInfo->getStackId(), $this->session->getTypeConverter()->coreItemStackToNet($playerInventory->getItem($selected))), $selected, $selected, ContainerIds::INVENTORY diff --git a/src/network/mcpe/StandardEntityEventBroadcaster.php b/src/network/mcpe/StandardEntityEventBroadcaster.php index 3e2df3994..b19a631dc 100644 --- a/src/network/mcpe/StandardEntityEventBroadcaster.php +++ b/src/network/mcpe/StandardEntityEventBroadcaster.php @@ -103,12 +103,12 @@ final class StandardEntityEventBroadcaster implements EntityEventBroadcaster{ public function onMobMainHandItemChange(array $recipients, Human $mob) : void{ //TODO: we could send zero for slot here because remote players don't need to know which slot was selected - $inv = $mob->getInventory(); + $inv = $mob->getHotbar(); $this->sendDataPacket($recipients, MobEquipmentPacket::create( $mob->getId(), - ItemStackWrapper::legacy($this->typeConverter->coreItemStackToNet($inv->getItemInHand())), - $inv->getHeldItemIndex(), - $inv->getHeldItemIndex(), + ItemStackWrapper::legacy($this->typeConverter->coreItemStackToNet($inv->getHeldItem())), + $inv->getSelectedIndex(), + $inv->getSelectedIndex(), ContainerIds::INVENTORY )); } diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index e74eb87c6..8c101853f 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -304,11 +304,11 @@ class InGamePacketHandler extends PacketHandler{ switch($packet->eventId){ case ActorEvent::EATING_ITEM: //TODO: ignore this and handle it server-side - $item = $this->player->getInventory()->getItemInHand(); + $item = $this->player->getHotbar()->getHeldItem(); if($item->isNull()){ return false; } - $this->player->broadcastAnimation(new ConsumingItemAnimation($this->player, $this->player->getInventory()->getItemInHand())); + $this->player->broadcastAnimation(new ConsumingItemAnimation($this->player, $item)); break; default: return false; diff --git a/src/player/Player.php b/src/player/Player.php index 858ad6bf5..3dba79931 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -347,7 +347,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } private function callDummyItemHeldEvent() : void{ - $slot = $this->inventory->getHeldItemIndex(); + $slot = $this->hotbar->getSelectedIndex(); $event = new PlayerItemHeldEvent($this, $this->inventory->getItem($slot), $slot); $event->call(); @@ -362,7 +362,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->inventory->getListeners()->add(new CallbackInventoryListener( function(Inventory $unused, int $slot) : void{ - if($slot === $this->inventory->getHeldItemIndex()){ + if($slot === $this->hotbar->getSelectedIndex()){ $this->setUsingItem(false); $this->callDummyItemHeldEvent(); @@ -1540,10 +1540,10 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } public function selectHotbarSlot(int $hotbarSlot) : bool{ - if(!$this->inventory->isHotbarSlot($hotbarSlot)){ //TODO: exception here? + if(!$this->hotbar->isHotbarSlot($hotbarSlot)){ //TODO: exception here? return false; } - if($hotbarSlot === $this->inventory->getHeldItemIndex()){ + if($hotbarSlot === $this->hotbar->getSelectedIndex()){ return true; } @@ -1553,7 +1553,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return false; } - $this->inventory->setHeldItemIndex($hotbarSlot); + $this->hotbar->setSelectedIndex($hotbarSlot); $this->setUsingItem(false); return true; @@ -1565,7 +1565,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ private function returnItemsFromAction(Item $oldHeldItem, Item $newHeldItem, array $extraReturnedItems) : void{ $heldItemChanged = false; - if(!$newHeldItem->equalsExact($oldHeldItem) && $oldHeldItem->equalsExact($this->inventory->getItemInHand())){ + if(!$newHeldItem->equalsExact($oldHeldItem) && $oldHeldItem->equalsExact($this->hotbar->getHeldItem())){ //determine if the item was changed in some meaningful way, or just damaged/changed count //if it was really changed we always need to set it, whether we have finite resources or not $newReplica = clone $oldHeldItem; @@ -1579,7 +1579,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($newHeldItem instanceof Durable && $newHeldItem->isBroken()){ $this->broadcastSound(new ItemBreakSound()); } - $this->inventory->setItemInHand($newHeldItem); + $this->hotbar->setHeldItem($newHeldItem); $heldItemChanged = true; } } @@ -1589,7 +1589,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ } if($heldItemChanged && count($extraReturnedItems) > 0 && $newHeldItem->isNull()){ - $this->inventory->setItemInHand(array_shift($extraReturnedItems)); + $this->hotbar->setHeldItem(array_shift($extraReturnedItems)); } foreach($this->inventory->addItem(...$extraReturnedItems) as $drop){ //TODO: we can't generate a transaction for this since the items aren't coming from an inventory :( @@ -1611,7 +1611,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ */ public function useHeldItem() : bool{ $directionVector = $this->getDirectionVector(); - $item = $this->inventory->getItemInHand(); + $item = $this->hotbar->getHeldItem(); $oldItem = clone $item; $ev = new PlayerItemUseEvent($this, $item, $directionVector); @@ -1645,7 +1645,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ * @return bool if the consumption succeeded. */ public function consumeHeldItem() : bool{ - $slot = $this->inventory->getItemInHand(); + $slot = $this->hotbar->getHeldItem(); if($slot instanceof ConsumableItem){ $oldItem = clone $slot; @@ -1678,7 +1678,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ */ public function releaseHeldItem() : bool{ try{ - $item = $this->inventory->getItemInHand(); + $item = $this->hotbar->getHeldItem(); if(!$this->isUsingItem() || $this->hasItemCooldown($item)){ return false; } @@ -1748,21 +1748,21 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ private function equipOrAddPickedItem(int $existingSlot, Item $item) : void{ if($existingSlot !== -1){ - if($existingSlot < $this->inventory->getHotbarSize()){ - $this->inventory->setHeldItemIndex($existingSlot); + if($existingSlot < $this->hotbar->getSize()){ + $this->hotbar->setSelectedIndex($existingSlot); }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); + $this->inventory->swap($this->hotbar->getSelectedIndex(), $existingSlot); } }else{ $firstEmpty = $this->inventory->firstEmpty(); if($firstEmpty === -1){ //full inventory - $this->inventory->setItemInHand($item); - }elseif($firstEmpty < $this->inventory->getHotbarSize()){ + $this->hotbar->setHeldItem($item); + }elseif($firstEmpty < $this->hotbar->getSize()){ $this->inventory->setItem($firstEmpty, $item); - $this->inventory->setHeldItemIndex($firstEmpty); + $this->hotbar->setSelectedIndex($firstEmpty); }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); - $this->inventory->setItemInHand($item); + $this->inventory->swap($this->hotbar->getSelectedIndex(), $firstEmpty); + $this->hotbar->setHeldItem($item); } } } @@ -1779,7 +1779,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $target = $this->getWorld()->getBlock($pos); - $ev = new PlayerInteractEvent($this, $this->inventory->getItemInHand(), $target, null, $face, PlayerInteractEvent::LEFT_CLICK_BLOCK); + $ev = new PlayerInteractEvent($this, $this->hotbar->getHeldItem(), $target, null, $face, PlayerInteractEvent::LEFT_CLICK_BLOCK); if($this->isSpectator()){ $ev->cancel(); } @@ -1788,7 +1788,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return false; } $this->broadcastAnimation(new ArmSwingAnimation($this), $this->getViewers()); - if($target->onAttack($this->inventory->getItemInHand(), $face, $this)){ + if($target->onAttack($this->hotbar->getHeldItem(), $face, $this)){ return true; } @@ -1829,7 +1829,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($this->canInteract($pos->add(0.5, 0.5, 0.5), $this->isCreative() ? self::MAX_REACH_DISTANCE_CREATIVE : self::MAX_REACH_DISTANCE_SURVIVAL)){ $this->broadcastAnimation(new ArmSwingAnimation($this), $this->getViewers()); $this->stopBreakBlock($pos); - $item = $this->inventory->getItemInHand(); + $item = $this->hotbar->getHeldItem(); $oldItem = clone $item; $returnedItems = []; if($this->getWorld()->useBreakOn($pos, $item, $this, true, $returnedItems)){ @@ -1854,7 +1854,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($this->canInteract($pos->add(0.5, 0.5, 0.5), $this->isCreative() ? self::MAX_REACH_DISTANCE_CREATIVE : self::MAX_REACH_DISTANCE_SURVIVAL)){ $this->broadcastAnimation(new ArmSwingAnimation($this), $this->getViewers()); - $item = $this->inventory->getItemInHand(); //this is a copy of the real item + $item = $this->hotbar->getHeldItem(); //this is a copy of the real item $oldItem = clone $item; $returnedItems = []; if($this->getWorld()->useItemOn($pos, $item, $face, $clickOffset, $this, true, $returnedItems)){ @@ -1883,7 +1883,7 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return false; } - $heldItem = $this->inventory->getItemInHand(); + $heldItem = $this->hotbar->getHeldItem(); $oldItem = clone $heldItem; $ev = new EntityDamageByEntityEvent($this, $entity, EntityDamageEvent::CAUSE_ENTITY_ATTACK, $heldItem->getAttackPoints()); @@ -1969,15 +1969,15 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $ev->call(); - $item = $this->inventory->getItemInHand(); + $item = $this->hotbar->getHeldItem(); $oldItem = clone $item; if(!$ev->isCancelled()){ if($item->onInteractEntity($this, $entity, $clickPos)){ - if($this->hasFiniteResources() && !$item->equalsExact($oldItem) && $oldItem->equalsExact($this->inventory->getItemInHand())){ + if($this->hasFiniteResources() && !$item->equalsExact($oldItem) && $oldItem->equalsExact($this->hotbar->getHeldItem())){ if($item instanceof Durable && $item->isBroken()){ $this->broadcastSound(new ItemBreakSound()); } - $this->inventory->setItemInHand($item); + $this->hotbar->setHeldItem($item); } } return $entity->onInteract($this, $clickPos); @@ -2405,8 +2405,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->getWorld()->dropItem($this->location, $item); } + $this->hotbar->setSelectedIndex(0); $clearInventory = fn(Inventory $inventory) => $inventory->setContents(array_filter($inventory->getContents(), fn(Item $item) => $item->keepOnDeath())); - $this->inventory->setHeldItemIndex(0); $clearInventory($this->inventory); $clearInventory($this->armorInventory); $clearInventory($this->offHandInventory); diff --git a/src/player/SurvivalBlockBreakHandler.php b/src/player/SurvivalBlockBreakHandler.php index e31e77ef7..95db39168 100644 --- a/src/player/SurvivalBlockBreakHandler.php +++ b/src/player/SurvivalBlockBreakHandler.php @@ -66,7 +66,7 @@ final class SurvivalBlockBreakHandler{ return 0.0; } //TODO: improve this to take stuff like swimming, ladders, enchanted tools into account, fix wrong tool break time calculations for bad tools (pmmp/PocketMine-MP#211) - $breakTimePerTick = $this->block->getBreakInfo()->getBreakTime($this->player->getInventory()->getItemInHand()) * 20; + $breakTimePerTick = $this->block->getBreakInfo()->getBreakTime($this->player->getHotbar()->getHeldItem()) * 20; if($breakTimePerTick > 0){ return 1 / $breakTimePerTick;