diff --git a/src/entity/Entity.php b/src/entity/Entity.php index c55a8716c..68f5cbdd3 100644 --- a/src/entity/Entity.php +++ b/src/entity/Entity.php @@ -35,6 +35,7 @@ use pocketmine\event\entity\EntityMotionEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\entity\EntitySpawnEvent; use pocketmine\event\entity\EntityTeleportEvent; +use pocketmine\item\Item; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; use pocketmine\math\Vector2; @@ -1560,6 +1561,13 @@ abstract class Entity{ $this->hasSpawned = []; } + /** + * Returns the item that players will equip when middle-clicking on this entity. + */ + public function getPickedItem() : ?Item{ + return null; + } + /** * Flags the entity to be removed from the world on the next tick. */ diff --git a/src/entity/Squid.php b/src/entity/Squid.php index 75c50061b..a0f3fef48 100644 --- a/src/entity/Squid.php +++ b/src/entity/Squid.php @@ -26,6 +26,7 @@ namespace pocketmine\entity; use pocketmine\entity\animation\SquidInkCloudAnimation; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; @@ -124,4 +125,8 @@ class Squid extends WaterAnimal{ VanillaItems::INK_SAC()->setCount(mt_rand(1, 3)) ]; } + + public function getPickedItem() : ?Item{ + return VanillaItems::SQUID_SPAWN_EGG(); + } } diff --git a/src/entity/Villager.php b/src/entity/Villager.php index 376401a5d..fee65b014 100644 --- a/src/entity/Villager.php +++ b/src/entity/Villager.php @@ -23,6 +23,8 @@ declare(strict_types=1); namespace pocketmine\entity; +use pocketmine\item\Item; +use pocketmine\item\VanillaItems; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection; @@ -87,6 +89,10 @@ class Villager extends Living implements Ageable{ return $this->baby; } + public function getPickedItem() : ?Item{ + return VanillaItems::VILLAGER_SPAWN_EGG(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); $properties->setGenericFlag(EntityMetadataFlags::BABY, $this->baby); diff --git a/src/entity/Zombie.php b/src/entity/Zombie.php index 18fc2207e..159a2dd25 100644 --- a/src/entity/Zombie.php +++ b/src/entity/Zombie.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\entity; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; use function mt_rand; @@ -65,4 +66,8 @@ class Zombie extends Living{ //TODO: check for equipment and whether it's a baby return 5; } + + public function getPickedItem() : ?Item{ + return VanillaItems::ZOMBIE_SPAWN_EGG(); + } } diff --git a/src/entity/object/FallingBlock.php b/src/entity/object/FallingBlock.php index 9d8af8934..66d4049f8 100644 --- a/src/entity/object/FallingBlock.php +++ b/src/entity/object/FallingBlock.php @@ -35,6 +35,7 @@ use pocketmine\entity\Location; use pocketmine\event\entity\EntityBlockChangeEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; @@ -194,6 +195,10 @@ class FallingBlock extends Entity{ return $nbt; } + public function getPickedItem() : ?Item{ + return $this->block->asItem(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); diff --git a/src/entity/object/Painting.php b/src/entity/object/Painting.php index f6449883c..641d04051 100644 --- a/src/entity/object/Painting.php +++ b/src/entity/object/Painting.php @@ -28,6 +28,7 @@ use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Location; use pocketmine\event\entity\EntityDamageByEntityEvent; +use pocketmine\item\Item; use pocketmine\item\VanillaItems; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Facing; @@ -165,6 +166,10 @@ class Painting extends Entity{ )); } + public function getPickedItem() : ?Item{ + return VanillaItems::PAINTING(); + } + /** * Returns the painting motive (which image is displayed on the painting) */ diff --git a/src/entity/object/PrimedTNT.php b/src/entity/object/PrimedTNT.php index ec621adfb..af3c97922 100644 --- a/src/entity/object/PrimedTNT.php +++ b/src/entity/object/PrimedTNT.php @@ -23,11 +23,13 @@ declare(strict_types=1); namespace pocketmine\entity\object; +use pocketmine\block\VanillaBlocks; use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Explosive; use pocketmine\event\entity\EntityDamageEvent; use pocketmine\event\entity\EntityPreExplodeEvent; +use pocketmine\item\Item; use pocketmine\math\Vector3; use pocketmine\nbt\tag\CompoundTag; use pocketmine\network\mcpe\protocol\types\entity\EntityIds; @@ -127,6 +129,10 @@ class PrimedTNT extends Entity implements Explosive{ } } + public function getPickedItem() : ?Item{ + return VanillaBlocks::TNT()->setWorksUnderwater($this->worksUnderwater)->asItem(); + } + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); diff --git a/src/event/player/PlayerEntityPickEvent.php b/src/event/player/PlayerEntityPickEvent.php new file mode 100644 index 000000000..3c742d6c4 --- /dev/null +++ b/src/event/player/PlayerEntityPickEvent.php @@ -0,0 +1,53 @@ +player = $player; + } + + public function getEntity() : Entity{ + return $this->entityClicked; + } + + public function getResultItem() : Item{ + return $this->resultItem; + } +} diff --git a/src/network/mcpe/handler/InGamePacketHandler.php b/src/network/mcpe/handler/InGamePacketHandler.php index 9e0039db4..e74eb87c6 100644 --- a/src/network/mcpe/handler/InGamePacketHandler.php +++ b/src/network/mcpe/handler/InGamePacketHandler.php @@ -671,7 +671,7 @@ class InGamePacketHandler extends PacketHandler{ } public function handleActorPickRequest(ActorPickRequestPacket $packet) : bool{ - return false; //TODO + return $this->player->pickEntity($packet->actorUniqueId); } public function handlePlayerAction(PlayerActionPacket $packet) : bool{ diff --git a/src/player/Player.php b/src/player/Player.php index 192e26a5f..999c07a93 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -57,6 +57,7 @@ use pocketmine\event\player\PlayerDisplayNameChangeEvent; use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEntityInteractEvent; +use pocketmine\event\player\PlayerEntityPickEvent; use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\event\player\PlayerGameModeChangeEvent; use pocketmine\event\player\PlayerInteractEvent; @@ -1709,29 +1710,58 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $ev->call(); if(!$ev->isCancelled()){ - if($existingSlot !== -1){ - if($existingSlot < $this->inventory->getHotbarSize()){ - $this->inventory->setHeldItemIndex($existingSlot); - }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); - } - }else{ - $firstEmpty = $this->inventory->firstEmpty(); - if($firstEmpty === -1){ //full inventory - $this->inventory->setItemInHand($item); - }elseif($firstEmpty < $this->inventory->getHotbarSize()){ - $this->inventory->setItem($firstEmpty, $item); - $this->inventory->setHeldItemIndex($firstEmpty); - }else{ - $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); - $this->inventory->setItemInHand($item); - } - } + $this->equipOrAddPickedItem($existingSlot, $item); } return true; } + public function pickEntity(int $entityId) : bool{ + $entity = $this->getWorld()->getEntity($entityId); + if($entity === null){ + return true; + } + + $item = $entity->getPickedItem(); + if($item === null){ + return true; + } + + $ev = new PlayerEntityPickEvent($this, $entity, $item); + $existingSlot = $this->inventory->first($item); + if($existingSlot === -1 && ($this->hasFiniteResources() || $this->isSpectator())){ + $ev->cancel(); + } + $ev->call(); + + if(!$ev->isCancelled()){ + $this->equipOrAddPickedItem($existingSlot, $item); + } + + return true; + } + + private function equipOrAddPickedItem(int $existingSlot, Item $item) : void{ + if($existingSlot !== -1){ + if($existingSlot < $this->inventory->getHotbarSize()){ + $this->inventory->setHeldItemIndex($existingSlot); + }else{ + $this->inventory->swap($this->inventory->getHeldItemIndex(), $existingSlot); + } + }else{ + $firstEmpty = $this->inventory->firstEmpty(); + if($firstEmpty === -1){ //full inventory + $this->inventory->setItemInHand($item); + }elseif($firstEmpty < $this->inventory->getHotbarSize()){ + $this->inventory->setItem($firstEmpty, $item); + $this->inventory->setHeldItemIndex($firstEmpty); + }else{ + $this->inventory->swap($this->inventory->getHeldItemIndex(), $firstEmpty); + $this->inventory->setItemInHand($item); + } + } + } + /** * Performs a left-click (attack) action on the block. *