Files
PocketMine-MP/src/block/ItemFrame.php
Dylan K. Taylor 2f3fcef97c Fixed blocks incorrectly using blockClicked for support checks
this caused some interesting bugs, such as being able to place floating pressure plates by clicking on the side of a solid block halfway up a wall.
2023-01-12 19:36:23 +00:00

200 lines
6.1 KiB
PHP

<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\block\tile\ItemFrame as TileItemFrame;
use pocketmine\block\utils\BlockDataSerializer;
use pocketmine\block\utils\HorizontalFacingTrait;
use pocketmine\item\Item;
use pocketmine\math\Facing;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use pocketmine\world\BlockTransaction;
use pocketmine\world\sound\ItemFrameAddItemSound;
use pocketmine\world\sound\ItemFrameRemoveItemSound;
use pocketmine\world\sound\ItemFrameRotateItemSound;
use function is_infinite;
use function is_nan;
use function lcg_value;
class ItemFrame extends Flowable{
use HorizontalFacingTrait;
public const ROTATIONS = 8;
protected bool $hasMap = false; //makes frame appear large if set
protected ?Item $framedItem = null;
protected int $itemRotation = 0;
protected float $itemDropChance = 1.0;
protected function writeStateToMeta() : int{
return BlockDataSerializer::write5MinusHorizontalFacing($this->facing) | ($this->hasMap ? BlockLegacyMetadata::ITEM_FRAME_FLAG_HAS_MAP : 0);
}
public function readStateFromData(int $id, int $stateMeta) : void{
$this->facing = BlockDataSerializer::read5MinusHorizontalFacing($stateMeta);
$this->hasMap = ($stateMeta & BlockLegacyMetadata::ITEM_FRAME_FLAG_HAS_MAP) !== 0;
}
public function readStateFromWorld() : void{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileItemFrame){
$this->framedItem = $tile->getItem();
if($this->framedItem->isNull()){
$this->framedItem = null;
}
$this->itemRotation = $tile->getItemRotation() % self::ROTATIONS;
$this->itemDropChance = $tile->getItemDropChance();
}
}
public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileItemFrame){
$tile->setItem($this->framedItem);
$tile->setItemRotation($this->itemRotation);
$tile->setItemDropChance($this->itemDropChance);
}
}
public function getStateBitmask() : int{
return 0b111;
}
public function getFramedItem() : ?Item{
return $this->framedItem !== null ? clone $this->framedItem : null;
}
/** @return $this */
public function setFramedItem(?Item $item) : self{
if($item === null || $item->isNull()){
$this->framedItem = null;
$this->itemRotation = 0;
}else{
$this->framedItem = clone $item;
}
return $this;
}
public function getItemRotation() : int{
return $this->itemRotation;
}
/** @return $this */
public function setItemRotation(int $itemRotation) : self{
$this->itemRotation = $itemRotation;
return $this;
}
public function getItemDropChance() : float{
return $this->itemDropChance;
}
/** @return $this */
public function setItemDropChance(float $itemDropChance) : self{
if($itemDropChance < 0.0 || $itemDropChance > 1.0 || is_nan($itemDropChance) || is_infinite($itemDropChance)){
throw new \InvalidArgumentException("Drop chance must be in range 0-1");
}
$this->itemDropChance = $itemDropChance;
return $this;
}
public function hasMap() : bool{ return $this->hasMap; }
/**
* This can be set irrespective of whether the frame actually contains a map or not. When set, the frame stretches
* to the edges of the block without leaving space around the edges.
*
* @return $this
*/
public function setHasMap(bool $hasMap) : self{
$this->hasMap = $hasMap;
return $this;
}
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($this->framedItem !== null){
$this->itemRotation = ($this->itemRotation + 1) % self::ROTATIONS;
$this->position->getWorld()->addSound($this->position, new ItemFrameRotateItemSound());
}elseif(!$item->isNull()){
$this->framedItem = $item->pop();
$this->position->getWorld()->addSound($this->position, new ItemFrameAddItemSound());
}else{
return true;
}
$this->position->getWorld()->setBlock($this->position, $this);
return true;
}
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
if($this->framedItem === null){
return false;
}
$world = $this->position->getWorld();
if(lcg_value() <= $this->itemDropChance){
$world->dropItem($this->position->add(0.5, 0.5, 0.5), clone $this->framedItem);
$world->addSound($this->position, new ItemFrameRemoveItemSound());
}
$this->setFramedItem(null);
$world->setBlock($this->position, $this);
return true;
}
public function onNearbyBlockChange() : void{
if(!$this->getSide(Facing::opposite($this->facing))->isSolid()){
$this->position->getWorld()->useBreakOn($this->position);
}
}
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
if($face === Facing::DOWN || $face === Facing::UP || !$blockReplace->getSide(Facing::opposite($face))->isSolid()){
return false;
}
$this->facing = $face;
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
}
public function getDropsForCompatibleTool(Item $item) : array{
$drops = parent::getDropsForCompatibleTool($item);
if($this->framedItem !== null && lcg_value() <= $this->itemDropChance){
$drops[] = clone $this->framedItem;
}
return $drops;
}
public function getPickedItem(bool $addUserData = false) : Item{
return $this->framedItem !== null ? clone $this->framedItem : parent::getPickedItem($addUserData);
}
}