From d0ff6d2e365dad0fa16a320d78f6d6e52be18005 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Sat, 16 Jul 2022 19:50:33 +0100 Subject: [PATCH] Enable various types of interaction to return items to the player, without needing to have a bunch of boilerplate creative mode and held item checks it became glaringly obvious that this was needed because of #4827 and #4868. this is further needed with the addition of cauldrons. --- src/block/Anvil.php | 2 +- src/block/Bamboo.php | 2 +- src/block/BambooSapling.php | 2 +- src/block/Barrel.php | 2 +- src/block/BaseCake.php | 2 +- src/block/Bed.php | 2 +- src/block/Bell.php | 2 +- src/block/Block.php | 8 ++- src/block/BrewingStand.php | 2 +- src/block/Button.php | 2 +- src/block/Cake.php | 4 +- src/block/CakeWithCandle.php | 6 +-- src/block/CartographyTable.php | 2 +- src/block/ChemistryTable.php | 2 +- src/block/Chest.php | 2 +- src/block/CocoaBlock.php | 2 +- src/block/CraftingTable.php | 2 +- src/block/Crops.php | 2 +- src/block/DaylightSensor.php | 2 +- src/block/Dirt.php | 2 +- src/block/Door.php | 2 +- src/block/DragonEgg.php | 2 +- src/block/EnchantingTable.php | 2 +- src/block/EnderChest.php | 2 +- src/block/FenceGate.php | 2 +- src/block/FlowerPot.php | 2 +- src/block/Furnace.php | 2 +- src/block/Grass.php | 2 +- src/block/Hopper.php | 2 +- src/block/Ice.php | 4 +- src/block/ItemFrame.php | 2 +- src/block/Jukebox.php | 6 +-- src/block/Lectern.php | 2 +- src/block/Lever.php | 2 +- src/block/Light.php | 2 +- src/block/Loom.php | 2 +- src/block/Pumpkin.php | 2 +- src/block/RedstoneComparator.php | 2 +- src/block/RedstoneOre.php | 2 +- src/block/RedstoneRepeater.php | 2 +- src/block/Sapling.php | 2 +- src/block/SeaPickle.php | 4 +- src/block/ShulkerBox.php | 2 +- src/block/SmithingTable.php | 2 +- src/block/Stonecutter.php | 2 +- src/block/Sugarcane.php | 2 +- src/block/SweetBerryBush.php | 2 +- src/block/TNT.php | 6 +-- src/block/Trapdoor.php | 2 +- src/block/Wood.php | 2 +- src/block/utils/CandleTrait.php | 2 +- src/block/utils/CopperTrait.php | 2 +- src/item/Armor.php | 11 ++-- src/item/Axe.php | 4 +- src/item/Bow.php | 2 +- src/item/Bucket.php | 15 ++---- src/item/FlintSteel.php | 2 +- src/item/Hoe.php | 4 +- src/item/Item.php | 20 +++++-- src/item/LiquidBucket.php | 7 ++- src/item/PaintingItem.php | 2 +- src/item/Pickaxe.php | 4 +- src/item/ProjectileItem.php | 2 +- src/item/Shears.php | 2 +- src/item/Shovel.php | 4 +- src/item/SpawnEgg.php | 2 +- src/item/Sword.php | 4 +- src/player/Player.php | 90 ++++++++++++++++++-------------- src/world/World.php | 29 +++++----- 69 files changed, 177 insertions(+), 155 deletions(-) diff --git a/src/block/Anvil.php b/src/block/Anvil.php index ef8ef3f620..aed4dac0b1 100644 --- a/src/block/Anvil.php +++ b/src/block/Anvil.php @@ -89,7 +89,7 @@ class Anvil extends Transparent implements Fallable{ return SupportType::NONE(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $player->setCurrentWindow(new AnvilInventory($this->position)); } diff --git a/src/block/Bamboo.php b/src/block/Bamboo.php index c8be857b0b..61cb7c6655 100644 --- a/src/block/Bamboo.php +++ b/src/block/Bamboo.php @@ -149,7 +149,7 @@ class Bamboo extends Transparent{ return $top; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ $top = $this->seekToTop(); if($top->grow(self::getMaxHeight($top->position->getFloorX(), $top->position->getFloorZ()), mt_rand(1, 2), $player)){ diff --git a/src/block/BambooSapling.php b/src/block/BambooSapling.php index e1f23c5657..d005f37647 100644 --- a/src/block/BambooSapling.php +++ b/src/block/BambooSapling.php @@ -73,7 +73,7 @@ final class BambooSapling extends Flowable{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer || $item instanceof ItemBamboo){ if($this->grow($player)){ $item->pop(); diff --git a/src/block/Barrel.php b/src/block/Barrel.php index 2efef6bbcf..14ef97e099 100644 --- a/src/block/Barrel.php +++ b/src/block/Barrel.php @@ -81,7 +81,7 @@ class Barrel extends Opaque{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $barrel = $this->position->getWorld()->getTile($this->position); if($barrel instanceof TileBarrel){ diff --git a/src/block/BaseCake.php b/src/block/BaseCake.php index 5fcca644a7..21fd6336a0 100644 --- a/src/block/BaseCake.php +++ b/src/block/BaseCake.php @@ -54,7 +54,7 @@ abstract class BaseCake extends Transparent implements FoodSource{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ return $player->consumeObject($this); } diff --git a/src/block/Bed.php b/src/block/Bed.php index 4fd65e9090..4bf4dac238 100644 --- a/src/block/Bed.php +++ b/src/block/Bed.php @@ -130,7 +130,7 @@ class Bed extends Transparent{ return null; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $other = $this->getOtherHalf(); $playerPos = $player->getPosition(); diff --git a/src/block/Bell.php b/src/block/Bell.php index bc13a33247..3f60774306 100644 --- a/src/block/Bell.php +++ b/src/block/Bell.php @@ -139,7 +139,7 @@ final class Bell extends Transparent{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $faceHit = Facing::opposite($player->getHorizontalFacing()); if($this->attachmentType->equals(BellAttachmentType::CEILING())){ diff --git a/src/block/Block.php b/src/block/Block.php index 20f77c8f11..d521cb522f 100644 --- a/src/block/Block.php +++ b/src/block/Block.php @@ -275,8 +275,10 @@ class Block{ /** * Do the actions needed so the block is broken with the Item + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if full) */ - public function onBreak(Item $item, ?Player $player = null) : bool{ + public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{ if(($t = $this->position->getWorld()->getTile($this->position)) !== null){ $t->onBlockDestroyed(); } @@ -315,8 +317,10 @@ class Block{ /** * Do actions when interacted by Item. Returns if it has done anything + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ return false; } diff --git a/src/block/BrewingStand.php b/src/block/BrewingStand.php index ee39b0ea3b..c35576d585 100644 --- a/src/block/BrewingStand.php +++ b/src/block/BrewingStand.php @@ -118,7 +118,7 @@ class BrewingStand extends Transparent{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $stand = $this->position->getWorld()->getTile($this->position); if($stand instanceof TileBrewingStand && $stand->canOpenWith($item->getCustomName())){ diff --git a/src/block/Button.php b/src/block/Button.php index 6cdcae3514..1ffca43875 100644 --- a/src/block/Button.php +++ b/src/block/Button.php @@ -69,7 +69,7 @@ abstract class Button extends Flowable{ abstract protected function getActivationTime() : int; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->pressed){ $this->pressed = true; $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/block/Cake.php b/src/block/Cake.php index 31d1c3ad96..e6b152ce02 100644 --- a/src/block/Cake.php +++ b/src/block/Cake.php @@ -70,7 +70,7 @@ class Cake extends BaseCake{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof ItemBlock){ $block = $item->getBlock(); $resultBlock = null; @@ -87,7 +87,7 @@ class Cake extends BaseCake{ } } - return parent::onInteract($item, $face, $clickVector, $player); + return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); } public function getResidue() : Block{ diff --git a/src/block/CakeWithCandle.php b/src/block/CakeWithCandle.php index 63278ea362..187442cd98 100644 --- a/src/block/CakeWithCandle.php +++ b/src/block/CakeWithCandle.php @@ -51,12 +51,12 @@ class CakeWithCandle extends BaseCake{ return VanillaBlocks::CANDLE(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ - if($this->onInteractCandle($item, $face, $clickVector, $player)){ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($this->onInteractCandle($item, $face, $clickVector, $player, $returnedItems)){ return true; } - return parent::onInteract($item, $face, $clickVector, $player); + return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); } public function getDropsForCompatibleTool(Item $item) : array{ diff --git a/src/block/CartographyTable.php b/src/block/CartographyTable.php index 195c180706..67d950c5ac 100644 --- a/src/block/CartographyTable.php +++ b/src/block/CartographyTable.php @@ -30,7 +30,7 @@ use pocketmine\player\Player; final class CartographyTable extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new CartographyTableInventory($this->position)); } diff --git a/src/block/ChemistryTable.php b/src/block/ChemistryTable.php index d1e008d0e9..27fb636746 100644 --- a/src/block/ChemistryTable.php +++ b/src/block/ChemistryTable.php @@ -33,7 +33,7 @@ final class ChemistryTable extends Opaque{ use FacesOppositePlacingPlayerTrait; use HorizontalFacingTrait; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ //TODO return false; } diff --git a/src/block/Chest.php b/src/block/Chest.php index 55a0591ca1..0019e16d50 100644 --- a/src/block/Chest.php +++ b/src/block/Chest.php @@ -74,7 +74,7 @@ class Chest extends Transparent{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $chest = $this->position->getWorld()->getTile($this->position); diff --git a/src/block/CocoaBlock.php b/src/block/CocoaBlock.php index bc6b892ca0..4144bd754d 100644 --- a/src/block/CocoaBlock.php +++ b/src/block/CocoaBlock.php @@ -101,7 +101,7 @@ class CocoaBlock extends Transparent{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow()){ $item->pop(); diff --git a/src/block/CraftingTable.php b/src/block/CraftingTable.php index cc837d0034..dcd9edce2c 100644 --- a/src/block/CraftingTable.php +++ b/src/block/CraftingTable.php @@ -30,7 +30,7 @@ use pocketmine\player\Player; class CraftingTable extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $player->setCurrentWindow(new CraftingTableInventory($this->position)); } diff --git a/src/block/Crops.php b/src/block/Crops.php index 52242cfe45..092632bb27 100644 --- a/src/block/Crops.php +++ b/src/block/Crops.php @@ -68,7 +68,7 @@ abstract class Crops extends Flowable{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->age < self::MAX_AGE && $item instanceof Fertilizer){ $block = clone $this; $block->age += mt_rand(2, 5); diff --git a/src/block/DaylightSensor.php b/src/block/DaylightSensor.php index 93ea4ce607..77a28274ce 100644 --- a/src/block/DaylightSensor.php +++ b/src/block/DaylightSensor.php @@ -81,7 +81,7 @@ class DaylightSensor extends Transparent{ return SupportType::NONE(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->inverted = !$this->inverted; $this->signalStrength = $this->recalculateSignalStrength(); $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/block/Dirt.php b/src/block/Dirt.php index a3c6da660a..cdf24be721 100644 --- a/src/block/Dirt.php +++ b/src/block/Dirt.php @@ -53,7 +53,7 @@ class Dirt extends Opaque{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($face === Facing::UP && $item instanceof Hoe){ $item->applyDamage(1); diff --git a/src/block/Door.php b/src/block/Door.php index 6657a18f04..d5497bb7c7 100644 --- a/src/block/Door.php +++ b/src/block/Door.php @@ -148,7 +148,7 @@ class Door extends Transparent{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->open = !$this->open; $other = $this->getSide($this->top ? Facing::DOWN : Facing::UP); diff --git a/src/block/DragonEgg.php b/src/block/DragonEgg.php index db6512a9cd..6b330246b7 100644 --- a/src/block/DragonEgg.php +++ b/src/block/DragonEgg.php @@ -48,7 +48,7 @@ class DragonEgg extends Transparent implements Fallable{ return null; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->teleport(); return true; } diff --git a/src/block/EnchantingTable.php b/src/block/EnchantingTable.php index b76147840f..f80d52d124 100644 --- a/src/block/EnchantingTable.php +++ b/src/block/EnchantingTable.php @@ -44,7 +44,7 @@ class EnchantingTable extends Transparent{ return SupportType::NONE(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ //TODO lock diff --git a/src/block/EnderChest.php b/src/block/EnderChest.php index e71206b567..68c2805f9a 100644 --- a/src/block/EnderChest.php +++ b/src/block/EnderChest.php @@ -54,7 +54,7 @@ class EnderChest extends Transparent{ return SupportType::NONE(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $enderChest = $this->position->getWorld()->getTile($this->position); if($enderChest instanceof TileEnderChest && $this->getSide(Facing::UP)->isTransparent()){ diff --git a/src/block/FenceGate.php b/src/block/FenceGate.php index 4fa5313637..2eafb99587 100644 --- a/src/block/FenceGate.php +++ b/src/block/FenceGate.php @@ -109,7 +109,7 @@ class FenceGate extends Transparent{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->open = !$this->open; if($this->open && $player !== null){ $playerFacing = $player->getHorizontalFacing(); diff --git a/src/block/FlowerPot.php b/src/block/FlowerPot.php index e4dc03fb2c..6099539a04 100644 --- a/src/block/FlowerPot.php +++ b/src/block/FlowerPot.php @@ -112,7 +112,7 @@ class FlowerPot extends Flowable{ return $block->getSupportType(Facing::UP)->hasCenterSupport(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $plant = $item->getBlock(); if($this->plant !== null){ if($this->isValidPlant($plant)){ diff --git a/src/block/Furnace.php b/src/block/Furnace.php index c1b29c9863..5e2f19aabd 100644 --- a/src/block/Furnace.php +++ b/src/block/Furnace.php @@ -67,7 +67,7 @@ class Furnace extends Opaque{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $furnace = $this->position->getWorld()->getTile($this->position); if($furnace instanceof TileFurnace && $furnace->canOpenWith($item->getCustomName())){ diff --git a/src/block/Grass.php b/src/block/Grass.php index 16580b5dbf..342565641f 100644 --- a/src/block/Grass.php +++ b/src/block/Grass.php @@ -87,7 +87,7 @@ class Grass extends Opaque{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($face !== Facing::UP){ return false; } diff --git a/src/block/Hopper.php b/src/block/Hopper.php index c1d01654c1..9b0996d924 100644 --- a/src/block/Hopper.php +++ b/src/block/Hopper.php @@ -93,7 +93,7 @@ class Hopper extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $tile = $this->position->getWorld()->getTile($this->position); if($tile instanceof TileHopper){ //TODO: find a way to have inventories open on click without this boilerplate in every block diff --git a/src/block/Ice.php b/src/block/Ice.php index 7edbc26596..ad83bb6ba7 100644 --- a/src/block/Ice.php +++ b/src/block/Ice.php @@ -38,12 +38,12 @@ class Ice extends Transparent{ return 0.98; } - public function onBreak(Item $item, ?Player $player = null) : bool{ + public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{ if(($player === null || $player->isSurvival()) && !$item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){ $this->position->getWorld()->setBlock($this->position, VanillaBlocks::WATER()); return true; } - return parent::onBreak($item, $player); + return parent::onBreak($item, $player, $returnedItems); } public function ticksRandomly() : bool{ diff --git a/src/block/ItemFrame.php b/src/block/ItemFrame.php index 831f39760b..ec6d778804 100644 --- a/src/block/ItemFrame.php +++ b/src/block/ItemFrame.php @@ -153,7 +153,7 @@ class ItemFrame extends Flowable{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->framedItem !== null){ $this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS; }elseif(!$item->isNull()){ diff --git a/src/block/Jukebox.php b/src/block/Jukebox.php index 185426600c..3d5f340b3a 100644 --- a/src/block/Jukebox.php +++ b/src/block/Jukebox.php @@ -39,7 +39,7 @@ class Jukebox extends Opaque{ return 300; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ if($this->record !== null){ $this->ejectRecord(); @@ -83,9 +83,9 @@ class Jukebox extends Opaque{ $this->getPosition()->getWorld()->addSound($this->getPosition(), new RecordStopSound()); } - public function onBreak(Item $item, ?Player $player = null) : bool{ + public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{ $this->stopSound(); - return parent::onBreak($item, $player); + return parent::onBreak($item, $player, $returnedItems); } public function getDropsForCompatibleTool(Item $item) : array{ diff --git a/src/block/Lectern.php b/src/block/Lectern.php index 6e7026ff80..cb5a9d9fee 100644 --- a/src/block/Lectern.php +++ b/src/block/Lectern.php @@ -127,7 +127,7 @@ class Lectern extends Transparent{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->book === null && $item instanceof WritableBookBase){ $this->position->getWorld()->setBlock($this->position, $this->setBook($item)); $this->position->getWorld()->addSound($this->position, new LecternPlaceBookSound()); diff --git a/src/block/Lever.php b/src/block/Lever.php index d25ce1c44d..55f7ba0e89 100644 --- a/src/block/Lever.php +++ b/src/block/Lever.php @@ -106,7 +106,7 @@ class Lever extends Flowable{ } } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->activated = !$this->activated; $this->position->getWorld()->setBlock($this->position, $this); $this->position->getWorld()->addSound( diff --git a/src/block/Light.php b/src/block/Light.php index 9313cb19f6..804f98871c 100644 --- a/src/block/Light.php +++ b/src/block/Light.php @@ -63,7 +63,7 @@ final class Light extends Flowable{ return $blockReplace->canBeReplaced() && $blockReplace->getTypeId() !== $this->getTypeId(); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->level = $this->level === self::MAX_LIGHT_LEVEL ? self::MIN_LIGHT_LEVEL : $this->level + 1; diff --git a/src/block/Loom.php b/src/block/Loom.php index e75594ece9..a10b577232 100644 --- a/src/block/Loom.php +++ b/src/block/Loom.php @@ -34,7 +34,7 @@ final class Loom extends Opaque{ use FacesOppositePlacingPlayerTrait; use HorizontalFacingTrait; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new LoomInventory($this->position)); return true; diff --git a/src/block/Pumpkin.php b/src/block/Pumpkin.php index 2f374670e3..19b3b2e563 100644 --- a/src/block/Pumpkin.php +++ b/src/block/Pumpkin.php @@ -33,7 +33,7 @@ use function in_array; class Pumpkin extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Shears && in_array($face, Facing::HORIZONTAL, true)){ $item->applyDamage(1); $this->position->getWorld()->setBlock($this->position, VanillaBlocks::CARVED_PUMPKIN()->setFacing($face)); diff --git a/src/block/RedstoneComparator.php b/src/block/RedstoneComparator.php index 769c7ef96a..cd2004460d 100644 --- a/src/block/RedstoneComparator.php +++ b/src/block/RedstoneComparator.php @@ -103,7 +103,7 @@ class RedstoneComparator extends Flowable{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->isSubtractMode = !$this->isSubtractMode; $this->position->getWorld()->setBlock($this->position, $this); return true; diff --git a/src/block/RedstoneOre.php b/src/block/RedstoneOre.php index 1a7ed5e3b1..044bb31d00 100644 --- a/src/block/RedstoneOre.php +++ b/src/block/RedstoneOre.php @@ -60,7 +60,7 @@ class RedstoneOre extends Opaque{ return $this->lit ? 9 : 0; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->lit){ $this->lit = true; $this->position->getWorld()->setBlock($this->position, $this); //no return here - this shouldn't prevent block placement diff --git a/src/block/RedstoneRepeater.php b/src/block/RedstoneRepeater.php index b680d52cc8..39b720a697 100644 --- a/src/block/RedstoneRepeater.php +++ b/src/block/RedstoneRepeater.php @@ -88,7 +88,7 @@ class RedstoneRepeater extends Flowable{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(++$this->delay > self::MAX_DELAY){ $this->delay = self::MIN_DELAY; } diff --git a/src/block/Sapling.php b/src/block/Sapling.php index 82afb3334c..3d9a327760 100644 --- a/src/block/Sapling.php +++ b/src/block/Sapling.php @@ -74,7 +74,7 @@ class Sapling extends Flowable{ return false; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer && $this->grow($player)){ $item->pop(); diff --git a/src/block/SeaPickle.php b/src/block/SeaPickle.php index d6e0f32a7e..c79363062b 100644 --- a/src/block/SeaPickle.php +++ b/src/block/SeaPickle.php @@ -103,9 +103,9 @@ class SeaPickle extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ //TODO: bonemeal logic (requires coral) - return parent::onInteract($item, $face, $clickVector, $player); + return parent::onInteract($item, $face, $clickVector, $player, $returnedItems); } public function getDropsForCompatibleTool(Item $item) : array{ diff --git a/src/block/ShulkerBox.php b/src/block/ShulkerBox.php index f37a8a73fb..1cc994ef24 100644 --- a/src/block/ShulkerBox.php +++ b/src/block/ShulkerBox.php @@ -97,7 +97,7 @@ class ShulkerBox extends Opaque{ return $result; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player instanceof Player){ $shulker = $this->position->getWorld()->getTile($this->position); diff --git a/src/block/SmithingTable.php b/src/block/SmithingTable.php index 5c4976cae5..741e9c02fd 100644 --- a/src/block/SmithingTable.php +++ b/src/block/SmithingTable.php @@ -30,7 +30,7 @@ use pocketmine\player\Player; final class SmithingTable extends Opaque{ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new SmithingTableInventory($this->position)); } diff --git a/src/block/Stonecutter.php b/src/block/Stonecutter.php index f31e95d2ce..7736381e43 100644 --- a/src/block/Stonecutter.php +++ b/src/block/Stonecutter.php @@ -37,7 +37,7 @@ class Stonecutter extends Transparent{ use FacesOppositePlacingPlayerTrait; use HorizontalFacingTrait; - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($player !== null){ $player->setCurrentWindow(new StonecutterInventory($this->position)); } diff --git a/src/block/Sugarcane.php b/src/block/Sugarcane.php index 6985cd4871..e3c8e80cc9 100644 --- a/src/block/Sugarcane.php +++ b/src/block/Sugarcane.php @@ -83,7 +83,7 @@ class Sugarcane extends Flowable{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof Fertilizer){ if(!$this->getSide(Facing::DOWN)->isSameType($this) && $this->grow()){ $item->pop(); diff --git a/src/block/SweetBerryBush.php b/src/block/SweetBerryBush.php index 1fa1e180c3..487dd81b17 100644 --- a/src/block/SweetBerryBush.php +++ b/src/block/SweetBerryBush.php @@ -88,7 +88,7 @@ class SweetBerryBush extends Flowable{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->age < self::STAGE_MATURE && $item instanceof Fertilizer){ $block = clone $this; $block->age++; diff --git a/src/block/TNT.php b/src/block/TNT.php index 1a749bdd25..840d74794e 100644 --- a/src/block/TNT.php +++ b/src/block/TNT.php @@ -81,15 +81,15 @@ class TNT extends Opaque{ return $this; } - public function onBreak(Item $item, ?Player $player = null) : bool{ + public function onBreak(Item $item, ?Player $player = null, array &$returnedItems = []) : bool{ if($this->unstable){ $this->ignite(); return true; } - return parent::onBreak($item, $player); + return parent::onBreak($item, $player, $returnedItems); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item instanceof FlintSteel || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){ if($item instanceof Durable){ $item->applyDamage(1); diff --git a/src/block/Trapdoor.php b/src/block/Trapdoor.php index 74306d35ea..a0288d3598 100644 --- a/src/block/Trapdoor.php +++ b/src/block/Trapdoor.php @@ -93,7 +93,7 @@ class Trapdoor extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ $this->open = !$this->open; $this->position->getWorld()->setBlock($this->position, $this); $this->position->getWorld()->addSound($this->position, new DoorSound()); diff --git a/src/block/Wood.php b/src/block/Wood.php index 276c4b6798..401ca5bd71 100644 --- a/src/block/Wood.php +++ b/src/block/Wood.php @@ -69,7 +69,7 @@ class Wood extends Opaque{ return $this->woodType->isFlammable() ? 5 : 0; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->stripped && $item instanceof Axe){ $item->applyDamage(1); $this->stripped = true; diff --git a/src/block/utils/CandleTrait.php b/src/block/utils/CandleTrait.php index d838299cce..154ccb8287 100644 --- a/src/block/utils/CandleTrait.php +++ b/src/block/utils/CandleTrait.php @@ -63,7 +63,7 @@ trait CandleTrait{ } /** @see Block::onInteract() */ - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if($item->getTypeId() === ItemTypeIds::FLINT_AND_STEEL || $item->hasEnchantment(VanillaEnchantments::FIRE_ASPECT())){ if($this->lit){ return true; diff --git a/src/block/utils/CopperTrait.php b/src/block/utils/CopperTrait.php index e30afe69f0..c1730cb262 100644 --- a/src/block/utils/CopperTrait.php +++ b/src/block/utils/CopperTrait.php @@ -68,7 +68,7 @@ trait CopperTrait{ return $this; } - public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ if(!$this->waxed && $item->getTypeId() === ItemTypeIds::HONEYCOMB){ $this->waxed = true; $this->position->getWorld()->setBlock($this->position, $this); diff --git a/src/item/Armor.php b/src/item/Armor.php index 4574165d2e..6fb538cd60 100644 --- a/src/item/Armor.php +++ b/src/item/Armor.php @@ -129,16 +129,15 @@ class Armor extends Durable{ return 0; } - public function onClickAir(Player $player, Vector3 $directionVector) : ItemUseResult{ + public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{ $existing = $player->getArmorInventory()->getItem($this->getArmorSlot()); $thisCopy = clone $this; $new = $thisCopy->pop(); $player->getArmorInventory()->setItem($this->getArmorSlot(), $new); - if($thisCopy->getCount() === 0){ - $player->getInventory()->setItemInHand($existing); - }else{ //if the stack size was bigger than 1 (usually won't happen, but might be caused by plugins - $player->getInventory()->setItemInHand($thisCopy); - $player->getInventory()->addItem($existing); + $player->getInventory()->setItemInHand($existing); + if(!$thisCopy->isNull()){ + //if the stack size was bigger than 1 (usually won't happen, but might be caused by plugins) + $returnedItems[] = $thisCopy; } return ItemUseResult::SUCCESS(); } diff --git a/src/item/Axe.php b/src/item/Axe.php index 40b8b67362..57b378311d 100644 --- a/src/item/Axe.php +++ b/src/item/Axe.php @@ -41,14 +41,14 @@ class Axe extends TieredTool{ return $this->tier->getBaseAttackPoints() - 1; } - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ if(!$block->getBreakInfo()->breaksInstantly()){ return $this->applyDamage(1); } return false; } - public function onAttackEntity(Entity $victim) : bool{ + public function onAttackEntity(Entity $victim, array &$returnedItems) : bool{ return $this->applyDamage(2); } } diff --git a/src/item/Bow.php b/src/item/Bow.php index 460b844c65..3c1320f037 100644 --- a/src/item/Bow.php +++ b/src/item/Bow.php @@ -44,7 +44,7 @@ class Bow extends Tool implements Releasable{ return 385; } - public function onReleaseUsing(Player $player) : ItemUseResult{ + public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseResult{ $arrow = VanillaItems::ARROW(); $inventory = match(true){ $player->getOffHandInventory()->contains($arrow) => $player->getOffHandInventory(), diff --git a/src/item/Bucket.php b/src/item/Bucket.php index accfa976b0..b788a30226 100644 --- a/src/item/Bucket.php +++ b/src/item/Bucket.php @@ -37,7 +37,7 @@ class Bucket extends Item{ return 16; } - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ //TODO: move this to generic placement logic if($blockClicked instanceof Liquid && $blockClicked->isSource()){ $stack = clone $this; @@ -57,16 +57,9 @@ class Bucket extends Item{ if(!$ev->isCancelled()){ $player->getWorld()->setBlock($blockClicked->getPosition(), VanillaBlocks::AIR()); $player->getWorld()->addSound($blockClicked->getPosition()->add(0.5, 0.5, 0.5), $blockClicked->getBucketFillSound()); - if($player->hasFiniteResources()){ - if($stack->getCount() === 0){ - $player->getInventory()->setItemInHand($ev->getItem()); - }else{ - $player->getInventory()->setItemInHand($stack); - $player->getInventory()->addItem($ev->getItem()); - } - }else{ - $player->getInventory()->addItem($ev->getItem()); - } + + $this->pop(); + $returnedItems[] = $ev->getItem(); return ItemUseResult::SUCCESS(); } diff --git a/src/item/FlintSteel.php b/src/item/FlintSteel.php index 567f9d0f59..0ff2e754cd 100644 --- a/src/item/FlintSteel.php +++ b/src/item/FlintSteel.php @@ -32,7 +32,7 @@ use pocketmine\world\sound\FlintSteelSound; class FlintSteel extends Tool{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if($blockReplace->getTypeId() === BlockTypeIds::AIR){ $world = $player->getWorld(); $world->setBlock($blockReplace->getPosition(), VanillaBlocks::FIRE()); diff --git a/src/item/Hoe.php b/src/item/Hoe.php index 8fd1bbad63..1f2c2efaed 100644 --- a/src/item/Hoe.php +++ b/src/item/Hoe.php @@ -33,11 +33,11 @@ class Hoe extends TieredTool{ return BlockToolType::HOE; } - public function onAttackEntity(Entity $victim) : bool{ + public function onAttackEntity(Entity $victim, array &$returnedItems) : bool{ return $this->applyDamage(1); } - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ if(!$block->getBreakInfo()->breaksInstantly()){ return $this->applyDamage(1); } diff --git a/src/item/Item.php b/src/item/Item.php index 4f0b814bfa..7170f4fabb 100644 --- a/src/item/Item.php +++ b/src/item/Item.php @@ -513,38 +513,48 @@ class Item implements \JsonSerializable{ /** * Called when a player uses this item on a block. + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ return ItemUseResult::NONE(); } /** * Called when a player uses the item on air, for example throwing a projectile. * Returns whether the item was changed, for example count decrease or durability change. + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onClickAir(Player $player, Vector3 $directionVector) : ItemUseResult{ + public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{ return ItemUseResult::NONE(); } /** * Called when a player is using this item and releases it. Used to handle bow shoot actions. * Returns whether the item was changed, for example count decrease or durability change. + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onReleaseUsing(Player $player) : ItemUseResult{ + public function onReleaseUsing(Player $player, array &$returnedItems) : ItemUseResult{ return ItemUseResult::NONE(); } /** * Called when this item is used to destroy a block. Usually used to update durability. + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ return false; } /** * Called when this item is used to attack an entity. Usually used to update durability. + * + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function onAttackEntity(Entity $victim) : bool{ + public function onAttackEntity(Entity $victim, array &$returnedItems) : bool{ return false; } diff --git a/src/item/LiquidBucket.php b/src/item/LiquidBucket.php index 8e6d41716b..740f79b116 100644 --- a/src/item/LiquidBucket.php +++ b/src/item/LiquidBucket.php @@ -54,7 +54,7 @@ class LiquidBucket extends Item{ return VanillaItems::BUCKET(); } - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if(!$blockReplace->canBeReplaced()){ return ItemUseResult::NONE(); } @@ -68,9 +68,8 @@ class LiquidBucket extends Item{ $player->getWorld()->setBlock($blockReplace->getPosition(), $resultBlock->getFlowingForm()); $player->getWorld()->addSound($blockReplace->getPosition()->add(0.5, 0.5, 0.5), $resultBlock->getBucketEmptySound()); - if($player->hasFiniteResources()){ - $player->getInventory()->setItemInHand($ev->getItem()); - } + $this->pop(); + $returnedItems[] = $ev->getItem(); return ItemUseResult::SUCCESS(); } diff --git a/src/item/PaintingItem.php b/src/item/PaintingItem.php index 1c649c8e06..f3821d0b26 100644 --- a/src/item/PaintingItem.php +++ b/src/item/PaintingItem.php @@ -37,7 +37,7 @@ use function count; class PaintingItem extends Item{ - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ if(Facing::axis($face) === Axis::Y){ return ItemUseResult::NONE(); } diff --git a/src/item/Pickaxe.php b/src/item/Pickaxe.php index 9796785072..0c8fa01642 100644 --- a/src/item/Pickaxe.php +++ b/src/item/Pickaxe.php @@ -41,14 +41,14 @@ class Pickaxe extends TieredTool{ return $this->tier->getBaseAttackPoints() - 2; } - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ if(!$block->getBreakInfo()->breaksInstantly()){ return $this->applyDamage(1); } return false; } - public function onAttackEntity(Entity $victim) : bool{ + public function onAttackEntity(Entity $victim, array &$returnedItems) : bool{ return $this->applyDamage(2); } } diff --git a/src/item/ProjectileItem.php b/src/item/ProjectileItem.php index e36eed21ab..8056af505a 100644 --- a/src/item/ProjectileItem.php +++ b/src/item/ProjectileItem.php @@ -36,7 +36,7 @@ abstract class ProjectileItem extends Item{ abstract protected function createEntity(Location $location, Player $thrower) : Throwable; - public function onClickAir(Player $player, Vector3 $directionVector) : ItemUseResult{ + public function onClickAir(Player $player, Vector3 $directionVector, array &$returnedItems) : ItemUseResult{ $location = $player->getLocation(); $projectile = $this->createEntity(Location::fromObject($player->getEyePos(), $player->getWorld(), $location->yaw, $location->pitch), $player); diff --git a/src/item/Shears.php b/src/item/Shears.php index a806e649f3..9c70d6cb2c 100644 --- a/src/item/Shears.php +++ b/src/item/Shears.php @@ -44,7 +44,7 @@ class Shears extends Tool{ return 15; } - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ return $this->applyDamage(1); } } diff --git a/src/item/Shovel.php b/src/item/Shovel.php index dde12f4114..d3d0ae4298 100644 --- a/src/item/Shovel.php +++ b/src/item/Shovel.php @@ -41,14 +41,14 @@ class Shovel extends TieredTool{ return $this->tier->getBaseAttackPoints() - 3; } - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ if(!$block->getBreakInfo()->breaksInstantly()){ return $this->applyDamage(1); } return false; } - public function onAttackEntity(Entity $victim) : bool{ + public function onAttackEntity(Entity $victim, array &$returnedItems) : bool{ return $this->applyDamage(2); } } diff --git a/src/item/SpawnEgg.php b/src/item/SpawnEgg.php index 84e8e2cf5d..1147a951dd 100644 --- a/src/item/SpawnEgg.php +++ b/src/item/SpawnEgg.php @@ -34,7 +34,7 @@ abstract class SpawnEgg extends Item{ abstract protected function createEntity(World $world, Vector3 $pos, float $yaw, float $pitch) : Entity; - public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : ItemUseResult{ + public function onInteractBlock(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, array &$returnedItems) : ItemUseResult{ $entity = $this->createEntity($player->getWorld(), $blockReplace->getPosition()->add(0.5, 0, 0.5), lcg_value() * 360, 0); if($this->hasCustomName()){ diff --git a/src/item/Sword.php b/src/item/Sword.php index aee1e997e4..5576f3c125 100644 --- a/src/item/Sword.php +++ b/src/item/Sword.php @@ -49,14 +49,14 @@ class Sword extends TieredTool{ return 10; } - public function onDestroyBlock(Block $block) : bool{ + public function onDestroyBlock(Block $block, array &$returnedItems) : bool{ if(!$block->getBreakInfo()->breaksInstantly()){ return $this->applyDamage(2); } return false; } - public function onAttackEntity(Entity $victim) : bool{ + public function onAttackEntity(Entity $victim, array &$returnedItems) : bool{ return $this->applyDamage(1); } } diff --git a/src/player/Player.php b/src/player/Player.php index aa391ccccd..ddea775a23 100644 --- a/src/player/Player.php +++ b/src/player/Player.php @@ -54,6 +54,7 @@ use pocketmine\event\player\PlayerChatEvent; use pocketmine\event\player\PlayerCommandPreprocessEvent; use pocketmine\event\player\PlayerDeathEvent; use pocketmine\event\player\PlayerDisplayNameChangeEvent; +use pocketmine\event\player\PlayerDropItemEvent; use pocketmine\event\player\PlayerEmoteEvent; use pocketmine\event\player\PlayerEntityInteractEvent; use pocketmine\event\player\PlayerExhaustEvent; @@ -1443,6 +1444,39 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return true; } + /** + * @param Item[] $extraReturnedItems + */ + private function returnItemsFromAction(Item $oldHeldItem, Item $newHeldItem, array $extraReturnedItems) : void{ + $heldItemChanged = false; + if($this->hasFiniteResources()){ + if(!$newHeldItem->equalsExact($oldHeldItem) && $oldHeldItem->equalsExact($this->inventory->getItemInHand())){ + if($newHeldItem instanceof Durable && $newHeldItem->isBroken()){ + $this->broadcastSound(new ItemBreakSound()); + } + $this->inventory->setItemInHand($newHeldItem); + $heldItemChanged = true; + } + }else{ + $newHeldItem = $oldHeldItem; + } + + if($heldItemChanged && count($extraReturnedItems) > 0 && $newHeldItem->isNull()){ + $this->inventory->setItemInHand(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 :( + $ev = new PlayerDropItemEvent($this, $drop); + if($this->isSpectator()){ + $ev->cancel(); + } + $ev->call(); + if(!$ev->isCancelled()){ + $this->dropItem($drop); + } + } + } + /** * Activates the item in hand, for example throwing a projectile. * @@ -1464,18 +1498,14 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ return false; } - $result = $item->onClickAir($this, $directionVector); + $returnedItems = []; + $result = $item->onClickAir($this, $directionVector, $returnedItems); if($result->equals(ItemUseResult::FAIL())){ return false; } $this->resetItemCooldown($item); - if($this->hasFiniteResources() && !$item->equalsExact($oldItem) && $oldItem->equalsExact($this->inventory->getItemInHand())){ - if($item instanceof Durable && $item->isBroken()){ - $this->broadcastSound(new ItemBreakSound()); - } - $this->inventory->setItemInHand($item); - } + $this->returnItemsFromAction($oldItem, $item, $returnedItems); $this->setUsingItem($item instanceof Releasable && $item->canStartUsingItem($this)); @@ -1505,11 +1535,8 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->setUsingItem(false); $this->resetItemCooldown($slot); - if($this->hasFiniteResources() && $oldItem->equalsExact($this->inventory->getItemInHand())){ - $slot->pop(); - $this->inventory->setItemInHand($slot); - $this->inventory->addItem($slot->getResidue()); - } + $slot->pop(); + $this->returnItemsFromAction($oldItem, $slot, [$slot->getResidue()]); return true; } @@ -1531,15 +1558,11 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $oldItem = clone $item; - $result = $item->onReleaseUsing($this); + $returnedItems = []; + $result = $item->onReleaseUsing($this, $returnedItems); if($result->equals(ItemUseResult::SUCCESS())){ $this->resetItemCooldown($item); - if(!$item->equalsExact($oldItem) && $oldItem->equalsExact($this->inventory->getItemInHand())){ - if($item instanceof Durable && $item->isBroken()){ - $this->broadcastSound(new ItemBreakSound()); - } - $this->inventory->setItemInHand($item); - } + $this->returnItemsFromAction($oldItem, $item, $returnedItems); return true; } @@ -1652,13 +1675,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->stopBreakBlock($pos); $item = $this->inventory->getItemInHand(); $oldItem = clone $item; - if($this->getWorld()->useBreakOn($pos, $item, $this, true)){ - if($this->hasFiniteResources() && !$item->equalsExact($oldItem) && $oldItem->equalsExact($this->inventory->getItemInHand())){ - if($item instanceof Durable && $item->isBroken()){ - $this->broadcastSound(new ItemBreakSound()); - } - $this->inventory->setItemInHand($item); - } + $returnedItems = []; + if($this->getWorld()->useBreakOn($pos, $item, $this, true, $returnedItems)){ + $this->returnItemsFromAction($oldItem, $item, $returnedItems); $this->hungerManager->exhaust(0.005, PlayerExhaustEvent::CAUSE_MINING); return true; } @@ -1681,13 +1700,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ $this->broadcastAnimation(new ArmSwingAnimation($this), $this->getViewers()); $item = $this->inventory->getItemInHand(); //this is a copy of the real item $oldItem = clone $item; - if($this->getWorld()->useItemOn($pos, $item, $face, $clickOffset, $this, true)){ - if($this->hasFiniteResources() && !$item->equalsExact($oldItem) && $oldItem->equalsExact($this->inventory->getItemInHand())){ - if($item instanceof Durable && $item->isBroken()){ - $this->broadcastSound(new ItemBreakSound()); - } - $this->inventory->setItemInHand($item); - } + $returnedItems = []; + if($this->getWorld()->useItemOn($pos, $item, $face, $clickOffset, $this, true, $returnedItems)){ + $this->returnItemsFromAction($oldItem, $item, $returnedItems); return true; } }else{ @@ -1762,12 +1777,9 @@ class Player extends Human implements CommandSender, ChunkListener, IPlayer{ if($this->isAlive()){ //reactive damage like thorns might cause us to be killed by attacking another mob, which //would mean we'd already have dropped the inventory by the time we reached here - if($heldItem->onAttackEntity($entity) && $this->hasFiniteResources() && $oldItem->equalsExact($this->inventory->getItemInHand())){ //always fire the hook, even if we are survival - if($heldItem instanceof Durable && $heldItem->isBroken()){ - $this->broadcastSound(new ItemBreakSound()); - } - $this->inventory->setItemInHand($heldItem); - } + $returnedItems = []; + $heldItem->onAttackEntity($entity, $returnedItems); + $this->returnItemsFromAction($oldItem, $heldItem, $returnedItems); $this->hungerManager->exhaust(0.1, PlayerExhaustEvent::CAUSE_ATTACK); } diff --git a/src/world/World.php b/src/world/World.php index fab04e87d0..3f905e9622 100644 --- a/src/world/World.php +++ b/src/world/World.php @@ -1660,9 +1660,10 @@ class World implements ChunkManager{ * Tries to break a block using a item, including Player time checks if available * It'll try to lower the durability if Item is a tool, and set it to Air if broken. * - * @param Item $item reference parameter (if null, can break anything) + * @param Item &$item reference parameter (if null, can break anything) + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped, if the inventory is full) */ - public function useBreakOn(Vector3 $vector, Item &$item = null, ?Player $player = null, bool $createParticles = false) : bool{ + public function useBreakOn(Vector3 $vector, Item &$item = null, ?Player $player = null, bool $createParticles = false, array &$returnedItems = []) : bool{ $vector = $vector->floor(); $chunkX = $vector->getFloorX() >> Chunk::COORD_BIT_SIZE; @@ -1724,10 +1725,10 @@ class World implements ChunkManager{ } foreach($affectedBlocks as $t){ - $this->destroyBlockInternal($t, $item, $player, $createParticles); + $this->destroyBlockInternal($t, $item, $player, $createParticles, $returnedItems); } - $item->onDestroyBlock($target); + $item->onDestroyBlock($target, $returnedItems); if(count($drops) > 0){ $dropPos = $vector->add(0.5, 0.5, 0.5); @@ -1745,12 +1746,15 @@ class World implements ChunkManager{ return true; } - private function destroyBlockInternal(Block $target, Item $item, ?Player $player = null, bool $createParticles = false) : void{ + /** + * @param Item[] &$returnedItems + */ + private function destroyBlockInternal(Block $target, Item $item, ?Player $player, bool $createParticles, array &$returnedItems) : void{ if($createParticles){ $this->addParticle($target->getPosition()->add(0.5, 0.5, 0.5), new BlockBreakParticle($target)); } - $target->onBreak($item, $player); + $target->onBreak($item, $player, $returnedItems); $tile = $this->getTile($target->getPosition()); if($tile !== null){ @@ -1761,10 +1765,11 @@ class World implements ChunkManager{ /** * Uses a item on a position and face, placing it or activating the block * - * @param Player|null $player default null - * @param bool $playSound Whether to play a block-place sound if the block was placed successfully. + * @param Player|null $player default null + * @param bool $playSound Whether to play a block-place sound if the block was placed successfully. + * @param Item[] &$returnedItems Items to be added to the target's inventory (or dropped if the inventory is full) */ - public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false) : bool{ + public function useItemOn(Vector3 $vector, Item &$item, int $face, ?Vector3 $clickVector = null, ?Player $player = null, bool $playSound = false, array &$returnedItems = []) : bool{ $blockClicked = $this->getBlock($vector); $blockReplace = $blockClicked->getSide($face); @@ -1794,18 +1799,18 @@ class World implements ChunkManager{ $ev->call(); if(!$ev->isCancelled()){ - if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player)){ + if((!$player->isSneaking() || $item->isNull()) && $blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ return true; } - $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector); + $result = $item->onInteractBlock($player, $blockReplace, $blockClicked, $face, $clickVector, $returnedItems); if(!$result->equals(ItemUseResult::NONE())){ return $result->equals(ItemUseResult::SUCCESS()); } }else{ return false; } - }elseif($blockClicked->onInteract($item, $face, $clickVector, $player)){ + }elseif($blockClicked->onInteract($item, $face, $clickVector, $player, $returnedItems)){ return true; }