54    private const TAG_STUCK_ON_BLOCK_POS = 
"StuckToBlockPos";
 
   55    private const TAG_DAMAGE = 
"damage"; 
 
   56    private const TAG_TILE_X = 
"tileX"; 
 
   57    private const TAG_TILE_Y = 
"tileY"; 
 
   58    private const TAG_TILE_Z = 
"tileZ"; 
 
   60    protected float $damage = 0.0;
 
   61    protected ?
Vector3 $blockHit = 
null;
 
   64        parent::__construct($location, $nbt);
 
   65        if($shootingEntity !== 
null){
 
   71        if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
 
   72            parent::attack($source);
 
   76    protected function initEntity(
CompoundTag $nbt) : 
void{
 
   77        parent::initEntity($nbt);
 
   79        $this->setMaxHealth(1);
 
   81        $this->damage = $nbt->getDouble(self::TAG_DAMAGE, $this->damage);
 
   83        if(($stuckOnBlockPosTag = $nbt->
getListTag(self::TAG_STUCK_ON_BLOCK_POS, IntTag::class)) !== 
null){
 
   84            if(count($stuckOnBlockPosTag) !== 3){
 
   88            $values = $stuckOnBlockPosTag->getValue();
 
   90            $this->blockHit = 
new Vector3($values[0]->getValue(), $values[1]->getValue(), $values[2]->getValue());
 
   91        }elseif(($tileXTag = $nbt->
getTag(self::TAG_TILE_X)) instanceof 
IntTag && ($tileYTag = $nbt->
getTag(self::TAG_TILE_Y)) instanceof 
IntTag && ($tileZTag = $nbt->
getTag(self::TAG_TILE_Z)) instanceof 
IntTag){
 
   92            $this->blockHit = 
new Vector3($tileXTag->getValue(), $tileYTag->getValue(), $tileZTag->getValue());
 
   96    public function canCollideWith(
Entity $entity) : 
bool{
 
   97        return ($entity instanceof 
Living || $entity instanceof 
EndCrystal) && !$this->onGround;
 
  100    public function canBeCollidedWith() : 
bool{
 
  109        return $this->damage;
 
 
  116        $this->damage = $damage;
 
 
  123        return (int) ceil($this->damage);
 
 
  127        $nbt = parent::saveNBT();
 
  129        $nbt->
setDouble(self::TAG_DAMAGE, $this->damage);
 
  131        if($this->blockHit !== 
null){
 
  133                new IntTag($this->blockHit->getFloorX()),
 
  134                new IntTag($this->blockHit->getFloorY()),
 
  135                new IntTag($this->blockHit->getFloorZ())
 
  142    protected function applyDragBeforeGravity() : bool{
 
  146    public function onNearbyBlockChange() : void{
 
  147        if($this->blockHit !== null && $this->getWorld()->isInLoadedTerrain($this->blockHit)){
 
  148            $blockHit = $this->getWorld()->getBlock($this->blockHit);
 
  149            if(!$blockHit->collidesWithBB($this->getBoundingBox()->expandedCopy(0.001, 0.001, 0.001))){
 
  150                $this->blockHit = 
null;
 
  154        parent::onNearbyBlockChange();
 
  158        return $this->blockHit === null && parent::hasMovementUpdate();
 
 
  161    protected function move(
float $dx, 
float $dy, 
float $dz) : void{
 
  162        $this->blocksAround = null;
 
  164        Timings::$projectileMove->startTiming();
 
  165        Timings::$projectileMoveRayTrace->startTiming();
 
  167        $start = $this->location->asVector3();
 
  168        $end = $start->add($dx, $dy, $dz);
 
  172        $world = $this->getWorld();
 
  173        foreach(VoxelRayTrace::betweenPoints($start, $end) as $vector3){
 
  174            $block = $world->getBlockAt($vector3->x, $vector3->y, $vector3->z);
 
  176            $blockHitResult = $this->calculateInterceptWithBlock($block, $start, $end);
 
  177            if($blockHitResult !== 
null){
 
  178                $end = $blockHitResult->hitVector;
 
  179                $hitResult = [$block, $blockHitResult];
 
  184        $entityDistance = PHP_INT_MAX;
 
  186        $newDiff = $end->subtractVector($start);
 
  187        foreach($world->getCollidingEntities($this->boundingBox->addCoord($newDiff->x, $newDiff->y, $newDiff->z)->expandedCopy(1, 1, 1), $this) as $entity){
 
  188            if($entity->getId() === $this->getOwningEntityId() && $this->ticksLived < 5){
 
  192            $entityBB = $entity->boundingBox->expandedCopy(0.3, 0.3, 0.3);
 
  193            $entityHitResult = $entityBB->calculateIntercept($start, $end);
 
  195            if($entityHitResult === 
null){
 
  199            $distance = $this->location->distanceSquared($entityHitResult->hitVector);
 
  201            if($distance < $entityDistance){
 
  202                $entityDistance = $distance;
 
  203                $hitResult = [$entity, $entityHitResult];
 
  204                $end = $entityHitResult->hitVector;
 
  208        Timings::$projectileMoveRayTrace->stopTiming();
 
  210        $this->location = Location::fromObject(
 
  212            $this->location->world,
 
  213            $this->location->yaw,
 
  214            $this->location->pitch
 
  216        $this->recalculateBoundingBox();
 
  218        if($hitResult !== 
null){
 
  219            [$objectHit, $rayTraceResult] = $hitResult;
 
  220            if($objectHit instanceof Entity){
 
  221                $ev = 
new ProjectileHitEntityEvent($this, $rayTraceResult, $objectHit);
 
  222                $specificHitFunc = fn() => $this->onHitEntity($objectHit, $rayTraceResult);
 
  224                $ev = 
new ProjectileHitBlockEvent($this, $rayTraceResult, $objectHit);
 
  225                $specificHitFunc = fn() => $this->onHitBlock($objectHit, $rayTraceResult);
 
  228            $motionBeforeOnHit = clone $this->motion;
 
  233            $this->isCollided = $this->onGround = 
true;
 
  234            if($motionBeforeOnHit->equals($this->motion)){
 
  235                $this->motion = Vector3::zero();
 
  238            $this->isCollided = $this->onGround = 
false;
 
  239            $this->blockHit = 
null;
 
  242            $f = sqrt(($this->motion->x ** 2) + ($this->motion->z ** 2));
 
  244                atan2($this->motion->x, $this->motion->z) * 180 / M_PI,
 
  245                atan2($this->motion->y, $f) * 180 / M_PI
 
  249        $world->onEntityMoved($this);
 
  250        $this->checkBlockIntersections();
 
  252        Timings::$projectileMove->stopTiming();
 
  263        return $block->calculateIntercept($start, $end);
 
 
  278        $damage = $this->getResultDamage();
 
  281            $owner = $this->getOwningEntity();
 
  288            $entityHit->attack($ev);
 
  290            if($this->isOnFire()){
 
  293                if(!$ev->isCancelled()){
 
  294                    $entityHit->setOnFire($ev->getDuration());
 
  299        if($this->despawnsOnEntityHit()){
 
  300            $this->flagForDespawn();
 
 
  308        $this->blockHit = $blockHit->getPosition()->asVector3();