PocketMine-MP 5.21.2 git-a6534ecbbbcf369264567d27e5ed70f7f5be9816
Loading...
Searching...
No Matches
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
49use function abs;
50use function min;
51use function round;
52
53class FallingBlock extends Entity{
54 private const TAG_FALLING_BLOCK = "FallingBlock"; //TAG_Compound
55 private const TAG_TILE_ID = "TileID"; //TAG_Int
56 private const TAG_TILE = "Tile"; //TAG_Byte
57 private const TAG_DATA = "Data"; //TAG_Byte
58
59 public function getNetworkTypeId() : string{ return EntityIds::FALLING_BLOCK; }
60
61 protected Block $block;
62
63 public function __construct(Location $location, Block $block, ?CompoundTag $nbt = null){
64 $this->block = $block;
65 parent::__construct($location, $nbt);
66 }
67
68 protected function getInitialSizeInfo() : EntitySizeInfo{ return new EntitySizeInfo(0.98, 0.98); }
69
70 protected function getInitialDragMultiplier() : float{ return 0.02; }
71
72 protected function getInitialGravity() : float{ return 0.04; }
73
74 public static function parseBlockNBT(RuntimeBlockStateRegistry $factory, CompoundTag $nbt) : Block{
75
76 //TODO: 1.8+ save format
77 $blockDataUpgrader = GlobalBlockStateHandlers::getUpgrader();
78 if(($fallingBlockTag = $nbt->getCompoundTag(self::TAG_FALLING_BLOCK)) !== null){
79 try{
80 $blockStateData = $blockDataUpgrader->upgradeBlockStateNbt($fallingBlockTag);
82 throw new SavedDataLoadingException("Invalid falling block blockstate: " . $e->getMessage(), 0, $e);
83 }
84 }else{
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();
89 }else{
90 throw new SavedDataLoadingException("Missing legacy falling block info");
91 }
92 $damage = $nbt->getByte(self::TAG_DATA, 0);
93
94 try{
95 $blockStateData = $blockDataUpgrader->upgradeIntIdMeta($blockId, $damage);
96 }catch(BlockStateDeserializeException $e){
97 throw new SavedDataLoadingException("Invalid legacy falling block data: " . $e->getMessage(), 0, $e);
98 }
99 }
100
101 try{
102 $blockStateId = GlobalBlockStateHandlers::getDeserializer()->deserialize($blockStateData);
103 }catch(BlockStateDeserializeException $e){
104 throw new SavedDataLoadingException($e->getMessage(), 0, $e);
105 }
106
107 return $factory->fromStateId($blockStateId);
108 }
109
110 public function canCollideWith(Entity $entity) : bool{
111 return false;
112 }
113
114 public function canBeMovedByCurrents() : bool{
115 return false;
116 }
117
118 public function attack(EntityDamageEvent $source) : void{
119 if($source->getCause() === EntityDamageEvent::CAUSE_VOID){
120 parent::attack($source);
121 }
122 }
123
124 protected function entityBaseTick(int $tickDiff = 1) : bool{
125 if($this->closed){
126 return false;
127 }
128
129 $hasUpdate = parent::entityBaseTick($tickDiff);
130
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();
134
135 $this->block->position($world, $pos->x, $pos->y, $pos->z);
136
137 $blockTarget = null;
138 if($this->block instanceof Fallable){
139 $blockTarget = $this->block->tickFalling();
140 }
141
142 if($this->onGround || $blockTarget !== null){
143 $this->flagForDespawn();
144
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));
150 }else{
151 $ev = new EntityBlockChangeEvent($this, $block, $blockResult);
152 $ev->call();
153 if(!$ev->isCancelled()){
154 $b = $ev->getTo();
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);
158 }
159 }
160 }
161 $hasUpdate = true;
162 }
163 }
164
165 return $hasUpdate;
166 }
167
168 protected function onHitGround() : ?float{
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){
175 $ev = new EntityDamageByEntityEvent($this, $entity, EntityDamageEvent::CAUSE_FALLING_BLOCK, $damage);
176 $entity->attack($ev);
177 }
178 }
179 }
180 if(!$this->block->onHitGround($this)){
181 $this->flagForDespawn();
182 }
183 }
184 return null;
185 }
186
187 public function getBlock() : Block{
188 return $this->block;
189 }
190
191 public function saveNBT() : CompoundTag{
192 $nbt = parent::saveNBT();
193 $nbt->setTag(self::TAG_FALLING_BLOCK, GlobalBlockStateHandlers::getSerializer()->serialize($this->block->getStateId())->toNbt());
194
195 return $nbt;
196 }
197
198 public function getPickedItem() : ?Item{
199 return $this->block->asItem();
200 }
201
202 protected function syncNetworkData(EntityMetadataCollection $properties) : void{
203 parent::syncNetworkData($properties);
204
205 $properties->setInt(EntityMetadataProperties::VARIANT, TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId()));
206 }
207
208 public function getOffsetPosition(Vector3 $vector3) : Vector3{
209 return $vector3->add(0, 0.49, 0); //TODO: check if height affects this
210 }
211}
setTag(string $name, Tag $tag)