diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 422a6467a..17a750799 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -299,6 +299,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ */ protected $lastPingMeasure = 1; + /** @var int[] ID => ticks map */ + protected $usedItemsCooldown = []; + /** * @return TranslationContainer|string */ @@ -860,6 +863,39 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return $this->startAction === -1 ? -1 : ($this->server->getTick() - $this->startAction); } + /** + * Returns whether the player has a cooldown period left before it can use the given item again. + * + * @param Item $item + * + * @return bool + */ + public function hasItemCooldown(Item $item) : bool{ + $this->checkItemCooldowns(); + return isset($this->usedItemsCooldown[$item->getId()]); + } + + /** + * Resets the player's cooldown time for the given item back to the maximum. + * + * @param Item $item + */ + public function resetItemCooldown(Item $item) : void{ + $ticks = $item->getCooldownTicks(); + if($ticks > 0){ + $this->usedItemsCooldown[$item->getId()] = $this->server->getTick() + $ticks; + } + } + + protected function checkItemCooldowns() : void{ + $serverTick = $this->server->getTick(); + foreach($this->usedItemsCooldown as $itemId => $cooldownUntil){ + if($cooldownUntil <= $serverTick){ + unset($this->usedItemsCooldown[$itemId]); + } + } + } + protected function switchLevel(Level $targetLevel) : bool{ $oldLevel = $this->level; if(parent::switchLevel($targetLevel)){ @@ -2384,6 +2420,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ } $ev = new PlayerInteractEvent($this, $item, null, $directionVector, $face, PlayerInteractEvent::RIGHT_CLICK_AIR); + if($this->hasItemCooldown($item)){ + $ev->setCancelled(); + } $this->server->getPluginManager()->callEvent($ev); @@ -2392,8 +2431,11 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } - if($item->onClickAir($this, $directionVector) and $this->isSurvival()){ - $this->inventory->setItemInHand($item); + if($item->onClickAir($this, $directionVector)){ + $this->resetItemCooldown($item); + if($this->isSurvival()){ + $this->inventory->setItemInHand($item); + } } $this->setUsingItem(true); @@ -2486,7 +2528,12 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ case InventoryTransactionPacket::RELEASE_ITEM_ACTION_RELEASE: if($this->isUsingItem()){ $item = $this->inventory->getItemInHand(); + if($this->hasItemCooldown($item)){ + $this->inventory->sendContents($this); + return false; + } if($item->onReleaseUsing($this)){ + $this->resetItemCooldown($item); $this->inventory->setItemInHand($item); } }else{ @@ -2499,6 +2546,9 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ if($slot instanceof Consumable){ $ev = new PlayerItemConsumeEvent($this, $slot); + if($this->hasItemCooldown($slot)){ + $ev->setCancelled(); + } $this->server->getPluginManager()->callEvent($ev); if($ev->isCancelled() or !$this->consumeObject($slot)){ @@ -2506,6 +2556,8 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return true; } + $this->resetItemCooldown($slot); + if($this->isSurvival()){ $slot->pop(); $this->inventory->setItemInHand($slot); diff --git a/src/pocketmine/item/ChorusFruit.php b/src/pocketmine/item/ChorusFruit.php index 9721bff29..d2a616a14 100644 --- a/src/pocketmine/item/ChorusFruit.php +++ b/src/pocketmine/item/ChorusFruit.php @@ -84,4 +84,8 @@ class ChorusFruit extends Food{ break; } } + + public function getCooldownTicks() : int{ + return 20; + } } diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index d63b41654..597ce86ff 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -821,6 +821,15 @@ class Item implements ItemIds, \JsonSerializable{ return false; } + /** + * Returns the number of ticks a player must wait before activating this item again. + * + * @return int + */ + public function getCooldownTicks() : int{ + return 0; + } + /** * Compares an Item to this Item and check if they match. *