From f4a9c03ccefc304842eb0b6bfdfc44135cba26ee Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Thu, 1 Sep 2022 21:54:59 -0500 Subject: [PATCH 01/47] Implement lingering potion --- .../ItemSerializerDeserializerRegistrar.php | 9 + src/entity/EntityFactory.php | 9 + src/entity/effect/EffectManager.php | 4 +- src/entity/object/AreaEffectCloud.php | 350 ++++++++++++++++++ src/entity/projectile/SplashPotion.php | 21 +- src/item/LingeringPotion.php | 67 ++++ src/item/StringToItemParser.php | 43 +++ src/item/VanillaItems.php | 1 + 8 files changed, 496 insertions(+), 8 deletions(-) create mode 100644 src/entity/object/AreaEffectCloud.php create mode 100644 src/item/LingeringPotion.php diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 62ab3d7f3fb..29a15f0ac67 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -39,6 +39,7 @@ use pocketmine\item\Banner; use pocketmine\item\Dye; use pocketmine\item\Item; +use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\SplashPotion; use pocketmine\item\SuspiciousStew; @@ -461,6 +462,14 @@ function(Banner $item, int $meta) : void{ }, fn(Banner $item) => DyeColorIdMap::getInstance()->toInvertedId($item->getColor()) ); + $this->map1to1ItemWithMeta( + Ids::LINGERING_POTION, + Items::LINGERING_POTION(), + function(LingeringPotion $item, int $meta) : void{ + $item->setType(PotionTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown potion type ID $meta")); + }, + fn(LingeringPotion $item) => PotionTypeIdMap::getInstance()->toId($item->getType()) + ); $this->map1to1ItemWithMeta( Ids::POTION, Items::POTION(), diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 90b3f659fae..1d3f7fc70e2 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -32,6 +32,7 @@ use pocketmine\data\bedrock\PotionTypeIds; use pocketmine\data\SavedDataLoadingException; use pocketmine\entity\EntityDataHelper as Helper; +use pocketmine\entity\object\AreaEffectCloud; use pocketmine\entity\object\ExperienceOrb; use pocketmine\entity\object\FallingBlock; use pocketmine\entity\object\ItemEntity; @@ -81,6 +82,14 @@ public function __construct(){ //define legacy save IDs first - use them for saving for maximum compatibility with Minecraft PC //TODO: index them by version to allow proper multi-save compatibility + $this->register(AreaEffectCloud::class, function(World $world, CompoundTag $nbt) : AreaEffectCloud{ + $potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER)); + if($potionType === null){ + throw new SavedDataLoadingException("No such potion type"); + } + return new AreaEffectCloud(Helper::parseLocation($nbt, $world), $potionType, $nbt); + }, ['AreaEffectCloud', 'minecraft:area_effect_cloud'], LegacyIds::AREA_EFFECT_CLOUD); + $this->register(Arrow::class, function(World $world, CompoundTag $nbt) : Arrow{ return new Arrow(Helper::parseLocation($nbt, $world), null, $nbt->getByte(Arrow::TAG_CRIT, 0) === 1, $nbt); }, ['Arrow', 'minecraft:arrow'], LegacyIds::ARROW); diff --git a/src/entity/effect/EffectManager.php b/src/entity/effect/EffectManager.php index 747662dc713..07f36c2ff90 100644 --- a/src/entity/effect/EffectManager.php +++ b/src/entity/effect/EffectManager.php @@ -24,7 +24,7 @@ namespace pocketmine\entity\effect; use pocketmine\color\Color; -use pocketmine\entity\Living; +use pocketmine\entity\Entity; use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; use pocketmine\utils\ObjectSet; @@ -51,7 +51,7 @@ class EffectManager{ protected ObjectSet $effectRemoveHooks; public function __construct( - private Living $entity + private Entity $entity ){ $this->bubbleColor = new Color(0, 0, 0, 0); $this->effectAddHooks = new ObjectSet(); diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php new file mode 100644 index 00000000000..98c77b130a9 --- /dev/null +++ b/src/entity/object/AreaEffectCloud.php @@ -0,0 +1,350 @@ + */ + protected array $victims = []; + + protected int $duration = 600; + protected int $durationOnUse = 0; + protected int $reapplicationDelay = 20; + + protected float $radius = 3.0; + protected float $radiusOnUse = -0.5; + protected float $radiusPerTick = -0.005; + + public function __construct(Location $location, PotionType $potionType, ?CompoundTag $nbt = null){ + $this->potionType = $potionType; + $this->bubbleColor = new Color(0, 0, 0, 0); + parent::__construct($location, $nbt); + } + + protected function getInitialSizeInfo() : EntitySizeInfo{ return new EntitySizeInfo(0.5, $this->radius * 2); } + + protected function getInitialDragMultiplier() : float{ return 0.0; } + + protected function getInitialGravity() : float{ return 0.0; } + + protected function initEntity(CompoundTag $nbt) : void{ + parent::initEntity($nbt); + + $this->effectManager = new EffectManager($this); + $this->effectManager->getEffectAddHooks()->add(function() : void{ $this->recalculateEffectColor(); }); + $this->effectManager->getEffectRemoveHooks()->add(function() : void{ $this->recalculateEffectColor(); }); + + $this->age = $nbt->getShort("Age", 0); + $this->duration = $nbt->getInt("Duration", 600); + $this->durationOnUse = $nbt->getInt("DurationOnUse", 0); + $this->reapplicationDelay = $nbt->getInt("ReapplicationDelay", 20); + $this->radius = $nbt->getFloat("Radius", 3.0); + $this->radiusOnUse = $nbt->getFloat("RadiusOnUse", -0.5); + $this->radiusPerTick = $nbt->getFloat("RadiusPerTick", -0.005); + + /** @var CompoundTag[]|ListTag|null $effectsTag */ + $effectsTag = $nbt->getListTag("mobEffects"); + if($effectsTag !== null){ + foreach($effectsTag as $e){ + $effect = EffectIdMap::getInstance()->fromId($e->getByte("Id")); + if($effect === null){ + continue; + } + + $this->effectManager->add(new EffectInstance( + $effect, + $e->getInt("Duration"), + Binary::unsignByte($e->getByte("Amplifier")), + $e->getByte("ShowParticles", 1) !== 0, + $e->getByte("Ambient", 0) !== 0 + )); + } + } + $this->recalculateEffectColor(); + } + + public function isFireProof() : bool{ + return true; + } + public function saveNBT() : CompoundTag{ + $nbt = parent::saveNBT(); + + $nbt->setShort("Age", $this->age); + $nbt->setShort("PotionId", PotionTypeIdMap::getInstance()->toId($this->potionType)); + $nbt->setInt("Duration", $this->duration); + $nbt->setInt("DurationOnUse", $this->durationOnUse); + $nbt->setInt("ReapplicationDelay", $this->reapplicationDelay); + $nbt->setFloat("Radius", $this->radius); + $nbt->setFloat("RadiusOnUse", $this->radiusOnUse); + $nbt->setFloat("RadiusPerTick", $this->radiusPerTick); + + if(count($this->effectManager->all()) > 0){ + $effects = []; + foreach($this->effectManager->all() as $effect){ + $effects[] = CompoundTag::create() + ->setByte("Id", EffectIdMap::getInstance()->toId($effect->getType())) + ->setByte("Amplifier", Binary::signByte($effect->getAmplifier())) + ->setInt("Duration", $effect->getDuration()) + ->setByte("Ambient", $effect->isAmbient() ? 1 : 0) + ->setByte("ShowParticles", $effect->isVisible() ? 1 : 0); + } + $nbt->setTag("mobEffects", new ListTag($effects)); + } + + return $nbt; + } + + /** + * Recalculates the area entity's potion bubbles colour based on the active effects. + */ + protected function recalculateEffectColor() : void{ + /** @var Color[] $colors */ + $colors = []; + $ambient = true; + foreach($this->getCloudEffects() as $effect){ + if($effect->isVisible()){ + $level = $effect->getEffectLevel(); + $color = $effect->getColor(); + for($i = 0; $i < $level; ++$i){ + $colors[] = $color; + } + + if(!$effect->isAmbient()){ + $ambient = false; + } + } + } + + if(count($colors) > 0){ + $this->bubbleColor = Color::mix(...$colors); + $this->onlyAmbientEffects = $ambient; + }else{ + $this->bubbleColor = PotionSplashParticle::DEFAULT_COLOR(); + $this->onlyAmbientEffects = false; + } + $this->networkPropertiesDirty = true; + } + + public function getAge() : int{ + return $this->age; + } + + public function getPotionType() : PotionType{ + return $this->potionType; + } + + public function setPotionType(PotionType $type) : void{ + $this->potionType = $type; + $this->recalculateEffectColor(); + $this->networkPropertiesDirty = true; + } + + public function getEffects() : EffectManager{ + return $this->effectManager; + } + + public function getBubbleColor() : Color{ + return $this->bubbleColor; + } + + public function hasOnlyAmbientEffects() : bool{ + return $this->onlyAmbientEffects; + } + + public function getRadius() : float{ + return $this->radius; + } + + public function setRadius(float $radius) : void{ + $this->radius = $radius; + $this->setSize($this->getInitialSizeInfo()); + $this->networkPropertiesDirty = true; + } + + public function getRadiusOnUse() : float{ + return $this->radiusOnUse; + } + + public function setRadiusOnUse(float $radiusOnUse) : void{ + $this->radiusOnUse = $radiusOnUse; + $this->networkPropertiesDirty = true; + } + + public function getRadiusPerTick() : float{ + return $this->radiusPerTick; + } + + public function setRadiusPerTick(float $radiusPerTick) : void{ + $this->radiusPerTick = $radiusPerTick; + $this->networkPropertiesDirty = true; + } + + public function getDuration() : int{ + return $this->duration; + } + + public function setDuration(int $duration) : void{ + $this->duration = $duration; + $this->networkPropertiesDirty = true; + } + + public function getDurationOnUse() : int{ + return $this->durationOnUse; + } + + public function setDurationOnUse(int $durationOnUse) : void{ + $this->durationOnUse = $durationOnUse; + $this->networkPropertiesDirty = true; + } + + public function getReapplicationDelay() : int{ + return $this->reapplicationDelay; + } + + public function setReapplicationDelay(int $delay) : void{ + $this->reapplicationDelay = $delay; + $this->networkPropertiesDirty = true; + } + + protected function entityBaseTick(int $tickDiff = 1) : bool{ + $hasUpdate = parent::entityBaseTick($tickDiff); + + $this->age += $tickDiff; + if($this->age > $this->duration){ + $this->flagForDespawn(); + return true; + } + if($this->age > 10){ + //Area effect clouds only trigger updates every ten ticks. + if($this->age % 10 === 0){ + $this->setRadius($this->radius + ($this->radiusPerTick * $tickDiff)); + if($this->radius < 0.5){ + $this->flagForDespawn(); + return true; + } + foreach($this->victims as $entityId => $expiration){ + if($this->age >= $expiration){ + unset($this->victims[$entityId]); + } + } + foreach($this->getWorld()->getNearbyEntities($this->getBoundingBox(), $this) as $entity){ + if(!$entity instanceof Living || !$entity->isAlive() || isset($this->victims[$entity->getId()])){ + continue; + } + + $entityPosition = $entity->getPosition(); + $xDiff = $entityPosition->getX() - $this->location->getX(); + $zDiff = $entityPosition->getZ() - $this->location->getZ(); + if(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2){ + continue; + } + + foreach($this->getCloudEffects() as $effect){ + if($effect->getType() instanceof InstantEffect){ + $effect->getType()->applyEffect($entity, $effect, 0.5, $this); + }else{ + $entity->getEffects()->add($effect); + } + } + $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; + if($this->radiusOnUse !== 0.0){ + $this->setRadius($this->radius + $this->radiusOnUse); + if($this->radius <= 0){ + $this->flagForDespawn(); + return true; + } + } + if($this->durationOnUse !== 0){ + $this->setDuration($this->duration + $this->durationOnUse); + if($this->duration <= 0){ + $this->flagForDespawn(); + return true; + } + } + } + } + } + + return $hasUpdate; + } + + /** + * Returns the effects the area effect cloud provides. + * + * @return EffectInstance[] + */ + public function getCloudEffects() : array{ + return array_merge(array_map(function(EffectInstance $effect) : EffectInstance{ + return $effect->getType() instanceof InstantEffect ? $effect : $effect->setDuration((int) round($effect->getDuration() / 4)); + }, $this->potionType->getEffects()), + array_map(function(EffectInstance $effect) : EffectInstance{ + return clone $effect; + }, $this->effectManager->all()) + ); + } + + protected function syncNetworkData(EntityMetadataCollection $properties) : void{ + parent::syncNetworkData($properties); + + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_DURATION, $this->duration); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->radius); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, $this->radiusPerTick); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, $this->radiusOnUse); + $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->bubbleColor->toARGB())); + $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->onlyAmbientEffects ? 1 : 0); + } +} diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index b4adb6eece6..2f43eaa42ef 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -32,6 +32,7 @@ use pocketmine\entity\Entity; use pocketmine\entity\Living; use pocketmine\entity\Location; +use pocketmine\entity\object\AreaEffectCloud; use pocketmine\event\entity\ProjectileHitBlockEvent; use pocketmine\event\entity\ProjectileHitEntityEvent; use pocketmine\event\entity\ProjectileHitEvent; @@ -94,8 +95,8 @@ protected function onHit(ProjectileHitEvent $event) : void{ $this->getWorld()->addParticle($this->location, $particle); $this->broadcastSound(new PotionSplashSound()); - if($hasEffects){ - if(!$this->willLinger()){ + if(!$this->willLinger()){ + if($hasEffects){ foreach($this->getWorld()->getNearbyEntities($this->boundingBox->expandedCopy(4.125, 2.125, 4.125), $this) as $entity){ if($entity instanceof Living && $entity->isAlive()){ $distanceSquared = $entity->getEyePos()->distanceSquared($this->location); @@ -124,10 +125,15 @@ protected function onHit(ProjectileHitEvent $event) : void{ } } } - }else{ - //TODO: lingering potions } - }elseif($event instanceof ProjectileHitBlockEvent && $this->getPotionType()->equals(PotionType::WATER())){ + }else{ + $entity = new AreaEffectCloud($this->location, $this->potionType); + $entity->setRadius(3.0); + $entity->setRadiusOnUse(-0.5); + $entity->setRadiusPerTick(-$entity->getRadius() / $entity->getDuration()); + $entity->spawnToAll(); + } + if(!$hasEffects && $event instanceof ProjectileHitBlockEvent && $this->getPotionType()->equals(PotionType::WATER())){ $blockIn = $event->getBlockHit()->getSide($event->getRayTraceResult()->getHitFace()); if($blockIn->getTypeId() === BlockTypeIds::FIRE){ @@ -162,10 +168,13 @@ public function willLinger() : bool{ /** * Sets whether this splash potion will create an area-effect-cloud when it lands. + * + * @return $this */ - public function setLinger(bool $value = true) : void{ + public function setLinger(bool $value = true) : self{ $this->linger = $value; $this->networkPropertiesDirty = true; + return $this; } /** diff --git a/src/item/LingeringPotion.php b/src/item/LingeringPotion.php new file mode 100644 index 00000000000..9ba3d3d4992 --- /dev/null +++ b/src/item/LingeringPotion.php @@ -0,0 +1,67 @@ +potionType = PotionType::WATER(); + parent::__construct($identifier, $name); + } + + protected function describeType(RuntimeDataReader|RuntimeDataWriter $w) : void{ + $w->potionType($this->potionType); + } + + public function getType() : PotionType{ return $this->potionType; } + + /** + * @return $this + */ + public function setType(PotionType $type) : self{ + $this->potionType = $type; + return $this; + } + + public function getMaxStackSize() : int{ + return 1; + } + + protected function createEntity(Location $location, Player $thrower) : Throwable{ + return (new SplashPotionEntity($location, $thrower, $this->potionType))->setLinger(); + } + + public function getThrowForce() : float{ + return 0.5; + } +} diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index 66378551b3e..cf19e532838 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1133,6 +1133,7 @@ private static function registerItems(self $result) : void{ $result->register("apple_enchanted", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("appleenchanted", fn() => Items::ENCHANTED_GOLDEN_APPLE()); $result->register("arrow", fn() => Items::ARROW()); + $result->register("awkward_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::AWKWARD())); $result->register("awkward_potion", fn() => Items::POTION()->setType(PotionType::AWKWARD())); $result->register("awkward_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::AWKWARD())); $result->register("baked_potato", fn() => Items::BAKED_POTATO()); @@ -1252,6 +1253,7 @@ private static function registerItems(self $result) : void{ $result->register("feather", fn() => Items::FEATHER()); $result->register("fermented_spider_eye", fn() => Items::FERMENTED_SPIDER_EYE()); $result->register("fire_charge", fn() => Items::FIRE_CHARGE()); + $result->register("fire_resistance_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::FIRE_RESISTANCE())); $result->register("fire_resistance_potion", fn() => Items::POTION()->setType(PotionType::FIRE_RESISTANCE())); $result->register("fire_resistance_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::FIRE_RESISTANCE())); $result->register("fish", fn() => Items::RAW_FISH()); @@ -1288,14 +1290,17 @@ private static function registerItems(self $result) : void{ $result->register("golden_shovel", fn() => Items::GOLDEN_SHOVEL()); $result->register("golden_sword", fn() => Items::GOLDEN_SWORD()); $result->register("gunpowder", fn() => Items::GUNPOWDER()); + $result->register("harming_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::HARMING())); $result->register("harming_potion", fn() => Items::POTION()->setType(PotionType::HARMING())); $result->register("harming_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::HARMING())); + $result->register("healing_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::HEALING())); $result->register("healing_potion", fn() => Items::POTION()->setType(PotionType::HEALING())); $result->register("healing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::HEALING())); $result->register("heart_of_the_sea", fn() => Items::HEART_OF_THE_SEA()); $result->register("honey_bottle", fn() => Items::HONEY_BOTTLE()); $result->register("honeycomb", fn() => Items::HONEYCOMB()); $result->register("ink_sac", fn() => Items::INK_SAC()); + $result->register("invisibility_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::INVISIBILITY())); $result->register("invisibility_potion", fn() => Items::POTION()->setType(PotionType::INVISIBILITY())); $result->register("invisibility_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::INVISIBILITY())); $result->register("iron_axe", fn() => Items::IRON_AXE()); @@ -1312,6 +1317,7 @@ private static function registerItems(self $result) : void{ $result->register("jungle_boat", fn() => Items::JUNGLE_BOAT()); $result->register("lapis_lazuli", fn() => Items::LAPIS_LAZULI()); $result->register("lava_bucket", fn() => Items::LAVA_BUCKET()); + $result->register("leaping_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LEAPING())); $result->register("leaping_potion", fn() => Items::POTION()->setType(PotionType::LEAPING())); $result->register("leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LEAPING())); $result->register("leather", fn() => Items::LEATHER()); @@ -1322,32 +1328,47 @@ private static function registerItems(self $result) : void{ $result->register("leather_leggings", fn() => Items::LEATHER_PANTS()); $result->register("leather_pants", fn() => Items::LEATHER_PANTS()); $result->register("leather_tunic", fn() => Items::LEATHER_TUNIC()); + $result->register("lingering_potion", fn() => Items::LINGERING_POTION()); + $result->register("long_fire_resistance_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_FIRE_RESISTANCE())); $result->register("long_fire_resistance_potion", fn() => Items::POTION()->setType(PotionType::LONG_FIRE_RESISTANCE())); $result->register("long_fire_resistance_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_FIRE_RESISTANCE())); + $result->register("long_invisibility_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_INVISIBILITY())); $result->register("long_invisibility_potion", fn() => Items::POTION()->setType(PotionType::LONG_INVISIBILITY())); $result->register("long_invisibility_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_INVISIBILITY())); + $result->register("long_leaping_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_LEAPING())); $result->register("long_leaping_potion", fn() => Items::POTION()->setType(PotionType::LONG_LEAPING())); $result->register("long_leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_LEAPING())); + $result->register("long_mundane_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_MUNDANE())); $result->register("long_mundane_potion", fn() => Items::POTION()->setType(PotionType::LONG_MUNDANE())); $result->register("long_mundane_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_MUNDANE())); + $result->register("long_night_vision_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_NIGHT_VISION())); $result->register("long_night_vision_potion", fn() => Items::POTION()->setType(PotionType::LONG_NIGHT_VISION())); $result->register("long_night_vision_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_NIGHT_VISION())); + $result->register("long_poison_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_POISON())); $result->register("long_poison_potion", fn() => Items::POTION()->setType(PotionType::LONG_POISON())); $result->register("long_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_POISON())); + $result->register("long_regeneration_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_REGENERATION())); $result->register("long_regeneration_potion", fn() => Items::POTION()->setType(PotionType::LONG_REGENERATION())); $result->register("long_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_REGENERATION())); + $result->register("long_slow_falling_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_SLOW_FALLING())); $result->register("long_slow_falling_potion", fn() => Items::POTION()->setType(PotionType::LONG_SLOW_FALLING())); $result->register("long_slow_falling_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SLOW_FALLING())); + $result->register("long_slowness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_SLOWNESS())); $result->register("long_slowness_potion", fn() => Items::POTION()->setType(PotionType::LONG_SLOWNESS())); $result->register("long_slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SLOWNESS())); + $result->register("long_strength_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_STRENGTH())); $result->register("long_strength_potion", fn() => Items::POTION()->setType(PotionType::LONG_STRENGTH())); $result->register("long_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_STRENGTH())); + $result->register("long_swiftness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_SWIFTNESS())); $result->register("long_swiftness_potion", fn() => Items::POTION()->setType(PotionType::LONG_SWIFTNESS())); $result->register("long_swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_SWIFTNESS())); + $result->register("long_turtle_master_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_TURTLE_MASTER())); $result->register("long_turtle_master_potion", fn() => Items::POTION()->setType(PotionType::LONG_TURTLE_MASTER())); $result->register("long_turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_TURTLE_MASTER())); + $result->register("long_water_breathing_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_WATER_BREATHING())); $result->register("long_water_breathing_potion", fn() => Items::POTION()->setType(PotionType::LONG_WATER_BREATHING())); $result->register("long_water_breathing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_WATER_BREATHING())); + $result->register("long_weakness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::LONG_WEAKNESS())); $result->register("long_weakness_potion", fn() => Items::POTION()->setType(PotionType::LONG_WEAKNESS())); $result->register("long_weakness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::LONG_WEAKNESS())); $result->register("magma_cream", fn() => Items::MAGMA_CREAM()); @@ -1356,6 +1377,7 @@ private static function registerItems(self $result) : void{ $result->register("melon_slice", fn() => Items::MELON()); $result->register("milk_bucket", fn() => Items::MILK_BUCKET()); $result->register("minecart", fn() => Items::MINECART()); + $result->register("mundane_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::MUNDANE())); $result->register("mundane_potion", fn() => Items::POTION()->setType(PotionType::MUNDANE())); $result->register("mundane_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::MUNDANE())); $result->register("mushroom_stew", fn() => Items::MUSHROOM_STEW()); @@ -1381,12 +1403,14 @@ private static function registerItems(self $result) : void{ $result->register("netherite_shovel", fn() => Items::NETHERITE_SHOVEL()); $result->register("netherite_sword", fn() => Items::NETHERITE_SWORD()); $result->register("netherstar", fn() => Items::NETHER_STAR()); + $result->register("night_vision_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::NIGHT_VISION())); $result->register("night_vision_potion", fn() => Items::POTION()->setType(PotionType::NIGHT_VISION())); $result->register("night_vision_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::NIGHT_VISION())); $result->register("oak_boat", fn() => Items::OAK_BOAT()); $result->register("painting", fn() => Items::PAINTING()); $result->register("paper", fn() => Items::PAPER()); $result->register("phantom_membrane", fn() => Items::PHANTOM_MEMBRANE()); + $result->register("poison_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::POISON())); $result->register("poison_potion", fn() => Items::POTION()->setType(PotionType::POISON())); $result->register("poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::POISON())); $result->register("poisonous_potato", fn() => Items::POISONOUS_POTATO()); @@ -1430,6 +1454,7 @@ private static function registerItems(self $result) : void{ $result->register("record_ward", fn() => Items::RECORD_WARD()); $result->register("redstone", fn() => Items::REDSTONE_DUST()); $result->register("redstone_dust", fn() => Items::REDSTONE_DUST()); + $result->register("regeneration_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::REGENERATION())); $result->register("regeneration_potion", fn() => Items::POTION()->setType(PotionType::REGENERATION())); $result->register("regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::REGENERATION())); $result->register("rotten_flesh", fn() => Items::ROTTEN_FLESH()); @@ -1440,8 +1465,10 @@ private static function registerItems(self $result) : void{ $result->register("shulker_shell", fn() => Items::SHULKER_SHELL()); $result->register("slime_ball", fn() => Items::SLIMEBALL()); $result->register("slimeball", fn() => Items::SLIMEBALL()); + $result->register("slow_falling_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::SLOW_FALLING())); $result->register("slow_falling_potion", fn() => Items::POTION()->setType(PotionType::SLOW_FALLING())); $result->register("slow_falling_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SLOW_FALLING())); + $result->register("slowness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::SLOWNESS())); $result->register("slowness_potion", fn() => Items::POTION()->setType(PotionType::SLOWNESS())); $result->register("slowness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SLOWNESS())); $result->register("snowball", fn() => Items::SNOWBALL()); @@ -1459,46 +1486,62 @@ private static function registerItems(self $result) : void{ $result->register("stone_pickaxe", fn() => Items::STONE_PICKAXE()); $result->register("stone_shovel", fn() => Items::STONE_SHOVEL()); $result->register("stone_sword", fn() => Items::STONE_SWORD()); + $result->register("strength_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRENGTH())); $result->register("strength_potion", fn() => Items::POTION()->setType(PotionType::STRENGTH())); $result->register("strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRENGTH())); $result->register("string", fn() => Items::STRING()); + $result->register("strong_harming_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_HARMING())); $result->register("strong_harming_potion", fn() => Items::POTION()->setType(PotionType::STRONG_HARMING())); $result->register("strong_harming_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_HARMING())); + $result->register("strong_healing_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_HEALING())); $result->register("strong_healing_potion", fn() => Items::POTION()->setType(PotionType::STRONG_HEALING())); $result->register("strong_healing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_HEALING())); + $result->register("strong_leaping_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_LEAPING())); $result->register("strong_leaping_potion", fn() => Items::POTION()->setType(PotionType::STRONG_LEAPING())); $result->register("strong_leaping_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_LEAPING())); + $result->register("strong_poison_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_POISON())); $result->register("strong_poison_potion", fn() => Items::POTION()->setType(PotionType::STRONG_POISON())); $result->register("strong_poison_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_POISON())); + $result->register("strong_regeneration_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_REGENERATION())); $result->register("strong_regeneration_potion", fn() => Items::POTION()->setType(PotionType::STRONG_REGENERATION())); $result->register("strong_regeneration_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_REGENERATION())); + $result->register("strong_strength_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_STRENGTH())); $result->register("strong_strength_potion", fn() => Items::POTION()->setType(PotionType::STRONG_STRENGTH())); $result->register("strong_strength_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_STRENGTH())); + $result->register("strong_swiftness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_SWIFTNESS())); $result->register("strong_swiftness_potion", fn() => Items::POTION()->setType(PotionType::STRONG_SWIFTNESS())); $result->register("strong_swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_SWIFTNESS())); + $result->register("strong_turtle_master_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::STRONG_TURTLE_MASTER())); $result->register("strong_turtle_master_potion", fn() => Items::POTION()->setType(PotionType::STRONG_TURTLE_MASTER())); $result->register("strong_turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::STRONG_TURTLE_MASTER())); $result->register("sugar", fn() => Items::SUGAR()); $result->register("suspicious_stew", fn() => Items::SUSPICIOUS_STEW()); $result->register("sweet_berries", fn() => Items::SWEET_BERRIES()); + $result->register("swiftness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::SWIFTNESS())); $result->register("swiftness_potion", fn() => Items::POTION()->setType(PotionType::SWIFTNESS())); $result->register("swiftness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::SWIFTNESS())); + $result->register("thick_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::THICK())); $result->register("thick_potion", fn() => Items::POTION()->setType(PotionType::THICK())); $result->register("thick_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::THICK())); $result->register("totem", fn() => Items::TOTEM()); + $result->register("turtle_master_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::TURTLE_MASTER())); $result->register("turtle_master_potion", fn() => Items::POTION()->setType(PotionType::TURTLE_MASTER())); $result->register("turtle_master_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::TURTLE_MASTER())); $result->register("turtle_shell_piece", fn() => Items::SCUTE()); $result->register("villager_spawn_egg", fn() => Items::VILLAGER_SPAWN_EGG()); $result->register("water_breathing_potion", fn() => Items::POTION()->setType(PotionType::WATER_BREATHING())); + $result->register("water_breathing_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::WATER_BREATHING())); $result->register("water_breathing_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WATER_BREATHING())); $result->register("water_bucket", fn() => Items::WATER_BUCKET()); + $result->register("water_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::WATER())); $result->register("water_potion", fn() => Items::POTION()->setType(PotionType::WATER())); $result->register("water_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WATER())); + $result->register("weakness_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::WEAKNESS())); $result->register("weakness_potion", fn() => Items::POTION()->setType(PotionType::WEAKNESS())); $result->register("weakness_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WEAKNESS())); $result->register("wheat", fn() => Items::WHEAT()); $result->register("wheat_seeds", fn() => Items::WHEAT_SEEDS()); + $result->register("wither_lingering_potion", fn() => Items::LINGERING_POTION()->setType(PotionType::WITHER())); $result->register("wither_potion", fn() => Items::POTION()->setType(PotionType::WITHER())); $result->register("wither_splash_potion", fn() => Items::SPLASH_POTION()->setType(PotionType::WITHER())); $result->register("wooden_axe", fn() => Items::WOODEN_AXE()); diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index d7091041e7d..2cec769a9ec 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -449,6 +449,7 @@ protected static function setup() : void{ self::register("lapis_lazuli", new Item(new IID(Ids::LAPIS_LAZULI), "Lapis Lazuli")); self::register("lava_bucket", new LiquidBucket(new IID(Ids::LAVA_BUCKET), "Lava Bucket", Blocks::LAVA())); self::register("leather", new Item(new IID(Ids::LEATHER), "Leather")); + self::register("lingering_potion", new LingeringPotion(new IID(Ids::LINGERING_POTION), "Lingering Potion")); self::register("magma_cream", new Item(new IID(Ids::MAGMA_CREAM), "Magma Cream")); self::register("mangrove_sign", new ItemBlockWallOrFloor(new IID(Ids::MANGROVE_SIGN), Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); self::register("melon", new Melon(new IID(Ids::MELON), "Melon")); From c8e3037826731e7a5325a91ff58329b80a407f30 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Thu, 1 Sep 2022 22:00:14 -0500 Subject: [PATCH 02/47] Add LingeringPotion doc --- src/item/VanillaItems.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 2cec769a9ec..61735cd7095 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -203,6 +203,7 @@ * @method static Armor LEATHER_CAP() * @method static Armor LEATHER_PANTS() * @method static Armor LEATHER_TUNIC() + * @method static LingeringPotion LINGERING_POTION() * @method static Item MAGMA_CREAM() * @method static ItemBlockWallOrFloor MANGROVE_SIGN() * @method static Melon MELON() From e66d376e59b6fd15fbd8b007cfdbf03d3780d332 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 2 Sep 2022 11:43:02 -0500 Subject: [PATCH 03/47] Implement an EffectContainer --- src/entity/effect/EffectContainer.php | 152 ++++++++++++++++++++++++++ src/entity/effect/EffectManager.php | 103 ++--------------- src/entity/object/AreaEffectCloud.php | 22 ++-- 3 files changed, 173 insertions(+), 104 deletions(-) create mode 100644 src/entity/effect/EffectContainer.php diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php new file mode 100644 index 00000000000..cd731612b9a --- /dev/null +++ b/src/entity/effect/EffectContainer.php @@ -0,0 +1,152 @@ + + */ + protected ObjectSet $effectAddHooks; + /** + * @var \Closure[]|ObjectSet + * @phpstan-var ObjectSet<\Closure(EffectInstance) : void> + */ + protected ObjectSet $effectRemoveHooks; + + public function __construct(){ + $this->effectAddHooks = new ObjectSet(); + $this->effectRemoveHooks = new ObjectSet(); + } + + /** + * Returns an array of Effects currently active. + * @return EffectInstance[] + */ + public function all() : array{ + return $this->effects; + } + + /** + * Removes all effects. + */ + public function clear() : void{ + foreach($this->effects as $effect){ + $this->remove($effect->getType()); + } + } + + /** + * Removes the effect with the specified ID. + */ + public function remove(Effect $effectType) : void{ + $index = spl_object_id($effectType); + if(isset($this->effects[$index])){ + $effect = $this->effects[$index]; + + unset($this->effects[$index]); + foreach($this->effectRemoveHooks as $hook){ + $hook($effect); + } + } + } + + /** + * Returns the effect instance active with the specified ID, or null if does not have the + * effect. + */ + public function get(Effect $effect) : ?EffectInstance{ + return $this->effects[spl_object_id($effect)] ?? null; + } + + /** + * Returns whether the specified effect is active. + */ + public function has(Effect $effect) : bool{ + return isset($this->effects[spl_object_id($effect)]); + } + + /** + * In the following cases it will return true: + * If a weaker effect of the same type is already applied. + * If a weaker or equal-strength effect is already applied but has a shorter duration. + * + * @return bool whether the effect can been applied. + */ + public function canAdd(EffectInstance $effect) : bool{ + $index = spl_object_id($effect->getType()); + if(isset($this->effects[$index])){ + $oldEffect = $this->effects[$index]; + if( + abs($effect->getAmplifier()) < $oldEffect->getAmplifier() + || (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier()) && $effect->getDuration() < $oldEffect->getDuration()) + ){ + return false; + } + } + return true; + } + + /** + * Adds an effect. + * + * @return bool whether the effect has been successfully applied. + */ + public function add(EffectInstance $effect, bool $force = false) : bool{ + if($force || $this->canAdd($effect)){ + $index = spl_object_id($effect->getType()); + foreach($this->effectAddHooks as $hook){ + $hook($effect, isset($this->effects[$index])); + } + + $this->effects[$index] = $effect; + return true; + } + + return false; + } + + /** + * @return \Closure[]|ObjectSet + * @phpstan-return ObjectSet<\Closure(EffectInstance, bool $replacesOldEffect) : void> + */ + public function getEffectAddHooks() : ObjectSet{ + return $this->effectAddHooks; + } + + /** + * @return \Closure[]|ObjectSet + * @phpstan-return ObjectSet<\Closure(EffectInstance) : void> + */ + public function getEffectRemoveHooks() : ObjectSet{ + return $this->effectRemoveHooks; + } +} diff --git a/src/entity/effect/EffectManager.php b/src/entity/effect/EffectManager.php index 07f36c2ff90..808c15dbb43 100644 --- a/src/entity/effect/EffectManager.php +++ b/src/entity/effect/EffectManager.php @@ -24,55 +24,21 @@ namespace pocketmine\entity\effect; use pocketmine\color\Color; -use pocketmine\entity\Entity; +use pocketmine\entity\Living; use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; -use pocketmine\utils\ObjectSet; -use function abs; -use function count; use function spl_object_id; -class EffectManager{ - /** @var EffectInstance[] */ - protected array $effects = []; +class EffectManager extends EffectContainer{ protected Color $bubbleColor; protected bool $onlyAmbientEffects = false; - /** - * @var \Closure[]|ObjectSet - * @phpstan-var ObjectSet<\Closure(EffectInstance, bool $replacesOldEffect) : void> - */ - protected ObjectSet $effectAddHooks; - /** - * @var \Closure[]|ObjectSet - * @phpstan-var ObjectSet<\Closure(EffectInstance) : void> - */ - protected ObjectSet $effectRemoveHooks; - public function __construct( - private Entity $entity + private Living $entity ){ $this->bubbleColor = new Color(0, 0, 0, 0); - $this->effectAddHooks = new ObjectSet(); - $this->effectRemoveHooks = new ObjectSet(); - } - - /** - * Returns an array of Effects currently active on the mob. - * @return EffectInstance[] - */ - public function all() : array{ - return $this->effects; - } - - /** - * Removes all effects from the mob. - */ - public function clear() : void{ - foreach($this->effects as $effect){ - $this->remove($effect->getType()); - } + parent::__construct(); } /** @@ -91,31 +57,13 @@ public function remove(Effect $effectType) : void{ return; } - unset($this->effects[$index]); $effect->getType()->remove($this->entity, $effect); - foreach($this->effectRemoveHooks as $hook){ - $hook($effect); - } + parent::remove($effectType); $this->recalculateEffectColor(); } } - /** - * Returns the effect instance active on this entity with the specified ID, or null if the mob does not have the - * effect. - */ - public function get(Effect $effect) : ?EffectInstance{ - return $this->effects[spl_object_id($effect)] ?? null; - } - - /** - * Returns whether the specified effect is active on the mob. - */ - public function has(Effect $effect) : bool{ - return isset($this->effects[spl_object_id($effect)]); - } - /** * Adds an effect to the mob. * If a weaker effect of the same type is already applied, it will be replaced. @@ -123,28 +71,17 @@ public function has(Effect $effect) : bool{ * * @return bool whether the effect has been successfully applied. */ - public function add(EffectInstance $effect) : bool{ - $oldEffect = null; - $cancelled = false; - + public function add(EffectInstance $effect, bool $force = false) : bool{ $index = spl_object_id($effect->getType()); - if(isset($this->effects[$index])){ - $oldEffect = $this->effects[$index]; - if( - abs($effect->getAmplifier()) < $oldEffect->getAmplifier() - || (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier()) && $effect->getDuration() < $oldEffect->getDuration()) - ){ - $cancelled = true; - } - } + $oldEffect = $this->effects[$index] ?? null; $ev = new EntityEffectAddEvent($this->entity, $effect, $oldEffect); - if($cancelled){ + if(!$this->canAdd($effect)){ $ev->cancel(); } $ev->call(); - if($ev->isCancelled()){ + if(!$force && $ev->isCancelled()){ return false; } @@ -153,11 +90,7 @@ public function add(EffectInstance $effect) : bool{ } $effect->getType()->add($this->entity, $effect); - foreach($this->effectAddHooks as $hook){ - $hook($effect, $oldEffect !== null); - } - - $this->effects[$index] = $effect; + parent::add($effect, true); $this->recalculateEffectColor(); @@ -216,20 +149,4 @@ public function tick(int $tickDiff = 1) : bool{ return count($this->effects) > 0; } - - /** - * @return \Closure[]|ObjectSet - * @phpstan-return ObjectSet<\Closure(EffectInstance, bool $replacesOldEffect) : void> - */ - public function getEffectAddHooks() : ObjectSet{ - return $this->effectAddHooks; - } - - /** - * @return \Closure[]|ObjectSet - * @phpstan-return ObjectSet<\Closure(EffectInstance) : void> - */ - public function getEffectRemoveHooks() : ObjectSet{ - return $this->effectRemoveHooks; - } } diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 98c77b130a9..e4c9d2ada7c 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -27,7 +27,7 @@ use pocketmine\data\bedrock\EffectIdMap; use pocketmine\data\bedrock\PotionTypeIdMap; use pocketmine\entity\effect\EffectInstance; -use pocketmine\entity\effect\EffectManager; +use pocketmine\entity\effect\EffectContainer; use pocketmine\entity\effect\InstantEffect; use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; @@ -53,7 +53,7 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected int $age = 0; protected PotionType $potionType; - protected EffectManager $effectManager; + protected EffectContainer $effectContainer; protected Color $bubbleColor; protected bool $onlyAmbientEffects = false; @@ -84,9 +84,9 @@ protected function getInitialGravity() : float{ return 0.0; } protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); - $this->effectManager = new EffectManager($this); - $this->effectManager->getEffectAddHooks()->add(function() : void{ $this->recalculateEffectColor(); }); - $this->effectManager->getEffectRemoveHooks()->add(function() : void{ $this->recalculateEffectColor(); }); + $this->effectContainer = new EffectContainer(); + $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->recalculateEffectColor(); }); + $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->recalculateEffectColor(); }); $this->age = $nbt->getShort("Age", 0); $this->duration = $nbt->getInt("Duration", 600); @@ -105,7 +105,7 @@ protected function initEntity(CompoundTag $nbt) : void{ continue; } - $this->effectManager->add(new EffectInstance( + $this->effectContainer->add(new EffectInstance( $effect, $e->getInt("Duration"), Binary::unsignByte($e->getByte("Amplifier")), @@ -132,9 +132,9 @@ public function saveNBT() : CompoundTag{ $nbt->setFloat("RadiusOnUse", $this->radiusOnUse); $nbt->setFloat("RadiusPerTick", $this->radiusPerTick); - if(count($this->effectManager->all()) > 0){ + if(count($this->effectContainer->all()) > 0){ $effects = []; - foreach($this->effectManager->all() as $effect){ + foreach($this->effectContainer->all() as $effect){ $effects[] = CompoundTag::create() ->setByte("Id", EffectIdMap::getInstance()->toId($effect->getType())) ->setByte("Amplifier", Binary::signByte($effect->getAmplifier())) @@ -193,8 +193,8 @@ public function setPotionType(PotionType $type) : void{ $this->networkPropertiesDirty = true; } - public function getEffects() : EffectManager{ - return $this->effectManager; + public function getEffects() : EffectContainer{ + return $this->effectContainer; } public function getBubbleColor() : Color{ @@ -333,7 +333,7 @@ public function getCloudEffects() : array{ }, $this->potionType->getEffects()), array_map(function(EffectInstance $effect) : EffectInstance{ return clone $effect; - }, $this->effectManager->all()) + }, $this->effectContainer->all()) ); } From 08de81c15a7ded2b8c500b4d9541c191c230eeb6 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 2 Sep 2022 12:07:04 -0500 Subject: [PATCH 04/47] Cauldron integration --- src/block/Cauldron.php | 3 ++- src/block/WaterCauldron.php | 3 ++- src/block/tile/Cauldron.php | 7 +++---- src/entity/effect/EffectManager.php | 1 + src/entity/object/AreaEffectCloud.php | 2 +- 5 files changed, 9 insertions(+), 7 deletions(-) diff --git a/src/block/Cauldron.php b/src/block/Cauldron.php index da1a938b297..a95ecc82d8f 100644 --- a/src/block/Cauldron.php +++ b/src/block/Cauldron.php @@ -27,6 +27,7 @@ use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; +use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\PotionType; use pocketmine\item\SplashPotion; @@ -82,7 +83,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $this->fill(FillableCauldron::MAX_FILL_LEVEL, VanillaBlocks::LAVA_CAULDRON(), $item, VanillaItems::BUCKET(), $returnedItems); }elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){ //TODO: powder snow cauldron - }elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion + }elseif($item instanceof LingeringPotion || $item instanceof Potion || $item instanceof SplashPotion){ if($item->getType()->equals(PotionType::WATER())){ $this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems); }else{ diff --git a/src/block/WaterCauldron.php b/src/block/WaterCauldron.php index 8b5aa033029..6eca17789bb 100644 --- a/src/block/WaterCauldron.php +++ b/src/block/WaterCauldron.php @@ -33,6 +33,7 @@ use pocketmine\item\Item; use pocketmine\item\ItemBlock; use pocketmine\item\ItemTypeIds; +use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\PotionType; use pocketmine\item\SplashPotion; @@ -123,7 +124,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $this->position->getWorld()->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronAddDyeSound()); $item->pop(); - }elseif($item instanceof Potion || $item instanceof SplashPotion){ //TODO: lingering potion + }elseif($item instanceof LingeringPotion || $item instanceof Potion || $item instanceof SplashPotion){ if($item->getType()->equals(PotionType::WATER())){ $this->setCustomWaterColor(null)->addFillLevels(self::WATER_BOTTLE_FILL_AMOUNT, $item, VanillaItems::GLASS_BOTTLE(), $returnedItems); }else{ diff --git a/src/block/tile/Cauldron.php b/src/block/tile/Cauldron.php index d10f97e1494..ebec88f0dae 100644 --- a/src/block/tile/Cauldron.php +++ b/src/block/tile/Cauldron.php @@ -31,6 +31,7 @@ use pocketmine\data\SavedDataLoadingException; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; +use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\SplashPotion; use pocketmine\item\VanillaItems; @@ -76,8 +77,7 @@ protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ default => throw new AssumptionFailedError("Unexpected potion item type") }); - //TODO: lingering potion - $type = $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; + $type = $this->potionItem instanceof LingeringPotion || $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; $nbt->setShort(self::TAG_POTION_ID, $type === null ? self::POTION_ID_NONE : PotionTypeIdMap::getInstance()->toId($type)); if($this->customWaterColor !== null){ @@ -115,8 +115,7 @@ protected function writeSaveData(CompoundTag $nbt) : void{ default => throw new AssumptionFailedError("Unexpected potion item type") }); - //TODO: lingering potion - $type = $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; + $type = $this->potionItem instanceof LingeringPotion || $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; $nbt->setShort(self::TAG_POTION_ID, $type === null ? self::POTION_ID_NONE : PotionTypeIdMap::getInstance()->toId($type)); if($this->customWaterColor !== null){ diff --git a/src/entity/effect/EffectManager.php b/src/entity/effect/EffectManager.php index 808c15dbb43..7aa24db345e 100644 --- a/src/entity/effect/EffectManager.php +++ b/src/entity/effect/EffectManager.php @@ -27,6 +27,7 @@ use pocketmine\entity\Living; use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; +use function count; use function spl_object_id; class EffectManager extends EffectContainer{ diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index e4c9d2ada7c..2aa241c21b1 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -26,8 +26,8 @@ use pocketmine\color\Color; use pocketmine\data\bedrock\EffectIdMap; use pocketmine\data\bedrock\PotionTypeIdMap; -use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\EffectContainer; +use pocketmine\entity\effect\EffectInstance; use pocketmine\entity\effect\InstantEffect; use pocketmine\entity\Entity; use pocketmine\entity\EntitySizeInfo; From 5ff5256265cbfc6a47fbf50f01e437f5b3334607 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 2 Sep 2022 19:05:39 -0500 Subject: [PATCH 05/47] Constants usage! :D --- src/block/tile/Cauldron.php | 2 +- src/entity/object/AreaEffectCloud.php | 144 ++++++++++++++----------- src/entity/projectile/SplashPotion.php | 2 - 3 files changed, 82 insertions(+), 66 deletions(-) diff --git a/src/block/tile/Cauldron.php b/src/block/tile/Cauldron.php index ebec88f0dae..e4c4612fdd3 100644 --- a/src/block/tile/Cauldron.php +++ b/src/block/tile/Cauldron.php @@ -96,7 +96,7 @@ public function readSaveData(CompoundTag $nbt) : void{ $this->potionItem = match($containerType){ self::POTION_CONTAINER_TYPE_NORMAL => VanillaItems::POTION()->setType($potionType), self::POTION_CONTAINER_TYPE_SPLASH => VanillaItems::SPLASH_POTION()->setType($potionType), - self::POTION_CONTAINER_TYPE_LINGERING => throw new SavedDataLoadingException("Not implemented"), + self::POTION_CONTAINER_TYPE_LINGERING => VanillaItems::LINGERING_POTION()->setType($potionType), default => throw new SavedDataLoadingException("Invalid potion container type ID $containerType") }; }else{ diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 2aa241c21b1..a976f06fe44 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -48,6 +48,26 @@ class AreaEffectCloud extends Entity{ + public const DURATION = 600; + public const DURATION_ON_USE = 0; + + public const WAIT_TIME = 10; + public const REAPPLICATION_DELAY = 20; + + public const RADIUS = 3.0; + public const RADIUS_ON_USE = -0.5; + public const RADIUS_PER_TICK = -0.005; + + public const TAG_POTION_ID = "PotionId"; //TAG_Short + public const TAG_AGE = "Age"; //TAG_Int + public const TAG_DURATION = "Duration"; //TAG_Int + public const TAG_DURATION_ON_USE = "DurationOnUse"; //TAG_Int + public const TAG_REAPPLICATION_DELAY = "ReapplicationDelay"; //TAG_Int + public const TAG_RADIUS = "Radius"; //TAG_Float + public const TAG_RADIUS_ON_USE = "RadiusOnUse"; //TAG_Float + public const TAG_RADIUS_PER_TICK = "RadiusPerTick"; //TAG_Float + public const TAG_EFFECTS = "mobEffects"; //TAG_List + public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFECT_CLOUD; } protected int $age = 0; @@ -61,17 +81,17 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC /** @var array */ protected array $victims = []; - protected int $duration = 600; - protected int $durationOnUse = 0; - protected int $reapplicationDelay = 20; + protected int $duration = self::DURATION; + protected int $durationOnUse = self::DURATION_ON_USE; + protected int $reapplicationDelay = self::REAPPLICATION_DELAY; - protected float $radius = 3.0; - protected float $radiusOnUse = -0.5; + protected float $radius = self::RADIUS; + protected float $radiusOnUse = self::RADIUS_ON_USE; protected float $radiusPerTick = -0.005; public function __construct(Location $location, PotionType $potionType, ?CompoundTag $nbt = null){ $this->potionType = $potionType; - $this->bubbleColor = new Color(0, 0, 0, 0); + $this->bubbleColor = PotionSplashParticle::DEFAULT_COLOR(); parent::__construct($location, $nbt); } @@ -88,16 +108,16 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->recalculateEffectColor(); }); $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->recalculateEffectColor(); }); - $this->age = $nbt->getShort("Age", 0); - $this->duration = $nbt->getInt("Duration", 600); - $this->durationOnUse = $nbt->getInt("DurationOnUse", 0); - $this->reapplicationDelay = $nbt->getInt("ReapplicationDelay", 20); - $this->radius = $nbt->getFloat("Radius", 3.0); - $this->radiusOnUse = $nbt->getFloat("RadiusOnUse", -0.5); - $this->radiusPerTick = $nbt->getFloat("RadiusPerTick", -0.005); + $this->age = $nbt->getInt(self::TAG_AGE, 0); + $this->duration = $nbt->getInt(self::TAG_DURATION, self::DURATION); + $this->durationOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); + $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); + $this->radius = $nbt->getFloat(self::TAG_RADIUS, self::RADIUS); + $this->radiusOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); + $this->radiusPerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); /** @var CompoundTag[]|ListTag|null $effectsTag */ - $effectsTag = $nbt->getListTag("mobEffects"); + $effectsTag = $nbt->getListTag(self::TAG_EFFECTS); if($effectsTag !== null){ foreach($effectsTag as $e){ $effect = EffectIdMap::getInstance()->fromId($e->getByte("Id")); @@ -123,14 +143,14 @@ public function isFireProof() : bool{ public function saveNBT() : CompoundTag{ $nbt = parent::saveNBT(); - $nbt->setShort("Age", $this->age); - $nbt->setShort("PotionId", PotionTypeIdMap::getInstance()->toId($this->potionType)); - $nbt->setInt("Duration", $this->duration); - $nbt->setInt("DurationOnUse", $this->durationOnUse); - $nbt->setInt("ReapplicationDelay", $this->reapplicationDelay); - $nbt->setFloat("Radius", $this->radius); - $nbt->setFloat("RadiusOnUse", $this->radiusOnUse); - $nbt->setFloat("RadiusPerTick", $this->radiusPerTick); + $nbt->setInt(self::TAG_AGE, $this->age); + $nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->potionType)); + $nbt->setInt(self::TAG_DURATION, $this->duration); + $nbt->setInt(self::TAG_DURATION_ON_USE, $this->durationOnUse); + $nbt->setInt(self::TAG_REAPPLICATION_DELAY, $this->reapplicationDelay); + $nbt->setFloat(self::TAG_RADIUS, $this->radius); + $nbt->setFloat(self::TAG_RADIUS_ON_USE, $this->radiusOnUse); + $nbt->setFloat(self::self::TAG_RADIUS_PER_TICK, $this->radiusPerTick); if(count($this->effectContainer->all()) > 0){ $effects = []; @@ -142,7 +162,7 @@ public function saveNBT() : CompoundTag{ ->setByte("Ambient", $effect->isAmbient() ? 1 : 0) ->setByte("ShowParticles", $effect->isVisible() ? 1 : 0); } - $nbt->setTag("mobEffects", new ListTag($effects)); + $nbt->setTag(self::TAG_EFFECTS, new ListTag($effects)); } return $nbt; @@ -268,52 +288,50 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $this->flagForDespawn(); return true; } - if($this->age > 10){ - //Area effect clouds only trigger updates every ten ticks. - if($this->age % 10 === 0){ - $this->setRadius($this->radius + ($this->radiusPerTick * $tickDiff)); - if($this->radius < 0.5){ - $this->flagForDespawn(); - return true; + //Area effect clouds only trigger updates every ten ticks. + if($this->age >= self::WAIT_TIME && $this->age % self::WAIT_TIME === 0){ + $this->setRadius($this->radius + ($this->radiusPerTick * $tickDiff)); + if($this->radius < 0.5){ + $this->flagForDespawn(); + return true; + } + foreach($this->victims as $entityId => $expiration){ + if($this->age >= $expiration){ + unset($this->victims[$entityId]); } - foreach($this->victims as $entityId => $expiration){ - if($this->age >= $expiration){ - unset($this->victims[$entityId]); - } + } + foreach($this->getWorld()->getNearbyEntities($this->getBoundingBox(), $this) as $entity){ + if(!$entity instanceof Living || !$entity->isAlive() || isset($this->victims[$entity->getId()])){ + continue; } - foreach($this->getWorld()->getNearbyEntities($this->getBoundingBox(), $this) as $entity){ - if(!$entity instanceof Living || !$entity->isAlive() || isset($this->victims[$entity->getId()])){ - continue; - } - $entityPosition = $entity->getPosition(); - $xDiff = $entityPosition->getX() - $this->location->getX(); - $zDiff = $entityPosition->getZ() - $this->location->getZ(); - if(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2){ - continue; - } + $entityPosition = $entity->getPosition(); + $xDiff = $entityPosition->getX() - $this->location->getX(); + $zDiff = $entityPosition->getZ() - $this->location->getZ(); + if(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2){ + continue; + } - foreach($this->getCloudEffects() as $effect){ - if($effect->getType() instanceof InstantEffect){ - $effect->getType()->applyEffect($entity, $effect, 0.5, $this); - }else{ - $entity->getEffects()->add($effect); - } + foreach($this->getCloudEffects() as $effect){ + if($effect->getType() instanceof InstantEffect){ + $effect->getType()->applyEffect($entity, $effect, 0.5, $this); + }else{ + $entity->getEffects()->add($effect); } - $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; - if($this->radiusOnUse !== 0.0){ - $this->setRadius($this->radius + $this->radiusOnUse); - if($this->radius <= 0){ - $this->flagForDespawn(); - return true; - } + } + $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; + if($this->radiusOnUse !== 0.0){ + $this->setRadius($this->radius + $this->radiusOnUse); + if($this->radius <= 0){ + $this->flagForDespawn(); + return true; } - if($this->durationOnUse !== 0){ - $this->setDuration($this->duration + $this->durationOnUse); - if($this->duration <= 0){ - $this->flagForDespawn(); - return true; - } + } + if($this->durationOnUse !== 0){ + $this->setDuration($this->duration + $this->durationOnUse); + if($this->duration <= 0){ + $this->flagForDespawn(); + return true; } } } diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index 2f43eaa42ef..fa3c29e0f7c 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -128,8 +128,6 @@ protected function onHit(ProjectileHitEvent $event) : void{ } }else{ $entity = new AreaEffectCloud($this->location, $this->potionType); - $entity->setRadius(3.0); - $entity->setRadiusOnUse(-0.5); $entity->setRadiusPerTick(-$entity->getRadius() / $entity->getDuration()); $entity->spawnToAll(); } From 52500787b64605e605d6d16f75c1163ef3554260 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Fri, 2 Sep 2022 22:49:28 -0500 Subject: [PATCH 06/47] Vanilla parity --- src/entity/object/AreaEffectCloud.php | 21 +++++++++++++-------- src/entity/projectile/SplashPotion.php | 2 +- 2 files changed, 14 insertions(+), 9 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index a976f06fe44..39b34a92a13 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -52,7 +52,7 @@ class AreaEffectCloud extends Entity{ public const DURATION_ON_USE = 0; public const WAIT_TIME = 10; - public const REAPPLICATION_DELAY = 20; + public const REAPPLICATION_DELAY = 0; public const RADIUS = 3.0; public const RADIUS_ON_USE = -0.5; @@ -150,7 +150,7 @@ public function saveNBT() : CompoundTag{ $nbt->setInt(self::TAG_REAPPLICATION_DELAY, $this->reapplicationDelay); $nbt->setFloat(self::TAG_RADIUS, $this->radius); $nbt->setFloat(self::TAG_RADIUS_ON_USE, $this->radiusOnUse); - $nbt->setFloat(self::self::TAG_RADIUS_PER_TICK, $this->radiusPerTick); + $nbt->setFloat(self::TAG_RADIUS_PER_TICK, $this->radiusPerTick); if(count($this->effectContainer->all()) > 0){ $effects = []; @@ -290,11 +290,12 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ } //Area effect clouds only trigger updates every ten ticks. if($this->age >= self::WAIT_TIME && $this->age % self::WAIT_TIME === 0){ - $this->setRadius($this->radius + ($this->radiusPerTick * $tickDiff)); + $radius = $this->radius + ($this->radiusPerTick * $tickDiff); if($this->radius < 0.5){ $this->flagForDespawn(); return true; } + $this->setRadius($radius); foreach($this->victims as $entityId => $expiration){ if($this->age >= $expiration){ unset($this->victims[$entityId]); @@ -319,20 +320,24 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $entity->getEffects()->add($effect); } } - $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; + if($this->reapplicationDelay !== 0){ + $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; + } if($this->radiusOnUse !== 0.0){ - $this->setRadius($this->radius + $this->radiusOnUse); - if($this->radius <= 0){ + $radius = $this->radius + $this->radiusOnUse; + if($radius <= 0){ $this->flagForDespawn(); return true; } + $this->setRadius($radius); } if($this->durationOnUse !== 0){ - $this->setDuration($this->duration + $this->durationOnUse); - if($this->duration <= 0){ + $duration = $this->duration + $this->durationOnUse; + if($duration <= 0){ $this->flagForDespawn(); return true; } + $this->setDuration($duration); } } } diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index fa3c29e0f7c..44fb8fd1f58 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -127,7 +127,7 @@ protected function onHit(ProjectileHitEvent $event) : void{ } } }else{ - $entity = new AreaEffectCloud($this->location, $this->potionType); + $entity = new AreaEffectCloud(Location::fromObject($this->location->floor()->add(0.5, 0.5, 0.5), $this->getWorld()), $this->potionType); $entity->setRadiusPerTick(-$entity->getRadius() / $entity->getDuration()); $entity->spawnToAll(); } From ffe7158eb57831e512ce0475f51725f4168e2102 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 3 Sep 2022 18:56:33 -0500 Subject: [PATCH 07/47] More vanilla parity! --- src/entity/effect/EffectContainer.php | 7 ++++--- src/entity/object/AreaEffectCloud.php | 24 ++++++++---------------- src/entity/projectile/SplashPotion.php | 9 +++++++++ 3 files changed, 21 insertions(+), 19 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index cd731612b9a..f07a9f63609 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -123,11 +123,12 @@ public function canAdd(EffectInstance $effect) : bool{ public function add(EffectInstance $effect, bool $force = false) : bool{ if($force || $this->canAdd($effect)){ $index = spl_object_id($effect->getType()); - foreach($this->effectAddHooks as $hook){ - $hook($effect, isset($this->effects[$index])); - } + $replacesOldEffect = isset($this->effects[$index]); $this->effects[$index] = $effect; + foreach($this->effectAddHooks as $hook){ + $hook($effect, $replacesOldEffect); + } return true; } diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 39b34a92a13..8104e1c6127 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -42,7 +42,6 @@ use pocketmine\utils\Binary; use pocketmine\world\particle\PotionSplashParticle; use function array_map; -use function array_merge; use function count; use function round; @@ -52,7 +51,7 @@ class AreaEffectCloud extends Entity{ public const DURATION_ON_USE = 0; public const WAIT_TIME = 10; - public const REAPPLICATION_DELAY = 0; + public const REAPPLICATION_DELAY = 40; public const RADIUS = 3.0; public const RADIUS_ON_USE = -0.5; @@ -207,12 +206,6 @@ public function getPotionType() : PotionType{ return $this->potionType; } - public function setPotionType(PotionType $type) : void{ - $this->potionType = $type; - $this->recalculateEffectColor(); - $this->networkPropertiesDirty = true; - } - public function getEffects() : EffectContainer{ return $this->effectContainer; } @@ -317,7 +310,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ if($effect->getType() instanceof InstantEffect){ $effect->getType()->applyEffect($entity, $effect, 0.5, $this); }else{ - $entity->getEffects()->add($effect); + $entity->getEffects()->add($effect->setDuration((int) round($effect->getDuration() / 4))); } } if($this->reapplicationDelay !== 0){ @@ -348,16 +341,15 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ /** * Returns the effects the area effect cloud provides. * + * Used to get COPIES to avoid accidentally modifying the same effect instance + * already applied to another entity. + * * @return EffectInstance[] */ public function getCloudEffects() : array{ - return array_merge(array_map(function(EffectInstance $effect) : EffectInstance{ - return $effect->getType() instanceof InstantEffect ? $effect : $effect->setDuration((int) round($effect->getDuration() / 4)); - }, $this->potionType->getEffects()), - array_map(function(EffectInstance $effect) : EffectInstance{ - return clone $effect; - }, $this->effectContainer->all()) - ); + return array_map(function(EffectInstance $effect) : EffectInstance{ + return clone $effect; + }, $this->effectContainer->all()); } protected function syncNetworkData(EntityMetadataCollection $properties) : void{ diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index 44fb8fd1f58..8c2f0fb6f64 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -129,6 +129,15 @@ protected function onHit(ProjectileHitEvent $event) : void{ }else{ $entity = new AreaEffectCloud(Location::fromObject($this->location->floor()->add(0.5, 0.5, 0.5), $this->getWorld()), $this->potionType); $entity->setRadiusPerTick(-$entity->getRadius() / $entity->getDuration()); + if(($owner = $this->getOwningEntity()) !== null && !$owner->isClosed()){ + $entity->setOwningEntity($owner); + } + foreach($this->getPotionEffects() as $effect){ + $entity->getEffects()->add($effect); + if($effect->getType() instanceof InstantEffect){ + $entity->setReapplicationDelay(0); + } + } $entity->spawnToAll(); } if(!$hasEffects && $event instanceof ProjectileHitBlockEvent && $this->getPotionType()->equals(PotionType::WATER())){ From deeeeb6e91a90c1d88fe5651cb78fe63c4593a00 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 5 Sep 2022 21:08:41 -0500 Subject: [PATCH 08/47] Match vanilla properties --- src/entity/object/AreaEffectCloud.php | 40 ++++++++++++++++++++------- 1 file changed, 30 insertions(+), 10 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 8104e1c6127..5a3d3e15dda 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -58,7 +58,7 @@ class AreaEffectCloud extends Entity{ public const RADIUS_PER_TICK = -0.005; public const TAG_POTION_ID = "PotionId"; //TAG_Short - public const TAG_AGE = "Age"; //TAG_Int + public const TAG_SPAWN_TICK = "SpawnTick"; //TAG_Long public const TAG_DURATION = "Duration"; //TAG_Int public const TAG_DURATION_ON_USE = "DurationOnUse"; //TAG_Int public const TAG_REAPPLICATION_DELAY = "ReapplicationDelay"; //TAG_Int @@ -84,6 +84,8 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected int $durationOnUse = self::DURATION_ON_USE; protected int $reapplicationDelay = self::REAPPLICATION_DELAY; + protected int $waiting = self::WAIT_TIME; + protected float $radius = self::RADIUS; protected float $radiusOnUse = self::RADIUS_ON_USE; protected float $radiusPerTick = -0.005; @@ -107,7 +109,8 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->recalculateEffectColor(); }); $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->recalculateEffectColor(); }); - $this->age = $nbt->getInt(self::TAG_AGE, 0); + $worldTime = $this->getWorld()->getTime(); + $this->age = $worldTime - $nbt->getLong(self::TAG_SPAWN_TICK, $worldTime); $this->duration = $nbt->getInt(self::TAG_DURATION, self::DURATION); $this->durationOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); @@ -136,13 +139,10 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->recalculateEffectColor(); } - public function isFireProof() : bool{ - return true; - } public function saveNBT() : CompoundTag{ $nbt = parent::saveNBT(); - $nbt->setInt(self::TAG_AGE, $this->age); + $nbt->setLong(self::TAG_SPAWN_TICK, $this->getWorld()->getTime() - $this->age); $nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->potionType)); $nbt->setInt(self::TAG_DURATION, $this->duration); $nbt->setInt(self::TAG_DURATION_ON_USE, $this->durationOnUse); @@ -167,6 +167,14 @@ public function saveNBT() : CompoundTag{ return $nbt; } + public function isFireProof() : bool{ + return true; + } + + public function canBeCollidedWith() : bool{ + return false; + } + /** * Recalculates the area entity's potion bubbles colour based on the active effects. */ @@ -246,6 +254,15 @@ public function setRadiusPerTick(float $radiusPerTick) : void{ $this->networkPropertiesDirty = true; } + public function getWaiting() : int{ + return $this->waiting; + } + + public function setWaiting(int $time) : void{ + $this->waiting = $time; + $this->networkPropertiesDirty = true; + } + public function getDuration() : int{ return $this->duration; } @@ -281,8 +298,9 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $this->flagForDespawn(); return true; } - //Area effect clouds only trigger updates every ten ticks. - if($this->age >= self::WAIT_TIME && $this->age % self::WAIT_TIME === 0){ + $waiting = $this->waiting - $tickDiff; + if($waiting <= 0){ + $waiting = self::WAIT_TIME; $radius = $this->radius + ($this->radiusPerTick * $tickDiff); if($this->radius < 0.5){ $this->flagForDespawn(); @@ -294,8 +312,8 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ unset($this->victims[$entityId]); } } - foreach($this->getWorld()->getNearbyEntities($this->getBoundingBox(), $this) as $entity){ - if(!$entity instanceof Living || !$entity->isAlive() || isset($this->victims[$entity->getId()])){ + foreach($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this) as $entity){ + if(!$entity instanceof Living || isset($this->victims[$entity->getId()])){ continue; } @@ -334,6 +352,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ } } } + $this->setWaiting($waiting); return $hasUpdate; } @@ -359,6 +378,7 @@ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->radius); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, $this->radiusPerTick); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, $this->radiusOnUse); + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $this->waiting); $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->bubbleColor->toARGB())); $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->onlyAmbientEffects ? 1 : 0); } From cb6cf6a951c011d96893eb19e9927a8aea4c0138 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Tue, 6 Sep 2022 15:56:55 -0500 Subject: [PATCH 09/47] Use RADIUS_PER_TICK const --- src/entity/object/AreaEffectCloud.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 5a3d3e15dda..2ddca814368 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -88,7 +88,7 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected float $radius = self::RADIUS; protected float $radiusOnUse = self::RADIUS_ON_USE; - protected float $radiusPerTick = -0.005; + protected float $radiusPerTick = self::RADIUS_PER_TICK; public function __construct(Location $location, PotionType $potionType, ?CompoundTag $nbt = null){ $this->potionType = $potionType; From 2ce169d0ab904ac3120f16b40de3312e25f83b92 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 19 Sep 2022 22:09:47 -0500 Subject: [PATCH 10/47] A lot of changes - Move bubble color calculation to EffectContainer - Implement API for effect validator for bubbles - Implement API for default bubbles color - Match more AreaEffectCloud properties with vanilla --- src/entity/effect/EffectContainer.php | 78 ++++++++++++++++ src/entity/effect/EffectManager.php | 50 +---------- src/entity/object/AreaEffectCloud.php | 119 +++++++++++++------------ src/entity/projectile/SplashPotion.php | 2 +- 4 files changed, 140 insertions(+), 109 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index f07a9f63609..445105b4c8e 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -23,11 +23,19 @@ namespace pocketmine\entity\effect; +use DaveRandom\CallbackValidator\BuiltInTypes; +use DaveRandom\CallbackValidator\CallbackType; +use DaveRandom\CallbackValidator\ParameterType; +use DaveRandom\CallbackValidator\ReturnType; +use pocketmine\color\Color; use pocketmine\utils\ObjectSet; +use pocketmine\utils\Utils; use function abs; +use function count; use function spl_object_id; class EffectContainer{ + /** @var EffectInstance[] */ protected array $effects = []; @@ -42,9 +50,24 @@ class EffectContainer{ */ protected ObjectSet $effectRemoveHooks; + protected Color $defaultBubbleColor; + + protected ?Color $bubbleColor = null; + + /** @phpstan-var \Closure(EffectInstance) : bool */ + protected bool $onlyAmbientEffects = false; + + /** Validates whether an effect will be used for bubbles color calculation. */ + protected \Closure $effectValidatorForBubbles; + public function __construct(){ + $this->defaultBubbleColor = new Color(0, 0, 0, 0); $this->effectAddHooks = new ObjectSet(); $this->effectRemoveHooks = new ObjectSet(); + + $this->setEffectValidatorForBubbles(function(EffectInstance $effect) : bool{ + return $effect->isVisible() && $effect->getType()->hasBubbles(); + }); } /** @@ -76,6 +99,8 @@ public function remove(Effect $effectType) : void{ foreach($this->effectRemoveHooks as $hook){ $hook($effect); } + + $this->recalculateEffectColor(); } } @@ -129,12 +154,65 @@ public function add(EffectInstance $effect, bool $force = false) : bool{ foreach($this->effectAddHooks as $hook){ $hook($effect, $replacesOldEffect); } + + $this->recalculateEffectColor(); return true; } return false; } + public function setEffectValidatorForBubbles(\Closure $closure) : void{ + Utils::validateCallableSignature(new CallbackType(new ReturnType(BuiltInTypes::BOOL), new ParameterType("effect", EffectInstance::class)), $closure); + $this->effectValidatorForBubbles = $closure; + } + + /** + * Recalculates the potion bubbles colour based on the active effects. + */ + protected function recalculateEffectColor() : void{ + /** @var Color[] $colors */ + $colors = []; + $ambient = true; + foreach($this->effects as $effect){ + if(($this->effectValidatorForBubbles)($effect)){ + $level = $effect->getEffectLevel(); + $color = $effect->getColor(); + for($i = 0; $i < $level; ++$i){ + $colors[] = $color; + } + + if(!$effect->isAmbient()){ + $ambient = false; + } + } + } + + if(count($colors) > 0){ + $this->bubbleColor = Color::mix(...$colors); + $this->onlyAmbientEffects = $ambient; + }else{ + $this->bubbleColor = null; + $this->onlyAmbientEffects = false; + } + } + + public function getDefaultBubbleColor() : Color{ + return $this->defaultBubbleColor = $color; + } + + public function setDefaultBubbleColor(Color $color) : void{ + $this->defaultBubbleColor = $color; + } + + public function getBubbleColor() : Color{ + return $this->bubbleColor ?? $this->defaultBubbleColor; + } + + public function hasOnlyAmbientEffects() : bool{ + return $this->onlyAmbientEffects; + } + /** * @return \Closure[]|ObjectSet * @phpstan-return ObjectSet<\Closure(EffectInstance, bool $replacesOldEffect) : void> diff --git a/src/entity/effect/EffectManager.php b/src/entity/effect/EffectManager.php index 7aa24db345e..4cdefb3243a 100644 --- a/src/entity/effect/EffectManager.php +++ b/src/entity/effect/EffectManager.php @@ -23,7 +23,6 @@ namespace pocketmine\entity\effect; -use pocketmine\color\Color; use pocketmine\entity\Living; use pocketmine\event\entity\EntityEffectAddEvent; use pocketmine\event\entity\EntityEffectRemoveEvent; @@ -32,13 +31,9 @@ class EffectManager extends EffectContainer{ - protected Color $bubbleColor; - protected bool $onlyAmbientEffects = false; - public function __construct( private Living $entity ){ - $this->bubbleColor = new Color(0, 0, 0, 0); parent::__construct(); } @@ -60,8 +55,6 @@ public function remove(Effect $effectType) : void{ $effect->getType()->remove($this->entity, $effect); parent::remove($effectType); - - $this->recalculateEffectColor(); } } @@ -91,49 +84,8 @@ public function add(EffectInstance $effect, bool $force = false) : bool{ } $effect->getType()->add($this->entity, $effect); - parent::add($effect, true); - - $this->recalculateEffectColor(); - - return true; - } - - /** - * Recalculates the mob's potion bubbles colour based on the active effects. - */ - protected function recalculateEffectColor() : void{ - /** @var Color[] $colors */ - $colors = []; - $ambient = true; - foreach($this->effects as $effect){ - if($effect->isVisible() && $effect->getType()->hasBubbles()){ - $level = $effect->getEffectLevel(); - $color = $effect->getColor(); - for($i = 0; $i < $level; ++$i){ - $colors[] = $color; - } - - if(!$effect->isAmbient()){ - $ambient = false; - } - } - } - - if(count($colors) > 0){ - $this->bubbleColor = Color::mix(...$colors); - $this->onlyAmbientEffects = $ambient; - }else{ - $this->bubbleColor = new Color(0, 0, 0, 0); - $this->onlyAmbientEffects = false; - } - } - - public function getBubbleColor() : Color{ - return $this->bubbleColor; - } - public function hasOnlyAmbientEffects() : bool{ - return $this->onlyAmbientEffects; + return parent::add($effect, true); } public function tick(int $tickDiff = 1) : bool{ diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 2ddca814368..a8a0edeb5af 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -23,7 +23,6 @@ namespace pocketmine\entity\object; -use pocketmine\color\Color; use pocketmine\data\bedrock\EffectIdMap; use pocketmine\data\bedrock\PotionTypeIdMap; use pocketmine\entity\effect\EffectContainer; @@ -54,15 +53,19 @@ class AreaEffectCloud extends Entity{ public const REAPPLICATION_DELAY = 40; public const RADIUS = 3.0; + public const RADIUS_CHANGE_ON_PICKUP = -0.5; public const RADIUS_ON_USE = -0.5; public const RADIUS_PER_TICK = -0.005; public const TAG_POTION_ID = "PotionId"; //TAG_Short public const TAG_SPAWN_TICK = "SpawnTick"; //TAG_Long public const TAG_DURATION = "Duration"; //TAG_Int + public const TAG_PICKUP_COUNT = "PickupCount"; //TAG_Int public const TAG_DURATION_ON_USE = "DurationOnUse"; //TAG_Int public const TAG_REAPPLICATION_DELAY = "ReapplicationDelay"; //TAG_Int + public const TAG_INITIAL_RADIUS = "InitialRadius"; //TAG_Float public const TAG_RADIUS = "Radius"; //TAG_Float + public const TAG_RADIUS_CHANGE_ON_PICKUP = "RadiusChangeOnPickup"; //TAG_Float public const TAG_RADIUS_ON_USE = "RadiusOnUse"; //TAG_Float public const TAG_RADIUS_PER_TICK = "RadiusPerTick"; //TAG_Float public const TAG_EFFECTS = "mobEffects"; //TAG_List @@ -74,25 +77,28 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected PotionType $potionType; protected EffectContainer $effectContainer; - protected Color $bubbleColor; - protected bool $onlyAmbientEffects = false; - - /** @var array */ + /** + * @var array entity ID => expiration + */ protected array $victims = []; protected int $duration = self::DURATION; protected int $durationOnUse = self::DURATION_ON_USE; protected int $reapplicationDelay = self::REAPPLICATION_DELAY; + protected int $pickupCount = 0; + + /** The entity will not do any update until its age reaches this value. */ protected int $waiting = self::WAIT_TIME; + protected float $initialRadius = self::RADIUS; protected float $radius = self::RADIUS; + protected float $radiusChangeOnPickup = self::RADIUS_CHANGE_ON_PICKUP; protected float $radiusOnUse = self::RADIUS_ON_USE; protected float $radiusPerTick = self::RADIUS_PER_TICK; public function __construct(Location $location, PotionType $potionType, ?CompoundTag $nbt = null){ $this->potionType = $potionType; - $this->bubbleColor = PotionSplashParticle::DEFAULT_COLOR(); parent::__construct($location, $nbt); } @@ -106,15 +112,22 @@ protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); $this->effectContainer = new EffectContainer(); - $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->recalculateEffectColor(); }); - $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->recalculateEffectColor(); }); + $this->effectContainer->setDefaultBubbleColor(PotionSplashParticle::DEFAULT_COLOR()); + $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->networkPropertiesDirty = true; }); + $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->networkPropertiesDirty = true; }); + $this->effectContainer->setEffectValidatorForBubbles(function(EffectInstance $effect) : bool{ + return $effect->isVisible(); + }); $worldTime = $this->getWorld()->getTime(); $this->age = $worldTime - $nbt->getLong(self::TAG_SPAWN_TICK, $worldTime); $this->duration = $nbt->getInt(self::TAG_DURATION, self::DURATION); $this->durationOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); + $this->pickupCount = $nbt->getInt(self::TAG_PICKUP_COUNT, 0); $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); + $this->initialRadius = $nbt->getFloat(self::TAG_INITIAL_RADIUS, self::RADIUS); $this->radius = $nbt->getFloat(self::TAG_RADIUS, self::RADIUS); + $this->radiusChangeOnPickup = $nbt->getFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, self::RADIUS_CHANGE_ON_PICKUP); $this->radiusOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); $this->radiusPerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); @@ -136,7 +149,6 @@ protected function initEntity(CompoundTag $nbt) : void{ )); } } - $this->recalculateEffectColor(); } public function saveNBT() : CompoundTag{ @@ -146,8 +158,11 @@ public function saveNBT() : CompoundTag{ $nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->potionType)); $nbt->setInt(self::TAG_DURATION, $this->duration); $nbt->setInt(self::TAG_DURATION_ON_USE, $this->durationOnUse); + $nbt->setInt(self::TAG_PICKUP_COUNT, $this->pickupCount); $nbt->setInt(self::TAG_REAPPLICATION_DELAY, $this->reapplicationDelay); + $nbt->setFloat(self::TAG_INITIAL_RADIUS, $this->initialRadius); $nbt->setFloat(self::TAG_RADIUS, $this->radius); + $nbt->setFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, $this->radiusChangeOnPickup); $nbt->setFloat(self::TAG_RADIUS_ON_USE, $this->radiusOnUse); $nbt->setFloat(self::TAG_RADIUS_PER_TICK, $this->radiusPerTick); @@ -175,37 +190,6 @@ public function canBeCollidedWith() : bool{ return false; } - /** - * Recalculates the area entity's potion bubbles colour based on the active effects. - */ - protected function recalculateEffectColor() : void{ - /** @var Color[] $colors */ - $colors = []; - $ambient = true; - foreach($this->getCloudEffects() as $effect){ - if($effect->isVisible()){ - $level = $effect->getEffectLevel(); - $color = $effect->getColor(); - for($i = 0; $i < $level; ++$i){ - $colors[] = $color; - } - - if(!$effect->isAmbient()){ - $ambient = false; - } - } - } - - if(count($colors) > 0){ - $this->bubbleColor = Color::mix(...$colors); - $this->onlyAmbientEffects = $ambient; - }else{ - $this->bubbleColor = PotionSplashParticle::DEFAULT_COLOR(); - $this->onlyAmbientEffects = false; - } - $this->networkPropertiesDirty = true; - } - public function getAge() : int{ return $this->age; } @@ -218,22 +202,37 @@ public function getEffects() : EffectContainer{ return $this->effectContainer; } - public function getBubbleColor() : Color{ - return $this->bubbleColor; + public function getInitialRadius() : float{ + return $this->initialRadius; } - public function hasOnlyAmbientEffects() : bool{ - return $this->onlyAmbientEffects; + /** + * Sets the initial radius. + * + * @throws \RuntimeException if something attempted to set when it is already ticking + */ + public function setInitialRadius(float $radius) : void{ + if($this->age > 0){ + throw new \RuntimeException("Trying to set radius after initialization"); + } + $this->initialRadius = $radius; + $this->setRadius($radius); + $this->networkPropertiesDirty = true; } + /** + * Gets the current radius. + */ public function getRadius() : float{ return $this->radius; } - public function setRadius(float $radius) : void{ + /** + * Sets the radius only server-side, client calculates the radius by himself + */ + protected function setRadius(float $radius) : void{ $this->radius = $radius; $this->setSize($this->getInitialSizeInfo()); - $this->networkPropertiesDirty = true; } public function getRadiusOnUse() : float{ @@ -294,19 +293,17 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $hasUpdate = parent::entityBaseTick($tickDiff); $this->age += $tickDiff; - if($this->age > $this->duration){ + $radius = $this->radius + ($this->radiusPerTick * $tickDiff); + if($radius < 0.75){ $this->flagForDespawn(); return true; } - $waiting = $this->waiting - $tickDiff; - if($waiting <= 0){ - $waiting = self::WAIT_TIME; - $radius = $this->radius + ($this->radiusPerTick * $tickDiff); - if($this->radius < 0.5){ + $this->setRadius($radius); + if($this->age >= $this->waiting){ + if($this->age > $this->duration){ $this->flagForDespawn(); return true; } - $this->setRadius($radius); foreach($this->victims as $entityId => $expiration){ if($this->age >= $expiration){ unset($this->victims[$entityId]); @@ -351,8 +348,9 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $this->setDuration($duration); } } + $this->setWaiting($this->age + self::WAIT_TIME); + $hasUpdate = true; } - $this->setWaiting($waiting); return $hasUpdate; } @@ -374,12 +372,15 @@ public function getCloudEffects() : array{ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); + $spawnTime = $this->getWorld()->getTime() - $this->age; + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_SPAWN_TIME, $spawnTime); $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_DURATION, $this->duration); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->radius); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->initialRadius); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, $this->radiusPerTick); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, $this->radiusOnUse); - $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $this->waiting); - $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->bubbleColor->toARGB())); - $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->onlyAmbientEffects ? 1 : 0); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, $this->radiusChangeOnPickup); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_PICKUP_COUNT, $this->pickupCount); + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $spawnTime + $this->waiting); + $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->effectContainer->getBubbleColor()->toARGB())); + $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectContainer->hasOnlyAmbientEffects() ? 1 : 0); } } diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index 8c2f0fb6f64..1a1b704e3a8 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -128,7 +128,7 @@ protected function onHit(ProjectileHitEvent $event) : void{ } }else{ $entity = new AreaEffectCloud(Location::fromObject($this->location->floor()->add(0.5, 0.5, 0.5), $this->getWorld()), $this->potionType); - $entity->setRadiusPerTick(-$entity->getRadius() / $entity->getDuration()); + $entity->setRadiusPerTick(-$entity->getInitialRadius() / $entity->getDuration()); if(($owner = $this->getOwningEntity()) !== null && !$owner->isClosed()){ $entity->setOwningEntity($owner); } From f4795484b29ef2e58a2aae9dfb41e954c9051c82 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 19 Sep 2022 22:20:01 -0500 Subject: [PATCH 11/47] shut --- src/entity/effect/EffectContainer.php | 10 +++++++--- src/entity/object/AreaEffectCloud.php | 4 +--- 2 files changed, 8 insertions(+), 6 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index 445105b4c8e..d94499c8a63 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -44,6 +44,7 @@ class EffectContainer{ * @phpstan-var ObjectSet<\Closure(EffectInstance, bool $replacesOldEffect) : void> */ protected ObjectSet $effectAddHooks; + /** * @var \Closure[]|ObjectSet * @phpstan-var ObjectSet<\Closure(EffectInstance) : void> @@ -54,10 +55,13 @@ class EffectContainer{ protected ?Color $bubbleColor = null; - /** @phpstan-var \Closure(EffectInstance) : bool */ protected bool $onlyAmbientEffects = false; - /** Validates whether an effect will be used for bubbles color calculation. */ + /** + * Validates whether an effect will be used for bubbles color calculation. + * + * @phpstan-var \Closure(EffectInstance) : bool + */ protected \Closure $effectValidatorForBubbles; public function __construct(){ @@ -198,7 +202,7 @@ protected function recalculateEffectColor() : void{ } public function getDefaultBubbleColor() : Color{ - return $this->defaultBubbleColor = $color; + return $this->defaultBubbleColor; } public function setDefaultBubbleColor(Color $color) : void{ diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index a8a0edeb5af..96dceacfb7b 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -77,9 +77,7 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected PotionType $potionType; protected EffectContainer $effectContainer; - /** - * @var array entity ID => expiration - */ + /** @var array entity ID => expiration */ protected array $victims = []; protected int $duration = self::DURATION; From c030d77252bd8d7582ab4357d73d0fbbc8be3c22 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 19 Sep 2022 22:31:31 -0500 Subject: [PATCH 12/47] Shut! --- src/entity/effect/EffectContainer.php | 9 ++++++--- 1 file changed, 6 insertions(+), 3 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index d94499c8a63..ef5099aaf7c 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -166,9 +166,12 @@ public function add(EffectInstance $effect, bool $force = false) : bool{ return false; } - public function setEffectValidatorForBubbles(\Closure $closure) : void{ - Utils::validateCallableSignature(new CallbackType(new ReturnType(BuiltInTypes::BOOL), new ParameterType("effect", EffectInstance::class)), $closure); - $this->effectValidatorForBubbles = $closure; + /** + * @phpstan-param \Closure(EffectInstance) : bool $effectValidator + */ + public function setEffectValidatorForBubbles(\Closure $effectValidator) : void{ + Utils::validateCallableSignature(new CallbackType(new ReturnType(BuiltInTypes::BOOL), new ParameterType("effect", EffectInstance::class)), $effectValidator); + $this->effectValidatorForBubbles = $effectValidator; } /** From 3bbc9c2d5c04fc4e32e68e4569829537de1f60b2 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 8 Oct 2022 11:58:50 -0500 Subject: [PATCH 13/47] Move initial radius to constructor --- src/entity/EntityFactory.php | 9 ++++++-- src/entity/object/AreaEffectCloud.php | 31 ++++++++------------------- 2 files changed, 16 insertions(+), 24 deletions(-) diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index adc2d354f28..f5eea8753f5 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -83,11 +83,16 @@ public function __construct(){ //TODO: index them by version to allow proper multi-save compatibility $this->register(AreaEffectCloud::class, function(World $world, CompoundTag $nbt) : AreaEffectCloud{ - $potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort("PotionId", PotionTypeIds::WATER)); + $potionType = PotionTypeIdMap::getInstance()->fromId($nbt->getShort(AreaEffectCloud::TAG_POTION_ID, PotionTypeIds::WATER)); if($potionType === null){ throw new SavedDataLoadingException("No such potion type"); } - return new AreaEffectCloud(Helper::parseLocation($nbt, $world), $potionType, $nbt); + return new AreaEffectCloud( + Helper::parseLocation($nbt, $world), + $potionType, + $nbt->getFloatTag(AreaEffectCloud::TAG_INITIAL_RADIUS, AreaEffectCloud::DEFAULT_RADIUS), + $nbt + ); }, ['AreaEffectCloud', 'minecraft:area_effect_cloud'], LegacyIds::AREA_EFFECT_CLOUD); $this->register(Arrow::class, function(World $world, CompoundTag $nbt) : Arrow{ diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 96dceacfb7b..51686b4bc92 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -52,7 +52,7 @@ class AreaEffectCloud extends Entity{ public const WAIT_TIME = 10; public const REAPPLICATION_DELAY = 40; - public const RADIUS = 3.0; + public const DEFAULT_RADIUS = 3.0; public const RADIUS_CHANGE_ON_PICKUP = -0.5; public const RADIUS_ON_USE = -0.5; public const RADIUS_PER_TICK = -0.005; @@ -74,7 +74,6 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected int $age = 0; - protected PotionType $potionType; protected EffectContainer $effectContainer; /** @var array entity ID => expiration */ @@ -89,14 +88,17 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC /** The entity will not do any update until its age reaches this value. */ protected int $waiting = self::WAIT_TIME; - protected float $initialRadius = self::RADIUS; - protected float $radius = self::RADIUS; + protected float $radius; protected float $radiusChangeOnPickup = self::RADIUS_CHANGE_ON_PICKUP; protected float $radiusOnUse = self::RADIUS_ON_USE; protected float $radiusPerTick = self::RADIUS_PER_TICK; - public function __construct(Location $location, PotionType $potionType, ?CompoundTag $nbt = null){ - $this->potionType = $potionType; + public function __construct( + Location $location, + protected PotionType $potionType, + protected float $initialRadius = self::DEFAULT_RADIUS, + ?CompoundTag $nbt = null + ){ parent::__construct($location, $nbt); } @@ -123,8 +125,7 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->durationOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); $this->pickupCount = $nbt->getInt(self::TAG_PICKUP_COUNT, 0); $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); - $this->initialRadius = $nbt->getFloat(self::TAG_INITIAL_RADIUS, self::RADIUS); - $this->radius = $nbt->getFloat(self::TAG_RADIUS, self::RADIUS); + $this->radius = $nbt->getFloat(self::TAG_RADIUS, $this->initialRadius); $this->radiusChangeOnPickup = $nbt->getFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, self::RADIUS_CHANGE_ON_PICKUP); $this->radiusOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); $this->radiusPerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); @@ -204,20 +205,6 @@ public function getInitialRadius() : float{ return $this->initialRadius; } - /** - * Sets the initial radius. - * - * @throws \RuntimeException if something attempted to set when it is already ticking - */ - public function setInitialRadius(float $radius) : void{ - if($this->age > 0){ - throw new \RuntimeException("Trying to set radius after initialization"); - } - $this->initialRadius = $radius; - $this->setRadius($radius); - $this->networkPropertiesDirty = true; - } - /** * Gets the current radius. */ From ae283105e07537810b06c778d2eae327714e7507 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 8 Oct 2022 12:05:50 -0500 Subject: [PATCH 14/47] shut --- src/entity/EntityFactory.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index f5eea8753f5..92c48ab8ae4 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -90,7 +90,7 @@ public function __construct(){ return new AreaEffectCloud( Helper::parseLocation($nbt, $world), $potionType, - $nbt->getFloatTag(AreaEffectCloud::TAG_INITIAL_RADIUS, AreaEffectCloud::DEFAULT_RADIUS), + $nbt->getFloat(AreaEffectCloud::TAG_INITIAL_RADIUS, AreaEffectCloud::DEFAULT_RADIUS), $nbt ); }, ['AreaEffectCloud', 'minecraft:area_effect_cloud'], LegacyIds::AREA_EFFECT_CLOUD); From 80c20c0cbd678130eefc51132c913a976336b261 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 8 Oct 2022 12:58:36 -0500 Subject: [PATCH 15/47] Fix radius --- src/entity/object/AreaEffectCloud.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 51686b4bc92..f2e402fb8b4 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -88,7 +88,7 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC /** The entity will not do any update until its age reaches this value. */ protected int $waiting = self::WAIT_TIME; - protected float $radius; + protected float $radius = self::DEFAULT_RADIUS; protected float $radiusChangeOnPickup = self::RADIUS_CHANGE_ON_PICKUP; protected float $radiusOnUse = self::RADIUS_ON_USE; protected float $radiusPerTick = self::RADIUS_PER_TICK; @@ -125,7 +125,7 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->durationOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); $this->pickupCount = $nbt->getInt(self::TAG_PICKUP_COUNT, 0); $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); - $this->radius = $nbt->getFloat(self::TAG_RADIUS, $this->initialRadius); + $this->setRadius($nbt->getFloat(self::TAG_RADIUS, $this->initialRadius)); $this->radiusChangeOnPickup = $nbt->getFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, self::RADIUS_CHANGE_ON_PICKUP); $this->radiusOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); $this->radiusPerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); From 6c9f96a19e249079c9eef3e2b3fded0dbdfde048 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 8 Oct 2022 15:15:03 -0500 Subject: [PATCH 16/47] Improve documentation --- src/entity/object/AreaEffectCloud.php | 51 +++++++++++++++++++++++++-- 1 file changed, 48 insertions(+), 3 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index f2e402fb8b4..285c2b00c41 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -85,7 +85,6 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected int $pickupCount = 0; - /** The entity will not do any update until its age reaches this value. */ protected int $waiting = self::WAIT_TIME; protected float $radius = self::DEFAULT_RADIUS; @@ -93,6 +92,8 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC protected float $radiusOnUse = self::RADIUS_ON_USE; protected float $radiusPerTick = self::RADIUS_PER_TICK; + //TODO: Custom particle. + public function __construct( Location $location, protected PotionType $potionType, @@ -201,74 +202,118 @@ public function getEffects() : EffectContainer{ return $this->effectContainer; } + /** + * Returns the initial radius. + */ public function getInitialRadius() : float{ return $this->initialRadius; } /** - * Gets the current radius. + * Returns the current radius. */ public function getRadius() : float{ return $this->radius; } /** - * Sets the radius only server-side, client calculates the radius by himself + * Sets the radius only server-side, client calculates the radius by himself. */ protected function setRadius(float $radius) : void{ $this->radius = $radius; $this->setSize($this->getInitialSizeInfo()); } + /** + * Returns the amount that the radius of this cloud will add by when it + * applies an effect to an entity. Usually negative resulting in a radius reduction. + */ public function getRadiusOnUse() : float{ return $this->radiusOnUse; } + /** + * Returns the amount that the radius of this cloud will add by when it + * applies an effect to an entity. + */ public function setRadiusOnUse(float $radiusOnUse) : void{ $this->radiusOnUse = $radiusOnUse; $this->networkPropertiesDirty = true; } + /** + * Returns the amount that the radius of this cloud will add by when an update + * is performed. Usually negative resulting in a radius reduction. + */ public function getRadiusPerTick() : float{ return $this->radiusPerTick; } + /** + * Sets the amount that the radius of this cloud will add by when an update is performed. + */ public function setRadiusPerTick(float $radiusPerTick) : void{ $this->radiusPerTick = $radiusPerTick; $this->networkPropertiesDirty = true; } + /** + * Returns the value that must be reached by age to perform an update. + */ public function getWaiting() : int{ return $this->waiting; } + /** + * Sets the value that must be reached by age to perform an update. + */ public function setWaiting(int $time) : void{ $this->waiting = $time; $this->networkPropertiesDirty = true; } + /** + * Returns the duration which this cloud will exist for (in ticks). + */ public function getDuration() : int{ return $this->duration; } + /** + * Sets the duration which this cloud will exist for (in ticks). + */ public function setDuration(int $duration) : void{ $this->duration = $duration; $this->networkPropertiesDirty = true; } + /** + * Returns the amount that the duration of this cloud will add by when it + * applies an effect to an entity. + */ public function getDurationOnUse() : int{ return $this->durationOnUse; } + /** + * Sets the amount that the duration of this cloud will add by when it + * applies an effect to an entity. + */ public function setDurationOnUse(int $durationOnUse) : void{ $this->durationOnUse = $durationOnUse; $this->networkPropertiesDirty = true; } + /** + * Returns the time that an entity will be immune from subsequent exposure. + */ public function getReapplicationDelay() : int{ return $this->reapplicationDelay; } + /** + * Sets the time that an entity will be immune from subsequent exposure. + */ public function setReapplicationDelay(int $delay) : void{ $this->reapplicationDelay = $delay; $this->networkPropertiesDirty = true; From 3f64d4d1b7aeef81f69c212490752ed8d91ff796 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Fri, 14 Oct 2022 10:42:22 +0100 Subject: [PATCH 17/47] Rearrange and rename some properties --- src/entity/object/AreaEffectCloud.php | 60 +++++++++++++------------- src/entity/projectile/SplashPotion.php | 2 +- 2 files changed, 31 insertions(+), 31 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 285c2b00c41..fd562693ea3 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -79,18 +79,18 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC /** @var array entity ID => expiration */ protected array $victims = []; + protected int $waiting = self::WAIT_TIME; protected int $duration = self::DURATION; - protected int $durationOnUse = self::DURATION_ON_USE; + protected int $durationChangeOnUse = self::DURATION_ON_USE; + protected int $reapplicationDelay = self::REAPPLICATION_DELAY; protected int $pickupCount = 0; - - protected int $waiting = self::WAIT_TIME; + protected float $radiusChangeOnPickup = self::RADIUS_CHANGE_ON_PICKUP; protected float $radius = self::DEFAULT_RADIUS; - protected float $radiusChangeOnPickup = self::RADIUS_CHANGE_ON_PICKUP; - protected float $radiusOnUse = self::RADIUS_ON_USE; - protected float $radiusPerTick = self::RADIUS_PER_TICK; + protected float $radiusChangeOnUse = self::RADIUS_ON_USE; + protected float $radiusChangePerTick = self::RADIUS_PER_TICK; //TODO: Custom particle. @@ -123,13 +123,13 @@ protected function initEntity(CompoundTag $nbt) : void{ $worldTime = $this->getWorld()->getTime(); $this->age = $worldTime - $nbt->getLong(self::TAG_SPAWN_TICK, $worldTime); $this->duration = $nbt->getInt(self::TAG_DURATION, self::DURATION); - $this->durationOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); + $this->durationChangeOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); $this->pickupCount = $nbt->getInt(self::TAG_PICKUP_COUNT, 0); $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); $this->setRadius($nbt->getFloat(self::TAG_RADIUS, $this->initialRadius)); $this->radiusChangeOnPickup = $nbt->getFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, self::RADIUS_CHANGE_ON_PICKUP); - $this->radiusOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); - $this->radiusPerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); + $this->radiusChangeOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); + $this->radiusChangePerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); /** @var CompoundTag[]|ListTag|null $effectsTag */ $effectsTag = $nbt->getListTag(self::TAG_EFFECTS); @@ -157,14 +157,14 @@ public function saveNBT() : CompoundTag{ $nbt->setLong(self::TAG_SPAWN_TICK, $this->getWorld()->getTime() - $this->age); $nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->potionType)); $nbt->setInt(self::TAG_DURATION, $this->duration); - $nbt->setInt(self::TAG_DURATION_ON_USE, $this->durationOnUse); + $nbt->setInt(self::TAG_DURATION_ON_USE, $this->durationChangeOnUse); $nbt->setInt(self::TAG_PICKUP_COUNT, $this->pickupCount); $nbt->setInt(self::TAG_REAPPLICATION_DELAY, $this->reapplicationDelay); $nbt->setFloat(self::TAG_INITIAL_RADIUS, $this->initialRadius); $nbt->setFloat(self::TAG_RADIUS, $this->radius); $nbt->setFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, $this->radiusChangeOnPickup); - $nbt->setFloat(self::TAG_RADIUS_ON_USE, $this->radiusOnUse); - $nbt->setFloat(self::TAG_RADIUS_PER_TICK, $this->radiusPerTick); + $nbt->setFloat(self::TAG_RADIUS_ON_USE, $this->radiusChangeOnUse); + $nbt->setFloat(self::TAG_RADIUS_PER_TICK, $this->radiusChangePerTick); if(count($this->effectContainer->all()) > 0){ $effects = []; @@ -228,16 +228,16 @@ protected function setRadius(float $radius) : void{ * Returns the amount that the radius of this cloud will add by when it * applies an effect to an entity. Usually negative resulting in a radius reduction. */ - public function getRadiusOnUse() : float{ - return $this->radiusOnUse; + public function getRadiusChangeOnUse() : float{ + return $this->radiusChangeOnUse; } /** * Returns the amount that the radius of this cloud will add by when it * applies an effect to an entity. */ - public function setRadiusOnUse(float $radiusOnUse) : void{ - $this->radiusOnUse = $radiusOnUse; + public function setRadiusChangeOnUse(float $radiusChangeOnUse) : void{ + $this->radiusChangeOnUse = $radiusChangeOnUse; $this->networkPropertiesDirty = true; } @@ -245,15 +245,15 @@ public function setRadiusOnUse(float $radiusOnUse) : void{ * Returns the amount that the radius of this cloud will add by when an update * is performed. Usually negative resulting in a radius reduction. */ - public function getRadiusPerTick() : float{ - return $this->radiusPerTick; + public function getRadiusChangePerTick() : float{ + return $this->radiusChangePerTick; } /** * Sets the amount that the radius of this cloud will add by when an update is performed. */ - public function setRadiusPerTick(float $radiusPerTick) : void{ - $this->radiusPerTick = $radiusPerTick; + public function setRadiusChangePerTick(float $radiusChangePerTick) : void{ + $this->radiusChangePerTick = $radiusChangePerTick; $this->networkPropertiesDirty = true; } @@ -291,16 +291,16 @@ public function setDuration(int $duration) : void{ * Returns the amount that the duration of this cloud will add by when it * applies an effect to an entity. */ - public function getDurationOnUse() : int{ - return $this->durationOnUse; + public function getDurationChangeOnUse() : int{ + return $this->durationChangeOnUse; } /** * Sets the amount that the duration of this cloud will add by when it * applies an effect to an entity. */ - public function setDurationOnUse(int $durationOnUse) : void{ - $this->durationOnUse = $durationOnUse; + public function setDurationChangeOnUse(int $durationChangeOnUse) : void{ + $this->durationChangeOnUse = $durationChangeOnUse; $this->networkPropertiesDirty = true; } @@ -323,7 +323,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $hasUpdate = parent::entityBaseTick($tickDiff); $this->age += $tickDiff; - $radius = $this->radius + ($this->radiusPerTick * $tickDiff); + $radius = $this->radius + ($this->radiusChangePerTick * $tickDiff); if($radius < 0.75){ $this->flagForDespawn(); return true; @@ -361,16 +361,16 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ if($this->reapplicationDelay !== 0){ $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; } - if($this->radiusOnUse !== 0.0){ - $radius = $this->radius + $this->radiusOnUse; + if($this->radiusChangeOnUse !== 0.0){ + $radius = $this->radius + $this->radiusChangeOnUse; if($radius <= 0){ $this->flagForDespawn(); return true; } $this->setRadius($radius); } - if($this->durationOnUse !== 0){ - $duration = $this->duration + $this->durationOnUse; + if($this->durationChangeOnUse !== 0){ + $duration = $this->duration + $this->durationChangeOnUse; if($duration <= 0){ $this->flagForDespawn(); return true; @@ -406,7 +406,7 @@ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_SPAWN_TIME, $spawnTime); $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_DURATION, $this->duration); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->initialRadius); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, $this->radiusPerTick); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, $this->radiusChangePerTick); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, $this->radiusChangeOnPickup); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_PICKUP_COUNT, $this->pickupCount); $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $spawnTime + $this->waiting); diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index c492a290fe4..88314ddb2ad 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -128,7 +128,7 @@ protected function onHit(ProjectileHitEvent $event) : void{ } }else{ $entity = new AreaEffectCloud(Location::fromObject($this->location->floor()->add(0.5, 0.5, 0.5), $this->getWorld()), $this->potionType); - $entity->setRadiusPerTick(-$entity->getInitialRadius() / $entity->getDuration()); + $entity->setRadiusChangePerTick(-$entity->getInitialRadius() / $entity->getDuration()); if(($owner = $this->getOwningEntity()) !== null && !$owner->isClosed()){ $entity->setOwningEntity($owner); } From 42f93d5fdfdddd5c31098071d1f70f50eb22823a Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 15 Oct 2022 10:30:10 -0500 Subject: [PATCH 18/47] Mark as protected NBT tags --- src/entity/object/AreaEffectCloud.php | 20 ++++++++++---------- 1 file changed, 10 insertions(+), 10 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index fd562693ea3..6620be73906 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -58,17 +58,17 @@ class AreaEffectCloud extends Entity{ public const RADIUS_PER_TICK = -0.005; public const TAG_POTION_ID = "PotionId"; //TAG_Short - public const TAG_SPAWN_TICK = "SpawnTick"; //TAG_Long - public const TAG_DURATION = "Duration"; //TAG_Int - public const TAG_PICKUP_COUNT = "PickupCount"; //TAG_Int - public const TAG_DURATION_ON_USE = "DurationOnUse"; //TAG_Int - public const TAG_REAPPLICATION_DELAY = "ReapplicationDelay"; //TAG_Int + protected const TAG_SPAWN_TICK = "SpawnTick"; //TAG_Long + protected const TAG_DURATION = "Duration"; //TAG_Int + protected const TAG_PICKUP_COUNT = "PickupCount"; //TAG_Int + protected const TAG_DURATION_ON_USE = "DurationOnUse"; //TAG_Int + protected const TAG_REAPPLICATION_DELAY = "ReapplicationDelay"; //TAG_Int public const TAG_INITIAL_RADIUS = "InitialRadius"; //TAG_Float - public const TAG_RADIUS = "Radius"; //TAG_Float - public const TAG_RADIUS_CHANGE_ON_PICKUP = "RadiusChangeOnPickup"; //TAG_Float - public const TAG_RADIUS_ON_USE = "RadiusOnUse"; //TAG_Float - public const TAG_RADIUS_PER_TICK = "RadiusPerTick"; //TAG_Float - public const TAG_EFFECTS = "mobEffects"; //TAG_List + protected const TAG_RADIUS = "Radius"; //TAG_Float + protected const TAG_RADIUS_CHANGE_ON_PICKUP = "RadiusChangeOnPickup"; //TAG_Float + protected const TAG_RADIUS_ON_USE = "RadiusOnUse"; //TAG_Float + protected const TAG_RADIUS_PER_TICK = "RadiusPerTick"; //TAG_Float + protected const TAG_EFFECTS = "mobEffects"; //TAG_List public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFECT_CLOUD; } From 0c5946d9157520675222225e1c330e26d2477cdb Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 15 Oct 2022 10:44:57 -0500 Subject: [PATCH 19/47] Document units --- src/entity/object/AreaEffectCloud.php | 45 ++++++++++++++------------- 1 file changed, 24 insertions(+), 21 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 6620be73906..663edf42ccf 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -46,16 +46,16 @@ class AreaEffectCloud extends Entity{ - public const DURATION = 600; - public const DURATION_ON_USE = 0; + public const DURATION = 600; // in ticks + public const DURATION_ON_USE = 0; // in ticks - public const WAIT_TIME = 10; - public const REAPPLICATION_DELAY = 40; + public const WAIT_TIME = 10; // in ticks + public const REAPPLICATION_DELAY = 40; // in ticks - public const DEFAULT_RADIUS = 3.0; - public const RADIUS_CHANGE_ON_PICKUP = -0.5; - public const RADIUS_ON_USE = -0.5; - public const RADIUS_PER_TICK = -0.005; + public const DEFAULT_RADIUS = 3.0; // in blocks + public const RADIUS_CHANGE_ON_PICKUP = -0.5; // in blocks + public const RADIUS_ON_USE = -0.5; // in blocks + public const RADIUS_PER_TICK = -0.005; // in blocks public const TAG_POTION_ID = "PotionId"; //TAG_Short protected const TAG_SPAWN_TICK = "SpawnTick"; //TAG_Long @@ -190,6 +190,9 @@ public function canBeCollidedWith() : bool{ return false; } + /** + * Returns the time it have lived (in ticks). + */ public function getAge() : int{ return $this->age; } @@ -203,21 +206,21 @@ public function getEffects() : EffectContainer{ } /** - * Returns the initial radius. + * Returns the initial radius (in blocks). */ public function getInitialRadius() : float{ return $this->initialRadius; } /** - * Returns the current radius. + * Returns the current radius (in blocks). */ public function getRadius() : float{ return $this->radius; } /** - * Sets the radius only server-side, client calculates the radius by himself. + * Sets the radius only server-side, client calculates the radius by himself (in blocks). */ protected function setRadius(float $radius) : void{ $this->radius = $radius; @@ -226,7 +229,7 @@ protected function setRadius(float $radius) : void{ /** * Returns the amount that the radius of this cloud will add by when it - * applies an effect to an entity. Usually negative resulting in a radius reduction. + * applies an effect to an entity (in blocks). Usually negative resulting in a radius reduction. */ public function getRadiusChangeOnUse() : float{ return $this->radiusChangeOnUse; @@ -234,7 +237,7 @@ public function getRadiusChangeOnUse() : float{ /** * Returns the amount that the radius of this cloud will add by when it - * applies an effect to an entity. + * applies an effect to an entity (in blocks). */ public function setRadiusChangeOnUse(float $radiusChangeOnUse) : void{ $this->radiusChangeOnUse = $radiusChangeOnUse; @@ -243,14 +246,14 @@ public function setRadiusChangeOnUse(float $radiusChangeOnUse) : void{ /** * Returns the amount that the radius of this cloud will add by when an update - * is performed. Usually negative resulting in a radius reduction. + * is performed (in blocks). Usually negative resulting in a radius reduction. */ public function getRadiusChangePerTick() : float{ return $this->radiusChangePerTick; } /** - * Sets the amount that the radius of this cloud will add by when an update is performed. + * Sets the amount that the radius of this cloud will add by when an update is performed (in blocks). */ public function setRadiusChangePerTick(float $radiusChangePerTick) : void{ $this->radiusChangePerTick = $radiusChangePerTick; @@ -258,14 +261,14 @@ public function setRadiusChangePerTick(float $radiusChangePerTick) : void{ } /** - * Returns the value that must be reached by age to perform an update. + * Returns the value that must be reached by age to perform an update (in ticks). */ public function getWaiting() : int{ return $this->waiting; } /** - * Sets the value that must be reached by age to perform an update. + * Sets the value that must be reached by age to perform an update (in ticks). */ public function setWaiting(int $time) : void{ $this->waiting = $time; @@ -289,7 +292,7 @@ public function setDuration(int $duration) : void{ /** * Returns the amount that the duration of this cloud will add by when it - * applies an effect to an entity. + * applies an effect to an entity (in ticks). */ public function getDurationChangeOnUse() : int{ return $this->durationChangeOnUse; @@ -297,7 +300,7 @@ public function getDurationChangeOnUse() : int{ /** * Sets the amount that the duration of this cloud will add by when it - * applies an effect to an entity. + * applies an effect to an entity (in ticks). */ public function setDurationChangeOnUse(int $durationChangeOnUse) : void{ $this->durationChangeOnUse = $durationChangeOnUse; @@ -305,14 +308,14 @@ public function setDurationChangeOnUse(int $durationChangeOnUse) : void{ } /** - * Returns the time that an entity will be immune from subsequent exposure. + * Returns the time that an entity will be immune from subsequent exposure (in ticks). */ public function getReapplicationDelay() : int{ return $this->reapplicationDelay; } /** - * Sets the time that an entity will be immune from subsequent exposure. + * Sets the time that an entity will be immune from subsequent exposure (in ticks). */ public function setReapplicationDelay(int $delay) : void{ $this->reapplicationDelay = $delay; From 8a910e0adb5f792850def11b70d67ef835e1dc63 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 15 Oct 2022 11:34:57 -0500 Subject: [PATCH 20/47] Avoid age underflow --- src/entity/object/AreaEffectCloud.php | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 663edf42ccf..3aedd8810d1 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -42,6 +42,7 @@ use pocketmine\world\particle\PotionSplashParticle; use function array_map; use function count; +use function max; use function round; class AreaEffectCloud extends Entity{ @@ -121,7 +122,7 @@ protected function initEntity(CompoundTag $nbt) : void{ }); $worldTime = $this->getWorld()->getTime(); - $this->age = $worldTime - $nbt->getLong(self::TAG_SPAWN_TICK, $worldTime); + $this->age = max($worldTime - $nbt->getLong(self::TAG_SPAWN_TICK, $worldTime), 0); $this->duration = $nbt->getInt(self::TAG_DURATION, self::DURATION); $this->durationChangeOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); $this->pickupCount = $nbt->getInt(self::TAG_PICKUP_COUNT, 0); From df50e71916d9788d0d465397b345d6eee48adc41 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 16 Oct 2022 00:15:19 -0500 Subject: [PATCH 21/47] Implement AreaEffectCloudApplyEvent --- src/entity/object/AreaEffectCloud.php | 25 +++++-- .../entity/AreaEffectCloudApplyEvent.php | 69 +++++++++++++++++++ 2 files changed, 87 insertions(+), 7 deletions(-) create mode 100644 src/event/entity/AreaEffectCloudApplyEvent.php diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 3aedd8810d1..17226dfe895 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -32,6 +32,7 @@ use pocketmine\entity\EntitySizeInfo; use pocketmine\entity\Living; use pocketmine\entity\Location; +use pocketmine\event\entity\AreaEffectCloudApplyEvent; use pocketmine\item\PotionType; use pocketmine\nbt\tag\CompoundTag; use pocketmine\nbt\tag\ListTag; @@ -40,6 +41,7 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\utils\Binary; use pocketmine\world\particle\PotionSplashParticle; +use function array_filter; use function array_map; use function count; use function max; @@ -343,18 +345,28 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ unset($this->victims[$entityId]); } } - foreach($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this) as $entity){ + $this->setWaiting($this->age + self::WAIT_TIME); + + $entities = array_filter($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this), function(Entity $entity) : bool{ if(!$entity instanceof Living || isset($this->victims[$entity->getId()])){ - continue; + return false; } - $entityPosition = $entity->getPosition(); $xDiff = $entityPosition->getX() - $this->location->getX(); $zDiff = $entityPosition->getZ() - $this->location->getZ(); - if(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2){ - continue; - } + return !(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2); + }); + if(count($entities) === 0){ + return $hasUpdate; + } + $ev = new AreaEffectCloudApplyEvent($this, $entities); + $ev->call(); + if($ev->isCancelled()){ + return $hasUpdate; + } + + foreach($ev->getAffectedEntities() as $entity){ foreach($this->getCloudEffects() as $effect){ if($effect->getType() instanceof InstantEffect){ $effect->getType()->applyEffect($entity, $effect, 0.5, $this); @@ -382,7 +394,6 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $this->setDuration($duration); } } - $this->setWaiting($this->age + self::WAIT_TIME); $hasUpdate = true; } diff --git a/src/event/entity/AreaEffectCloudApplyEvent.php b/src/event/entity/AreaEffectCloudApplyEvent.php new file mode 100644 index 00000000000..63a786eecd2 --- /dev/null +++ b/src/event/entity/AreaEffectCloudApplyEvent.php @@ -0,0 +1,69 @@ + + */ +class AreaEffectCloudApplyEvent extends EntityEvent implements Cancellable{ + use CancellableTrait; + + /** + * @param Living[] $affectedEntities + */ + public function __construct( + AreaEffectCloud $entity, + protected array $affectedEntities + ){ + $this->entity = $entity; + } + + /** + * @return AreaEffectCloud + */ + public function getEntity(){ + return $this->entity; + } + + /** + * Returns the effected entities. + * + * It is important to note that not every entity is guaranteed to be effected. + * The cloud may die during the application of its effects due to the depletion + * of {@link AreaEffectCloud::getDurationChangeOnUse()} or {@link AreaEffectCloud::getRadiusChangeOnUse()}. + * + * @return Living[] + */ + public function getAffectedEntities() : array{ + return $this->affectedEntities; + } +} From 6f968adf2e7011f8840b6a3ad043634b0f26f573 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 16 Oct 2022 00:31:50 -0500 Subject: [PATCH 22/47] shut PHPStan! --- src/entity/object/AreaEffectCloud.php | 1 + 1 file changed, 1 insertion(+) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 17226dfe895..2f9a129bfd4 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -347,6 +347,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ } $this->setWaiting($this->age + self::WAIT_TIME); + /** @var Living[] $entities */ $entities = array_filter($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this), function(Entity $entity) : bool{ if(!$entity instanceof Living || isset($this->victims[$entity->getId()])){ return false; From 8922880ecc16f2778ff153c79afd02d89f933810 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Sun, 16 Oct 2022 10:01:32 -0500 Subject: [PATCH 23/47] Apply suggestions from dktapps Co-authored-by: Dylan T. --- src/event/entity/AreaEffectCloudApplyEvent.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/event/entity/AreaEffectCloudApplyEvent.php b/src/event/entity/AreaEffectCloudApplyEvent.php index 63a786eecd2..e9dcef3fa50 100644 --- a/src/event/entity/AreaEffectCloudApplyEvent.php +++ b/src/event/entity/AreaEffectCloudApplyEvent.php @@ -57,9 +57,9 @@ public function getEntity(){ /** * Returns the effected entities. * - * It is important to note that not every entity is guaranteed to be effected. - * The cloud may die during the application of its effects due to the depletion - * of {@link AreaEffectCloud::getDurationChangeOnUse()} or {@link AreaEffectCloud::getRadiusChangeOnUse()}. + * It is important to note that not every entity is guaranteed to be affected. + * The cloud may despawn during the application of its effects due to the depletion + * by {@link AreaEffectCloud::getDurationChangeOnUse()} or {@link AreaEffectCloud::getRadiusChangeOnUse()}. * * @return Living[] */ From 823cea3e9bd58856156f7fca946a18bb13b6eb90 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 16 Oct 2022 12:05:07 -0500 Subject: [PATCH 24/47] Ensure that all entities returned by `AreaEffectCloudApplyEvent::getAffectedEntities()` will be affected. --- src/entity/object/AreaEffectCloud.php | 52 +++++++++++-------- .../entity/AreaEffectCloudApplyEvent.php | 6 +-- 2 files changed, 32 insertions(+), 26 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 2f9a129bfd4..d48c023325c 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -41,7 +41,6 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\utils\Binary; use pocketmine\world\particle\PotionSplashParticle; -use function array_filter; use function array_map; use function count; use function max; @@ -348,15 +347,33 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $this->setWaiting($this->age + self::WAIT_TIME); /** @var Living[] $entities */ - $entities = array_filter($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this), function(Entity $entity) : bool{ + $entities = []; + $radiusChange = 0.0; + $durationChange = 0; + foreach($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this) as $entity){ if(!$entity instanceof Living || isset($this->victims[$entity->getId()])){ - return false; + continue; } $entityPosition = $entity->getPosition(); $xDiff = $entityPosition->getX() - $this->location->getX(); $zDiff = $entityPosition->getZ() - $this->location->getZ(); - return !(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2); - }); + if(($xDiff ** 2 + $zDiff ** 2) > $this->radius ** 2){ + continue; + } + $entities[] = $entity; + if($this->radiusChangeOnUse !== 0.0){ + $radiusChange += $this->radiusChangeOnUse; + if($this->radius + $radiusChange <= 0){ + break; + } + } + if($this->durationChangeOnUse !== 0){ + $durationChange += $this->durationChangeOnUse; + if($this->duration + $durationChange <= 0){ + break; + } + } + } if(count($entities) === 0){ return $hasUpdate; } @@ -378,23 +395,16 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ if($this->reapplicationDelay !== 0){ $this->victims[$entity->getId()] = $this->age + $this->reapplicationDelay; } - if($this->radiusChangeOnUse !== 0.0){ - $radius = $this->radius + $this->radiusChangeOnUse; - if($radius <= 0){ - $this->flagForDespawn(); - return true; - } - $this->setRadius($radius); - } - if($this->durationChangeOnUse !== 0){ - $duration = $this->duration + $this->durationChangeOnUse; - if($duration <= 0){ - $this->flagForDespawn(); - return true; - } - $this->setDuration($duration); - } } + + $radius = $this->radius + $radiusChange; + $duration = $this->duration + $durationChange; + if($radius <= 0 || $duration <= 0){ + $this->flagForDespawn(); + return true; + } + $this->setRadius($radius); + $this->setDuration($duration); $hasUpdate = true; } diff --git a/src/event/entity/AreaEffectCloudApplyEvent.php b/src/event/entity/AreaEffectCloudApplyEvent.php index e9dcef3fa50..1373a95f4bb 100644 --- a/src/event/entity/AreaEffectCloudApplyEvent.php +++ b/src/event/entity/AreaEffectCloudApplyEvent.php @@ -55,11 +55,7 @@ public function getEntity(){ } /** - * Returns the effected entities. - * - * It is important to note that not every entity is guaranteed to be affected. - * The cloud may despawn during the application of its effects due to the depletion - * by {@link AreaEffectCloud::getDurationChangeOnUse()} or {@link AreaEffectCloud::getRadiusChangeOnUse()}. + * Returns the affected entities. * * @return Living[] */ From 5915074975dc0f5f223b0cfb153cb9d9edb13ba4 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Thu, 22 Dec 2022 18:38:24 -0500 Subject: [PATCH 25/47] Hack to disable client side radius calculadtion --- src/entity/EntityFactory.php | 2 +- src/entity/object/AreaEffectCloud.php | 38 ++++++++++++++++++++------- 2 files changed, 30 insertions(+), 10 deletions(-) diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 87dbc368514..481b2e8d98a 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -96,7 +96,7 @@ public function __construct(){ $nbt->getFloat(AreaEffectCloud::TAG_INITIAL_RADIUS, AreaEffectCloud::DEFAULT_RADIUS), $nbt ); - }, ['AreaEffectCloud', 'minecraft:area_effect_cloud'], LegacyIds::AREA_EFFECT_CLOUD); + }, ['AreaEffectCloud', 'minecraft:area_effect_cloud']); $this->register(Arrow::class, function(World $world, CompoundTag $nbt) : Arrow{ return new Arrow(Helper::parseLocation($nbt, $world), null, $nbt->getByte(Arrow::TAG_CRIT, 0) === 1, $nbt); diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index d48c023325c..838b036771c 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -222,11 +222,32 @@ public function getRadius() : float{ } /** - * Sets the radius only server-side, client calculates the radius by himself (in blocks). + * Sets the radius (in blocks). */ protected function setRadius(float $radius) : void{ $this->radius = $radius; $this->setSize($this->getInitialSizeInfo()); + $this->networkPropertiesDirty = true; + } + + /** + * Returns the amount that the radius of this cloud will add by when it is + * picked up (in blocks). Usually negative resulting in a radius reduction. + * + * Applied when getting dragon breath bottle. + */ + public function getRadiusChangeOnPickup() : float{ + return $this->radiusChangeOnPickup; + } + + /** + * Sets the amount that the radius of this cloud will add by when it is + * picked up (in blocks). Usually negative resulting in a radius reduction. + * + * Applied when getting dragon breath bottle. + */ + public function setRadiusChangeOnPickup(float $radiusChangeOnPickup) : void{ + $this->radiusChangeOnPickup = $radiusChangeOnPickup; } /** @@ -238,12 +259,11 @@ public function getRadiusChangeOnUse() : float{ } /** - * Returns the amount that the radius of this cloud will add by when it + * Sets the amount that the radius of this cloud will add by when it * applies an effect to an entity (in blocks). */ public function setRadiusChangeOnUse(float $radiusChangeOnUse) : void{ $this->radiusChangeOnUse = $radiusChangeOnUse; - $this->networkPropertiesDirty = true; } /** @@ -259,7 +279,6 @@ public function getRadiusChangePerTick() : float{ */ public function setRadiusChangePerTick(float $radiusChangePerTick) : void{ $this->radiusChangePerTick = $radiusChangePerTick; - $this->networkPropertiesDirty = true; } /** @@ -306,7 +325,6 @@ public function getDurationChangeOnUse() : int{ */ public function setDurationChangeOnUse(int $durationChangeOnUse) : void{ $this->durationChangeOnUse = $durationChangeOnUse; - $this->networkPropertiesDirty = true; } /** @@ -329,7 +347,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ $this->age += $tickDiff; $radius = $this->radius + ($this->radiusChangePerTick * $tickDiff); - if($radius < 0.75){ + if($radius < 0.5){ $this->flagForDespawn(); return true; } @@ -431,12 +449,14 @@ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ $spawnTime = $this->getWorld()->getTime() - $this->age; $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_SPAWN_TIME, $spawnTime); $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_DURATION, $this->duration); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->initialRadius); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, $this->radiusChangePerTick); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, $this->radiusChangeOnPickup); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->radius); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_PICKUP_COUNT, $this->pickupCount); $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $spawnTime + $this->waiting); $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->effectContainer->getBubbleColor()->toARGB())); $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectContainer->hasOnlyAmbientEffects() ? 1 : 0); + + //TODO: HACK! we purposely fill these in with invalid values to disable the client-sided radius calculation. + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, 0); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, 0); } } From e5287f523d4ae11a9b1781437510bfe165662af6 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 19 Feb 2024 18:32:48 -0500 Subject: [PATCH 26/47] Use of native enum case accessor --- src/item/LingeringPotion.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/LingeringPotion.php b/src/item/LingeringPotion.php index 9ba3d3d4992..28039f2a086 100644 --- a/src/item/LingeringPotion.php +++ b/src/item/LingeringPotion.php @@ -35,7 +35,7 @@ class LingeringPotion extends ProjectileItem{ private PotionType $potionType; public function __construct(ItemIdentifier $identifier, string $name){ - $this->potionType = PotionType::WATER(); + $this->potionType = PotionType::WATER; parent::__construct($identifier, $name); } From 20488a34637410b8074c6a3c125a302c17cdd141 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 1 Dec 2024 21:18:19 -0500 Subject: [PATCH 27/47] Make `SplashPotion::setLinger()` not fluent :( Always BC... --- src/entity/projectile/SplashPotion.php | 3 +-- src/item/LingeringPotion.php | 4 +++- 2 files changed, 4 insertions(+), 3 deletions(-) diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index 6b8db61320b..b63c0c5b612 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -180,10 +180,9 @@ public function willLinger() : bool{ * * @return $this */ - public function setLinger(bool $value = true) : self{ + public function setLinger(bool $value = true) : void{ $this->linger = $value; $this->networkPropertiesDirty = true; - return $this; } /** diff --git a/src/item/LingeringPotion.php b/src/item/LingeringPotion.php index 28039f2a086..22356ba88bf 100644 --- a/src/item/LingeringPotion.php +++ b/src/item/LingeringPotion.php @@ -58,7 +58,9 @@ public function getMaxStackSize() : int{ } protected function createEntity(Location $location, Player $thrower) : Throwable{ - return (new SplashPotionEntity($location, $thrower, $this->potionType))->setLinger(); + $projectile = new SplashPotionEntity($location, $thrower, $this->potionType); + $projectile->setLinger(); + return $projectile; } public function getThrowForce() : float{ From 4e97049c76f33c5ac362e6c5c07c506b6cfab0d7 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 1 Dec 2024 21:34:20 -0500 Subject: [PATCH 28/47] Remove `EffectManager::add()` `$force` param :( --- src/entity/effect/EffectContainer.php | 1 + src/entity/effect/EffectManager.php | 4 ++-- 2 files changed, 3 insertions(+), 2 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index ef5099aaf7c..f89f20983ae 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -146,6 +146,7 @@ public function canAdd(EffectInstance $effect) : bool{ /** * Adds an effect. + * If the force parameter it's true or {@link EffectContainer::canAdd()} conditions are met. * * @return bool whether the effect has been successfully applied. */ diff --git a/src/entity/effect/EffectManager.php b/src/entity/effect/EffectManager.php index 4cdefb3243a..99d64fd7c10 100644 --- a/src/entity/effect/EffectManager.php +++ b/src/entity/effect/EffectManager.php @@ -65,7 +65,7 @@ public function remove(Effect $effectType) : void{ * * @return bool whether the effect has been successfully applied. */ - public function add(EffectInstance $effect, bool $force = false) : bool{ + public function add(EffectInstance $effect) : bool{ $index = spl_object_id($effect->getType()); $oldEffect = $this->effects[$index] ?? null; @@ -75,7 +75,7 @@ public function add(EffectInstance $effect, bool $force = false) : bool{ } $ev->call(); - if(!$force && $ev->isCancelled()){ + if($ev->isCancelled()){ return false; } From cef5f2182f1b28df9124298b681293c384e11203 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 1 Dec 2024 21:43:13 -0500 Subject: [PATCH 29/47] Ugh remove $force param from EffectContainer too this was useful to skip unnecesary checks :( --- src/entity/effect/EffectContainer.php | 6 +++--- src/entity/effect/EffectManager.php | 9 +-------- 2 files changed, 4 insertions(+), 11 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index f89f20983ae..c8f2ef9c271 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -146,12 +146,12 @@ public function canAdd(EffectInstance $effect) : bool{ /** * Adds an effect. - * If the force parameter it's true or {@link EffectContainer::canAdd()} conditions are met. + * If {@link EffectContainer::canAdd()} conditions are met. * * @return bool whether the effect has been successfully applied. */ - public function add(EffectInstance $effect, bool $force = false) : bool{ - if($force || $this->canAdd($effect)){ + public function add(EffectInstance $effect) : bool{ + if($this->canAdd($effect)){ $index = spl_object_id($effect->getType()); $replacesOldEffect = isset($this->effects[$index]); diff --git a/src/entity/effect/EffectManager.php b/src/entity/effect/EffectManager.php index 99d64fd7c10..b7fd9396169 100644 --- a/src/entity/effect/EffectManager.php +++ b/src/entity/effect/EffectManager.php @@ -58,13 +58,6 @@ public function remove(Effect $effectType) : void{ } } - /** - * Adds an effect to the mob. - * If a weaker effect of the same type is already applied, it will be replaced. - * If a weaker or equal-strength effect is already applied but has a shorter duration, it will be replaced. - * - * @return bool whether the effect has been successfully applied. - */ public function add(EffectInstance $effect) : bool{ $index = spl_object_id($effect->getType()); $oldEffect = $this->effects[$index] ?? null; @@ -85,7 +78,7 @@ public function add(EffectInstance $effect) : bool{ $effect->getType()->add($this->entity, $effect); - return parent::add($effect, true); + return parent::add($effect); } public function tick(int $tickDiff = 1) : bool{ From 09d1b36776289d4f63c8457abb3382160bdf2f80 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sun, 1 Dec 2024 21:48:05 -0500 Subject: [PATCH 30/47] ... --- src/entity/projectile/SplashPotion.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index b63c0c5b612..cb04f827329 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -177,8 +177,6 @@ public function willLinger() : bool{ /** * Sets whether this splash potion will create an area-effect-cloud when it lands. - * - * @return $this */ public function setLinger(bool $value = true) : void{ $this->linger = $value; From 28aa818a352ab3b9abf75954601b9d07efd1f63a Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Mon, 2 Dec 2024 22:40:03 -0500 Subject: [PATCH 31/47] Modernize code on item\LingeringPotion --- src/item/LingeringPotion.php | 14 ++++---------- 1 file changed, 4 insertions(+), 10 deletions(-) diff --git a/src/item/LingeringPotion.php b/src/item/LingeringPotion.php index 22356ba88bf..a4abd470b19 100644 --- a/src/item/LingeringPotion.php +++ b/src/item/LingeringPotion.php @@ -23,8 +23,7 @@ namespace pocketmine\item; -use pocketmine\data\runtime\RuntimeDataReader; -use pocketmine\data\runtime\RuntimeDataWriter; +use pocketmine\data\runtime\RuntimeDataDescriber; use pocketmine\entity\Location; use pocketmine\entity\projectile\SplashPotion as SplashPotionEntity; use pocketmine\entity\projectile\Throwable; @@ -32,15 +31,10 @@ class LingeringPotion extends ProjectileItem{ - private PotionType $potionType; + private PotionType $potionType = PotionType::WATER; - public function __construct(ItemIdentifier $identifier, string $name){ - $this->potionType = PotionType::WATER; - parent::__construct($identifier, $name); - } - - protected function describeType(RuntimeDataReader|RuntimeDataWriter $w) : void{ - $w->potionType($this->potionType); + protected function describeState(RuntimeDataDescriber $w) : void{ + $w->enum($this->potionType); } public function getType() : PotionType{ return $this->potionType; } From 52943e29c8f0a89b223ac2f001ac0c2aa34fecdc Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Mon, 2 Dec 2024 22:41:01 -0500 Subject: [PATCH 32/47] StringToItemParser: SPLASH_POTION => LINGERING_POTION my bad :/ Co-authored-by: Dylan T. --- src/item/StringToItemParser.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/StringToItemParser.php b/src/item/StringToItemParser.php index aed4023afc4..89529576ae9 100644 --- a/src/item/StringToItemParser.php +++ b/src/item/StringToItemParser.php @@ -1202,7 +1202,7 @@ private static function registerDynamicItems(self $result) : void{ $result->register($prefix("potion"), fn() => Items::POTION()->setType($potionType)); $result->register($prefix("splash_potion"), fn() => Items::SPLASH_POTION()->setType($potionType)); - $result->register($prefix("lingering_potion"), fn() => Items::SPLASH_POTION()->setType($potionType)); + $result->register($prefix("lingering_potion"), fn() => Items::LINGERING_POTION()->setType($potionType)); } } From dc7d6c54b02b087645ccc23b1b343929413b4cf7 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Sat, 7 Dec 2024 11:52:25 -0500 Subject: [PATCH 33/47] Updade EffectContainer::canAdd() documentation Co-authored-by: Dylan T. --- src/entity/effect/EffectContainer.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index c8f2ef9c271..8ffde4dba9d 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -125,8 +125,8 @@ public function has(Effect $effect) : bool{ /** * In the following cases it will return true: - * If a weaker effect of the same type is already applied. - * If a weaker or equal-strength effect is already applied but has a shorter duration. + * - if the effect type is not already applied + * - if an existing effect of the same type can be replaced (due to shorter duration or lower level) * * @return bool whether the effect can been applied. */ From d78bf091f96d595d321c74db5e087f482bf78996 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 7 Dec 2024 11:53:43 -0500 Subject: [PATCH 34/47] Remove @return --- src/entity/effect/EffectContainer.php | 2 -- 1 file changed, 2 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index 8ffde4dba9d..7db11f6d4e4 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -127,8 +127,6 @@ public function has(Effect $effect) : bool{ * In the following cases it will return true: * - if the effect type is not already applied * - if an existing effect of the same type can be replaced (due to shorter duration or lower level) - * - * @return bool whether the effect can been applied. */ public function canAdd(EffectInstance $effect) : bool{ $index = spl_object_id($effect->getType()); From 3e04f3b68619740676bd1cedaaa9f83d169af85e Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 7 Dec 2024 12:38:17 -0500 Subject: [PATCH 35/47] Rename EffectContainer::setEffectValidatorForBubbles() method and add documentation --- src/entity/effect/EffectContainer.php | 6 ++++-- src/entity/object/AreaEffectCloud.php | 2 +- 2 files changed, 5 insertions(+), 3 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index 7db11f6d4e4..145785e5cbc 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -69,7 +69,7 @@ public function __construct(){ $this->effectAddHooks = new ObjectSet(); $this->effectRemoveHooks = new ObjectSet(); - $this->setEffectValidatorForBubbles(function(EffectInstance $effect) : bool{ + $this->setEffectFilterForBubbles(function(EffectInstance $effect) : bool{ return $effect->isVisible() && $effect->getType()->hasBubbles(); }); } @@ -166,9 +166,11 @@ public function add(EffectInstance $effect) : bool{ } /** + * Sets the filter that determines which effects will be displayed in the bubbles. + * * @phpstan-param \Closure(EffectInstance) : bool $effectValidator */ - public function setEffectValidatorForBubbles(\Closure $effectValidator) : void{ + public function setEffectFilterForBubbles(\Closure $effectValidator) : void{ Utils::validateCallableSignature(new CallbackType(new ReturnType(BuiltInTypes::BOOL), new ParameterType("effect", EffectInstance::class)), $effectValidator); $this->effectValidatorForBubbles = $effectValidator; } diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 838b036771c..a7e87f272de 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -118,7 +118,7 @@ protected function initEntity(CompoundTag $nbt) : void{ $this->effectContainer->setDefaultBubbleColor(PotionSplashParticle::DEFAULT_COLOR()); $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->networkPropertiesDirty = true; }); $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->networkPropertiesDirty = true; }); - $this->effectContainer->setEffectValidatorForBubbles(function(EffectInstance $effect) : bool{ + $this->effectContainer->setEffectFilterForBubbles(function(EffectInstance $effect) : bool{ return $effect->isVisible(); }); From 012894764c1e87569a893014db3ad8e8223f0817 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Sat, 7 Dec 2024 18:10:50 -0500 Subject: [PATCH 36/47] Introduce $linger param on SplashPotion this removes code duplication between LingerPotion and SpashPotion --- src/block/Cauldron.php | 3 +- src/block/WaterCauldron.php | 3 +- src/block/tile/Cauldron.php | 5 +- .../ItemSerializerDeserializerRegistrar.php | 5 +- src/item/LingeringPotion.php | 63 ------------------- src/item/SplashPotion.php | 21 ++++++- src/item/VanillaItems.php | 4 +- 7 files changed, 28 insertions(+), 76 deletions(-) delete mode 100644 src/item/LingeringPotion.php diff --git a/src/block/Cauldron.php b/src/block/Cauldron.php index 06db66f0c95..1d275e137fc 100644 --- a/src/block/Cauldron.php +++ b/src/block/Cauldron.php @@ -27,7 +27,6 @@ use pocketmine\block\utils\SupportType; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; -use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\PotionType; use pocketmine\item\SplashPotion; @@ -83,7 +82,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $this->fill(FillableCauldron::MAX_FILL_LEVEL, VanillaBlocks::LAVA_CAULDRON(), $item, VanillaItems::BUCKET(), $returnedItems); }elseif($item->getTypeId() === ItemTypeIds::POWDER_SNOW_BUCKET){ //TODO: powder snow cauldron - }elseif($item instanceof LingeringPotion || $item instanceof Potion || $item instanceof SplashPotion){ + }elseif($item instanceof Potion || $item instanceof SplashPotion){ if($item->getType() === PotionType::WATER){ $this->fill(WaterCauldron::WATER_BOTTLE_FILL_AMOUNT, VanillaBlocks::WATER_CAULDRON(), $item, VanillaItems::GLASS_BOTTLE(), $returnedItems); }else{ diff --git a/src/block/WaterCauldron.php b/src/block/WaterCauldron.php index 8c6687162bf..a620357fa4d 100644 --- a/src/block/WaterCauldron.php +++ b/src/block/WaterCauldron.php @@ -32,7 +32,6 @@ use pocketmine\item\Dye; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; -use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\PotionType; use pocketmine\item\SplashPotion; @@ -123,7 +122,7 @@ public function onInteract(Item $item, int $face, Vector3 $clickVector, ?Player $world->addSound($this->position->add(0.5, 0.5, 0.5), new CauldronAddDyeSound()); $item->pop(); - }elseif($item instanceof LingeringPotion || $item instanceof Potion || $item instanceof SplashPotion){ + }elseif($item instanceof Potion || $item instanceof SplashPotion){ if($item->getType() === PotionType::WATER){ $this->setCustomWaterColor(null)->addFillLevels(self::WATER_BOTTLE_FILL_AMOUNT, $item, VanillaItems::GLASS_BOTTLE(), $returnedItems); }else{ diff --git a/src/block/tile/Cauldron.php b/src/block/tile/Cauldron.php index e4c4612fdd3..53d2ae3892d 100644 --- a/src/block/tile/Cauldron.php +++ b/src/block/tile/Cauldron.php @@ -31,7 +31,6 @@ use pocketmine\data\SavedDataLoadingException; use pocketmine\item\Item; use pocketmine\item\ItemTypeIds; -use pocketmine\item\LingeringPotion; use pocketmine\item\Potion; use pocketmine\item\SplashPotion; use pocketmine\item\VanillaItems; @@ -77,7 +76,7 @@ protected function addAdditionalSpawnData(CompoundTag $nbt) : void{ default => throw new AssumptionFailedError("Unexpected potion item type") }); - $type = $this->potionItem instanceof LingeringPotion || $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; + $type = $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; $nbt->setShort(self::TAG_POTION_ID, $type === null ? self::POTION_ID_NONE : PotionTypeIdMap::getInstance()->toId($type)); if($this->customWaterColor !== null){ @@ -115,7 +114,7 @@ protected function writeSaveData(CompoundTag $nbt) : void{ default => throw new AssumptionFailedError("Unexpected potion item type") }); - $type = $this->potionItem instanceof LingeringPotion || $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; + $type = $this->potionItem instanceof Potion || $this->potionItem instanceof SplashPotion ? $this->potionItem->getType() : null; $nbt->setShort(self::TAG_POTION_ID, $type === null ? self::POTION_ID_NONE : PotionTypeIdMap::getInstance()->toId($type)); if($this->customWaterColor !== null){ diff --git a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php index 77baae969a9..febed34ad5f 100644 --- a/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php +++ b/src/data/bedrock/item/ItemSerializerDeserializerRegistrar.php @@ -41,7 +41,6 @@ use pocketmine\item\Dye; use pocketmine\item\GoatHorn; use pocketmine\item\Item; -use pocketmine\item\LingeringPotion; use pocketmine\item\Medicine; use pocketmine\item\Potion; use pocketmine\item\SplashPotion; @@ -500,10 +499,10 @@ function(GoatHorn $item, int $meta) : void{ $this->map1to1ItemWithMeta( Ids::LINGERING_POTION, Items::LINGERING_POTION(), - function(LingeringPotion $item, int $meta) : void{ + function(SplashPotion $item, int $meta) : void{ $item->setType(PotionTypeIdMap::getInstance()->fromId($meta) ?? throw new ItemTypeDeserializeException("Unknown potion type ID $meta")); }, - fn(LingeringPotion $item) => PotionTypeIdMap::getInstance()->toId($item->getType()) + fn(SplashPotion $item) => PotionTypeIdMap::getInstance()->toId($item->getType()) ); $this->map1to1ItemWithMeta( Ids::MEDICINE, diff --git a/src/item/LingeringPotion.php b/src/item/LingeringPotion.php deleted file mode 100644 index a4abd470b19..00000000000 --- a/src/item/LingeringPotion.php +++ /dev/null @@ -1,63 +0,0 @@ -enum($this->potionType); - } - - public function getType() : PotionType{ return $this->potionType; } - - /** - * @return $this - */ - public function setType(PotionType $type) : self{ - $this->potionType = $type; - return $this; - } - - public function getMaxStackSize() : int{ - return 1; - } - - protected function createEntity(Location $location, Player $thrower) : Throwable{ - $projectile = new SplashPotionEntity($location, $thrower, $this->potionType); - $projectile->setLinger(); - return $projectile; - } - - public function getThrowForce() : float{ - return 0.5; - } -} diff --git a/src/item/SplashPotion.php b/src/item/SplashPotion.php index e1c9167ac34..b7bd6f374c3 100644 --- a/src/item/SplashPotion.php +++ b/src/item/SplashPotion.php @@ -33,6 +33,16 @@ class SplashPotion extends ProjectileItem{ private PotionType $potionType = PotionType::WATER; + public function __construct( + ItemIdentifier $identifier, + string $name = "Splash Potion", + array $enchantmentTags = [], + private bool $linger = false + ){ + //TODO: remove unnecessary default parameters in PM6, they remain because backward compatibility + parent::__construct($identifier, $name, $enchantmentTags); + } + protected function describeState(RuntimeDataDescriber $w) : void{ $w->enum($this->potionType); } @@ -52,10 +62,19 @@ public function getMaxStackSize() : int{ } protected function createEntity(Location $location, Player $thrower) : Throwable{ - return new SplashPotionEntity($location, $thrower, $this->potionType); + $projectile = new SplashPotionEntity($location, $thrower, $this->potionType); + $projectile->setLinger($this->linger); + return $projectile; } public function getThrowForce() : float{ return 0.5; } + + /** + * Returns whether this splash potion will create an area-effect cloud when it lands on it's projectile form. + */ + public function willLinger() : bool{ + return $this->linger; + } } diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 2a8f016375f..1eeed1b4821 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -214,7 +214,7 @@ * @method static Armor LEATHER_CAP() * @method static Armor LEATHER_PANTS() * @method static Armor LEATHER_TUNIC() - * @method static LingeringPotion LINGERING_POTION() + * @method static SplashPotion LINGERING_POTION() * @method static Item MAGMA_CREAM() * @method static Boat MANGROVE_BOAT() * @method static ItemBlockWallOrFloor MANGROVE_SIGN() @@ -515,7 +515,7 @@ protected static function setup() : void{ self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli")); self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA())); self::register("leather", fn(IID $id) => new Item($id, "Leather")); - self::register("lingering_potion", fn(IID $id) => new LingeringPotion($id, "Lingering Potion")); + self::register("lingering_potion", fn(IID $id) => new SplashPotion($id, "Lingering Potion", [], true)); self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream")); self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine")); From dd1c9f58aef81f954530c538ff7819eb13e2235c Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:27:19 -0500 Subject: [PATCH 37/47] Update SplashPotion::willLinger() doc Co-authored-by: Dylan T. --- src/item/SplashPotion.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/SplashPotion.php b/src/item/SplashPotion.php index b7bd6f374c3..947b1b4bed0 100644 --- a/src/item/SplashPotion.php +++ b/src/item/SplashPotion.php @@ -72,7 +72,7 @@ public function getThrowForce() : float{ } /** - * Returns whether this splash potion will create an area-effect cloud when it lands on it's projectile form. + * Returns whether this splash potion will create an area-effect cloud on impact. */ public function willLinger() : bool{ return $this->linger; From 4ab9bc78d27b7de02ff6ade12da17e2a97d3ab08 Mon Sep 17 00:00:00 2001 From: IvanCraft623 <57236932+IvanCraft623@users.noreply.github.com> Date: Thu, 12 Dec 2024 10:35:25 -0500 Subject: [PATCH 38/47] Skip $enchantmentTags param on SplashPotion construction :P Co-authored-by: Dylan T. --- src/item/VanillaItems.php | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/item/VanillaItems.php b/src/item/VanillaItems.php index 1eeed1b4821..40e889cf946 100644 --- a/src/item/VanillaItems.php +++ b/src/item/VanillaItems.php @@ -515,7 +515,7 @@ protected static function setup() : void{ self::register("lapis_lazuli", fn(IID $id) => new Item($id, "Lapis Lazuli")); self::register("lava_bucket", fn(IID $id) => new LiquidBucket($id, "Lava Bucket", Blocks::LAVA())); self::register("leather", fn(IID $id) => new Item($id, "Leather")); - self::register("lingering_potion", fn(IID $id) => new SplashPotion($id, "Lingering Potion", [], true)); + self::register("lingering_potion", fn(IID $id) => new SplashPotion($id, "Lingering Potion", linger: true)); self::register("magma_cream", fn(IID $id) => new Item($id, "Magma Cream")); self::register("mangrove_sign", fn(IID $id) => new ItemBlockWallOrFloor($id, Blocks::MANGROVE_SIGN(), Blocks::MANGROVE_WALL_SIGN())); self::register("medicine", fn(IID $id) => new Medicine($id, "Medicine")); From 6d854d362b6e60db6dda4ce269d8a2e621e04bde Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Thu, 12 Dec 2024 11:30:44 -0500 Subject: [PATCH 39/47] Get rid of EffectContainer::defaultBubbleColor --- src/entity/effect/EffectContainer.php | 18 ++++-------------- src/entity/object/AreaEffectCloud.php | 5 +++-- 2 files changed, 7 insertions(+), 16 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index 145785e5cbc..cc87069123b 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -51,9 +51,7 @@ class EffectContainer{ */ protected ObjectSet $effectRemoveHooks; - protected Color $defaultBubbleColor; - - protected ?Color $bubbleColor = null; + protected Color $bubbleColor; protected bool $onlyAmbientEffects = false; @@ -65,7 +63,7 @@ class EffectContainer{ protected \Closure $effectValidatorForBubbles; public function __construct(){ - $this->defaultBubbleColor = new Color(0, 0, 0, 0); + $this->bubbleColor = new Color(0, 0, 0, 0); $this->effectAddHooks = new ObjectSet(); $this->effectRemoveHooks = new ObjectSet(); @@ -200,21 +198,13 @@ protected function recalculateEffectColor() : void{ $this->bubbleColor = Color::mix(...$colors); $this->onlyAmbientEffects = $ambient; }else{ - $this->bubbleColor = null; + $this->bubbleColor = new Color(0, 0, 0, 0); $this->onlyAmbientEffects = false; } } - public function getDefaultBubbleColor() : Color{ - return $this->defaultBubbleColor; - } - - public function setDefaultBubbleColor(Color $color) : void{ - $this->defaultBubbleColor = $color; - } - public function getBubbleColor() : Color{ - return $this->bubbleColor ?? $this->defaultBubbleColor; + return $this->bubbleColor; } public function hasOnlyAmbientEffects() : bool{ diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index a7e87f272de..736efdded6c 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -115,7 +115,6 @@ protected function initEntity(CompoundTag $nbt) : void{ parent::initEntity($nbt); $this->effectContainer = new EffectContainer(); - $this->effectContainer->setDefaultBubbleColor(PotionSplashParticle::DEFAULT_COLOR()); $this->effectContainer->getEffectAddHooks()->add(function() : void{ $this->networkPropertiesDirty = true; }); $this->effectContainer->getEffectRemoveHooks()->add(function() : void{ $this->networkPropertiesDirty = true; }); $this->effectContainer->setEffectFilterForBubbles(function(EffectInstance $effect) : bool{ @@ -452,7 +451,9 @@ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->radius); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_PICKUP_COUNT, $this->pickupCount); $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $spawnTime + $this->waiting); - $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt($this->effectContainer->getBubbleColor()->toARGB())); + $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt(( + count($this->effectContainer->all()) === 0 ? PotionSplashParticle::DEFAULT_COLOR() : $this->effectContainer->getBubbleColor() + )->toARGB())); $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectContainer->hasOnlyAmbientEffects() ? 1 : 0); //TODO: HACK! we purposely fill these in with invalid values to disable the client-sided radius calculation. From a413c3be462206afc58bdeb459cc6eae334a8d57 Mon Sep 17 00:00:00 2001 From: IvanCraft623 Date: Thu, 12 Dec 2024 11:34:07 -0500 Subject: [PATCH 40/47] Add missing filter -> validator terminology changes --- src/entity/effect/EffectContainer.php | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/src/entity/effect/EffectContainer.php b/src/entity/effect/EffectContainer.php index cc87069123b..5a50db0a139 100644 --- a/src/entity/effect/EffectContainer.php +++ b/src/entity/effect/EffectContainer.php @@ -60,7 +60,7 @@ class EffectContainer{ * * @phpstan-var \Closure(EffectInstance) : bool */ - protected \Closure $effectValidatorForBubbles; + protected \Closure $effectFilterForBubbles; public function __construct(){ $this->bubbleColor = new Color(0, 0, 0, 0); @@ -170,7 +170,7 @@ public function add(EffectInstance $effect) : bool{ */ public function setEffectFilterForBubbles(\Closure $effectValidator) : void{ Utils::validateCallableSignature(new CallbackType(new ReturnType(BuiltInTypes::BOOL), new ParameterType("effect", EffectInstance::class)), $effectValidator); - $this->effectValidatorForBubbles = $effectValidator; + $this->effectFilterForBubbles = $effectValidator; } /** @@ -181,7 +181,7 @@ protected function recalculateEffectColor() : void{ $colors = []; $ambient = true; foreach($this->effects as $effect){ - if(($this->effectValidatorForBubbles)($effect)){ + if(($this->effectFilterForBubbles)($effect)){ $level = $effect->getEffectLevel(); $color = $effect->getColor(); for($i = 0; $i < $level; ++$i){ From 9f3f4869673fc8d555edcdf44ad00d2dab5cc9bb Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 22:13:23 +0000 Subject: [PATCH 41/47] Cleanup, terminology improvements, make AreaEffectCloud constructor behaviour less surprising --- src/entity/EntityFactory.php | 1 - src/entity/object/AreaEffectCloud.php | 118 +++++++++--------- src/entity/projectile/SplashPotion.php | 12 +- .../entity/AreaEffectCloudApplyEvent.php | 3 +- 4 files changed, 61 insertions(+), 73 deletions(-) diff --git a/src/entity/EntityFactory.php b/src/entity/EntityFactory.php index 21c0f554c00..fd337991c55 100644 --- a/src/entity/EntityFactory.php +++ b/src/entity/EntityFactory.php @@ -95,7 +95,6 @@ public function __construct(){ return new AreaEffectCloud( Helper::parseLocation($nbt, $world), $potionType, - $nbt->getFloat(AreaEffectCloud::TAG_INITIAL_RADIUS, AreaEffectCloud::DEFAULT_RADIUS), $nbt ); }, ['AreaEffectCloud', 'minecraft:area_effect_cloud']); diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 736efdded6c..2e04bf72a69 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -48,16 +48,16 @@ class AreaEffectCloud extends Entity{ - public const DURATION = 600; // in ticks - public const DURATION_ON_USE = 0; // in ticks + public const DEFAULT_DURATION = 600; // in ticks + public const DEFAULT_DURATION_CHANGE_ON_USE = 0; // in ticks - public const WAIT_TIME = 10; // in ticks + public const UPDATE_DELAY = 10; // in ticks public const REAPPLICATION_DELAY = 40; // in ticks public const DEFAULT_RADIUS = 3.0; // in blocks - public const RADIUS_CHANGE_ON_PICKUP = -0.5; // in blocks - public const RADIUS_ON_USE = -0.5; // in blocks - public const RADIUS_PER_TICK = -0.005; // in blocks + public const DEFAULT_RADIUS_CHANGE_ON_PICKUP = -0.5; // in blocks + public const DEFAULT_RADIUS_CHANGE_ON_USE = -0.5; // in blocks + public const DEFAULT_RADIUS_CHANGE_PER_TICK = -(self::DEFAULT_RADIUS / self::DEFAULT_DURATION); // in blocks public const TAG_POTION_ID = "PotionId"; //TAG_Short protected const TAG_SPAWN_TICK = "SpawnTick"; //TAG_Long @@ -65,7 +65,7 @@ class AreaEffectCloud extends Entity{ protected const TAG_PICKUP_COUNT = "PickupCount"; //TAG_Int protected const TAG_DURATION_ON_USE = "DurationOnUse"; //TAG_Int protected const TAG_REAPPLICATION_DELAY = "ReapplicationDelay"; //TAG_Int - public const TAG_INITIAL_RADIUS = "InitialRadius"; //TAG_Float + protected const TAG_INITIAL_RADIUS = "InitialRadius"; //TAG_Float protected const TAG_RADIUS = "Radius"; //TAG_Float protected const TAG_RADIUS_CHANGE_ON_PICKUP = "RadiusChangeOnPickup"; //TAG_Float protected const TAG_RADIUS_ON_USE = "RadiusOnUse"; //TAG_Float @@ -81,25 +81,23 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC /** @var array entity ID => expiration */ protected array $victims = []; - protected int $waiting = self::WAIT_TIME; - protected int $duration = self::DURATION; - protected int $durationChangeOnUse = self::DURATION_ON_USE; + protected int $nextUpdateAge = self::UPDATE_DELAY; + protected int $maxAge = self::DEFAULT_DURATION; + protected int $maxAgeChangeOnUse = self::DEFAULT_DURATION_CHANGE_ON_USE; protected int $reapplicationDelay = self::REAPPLICATION_DELAY; protected int $pickupCount = 0; - protected float $radiusChangeOnPickup = self::RADIUS_CHANGE_ON_PICKUP; + protected float $radiusChangeOnPickup = self::DEFAULT_RADIUS_CHANGE_ON_PICKUP; + protected float $initialRadius = self::DEFAULT_RADIUS; protected float $radius = self::DEFAULT_RADIUS; - protected float $radiusChangeOnUse = self::RADIUS_ON_USE; - protected float $radiusChangePerTick = self::RADIUS_PER_TICK; - - //TODO: Custom particle. + protected float $radiusChangeOnUse = self::DEFAULT_RADIUS_CHANGE_ON_USE; + protected float $radiusChangePerTick = self::DEFAULT_RADIUS_CHANGE_PER_TICK; public function __construct( Location $location, protected PotionType $potionType, - protected float $initialRadius = self::DEFAULT_RADIUS, ?CompoundTag $nbt = null ){ parent::__construct($location, $nbt); @@ -123,14 +121,16 @@ protected function initEntity(CompoundTag $nbt) : void{ $worldTime = $this->getWorld()->getTime(); $this->age = max($worldTime - $nbt->getLong(self::TAG_SPAWN_TICK, $worldTime), 0); - $this->duration = $nbt->getInt(self::TAG_DURATION, self::DURATION); - $this->durationChangeOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DURATION_ON_USE); + $this->maxAge = $nbt->getInt(self::TAG_DURATION, self::DEFAULT_DURATION); + $this->maxAgeChangeOnUse = $nbt->getInt(self::TAG_DURATION_ON_USE, self::DEFAULT_DURATION_CHANGE_ON_USE); $this->pickupCount = $nbt->getInt(self::TAG_PICKUP_COUNT, 0); $this->reapplicationDelay = $nbt->getInt(self::TAG_REAPPLICATION_DELAY, self::REAPPLICATION_DELAY); + + $this->initialRadius = $nbt->getFloat(self::TAG_INITIAL_RADIUS, self::DEFAULT_RADIUS); $this->setRadius($nbt->getFloat(self::TAG_RADIUS, $this->initialRadius)); - $this->radiusChangeOnPickup = $nbt->getFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, self::RADIUS_CHANGE_ON_PICKUP); - $this->radiusChangeOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::RADIUS_ON_USE); - $this->radiusChangePerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::RADIUS_PER_TICK); + $this->radiusChangeOnPickup = $nbt->getFloat(self::TAG_RADIUS_CHANGE_ON_PICKUP, self::DEFAULT_RADIUS_CHANGE_ON_PICKUP); + $this->radiusChangeOnUse = $nbt->getFloat(self::TAG_RADIUS_ON_USE, self::DEFAULT_RADIUS_CHANGE_ON_USE); + $this->radiusChangePerTick = $nbt->getFloat(self::TAG_RADIUS_PER_TICK, self::DEFAULT_RADIUS_CHANGE_PER_TICK); /** @var CompoundTag[]|ListTag|null $effectsTag */ $effectsTag = $nbt->getListTag(self::TAG_EFFECTS); @@ -149,6 +149,13 @@ protected function initEntity(CompoundTag $nbt) : void{ $e->getByte("Ambient", 0) !== 0 )); } + }else{ + foreach($this->potionType->getEffects() as $effect){ + $this->effectContainer->add($effect); + if($effect->getType() instanceof InstantEffect){ + $this->setReapplicationDelay(0); + } + } } } @@ -157,8 +164,8 @@ public function saveNBT() : CompoundTag{ $nbt->setLong(self::TAG_SPAWN_TICK, $this->getWorld()->getTime() - $this->age); $nbt->setShort(self::TAG_POTION_ID, PotionTypeIdMap::getInstance()->toId($this->potionType)); - $nbt->setInt(self::TAG_DURATION, $this->duration); - $nbt->setInt(self::TAG_DURATION_ON_USE, $this->durationChangeOnUse); + $nbt->setInt(self::TAG_DURATION, $this->maxAge); + $nbt->setInt(self::TAG_DURATION_ON_USE, $this->maxAgeChangeOnUse); $nbt->setInt(self::TAG_PICKUP_COUNT, $this->pickupCount); $nbt->setInt(self::TAG_REAPPLICATION_DELAY, $this->reapplicationDelay); $nbt->setFloat(self::TAG_INITIAL_RADIUS, $this->initialRadius); @@ -192,7 +199,7 @@ public function canBeCollidedWith() : bool{ } /** - * Returns the time it have lived (in ticks). + * Returns the current age of the cloud (in ticks). */ public function getAge() : int{ return $this->age; @@ -221,7 +228,7 @@ public function getRadius() : float{ } /** - * Sets the radius (in blocks). + * Sets the current radius (in blocks). */ protected function setRadius(float $radius) : void{ $this->radius = $radius; @@ -281,49 +288,34 @@ public function setRadiusChangePerTick(float $radiusChangePerTick) : void{ } /** - * Returns the value that must be reached by age to perform an update (in ticks). - */ - public function getWaiting() : int{ - return $this->waiting; - } - - /** - * Sets the value that must be reached by age to perform an update (in ticks). + * Returns the age at which the cloud will despawn. */ - public function setWaiting(int $time) : void{ - $this->waiting = $time; - $this->networkPropertiesDirty = true; + public function getMaxAge() : int{ + return $this->maxAge; } /** - * Returns the duration which this cloud will exist for (in ticks). + * Sets the age at which the cloud will despawn. */ - public function getDuration() : int{ - return $this->duration; - } - - /** - * Sets the duration which this cloud will exist for (in ticks). - */ - public function setDuration(int $duration) : void{ - $this->duration = $duration; + public function setMaxAge(int $maxAge) : void{ + $this->maxAge = $maxAge; $this->networkPropertiesDirty = true; } /** - * Returns the amount that the duration of this cloud will add by when it + * Returns the amount that the max age of this cloud will change by when it * applies an effect to an entity (in ticks). */ - public function getDurationChangeOnUse() : int{ - return $this->durationChangeOnUse; + public function getMaxAgeChangeOnUse() : int{ + return $this->maxAgeChangeOnUse; } /** - * Sets the amount that the duration of this cloud will add by when it + * Sets the amount that the max age of this cloud will change by when it * applies an effect to an entity (in ticks). */ - public function setDurationChangeOnUse(int $durationChangeOnUse) : void{ - $this->durationChangeOnUse = $durationChangeOnUse; + public function setMaxAgeChangeOnUse(int $maxAgeChangeOnUse) : void{ + $this->maxAgeChangeOnUse = $maxAgeChangeOnUse; } /** @@ -351,22 +343,24 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ return true; } $this->setRadius($radius); - if($this->age >= $this->waiting){ - if($this->age > $this->duration){ + if($this->age >= $this->nextUpdateAge){ + if($this->age > $this->maxAge){ $this->flagForDespawn(); return true; } + $this->nextUpdateAge = $this->age + self::UPDATE_DELAY; + $this->networkPropertiesDirty = true; + foreach($this->victims as $entityId => $expiration){ if($this->age >= $expiration){ unset($this->victims[$entityId]); } } - $this->setWaiting($this->age + self::WAIT_TIME); /** @var Living[] $entities */ $entities = []; $radiusChange = 0.0; - $durationChange = 0; + $maxAgeChange = 0; foreach($this->getWorld()->getCollidingEntities($this->getBoundingBox(), $this) as $entity){ if(!$entity instanceof Living || isset($this->victims[$entity->getId()])){ continue; @@ -384,9 +378,9 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ break; } } - if($this->durationChangeOnUse !== 0){ - $durationChange += $this->durationChangeOnUse; - if($this->duration + $durationChange <= 0){ + if($this->maxAgeChangeOnUse !== 0){ + $maxAgeChange += $this->maxAgeChangeOnUse; + if($this->maxAge + $maxAgeChange <= 0){ break; } } @@ -415,13 +409,13 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ } $radius = $this->radius + $radiusChange; - $duration = $this->duration + $durationChange; - if($radius <= 0 || $duration <= 0){ + $maxAge = $this->maxAge + $maxAgeChange; + if($radius <= 0 || $maxAge <= 0){ $this->flagForDespawn(); return true; } $this->setRadius($radius); - $this->setDuration($duration); + $this->setMaxAge($maxAge); $hasUpdate = true; } diff --git a/src/entity/projectile/SplashPotion.php b/src/entity/projectile/SplashPotion.php index cb04f827329..65a8204013b 100644 --- a/src/entity/projectile/SplashPotion.php +++ b/src/entity/projectile/SplashPotion.php @@ -129,17 +129,13 @@ protected function onHit(ProjectileHitEvent $event) : void{ } } }else{ - $entity = new AreaEffectCloud(Location::fromObject($this->location->floor()->add(0.5, 0.5, 0.5), $this->getWorld()), $this->potionType); - $entity->setRadiusChangePerTick(-$entity->getInitialRadius() / $entity->getDuration()); + $entity = new AreaEffectCloud( + Location::fromObject($this->location->floor()->add(0.5, 0.5, 0.5), $this->getWorld()), + $this->potionType + ); if(($owner = $this->getOwningEntity()) !== null && !$owner->isClosed()){ $entity->setOwningEntity($owner); } - foreach($this->getPotionEffects() as $effect){ - $entity->getEffects()->add($effect); - if($effect->getType() instanceof InstantEffect){ - $entity->setReapplicationDelay(0); - } - } $entity->spawnToAll(); } if(!$hasEffects && $event instanceof ProjectileHitBlockEvent && $this->getPotionType() === PotionType::WATER){ diff --git a/src/event/entity/AreaEffectCloudApplyEvent.php b/src/event/entity/AreaEffectCloudApplyEvent.php index 1373a95f4bb..c6246a339c4 100644 --- a/src/event/entity/AreaEffectCloudApplyEvent.php +++ b/src/event/entity/AreaEffectCloudApplyEvent.php @@ -29,8 +29,7 @@ use pocketmine\event\CancellableTrait; /** - * Called when a area effect cloud applies it's effects. Happens once - * every {@link AreaEffectCloud::getWaiting()} is reached and there are affected entities. + * Called when an area effect cloud applies effects to entities. * * @phpstan-extends EntityEvent */ From 33a91381eef21ba869c3c949afd04dde604bdcb1 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 22:43:38 +0000 Subject: [PATCH 42/47] Remove crap that has no business being sent over the network --- src/entity/object/AreaEffectCloud.php | 16 +++++++--------- 1 file changed, 7 insertions(+), 9 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 2e04bf72a69..0902d6f8d91 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -299,7 +299,6 @@ public function getMaxAge() : int{ */ public function setMaxAge(int $maxAge) : void{ $this->maxAge = $maxAge; - $this->networkPropertiesDirty = true; } /** @@ -330,7 +329,6 @@ public function getReapplicationDelay() : int{ */ public function setReapplicationDelay(int $delay) : void{ $this->reapplicationDelay = $delay; - $this->networkPropertiesDirty = true; } protected function entityBaseTick(int $tickDiff = 1) : bool{ @@ -439,19 +437,19 @@ public function getCloudEffects() : array{ protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); - $spawnTime = $this->getWorld()->getTime() - $this->age; - $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_SPAWN_TIME, $spawnTime); - $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_DURATION, $this->duration); + //visual properties $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS, $this->radius); - $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_PICKUP_COUNT, $this->pickupCount); - $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, $spawnTime + $this->waiting); $properties->setInt(EntityMetadataProperties::POTION_COLOR, Binary::signInt(( count($this->effectContainer->all()) === 0 ? PotionSplashParticle::DEFAULT_COLOR() : $this->effectContainer->getBubbleColor() )->toARGB())); - $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, $this->effectContainer->hasOnlyAmbientEffects() ? 1 : 0); - //TODO: HACK! we purposely fill these in with invalid values to disable the client-sided radius calculation. + //these are properties the client expects, and are used for client-sided logic, which we don't want + $properties->setByte(EntityMetadataProperties::POTION_AMBIENT, 0); + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_DURATION, -1); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_CHANGE_ON_PICKUP, 0); $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_RADIUS_PER_TICK, 0); + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_SPAWN_TIME, 0); + $properties->setFloat(EntityMetadataProperties::AREA_EFFECT_CLOUD_PICKUP_COUNT, 0); + $properties->setInt(EntityMetadataProperties::AREA_EFFECT_CLOUD_WAITING, 0); } } From 0be33c66f31ba0761f8a8181adb377bcfe9f295e Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 22:51:46 +0000 Subject: [PATCH 43/47] we're not syncing this over the network anymore --- src/entity/object/AreaEffectCloud.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 0902d6f8d91..713cd3cb2cf 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -347,7 +347,6 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ return true; } $this->nextUpdateAge = $this->age + self::UPDATE_DELAY; - $this->networkPropertiesDirty = true; foreach($this->victims as $entityId => $expiration){ if($this->age >= $expiration){ From 3e17f5000e067d2f215208b5577f4b576fd8d92b Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 23:34:47 +0000 Subject: [PATCH 44/47] Remove useless property --- src/entity/object/AreaEffectCloud.php | 8 +++----- 1 file changed, 3 insertions(+), 5 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 713cd3cb2cf..d5bb67e3073 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -81,7 +81,6 @@ public static function getNetworkTypeId() : string{ return EntityIds::AREA_EFFEC /** @var array entity ID => expiration */ protected array $victims = []; - protected int $nextUpdateAge = self::UPDATE_DELAY; protected int $maxAge = self::DEFAULT_DURATION; protected int $maxAgeChangeOnUse = self::DEFAULT_DURATION_CHANGE_ON_USE; @@ -151,7 +150,7 @@ protected function initEntity(CompoundTag $nbt) : void{ } }else{ foreach($this->potionType->getEffects() as $effect){ - $this->effectContainer->add($effect); + $this->effectContainer->add($effect->setDuration((int) round($effect->getDuration() / 4))); if($effect->getType() instanceof InstantEffect){ $this->setReapplicationDelay(0); } @@ -341,12 +340,11 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ return true; } $this->setRadius($radius); - if($this->age >= $this->nextUpdateAge){ + if($this->age >= self::UPDATE_DELAY && ($this->age % self::UPDATE_DELAY) === 0){ if($this->age > $this->maxAge){ $this->flagForDespawn(); return true; } - $this->nextUpdateAge = $this->age + self::UPDATE_DELAY; foreach($this->victims as $entityId => $expiration){ if($this->age >= $expiration){ @@ -397,7 +395,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ if($effect->getType() instanceof InstantEffect){ $effect->getType()->applyEffect($entity, $effect, 0.5, $this); }else{ - $entity->getEffects()->add($effect->setDuration((int) round($effect->getDuration() / 4))); + $entity->getEffects()->add($effect); } } if($this->reapplicationDelay !== 0){ From 2e26c084fbc293b846d5c22b6ca0d354226bb850 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 23:36:03 +0000 Subject: [PATCH 45/47] oops --- src/entity/object/AreaEffectCloud.php | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index d5bb67e3073..6120769cc6d 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -150,7 +150,7 @@ protected function initEntity(CompoundTag $nbt) : void{ } }else{ foreach($this->potionType->getEffects() as $effect){ - $this->effectContainer->add($effect->setDuration((int) round($effect->getDuration() / 4))); + $this->effectContainer->add($effect); if($effect->getType() instanceof InstantEffect){ $this->setReapplicationDelay(0); } @@ -395,7 +395,7 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ if($effect->getType() instanceof InstantEffect){ $effect->getType()->applyEffect($entity, $effect, 0.5, $this); }else{ - $entity->getEffects()->add($effect); + $entity->getEffects()->add($effect->setDuration((int) round($effect->getDuration() / 4))); } } if($this->reapplicationDelay !== 0){ From 6a59e988f3a84a84587bb25ab45869e87e0faad5 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 23:39:39 +0000 Subject: [PATCH 46/47] Remove internal function --- src/entity/object/AreaEffectCloud.php | 17 ++--------------- 1 file changed, 2 insertions(+), 15 deletions(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index 6120769cc6d..d601c17cf85 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -391,7 +391,8 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ } foreach($ev->getAffectedEntities() as $entity){ - foreach($this->getCloudEffects() as $effect){ + foreach($this->effectContainer->all() as $effect){ + $effect = clone $effect; //avoid accidental modification if($effect->getType() instanceof InstantEffect){ $effect->getType()->applyEffect($entity, $effect, 0.5, $this); }else{ @@ -417,20 +418,6 @@ protected function entityBaseTick(int $tickDiff = 1) : bool{ return $hasUpdate; } - /** - * Returns the effects the area effect cloud provides. - * - * Used to get COPIES to avoid accidentally modifying the same effect instance - * already applied to another entity. - * - * @return EffectInstance[] - */ - public function getCloudEffects() : array{ - return array_map(function(EffectInstance $effect) : EffectInstance{ - return clone $effect; - }, $this->effectContainer->all()); - } - protected function syncNetworkData(EntityMetadataCollection $properties) : void{ parent::syncNetworkData($properties); From d0647099a69e136ed4854cbb4bcb554789853e95 Mon Sep 17 00:00:00 2001 From: "Dylan K. Taylor" Date: Thu, 19 Dec 2024 23:50:28 +0000 Subject: [PATCH 47/47] Fucking CS --- src/entity/object/AreaEffectCloud.php | 1 - 1 file changed, 1 deletion(-) diff --git a/src/entity/object/AreaEffectCloud.php b/src/entity/object/AreaEffectCloud.php index d601c17cf85..6ea7a4790fd 100644 --- a/src/entity/object/AreaEffectCloud.php +++ b/src/entity/object/AreaEffectCloud.php @@ -41,7 +41,6 @@ use pocketmine\network\mcpe\protocol\types\entity\EntityMetadataProperties; use pocketmine\utils\Binary; use pocketmine\world\particle\PotionSplashParticle; -use function array_map; use function count; use function max; use function round;