diff --git a/src/pocketmine/Player.php b/src/pocketmine/Player.php index 074d5c51a..eb999f3a2 100644 --- a/src/pocketmine/Player.php +++ b/src/pocketmine/Player.php @@ -1545,7 +1545,7 @@ class Player extends Human implements CommandSender, InventoryHolder, IPlayer{ new Float("", $this->pitch) ]), ]); - $arrow = new Arrow($this->chunk, $nbt); + $arrow = new Arrow($this->chunk, $nbt, $this); $arrow->spawnToAll(); } //} diff --git a/src/pocketmine/entity/Arrow.php b/src/pocketmine/entity/Arrow.php index df29c1990..bc9c2da92 100644 --- a/src/pocketmine/entity/Arrow.php +++ b/src/pocketmine/entity/Arrow.php @@ -22,8 +22,12 @@ namespace pocketmine\entity; +use pocketmine\event\entity\EntityDamageByEntityEvent; use pocketmine\event\entity\EntityDamageEvent; +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; @@ -36,9 +40,20 @@ class Arrow extends Projectile{ public $width = 0.5; public $length = 0.5; public $height = 0.5; + + /** @var Entity */ + public $shootingEntity = null; + protected $gravity = 0.05; protected $drag = 0.01; + private $damage = 6; + + public function __construct(FullChunk $chunk, Compound $nbt, Entity $shootingEntity = null){ + $this->shootingEntity = $shootingEntity; + parent::__construct($chunk, $nbt); + } + protected function initEntity(){ $this->namedtag->id = new String("id", "Arrow"); $this->setMaxHealth(1); @@ -46,6 +61,7 @@ class Arrow extends Projectile{ if(isset($this->namedtag->Age)){ $this->age = $this->namedtag["Age"]; } + } public function onUpdate(){ @@ -55,9 +71,64 @@ class Arrow extends Projectile{ return false; } + $movingObjectPosition = null; + $this->motionY -= $this->gravity; $this->inBlock = $this->checkObstruction($this->x, ($this->boundingBox->minY + $this->boundingBox->maxY) / 2, $this->z); + + $moveVector = new Vector3($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){ + $motion = sqrt($this->motionX ** 2 + $this->motionY ** 2 + $this->motionZ ** 2); + $damage = ceil($motion * $this->damage); + + + $ev = new EntityDamageByEntityEvent($this->shootingEntity === null ? $this : $this->shootingEntity, $movingObjectPosition->entityHit, EntityDamageEvent::CAUSE_PROJECTILE, $damage); + + $this->server->getPluginManager()->callEvent($ev); + + if(!$ev->isCancelled()){ + $movingObjectPosition->entityHit->attack($damage, $ev); + if($this->fireTicks > 0){ + $movingObjectPosition->entityHit->setOnFire(5); + } + $this->kill(); + } + } + } + $this->move($this->motionX, $this->motionY, $this->motionZ); $friction = 1 - $this->drag; @@ -81,6 +152,12 @@ class Arrow extends Projectile{ $this->motionZ = 0; } + if($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); + } + if($this->age > 1200){ $this->kill(); } diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index 1e1795313..5651b14a5 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -358,7 +358,7 @@ abstract class Entity extends Position implements Metadatable{ } public function canCollideWith(Entity $entity){ - return !$this->justCreated; + return !$this->justCreated and $entity !== $this; } protected function checkObstruction($x, $y, $z){ diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 55a502da7..c70ddfe4b 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -723,7 +723,6 @@ class Level implements ChunkManager, Metadatable{ return $collides; } - /** * Gets the Block object on the Vector3 location * diff --git a/src/pocketmine/level/MovingObjectPosition.php b/src/pocketmine/level/MovingObjectPosition.php new file mode 100644 index 000000000..82757d791 --- /dev/null +++ b/src/pocketmine/level/MovingObjectPosition.php @@ -0,0 +1,83 @@ +typeOfHit = 0; + $ob->blockX = $x; + $ob->blockY = $y; + $ob->blockZ = $z; + $ob->hitVector = new Vector3($hitVector->x, $hitVector->y, $hitVector->z); + return $ob; + } + + /** + * @param Entity $entity + * + * @return MovingObjectPosition + */ + public static function fromEntity(Entity $entity){ + $ob = new MovingObjectPosition; + $ob->typeOfHit = 1; + $ob->entityHit = $entity; + $ob->hitVector = new Vector3($entity->x, $entity->y, $entity->z); + return $ob; + } +} diff --git a/src/pocketmine/math/AxisAlignedBB.php b/src/pocketmine/math/AxisAlignedBB.php index 401167442..ebeb7e92d 100644 --- a/src/pocketmine/math/AxisAlignedBB.php +++ b/src/pocketmine/math/AxisAlignedBB.php @@ -20,6 +20,7 @@ */ namespace pocketmine\math; +use pocketmine\level\MovingObjectPosition; /** * WARNING: This class is available on the PocketMine-MP Zephir project. @@ -54,25 +55,27 @@ class AxisAlignedBB{ } public function addCoord($x, $y, $z){ + $vec = clone $this; + if($x < 0){ - $this->minX += $x; + $vec->minX += $x; }elseif($x > 0){ - $this->maxX += $x; + $vec->maxX += $x; } if($y < 0){ - $this->minY += $y; + $vec->minY += $y; }elseif($y > 0){ - $this->maxY += $y; + $vec->maxY += $y; } if($z < 0){ - $this->minZ += $z; + $vec->minZ += $z; }elseif($z > 0){ - $this->maxZ += $z; + $vec->maxZ += $z; } - return $this; + return $vec; } public function grow($x, $y, $z){ @@ -261,9 +264,81 @@ class AxisAlignedBB{ return $vector->x >= $this->minX and $vector->x <= $this->maxX and $vector->y >= $this->minY and $vector->y <= $this->maxY; } - /* - public function calculateIntercept(...){ - + + public function calculateIntercept(Vector3 $pos1, Vector3 $pos2){ + $v1 = $pos1->getIntermediateWithXValue($pos2, $this->minX); + $v2 = $pos1->getIntermediateWithXValue($pos2, $this->maxX); + $v3 = $pos1->getIntermediateWithYValue($pos2, $this->minY); + $v4 = $pos1->getIntermediateWithYValue($pos2, $this->maxY); + $v5 = $pos1->getIntermediateWithZValue($pos2, $this->minZ); + $v6 = $pos1->getIntermediateWithZValue($pos2, $this->maxZ); + + if($v1 !== null and !$this->isVectorInYZ($v1)){ + $v1 = null; + } + + if($v2 !== null and !$this->isVectorInYZ($v2)){ + $v2 = null; + } + + if($v3 !== null and !$this->isVectorInXZ($v3)){ + $v3 = null; + } + + if($v4 !== null and !$this->isVectorInXZ($v4)){ + $v4 = null; + } + + if($v5 !== null and !$this->isVectorInXY($v5)){ + $v5 = null; + } + + if($v6 !== null and !$this->isVectorInXY($v6)){ + $v6 = null; + } + + $vector = $v1; + + if($v2 !== null and ($vector === null or $pos1->distanceSquared($v2) < $pos1->distanceSquared($vector))){ + $vector = $v2; + } + + if($v3 !== null and ($vector === null or $pos1->distanceSquared($v3) < $pos1->distanceSquared($vector))){ + $vector = $v3; + } + + if($v4 !== null and ($vector === null or $pos1->distanceSquared($v4) < $pos1->distanceSquared($vector))){ + $vector = $v4; + } + + if($v5 !== null and ($vector === null or $pos1->distanceSquared($v5) < $pos1->distanceSquared($vector))){ + $vector = $v5; + } + + if($v6 !== null and ($vector === null or $pos1->distanceSquared($v6) < $pos1->distanceSquared($vector))){ + $vector = $v6; + } + + if($vector === null){ + return null; + } + + $f = -1; + + if($vector === $v1){ + $f = 4; + }elseif($vector === $v2){ + $f = 5; + }elseif($vector === $v3){ + $f = 0; + }elseif($vector === $v4){ + $f = 1; + }elseif($vector === $v5){ + $f = 2; + }elseif($vector === $v6){ + $f = 3; + } + + return MovingObjectPosition::fromBlock(0, 0, 0, $f, $vector); } - */ } \ No newline at end of file diff --git a/src/pocketmine/math/Vector3.php b/src/pocketmine/math/Vector3.php index 02d408840..ae118a6ba 100644 --- a/src/pocketmine/math/Vector3.php +++ b/src/pocketmine/math/Vector3.php @@ -200,6 +200,87 @@ class Vector3{ ); } + /** + * Returns a new vector with x value equal to the second parameter, along the line between this vector and the + * passed in vector, or null if not possible. + * + * @param Vector3 $v + * @param float $x + * + * @return Vector3 + */ + public function getIntermediateWithXValue(Vector3 $v, $x){ + $xDiff = $v->x - $this->x; + $yDiff = $v->y - $this->y; + $zDiff = $v->z - $this->z; + + if(($xDiff ** 2) < 1){ + return null; + } + + $f = ($x - $this->x) / $xDiff; + + if($f < 0 or $f > 1){ + return null; + }else{ + return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); + } + } + + /** + * Returns a new vector with y value equal to the second parameter, along the line between this vector and the + * passed in vector, or null if not possible. + * + * @param Vector3 $v + * @param float $y + * + * @return Vector3 + */ + public function getIntermediateWithYValue(Vector3 $v, $y){ + $xDiff = $v->x - $this->x; + $yDiff = $v->y - $this->y; + $zDiff = $v->z - $this->z; + + if(($yDiff ** 2) < 1){ + return null; + } + + $f = ($y - $this->y) / $yDiff; + + if($f < 0 or $f > 1){ + return null; + }else{ + return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); + } + } + + /** + * Returns a new vector with z value equal to the second parameter, along the line between this vector and the + * passed in vector, or null if not possible. + * + * @param Vector3 $v + * @param float $z + * + * @return Vector3 + */ + public function getIntermediateWithZValue(Vector3 $v, $z){ + $xDiff = $v->x - $this->x; + $yDiff = $v->y - $this->y; + $zDiff = $v->z - $this->z; + + if(($zDiff ** 2) < 1){ + return null; + } + + $f = ($z - $this->z) / $zDiff; + + if($f < 0 or $f > 1){ + return null; + }else{ + return new Vector3($this->x + $xDiff * $f, $this->y + $yDiff * $f, $this->z + $zDiff * $f); + } + } + public function __toString(){ return "Vector3(x=" . $this->x . ",y=" . $this->y . ",z=" . $this->z . ")"; }