58 private bool $waxed =
false;
60 protected ?
int $editorEntityRuntimeId =
null;
63 private \Closure $asItemCallback;
69 $this->woodType = $woodType;
70 parent::__construct($idInfo, $name, $typeInfo);
73 $this->asItemCallback = $asItemCallback;
78 $tile = $this->position->getWorld()->getTile($this->position);
79 if($tile instanceof TileSign){
80 $this->text = $tile->getText();
81 $this->backText = $tile->getBackText();
82 $this->waxed = $tile->isWaxed();
83 $this->editorEntityRuntimeId = $tile->getEditorEntityRuntimeId();
90 parent::writeStateToWorld();
91 $tile = $this->position->getWorld()->getTile($this->position);
92 assert($tile instanceof TileSign);
93 $tile->setText($this->text);
94 $tile->setBackText($this->backText);
95 $tile->setWaxed($this->waxed);
96 $tile->setEditorEntityRuntimeId($this->editorEntityRuntimeId);
112 return SupportType::NONE;
115 abstract protected function getSupportingFace() :
Facing;
118 if($this->getSide($this->getSupportingFace())->getTypeId() ===
BlockTypeIds::AIR){
119 $this->position->getWorld()->useBreakOn($this->position);
124 if($player !== null){
125 $this->editorEntityRuntimeId = $player->getId();
127 return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
131 $player = $this->editorEntityRuntimeId !== null ?
132 $this->position->getWorld()->getEntity($this->editorEntityRuntimeId) :
134 if($player instanceof
Player){
135 $player->openSignEditor($this->position);
139 private function doSignChange(SignText $newText,
Player $player, Item $item,
bool $frontFace) : bool{
140 $ev = new SignChangeEvent($this, $player, $newText, $frontFace);
142 if(!$ev->isCancelled()){
143 $this->setFaceText($frontFace, $ev->getNewText());
144 $this->position->getWorld()->setBlock($this->position, $this);
152 private function changeSignGlowingState(
bool $glowing, Player $player, Item $item,
bool $frontFace) : bool{
153 $text = $this->getFaceText($frontFace);
154 if($text->
isGlowing() !== $glowing && $this->doSignChange(
new SignText($text->
getLines(), $text->
getBaseColor(), $glowing), $player, $item, $frontFace)){
155 $this->position->getWorld()->addSound($this->position, new InkSacUseSound());
161 private function wax(Player $player, Item $item) : bool{
167 $this->position->getWorld()->setBlock($this->position, $this);
174 if($player === null){
181 $frontFace = $this->interactsFront($this->getHitboxCenter(), $player->getPosition(), $this->getFacingDegrees());
183 $dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
184 ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
185 ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
186 ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
189 if($dyeColor !==
null){
190 $color = $dyeColor === DyeColor::BLACK ?
new Color(0, 0, 0) : $dyeColor->getRgbValue();
191 $text = $this->getFaceText($frontFace);
194 $this->doSignChange(
new SignText($text->
getLines(), $color, $text->
isGlowing()), $player, $item, $frontFace)
196 $this->position->getWorld()->addSound($this->position,
new DyeUseSound());
199 }elseif(match($item->getTypeId()){
200 ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item, $frontFace),
201 ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item, $frontFace),
202 ItemTypeIds::HONEYCOMB => $this->wax($player, $item),
208 $player->openSignEditor($this->position, $frontFace);
213 private function interactsFront(Vector3 $hitboxCenter, Vector3 $playerPosition,
float $signFacingDegrees) : bool{
214 $playerCenterDiffX = $playerPosition->x - $hitboxCenter->x;
215 $playerCenterDiffZ = $playerPosition->z - $hitboxCenter->z;
217 $f1 = rad2deg(atan2($playerCenterDiffZ, $playerCenterDiffX)) - 90.0;
219 $rotationDiff = $signFacingDegrees - $f1;
220 $rotation = fmod($rotationDiff + 180.0, 360.0) - 180.0;
221 return abs($rotation) <= 90.0;
228 return $this->position->add(0.5, 0.5, 0.5);
257 public function getFaceText(
bool $frontFace) :
SignText{
258 return $frontFace ? $this->text : $this->backText;
263 $frontFace ? $this->text = $text : $this->backText = $text;
270 public function isWaxed() : bool{ return $this->waxed; }
274 $this->waxed = $waxed;
287 $this->editorEntityRuntimeId = $editorEntityRuntimeId;
296 return $this->updateFaceText($author, true, $text);
307 foreach($text->
getLines() as $line){
308 $size += strlen($line);
311 throw new \UnexpectedValueException($author->
getName() .
" tried to write $size bytes of text onto a sign (bigger than max 1000)");
313 $oldText = $this->getFaceText($frontFace);
314 $ev =
new SignChangeEvent($this, $author,
new SignText(array_map(
function(
string $line) :
string{
315 return TextFormat::clean($line,
false);
316 }, $text->
getLines()), $oldText->getBaseColor(), $oldText->isGlowing()), $frontFace);
317 if($this->waxed || $this->editorEntityRuntimeId !== $author->getId()){
321 if(!$ev->isCancelled()){
322 $this->setFaceText($frontFace, $ev->getNewText());
323 $this->setEditorEntityRuntimeId(
null);
324 $this->position->getWorld()->setBlock($this->position, $this);
332 return ($this->asItemCallback)();
336 return $this->woodType->isFlammable() ? 200 : 0;
place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, Facing $face, Vector3 $clickVector, ?Player $player=null)