PocketMine-MP 5.21.2 git-a6534ecbbbcf369264567d27e5ed70f7f5be9816
Loading...
Searching...
No Matches
Painting.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
42use function ceil;
43
44class Painting extends Entity{
45 public const TAG_TILE_X = "TileX"; //TAG_Int
46 public const TAG_TILE_Y = "TileY"; //TAG_Int
47 public const TAG_TILE_Z = "TileZ"; //TAG_Int
48 public const TAG_FACING_JE = "Facing"; //TAG_Byte
49 public const TAG_DIRECTION_BE = "Direction"; //TAG_Byte
50 public const TAG_MOTIVE = "Motive"; //TAG_String
51
52 public function getNetworkTypeId() : string{ return EntityIds::PAINTING; }
53
54 public const DATA_TO_FACING = [
55 0 => Facing::SOUTH,
56 1 => Facing::WEST,
57 2 => Facing::NORTH,
58 3 => Facing::EAST
59 ];
60 private const FACING_TO_DATA = [
61 Facing::SOUTH => 0,
62 Facing::WEST => 1,
63 Facing::NORTH => 2,
64 Facing::EAST => 3
65 ];
66
67 protected Vector3 $blockIn;
68 protected int $facing;
69 protected PaintingMotive $motive;
70
71 public function __construct(Location $location, Vector3 $blockIn, int $facing, PaintingMotive $motive, ?CompoundTag $nbt = null){
72 $this->motive = $motive;
73 $this->blockIn = $blockIn->asVector3();
74 $this->facing = $facing;
75 parent::__construct($location, $nbt);
76 }
77
78 protected function getInitialSizeInfo() : EntitySizeInfo{
79 //these aren't accurate, but it doesn't matter since they aren't used (vanilla PC does something similar)
80 return new EntitySizeInfo(0.5, 0.5);
81 }
82
83 protected function getInitialDragMultiplier() : float{ return 1.0; }
84
85 protected function getInitialGravity() : float{ return 0.0; }
86
87 protected function initEntity(CompoundTag $nbt) : void{
88 $this->setMaxHealth(1);
89 $this->setHealth(1);
90 parent::initEntity($nbt);
91 }
92
93 public function saveNBT() : CompoundTag{
94 $nbt = parent::saveNBT();
95 $nbt->setInt(self::TAG_TILE_X, (int) $this->blockIn->x);
96 $nbt->setInt(self::TAG_TILE_Y, (int) $this->blockIn->y);
97 $nbt->setInt(self::TAG_TILE_Z, (int) $this->blockIn->z);
98
99 $nbt->setByte(self::TAG_FACING_JE, self::FACING_TO_DATA[$this->facing]);
100 $nbt->setByte(self::TAG_DIRECTION_BE, self::FACING_TO_DATA[$this->facing]); //Save both for full compatibility
101
102 $nbt->setString(self::TAG_MOTIVE, $this->motive->getName());
103
104 return $nbt;
105 }
106
107 protected function onDeath() : void{
108 parent::onDeath();
109
110 $drops = true;
111
112 if($this->lastDamageCause instanceof EntityDamageByEntityEvent){
113 $killer = $this->lastDamageCause->getDamager();
114 if($killer instanceof Player && !$killer->hasFiniteResources()){
115 $drops = false;
116 }
117 }
118
119 if($drops){
120 //non-living entities don't have a way to create drops generically yet
121 $this->getWorld()->dropItem($this->location, VanillaItems::PAINTING());
122 }
123 $this->getWorld()->addParticle($this->location->add(0.5, 0.5, 0.5), new BlockBreakParticle(VanillaBlocks::OAK_PLANKS()));
124 }
125
126 protected function recalculateBoundingBox() : void{
127 $side = $this->blockIn->getSide($this->facing);
128 $this->boundingBox = self::getPaintingBB($this->facing, $this->getMotive())->offset($side->x, $side->y, $side->z);
129 }
130
131 public function onNearbyBlockChange() : void{
132 parent::onNearbyBlockChange();
133
134 if(!self::canFit($this->getWorld(), $this->blockIn->getSide($this->facing), $this->facing, false, $this->getMotive())){
135 $this->kill();
136 }
137 }
138
139 public function onRandomUpdate() : void{
140 //NOOP
141 }
142
143 public function hasMovementUpdate() : bool{
144 return false;
145 }
146
147 protected function updateMovement(bool $teleport = false) : void{
148
149 }
150
151 public function canBeCollidedWith() : bool{
152 return false;
153 }
154
155 protected function sendSpawnPacket(Player $player) : void{
156 $player->getNetworkSession()->sendDataPacket(AddPaintingPacket::create(
157 $this->getId(), //TODO: entity unique ID
158 $this->getId(),
159 new Vector3(
160 ($this->boundingBox->minX + $this->boundingBox->maxX) / 2,
161 ($this->boundingBox->minY + $this->boundingBox->maxY) / 2,
162 ($this->boundingBox->minZ + $this->boundingBox->maxZ) / 2
163 ),
164 self::FACING_TO_DATA[$this->facing],
165 $this->motive->getName()
166 ));
167 }
168
169 public function getPickedItem() : ?Item{
170 return VanillaItems::PAINTING();
171 }
172
176 public function getMotive() : PaintingMotive{
177 return $this->motive;
178 }
179
180 public function getFacing() : int{
181 return $this->facing;
182 }
183
187 private static function getPaintingBB(int $facing, PaintingMotive $motive) : AxisAlignedBB{
188 $width = $motive->getWidth();
189 $height = $motive->getHeight();
190
191 $horizontalStart = (int) (ceil($width / 2) - 1);
192 $verticalStart = (int) (ceil($height / 2) - 1);
193
194 return AxisAlignedBB::one()
195 ->trim($facing, 15 / 16)
196 ->extend(Facing::rotateY($facing, true), $horizontalStart)
197 ->extend(Facing::rotateY($facing, false), -$horizontalStart + $width - 1)
198 ->extend(Facing::DOWN, $verticalStart)
199 ->extend(Facing::UP, -$verticalStart + $height - 1);
200 }
201
205 public static function canFit(World $world, Vector3 $blockIn, int $facing, bool $checkOverlap, PaintingMotive $motive) : bool{
206 $width = $motive->getWidth();
207 $height = $motive->getHeight();
208
209 $horizontalStart = (int) (ceil($width / 2) - 1);
210 $verticalStart = (int) (ceil($height / 2) - 1);
211
212 $rotatedFace = Facing::rotateY($facing, false);
213
214 $oppositeSide = Facing::opposite($facing);
215
216 $startPos = $blockIn->asVector3()->getSide(Facing::opposite($rotatedFace), $horizontalStart)->getSide(Facing::DOWN, $verticalStart);
217
218 for($w = 0; $w < $width; ++$w){
219 for($h = 0; $h < $height; ++$h){
220 $pos = $startPos->getSide($rotatedFace, $w)->getSide(Facing::UP, $h);
221
222 $block = $world->getBlockAt($pos->x, $pos->y, $pos->z);
223 if($block->isSolid() || !$block->getSide($oppositeSide)->isSolid()){
224 return false;
225 }
226 }
227 }
228
229 if($checkOverlap){
230 $bb = self::getPaintingBB($facing, $motive)->offset($blockIn->x, $blockIn->y, $blockIn->z);
231
232 foreach($world->getNearbyEntities($bb) as $entity){
233 if($entity instanceof self){
234 return false;
235 }
236 }
237 }
238
239 return true;
240 }
241}
static canFit(World $world, Vector3 $blockIn, int $facing, bool $checkOverlap, PaintingMotive $motive)
Definition Painting.php:205
setInt(string $name, int $value)
getBlockAt(int $x, int $y, int $z, bool $cached=true, bool $addToCache=true)
Definition World.php:1883
getNearbyEntities(AxisAlignedBB $bb, ?Entity $entity=null)
Definition World.php:2326