Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Implement beacon #4687

Open
wants to merge 42 commits into
base: minor-next
Choose a base branch
from
Open
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
42 commits
Select commit Hold shift + click to select a range
709cbd5
implement beacon
ShockedPlot7560 Jan 1, 2022
bcae409
fix styling
ShockedPlot7560 Jan 1, 2022
27979c1
prevent beacon desactivation
ShockedPlot7560 Jan 1, 2022
991334e
fix: enchantment level 3
ShockedPlot7560 Jan 1, 2022
cb33b5d
fix parameter lint
ShockedPlot7560 Jan 1, 2022
4a3c7cf
fix style
ShockedPlot7560 Jan 1, 2022
2ea22b4
Made improvements to the beacon PR (#1)
JavierLeon9966 Jan 17, 2022
a61aace
implement beacon
ShockedPlot7560 Jan 1, 2022
5bc5d63
fix styling
ShockedPlot7560 Jan 1, 2022
4d71b9e
prevent beacon desactivation
ShockedPlot7560 Jan 1, 2022
c487433
fix: enchantment level 3
ShockedPlot7560 Jan 1, 2022
b4ab0c4
fix parameter lint
ShockedPlot7560 Jan 1, 2022
f918f18
fix style
ShockedPlot7560 Jan 1, 2022
34b3f27
Made improvements to the beacon PR (#1)
JavierLeon9966 Jan 17, 2022
357afcc
remove useless phpDoc
ShockedPlot7560 Jan 17, 2022
e628148
remove useless phpDoc
ShockedPlot7560 Jan 17, 2022
ee06f86
delete Beaconinventory
ShockedPlot7560 Jan 17, 2022
2b348e5
rename Beaconinventory to BeaconInventory
ShockedPlot7560 Jan 17, 2022
30e3252
Moove allowed effect Ids to dedicated function
ShockedPlot7560 Jan 17, 2022
0752052
implement correctly beacon inventory to TypeConverter
ShockedPlot7560 Jan 17, 2022
38aab84
Applied almost all suggestions
JavierLeon9966 Jan 18, 2022
25c2b5c
merge beacon branch
ShockedPlot7560 Jan 18, 2022
5288af0
fix: TypeConverter
ShockedPlot7560 Jan 18, 2022
0c6a873
allow primaryEffect to be null
ShockedPlot7560 Jan 18, 2022
2656c6e
remove EffectIdMap from variable
ShockedPlot7560 Jan 18, 2022
816ae70
add beacon level verification before activation
ShockedPlot7560 Jan 18, 2022
3226ef4
add BeaconActivateEvent
ShockedPlot7560 Jan 18, 2022
4f797ca
fix PHPstan lint
ShockedPlot7560 Jan 18, 2022
a54b452
add view to sky checking before activation
ShockedPlot7560 Jan 18, 2022
6078894
check primaryEffect before iterating
ShockedPlot7560 Jan 18, 2022
7167539
remove item from inventory
ShockedPlot7560 Jan 18, 2022
f6b63e0
return statement before iterating effects
ShockedPlot7560 Jan 18, 2022
75064a4
add item remove from inventory, fix mistake with effect management
ShockedPlot7560 Jan 18, 2022
9ac6ec8
moove sky checking after beacon level validation
ShockedPlot7560 Jan 18, 2022
f677f82
add verification and constant
ShockedPlot7560 Jan 18, 2022
5e72e0f
add beacon level verification for secondary effect
ShockedPlot7560 Jan 18, 2022
1b3e223
remove mistake
ShockedPlot7560 Jan 18, 2022
7624934
Remove dead code
dktapps Jan 19, 2022
c3bfe3c
prefer typed variable has PHPdoc
ShockedPlot7560 Mar 13, 2022
c96768d
Merge remote-tracking branch 'upstream/minor-next' into beacon
IvanCraft623 Jun 19, 2024
df7280c
Update to latest PM changes
IvanCraft623 Jun 19, 2024
6e37cd8
Merge remote-tracking branch 'origin/minor-next' into beacon
ShockedPlot7560 Nov 20, 2024
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
200 changes: 199 additions & 1 deletion src/block/Beacon.php
Original file line number Diff line number Diff line change
Expand Up @@ -23,11 +23,209 @@

namespace pocketmine\block;

use InvalidArgumentException;
use pocketmine\block\inventory\BeaconInventory;
use pocketmine\block\tile\Beacon as TileBeacon;
use pocketmine\data\bedrock\EffectIdMap;
use pocketmine\entity\effect\Effect;
use pocketmine\entity\effect\EffectInstance;
use pocketmine\entity\effect\VanillaEffects;
use pocketmine\item\Item;
use pocketmine\item\ItemTypeIds;
use pocketmine\math\Vector3;
use pocketmine\player\Player;
use function array_merge;

final class Beacon extends Transparent{

public const MIN_LEVEL_BEACON = 1;
public const MAX_LEVEL_BEACON = 4;
Comment on lines +41 to +42
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Inclunding BEACON in the constant name seems redundant since it is on the Beacon class


public const ALLOWED_ITEM_IDS = [
ItemTypeIds::IRON_INGOT => true,
ItemTypeIds::GOLD_INGOT => true,
ItemTypeIds::DIAMOND => true,
ItemTypeIds::EMERALD => true,
ItemTypeIds::NETHERITE_INGOT => true
];

private const ALLOWED_BLOCK_IDS = [
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

It would be nice to allow plugins to be able to modify this

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Type tags would be the best thing

BlockTypeIds::IRON => true,
BlockTypeIds::GOLD => true,
BlockTypeIds::DIAMOND => true,
BlockTypeIds::EMERALD => true,
BlockTypeIds::NETHERITE => true
];

private ?Effect $primaryEffect = null;
private ?Effect $secondaryEffect = null;

public function readStateFromWorld() : Block{
parent::readStateFromWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBeacon){
$this->primaryEffect = EffectIdMap::getInstance()->fromId($tile->getPrimaryEffect());
$this->secondaryEffect = EffectIdMap::getInstance()->fromId($tile->getSecondaryEffect());
}

return $this;
}

public function writeStateToWorld() : void{
parent::writeStateToWorld();
$tile = $this->position->getWorld()->getTile($this->position);
if($tile instanceof TileBeacon){
if($this->primaryEffect instanceof Effect){
$tile->setPrimaryEffect(EffectIdMap::getInstance()->toId($this->primaryEffect));
}
if($this->secondaryEffect instanceof Effect){
$tile->setSecondaryEffect(EffectIdMap::getInstance()->toId($this->secondaryEffect));
}
}
}

public function getBeaconLevel() : int {
$beaconLevel = 0;
for($i = self::MIN_LEVEL_BEACON; $i <= self::MAX_LEVEL_BEACON; $i++){
if(!$this->isBeaconLevelValid($i)){
break;
}
$beaconLevel++;
}
return $beaconLevel;
}

public function getPrimaryEffect() : ?Effect{
return $this->primaryEffect;
}

/** @return $this */
public function setPrimaryEffect(?Effect $primaryEffect) : self{
$this->primaryEffect = $primaryEffect;
return $this;
}

public function getSecondaryEffect() : ?Effect{
return $this->secondaryEffect;
}

/** @return $this */
public function setSecondaryEffect(?Effect $secondaryEffect) : self{
$this->secondaryEffect = $secondaryEffect;
return $this;
}

public function getLightLevel() : int{
return 15;
}

//TODO
public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $player = null, array &$returnedItems = []) : bool{
if($player instanceof Player){
$player->setCurrentWindow(new BeaconInventory($this->position));
}

return true;
}

public function onScheduledUpdate() : void{
$this->position->getWorld()->scheduleDelayedBlockUpdate($this->position, 20 * 3);
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

According to the wiki, updates are every 4 seconds


if($this->primaryEffect === null){
return;
}
if(($beaconLevel = $this->getBeaconLevel()) >= self::MIN_LEVEL_BEACON){
if(!$this->viewSky()){
return;
}

Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

We need some validation here, it is possible that players might destroy blocks on any level and some effects will be no longer avaible

$radius = (10 * $beaconLevel) + 10;
ShockedPlot7560 marked this conversation as resolved.
Show resolved Hide resolved
$effectDuration = 9 + (2 * $beaconLevel);

$world = $this->position->getWorld();
$aabb = $this->getCollisionBoxes()[0]->expandedCopy($radius, $radius, $radius)->addCoord(0, $world->getMaxY(), 0);
if($this->primaryEffect === $this->secondaryEffect){
foreach($world->getNearbyEntities($aabb) as $entity){
if($entity instanceof Player){
$entity->getEffects()->add(new EffectInstance($this->primaryEffect, $effectDuration * 20, 1));
}
}
}else{
$effects = [$this->primaryEffect];
if($this->secondaryEffect !== null){
$effects[] = $this->secondaryEffect;
}
foreach($world->getNearbyEntities($aabb) as $entity){
if($entity instanceof Player){
foreach($effects as $effect){
$entity->getEffects()->add(new EffectInstance($effect, $effectDuration * 20, 0));
}
}
}
}
}
}

public function isBeaconLevelValid(int $level) : bool{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Please document this function, i feel that the name isn't clear enough

if($level < self::MIN_LEVEL_BEACON || $level > self::MAX_LEVEL_BEACON){
throw new InvalidArgumentException("Beacon level must be in range " . self::MIN_LEVEL_BEACON . "-" . self::MAX_LEVEL_BEACON . ", $level given");
}

$world = $this->position->getWorld();
$pos = $this->position->subtract(0, $level, 0);
for($x = -$level; $x <= $level; $x++){
for($z = -$level; $z <= $level; $z++){
$block = $world->getBlock($pos->add($x, 0, $z));
if(!isset(self::ALLOWED_BLOCK_IDS[$block->getTypeId()])){
return false;
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

I really don't like this. I know that's the most obvious way to do it, but it's going to lag like shit.

}
}
}
return true;
}

public function viewSky() : bool{
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

The function name is not the most appropriate.

$world = $this->position->getWorld();
$maxY = $world->getMaxY();
$block = $this;
for($y = 0; $y <= $maxY; $y++){
$block = $world->getBlock($block->position->up());
if(!$block instanceof Transparent && !$block instanceof Bedrock){
return false;
}
}
return true;
}

/** @return Effect[][] */
public function getLevelEffect() : array {
return [
self::MIN_LEVEL_BEACON => [
VanillaEffects::HASTE(),
VanillaEffects::SPEED()
],
self::MIN_LEVEL_BEACON + 1 => [
VanillaEffects::RESISTANCE(),
VanillaEffects::JUMP_BOOST()
],
self::MIN_LEVEL_BEACON + 2 => [
VanillaEffects::STRENGTH()
],
self::MIN_LEVEL_BEACON + 3 => [
VanillaEffects::REGENERATION()
]
];
}

/** @return Effect[] */
public function getAllowedEffect(int $beaconLevel) : array {
Copy link
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

Suggested change
public function getAllowedEffect(int $beaconLevel) : array {
public function getAllowedEffects(int $beaconLevel) : array {

if($beaconLevel < self::MIN_LEVEL_BEACON || $beaconLevel > self::MAX_LEVEL_BEACON){
throw new InvalidArgumentException("Beacon level must be in range " . self::MIN_LEVEL_BEACON . "-" . self::MAX_LEVEL_BEACON . ", $beaconLevel given");
}
$levelEffects = $this->getLevelEffect();
$allowed = [];
for ($i = self::MIN_LEVEL_BEACON; $i <= $beaconLevel; $i++) {
$allowed = array_merge($levelEffects[$i], $allowed);
}
return $allowed;
}
}
48 changes: 48 additions & 0 deletions src/block/inventory/BeaconInventory.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,48 @@
<?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\inventory;

use pocketmine\inventory\SimpleInventory;
use pocketmine\inventory\TemporaryInventory;
use pocketmine\item\Item;
use pocketmine\world\Position;

class BeaconInventory extends SimpleInventory implements BlockInventory, TemporaryInventory{
use BlockInventoryTrait;

public const SLOT_INPUT = 0;

public function __construct(Position $holder){
$this->holder = $holder;
parent::__construct(1);
}

public function getInput() : Item{
return $this->getItem(self::SLOT_INPUT);
}

public function setInput(Item $item) : void{
$this->setItem(self::SLOT_INPUT, $item);
}
}
61 changes: 61 additions & 0 deletions src/event/block/BeaconActivateEvent.php
Original file line number Diff line number Diff line change
@@ -0,0 +1,61 @@
<?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\effect\Effect;
use pocketmine\event\Cancellable;
use pocketmine\event\CancellableTrait;

/**
* Called when a player activates a beacon with the interface.
*/
class BeaconActivateEvent extends BlockEvent implements Cancellable{
use CancellableTrait;

protected Effect $primaryEffect;
protected ?Effect $secondaryEffect;

public function __construct(Block $block, Effect $primaryEffect, ?Effect $secondaryEffect = null){
parent::__construct($block);
$this->primaryEffect = $primaryEffect;
$this->secondaryEffect = $secondaryEffect;
}

public function getPrimaryEffect() : Effect{
return $this->primaryEffect;
}

public function getSecondaryEffect() : ?Effect{
return $this->secondaryEffect;
}

public function setPrimaryEffect(Effect $primary) : void{
$this->primaryEffect = $primary;
}

public function setSecondaryEffect(?Effect $secondary) : void{
$this->secondaryEffect = $secondary;
}
}
Loading