Merge branch 'master' into mcpe-1.2

This commit is contained in:
Dylan K. Taylor 2017-08-08 10:23:19 +01:00
commit 1d0f0a2999
35 changed files with 372 additions and 254 deletions

View File

@ -688,7 +688,7 @@ class Block extends Position implements BlockIds, Metadatable{
return $this->getLevel()->getBlock(Vector3::getSide($side, $step)); return $this->getLevel()->getBlock(Vector3::getSide($side, $step));
} }
return Block::get(Item::AIR, 0, Position::fromObject(Vector3::getSide($side, $step))); return Block::get(Block::AIR, 0, Position::fromObject(Vector3::getSide($side, $step)));
} }
/** /**

View File

@ -50,7 +50,7 @@ class Dirt extends Solid{
public function onActivate(Item $item, Player $player = null){ public function onActivate(Item $item, Player $player = null){
if($item->isHoe()){ if($item->isHoe()){
$item->useOn($this); $item->useOn($this);
$this->getLevel()->setBlock($this, Block::get(Item::FARMLAND, 0), true); $this->getLevel()->setBlock($this, Block::get(Block::FARMLAND, 0), true);
return true; return true;
} }

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\entity\Arrow; use pocketmine\entity\Arrow;
use pocketmine\entity\Effect;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\entity\EntityCombustByBlockEvent; use pocketmine\event\entity\EntityCombustByBlockEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByBlockEvent;
@ -63,10 +62,8 @@ class Fire extends Flowable{
} }
public function onEntityCollide(Entity $entity){ public function onEntityCollide(Entity $entity){
if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1); $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_FIRE, 1);
$entity->attack($ev->getFinalDamage(), $ev); $entity->attack($ev->getFinalDamage(), $ev);
}
$ev = new EntityCombustByBlockEvent($this, $entity, 8); $ev = new EntityCombustByBlockEvent($this, $entity, 8);
if($entity instanceof Arrow){ if($entity instanceof Arrow){

View File

@ -1,5 +1,6 @@
<?php <?php
/* /*
* *
* ____ _ _ __ __ _ __ __ ____ * ____ _ _ __ __ _ __ __ ____
@ -19,6 +20,8 @@
* *
*/ */
declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\item\Item; use pocketmine\item\Item;

View File

