From 5874ce582ab0440dcff7e300708f126e084e4aff Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Mon, 19 Jul 2021 17:00:56 +0100 Subject: [PATCH] Implemented bells --- src/block/Bell.php | 162 ++++++++++++++++++ src/block/BlockFactory.php | 3 +- src/block/VanillaBlocks.php | 2 + src/block/tile/Bell.php | 86 ++++++++++ src/block/tile/TileFactory.php | 2 +- src/block/utils/BellAttachmentType.php | 49 ++++++ src/world/sound/BellRingSound.php | 34 ++++ .../block_factory_consistency_check.json | 2 +- 8 files changed, 337 insertions(+), 3 deletions(-) create mode 100644 src/block/Bell.php create mode 100644 src/block/tile/Bell.php create mode 100644 src/block/utils/BellAttachmentType.php create mode 100644 src/world/sound/BellRingSound.php diff --git a/src/block/Bell.php b/src/block/Bell.php new file mode 100644 index 000000000..954e55c1c --- /dev/null +++ b/src/block/Bell.php @@ -0,0 +1,162 @@ +attachmentType = BellAttachmentType::FLOOR(); + parent::__construct($idInfo, $name, $breakInfo); + } + + public function readStateFromData(int $id, int $stateMeta) : void{ + $this->setFacing(BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03)); + + $attachmentType = [ + BlockLegacyMetadata::BELL_ATTACHMENT_FLOOR => BellAttachmentType::FLOOR(), + BlockLegacyMetadata::BELL_ATTACHMENT_CEILING => BellAttachmentType::CEILING(), + BlockLegacyMetadata::BELL_ATTACHMENT_ONE_WALL => BellAttachmentType::ONE_WALL(), + BlockLegacyMetadata::BELL_ATTACHMENT_TWO_WALLS => BellAttachmentType::TWO_WALLS() + ][($stateMeta >> 2) & 0b11] ?? null; + if($attachmentType === null){ + throw new InvalidBlockStateException("No such attachment type"); + } + $this->setAttachmentType($attachmentType); + } + + public function writeStateToMeta() : int{ + $attachmentTypeMeta = [ + BellAttachmentType::FLOOR()->id() => BlockLegacyMetadata::BELL_ATTACHMENT_FLOOR, + BellAttachmentType::CEILING()->id() => BlockLegacyMetadata::BELL_ATTACHMENT_CEILING, + BellAttachmentType::ONE_WALL()->id() => BlockLegacyMetadata::BELL_ATTACHMENT_ONE_WALL, + BellAttachmentType::TWO_WALLS()->id() => BlockLegacyMetadata::BELL_ATTACHMENT_TWO_WALLS + ][$this->getAttachmentType()->id()] ?? null; + if($attachmentTypeMeta === null){ + throw new AssumptionFailedError("Mapping should cover all cases"); + } + return BlockDataSerializer::writeLegacyHorizontalFacing($this->getFacing()) | ($attachmentTypeMeta << 2); + } + + public function getStateBitmask() : int{ + return 0b1111; + } + + public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; } + + /** @return $this */ + public function setAttachmentType(BellAttachmentType $attachmentType) : self{ + $this->attachmentType = $attachmentType; + return $this; + } + + private function canBeSupportedBy(Block $block) : bool{ + //TODO: this isn't the actual logic, but it's the closest approximation we can support for now + return $block->isSolid(); + } + + public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if($face === Facing::UP){ + if(!$this->canBeSupportedBy($tx->fetchBlock($this->pos->down()))){ + return false; + } + if($player !== null){ + $this->setFacing(Facing::opposite($player->getHorizontalFacing())); + } + $this->setAttachmentType(BellAttachmentType::FLOOR()); + }elseif($face === Facing::DOWN){ + if(!$this->canBeSupportedBy($tx->fetchBlock($this->pos->up()))){ + return false; + } + $this->setAttachmentType(BellAttachmentType::CEILING()); + }else{ + $this->setFacing($face); + if($this->canBeSupportedBy($tx->fetchBlock($this->pos->getSide(Facing::opposite($face))))){ + $this->setAttachmentType(BellAttachmentType::ONE_WALL()); + }else{ + return false; + } + if($this->canBeSupportedBy($tx->fetchBlock($this->pos->getSide($face)))){ + $this->setAttachmentType(BellAttachmentType::TWO_WALLS()); + } + } + return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player); + } + + public function onNearbyBlockChange() : void{ + if( + ($this->attachmentType->equals(BellAttachmentType::CEILING()) && !$this->canBeSupportedBy($this->getSide(Facing::UP))) || + ($this->attachmentType->equals(BellAttachmentType::FLOOR()) && !$this->canBeSupportedBy($this->getSide(Facing::DOWN))) || + ($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) && !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing)))) || + ($this->attachmentType->equals(BellAttachmentType::TWO_WALLS()) && (!$this->canBeSupportedBy($this->getSide($this->facing)) || !$this->canBeSupportedBy($this->getSide(Facing::opposite($this->facing))))) + ){ + $this->pos->getWorld()->useBreakOn($this->pos); + } + } + + public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ + if($player !== null){ + $faceHit = Facing::opposite($player->getHorizontalFacing()); + if($this->attachmentType->equals(BellAttachmentType::CEILING())){ + $this->ring($faceHit); + } + if($this->attachmentType->equals(BellAttachmentType::FLOOR()) && Facing::axis($faceHit) === Facing::axis($this->facing)){ + $this->ring($faceHit); + } + if( + ($this->attachmentType->equals(BellAttachmentType::ONE_WALL()) || $this->attachmentType->equals(BellAttachmentType::TWO_WALLS())) && + ($faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true)) + ){ + $this->ring($faceHit); + } + } + + return true; + } + + public function ring(int $faceHit) : void{ + $this->pos->getWorld()->addSound($this->pos, new BellRingSound()); + $tile = $this->pos->getWorld()->getTile($this->pos); + if($tile instanceof TileBell){ + $this->pos->getWorld()->broadcastPacketToViewers($this->pos, $tile->createFakeUpdatePacket($faceHit)); + } + } +} diff --git a/src/block/BlockFactory.php b/src/block/BlockFactory.php index 6da50285e..35ee2b008 100644 --- a/src/block/BlockFactory.php +++ b/src/block/BlockFactory.php @@ -31,6 +31,7 @@ use pocketmine\block\tile\Banner as TileBanner; use pocketmine\block\tile\Barrel as TileBarrel; use pocketmine\block\tile\Beacon as TileBeacon; use pocketmine\block\tile\Bed as TileBed; +use pocketmine\block\tile\Bell as TileBell; use pocketmine\block\tile\BrewingStand as TileBrewingStand; use pocketmine\block\tile\Chest as TileChest; use pocketmine\block\tile\Comparator as TileComparator; @@ -118,6 +119,7 @@ class BlockFactory{ $this->register(new Bedrock(new BID(Ids::BEDROCK, 0), "Bedrock", BlockBreakInfo::indestructible())); $this->register(new Beetroot(new BID(Ids::BEETROOT_BLOCK, 0), "Beetroot Block", BlockBreakInfo::instant())); + $this->register(new Bell(new BID(Ids::BELL, 0, null, TileBell::class), "Bell", new BlockBreakInfo(5.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel()))); $this->register(new BlueIce(new BID(Ids::BLUE_ICE, 0), "Blue Ice", new BlockBreakInfo(2.8, BlockToolType::PICKAXE))); $this->register(new BoneBlock(new BID(Ids::BONE_BLOCK, 0), "Bone Block", new BlockBreakInfo(2.0, BlockToolType::PICKAXE, ToolTier::WOOD()->getHarvestLevel()))); $this->register(new Bookshelf(new BID(Ids::BOOKSHELF, 0), "Bookshelf", new BlockBreakInfo(1.5, BlockToolType::AXE))); @@ -542,7 +544,6 @@ class BlockFactory{ )); //region --- auto-generated TODOs for bedrock-1.11.0 --- - //TODO: minecraft:bell //TODO: minecraft:blast_furnace //TODO: minecraft:bubble_column //TODO: minecraft:campfire diff --git a/src/block/VanillaBlocks.php b/src/block/VanillaBlocks.php index 1da1d079f..003ffafbc 100644 --- a/src/block/VanillaBlocks.php +++ b/src/block/VanillaBlocks.php @@ -65,6 +65,7 @@ use function assert; * @method static Bed BED() * @method static Bedrock BEDROCK() * @method static Beetroot BEETROOTS() + * @method static Bell BELL() * @method static WoodenButton BIRCH_BUTTON() * @method static WoodenDoor BIRCH_DOOR() * @method static WoodenFence BIRCH_FENCE() @@ -626,6 +627,7 @@ final class VanillaBlocks{ self::register("bed", $factory->get(26, 0)); self::register("bedrock", $factory->get(7, 0)); self::register("beetroots", $factory->get(244, 0)); + self::register("bell", $factory->get(461, 0)); self::register("birch_button", $factory->get(396, 0)); self::register("birch_door", $factory->get(194, 0)); self::register("birch_fence", $factory->get(85, 2)); diff --git a/src/block/tile/Bell.php b/src/block/tile/Bell.php new file mode 100644 index 000000000..248c260ca --- /dev/null +++ b/src/block/tile/Bell.php @@ -0,0 +1,86 @@ +ringing; } + + public function setRinging(bool $ringing) : void{ $this->ringing = $ringing; } + + public function getFacing() : int{ return $this->facing; } + + public function setFacing(int $facing) : void{ $this->facing = $facing; } + + public function getTicks() : int{ return $this->ticks; } + + public function setTicks(int $ticks) : void{ $this->ticks = $ticks; } + + protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ + $nbt->setByte(self::TAG_RINGING, $this->ringing ? 1 : 0); + $nbt->setInt(self::TAG_DIRECTION, $this->facing); + $nbt->setInt(self::TAG_TICKS, $this->ticks); + } + + public function readSaveData(CompoundTag $nbt) : void{ + $this->ringing = $nbt->getByte(self::TAG_RINGING, 0) !== 0; + $this->facing = $nbt->getInt(self::TAG_DIRECTION, Facing::NORTH); + $this->ticks = $nbt->getInt(self::TAG_TICKS, 0); + } + + protected function writeSaveData(CompoundTag $nbt) : void{ + $nbt->setByte(self::TAG_RINGING, $this->ringing ? 1 : 0); + $nbt->setInt(self::TAG_DIRECTION, $this->facing); + $nbt->setInt(self::TAG_TICKS, $this->ticks); + } + + /** + * TODO: HACK! + * Creates a BlockActorDataPacket that triggers the ringing animation on a bell block. + * + * Bedrock team overcomplicated making bells ring when they implemented this; this would have been better and much + * simpler as a BlockEventPacket. It's simpler to implement bells with this hack than to follow Mojang's complicated + * mess. + */ + public function createFakeUpdatePacket(int $bellHitFace) : BlockActorDataPacket{ + $nbt = $this->getSpawnCompound(); + $nbt->setByte(self::TAG_RINGING, 1); + $nbt->setInt(self::TAG_DIRECTION, BlockDataSerializer::writeLegacyHorizontalFacing($bellHitFace)); + $nbt->setInt(self::TAG_TICKS, 0); + return BlockActorDataPacket::create($this->pos->getFloorX(), $this->pos->getFloorY(), $this->pos->getFloorZ(), new CacheableNbt($nbt)); + } +} diff --git a/src/block/tile/TileFactory.php b/src/block/tile/TileFactory.php index 0e37366d7..7e36a7057 100644 --- a/src/block/tile/TileFactory.php +++ b/src/block/tile/TileFactory.php @@ -53,6 +53,7 @@ final class TileFactory{ $this->register(Banner::class, ["Banner", "minecraft:banner"]); $this->register(Beacon::class, ["Beacon", "minecraft:beacon"]); $this->register(Bed::class, ["Bed", "minecraft:bed"]); + $this->register(Bell::class, ["Bell", "minecraft:bell"]); $this->register(BrewingStand::class, ["BrewingStand", "minecraft:brewing_stand"]); $this->register(Chest::class, ["Chest", "minecraft:chest"]); $this->register(Comparator::class, ["Comparator", "minecraft:comparator"]); @@ -70,7 +71,6 @@ final class TileFactory{ $this->register(Sign::class, ["Sign", "minecraft:sign"]); $this->register(Skull::class, ["Skull", "minecraft:skull"]); - //TODO: Bell //TODO: BlastFurnace //TODO: Campfire //TODO: Cauldron diff --git a/src/block/utils/BellAttachmentType.php b/src/block/utils/BellAttachmentType.php new file mode 100644 index 000000000..f07b047c9 --- /dev/null +++ b/src/block/utils/BellAttachmentType.php @@ -0,0 +1,49 @@ +