mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-22 08:44:01 +00:00
Added Living->getTargetBlock(), Living->getLineOfSight(), Vector3 side constants, Vector3::getOppositeSide()
This commit is contained in:
parent
6424934df6
commit
5c4e7b6ee0
@ -22,6 +22,7 @@
|
||||
namespace pocketmine\entity;
|
||||
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\event\entity\EntityDeathEvent;
|
||||
@ -32,6 +33,7 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\Short;
|
||||
use pocketmine\network\protocol\EntityEventPacket;
|
||||
use pocketmine\Server;
|
||||
use pocketmine\utils\BlockIterator;
|
||||
|
||||
abstract class Living extends Entity implements Damageable{
|
||||
|
||||
@ -168,4 +170,63 @@ abstract class Living extends Entity implements Damageable{
|
||||
public function getDrops(){
|
||||
return [];
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $maxDistance
|
||||
* @param int $maxLength
|
||||
* @param array $transparent
|
||||
*
|
||||
* @return Block[]
|
||||
*/
|
||||
public function getLineOfSight($maxDistance, $maxLength = 0, array $transparent = []){
|
||||
if($maxDistance > 120){
|
||||
$maxDistance = 120;
|
||||
}
|
||||
|
||||
if(count($transparent) === 0){
|
||||
$transparent = null;
|
||||
}
|
||||
|
||||
$blocks = [];
|
||||
$itr = new BlockIterator($this->level, $this->getPosition(), $this->getEyeHeight(), $maxDistance);
|
||||
|
||||
while($itr->valid()){
|
||||
$itr->next();
|
||||
$block = $itr->current();
|
||||
$blocks[] = $block;
|
||||
|
||||
if($maxLength !== 0 and count($blocks) > $maxLength){
|
||||
array_shift($blocks);
|
||||
}
|
||||
|
||||
$id = $block->getID();
|
||||
|
||||
if($transparent === null){
|
||||
if($id !== 0){
|
||||
break;
|
||||
}
|
||||
}else{
|
||||
if(!isset($transparent[$id])){
|
||||
break;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
return $blocks;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param int $maxDistance
|
||||
* @param array $transparent
|
||||
*
|
||||
* @return Block
|
||||
*/
|
||||
public function getTargetBlock($maxDistance, array $transparent = []){
|
||||
$block = array_shift($this->getLineOfSight($maxDistance, 1, $transparent));
|
||||
if($block instanceof Block){
|
||||
return $block;
|
||||
}
|
||||
|
||||
return null;
|
||||
}
|
||||
}
|
||||
|
@ -26,6 +26,14 @@ namespace pocketmine\math;
|
||||
* If this class is modified, remember to modify the PHP C extension.
|
||||
*/
|
||||
class Vector3{
|
||||
|
||||
const SIDE_DOWN = 0;
|
||||
const SIDE_UP = 1;
|
||||
const SIDE_NORTH = 2;
|
||||
const SIDE_SOUTH = 3;
|
||||
const SIDE_WEST = 4;
|
||||
const SIDE_EAST = 5;
|
||||
|
||||
public $x;
|
||||
public $y;
|
||||
public $z;
|
||||
@ -136,23 +144,42 @@ class Vector3{
|
||||
|
||||
public function getSide($side, $step = 1){
|
||||
switch((int) $side){
|
||||
case 0:
|
||||
case self::SIDE_DOWN:
|
||||
return new Vector3($this->x, $this->y - $step, $this->z);
|
||||
case 1:
|
||||
case self::SIDE_UP:
|
||||
return new Vector3($this->x, $this->y + $step, $this->z);
|
||||
case 2:
|
||||
case self::SIDE_NORTH:
|
||||
return new Vector3($this->x, $this->y, $this->z - $step);
|
||||
case 3:
|
||||
case self::SIDE_SOUTH:
|
||||
return new Vector3($this->x, $this->y, $this->z + $step);
|
||||
case 4:
|
||||
case self::SIDE_WEST:
|
||||
return new Vector3($this->x - $step, $this->y, $this->z);
|
||||
case 5:
|
||||
case self::SIDE_EAST:
|
||||
return new Vector3($this->x + $step, $this->y, $this->z);
|
||||
default:
|
||||
return $this;
|
||||
}
|
||||
}
|
||||
|
||||
public static function getOppositeSide($side){
|
||||
switch((int) $side){
|
||||
case self::SIDE_DOWN:
|
||||
return self::SIDE_UP;
|
||||
case self::SIDE_UP:
|
||||
return self::SIDE_DOWN;
|
||||
case self::SIDE_NORTH:
|
||||
return self::SIDE_SOUTH;
|
||||
case self::SIDE_SOUTH:
|
||||
return self::SIDE_NORTH;
|
||||
case self::SIDE_WEST:
|
||||
return self::SIDE_EAST;
|
||||
case self::SIDE_EAST:
|
||||
return self::SIDE_WEST;
|
||||
default:
|
||||
return -1;
|
||||
}
|
||||
}
|
||||
|
||||
public function distance(Vector3 $pos){
|
||||
return sqrt($this->distanceSquared($pos));
|
||||
}
|
||||
|
290
src/pocketmine/utils/BlockIterator.php
Normal file
290
src/pocketmine/utils/BlockIterator.php
Normal file
@ -0,0 +1,290 @@
|
||||
<?php
|
||||
|
||||
/*
|
||||
*
|
||||
* ____ _ _ __ __ _ __ __ ____
|
||||
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
|
||||
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
|
||||
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
|
||||
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
|
||||
*
|
||||
* This program is free software: you can redistribute it and/or modify
|
||||
* it under the terms of the GNU Lesser General Public License as published by
|
||||
* the Free Software Foundation, either version 3 of the License, or
|
||||
* (at your option) any later version.
|
||||
*
|
||||
* @author PocketMine Team
|
||||
* @link http://www.pocketmine.net/
|
||||
*
|
||||
*
|
||||
*/
|
||||
|
||||
namespace pocketmine\utils;
|
||||
|
||||
use pocketmine\level\Level;
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\math\Vector3;
|
||||
|
||||
/**
|
||||
* This class performs ray tracing and iterates along blocks on a line
|
||||
*/
|
||||
class BlockIterator implements \Iterator{
|
||||
|
||||
/** @var Level */
|
||||
private $level;
|
||||
private $maxDistance;
|
||||
|
||||
private static $gridSize = 16777216; //1 << 24
|
||||
|
||||
private $end = false;
|
||||
|
||||
/** @var Block[] */
|
||||
private $blockQueue = [null, null, null];
|
||||
private $currentBlock = 0;
|
||||
private $currentDistance = 0;
|
||||
private $maxDistanceInt = 0;
|
||||
|
||||
private $secondError;
|
||||
private $thirdError;
|
||||
|
||||
private $secondStep;
|
||||
private $thirdStep;
|
||||
|
||||
private $mainFace;
|
||||
private $secondFace;
|
||||
private $thirdFace;
|
||||
|
||||
public function __construct(Level $level, Vector3 $start, Vector3 $direction, $yOffset = 0, $maxDistance = 0){
|
||||
$this->level = $level;
|
||||
$this->maxDistance = (int) $maxDistance;
|
||||
|
||||
$startClone = clone $start;
|
||||
$startClone->y += $yOffset;
|
||||
|
||||
$this->currentDistance = 0;
|
||||
|
||||
$mainDirection = 0;
|
||||
$secondDirection = 0;
|
||||
$thirdDirection = 0;
|
||||
|
||||
$mainPosition = 0;
|
||||
$secondPosition = 0;
|
||||
$thirdPosition = 0;
|
||||
|
||||
$startBlock = $this->level->getBlock($startClone->floor());
|
||||
|
||||
if($this->getXLength($direction) > $mainDirection){
|
||||
$this->mainFace = $this->getXFace($direction);
|
||||
$mainDirection = $this->getXLength($direction);
|
||||
$mainPosition = $this->getXPosition($direction, $startClone, $startBlock);
|
||||
|
||||
$this->secondFace = $this->getYFace($direction);
|
||||
$secondDirection = $this->getYLength($direction);
|
||||
$secondPosition = $this->getYPosition($direction, $startClone, $startBlock);
|
||||
|
||||
$this->thirdFace = $this->getZFace($direction);
|
||||
$thirdDirection = $this->getZLength($direction);
|
||||
$thirdPosition = $this->getZPosition($direction, $startClone, $startBlock);
|
||||
}
|
||||
if($this->getYLength($direction) > $mainDirection){
|
||||
$this->mainFace = $this->getYFace($direction);
|
||||
$mainDirection = $this->getYLength($direction);
|
||||
$mainPosition = $this->getYPosition($direction, $startClone, $startBlock);
|
||||
|
||||
$this->secondFace = $this->getZFace($direction);
|
||||
$secondDirection = $this->getZLength($direction);
|
||||
$secondPosition = $this->getZPosition($direction, $startClone, $startBlock);
|
||||
|
||||
$this->thirdFace = $this->getXFace($direction);
|
||||
$thirdDirection = $this->getXLength($direction);
|
||||
$thirdPosition = $this->getXPosition($direction, $startClone, $startBlock);
|
||||
}
|
||||
if($this->getZLength($direction) > $mainDirection){
|
||||
$this->mainFace = $this->getZFace($direction);
|
||||
$mainDirection = $this->getZLength($direction);
|
||||
$mainPosition = $this->getZPosition($direction, $startClone, $startBlock);
|
||||
|
||||
$this->secondFace = $this->getXFace($direction);
|
||||
$secondDirection = $this->getXLength($direction);
|
||||
$secondPosition = $this->getXPosition($direction, $startClone, $startBlock);
|
||||
|
||||
$this->thirdFace = $this->getYFace($direction);
|
||||
$thirdDirection = $this->getYLength($direction);
|
||||
$thirdPosition = $this->getYPosition($direction, $startClone, $startBlock);
|
||||
}
|
||||
|
||||
$d = $mainPosition / $mainDirection;
|
||||
$secondd = $secondPosition - $secondDirection * $d;
|
||||
$thirdd = $thirdPosition - $thirdDirection * $d;
|
||||
|
||||
$this->secondError = floor($secondd * self::$gridSize);
|
||||
$this->secondStep = round($secondDirection / $mainDirection * self::$gridSize);
|
||||
$this->thirdError = floor($thirdd * self::$gridSize);
|
||||
$this->thirdStep = round($thirdDirection / $mainDirection * self::$gridSize);
|
||||
|
||||
if($this->secondError + $this->secondStep <= 0){
|
||||
$this->secondError = -$this->secondStep + 1;
|
||||
}
|
||||
|
||||
if($this->thirdError + $this->thirdStep <= 0){
|
||||
$this->thirdError = -$this->thirdStep + 1;
|
||||
}
|
||||
|
||||
$lastBlock = $startBlock->getSide(Vector3::getOppositeSide($this->mainFace));
|
||||
|
||||
if($this->secondError < 0){
|
||||
$this->secondError += self::$gridSize;
|
||||
$lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->secondFace));
|
||||
}
|
||||
|
||||
if($this->thirdError < 0){
|
||||
$this->thirdError += self::$gridSize;
|
||||
$lastBlock = $lastBlock->getSide(Vector3::getOppositeSide($this->thirdFace));
|
||||
}
|
||||
|
||||
$this->secondError -= self::$gridSize;
|
||||
$this->thirdError -= self::$gridSize;
|
||||
|
||||
$this->blockQueue[0] = $lastBlock;
|
||||
|
||||
$this->currentBlock = -1;
|
||||
|
||||
$this->scan();
|
||||
|
||||
$startBlockFound = false;
|
||||
|
||||
for($cnt = $this->currentBlock; $cnt >= 0; --$cnt){
|
||||
if($this->blockEquals($this->blockQueue[$cnt], $startBlock)){
|
||||
$this->currentBlock = $cnt;
|
||||
$startBlockFound = true;
|
||||
break;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$startBlockFound){
|
||||
throw new \Exception("Start block missed in BlockIterator");
|
||||
}
|
||||
|
||||
$this->maxDistanceInt = round($maxDistance / (sqrt($mainDirection ** 2 + $secondDirection ** 2 + $thirdDirection ** 2) / $mainDirection));
|
||||
}
|
||||
|
||||
private function blockEquals(Block $a, Block $b){
|
||||
return $a->x === $b->x and $a->y === $b->y and $a->z === $b->z;
|
||||
}
|
||||
|
||||
private function getXFace(Vector3 $direction){
|
||||
return (($direction->x) > 0) ? Vector3::SIDE_EAST : Vector3::SIDE_WEST;
|
||||
}
|
||||
|
||||
private function getYFace(Vector3 $direction){
|
||||
return (($direction->y) > 0) ? Vector3::SIDE_UP : Vector3::SIDE_DOWN;
|
||||
}
|
||||
|
||||
private function getZFace(Vector3 $direction){
|
||||
return (($direction->z) > 0) ? Vector3::SIDE_SOUTH : Vector3::SIDE_NORTH;
|
||||
}
|
||||
|
||||
private function getXLength(Vector3 $direction){
|
||||
return abs($direction->x);
|
||||
}
|
||||
|
||||
private function getYLength(Vector3 $direction){
|
||||
return abs($direction->y);
|
||||
}
|
||||
|
||||
private function getZLength(Vector3 $direction){
|
||||
return abs($direction->z);
|
||||
}
|
||||
|
||||
private function getPosition($direction, $position, $blockPosition){
|
||||
return $direction > 0 ? ($position - $blockPosition) : ($blockPosition + 1 - $position);
|
||||
}
|
||||
|
||||
private function getXPosition(Vector3 $direction, Vector3 $position, Block $block){
|
||||
return $this->getPosition($direction->x, $position->x, $block->x);
|
||||
}
|
||||
|
||||
private function getYPosition(Vector3 $direction, Vector3 $position, Block $block){
|
||||
return $this->getPosition($direction->y, $position->y, $block->y);
|
||||
}
|
||||
|
||||
private function getZPosition(Vector3 $direction, Vector3 $position, Block $block){
|
||||
return $this->getPosition($direction->z, $position->z, $block->z);
|
||||
}
|
||||
|
||||
public function next(){
|
||||
$this->scan();
|
||||
|
||||
if($this->currentBlock <= -1){
|
||||
throw new \OutOfBoundsException;
|
||||
}else{
|
||||
--$this->currentBlock;
|
||||
}
|
||||
}
|
||||
|
||||
public function current(){
|
||||
return $this->blockQueue[$this->currentBlock];
|
||||
}
|
||||
|
||||
public function rewind(){
|
||||
|
||||
}
|
||||
|
||||
public function key(){
|
||||
return $this->currentBlock;
|
||||
}
|
||||
|
||||
public function valid(){
|
||||
$this->scan();
|
||||
return $this->currentBlock !== -1;
|
||||
}
|
||||
|
||||
private function scan(){
|
||||
if($this->currentBlock >= 0){
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->maxDistance !== 0 and $this->currentDistance > $this->maxDistanceInt){
|
||||
$this->end = true;
|
||||
return;
|
||||
}
|
||||
|
||||
if($this->end){
|
||||
return;
|
||||
}
|
||||
|
||||
++$this->currentDistance;
|
||||
|
||||
$this->secondError += $this->secondStep;
|
||||
$this->thirdError += $this->thirdStep;
|
||||
|
||||
if($this->secondError > 0 and $this->thirdError > 0){
|
||||
$this->blockQueue[2] = $this->blockQueue[0]->getSide($this->mainFace);
|
||||
|
||||
if($this->secondStep * $this->thirdError < $this->thirdStep * $this->secondError){
|
||||
$this->blockQueue[1] = $this->blockQueue[2]->getSide($this->secondFace);
|
||||
$this->blockQueue[0] = $this->blockQueue[1]->getSide($this->thirdFace);
|
||||
}else{
|
||||
$this->blockQueue[1] = $this->blockQueue[2]->getSide($this->thirdFace);
|
||||
$this->blockQueue[0] = $this->blockQueue[1]->getSide($this->secondFace);
|
||||
}
|
||||
|
||||
$this->thirdError -= self::$gridSize;
|
||||
$this->secondError -= self::$gridSize;
|
||||
$this->currentBlock = 2;
|
||||
}elseif($this->secondError > 0){
|
||||
$this->blockQueue[1] = $this->blockQueue[0]->getSide($this->mainFace);
|
||||
$this->blockQueue[0] = $this->blockQueue[1]->getSide($this->thirdFace);
|
||||
$this->secondError -= self::$gridSize;
|
||||
$this->currentBlock = 1;
|
||||
}elseif($this->thirdError > 0){
|
||||
$this->blockQueue[1] = $this->blockQueue[0]->getSide($this->mainFace);
|
||||
$this->blockQueue[0] = $this->blockQueue[1]->getSide($this->secondFace);
|
||||
$this->thirdError -= self::$gridSize;
|
||||
$this->currentBlock = 1;
|
||||
}else{
|
||||
$this->blockQueue[0] = $this->blockQueue[0]->getSide($this->mainFace);
|
||||
$this->currentBlock = 0;
|
||||
}
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user