Skip to content
This repository was archived by the owner on Nov 18, 2025. It is now read-only.
Open
Show file tree
Hide file tree
Changes from all commits
Commits
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
20 changes: 10 additions & 10 deletions src/BlockHorizons/Fireworks/Loader.php
Original file line number Diff line number Diff line change
@@ -1,6 +1,5 @@
<?php

declare(strict_types = 1);
declare(strict_types=1);

namespace BlockHorizons\Fireworks;

Expand All @@ -12,19 +11,20 @@
use pocketmine\item\ItemFactory;
use pocketmine\item\ItemIdentifier;
use pocketmine\item\ItemIds;
use pocketmine\item\VanillaItems;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\plugin\PluginBase;
use pocketmine\world\World;

class Loader extends PluginBase
{

public function onEnable(): void
{
ItemFactory::getInstance()->register(new Fireworks(new ItemIdentifier(ItemIds::FIREWORKS, 0), "Fireworks"), true);
EntityFactory::getInstance()->register(FireworksRocket::class, static function (World $world, CompoundTag $nbt): FireworksRocket {
return new FireworksRocket(EntityDataHelper::parseLocation($nbt, $world), ItemFactory::getInstance()->get(ItemIds::FIREWORKS));
}, ["FireworksRocket", EntityIds::FIREWORKS_ROCKET], EntityLegacyIds::FIREWORKS_ROCKET);
}
public function onEnable(): void
{
$item = new Fireworks(new ItemIdentifier(ItemIds::FIREWORKS, 0), "Fireworks");
ItemFactory::getInstance()->register($item, true);
EntityFactory::getInstance()->register(FireworksRocket::class, static function (World $world, CompoundTag $nbt) use ($item): FireworksRocket {
return new FireworksRocket(EntityDataHelper::parseLocation($nbt, $world), $item);
}, ["FireworksRocket", EntityIds::FIREWORKS_ROCKET], EntityLegacyIds::FIREWORKS_ROCKET);
}
}
215 changes: 110 additions & 105 deletions src/BlockHorizons/Fireworks/entity/FireworksRocket.php
Original file line number Diff line number Diff line change
Expand Up @@ -12,114 +12,119 @@
use pocketmine\math\Vector3;
use pocketmine\nbt\tag\CompoundTag;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\types\CacheableNbt;
use pocketmine\network\mcpe\protocol\types\entity\EntityIds;
use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataCollection;
use pocketmine\network\mcpe\protocol\types\LevelSoundEvent;

class FireworksRocket extends Entity
{

public const DATA_FIREWORK_ITEM = 16; //firework item

public static function getNetworkTypeId(): string
{
return EntityIds::FIREWORKS_ROCKET;
}

/** @var int */
protected $lifeTime = 0;
/** @var Fireworks */
protected $fireworks;

public function __construct(Location $location, Fireworks $fireworks, ?int $lifeTime = null)
{
$this->fireworks = $fireworks;
parent::__construct($location, $fireworks->getNamedTag());
$this->setMotion(new Vector3(0.001, 0.05, 0.001));

if ($fireworks->getNamedTag()->getCompoundTag("Fireworks") !== null) {
$this->setLifeTime($lifeTime ?? $fireworks->getRandomizedFlightDuration());
}

$location->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEventPacket::SOUND_LAUNCH, $this->location->asVector3()));
}

protected function tryChangeMovement(): void
{
$this->motion->x *= 1.15;
$this->motion->y += 0.04;
$this->motion->z *= 1.15;
}

public function entityBaseTick(int $tickDiff = 1): bool
{
if ($this->closed) {
return false;
}

$hasUpdate = parent::entityBaseTick($tickDiff);
if ($this->doLifeTimeTick()) {
$hasUpdate = true;
}

return $hasUpdate;
}

public function setLifeTime(int $life): void
{
$this->lifeTime = $life;
}

protected function doLifeTimeTick(): bool
{
if (--$this->lifeTime < 0 && !$this->isFlaggedForDespawn()) {
$this->doExplosionAnimation();
$this->playSounds();
$this->flagForDespawn();
return true;
}

return false;
}

protected function doExplosionAnimation(): void
{
$this->broadcastAnimation(new FireworkParticleAnimation($this), $this->getViewers());
}

public function playSounds(): void
{
// This late in, there's 0 chance fireworks tag is null
$fireworksTag = $this->fireworks->getNamedTag()->getCompoundTag("Fireworks");
$explosionsTag = $fireworksTag->getListTag("Explosions");
if ($explosionsTag === null) {
// We don't throw an error here since there are fireworks that can die without noise or particles,
// which means they are lacking an explosion tag.
return;
}

foreach ($explosionsTag->getValue() as $info) {
if ($info instanceof CompoundTag) {
if ($info->getByte("FireworkType", 0) === Fireworks::TYPE_HUGE_SPHERE) {
$this->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEventPacket::SOUND_LARGE_BLAST, $this->location->asVector3()));
} else {
$this->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEventPacket::SOUND_BLAST, $this->location->asVector3()));
}

