Refactored effects handling, split up concerns of effect types and instances

Removed json insanity for effects

Split up effect types and effect instances

Saturation is an instant effect
This commit is contained in:
Dylan K. Taylor 2018-03-07 12:42:31 +00:00
parent c7f8796136
commit dc3bf8546e
17 changed files with 442 additions and 453 deletions

View File

@ -29,6 +29,7 @@ use pocketmine\block\BlockFactory;
use pocketmine\command\Command;
use pocketmine\command\CommandSender;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\entity\Human;
use pocketmine\entity\Item as DroppedItem;
@ -114,6 +115,7 @@ use pocketmine\network\mcpe\protocol\ItemFrameDropItemPacket;
use pocketmine\network\mcpe\protocol\LevelEventPacket;
use pocketmine\network\mcpe\protocol\LevelSoundEventPacket;
use pocketmine\network\mcpe\protocol\LoginPacket;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\network\mcpe\protocol\MobEquipmentPacket;
use pocketmine\network\mcpe\protocol\MovePlayerPacket;
use pocketmine\network\mcpe\protocol\PlayerActionPacket;
@ -1722,6 +1724,27 @@ class Player extends Human implements CommandSender, ChunkLoader, IPlayer{
return $this->isCreative() or parent::canBreathe();
}
protected function sendEffectAdd(EffectInstance $effect, bool $replacesOldEffect) : void{
$pk = new MobEffectPacket();
$pk->entityRuntimeId = $this->getId();
$pk->eventId = $replacesOldEffect ? MobEffectPacket::EVENT_MODIFY : MobEffectPacket::EVENT_ADD;
$pk->effectId = $effect->getId();
$pk->amplifier = $effect->getAmplifier();
$pk->particles = $effect->isVisible();
$pk->duration = $effect->getDuration();
$this->dataPacket($pk);
}
protected function sendEffectRemove(EffectInstance $effect) : void{
$pk = new MobEffectPacket();
$pk->entityRuntimeId = $this->getId();
$pk->eventId = MobEffectPacket::EVENT_REMOVE;
$pk->effectId = $effect->getId();
$this->dataPacket($pk);
}
public function checkNetwork(){
if(!$this->isOnline()){
return;

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine\block;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Living;
use pocketmine\item\FoodSource;
use pocketmine\item\Item;
@ -127,7 +127,7 @@ class Cake extends Transparent implements FoodSource{
}
/**
* @return Effect[]
* @return EffectInstance[]
*/
public function getAdditionalEffects() : array{
return [];

View File

@ -26,6 +26,7 @@ namespace pocketmine\command\defaults;
use pocketmine\command\CommandSender;
use pocketmine\command\utils\InvalidCommandSyntaxException;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\lang\TranslationContainer;
use pocketmine\utils\TextFormat;
@ -81,7 +82,7 @@ class EffectCommand extends VanillaCommand{
if(count($args) >= 3){
$duration = ((int) $args[2]) * 20; //ticks
}else{
$duration = $effect->getDefaultDuration();
$duration = null;
}
if(count($args) >= 4){
@ -95,10 +96,11 @@ class EffectCommand extends VanillaCommand{
}
}
$visible = true;
if(count($args) >= 5){
$v = strtolower($args[4]);
if($v === "on" or $v === "true" or $v === "t" or $v === "1"){
$effect->setVisible(false);
$visible = false;
}
}
@ -115,10 +117,9 @@ class EffectCommand extends VanillaCommand{
$player->removeEffect($effect->getId());
$sender->sendMessage(new TranslationContainer("commands.effect.success.removed", [$effect->getName(), $player->getDisplayName()]));
}else{
$effect->setDuration($duration)->setAmplifier($amplification);
$player->addEffect($effect);
self::broadcastCommandMessage($sender, new TranslationContainer("%commands.effect.success", [$effect->getName(), $effect->getAmplifier(), $player->getDisplayName(), $effect->getDuration() / 20, $effect->getId()]));
$instance = new EffectInstance($effect, $duration, $amplification, $visible);
$player->addEffect($instance);
self::broadcastCommandMessage($sender, new TranslationContainer("%commands.effect.success", [$effect->getName(), $instance->getAmplifier(), $player->getDisplayName(), $instance->getDuration() / 20, $effect->getId()]));
}

View File

@ -26,10 +26,7 @@ namespace pocketmine\entity;
use pocketmine\event\entity\EntityDamageEvent;
use pocketmine\event\entity\EntityRegainHealthEvent;
use pocketmine\event\player\PlayerExhaustEvent;
use pocketmine\network\mcpe\protocol\MobEffectPacket;
use pocketmine\Player;
use pocketmine\utils\Color;
use pocketmine\utils\Config;
class Effect{
public const SPEED = 1;
@ -61,34 +58,39 @@ class Effect{
/** @var Effect[] */
protected static $effects = [];
public static function init(){
$config = new Config(\pocketmine\RESOURCE_PATH . "effects.json", Config::JSON, []);
foreach($config->getAll() as $name => $data){
$color = hexdec(substr($data["color"], 1));
$a = ($color >> 24) & 0xff;
$r = ($color >> 16) & 0xff;
$g = ($color >> 8) & 0xff;
$b = $color & 0xff;
self::registerEffect($name, new Effect(
$data["id"],
"%potion." . $data["name"],
new Color($r, $g, $b, $a),
$data["isBad"] ?? false,
$data["default_duration"] ?? 300 * 20,
$data["has_bubbles"] ?? true
));
}
public static function init() : void{
self::registerEffect(new Effect(Effect::SPEED, "%potion.moveSpeed", new Color(0x7c, 0xaf, 0xc6)));
self::registerEffect(new Effect(Effect::SLOWNESS, "%potion.moveSlowdown", new Color(0x5a, 0x6c, 0x81), true));
self::registerEffect(new Effect(Effect::HASTE, "%potion.digSpeed", new Color(0xd9, 0xc0, 0x43)));
self::registerEffect(new Effect(Effect::MINING_FATIGUE, "%potion.digSlowDown", new Color(0x4a, 0x42, 0x17), true));
self::registerEffect(new Effect(Effect::STRENGTH, "%potion.damageBoost", new Color(0x93, 0x24, 0x23)));
self::registerEffect(new Effect(Effect::INSTANT_HEALTH, "%potion.heal", new Color(0xf8, 0x24, 0x23), false, 1, false));
self::registerEffect(new Effect(Effect::INSTANT_DAMAGE, "%potion.harm", new Color(0x43, 0x0a, 0x09), true, 1, false));
self::registerEffect(new Effect(Effect::JUMP_BOOST, "%potion.jump", new Color(0x22, 0xff, 0x4c)));
self::registerEffect(new Effect(Effect::NAUSEA, "%potion.confusion", new Color(0x55, 0x1d, 0x4a), true));
self::registerEffect(new Effect(Effect::REGENERATION, "%potion.regeneration", new Color(0xcd, 0x5c, 0xab)));
self::registerEffect(new Effect(Effect::RESISTANCE, "%potion.resistance", new Color(0x99, 0x45, 0x3a)));
self::registerEffect(new Effect(Effect::FIRE_RESISTANCE, "%potion.fireResistance", new Color(0xe4, 0x9a, 0x3a)));
self::registerEffect(new Effect(Effect::WATER_BREATHING, "%potion.waterBreathing", new Color(0x2e, 0x52, 0x99)));
self::registerEffect(new Effect(Effect::INVISIBILITY, "%potion.invisibility", new Color(0x7f, 0x83, 0x92)));
self::registerEffect(new Effect(Effect::BLINDNESS, "%potion.blindness", new Color(0x1f, 0x1f, 0x23), true));
self::registerEffect(new Effect(Effect::NIGHT_VISION, "%potion.nightVision", new Color(0x1f, 0x1f, 0xa1)));
self::registerEffect(new Effect(Effect::HUNGER, "%potion.hunger", new Color(0x58, 0x76, 0x53), true));
self::registerEffect(new Effect(Effect::WEAKNESS, "%potion.weakness", new Color(0x48, 0x4d, 0x48), true));
self::registerEffect(new Effect(Effect::POISON, "%potion.poison", new Color(0x4e, 0x93, 0x31), true));
self::registerEffect(new Effect(Effect::WITHER, "%potion.wither", new Color(0x35, 0x2a, 0x27), true));
self::registerEffect(new Effect(Effect::HEALTH_BOOST, "%potion.healthBoost", new Color(0xf8, 0x7d, 0x23)));
self::registerEffect(new Effect(Effect::ABSORPTION, "%potion.absorption", new Color(0x25, 0x52, 0xa5)));
self::registerEffect(new Effect(Effect::SATURATION, "%potion.saturation", new Color(0xf8, 0x24, 0x23), false, 1));
self::registerEffect(new Effect(Effect::LEVITATION, "%potion.levitation", new Color(0xce, 0xff, 0xff)));
self::registerEffect(new Effect(Effect::FATAL_POISON, "%potion.poison", new Color(0x4e, 0x93, 0x31), true));
}
/**
* @param string $internalName
* @param Effect $effect
*/
public static function registerEffect(string $internalName, Effect $effect){
public static function registerEffect(Effect $effect) : void{
self::$effects[$effect->getId()] = $effect;
self::$effects[$internalName] = $effect;
}
/**
@ -96,11 +98,8 @@ class Effect{
*
* @return Effect|null
*/
public static function getEffect(int $id){
if(isset(self::$effects[$id])){
return clone self::$effects[$id];
}
return null;
public static function getEffect(int $id) : ?Effect{
return self::$effects[$id] ?? null;
}
/**
@ -108,9 +107,10 @@ class Effect{
*
* @return Effect|null
*/
public static function getEffectByName(string $name){
if(isset(self::$effects[$name])){
return clone self::$effects[$name];
public static function getEffectByName(string $name) : ?Effect{
$const = self::class . "::" . strtoupper($name);
if(\defined($const)){
return self::getEffect(\constant($const));
}
return null;
}
@ -119,22 +119,14 @@ class Effect{
protected $id;
/** @var string */
protected $name;
/** @var int */
protected $duration;
/** @var int */
protected $amplifier = 0;
/** @var Color */
protected $color;
/** @var bool */
protected $visible = true;
/** @var bool */
protected $ambient = false;
/** @var bool */
protected $bad;
/** @var int */
protected $defaultDuration = 300 * 20;
protected $defaultDuration;
/** @var bool */
protected $hasBubbles = true;
protected $hasBubbles;
/**
* @param int $id Effect ID as per Minecraft PE
@ -147,21 +139,12 @@ class Effect{
public function __construct(int $id, string $name, Color $color, bool $isBad = false, int $defaultDuration = 300 * 20, bool $hasBubbles = true){
$this->id = $id;
$this->name = $name;
$this->bad = $isBad;
$this->color = $color;
$this->bad = $isBad;
$this->defaultDuration = $defaultDuration;
$this->duration = $defaultDuration;
$this->hasBubbles = $hasBubbles;
}
/**
* Returns the translation key used to translate this effect's name.
* @return string
*/
public function getName() : string{
return $this->name;
}
/**
* Returns the effect ID as per Minecraft PE
* @return int
@ -171,25 +154,29 @@ class Effect{
}
/**
* Sets the duration in ticks of the effect.
* @param $ticks
*
* @return $this
* Returns the translation key used to translate this effect's name.
* @return string
*/
public function setDuration(int $ticks){
if($ticks < 0 or $ticks > INT32_MAX){
throw new \InvalidArgumentException("Effect duration must be in range of 0 - " . INT32_MAX);
}
$this->duration = $ticks;
return $this;
public function getName() : string{
return $this->name;
}
/**
* Returns the duration remaining of the effect in ticks.
* @return int
* Returns a Color object representing this effect's particle colour.
* @return Color
*/
public function getDuration() : int{
return $this->duration;
public function getColor() : Color{
return clone $this->color;
}
/**
* Returns whether this effect is harmful.
* TODO: implement inverse effect results for undead mobs
*
* @return bool
*/
public function isBad() : bool{
return $this->bad;
}
/**
@ -208,109 +195,29 @@ class Effect{
return $this->hasBubbles;
}
/**
* Returns whether this effect will produce some visible effect, such as bubbles or particles.
* NOTE: Do not confuse this with {@link Effect#hasBubbles}. For example, Instant Damage does not have bubbles, but still produces visible effects (particles).
*
* @return bool
*/
public function isVisible() : bool{
return $this->visible;
}
/**
* Changes the visibility of the effect.
*
* @param bool $bool
*
* @return $this
*/
public function setVisible(bool $bool){
$this->visible = $bool;
return $this;
}
/**
* Returns the level of this effect, which is always one higher than the amplifier.
*
* @return int
*/
public function getEffectLevel() : int{
return $this->amplifier + 1;
}
/**
* Returns the amplifier of this effect.
* @return int
*/
public function getAmplifier() : int{
return $this->amplifier;
}
/**
* @param int $amplifier
*
* @return $this
*/
public function setAmplifier(int $amplifier){
$this->amplifier = ($amplifier & 0xff);
return $this;
}
/**
* Returns whether the effect originated from the ambient environment.
* Ambient effects can originate from things such as a Beacon's area of effect radius.
* If this flag is set, the amount of visible particles will be reduced by a factor of 5.
*
* @return bool
*/
public function isAmbient() : bool{
return $this->ambient;
}
/**
* Sets the ambiency of this effect.
*
* @param bool $ambient
*
* @return $this
*/
public function setAmbient(bool $ambient = true){
$this->ambient = $ambient;
return $this;
}
/**
* Returns whether this effect is harmful.
* TODO: implement inverse effect results for undead mobs
*
* @return bool
*/
public function isBad() : bool{
return $this->bad;
}
/**
* Returns whether the effect will do something on the current tick.
*
* @param EffectInstance $instance
*
* @return bool
*/
public function canTick() : bool{
public function canTick(EffectInstance $instance) : bool{
switch($this->id){
case Effect::POISON:
case Effect::FATAL_POISON:
if(($interval = (25 >> $this->amplifier)) > 0){
return ($this->duration % $interval) === 0;
if(($interval = (25 >> $instance->getAmplifier())) > 0){
return ($instance->getDuration() % $interval) === 0;
}
return true;
case Effect::WITHER:
if(($interval = (50 >> $this->amplifier)) > 0){
return ($this->duration % $interval) === 0;
if(($interval = (50 >> $instance->getAmplifier())) > 0){
return ($instance->getDuration() % $interval) === 0;
}
return true;
case Effect::REGENERATION:
if(($interval = (40 >> $this->amplifier)) > 0){
return ($this->duration % $interval) === 0;
if(($interval = (40 >> $instance->getAmplifier())) > 0){
return ($instance->getDuration() % $interval) === 0;
}
return true;
case Effect::HUNGER:
@ -325,11 +232,12 @@ class Effect{
}
/**
* Applies effect results to an entity.
* Applies effect results to an entity. This will not be called unless canTick() returns true.
*
* @param Entity $entity
* @param Living $entity
* @param EffectInstance $instance
*/
public function applyEffect(Entity $entity){
public function applyEffect(Living $entity, EffectInstance $instance) : void{
switch($this->id){
/** @noinspection PhpMissingBreakStatementInspection */
case Effect::POISON:
@ -355,72 +263,35 @@ class Effect{
case Effect::HUNGER:
if($entity instanceof Human){
$entity->exhaust(0.025 * $this->getEffectLevel(), PlayerExhaustEvent::CAUSE_POTION);
$entity->exhaust(0.025 * $instance->getEffectLevel(), PlayerExhaustEvent::CAUSE_POTION);
}
break;
case Effect::INSTANT_HEALTH:
//TODO: add particles (witch spell)
if($entity->getHealth() < $entity->getMaxHealth()){
$entity->heal(new EntityRegainHealthEvent($entity, 4 << $this->amplifier, EntityRegainHealthEvent::CAUSE_MAGIC));
$entity->heal(new EntityRegainHealthEvent($entity, 4 << $instance->getAmplifier(), EntityRegainHealthEvent::CAUSE_MAGIC));
}
break;
case Effect::INSTANT_DAMAGE:
//TODO: add particles (witch spell)
$entity->attack(new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 4 << $this->amplifier));
$entity->attack(new EntityDamageEvent($entity, EntityDamageEvent::CAUSE_MAGIC, 4 << $instance->getAmplifier()));
break;
case Effect::SATURATION:
if($entity instanceof Human){
$entity->addFood($this->getEffectLevel());
$entity->addSaturation($this->getEffectLevel() * 2);
$entity->addFood($instance->getEffectLevel());
$entity->addSaturation($instance->getEffectLevel() * 2);
}
break;
}
}
/**
* Returns a Color object representing this effect's particle colour.
* @return Color
*/
public function getColor() : Color{
return clone $this->color;
}
/**
* Sets the color of this effect.
* Applies effects to the entity when the effect is first added.
*
* @param Color $color
* @param Living $entity
* @param EffectInstance $instance
*/
public function setColor(Color $color){
$this->color = clone $color;
}
/**
* Adds this effect to the Entity, performing effect overriding as specified.
*
* @param Entity $entity
* @param Effect|null $oldEffect
*/
public function add(Entity $entity, Effect $oldEffect = null){
if($entity instanceof Player){
$pk = new MobEffectPacket();
$pk->entityRuntimeId = $entity->getId();
$pk->effectId = $this->getId();
$pk->amplifier = $this->getAmplifier();
$pk->particles = $this->isVisible();
$pk->duration = $this->getDuration();
if($oldEffect !== null){
$pk->eventId = MobEffectPacket::EVENT_MODIFY;
}else{
$pk->eventId = MobEffectPacket::EVENT_ADD;
}
$entity->dataPacket($pk);
}
if($oldEffect !== null){
$oldEffect->remove($entity, false);
}
public function add(Living $entity, EffectInstance $instance) : void{
switch($this->id){
case Effect::INVISIBILITY:
$entity->setInvisible();
@ -428,19 +299,18 @@ class Effect{
break;
case Effect::SPEED:
$attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED);
$attr->setValue($attr->getValue() * (1 + 0.2 * $this->getEffectLevel()));
$attr->setValue($attr->getValue() * (1 + 0.2 * $instance->getEffectLevel()));
break;
case Effect::SLOWNESS:
$attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED);
$attr->setValue($attr->getValue() * (1 - 0.15 * $this->getEffectLevel()), true);
$attr->setValue($attr->getValue() * (1 - 0.15 * $instance->getEffectLevel()), true);
break;
case Effect::HEALTH_BOOST:
$attr = $entity->getAttributeMap()->getAttribute(Attribute::HEALTH);
$attr->setMaxValue($attr->getMaxValue() + 4 * $this->getEffectLevel());
$entity->setMaxHealth($entity->getMaxHealth() + 4 * $instance->getEffectLevel());
break;
case Effect::ABSORPTION:
$new = (4 * $this->getEffectLevel());
$new = (4 * $instance->getEffectLevel());
if($new > $entity->getAbsorption()){
$entity->setAbsorption($new);
}
@ -451,19 +321,10 @@ class Effect{
/**
* Removes the effect from the entity, resetting any changed values back to their original defaults.
*
* @param Entity $entity
* @param bool $send
* @param Living $entity
* @param EffectInstance $instance
*/
public function remove(Entity $entity, bool $send = true){
if($send and $entity instanceof Player){
$pk = new MobEffectPacket();
$pk->entityRuntimeId = $entity->getId();
$pk->eventId = MobEffectPacket::EVENT_REMOVE;
$pk->effectId = $this->getId();
$entity->dataPacket($pk);
}
public function remove(Living $entity, EffectInstance $instance) : void{
switch($this->id){
case Effect::INVISIBILITY:
$entity->setInvisible(false);
@ -471,15 +332,14 @@ class Effect{
break;
case Effect::SPEED:
$attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED);
$attr->setValue($attr->getValue() / (1 + 0.2 * $this->getEffectLevel()));
$attr->setValue($attr->getValue() / (1 + 0.2 * $instance->getEffectLevel()));
break;
case Effect::SLOWNESS:
$attr = $entity->getAttributeMap()->getAttribute(Attribute::MOVEMENT_SPEED);
$attr->setValue($attr->getValue() / (1 - 0.15 * $this->getEffectLevel()));
$attr->setValue($attr->getValue() / (1 - 0.15 * $instance->getEffectLevel()));
break;
case Effect::HEALTH_BOOST:
$attr = $entity->getAttributeMap()->getAttribute(Attribute::HEALTH);
$attr->setMaxValue($attr->getMaxValue() - 4 * $this->getEffectLevel());
$entity->setMaxHealth($entity->getMaxHealth() - 4 * $instance->getEffectLevel());
break;
case Effect::ABSORPTION:
$entity->setAbsorption(0);

View File

@ -0,0 +1,221 @@
<?php
/*
*
* ____ _ _ __ __ _ __ __ ____
* | _ \ ___ ___| | _____| |_| \/ (_)_ __ ___ | \/ | _ \
* | |_) / _ \ / __| |/ / _ \ __| |\/| | | '_ \ / _ \_____| |\/| | |_) |
* | __/ (_) | (__| < __/ |_| | | | | | | | __/_____| | | | __/
* |_| \___/ \___|_|\_\___|\__|_| |_|_|_| |_|\___| |_| |_|_|
*
* This program is free software: you can redistribute it and/or modify
* it under the terms of the GNU Lesser General Public License as published by
* the Free Software Foundation, either version 3 of the License, or
* (at your option) any later version.
*
* @author PocketMine Team
* @link http://www.pocketmine.net/
*
*
*/
declare(strict_types=1);
namespace pocketmine\entity;
use pocketmine\utils\Color;
class EffectInstance{
/** @var Effect */
private $effectType;
/** @var int */
private $duration;
/** @var int */
private $amplifier;
/** @var bool */
private $visible;
/** @var bool */
private $ambient;
/** @var Color */
private $color;
/**
* @param Effect $effectType
* @param int|null $duration Passing null will use the effect type's default duration
* @param int $amplifier
* @param bool $visible
* @param bool $ambient
* @param null|Color $overrideColor
*/
public function __construct(Effect $effectType, ?int $duration = null, int $amplifier = 0, bool $visible = true, bool $ambient = false, ?Color $overrideColor = null){
$this->effectType = $effectType;
$this->setDuration($duration ?? $effectType->getDefaultDuration());
$this->amplifier = $amplifier;
$this->visible = $visible;
$this->ambient = $ambient;
$this->color = $overrideColor ?? $effectType->getColor();
}
public function getId() : int{
return $this->effectType->getId();
}
/**
* @return Effect
*/
public function getType() : Effect{
return $this->effectType;
}
/**
* @return int
*/
public function getDuration() : int{
return $this->duration;
}
/**
* @param int $duration
* @throws \InvalidArgumentException
*
* @return $this
*/
public function setDuration(int $duration) : EffectInstance{
if($duration < 0 or $duration > INT32_MAX){
throw new \InvalidArgumentException("Effect duration must be in range 0 - " . INT32_MAX . ", got $duration");
}
$this->duration = $duration;
return $this;
}
/**
* Decreases the duration by the given number of ticks, without dropping below zero.
*
* @param int $ticks
*
* @return $this
*/
public function decreaseDuration(int $ticks) : EffectInstance{
$this->duration = max(0, $this->duration - $ticks);
return $this;
}
/**
* Returns whether the duration has run out.
*
* @return bool
*/
public function hasExpired() : bool{
return $this->duration <= 0;
}
/**
* @return int
*/
public function getAmplifier() : int{
return $this->amplifier;
}
/**
* Returns the level of this effect, which is always one higher than the amplifier.
*
* @return int
*/
public function getEffectLevel() : int{
return $this->amplifier + 1;
}
/**
* @param int $amplifier
*
* @return $this
*/
public function setAmplifier(int $amplifier) : EffectInstance{
$this->amplifier = $amplifier;
return $this;
}
/**
* Returns whether this effect will produce some visible effect, such as bubbles or particles.
*
* @return bool
*/
public function isVisible() : bool{
return $this->visible;
}
/**
* @param bool $visible
*
* @return $this
*/
public function setVisible(bool $visible = true) : EffectInstance{
$this->visible = $visible;
return $this;
}
/**
* Returns whether the effect originated from the ambient environment.
* Ambient effects can originate from things such as a Beacon's area of effect radius.
* If this flag is set, the amount of visible particles will be reduced by a factor of 5.
*
* @return bool
*/
public function isAmbient() : bool{
return $this->ambient;
}
/**
* @param bool $ambient
*
* @return $this
*/
public function setAmbient(bool $ambient = true) : EffectInstance{
$this->ambient = $ambient;
return $this;
}
/**
* Returns the particle colour of this effect instance. This can be overridden on a per-EffectInstance basis, so it
* is not reflective of the default colour of the effect.
*
* @return Color
*/
public function getColor() : Color{
return clone $this->color;
}
/**
* Sets the colour of this EffectInstance.
*
* @param Color $color
*
* @return EffectInstance
*/
public function setColor(Color $color) : EffectInstance{
$this->color = clone $color;
return $this;
}
/**
* Resets the colour of this EffectInstance to the default specified by its type.
*
* @return EffectInstance
*/
public function resetColor() : EffectInstance{
$this->color = $this->effectType->getColor();
return $this;
}
}

View File

@ -63,7 +63,7 @@ abstract class Living extends Entity implements Damageable{
protected $jumpVelocity = 0.42;
/** @var Effect[] */
/** @var EffectInstance[] */
protected $effects = [];
/** @var ArmorInventory */
@ -95,16 +95,18 @@ abstract class Living extends Entity implements Damageable{
$activeEffectsTag = $this->namedtag->getListTag("ActiveEffects");
if($activeEffectsTag !== null){
foreach($activeEffectsTag as $e){
$amplifier = Binary::unsignByte($e->getByte("Amplifier")); //0-255 only
$effect = Effect::getEffect($e->getByte("Id"));
if($effect === null){
continue;
}
$effect->setAmplifier($amplifier)->setDuration($e->getInt("Duration"))->setVisible($e->getByte("ShowParticles", 1) > 0)->setAmbient($e->getByte("Ambient", 0) !== 0);
$this->addEffect($effect);
$this->addEffect(new EffectInstance(
$effect,
$e->getInt("Duration"),
Binary::unsignByte($e->getByte("Amplifier")),
$e->getByte("ShowParticles", 1) !== 0,
$e->getByte("Ambient", 0) !== 0
));
}
}
}
@ -174,7 +176,7 @@ abstract class Living extends Entity implements Damageable{
/**
* Returns an array of Effects currently active on the mob.
* @return Effect[]
* @return EffectInstance[]
*/
public function getEffects() : array{
return $this->effects;
@ -203,7 +205,8 @@ abstract class Living extends Entity implements Damageable{
}
unset($this->effects[$effectId]);
$effect->remove($this);
$effect->getType()->remove($this, $effect);
$this->sendEffectRemove($effect);
$this->recalculateEffectColor();
}
@ -215,7 +218,7 @@ abstract class Living extends Entity implements Damageable{
*
* @param int $effectId
*
* @return Effect|null
* @return EffectInstance|null
*/
public function getEffect(int $effectId){
return $this->effects[$effectId] ?? null;
@ -245,11 +248,11 @@ abstract class Living extends Entity implements Damageable{
* 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.
*
* @param Effect $effect
* @param EffectInstance $effect
*
* @return bool whether the effect has been successfully applied.
*/
public function addEffect(Effect $effect) : bool{
public function addEffect(EffectInstance $effect) : bool{
$oldEffect = null;
$cancelled = false;
@ -271,7 +274,13 @@ abstract class Living extends Entity implements Damageable{
return false;
}
$effect->add($this, $oldEffect);
if($oldEffect !== null){
$oldEffect->getType()->remove($this, $oldEffect);
}
$effect->getType()->add($this, $effect);
$this->sendEffectAdd($effect, $oldEffect !== null);
$this->effects[$effect->getId()] = $effect;
$this->recalculateEffectColor();
@ -287,7 +296,7 @@ abstract class Living extends Entity implements Damageable{
$colors = [];
$ambient = true;
foreach($this->effects as $effect){
if($effect->isVisible() and $effect->hasBubbles()){
if($effect->isVisible() and $effect->getType()->hasBubbles()){
$level = $effect->getEffectLevel();
$color = $effect->getColor();
for($i = 0; $i < $level; ++$i){
@ -327,6 +336,14 @@ abstract class Living extends Entity implements Damageable{
}
}
protected function sendEffectAdd(EffectInstance $effect, bool $replacesOldEffect) : void{
}
protected function sendEffectRemove(EffectInstance $effect) : void{
}
/**
* Causes the mob to consume the given Consumable object, applying applicable effects, health bonuses, food bonuses,
* etc.
@ -609,13 +626,14 @@ abstract class Living extends Entity implements Damageable{
}
protected function doEffectsTick(int $tickDiff = 1){
foreach($this->effects as $effect){
if($effect->canTick()){
$effect->applyEffect($this);
foreach($this->effects as $instance){
$type = $instance->getType();
if($type->canTick($instance)){
$type->applyEffect($this, $instance);
}
$effect->setDuration(max(0, $effect->getDuration() - $tickDiff));
if($effect->getDuration() <= 0){
$this->removeEffect($effect->getId());
$instance->decreaseDuration($tickDiff);
if($instance->hasExpired()){
$this->removeEffect($instance->getId());
}
}
}

View File

@ -23,7 +23,7 @@ declare(strict_types=1);
namespace pocketmine\event\entity;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Entity;
/**
@ -32,15 +32,15 @@ use pocketmine\entity\Entity;
class EntityEffectAddEvent extends EntityEffectEvent{
public static $handlerList = null;
/** @var Effect|null */
/** @var EffectInstance|null */
private $oldEffect;
/**
* @param Entity $entity
* @param Effect $effect
* @param Effect|null $oldEffect
* @param Entity $entity
* @param EffectInstance $effect
* @param EffectInstance $oldEffect
*/
public function __construct(Entity $entity, Effect $effect, Effect $oldEffect = null){
public function __construct(Entity $entity, EffectInstance $effect, EffectInstance $oldEffect = null){
parent::__construct($entity, $effect);
$this->oldEffect = $oldEffect;
}
@ -58,11 +58,11 @@ class EntityEffectAddEvent extends EntityEffectEvent{
* @return bool
*/
public function hasOldEffect() : bool{
return $this->oldEffect instanceof Effect;
return $this->oldEffect instanceof EffectInstance;
}
/**
* @return Effect|null
* @return EffectInstance|null
*/
public function getOldEffect(){
return $this->oldEffect;

View File

@ -23,21 +23,21 @@ declare(strict_types=1);
namespace pocketmine\event\entity;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Entity;
use pocketmine\event\Cancellable;
class EntityEffectEvent extends EntityEvent implements Cancellable{
/** @var Effect */
/** @var EffectInstance */
private $effect;
public function __construct(Entity $entity, Effect $effect){
public function __construct(Entity $entity, EffectInstance $effect){
$this->entity = $entity;
$this->effect = $effect;
}
public function getEffect() : Effect{
public function getEffect() : EffectInstance{
return $this->effect;
}
}

View File

@ -24,7 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\block\Block;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Living;
/**
@ -41,7 +41,7 @@ interface Consumable{
public function getResidue();
/**
* @return Effect[]
* @return EffectInstance[]
*/
public function getAdditionalEffects() : array;

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
class Fish extends Food{
public const FISH_FISH = 0;
@ -71,9 +72,9 @@ class Fish extends Food{
public function getAdditionalEffects() : array{
return $this->meta === self::FISH_PUFFERFISH ? [
Effect::getEffect(Effect::HUNGER)->setDuration(300)->setAmplifier(2),
Effect::getEffect(Effect::NAUSEA)->setDuration(300)->setAmplifier(1),
Effect::getEffect(Effect::POISON)->setDuration(1200)->setAmplifier(3),
new EffectInstance(Effect::getEffect(Effect::HUNGER), 300, 2),
new EffectInstance(Effect::getEffect(Effect::NAUSEA), 300, 1),
new EffectInstance(Effect::getEffect(Effect::POISON), 1200, 3)
] : [];
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
class GoldenApple extends Food{
@ -45,8 +46,8 @@ class GoldenApple extends Food{
public function getAdditionalEffects() : array{
return [
Effect::getEffect(Effect::REGENERATION)->setDuration(100)->setAmplifier(1),
Effect::getEffect(Effect::ABSORPTION)->setDuration(2400)
new EffectInstance(Effect::getEffect(Effect::REGENERATION), 100, 1),
new EffectInstance(Effect::getEffect(Effect::ABSORPTION), 2400)
];
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
class GoldenAppleEnchanted extends GoldenApple{
@ -33,10 +34,10 @@ class GoldenAppleEnchanted extends GoldenApple{
public function getAdditionalEffects() : array{
return [
Effect::getEffect(Effect::REGENERATION)->setDuration(600)->setAmplifier(4),
Effect::getEffect(Effect::ABSORPTION)->setDuration(2400)->setAmplifier(3),
Effect::getEffect(Effect::DAMAGE_RESISTANCE)->setDuration(6000),
Effect::getEffect(Effect::FIRE_RESISTANCE)->setDuration(6000),
new EffectInstance(Effect::getEffect(Effect::REGENERATION), 600, 4),
new EffectInstance(Effect::getEffect(Effect::ABSORPTION), 2400, 3),
new EffectInstance(Effect::getEffect(Effect::RESISTANCE), 6000),
new EffectInstance(Effect::getEffect(Effect::FIRE_RESISTANCE), 6000)
];
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
use pocketmine\entity\Living;
class Potion extends Item implements Consumable{
@ -70,7 +71,7 @@ class Potion extends Item implements Consumable{
* Returns a list of effects applied by potions with the specified ID.
*
* @param int $id
* @return Effect[]
* @return EffectInstance[]
*
* @throws \InvalidArgumentException if the potion type is unknown
*/
@ -84,131 +85,131 @@ class Potion extends Item implements Consumable{
return [];
case self::NIGHT_VISION:
return [
Effect::getEffect(Effect::NIGHT_VISION)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::NIGHT_VISION), 3600)
];
case self::LONG_NIGHT_VISION:
return [
Effect::getEffect(Effect::NIGHT_VISION)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::NIGHT_VISION), 9600)
];
case self::INVISIBILITY:
return [
Effect::getEffect(Effect::INVISIBILITY)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::INVISIBILITY), 3600)
];
case self::LONG_INVISIBILITY:
return [
Effect::getEffect(Effect::INVISIBILITY)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::INVISIBILITY), 9600)
];
case self::LEAPING:
return [
Effect::getEffect(Effect::JUMP_BOOST)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::JUMP_BOOST), 3600)
];
case self::LONG_LEAPING:
return [
Effect::getEffect(Effect::JUMP_BOOST)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::JUMP_BOOST), 9600)
];
case self::STRONG_LEAPING:
return [
Effect::getEffect(Effect::JUMP_BOOST)->setDuration(1800)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::JUMP_BOOST), 1800, 1)
];
case self::FIRE_RESISTANCE:
return [
Effect::getEffect(Effect::FIRE_RESISTANCE)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::FIRE_RESISTANCE), 3600)
];
case self::LONG_FIRE_RESISTANCE:
return [
Effect::getEffect(Effect::FIRE_RESISTANCE)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::FIRE_RESISTANCE), 9600)
];
case self::SWIFTNESS:
return [
Effect::getEffect(Effect::SPEED)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::SPEED), 3600)
];
case self::LONG_SWIFTNESS:
return [
Effect::getEffect(Effect::SPEED)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::SPEED), 9600)
];
case self::STRONG_SWIFTNESS:
return [
Effect::getEffect(Effect::SPEED)->setDuration(1800)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::SPEED), 1800, 1)
];
case self::SLOWNESS:
return [
Effect::getEffect(Effect::SLOWNESS)->setDuration(1800)
new EffectInstance(Effect::getEffect(Effect::SLOWNESS), 1800)
];
case self::LONG_SLOWNESS:
return [
Effect::getEffect(Effect::SLOWNESS)->setDuration(4800)
new EffectInstance(Effect::getEffect(Effect::SLOWNESS), 4800)
];
case self::WATER_BREATHING:
return [
Effect::getEffect(Effect::WATER_BREATHING)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::WATER_BREATHING), 3600)
];
case self::LONG_WATER_BREATHING:
return [
Effect::getEffect(Effect::WATER_BREATHING)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::WATER_BREATHING), 9600)
];
case self::HEALING:
return [
Effect::getEffect(Effect::INSTANT_HEALTH)
new EffectInstance(Effect::getEffect(Effect::INSTANT_HEALTH))
];
case self::STRONG_HEALING:
return [
Effect::getEffect(Effect::INSTANT_HEALTH)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::INSTANT_HEALTH), null, 1)
];
case self::HARMING:
return [
Effect::getEffect(Effect::INSTANT_DAMAGE)
new EffectInstance(Effect::getEffect(Effect::INSTANT_DAMAGE))
];
case self::STRONG_HARMING:
return [
Effect::getEffect(Effect::INSTANT_DAMAGE)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::INSTANT_DAMAGE), null, 1)
];
case self::POISON:
return [
Effect::getEffect(Effect::POISON)->setDuration(900)
new EffectInstance(Effect::getEffect(Effect::POISON), 900)
];
case self::LONG_POISON:
return [
Effect::getEffect(Effect::POISON)->setDuration(2400)
new EffectInstance(Effect::getEffect(Effect::POISON), 2400)
];
case self::STRONG_POISON:
return [
Effect::getEffect(Effect::POISON)->setDuration(440)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::POISON), 440, 1)
];
case self::REGENERATION:
return [
Effect::getEffect(Effect::REGENERATION)->setDuration(900)
new EffectInstance(Effect::getEffect(Effect::REGENERATION), 900)
];
case self::LONG_REGENERATION:
return [
Effect::getEffect(Effect::REGENERATION)->setDuration(2400)
new EffectInstance(Effect::getEffect(Effect::REGENERATION), 2400)
];
case self::STRONG_REGENERATION:
return [
Effect::getEffect(Effect::REGENERATION)->setDuration(440)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::REGENERATION), 440, 1)
];
case self::STRENGTH:
return [
Effect::getEffect(Effect::STRENGTH)->setDuration(3600)
new EffectInstance(Effect::getEffect(Effect::STRENGTH), 3600)
];
case self::LONG_STRENGTH:
return [
Effect::getEffect(Effect::STRENGTH)->setDuration(9600)
new EffectInstance(Effect::getEffect(Effect::STRENGTH), 9600)
];
case self::STRONG_STRENGTH:
return [
Effect::getEffect(Effect::STRENGTH)->setDuration(1800)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::STRENGTH), 1800, 1)
];
case self::WEAKNESS:
return [
Effect::getEffect(Effect::WEAKNESS)->setDuration(1800)
new EffectInstance(Effect::getEffect(Effect::WEAKNESS), 1800)
];
case self::LONG_WEAKNESS:
return [
Effect::getEffect(Effect::WEAKNESS)->setDuration(4800)
new EffectInstance(Effect::getEffect(Effect::WEAKNESS), 4800)
];
case self::WITHER:
return [
Effect::getEffect(Effect::WITHER)->setDuration(800)->setAmplifier(1)
new EffectInstance(Effect::getEffect(Effect::WITHER), 800, 1)
];
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
class RawChicken extends Food{
public function __construct(int $meta = 0){
@ -39,7 +40,7 @@ class RawChicken extends Food{
}
public function getAdditionalEffects() : array{
return mt_rand(0, 9) < 3 ? [Effect::getEffect(Effect::HUNGER)->setDuration(600)] : [];
return mt_rand(0, 9) < 3 ? [new EffectInstance(Effect::getEffect(Effect::HUNGER), 600)] : [];
}
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
class RottenFlesh extends Food{
@ -42,7 +43,7 @@ class RottenFlesh extends Food{
public function getAdditionalEffects() : array{
if(lcg_value() <= 0.8){
return [
Effect::getEffect(Effect::HUNGER)->setDuration(600)
new EffectInstance(Effect::getEffect(Effect::HUNGER), 600)
];
}

View File

@ -24,6 +24,7 @@ declare(strict_types=1);
namespace pocketmine\item;
use pocketmine\entity\Effect;
use pocketmine\entity\EffectInstance;
class SpiderEye extends Food{
public function __construct(int $meta = 0){
@ -39,6 +40,6 @@ class SpiderEye extends Food{
}
public function getAdditionalEffects() : array{
return [Effect::getEffect(Effect::POISON)->setDuration(80)];
return [new EffectInstance(Effect::getEffect(Effect::POISON), 80)];
}
}

View File

@ -1,141 +0,0 @@
{
"speed": {
"id": 1,
"color": "#FF7CAFC6",
"name": "moveSpeed"
},
"slowness": {
"id": 2,
"color": "#FF5A6C81",
"name": "moveSlowdown",
"isBad": true
},
"haste": {
"id": 3,
"color": "#FFD9C043",
"name": "digSpeed"
},
"mining_fatigue": {
"id": 4,
"color": "#FF4A4217",
"name": "digSlowDown",
"isBad": true
},
"strength": {
"id": 5,
"color": "#FF932423",
"name": "damageBoost"
},
"instant_health": {
"id": 6,
"color": "#FFF82423",
"name": "heal",
"default_duration": 1,
"has_bubbles": false
},
"instant_damage": {
"id": 7,
"color": "#FF430A09",
"name": "harm",
"isBad": true,
"default_duration": 1,
"has_bubbles": false
},
"jump_boost": {
"id": 8,
"color": "#FF22FF4C",
"name": "jump"
},
"nausea": {
"id": 9,
"color": "#FF551D4A",
"name": "confusion",
"isBad": true
},
"regeneration": {
"id": 10,
"color": "#FFCD5CAB",
"name": "regeneration"
},
"resistance": {
"id": 11,
"color": "#FF99453A",
"name": "resistance"
},
"fire_resistance": {
"id": 12,
"color": "#FFE49A3A",
"name": "fireResistance"
},
"water_breathing": {
"id": 13,
"color": "#FF2E5299",
"name": "waterBreathing"
},
"invisibility": {
"id": 14,
"color": "#FF7F8392",
"name": "invisibility"
},
"blindness": {
"id": 15,
"color": "#FF1F1F23",
"name": "blindness",
"isBad": true
},
"night_vision": {
"id": 16,
"color": "#FF1F1FA1",
"name": "nightVision"
},
"hunger": {
"id": 17,
"color": "#FF587653",
"name": "hunger",
"isBad": true
},
"weakness": {
"id": 18,
"color": "#FF484D48",
"name": "weakness",
"isBad": true
},
"poison": {
"id": 19,
"color": "#FF4E9331",
"name": "poison",
"isBad": true
},
"wither": {
"id": 20,
"color": "#FF352A27",
"name": "wither",
"isBad": true
},
"health_boost": {
"id": 21,
"color": "#FFF87D23",
"name": "healthBoost"
},
"absorption": {
"id": 22,
"color": "#FF2552A5",
"name": "absorption"
},
"saturation": {
"id": 23,
"color": "#FFF82423",
"name": "saturation"
},
"levitation": {
"id": 24,
"color": "#FFCEFFFF",
"name": "levitation"
},
"fatal_poison": {
"id": 25,
"color": "#FF4E9331",
"name": "poison",
"isBad": true
}
}