mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-20 16:00:20 +00:00
Merge branch 'minor-next' into stable
This commit is contained in:
commit
a10e4b6481
3
.github/workflows/main.yml
vendored
3
.github/workflows/main.yml
vendored
@ -152,9 +152,6 @@ jobs:
|
||||
- name: Regenerate KnownTranslation APIs
|
||||
run: php build/generate-known-translation-apis.php
|
||||
|
||||
- name: Regenerate RuntimeEnum(De)serializer
|
||||
run: php build/generate-runtime-enum-serializers.php
|
||||
|
||||
- name: Regenerate BedrockData available files constants
|
||||
run: php build/generate-bedrockdata-path-consts.php
|
||||
|
||||
|
@ -1,265 +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\build\generate_runtime_enum_serializers;
|
||||
|
||||
use pocketmine\block\utils\BellAttachmentType;
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\DirtType;
|
||||
use pocketmine\block\utils\DripleafState;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FroglightType;
|
||||
use pocketmine\block\utils\LeverFacing;
|
||||
use pocketmine\block\utils\MobHeadType;
|
||||
use pocketmine\block\utils\MushroomBlockType;
|
||||
use pocketmine\block\utils\SlabType;
|
||||
use pocketmine\item\MedicineType;
|
||||
use pocketmine\item\PotionType;
|
||||
use pocketmine\item\SuspiciousStewType;
|
||||
use function array_key_first;
|
||||
use function array_keys;
|
||||
use function array_map;
|
||||
use function ceil;
|
||||
use function count;
|
||||
use function dirname;
|
||||
use function file_put_contents;
|
||||
use function implode;
|
||||
use function ksort;
|
||||
use function lcfirst;
|
||||
use function log;
|
||||
use function ob_get_clean;
|
||||
use function ob_start;
|
||||
use const SORT_STRING;
|
||||
|
||||
require dirname(__DIR__) . '/vendor/autoload.php';
|
||||
|
||||
/**
|
||||
* @param string[] $memberNames
|
||||
* @phpstan-param list<string> $memberNames
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
function buildWriterFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
|
||||
$bits = getBitsRequired($memberNames);
|
||||
$lines = [];
|
||||
|
||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
||||
$lines[] = "\t\$this->writeInt($bits, match(\$value){";
|
||||
|
||||
foreach($memberNames as $key => $memberName){
|
||||
$lines[] = "\t\t$memberName => $key,";
|
||||
}
|
||||
$lines[] = "\t\tdefault => throw new \pocketmine\utils\AssumptionFailedError(\"All $virtualTypeName cases should be covered\")";
|
||||
$lines[] = "\t});";
|
||||
$lines[] = "}";
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $memberNames
|
||||
* @phpstan-param list<string> $memberNames
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
function buildReaderFunc(string $virtualTypeName, string $nativeTypeName, array $memberNames, string $functionName) : array{
|
||||
$bits = getBitsRequired($memberNames);
|
||||
$lines = [];
|
||||
|
||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
||||
$lines[] = "\t\$value = match(\$this->readInt($bits)){";
|
||||
|
||||
foreach($memberNames as $key => $memberName){
|
||||
$lines[] = "\t\t$key => $memberName,";
|
||||
}
|
||||
$lines[] = "\t\tdefault => throw new InvalidSerializedRuntimeDataException(\"Invalid serialized value for $virtualTypeName\")";
|
||||
$lines[] = "\t};";
|
||||
$lines[] = "}";
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
function buildInterfaceFunc(string $nativeTypeName, string $functionName) : string{
|
||||
return "public function $functionName(\\$nativeTypeName &\$value) : void;";
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[] $memberNames
|
||||
* @phpstan-param list<string> $memberNames
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
function buildSizeCalculationFunc(string $nativeTypeName, string $functionName, array $memberNames) : array{
|
||||
$lines = [];
|
||||
$lines[] = "public function $functionName(\\$nativeTypeName &\$value) : void{";
|
||||
$lines[] = "\t\$this->addBits(" . getBitsRequired($memberNames) . ");";
|
||||
$lines[] = "}";
|
||||
|
||||
return $lines;
|
||||
}
|
||||
|
||||
/**
|
||||
* @param mixed[] $members
|
||||
*/
|
||||
function getBitsRequired(array $members) : int{
|
||||
return (int) ceil(log(count($members), 2));
|
||||
}
|
||||
|
||||
/**
|
||||
* @param object[] $members
|
||||
* @phpstan-param array<string, object> $members
|
||||
*
|
||||
* @return string[]
|
||||
* @phpstan-return list<string>
|
||||
*/
|
||||
function stringifyEnumMembers(array $members, string $enumClass) : array{
|
||||
ksort($members, SORT_STRING);
|
||||
return array_map(fn(string $enumCaseName) => "\\$enumClass::$enumCaseName()", array_keys($members));
|
||||
}
|
||||
|
||||
$enumsUsed = [
|
||||
BellAttachmentType::getAll(),
|
||||
CopperOxidation::getAll(),
|
||||
CoralType::getAll(),
|
||||
DirtType::getAll(),
|
||||
DripleafState::getAll(),
|
||||
DyeColor::getAll(),
|
||||
FroglightType::getAll(),
|
||||
LeverFacing::getAll(),
|
||||
MedicineType::getAll(),
|
||||
MushroomBlockType::getAll(),
|
||||
MobHeadType::getAll(),
|
||||
SlabType::getAll(),
|
||||
SuspiciousStewType::getAll(),
|
||||
PotionType::getAll()
|
||||
];
|
||||
|
||||
$readerFuncs = [
|
||||
"" => [
|
||||
"abstract protected function readInt(int \$bits) : int;"
|
||||
]
|
||||
];
|
||||
$writerFuncs = [
|
||||
"" => [
|
||||
"abstract protected function writeInt(int \$bits, int \$value) : void;"
|
||||
]
|
||||
];
|
||||
$interfaceFuncs = [];
|
||||
$sizeCalculationFuncs = [
|
||||
"" => [
|
||||
"abstract protected function addBits(int \$bits) : void;"
|
||||
]
|
||||
];
|
||||
|
||||
foreach($enumsUsed as $enumMembers){
|
||||
if(count($enumMembers) === 0){
|
||||
throw new \InvalidArgumentException("Enum members cannot be empty");
|
||||
}
|
||||
$reflect = new \ReflectionClass($enumMembers[array_key_first($enumMembers)]);
|
||||
$virtualTypeName = $reflect->getShortName();
|
||||
$nativeTypeName = $reflect->getName();
|
||||
$functionName = lcfirst($virtualTypeName);
|
||||
|
||||
$stringifiedMembers = stringifyEnumMembers($enumMembers, $nativeTypeName);
|
||||
$writerFuncs[$functionName] = buildWriterFunc(
|
||||
$virtualTypeName,
|
||||
$nativeTypeName,
|
||||
$stringifiedMembers,
|
||||
$functionName
|
||||
);
|
||||
$readerFuncs[$functionName] = buildReaderFunc(
|
||||
$virtualTypeName,
|
||||
$nativeTypeName,
|
||||
$stringifiedMembers,
|
||||
$functionName
|
||||
);
|
||||
$interfaceFuncs[$functionName] = [buildInterfaceFunc(
|
||||
$nativeTypeName,
|
||||
$functionName
|
||||
)];
|
||||
$sizeCalculationFuncs[$functionName] = buildSizeCalculationFunc(
|
||||
$nativeTypeName,
|
||||
$functionName,
|
||||
$stringifiedMembers
|
||||
);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string[][] $functions
|
||||
* @phpstan-param array<string, list<string>> $functions
|
||||
*/
|
||||
function printFunctions(array $functions, string $className, string $classType) : void{
|
||||
ksort($functions, SORT_STRING);
|
||||
|
||||
ob_start();
|
||||
|
||||
echo <<<'HEADER'
|
||||
<?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\data\runtime;
|
||||
|
||||
/**
|
||||
* This class is auto-generated. Do not edit it manually.
|
||||
* @see build/generate-runtime-enum-serializers.php
|
||||
*/
|
||||
|
||||
HEADER;
|
||||
|
||||
echo "$classType $className{\n\n";
|
||||
echo implode("\n\n", array_map(fn(array $functionLines) => "\t" . implode("\n\t", $functionLines), $functions));
|
||||
echo "\n\n}\n";
|
||||
|
||||
file_put_contents(dirname(__DIR__) . '/src/data/runtime/' . $className . '.php', ob_get_clean());
|
||||
}
|
||||
|
||||
printFunctions($writerFuncs, "RuntimeEnumSerializerTrait", "trait");
|
||||
printFunctions($readerFuncs, "RuntimeEnumDeserializerTrait", "trait");
|
||||
printFunctions($interfaceFuncs, "RuntimeEnumDescriber", "interface");
|
||||
printFunctions($sizeCalculationFuncs, "RuntimeEnumSizeCalculatorTrait", "trait");
|
||||
|
||||
echo "Done. Don't forget to run CS fixup after generating code.\n";
|
@ -10,6 +10,7 @@ includes:
|
||||
- vendor/phpstan/phpstan-strict-rules/rules.neon
|
||||
|
||||
rules:
|
||||
- pocketmine\phpstan\rules\DeprecatedLegacyEnumAccessRule
|
||||
- pocketmine\phpstan\rules\DisallowEnumComparisonRule
|
||||
- pocketmine\phpstan\rules\DisallowForeachByReferenceRule
|
||||
- pocketmine\phpstan\rules\UnsafeForeachArrayOfStringRule
|
||||
|
@ -390,7 +390,7 @@ class Server{
|
||||
}
|
||||
|
||||
public function getGamemode() : GameMode{
|
||||
return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE, GameMode::SURVIVAL()->name())) ?? GameMode::SURVIVAL();
|
||||
return GameMode::fromString($this->configGroup->getConfigString(ServerProperties::GAME_MODE)) ?? GameMode::SURVIVAL;
|
||||
}
|
||||
|
||||
public function getForceGamemode() : bool{
|
||||
@ -818,7 +818,7 @@ class Server{
|
||||
ServerProperties::ENABLE_IPV6 => true,
|
||||
ServerProperties::WHITELIST => false,
|
||||
ServerProperties::MAX_PLAYERS => self::DEFAULT_MAX_PLAYERS,
|
||||
ServerProperties::GAME_MODE => GameMode::SURVIVAL()->name(),
|
||||
ServerProperties::GAME_MODE => GameMode::SURVIVAL->name, //TODO: this probably shouldn't use the enum name directly
|
||||
ServerProperties::FORCE_GAME_MODE => false,
|
||||
ServerProperties::HARDCORE => false,
|
||||
ServerProperties::PVP => true,
|
||||
@ -1020,7 +1020,7 @@ class Server{
|
||||
$this->forceShutdownExit();
|
||||
return;
|
||||
}
|
||||
if(!$this->enablePlugins(PluginEnableOrder::STARTUP())){
|
||||
if(!$this->enablePlugins(PluginEnableOrder::STARTUP)){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
||||
$this->forceShutdownExit();
|
||||
return;
|
||||
@ -1031,7 +1031,7 @@ class Server{
|
||||
return;
|
||||
}
|
||||
|
||||
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD())){
|
||||
if(!$this->enablePlugins(PluginEnableOrder::POSTWORLD)){
|
||||
$this->logger->emergency($this->language->translate(KnownTranslationFactory::pocketmine_plugin_someEnableErrors()));
|
||||
$this->forceShutdownExit();
|
||||
return;
|
||||
@ -1387,14 +1387,14 @@ class Server{
|
||||
public function enablePlugins(PluginEnableOrder $type) : bool{
|
||||
$allSuccess = true;
|
||||
foreach($this->pluginManager->getPlugins() as $plugin){
|
||||
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder()->equals($type)){
|
||||
if(!$plugin->isEnabled() && $plugin->getDescription()->getOrder() === $type){
|
||||
if(!$this->pluginManager->enablePlugin($plugin)){
|
||||
$allSuccess = false;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
if($type->equals(PluginEnableOrder::POSTWORLD())){
|
||||
if($type === PluginEnableOrder::POSTWORLD){
|
||||
$this->commandMap->registerServerAliases();
|
||||
}
|
||||
|
||||
|
133
src/block/AmethystCluster.php
Normal file
133
src/block/AmethystCluster.php
Normal file
@ -0,0 +1,133 @@
|
||||
<?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\utils\AmethystTrait;
|
||||
use pocketmine\block\utils\AnyFacingTrait;
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class AmethystCluster extends Transparent{
|
||||
use AmethystTrait;
|
||||
use AnyFacingTrait;
|
||||
|
||||
public const STAGE_SMALL_BUD = 0;
|
||||
public const STAGE_MEDIUM_BUD = 1;
|
||||
public const STAGE_LARGE_BUD = 2;
|
||||
public const STAGE_CLUSTER = 3;
|
||||
|
||||
private int $stage = self::STAGE_CLUSTER;
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedIntAuto(self::STAGE_SMALL_BUD, self::STAGE_CLUSTER, $this->stage);
|
||||
}
|
||||
|
||||
public function getStage() : int{ return $this->stage; }
|
||||
|
||||
public function setStage(int $stage) : self{
|
||||
if($stage < self::STAGE_SMALL_BUD || $stage > self::STAGE_CLUSTER){
|
||||
throw new \InvalidArgumentException("Size must be in range " . self::STAGE_SMALL_BUD . " ... " . self::STAGE_CLUSTER);
|
||||
}
|
||||
$this->stage = $stage;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{
|
||||
return match($this->stage){
|
||||
self::STAGE_SMALL_BUD => 1,
|
||||
self::STAGE_MEDIUM_BUD => 2,
|
||||
self::STAGE_LARGE_BUD => 4,
|
||||
self::STAGE_CLUSTER => 5,
|
||||
default => throw new AssumptionFailedError("Invalid stage $this->stage"),
|
||||
};
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$myAxis = Facing::axis($this->facing);
|
||||
|
||||
$box = AxisAlignedBB::one();
|
||||
foreach([Axis::Y, Axis::Z, Axis::X] as $axis){
|
||||
if($axis === $myAxis){
|
||||
continue;
|
||||
}
|
||||
$box->squash($axis, $this->stage === self::STAGE_SMALL_BUD ? 4 / 16 : 3 / 16);
|
||||
}
|
||||
$box->trim($this->facing, 1 - ($this->stage === self::STAGE_CLUSTER ? 7 / 16 : ($this->stage + 3) / 16));
|
||||
|
||||
return [$box];
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block, int $facing) : bool{
|
||||
return $block->getAdjacentSupportType($facing) === SupportType::FULL;
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace, Facing::opposite($face))){
|
||||
return false;
|
||||
}
|
||||
|
||||
$this->facing = $face;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this, Facing::opposite($this->facing))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
if($this->stage === self::STAGE_CLUSTER){
|
||||
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 4, maxBase: 4))];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
if($this->stage === self::STAGE_CLUSTER){
|
||||
return [VanillaItems::AMETHYST_SHARD()->setCount(FortuneDropHelper::weighted($item, min: 2, maxBase: 2))];
|
||||
}
|
||||
|
||||
return [];
|
||||
}
|
||||
}
|
@ -52,7 +52,7 @@ class Anvil extends Transparent implements Fallable{
|
||||
private int $damage = self::UNDAMAGED;
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(2, self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
|
||||
$w->boundedIntAuto(self::UNDAMAGED, self::VERY_DAMAGED, $this->damage);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
@ -78,7 +78,7 @@ class Anvil extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
@ -46,6 +47,7 @@ use function mt_rand;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class Bamboo extends Transparent{
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const NO_LEAVES = 0;
|
||||
public const SMALL_LEAVES = 1;
|
||||
@ -56,7 +58,7 @@ class Bamboo extends Transparent{
|
||||
protected int $leafSize = self::NO_LEAVES;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(2, self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
|
||||
$w->boundedIntAuto(self::NO_LEAVES, self::LARGE_LEAVES, $this->leafSize);
|
||||
$w->bool($this->thick);
|
||||
$w->bool($this->ready);
|
||||
}
|
||||
@ -95,7 +97,7 @@ class Bamboo extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private static function getOffsetSeed(int $x, int $y, int $z) : int{
|
||||
@ -120,12 +122,14 @@ class Bamboo extends Transparent{
|
||||
return new Vector3($retX, 0, $retZ);
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return
|
||||
$block->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$block->hasTypeTag(BlockTypeTags::SAND);
|
||||
$supportBlock->hasSameTypeId($this) ||
|
||||
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
private function seekToTop() : Bamboo{
|
||||
@ -153,14 +157,6 @@ class Bamboo extends Transparent{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
$below = $world->getBlock($this->position->down());
|
||||
if(!$this->canBeSupportedBy($below) && !$below->hasSameTypeId($this)){
|
||||
$world->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function grow(int $maxHeight, int $growAmount, ?Player $player) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||
|
@ -23,17 +23,21 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Bamboo as ItemBamboo;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class BambooSapling extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
private bool $ready = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
@ -48,19 +52,13 @@ final class BambooSapling extends Flowable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return
|
||||
$block->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||
$block->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$block->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedBy($blockReplace->position->getWorld()->getBlock($blockReplace->position->down()))){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
$supportBlock->getTypeId() === BlockTypeIds::GRAVEL ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
@ -73,13 +71,6 @@ final class BambooSapling extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($world->getBlock($this->position->down()))){
|
||||
$world->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : bool{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$world->getBlock($this->position->up())->canBeReplaced()){
|
||||
|
@ -26,7 +26,6 @@ namespace pocketmine\block;
|
||||
use pocketmine\block\tile\Banner as TileBanner;
|
||||
use pocketmine\block\utils\BannerPatternLayer;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Banner as ItemBanner;
|
||||
use pocketmine\item\Item;
|
||||
@ -48,11 +47,6 @@ abstract class BaseBanner extends Transparent{
|
||||
*/
|
||||
protected array $patterns = [];
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::BLACK();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
@ -111,7 +105,7 @@ abstract class BaseBanner extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
|
@ -131,6 +131,6 @@ abstract class BaseBigDripleaf extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\effect\EffectInstance;
|
||||
use pocketmine\entity\FoodSource;
|
||||
@ -31,27 +32,16 @@ use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
abstract class BaseCake extends Transparent implements FoodSource{
|
||||
use StaticSupportTrait;
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){ //Replace with common break method
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
@ -33,11 +32,6 @@ use function mt_rand;
|
||||
abstract class BaseCoral extends Transparent{
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->coralType = CoralType::TUBE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->dead){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
||||
@ -78,6 +72,6 @@ abstract class BaseCoral extends Transparent{
|
||||
protected function recalculateCollisionBoxes() : array{ return []; }
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -103,7 +103,7 @@ abstract class BaseSign extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
abstract protected function getSupportingFace() : int;
|
||||
@ -172,13 +172,13 @@ abstract class BaseSign extends Transparent{
|
||||
}
|
||||
|
||||
$dyeColor = $item instanceof Dye ? $item->getColor() : match($item->getTypeId()){
|
||||
ItemTypeIds::BONE_MEAL => DyeColor::WHITE(),
|
||||
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE(),
|
||||
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN(),
|
||||
ItemTypeIds::BONE_MEAL => DyeColor::WHITE,
|
||||
ItemTypeIds::LAPIS_LAZULI => DyeColor::BLUE,
|
||||
ItemTypeIds::COCOA_BEANS => DyeColor::BROWN,
|
||||
default => null
|
||||
};
|
||||
if($dyeColor !== null){
|
||||
$color = $dyeColor->equals(DyeColor::BLACK()) ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
|
||||
$color = $dyeColor === DyeColor::BLACK ? new Color(0, 0, 0) : $dyeColor->getRgbValue();
|
||||
if(
|
||||
$color->toARGB() !== $this->text->getBaseColor()->toARGB() &&
|
||||
$this->doSignChange(new SignText($this->text->getLines(), $color, $this->text->isGlowing()), $player, $item)
|
||||
|
@ -48,11 +48,6 @@ class Bed extends Transparent{
|
||||
protected bool $occupied = false;
|
||||
protected bool $head = false;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::RED();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->bool($this->occupied);
|
||||
@ -65,6 +60,8 @@ class Bed extends Transparent{
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if($tile instanceof TileBed){
|
||||
$this->color = $tile->getColor();
|
||||
}else{
|
||||
$this->color = DyeColor::RED; //legacy pre-1.1 beds don't have tiles
|
||||
}
|
||||
|
||||
return $this;
|
||||
@ -87,7 +84,7 @@ class Bed extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function isHeadPart() : bool{
|
||||
@ -209,7 +206,7 @@ class Bed extends Transparent{
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
|
||||
}
|
||||
|
||||
public function getMaxStackSize() : int{ return 1; }
|
||||
|
@ -35,32 +35,26 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\BellRingSound;
|
||||
|
||||
final class Bell extends Transparent{
|
||||
use HorizontalFacingTrait;
|
||||
|
||||
private BellAttachmentType $attachmentType;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->attachmentType = BellAttachmentType::FLOOR();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
private BellAttachmentType $attachmentType = BellAttachmentType::FLOOR;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->bellAttachmentType($this->attachmentType);
|
||||
$w->enum($this->attachmentType);
|
||||
$w->horizontalFacing($this->facing);
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
if($this->attachmentType->equals(BellAttachmentType::FLOOR())){
|
||||
if($this->attachmentType === BellAttachmentType::FLOOR){
|
||||
return [
|
||||
AxisAlignedBB::one()->squash(Facing::axis($this->facing), 1 / 4)->trim(Facing::UP, 3 / 16)
|
||||
];
|
||||
}
|
||||
if($this->attachmentType->equals(BellAttachmentType::CEILING())){
|
||||
if($this->attachmentType === BellAttachmentType::CEILING){
|
||||
return [
|
||||
AxisAlignedBB::one()->contract(1 / 4, 0, 1 / 4)->trim(Facing::DOWN, 1 / 4)
|
||||
];
|
||||
@ -72,12 +66,12 @@ final class Bell extends Transparent{
|
||||
->trim(Facing::DOWN, 1 / 4);
|
||||
|
||||
return [
|
||||
$this->attachmentType->equals(BellAttachmentType::ONE_WALL()) ? $box->trim($this->facing, 3 / 16) : $box
|
||||
$this->attachmentType === BellAttachmentType::ONE_WALL ? $box->trim($this->facing, 3 / 16) : $box
|
||||
];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function getAttachmentType() : BellAttachmentType{ return $this->attachmentType; }
|
||||
@ -89,7 +83,7 @@ final class Bell extends Transparent{
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
|
||||
return $block->getAdjacentSupportType($face) !== SupportType::NONE;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -100,15 +94,15 @@ final class Bell extends Transparent{
|
||||
if($player !== null){
|
||||
$this->setFacing(Facing::opposite($player->getHorizontalFacing()));
|
||||
}
|
||||
$this->setAttachmentType(BellAttachmentType::FLOOR());
|
||||
$this->setAttachmentType(BellAttachmentType::FLOOR);
|
||||
}elseif($face === Facing::DOWN){
|
||||
$this->setAttachmentType(BellAttachmentType::CEILING());
|
||||
$this->setAttachmentType(BellAttachmentType::CEILING);
|
||||
}else{
|
||||
$this->setFacing($face);
|
||||
$this->setAttachmentType(
|
||||
$this->canBeSupportedAt($blockReplace, $face) ?
|
||||
BellAttachmentType::TWO_WALLS() :
|
||||
BellAttachmentType::ONE_WALL()
|
||||
BellAttachmentType::TWO_WALLS :
|
||||
BellAttachmentType::ONE_WALL
|
||||
);
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
@ -116,11 +110,10 @@ final class Bell extends Transparent{
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
foreach(match($this->attachmentType){
|
||||
BellAttachmentType::CEILING() => [Facing::UP],
|
||||
BellAttachmentType::FLOOR() => [Facing::DOWN],
|
||||
BellAttachmentType::ONE_WALL() => [Facing::opposite($this->facing)],
|
||||
BellAttachmentType::TWO_WALLS() => [$this->facing, Facing::opposite($this->facing)],
|
||||
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
|
||||
BellAttachmentType::CEILING => [Facing::UP],
|
||||
BellAttachmentType::FLOOR => [Facing::DOWN],
|
||||
BellAttachmentType::ONE_WALL => [Facing::opposite($this->facing)],
|
||||
BellAttachmentType::TWO_WALLS => [$this->facing, Facing::opposite($this->facing)]
|
||||
} as $supportBlockDirection){
|
||||
if(!$this->canBeSupportedAt($this, $supportBlockDirection)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
@ -159,10 +152,9 @@ final class Bell extends Transparent{
|
||||
|
||||
private function isValidFaceToRing(int $faceHit) : bool{
|
||||
return match($this->attachmentType){
|
||||
BellAttachmentType::CEILING() => true,
|
||||
BellAttachmentType::FLOOR() => Facing::axis($faceHit) === Facing::axis($this->facing),
|
||||
BellAttachmentType::ONE_WALL(), BellAttachmentType::TWO_WALLS() => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
|
||||
default => throw new AssumptionFailedError("All cases of BellAttachmentType must be handled")
|
||||
BellAttachmentType::CEILING => true,
|
||||
BellAttachmentType::FLOOR => Facing::axis($faceHit) === Facing::axis($this->facing),
|
||||
BellAttachmentType::ONE_WALL, BellAttachmentType::TWO_WALLS => $faceHit === Facing::rotateY($this->facing, false) || $faceHit === Facing::rotateY($this->facing, true),
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -30,22 +30,16 @@ use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\sound\DripleafTiltDownSound;
|
||||
use pocketmine\world\sound\DripleafTiltUpSound;
|
||||
|
||||
class BigDripleafHead extends BaseBigDripleaf{
|
||||
|
||||
protected DripleafState $leafState;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->leafState = DripleafState::STABLE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
protected DripleafState $leafState = DripleafState::STABLE;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
parent::describeBlockOnlyState($w);
|
||||
$w->dripleafState($this->leafState);
|
||||
$w->enum($this->leafState);
|
||||
}
|
||||
|
||||
protected function isHead() : bool{
|
||||
@ -76,20 +70,20 @@ class BigDripleafHead extends BaseBigDripleaf{
|
||||
|
||||
private function getLeafTopOffset() : float{
|
||||
return match($this->leafState){
|
||||
DripleafState::STABLE(), DripleafState::UNSTABLE() => 1 / 16,
|
||||
DripleafState::PARTIAL_TILT() => 3 / 16,
|
||||
DripleafState::STABLE, DripleafState::UNSTABLE => 1 / 16,
|
||||
DripleafState::PARTIAL_TILT => 3 / 16,
|
||||
default => 0
|
||||
};
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if(!$entity instanceof Projectile && $this->leafState->equals(DripleafState::STABLE())){
|
||||
if(!$entity instanceof Projectile && $this->leafState === DripleafState::STABLE){
|
||||
//the entity must be standing on top of the leaf - do not collapse if the entity is standing underneath
|
||||
$intersection = AxisAlignedBB::one()
|
||||
->offset($this->position->x, $this->position->y, $this->position->z)
|
||||
->trim(Facing::DOWN, 1 - $this->getLeafTopOffset());
|
||||
if($entity->getBoundingBox()->intersectsWith($intersection)){
|
||||
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE());
|
||||
$this->setTiltAndScheduleTick(DripleafState::UNSTABLE);
|
||||
return false;
|
||||
}
|
||||
}
|
||||
@ -97,22 +91,21 @@ class BigDripleafHead extends BaseBigDripleaf{
|
||||
}
|
||||
|
||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
|
||||
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT());
|
||||
if($this->leafState !== DripleafState::FULL_TILT){
|
||||
$this->setTiltAndScheduleTick(DripleafState::FULL_TILT);
|
||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
||||
}
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
if(!$this->leafState->equals(DripleafState::STABLE())){
|
||||
if($this->leafState->equals(DripleafState::FULL_TILT())){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE()));
|
||||
if($this->leafState !== DripleafState::STABLE){
|
||||
if($this->leafState === DripleafState::FULL_TILT){
|
||||
$this->position->getWorld()->setBlock($this->position, $this->setLeafState(DripleafState::STABLE));
|
||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltUpSound());
|
||||
}else{
|
||||
$this->setTiltAndScheduleTick(match($this->leafState->id()){
|
||||
DripleafState::UNSTABLE()->id() => DripleafState::PARTIAL_TILT(),
|
||||
DripleafState::PARTIAL_TILT()->id() => DripleafState::FULL_TILT(),
|
||||
default => throw new AssumptionFailedError("All types should be covered")
|
||||
$this->setTiltAndScheduleTick(match($this->leafState){
|
||||
DripleafState::UNSTABLE => DripleafState::PARTIAL_TILT,
|
||||
DripleafState::PARTIAL_TILT => DripleafState::FULL_TILT,
|
||||
});
|
||||
$this->position->getWorld()->addSound($this->position, new DripleafTiltDownSound());
|
||||
}
|
||||
@ -120,7 +113,7 @@ class BigDripleafHead extends BaseBigDripleaf{
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
if(!$this->leafState->equals(DripleafState::FULL_TILT())){
|
||||
if($this->leafState !== DripleafState::FULL_TILT){
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
->trim(Facing::DOWN, 11 / 16)
|
||||
|
@ -42,7 +42,6 @@ use pocketmine\item\enchantment\ItemEnchantmentTags;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\ItemBlock;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
@ -50,22 +49,26 @@ use pocketmine\math\Vector3;
|
||||
use pocketmine\nbt\tag\CompoundTag;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\utils\Binary;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\format\Chunk;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\World;
|
||||
use function count;
|
||||
use function get_class;
|
||||
use function hash;
|
||||
use const PHP_INT_MAX;
|
||||
|
||||
class Block{
|
||||
public const INTERNAL_STATE_DATA_BITS = 8;
|
||||
public const INTERNAL_STATE_DATA_BITS = 11;
|
||||
public const INTERNAL_STATE_DATA_MASK = ~(~0 << self::INTERNAL_STATE_DATA_BITS);
|
||||
|
||||
/**
|
||||
* @internal
|
||||
* Hardcoded int is `Binary::readLong(hash('xxh3', Binary::writeLLong(BlockTypeIds::AIR), binary: true))`
|
||||
* TODO: it would be much easier if we could just make this 0 or some other easy value
|
||||
*/
|
||||
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (BlockTypeIds::AIR & self::INTERNAL_STATE_DATA_MASK);
|
||||
public const EMPTY_STATE_ID = (BlockTypeIds::AIR << self::INTERNAL_STATE_DATA_BITS) | (-7482769108513497636 & self::INTERNAL_STATE_DATA_MASK);
|
||||
|
||||
protected BlockIdentifier $idInfo;
|
||||
protected string $fallbackName;
|
||||
@ -80,6 +83,23 @@ class Block{
|
||||
|
||||
private Block $defaultState;
|
||||
|
||||
private int $stateIdXorMask;
|
||||
|
||||
/**
|
||||
* Computes the mask to be XOR'd with the state data.
|
||||
* This is to improve distribution of the state data bits, which occupy the least significant bits of the state ID.
|
||||
* Improved distribution improves PHP array performance when using the state ID as a key, as PHP arrays use some of
|
||||
* the lower bits of integer keys directly without hashing.
|
||||
*
|
||||
* The type ID is included in the XOR mask. This is not necessary to improve distribution, but it reduces the number
|
||||
* of operations required to compute the state ID (micro optimization).
|
||||
*/
|
||||
private static function computeStateIdXorMask(int $typeId) : int{
|
||||
return
|
||||
$typeId << self::INTERNAL_STATE_DATA_BITS |
|
||||
(Binary::readLong(hash('xxh3', Binary::writeLLong($typeId), binary: true)) & self::INTERNAL_STATE_DATA_MASK);
|
||||
}
|
||||
|
||||
/**
|
||||
* @param string $name English name of the block type (TODO: implement translations)
|
||||
*/
|
||||
@ -97,6 +117,9 @@ class Block{
|
||||
$this->describeBlockOnlyState($calculator);
|
||||
$this->requiredBlockOnlyStateDataBits = $calculator->getBitsUsed();
|
||||
|
||||
$this->stateIdXorMask = self::computeStateIdXorMask($idInfo->getBlockTypeId());
|
||||
|
||||
//this must be done last, otherwise the defaultState could have uninitialized fields
|
||||
$defaultState = clone $this;
|
||||
$this->defaultState = $defaultState;
|
||||
$defaultState->defaultState = $defaultState;
|
||||
@ -152,13 +175,7 @@ class Block{
|
||||
* {@link RuntimeBlockStateRegistry::fromStateId()}.
|
||||
*/
|
||||
public function getStateId() : int{
|
||||
$typeId = $this->getTypeId();
|
||||
//TODO: this XOR mask improves hashtable distribution, but it's only effective if the number of unique block
|
||||
//type IDs is larger than the number of available state data bits. We should probably hash (e.g. using xxhash)
|
||||
//the type ID to create a better mask.
|
||||
//Alternatively, we could hash the whole state ID, but this is currently problematic, since we currently need
|
||||
//to be able to recover the state data from the state ID because of UnknownBlock.
|
||||
return ($typeId << self::INTERNAL_STATE_DATA_BITS) | ($this->encodeFullState() ^ ($typeId & self::INTERNAL_STATE_DATA_MASK));
|
||||
return $this->encodeFullState() ^ $this->stateIdXorMask;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -228,12 +245,6 @@ class Block{
|
||||
}
|
||||
}
|
||||
|
||||
private function decodeFullState(int $data) : void{
|
||||
$reader = new RuntimeDataReader($this->requiredBlockItemStateDataBits + $this->requiredBlockOnlyStateDataBits, $data);
|
||||
$this->decodeBlockItemState($reader->readInt($this->requiredBlockItemStateDataBits));
|
||||
$this->decodeBlockOnlyState($reader->readInt($this->requiredBlockOnlyStateDataBits));
|
||||
}
|
||||
|
||||
private function encodeBlockItemState() : int{
|
||||
$writer = new RuntimeDataWriter($this->requiredBlockItemStateDataBits);
|
||||
|
||||
@ -304,34 +315,44 @@ class Block{
|
||||
if($bits > Block::INTERNAL_STATE_DATA_BITS){
|
||||
throw new \LogicException("Block state data cannot use more than " . Block::INTERNAL_STATE_DATA_BITS . " bits");
|
||||
}
|
||||
for($stateData = 0; $stateData < (1 << $bits); ++$stateData){
|
||||
$v = clone $this;
|
||||
for($blockItemStateData = 0; $blockItemStateData < (1 << $this->requiredBlockItemStateDataBits); ++$blockItemStateData){
|
||||
$withType = clone $this;
|
||||
try{
|
||||
$v->decodeFullState($stateData);
|
||||
if($v->encodeFullState() !== $stateData){
|
||||
throw new \LogicException(static::class . "::decodeStateData() accepts invalid state data (returned " . $v->encodeFullState() . " for input $stateData)");
|
||||
$withType->decodeBlockItemState($blockItemStateData);
|
||||
$encoded = $withType->encodeBlockItemState();
|
||||
if($encoded !== $blockItemStateData){
|
||||
throw new \LogicException(static::class . "::decodeBlockItemState() accepts invalid inputs (returned $encoded for input $blockItemStateData)");
|
||||
}
|
||||
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
|
||||
continue;
|
||||
}
|
||||
|
||||
yield $v;
|
||||
for($blockOnlyStateData = 0; $blockOnlyStateData < (1 << $this->requiredBlockOnlyStateDataBits); ++$blockOnlyStateData){
|
||||
$withState = clone $withType;
|
||||
try{
|
||||
$withState->decodeBlockOnlyState($blockOnlyStateData);
|
||||
$encoded = $withState->encodeBlockOnlyState();
|
||||
if($encoded !== $blockOnlyStateData){
|
||||
throw new \LogicException(static::class . "::decodeBlockOnlyState() accepts invalid inputs (returned $encoded for input $blockOnlyStateData)");
|
||||
}
|
||||
}catch(InvalidSerializedRuntimeDataException){ //invalid property combination, leave it
|
||||
continue;
|
||||
}
|
||||
|
||||
yield $withState;
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Called when this block is created, set, or has a neighbouring block update, to re-detect dynamic properties which
|
||||
* are not saved on the world.
|
||||
*
|
||||
* Clears any cached precomputed objects, such as bounding boxes. Remove any outdated precomputed things such as
|
||||
* AABBs and force recalculation.
|
||||
* are not saved in the blockstate ID.
|
||||
* If any such properties are updated, don't forget to clear things like AABB caches if necessary.
|
||||
*
|
||||
* A replacement block may be returned. This is useful if the block type changed due to reading of world data (e.g.
|
||||
* data from a block entity).
|
||||
*/
|
||||
public function readStateFromWorld() : Block{
|
||||
$this->collisionBoxes = null;
|
||||
|
||||
return $this;
|
||||
}
|
||||
|
||||
@ -571,6 +592,7 @@ class Block{
|
||||
*/
|
||||
final public function position(World $world, int $x, int $y, int $z) : void{
|
||||
$this->position = new Position($x, $y, $z, $world);
|
||||
$this->collisionBoxes = null;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -721,8 +743,14 @@ class Block{
|
||||
* @return Block
|
||||
*/
|
||||
public function getSide(int $side, int $step = 1){
|
||||
if($this->position->isValid()){
|
||||
return $this->position->getWorld()->getBlock($this->position->getSide($side, $step));
|
||||
$position = $this->position;
|
||||
if($position->isValid()){
|
||||
[$dx, $dy, $dz] = Facing::OFFSET[$side] ?? [0, 0, 0];
|
||||
return $position->getWorld()->getBlockAt(
|
||||
$position->x + ($dx * $step),
|
||||
$position->y + ($dy * $step),
|
||||
$position->z + ($dz * $step)
|
||||
);
|
||||
}
|
||||
|
||||
throw new \LogicException("Block does not have a valid world");
|
||||
@ -736,8 +764,14 @@ class Block{
|
||||
*/
|
||||
public function getHorizontalSides() : \Generator{
|
||||
$world = $this->position->getWorld();
|
||||
foreach($this->position->sidesAroundAxis(Axis::Y) as $vector3){
|
||||
yield $world->getBlock($vector3);
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
|
||||
//TODO: yield Facing as the key?
|
||||
yield $world->getBlockAt(
|
||||
$this->position->x + $dx,
|
||||
$this->position->y + $dy,
|
||||
$this->position->z + $dz
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -749,8 +783,13 @@ class Block{
|
||||
*/
|
||||
public function getAllSides() : \Generator{
|
||||
$world = $this->position->getWorld();
|
||||
foreach($this->position->sides() as $vector3){
|
||||
yield $world->getBlock($vector3);
|
||||
foreach(Facing::OFFSET as [$dx, $dy, $dz]){
|
||||
//TODO: yield Facing as the key?
|
||||
yield $world->getBlockAt(
|
||||
$this->position->x + $dx,
|
||||
$this->position->y + $dy,
|
||||
$this->position->z + $dz
|
||||
);
|
||||
}
|
||||
}
|
||||
|
||||
@ -877,7 +916,7 @@ class Block{
|
||||
* blocks placed on the given face can be supported by this block.
|
||||
*/
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::FULL();
|
||||
return SupportType::FULL;
|
||||
}
|
||||
|
||||
protected function getAdjacentSupportType(int $facing) : SupportType{
|
||||
|
@ -562,10 +562,10 @@ final class BlockTypeIds{
|
||||
public const WEIGHTED_PRESSURE_PLATE_HEAVY = 10532;
|
||||
public const WEIGHTED_PRESSURE_PLATE_LIGHT = 10533;
|
||||
public const WHEAT = 10534;
|
||||
|
||||
public const BUDDING_AMETHYST = 10535;
|
||||
public const WHITE_TULIP = 10536;
|
||||
public const WOOL = 10537;
|
||||
|
||||
public const AMETHYST_CLUSTER = 10538;
|
||||
public const GLAZED_TERRACOTTA = 10539;
|
||||
public const AMETHYST = 10540;
|
||||
public const ANCIENT_DEBRIS = 10541;
|
||||
@ -737,8 +737,16 @@ final class BlockTypeIds{
|
||||
public const BIG_DRIPLEAF_HEAD = 10707;
|
||||
public const BIG_DRIPLEAF_STEM = 10708;
|
||||
public const PINK_PETALS = 10709;
|
||||
public const CRIMSON_ROOTS = 10710;
|
||||
public const WARPED_ROOTS = 10711;
|
||||
public const CHISELED_BOOKSHELF = 10712;
|
||||
public const TORCHFLOWER = 10713;
|
||||
public const TORCHFLOWER_CROP = 10714;
|
||||
public const PITCHER_PLANT = 10715;
|
||||
public const PITCHER_CROP = 10716;
|
||||
public const DOUBLE_PITCHER_CROP = 10717;
|
||||
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10710;
|
||||
public const FIRST_UNUSED_BLOCK_ID = 10718;
|
||||
|
||||
private static int $nextDynamicId = self::FIRST_UNUSED_BLOCK_ID;
|
||||
|
||||
|
@ -34,6 +34,7 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function array_key_exists;
|
||||
use function spl_object_id;
|
||||
|
||||
class BrewingStand extends Transparent{
|
||||
|
||||
@ -44,7 +45,7 @@ class BrewingStand extends Transparent{
|
||||
protected array $slots = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->brewingStandSlots($this->slots);
|
||||
$w->enumSet($this->slots, BrewingStandSlot::cases());
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
@ -61,18 +62,18 @@ class BrewingStand extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function hasSlot(BrewingStandSlot $slot) : bool{
|
||||
return array_key_exists($slot->id(), $this->slots);
|
||||
return array_key_exists(spl_object_id($slot), $this->slots);
|
||||
}
|
||||
|
||||
public function setSlot(BrewingStandSlot $slot, bool $occupied) : self{
|
||||
if($occupied){
|
||||
$this->slots[$slot->id()] = $slot;
|
||||
$this->slots[spl_object_id($slot)] = $slot;
|
||||
}else{
|
||||
unset($this->slots[$slot->id()]);
|
||||
unset($this->slots[spl_object_id($slot)]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@ -89,7 +90,7 @@ class BrewingStand extends Transparent{
|
||||
public function setSlots(array $slots) : self{
|
||||
$this->slots = [];
|
||||
foreach($slots as $slot){
|
||||
$this->slots[$slot->id()] = $slot;
|
||||
$this->slots[spl_object_id($slot)] = $slot;
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
@ -114,7 +115,7 @@ class BrewingStand extends Transparent{
|
||||
}
|
||||
|
||||
$changed = false;
|
||||
foreach(BrewingStandSlot::getAll() as $slot){
|
||||
foreach(BrewingStandSlot::cases() as $slot){
|
||||
$occupied = !$brewing->getInventory()->isSlotEmpty($slot->getSlotNumber());
|
||||
if($occupied !== $this->hasSlot($slot)){
|
||||
$this->setSlot($slot, $occupied);
|
||||
|
68
src/block/BuddingAmethyst.php
Normal file
68
src/block/BuddingAmethyst.php
Normal file
@ -0,0 +1,68 @@
|
||||
<?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\utils\AmethystTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use function array_rand;
|
||||
use function mt_rand;
|
||||
|
||||
final class BuddingAmethyst extends Opaque{
|
||||
use AmethystTrait;
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(mt_rand(1, 5) === 1){
|
||||
$face = Facing::ALL[array_rand(Facing::ALL)];
|
||||
|
||||
$adjacent = $this->getSide($face);
|
||||
//TODO: amethyst buds can spawn in water - we need waterlogging support for this
|
||||
|
||||
$newStage = null;
|
||||
|
||||
if($adjacent->getTypeId() === BlockTypeIds::AIR){
|
||||
$newStage = AmethystCluster::STAGE_SMALL_BUD;
|
||||
}elseif(
|
||||
$adjacent->getTypeId() === BlockTypeIds::AMETHYST_CLUSTER &&
|
||||
$adjacent instanceof AmethystCluster &&
|
||||
$adjacent->getStage() < AmethystCluster::STAGE_CLUSTER &&
|
||||
$adjacent->getFacing() === $face
|
||||
){
|
||||
$newStage = $adjacent->getStage() + 1;
|
||||
}
|
||||
if($newStage !== null){
|
||||
BlockEventHelper::grow($adjacent, VanillaBlocks::AMETHYST_CLUSTER()->setStage($newStage)->setFacing($face), null);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
}
|
@ -23,39 +23,22 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\entity\EntityDamageByBlockEvent;
|
||||
use pocketmine\event\entity\EntityDamageEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Cactus extends Transparent{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
}
|
||||
@ -69,7 +52,7 @@ class Cactus extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
@ -78,23 +61,18 @@ class Cactus extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->hasSameTypeId($this) || $block->hasTypeTag(BlockTypeTags::SAND);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
$b = $this->getSide($side);
|
||||
if($b->isSolid()){
|
||||
$world->useBreakOn($this->position);
|
||||
break;
|
||||
}
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
if(!$supportBlock->hasSameTypeId($this) && !$supportBlock->hasTypeTag(BlockTypeTags::SAND)){
|
||||
return false;
|
||||
}
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
if($block->getSide($side)->isSolid()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return true;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
@ -124,18 +102,4 @@ class Cactus extends Transparent{
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
foreach(Facing::HORIZONTAL as $side){
|
||||
if($this->getSide($side)->isSolid()){
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
}
|
||||
|
@ -37,7 +37,7 @@ class Cake extends BaseCake{
|
||||
protected int $bites = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_BITES, $this->bites);
|
||||
$w->boundedIntAuto(0, self::MAX_BITES, $this->bites);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -30,7 +30,7 @@ class CakeWithDyedCandle extends CakeWithCandle{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
$this->color = DyeColor::WHITE;
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
|
@ -48,7 +48,7 @@ class Candle extends Transparent{
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$this->encodeLitState($w);
|
||||
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
}
|
||||
|
||||
public function getCount() : int{ return $this->count; }
|
||||
@ -91,7 +91,7 @@ class Candle extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
||||
|
@ -24,21 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Carpet extends Flowable{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
use StaticSupportTrait;
|
||||
|
||||
public function isSolid() : bool{
|
||||
return true;
|
||||
@ -51,19 +43,8 @@ class Carpet extends Flowable{
|
||||
return [AxisAlignedBB::one()->trim(Facing::UP, 15 / 16)];
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::AIR){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::AIR;
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
|
@ -61,7 +61,7 @@ final class Cauldron extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
|
||||
return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -83,7 +83,7 @@ final class Cauldron extends Transparent{
|
||||
}elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){
|
||||
//TODO: powder snow cauldron
|
||||
}elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion
|
||||
if($item->getType()->equals(PotionType::WATER())){
|
||||
if($item->getType() === PotionType::WATER){
|
||||
$this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
||||
}else{
|
||||
$this->fill(PotionCauldron::POTION_FILL_AMOUNT, VanillaBlocks::POTION_CAULDRON()->setPotionItem($item), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems);
|
||||
|
@ -23,7 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
@ -38,14 +40,16 @@ use pocketmine\world\sound\GlowBerriesPickSound;
|
||||
use function mt_rand;
|
||||
|
||||
class CaveVines extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MAX_AGE = 25;
|
||||
|
||||
protected int $age = 0;
|
||||
protected bool $berries = false;
|
||||
protected bool $head = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
|
||||
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
|
||||
$w->bool($this->berries);
|
||||
$w->bool($this->head);
|
||||
}
|
||||
@ -66,19 +70,6 @@ class CaveVines extends Flowable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function getAge() : int{
|
||||
return $this->age;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function canClimb() : bool{
|
||||
return true;
|
||||
}
|
||||
@ -89,19 +80,10 @@ class CaveVines extends Flowable{
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::UP);
|
||||
return $supportBlock->getSupportType(Facing::DOWN)->equals(SupportType::FULL()) || $supportBlock->hasSameTypeId($this);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
return $supportBlock->getSupportType(Facing::DOWN) === SupportType::FULL || $supportBlock->hasSameTypeId($this);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
$this->age = mt_rand(0, self::MAX_AGE);
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -177,6 +159,6 @@ class CaveVines extends Flowable{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -33,7 +33,7 @@ final class Chain extends Transparent{
|
||||
use PillarRotationTrait;
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
||||
return $this->axis === Axis::Y && Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
|
@ -45,7 +45,7 @@ class Chest extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onPostPlace() : void{
|
||||
|
134
src/block/ChiseledBookshelf.php
Normal file
134
src/block/ChiseledBookshelf.php
Normal file
@ -0,0 +1,134 @@
|
||||
<?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\ChiseledBookshelf as TileChiseledBookshelf;
|
||||
use pocketmine\block\utils\ChiseledBookshelfSlot;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Book;
|
||||
use pocketmine\item\EnchantedBook;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\WritableBookBase;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use function spl_object_id;
|
||||
|
||||
class ChiseledBookshelf extends Opaque{
|
||||
use HorizontalFacingTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
/**
|
||||
* @var ChiseledBookshelfSlot[]
|
||||
* @phpstan-var array<int, ChiseledBookshelfSlot>
|
||||
*/
|
||||
private array $slots = [];
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->enumSet($this->slots, ChiseledBookshelfSlot::cases());
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns whether the given slot is displayed as occupied.
|
||||
* This doesn't guarantee that there is or isn't a book in the bookshelf's inventory.
|
||||
*/
|
||||
public function hasSlot(ChiseledBookshelfSlot $slot) : bool{
|
||||
return isset($this->slots[spl_object_id($slot)]);
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets whether the given slot is displayed as occupied.
|
||||
*
|
||||
* This doesn't modify the bookshelf's inventory, so you can use this to make invisible
|
||||
* books or display books that aren't actually in the bookshelf.
|
||||
*
|
||||
* To modify the contents of the bookshelf inventory, access the tile inventory.
|
||||
*
|
||||
* @return $this
|
||||
*/
|
||||
public function setSlot(ChiseledBookshelfSlot $slot, bool $occupied) : self{
|
||||
if($occupied){
|
||||
$this->slots[spl_object_id($slot)] = $slot;
|
||||
}else{
|
||||
unset($this->slots[spl_object_id($slot)]);
|
||||
}
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns which slots of the bookshelf are displayed as occupied.
|
||||
* As above, these values do not necessarily reflect the contents of the bookshelf inventory,
|
||||
* although they usually will unless modified by plugins.
|
||||
*
|
||||
* @return ChiseledBookshelfSlot[]
|
||||
* @phpstan-return array<int, ChiseledBookshelfSlot>
|
||||
*/
|
||||
public function getSlots() : array{
|
||||
return $this->slots;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($face !== $this->facing){
|
||||
return false;
|
||||
}
|
||||
|
||||
$x = Facing::axis($face) === Axis::X ? $clickVector->getZ() : $clickVector->getX();
|
||||
$slot = ChiseledBookshelfSlot::fromBlockFaceCoordinates(
|
||||
Facing::isPositive(Facing::rotateY($face, true)) ? 1 - $x : $x,
|
||||
$clickVector->y
|
||||
);
|
||||
$tile = $this->position->getWorld()->getTile($this->position);
|
||||
if(!$tile instanceof TileChiseledBookshelf){
|
||||
return false;
|
||||
}
|
||||
|
||||
$inventory = $tile->getInventory();
|
||||
if(!$inventory->isSlotEmpty($slot->value)){
|
||||
$returnedItems[] = $inventory->getItem($slot->value);
|
||||
$inventory->clear($slot->value);
|
||||
$this->setSlot($slot, false);
|
||||
}elseif($item instanceof WritableBookBase || $item instanceof Book || $item instanceof EnchantedBook){
|
||||
//TODO: type tags like blocks would be better for this
|
||||
$inventory->setItem($slot->value, $item->pop());
|
||||
$this->setSlot($slot, true);
|
||||
}else{
|
||||
return true;
|
||||
}
|
||||
|
||||
$this->position->getWorld()->setBlock($this->position, $this);
|
||||
return true;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [];
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
}
|
@ -23,52 +23,38 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\entity\projectile\Projectile;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\RayTraceResult;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\Position;
|
||||
use pocketmine\world\sound\ChorusFlowerDieSound;
|
||||
use pocketmine\world\sound\ChorusFlowerGrowSound;
|
||||
use pocketmine\world\World;
|
||||
use function array_rand;
|
||||
use function min;
|
||||
use function mt_rand;
|
||||
|
||||
final class ChorusFlower extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MIN_AGE = 0;
|
||||
public const MAX_AGE = 5;
|
||||
|
||||
private const MAX_STEM_HEIGHT = 5;
|
||||
|
||||
private int $age = self::MIN_AGE;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, self::MIN_AGE, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < self::MIN_AGE || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in the range " . self::MIN_AGE . " ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
return [AxisAlignedBB::one()];
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Position $position) : bool{
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$position = $block->getPosition();
|
||||
$world = $position->getWorld();
|
||||
$down = $world->getBlock($position->down());
|
||||
|
||||
@ -93,25 +79,10 @@ final class ChorusFlower extends Flowable{
|
||||
return $plantAdjacent;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace->getPosition())){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this->position)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function onProjectileHit(Projectile $projectile, RayTraceResult $hitResult) : void{
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
|
||||
|
||||
/**
|
||||
* @phpstan-return array{int, bool}
|
||||
*/
|
||||
@ -181,11 +152,13 @@ final class ChorusFlower extends Flowable{
|
||||
if($tx === null){
|
||||
$tx = new BlockTransaction($this->position->getWorld());
|
||||
}
|
||||
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge($this->getAge() + $ageChange));
|
||||
$tx->addBlock($this->position->getSide($facing), (clone $this)->setAge(min(self::MAX_AGE, $this->getAge() + $ageChange)));
|
||||
|
||||
return $tx;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{ return $this->age < self::MAX_AGE; }
|
||||
|
||||
public function onRandomTick() : void{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
|
@ -23,18 +23,16 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\Position;
|
||||
use function mt_rand;
|
||||
|
||||
final class ChorusPlant extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$bb = AxisAlignedBB::one();
|
||||
@ -52,7 +50,8 @@ final class ChorusPlant extends Flowable{
|
||||
return $block->hasSameTypeId($this) || $block->getTypeId() === BlockTypeIds::END_STONE;
|
||||
}
|
||||
|
||||
private function canStay(Position $position) : bool{
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$position = $block->getPosition();
|
||||
$world = $position->getWorld();
|
||||
|
||||
$down = $world->getBlock($position->down());
|
||||
@ -72,24 +71,7 @@ final class ChorusPlant extends Flowable{
|
||||
}
|
||||
}
|
||||
|
||||
if($this->canBeSupportedBy($down)){
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canStay($blockReplace->getPosition())){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canStay($this->position)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
return $this->canBeSupportedBy($down);
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
@ -41,25 +42,13 @@ use function mt_rand;
|
||||
|
||||
class CocoaBlock extends Transparent{
|
||||
use HorizontalFacingTrait;
|
||||
use AgeableTrait;
|
||||
|
||||
public const MAX_AGE = 2;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
$w->boundedIntAuto(0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
/**
|
||||
@ -77,11 +66,11 @@ class CocoaBlock extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function canAttachTo(Block $block) : bool{
|
||||
return $block instanceof Wood && $block->getWoodType()->equals(WoodType::JUNGLE());
|
||||
return $block instanceof Wood && $block->getWoodType() === WoodType::JUNGLE;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -110,7 +99,7 @@ class CocoaBlock extends Transparent{
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
return $this->age < self::MAX_AGE;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
class Concrete extends Opaque{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -25,7 +25,6 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\Fallable;
|
||||
use pocketmine\block\utils\FallableTrait;
|
||||
use pocketmine\math\Facing;
|
||||
@ -36,11 +35,6 @@ class ConcretePowder extends Opaque implements Fallable{
|
||||
onNearbyBlockChange as protected startFalling;
|
||||
}
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(($water = $this->getAdjacentWater()) !== null){
|
||||
BlockEventHelper::form($this, VanillaBlocks::CONCRETE()->setColor($this->color), $water);
|
||||
|
@ -23,14 +23,8 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\CopperOxidation;
|
||||
use pocketmine\block\utils\CopperTrait;
|
||||
|
||||
class CopperStairs extends Stair{
|
||||
use CopperTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->oxidation = CopperOxidation::NONE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -23,29 +23,11 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class Coral extends BaseCoral{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
use StaticSupportTrait;
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
|
@ -24,7 +24,6 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\CoralType;
|
||||
use pocketmine\block\utils\CoralTypeTrait;
|
||||
use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
@ -32,11 +31,6 @@ use function mt_rand;
|
||||
final class CoralBlock extends Opaque{
|
||||
use CoralTypeTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->coralType = CoralType::TUBE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->dead){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(40, 200));
|
||||
|
@ -23,51 +23,34 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
abstract class Crops extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MAX_AGE = 7;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockReplace->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($this->age < self::MAX_AGE && $item instanceof Fertilizer){
|
||||
$block = clone $this;
|
||||
$block->age += mt_rand(2, 5);
|
||||
if($block->age > self::MAX_AGE){
|
||||
$block->age = self::MAX_AGE;
|
||||
$tempAge = $block->age + mt_rand(2, 5);
|
||||
if($tempAge > self::MAX_AGE){
|
||||
$tempAge = self::MAX_AGE;
|
||||
}
|
||||
$block->age = $tempAge;
|
||||
if(BlockEventHelper::grow($this, $block, $player)){
|
||||
$item->pop();
|
||||
}
|
||||
@ -78,14 +61,8 @@ abstract class Crops extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::FARMLAND){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
return $this->age < self::MAX_AGE;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
|
@ -42,7 +42,7 @@ class DaylightSensor extends Transparent{
|
||||
protected bool $inverted = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(4, 0, 15, $this->signalStrength);
|
||||
$w->boundedIntAuto(0, 15, $this->signalStrength);
|
||||
$w->bool($this->inverted);
|
||||
}
|
||||
|
||||
@ -70,7 +70,7 @@ class DaylightSensor extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -23,29 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
class DeadBush extends Flowable{
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedBy($this->getSide(Facing::DOWN))){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
use StaticSupportTrait;
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
return [
|
||||
@ -65,11 +50,12 @@ class DeadBush extends Flowable{
|
||||
return 100;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return
|
||||
$block->hasTypeTag(BlockTypeTags::SAND) ||
|
||||
$block->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
match($block->getTypeId()){
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::SAND) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD) ||
|
||||
match($supportBlock->getTypeId()){
|
||||
//can't use DIRT tag here because it includes farmland
|
||||
BlockTypeIds::PODZOL,
|
||||
BlockTypeIds::MYCELIUM,
|
||||
|
@ -38,15 +38,10 @@ use pocketmine\world\sound\ItemUseOnBlockSound;
|
||||
use pocketmine\world\sound\WaterSplashSound;
|
||||
|
||||
class Dirt extends Opaque{
|
||||
protected DirtType $dirtType;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->dirtType = DirtType::NORMAL();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
protected DirtType $dirtType = DirtType::NORMAL;
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->dirtType($this->dirtType);
|
||||
$w->enum($this->dirtType);
|
||||
}
|
||||
|
||||
public function getDirtType() : DirtType{ return $this->dirtType; }
|
||||
@ -67,16 +62,16 @@ class Dirt extends Opaque{
|
||||
|
||||
$item->applyDamage(1);
|
||||
|
||||
$newBlock = $this->dirtType->equals(DirtType::NORMAL()) ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
|
||||
$newBlock = $this->dirtType === DirtType::NORMAL ? VanillaBlocks::FARMLAND() : VanillaBlocks::DIRT();
|
||||
$center = $this->position->add(0.5, 0.5, 0.5);
|
||||
$world->addSound($center, new ItemUseOnBlockSound($newBlock));
|
||||
$world->setBlock($this->position, $newBlock);
|
||||
if($this->dirtType->equals(DirtType::ROOTED())){
|
||||
if($this->dirtType === DirtType::ROOTED){
|
||||
$world->dropItem($center, VanillaBlocks::HANGING_ROOTS()->asItem());
|
||||
}
|
||||
|
||||
return true;
|
||||
}elseif($this->dirtType->equals(DirtType::ROOTED()) && $item instanceof Fertilizer){
|
||||
}elseif($this->dirtType === DirtType::ROOTED && $item instanceof Fertilizer){
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getTypeId() !== BlockTypeIds::AIR){
|
||||
return true;
|
||||
@ -85,7 +80,7 @@ class Dirt extends Opaque{
|
||||
$item->pop();
|
||||
$world->setBlock($down->position, VanillaBlocks::HANGING_ROOTS());
|
||||
//TODO: bonemeal particles, growth sounds
|
||||
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType()->equals(PotionType::WATER())){
|
||||
}elseif(($item instanceof Potion || $item instanceof SplashPotion) && $item->getType() === PotionType::WATER){
|
||||
$item->pop();
|
||||
$world->setBlock($this->position, VanillaBlocks::MUD());
|
||||
$world->addSound($this->position, new WaterSplashSound(0.5));
|
||||
|
@ -51,6 +51,8 @@ class Door extends Transparent{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
|
||||
$this->collisionBoxes = null;
|
||||
|
||||
//copy door properties from other half
|
||||
$other = $this->getSide($this->top ? Facing::DOWN : Facing::UP);
|
||||
if($other instanceof Door && $other->hasSameTypeId($this)){
|
||||
@ -102,7 +104,7 @@ class Door extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
110
src/block/DoublePitcherCrop.php
Normal file
110
src/block/DoublePitcherCrop.php
Normal file
@ -0,0 +1,110 @@
|
||||
<?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\utils\AgeableTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
final class DoublePitcherCrop extends DoublePlant{
|
||||
use AgeableTrait {
|
||||
describeBlockOnlyState as describeAge;
|
||||
}
|
||||
|
||||
public const MAX_AGE = 1;
|
||||
|
||||
public function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
parent::describeBlockOnlyState($w);
|
||||
$this->describeAge($w);
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
if($this->top){
|
||||
return [];
|
||||
}
|
||||
|
||||
//the pod exists only in the bottom half of the plant
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
->trim(Facing::UP, 11 / 16)
|
||||
->squash(Axis::X, 3 / 16)
|
||||
->squash(Axis::Z, 3 / 16)
|
||||
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
|
||||
];
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : bool{
|
||||
if($this->age >= self::MAX_AGE){
|
||||
return false;
|
||||
}
|
||||
|
||||
$bottom = $this->top ? $this->getSide(Facing::DOWN) : $this;
|
||||
$top = $this->top ? $this : $this->getSide(Facing::UP);
|
||||
if($top->getTypeId() !== BlockTypeIds::AIR && !$top->hasSameTypeId($this)){
|
||||
return false;
|
||||
}
|
||||
|
||||
$newState = (clone $this)->setAge($this->age + 1);
|
||||
|
||||
$tx = new BlockTransaction($this->position->getWorld());
|
||||
$tx->addBlock($bottom->position, (clone $newState)->setTop(false));
|
||||
$tx->addBlock($top->position, (clone $newState)->setTop(true));
|
||||
|
||||
$ev = new StructureGrowEvent($bottom, $tx, $player);
|
||||
$ev->call();
|
||||
|
||||
return !$ev->isCancelled() && $tx->apply();
|
||||
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer && $this->grow($player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return $this->age < self::MAX_AGE && !$this->top;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
//TODO: the growth speed is influenced by farmland and nearby crops
|
||||
//only the bottom half of the plant can grow randomly
|
||||
if(mt_rand(0, 2) === 0 && !$this->top){
|
||||
$this->grow(null);
|
||||
}
|
||||
}
|
||||
}
|
@ -50,7 +50,7 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function onAttack(Item $item, int $face, ?Player $player = null) : bool{
|
||||
if($player !== null && !$player->getGamemode()->equals(GameMode::CREATIVE())){
|
||||
if($player !== null && $player->getGamemode() !== GameMode::CREATIVE){
|
||||
$this->teleport();
|
||||
return true;
|
||||
}
|
||||
@ -82,6 +82,6 @@ class DragonEgg extends Transparent implements Fallable{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -24,19 +24,13 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
class DyedCandle extends Candle{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function getCandleIfCompatibleType(Block $block) : ?Candle{
|
||||
$result = parent::getCandleIfCompatibleType($block);
|
||||
//different coloured candles can't be combined in the same block
|
||||
return $result instanceof DyedCandle && $result->color->equals($this->color) ? $result : null;
|
||||
return $result instanceof DyedCandle && $result->color === $this->color ? $result : null;
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class DyedShulkerBox extends ShulkerBox{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -41,7 +41,7 @@ class EnchantingTable extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -49,7 +49,7 @@ class EnderChest extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
|
@ -31,15 +31,40 @@ use pocketmine\event\entity\EntityTrampleFarmlandEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use function intdiv;
|
||||
use function lcg_value;
|
||||
|
||||
class Farmland extends Transparent{
|
||||
public const MAX_WETNESS = 7;
|
||||
|
||||
private const WATER_SEARCH_HORIZONTAL_LENGTH = 9;
|
||||
|
||||
private const WATER_SEARCH_VERTICAL_LENGTH = 2;
|
||||
|
||||
private const WATER_POSITION_INDEX_UNKNOWN = -1;
|
||||
/** Total possible options for water X/Z indexes */
|
||||
private const WATER_POSITION_INDICES_TOTAL = (self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2) * 2;
|
||||
|
||||
protected int $wetness = 0; //"moisture" blockstate property in PC
|
||||
|
||||
/**
|
||||
* Cached value indicating the relative coordinates of the most recently found water block.
|
||||
*
|
||||
* If this is set to a non-unknown value, the farmland block will check the relative coordinates indicated by
|
||||
* this value for water, before searching the entire 9x2x9 grid around the farmland. This significantly benefits
|
||||
* hydrating or fully hydrated farmland, avoiding the need for costly searches on every random tick.
|
||||
*
|
||||
* If the coordinates indicated don't contain water, the full 9x2x9 volume will be searched as before. A new index
|
||||
* will be recorded if water is found, otherwise it will be set to unknown and future searches will search the full
|
||||
* 9x2x9 volume again.
|
||||
*
|
||||
* This property is not exposed to the API or saved on disk. It is only used by PocketMine-MP at runtime as a cache.
|
||||
*/
|
||||
private int $waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_WETNESS, $this->wetness);
|
||||
$w->boundedIntAuto(0, self::MAX_WETNESS, $this->wetness);
|
||||
$w->boundedIntAuto(-1, self::WATER_POSITION_INDICES_TOTAL - 1, $this->waterPositionIndex);
|
||||
}
|
||||
|
||||
public function getWetness() : int{ return $this->wetness; }
|
||||
@ -53,6 +78,22 @@ class Farmland extends Transparent{
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function getWaterPositionIndex() : int{ return $this->waterPositionIndex; }
|
||||
|
||||
/**
|
||||
* @internal
|
||||
*/
|
||||
public function setWaterPositionIndex(int $waterPositionIndex) : self{
|
||||
if($waterPositionIndex < -1 || $waterPositionIndex >= self::WATER_POSITION_INDICES_TOTAL){
|
||||
throw new \InvalidArgumentException("Water XZ index must be in range -1 ... " . (self::WATER_POSITION_INDICES_TOTAL - 1));
|
||||
}
|
||||
$this->waterPositionIndex = $waterPositionIndex;
|
||||
return $this;
|
||||
}
|
||||
|
||||
/**
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
@ -72,6 +113,11 @@ class Farmland extends Transparent{
|
||||
|
||||
public function onRandomTick() : void{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
//this property may be updated by canHydrate() - track this so we know if we need to set the block again
|
||||
$oldWaterPositionIndex = $this->waterPositionIndex;
|
||||
$changed = false;
|
||||
|
||||
if(!$this->canHydrate()){
|
||||
if($this->wetness > 0){
|
||||
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, $this->wetness - 1);
|
||||
@ -79,9 +125,11 @@ class Farmland extends Transparent{
|
||||
if(!$event->isCancelled()){
|
||||
$this->wetness = $event->getNewHydration();
|
||||
$world->setBlock($this->position, $this, false);
|
||||
$changed = true;
|
||||
}
|
||||
}else{
|
||||
$world->setBlock($this->position, VanillaBlocks::DIRT());
|
||||
$changed = true;
|
||||
}
|
||||
}elseif($this->wetness < self::MAX_WETNESS){
|
||||
$event = new FarmlandHydrationChangeEvent($this, $this->wetness, self::MAX_WETNESS);
|
||||
@ -89,8 +137,14 @@ class Farmland extends Transparent{
|
||||
if(!$event->isCancelled()){
|
||||
$this->wetness = $event->getNewHydration();
|
||||
$world->setBlock($this->position, $this, false);
|
||||
$changed = true;
|
||||
}
|
||||
}
|
||||
|
||||
if(!$changed && $oldWaterPositionIndex !== $this->waterPositionIndex){
|
||||
//ensure the water square index is saved regardless of whether anything else happened
|
||||
$world->setBlock($this->position, $this, false);
|
||||
}
|
||||
}
|
||||
|
||||
public function onEntityLand(Entity $entity) : ?float{
|
||||
@ -105,19 +159,39 @@ class Farmland extends Transparent{
|
||||
}
|
||||
|
||||
protected function canHydrate() : bool{
|
||||
//TODO: check rain
|
||||
$start = $this->position->add(-4, 0, -4);
|
||||
$end = $this->position->add(4, 1, 4);
|
||||
for($y = $start->y; $y <= $end->y; ++$y){
|
||||
for($z = $start->z; $z <= $end->z; ++$z){
|
||||
for($x = $start->x; $x <= $end->x; ++$x){
|
||||
if($this->position->getWorld()->getBlockAt($x, $y, $z) instanceof Water){
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$startX = $this->position->getFloorX() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
|
||||
$startY = $this->position->getFloorY();
|
||||
$startZ = $this->position->getFloorZ() - (int) (self::WATER_SEARCH_HORIZONTAL_LENGTH / 2);
|
||||
|
||||
if($this->waterPositionIndex !== self::WATER_POSITION_INDEX_UNKNOWN){
|
||||
$raw = $this->waterPositionIndex;
|
||||
$x = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
|
||||
$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
|
||||
$z = $raw % self::WATER_SEARCH_HORIZONTAL_LENGTH;
|
||||
$raw = intdiv($raw, self::WATER_SEARCH_HORIZONTAL_LENGTH);
|
||||
$y = $raw % self::WATER_SEARCH_VERTICAL_LENGTH;
|
||||
|
||||
if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
|
||||
return true;
|
||||
}
|
||||
}
|
||||
|
||||
//no water found at cached position - search the whole area
|
||||
//y will increment after x/z have been exhausted, as usually water will be at the same Y as the farmland
|
||||
for($y = 0; $y < self::WATER_SEARCH_VERTICAL_LENGTH; $y++){
|
||||
for($x = 0; $x < self::WATER_SEARCH_HORIZONTAL_LENGTH; $x++){
|
||||
for($z = 0; $z < self::WATER_SEARCH_HORIZONTAL_LENGTH; $z++){
|
||||
if($world->getBlockAt($startX + $x, $startY + $y, $startZ + $z) instanceof Water){
|
||||
$this->waterPositionIndex = $x + ($z * self::WATER_SEARCH_HORIZONTAL_LENGTH) + ($y * self::WATER_SEARCH_HORIZONTAL_LENGTH ** 2);
|
||||
return true;
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
$this->waterPositionIndex = self::WATER_POSITION_INDEX_UNKNOWN;
|
||||
return false;
|
||||
}
|
||||
|
||||
|
@ -40,9 +40,11 @@ class Fence extends Transparent{
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
|
||||
$this->collisionBoxes = null;
|
||||
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
$block = $this->getSide($facing);
|
||||
if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing))->equals(SupportType::FULL())){
|
||||
if($block instanceof static || $block instanceof FenceGate || $block->getSupportType(Facing::opposite($facing)) === SupportType::FULL){
|
||||
$this->connections[$facing] = true;
|
||||
}else{
|
||||
unset($this->connections[$facing]);
|
||||
@ -98,6 +100,6 @@ class Fence extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER() : SupportType::NONE();
|
||||
return Facing::axis($facing) === Axis::Y ? SupportType::CENTER : SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -72,7 +72,7 @@ class FenceGate extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function checkInWall() : bool{
|
||||
|
@ -38,7 +38,7 @@ abstract class FillableCauldron extends Transparent{
|
||||
private int $fillLevel = self::MIN_FILL_LEVEL;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
|
||||
$w->boundedIntAuto(self::MIN_FILL_LEVEL, self::MAX_FILL_LEVEL, $this->fillLevel);
|
||||
}
|
||||
|
||||
public function getFillLevel() : int{ return $this->fillLevel; }
|
||||
@ -64,7 +64,7 @@ abstract class FillableCauldron extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return $facing === Facing::UP ? SupportType::EDGE() : SupportType::NONE();
|
||||
return $facing === Facing::UP ? SupportType::EDGE : SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function withFillLevel(int $fillLevel) : Block{
|
||||
|
@ -23,9 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\BlockBurnEvent;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\world\format\Chunk;
|
||||
@ -36,31 +36,16 @@ use function min;
|
||||
use function mt_rand;
|
||||
|
||||
class Fire extends BaseFire{
|
||||
use AgeableTrait;
|
||||
|
||||
public const MAX_AGE = 15;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(4, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function getFireDamage() : int{
|
||||
return 1;
|
||||
}
|
||||
|
||||
private function canBeSupportedBy(Block $block) : bool{
|
||||
return $block->getSupportType(Facing::UP)->equals(SupportType::FULL());
|
||||
return $block->getSupportType(Facing::UP) === SupportType::FULL;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
@ -23,6 +23,7 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
@ -35,6 +36,8 @@ use function atan2;
|
||||
use function rad2deg;
|
||||
|
||||
final class FloorCoralFan extends BaseCoral{
|
||||
use StaticSupportTrait;
|
||||
|
||||
private int $axis = Axis::X;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
@ -53,9 +56,6 @@ final class FloorCoralFan extends BaseCoral{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
if($player !== null){
|
||||
$playerBlockPos = $player->getPosition()->floor();
|
||||
$directionVector = $blockReplace->getPosition()->subtractVector($playerBlockPos)->normalize();
|
||||
@ -73,15 +73,6 @@ final class FloorCoralFan extends BaseCoral{
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$world = $this->position->getWorld();
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$world->useBreakOn($this->position);
|
||||
}else{
|
||||
parent::onNearbyBlockChange();
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
|
@ -48,6 +48,6 @@ abstract class Flowable extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -23,28 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Flower extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||
}
|
||||
|
||||
public function getFlameEncouragement() : int{
|
||||
|
@ -24,15 +24,16 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\tile\FlowerPot as TileFlowerPot;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function assert;
|
||||
|
||||
class FlowerPot extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
protected ?Block $plant = null;
|
||||
|
||||
@ -89,20 +90,6 @@ class FlowerPot extends Flowable{
|
||||
return [AxisAlignedBB::one()->contract(3 / 16, 0, 3 / 16)->trim(Facing::UP, 5 / 8)];
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
|
@ -28,15 +28,10 @@ use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
|
||||
final class Froglight extends SimplePillar{
|
||||
|
||||
private FroglightType $froglightType;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->froglightType = FroglightType::OCHRE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
private FroglightType $froglightType = FroglightType::OCHRE;
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->froglightType($this->froglightType);
|
||||
$w->enum($this->froglightType);
|
||||
}
|
||||
|
||||
public function getFroglightType() : FroglightType{ return $this->froglightType; }
|
||||
|
@ -23,30 +23,15 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use function mt_rand;
|
||||
|
||||
class FrostedIce extends Ice{
|
||||
use AgeableTrait;
|
||||
|
||||
public const MAX_AGE = 3;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ... " . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, mt_rand(20, 40));
|
||||
}
|
||||
|
@ -24,15 +24,9 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
use pocketmine\block\utils\FacesOppositePlacingPlayerTrait;
|
||||
|
||||
class GlazedTerracotta extends Opaque{
|
||||
use ColoredTrait;
|
||||
use FacesOppositePlacingPlayerTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::BLACK();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -95,7 +95,7 @@ class GlowLichen extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function canBeReplaced() : bool{
|
||||
@ -121,7 +121,7 @@ class GlowLichen extends Transparent{
|
||||
$changed = false;
|
||||
|
||||
foreach($this->faces as $face){
|
||||
if(!$this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
|
||||
if($this->getAdjacentSupportType($face) !== SupportType::FULL){
|
||||
unset($this->faces[$face]);
|
||||
$changed = true;
|
||||
}
|
||||
@ -156,7 +156,7 @@ class GlowLichen extends Transparent{
|
||||
$supportBlock = $world->getBlock($replacePos->getSide($spreadFace));
|
||||
$supportFace = Facing::opposite($spreadFace);
|
||||
|
||||
if(!$supportBlock->getSupportType($supportFace)->equals(SupportType::FULL())){
|
||||
if($supportBlock->getSupportType($supportFace) !== SupportType::FULL){
|
||||
return false;
|
||||
}
|
||||
|
||||
@ -268,7 +268,7 @@ class GlowLichen extends Transparent{
|
||||
private function getAvailableFaces() : array{
|
||||
$faces = [];
|
||||
foreach(Facing::ALL as $face){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face)->equals(SupportType::FULL())){
|
||||
if(!$this->hasFace($face) && $this->getAdjacentSupportType($face) === SupportType::FULL){
|
||||
$faces[$face] = $face;
|
||||
}
|
||||
}
|
||||
|
@ -69,7 +69,7 @@ class Grass extends Opaque{
|
||||
$b = $world->getBlockAt($x, $y, $z);
|
||||
if(
|
||||
!($b instanceof Dirt) ||
|
||||
!$b->getDirtType()->equals(DirtType::NORMAL()) ||
|
||||
$b->getDirtType() !== DirtType::NORMAL ||
|
||||
$world->getFullLightAt($x, $y + 1, $z) < 4 ||
|
||||
$world->getBlockAt($x, $y + 1, $z)->getLightFilter() >= 2
|
||||
){
|
||||
|
@ -23,32 +23,18 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\enchantment\VanillaEnchantments;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class HangingRoots extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::UP)->hasCenterSupport(); //weird I know, but they can be placed on the bottom of fences
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function getDropsForIncompatibleTool(Item $item) : array{
|
||||
if($item->hasEnchantment(VanillaEnchantments::SILK_TOUCH())){
|
||||
return $this->getDropsForCompatibleTool($item);
|
||||
|
@ -68,9 +68,9 @@ class Hopper extends Transparent{
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return match($facing){
|
||||
Facing::UP => SupportType::FULL(),
|
||||
Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER() : SupportType::NONE(),
|
||||
default => SupportType::NONE()
|
||||
Facing::UP => SupportType::FULL,
|
||||
Facing::DOWN => $this->facing === Facing::DOWN ? SupportType::CENTER : SupportType::NONE,
|
||||
default => SupportType::NONE
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -164,7 +164,7 @@ class ItemFrame extends Flowable{
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return !$block->getAdjacentSupportType($face)->equals(SupportType::NONE());
|
||||
return $block->getAdjacentSupportType($face) !== SupportType::NONE;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
|
@ -66,7 +66,7 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
@ -85,6 +85,6 @@ class Ladder extends Transparent{
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block, int $face) : bool{
|
||||
return $block->getAdjacentSupportType($face)->equals(SupportType::FULL());
|
||||
return $block->getAdjacentSupportType($face) === SupportType::FULL;
|
||||
}
|
||||
}
|
||||
|
@ -73,7 +73,7 @@ class Lantern extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
|
@ -33,12 +33,13 @@ use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\AssumptionFailedError;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\World;
|
||||
use function mt_rand;
|
||||
|
||||
class Leaves extends Transparent{
|
||||
private const MAX_LOG_DISTANCE = 4;
|
||||
|
||||
protected LeavesType $leavesType; //immutable for now
|
||||
protected bool $noDecay = false;
|
||||
protected bool $checkDecay = false;
|
||||
@ -92,7 +93,7 @@ class Leaves extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
if($block instanceof Leaves && $distance <= 4){
|
||||
if($block instanceof Leaves && $distance <= self::MAX_LOG_DISTANCE){
|
||||
foreach(Facing::ALL as $side){
|
||||
if($this->findLog($pos->getSide($side), $visited, $distance + 1)){
|
||||
return true;
|
||||
@ -111,7 +112,7 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
return !$this->noDecay && $this->checkDecay;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
@ -147,23 +148,22 @@ class Leaves extends Transparent{
|
||||
if(FortuneDropHelper::bonusChanceDivisor($item, 20, 4)){ //Saplings
|
||||
// TODO: according to the wiki, the jungle saplings have a different drop rate
|
||||
$sapling = (match($this->leavesType){
|
||||
LeavesType::ACACIA() => VanillaBlocks::ACACIA_SAPLING(),
|
||||
LeavesType::BIRCH() => VanillaBlocks::BIRCH_SAPLING(),
|
||||
LeavesType::DARK_OAK() => VanillaBlocks::DARK_OAK_SAPLING(),
|
||||
LeavesType::JUNGLE() => VanillaBlocks::JUNGLE_SAPLING(),
|
||||
LeavesType::OAK() => VanillaBlocks::OAK_SAPLING(),
|
||||
LeavesType::SPRUCE() => VanillaBlocks::SPRUCE_SAPLING(),
|
||||
LeavesType::MANGROVE(), //TODO: mangrove propagule
|
||||
LeavesType::AZALEA(), LeavesType::FLOWERING_AZALEA() => null, //TODO: azalea
|
||||
LeavesType::CHERRY() => null, //TODO: cherry
|
||||
default => throw new AssumptionFailedError("Unreachable")
|
||||
LeavesType::ACACIA => VanillaBlocks::ACACIA_SAPLING(),
|
||||
LeavesType::BIRCH => VanillaBlocks::BIRCH_SAPLING(),
|
||||
LeavesType::DARK_OAK => VanillaBlocks::DARK_OAK_SAPLING(),
|
||||
LeavesType::JUNGLE => VanillaBlocks::JUNGLE_SAPLING(),
|
||||
LeavesType::OAK => VanillaBlocks::OAK_SAPLING(),
|
||||
LeavesType::SPRUCE => VanillaBlocks::SPRUCE_SAPLING(),
|
||||
LeavesType::MANGROVE, //TODO: mangrove propagule
|
||||
LeavesType::AZALEA, LeavesType::FLOWERING_AZALEA => null, //TODO: azalea
|
||||
LeavesType::CHERRY => null, //TODO: cherry
|
||||
})?->asItem();
|
||||
if($sapling !== null){
|
||||
$drops[] = $sapling;
|
||||
}
|
||||
}
|
||||
if(
|
||||
($this->leavesType->equals(LeavesType::OAK()) || $this->leavesType->equals(LeavesType::DARK_OAK())) &&
|
||||
($this->leavesType === LeavesType::OAK || $this->leavesType === LeavesType::DARK_OAK) &&
|
||||
FortuneDropHelper::bonusChanceDivisor($item, 200, 20)
|
||||
){ //Apples
|
||||
$drops[] = VanillaItems::APPLE();
|
||||
@ -188,6 +188,6 @@ class Leaves extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -87,7 +87,7 @@ class Lectern extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function isProducingSignal() : bool{ return $this->producingSignal; }
|
||||
|
@ -36,16 +36,11 @@ use pocketmine\world\sound\RedstonePowerOffSound;
|
||||
use pocketmine\world\sound\RedstonePowerOnSound;
|
||||
|
||||
class Lever extends Flowable{
|
||||
protected LeverFacing $facing;
|
||||
protected LeverFacing $facing = LeverFacing::UP_AXIS_X;
|
||||
protected bool $activated = false;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->facing = LeverFacing::UP_AXIS_X();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->leverFacing($this->facing);
|
||||
$w->enum($this->facing);
|
||||
$w->bool($this->activated);
|
||||
}
|
||||
|
||||
@ -77,12 +72,12 @@ class Lever extends Flowable{
|
||||
return $x;
|
||||
};
|
||||
$this->facing = match($face){
|
||||
Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X(), LeverFacing::DOWN_AXIS_Z()),
|
||||
Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X(), LeverFacing::UP_AXIS_Z()),
|
||||
Facing::NORTH => LeverFacing::NORTH(),
|
||||
Facing::SOUTH => LeverFacing::SOUTH(),
|
||||
Facing::WEST => LeverFacing::WEST(),
|
||||
Facing::EAST => LeverFacing::EAST(),
|
||||
Facing::DOWN => $selectUpDownPos(LeverFacing::DOWN_AXIS_X, LeverFacing::DOWN_AXIS_Z),
|
||||
Facing::UP => $selectUpDownPos(LeverFacing::UP_AXIS_X, LeverFacing::UP_AXIS_Z),
|
||||
Facing::NORTH => LeverFacing::NORTH,
|
||||
Facing::SOUTH => LeverFacing::SOUTH,
|
||||
Facing::WEST => LeverFacing::WEST,
|
||||
Facing::EAST => LeverFacing::EAST,
|
||||
default => throw new AssumptionFailedError("Bad facing value"),
|
||||
};
|
||||
|
||||
|
@ -35,7 +35,7 @@ final class Light extends Flowable{
|
||||
private int $level = self::MAX_LIGHT_LEVEL;
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(4, self::MIN_LIGHT_LEVEL, self::MAX_LIGHT_LEVEL, $this->level);
|
||||
$w->boundedIntAuto(self::MIN_LIGHT_LEVEL, self::MAX_LIGHT_LEVEL, $this->level);
|
||||
}
|
||||
|
||||
public function getLightLevel() : int{ return $this->level; }
|
||||
|
@ -49,7 +49,7 @@ abstract class Liquid extends Transparent{
|
||||
protected bool $still = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, 0, self::MAX_DECAY, $this->decay);
|
||||
$w->boundedIntAuto(0, self::MAX_DECAY, $this->decay);
|
||||
$w->bool($this->falling);
|
||||
$w->bool($this->still);
|
||||
}
|
||||
@ -97,7 +97,7 @@ abstract class Liquid extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
@ -165,23 +165,22 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
$vX = $vY = $vZ = 0;
|
||||
|
||||
$x = $this->position->getFloorX();
|
||||
$y = $this->position->getFloorY();
|
||||
$z = $this->position->getFloorZ();
|
||||
|
||||
$decay = $this->getEffectiveFlowDecay($this);
|
||||
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
foreach(Facing::HORIZONTAL as $j){
|
||||
$x = $this->position->x;
|
||||
$y = $this->position->y;
|
||||
$z = $this->position->z;
|
||||
[$dx, $dy, $dz] = Facing::OFFSET[$j];
|
||||
|
||||
match($j){
|
||||
Facing::WEST => --$x,
|
||||
Facing::EAST => ++$x,
|
||||
Facing::NORTH => --$z,
|
||||
Facing::SOUTH => ++$z
|
||||
};
|
||||
$sideX = $x + $dx;
|
||||
$sideY = $y + $dy;
|
||||
$sideZ = $z + $dz;
|
||||
|
||||
$sideBlock = $world->getBlockAt($x, $y, $z);
|
||||
$sideBlock = $world->getBlockAt($sideX, $sideY, $sideZ);
|
||||
$blockDecay = $this->getEffectiveFlowDecay($sideBlock);
|
||||
|
||||
if($blockDecay < 0){
|
||||
@ -189,21 +188,21 @@ abstract class Liquid extends Transparent{
|
||||
continue;
|
||||
}
|
||||
|
||||
$blockDecay = $this->getEffectiveFlowDecay($world->getBlockAt($x, $y - 1, $z));
|
||||
$blockDecay = $this->getEffectiveFlowDecay($world->getBlockAt($sideX, $sideY - 1, $sideZ));
|
||||
|
||||
if($blockDecay >= 0){
|
||||
$realDecay = $blockDecay - ($decay - 8);
|
||||
$vX += ($x - $this->position->x) * $realDecay;
|
||||
$vY += ($y - $this->position->y) * $realDecay;
|
||||
$vZ += ($z - $this->position->z) * $realDecay;
|
||||
$vX += $dx * $realDecay;
|
||||
$vY += $dy * $realDecay;
|
||||
$vZ += $dz * $realDecay;
|
||||
}
|
||||
|
||||
continue;
|
||||
}else{
|
||||
$realDecay = $blockDecay - $decay;
|
||||
$vX += ($x - $this->position->x) * $realDecay;
|
||||
$vY += ($y - $this->position->y) * $realDecay;
|
||||
$vZ += ($z - $this->position->z) * $realDecay;
|
||||
$vX += $dx * $realDecay;
|
||||
$vY += $dy * $realDecay;
|
||||
$vZ += $dz * $realDecay;
|
||||
}
|
||||
}
|
||||
|
||||
@ -211,10 +210,10 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
if($this->falling){
|
||||
foreach(Facing::HORIZONTAL as $facing){
|
||||
$pos = $this->position->getSide($facing);
|
||||
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
|
||||
if(
|
||||
!$this->canFlowInto($world->getBlockAt($pos->x, $pos->y, $pos->z)) ||
|
||||
!$this->canFlowInto($world->getBlockAt($pos->x, $pos->y + 1, $pos->z))
|
||||
!$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy, $z + $dz)) ||
|
||||
!$this->canFlowInto($world->getBlockAt($x + $dx, $y + $dy + 1, $z + $dz))
|
||||
){
|
||||
$vector = $vector->normalize()->add(0, -6, 0);
|
||||
break;
|
||||
@ -260,13 +259,17 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$x = $this->position->getFloorX();
|
||||
$y = $this->position->getFloorY();
|
||||
$z = $this->position->getFloorZ();
|
||||
|
||||
if(!$this->isSource()){
|
||||
$smallestFlowDecay = -100;
|
||||
$this->adjacentSources = 0;
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x, $this->position->y, $this->position->z - 1), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x, $this->position->y, $this->position->z + 1), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x - 1, $this->position->y, $this->position->z), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($this->position->x + 1, $this->position->y, $this->position->z), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x, $y, $z - 1), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x, $y, $z + 1), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x - 1, $y, $z), $smallestFlowDecay);
|
||||
$smallestFlowDecay = $this->getSmallestFlowDecay($world->getBlockAt($x + 1, $y, $z), $smallestFlowDecay);
|
||||
|
||||
$newDecay = $smallestFlowDecay + $multiplier;
|
||||
$falling = false;
|
||||
@ -275,13 +278,13 @@ abstract class Liquid extends Transparent{
|
||||
$newDecay = -1;
|
||||
}
|
||||
|
||||
if($this->getEffectiveFlowDecay($world->getBlockAt($this->position->x, $this->position->y + 1, $this->position->z)) >= 0){
|
||||
if($this->getEffectiveFlowDecay($world->getBlockAt($x, $y + 1, $z)) >= 0){
|
||||
$falling = true;
|
||||
}
|
||||
|
||||
$minAdjacentSources = $this->getMinAdjacentSourcesToFormSource();
|
||||
if($minAdjacentSources !== null && $this->adjacentSources >= $minAdjacentSources){
|
||||
$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
|
||||
$bottomBlock = $world->getBlockAt($x, $y - 1, $z);
|
||||
if($bottomBlock->isSolid() || ($bottomBlock instanceof Liquid && $bottomBlock->hasSameTypeId($this) && $bottomBlock->isSource())){
|
||||
$newDecay = 0;
|
||||
$falling = false;
|
||||
@ -290,17 +293,17 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
if($falling !== $this->falling || (!$falling && $newDecay !== $this->decay)){
|
||||
if(!$falling && $newDecay < 0){
|
||||
$world->setBlock($this->position, VanillaBlocks::AIR());
|
||||
$world->setBlockAt($x, $y, $z, VanillaBlocks::AIR());
|
||||
return;
|
||||
}
|
||||
|
||||
$this->falling = $falling;
|
||||
$this->decay = $falling ? 0 : $newDecay;
|
||||
$world->setBlock($this->position, $this); //local block update will cause an update to be scheduled
|
||||
$world->setBlockAt($x, $y, $z, $this); //local block update will cause an update to be scheduled
|
||||
}
|
||||
}
|
||||
|
||||
$bottomBlock = $world->getBlockAt($this->position->x, $this->position->y - 1, $this->position->z);
|
||||
$bottomBlock = $world->getBlockAt($x, $y - 1, $z);
|
||||
|
||||
$this->flowIntoBlock($bottomBlock, 0, true);
|
||||
|
||||
@ -313,8 +316,9 @@ abstract class Liquid extends Transparent{
|
||||
|
||||
if($adjacentDecay <= self::MAX_DECAY){
|
||||
$calculator = new MinimumCostFlowCalculator($world, $this->getFlowDecayPerBlock(), $this->canFlowInto(...));
|
||||
foreach($calculator->getOptimalFlowDirections($this->position->getFloorX(), $this->position->getFloorY(), $this->position->getFloorZ()) as $facing){
|
||||
$this->flowIntoBlock($world->getBlock($this->position->getSide($facing)), $adjacentDecay, false);
|
||||
foreach($calculator->getOptimalFlowDirections($x, $y, $z) as $facing){
|
||||
[$dx, $dy, $dz] = Facing::OFFSET[$facing];
|
||||
$this->flowIntoBlock($world->getBlockAt($x + $dx, $y + $dy, $z + $dz), $adjacentDecay, false);
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -39,18 +39,13 @@ class MobHead extends Flowable{
|
||||
public const MIN_ROTATION = 0;
|
||||
public const MAX_ROTATION = 15;
|
||||
|
||||
protected MobHeadType $mobHeadType;
|
||||
protected MobHeadType $mobHeadType = MobHeadType::SKELETON;
|
||||
|
||||
protected int $facing = Facing::NORTH;
|
||||
protected int $rotation = self::MIN_ROTATION; //TODO: split this into floor skull and wall skull handling
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->mobHeadType = MobHeadType::SKELETON(); //TODO: this should be a parameter
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
$w->mobHeadType($this->mobHeadType);
|
||||
$w->enum($this->mobHeadType);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
|
@ -42,6 +42,6 @@ class MonsterSpawner extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -52,7 +52,7 @@ class Mycelium extends Opaque{
|
||||
$z = mt_rand($this->position->z - 1, $this->position->z + 1);
|
||||
$world = $this->position->getWorld();
|
||||
$block = $world->getBlockAt($x, $y, $z);
|
||||
if($block instanceof Dirt && $block->getDirtType()->equals(DirtType::NORMAL())){
|
||||
if($block instanceof Dirt && $block->getDirtType() === DirtType::NORMAL){
|
||||
if($block->getSide(Facing::UP) instanceof Transparent){
|
||||
BlockEventHelper::spread($block, VanillaBlocks::MYCELIUM(), $this);
|
||||
}
|
||||
|
@ -70,7 +70,7 @@ class NetherPortal extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function getDrops(Item $item) : array{
|
||||
|
39
src/block/NetherRoots.php
Normal file
39
src/block/NetherRoots.php
Normal file
@ -0,0 +1,39 @@
|
||||
<?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\utils\StaticSupportTrait;
|
||||
use pocketmine\math\Facing;
|
||||
|
||||
final class NetherRoots extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
//TODO: nylium, moss
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::DIRT) ||
|
||||
$supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||
}
|
||||
}
|
@ -23,9 +23,10 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
@ -41,44 +42,23 @@ use function mt_rand;
|
||||
* This class is used for Weeping & Twisting vines, because they have same behaviour
|
||||
*/
|
||||
class NetherVines extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MAX_AGE = 25;
|
||||
|
||||
/** Direction the vine grows towards. */
|
||||
private int $growthFace;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $growthFace){
|
||||
$this->growthFace = $growthFace;
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(5, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{
|
||||
return $this->age;
|
||||
}
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0-" . self::MAX_AGE);
|
||||
}
|
||||
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function isAffectedBySilkTouch() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function canClimb() : bool{
|
||||
return true;
|
||||
}
|
||||
@ -88,12 +68,6 @@ class NetherVines extends Flowable{
|
||||
return $supportBlock->getSupportType($this->growthFace)->hasCenterSupport() || $supportBlock->hasSameTypeId($this);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the block at the end of the vine structure furthest from the supporting block.
|
||||
*/
|
||||
@ -106,9 +80,6 @@ class NetherVines extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
$this->age = mt_rand(0, self::MAX_AGE - 1);
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
@ -123,8 +94,12 @@ class NetherVines extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return $this->age < self::MAX_AGE;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
if(mt_rand(1, 10) === 1 && $this->age < self::MAX_AGE){
|
||||
if($this->age < self::MAX_AGE && mt_rand(1, 10) === 1){
|
||||
if($this->getSide($this->growthFace)->canBeReplaced()){
|
||||
$this->grow(null);
|
||||
}
|
||||
@ -184,6 +159,6 @@ class NetherVines extends Flowable{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -23,53 +23,26 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\FortuneDropHelper;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
class NetherWartPlant extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MAX_AGE = 3;
|
||||
|
||||
protected int $age = 0;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(2, 0, self::MAX_AGE, $this->age);
|
||||
}
|
||||
|
||||
public function getAge() : int{ return $this->age; }
|
||||
|
||||
/** @return $this */
|
||||
public function setAge(int $age) : self{
|
||||
if($age < 0 || $age > self::MAX_AGE){
|
||||
throw new \InvalidArgumentException("Age must be in range 0 ..." . self::MAX_AGE);
|
||||
}
|
||||
$this->age = $age;
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->getTypeId() === BlockTypeIds::SOUL_SAND){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if($this->getSide(Facing::DOWN)->getTypeId() !== BlockTypeIds::SOUL_SAND){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::SOUL_SAND;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
return $this->age < self::MAX_AGE;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
@ -35,6 +36,9 @@ use pocketmine\world\BlockTransaction;
|
||||
|
||||
class PinkPetals extends Flowable{
|
||||
use HorizontalFacingTrait;
|
||||
use StaticSupportTrait {
|
||||
canBePlacedAt as supportedWhenPlacedAt;
|
||||
}
|
||||
|
||||
public const MIN_COUNT = 1;
|
||||
public const MAX_COUNT = 4;
|
||||
@ -43,7 +47,7 @@ class PinkPetals extends Flowable{
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
}
|
||||
|
||||
public function getCount() : int{
|
||||
@ -65,20 +69,11 @@ class PinkPetals extends Flowable{
|
||||
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || parent::canBePlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
return ($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT) || $this->supportedWhenPlacedAt($blockReplace, $clickVector, $face, $isClickedBlock);
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
return false;
|
||||
}
|
||||
if($blockReplace instanceof PinkPetals && $blockReplace->getCount() < self::MAX_COUNT){
|
||||
$this->count = $blockReplace->getCount() + 1;
|
||||
$this->facing = $blockReplace->getFacing();
|
||||
|
105
src/block/PitcherCrop.php
Normal file
105
src/block/PitcherCrop.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\block;
|
||||
|
||||
use pocketmine\block\utils\AgeableTrait;
|
||||
use pocketmine\block\utils\BlockEventHelper;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use function mt_rand;
|
||||
|
||||
final class PitcherCrop extends Flowable{
|
||||
use AgeableTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MAX_AGE = 2;
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getSide(Facing::DOWN)->getTypeId() === BlockTypeIds::FARMLAND;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
$widthTrim = $this->age === 0 ? 5 : 3;
|
||||
$heightTrim = $this->age === 0 ? 13 : 11;
|
||||
return [
|
||||
AxisAlignedBB::one()
|
||||
->trim(Facing::UP, $heightTrim / 16)
|
||||
->squash(Axis::X, $widthTrim / 16)
|
||||
->squash(Axis::Z, $widthTrim / 16)
|
||||
->extend(Facing::DOWN, 1 / 16) //presumably this is to correct for farmland being 15/16 of a block tall
|
||||
];
|
||||
}
|
||||
|
||||
private function grow(?Player $player) : bool{
|
||||
if($this->age > self::MAX_AGE){
|
||||
return false;
|
||||
}
|
||||
|
||||
if($this->age === self::MAX_AGE){
|
||||
$up = $this->getSide(Facing::UP);
|
||||
if($up->getTypeId() !== BlockTypeIds::AIR){
|
||||
return false;
|
||||
}
|
||||
|
||||
$tx = new BlockTransaction($this->position->getWorld());
|
||||
$tx->addBlock($this->position, VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(false));
|
||||
$tx->addBlock($this->position->up(), VanillaBlocks::DOUBLE_PITCHER_CROP()->setTop(true));
|
||||
|
||||
$ev = new StructureGrowEvent($this, $tx, $player);
|
||||
$ev->call();
|
||||
|
||||
return !$ev->isCancelled() && $tx->apply();
|
||||
}
|
||||
|
||||
return BlockEventHelper::grow($this, (clone $this)->setAge($this->age + 1), $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
if($item instanceof Fertilizer && $this->grow($player)){
|
||||
$item->pop();
|
||||
return true;
|
||||
}
|
||||
|
||||
return false;
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
//TODO: the growth speed is influenced by farmland and nearby crops
|
||||
if(mt_rand(0, 2) === 0){
|
||||
$this->grow(null);
|
||||
}
|
||||
}
|
||||
}
|
@ -23,21 +23,19 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\block\PressurePlateUpdateEvent;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Axis;
|
||||
use pocketmine\math\AxisAlignedBB;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\sound\PressurePlateActivateSound;
|
||||
use pocketmine\world\sound\PressurePlateDeactivateSound;
|
||||
use function count;
|
||||
|
||||
abstract class PressurePlate extends Transparent{
|
||||
use StaticSupportTrait;
|
||||
|
||||
private readonly int $deactivationDelayTicks;
|
||||
|
||||
@ -60,24 +58,11 @@ abstract class PressurePlate extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
|
||||
}
|
||||
|
||||
public function hasEntityCollision() : bool{
|
||||
|
@ -29,17 +29,12 @@ use pocketmine\item\Item;
|
||||
use function mt_rand;
|
||||
|
||||
class RedMushroomBlock extends Opaque{
|
||||
protected MushroomBlockType $mushroomBlockType;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->mushroomBlockType = MushroomBlockType::ALL_CAP();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
protected MushroomBlockType $mushroomBlockType = MushroomBlockType::ALL_CAP;
|
||||
|
||||
public function describeBlockItemState(RuntimeDataDescriber $w) : void{
|
||||
//these blocks always drop as all-cap, but may exist in other forms in the inventory (particularly creative),
|
||||
//so this information needs to be kept in the type info
|
||||
$w->mushroomBlockType($this->mushroomBlockType);
|
||||
$w->enum($this->mushroomBlockType);
|
||||
}
|
||||
|
||||
public function getMushroomBlockType() : MushroomBlockType{ return $this->mushroomBlockType; }
|
||||
@ -61,10 +56,10 @@ class RedMushroomBlock extends Opaque{
|
||||
}
|
||||
|
||||
public function getSilkTouchDrops(Item $item) : array{
|
||||
return [(clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP())->asItem()];
|
||||
return [(clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP)->asItem()];
|
||||
}
|
||||
|
||||
public function getPickedItem(bool $addUserData = false) : Item{
|
||||
return (clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP())->asItem();
|
||||
return (clone $this)->setMushroomBlockType(MushroomBlockType::ALL_CAP)->asItem();
|
||||
}
|
||||
}
|
||||
|
@ -27,6 +27,7 @@ use pocketmine\block\tile\Comparator;
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -41,6 +42,7 @@ class RedstoneComparator extends Flowable{
|
||||
use HorizontalFacingTrait;
|
||||
use AnalogRedstoneSignalEmitterTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
protected bool $isSubtractMode = false;
|
||||
|
||||
@ -85,14 +87,10 @@ class RedstoneComparator extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
@ -101,14 +99,8 @@ class RedstoneComparator extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
|
||||
}
|
||||
|
||||
//TODO: redstone functionality
|
||||
|
@ -70,7 +70,7 @@ class RedstoneOre extends Opaque{
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
return $this->lit;
|
||||
}
|
||||
|
||||
public function onRandomTick() : void{
|
||||
|
@ -25,6 +25,7 @@ namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\HorizontalFacingTrait;
|
||||
use pocketmine\block\utils\PoweredByRedstoneTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\item\Item;
|
||||
@ -37,6 +38,7 @@ use pocketmine\world\BlockTransaction;
|
||||
class RedstoneRepeater extends Flowable{
|
||||
use HorizontalFacingTrait;
|
||||
use PoweredByRedstoneTrait;
|
||||
use StaticSupportTrait;
|
||||
|
||||
public const MIN_DELAY = 1;
|
||||
public const MAX_DELAY = 4;
|
||||
@ -45,7 +47,7 @@ class RedstoneRepeater extends Flowable{
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->horizontalFacing($this->facing);
|
||||
$w->boundedInt(2, self::MIN_DELAY, self::MAX_DELAY, $this->delay);
|
||||
$w->boundedIntAuto(self::MIN_DELAY, self::MAX_DELAY, $this->delay);
|
||||
$w->bool($this->powered);
|
||||
}
|
||||
|
||||
@ -68,15 +70,11 @@ class RedstoneRepeater extends Flowable{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
if($player !== null){
|
||||
$this->facing = Facing::opposite($player->getHorizontalFacing());
|
||||
}
|
||||
|
||||
return false;
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
@ -87,14 +85,8 @@ class RedstoneRepeater extends Flowable{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return !$block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::NONE());
|
||||
return $block->getAdjacentSupportType(Facing::DOWN) !== SupportType::NONE;
|
||||
}
|
||||
|
||||
//TODO: redstone functionality
|
||||
|
@ -24,22 +24,14 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\item\VanillaItems;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class RedstoneWire extends Flowable{
|
||||
use AnalogRedstoneSignalEmitterTrait;
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($this->canBeSupportedAt($blockReplace)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
return false;
|
||||
}
|
||||
use StaticSupportTrait;
|
||||
|
||||
public function readStateFromWorld() : Block{
|
||||
parent::readStateFromWorld();
|
||||
@ -48,12 +40,6 @@ class RedstoneWire extends Flowable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->hasCenterSupport();
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\SaplingType;
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use pocketmine\event\block\StructureGrowEvent;
|
||||
use pocketmine\item\Fertilizer;
|
||||
@ -32,11 +33,12 @@ use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\utils\Random;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
use pocketmine\world\generator\object\TreeFactory;
|
||||
use function mt_rand;
|
||||
|
||||
class Sapling extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
protected bool $ready = false;
|
||||
|
||||
private SaplingType $saplingType;
|
||||
@ -58,13 +60,9 @@ class Sapling extends Flowable{
|
||||
return $this;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if($down->hasTypeTag(BlockTypeTags::DIRT) || $down->hasTypeTag(BlockTypeTags::MUD)){
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
return false;
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
$supportBlock = $block->getSide(Facing::DOWN);
|
||||
return $supportBlock->hasTypeTag(BlockTypeTags::DIRT) || $supportBlock->hasTypeTag(BlockTypeTags::MUD);
|
||||
}
|
||||
|
||||
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
|
||||
@ -77,13 +75,6 @@ class Sapling extends Flowable{
|
||||
return false;
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
$down = $this->getSide(Facing::DOWN);
|
||||
if(!$down->hasTypeTag(BlockTypeTags::DIRT) && !$down->hasTypeTag(BlockTypeTags::MUD)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
}
|
||||
|
||||
public function ticksRandomly() : bool{
|
||||
return true;
|
||||
}
|
||||
|
@ -39,7 +39,7 @@ class SeaPickle extends Transparent{
|
||||
protected bool $underwater = false;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(2, self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
$w->boundedIntAuto(self::MIN_COUNT, self::MAX_COUNT, $this->count);
|
||||
$w->bool($this->underwater);
|
||||
}
|
||||
|
||||
@ -78,7 +78,7 @@ class SeaPickle extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function canBePlacedAt(Block $blockReplace, Vector3 $clickVector, int $face, bool $isClickedBlock) : bool{
|
||||
|
@ -113,6 +113,6 @@ class ShulkerBox extends Opaque{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
}
|
||||
|
@ -34,19 +34,18 @@ use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
class Slab extends Transparent{
|
||||
protected SlabType $slabType;
|
||||
protected SlabType $slabType = SlabType::BOTTOM;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->slabType = SlabType::BOTTOM();
|
||||
parent::__construct($idInfo, $name . " Slab", $typeInfo);
|
||||
}
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->slabType($this->slabType);
|
||||
$w->enum($this->slabType);
|
||||
}
|
||||
|
||||
public function isTransparent() : bool{
|
||||
return !$this->slabType->equals(SlabType::DOUBLE());
|
||||
return $this->slabType !== SlabType::DOUBLE;
|
||||
}
|
||||
|
||||
/**
|
||||
@ -69,8 +68,8 @@ class Slab extends Transparent{
|
||||
return true;
|
||||
}
|
||||
|
||||
if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->hasSameTypeId($this)){
|
||||
if($blockReplace->slabType->equals(SlabType::TOP())){ //Trying to combine with top slab
|
||||
if($blockReplace instanceof Slab && $blockReplace->slabType !== SlabType::DOUBLE && $blockReplace->hasSameTypeId($this)){
|
||||
if($blockReplace->slabType === SlabType::TOP){ //Trying to combine with top slab
|
||||
return $clickVector->y <= 0.5 || (!$isClickedBlock && $face === Facing::UP);
|
||||
}else{
|
||||
return $clickVector->y >= 0.5 || (!$isClickedBlock && $face === Facing::DOWN);
|
||||
@ -81,14 +80,14 @@ class Slab extends Transparent{
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if($blockReplace instanceof Slab && !$blockReplace->slabType->equals(SlabType::DOUBLE()) && $blockReplace->hasSameTypeId($this) && (
|
||||
($blockReplace->slabType->equals(SlabType::TOP()) && ($clickVector->y <= 0.5 || $face === Facing::UP)) ||
|
||||
($blockReplace->slabType->equals(SlabType::BOTTOM()) && ($clickVector->y >= 0.5 || $face === Facing::DOWN))
|
||||
if($blockReplace instanceof Slab && $blockReplace->slabType !== SlabType::DOUBLE && $blockReplace->hasSameTypeId($this) && (
|
||||
($blockReplace->slabType === SlabType::TOP && ($clickVector->y <= 0.5 || $face === Facing::UP)) ||
|
||||
($blockReplace->slabType === SlabType::BOTTOM && ($clickVector->y >= 0.5 || $face === Facing::DOWN))
|
||||
)){
|
||||
//Clicked in empty half of existing slab
|
||||
$this->slabType = SlabType::DOUBLE();
|
||||
$this->slabType = SlabType::DOUBLE;
|
||||
}else{
|
||||
$this->slabType = (($face !== Facing::UP && $clickVector->y > 0.5) || $face === Facing::DOWN) ? SlabType::TOP() : SlabType::BOTTOM();
|
||||
$this->slabType = (($face !== Facing::UP && $clickVector->y > 0.5) || $face === Facing::DOWN) ? SlabType::TOP : SlabType::BOTTOM;
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
@ -98,22 +97,22 @@ class Slab extends Transparent{
|
||||
* @return AxisAlignedBB[]
|
||||
*/
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
if($this->slabType->equals(SlabType::DOUBLE())){
|
||||
if($this->slabType === SlabType::DOUBLE){
|
||||
return [AxisAlignedBB::one()];
|
||||
}
|
||||
return [AxisAlignedBB::one()->trim($this->slabType->equals(SlabType::TOP()) ? Facing::DOWN : Facing::UP, 0.5)];
|
||||
return [AxisAlignedBB::one()->trim($this->slabType === SlabType::TOP ? Facing::DOWN : Facing::UP, 0.5)];
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
if($this->slabType->equals(SlabType::DOUBLE())){
|
||||
return SupportType::FULL();
|
||||
}elseif(($facing === Facing::UP && $this->slabType->equals(SlabType::TOP())) || ($facing === Facing::DOWN && $this->slabType->equals(SlabType::BOTTOM()))){
|
||||
return SupportType::FULL();
|
||||
if($this->slabType === SlabType::DOUBLE){
|
||||
return SupportType::FULL;
|
||||
}elseif(($facing === Facing::UP && $this->slabType === SlabType::TOP) || ($facing === Facing::DOWN && $this->slabType === SlabType::BOTTOM)){
|
||||
return SupportType::FULL;
|
||||
}
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
public function getDropsForCompatibleTool(Item $item) : array{
|
||||
return [$this->asItem()->setCount($this->slabType->equals(SlabType::DOUBLE()) ? 2 : 1)];
|
||||
return [$this->asItem()->setCount($this->slabType === SlabType::DOUBLE ? 2 : 1)];
|
||||
}
|
||||
}
|
||||
|
@ -161,7 +161,7 @@ class SmallDripleaf extends Transparent{
|
||||
}
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
protected function recalculateCollisionBoxes() : array{
|
||||
|
@ -47,7 +47,7 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
protected int $layers = self::MIN_LAYERS;
|
||||
|
||||
protected function describeBlockOnlyState(RuntimeDataDescriber $w) : void{
|
||||
$w->boundedInt(3, self::MIN_LAYERS, self::MAX_LAYERS, $this->layers);
|
||||
$w->boundedIntAuto(self::MIN_LAYERS, self::MAX_LAYERS, $this->layers);
|
||||
}
|
||||
|
||||
public function getLayers() : int{ return $this->layers; }
|
||||
@ -75,13 +75,13 @@ class SnowLayer extends Flowable implements Fallable{
|
||||
|
||||
public function getSupportType(int $facing) : SupportType{
|
||||
if(!$this->canBeReplaced()){
|
||||
return SupportType::FULL();
|
||||
return SupportType::FULL;
|
||||
}
|
||||
return SupportType::NONE();
|
||||
return SupportType::NONE;
|
||||
}
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::DOWN)->equals(SupportType::FULL());
|
||||
return $block->getAdjacentSupportType(Facing::DOWN) === SupportType::FULL;
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
|
@ -23,30 +23,14 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\StaticSupportTrait;
|
||||
use pocketmine\block\utils\SupportType;
|
||||
use pocketmine\item\Item;
|
||||
use pocketmine\math\Facing;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\player\Player;
|
||||
use pocketmine\world\BlockTransaction;
|
||||
|
||||
final class SporeBlossom extends Flowable{
|
||||
use StaticSupportTrait;
|
||||
|
||||
private function canBeSupportedAt(Block $block) : bool{
|
||||
return $block->getAdjacentSupportType(Facing::UP)->equals(SupportType::FULL());
|
||||
}
|
||||
|
||||
public function place(BlockTransaction $tx, Item $item, Block $blockReplace, Block $blockClicked, int $face, Vector3 $clickVector, ?Player $player = null) : bool{
|
||||
if(!$this->canBeSupportedAt($blockReplace)){
|
||||
return false;
|
||||
}
|
||||
|
||||
return parent::place($tx, $item, $blockReplace, $blockClicked, $face, $clickVector, $player);
|
||||
}
|
||||
|
||||
public function onNearbyBlockChange() : void{
|
||||
if(!$this->canBeSupportedAt($this)){
|
||||
$this->position->getWorld()->useBreakOn($this->position);
|
||||
}
|
||||
return $block->getAdjacentSupportType(Facing::UP) === SupportType::FULL;
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedGlass extends Glass{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedGlassPane extends GlassPane{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedHardenedClay extends HardenedClay{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedHardenedGlass extends HardenedGlass{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
@ -24,13 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\ColoredTrait;
|
||||
use pocketmine\block\utils\DyeColor;
|
||||
|
||||
final class StainedHardenedGlassPane extends HardenedGlassPane{
|
||||
use ColoredTrait;
|
||||
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo){
|
||||
$this->color = DyeColor::WHITE();
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
}
|
||||
}
|
||||
|
Some files were not shown because too many files have changed in this diff Show More
Loading…
x
Reference in New Issue
Block a user