@ -40,7 +40,7 @@ class GlowingRedstoneOre extends RedstoneOre{
public function onUpdate($type){ public function onUpdate($type){
if($type === Level::BLOCK_UPDATE_SCHEDULED or $type === Level::BLOCK_UPDATE_RANDOM){ if($type === Level::BLOCK_UPDATE_SCHEDULED or $type === Level::BLOCK_UPDATE_RANDOM){
$this->getLevel()->setBlock($this, Block::get(Item::REDSTONE_ORE, $this->meta), false, false); $this->getLevel()->setBlock($this, Block::get(Block::REDSTONE_ORE, $this->meta), false, false);
return Level::BLOCK_UPDATE_WEAK; return Level::BLOCK_UPDATE_WEAK;
} }

View File

@ -30,7 +30,6 @@ use pocketmine\level\generator\object\TallGrass as TallGrassObject;
use pocketmine\level\Level; use pocketmine\level\Level;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\Server;
use pocketmine\utils\Random; use pocketmine\utils\Random;
class Grass extends Solid{ class Grass extends Solid{

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\entity\Effect;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\entity\EntityCombustByBlockEvent; use pocketmine\event\entity\EntityCombustByBlockEvent;
use pocketmine\event\entity\EntityDamageByBlockEvent; use pocketmine\event\entity\EntityDamageByBlockEvent;
@ -50,10 +49,9 @@ class Lava extends Liquid{
public function onEntityCollide(Entity $entity){ public function onEntityCollide(Entity $entity){
$entity->fallDistance *= 0.5; $entity->fallDistance *= 0.5;
if(!$entity->hasEffect(Effect::FIRE_RESISTANCE)){
$ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4); $ev = new EntityDamageByBlockEvent($this, $entity, EntityDamageEvent::CAUSE_LAVA, 4);
$entity->attack($ev->getFinalDamage(), $ev); $entity->attack($ev->getFinalDamage(), $ev);
}
$ev = new EntityCombustByBlockEvent($this, $entity, 15); $ev = new EntityCombustByBlockEvent($this, $entity, 15);
Server::getInstance()->getPluginManager()->callEvent($ev); Server::getInstance()->getPluginManager()->callEvent($ev);

View File

@ -23,11 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\event\block\LeavesDecayEvent;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\Level;
use pocketmine\Player;
use pocketmine\Server;
class Leaves2 extends Leaves{ class Leaves2 extends Leaves{

View File

@ -434,9 +434,9 @@ abstract class Liquid extends Transparent{
if($colliding){ if($colliding){
if($this->getDamage() === 0){ if($this->getDamage() === 0){
$this->getLevel()->setBlock($this, Block::get(Item::OBSIDIAN), true, true); $this->getLevel()->setBlock($this, Block::get(Block::OBSIDIAN), true, true);
}elseif($this->getDamage() <= 4){ }elseif($this->getDamage() <= 4){
$this->getLevel()->setBlock($this, Block::get(Item::COBBLESTONE), true, true); $this->getLevel()->setBlock($this, Block::get(Block::COBBLESTONE), true, true);
} }
} }
} }

View File

@ -50,7 +50,7 @@ class RedstoneOre extends Solid{
public function onUpdate($type){ public function onUpdate($type){
if($type === Level::BLOCK_UPDATE_NORMAL or $type === Level::BLOCK_UPDATE_TOUCH){ if($type === Level::BLOCK_UPDATE_NORMAL or $type === Level::BLOCK_UPDATE_TOUCH){
$this->getLevel()->setBlock($this, Block::get(Item::GLOWING_REDSTONE_ORE, $this->meta)); $this->getLevel()->setBlock($this, Block::get(Block::GLOWING_REDSTONE_ORE, $this->meta));
return Level::BLOCK_UPDATE_WEAK; return Level::BLOCK_UPDATE_WEAK;
} }

View File

@ -24,7 +24,6 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\level\Level;
use pocketmine\math\AxisAlignedBB; use pocketmine\math\AxisAlignedBB;
use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;

View File

@ -25,8 +25,6 @@ namespace pocketmine\block;
use pocketmine\item\Item; use pocketmine\item\Item;
use pocketmine\item\Tool; use pocketmine\item\Tool;
use pocketmine\math\AxisAlignedBB;
use pocketmine\Player;
class Slab extends WoodenSlab{ class Slab extends WoodenSlab{
const STONE = 0; const STONE = 0;

View File

@ -23,8 +23,6 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\level\Level;
class StillLava extends Lava{ class StillLava extends Lava{
protected $id = self::STILL_LAVA; protected $id = self::STILL_LAVA;

View File

@ -23,8 +23,6 @@ declare(strict_types=1);
namespace pocketmine\block; namespace pocketmine\block;
use pocketmine\level\Level;
class StillWater extends Water{ class StillWater extends Water{
protected $id = self::STILL_WATER; protected $id = self::STILL_WATER;

View File

@ -51,17 +51,14 @@ use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\DoubleTag; use pocketmine\nbt\tag\DoubleTag;
use pocketmine\nbt\tag\FloatTag; use pocketmine\nbt\tag\FloatTag;
use pocketmine\nbt\tag\IntTag;
use pocketmine\nbt\tag\ListTag; use pocketmine\nbt\tag\ListTag;
use pocketmine\nbt\tag\ShortTag; use pocketmine\nbt\tag\ShortTag;
use pocketmine\nbt\tag\StringTag; use pocketmine\nbt\tag\StringTag;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\RemoveEntityPacket; use pocketmine\network\mcpe\protocol\RemoveEntityPacket;
use pocketmine\network\mcpe\protocol\SetEntityDataPacket; use pocketmine\network\mcpe\protocol\SetEntityDataPacket;
use pocketmine\Player; use pocketmine\Player;
use pocketmine\plugin\Plugin; use pocketmine\plugin\Plugin;
use pocketmine\Server; use pocketmine\Server;
use pocketmine\utils\Binary;
abstract class Entity extends Location implements Metadatable{ abstract class Entity extends Location implements Metadatable{
@ -227,9 +224,6 @@ abstract class Entity extends Location implements Metadatable{
*/ */
protected $hasSpawned = []; protected $hasSpawned = [];
/** @var Effect[] */
protected $effects = [];
protected $id; protected $id;
protected $dataProperties = [ protected $dataProperties = [
@ -296,7 +290,8 @@ abstract class Entity extends Location implements Metadatable{
protected $stepHeight = 0; protected $stepHeight = 0;
public $keepMovement = false; public $keepMovement = false;
public $fallDistance = 0; /** @var float */
public $fallDistance = 0.0;
public $ticksLived = 0; public $ticksLived = 0;
public $lastUpdate; public $lastUpdate;
public $maxFireTicks; public $maxFireTicks;
@ -485,6 +480,11 @@ abstract class Entity extends Location implements Metadatable{
$this->setDataProperty(self::DATA_SCALE, self::DATA_TYPE_FLOAT, $value); $this->setDataProperty(self::DATA_SCALE, self::DATA_TYPE_FLOAT, $value);
} }
public function getBoundingBox(){
return $this->boundingBox;
}
public function isSneaking(){ public function isSneaking(){
return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING); return $this->getDataFlag(self::DATA_FLAGS, self::DATA_FLAG_SNEAKING);
} }
@ -622,85 +622,59 @@ abstract class Entity extends Location implements Metadatable{
} }
/** /**
* @deprecated
*
* @return Effect[] * @return Effect[]
*/ */
public function getEffects(){ public function getEffects() : array{
return $this->effects; return [];
} }
/**
* @deprecated
*/
public function removeAllEffects(){ public function removeAllEffects(){
foreach($this->effects as $effect){
$this->removeEffect($effect->getId());
}
} }
public function removeEffect($effectId){ /**
if(isset($this->effects[$effectId])){ * @deprecated
$effect = $this->effects[$effectId]; *
unset($this->effects[$effectId]); * @param int $effectId
$effect->remove($this); */
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){ public function addEffect(Effect $effect){
if(isset($this->effects[$effect->getId()])){ throw new \BadMethodCallException("Cannot add effects to non-living entities");
$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);
}
} }
/** /**
@ -780,23 +754,6 @@ abstract class Entity extends Location implements Metadatable{
$this->namedtag->Air = new ShortTag("Air", $this->getDataProperty(self::DATA_AIR)); $this->namedtag->Air = new ShortTag("Air", $this->getDataProperty(self::DATA_AIR));
$this->namedtag->OnGround = new ByteTag("OnGround", $this->onGround ? 1 : 0); $this->namedtag->OnGround = new ByteTag("OnGround", $this->onGround ? 1 : 0);
$this->namedtag->Invulnerable = new ByteTag("Invulnerable", $this->invulnerable ? 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(){ protected function initEntity(){
@ -810,24 +767,10 @@ abstract class Entity extends Location implements Metadatable{
} }
$this->scheduleUpdate(); $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(){ protected function addAttributes(){
} }
/** /**
@ -846,18 +789,13 @@ abstract class Entity extends Location implements Metadatable{
} }
} }
/**
* @deprecated
*
* @param Player $player
*/
public function sendPotionEffects(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);
}
} }
/** /**
@ -906,15 +844,6 @@ abstract class Entity extends Location implements Metadatable{
* *
*/ */
public function attack($damage, EntityDamageEvent $source){ 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); $this->server->getPluginManager()->callEvent($source);
if($source->isCancelled()){ if($source->isCancelled()){
return; return;
@ -986,11 +915,11 @@ abstract class Entity extends Location implements Metadatable{
} }
} }
public function getAbsorption() : int{ public function getAbsorption() : float{
return 0; return 0;
} }
public function setAbsorption(int $absorption){ public function setAbsorption(float $absorption){
} }
@ -1126,8 +1055,6 @@ abstract class Entity extends Location implements Metadatable{
} }
public function entityBaseTick($tickDiff = 1){ public function entityBaseTick($tickDiff = 1){
Timings::$timerEntityBaseTick->startTiming();
//TODO: check vehicles //TODO: check vehicles
$this->blocksAround = null; $this->blocksAround = null;
@ -1140,7 +1067,6 @@ abstract class Entity extends Location implements Metadatable{
$this->close(); $this->close();
} }
Timings::$timerEntityBaseTick->stopTiming();
return false; return false;
} }
@ -1149,18 +1075,6 @@ abstract class Entity extends Location implements Metadatable{
$this->changedDataProperties = []; $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; $hasUpdate = false;
$this->checkBlockCollision(); $this->checkBlockCollision();
@ -1171,27 +1085,8 @@ abstract class Entity extends Location implements Metadatable{
$hasUpdate = true; $hasUpdate = true;
} }
if($this->fireTicks > 0){ if($this->isOnFire()){
if($this->isFireProof()){ $hasUpdate = ($hasUpdate || $this->doOnFireTick($tickDiff));
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->noDamageTicks > 0){ if($this->noDamageTicks > 0){
@ -1204,11 +1099,39 @@ abstract class Entity extends Location implements Metadatable{
$this->age += $tickDiff; $this->age += $tickDiff;
$this->ticksLived += $tickDiff; $this->ticksLived += $tickDiff;
Timings::$timerEntityBaseTick->stopTiming();
return $hasUpdate; 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(){ protected function updateMovement(){
$diffPosition = ($this->x - $this->lastX) ** 2 + ($this->y - $this->lastY) ** 2 + ($this->z - $this->lastZ) ** 2; $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; $diffRotation = ($this->yaw - $this->lastYaw) ** 2 + ($this->pitch - $this->lastPitch) ** 2;
@ -1277,7 +1200,9 @@ abstract class Entity extends Location implements Metadatable{
$this->timings->startTiming(); $this->timings->startTiming();
Timings::$timerEntityBaseTick->startTiming();
$hasUpdate = $this->entityBaseTick($tickDiff); $hasUpdate = $this->entityBaseTick($tickDiff);
Timings::$timerEntityBaseTick->stopTiming();
$this->updateMovement(); $this->updateMovement();
@ -1301,6 +1226,13 @@ abstract class Entity extends Location implements Metadatable{
if($ticks > $this->fireTicks){ if($ticks > $this->fireTicks){
$this->fireTicks = $ticks; $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{ public function isFireProof() : bool{
@ -1325,25 +1257,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(){ public function canTriggerWalking(){
return true; return true;
} }
public function resetFallDistance(){ 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($onGround === true){
if($this->fallDistance > 0){ if($this->fallDistance > 0){
if($this instanceof Living){
$this->fall($this->fallDistance); $this->fall($this->fallDistance);
}
$this->resetFallDistance(); $this->resetFallDistance();
} }
}elseif($distanceThisTick < 0){ }elseif($distanceThisTick < 0){
@ -1351,16 +1282,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 public function handleLavaMovement(){ //TODO

View File

@ -32,8 +32,15 @@ use pocketmine\event\entity\EntityRegainHealthEvent;
use pocketmine\event\Timings; use pocketmine\event\Timings;
use pocketmine\item\Item as ItemItem; use pocketmine\item\Item as ItemItem;
use pocketmine\math\Vector3; use pocketmine\math\Vector3;
use pocketmine\nbt\tag\ByteTag;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\nbt\tag\FloatTag; 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\EntityEventPacket;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\Player;
use pocketmine\utils\Binary;
use pocketmine\utils\BlockIterator; use pocketmine\utils\BlockIterator;
abstract class Living extends Entity implements Damageable{ abstract class Living extends Entity implements Damageable{
@ -47,6 +54,11 @@ abstract class Living extends Entity implements Damageable{
protected $jumpVelocity = 0.42; protected $jumpVelocity = 0.42;
/** @var Effect[] */
protected $effects = [];
abstract public function getName();
protected function initEntity(){ protected function initEntity(){
parent::initEntity(); parent::initEntity();
@ -62,6 +74,21 @@ abstract class Living extends Entity implements Damageable{
} }
$this->setHealth($this->namedtag["Health"]); $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(){ protected function addAttributes(){
@ -93,20 +120,36 @@ abstract class Living extends Entity implements Damageable{
$this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount); $this->attributeMap->getAttribute(Attribute::HEALTH)->setMaxValue($amount);
} }
public function getAbsorption() : int{ public function getAbsorption() : float{
return (int) $this->attributeMap->getAttribute(Attribute::ABSORPTION)->getValue(); return $this->attributeMap->getAttribute(Attribute::ABSORPTION)->getValue();
} }
public function setAbsorption(int $absorption){ public function setAbsorption(float $absorption){
$this->attributeMap->getAttribute(Attribute::ABSORPTION)->setValue($absorption); $this->attributeMap->getAttribute(Attribute::ABSORPTION)->setValue($absorption);
} }
public function saveNBT(){ public function saveNBT(){
parent::saveNBT(); parent::saveNBT();
$this->namedtag->Health = new FloatTag("Health", $this->getHealth()); $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){ public function hasLineOfSight(Entity $entity){
//TODO: head height //TODO: head height
@ -123,6 +166,140 @@ abstract class Living extends Entity implements Damageable{
$this->attackTime = 0; $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. * Returns the initial upwards velocity of a jumping entity in blocks/tick, including additional velocity due to effects.
* @return float * @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){ public function attack($damage, EntityDamageEvent $source){
if($this->attackTime > 0 or $this->noDamageTicks > 0){ if($this->attackTime > 0 or $this->noDamageTicks > 0){
$lastCause = $this->getLastDamageCause(); $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); parent::attack($damage, $source);
if($source->isCancelled()){ if($source->isCancelled()){
@ -224,6 +422,8 @@ abstract class Living extends Entity implements Damageable{
$hasUpdate = parent::entityBaseTick($tickDiff); $hasUpdate = parent::entityBaseTick($tickDiff);
$this->doEffectsTick($tickDiff);
if($this->isAlive()){ if($this->isAlive()){
if($this->isInsideOfSolid()){ if($this->isInsideOfSolid()){
$hasUpdate = true; $hasUpdate = true;
@ -271,6 +471,26 @@ abstract class Living extends Entity implements Damageable{
return $hasUpdate; 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[] * @return ItemItem[]
*/ */

View File

@ -25,6 +25,7 @@ namespace pocketmine\event\entity;
use pocketmine\entity\Effect; use pocketmine\entity\Effect;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\entity\Living;
/** /**
* Called when an entity takes damage from another entity. * Called when an entity takes damage from another entity.
@ -51,6 +52,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
} }
protected function addAttackerModifiers(Entity $damager){ protected function addAttackerModifiers(Entity $damager){
if($damager instanceof Living){ //TODO: move this to entity classes
if($damager->hasEffect(Effect::STRENGTH)){ if($damager->hasEffect(Effect::STRENGTH)){
$this->setDamage($this->getDamage(self::MODIFIER_BASE) * 0.3 * $damager->getEffect(Effect::STRENGTH)->getEffectLevel(), self::MODIFIER_STRENGTH); $this->setDamage($this->getDamage(self::MODIFIER_BASE) * 0.3 * $damager->getEffect(Effect::STRENGTH)->getEffectLevel(), self::MODIFIER_STRENGTH);
} }
@ -59,6 +61,7 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
$this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.2 * $damager->getEffect(Effect::WEAKNESS)->getEffectLevel()), self::MODIFIER_WEAKNESS); $this->setDamage(-($this->getDamage(self::MODIFIER_BASE) * 0.2 * $damager->getEffect(Effect::WEAKNESS)->getEffectLevel()), self::MODIFIER_WEAKNESS);
} }
} }
}
/** /**
* Returns the attacking entity, or null if the attacker has been killed or closed. * Returns the attacking entity, or null if the attacker has been killed or closed.

View File

@ -23,7 +23,6 @@ declare(strict_types=1);
namespace pocketmine\event\entity; namespace pocketmine\event\entity;
use pocketmine\entity\Effect;
use pocketmine\entity\Entity; use pocketmine\entity\Entity;
use pocketmine\event\Cancellable; use pocketmine\event\Cancellable;
@ -86,10 +85,6 @@ class EntityDamageEvent extends EntityEvent implements Cancellable{
if(!isset($this->modifiers[self::MODIFIER_BASE])){ if(!isset($this->modifiers[self::MODIFIER_BASE])){
throw new \InvalidArgumentException("BASE Damage modifier missing"); 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);
}
} }
/** /**

View File

@ -139,21 +139,7 @@ class SimpleTransactionGroup implements TransactionGroup{
$haveItems = []; $haveItems = [];
$needItems = []; $needItems = [];
if($this->matchItems($needItems, $haveItems) and count($this->transactions) > 0){ return $this->matchItems($needItems, $haveItems) and count($this->transactions) > 0 and ((count($haveItems) === 0 and count($needItems) === 0) or $this->source->isCreative(true));
if(count($haveItems) === 0 and count($needItems) === 0){
return true;
}elseif($this->source->isCreative(true) and count($needItems) > 0){ //Added items from creative inventory
foreach($needItems as $item){
if(Item::getCreativeItemIndex($item) === -1 and $item->getId() !== Item::AIR){
return false;
}
}
return true;
}
}
return false;
} }
/** /**

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Bed extends Item{ class Bed extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::BED_BLOCK); $this->block = Block::get(Block::BED_BLOCK);
parent::__construct(self::BED, $meta, $count, "Bed"); parent::__construct(self::BED, $meta, $count, "Bed");
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class BeetrootSeeds extends Item{ class BeetrootSeeds extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::BEETROOT_BLOCK); $this->block = Block::get(Block::BEETROOT_BLOCK);
parent::__construct(self::BEETROOT_SEEDS, $meta, $count, "Beetroot Seeds"); parent::__construct(self::BEETROOT_SEEDS, $meta, $count, "Beetroot Seeds");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Cake extends Item{ class Cake extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::CAKE_BLOCK); $this->block = Block::get(Block::CAKE_BLOCK);
parent::__construct(self::CAKE, $meta, $count, "Cake"); parent::__construct(self::CAKE, $meta, $count, "Cake");
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Carrot extends Food{ class Carrot extends Food{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::CARROT_BLOCK); $this->block = Block::get(Block::CARROT_BLOCK);
parent::__construct(self::CARROT, $meta, $count, "Carrot"); parent::__construct(self::CARROT, $meta, $count, "Carrot");
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class FlowerPot extends Item{ class FlowerPot extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::FLOWER_POT_BLOCK); $this->block = Block::get(Block::FLOWER_POT_BLOCK);
parent::__construct(self::FLOWER_POT, $meta, $count, "Flower Pot"); parent::__construct(self::FLOWER_POT, $meta, $count, "Flower Pot");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class IronDoor extends Item{ class IronDoor extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::IRON_DOOR_BLOCK); $this->block = Block::get(Block::IRON_DOOR_BLOCK);
parent::__construct(self::IRON_DOOR, $meta, $count, "Iron Door"); parent::__construct(self::IRON_DOOR, $meta, $count, "Iron Door");
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class MelonSeeds extends Item{ class MelonSeeds extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::MELON_STEM); $this->block = Block::get(Block::MELON_STEM);
parent::__construct(self::MELON_SEEDS, $meta, $count, "Melon Seeds"); parent::__construct(self::MELON_SEEDS, $meta, $count, "Melon Seeds");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Potato extends Food{ class Potato extends Food{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::POTATO_BLOCK); $this->block = Block::get(Block::POTATO_BLOCK);
parent::__construct(self::POTATO, $meta, $count, "Potato"); parent::__construct(self::POTATO, $meta, $count, "Potato");
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class PumpkinSeeds extends Item{ class PumpkinSeeds extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::PUMPKIN_STEM); $this->block = Block::get(Block::PUMPKIN_STEM);
parent::__construct(self::PUMPKIN_SEEDS, $meta, $count, "Pumpkin Seeds"); parent::__construct(self::PUMPKIN_SEEDS, $meta, $count, "Pumpkin Seeds");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Sign extends Item{ class Sign extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::SIGN_POST); $this->block = Block::get(Block::SIGN_POST);
parent::__construct(self::SIGN, $meta, $count, "Sign"); parent::__construct(self::SIGN, $meta, $count, "Sign");
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Skull extends Item{ class Skull extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::SKULL_BLOCK); $this->block = Block::get(Block::SKULL_BLOCK);
parent::__construct(self::SKULL, $meta, $count, "Mob Head"); parent::__construct(self::SKULL, $meta, $count, "Mob Head");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class Sugarcane extends Item{ class Sugarcane extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::SUGARCANE_BLOCK); $this->block = Block::get(Block::SUGARCANE_BLOCK);
parent::__construct(self::SUGARCANE, $meta, $count, "Sugar Cane"); parent::__construct(self::SUGARCANE, $meta, $count, "Sugar Cane");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class WheatSeeds extends Item{ class WheatSeeds extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::WHEAT_BLOCK); $this->block = Block::get(Block::WHEAT_BLOCK);
parent::__construct(self::WHEAT_SEEDS, $meta, $count, "Wheat Seeds"); parent::__construct(self::WHEAT_SEEDS, $meta, $count, "Wheat Seeds");
} }
} }

View File

@ -27,7 +27,7 @@ use pocketmine\block\Block;
class WoodenDoor extends Item{ class WoodenDoor extends Item{
public function __construct($meta = 0, $count = 1){ public function __construct($meta = 0, $count = 1){
$this->block = Block::get(Item::WOODEN_DOOR_BLOCK); $this->block = Block::get(Block::WOODEN_DOOR_BLOCK);
parent::__construct(self::WOODEN_DOOR, $meta, $count, "Wooden Door"); parent::__construct(self::WOODEN_DOOR, $meta, $count, "Wooden Door");
} }

View File

@ -387,7 +387,7 @@ class Level implements ChunkManager, Metadatable{
public function unregisterGenerator(){ public function unregisterGenerator(){
$size = $this->server->getScheduler()->getAsyncTaskPoolSize(); $size = $this->server->getScheduler()->getAsyncTaskPoolSize();
for($i = 0; $i < $size; ++$i){ for($i = 0; $i < $size; ++$i){
$this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorUnregisterTask($this, $this->generatorInstance), $i); $this->server->getScheduler()->scheduleAsyncTaskToWorker(new GeneratorUnregisterTask($this), $i);
} }
} }

View File

@ -191,7 +191,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
$this->namedtag->BurnTime->setValue($ev->getBurnTime()); $this->namedtag->BurnTime->setValue($ev->getBurnTime());
$this->namedtag->BurnTicks = new ShortTag("BurnTicks", 0); $this->namedtag->BurnTicks = new ShortTag("BurnTicks", 0);
if($this->getBlock()->getId() === Item::FURNACE){ if($this->getBlock()->getId() === Item::FURNACE){
$this->getLevel()->setBlock($this, Block::get(Item::BURNING_FURNACE, $this->getBlock()->getDamage()), true); $this->getLevel()->setBlock($this, Block::get(Block::BURNING_FURNACE, $this->getBlock()->getDamage()), true);
} }
if($this->namedtag->BurnTime->getValue() > 0 and $ev->isBurning()){ if($this->namedtag->BurnTime->getValue() > 0 and $ev->isBurning()){
@ -254,7 +254,7 @@ class Furnace extends Spawnable implements InventoryHolder, Container, Nameable{
$ret = true; $ret = true;
}else{ }else{
if($this->getBlock()->getId() === Item::BURNING_FURNACE){ if($this->getBlock()->getId() === Item::BURNING_FURNACE){
$this->getLevel()->setBlock($this, Block::get(Item::FURNACE, $this->getBlock()->getDamage()), true); $this->getLevel()->setBlock($this, Block::get(Block::FURNACE, $this->getBlock()->getDamage()), true);
} }
$this->namedtag->BurnTime->setValue(0); $this->namedtag->BurnTime->setValue(0);
$this->namedtag->CookTime->setValue(0); $this->namedtag->CookTime->setValue(0);