PocketMine-MP 5.15.1 git-fb9a74e8799c71ed8292cfa53abe7a4c9204629d
FallingBlock.php
1<?php
2
3/*
4 *
5 * ____ _ _ __ __ _ __ __ ____
6 * | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
7 * | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
8 * | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
9 * |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
10 *
11 * This program is free software: you can redistribute it and/or modify
12 * it under the terms of the GNU Lesser General Public License as published by
13 * the Free Software Foundation, either version 3 of the License, or
14 * (at your option) any later version.
15 *
16 * @author PocketMine Team
17 * @link http://www.pocketmine.net/
18 *
19 *
20 */
21
22declare(strict_types=1);
23
24namespace pocketmine\entity\object;
25
48use function abs;
49use function min;
50use function round;
51
52class FallingBlock extends Entity{
53 private const TAG_FALLING_BLOCK = "FallingBlock"; //TAG_Compound
54 private const TAG_TILE_ID = "TileID"; //TAG_Int
55 private const TAG_TILE = "Tile"; //TAG_Byte
56 private const TAG_DATA = "Data"; //TAG_Byte
57
58 public static function getNetworkTypeId() : string{ return EntityIds::FALLING_BLOCK; }
59
60 protected Block $block;
61
62 public function __construct(Location $location, Block $block, ?CompoundTag $nbt = null){
63 $this->block = $block;
64 parent::__construct($location, $nbt);
65 }
66
67 protected function getInitialSizeInfo() : EntitySizeInfo{ return new EntitySizeInfo(0.98, 0.98); }
68
69 protected function getInitialDragMultiplier() : float{ return 0.02; }
70
71 protected function getInitialGravity() : float{ return 0.04; }
72
73 public static function parseBlockNBT(RuntimeBlockStateRegistry $factory, CompoundTag $nbt) : Block{
74
75 //TODO: 1.8+ save format
76 $blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
77 if(($fallingBlockTag = $nbt->getCompoundTag(self::TAG_FALLING_BLOCK)) !== null){
78 try{
79 $blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($fallingBlockTag);
80 }catch(BlockStateDeserializeException $e){
81 throw new SavedDataLoadingException("Invalid falling block blockstate: " . $e->getMessage(), 0, $e);
82 }
83 }else{
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();
88 }else{
89 throw new SavedDataLoadingException("Missing legacy falling block info");
90 }
91 $damage = $nbt->getByte(self::TAG_DATA, 0);
92
93 try{
94 $blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $damage);
95 }catch(BlockStateDeserializeException $e){
96 throw new SavedDataLoadingException("Invalid legacy falling block data: " . $e->getMessage(), 0, $e);
97 }
98 }
99
100 try{
101 $blockStateId = GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData);
102 }catch(BlockStateDeserializeException $e){
103 throw new SavedDataLoadingException($e->getMessage(), 0, $e);
104 }
105
106 return $factory->fromStateId($blockStateId);
107 }
108
109 public function canCollideWith(Entity $entity) : bool{
110 return false;
111 }
112
113 public function canBeMovedByCurrents() : bool{
114 return false;
115 }
116
117 public function attack(EntityDamageEvent $source) : void{
118 if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
119 parent::attack($source);
120 }
121 }
122
123 protected function entityBaseTick(int $tickDiff = 1) : bool{
124 if($this->closed){
125 return false;
126 }
127
128 $hasUpdate = parent::entityBaseTick($tickDiff);
129
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();
133
134 $this->block->position($world, $pos->x, $pos->y, $pos->z);
135
136 $blockTarget = null;
137 if($this->block instanceof Fallable){
138 $blockTarget = $this->block->tickFalling();
139 }
140
141 if($this->onGround || $blockTarget !== null){
142 $this->flagForDespawn();
143
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));
149 }else{
150 $ev = new EntityBlockChangeEvent($this, $block, $blockResult);
151 $ev->call();
152 if(!$ev->isCancelled()){
153 $b = $ev->getTo();
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);
157 }
158 }
159 }
160 $hasUpdate = true;
161 }
162 }
163
164 return $hasUpdate;
165 }
166
167 protected function onHitGround() : ?float{
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){
174 $ev = new EntityDamageByEntityEvent($this, $entity, EntityDamageEvent::CAUSE_FALLING_BLOCK, $damage);
175 $entity->attack($ev);
176 }
177 }
178 }
179 if(!$this->block->onHitGround($this)){
180 $this->flagForDespawn();
181 }
182 }
183 return null;
184 }
185
186 public function getBlock() : Block{
187 return $this->block;
188 }
189
190 public function saveNBT() : CompoundTag{
191 $nbt = parent::saveNBT();
192 $nbt->setTag(self::TAG_FALLING_BLOCK, GlobalBlockStateHandlers::getSerializer()->serialize($this->block->getStateId())->toNbt());
193
194 return $nbt;
195 }
196
197 protected function syncNetworkData(EntityMetadataCollection $properties) : void{
198 parent::syncNetworkData($properties);
199
200 $properties->setInt(EntityMetadataProperties::VARIANT, TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()));
201 }
202
203 public function getOffsetPosition(Vector3 $vector3) : Vector3{
204 return $vector3->add(0, 0.49, 0); //TODO: check if height affects this
205 }
206}
setTag(string $name, Tag $tag)