From 2601e359907fca98d95b10454256157977282f90 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Wed, 5 Jul 2017 12:40:28 +0100 Subject: [PATCH] Cleaned up and added API for entity air supply, fixed oxygen being used in creative/spectator this commit also includes respiration checks because it's cherry-picked from api3/blocks, but respiration won't work until it's registered. --- src/pocketmine/Player.php | 4 + src/pocketmine/entity/Human.php | 11 +++ src/pocketmine/entity/Living.php | 119 ++++++++++++++++++++------ src/pocketmine/entity/WaterAnimal.php | 11 +++ 4 files changed, 117 insertions(+), 28 deletions(-) diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 65dbd4bc3..fdf1a2204 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1713,6 +1713,10 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{ return 0.0; } + public function canBreathe() : bool{ + return $this->isCreative() or parent::canBreathe(); + } + public function checkNetwork(){ if(!$this->isOnline()){ return; diff --git a/src/pocketmine/entity/Human.php b/src/pocketmine/entity/Human.php index 46f58eb8f..fe0c1ee5a 100644 --- a/src/pocketmine/entity/Human.php +++ b/src/pocketmine/entity/Human.php @@ -28,6 +28,9 @@ use pocketmine\event\entity\EntityRegainHealthEvent; use pocketmine\event\player\PlayerExhaustEvent; 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; @@ -454,6 +457,14 @@ class Human extends Creature implements ProjectileSource, InventoryHolder{ } } + protected function doAirSupplyTick(int $tickDiff){ + //TODO: allow this to apply to other mobs + if(($ench = $this->inventory->getHelmet()->getEnchantment(Enchantment::RESPIRATION)) === null or + lcg_value() <= (1 / ($ench->getLevel() + 1))){ + parent::doAirSupplyTick($tickDiff); + } + } + public function getName() : string{ return $this->getNameTag(); } diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 0c968cd0f..7e06285f8 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -422,7 +422,6 @@ abstract class Living extends Entity implements Damageable{ public function entityBaseTick(int $tickDiff = 1) : bool{ Timings::$timerLivingEntityBaseTick->startTiming(); - $this->setGenericFlag(self::DATA_FLAG_BREATHING, !$this->isInsideOfWater()); $hasUpdate = parent::entityBaseTick($tickDiff); @@ -435,34 +434,14 @@ abstract class Living extends Entity implements Damageable{ $this->attack($ev); } - if(!$this->hasEffect(Effect::WATER_BREATHING) and $this->isInsideOfWater()){ - if($this instanceof WaterAnimal){ - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400); - }else{ - $hasUpdate = true; - $airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff; - if($airTicks <= -20){ - $airTicks = 0; - - $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2); - $this->attack($ev); - } - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); - } - }else{ - if($this instanceof WaterAnimal){ - $hasUpdate = true; - $airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff; - if($airTicks <= -20){ - $airTicks = 0; - - $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2); - $this->attack($ev); - } - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); - }else{ - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 400); + if(!$this->canBreathe()){ + if($this->isBreathing()){ + $this->setBreathing(false); } + $this->doAirSupplyTick($tickDiff); + }elseif(!$this->isBreathing()){ + $this->setBreathing(true); + $this->setAirSupplyTicks($this->getMaxAirSupplyTicks()); } } @@ -495,6 +474,90 @@ abstract class Living extends Entity implements Damageable{ } } + /** + * Ticks the entity's air supply when it cannot breathe. + * @param int $tickDiff + */ + protected function doAirSupplyTick(int $tickDiff){ + $ticks = $this->getAirSupplyTicks() - $tickDiff; + + if($ticks <= -20){ + $this->setAirSupplyTicks(0); + $this->onAirExpired(); + }else{ + $this->setAirSupplyTicks($ticks); + } + } + + /** + * Returns whether the entity can currently breathe. + * @return bool + */ + public function canBreathe() : bool{ + return $this->hasEffect(Effect::WATER_BREATHING) or !$this->isInsideOfWater(); + } + + /** + * Returns whether the entity is currently breathing or not. If this is false, the entity's air supply will be used. + * @return bool + */ + public function isBreathing() : bool{ + return $this->getGenericFlag(self::DATA_FLAG_BREATHING); + } + + /** + * Sets whether the entity is currently breathing. If false, it will cause the entity's air supply to be used. + * For players, this also shows the oxygen bar. + * + * @param bool $value + */ + public function setBreathing(bool $value = true){ + $this->setGenericFlag(self::DATA_FLAG_BREATHING, $value); + } + + /** + * Returns the number of ticks remaining in the entity's air supply. Note that the entity may survive longer than + * this amount of time without damage due to enchantments such as Respiration. + * + * @return int + */ + public function getAirSupplyTicks() : int{ + return $this->getDataProperty(self::DATA_AIR); + } + + /** + * Sets the number of air ticks left in the entity's air supply. + * @param int $ticks + */ + public function setAirSupplyTicks(int $ticks){ + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $ticks); + } + + /** + * Returns the maximum amount of air ticks the entity's air supply can contain. + * @return int + */ + public function getMaxAirSupplyTicks() : int{ + return $this->getDataProperty(self::DATA_MAX_AIR); + } + + /** + * Sets the maximum amount of air ticks the air supply can hold. + * @param int $ticks + */ + public function setMaxAirSupplyTicks(int $ticks){ + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $ticks); + } + + /** + * Called when the entity's air supply ticks reaches -20 or lower. The entity will usually take damage at this point + * and then the supply is reset to 0, so this method will be called roughly every second. + */ + public function onAirExpired(){ + $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2); + $this->attack($ev); + } + /** * @return ItemItem[] */ diff --git a/src/pocketmine/entity/WaterAnimal.php b/src/pocketmine/entity/WaterAnimal.php index eb1452c5a..b811f71da 100644 --- a/src/pocketmine/entity/WaterAnimal.php +++ b/src/pocketmine/entity/WaterAnimal.php @@ -23,9 +23,20 @@ declare(strict_types=1); namespace pocketmine\entity; +use pocketmine\event\entity\EntityDamageEvent; + abstract class WaterAnimal extends Creature implements Ageable{ public function isBaby() : bool{ return $this->getGenericFlag(self::DATA_FLAG_BABY); } + + public function canBreathe() : bool{ + return $this->isInsideOfWater(); + } + + public function onAirExpired(){ + $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_SUFFOCATION, 2); + $this->attack($ev); + } }