if ($info->getByte("FireworkFlicker", 0) === 1) {
$this->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEventPacket::SOUND_TWINKLE, $this->location->asVector3()));
}
}
}
}

public function syncNetworkData(EntityMetadataCollection $properties): void
{
parent::syncNetworkData($properties);
$properties->setCompoundTag(self::DATA_FIREWORK_ITEM, $this->fireworks->getNamedTag());
}

protected function getInitialSizeInfo(): EntitySizeInfo
{
return new EntitySizeInfo(0.25, 0.25);
}
public const DATA_FIREWORK_ITEM = 16; //firework item
protected int $lifeTime = 0;
protected Fireworks $fireworks;

/**
* @throws \Exception
*/
public function __construct(Location $location, Fireworks $fireworks, ?int $lifeTime = null)
{
$this->fireworks = $fireworks;
parent::__construct($location, $fireworks->getNamedTag());
$this->setMotion(new Vector3(0.001, 0.05, 0.001));

if ($fireworks->getNamedTag()->getCompoundTag("Fireworks") !== null) {
$this->setLifeTime($lifeTime ?? $fireworks->getRandomizedFlightDuration());
}

$location->getWorld()->broadcastPacketToViewers($location, LevelSoundEventPacket::create(LevelSoundEvent::LAUNCH, $location, -1, ":", false, false));
}

public function setLifeTime(int $life): void
{
$this->lifeTime = $life;
}

public static function getNetworkTypeId(): string
{
return EntityIds::FIREWORKS_ROCKET;
}

public function entityBaseTick(int $tickDiff = 1): bool
{
if ($this->closed) {
return false;
}

$hasUpdate = parent::entityBaseTick($tickDiff);
if ($this->doLifeTimeTick()) {
$hasUpdate = true;
}

return $hasUpdate;
}

protected function doLifeTimeTick(): bool
{
if (--$this->lifeTime < 0 && !$this->isFlaggedForDespawn()) {
$this->doExplosionAnimation();
$this->playSounds();
$this->flagForDespawn();
return true;
}

return false;
}

protected function doExplosionAnimation(): void
{
$this->broadcastAnimation(new FireworkParticleAnimation($this), $this->getViewers());
}

public function playSounds(): void
{
// This late in, there's 0 chance fireworks tag is null
$fireworksTag = $this->fireworks->getNamedTag()->getCompoundTag("Fireworks");
if (!isset($fireworksTag)) {
return;

}
$explosionsTag = $fireworksTag->getListTag("Explosions");
if ($explosionsTag === null) {
// We don't throw an error here since there are fireworks that can die without noise or particles,
// which means they are lacking an explosion tag.
return;
}

foreach ($explosionsTag->getValue() as $info) {
if ($info instanceof CompoundTag) {
if ($info->getByte("FireworkType", 0) === Fireworks::TYPE_HUGE_SPHERE) {
$this->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEvent::LARGE_BLAST, $this->location, -1, ":", false, false));
} else {
$this->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEvent::BLAST, $this->location, -1, ":", false, false));
}

if ($info->getByte("FireworkFlicker", 0) === 1) {
$this->getWorld()->broadcastPacketToViewers($this->location, LevelSoundEventPacket::create(LevelSoundEvent::TWINKLE, $this->location, -1, ":", false, false));
}
}
}
}

public function syncNetworkData(EntityMetadataCollection $properties): void
{
parent::syncNetworkData($properties);
$properties->setCompoundTag(self::DATA_FIREWORK_ITEM, new CacheableNbt($this->fireworks->getNamedTag()));
}

protected function tryChangeMovement(): void
{
$this->motion->x *= 1.15;
$this->motion->y += 0.04;
$this->motion->z *= 1.15;
}

protected function getInitialSizeInfo(): EntitySizeInfo
{
return new EntitySizeInfo(0.25, 0.25);
}
}
Original file line number Diff line number Diff line change
Expand Up @@ -7,21 +7,21 @@
use BlockHorizons\Fireworks\entity\FireworksRocket;
use pocketmine\entity\animation\Animation;
use pocketmine\network\mcpe\protocol\ActorEventPacket;
use pocketmine\network\mcpe\protocol\types\ActorEvent;

class FireworkParticleAnimation implements Animation
{
/** @var FireworksRocket */
private $firework;
private FireworksRocket $firework;

public function __construct(FireworksRocket $firework)
{
$this->firework = $firework;
}
public function __construct(FireworksRocket $firework)
{
$this->firework = $firework;
}

public function encode(): array
{
return [
ActorEventPacket::create($this->firework->getId(), ActorEventPacket::FIREWORK_PARTICLES, 0)
];
}
public function encode(): array
{
return [
ActorEventPacket::create($this->firework->getId(), ActorEvent::FIREWORK_PARTICLES, 0)
];
}
}
Loading