color = DyeColor::RED(); } protected function writeStateToMeta() : int{ return BlockDataSerializer::writeLegacyHorizontalFacing($this->facing) | ($this->occupied ? BlockLegacyMetadata::BED_FLAG_OCCUPIED : 0) | ($this->head ? BlockLegacyMetadata::BED_FLAG_HEAD : 0); } public function readStateFromData(int $id, int $stateMeta) : void{ $this->facing = BlockDataSerializer::readLegacyHorizontalFacing($stateMeta & 0x03); $this->occupied = ($stateMeta & BlockLegacyMetadata::BED_FLAG_OCCUPIED) !== 0; $this->head = ($stateMeta & BlockLegacyMetadata::BED_FLAG_HEAD) !== 0; } public function getStateBitmask() : int{ return 0b1111; } public function readStateFromWorld() : void{ parent::readStateFromWorld(); //read extra state information from the tile - this is an ugly hack $tile = $this->pos->getWorld()->getTile($this->pos); if($tile instanceof TileBed){ $this->color = $tile->getColor(); } } public function writeStateToWorld() : void{ parent::writeStateToWorld(); //extra block properties storage hack $tile = $this->pos->getWorld()->getTile($this->pos); if($tile instanceof TileBed){ $tile->setColor($this->color); } } /** * @return AxisAlignedBB[] */ protected function recalculateCollisionBoxes() : array{ return [AxisAlignedBB::one()->trim(Facing::UP, 7 / 16)]; } public function isHeadPart() : bool{ return $this->head; } public function isOccupied() : bool{ return $this->occupied; } public function setOccupied(bool $occupied = true) : void{ $this->occupied = $occupied; $this->pos->getWorld()->setBlock($this->pos, $this, false); if(($other = $this->getOtherHalf()) !== null){ $other->occupied = $occupied; $this->pos->getWorld()->setBlock($other->pos, $other, false); } } private function getOtherHalfSide() : int{ return $this->head ? Facing::opposite($this->facing) : $this->facing; } public function getOtherHalf() : ?Bed{ $other = $this->getSide($this->getOtherHalfSide()); if($other instanceof Bed and $other->head !== $this->head and $other->facing === $this->facing){ return $other; } return null; } public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($player !== null){ $other = $this->getOtherHalf(); $playerPos = $player->getPosition(); if($other === null){ $player->sendMessage(TextFormat::GRAY . "This bed is incomplete"); return true; }elseif($playerPos->distanceSquared($this->pos) > 4 and $playerPos->distanceSquared($other->pos) > 4){ $player->sendMessage(new TranslationContainer(TextFormat::GRAY . "%tile.bed.tooFar")); return true; } $time = $this->pos->getWorld()->getTimeOfDay(); $isNight = ($time >= World::TIME_NIGHT and $time < World::TIME_SUNRISE); if(!$isNight){ $player->sendMessage(new TranslationContainer(TextFormat::GRAY . "%tile.bed.noSleep")); return true; } $b = ($this->isHeadPart() ? $this : $other); if($b->isOccupied()){ $player->sendMessage(new TranslationContainer(TextFormat::GRAY . "%tile.bed.occupied")); return true; } $player->sleepOn($b->pos); } return true; } public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{ if($item instanceof ItemBed){ //TODO: the item should do this $this->color = $item->getColor(); } $down = $this->getSide(Facing::DOWN); if(!$down->isTransparent()){ $this->facing = $player !== null ? $player->getHorizontalFacing() : Facing::NORTH; $next = $this->getSide($this->getOtherHalfSide()); if($next->canBeReplaced() and !$next->getSide(Facing::DOWN)->isTransparent()){ $nextState = clone $this; $nextState->head = true; $tx->addBlock($blockReplace->pos, $this)->addBlock($next->pos, $nextState); return true; } } return false; } public function getDrops(Item $item) : array{ if($this->head){ return parent::getDrops($item); } return []; } public function asItem() : Item{ return ItemFactory::getInstance()->get($this->idInfo->getItemId(), DyeColorIdMap::getInstance()->toId($this->color)); } public function getAffectedBlocks() : array{ if(($other = $this->getOtherHalf()) !== null){ return [$this, $other]; } return parent::getAffectedBlocks(); } }