From d3add78d3e5d949a26af5c63eb3e0e8f8c0f2ad1 Mon Sep 17 00:00:00 2001 From: =?UTF-8?q?Javier=20Le=C3=B3n?= <58715544+JavierLeon9966@users.noreply.github.com> Date: Fri, 15 Nov 2024 18:27:10 -0300 Subject: [PATCH] Add support for basic entity picking via middle-click (#5397) Support for more advanced stuff like NBT copying wasn't added in this PR, as the NBT used by PM is currently an inconsistent mess and doesn't play nice with vanilla. In the interests of avoiding this mess propagating, it's been left for another time. Adds PlayerEntityPickEvent a la PlayerBlockPickEvent and Entity->getPickedItem(). --- src/entity/Entity.php | 8 +++ src/entity/Squid.php | 5 ++ src/entity/Villager.php | 6 ++ src/entity/Zombie.php | 5 ++ src/entity/object/FallingBlock.php | 5 ++ src/entity/object/Painting.php | 5 ++ src/entity/object/PrimedTNT.php | 6 ++ src/event/player/PlayerEntityPickEvent.php | 53 +++++++++++++++ .../mcpe/handler/InGamePacketHandler.php | 2 +- src/player/Player.php | 66 ++++++++++++++----- 10 files changed, 142 insertions(+), 19 deletions(-) create mode 100644 src/event/player/PlayerEntityPickEvent.php 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. *