From 15d6fd86e2b221686865a1838e11cdab243cf084 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 12 Oct 2017 16:29:24 +0100 Subject: [PATCH] Added basic support for blocks with multiple AABBs, fixed stairs (#1303) --- src/pocketmine/block/Air.php | 4 + src/pocketmine/block/Block.php | 69 +++++++++++++--- src/pocketmine/block/Stair.php | 137 ++++++++++--------------------- src/pocketmine/entity/Entity.php | 4 +- src/pocketmine/level/Level.php | 32 ++++---- 5 files changed, 127 insertions(+), 119 deletions(-) diff --git a/src/pocketmine/block/Air.php b/src/pocketmine/block/Air.php index 392d103cb..8638bb96b 100644 --- a/src/pocketmine/block/Air.php +++ b/src/pocketmine/block/Air.php @@ -71,6 +71,10 @@ class Air extends Transparent{ return null; } + public function getCollisionBoxes() : array{ + return []; + } + public function getHardness() : float{ return -1; } diff --git a/src/pocketmine/block/Block.php b/src/pocketmine/block/Block.php index 5664bb865..e7b061ed2 100644 --- a/src/pocketmine/block/Block.php +++ b/src/pocketmine/block/Block.php @@ -69,6 +69,10 @@ class Block extends Position implements BlockIds, Metadatable{ /** @var AxisAlignedBB */ public $boundingBox = null; + + /** @var AxisAlignedBB[]|null */ + protected $collisionBoxes = null; + /** * @param int $id The block type's ID, 0-255 * @param int $meta Meta value of the block type @@ -489,9 +493,15 @@ class Block extends Position implements BlockIds, Metadatable{ * @return bool */ public function collidesWithBB(AxisAlignedBB $bb) : bool{ - $bb2 = $this->getBoundingBox(); + $bbs = $this->getCollisionBoxes(); - return $bb2 !== null and $bb->intersectsWith($bb2); + foreach($bbs as $bb2){ + if($bb->intersectsWith($bb2)){ + return true; + } + } + + return false; } /** @@ -501,6 +511,28 @@ class Block extends Position implements BlockIds, Metadatable{ } + /** + * @return AxisAlignedBB[] + */ + public function getCollisionBoxes() : array{ + if($this->collisionBoxes === null){ + $this->collisionBoxes = $this->recalculateCollisionBoxes(); + } + + return $this->collisionBoxes; + } + + /** + * @return AxisAlignedBB[] + */ + protected function recalculateCollisionBoxes() : array{ + if($bb = $this->recalculateBoundingBox()){ + return [$bb]; + } + + return []; + } + /** * @return AxisAlignedBB|null */ @@ -532,19 +564,36 @@ class Block extends Position implements BlockIds, Metadatable{ * @return MovingObjectPosition|null */ public function calculateIntercept(Vector3 $pos1, Vector3 $pos2) : ?MovingObjectPosition{ - $bb = $this->getBoundingBox(); - if($bb === null){ + $bbs = $this->getCollisionBoxes(); + if(empty($bbs)){ return null; } - $result = $bb->calculateIntercept($pos1, $pos2); - if($result !== null){ - $result->blockX = $this->x; - $result->blockY = $this->y; - $result->blockZ = $this->z; + /** @var MovingObjectPosition|null $currentHit */ + $currentHit = null; + /** @var int|float $currentDistance */ + $currentDistance = PHP_INT_MAX; + + foreach($bbs as $bb){ + $nextHit = $bb->calculateIntercept($pos1, $pos2); + if($nextHit === null){ + continue; + } + + $nextDistance = $nextHit->hitVector->distanceSquared($pos1); + if($nextDistance < $currentDistance){ + $currentHit = $nextHit; + $currentDistance = $nextDistance; + } } - return $result; + if($currentHit !== null){ + $currentHit->blockX = $this->x; + $currentHit->blockY = $this->y; + $currentHit->blockZ = $this->z; + } + + return $currentHit; } public function setMetadata(string $metadataKey, MetadataValue $newMetadataValue){ diff --git a/src/pocketmine/block/Stair.php b/src/pocketmine/block/Stair.php index 5bd36daf2..a65f07230 100644 --- a/src/pocketmine/block/Stair.php +++ b/src/pocketmine/block/Stair.php @@ -31,103 +31,56 @@ use pocketmine\Player; abstract class Stair extends Transparent{ - /* - public function collidesWithBB(AxisAlignedBB $bb, &$list = []){ - $damage = $this->getDamage(); - $j = $damage & 0x03; + protected function recalculateCollisionBoxes() : array{ + //TODO: handle corners - $f = 0; - $f1 = 0.5; - $f2 = 0.5; - $f3 = 1; + $minYSlab = ($this->meta & 0x04) === 0 ? 0 : 0.5; + $maxYSlab = $minYSlab + 0.5; - if(($damage & 0x04) > 0){ - $f = 0.5; - $f1 = 1; - $f2 = 0; - $f3 = 0.5; + $bbs = [ + new AxisAlignedBB( + $this->x, + $this->y + $minYSlab, + $this->z, + $this->x + 1, + $this->y + $maxYSlab, + $this->z + 1 + ) + ]; + + $minY = ($this->meta & 0x04) === 0 ? 0.5 : 0; + $maxY = $minY + 0.5; + + $rotationMeta = $this->meta & 0x03; + + $minX = $minZ = 0; + $maxX = $maxZ = 1; + + switch($rotationMeta){ + case 0: + $minX = 0.5; + break; + case 1: + $maxX = 0.5; + break; + case 2: + $minZ = 0.5; + break; + case 3: + $maxZ = 0.5; + break; } - if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( - $this->x, - $this->y + $f, - $this->z, - $this->x + 1, - $this->y + $f1, - $this->z + 1 - ))){ - $list[] = $bb2; - } + $bbs[] = new AxisAlignedBB( + $this->x + $minX, + $this->y + $minY, + $this->z + $minZ, + $this->x + $maxX, + $this->y + $maxY, + $this->z + $maxZ + ); - if($j === 0){ - if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( - $this->x + 0.5, - $this->y + $f2, - $this->z, - $this->x + 1, - $this->y + $f3, - $this->z + 1 - ))){ - $list[] = $bb2; - } - }elseif($j === 1){ - if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( - $this->x, - $this->y + $f2, - $this->z, - $this->x + 0.5, - $this->y + $f3, - $this->z + 1 - ))){ - $list[] = $bb2; - } - }elseif($j === 2){ - if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( - $this->x, - $this->y + $f2, - $this->z + 0.5, - $this->x + 1, - $this->y + $f3, - $this->z + 1 - ))){ - $list[] = $bb2; - } - }elseif($j === 3){ - if($bb->intersectsWith($bb2 = AxisAlignedBB::getBoundingBoxFromPool( - $this->x, - $this->y + $f2, - $this->z, - $this->x + 1, - $this->y + $f3, - $this->z + 0.5 - ))){ - $list[] = $bb2; - } - } - } - */ - - protected function recalculateBoundingBox() : ?AxisAlignedBB{ - - if(($this->getDamage() & 0x04) > 0){ - return new AxisAlignedBB( - $this->x, - $this->y + 0.5, - $this->z, - $this->x + 1, - $this->y + 1, - $this->z + 1 - ); - }else{ - return new AxisAlignedBB( - $this->x, - $this->y, - $this->z, - $this->x + 1, - $this->y + 0.5, - $this->z + 1 - ); - } + return $bbs; } public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $facePos, Player $player = null) : bool{ diff --git a/src/pocketmine/entity/Entity.php b/src/pocketmine/entity/Entity.php index f861ff5f1..1e3bfe351 100644 --- a/src/pocketmine/entity/Entity.php +++ b/src/pocketmine/entity/Entity.php @@ -1497,9 +1497,7 @@ abstract class Entity extends Location implements Metadatable{ public function isInsideOfSolid() : bool{ $block = $this->level->getBlock($this->temporalVector->setComponents(Math::floorFloat($this->x), Math::floorFloat($y = ($this->y + $this->getEyeHeight())), Math::floorFloat($this->z))); - $bb = $block->getBoundingBox(); - - return $bb !== null and $block->isSolid() and !$block->isTransparent() and $bb->intersectsWith($this->getBoundingBox()); + return $block->isSolid() and !$block->isTransparent() and $block->collidesWithBB($this->getBoundingBox()); } public function fastMove(float $dx, float $dy, float $dz) : bool{ diff --git a/src/pocketmine/level/Level.php b/src/pocketmine/level/Level.php index 4aa16f4eb..a86a87ef2 100644 --- a/src/pocketmine/level/Level.php +++ b/src/pocketmine/level/Level.php @@ -1188,7 +1188,9 @@ class Level implements ChunkManager, Metadatable{ for($y = $minY; $y <= $maxY; ++$y){ $block = $this->getBlock($this->temporalVector->setComponents($x, $y, $z)); if(!$block->canPassThrough() and $block->collidesWithBB($bb)){ - $collides[] = $block->getBoundingBox(); + foreach($block->getCollisionBoxes() as $blockBB){ + $collides[] = $blockBB; + } } } } @@ -1789,21 +1791,23 @@ class Level implements ChunkManager, Metadatable{ return false; } - if($hand->isSolid() === true and $hand->getBoundingBox() !== null){ - $entities = $this->getCollidingEntities($hand->getBoundingBox()); - foreach($entities as $e){ - if($e instanceof Arrow or $e instanceof DroppedItem or ($e instanceof Player and $e->isSpectator())){ - continue; + if($hand->isSolid()){ + foreach($hand->getCollisionBoxes() as $collisionBox){ + $entities = $this->getCollidingEntities($collisionBox); + foreach($entities as $e){ + if($e instanceof Arrow or $e instanceof DroppedItem or ($e instanceof Player and $e->isSpectator())){ + continue; + } + + return false; //Entity in block } - return false; //Entity in block - } - - if($player !== null){ - if(($diff = $player->getNextPosition()->subtract($player->getPosition())) and $diff->lengthSquared() > 0.00001){ - $bb = $player->getBoundingBox()->getOffsetBoundingBox($diff->x, $diff->y, $diff->z); - if($hand->getBoundingBox()->intersectsWith($bb)){ - return false; //Inside player BB + if($player !== null){ + if(($diff = $player->getNextPosition()->subtract($player->getPosition())) and $diff->lengthSquared() > 0.00001){ + $bb = $player->getBoundingBox()->getOffsetBoundingBox($diff->x, $diff->y, $diff->z); + if($collisionBox->intersectsWith($bb)){ + return false; //Inside player BB + } } } }