-
-
Notifications
You must be signed in to change notification settings - Fork 1.6k
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
base: minor-next
Are you sure you want to change the base?
Implement beacon #4687
Changes from all commits
709cbd5
bcae409
27979c1
991334e
cb33b5d
4a3c7cf
2ea22b4
a61aace
5bc5d63
4d71b9e
c487433
b4ab0c4
f918f18
34b3f27
357afcc
e628148
ee06f86
2b348e5
30e3252
0752052
38aab84
25c2b5c
5288af0
0c6a873
2656c6e
816ae70
3226ef4
4f797ca
a54b452
6078894
7167539
f6b63e0
75064a4
9ac6ec8
f677f82
5e72e0f
1b3e223
7624934
c3bfe3c
c96768d
df7280c
6e37cd8
File filter
Filter by extension
Conversations
Jump to
Diff view
Diff view
There are no files selected for viewing
Original file line number | Diff line number | Diff line change | ||||
---|---|---|---|---|---|---|
|
@@ -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; | ||||||
|
||||||
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 = [ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 There was a problem hiding this comment. Choose a reason for hiding this commentThe 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); | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||||||
} | ||||||
|
||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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{ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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; | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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{ | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe 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 { | ||||||
There was a problem hiding this comment. Choose a reason for hiding this commentThe reason will be displayed to describe this comment to others. Learn more.
Suggested change
|
||||||
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; | ||||||
} | ||||||
} |
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); | ||
} | ||
} |
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; | ||
} | ||
} |
There was a problem hiding this comment.
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 theBeacon
class