diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 8c738c533..b8dfbf512 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -29,6 +29,7 @@ use pocketmine\entity\Entity; use pocketmine\entity\Human; use pocketmine\entity\Living; use pocketmine\entity\Projectile; +use pocketmine\entity\Snowball; use pocketmine\event\block\SignChangeEvent; use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; @@ -1629,7 +1630,42 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ $this->dataPacket($pk); break; }elseif($packet->face === 0xff){ - //TODO: add event + $item = $this->inventory->getItemInHand(); + if($item->getID() === Item::SNOWBALL){ + $nbt = new Compound("", [ + "Pos" => new Enum("Pos", [ + new Double("", $this->x), + new Double("", $this->y + $this->getEyeHeight()), + new Double("", $this->z) + ]), + "Motion" => new Enum("Motion", [ + new Double("", -sin($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)), + new Double("", -sin($this->pitch / 180 * M_PI)), + new Double("", cos($this->yaw / 180 * M_PI) * cos($this->pitch / 180 * M_PI)) + ]), + "Rotation" => new Enum("Rotation", [ + new Float("", $this->yaw), + new Float("", $this->pitch) + ]), + ]); + + $f = 1.5; + $snowball = new Snowball($this->chunk, $nbt, $this); + $snowball->setMotion($snowball->getMotion()->multiply($f)); + if($this->isSurvival()){ + $this->inventory->removeItem(Item::get(Item::SNOWBALL, 0, 1)); + } + if($snowball instanceof Projectile){ + $this->server->getPluginManager()->callEvent($projectileEv = ProjectileLaunchEvent::createEvent($snowball)); + if($projectileEv->isCancelled()){ + $snowball->kill(); + }else{ + $snowball->spawnToAll(); + } + }else{ + $snowball->spawnToAll(); + } + } $this->inAction = true; $this->startAction = microtime(true); $this->sendMetadata($this->getViewers()); diff --git a/src/pocketmine/entity/Arrow.php b/src/pocketmine/entity/Arrow.php index 086e5158a..f0a127a8b 100644 --- a/src/pocketmine/entity/Arrow.php +++ b/src/pocketmine/entity/Arrow.php @@ -21,17 +21,8 @@ namespace pocketmine\entity; - -use pocketmine\event\entity\EntityCombustByEntityEvent; -use pocketmine\event\entity\EntityDamageByEntityEvent; -use pocketmine\event\entity\EntityDamageEvent; -use pocketmine\event\entity\EntityRegainHealthEvent; -use pocketmine\event\entity\ProjectileHitEvent; use pocketmine\level\format\FullChunk; -use pocketmine\level\MovingObjectPosition; -use pocketmine\math\Vector3; use pocketmine\nbt\tag\Compound; -use pocketmine\nbt\tag\Short; use pocketmine\nbt\tag\String; use pocketmine\network\protocol\AddEntityPacket; use pocketmine\network\protocol\SetEntityMotionPacket; @@ -44,13 +35,10 @@ class Arrow extends Projectile{ public $length = 0.5; public $height = 0.5; - /** @var Entity */ - public $shootingEntity = null; - protected $gravity = 0.05; protected $drag = 0.01; - private $damage = 6; + protected $damage = 6; public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null){ $this->shootingEntity = $shootingEntity; @@ -59,11 +47,7 @@ class Arrow extends Projectile{ protected function initEntity(){ $this->namedtag->id = new String("id", "Arrow"); - $this->setMaxHealth(1); - $this->setHealth(1); - if(isset($this->namedtag->Age)){ - $this->age = $this->namedtag["Age"]; - } + parent::initEntity(); } @@ -74,105 +58,11 @@ class Arrow extends Projectile{ $this->timings->startTiming(); - $tickDiff = max(1, $currentTick - $this->lastUpdate); - $this->lastUpdate = $currentTick; - - $hasUpdate = $this->entityBaseTick($tickDiff); - - if(!$this->dead){ - - $movingObjectPosition = null; - - $this->motionY -= $this->gravity; - - $this->keepMovement = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z); - - $moveVector = Vector3::createVector($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); - - $list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this); - - $nearDistance = PHP_INT_MAX; - $nearEntity = null; - - foreach($list as $entity){ - if(/*!$entity->canCollideWith($this) or */ - ($entity === $this->shootingEntity and $this->ticksLived < 5) - ){ - continue; - } - - $axisalignedbb = $entity->boundingBox->grow(0.3, 0.3, 0.3); - $ob = $axisalignedbb->calculateIntercept($this, $moveVector); - - if($ob === null){ - continue; - } - - $distance = $this->distance($ob->hitVector); - - if($distance < $nearDistance){ - $nearDistance = $distance; - $nearEntity = $entity; - } - } - - if($nearEntity !== null){ - $movingObjectPosition = MovingObjectPosition::fromEntity($nearEntity); - } - - if($movingObjectPosition !== null){ - if($movingObjectPosition->entityHit !== null){ - - $this->server->getPluginManager()->callEvent(ProjectileHitEvent::createEvent($this)); - - $motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2); - $damage = ceil($motion * $this->damage); - - - $ev = EntityDamageByEntityEvent::createEvent($this->shootingEntity === null ? $this : $this->shootingEntity, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); - - $this->server->getPluginManager()->callEvent($ev); - - if(!$ev->isCancelled()){ - $movingObjectPosition->entityHit->attack($ev->getFinalDamage(), $ev); - } - - if($this->fireTicks > 0){ - $ev = EntityCombustByEntityEvent::createEvent($this, $movingObjectPosition->entityHit, 5); - $this->server->getPluginManager()->callEvent($ev); - if(!$ev->isCancelled()){ - $movingObjectPosition->entityHit->setOnFire($ev->getDuration()); - } - } - - $this->kill(); - return true; - } - } - - $this->move($this->motionX, $this->motionY, $this->motionZ); - - if($this->onGround and ($this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0)){ - $this->motionX = 0; - $this->motionY = 0; - $this->motionZ = 0; - - $this->server->getPluginManager()->callEvent(ProjectileHitEvent::createEvent($this)); - } - - if(!$this->onGround or $this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0){ - $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); - $this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI); - $this->pitch = (atan2($this->motionY, $f) * 180 / M_PI); - $hasUpdate = true; - } - - if($this->age > 1200){ - $this->kill(); - $hasUpdate = true; - } - $this->updateMovement(); + $hasUpdate = parent::onUpdate($currentTick); + if($this->age > 1200){ + $this->kill(); + $hasUpdate = true; } $this->timings->stopTiming(); @@ -180,34 +70,6 @@ class Arrow extends Projectile{ return $hasUpdate; } - public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ - - } - - public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){ - - } - - public function saveNBT(){ - parent::saveNBT(); - $this->namedtag->Age = new Short("Age", $this->age); - } - - public function getData(){ - $flags = 0; - $flags |= $this->fireTicks > 0 ? 1 : 0; - - return [ - 0 => ["type" => 0, "value" => $flags], - 1 => ["type" => 1, "value" => $this->airTicks], - 16 => ["type" => 0, "value" => 0] //Is critical - ]; - } - - public function canCollideWith(Entity $entity){ - return $entity instanceof Living and !$this->onGround; - } - public function spawnTo(Player $player){ $pk = AddEntityPacket::getFromPool(); $pk->type = Arrow::NETWORK_ID; diff --git a/src/pocketmine/entity/Projectile.php b/src/pocketmine/entity/Projectile.php index 320a49e53..3de60df75 100644 --- a/src/pocketmine/entity/Projectile.php +++ b/src/pocketmine/entity/Projectile.php @@ -22,6 +22,162 @@ namespace pocketmine\entity; +use pocketmine\event\entity\EntityCombustByEntityEvent; +use pocketmine\event\entity\EntityDamageByEntityEvent; +use pocketmine\event\entity\EntityDamageEvent; +use pocketmine\event\entity\EntityRegainHealthEvent; +use pocketmine\event\entity\ProjectileHitEvent; +use pocketmine\level\MovingObjectPosition; +use pocketmine\math\Vector3; +use pocketmine\nbt\tag\Short; + abstract class Projectile extends Entity{ + /** @var Entity */ + public $shootingEntity = null; + protected $damage = 0; + + protected function initEntity(){ + $this->setMaxHealth(1); + $this->setHealth(1); + if(isset($this->namedtag->Age)){ + $this->age = $this->namedtag["Age"]; + } + + } + + public function canCollideWith(Entity $entity){ + return $entity instanceof Living and !$this->onGround; + } + + public function getData(){ + $flags = 0; + $flags |= $this->fireTicks > 0 ? 1 : 0; + + return [ + 0 => ["type" => 0, "value" => $flags], + 1 => ["type" => 1, "value" => $this->airTicks], + 16 => ["type" => 0, "value" => 0] //Is critical + ]; + } + + public function saveNBT(){ + parent::saveNBT(); + $this->namedtag->Age = new Short("Age", $this->age); + } + + public function attack($damage, $source = EntityDamageEvent::CAUSE_MAGIC){ + + } + + public function heal($amount, $source = EntityRegainHealthEvent::CAUSE_MAGIC){ + + } + + public function onUpdate($currentTick){ + if($this->closed){ + return false; + } + + + + $tickDiff = max(1, $currentTick - $this->lastUpdate); + $this->lastUpdate = $currentTick; + + $hasUpdate = $this->entityBaseTick($tickDiff); + + if(!$this->dead){ + + $movingObjectPosition = null; + + $this->motionY -= $this->gravity; + + $this->keepMovement = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z); + + $moveVector = Vector3::createVector($this->x + $this->motionX, $this->y + $this->motionY, $this->z + $this->motionZ); + + $list = $this->getLevel()->getCollidingEntities($this->boundingBox->addCoord($this->motionX, $this->motionY, $this->motionZ)->expand(1, 1, 1), $this); + + $nearDistance = PHP_INT_MAX; + $nearEntity = null; + + foreach($list as $entity){ + if(/*!$entity->canCollideWith($this) or */ + ($entity === $this->shootingEntity and $this->ticksLived < 5) + ){ + continue; + } + + $axisalignedbb = $entity->boundingBox->grow(0.3, 0.3, 0.3); + $ob = $axisalignedbb->calculateIntercept($this, $moveVector); + + if($ob === null){ + continue; + } + + $distance = $this->distance($ob->hitVector); + + if($distance < $nearDistance){ + $nearDistance = $distance; + $nearEntity = $entity; + } + } + + if($nearEntity !== null){ + $movingObjectPosition = MovingObjectPosition::fromEntity($nearEntity); + } + + if($movingObjectPosition !== null){ + if($movingObjectPosition->entityHit !== null){ + + $this->server->getPluginManager()->callEvent(ProjectileHitEvent::createEvent($this)); + + $motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2); + $damage = ceil($motion * $this->damage); + + + $ev = EntityDamageByEntityEvent::createEvent($this->shootingEntity === null ? $this : $this->shootingEntity, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); + + $this->server->getPluginManager()->callEvent($ev); + + if(!$ev->isCancelled()){ + $movingObjectPosition->entityHit->attack($ev->getFinalDamage(), $ev); + } + + if($this->fireTicks > 0){ + $ev = EntityCombustByEntityEvent::createEvent($this, $movingObjectPosition->entityHit, 5); + $this->server->getPluginManager()->callEvent($ev); + if(!$ev->isCancelled()){ + $movingObjectPosition->entityHit->setOnFire($ev->getDuration()); + } + } + + $this->kill(); + return true; + } + } + + $this->move($this->motionX, $this->motionY, $this->motionZ); + + if($this->onGround and ($this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0)){ + $this->motionX = 0; + $this->motionY = 0; + $this->motionZ = 0; + + $this->server->getPluginManager()->callEvent(ProjectileHitEvent::createEvent($this)); + } + + if(!$this->onGround or $this->motionX != 0 or $this->motionY != 0 or $this->motionZ != 0){ + $f = sqrt(($this->motionX ** 2) + ($this->motionZ ** 2)); + $this->yaw = (atan2($this->motionX, $this->motionZ) * 180 / M_PI); + $this->pitch = (atan2($this->motionY, $f) * 180 / M_PI); + $hasUpdate = true; + } + + $this->updateMovement(); + + } + + return $hasUpdate; + } } \ No newline at end of file diff --git a/src/pocketmine/entity/Snowball.php b/src/pocketmine/entity/Snowball.php index 87761b64d..4d70cc8a3 100644 --- a/src/pocketmine/entity/Snowball.php +++ b/src/pocketmine/entity/Snowball.php @@ -22,10 +22,68 @@ namespace pocketmine\entity; +use pocketmine\level\format\FullChunk; +use pocketmine\nbt\tag\Compound; use pocketmine\nbt\tag\String; +use pocketmine\network\protocol\AddEntityPacket; +use pocketmine\network\protocol\SetEntityMotionPacket; +use pocketmine\Player; class Snowball extends Projectile{ + const NETWORK_ID = 81; + + public $width = 0.25; + public $length = 0.25; + public $height = 0.25; + + protected $gravity = 0.03; + protected $drag = 0.01; + + public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null){ + $this->shootingEntity = $shootingEntity; + parent::__construct($chunk, $nbt); + } + + public function onUpdate($currentTick){ + if($this->closed){ + return false; + } + + $this->timings->startTiming(); + + $hasUpdate = parent::onUpdate($currentTick); + + if($this->age > 1200 or $this->onGround){ + $this->kill(); + $hasUpdate = true; + } + + $this->timings->stopTiming(); + + return $hasUpdate; + } + protected function initEntity(){ $this->namedtag->id = new String("id", "Snowball"); + parent::initEntity(); + } + + public function spawnTo(Player $player){ + $pk = AddEntityPacket::getFromPool(); + $pk->type = Snowball::NETWORK_ID; + $pk->eid = $this->getID(); + $pk->x = $this->x; + $pk->y = $this->y; + $pk->z = $this->z; + $pk->did = 0; //TODO: send motion here + $player->dataPacket($pk); + + $pk = SetEntityMotionPacket::getFromPool(); + $pk->entities = [ + [$this->getID(), $this->motionX, $this->motionY, $this->motionZ] + ]; + $player->dataPacket($pk); + + parent::spawnTo($player); } } \ No newline at end of file