diff --git a/src/block/BaseSign.php b/src/block/BaseSign.php index a1413ccbf..bd6316af9 100644 --- a/src/block/BaseSign.php +++ b/src/block/BaseSign.php @@ -24,17 +24,23 @@ declare(strict_types=1); namespace pocketmine\block; use pocketmine\block\tile\Sign as TileSign; +use pocketmine\block\utils\DyeColor; use pocketmine\block\utils\SignText; use pocketmine\block\utils\SupportType; use pocketmine\block\utils\WoodType; use pocketmine\block\utils\WoodTypeTrait; +use pocketmine\color\Color; use pocketmine\event\block\SignChangeEvent; +use pocketmine\item\Dye; use pocketmine\item\Item; +use pocketmine\item\ItemTypeIds; use pocketmine\math\AxisAlignedBB; use pocketmine\math\Vector3; use pocketmine\player\Player; use pocketmine\utils\TextFormat; use pocketmine\world\BlockTransaction; +use pocketmine\world\sound\DyeUseSound; +use pocketmine\world\sound\InkSacUseSound; use function array_map; use function assert; use function strlen; @@ -111,6 +117,55 @@ abstract class BaseSign extends Transparent{ return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); } + private function doSignChange(SignText $newText, Player $player, Item $item) : bool{ + $ev = new SignChangeEvent($this, $player, $newText); + $ev->call(); + if(!$ev->isCancelled()){ + $this->text = $ev->getNewText(); + $this->position->getWorld()->setBlock($this->position, $this); + $item->pop(); + return true; + } + + return false; + } + + private function changeSignGlowingState(bool $glowing, Player $player, Item $item) : bool{ + if($this->text->isGlowing() !== $glowing && $this->doSignChange(new SignText($this->text->getLines(), $this->text->getBaseColor(), $glowing), $player, $item)){ + $this->position->getWorld()->addSound($this->position, new InkSacUseSound()); + return true; + } + return false; + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{ + if($player === null){ + return false; + } + $dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){ + ItemTypeIds::BONE_MEAL => DyeColor::WHITE(), + ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(), + ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(), + default => null + }; + if($dyeColor !== null){ + $color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue(); + if($color->toARGB() === $this->text->getBaseColor()->toARGB()){ + return false; + } + + if($this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)){ + $this->position->getWorld()->addSound($this->position, new DyeUseSound()); + return true; + } + }elseif($item->getTypeId() === ItemTypeIds::INK_SAC){ + return $this->changeSignGlowingState(false, $player, $item); + }elseif($item->getTypeId() === ItemTypeIds::GLOW_INK_SAC){ + return $this->changeSignGlowingState(true, $player, $item); + } + return false; + } + /** * Returns an object containing information about the sign text. */ diff --git a/src/block/tile/Sign.php b/src/block/tile/Sign.php index b4383d157..3d3d30713 100644 --- a/src/block/tile/Sign.php +++ b/src/block/tile/Sign.php @@ -24,9 +24,13 @@ declare(strict_types=1); namespace pocketmine\block\tile; use pocketmine\block\utils\SignText; +use pocketmine\color\Color; use pocketmine\math\Vector3; +use pocketmine\nbt\tag\ByteTag; use pocketmine\nbt\tag\CompoundTag; +use pocketmine\nbt\tag\IntTag; use pocketmine\nbt\tag\StringTag; +use pocketmine\utils\Binary; use pocketmine\world\World; use function array_pad; use function array_slice; @@ -42,6 +46,13 @@ use function sprintf; class Sign extends Spawnable{ public const TAG_TEXT_BLOB = "Text"; public const TAG_TEXT_LINE = "Text%d"; //sprintf()able + public const TAG_TEXT_COLOR = "SignTextColor"; + public const TAG_GLOWING_TEXT = "IgnoreLighting"; + /** + * This tag is set to indicate that MCPE-117835 has been addressed in whatever version this sign was created. + * @see https://bugs.mojang.com/browse/MCPE-117835 + */ + public const TAG_LEGACY_BUG_RESOLVE = "TextIgnoreLegacyBugResolved"; /** * @return string[] @@ -60,7 +71,20 @@ class Sign extends Spawnable{ public function readSaveData(CompoundTag $nbt) : void{ if(($textBlobTag = $nbt->getTag(self::TAG_TEXT_BLOB)) instanceof StringTag){ //MCPE 1.2 save format - $this->text = SignText::fromBlob(mb_scrub($textBlobTag->getValue(), 'UTF-8')); + $baseColor = new Color(0, 0, 0); + $glowingText = false; + if(($baseColorTag = $nbt->getTag(self::TAG_TEXT_COLOR)) instanceof IntTag){ + $baseColor = Color::fromARGB(Binary::unsignInt($baseColorTag->getValue())); + } + if( + ($glowingTextTag = $nbt->getTag(self::TAG_GLOWING_TEXT)) instanceof ByteTag && + ($lightingBugResolvedTag = $nbt->getTag(self::TAG_LEGACY_BUG_RESOLVE)) instanceof ByteTag + ){ + //both of these must be 1 - if only one is set, it's a leftover from 1.16.210 experimental features + //see https://bugs.mojang.com/browse/MCPE-117835 + $glowingText = $glowingTextTag->getValue() !== 0 && $lightingBugResolvedTag->getValue() !== 0; + } + $this->text = SignText::fromBlob(mb_scrub($textBlobTag->getValue(), 'UTF-8'), $baseColor, $glowingText); }else{ $text = []; for($i = 0; $i < SignText::LINE_COUNT; ++$i){ @@ -80,6 +104,9 @@ class Sign extends Spawnable{ $textKey = sprintf(self::TAG_TEXT_LINE, $i + 1); $nbt->setString($textKey, $this->text->getLine($i)); } + $nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())); + $nbt->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0); + $nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1); } public function getText() : SignText{ @@ -108,5 +135,8 @@ class Sign extends Spawnable{ protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ $nbt->setString(self::TAG_TEXT_BLOB, implode("\n", $this->text->getLines())); + $nbt->setInt(self::TAG_TEXT_COLOR, Binary::signInt($this->text->getBaseColor()->toARGB())); + $nbt->setByte(self::TAG_GLOWING_TEXT, $this->text->isGlowing() ? 1 : 0); + $nbt->setByte(self::TAG_LEGACY_BUG_RESOLVE, 1); } } diff --git a/src/block/utils/SignText.php b/src/block/utils/SignText.php index 6f3cb69dd..cdde8f5eb 100644 --- a/src/block/utils/SignText.php +++ b/src/block/utils/SignText.php @@ -23,6 +23,7 @@ declare(strict_types=1); namespace pocketmine\block\utils; +use pocketmine\color\Color; use pocketmine\utils\Utils; use function array_fill; use function array_pad; @@ -37,6 +38,8 @@ class SignText{ /** @var string[] */ private array $lines; + private Color $baseColor; + private bool $glowing; /** * @param string[]|null $lines index-sensitive; omitting an index will leave it unchanged @@ -45,7 +48,7 @@ class SignText{ * @throws \InvalidArgumentException if invalid keys (out of bounds or string) are found in the array * @throws \InvalidArgumentException if any line is not valid UTF-8 or contains a newline */ - public function __construct(?array $lines = null){ + public function __construct(?array $lines = null, ?Color $baseColor = null, bool $glowing = false){ $this->lines = array_fill(0, self::LINE_COUNT, ""); if($lines !== null){ if(count($lines) > self::LINE_COUNT){ @@ -61,6 +64,8 @@ class SignText{ $this->lines[$k] = $line; } } + $this->baseColor = $baseColor ?? new Color(0, 0, 0); + $this->glowing = $glowing; } /** @@ -69,8 +74,8 @@ class SignText{ * * @throws \InvalidArgumentException if the text is not valid UTF-8 */ - public static function fromBlob(string $blob) : SignText{ - return new self(array_slice(array_pad(explode("\n", $blob), self::LINE_COUNT, ""), 0, self::LINE_COUNT)); + public static function fromBlob(string $blob, ?Color $baseColor = null, bool $glowing = false) : SignText{ + return new self(array_slice(array_pad(explode("\n", $blob), self::LINE_COUNT, ""), 0, self::LINE_COUNT), $baseColor, $glowing); } /** @@ -103,4 +108,19 @@ class SignText{ $this->checkLineIndex($index); return $this->lines[$index]; } + + /** + * Returns the base text color of sign. Color codes using the ยง escape character will override this color when used. + */ + public function getBaseColor() : Color{ + return $this->baseColor; + } + + /** + * Returns whether the sign text is glowing. When true, the text will have an outline (usually a darker tone of the + * base color, or white for black text), and will glow in the dark, making it readable without any light sources. + */ + public function isGlowing() : bool{ + return $this->glowing; + } } diff --git a/src/world/sound/DyeUseSound.php b/src/world/sound/DyeUseSound.php new file mode 100644 index 000000000..5357c6e8b --- /dev/null +++ b/src/world/sound/DyeUseSound.php @@ -0,0 +1,35 @@ +