57    private bool $waxed = 
false;
 
   59    protected ?
int $editorEntityRuntimeId = 
null;
 
   62    private \Closure $asItemCallback;
 
   68        $this->woodType = $woodType;
 
   69        parent::__construct($idInfo, $name, $typeInfo);
 
   72        $this->asItemCallback = $asItemCallback;
 
 
   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();
 
 
   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);
 
 
  111        return SupportType::NONE;
 
 
  114    abstract protected function getSupportingFace() : int;
 
  117        if($this->getSide($this->getSupportingFace())->getTypeId() === 
BlockTypeIds::AIR){
 
  118            $this->position->getWorld()->useBreakOn($this->position);
 
 
  123        if($player !== null){
 
  124            $this->editorEntityRuntimeId = $player->getId();
 
  126        return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
 
 
  130        $player = $this->editorEntityRuntimeId !== null ?
 
  131            $this->position->getWorld()->getEntity($this->editorEntityRuntimeId) :
 
  133        if($player instanceof 
Player){
 
  134            $player->openSignEditor($this->position);
 
 
  138    private function doSignChange(SignText $newText, 
Player $player, Item $item, 
bool $frontFace) : bool{
 
  139        $ev = new SignChangeEvent($this, $player, $newText, $frontFace);
 
  141        if(!$ev->isCancelled()){
 
  142            $this->setFaceText($frontFace, $ev->getNewText());
 
  143            $this->position->getWorld()->setBlock($this->position, $this);
 
  151    private function changeSignGlowingState(
bool $glowing, Player $player, Item $item, 
bool $frontFace) : bool{
 
  152        $text = $this->getFaceText($frontFace);
 
  153        if($text->
isGlowing() !== $glowing && $this->doSignChange(
new SignText($text->
getLines(), $text->
getBaseColor(), $glowing), $player, $item, $frontFace)){
 
  154            $this->position->getWorld()->addSound($this->position, new InkSacUseSound());
 
  160    private function wax(Player $player, Item $item) : bool{
 
  166        $this->position->getWorld()->setBlock($this->position, $this);
 
  173        if($player === null){
 
  180        $frontFace = $this->interactsFront($this->getHitboxCenter(), $player->getPosition(), $this->getFacingDegrees());
 
  182        $dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
 
  183            ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
 
  184            ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
 
  185            ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
 
  188        if($dyeColor !== 
null){
 
  189            $color = $dyeColor === DyeColor::BLACK ? 
new Color(0, 0, 0) : $dyeColor->getRgbValue();
 
  190            $text = $this->getFaceText($frontFace);
 
  193                $this->doSignChange(
new SignText($text->
getLines(), $color, $text->
isGlowing()), $player, $item, $frontFace)
 
  195                $this->position->getWorld()->addSound($this->position, 
new DyeUseSound());
 
  198        }elseif(match($item->getTypeId()){
 
  199            ItemTypeIds::INK_SAC => $this->changeSignGlowingState(false, $player, $item, $frontFace),
 
  200            ItemTypeIds::GLOW_INK_SAC => $this->changeSignGlowingState(true, $player, $item, $frontFace),
 
  201            ItemTypeIds::HONEYCOMB => $this->wax($player, $item),
 
  207        $player->openSignEditor($this->position, $frontFace);
 
 
  212    private function interactsFront(Vector3 $hitboxCenter, Vector3 $playerPosition, 
float $signFacingDegrees) : bool{
 
  213        $playerCenterDiffX = $playerPosition->x - $hitboxCenter->x;
 
  214        $playerCenterDiffZ = $playerPosition->z - $hitboxCenter->z;
 
  216        $f1 = rad2deg(atan2($playerCenterDiffZ, $playerCenterDiffX)) - 90.0;
 
  218        $rotationDiff = $signFacingDegrees - $f1;
 
  219        $rotation = fmod($rotationDiff + 180.0, 360.0) - 180.0; 
 
  220        return abs($rotation) <= 90.0;
 
  227        return $this->position->add(0.5, 0.5, 0.5);
 
 
  256    public function getFaceText(
bool $frontFace) : 
SignText{
 
  257        return $frontFace ? $this->text : $this->backText;
 
  262        $frontFace ? $this->text = $text : $this->backText = $text;
 
 
  269    public function isWaxed() : bool{ return $this->waxed; }
 
  273        $this->waxed = $waxed;
 
 
  286        $this->editorEntityRuntimeId = $editorEntityRuntimeId;
 
 
  295        return $this->updateFaceText($author, true, $text);
 
 
  306        foreach($text->
getLines() as $line){
 
  307            $size += strlen($line);
 
  310            throw new \UnexpectedValueException($author->
getName() . 
" tried to write $size bytes of text onto a sign (bigger than max 1000)");
 
  312        $oldText = $this->getFaceText($frontFace);
 
  313        $ev = 
new SignChangeEvent($this, $author, 
new SignText(array_map(
function(
string $line) : 
string{
 
  314            return TextFormat::clean($line, 
false);
 
  315        }, $text->
getLines()), $oldText->getBaseColor(), $oldText->isGlowing()), $frontFace);
 
  316        if($this->waxed || $this->editorEntityRuntimeId !== $author->getId()){
 
  320        if(!$ev->isCancelled()){
 
  321            $this->setFaceText($frontFace, $ev->getNewText());
 
  322            $this->setEditorEntityRuntimeId(
null);
 
  323            $this->position->getWorld()->setBlock($this->position, $this);
 
 
  331        return ($this->asItemCallback)();
 
 
  335        return $this->woodType->isFlammable() ? 200 : 0;
 
 
 
place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player=null)