diff --git a/src/pocketmine/Server.php b/src/pocketmine/Server.php index 4d299333a..4a52bb250 100644 --- a/src/pocketmine/Server.php +++ b/src/pocketmine/Server.php @@ -39,6 +39,7 @@ use pocketmine\entity\Human; use pocketmine\entity\Item as DroppedItem; use pocketmine\entity\PrimedTNT; use pocketmine\entity\Snowball; +use pocketmine\entity\Squid; use pocketmine\entity\Villager; use pocketmine\entity\Zombie; use pocketmine\event\HandlerList; @@ -2415,6 +2416,7 @@ class Server{ Entity::registerEntity(Snowball::class); Entity::registerEntity(Villager::class); Entity::registerEntity(Zombie::class); + Entity::registerEntity(Squid::class); Entity::registerEntity(Human::class, true); } diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index 0b808250b..7b371ddd0 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -25,6 +25,7 @@ namespace pocketmine\block; use pocketmine\entity\Entity; +use pocketmine\entity\Squid; use pocketmine\entity\Villager; use pocketmine\entity\Zombie; use pocketmine\item\Item; @@ -494,7 +495,7 @@ class Block extends Position implements Metadatable{ //TODO: Slime [Item::SPAWN_EGG, Zombie::NETWORK_ID], //TODO: PigZombie - //TODO: Replace with Entity constants + [Item::SPAWN_EGG, Squid::NETWORK_ID], //Seeds diff --git a/src/pocketmine/entity/Ageable.php b/src/pocketmine/entity/Ageable.php index 871c8efec..7067af087 100644 --- a/src/pocketmine/entity/Ageable.php +++ b/src/pocketmine/entity/Ageable.php @@ -23,5 +23,9 @@ namespace pocketmine\entity; interface Ageable{ + const DATA_AGEABLE_FLAGS = 14; + const DATA_FLAG_BABY = 0; + + public function isBaby(); } \ No newline at end of file diff --git a/src/pocketmine/entity/Animal.php b/src/pocketmine/entity/Animal.php index 7803e2140..18bff79dc 100644 --- a/src/pocketmine/entity/Animal.php +++ b/src/pocketmine/entity/Animal.php @@ -24,4 +24,14 @@ namespace pocketmine\entity; abstract class Animal extends Creature implements Ageable{ + public function initEntity(){ + parent::initEntity(); + if($this->getDataProperty(self::DATA_AGEABLE_FLAGS) === null){ + $this->setDataProperty(self::DATA_AGEABLE_FLAGS, self::DATA_TYPE_BYTE, 0); + } + } + + public function isBaby(){ + return $this->getDataFlag(self::DATA_AGEABLE_FLAGS, self::DATA_FLAG_BABY); + } } \ No newline at end of file diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 00a3c7967..81f45e37d 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -196,12 +196,35 @@ abstract class Entity extends Location implements Metadatable{ /** @var \pocketmine\event\TimingsHandler */ protected $timings; + protected $MASK = 0; + public function __construct(FullChunk $chunk, Compound $nbt){ if($chunk === null or $chunk->getProvider() === null){ throw new ChunkException("Invalid garbage Chunk given to Entity"); } + $PF = 0x200; + if($this instanceof Living){ + $this->MASK |= 0x100 | $PF; + } + if($this instanceof Ageable){ + $this->MASK |= 0x400; + } + if($this instanceof Monster){ + $this->MASK |= 0x800; + } + if($this instanceof Animal){ + $this->MASK |= 0x1000; + } + if($this instanceof WaterAnimal){ + $this->MASK |= 0x2000; + } + if($this instanceof Tameable){ + $this->MASK |= 0x4000; + } + //AMBIENT = 0x100 | 0x8000; + $this->timings = Timings::getEntityTimings($this); if($this->eyeHeight === null){ diff --git a/src/pocketmine/entity/Living.php b/src/pocketmine/entity/Living.php index 37cb3b8a4..de45d18e4 100644 --- a/src/pocketmine/entity/Living.php +++ b/src/pocketmine/entity/Living.php @@ -167,17 +167,33 @@ abstract class Living extends Entity implements Damageable{ } if($this->dead !== true and !$this->hasEffect(Effect::WATER_BREATHING) and $this->isInsideOfWater()){ - $hasUpdate = true; - $airTicks = $this->getDataProperty(self::DATA_AIR) - $tickDiff; - if($airTicks <= -20){ - $airTicks = 0; + if($this instanceof WaterAnimal){ + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300); + }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->getFinalDamage(), $ev); + $ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_DROWNING, 2); + $this->attack($ev->getFinalDamage(), $ev); + } + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); } - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); }else{ - $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300); + 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->getFinalDamage(), $ev); + } + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, $airTicks); + }else{ + $this->setDataProperty(self::DATA_AIR, self::DATA_TYPE_SHORT, 300); + } } if($this->attackTime > 0){ diff --git a/src/pocketmine/entity/Squid.php b/src/pocketmine/entity/Squid.php new file mode 100644 index 000000000..6f8509323 --- /dev/null +++ b/src/pocketmine/entity/Squid.php @@ -0,0 +1,167 @@ +setMaxHealth(5); + parent::initEntity(); + } + + public function getName(){ + return "Squid"; + } + + public function attack($damage, EntityDamageEvent $source){ + parent::attack($damage, $source); + if($source->isCancelled()){ + return; + } + + if($source instanceof EntityDamageByEntityEvent){ + $this->swimSpeed = mt_rand(150, 350) / 2000; + $e = $source->getDamager(); + $this->swimDirection = (new Vector3($this->x - $e->x, $this->y - $e->y, $this->z - $e->z))->normalize(); + + $pk = new EntityEventPacket(); + $pk->eid = $this->getId(); + $pk->event = 15; + Server::broadcastPacket($this->hasSpawned, $pk); + } + } + + private function generateRandomDirection(){ + return new Vector3(mt_rand(-1000, 1000) / 1000, mt_rand(-500, 500) / 1000, mt_rand(-1000, 1000) / 1000); + } + + + public function onUpdate($currentTick){ + if($this->closed !== false){ + return false; + } + + if($currentTick % 20 === 0 and mt_rand(0, 100) < 5){ + $this->swimDirection = null; + } + + $this->lastUpdate = $currentTick; + + $this->timings->startTiming(); + + $hasUpdate = parent::onUpdate($currentTick); + + if(!$this->dead){ + + if($this->y > 62 and $this->swimDirection !== null){ + $this->swimDirection->y = -0.5; + } + + $inWater = $this->isInsideOfWater(); + if(!$inWater){ + $this->motionY -= $this->gravity; + $this->swimDirection = null; + }elseif($this->swimDirection !== null){ + if($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2 <= $this->swimDirection->lengthSquared()){ + $this->motionX = $this->swimDirection->x * $this->swimSpeed; + $this->motionY = $this->swimDirection->y * $this->swimSpeed; + $this->motionZ = $this->swimDirection->z * $this->swimSpeed; + } + }else{ + $this->swimDirection = $this->generateRandomDirection(); + $this->swimSpeed = mt_rand(50, 100) / 2000; + } + + $expectedPos = new Vector3($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); + + $this->move($this->motionX, $this->motionY, $this->motionZ); + + if($expectedPos->distanceSquared($this) > 0){ + $this->swimDirection = $this->generateRandomDirection(); + $this->swimSpeed = mt_rand(50, 100) / 2000; + } + + $friction = 1 - $this->drag; + + $this->motionX *= $friction; + $this->motionY *= 1 - $this->drag; + $this->motionZ *= $friction; + + $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); + $this->yaw = (-atan2($this->motionX, $this->motionZ) * 180 / M_PI); + $this->pitch = (-atan2($f, $this->motionY) * 180 / M_PI); + + if($this->onGround){ + $this->motionY *= -0.5; + } + + } + + $this->timings->stopTiming(); + + return $hasUpdate or !$this->onGround or $this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0; + } + + + public function spawnTo(Player $player){ + $pk = new AddEntityPacket(); + $pk->eid = $this->getId(); + $pk->type = Squid::NETWORK_ID | $this->MASK; + $pk->x = $this->x; + $pk->y = $this->y; + $pk->z = $this->z; + $pk->speedX = $this->motionX; + $pk->speedY = $this->motionY; + $pk->speedZ = $this->motionZ; + $pk->yaw = $this->yaw; + $pk->pitch = $this->pitch; + $pk->metadata = $this->dataProperties; + $player->dataPacket($pk); + + parent::spawnTo($player); + } + + public function getDrops(){ + return [ + ItemItem::get(ItemItem::DYE, 0, mt_rand(1, 3)) + ]; + } +} diff --git a/src/pocketmine/entity/Villager.php b/src/pocketmine/entity/Villager.php index 356174e7b..349575cf8 100644 --- a/src/pocketmine/entity/Villager.php +++ b/src/pocketmine/entity/Villager.php @@ -54,7 +54,7 @@ class Villager extends Creature implements NPC, Ageable{ public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); - $pk->type = Villager::NETWORK_ID; + $pk->type = Villager::NETWORK_ID | $this->MASK; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z; @@ -81,4 +81,8 @@ class Villager extends Creature implements NPC, Ageable{ public function getProfession(){ return $this->namedtag["Profession"]; } + + public function isBaby(){ + return $this->getDataFlag(self::DATA_AGEABLE_FLAGS, self::DATA_FLAG_BABY); + } } diff --git a/src/pocketmine/entity/WaterAnimal.php b/src/pocketmine/entity/WaterAnimal.php new file mode 100644 index 000000000..cfb2b033a --- /dev/null +++ b/src/pocketmine/entity/WaterAnimal.php @@ -0,0 +1,35 @@ +getDataProperty(self::DATA_AGEABLE_FLAGS) === null){ + $this->setDataProperty(self::DATA_AGEABLE_FLAGS, self::DATA_TYPE_BYTE, 0); + } + } + + public function isBaby(){ + return $this->getDataFlag(self::DATA_AGEABLE_FLAGS, self::DATA_FLAG_BABY); + } +} diff --git a/src/pocketmine/entity/Zombie.php b/src/pocketmine/entity/Zombie.php index a84d759d5..a828917e6 100644 --- a/src/pocketmine/entity/Zombie.php +++ b/src/pocketmine/entity/Zombie.php @@ -41,7 +41,7 @@ class Zombie extends Monster{ public function spawnTo(Player $player){ $pk = new AddEntityPacket(); $pk->eid = $this->getId(); - $pk->type = Zombie::NETWORK_ID; + $pk->type = Zombie::NETWORK_ID | $this->MASK; $pk->x = $this->x; $pk->y = $this->y; $pk->z = $this->z;