mirror of
https://github.com/pmmp/PocketMine-MP.git
synced 2025-09-02 08:13:10 +00:00
Extracted an EffectManager unit from Living
This commit is contained in:
parent
fe850a184c
commit
f356bf0893
@ -154,6 +154,17 @@ This version features substantial changes to the network system, improving coher
|
|||||||
- `Entity->entityBaseTick()` is now `protected`.
|
- `Entity->entityBaseTick()` is now `protected`.
|
||||||
- `Entity->move()` is now `protected`.
|
- `Entity->move()` is now `protected`.
|
||||||
- `Living->knockBack()` now accepts `float, float, float` (the first two parameters have been removed).
|
- `Living->knockBack()` now accepts `float, float, float` (the first two parameters have been removed).
|
||||||
|
- `Living->getEffects()` now returns `EffectManager` instead of `Effect[]`.
|
||||||
|
- The following classes have been added:
|
||||||
|
- `effect\EffectManager`: contains effect-management functionality extracted from `Living`
|
||||||
|
- The following API methods have been moved / renamed:
|
||||||
|
- `Living->removeAllEffects()` -> `EffectManager->clear()`
|
||||||
|
- `Living->removeEffect()` -> `EffectManager->remove()`
|
||||||
|
- `Living->addEffect()` -> `EffectManager->add()`
|
||||||
|
- `Living->getEffect()` -> `EffectManager->get()`
|
||||||
|
- `Living->hasEffect()` -> `EffectManager->has()`
|
||||||
|
- `Living->hasEffects()` -> `EffectManager->hasEffects()`
|
||||||
|
- `Living->getEffects()` -> `EffectManager->all()`
|
||||||
- The following classes have been removed:
|
- The following classes have been removed:
|
||||||
- `Creature`
|
- `Creature`
|
||||||
- `Damageable`
|
- `Damageable`
|
||||||
|
@ -59,11 +59,10 @@ class EffectCommand extends VanillaCommand{
|
|||||||
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound"));
|
$sender->sendMessage(new TranslationContainer(TextFormat::RED . "%commands.generic.player.notFound"));
|
||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
$effectManager = $player->getEffects();
|
||||||
|
|
||||||
if(strtolower($args[1]) === "clear"){
|
if(strtolower($args[1]) === "clear"){
|
||||||
foreach($player->getEffects() as $effect){
|
$effectManager->clear();
|
||||||
$player->removeEffect($effect->getType());
|
|
||||||
}
|
|
||||||
|
|
||||||
$sender->sendMessage(new TranslationContainer("commands.effect.success.removed.all", [$player->getDisplayName()]));
|
$sender->sendMessage(new TranslationContainer("commands.effect.success.removed.all", [$player->getDisplayName()]));
|
||||||
return true;
|
return true;
|
||||||
@ -107,8 +106,8 @@ class EffectCommand extends VanillaCommand{
|
|||||||
}
|
}
|
||||||
|
|
||||||
if($duration === 0){
|
if($duration === 0){
|
||||||
if(!$player->hasEffect($effect)){
|
if(!$effectManager->has($effect)){
|
||||||
if(count($player->getEffects()) === 0){
|
if(count($effectManager->all()) === 0){
|
||||||
$sender->sendMessage(new TranslationContainer("commands.effect.failure.notActive.all", [$player->getDisplayName()]));
|
$sender->sendMessage(new TranslationContainer("commands.effect.failure.notActive.all", [$player->getDisplayName()]));
|
||||||
}else{
|
}else{
|
||||||
$sender->sendMessage(new TranslationContainer("commands.effect.failure.notActive", [$effect->getName(), $player->getDisplayName()]));
|
$sender->sendMessage(new TranslationContainer("commands.effect.failure.notActive", [$effect->getName(), $player->getDisplayName()]));
|
||||||
@ -116,11 +115,11 @@ class EffectCommand extends VanillaCommand{
|
|||||||
return true;
|
return true;
|
||||||
}
|
}
|
||||||
|
|
||||||
$player->removeEffect($effect);
|
$effectManager->remove($effect);
|
||||||
$sender->sendMessage(new TranslationContainer("commands.effect.success.removed", [$effect->getName(), $player->getDisplayName()]));
|
$sender->sendMessage(new TranslationContainer("commands.effect.success.removed", [$effect->getName(), $player->getDisplayName()]));
|
||||||
}else{
|
}else{
|
||||||
$instance = new EffectInstance($effect, $duration, $amplification, $visible);
|
$instance = new EffectInstance($effect, $duration, $amplification, $visible);
|
||||||
$player->addEffect($instance);
|
$effectManager->add($instance);
|
||||||
self::broadcastCommandMessage($sender, new TranslationContainer("%commands.effect.success", [$effect->getName(), $instance->getAmplifier(), $player->getDisplayName(), $instance->getDuration() / 20, $effect->getId()]));
|
self::broadcastCommandMessage($sender, new TranslationContainer("%commands.effect.success", [$effect->getName(), $instance->getAmplifier(), $player->getDisplayName(), $instance->getDuration() / 20, $effect->getId()]));
|
||||||
}
|
}
|
||||||
|
|
||||||
|
@ -732,11 +732,11 @@ class Human extends Living implements ProjectileSource, InventoryHolder{
|
|||||||
parent::applyPostDamageEffects($source);
|
parent::applyPostDamageEffects($source);
|
||||||
$totemModifier = $source->getModifier(EntityDamageEvent::MODIFIER_TOTEM);
|
$totemModifier = $source->getModifier(EntityDamageEvent::MODIFIER_TOTEM);
|
||||||
if($totemModifier < 0){ //Totem prevented death
|
if($totemModifier < 0){ //Totem prevented death
|
||||||
$this->removeAllEffects();
|
$this->effectManager->clear();
|
||||||
|
|
||||||
$this->addEffect(new EffectInstance(Effect::REGENERATION(), 40 * 20, 1));
|
$this->effectManager->add(new EffectInstance(Effect::REGENERATION(), 40 * 20, 1));
|
||||||
$this->addEffect(new EffectInstance(Effect::FIRE_RESISTANCE(), 40 * 20, 1));
|
$this->effectManager->add(new EffectInstance(Effect::FIRE_RESISTANCE(), 40 * 20, 1));
|
||||||
$this->addEffect(new EffectInstance(Effect::ABSORPTION(), 5 * 20, 1));
|
$this->effectManager->add(new EffectInstance(Effect::ABSORPTION(), 5 * 20, 1));
|
||||||
|
|
||||||
$this->broadcastEntityEvent(EntityEventPacket::CONSUME_TOTEM);
|
$this->broadcastEntityEvent(EntityEventPacket::CONSUME_TOTEM);
|
||||||
$this->world->addSound($this->add(0, $this->eyeHeight, 0), new TotemUseSound());
|
$this->world->addSound($this->add(0, $this->eyeHeight, 0), new TotemUseSound());
|
||||||
|
@ -26,6 +26,7 @@ namespace pocketmine\entity;
|
|||||||
use pocketmine\block\Block;
|
use pocketmine\block\Block;
|
||||||
use pocketmine\entity\effect\Effect;
|
use pocketmine\entity\effect\Effect;
|
||||||
use pocketmine\entity\effect\EffectInstance;
|
use pocketmine\entity\effect\EffectInstance;
|
||||||
|
use pocketmine\entity\effect\EffectManager;
|
||||||
use pocketmine\event\entity\EntityDamageByChildEntityEvent;
|
use pocketmine\event\entity\EntityDamageByChildEntityEvent;
|
||||||
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
use pocketmine\event\entity\EntityDamageByEntityEvent;
|
||||||
use pocketmine\event\entity\EntityDamageEvent;
|
use pocketmine\event\entity\EntityDamageEvent;
|
||||||
@ -81,8 +82,8 @@ abstract class Living extends Entity{
|
|||||||
|
|
||||||
protected $jumpVelocity = 0.42;
|
protected $jumpVelocity = 0.42;
|
||||||
|
|
||||||
/** @var EffectInstance[] */
|
/** @var EffectManager */
|
||||||
protected $effects = [];
|
protected $effectManager;
|
||||||
|
|
||||||
/** @var ArmorInventory */
|
/** @var ArmorInventory */
|
||||||
protected $armorInventory;
|
protected $armorInventory;
|
||||||
@ -92,6 +93,8 @@ abstract class Living extends Entity{
|
|||||||
protected function initEntity(CompoundTag $nbt) : void{
|
protected function initEntity(CompoundTag $nbt) : void{
|
||||||
parent::initEntity($nbt);
|
parent::initEntity($nbt);
|
||||||
|
|
||||||
|
$this->effectManager = new EffectManager($this);
|
||||||
|
|
||||||
$this->armorInventory = new ArmorInventory($this);
|
$this->armorInventory = new ArmorInventory($this);
|
||||||
//TODO: load/save armor inventory contents
|
//TODO: load/save armor inventory contents
|
||||||
$this->armorInventory->addChangeListeners(CallbackInventoryChangeListener::onAnyChange(
|
$this->armorInventory->addChangeListeners(CallbackInventoryChangeListener::onAnyChange(
|
||||||
@ -122,7 +125,7 @@ abstract class Living extends Entity{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
|
|
||||||
$this->addEffect(new EffectInstance(
|
$this->effectManager->add(new EffectInstance(
|
||||||
$effect,
|
$effect,
|
||||||
$e->getInt("Duration"),
|
$e->getInt("Duration"),
|
||||||
Binary::unsignByte($e->getByte("Amplifier")),
|
Binary::unsignByte($e->getByte("Amplifier")),
|
||||||
@ -171,9 +174,9 @@ abstract class Living extends Entity{
|
|||||||
$nbt = parent::saveNBT();
|
$nbt = parent::saveNBT();
|
||||||
$nbt->setFloat("Health", $this->getHealth());
|
$nbt->setFloat("Health", $this->getHealth());
|
||||||
|
|
||||||
if(count($this->effects) > 0){
|
if(!empty($this->effectManager->all())){
|
||||||
$effects = [];
|
$effects = [];
|
||||||
foreach($this->effects as $effect){
|
foreach($this->effectManager->all() as $effect){
|
||||||
$effects[] = CompoundTag::create()
|
$effects[] = CompoundTag::create()
|
||||||
->setByte("Id", $effect->getId())
|
->setByte("Id", $effect->getId())
|
||||||
->setByte("Amplifier", Binary::signByte($effect->getAmplifier()))
|
->setByte("Amplifier", Binary::signByte($effect->getAmplifier()))
|
||||||
@ -195,162 +198,25 @@ abstract class Living extends Entity{
|
|||||||
//return $this->getLevel()->rayTraceBlocks(Vector3::createVector($this->x, $this->y + $this->height, $this->z), Vector3::createVector($entity->x, $entity->y + $entity->height, $entity->z)) === null;
|
//return $this->getLevel()->rayTraceBlocks(Vector3::createVector($this->x, $this->y + $this->height, $this->z), Vector3::createVector($entity->x, $entity->y + $entity->height, $entity->z)) === null;
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
public function getEffects() : EffectManager{
|
||||||
* Returns an array of Effects currently active on the mob.
|
return $this->effectManager;
|
||||||
* @return EffectInstance[]
|
|
||||||
*/
|
|
||||||
public function getEffects() : array{
|
|
||||||
return $this->effects;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Removes all effects from the mob.
|
* @internal
|
||||||
*/
|
|
||||||
public function removeAllEffects() : void{
|
|
||||||
foreach($this->effects as $effect){
|
|
||||||
$this->removeEffect($effect->getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Removes the effect with the specified ID from the mob.
|
|
||||||
*
|
|
||||||
* @param Effect $effectType
|
|
||||||
*/
|
|
||||||
public function removeEffect(Effect $effectType) : void{
|
|
||||||
$index = $effectType->getId();
|
|
||||||
if(isset($this->effects[$index])){
|
|
||||||
$effect = $this->effects[$index];
|
|
||||||
$hasExpired = $effect->hasExpired();
|
|
||||||
$ev = new EntityEffectRemoveEvent($this, $effect);
|
|
||||||
$ev->call();
|
|
||||||
if($ev->isCancelled()){
|
|
||||||
if($hasExpired and !$ev->getEffect()->hasExpired()){ //altered duration of an expired effect to make it not get removed
|
|
||||||
$this->onEffectAdded($ev->getEffect(), true);
|
|
||||||
}
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
unset($this->effects[$index]);
|
|
||||||
$effect->getType()->remove($this, $effect);
|
|
||||||
$this->onEffectRemoved($effect);
|
|
||||||
|
|
||||||
$this->recalculateEffectColor();
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns the effect instance active on this entity with the specified ID, or null if the mob does not have the
|
|
||||||
* effect.
|
|
||||||
*
|
|
||||||
* @param Effect $effect
|
|
||||||
*
|
|
||||||
* @return EffectInstance|null
|
|
||||||
*/
|
|
||||||
public function getEffect(Effect $effect) : ?EffectInstance{
|
|
||||||
return $this->effects[$effect->getId()] ?? null;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the specified effect is active on the mob.
|
|
||||||
*
|
|
||||||
* @param Effect $effect
|
|
||||||
*
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasEffect(Effect $effect) : bool{
|
|
||||||
return isset($this->effects[$effect->getId()]);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* Returns whether the mob has any active effects.
|
|
||||||
* @return bool
|
|
||||||
*/
|
|
||||||
public function hasEffects() : bool{
|
|
||||||
return !empty($this->effects);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* 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.
|
|
||||||
*
|
*
|
||||||
* @param EffectInstance $effect
|
* @param EffectInstance $effect
|
||||||
*
|
* @param bool $replacesOldEffect
|
||||||
* @return bool whether the effect has been successfully applied.
|
|
||||||
*/
|
*/
|
||||||
public function addEffect(EffectInstance $effect) : bool{
|
public function onEffectAdded(EffectInstance $effect, bool $replacesOldEffect) : void{
|
||||||
$oldEffect = null;
|
|
||||||
$cancelled = false;
|
|
||||||
|
|
||||||
$index = $effect->getType()->getId();
|
|
||||||
if(isset($this->effects[$index])){
|
|
||||||
$oldEffect = $this->effects[$index];
|
|
||||||
if(
|
|
||||||
abs($effect->getAmplifier()) < $oldEffect->getAmplifier()
|
|
||||||
or (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier()) and $effect->getDuration() < $oldEffect->getDuration())
|
|
||||||
){
|
|
||||||
$cancelled = true;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
$ev = new EntityEffectAddEvent($this, $effect, $oldEffect);
|
|
||||||
$ev->setCancelled($cancelled);
|
|
||||||
|
|
||||||
$ev->call();
|
|
||||||
if($ev->isCancelled()){
|
|
||||||
return false;
|
|
||||||
}
|
|
||||||
|
|
||||||
if($oldEffect !== null){
|
|
||||||
$oldEffect->getType()->remove($this, $oldEffect);
|
|
||||||
}
|
|
||||||
|
|
||||||
$effect->getType()->add($this, $effect);
|
|
||||||
$this->onEffectAdded($effect, $oldEffect !== null);
|
|
||||||
|
|
||||||
$this->effects[$index] = $effect;
|
|
||||||
|
|
||||||
$this->recalculateEffectColor();
|
|
||||||
|
|
||||||
return true;
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Recalculates the mob's potion bubbles colour based on the active effects.
|
* @internal
|
||||||
|
* @param EffectInstance $effect
|
||||||
*/
|
*/
|
||||||
protected function recalculateEffectColor() : void{
|
public function onEffectRemoved(EffectInstance $effect) : void{
|
||||||
/** @var Color[] $colors */
|
|
||||||
$colors = [];
|
|
||||||
$ambient = true;
|
|
||||||
foreach($this->effects as $effect){
|
|
||||||
if($effect->isVisible() and $effect->getType()->hasBubbles()){
|
|
||||||
$level = $effect->getEffectLevel();
|
|
||||||
$color = $effect->getColor();
|
|
||||||
for($i = 0; $i < $level; ++$i){
|
|
||||||
$colors[] = $color;
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!$effect->isAmbient()){
|
|
||||||
$ambient = false;
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
if(!empty($colors)){
|
|
||||||
$this->propertyManager->setInt(EntityMetadataProperties::POTION_COLOR, Color::mix(...$colors)->toARGB());
|
|
||||||
$this->propertyManager->setByte(EntityMetadataProperties::POTION_AMBIENT, $ambient ? 1 : 0);
|
|
||||||
}else{
|
|
||||||
$this->propertyManager->setInt(EntityMetadataProperties::POTION_COLOR, 0);
|
|
||||||
$this->propertyManager->setByte(EntityMetadataProperties::POTION_AMBIENT, 0);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function onEffectAdded(EffectInstance $effect, bool $replacesOldEffect) : void{
|
|
||||||
|
|
||||||
}
|
|
||||||
|
|
||||||
protected function onEffectRemoved(EffectInstance $effect) : void{
|
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -364,7 +230,7 @@ abstract class Living extends Entity{
|
|||||||
*/
|
*/
|
||||||
public function consumeObject(Consumable $consumable) : bool{
|
public function consumeObject(Consumable $consumable) : bool{
|
||||||
foreach($consumable->getAdditionalEffects() as $effect){
|
foreach($consumable->getAdditionalEffects() as $effect){
|
||||||
$this->addEffect($effect);
|
$this->effectManager->add($effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
$consumable->onConsume($this);
|
$consumable->onConsume($this);
|
||||||
@ -377,7 +243,7 @@ abstract class Living extends Entity{
|
|||||||
* @return float
|
* @return float
|
||||||
*/
|
*/
|
||||||
public function getJumpVelocity() : float{
|
public function getJumpVelocity() : float{
|
||||||
return $this->jumpVelocity + ($this->hasEffect(Effect::JUMP_BOOST()) ? ($this->getEffect(Effect::JUMP_BOOST())->getEffectLevel() / 10) : 0);
|
return $this->jumpVelocity + ($this->effectManager->has(Effect::JUMP_BOOST()) ? ($this->effectManager->get(Effect::JUMP_BOOST())->getEffectLevel() / 10) : 0);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@ -390,7 +256,7 @@ abstract class Living extends Entity{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function fall(float $fallDistance) : void{
|
public function fall(float $fallDistance) : void{
|
||||||
$damage = ceil($fallDistance - 3 - ($this->hasEffect(Effect::JUMP_BOOST()) ? $this->getEffect(Effect::JUMP_BOOST())->getEffectLevel() : 0));
|
$damage = ceil($fallDistance - 3 - ($this->effectManager->has(Effect::JUMP_BOOST()) ? $this->effectManager->get(Effect::JUMP_BOOST())->getEffectLevel() : 0));
|
||||||
if($damage > 0){
|
if($damage > 0){
|
||||||
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
|
$ev = new EntityDamageEvent($this, EntityDamageEvent::CAUSE_FALL, $damage);
|
||||||
$this->attack($ev);
|
$this->attack($ev);
|
||||||
@ -453,8 +319,8 @@ abstract class Living extends Entity{
|
|||||||
}
|
}
|
||||||
|
|
||||||
$cause = $source->getCause();
|
$cause = $source->getCause();
|
||||||
if($this->hasEffect(Effect::RESISTANCE()) and $cause !== EntityDamageEvent::CAUSE_VOID and $cause !== EntityDamageEvent::CAUSE_SUICIDE){
|
if($this->effectManager->has(Effect::RESISTANCE()) and $cause !== EntityDamageEvent::CAUSE_VOID and $cause !== EntityDamageEvent::CAUSE_SUICIDE){
|
||||||
$source->setModifier(-$source->getFinalDamage() * min(1, 0.2 * $this->getEffect(Effect::RESISTANCE())->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
|
$source->setModifier(-$source->getFinalDamage() * min(1, 0.2 * $this->effectManager->get(Effect::RESISTANCE())->getEffectLevel()), EntityDamageEvent::MODIFIER_RESISTANCE);
|
||||||
}
|
}
|
||||||
|
|
||||||
$totalEpf = 0;
|
$totalEpf = 0;
|
||||||
@ -534,7 +400,7 @@ abstract class Living extends Entity{
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if($this->hasEffect(Effect::FIRE_RESISTANCE()) and (
|
if($this->effectManager->has(Effect::FIRE_RESISTANCE()) and (
|
||||||
$source->getCause() === EntityDamageEvent::CAUSE_FIRE
|
$source->getCause() === EntityDamageEvent::CAUSE_FIRE
|
||||||
or $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK
|
or $source->getCause() === EntityDamageEvent::CAUSE_FIRE_TICK
|
||||||
or $source->getCause() === EntityDamageEvent::CAUSE_LAVA
|
or $source->getCause() === EntityDamageEvent::CAUSE_LAVA
|
||||||
@ -656,7 +522,7 @@ abstract class Living extends Entity{
|
|||||||
$hasUpdate = parent::entityBaseTick($tickDiff);
|
$hasUpdate = parent::entityBaseTick($tickDiff);
|
||||||
|
|
||||||
if($this->isAlive()){
|
if($this->isAlive()){
|
||||||
if($this->doEffectsTick($tickDiff)){
|
if($this->effectManager->tick($tickDiff)){
|
||||||
$hasUpdate = true;
|
$hasUpdate = true;
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -680,21 +546,6 @@ abstract class Living extends Entity{
|
|||||||
return $hasUpdate;
|
return $hasUpdate;
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function doEffectsTick(int $tickDiff = 1) : bool{
|
|
||||||
foreach($this->effects as $instance){
|
|
||||||
$type = $instance->getType();
|
|
||||||
if($type->canTick($instance)){
|
|
||||||
$type->applyEffect($this, $instance);
|
|
||||||
}
|
|
||||||
$instance->decreaseDuration($tickDiff);
|
|
||||||
if($instance->hasExpired()){
|
|
||||||
$this->removeEffect($instance->getType());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
return !empty($this->effects);
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water.
|
* Ticks the entity's air supply, consuming it when underwater and regenerating it when out of water.
|
||||||
*
|
*
|
||||||
@ -739,7 +590,7 @@ abstract class Living extends Entity{
|
|||||||
* @return bool
|
* @return bool
|
||||||
*/
|
*/
|
||||||
public function canBreathe() : bool{
|
public function canBreathe() : bool{
|
||||||
return $this->hasEffect(Effect::WATER_BREATHING()) or $this->hasEffect(Effect::CONDUIT_POWER()) or !$this->isUnderwater();
|
return $this->effectManager->has(Effect::WATER_BREATHING()) or $this->effectManager->has(Effect::CONDUIT_POWER()) or !$this->isUnderwater();
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
203
src/pocketmine/entity/effect/EffectManager.php
Normal file
203
src/pocketmine/entity/effect/EffectManager.php
Normal file
@ -0,0 +1,203 @@
|
|||||||
|
<?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\effect;
|
||||||
|
|
||||||
|
use function abs;
|
||||||
|
use pocketmine\entity\Living;
|
||||||
|
use pocketmine\event\entity\EntityEffectAddEvent;
|
||||||
|
use pocketmine\event\entity\EntityEffectRemoveEvent;
|
||||||
|
use pocketmine\network\mcpe\protocol\types\EntityMetadataProperties;
|
||||||
|
use pocketmine\utils\Color;
|
||||||
|
|
||||||
|
class EffectManager{
|
||||||
|
|
||||||
|
/** @var Living */
|
||||||
|
private $entity;
|
||||||
|
|
||||||
|
/** @var EffectInstance[] */
|
||||||
|
protected $effects = [];
|
||||||
|
|
||||||
|
public function __construct(Living $entity){
|
||||||
|
$this->entity = $entity;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Removes the effect with the specified ID from the mob.
|
||||||
|
*
|
||||||
|
* @param Effect $effectType
|
||||||
|
*/
|
||||||
|
public function remove(Effect $effectType) : void{
|
||||||
|
$index = $effectType->getId();
|
||||||
|
if(isset($this->effects[$index])){
|
||||||
|
$effect = $this->effects[$index];
|
||||||
|
$hasExpired = $effect->hasExpired();
|
||||||
|
$ev = new EntityEffectRemoveEvent($this->entity, $effect);
|
||||||
|
$ev->call();
|
||||||
|
if($ev->isCancelled()){
|
||||||
|
if($hasExpired and !$ev->getEffect()->hasExpired()){ //altered duration of an expired effect to make it not get removed
|
||||||
|
$this->entity->onEffectAdded($ev->getEffect(), true);
|
||||||
|
}
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
unset($this->effects[$index]);
|
||||||
|
$effect->getType()->remove($this->entity, $effect);
|
||||||
|
$this->entity->onEffectRemoved($effect);
|
||||||
|
|
||||||
|
$this->recalculateEffectColor();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns the effect instance active on this entity with the specified ID, or null if the mob does not have the
|
||||||
|
* effect.
|
||||||
|
*
|
||||||
|
* @param Effect $effect
|
||||||
|
*
|
||||||
|
* @return EffectInstance|null
|
||||||
|
*/
|
||||||
|
public function get(Effect $effect) : ?EffectInstance{
|
||||||
|
return $this->effects[$effect->getId()] ?? null;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Returns whether the specified effect is active on the mob.
|
||||||
|
*
|
||||||
|
* @param Effect $effect
|
||||||
|
*
|
||||||
|
* @return bool
|
||||||
|
*/
|
||||||
|
public function has(Effect $effect) : bool{
|
||||||
|
return isset($this->effects[$effect->getId()]);
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* 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.
|
||||||
|
*
|
||||||
|
* @param EffectInstance $effect
|
||||||
|
*
|
||||||
|
* @return bool whether the effect has been successfully applied.
|
||||||
|
*/
|
||||||
|
public function add(EffectInstance $effect) : bool{
|
||||||
|
$oldEffect = null;
|
||||||
|
$cancelled = false;
|
||||||
|
|
||||||
|
$index = $effect->getType()->getId();
|
||||||
|
if(isset($this->effects[$index])){
|
||||||
|
$oldEffect = $this->effects[$index];
|
||||||
|
if(
|
||||||
|
abs($effect->getAmplifier()) < $oldEffect->getAmplifier()
|
||||||
|
or (abs($effect->getAmplifier()) === abs($oldEffect->getAmplifier()) and $effect->getDuration() < $oldEffect->getDuration())
|
||||||
|
){
|
||||||
|
$cancelled = true;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$ev = new EntityEffectAddEvent($this->entity, $effect, $oldEffect);
|
||||||
|
$ev->setCancelled($cancelled);
|
||||||
|
|
||||||
|
$ev->call();
|
||||||
|
if($ev->isCancelled()){
|
||||||
|
return false;
|
||||||
|
}
|
||||||
|
|
||||||
|
if($oldEffect !== null){
|
||||||
|
$oldEffect->getType()->remove($this->entity, $oldEffect);
|
||||||
|
}
|
||||||
|
|
||||||
|
$effect->getType()->add($this->entity, $effect);
|
||||||
|
$this->entity->onEffectAdded($effect, $oldEffect !== null);
|
||||||
|
|
||||||
|
$this->effects[$index] = $effect;
|
||||||
|
|
||||||
|
$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() and $effect->getType()->hasBubbles()){
|
||||||
|
$level = $effect->getEffectLevel();
|
||||||
|
$color = $effect->getColor();
|
||||||
|
for($i = 0; $i < $level; ++$i){
|
||||||
|
$colors[] = $color;
|
||||||
|
}
|
||||||
|
|
||||||
|
if(!$effect->isAmbient()){
|
||||||
|
$ambient = false;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
$propManager = $this->entity->getDataPropertyManager();
|
||||||
|
if(!empty($colors)){
|
||||||
|
$propManager->setInt(EntityMetadataProperties::POTION_COLOR, Color::mix(...$colors)->toARGB());
|
||||||
|
$propManager->setByte(EntityMetadataProperties::POTION_AMBIENT, $ambient ? 1 : 0);
|
||||||
|
}else{
|
||||||
|
$propManager->setInt(EntityMetadataProperties::POTION_COLOR, 0);
|
||||||
|
$propManager->setByte(EntityMetadataProperties::POTION_AMBIENT, 0);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public function tick(int $tickDiff = 1) : bool{
|
||||||
|
foreach($this->effects as $instance){
|
||||||
|
$type = $instance->getType();
|
||||||
|
if($type->canTick($instance)){
|
||||||
|
$type->applyEffect($this->entity, $instance);
|
||||||
|
}
|
||||||
|
$instance->decreaseDuration($tickDiff);
|
||||||
|
if($instance->hasExpired()){
|
||||||
|
$this->remove($instance->getType());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
return !empty($this->effects);
|
||||||
|
}
|
||||||
|
}
|
@ -109,7 +109,7 @@ class SplashPotion extends Throwable{
|
|||||||
continue;
|
continue;
|
||||||
}
|
}
|
||||||
$effect->setDuration($newDuration);
|
$effect->setDuration($newDuration);
|
||||||
$entity->addEffect($effect);
|
$entity->getEffects()->add($effect);
|
||||||
}else{
|
}else{
|
||||||
$effect->getType()->applyEffect($entity, $effect, $distanceMultiplier, $this);
|
$effect->getType()->applyEffect($entity, $effect, $distanceMultiplier, $this);
|
||||||
}
|
}
|
||||||
|
@ -53,12 +53,13 @@ class EntityDamageByEntityEvent extends EntityDamageEvent{
|
|||||||
|
|
||||||
protected function addAttackerModifiers(Entity $damager) : void{
|
protected function addAttackerModifiers(Entity $damager) : void{
|
||||||
if($damager instanceof Living){ //TODO: move this to entity classes
|
if($damager instanceof Living){ //TODO: move this to entity classes
|
||||||
if($damager->hasEffect(Effect::STRENGTH())){
|
$effects = $damager->getEffects();
|
||||||
$this->setModifier($this->getBaseDamage() * 0.3 * $damager->getEffect(Effect::STRENGTH())->getEffectLevel(), self::MODIFIER_STRENGTH);
|
if($effects->has(Effect::STRENGTH())){
|
||||||
|
$this->setModifier($this->getBaseDamage() * 0.3 * $effects->get(Effect::STRENGTH())->getEffectLevel(), self::MODIFIER_STRENGTH);
|
||||||
}
|
}
|
||||||
|
|
||||||
if($damager->hasEffect(Effect::WEAKNESS())){
|
if($effects->has(Effect::WEAKNESS())){
|
||||||
$this->setModifier(-($this->getBaseDamage() * 0.2 * $damager->getEffect(Effect::WEAKNESS())->getEffectLevel()), self::MODIFIER_WEAKNESS);
|
$this->setModifier(-($this->getBaseDamage() * 0.2 * $effects->get(Effect::WEAKNESS())->getEffectLevel()), self::MODIFIER_WEAKNESS);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -40,6 +40,6 @@ class MilkBucket extends Item implements Consumable{
|
|||||||
}
|
}
|
||||||
|
|
||||||
public function onConsume(Living $consumer) : void{
|
public function onConsume(Living $consumer) : void{
|
||||||
$consumer->removeAllEffects();
|
$consumer->getEffects()->clear();
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
@ -86,7 +86,7 @@ class PreSpawnPacketHandler extends PacketHandler{
|
|||||||
$this->session->syncAttributes($this->player, true);
|
$this->session->syncAttributes($this->player, true);
|
||||||
$this->session->syncAvailableCommands();
|
$this->session->syncAvailableCommands();
|
||||||
$this->session->syncAdventureSettings($this->player);
|
$this->session->syncAdventureSettings($this->player);
|
||||||
foreach($this->player->getEffects() as $effect){
|
foreach($this->player->getEffects()->all() as $effect){
|
||||||
$this->session->onEntityEffectAdded($this->player, $effect, false);
|
$this->session->onEntityEffectAdded($this->player, $effect, false);
|
||||||
}
|
}
|
||||||
$this->player->sendData($this->player);
|
$this->player->sendData($this->player);
|
||||||
|
@ -1543,11 +1543,11 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
|
|||||||
return $this->isCreative() or parent::canBreathe();
|
return $this->isCreative() or parent::canBreathe();
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function onEffectAdded(EffectInstance $effect, bool $replacesOldEffect) : void{
|
public function onEffectAdded(EffectInstance $effect, bool $replacesOldEffect) : void{
|
||||||
$this->networkSession->onEntityEffectAdded($this, $effect, $replacesOldEffect);
|
$this->networkSession->onEntityEffectAdded($this, $effect, $replacesOldEffect);
|
||||||
}
|
}
|
||||||
|
|
||||||
protected function onEffectRemoved(EffectInstance $effect) : void{
|
public function onEffectRemoved(EffectInstance $effect) : void{
|
||||||
$this->networkSession->onEntityEffectRemoved($this, $effect);
|
$this->networkSession->onEntityEffectRemoved($this, $effect);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -1911,7 +1911,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
|
|||||||
}
|
}
|
||||||
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
|
$ev->setModifier($meleeEnchantmentDamage, EntityDamageEvent::MODIFIER_WEAPON_ENCHANTMENTS);
|
||||||
|
|
||||||
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->hasEffect(Effect::BLINDNESS()) and !$this->isUnderwater()){
|
if(!$this->isSprinting() and !$this->isFlying() and $this->fallDistance > 0 and !$this->effectManager->has(Effect::BLINDNESS()) and !$this->isUnderwater()){
|
||||||
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
$ev->setModifier($ev->getFinalDamage() / 2, EntityDamageEvent::MODIFIER_CRITICAL);
|
||||||
}
|
}
|
||||||
|
|
||||||
@ -2426,7 +2426,7 @@ class Player extends Human implements CommandSender, ChunkLoader, ChunkListener,
|
|||||||
$this->deadTicks = 0;
|
$this->deadTicks = 0;
|
||||||
$this->noDamageTicks = 60;
|
$this->noDamageTicks = 60;
|
||||||
|
|
||||||
$this->removeAllEffects();
|
$this->effectManager->clear();
|
||||||
$this->setHealth($this->getMaxHealth());
|
$this->setHealth($this->getMaxHealth());
|
||||||
|
|
||||||
foreach($this->attributeMap->getAll() as $attr){
|
foreach($this->attributeMap->getAll() as $attr){
|
||||||
|
Loading…
x
Reference in New Issue
Block a user