mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-04-21 16:24:05 +00:00
Implement pressure plate activation logic and events (#5991)
closes #5936 This implements all of the basic activation logic for pressure plates. It also introduces a PressurePlateUpdateEvent, which is called in pulses when entities are standing on top of the plate and when it deactivates. Deactivation can be detected by checking if the list of activating entities is empty. --------- Co-authored-by: Javier León <58715544+JavierLeon9966@users.noreply.github.com>
This commit is contained in:
parent
f516c3c502
commit
e323c5dd76
@ -24,14 +24,33 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
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{
|
||||
|
||||
private readonly int $deactivationDelayTicks;
|
||||
|
||||
public function __construct(
|
||||
BlockIdentifier $idInfo,
|
||||
string $name,
|
||||
BlockTypeInfo $typeInfo,
|
||||
int $deactivationDelayTicks = 20 //TODO: make this mandatory in PM6
|
||||
){
|
||||
parent::__construct($idInfo, $name, $typeInfo);
|
||||
$this->deactivationDelayTicks = $deactivationDelayTicks;
|
||||
}
|
||||
|
||||
public function isSolid() : bool{
|
||||
return false;
|
||||
}
|
||||
@ -61,5 +80,89 @@ abstract class PressurePlate extends Transparent{
|
||||
}
|
||||
}
|
||||
|
||||
//TODO
|
||||
public function hasEntityCollision() : bool{
|
||||
return true;
|
||||
}
|
||||
|
||||
public function onEntityInside(Entity $entity) : bool{
|
||||
if(!$this->hasOutputSignal()){
|
||||
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 0);
|
||||
}
|
||||
return true;
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns the AABB that entities must intersect to activate the pressure plate.
|
||||
* Note that this is not the same as the collision box (pressure plate doesn't have one), nor the visual bounding
|
||||
* box. The activation area has a height of 0.25 blocks.
|
||||
*/
|
||||
protected function getActivationBox() : AxisAlignedBB{
|
||||
return AxisAlignedBB::one()
|
||||
->squash(Axis::X, 1 / 8)
|
||||
->squash(Axis::Z, 1 / 8)
|
||||
->trim(Facing::UP, 3 / 4)
|
||||
->offset($this->position->x, $this->position->y, $this->position->z);
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: make this abstract in PM6
|
||||
*/
|
||||
protected function hasOutputSignal() : bool{
|
||||
return false;
|
||||
}
|
||||
|
||||
/**
|
||||
* TODO: make this abstract in PM6
|
||||
*
|
||||
* @param Entity[] $entities
|
||||
*
|
||||
* @return mixed[]
|
||||
* @phpstan-return array{Block, ?bool}
|
||||
*/
|
||||
protected function calculatePlateState(array $entities) : array{
|
||||
return [$this, null];
|
||||
}
|
||||
|
||||
/**
|
||||
* Filters entities which don't affect the pressure plate state from the given list.
|
||||
*
|
||||
* @param Entity[] $entities
|
||||
* @return Entity[]
|
||||
*/
|
||||
protected function filterIrrelevantEntities(array $entities) : array{
|
||||
return $entities;
|
||||
}
|
||||
|
||||
public function onScheduledUpdate() : void{
|
||||
$world = $this->position->getWorld();
|
||||
|
||||
$intersectionAABB = $this->getActivationBox();
|
||||
$activatingEntities = $this->filterIrrelevantEntities($world->getNearbyEntities($intersectionAABB));
|
||||
|
||||
//if an irrelevant entity is inside the full cube space of the pressure plate but not activating the plate,
|
||||
//it will cause scheduled updates on the plate every tick. We don't want to fire events in this case if the
|
||||
//plate is already deactivated.
|
||||
if(count($activatingEntities) > 0 || $this->hasOutputSignal()){
|
||||
[$newState, $pressedChange] = $this->calculatePlateState($activatingEntities);
|
||||
|
||||
//always call this, in case there are new entities on the plate
|
||||
if(PressurePlateUpdateEvent::hasHandlers()){
|
||||
$ev = new PressurePlateUpdateEvent($this, $newState, $activatingEntities);
|
||||
$ev->call();
|
||||
$newState = $ev->isCancelled() ? null : $ev->getNewState();
|
||||
}
|
||||
if($newState !== null){
|
||||
$world->setBlock($this->position, $newState);
|
||||
if($pressedChange !== null){
|
||||
$world->addSound($this->position, $pressedChange ?
|
||||
new PressurePlateActivateSound($this) :
|
||||
new PressurePlateDeactivateSound($this)
|
||||
);
|
||||
}
|
||||
}
|
||||
if($pressedChange ?? $this->hasOutputSignal()){
|
||||
$world->scheduleDelayedBlockUpdate($this->position, $this->deactivationDelayTicks);
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -24,6 +24,7 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\data\runtime\RuntimeDataDescriber;
|
||||
use function count;
|
||||
|
||||
abstract class SimplePressurePlate extends PressurePlate{
|
||||
protected bool $pressed = false;
|
||||
@ -39,4 +40,19 @@ abstract class SimplePressurePlate extends PressurePlate{
|
||||
$this->pressed = $pressed;
|
||||
return $this;
|
||||
}
|
||||
|
||||
protected function hasOutputSignal() : bool{
|
||||
return $this->pressed;
|
||||
}
|
||||
|
||||
protected function calculatePlateState(array $entities) : array{
|
||||
$newPressed = count($entities) > 0;
|
||||
if($newPressed === $this->pressed){
|
||||
return [$this, null];
|
||||
}
|
||||
return [
|
||||
(clone $this)->setPressed($newPressed),
|
||||
$newPressed
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,13 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\entity\Living;
|
||||
use function array_filter;
|
||||
|
||||
class StonePressurePlate extends SimplePressurePlate{
|
||||
|
||||
protected function filterIrrelevantEntities(array $entities) : array{
|
||||
return array_filter($entities, fn(Entity $e) => $e instanceof Living); //TODO: armor stands should activate stone plates too
|
||||
}
|
||||
}
|
||||
|
@ -1114,8 +1114,20 @@ final class VanillaBlocks{
|
||||
self::register("lily_pad", new WaterLily(new BID(Ids::LILY_PAD), "Lily Pad", new Info(BreakInfo::instant())));
|
||||
|
||||
$weightedPressurePlateBreakInfo = new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()));
|
||||
self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy(new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY), "Weighted Pressure Plate Heavy", $weightedPressurePlateBreakInfo));
|
||||
self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight(new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT), "Weighted Pressure Plate Light", $weightedPressurePlateBreakInfo));
|
||||
self::register("weighted_pressure_plate_heavy", new WeightedPressurePlateHeavy(
|
||||
new BID(Ids::WEIGHTED_PRESSURE_PLATE_HEAVY),
|
||||
"Weighted Pressure Plate Heavy",
|
||||
$weightedPressurePlateBreakInfo,
|
||||
deactivationDelayTicks: 10,
|
||||
signalStrengthFactor: 0.1
|
||||
));
|
||||
self::register("weighted_pressure_plate_light", new WeightedPressurePlateLight(
|
||||
new BID(Ids::WEIGHTED_PRESSURE_PLATE_LIGHT),
|
||||
"Weighted Pressure Plate Light",
|
||||
$weightedPressurePlateBreakInfo,
|
||||
deactivationDelayTicks: 10,
|
||||
signalStrengthFactor: 1.0
|
||||
));
|
||||
self::register("wheat", new Wheat(new BID(Ids::WHEAT), "Wheat Block", new Info(BreakInfo::instant())));
|
||||
|
||||
$leavesBreakInfo = new Info(new class(0.2, ToolType::HOE) extends BreakInfo{
|
||||
@ -1266,7 +1278,7 @@ final class VanillaBlocks{
|
||||
self::register($idName("door"), new WoodenDoor(WoodLikeBlockIdHelper::getDoorIdentifier($woodType), $name . " Door", $woodenDoorBreakInfo, $woodType));
|
||||
|
||||
self::register($idName("button"), new WoodenButton(WoodLikeBlockIdHelper::getButtonIdentifier($woodType), $name . " Button", $woodenButtonBreakInfo, $woodType));
|
||||
self::register($idName("pressure_plate"), new WoodenPressurePlate(WoodLikeBlockIdHelper::getPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType));
|
||||
self::register($idName("pressure_plate"), new WoodenPressurePlate(WoodLikeBlockIdHelper::getPressurePlateIdentifier($woodType), $name . " Pressure Plate", $woodenPressurePlateBreakInfo, $woodType, 20));
|
||||
self::register($idName("trapdoor"), new WoodenTrapdoor(WoodLikeBlockIdHelper::getTrapdoorIdentifier($woodType), $name . " Trapdoor", $woodenDoorBreakInfo, $woodType));
|
||||
|
||||
[$floorSignId, $wallSignId, $signAsItem] = WoodLikeBlockIdHelper::getSignInfo($woodType);
|
||||
@ -1491,7 +1503,7 @@ final class VanillaBlocks{
|
||||
$prefix = fn(string $thing) => "Polished Blackstone" . ($thing !== "" ? " $thing" : "");
|
||||
self::register("polished_blackstone", new Opaque(new BID(Ids::POLISHED_BLACKSTONE), $prefix(""), $blackstoneBreakInfo));
|
||||
self::register("polished_blackstone_button", new StoneButton(new BID(Ids::POLISHED_BLACKSTONE_BUTTON), $prefix("Button"), new Info(BreakInfo::pickaxe(0.5))));
|
||||
self::register("polished_blackstone_pressure_plate", new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD()))));
|
||||
self::register("polished_blackstone_pressure_plate", new StonePressurePlate(new BID(Ids::POLISHED_BLACKSTONE_PRESSURE_PLATE), $prefix("Pressure Plate"), new Info(BreakInfo::pickaxe(0.5, ToolTier::WOOD())), 20));
|
||||
self::register("polished_blackstone_slab", new Slab(new BID(Ids::POLISHED_BLACKSTONE_SLAB), $prefix(""), $slabBreakInfo));
|
||||
self::register("polished_blackstone_stairs", new Stair(new BID(Ids::POLISHED_BLACKSTONE_STAIRS), $prefix("Stairs"), $blackstoneBreakInfo));
|
||||
self::register("polished_blackstone_wall", new Wall(new BID(Ids::POLISHED_BLACKSTONE_WALL), $prefix("Wall"), $blackstoneBreakInfo));
|
||||
|
@ -24,7 +24,40 @@ declare(strict_types=1);
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\AnalogRedstoneSignalEmitterTrait;
|
||||
use function ceil;
|
||||
use function count;
|
||||
use function max;
|
||||
use function min;
|
||||
|
||||
abstract class WeightedPressurePlate extends PressurePlate{
|
||||
class WeightedPressurePlate extends PressurePlate{
|
||||
use AnalogRedstoneSignalEmitterTrait;
|
||||
|
||||
private readonly float $signalStrengthFactor;
|
||||
|
||||
/**
|
||||
* @param float $signalStrengthFactor Number of entities on the plate is divided by this value to get signal strength
|
||||
*/
|
||||
public function __construct(BlockIdentifier $idInfo, string $name, BlockTypeInfo $typeInfo, int $deactivationDelayTicks, float $signalStrengthFactor = 1.0){
|
||||
parent::__construct($idInfo, $name, $typeInfo, $deactivationDelayTicks);
|
||||
$this->signalStrengthFactor = $signalStrengthFactor;
|
||||
}
|
||||
|
||||
protected function hasOutputSignal() : bool{
|
||||
return $this->signalStrength > 0;
|
||||
}
|
||||
|
||||
protected function calculatePlateState(array $entities) : array{
|
||||
$newSignalStrength = min(15, max(0,
|
||||
(int) ceil(count($entities) * $this->signalStrengthFactor)
|
||||
));
|
||||
if($newSignalStrength === $this->signalStrength){
|
||||
return [$this, null];
|
||||
}
|
||||
$wasActive = $this->signalStrength !== 0;
|
||||
$isActive = $newSignalStrength !== 0;
|
||||
return [
|
||||
(clone $this)->setOutputSignalStrength($newSignalStrength),
|
||||
$wasActive !== $isActive ? $isActive : null
|
||||
];
|
||||
}
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class WeightedPressurePlateHeavy extends WeightedPressurePlate{
|
||||
|
||||
}
|
||||
|
@ -23,6 +23,9 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
/**
|
||||
* @deprecated
|
||||
*/
|
||||
class WeightedPressurePlateLight extends WeightedPressurePlate{
|
||||
|
||||
}
|
||||
|
@ -23,11 +23,23 @@ declare(strict_types=1);
|
||||
|
||||
namespace pocketmine\block;
|
||||
|
||||
use pocketmine\block\utils\WoodType;
|
||||
use pocketmine\block\utils\WoodTypeTrait;
|
||||
|
||||
class WoodenPressurePlate extends SimplePressurePlate{
|
||||
use WoodTypeTrait;
|
||||
|
||||
public function __construct(
|
||||
BlockIdentifier $idInfo,
|
||||
string $name,
|
||||
BlockTypeInfo $typeInfo,
|
||||
WoodType $woodType,
|
||||
int $deactivationDelayTicks = 20 //TODO: make this mandatory in PM6
|
||||
){
|
||||
$this->woodType = $woodType;
|
||||
parent::__construct($idInfo, $name, $typeInfo, $deactivationDelayTicks);
|
||||
}
|
||||
|
||||
public function getFuelTime() : int{
|
||||
return 300;
|
||||
}
|
||||
|
53
src/event/block/PressurePlateUpdateEvent.php
Normal file
53
src/event/block/PressurePlateUpdateEvent.php
Normal file
@ -0,0 +1,53 @@
|
||||
<?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\event\block;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\entity\Entity;
|
||||
use pocketmine\event\Cancellable;
|
||||
|
||||
/**
|
||||
* Called whenever the list of entities on a pressure plate changes.
|
||||
* Depending on the type of pressure plate, this might turn on/off its signal, or change the signal strength.
|
||||
*/
|
||||
final class PressurePlateUpdateEvent extends BaseBlockChangeEvent implements Cancellable{
|
||||
/**
|
||||
* @param Entity[] $activatingEntities
|
||||
*/
|
||||
public function __construct(
|
||||
Block $block,
|
||||
Block $newState,
|
||||
private array $activatingEntities
|
||||
){
|
||||
parent::__construct($block, $newState);
|
||||
}
|
||||
|
||||
/**
|
||||
* Returns a list of entities intersecting the pressure plate's activation box.
|
||||
* If the pressure plate is about to deactivate, this list will be empty.
|
||||
*
|
||||
* @return Entity[]
|
||||
*/
|
||||
public function getActivatingEntities() : array{ return $this->activatingEntities; }
|
||||
}
|
46
src/world/sound/PressurePlateActivateSound.php
Normal file
46
src/world/sound/PressurePlateActivateSound.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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\world\sound;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
final class PressurePlateActivateSound implements Sound{
|
||||
|
||||
public function __construct(
|
||||
private readonly Block $block
|
||||
){}
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(
|
||||
LevelSoundEvent::PRESSURE_PLATE_CLICK_ON,
|
||||
$pos,
|
||||
false,
|
||||
TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId())
|
||||
)];
|
||||
}
|
||||
}
|
46
src/world/sound/PressurePlateDeactivateSound.php
Normal file
46
src/world/sound/PressurePlateDeactivateSound.php
Normal file
@ -0,0 +1,46 @@
|
||||
<?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\world\sound;
|
||||
|
||||
use pocketmine\block\Block;
|
||||
use pocketmine\math\Vector3;
|
||||
use pocketmine\network\mcpe\convert\TypeConverter;
|
||||
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
|
||||
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;
|
||||
|
||||
final class PressurePlateDeactivateSound implements Sound{
|
||||
|
||||
public function __construct(
|
||||
private readonly Block $block
|
||||
){}
|
||||
|
||||
public function encode(Vector3 $pos) : array{
|
||||
return [LevelSoundEventPacket::nonActorSound(
|
||||
LevelSoundEvent::PRESSURE_PLATE_CLICK_OFF,
|
||||
$pos,
|
||||
false,
|
||||
TypeConverter::getInstance()->getBlockTranslator()->internalIdToNetworkId($this->block->getStateId())
|
||||
)];
|
||||
}
|
||||
}
|
Loading…
x
Reference in New Issue
Block a user