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.
This commit is contained in:
Dylan K. Taylor 2022-07-16 19:50:33 +01:00
parent 4afd3dcabf
commit d0ff6d2e36
No known key found for this signature in database
GPG Key ID: 8927471A91CAFD3D
69 changed files with 177 additions and 155 deletions

View File

@ -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));
}

View File

@ -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)){

View File

@ -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();

View File

@ -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){

View File

@ -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);
}

View File

@ -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();

View File

@ -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())){

View File

@ -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;
}

View File

@ -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())){

View File

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

View File

@ -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{

View File

@ -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{

View File

@ -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));
}

View File

@ -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;
}

View File

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

View File

@ -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();

View File

@ -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));
}

View File

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

View File

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

View File

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

View File

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

View File

@ -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;
}

View File

@ -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

View File

@ -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()){

View File

@ -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();

View File

@ -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)){

View File

@ -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())){

View File

@ -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;
}

View File

@ -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

View File

@ -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{

View File

@ -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()){

View File

@ -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{

View File

@ -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());

View File

@ -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(

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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;

View File

@ -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

View File

@ -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;
}

View File

@ -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();

View File

@ -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{

View File

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

View File

@ -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));
}

View File

@ -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));
}

View File

@ -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();

View File

@ -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++;

View File

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

View File

@ -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());

View File

@ -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;

View File

@ -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;

View File

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

View File

@ -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();
}

View File

@ -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);
}
}

View File

@ -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(),

View File

@ -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();
}

View File

@ -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());

View File

@ -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);
}

View File

@ -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;
}

View File

@ -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();
}

View File

@ -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();
}

View File

@ -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);
}
}

View File

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

View File

@ -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);
}
}

View File

@ -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);
}
}

View File

@ -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()){

View File

@ -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);
}
}

View File

@ -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);
}

View File

@ -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;
}