mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-07 02:08:21 +00:00
602 lines
13 KiB
PHP
602 lines
13 KiB
PHP
<?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/
|
|
*
|
|
*
|
|
*/
|
|
|
|
declare(strict_types=1);
|
|
|
|
/**
|
|
* All Block classes are in here
|
|
*/
|
|
namespace pocketmine\block;
|
|
|
|
use pocketmine\entity\Entity;
|
|
use pocketmine\item\Item;
|
|
use pocketmine\item\ItemFactory;
|
|
use pocketmine\item\Tool;
|
|
use pocketmine\level\Level;
|
|
use pocketmine\level\MovingObjectPosition;
|
|
use pocketmine\level\Position;
|
|
use pocketmine\math\AxisAlignedBB;
|
|
use pocketmine\math\Vector3;
|
|
use pocketmine\metadata\Metadatable;
|
|
use pocketmine\metadata\MetadataValue;
|
|
use pocketmine\Player;
|
|
use pocketmine\plugin\Plugin;
|
|
|
|
class Block extends Position implements BlockIds, Metadatable{
|
|
|
|
/**
|
|
* Returns a new Block instance with the specified ID, meta and position.
|
|
*
|
|
* This function redirects to {@link BlockFactory#get}.
|
|
*
|
|
* @param int $id
|
|
* @param int $meta
|
|
* @param Position|null $pos
|
|
*
|
|
* @return Block
|
|
*/
|
|
public static function get(int $id, int $meta = 0, Position $pos = null) : Block{
|
|
return BlockFactory::get($id, $meta, $pos);
|
|
}
|
|
|
|
/** @var int */
|
|
protected $id;
|
|
/** @var int */
|
|
protected $meta = 0;
|
|
/** @var string */
|
|
protected $fallbackName;
|
|
/** @var int|null */
|
|
protected $itemId;
|
|
|
|
/** @var AxisAlignedBB */
|
|
public $boundingBox = null;
|
|
|
|
/**
|
|
* @param int $id The block type's ID, 0-255
|
|
* @param int $meta Meta value of the block type
|
|
* @param string $name English name of the block type (TODO: implement translations)
|
|
* @param int $itemId The item ID of the block type, used for block picking and dropping items.
|
|
*/
|
|
public function __construct(int $id, int $meta = 0, string $name = "Unknown", int $itemId = null){
|
|
$this->id = $id;
|
|
$this->meta = $meta;
|
|
$this->fallbackName = $name;
|
|
$this->itemId = $itemId;
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function getName() : string{
|
|
return $this->fallbackName;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
final public function getId() : int{
|
|
return $this->id;
|
|
}
|
|
|
|
/**
|
|
* Returns the ID of the item form of the block.
|
|
* Used for drops for blocks (some blocks such as doors have a different item ID).
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getItemId() : int{
|
|
return $this->itemId ?? $this->getId();
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
final public function getDamage() : int{
|
|
return $this->meta;
|
|
}
|
|
|
|
/**
|
|
* @param int $meta
|
|
*/
|
|
final public function setDamage(int $meta){
|
|
if($meta < 0 or $meta > 0xf){
|
|
throw new \InvalidArgumentException("Block damage values must be 0-15, not $meta");
|
|
}
|
|
$this->meta = $meta;
|
|
}
|
|
|
|
/**
|
|
* Bitmask to use to remove superfluous information from block meta when getting its item form or name.
|
|
* This defaults to -1 (don't remove any data). Used to remove rotation data and bitflags from block drops.
|
|
*
|
|
* If your block should not have any meta value when it's dropped as an item, override this to return 0 in
|
|
* descendent classes.
|
|
*
|
|
* @return int
|
|
*/
|
|
public function getVariantBitmask() : int{
|
|
return -1;
|
|
}
|
|
|
|
/**
|
|
* Places the Block, using block space and block target, and side. Returns if the block has been placed.
|
|
*
|
|
* @param Item $item
|
|
* @param Block $blockReplace
|
|
* @param Block $blockClicked
|
|
* @param int $face
|
|
* @param Vector3 $facePos
|
|
* @param Player|null $player
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function place(Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $facePos, Player $player = null) : bool{
|
|
return $this->getLevel()->setBlock($this, $this, true, true);
|
|
}
|
|
|
|
/**
|
|
* Returns if the block can be broken with an specific Item
|
|
*
|
|
* @param Item $item
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function isBreakable(Item $item) : bool{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* Do the actions needed so the block is broken with the Item
|
|
*
|
|
* @param Item $item
|
|
* @param Player|null $player
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function onBreak(Item $item, Player $player = null) : bool{
|
|
return $this->getLevel()->setBlock($this, BlockFactory::get(Block::AIR), true, true);
|
|
}
|
|
|
|
/**
|
|
* Fires a block update on the Block
|
|
*
|
|
* @param int $type
|
|
*
|
|
* @return bool|int
|
|
*/
|
|
public function onUpdate(int $type){
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Do actions when activated by Item. Returns if it has done anything
|
|
*
|
|
* @param Item $item
|
|
* @param Player|null $player
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function onActivate(Item $item, Player $player = null) : bool{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns a base value used to compute block break times.
|
|
* @return float
|
|
*/
|
|
public function getHardness() : float{
|
|
return 10;
|
|
}
|
|
|
|
/**
|
|
* @deprecated
|
|
* @return float
|
|
*/
|
|
public function getResistance() : float{
|
|
return $this->getBlastResistance();
|
|
}
|
|
|
|
/**
|
|
* Returns the block's resistance to explosions. Usually 5x hardness.
|
|
* @return float
|
|
*/
|
|
public function getBlastResistance() : float{
|
|
return $this->getHardness() * 5;
|
|
}
|
|
|
|
/**
|
|
* @return int
|
|
*/
|
|
public function getToolType() : int{
|
|
return Tool::TYPE_NONE;
|
|
}
|
|
|
|
/**
|
|
* @return float
|
|
*/
|
|
public function getFrictionFactor() : float{
|
|
return 0.6;
|
|
}
|
|
|
|
/**
|
|
* @return int 0-15
|
|
*/
|
|
public function getLightLevel() : int{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the amount of light this block will filter out when light passes through this block.
|
|
* This value is used in light spread calculation.
|
|
*
|
|
* @return int 0-15
|
|
*/
|
|
public function getLightFilter() : int{
|
|
return 15;
|
|
}
|
|
|
|
/**
|
|
* Returns whether this block will diffuse sky light passing through it vertically.
|
|
* Diffusion means that full-strength sky light passing through this block will not be reduced, but will start being filtered below the block.
|
|
* Examples of this behaviour include leaves and cobwebs.
|
|
*
|
|
* Light-diffusing blocks are included by the heightmap.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function diffusesSkyLight() : bool{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns whether random block updates will be done on this block.
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function ticksRandomly() : bool{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* AKA: Block->isPlaceable
|
|
* @return bool
|
|
*/
|
|
public function canBePlaced() : bool{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* @param Block|null $with
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function canBeReplaced(Block $with = null) : bool{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* @return bool
|
|
*/
|
|
public function isTransparent() : bool{
|
|
return false;
|
|
}
|
|
|
|
public function isSolid() : bool{
|
|
return true;
|
|
}
|
|
|
|
/**
|
|
* AKA: Block->isFlowable
|
|
* @return bool
|
|
*/
|
|
public function canBeFlowedInto() : bool{
|
|
return false;
|
|
}
|
|
|
|
public function hasEntityCollision() : bool{
|
|
return false;
|
|
}
|
|
|
|
public function canPassThrough() : bool{
|
|
return false;
|
|
}
|
|
|
|
/**
|
|
* Returns whether entities can climb up this block.
|
|
* @return bool
|
|
*/
|
|
public function canClimb() : bool{
|
|
return false;
|
|
}
|
|
|
|
|
|
public function addVelocityToEntity(Entity $entity, Vector3 $vector){
|
|
|
|
}
|
|
|
|
/**
|
|
* Sets the block position to a new Position object
|
|
*
|
|
* @param Position $v
|
|
*/
|
|
final public function position(Position $v){
|
|
$this->x = (int) $v->x;
|
|
$this->y = (int) $v->y;
|
|
$this->z = (int) $v->z;
|
|
$this->level = $v->level;
|
|
$this->boundingBox = null;
|
|
}
|
|
|
|
/**
|
|
* Returns an array of Item objects to be dropped
|
|
*
|
|
* @param Item $item
|
|
*
|
|
* @return Item[]
|
|
*/
|
|
public function getDrops(Item $item) : array{
|
|
return [
|
|
ItemFactory::get($this->getItemId(), $this->getDamage() & $this->getVariantBitmask(), 1)
|
|
];
|
|
}
|
|
|
|
/**
|
|
* Returns the seconds that this block takes to be broken using an specific Item
|
|
*
|
|
* @param Item $item
|
|
*
|
|
* @return float
|
|
*/
|
|
public function getBreakTime(Item $item) : float{
|
|
$base = $this->getHardness() * 1.5;
|
|
if($this->canBeBrokenWith($item)){
|
|
if($this->getToolType() === Tool::TYPE_SHEARS and $item->isShears()){
|
|
$base /= 15;
|
|
}elseif(
|
|
($this->getToolType() === Tool::TYPE_PICKAXE and ($tier = $item->isPickaxe()) !== false) or
|
|
($this->getToolType() === Tool::TYPE_AXE and ($tier = $item->isAxe()) !== false) or
|
|
($this->getToolType() === Tool::TYPE_SHOVEL and ($tier = $item->isShovel()) !== false)
|
|
){
|
|
switch($tier){
|
|
case Tool::TIER_WOODEN:
|
|
$base /= 2;
|
|
break;
|
|
case Tool::TIER_STONE:
|
|
$base /= 4;
|
|
break;
|
|
case Tool::TIER_IRON:
|
|
$base /= 6;
|
|
break;
|
|
case Tool::TIER_DIAMOND:
|
|
$base /= 8;
|
|
break;
|
|
case Tool::TIER_GOLD:
|
|
$base /= 12;
|
|
break;
|
|
}
|
|
}
|
|
}else{
|
|
$base *= 3.33;
|
|
}
|
|
|
|
if($item->isSword()){
|
|
$base *= 0.5;
|
|
}
|
|
|
|
return $base;
|
|
}
|
|
|
|
public function canBeBrokenWith(Item $item) : bool{
|
|
return $this->getHardness() !== -1;
|
|
}
|
|
|
|
/**
|
|
* Returns the time in ticks which the block will fuel a furnace for.
|
|
* @return int
|
|
*/
|
|
public function getFuelTime() : int{
|
|
return 0;
|
|
}
|
|
|
|
/**
|
|
* Returns the Block on the side $side, works like Vector3::side()
|
|
*
|
|
* @param int $side
|
|
* @param int $step
|
|
*
|
|
* @return Block
|
|
*/
|
|
public function getSide($side, $step = 1){
|
|
if($this->isValid()){
|
|
return $this->getLevel()->getBlock(Vector3::getSide($side, $step));
|
|
}
|
|
|
|
return BlockFactory::get(Block::AIR, 0, Position::fromObject(Vector3::getSide($side, $step)));
|
|
}
|
|
|
|
/**
|
|
* @return string
|
|
*/
|
|
public function __toString(){
|
|
return "Block[" . $this->getName() . "] (" . $this->getId() . ":" . $this->getDamage() . ")";
|
|
}
|
|
|
|
/**
|
|
* Checks for collision against an AxisAlignedBB
|
|
*
|
|
* @param AxisAlignedBB $bb
|
|
*
|
|
* @return bool
|
|
*/
|
|
public function collidesWithBB(AxisAlignedBB $bb) : bool{
|
|
$bb2 = $this->getBoundingBox();
|
|
|
|
return $bb2 !== null and $bb->intersectsWith($bb2);
|
|
}
|
|
|
|
/**
|
|
* @param Entity $entity
|
|
*/
|
|
public function onEntityCollide(Entity $entity){
|
|
|
|
}
|
|
|
|
/**
|
|
* @return AxisAlignedBB|null
|
|
*/
|
|
public function getBoundingBox(){
|
|
if($this->boundingBox === null){
|
|
$this->boundingBox = $this->recalculateBoundingBox();
|
|
}
|
|
return $this->boundingBox;
|
|
}
|
|
|
|
/**
|
|
* @return AxisAlignedBB|null
|
|
*/
|
|
protected function recalculateBoundingBox(){
|
|
return new AxisAlignedBB(
|
|
$this->x,
|
|
$this->y,
|
|
$this->z,
|
|
$this->x + 1,
|
|
$this->y + 1,
|
|
$this->z + 1
|
|
);
|
|
}
|
|
|
|
/**
|
|
* @param Vector3 $pos1
|
|
* @param Vector3 $pos2
|
|
*
|
|
* @return MovingObjectPosition|null
|
|
*/
|
|
public function calculateIntercept(Vector3 $pos1, Vector3 $pos2){
|
|
$bb = $this->getBoundingBox();
|
|
if($bb === null){
|
|
return null;
|
|
}
|
|
|
|
$v1 = $pos1->getIntermediateWithXValue($pos2, $bb->minX);
|
|
$v2 = $pos1->getIntermediateWithXValue($pos2, $bb->maxX);
|
|
$v3 = $pos1->getIntermediateWithYValue($pos2, $bb->minY);
|
|
$v4 = $pos1->getIntermediateWithYValue($pos2, $bb->maxY);
|
|
$v5 = $pos1->getIntermediateWithZValue($pos2, $bb->minZ);
|
|
$v6 = $pos1->getIntermediateWithZValue($pos2, $bb->maxZ);
|
|
|
|
if($v1 !== null and !$bb->isVectorInYZ($v1)){
|
|
$v1 = null;
|
|
}
|
|
|
|
if($v2 !== null and !$bb->isVectorInYZ($v2)){
|
|
$v2 = null;
|
|
}
|
|
|
|
if($v3 !== null and !$bb->isVectorInXZ($v3)){
|
|
$v3 = null;
|
|
}
|
|
|
|
if($v4 !== null and !$bb->isVectorInXZ($v4)){
|
|
$v4 = null;
|
|
}
|
|
|
|
if($v5 !== null and !$bb->isVectorInXY($v5)){
|
|
$v5 = null;
|
|
}
|
|
|
|
if($v6 !== null and !$bb->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($this->x, $this->y, $this->z, $f, $vector->add($this->x, $this->y, $this->z));
|
|
}
|
|
|
|
public function setMetadata(string $metadataKey, MetadataValue $newMetadataValue){
|
|
if($this->getLevel() instanceof Level){
|
|
$this->getLevel()->getBlockMetadata()->setMetadata($this, $metadataKey, $newMetadataValue);
|
|
}
|
|
}
|
|
|
|
public function getMetadata(string $metadataKey){
|
|
if($this->getLevel() instanceof Level){
|
|
return $this->getLevel()->getBlockMetadata()->getMetadata($this, $metadataKey);
|
|
}
|
|
|
|
return null;
|
|
}
|
|
|
|
public function hasMetadata(string $metadataKey) : bool{
|
|
if($this->getLevel() instanceof Level){
|
|
return $this->getLevel()->getBlockMetadata()->hasMetadata($this, $metadataKey);
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
public function removeMetadata(string $metadataKey, Plugin $owningPlugin){
|
|
if($this->getLevel() instanceof Level){
|
|
$this->getLevel()->getBlockMetadata()->removeMetadata($this, $metadataKey, $owningPlugin);
|
|
}
|
|
}
|
|
}
|