PocketMine-MP 5.39.3 git-400eb2dddf91a9c112aa09f3b498ffc8c85e98ed
Loading...
Searching...
No Matches
BaseSign.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\block;
25
26use pocketmine\block\tile\Sign as TileSign;
27use pocketmine\block\utils\DyeColor;
29use pocketmine\block\utils\SupportType;
31use pocketmine\block\utils\WoodType;
32use pocketmine\block\utils\WoodTypeTrait;
44use function abs;
45use function array_map;
46use function assert;
47use function atan2;
48use function fmod;
49use function rad2deg;
50use function strlen;
51
52abstract class BaseSign extends Transparent implements WoodMaterial{
53 use WoodTypeTrait;
54
55 protected SignText $text; //TODO: rename this (BC break)
56 protected SignText $backText;
57 private bool $waxed = false;
58
59 protected ?int $editorEntityRuntimeId = null;
60
62 private \Closure $asItemCallback;
63
67 public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, WoodType $woodType, \Closure $asItemCallback){
68 $this->woodType = $woodType;
69 parent::__construct($idInfo, $name, $typeInfo);
70 $this->text = new SignText();
71 $this->backText = new SignText();
72 $this->asItemCallback = $asItemCallback;
73 }
74
75 public function readStateFromWorld() : Block{
76 parent::readStateFromWorld();
77 $tile = $this->position->getWorld()->getTile($this->position);
78 if($tile instanceof TileSign){
79 $this->text = $tile->getText();
80 $this->backText = $tile->getBackText();
81 $this->waxed = $tile->isWaxed();
82 $this->editorEntityRuntimeId = $tile->getEditorEntityRuntimeId();
83 }
84
85 return $this;
86 }
87
88 public function writeStateToWorld() : void{
89 parent::writeStateToWorld();
90 $tile = $this->position->getWorld()->getTile($this->position);
91 assert($tile instanceof TileSign);
92 $tile->setText($this->text);
93 $tile->setBackText($this->backText);
94 $tile->setWaxed($this->waxed);
95 $tile->setEditorEntityRuntimeId($this->editorEntityRuntimeId);
96 }
97
98 public function isSolid() : bool{
99 return false;
100 }
101
102 public function getMaxStackSize() : int{
103 return 16;
104 }
105
106 protected function recalculateCollisionBoxes() : array{
107 return [];
108 }
109
110 public function getSupportType(int $facing) : SupportType{
111 return SupportType::NONE;
112 }
113
114 abstract protected function getSupportingFace() : int;
115
116 public function onNearbyBlockChange() : void{
117 if($this->getSide($this->getSupportingFace())->getTypeId() === BlockTypeIds::AIR){
118 $this->position->getWorld()->useBreakOn($this->position);
119 }
120 }
121
122 public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
123 if($player !== null){
124 $this->editorEntityRuntimeId = $player->getId();
125 }
126 return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
127 }
128
129 public function onPostPlace() : void{
130 $player = $this->editorEntityRuntimeId !== null ?
131 $this->position->getWorld()->getEntity($this->editorEntityRuntimeId) :
132 null;
133 //TODO: HACK! We really shouldn't be keeping disconnected players (and generally flagged-for-despawn entities)
134 //in the world's entity table, but changing that is too risky for a hotfix. This workaround will do for now.
135 if($player instanceof Player && $player->isConnected()){
136 $player->openSignEditor($this->position);
137 }
138 }
139
140 private function doSignChange(SignText $newText, Player $player, Item $item, bool $frontFace) : bool{
141 $ev = new SignChangeEvent($this, $player, $newText, $frontFace);
142 $ev->call();
143 if(!$ev->isCancelled()){
144 $this->setFaceText($frontFace, $ev->getNewText());
145 $this->position->getWorld()->setBlock($this->position, $this);
146 $item->pop();
147 return true;
148 }
149
150 return false;
151 }
152
153 private function changeSignGlowingState(bool $glowing, Player $player, Item $item, bool $frontFace) : bool{
154 $text = $this->getFaceText($frontFace);
155 if($text->isGlowing() !== $glowing && $this->doSignChange(new SignText($text->getLines(), $text->getBaseColor(), $glowing), $player, $item, $frontFace)){
156 $this->position->getWorld()->addSound($this->position, new InkSacUseSound());
157 return true;
158 }
159 return false;
160 }
161
162 private function wax(Player $player, Item $item) : bool{
163 if($this->waxed){
164 return false;
165 }
166
167 $this->waxed = true;
168 $this->position->getWorld()->setBlock($this->position, $this);
169 $item->pop();
170
171 return true;
172 }
173
174 public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
175 if($player === null){
176 return false;
177 }
178 if($this->waxed){
179 return true;
180 }
181
182 $frontFace = $this->interactsFront($this->getHitboxCenter(), $player->getPosition(), $this->getFacingDegrees());
183
184 $dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
185 ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
186 ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
187 ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
188 default => null
189 };
190 if($dyeColor !== null){
191 $color = $dyeColor === DyeColor::BLACK ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
192 $text = $this->getFaceText($frontFace);
193 if(
194 $color->toARGB() !== $text->getBaseColor()->toARGB() &&
195 $this->doSignChange(new SignText($text->getLines(), $color, $text->isGlowing()), $player, $item, $frontFace)
196 ){
197 $this->position->getWorld()->addSound($this->position, new DyeUseSound());
198 return true;
199 }
200 }elseif(match($item->getTypeId()){
201 ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item, $frontFace),
202 ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item, $frontFace),
203 ItemTypeIds::HONEYCOMB => $this->wax($player, $item),
204 default => false
205 }){
206 return true;
207 }
208
209 $player->openSignEditor($this->position, $frontFace);
210
211 return true;
212 }
213
214 private function interactsFront(Vector3 $hitboxCenter, Vector3 $playerPosition, float $signFacingDegrees) : bool{
215 $playerCenterDiffX = $playerPosition->x - $hitboxCenter->x;
216 $playerCenterDiffZ = $playerPosition->z - $hitboxCenter->z;
217
218 $f1 = rad2deg(atan2($playerCenterDiffZ, $playerCenterDiffX)) - 90.0;
219
220 $rotationDiff = $signFacingDegrees - $f1;
221 $rotation = fmod($rotationDiff + 180.0, 360.0) - 180.0; // Normalize to [-180, 180]
222 return abs($rotation) <= 90.0;
223 }
224
228 protected function getHitboxCenter() : Vector3{
229 return $this->position->add(0.5, 0.5, 0.5);
230 }
231
235 protected function getFacingDegrees() : float{
236 return 0;
237 }
238
244 public function getText() : SignText{
245 return $this->text;
246 }
247
253 public function setText(SignText $text) : self{
254 $this->text = $text;
255 return $this;
256 }
257
258 public function getFaceText(bool $frontFace) : SignText{
259 return $frontFace ? $this->text : $this->backText;
260 }
261
263 public function setFaceText(bool $frontFace, SignText $text) : self{
264 $frontFace ? $this->text = $text : $this->backText = $text;
265 return $this;
266 }
267
271 public function isWaxed() : bool{ return $this->waxed; }
272
274 public function setWaxed(bool $waxed) : self{
275 $this->waxed = $waxed;
276 return $this;
277 }
278
284 public function getEditorEntityRuntimeId() : ?int{ return $this->editorEntityRuntimeId; }
285
287 public function setEditorEntityRuntimeId(?int $editorEntityRuntimeId) : self{
288 $this->editorEntityRuntimeId = $editorEntityRuntimeId;
289 return $this;
290 }
291
296 public function updateText(Player $author, SignText $text) : bool{
297 return $this->updateFaceText($author, true, $text);
298 }
299
306 public function updateFaceText(Player $author, bool $frontFace, SignText $text) : bool{
307 $size = 0;
308 foreach($text->getLines() as $line){
309 $size += strlen($line);
310 }
311 if($size > 1000){
312 throw new \UnexpectedValueException($author->getName() . " tried to write $size bytes of text onto a sign (bigger than max 1000)");
313 }
314 $oldText = $this->getFaceText($frontFace);
315 $ev = new SignChangeEvent($this, $author, new SignText(array_map(function(string $line) : string{
316 return TextFormat::clean($line, false);
317 }, $text->getLines()), $oldText->getBaseColor(), $oldText->isGlowing()), $frontFace);
318 if($this->waxed || $this->editorEntityRuntimeId !== $author->getId()){
319 $ev->cancel();
320 }
321 $ev->call();
322 if(!$ev->isCancelled()){
323 $this->setFaceText($frontFace, $ev->getNewText());
324 $this->setEditorEntityRuntimeId(null);
325 $this->position->getWorld()->setBlock($this->position, $this);
326 return true;
327 }
328
329 return false;
330 }
331
332 public function asItem() : Item{
333 return ($this->asItemCallback)();
334 }
335
336 public function getFuelTime() : int{
337 return $this->woodType->isFlammable() ? 200 : 0;
338 }
339}
setEditorEntityRuntimeId(?int $editorEntityRuntimeId)
Definition BaseSign.php:287
updateText(Player $author, SignText $text)
Definition BaseSign.php:296
onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player=null, array &$returnedItems=[])
Definition BaseSign.php:174
place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player=null)
Definition BaseSign.php:122
updateFaceText(Player $author, bool $frontFace, SignText $text)
Definition BaseSign.php:306
setFaceText(bool $frontFace, SignText $text)
Definition BaseSign.php:263
getSupportType(int $facing)
Definition BaseSign.php:110
setText(SignText $text)
Definition BaseSign.php:253
__construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, WoodType $woodType, \Closure $asItemCallback)
Definition BaseSign.php:67
openSignEditor(Vector3 $position, bool $frontFace=true)
Definition Player.php:2881