53 private const TAG_FALLING_BLOCK =
"FallingBlock";
54 private const TAG_TILE_ID =
"TileID";
55 private const TAG_TILE =
"Tile";
56 private const TAG_DATA =
"Data";
58 public static function getNetworkTypeId() :
string{
return EntityIds::FALLING_BLOCK; }
60 protected Block $block;
63 $this->block = $block;
64 parent::__construct($location, $nbt);
77 if(($fallingBlockTag = $nbt->
getCompoundTag(self::TAG_FALLING_BLOCK)) !==
null){
79 $blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($fallingBlockTag);
81 throw new SavedDataLoadingException(
"Invalid falling block blockstate: " . $e->getMessage(), 0, $e);
84 if(($tileIdTag = $nbt->
getTag(self::TAG_TILE_ID)) instanceof
IntTag){
85 $blockId = $tileIdTag->getValue();
86 }elseif(($tileTag = $nbt->
getTag(self::TAG_TILE)) instanceof ByteTag){
87 $blockId = $tileTag->getValue();
89 throw new SavedDataLoadingException(
"Missing legacy falling block info");
91 $damage = $nbt->getByte(self::TAG_DATA, 0);
94 $blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $damage);
95 }
catch(BlockStateDeserializeException $e){
96 throw new SavedDataLoadingException(
"Invalid legacy falling block data: " . $e->getMessage(), 0, $e);
101 $blockStateId = GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData);
102 }
catch(BlockStateDeserializeException $e){
103 throw new SavedDataLoadingException($e->getMessage(), 0, $e);
106 return $factory->fromStateId($blockStateId);
109 public function canCollideWith(Entity $entity) : bool{
119 parent::attack($source);
123 protected function entityBaseTick(
int $tickDiff = 1) : bool{
128 $hasUpdate = parent::entityBaseTick($tickDiff);
130 if(!$this->isFlaggedForDespawn()){
131 $world = $this->getWorld();
132 $pos = $this->location->add(-$this->size->getWidth() / 2, $this->size->getHeight(), -$this->size->getWidth() / 2)->floor();
134 $this->block->position($world, $pos->x, $pos->y, $pos->z);
137 if($this->block instanceof Fallable){
138 $blockTarget = $this->block->tickFalling();
141 if($this->onGround || $blockTarget !==
null){
142 $this->flagForDespawn();
144 $blockResult = $blockTarget ?? $this->block;
145 $block = $world->getBlock($pos);
146 if(!$block->
canBeReplaced() || !$world->isInWorld($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ()) || ($this->onGround && abs($this->location->y - $this->location->getFloorY()) > 0.001)){
147 $world->dropItem($this->location, $this->block->asItem());
148 $world->addSound($pos->add(0.5, 0.5, 0.5),
new BlockBreakSound($blockResult));
150 $ev =
new EntityBlockChangeEvent($this, $block, $blockResult);
152 if(!$ev->isCancelled()){
154 $world->setBlock($pos, $b);
155 if($this->onGround && $b instanceof Fallable && ($sound = $b->getLandSound()) !==
null){
156 $world->addSound($pos->add(0.5, 0.5, 0.5), $sound);
168 if($this->block instanceof
Fallable){
169 $damagePerBlock = $this->block->getFallDamagePerBlock();
170 if($damagePerBlock > 0 && ($fallenBlocks = round($this->fallDistance) - 1) > 0){
171 $damage = min($fallenBlocks * $damagePerBlock, $this->block->getMaxFallDamage());
172 foreach($this->getWorld()->getCollidingEntities($this->getBoundingBox()) as $entity){
173 if($entity instanceof
Living){
175 $entity->attack($ev);
179 if(!$this->block->onHitGround($this)){
180 $this->flagForDespawn();
186 public function getBlock() :
Block{
191 $nbt = parent::saveNBT();
192 $nbt->
setTag(self::TAG_FALLING_BLOCK, GlobalBlockStateHandlers::getSerializer()->serialize($this->block->getStateId())->toNbt());
197 protected function syncNetworkData(EntityMetadataCollection $properties) : void{
198 parent::syncNetworkData($properties);
200 $properties->setInt(EntityMetadataProperties::VARIANT, TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()));
203 public function getOffsetPosition(Vector3 $vector3) : Vector3{
204 return $vector3->add(0, 0.49, 0);