mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Separation of effect handling from Entity, fixes #886
This commit is contained in:
parent
c84218c55b
commit
6b5c0af161
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Arrow;
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityCombustByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
@ -63,10 +62,8 @@ class Fire extends Flowable{
|
||||
}
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
|
||||
$ev = new EntityCombustByBlockEvent($this, $entity, 8);
|
||||
if($entity instanceof Arrow){
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityCombustByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
@ -50,10 +49,9 @@ class Lava extends Liquid{
|
||||
|
||||
public function onEntityCollide(Entity $entity){
|
||||
$entity->fallDistance *= 0.5;
|
||||
if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
|
||||
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
|
||||
$entity->attack($ev->getFinalDamage(), $ev);
|
||||
|
||||
$ev = new EntityCombustByBlockEvent($this, $entity, 15);
|
||||
Server::getInstance()->getPluginManager()->callEvent($ev);
|
||||
|
@ -51,17 +51,14 @@ use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\DoubleTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\nbt\tag\ShortTag;
|
||||
use pocketmine\nbt\tag\StringTag;
|
||||
use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
|
||||
use pocketmine\network\mcpe\protocol\SetEntityDataPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\plugin\Plugin;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\Binary;
|
||||
|
||||
abstract class Entity extends Location implements Metadatable{
|
||||
|
||||
@ -225,9 +222,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
*/
|
||||
protected $hasSpawned = [];
|
||||
|
||||
/** @var Effect[] */
|
||||
protected $effects = [];
|
||||
|
||||
protected $id;
|
||||
|
||||
protected $dataProperties = [
|
||||
@ -294,7 +288,8 @@ abstract class Entity extends Location implements Metadatable{
|
||||
protected $stepHeight = 0;
|
||||
public $keepMovement = false;
|
||||
|
||||
public $fallDistance = 0;
|
||||
/** @var float */
|
||||
public $fallDistance = 0.0;
|
||||
public $ticksLived = 0;
|
||||
public $lastUpdate;
|
||||
public $maxFireTicks;
|
||||
@ -480,6 +475,11 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->setDataProperty(self::DATA_SCALE, self::DATA_TYPE_FLOAT, $value);
|
||||
}
|
||||
|
||||
public function getBoundingBox(){
|
||||
return $this->boundingBox;
|
||||
}
|
||||
|
||||
|
||||
public function isSneaking(){
|
||||
return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING);
|
||||
}
|
||||
@ -617,85 +617,59 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @return Effect[]
|
||||
*/
|
||||
public function getEffects(){
|
||||
return $this->effects;
|
||||
public function getEffects() : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
public function removeAllEffects(){
|
||||
foreach($this->effects as $effect){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
public function removeEffect($effectId){
|
||||
if(isset($this->effects[$effectId])){
|
||||
$effect = $this->effects[$effectId];
|
||||
unset($this->effects[$effectId]);
|
||||
$effect->remove($this);
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $effectId
|
||||
*/
|
||||
public function removeEffect(int $effectId){
|
||||
|
||||
$this->recalculateEffectColor();
|
||||
}
|
||||
}
|
||||
|
||||
public function getEffect($effectId){
|
||||
return $this->effects[$effectId] ?? null;
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $effectId
|
||||
*
|
||||
* @return Effect|null
|
||||
*/
|
||||
public function getEffect(int $effectId){
|
||||
return null;
|
||||
}
|
||||
|
||||
public function hasEffect($effectId){
|
||||
return isset($this->effects[$effectId]);
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param int $effectId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasEffect(int $effectId) : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param Effect $effect
|
||||
*/
|
||||
public function addEffect(Effect $effect){
|
||||
if(isset($this->effects[$effect->getId()])){
|
||||
$oldEffect = $this->effects[$effect->getId()];
|
||||
if(
|
||||
abs($effect->getAmplifier()) < $oldEffect->getAmplifier()
|
||||
or (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier())
|
||||
and $effect->getDuration() < $oldEffect->getDuration())
|
||||
){
|
||||
return;
|
||||
}
|
||||
$effect->add($this, true, $oldEffect);
|
||||
}else{
|
||||
$effect->add($this, false);
|
||||
}
|
||||
|
||||
$this->effects[$effect->getId()] = $effect;
|
||||
|
||||
$this->recalculateEffectColor();
|
||||
}
|
||||
|
||||
protected function recalculateEffectColor(){
|
||||
//TODO: add transparency values
|
||||
$color = [0, 0, 0]; //RGB
|
||||
$count = 0;
|
||||
$ambient = true;
|
||||
foreach($this->effects as $effect){
|
||||
if($effect->isVisible() and $effect->hasBubbles()){
|
||||
$c = $effect->getColor();
|
||||
$color[0] += $c[0] * $effect->getEffectLevel();
|
||||
$color[1] += $c[1] * $effect->getEffectLevel();
|
||||
$color[2] += $c[2] * $effect->getEffectLevel();
|
||||
$count += $effect->getEffectLevel();
|
||||
if(!$effect->isAmbient()){
|
||||
$ambient = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($count > 0){
|
||||
$r = ($color[0] / $count) & 0xff;
|
||||
$g = ($color[1] / $count) & 0xff;
|
||||
$b = ($color[2] / $count) & 0xff;
|
||||
|
||||
$this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0xff000000 | ($r << 16) | ($g << 8) | $b);
|
||||
$this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0);
|
||||
}else{
|
||||
$this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0);
|
||||
$this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, 0);
|
||||
}
|
||||
throw new \BadMethodCallException("Cannot add effects to non-living entities");
|
||||
}
|
||||
|
||||
/**
|
||||
@ -775,23 +749,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->namedtag->Air = new ShortTag("Air", $this->getDataProperty(self::DATA_AIR));
|
||||
$this->namedtag->OnGround = new ByteTag("OnGround", $this->onGround ? 1 : 0);
|
||||
$this->namedtag->Invulnerable = new ByteTag("Invulnerable", $this->invulnerable ? 1 : 0);
|
||||
|
||||
if(count($this->effects) > 0){
|
||||
$effects = [];
|
||||
foreach($this->effects as $effect){
|
||||
$effects[] = new CompoundTag("", [
|
||||
new ByteTag("Id", $effect->getId()),
|
||||
new ByteTag("Amplifier", Binary::signByte($effect->getAmplifier())),
|
||||
new IntTag("Duration", $effect->getDuration()),
|
||||
new ByteTag("Ambient", 0),
|
||||
new ByteTag("ShowParticles", $effect->isVisible() ? 1 : 0)
|
||||
]);
|
||||
}
|
||||
|
||||
$this->namedtag->ActiveEffects = new ListTag("ActiveEffects", $effects);
|
||||
}else{
|
||||
unset($this->namedtag->ActiveEffects);
|
||||
}
|
||||
}
|
||||
|
||||
protected function initEntity(){
|
||||
@ -805,24 +762,10 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
|
||||
$this->scheduleUpdate();
|
||||
|
||||
if(isset($this->namedtag->ActiveEffects)){
|
||||
foreach($this->namedtag->ActiveEffects->getValue() as $e){
|
||||
$amplifier = Binary::unsignByte($e->Amplifier->getValue()); //0-255 only
|
||||
|
||||
$effect = Effect::getEffect($e["Id"]);
|
||||
if($effect === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$effect->setAmplifier($amplifier)->setDuration($e["Duration"])->setVisible($e["ShowParticles"] > 0);
|
||||
|
||||
$this->addEffect($effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addAttributes(){
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
@ -841,18 +784,13 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*
|
||||
* @param Player $player
|
||||
*/
|
||||
public function sendPotionEffects(Player $player){
|
||||
foreach($this->effects as $effect){
|
||||
$pk = new MobEffectPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->effectId = $effect->getId();
|
||||
$pk->amplifier = $effect->getAmplifier();
|
||||
$pk->particles = $effect->isVisible();
|
||||
$pk->duration = $effect->getDuration();
|
||||
$pk->eventId = MobEffectPacket::EVENT_ADD;
|
||||
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
@ -901,15 +839,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
*
|
||||
*/
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
if($this->hasEffect(Effect::FIRE_RESISTANCE) and (
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_FIRE
|
||||
or $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK
|
||||
or $source->getCause() === EntityDamageEvent::CAUSE_LAVA
|
||||
)
|
||||
){
|
||||
$source->setCancelled();
|
||||
}
|
||||
|
||||
$this->server->getPluginManager()->callEvent($source);
|
||||
if($source->isCancelled()){
|
||||
return;
|
||||
@ -1141,18 +1070,6 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$this->changedDataProperties = [];
|
||||
}
|
||||
|
||||
if(count($this->effects) > 0){
|
||||
foreach($this->effects as $effect){
|
||||
if($effect->canTick()){
|
||||
$effect->applyEffect($this);
|
||||
}
|
||||
$effect->setDuration($effect->getDuration() - $tickDiff);
|
||||
if($effect->getDuration() <= 0){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$hasUpdate = false;
|
||||
|
||||
$this->checkBlockCollision();
|
||||
@ -1163,27 +1080,8 @@ abstract class Entity extends Location implements Metadatable{
|
||||
$hasUpdate = true;
|
||||
}
|
||||
|
||||
if($this->fireTicks > 0){
|
||||
if($this->isFireProof()){
|
||||
if($this->fireTicks > 1){
|
||||
$this->fireTicks = 1;
|
||||
}else{
|
||||
$this->fireTicks -= 1;
|
||||
}
|
||||
}else{
|
||||
if(!$this->hasEffect(Effect::FIRE_RESISTANCE) and (($this->fireTicks % 20) === 0 or $tickDiff > 20)){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
$this->fireTicks -= $tickDiff;
|
||||
}
|
||||
|
||||
if($this->fireTicks <= 0){
|
||||
$this->extinguish();
|
||||
}else{
|
||||
$this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, true);
|
||||
$hasUpdate = true;
|
||||
}
|
||||
if($this->isOnFire()){
|
||||
$hasUpdate = ($hasUpdate || $this->doOnFireTick($tickDiff));
|
||||
}
|
||||
|
||||
if($this->noDamageTicks > 0){
|
||||
@ -1199,6 +1097,36 @@ abstract class Entity extends Location implements Metadatable{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function doOnFireTick(int $tickDiff = 1) : bool{
|
||||
if($this->isFireProof() and $this->fireTicks > 1){
|
||||
$this->fireTicks = 1;
|
||||
}else{
|
||||
$this->fireTicks -= $tickDiff;
|
||||
}
|
||||
|
||||
|
||||
|
||||
if(($this->fireTicks % 20 === 0) or $tickDiff > 20){
|
||||
$this->dealFireDamage();
|
||||
}
|
||||
|
||||
if(!$this->isOnFire()){
|
||||
$this->extinguish();
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* Called to deal damage to entities when they are on fire.
|
||||
*/
|
||||
protected function dealFireDamage(){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FIRE_TICK, 1);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
|
||||
protected function updateMovement(){
|
||||
$diffPosition = ($this->x - $this->lastX) ** 2 + ($this->y - $this->lastY) ** 2 + ($this->z - $this->lastZ) ** 2;
|
||||
$diffRotation = ($this->yaw - $this->lastYaw) ** 2 + ($this->pitch - $this->lastPitch) ** 2;
|
||||
@ -1293,6 +1221,13 @@ abstract class Entity extends Location implements Metadatable{
|
||||
if($ticks > $this->fireTicks){
|
||||
$this->fireTicks = $ticks;
|
||||
}
|
||||
|
||||
$this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, true);
|
||||
}
|
||||
|
||||
public function extinguish(){
|
||||
$this->fireTicks = 0;
|
||||
$this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, false);
|
||||
}
|
||||
|
||||
public function isFireProof() : bool{
|
||||
@ -1317,25 +1252,24 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
public function extinguish(){
|
||||
$this->fireTicks = 0;
|
||||
$this->setDataFlag(self::DATA_FLAGS, self::DATA_FLAG_ONFIRE, false);
|
||||
}
|
||||
|
||||
|
||||
public function canTriggerWalking(){
|
||||
return true;
|
||||
}
|
||||
|
||||
public function resetFallDistance(){
|
||||
$this->fallDistance = 0;
|
||||
$this->fallDistance = 0.0;
|
||||
}
|
||||
|
||||
protected function updateFallState($distanceThisTick, $onGround){
|
||||
/**
|
||||
* @param float $distanceThisTick
|
||||
* @param bool $onGround
|
||||
*/
|
||||
protected function updateFallState(float $distanceThisTick, bool $onGround){
|
||||
if($onGround === true){
|
||||
if($this->fallDistance > 0){
|
||||
if($this instanceof Living){
|
||||
$this->fall($this->fallDistance);
|
||||
}
|
||||
$this->fall($this->fallDistance);
|
||||
$this->resetFallDistance();
|
||||
}
|
||||
}elseif($distanceThisTick < 0){
|
||||
@ -1343,16 +1277,13 @@ abstract class Entity extends Location implements Metadatable{
|
||||
}
|
||||
}
|
||||
|
||||
public function getBoundingBox(){
|
||||
return $this->boundingBox;
|
||||
}
|
||||
/**
|
||||
* Called when a falling entity hits the ground.
|
||||
*
|
||||
* @param float $fallDistance
|
||||
*/
|
||||
public function fall(float $fallDistance){
|
||||
|
||||
public function fall($fallDistance){
|
||||
$damage = floor($fallDistance - 3 - ($this->hasEffect(Effect::JUMP) ? $this->getEffect(Effect::JUMP)->getEffectLevel() : 0));
|
||||
if($damage > 0){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
}
|
||||
|
||||
public function handleLavaMovement(){ //TODO
|
||||
|
@ -32,8 +32,15 @@ use pocketmine\event\entity\EntityRegainHealthEvent;
|
||||
use pocketmine\event\Timings;
|
||||
use pocketmine\item\Item as ItemItem;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\ByteTag;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\nbt\tag\FloatTag;
|
||||
use pocketmine\nbt\tag\IntTag;
|
||||
use pocketmine\nbt\tag\ListTag;
|
||||
use pocketmine\network\mcpe\protocol\EntityEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\MobEffectPacket;
|
||||
use pocketmine\Player;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\utils\BlockIterator;
|
||||
|
||||
abstract class Living extends Entity implements Damageable{
|
||||
@ -47,6 +54,11 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
protected $jumpVelocity = 0.42;
|
||||
|
||||
/** @var Effect[] */
|
||||
protected $effects = [];
|
||||
|
||||
abstract public function getName();
|
||||
|
||||
protected function initEntity(){
|
||||
parent::initEntity();
|
||||
|
||||
@ -62,6 +74,21 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
|
||||
$this->setHealth($this->namedtag["Health"]);
|
||||
|
||||
if(isset($this->namedtag->ActiveEffects)){
|
||||
foreach($this->namedtag->ActiveEffects->getValue() as $e){
|
||||
$amplifier = Binary::unsignByte($e->Amplifier->getValue()); //0-255 only
|
||||
|
||||
$effect = Effect::getEffect($e["Id"]);
|
||||
if($effect === null){
|
||||
continue;
|
||||
}
|
||||
|
||||
$effect->setAmplifier($amplifier)->setDuration($e["Duration"])->setVisible($e["ShowParticles"] > 0);
|
||||
|
||||
$this->addEffect($effect);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function addAttributes(){
|
||||
@ -104,9 +131,25 @@ abstract class Living extends Entity implements Damageable{
|
||||
public function saveNBT(){
|
||||
parent::saveNBT();
|
||||
$this->namedtag->Health = new FloatTag("Health", $this->getHealth());
|
||||
|
||||
if(count($this->effects) > 0){
|
||||
$effects = [];
|
||||
foreach($this->effects as $effect){
|
||||
$effects[] = new CompoundTag("", [
|
||||
new ByteTag("Id", $effect->getId()),
|
||||
new ByteTag("Amplifier", Binary::signByte($effect->getAmplifier())),
|
||||
new IntTag("Duration", $effect->getDuration()),
|
||||
new ByteTag("Ambient", 0),
|
||||
new ByteTag("ShowParticles", $effect->isVisible() ? 1 : 0)
|
||||
]);
|
||||
}
|
||||
|
||||
$this->namedtag->ActiveEffects = new ListTag("ActiveEffects", $effects);
|
||||
}else{
|
||||
unset($this->namedtag->ActiveEffects);
|
||||
}
|
||||
}
|
||||
|
||||
abstract public function getName();
|
||||
|
||||
public function hasLineOfSight(Entity $entity){
|
||||
//TODO: head height
|
||||
@ -123,6 +166,140 @@ abstract class Living extends Entity implements Damageable{
|
||||
$this->attackTime = 0;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns an array of Effects currently active on the mob.
|
||||
* @return Effect[]
|
||||
*/
|
||||
public function getEffects() : array{
|
||||
return $this->effects;
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes all effects from the mob.
|
||||
*/
|
||||
public function removeAllEffects(){
|
||||
foreach($this->effects as $effect){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Removes the effect with the specified ID from the mob.
|
||||
*
|
||||
* @param int $effectId
|
||||
*/
|
||||
public function removeEffect(int $effectId){
|
||||
if(isset($this->effects[$effectId])){
|
||||
$effect = $this->effects[$effectId];
|
||||
unset($this->effects[$effectId]);
|
||||
$effect->remove($this);
|
||||
|
||||
$this->recalculateEffectColor();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the effect instance active on this entity with the specified ID, or null if the mob does not have the
|
||||
* effect.
|
||||
*
|
||||
* @param int $effectId
|
||||
*
|
||||
* @return Effect|null
|
||||
*/
|
||||
public function getEffect(int $effectId){
|
||||
return $this->effects[$effectId] ?? null;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the specified effect is active on the mob.
|
||||
*
|
||||
* @param int $effectId
|
||||
*
|
||||
* @return bool
|
||||
*/
|
||||
public function hasEffect(int $effectId) : bool{
|
||||
return isset($this->effects[$effectId]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Adds an effect to the mob.
|
||||
* If a weaker effect of the same type is already applied, it will be replaced.
|
||||
* If a weaker or equal-strength effect is already applied but has a shorter duration, it will be replaced.
|
||||
*
|
||||
* @param Effect $effect
|
||||
*/
|
||||
public function addEffect(Effect $effect){
|
||||
if(isset($this->effects[$effect->getId()])){
|
||||
$oldEffect = $this->effects[$effect->getId()];
|
||||
if(
|
||||
abs($effect->getAmplifier()) < $oldEffect->getAmplifier()
|
||||
or (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier()) and $effect->getDuration() < $oldEffect->getDuration())
|
||||
){
|
||||
return;
|
||||
}
|
||||
$effect->add($this, true, $oldEffect);
|
||||
}else{
|
||||
$effect->add($this, false);
|
||||
}
|
||||
|
||||
$this->effects[$effect->getId()] = $effect;
|
||||
|
||||
$this->recalculateEffectColor();
|
||||
}
|
||||
|
||||
/**
|
||||
* Recalculates the mob's potion bubbles colour based on the active effects.
|
||||
*/
|
||||
protected function recalculateEffectColor(){
|
||||
//TODO: add transparency values
|
||||
$color = [0, 0, 0]; //RGB
|
||||
$count = 0;
|
||||
$ambient = true;
|
||||
foreach($this->effects as $effect){
|
||||
if($effect->isVisible() and $effect->hasBubbles()){
|
||||
$c = $effect->getColor();
|
||||
$color[0] += $c[0] * $effect->getEffectLevel();
|
||||
$color[1] += $c[1] * $effect->getEffectLevel();
|
||||
$color[2] += $c[2] * $effect->getEffectLevel();
|
||||
$count += $effect->getEffectLevel();
|
||||
if(!$effect->isAmbient()){
|
||||
$ambient = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($count > 0){
|
||||
$r = ($color[0] / $count) & 0xff;
|
||||
$g = ($color[1] / $count) & 0xff;
|
||||
$b = ($color[2] / $count) & 0xff;
|
||||
|
||||
$this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0xff000000 | ($r << 16) | ($g << 8) | $b);
|
||||
$this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, $ambient ? 1 : 0);
|
||||
}else{
|
||||
$this->setDataProperty(Entity::DATA_POTION_COLOR, Entity::DATA_TYPE_INT, 0);
|
||||
$this->setDataProperty(Entity::DATA_POTION_AMBIENT, Entity::DATA_TYPE_BYTE, 0);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends the mob's potion effects to the specified player.
|
||||
* @param Player $player
|
||||
*/
|
||||
public function sendPotionEffects(Player $player){
|
||||
foreach($this->effects as $effect){
|
||||
$pk = new MobEffectPacket();
|
||||
$pk->entityRuntimeId = $this->id;
|
||||
$pk->effectId = $effect->getId();
|
||||
$pk->amplifier = $effect->getAmplifier();
|
||||
$pk->particles = $effect->isVisible();
|
||||
$pk->duration = $effect->getDuration();
|
||||
$pk->eventId = MobEffectPacket::EVENT_ADD;
|
||||
|
||||
$player->dataPacket($pk);
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
/**
|
||||
* Returns the initial upwards velocity of a jumping entity in blocks/tick, including additional velocity due to effects.
|
||||
* @return float
|
||||
@ -140,6 +317,14 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
}
|
||||
|
||||
public function fall(float $fallDistance){
|
||||
$damage = floor($fallDistance - 3 - ($this->hasEffect(Effect::JUMP) ? $this->getEffect(Effect::JUMP)->getEffectLevel() : 0));
|
||||
if($damage > 0){
|
||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
|
||||
$this->attack($ev->getFinalDamage(), $ev);
|
||||
}
|
||||
}
|
||||
|
||||
public function attack($damage, EntityDamageEvent $source){
|
||||
if($this->attackTime > 0 or $this->noDamageTicks > 0){
|
||||
$lastCause = $this->getLastDamageCause();
|
||||
@ -148,6 +333,19 @@ abstract class Living extends Entity implements Damageable{
|
||||
}
|
||||
}
|
||||
|
||||
if($this->hasEffect(Effect::FIRE_RESISTANCE) and (
|
||||
$source->getCause() === EntityDamageEvent::CAUSE_FIRE
|
||||
or $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK
|
||||
or $source->getCause() === EntityDamageEvent::CAUSE_LAVA
|
||||
)
|
||||
){
|
||||
$source->setCancelled();
|
||||
}
|
||||
|
||||
if($this->hasEffect(Effect::DAMAGE_RESISTANCE)){
|
||||
$source->setDamage(-($source->getDamage(EntityDamageEvent::MODIFIER_BASE) * 0.20 * $this->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
|
||||
}
|
||||
|
||||
parent::attack($damage, $source);
|
||||
|
||||
if($source->isCancelled()){
|
||||
@ -224,6 +422,8 @@ abstract class Living extends Entity implements Damageable{
|
||||
|
||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||
|
||||
$this->doEffectsTick($tickDiff);
|
||||
|
||||
if($this->isAlive()){
|
||||
if($this->isInsideOfSolid()){
|
||||
$hasUpdate = true;
|
||||
@ -271,6 +471,26 @@ abstract class Living extends Entity implements Damageable{
|
||||
return $hasUpdate;
|
||||
}
|
||||
|
||||
protected function doEffectsTick(int $tickDiff = 1){
|
||||
if(count($this->effects) > 0){
|
||||
foreach($this->effects as $effect){
|
||||
if($effect->canTick()){
|
||||
$effect->applyEffect($this);
|
||||
}
|
||||
$effect->setDuration($effect->getDuration() - $tickDiff);
|
||||
if($effect->getDuration() <= 0){
|
||||
$this->removeEffect($effect->getId());
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
protected function dealFireDamage(){
|
||||
if(!$this->hasEffect(Effect::FIRE_RESISTANCE)){
|
||||
parent::dealFireDamage();
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* @return ItemItem[]
|
||||
*/
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
|
||||
/**
|
||||
* Called when an entity takes damage from another entity.
|
||||
@ -51,12 +52,14 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
|
||||
}
|
||||
|
||||
protected function addAttackerModifiers(Entity $damager){
|
||||
if($damager->hasEffect(Effect::STRENGTH)){
|
||||
$this->setDamage($this->getDamage(self::MODIFIER_BASE) * 0.3 * $damager->getEffect(Effect::STRENGTH)->getEffectLevel(), self::MODIFIER_STRENGTH);
|
||||
}
|
||||
if($damager instanceof Living){ //TODO: move this to entity classes
|
||||
if($damager->hasEffect(Effect::STRENGTH)){
|
||||
$this->setDamage($this->getDamage(self::MODIFIER_BASE) * 0.3 * $damager->getEffect(Effect::STRENGTH)->getEffectLevel(), self::MODIFIER_STRENGTH);
|
||||
}
|
||||
|
||||
if($damager->hasEffect(Effect::WEAKNESS)){
|
||||
$this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.2 * $damager->getEffect(Effect::WEAKNESS)->getEffectLevel()), self::MODIFIER_WEAKNESS);
|
||||
if($damager->hasEffect(Effect::WEAKNESS)){
|
||||
$this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.2 * $damager->getEffect(Effect::WEAKNESS)->getEffectLevel()), self::MODIFIER_WEAKNESS);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -23,7 +23,6 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\event\entity;
|
||||
|
||||
use pocketmine\entity\Effect;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
|
||||
@ -86,10 +85,6 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
|
||||
if(!isset($this->modifiers[self::MODIFIER_BASE])){
|
||||
throw new \InvalidArgumentException("BASE Damage modifier missing");
|
||||
}
|
||||
|
||||
if($entity->hasEffect(Effect::DAMAGE_RESISTANCE)){
|
||||
$this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.20 * $entity->getEffect(Effect::DAMAGE_RESISTANCE)->getEffectLevel()), self::MODIFIER_RESISTANCE);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
Loading…
x
Reference in New Issue
Block a user