54 use AnimatedContainerLikeTrait;
56 use FacesOppositePlacingPlayerTrait;
58 protected ?ChestPairHalf $pairHalf =
null;
60 public function getPairHalf() : ?ChestPairHalf{
return $this->pairHalf; }
62 public function setPairHalf(?ChestPairHalf $pairHalf) :
self{
63 $this->pairHalf = $pairHalf;
69 $tile = $this->position->getWorld()->getTile($this->position);
71 $this->pairHalf =
null;
72 if($tile instanceof TileChest && ($pairXZ = $tile->getPairXZ()) !==
null){
73 [$pairX, $pairZ] = $pairXZ;
74 foreach(ChestPairHalf::cases() as $pairSide){
75 $pairDirection = $pairSide->getOtherHalfSide($this->facing);
76 $pairPosition = $this->position->getSide($pairDirection);
77 if($pairPosition->getFloorX() === $pairX && $pairPosition->getFloorZ() === $pairZ){
78 $this->pairHalf = $pairSide;
88 parent::writeStateToWorld();
89 $tile = $this->position->getWorld()->getTile($this->position);
90 assert($tile instanceof TileChest);
93 if($this->pairHalf !==
null){
94 $pairDirection = $this->pairHalf->getOtherHalfSide($this->facing);
95 $pairPosition = $this->position->getSide($pairDirection);
96 $pairXZ = [$pairPosition->getFloorX(), $pairPosition->getFloorZ()];
100 $tile->setPairXZ($pairXZ);
105 $facing = $this->facing->toFacing();
106 $box = AxisAlignedBB::one()
107 ->squashedCopy(Facing::axis($facing), 0.025)
108 ->trimmedCopy(Facing::UP, 0.05);
109 $pairSide = $this->pairHalf?->getOtherHalfSide($this->facing);
110 return [$pairSide !==
null ?
111 $box->trimmedCopy(Facing::opposite($pairSide), 0.025) :
112 $box->squashedCopy(Facing::axis(Facing::rotateY($facing,
true)), 0.025)
117 return SupportType::NONE;
120 private function getPossiblePair(ChestPairHalf $pairSide) : ?
Chest{
121 $pair = $this->getSide($pairSide->getOtherHalfSide($this->facing));
122 return $pair->
hasSameTypeId($this) && $pair instanceof
Chest && $pair->getFacing() === $this->facing ? $pair :
null;
125 public function getOtherHalf() : ?Chest{
126 return $this->pairHalf !== null && ($pair = $this->getPossiblePair($this->pairHalf)) !== null && $pair->pairHalf === $this->pairHalf->opposite() ? $pair : null;
132 $order = match($this->facing){
133 HorizontalFacingOption::NORTH, HorizontalFacingOption::WEST => [ChestPairHalf::LEFT, ChestPairHalf::RIGHT],
134 HorizontalFacingOption::SOUTH, HorizontalFacingOption::EAST => [ChestPairHalf::RIGHT, ChestPairHalf::LEFT]
136 $world = $this->position->getWorld();
137 foreach($order as $pairSide){
138 $possiblePair = $this->getPossiblePair($pairSide);
139 if($possiblePair !==
null && $possiblePair->pairHalf ===
null){
140 [$left, $right] = $pairSide === ChestPairHalf::LEFT ? [$this, $possiblePair] : [$possiblePair, $this];
142 if(!$ev->isCancelled() && $world->getBlock($this->position)->isSameState($this) && $world->getBlock($possiblePair->position)->isSameState($possiblePair)){
143 $world->setBlock($this->position, $this->setPairHalf($pairSide));
144 $world->setBlock($possiblePair->position, $possiblePair->setPairHalf($pairSide->opposite()));
155 if($this->pairHalf !== null && $this->getOtherHalf() === null){
156 $this->position->getWorld()->setBlock($this->position, $this->setPairHalf(
null));
161 foreach([$this, $this->getOtherHalf()] as $chest){
162 if($chest !==
null && !$chest->getSide(Facing::UP)->isTransparent()){
169 protected function getTile() : ?TileChest{
170 $tile = $this->position->getWorld()->getTile($this->position);
171 return $tile instanceof TileChest ? $tile :
null;
175 $thisInventory = $this->getTile()?->getRealInventory();
176 if($thisInventory ===
null){
179 $pairInventory = $this->getOtherHalf()?->getTile()?->getRealInventory();
180 if($pairInventory ===
null){
181 return $thisInventory;
184 [$left, $right] = $this->pairHalf === ChestPairHalf::LEFT ? [$thisInventory, $pairInventory] : [$pairInventory, $thisInventory];
185 return new CombinedInventoryProxy([$left, $right]);
188 protected function newMenu(Player $player, Inventory $inventory, Position $position) : InventoryWindow{
189 $pair = $this->getOtherHalf();
191 return new BlockInventoryWindow($player, $inventory, $position);
193 [$left, $right] = $this->pairHalf === ChestPairHalf::LEFT ? [$this, $pair] : [$pair, $this];
194 return new DoubleChestInventoryWindow($player, $inventory, $left->position, $right->position);
201 protected function getOpenSound() :
Sound{
205 protected function getCloseSound() : Sound{
206 return new ChestCloseSound();
209 protected function playAnimationVisual(Position $position,
bool $isOpen) : void{
212 $position->getWorld()->broadcastPacketToViewers($position, BlockEventPacket::create(BlockPosition::fromVector3($position), 1, $isOpen ? 1 : 0));
215 protected function doAnimationEffects(
bool $isOpen) : void{
216 $this->playAnimationVisual($this->position, $isOpen);
217 $this->playAnimationSound($this->position, $isOpen);
219 $pair = $this->getOtherHalf();
221 $this->playAnimationVisual($pair->position, $isOpen);
222 $this->playAnimationSound($pair->position, $isOpen);