54 private const TAG_FALLING_BLOCK =
"FallingBlock";
55 private const TAG_TILE_ID =
"TileID";
56 private const TAG_TILE =
"Tile";
57 private const TAG_DATA =
"Data";
59 public static function getNetworkTypeId() :
string{
return EntityIds::FALLING_BLOCK; }
61 protected Block $block;
64 $this->block = $block;
65 parent::__construct($location, $nbt);
78 if(($fallingBlockTag = $nbt->
getCompoundTag(self::TAG_FALLING_BLOCK)) !==
null){
80 $blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($fallingBlockTag);
82 throw new SavedDataLoadingException(
"Invalid falling block blockstate: " . $e->getMessage(), 0, $e);
85 if(($tileIdTag = $nbt->
getTag(self::TAG_TILE_ID)) instanceof
IntTag){
86 $blockId = $tileIdTag->getValue();
87 }elseif(($tileTag = $nbt->
getTag(self::TAG_TILE)) instanceof ByteTag){
88 $blockId = $tileTag->getValue();
90 throw new SavedDataLoadingException(
"Missing legacy falling block info");
92 $damage = $nbt->getByte(self::TAG_DATA, 0);
95 $blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $damage);
96 }
catch(BlockStateDeserializeException $e){
97 throw new SavedDataLoadingException(
"Invalid legacy falling block data: " . $e->getMessage(), 0, $e);
102 $blockStateId = GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData);
103 }
catch(BlockStateDeserializeException $e){
104 throw new SavedDataLoadingException($e->getMessage(), 0, $e);
107 return $factory->fromStateId($blockStateId);
110 public function canCollideWith(Entity $entity) : bool{
120 parent::attack($source);
124 protected function entityBaseTick(
int $tickDiff = 1) : bool{
129 $hasUpdate = parent::entityBaseTick($tickDiff);
131 if(!$this->isFlaggedForDespawn()){
132 $world = $this->getWorld();
133 $pos = $this->location->add(-$this->size->getWidth() / 2, $this->size->getHeight(), -$this->size->getWidth() / 2)->floor();
135 $this->block->position($world, $pos->x, $pos->y, $pos->z);
138 if($this->block instanceof Fallable){
139 $blockTarget = $this->block->tickFalling();
142 if($this->onGround || $blockTarget !==
null){
143 $this->flagForDespawn();
145 $blockResult = $blockTarget ?? $this->block;
146 $block = $world->getBlock($pos);
147 if(!$block->
canBeReplaced() || !$world->isInWorld($pos->getFloorX(), $pos->getFloorY(), $pos->getFloorZ()) || ($this->onGround && abs($this->location->y - $this->location->getFloorY()) > 0.001)){
148 $world->dropItem($this->location, $this->block->asItem());
149 $world->addSound($pos->add(0.5, 0.5, 0.5),
new BlockBreakSound($blockResult));
151 $ev =
new EntityBlockChangeEvent($this, $block, $blockResult);
153 if(!$ev->isCancelled()){
155 $world->setBlock($pos, $b);
156 if($this->onGround && $b instanceof Fallable && ($sound = $b->getLandSound()) !==
null){
157 $world->addSound($pos->add(0.5, 0.5, 0.5), $sound);
169 if($this->block instanceof
Fallable){
170 $damagePerBlock = $this->block->getFallDamagePerBlock();
171 if($damagePerBlock > 0 && ($fallenBlocks = round($this->fallDistance) - 1) > 0){
172 $damage = min($fallenBlocks * $damagePerBlock, $this->block->getMaxFallDamage());
173 foreach($this->getWorld()->getCollidingEntities($this->getBoundingBox()) as $entity){
174 if($entity instanceof
Living){
176 $entity->attack($ev);
180 if(!$this->block->onHitGround($this)){
181 $this->flagForDespawn();
187 public function getBlock() :
Block{
192 $nbt = parent::saveNBT();
193 $nbt->
setTag(self::TAG_FALLING_BLOCK, GlobalBlockStateHandlers::getSerializer()->serialize($this->block->getStateId())->toNbt());
199 return $this->block->asItem();
203 parent::syncNetworkData($properties);
205 $properties->setInt(EntityMetadataProperties::VARIANT, TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()));
209 return $vector3->add(0, 0.49, 0);