diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index c113d27cb..330c741ed 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -78,6 +78,7 @@ use pocketmine\inventory\PlayerInventory; use pocketmine\inventory\transaction\action\InventoryAction; use pocketmine\inventory\transaction\CraftingTransaction; use pocketmine\inventory\transaction\InventoryTransaction; +use pocketmine\item\Consumable; use pocketmine\item\Item; use pocketmine\item\ItemFactory; use pocketmine\item\WritableBook; @@ -2448,52 +2449,40 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ $this->inventory->setItemInHand($item); } }else{ - $this->inventory->sendContents($this); + break; } return true; case InventoryTransactionPacket::RELEASE_ITEM_ACTION_CONSUME: $slot = $this->inventory->getItemInHand(); - if($slot->canBeConsumed()){ + if($slot instanceof Consumable){ $ev = new PlayerItemConsumeEvent($this, $slot); - if(!$slot->canBeConsumedBy($this)){ - $ev->setCancelled(); - } $this->server->getPluginManager()->callEvent($ev); - if(!$ev->isCancelled()){ - $slot->onConsume($this); - }else{ - $this->inventory->sendContents($this); - } - return true; - }elseif($slot->getId() === Item::BUCKET and $slot->getDamage() === 1){ //Milk! - $this->server->getPluginManager()->callEvent($ev = new PlayerItemConsumeEvent($this, $slot)); - if($ev->isCancelled()){ + if($ev->isCancelled() or !$this->consumeObject($slot)){ $this->inventory->sendContents($this); - return true; } if($this->isSurvival()){ - --$slot->count; + $slot->pop(); $this->inventory->setItemInHand($slot); - $this->inventory->addItem(ItemFactory::get(Item::BUCKET, 0, 1)); + $this->inventory->addItem($slot->getResidue()); } - $this->removeAllEffects(); - return true; } - return false; + break; default: break; } }finally{ $this->setUsingItem(false); } + + $this->inventory->sendContents($this); break; default: $this->inventory->sendContents($this); diff --git a/src/pocketmine/block/Cake.php b/src/pocketmine/block/Cake.php index 53bb8e968..8ac27faaf 100644 --- a/src/pocketmine/block/Cake.php +++ b/src/pocketmine/block/Cake.php @@ -24,7 +24,7 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\entity\Effect; -use pocketmine\event\entity\EntityEatBlockEvent; +use pocketmine\entity\Living; use pocketmine\item\FoodSource; use pocketmine\item\Item; use pocketmine\level\Level; @@ -90,20 +90,9 @@ class Cake extends Transparent implements FoodSource{ } public function onActivate(Item $item, Player $player = null) : bool{ - //TODO: refactor this into generic food handling - if($player instanceof Player and $player->getFood() < $player->getMaxFood()){ - $player->getServer()->getPluginManager()->callEvent($ev = new EntityEatBlockEvent($player, $this)); - - if(!$ev->isCancelled()){ - $player->addFood($ev->getFoodRestore()); - $player->addSaturation($ev->getSaturationRestore()); - foreach($ev->getAdditionalEffects() as $effect){ - $player->addEffect($effect); - } - - $this->getLevel()->setBlock($this, $ev->getResidue()); - return true; - } + if($player !== null){ + $player->consumeObject($this); + return true; } return false; @@ -117,6 +106,13 @@ class Cake extends Transparent implements FoodSource{ return 0.4; } + public function requiresHunger() : bool{ + return true; + } + + /** + * @return Block + */ public function getResidue(){ $clone = clone $this; $clone->meta++; @@ -132,4 +128,8 @@ class Cake extends Transparent implements FoodSource{ public function getAdditionalEffects() : array{ return []; } + + public function onConsume(Living $consumer) : void{ + $this->level->setBlock($this, $this->getResidue()); + } } diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 30a1062de..47acefd6d 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -30,7 +30,9 @@ use pocketmine\event\player\PlayerExhaustEvent; use pocketmine\inventory\EnderChestInventory; use pocketmine\inventory\InventoryHolder; use pocketmine\inventory\PlayerInventory; +use pocketmine\item\Consumable; use pocketmine\item\enchantment\Enchantment; +use pocketmine\item\FoodSource; use pocketmine\item\Item as ItemItem; use pocketmine\level\Level; use pocketmine\nbt\NBT; @@ -271,6 +273,19 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ return $ev->getAmount(); } + public function consumeObject(Consumable $consumable) : bool{ + if($consumable instanceof FoodSource){ + if($consumable->requiresHunger() and $this->getFood() >= $this->getMaxFood()){ + return false; + } + + $this->addFood($consumable->getFoodRestore()); + $this->addSaturation($consumable->getSaturationRestore()); + } + + return parent::consumeObject($consumable); + } + public function getXpLevel() : int{ return (int) $this->attributeMap->getAttribute(Attribute::EXPERIENCE_LEVEL)->getValue(); } diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 39610ff2c..30aa735e3 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -32,6 +32,7 @@ use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\Timings; +use pocketmine\item\Consumable; use pocketmine\item\Item as ItemItem; use pocketmine\math\Vector3; use pocketmine\nbt\tag\ByteTag; @@ -322,6 +323,23 @@ abstract class Living extends Entity implements Damageable{ } } + /** + * Causes the mob to consume the given Consumable object, applying applicable effects, health bonuses, food bonuses, + * etc. + * + * @param Consumable $consumable + * + * @return bool + */ + public function consumeObject(Consumable $consumable) : bool{ + foreach($consumable->getAdditionalEffects() as $effect){ + $this->addEffect($effect); + } + + $consumable->onConsume($this); + + return true; + } /** * Returns the initial upwards velocity of a jumping entity in blocks/tick, including additional velocity due to effects. diff --git a/src/pocketmine/event/entity/EntityEatBlockEvent.php b/src/pocketmine/event/entity/EntityEatBlockEvent.php deleted file mode 100644 index 24e1a2357..000000000 --- a/src/pocketmine/event/entity/EntityEatBlockEvent.php +++ /dev/null @@ -1,54 +0,0 @@ -entity = $entity; - $this->foodSource = $foodSource; - $this->foodRestore = $foodSource->getFoodRestore(); - $this->saturationRestore = $foodSource->getSaturationRestore(); - $this->residue = $foodSource->getResidue(); - $this->additionalEffects = $foodSource->getAdditionalEffects(); - } - - public function getFoodSource() : FoodSource{ - return $this->foodSource; - } - - public function getFoodRestore() : int{ - return $this->foodRestore; - } - - public function setFoodRestore(int $foodRestore){ - $this->foodRestore = $foodRestore; - } - - public function getSaturationRestore() : float{ - return $this->saturationRestore; - } - - public function setSaturationRestore(float $saturationRestore){ - $this->saturationRestore = $saturationRestore; - } - - /** - * Returns the result of eating the food source. - * @return mixed - */ - public function getResidue(){ - return $this->residue; - } - - /** - * @param mixed $residue - */ - public function setResidue($residue){ - $this->residue = $residue; - } - - /** - * @return Effect[] - */ - public function getAdditionalEffects() : array{ - return $this->additionalEffects; - } - - /** - * @param Effect[] $additionalEffects - * - * @throws \TypeError - */ - public function setAdditionalEffects(array $additionalEffects){ - foreach($additionalEffects as $effect){ - if(!($effect instanceof Effect)){ - throw new \TypeError("Argument 1 passed to EntityEatEvent::setAdditionalEffects() must be an Effect array"); - } - } - $this->additionalEffects = $additionalEffects; - } -} diff --git a/src/pocketmine/event/entity/EntityEatItemEvent.php b/src/pocketmine/event/entity/EntityEatItemEvent.php deleted file mode 100644 index bbf3f18f8..000000000 --- a/src/pocketmine/event/entity/EntityEatItemEvent.php +++ /dev/null @@ -1,51 +0,0 @@ -meta === 1; //Milk + } + + public function onConsume(Living $consumer){ + $consumer->removeAllEffects(); + } } diff --git a/src/pocketmine/item/Consumable.php b/src/pocketmine/item/Consumable.php index 93e3bfc2f..6dcef3c55 100644 --- a/src/pocketmine/item/Consumable.php +++ b/src/pocketmine/item/Consumable.php @@ -23,14 +23,32 @@ declare(strict_types=1); namespace pocketmine\item; +use pocketmine\block\Block; use pocketmine\entity\Effect; +use pocketmine\entity\Living; +/** + * Interface implemented by objects that can be consumed by mobs. + */ interface Consumable{ + /** + * Returns the leftover that this Consumable produces when it is consumed. For Items, this is usually air, but could + * be an Item to add to a Player's inventory afterwards (such as a bowl). + * + * @return Item|Block|mixed + */ public function getResidue(); /** * @return Effect[] */ public function getAdditionalEffects() : array; + + /** + * Called when this Consumable is consumed by mob, after standard resulting effects have been applied. + * + * @param Living $consumer + */ + public function onConsume(Living $consumer); } diff --git a/src/pocketmine/item/Food.php b/src/pocketmine/item/Food.php index b654e3683..36f4cee4f 100644 --- a/src/pocketmine/item/Food.php +++ b/src/pocketmine/item/Food.php @@ -23,42 +23,25 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\Entity; -use pocketmine\entity\Human; -use pocketmine\event\entity\EntityEatItemEvent; +use pocketmine\entity\Living; abstract class Food extends Item implements FoodSource{ - public function canBeConsumed() : bool{ + public function requiresHunger() : bool{ return true; } - public function canBeConsumedBy(Entity $entity) : bool{ - return $entity instanceof Human and $entity->getFood() < $entity->getMaxFood(); - } - + /** + * @return Item + */ public function getResidue(){ - if($this->getCount() === 1){ - return ItemFactory::get(0); - }else{ - $new = clone $this; - $new->count--; - return $new; - } + return ItemFactory::get(Item::AIR, 0, 0); } public function getAdditionalEffects() : array{ return []; } - public function onConsume(Entity $human){ - $ev = new EntityEatItemEvent($human, $this); + public function onConsume(Living $consumer){ - $human->addSaturation($ev->getSaturationRestore()); - $human->addFood($ev->getFoodRestore()); - foreach($ev->getAdditionalEffects() as $effect){ - $human->addEffect($effect); - } - - $human->getInventory()->setItemInHand($ev->getResidue()); } } diff --git a/src/pocketmine/item/FoodSource.php b/src/pocketmine/item/FoodSource.php index 4c4e521fd..0e5b55335 100644 --- a/src/pocketmine/item/FoodSource.php +++ b/src/pocketmine/item/FoodSource.php @@ -23,9 +23,18 @@ declare(strict_types=1); namespace pocketmine\item; +/** + * Interface implemented by objects that can be consumed by players, giving them food and saturation. + */ interface FoodSource extends Consumable{ public function getFoodRestore() : int; public function getSaturationRestore() : float; + + /** + * Returns whether a Human eating this FoodSource must have a non-full hunger bar. + * @return bool + */ + public function requiresHunger() : bool; } diff --git a/src/pocketmine/item/GoldenApple.php b/src/pocketmine/item/GoldenApple.php index 4c080d39b..15583d3f7 100644 --- a/src/pocketmine/item/GoldenApple.php +++ b/src/pocketmine/item/GoldenApple.php @@ -24,8 +24,6 @@ declare(strict_types=1); namespace pocketmine\item; use pocketmine\entity\Effect; -use pocketmine\entity\Entity; -use pocketmine\entity\Human; class GoldenApple extends Food{ @@ -33,8 +31,8 @@ class GoldenApple extends Food{ parent::__construct(self::GOLDEN_APPLE, $meta, "Golden Apple"); } - public function canBeConsumedBy(Entity $entity) : bool{ - return $entity instanceof Human; + public function requiresHunger() : bool{ + return false; } public function getFoodRestore() : int{ diff --git a/src/pocketmine/item/Item.php b/src/pocketmine/item/Item.php index a54ed33eb..e1b477d2d 100644 --- a/src/pocketmine/item/Item.php +++ b/src/pocketmine/item/Item.php @@ -621,32 +621,6 @@ class Item implements ItemIds, \JsonSerializable{ return $this->block !== null and $this->block->canBePlaced(); } - /** - * Returns whether an entity can eat or drink this item. - * @return bool - */ - public function canBeConsumed() : bool{ - return false; - } - - /** - * Returns whether this item can be consumed by the supplied Entity. - * @param Entity $entity - * - * @return bool - */ - public function canBeConsumedBy(Entity $entity) : bool{ - return $this->canBeConsumed(); - } - - /** - * Called when the item is consumed by an Entity. - * @param Entity $entity - */ - public function onConsume(Entity $entity){ - - } - /** * Returns the block corresponding to this Item. * @return Block diff --git a/src/pocketmine/item/Potion.php b/src/pocketmine/item/Potion.php index cb644015e..2fe23e099 100644 --- a/src/pocketmine/item/Potion.php +++ b/src/pocketmine/item/Potion.php @@ -23,7 +23,7 @@ declare(strict_types=1); namespace pocketmine\item; -use pocketmine\entity\Entity; +use pocketmine\entity\Living; class Potion extends Item{ public function __construct(int $meta = 0){ @@ -38,7 +38,7 @@ class Potion extends Item{ return 1; } - public function onConsume(Entity $entity){ + public function onConsume(Living $consumer){ // TODO: Implement potions } }