mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 08:44:01 +00:00
Consumables refactor (#1796)
* Removed broken EntityEatEvents - these don't fit the pattern since they only apply to Human entities anyway. PlayerItemConsumeEvent and PlayerInteractEvent can be used for cancellation purposes, and plugins can do custom stuff without mess. * Restrict item consuming to Living entities only * Added FoodSource->requiresHunger() * Only items implementing the Consumable interface can now be consumed. * The effects from consuming items are now generic-ized by way of the Living->consume() function. This is overridden in Human to allow applying food and hunger. * Fixed the hardcoded mess for buckets
This commit is contained in:
parent
329fe7d844
commit
6e1df36188
@ -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);
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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();
|
||||
}
|
||||
|
@ -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.
|
||||
|
@ -1,54 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\FoodSource;
|
||||
|
||||
class EntityEatBlockEvent extends EntityEatEvent{
|
||||
public function __construct(Entity $entity, FoodSource $foodSource){
|
||||
if(!($foodSource instanceof Block)){
|
||||
throw new \InvalidArgumentException("Food source must be a block");
|
||||
}
|
||||
parent::__construct($entity, $foodSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Block
|
||||
*/
|
||||
public function getResidue(){
|
||||
return parent::getResidue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Block $residue
|
||||
*/
|
||||
public function setResidue($residue){
|
||||
if(!($residue instanceof Block)){
|
||||
throw new \InvalidArgumentException("Eating a Block can only result in a Block residue");
|
||||
}
|
||||
parent::setResidue($residue);
|
||||
}
|
||||
}
|
@ -1,109 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
use pocketmine\item\FoodSource;
|
||||
|
||||
class EntityEatEvent extends EntityEvent implements Cancellable{
|
||||
public static $handlerList = null;
|
||||
|
||||
/** @var FoodSource */
|
||||
private $foodSource;
|
||||
/** @var int */
|
||||
private $foodRestore;
|
||||
/** @var float */
|
||||
private $saturationRestore;
|
||||
/** @var mixed */
|
||||
private $residue;
|
||||
/** @var Effect[] */
|
||||
private $additionalEffects;
|
||||
|
||||
public function __construct(Entity $entity, FoodSource $foodSource){
|
||||
$this->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;
|
||||
}
|
||||
}
|
@ -1,51 +0,0 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\item\Food;
|
||||
use pocketmine\item\Item;
|
||||
|
||||
class EntityEatItemEvent extends EntityEatEvent{
|
||||
public function __construct(Entity $entity, Food $foodSource){
|
||||
parent::__construct($entity, $foodSource);
|
||||
}
|
||||
|
||||
/**
|
||||
* @return Item
|
||||
*/
|
||||
public function getResidue(){
|
||||
return parent::getResidue();
|
||||
}
|
||||
|
||||
/**
|
||||
* @param Item $residue
|
||||
*/
|
||||
public function setResidue($residue){
|
||||
if(!($residue instanceof Item)){
|
||||
throw new \InvalidArgumentException("Eating an Item can only result in an Item residue");
|
||||
}
|
||||
parent::setResidue($residue);
|
||||
}
|
||||
}
|
@ -27,13 +27,14 @@ use pocketmine\block\Air;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\block\BlockFactory;
|
||||
use pocketmine\block\Liquid;
|
||||
use pocketmine\entity\Living;
|
||||
use pocketmine\event\player\PlayerBucketEmptyEvent;
|
||||
use pocketmine\event\player\PlayerBucketFillEvent;
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\Player;
|
||||
|
||||
class Bucket extends Item{
|
||||
class Bucket extends Item implements Consumable{
|
||||
public function __construct(int $meta = 0){
|
||||
parent::__construct(self::BUCKET, $meta, "Bucket");
|
||||
}
|
||||
@ -85,4 +86,20 @@ class Bucket extends Item{
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function getResidue(){
|
||||
return ItemFactory::get(Item::BUCKET, 0, 1);
|
||||
}
|
||||
|
||||
public function getAdditionalEffects() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function canBeConsumed() : bool{
|
||||
return $this->meta === 1; //Milk
|
||||
}
|
||||
|
||||
public function onConsume(Living $consumer){
|
||||
$consumer->removeAllEffects();
|
||||
}
|
||||
}
|
||||
|
@ -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);
|
||||
}
|
||||
|
@ -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());
|
||||
}
|
||||
}
|
||||
|
@ -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;
|
||||
}
|
||||
|
@ -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{
|
||||
|
@ -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
|
||||
|
@ -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
|
||||
}
|
||||
}
|
||||
|
Loading…
x
Reference in New Issue
Block a user