mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-06-07 12:18:46 +00:00
Implemented Paintings (#2073)
This supports vanilla placement of paintings, with overlap and collision checking. Paintings are removed when a block is placed inside them or if any of their supporting blocks are removed. As per vanilla, a random painting is chosen from the largest subset that will fit into the given space.
This commit is contained in:
parent
c5336776a5
commit
c7f8796136
@ -30,6 +30,8 @@ use pocketmine\block\Block;
|
|||||||
use pocketmine\block\BlockFactory;
|
use pocketmine\block\BlockFactory;
|
||||||
use pocketmine\block\Water;
|
use pocketmine\block\Water;
|
||||||
use pocketmine\entity\object\ExperienceOrb;
|
use pocketmine\entity\object\ExperienceOrb;
|
||||||
|
use pocketmine\entity\object\Painting;
|
||||||
|
use pocketmine\entity\object\PaintingMotive;
|
||||||
use pocketmine\entity\projectile\Arrow;
|
use pocketmine\entity\projectile\Arrow;
|
||||||
use pocketmine\entity\projectile\Egg;
|
use pocketmine\entity\projectile\Egg;
|
||||||
use pocketmine\entity\projectile\Snowball;
|
use pocketmine\entity\projectile\Snowball;
|
||||||
@ -231,6 +233,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
|||||||
Entity::registerEntity(ExperienceOrb::class, false, ['XPOrb', 'minecraft:xp_orb']);
|
Entity::registerEntity(ExperienceOrb::class, false, ['XPOrb', 'minecraft:xp_orb']);
|
||||||
Entity::registerEntity(FallingSand::class, false, ['FallingSand', 'minecraft:falling_block']);
|
Entity::registerEntity(FallingSand::class, false, ['FallingSand', 'minecraft:falling_block']);
|
||||||
Entity::registerEntity(Item::class, false, ['Item', 'minecraft:item']);
|
Entity::registerEntity(Item::class, false, ['Item', 'minecraft:item']);
|
||||||
|
Entity::registerEntity(Painting::class, false, ['Painting', 'minecraft:painting']);
|
||||||
Entity::registerEntity(PrimedTNT::class, false, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt']);
|
Entity::registerEntity(PrimedTNT::class, false, ['PrimedTnt', 'PrimedTNT', 'minecraft:tnt']);
|
||||||
Entity::registerEntity(Snowball::class, false, ['Snowball', 'minecraft:snowball']);
|
Entity::registerEntity(Snowball::class, false, ['Snowball', 'minecraft:snowball']);
|
||||||
Entity::registerEntity(Squid::class, false, ['Squid', 'minecraft:squid']);
|
Entity::registerEntity(Squid::class, false, ['Squid', 'minecraft:squid']);
|
||||||
@ -241,6 +244,7 @@ abstract class Entity extends Location implements Metadatable, EntityIds{
|
|||||||
|
|
||||||
Attribute::init();
|
Attribute::init();
|
||||||
Effect::init();
|
Effect::init();
|
||||||
|
PaintingMotive::init();
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
|
301
src/pocketmine/entity/object/Painting.php
Normal file
301
src/pocketmine/entity/object/Painting.php
Normal file
@ -0,0 +1,301 @@
|
|||||||
|
<?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\entity\object;
|
||||||
|
|
||||||
|
use pocketmine\entity\Entity;
|
||||||
|
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||||
|
use pocketmine\item\Item;
|
||||||
|
use pocketmine\item\ItemFactory;
|
||||||
|
use pocketmine\level\Level;
|
||||||
|
use pocketmine\level\particle\DestroyParticle;
|
||||||
|
use pocketmine\math\AxisAlignedBB;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\nbt\tag\ByteTag;
|
||||||
|
use pocketmine\nbt\tag\CompoundTag;
|
||||||
|
use pocketmine\network\mcpe\protocol\AddPaintingPacket;
|
||||||
|
use pocketmine\Player;
|
||||||
|
|
||||||
|
class Painting extends Entity{
|
||||||
|
public const NETWORK_ID = self::PAINTING;
|
||||||
|
|
||||||
|
/** @var float */
|
||||||
|
protected $gravity = 0.0;
|
||||||
|
/** @var float */
|
||||||
|
protected $drag = 1.0;
|
||||||
|
|
||||||
|
/** @var Vector3 */
|
||||||
|
protected $blockIn;
|
||||||
|
/** @var int */
|
||||||
|
protected $direction = 0;
|
||||||
|
/** @var string */
|
||||||
|
protected $motive;
|
||||||
|
/** @var int */
|
||||||
|
protected $checkDestroyedTicker = 0;
|
||||||
|
|
||||||
|
public function __construct(Level $level, CompoundTag $nbt){
|
||||||
|
$this->motive = $nbt->getString("Motive");
|
||||||
|
$this->blockIn = new Vector3($nbt->getInt("TileX"), $nbt->getInt("TileY"), $nbt->getInt("TileZ"));
|
||||||
|
if($nbt->hasTag("Direction", ByteTag::class)){
|
||||||
|
$this->direction = $nbt->getByte("Direction");
|
||||||
|
}elseif($nbt->hasTag("Facing", ByteTag::class)){
|
||||||
|
$this->direction = $nbt->getByte("Facing");
|
||||||
|
}
|
||||||
|
parent::__construct($level, $nbt);
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function initEntity(){
|
||||||
|
$this->setMaxHealth(1);
|
||||||
|
$this->setHealth(1);
|
||||||
|
parent::initEntity();
|
||||||
|
}
|
||||||
|
|
||||||
|
public function saveNBT(){
|
||||||
|
parent::saveNBT();
|
||||||
|
$this->namedtag->setInt("TileX", (int) $this->blockIn->x);
|
||||||
|
$this->namedtag->setInt("TileY", (int) $this->blockIn->y);
|
||||||
|
$this->namedtag->setInt("TileZ", (int) $this->blockIn->z);
|
||||||
|
|
||||||
|
$this->namedtag->setByte("Facing", (int) $this->direction);
|
||||||
|
$this->namedtag->setByte("Direction", (int) $this->direction); //Save both for full compatibility
|
||||||
|
}
|
||||||
|
|
||||||
|
public function entityBaseTick(int $tickDiff = 1) : bool{
|
||||||
|
static $directions = [
|
||||||
|
0 => Vector3::SIDE_SOUTH,
|
||||||
|
1 => Vector3::SIDE_WEST,
|
||||||
|
2 => Vector3::SIDE_NORTH,
|
||||||
|
3 => Vector3::SIDE_EAST
|
||||||
|
];
|
||||||
|
|
||||||
|
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||||
|
|
||||||
|
if($this->checkDestroyedTicker++ > 10){
|
||||||
|
/*
|
||||||
|
* we don't have a way to only update on local block updates yet! since random chunk ticking always updates
|
||||||
|
* all the things
|
||||||
|
* ugly hack, but vanilla uses 100 ticks so on there it looks even worse
|
||||||
|
*/
|
||||||
|
$this->checkDestroyedTicker = 0;
|
||||||
|
$face = $directions[$this->direction];
|
||||||
|
if(!self::canFit($this->level, $this->blockIn->getSide($face), $face, false, $this->getMotive())){
|
||||||
|
$this->kill();
|
||||||
|
$hasUpdate = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return $hasUpdate; //doesn't need to be ticked always
|
||||||
|
}
|
||||||
|
|
||||||
|
public function kill(){
|
||||||
|
parent::kill();
|
||||||
|
|
||||||
|
$drops = true;
|
||||||
|
|
||||||
|
if($this->lastDamageCause instanceof EntityDamageByEntityEvent){
|
||||||
|
$killer = $this->lastDamageCause->getDamager();
|
||||||
|
if($killer instanceof Player and $killer->isCreative()){
|
||||||
|
$drops = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($drops){
|
||||||
|
//non-living entities don't have a way to create drops generically yet
|
||||||
|
$this->level->dropItem($this, ItemFactory::get(Item::PAINTING));
|
||||||
|
}
|
||||||
|
$this->level->addParticle(new DestroyParticle($this->add(0.5, 0.5, 0.5), Item::PAINTING));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function recalculateBoundingBox() : void{
|
||||||
|
static $directions = [
|
||||||
|
0 => Vector3::SIDE_SOUTH,
|
||||||
|
1 => Vector3::SIDE_WEST,
|
||||||
|
2 => Vector3::SIDE_NORTH,
|
||||||
|
3 => Vector3::SIDE_EAST
|
||||||
|
];
|
||||||
|
|
||||||
|
$facing = $directions[$this->direction];
|
||||||
|
|
||||||
|
$this->boundingBox->setBB(self::getPaintingBB($this->blockIn->getSide($facing), $facing, $this->getMotive()));
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function tryChangeMovement(){
|
||||||
|
$this->motionX = $this->motionY = $this->motionZ = 0;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function updateMovement(bool $teleport = false){
|
||||||
|
|
||||||
|
}
|
||||||
|
|
||||||
|
public function canBeCollidedWith() : bool{
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
protected function sendSpawnPacket(Player $player) : void{
|
||||||
|
$pk = new AddPaintingPacket();
|
||||||
|
$pk->entityRuntimeId = $this->getId();
|
||||||
|
$pk->x = $this->blockIn->x;
|
||||||
|
$pk->y = $this->blockIn->y;
|
||||||
|
$pk->z = $this->blockIn->z;
|
||||||
|
$pk->direction = $this->direction;
|
||||||
|
$pk->title = $this->motive;
|
||||||
|
|
||||||
|
$player->dataPacket($pk);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the painting motive (which image is displayed on the painting)
|
||||||
|
* @return PaintingMotive
|
||||||
|
*/
|
||||||
|
public function getMotive() : PaintingMotive{
|
||||||
|
return PaintingMotive::getMotiveByName($this->motive);
|
||||||
|
}
|
||||||
|
|
||||||
|
public function getDirection() : int{
|
||||||
|
return $this->direction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the bounding-box a painting with the specified motive would have at the given position and direction.
|
||||||
|
*
|
||||||
|
* @param Vector3 $blockIn
|
||||||
|
* @param int $facing
|
||||||
|
* @param PaintingMotive $motive
|
||||||
|
*
|
||||||
|
* @return AxisAlignedBB
|
||||||
|
*/
|
||||||
|
private static function getPaintingBB(Vector3 $blockIn, int $facing, PaintingMotive $motive) : AxisAlignedBB{
|
||||||
|
$width = $motive->getWidth();
|
||||||
|
$height = $motive->getHeight();
|
||||||
|
|
||||||
|
$horizontalStart = (int) (ceil($width / 2) - 1);
|
||||||
|
$verticalStart = (int) (ceil($height / 2) - 1);
|
||||||
|
|
||||||
|
$thickness = 1 / 16;
|
||||||
|
|
||||||
|
$minX = $maxX = 0;
|
||||||
|
$minZ = $maxZ = 0;
|
||||||
|
|
||||||
|
$minY = -$verticalStart;
|
||||||
|
$maxY = $minY + $height;
|
||||||
|
|
||||||
|
switch($facing){
|
||||||
|
case Vector3::SIDE_NORTH:
|
||||||
|
$minZ = 1 - $thickness;
|
||||||
|
$maxZ = 1;
|
||||||
|
$maxX = $horizontalStart + 1;
|
||||||
|
$minX = $maxX - $width;
|
||||||
|
break;
|
||||||
|
case Vector3::SIDE_SOUTH:
|
||||||
|
$minZ = 0;
|
||||||
|
$maxZ = $thickness;
|
||||||
|
$minX = -$horizontalStart;
|
||||||
|
$maxX = $minX + $width;
|
||||||
|
break;
|
||||||
|
case Vector3::SIDE_WEST:
|
||||||
|
$minX = 1 - $thickness;
|
||||||
|
$maxX = 1;
|
||||||
|
$minZ = -$horizontalStart;
|
||||||
|
$maxZ = $minZ + $width;
|
||||||
|
break;
|
||||||
|
case Vector3::SIDE_EAST:
|
||||||
|
$minX = 0;
|
||||||
|
$maxX = $thickness;
|
||||||
|
$maxZ = $horizontalStart + 1;
|
||||||
|
$minZ = $maxZ - $width;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
|
||||||
|
return new AxisAlignedBB(
|
||||||
|
$blockIn->x + $minX,
|
||||||
|
$blockIn->y + $minY,
|
||||||
|
$blockIn->z + $minZ,
|
||||||
|
$blockIn->x + $maxX,
|
||||||
|
$blockIn->y + $maxY,
|
||||||
|
$blockIn->z + $maxZ
|
||||||
|
);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether a painting with the specified motive can be placed at the given position.
|
||||||
|
*
|
||||||
|
* @param Level $level
|
||||||
|
* @param Vector3 $blockIn
|
||||||
|
* @param int $facing
|
||||||
|
* @param bool $checkOverlap
|
||||||
|
* @param PaintingMotive $motive
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public static function canFit(Level $level, Vector3 $blockIn, int $facing, bool $checkOverlap, PaintingMotive $motive) : bool{
|
||||||
|
$width = $motive->getWidth();
|
||||||
|
$height = $motive->getHeight();
|
||||||
|
|
||||||
|
$horizontalStart = (int) (ceil($width / 2) - 1);
|
||||||
|
$verticalStart = (int) (ceil($height / 2) - 1);
|
||||||
|
|
||||||
|
switch($facing){
|
||||||
|
case Vector3::SIDE_NORTH:
|
||||||
|
$rotatedFace = Vector3::SIDE_WEST;
|
||||||
|
break;
|
||||||
|
case Vector3::SIDE_WEST:
|
||||||
|
$rotatedFace = Vector3::SIDE_SOUTH;
|
||||||
|
break;
|
||||||
|
case Vector3::SIDE_SOUTH:
|
||||||
|
$rotatedFace = Vector3::SIDE_EAST;
|
||||||
|
break;
|
||||||
|
case Vector3::SIDE_EAST:
|
||||||
|
$rotatedFace = Vector3::SIDE_NORTH;
|
||||||
|
break;
|
||||||
|
default:
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$oppositeSide = Vector3::getOppositeSide($facing);
|
||||||
|
|
||||||
|
$startPos = $blockIn->asVector3()->getSide(Vector3::getOppositeSide($rotatedFace), $horizontalStart)->getSide(Vector3::SIDE_DOWN, $verticalStart);
|
||||||
|
|
||||||
|
for($w = 0; $w < $width; ++$w){
|
||||||
|
for($h = 0; $h < $height; ++$h){
|
||||||
|
$pos = $startPos->getSide($rotatedFace, $w)->getSide(Vector3::SIDE_UP, $h);
|
||||||
|
|
||||||
|
$block = $level->getBlockAt($pos->x, $pos->y, $pos->z);
|
||||||
|
if($block->isSolid() or !$block->getSide($oppositeSide)->isSolid()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if($checkOverlap){
|
||||||
|
$bb = self::getPaintingBB($blockIn, $facing, $motive);
|
||||||
|
|
||||||
|
foreach($level->getNearbyEntities($bb) as $entity){
|
||||||
|
if($entity instanceof self){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
127
src/pocketmine/entity/object/PaintingMotive.php
Normal file
127
src/pocketmine/entity/object/PaintingMotive.php
Normal file
@ -0,0 +1,127 @@
|
|||||||
|
<?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\entity\object;
|
||||||
|
|
||||||
|
class PaintingMotive{
|
||||||
|
/** @var PaintingMotive[] */
|
||||||
|
protected static $motives = [];
|
||||||
|
|
||||||
|
public static function init() : void{
|
||||||
|
foreach([
|
||||||
|
new PaintingMotive(1, 1, "Alban"),
|
||||||
|
new PaintingMotive(1, 1, "Aztec"),
|
||||||
|
new PaintingMotive(1, 1, "Aztec2"),
|
||||||
|
new PaintingMotive(1, 1, "Bomb"),
|
||||||
|
new PaintingMotive(1, 1, "Kebab"),
|
||||||
|
new PaintingMotive(1, 1, "Plant"),
|
||||||
|
new PaintingMotive(1, 1, "Wasteland"),
|
||||||
|
new PaintingMotive(1, 2, "Graham"),
|
||||||
|
new PaintingMotive(1, 2, "Wanderer"),
|
||||||
|
new PaintingMotive(2, 1, "Courbet"),
|
||||||
|
new PaintingMotive(2, 1, "Creebet"),
|
||||||
|
new PaintingMotive(2, 1, "Pool"),
|
||||||
|
new PaintingMotive(2, 1, "Sea"),
|
||||||
|
new PaintingMotive(2, 1, "Sunset"),
|
||||||
|
new PaintingMotive(2, 2, "Bust"),
|
||||||
|
new PaintingMotive(2, 2, "Earth"),
|
||||||
|
new PaintingMotive(2, 2, "Fire"),
|
||||||
|
new PaintingMotive(2, 2, "Match"),
|
||||||
|
new PaintingMotive(2, 2, "SkullAndRoses"),
|
||||||
|
new PaintingMotive(2, 2, "Stage"),
|
||||||
|
new PaintingMotive(2, 2, "Void"),
|
||||||
|
new PaintingMotive(2, 2, "Water"),
|
||||||
|
new PaintingMotive(2, 2, "Wind"),
|
||||||
|
new PaintingMotive(2, 2, "Wither"),
|
||||||
|
new PaintingMotive(4, 2, "Fighters"),
|
||||||
|
new PaintingMotive(4, 3, "DonkeyKong"),
|
||||||
|
new PaintingMotive(4, 3, "Skeleton"),
|
||||||
|
new PaintingMotive(4, 4, "BurningSkull"),
|
||||||
|
new PaintingMotive(4, 4, "Pigscene"),
|
||||||
|
new PaintingMotive(4, 4, "Pointer")
|
||||||
|
] as $motive){
|
||||||
|
self::registerMotive($motive);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param PaintingMotive $motive
|
||||||
|
*/
|
||||||
|
public static function registerMotive(PaintingMotive $motive) : void{
|
||||||
|
self::$motives[$motive->getName()] = $motive;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param string $name
|
||||||
|
* @return PaintingMotive|null
|
||||||
|
*/
|
||||||
|
public static function getMotiveByName(string $name) : ?PaintingMotive{
|
||||||
|
return self::$motives[$name] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return PaintingMotive[]
|
||||||
|
*/
|
||||||
|
public static function getAll() : array{
|
||||||
|
return self::$motives;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var string */
|
||||||
|
protected $name;
|
||||||
|
/** @var int */
|
||||||
|
protected $width;
|
||||||
|
/** @var int */
|
||||||
|
protected $height;
|
||||||
|
|
||||||
|
|
||||||
|
public function __construct(int $width, int $height, string $name){
|
||||||
|
$this->name = $name;
|
||||||
|
$this->width = $width;
|
||||||
|
$this->height = $height;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return string
|
||||||
|
*/
|
||||||
|
public function getName() : string{
|
||||||
|
return $this->name;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getWidth() : int{
|
||||||
|
return $this->width;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @return int
|
||||||
|
*/
|
||||||
|
public function getHeight() : int{
|
||||||
|
return $this->height;
|
||||||
|
}
|
||||||
|
|
||||||
|
public function __toString() : string{
|
||||||
|
return "PaintingMotive(name: " . $this->getName() . ", height: " . $this->getHeight() . ", width: " . $this->getWidth() . ")";
|
||||||
|
}
|
||||||
|
}
|
@ -105,7 +105,7 @@ class ItemFactory{
|
|||||||
self::registerItem(new Item(Item::FLINT, 0, "Flint"));
|
self::registerItem(new Item(Item::FLINT, 0, "Flint"));
|
||||||
self::registerItem(new RawPorkchop());
|
self::registerItem(new RawPorkchop());
|
||||||
self::registerItem(new CookedPorkchop());
|
self::registerItem(new CookedPorkchop());
|
||||||
self::registerItem(new Painting());
|
self::registerItem(new PaintingItem());
|
||||||
self::registerItem(new GoldenApple());
|
self::registerItem(new GoldenApple());
|
||||||
self::registerItem(new Sign());
|
self::registerItem(new Sign());
|
||||||
self::registerItem(new ItemBlock(Block::OAK_DOOR_BLOCK, 0, Item::OAK_DOOR));
|
self::registerItem(new ItemBlock(Block::OAK_DOOR_BLOCK, 0, Item::OAK_DOOR));
|
||||||
|
@ -1,94 +0,0 @@
|
|||||||
<?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\item;
|
|
||||||
|
|
||||||
use pocketmine\block\Block;
|
|
||||||
use pocketmine\math\Vector3;
|
|
||||||
use pocketmine\Player;
|
|
||||||
|
|
||||||
class Painting extends Item{
|
|
||||||
public function __construct(int $meta = 0){
|
|
||||||
parent::__construct(self::PAINTING, $meta, "Painting");
|
|
||||||
}
|
|
||||||
|
|
||||||
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
|
||||||
if($blockClicked->isTransparent() === false and $face > 1 and $blockReplace->isSolid() === false){
|
|
||||||
$faces = [
|
|
||||||
2 => 1,
|
|
||||||
3 => 3,
|
|
||||||
4 => 0,
|
|
||||||
5 => 2,
|
|
||||||
|
|
||||||
];
|
|
||||||
$motives = [
|
|
||||||
// Motive Width Height
|
|
||||||
["Kebab", 1, 1],
|
|
||||||
["Aztec", 1, 1],
|
|
||||||
["Alban", 1, 1],
|
|
||||||
["Aztec2", 1, 1],
|
|
||||||
["Bomb", 1, 1],
|
|
||||||
["Plant", 1, 1],
|
|
||||||
["Wasteland", 1, 1],
|
|
||||||
["Wanderer", 1, 2],
|
|
||||||
["Graham", 1, 2],
|
|
||||||
["Pool", 2, 1],
|
|
||||||
["Courbet", 2, 1],
|
|
||||||
["Sunset", 2, 1],
|
|
||||||
["Sea", 2, 1],
|
|
||||||
["Creebet", 2, 1],
|
|
||||||
["Match", 2, 2],
|
|
||||||
["Bust", 2, 2],
|
|
||||||
["Stage", 2, 2],
|
|
||||||
["Void", 2, 2],
|
|
||||||
["SkullAndRoses", 2, 2],
|
|
||||||
//array("Wither", 2, 2),
|
|
||||||
["Fighters", 4, 2],
|
|
||||||
["Skeleton", 4, 3],
|
|
||||||
["DonkeyKong", 4, 3],
|
|
||||||
["Pointer", 4, 4],
|
|
||||||
["Pigscene", 4, 4],
|
|
||||||
["Flaming Skull", 4, 4],
|
|
||||||
];
|
|
||||||
$motive = $motives[mt_rand(0, count($motives) - 1)];
|
|
||||||
$data = [
|
|
||||||
"x" => $blockClicked->x,
|
|
||||||
"y" => $blockClicked->y,
|
|
||||||
"z" => $blockClicked->z,
|
|
||||||
"yaw" => $faces[$face] * 90,
|
|
||||||
"Motive" => $motive[0],
|
|
||||||
];
|
|
||||||
//TODO
|
|
||||||
//$e = $server->api->entity->add($level, ENTITY_OBJECT, OBJECT_PAINTING, $data);
|
|
||||||
//$e->spawnToAll();
|
|
||||||
/*if(($player->gamemode & 0x01) === 0x00){
|
|
||||||
$player->removeItem(Item::get($this->getId(), $this->getDamage(), 1));
|
|
||||||
}*/
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
|
||||||
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
}
|
|
105
src/pocketmine/item/PaintingItem.php
Normal file
105
src/pocketmine/item/PaintingItem.php
Normal file
@ -0,0 +1,105 @@
|
|||||||
|
<?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\item;
|
||||||
|
|
||||||
|
use pocketmine\block\Block;
|
||||||
|
use pocketmine\entity\Entity;
|
||||||
|
use pocketmine\entity\object\Painting;
|
||||||
|
use pocketmine\entity\object\PaintingMotive;
|
||||||
|
use pocketmine\math\Vector3;
|
||||||
|
use pocketmine\network\mcpe\protocol\LevelEventPacket;
|
||||||
|
use pocketmine\Player;
|
||||||
|
|
||||||
|
class PaintingItem extends Item{
|
||||||
|
public function __construct(int $meta = 0){
|
||||||
|
parent::__construct(self::PAINTING, $meta, "Painting");
|
||||||
|
}
|
||||||
|
|
||||||
|
public function onActivate(Player $player, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector) : bool{
|
||||||
|
if(!$blockClicked->isTransparent() and $face > 1 and !$blockReplace->isSolid()){
|
||||||
|
/** @var PaintingMotive[] $motives */
|
||||||
|
$motives = [];
|
||||||
|
|
||||||
|
$totalDimension = 0;
|
||||||
|
foreach(PaintingMotive::getAll() as $motive){
|
||||||
|
$currentTotalDimension = $motive->getHeight() + $motive->getWidth();
|
||||||
|
if($currentTotalDimension < $totalDimension){
|
||||||
|
continue;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(Painting::canFit($player->level, $blockReplace, $face, true, $motive)){
|
||||||
|
if($currentTotalDimension > $totalDimension){
|
||||||
|
$totalDimension = $currentTotalDimension;
|
||||||
|
/*
|
||||||
|
* This drops all motive possibilities smaller than this
|
||||||
|
* We use the total of height + width to allow equal chance of horizontal/vertical paintings
|
||||||
|
* when there is an L-shape of space available.
|
||||||
|
*/
|
||||||
|
$motives = [];
|
||||||
|
}
|
||||||
|
|
||||||
|
$motives[] = $motive;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(empty($motives)){ //No space available
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
/** @var PaintingMotive $motive */
|
||||||
|
$motive = $motives[array_rand($motives)];
|
||||||
|
|
||||||
|
static $directions = [
|
||||||
|
Vector3::SIDE_SOUTH => 0,
|
||||||
|
Vector3::SIDE_WEST => 1,
|
||||||
|
Vector3::SIDE_NORTH => 2,
|
||||||
|
Vector3::SIDE_EAST => 3
|
||||||
|
];
|
||||||
|
|
||||||
|
$direction = $directions[$face] ?? -1;
|
||||||
|
if($direction === -1){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
$nbt = Entity::createBaseNBT($blockReplace, null, $direction * 90, 0);
|
||||||
|
$nbt->setByte("Direction", $direction);
|
||||||
|
$nbt->setString("Motive", $motive->getName());
|
||||||
|
$nbt->setInt("TileX", $blockClicked->getFloorX());
|
||||||
|
$nbt->setInt("TileY", $blockClicked->getFloorY());
|
||||||
|
$nbt->setInt("TileZ", $blockClicked->getFloorZ());
|
||||||
|
|
||||||
|
$entity = Entity::createEntity("Painting", $blockReplace->getLevel(), $nbt);
|
||||||
|
|
||||||
|
if($entity instanceof Entity){
|
||||||
|
--$this->count;
|
||||||
|
$entity->spawnToAll();
|
||||||
|
|
||||||
|
$player->getLevel()->broadcastLevelEvent($blockReplace->add(0.5, 0.5, 0.5), LevelEventPacket::EVENT_SOUND_ITEMFRAME_PLACE); //item frame and painting have the same sound
|
||||||
|
return true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
}
|
Loading…
x
Reference in New Issue
Block a user