diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/EffectList.java similarity index 99% rename from L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/CharEffectList.java rename to L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/EffectList.java index 6516b615db..91f9094627 100644 --- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/EffectList.java @@ -61,9 +61,9 @@ import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; * Methods may resemble List interface, although it doesn't implement such interface. * @author Zoey76 */ -public final class CharEffectList +public final class EffectList { - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); /** Queue containing all effects from buffs for this effect list. */ private volatile Queue _actives = new ConcurrentLinkedQueue<>(); /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ @@ -99,7 +99,7 @@ public final class CharEffectList * Constructor for effect list. * @param owner the creature that owns this effect list */ - public CharEffectList(Creature owner) + public EffectList(Creature owner) { _owner = owner; } diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/Creature.java index 97ba93127a..c088d0c400 100644 --- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/EffectList.java similarity index 99% rename from L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/CharEffectList.java rename to L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/EffectList.java index 6516b615db..91f9094627 100644 --- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/EffectList.java @@ -61,9 +61,9 @@ import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; * Methods may resemble List interface, although it doesn't implement such interface. * @author Zoey76 */ -public final class CharEffectList +public final class EffectList { - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); /** Queue containing all effects from buffs for this effect list. */ private volatile Queue _actives = new ConcurrentLinkedQueue<>(); /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ @@ -99,7 +99,7 @@ public final class CharEffectList * Constructor for effect list. * @param owner the creature that owns this effect list */ - public CharEffectList(Creature owner) + public EffectList(Creature owner) { _owner = owner; } diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/Creature.java index 97ba93127a..c088d0c400 100644 --- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/EffectList.java similarity index 99% rename from L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/CharEffectList.java rename to L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/EffectList.java index 6516b615db..91f9094627 100644 --- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/EffectList.java @@ -61,9 +61,9 @@ import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; * Methods may resemble List interface, although it doesn't implement such interface. * @author Zoey76 */ -public final class CharEffectList +public final class EffectList { - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); /** Queue containing all effects from buffs for this effect list. */ private volatile Queue _actives = new ConcurrentLinkedQueue<>(); /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ @@ -99,7 +99,7 @@ public final class CharEffectList * Constructor for effect list. * @param owner the creature that owns this effect list */ - public CharEffectList(Creature owner) + public EffectList(Creature owner) { _owner = owner; } diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/Creature.java index 97ba93127a..c088d0c400 100644 --- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/EffectList.java similarity index 99% rename from L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/CharEffectList.java rename to L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/EffectList.java index 6516b615db..91f9094627 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/EffectList.java @@ -61,9 +61,9 @@ import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; * Methods may resemble List interface, although it doesn't implement such interface. * @author Zoey76 */ -public final class CharEffectList +public final class EffectList { - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); /** Queue containing all effects from buffs for this effect list. */ private volatile Queue _actives = new ConcurrentLinkedQueue<>(); /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ @@ -99,7 +99,7 @@ public final class CharEffectList * Constructor for effect list. * @param owner the creature that owns this effect list */ - public CharEffectList(Creature owner) + public EffectList(Creature owner) { _owner = owner; } diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/Creature.java index 97ba93127a..c088d0c400 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java index 793e7ae65c..6d37f8dea4 100644 --- a/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java +++ b/L2J_Mobius_5.0_Salvation/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java @@ -16,7 +16,7 @@ */ package handlers.effecthandlers; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -75,7 +75,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackAeoreLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitAeoreLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) @@ -98,7 +98,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackSigelLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitSigelLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) @@ -121,7 +121,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackIssLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitIssLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) diff --git a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/Creature.java index 97ba93127a..c088d0c400 100644 --- a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_5.0_Salvation/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java index 793e7ae65c..6d37f8dea4 100644 --- a/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java +++ b/L2J_Mobius_5.5_EtinasFate/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java @@ -16,7 +16,7 @@ */ package handlers.effecthandlers; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -75,7 +75,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackAeoreLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitAeoreLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) @@ -98,7 +98,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackSigelLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitSigelLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) @@ -121,7 +121,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackIssLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitIssLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) diff --git a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/Creature.java index 97ba93127a..c088d0c400 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_5.5_EtinasFate/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java index 793e7ae65c..6d37f8dea4 100644 --- a/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java +++ b/L2J_Mobius_6.0_Fafurion/dist/game/data/scripts/handlers/effecthandlers/TrackLimitedSkill.java @@ -16,7 +16,7 @@ */ package handlers.effecthandlers; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -75,7 +75,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackAeoreLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitAeoreLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) @@ -98,7 +98,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackSigelLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitSigelLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) @@ -121,7 +121,7 @@ public class TrackLimitedSkill extends AbstractEffect private void trackIssLimit(Creature effector, Creature effected, Skill skill, int limitSkillId) { limitIssLevel = 0; - CharEffectList effectList = effected.getEffectList(); + EffectList effectList = effected.getEffectList(); for (BuffInfo debuff : effectList.getDebuffs()) { if (debuff.getSkill().getId() == limitSkillId) diff --git a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/Creature.java index cefaae0349..bf8746a90c 100644 --- a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4676,7 +4676,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index 86e1ccbf1f..ba32549f8e 100644 --- a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -783,7 +783,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_6.0_Fafurion/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlot.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlot.java index 58882d86e9..c4f53c0b79 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlot.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlot.java @@ -21,7 +21,7 @@ import java.util.EnumMap; import java.util.Map; import java.util.Map.Entry; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.conditions.Condition; @@ -80,7 +80,7 @@ public final class DispelBySlot extends AbstractEffect } final Creature effected = info.getEffected(); - final CharEffectList effectList = effected.getEffectList(); + final EffectList effectList = effected.getEffectList(); // There is no need to iterate over all buffs, // Just iterate once over all slots to dispel and get the buff with that abnormal if exists, // Operation of O(n) for the amount of slots to dispel (which is usually small) and O(1) to get the buff. diff --git a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlotProbability.java b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlotProbability.java index cb8fd9b743..195de097df 100644 --- a/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlotProbability.java +++ b/L2J_Mobius_CT_2.6_HighFive/dist/game/data/scripts/handlers/effecthandlers/DispelBySlotProbability.java @@ -22,7 +22,7 @@ import java.util.Map; import java.util.Map.Entry; import com.l2jmobius.commons.util.Rnd; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.conditions.Condition; @@ -83,7 +83,7 @@ public final class DispelBySlotProbability extends AbstractEffect } final Creature effected = info.getEffected(); - final CharEffectList effectList = effected.getEffectList(); + final EffectList effectList = effected.getEffectList(); // There is no need to iterate over all buffs, // Just iterate once over all slots to dispel and get the buff with that abnormal if exists, // Operation of O(n) for the amount of slots to dispel (which is usually small) and O(1) to get the buff. diff --git a/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/EffectList.java similarity index 95% rename from L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/CharEffectList.java rename to L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/EffectList.java index 6a0599126c..23b69bda7c 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/EffectList.java @@ -59,9 +59,9 @@ import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; * Methods may resemble List interface, although it doesn't implement such interface. * @author Zoey76 */ -public final class CharEffectList +public final class EffectList { - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); /** Queue containing all effects from buffs for this effect list. */ private volatile Queue _buffs = new ConcurrentLinkedQueue<>(); /** Queue containing all triggered skills for this effect list. */ @@ -101,7 +101,7 @@ public final class CharEffectList * Constructor for effect list. * @param owner the creature that owns this effect list */ - public CharEffectList(Creature owner) + public EffectList(Creature owner) { _owner = owner; } diff --git a/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/actor/Creature.java index 984d87ff4d..8f4e35ead8 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -59,7 +59,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TerritoryWarManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.Location; import com.l2jmobius.gameserver.model.Party; import com.l2jmobius.gameserver.model.PlayerCondOverride; @@ -235,7 +235,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _invulAgainst; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -358,7 +358,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -6232,7 +6232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe /** * Check if target is affected with special buff - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) * @param flag int * @return boolean */ diff --git a/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index 4a61fe91a9..942c2c4163 100644 --- a/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_CT_2.6_HighFive/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -221,7 +221,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/Creature.java index 7da13a6397..ee4d4e9fd8 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4675,7 +4675,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index a9aa49811d..23d246b634 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -773,7 +773,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/Creature.java index 7da13a6397..ee4d4e9fd8 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4675,7 +4675,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index a9aa49811d..23d246b634 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -773,7 +773,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_Classic_2.1_Zaken/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/Creature.java index 7da13a6397..ee4d4e9fd8 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4675,7 +4675,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index a9aa49811d..23d246b634 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -773,7 +773,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_Classic_2.2_Antharas/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed) diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/CharEffectList.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/CharEffectList.java deleted file mode 100644 index 6516b615db..0000000000 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/CharEffectList.java +++ /dev/null @@ -1,1178 +0,0 @@ -/* - * This file is part of the L2J Mobius project. - * - * This program is free software: you can redistribute it and/or modify - * it under the terms of the GNU General Public License as published by - * the Free Software Foundation, either version 3 of the License, or - * (at your option) any later version. - * - * This program is distributed in the hope that it will be useful, - * but WITHOUT ANY WARRANTY; without even the implied warranty of - * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU - * General Public License for more details. - * - * You should have received a copy of the GNU General Public License - * along with this program. If not, see . - */ -package com.l2jmobius.gameserver.model; - -import java.util.Collection; -import java.util.Collections; -import java.util.EnumSet; -import java.util.HashSet; -import java.util.List; -import java.util.Objects; -import java.util.Optional; -import java.util.Queue; -import java.util.Set; -import java.util.concurrent.ConcurrentHashMap; -import java.util.concurrent.ConcurrentLinkedQueue; -import java.util.concurrent.atomic.AtomicInteger; -import java.util.function.Predicate; -import java.util.logging.Logger; -import java.util.stream.Collectors; -import java.util.stream.Stream; - -import com.l2jmobius.Config; -import com.l2jmobius.gameserver.model.actor.Creature; -import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; -import com.l2jmobius.gameserver.model.effects.AbstractEffect; -import com.l2jmobius.gameserver.model.effects.EffectFlag; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; -import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; -import com.l2jmobius.gameserver.model.skills.AbnormalType; -import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; -import com.l2jmobius.gameserver.model.skills.BuffInfo; -import com.l2jmobius.gameserver.model.skills.EffectScope; -import com.l2jmobius.gameserver.model.skills.Skill; -import com.l2jmobius.gameserver.model.skills.SkillBuffType; -import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; -import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; -import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; -import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; -import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; - -/** - * Effect lists.
- * Holds all the buff infos that are affecting a creature.
- * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
- * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
- * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
- * Methods may resemble List interface, although it doesn't implement such interface. - * @author Zoey76 - */ -public final class CharEffectList -{ - private static final Logger LOGGER = Logger.getLogger(CharEffectList.class.getName()); - /** Queue containing all effects from buffs for this effect list. */ - private volatile Queue _actives = new ConcurrentLinkedQueue<>(); - /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _passives = ConcurrentHashMap.newKeySet(); - /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ - private volatile Set _options = ConcurrentHashMap.newKeySet(); - /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ - private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); - /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ - private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); - /** Set containing all abnormal visual effects this creature currently displays. */ - private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); - /** Short buff skill ID. */ - private BuffInfo _shortBuff = null; - /** Count of specific types of buffs. */ - private final AtomicInteger _buffCount = new AtomicInteger(); - private final AtomicInteger _triggerBuffCount = new AtomicInteger(); - private final AtomicInteger _danceCount = new AtomicInteger(); - private final AtomicInteger _toggleCount = new AtomicInteger(); - private final AtomicInteger _debuffCount = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on any action. */ - private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); - /** If {@code true} this effect list has buffs removed on damage. */ - private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); - /** Effect flags. */ - private long _effectFlags; - /** The owner of this effect list. */ - private final Creature _owner; - /** Hidden buffs count, prevents iterations. */ - private final AtomicInteger _hiddenBuffs = new AtomicInteger(); - - /** - * Constructor for effect list. - * @param owner the creature that owns this effect list - */ - public CharEffectList(Creature owner) - { - _owner = owner; - } - - /** - * Gets passive effects. - * @return an unmodifiable set containing all passives. - */ - public Set getPassives() - { - return Collections.unmodifiableSet(_passives); - } - - /** - * Gets option effects. - * @return an unmodifiable set containing all options. - */ - public Set getOptions() - { - return Collections.unmodifiableSet(_options); - } - - /** - * Gets all the active effects on this effect list. - * @return an unmodifiable set containing all the active effects on this effect list - */ - public Collection getEffects() - { - return Collections.unmodifiableCollection(_actives); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the buffs on this effect list - */ - public List getBuffs() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); - } - - /** - * Gets all the active positive effects on this effect list. - * @return all the dances songs on this effect list - */ - public List getDances() - { - return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); - } - - /** - * Gets all the active negative effects on this effect list. - * @return all the debuffs on this effect list - */ - public List getDebuffs() - { - return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); - } - - /** - * Verifies if this effect list contains the given skill ID.
- * @param skillId the skill ID to verify - * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise - */ - public boolean isAffectedBySkill(int skillId) - { - return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); - } - - /** - * Gets the first {@code BuffInfo} found in this effect list. - * @param skillId the skill ID - * @return {@code BuffInfo} of the first active or passive effect found. - */ - public BuffInfo getBuffInfoBySkillId(int skillId) - { - return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param type the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise - */ - public final boolean hasAbnormalType(AbnormalType type) - { - return _stackedEffects.contains(type); - } - - /** - * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
- * @param types the abnormal skill type - * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise - */ - public boolean hasAbnormalType(Collection types) - { - return _stackedEffects.stream().anyMatch(types::contains); - } - - /** - * @param type the {@code AbnormalType} to match for. - * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. - * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise - */ - public boolean hasAbnormalType(AbnormalType type, Predicate filter) - { - return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); - } - - /** - * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
- * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. - * @param type the abnormal skill type - * @return the {@code BuffInfo} if it's present, {@code null} otherwise - */ - public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) - { - return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; - } - - /** - * Adds {@code AbnormalType}s to the blocked buff slot set. - * @param blockedAbnormalTypes the blocked buff slot set to add - */ - public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) - { - _blockedAbnormalTypes.addAll(blockedAbnormalTypes); - } - - /** - * Removes {@code AbnormalType}s from the blocked buff slot set. - * @param blockedBuffSlots the blocked buff slot set to remove - * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise - */ - public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) - { - return _blockedAbnormalTypes.removeAll(blockedBuffSlots); - } - - /** - * Gets all the blocked {@code AbnormalType}s for this creature effect list. - * @return the current blocked {@code AbnormalType}s set in unmodifiable view. - */ - public Set getBlockedAbnormalTypes() - { - return Collections.unmodifiableSet(_blockedAbnormalTypes); - } - - /** - * Sets the Short Buff data and sends an update if the effected is a player. - * @param info the {@code BuffInfo} - */ - public void shortBuffStatusUpdate(BuffInfo info) - { - if (_owner.isPlayer()) - { - _shortBuff = info; - if (info == null) - { - _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); - } - else - { - _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); - } - } - } - - /** - * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
- * Prevents initialization. - * @return the number of buffs in this creature effect list - */ - public int getBuffCount() - { - return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; - } - - /** - * Gets the Songs/Dances count.
- * Prevents initialization. - * @return the number of Songs/Dances in this creature effect list - */ - public int getDanceCount() - { - return _danceCount.get(); - } - - /** - * Gets the triggered buffs count.
- * Prevents initialization. - * @return the number of triggered buffs in this creature effect list - */ - public int getTriggeredBuffCount() - { - return _triggerBuffCount.get(); - } - - /** - * Gets the toggled skills count.
- * Prevents initialization. - * @return the number of toggle skills in this creature effect list - */ - public int getToggleCount() - { - return _toggleCount.get(); - } - - /** - * Gets the debuff skills count.
- * Prevents initialization. - * @return the number of debuff effects in this creature effect list - */ - public int getDebuffCount() - { - return _debuffCount.get(); - } - - /** - * Gets the hidden buff count. - * @return the number of hidden buffs - */ - public int getHiddenBuffsCount() - { - return _hiddenBuffs.get(); - } - - /** - * Exits all effects in this effect list.
- * Stops all the effects, clear the effect lists and updates the effect flags and icons. - * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. - */ - public void stopAllEffects(boolean broadcast) - { - stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); - } - - /** - * Stops all effects in this effect list except those that last through death. - */ - public void stopAllEffectsExceptThoseThatLastThroughDeath() - { - stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); - } - - /** - * Stops all active toggle skills. - */ - public void stopAllToggles() - { - if (_toggleCount.get() > 0) - { - // Ignore necessary toggles. - stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); - } - } - - public void stopAllTogglesOfGroup(int toggleGroup) - { - if (_toggleCount.get() > 0) - { - stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllPassives(boolean update, boolean broadcast) - { - if (!_passives.isEmpty()) - { - _passives.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Stops all active dances/songs skills. - * @param update set to true to update the effect flags and icons - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopAllOptions(boolean update, boolean broadcast) - { - if (!_options.isEmpty()) - { - _options.forEach(this::remove); - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exit all effects having a specified flag.
- * @param effectFlag the flag of the effect to stop - */ - public void stopEffects(EffectFlag effectFlag) - { - if (isAffected(effectFlag)) - { - stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); - } - } - - /** - * Exits all effects created by a specific skill ID.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, Skill)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skillId the skill ID - */ - public void stopSkillEffects(boolean removed, int skillId) - { - final BuffInfo info = getBuffInfoBySkillId(skillId); - if (info != null) - { - remove(info, removed, true, true); - } - } - - /** - * Exits all effects created by a specific skill.
- * Removes the effects from the effect list.
- * Removes the stats from the creature.
- * Updates the effect flags and icons.
- * Presents overload:
- * {@link #stopSkillEffects(boolean, int)}
- * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param skill the skill - */ - public void stopSkillEffects(boolean removed, Skill skill) - { - stopSkillEffects(removed, skill.getId()); - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}.
- * This function should not be used recursively, because it updates on every execute. - * @param type the skill {@code AbnormalType} - * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise - */ - public boolean stopEffects(AbnormalType type) - { - if (hasAbnormalType(type)) - { - stopEffects(i -> i.isAbnormalType(type), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects created by a specific skill {@code AbnormalType}s.
- * @param types the skill {@code AbnormalType}s to be checked and removed. - * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise - */ - public boolean stopEffects(Collection types) - { - if (hasAbnormalType(types)) - { - stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); - return true; - } - - return false; - } - - /** - * Exits all effects matched by a specific filter.
- * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. - * @param update update effect flags and icons after the operation finishes. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void stopEffects(Predicate filter, boolean update, boolean broadcast) - { - if (!_actives.isEmpty()) - { - _actives.stream().filter(filter).forEach(this::remove); - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - } - - /** - * Exits all buffs effects of the skills with "removedOnAnyAction" set.
- * Called on any action except movement (attack, cast). - */ - public void stopEffectsOnAction() - { - if (_hasBuffsRemovedOnAnyAction.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); - } - } - - public void stopEffectsOnDamage() - { - if (_hasBuffsRemovedOnDamage.get() > 0) - { - stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); - } - } - - /** - * Checks if a given effect limitation is exceeded. - * @param buffTypes the {@code SkillBuffType} of the skill. - * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. - */ - private boolean isLimitExceeded(SkillBuffType... buffTypes) - { - for (SkillBuffType buffType : buffTypes) - { - switch (buffType) - { - case TRIGGER: - { - if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) - { - return true; - } - } - case DANCE: - { - if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) - { - return true; - } - } - // case TOGGLE: Do toggles have limit? - case DEBUFF: - { - if (_debuffCount.get() > 24) - { - return true; - } - } - case BUFF: - { - if (getBuffCount() > _owner.getStat().getMaxBuffCount()) - { - return true; - } - } - } - } - - return false; - } - - /** - * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. - * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. - * @return the new count of the given {@code BuffInfo}'s category. - */ - private int increaseDecreaseCount(BuffInfo info, boolean increase) - { - // If it's a hidden buff, manage hidden buff count. - if (!info.isInUse()) - { - if (increase) - { - _hiddenBuffs.incrementAndGet(); - } - else - { - _hiddenBuffs.decrementAndGet(); - } - } - - // Update flag for skills being removed on action or damage. - if (info.getSkill().isRemovedOnAnyActionExceptMove()) - { - if (increase) - { - _hasBuffsRemovedOnAnyAction.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnAnyAction.decrementAndGet(); - } - } - if (info.getSkill().isRemovedOnDamage()) - { - if (increase) - { - _hasBuffsRemovedOnDamage.incrementAndGet(); - } - else - { - _hasBuffsRemovedOnDamage.decrementAndGet(); - } - } - - // Increase specific buff count - switch (info.getSkill().getBuffType()) - { - case TRIGGER: - { - return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); - } - case DANCE: - { - return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); - } - case TOGGLE: - { - return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); - } - case DEBUFF: - { - return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); - } - case BUFF: - { - return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); - } - } - - return 0; - } - - /** - * Removes a set of effects from this effect list.
- * Does NOT update effect icons and flags. - * @param info the effects to remove - */ - private void remove(BuffInfo info) - { - remove(info, true, false, false); - } - - /** - * Removes a set of effects from this effect list. - * @param info the effects to remove - * @param removed {@code true} if the effect is removed, {@code false} otherwise - * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. - * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. - */ - public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) - { - if (info == null) - { - return; - } - - if (info.getOption() != null) - { - // Remove separately if its an option. - removeOption(info, removed); - } - else if (info.getSkill().isPassive()) - { - // Remove Passive effect. - removePassive(info, removed); - } - else - { - // Remove active effect. - removeActive(info, removed); - if (_owner.isNpc()) // Fix for all NPC debuff animations removed. - { - updateEffectList(broadcast); - } - } - - // Update stats, effect flags and icons. - if (update) - { - updateEffectList(broadcast); - } - } - - /** - * @param info - * @param removed - */ - private synchronized void removeActive(BuffInfo info, boolean removed) - { - if (!_actives.isEmpty()) - { - // Removes the buff from the given effect list. - _actives.remove(info); - - // Remove short buff. - if (info == _shortBuff) - { - shortBuffStatusUpdate(null); - } - - // Stop the buff effects. - info.stopAllEffects(removed); - - // Decrease specific buff count - increaseDecreaseCount(info, false); - - info.getSkill().applyEffectScope(EffectScope.END, info, true, false); - } - } - - private void removePassive(BuffInfo info, boolean removed) - { - if (!_passives.isEmpty()) - { - _passives.remove(info); - info.stopAllEffects(removed); - } - } - - private void removeOption(BuffInfo info, boolean removed) - { - if (!_options.isEmpty()) - { - _options.remove(info); - info.stopAllEffects(removed); - } - } - - /** - * Adds a set of effects to this effect list. - * @param info the {@code BuffInfo} - */ - public void add(BuffInfo info) - { - if (info == null) - { - return; - } - - // Prevent adding and initializing buffs/effects on dead creatures. - if (info.getEffected().isDead()) - { - return; - } - - if (info.getSkill() == null) - { - // Only options are without skills. - addOption(info); - } - else if (info.getSkill().isPassive()) - { - // Passive effects are treated specially - addPassive(info); - } - else - { - // Add active effect - addActive(info); - } - - // Update stats, effect flags and icons. - updateEffectList(true); - } - - private synchronized void addActive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. - if (info.getEffected().isDead()) - { - return; - } - - if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) - { - return; - } - - // Fix for stacking trigger skills - if (skill.isTriggeredSkill()) - { - final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); - if (triggerInfo != null) - { - if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) - { - return; - } - } - } - - if (info.getEffector() != null) - { - // Check for debuffs against target. - if ((info.getEffector() != info.getEffected()) && skill.isBad()) - { - // Check if effected is debuff blocked. - if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) - { - return; - } - - if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) - { - return; - } - } - - // Check if buff skills are blocked. - if (info.getEffected().isBuffBlocked() && !skill.isBad()) - { - return; - } - } - - // Manage effect stacking. - if (hasAbnormalType(skill.getAbnormalType())) - { - for (BuffInfo existingInfo : _actives) - { - final Skill existingSkill = existingInfo.getSkill(); - // Check if existing effect should be removed due to stack. - // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. - if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) - { - // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. - if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) - { - if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) - { - continue; - } - } - - // The effect we are adding overrides the existing effect. Delete or disable the existing effect. - if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) - { - // If it is an herb, set as not in use the lesser buff, unless it is the same skill. - if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) - { - existingInfo.setInUse(false); - _hiddenBuffs.incrementAndGet(); - } - else - { - // Remove effect that gets overridden. - remove(existingInfo); - } - } - else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. - { - info.setInUse(false); - } - else // The effect we try to add should be overridden. - { - return; - } - } - } - } - - // Increase buff count. - increaseDecreaseCount(info, true); - - // Check if any effect limit is exceeded. - if (isLimitExceeded(SkillBuffType.values())) - { - // Check for each category. - for (BuffInfo existingInfo : _actives) - { - if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) - { - remove(existingInfo); - } - - // Break further loops if there is no any other limit exceeding. - if (!isLimitExceeded(SkillBuffType.values())) - { - break; - } - } - } - - // After removing old buff (same ID) or stacked buff (same abnormal type), - // Add the buff to the end of the effect list. - _actives.add(info); - // Initialize effects. - info.initializeEffects(); - } - - private void addPassive(BuffInfo info) - { - final Skill skill = info.getSkill(); - - // Passive effects don't need stack type! - if (!skill.getAbnormalType().isNone()) - { - LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); - } - - // Check for passive skill conditions. - if (!skill.checkCondition(info.getEffector(), info.getEffected())) - { - return; - } - - // Remove previous passives of this id. - _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> - { - b.setInUse(false); - _passives.remove(b); - }); - - _passives.add(info); - - // Initialize effects. - info.initializeEffects(); - } - - private void addOption(BuffInfo info) - { - if (info.getOption() != null) - { - // Remove previous options of this id. - _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> - { - b.setInUse(false); - _options.remove(b); - }); - - _options.add(info); - - // Initialize effects. - info.initializeEffects(); - } - } - - /** - * Update effect icons.
- * Prevents initialization. - * @param partyOnly {@code true} only party icons need to be updated. - */ - public void updateEffectIcons(boolean partyOnly) - { - final PlayerInstance player = _owner.getActingPlayer(); - if (player != null) - { - final Party party = player.getParty(); - final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); - final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); - final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); - - if (!_actives.isEmpty()) - { - //@formatter:off - _actives.stream() - .filter(Objects::nonNull) - .filter(BuffInfo::isInUse) - .forEach(info -> - { - if (info.getSkill().isHealingPotionSkill()) - { - shortBuffStatusUpdate(info); - } - else - { - asu.ifPresent(a -> a.addSkill(info)); - ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); - os.ifPresent(o -> o.addSkill(info)); - } - }); - //@formatter:on - } - - // Send icon update for player buff bar. - asu.ifPresent(_owner::sendPacket); - - // Player or summon is in party. Broadcast packet to everyone in the party. - if (party != null) - { - ps.ifPresent(party::broadcastPacket); - } - else // Not in party, then its a summon info for its owner. - { - ps.ifPresent(player::sendPacket); - } - - // Send icon update to all olympiad observers. - if (os.isPresent()) - { - final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); - if ((game != null) && game.isBattleStarted()) - { - os.ifPresent(game.getStadium()::broadcastPacketToObservers); - } - } - } - - // Update effect icons for everyone targeting this owner. - final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); - - // @formatter:off - _owner.getStatus().getStatusListener().stream() - .filter(Objects::nonNull) - .filter(WorldObject::isPlayer) - .map(Creature::getActingPlayer) - .forEach(upd::sendTo); - // @formatter:on - - if (_owner.isPlayer() && (_owner.getTarget() == _owner)) - { - _owner.sendPacket(upd); - } - } - - /** - * Gets the currently applied abnormal visual effects. - * @return the abnormal visual effects - */ - public Set getCurrentAbnormalVisualEffects() - { - return _abnormalVisualEffects; - } - - /** - * Checks if the creature has the abnormal visual effect. - * @param ave the abnormal visual effect - * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise - */ - public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) - { - return _abnormalVisualEffects.contains(ave); - } - - /** - * Adds the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.add(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Removes the abnormal visual and sends packet for updating them in client. - * @param aves the abnormal visual effects - */ - public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) - { - for (AbnormalVisualEffect ave : aves) - { - _abnormalVisualEffects.remove(ave); - } - _owner.updateAbnormalVisualEffects(); - } - - /** - * Wrapper to update abnormal icons and effect flags. - * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. - */ - private void updateEffectList(boolean broadcast) - { - // Create new empty flags. - long flags = 0; - final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); - final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); - final Set unhideBuffs = new HashSet<>(); - - // Recalculate new flags - for (BuffInfo info : _actives) - { - if (info != null) - { - final Skill skill = info.getSkill(); - - // Handle hidden buffs. Check if there was such abnormal before so we can continue. - if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) - { - // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. - if (info.isInUse()) - { - unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); - } - // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. - else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) - { - unhideBuffs.add(info); - } - } - - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - - // Add the AbnormalType flag. - abnormalTypeFlags.add(skill.getAbnormalType()); - - // Add AbnormalVisualEffect flag. - if (skill.hasAbnormalVisualEffects()) - { - for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) - { - abnormalVisualEffectFlags.add(ave); - _abnormalVisualEffects.add(ave); - } - if (broadcast) - { - _owner.updateAbnormalVisualEffects(); - } - } - } - } - // Add passive effect flags. - for (BuffInfo info : _passives) - { - if (info != null) - { - // Add the EffectType flag. - for (AbstractEffect e : info.getEffects()) - { - flags |= e.getEffectFlags(); - } - } - } - - // Replace the old flags with the new flags. - _effectFlags = flags; - _stackedEffects = abnormalTypeFlags; - - // Unhide the selected buffs. - unhideBuffs.forEach(b -> - { - b.setInUse(true); - _hiddenBuffs.decrementAndGet(); - }); - - // Recalculate all stats - _owner.getStat().recalculateStats(broadcast); - - if (broadcast) - { - // Check if there is change in AbnormalVisualEffect - if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) - { - _abnormalVisualEffects = abnormalVisualEffectFlags; - _owner.updateAbnormalVisualEffects(); - } - - // Send updates to the client - updateEffectIcons(false); - } - } - - /** - * Check if target is affected with special buff - * @param flag of special buff - * @return boolean true if affected - */ - public boolean isAffected(EffectFlag flag) - { - return (_effectFlags & flag.getMask()) != 0; - } -} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/EffectList.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/EffectList.java new file mode 100644 index 0000000000..91f9094627 --- /dev/null +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/EffectList.java @@ -0,0 +1,1178 @@ +/* + * This file is part of the L2J Mobius project. + * + * This program is free software: you can redistribute it and/or modify + * it under the terms of the GNU General Public License as published by + * the Free Software Foundation, either version 3 of the License, or + * (at your option) any later version. + * + * This program is distributed in the hope that it will be useful, + * but WITHOUT ANY WARRANTY; without even the implied warranty of + * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU + * General Public License for more details. + * + * You should have received a copy of the GNU General Public License + * along with this program. If not, see . + */ +package com.l2jmobius.gameserver.model; + +import java.util.Collection; +import java.util.Collections; +import java.util.EnumSet; +import java.util.HashSet; +import java.util.List; +import java.util.Objects; +import java.util.Optional; +import java.util.Queue; +import java.util.Set; +import java.util.concurrent.ConcurrentHashMap; +import java.util.concurrent.ConcurrentLinkedQueue; +import java.util.concurrent.atomic.AtomicInteger; +import java.util.function.Predicate; +import java.util.logging.Logger; +import java.util.stream.Collectors; +import java.util.stream.Stream; + +import com.l2jmobius.Config; +import com.l2jmobius.gameserver.model.actor.Creature; +import com.l2jmobius.gameserver.model.actor.instance.PlayerInstance; +import com.l2jmobius.gameserver.model.effects.AbstractEffect; +import com.l2jmobius.gameserver.model.effects.EffectFlag; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager; +import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask; +import com.l2jmobius.gameserver.model.skills.AbnormalType; +import com.l2jmobius.gameserver.model.skills.AbnormalVisualEffect; +import com.l2jmobius.gameserver.model.skills.BuffInfo; +import com.l2jmobius.gameserver.model.skills.EffectScope; +import com.l2jmobius.gameserver.model.skills.Skill; +import com.l2jmobius.gameserver.model.skills.SkillBuffType; +import com.l2jmobius.gameserver.network.serverpackets.AbnormalStatusUpdate; +import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; +import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadSpelledInfo; +import com.l2jmobius.gameserver.network.serverpackets.PartySpelled; +import com.l2jmobius.gameserver.network.serverpackets.ShortBuffStatusUpdate; + +/** + * Effect lists.
+ * Holds all the buff infos that are affecting a creature.
+ * Manages the logic that controls whether a buff is added, remove, replaced or set inactive.
+ * Uses maps with skill ID as key and buff info DTO as value to avoid iterations.
+ * Uses Double-Checked Locking to avoid useless initialization and synchronization issues and overhead.
+ * Methods may resemble List interface, although it doesn't implement such interface. + * @author Zoey76 + */ +public final class EffectList +{ + private static final Logger LOGGER = Logger.getLogger(EffectList.class.getName()); + /** Queue containing all effects from buffs for this effect list. */ + private volatile Queue _actives = new ConcurrentLinkedQueue<>(); + /** List containing all passives for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _passives = ConcurrentHashMap.newKeySet(); + /** List containing all options for this effect list. They bypass most of the actions and they are not included in most operations. */ + private volatile Set _options = ConcurrentHashMap.newKeySet(); + /** Map containing the all stacked effect in progress for each {@code AbnormalType}. */ + private volatile Set _stackedEffects = EnumSet.noneOf(AbnormalType.class); + /** Set containing all {@code AbnormalType}s that shouldn't be added to this creature effect list. */ + private volatile Set _blockedAbnormalTypes = EnumSet.noneOf(AbnormalType.class); + /** Set containing all abnormal visual effects this creature currently displays. */ + private volatile Set _abnormalVisualEffects = EnumSet.noneOf(AbnormalVisualEffect.class); + /** Short buff skill ID. */ + private BuffInfo _shortBuff = null; + /** Count of specific types of buffs. */ + private final AtomicInteger _buffCount = new AtomicInteger(); + private final AtomicInteger _triggerBuffCount = new AtomicInteger(); + private final AtomicInteger _danceCount = new AtomicInteger(); + private final AtomicInteger _toggleCount = new AtomicInteger(); + private final AtomicInteger _debuffCount = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on any action. */ + private final AtomicInteger _hasBuffsRemovedOnAnyAction = new AtomicInteger(); + /** If {@code true} this effect list has buffs removed on damage. */ + private final AtomicInteger _hasBuffsRemovedOnDamage = new AtomicInteger(); + /** Effect flags. */ + private long _effectFlags; + /** The owner of this effect list. */ + private final Creature _owner; + /** Hidden buffs count, prevents iterations. */ + private final AtomicInteger _hiddenBuffs = new AtomicInteger(); + + /** + * Constructor for effect list. + * @param owner the creature that owns this effect list + */ + public EffectList(Creature owner) + { + _owner = owner; + } + + /** + * Gets passive effects. + * @return an unmodifiable set containing all passives. + */ + public Set getPassives() + { + return Collections.unmodifiableSet(_passives); + } + + /** + * Gets option effects. + * @return an unmodifiable set containing all options. + */ + public Set getOptions() + { + return Collections.unmodifiableSet(_options); + } + + /** + * Gets all the active effects on this effect list. + * @return an unmodifiable set containing all the active effects on this effect list + */ + public Collection getEffects() + { + return Collections.unmodifiableCollection(_actives); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the buffs on this effect list + */ + public List getBuffs() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isBuff()).collect(Collectors.toList()); + } + + /** + * Gets all the active positive effects on this effect list. + * @return all the dances songs on this effect list + */ + public List getDances() + { + return _actives.stream().filter(b -> b.getSkill().getBuffType().isDance()).collect(Collectors.toList()); + } + + /** + * Gets all the active negative effects on this effect list. + * @return all the debuffs on this effect list + */ + public List getDebuffs() + { + return _actives.stream().filter(b -> b.getSkill().isDebuff()).collect(Collectors.toList()); + } + + /** + * Verifies if this effect list contains the given skill ID.
+ * @param skillId the skill ID to verify + * @return {@code true} if the skill ID is present in the effect list (includes active and passive effects), {@code false} otherwise + */ + public boolean isAffectedBySkill(int skillId) + { + return (_actives.stream().anyMatch(i -> i.getSkill().getId() == skillId)) || (_passives.stream().anyMatch(i -> i.getSkill().getId() == skillId)); + } + + /** + * Gets the first {@code BuffInfo} found in this effect list. + * @param skillId the skill ID + * @return {@code BuffInfo} of the first active or passive effect found. + */ + public BuffInfo getBuffInfoBySkillId(int skillId) + { + return Stream.concat(_actives.stream(), _passives.stream()).filter(b -> b.getSkill().getId() == skillId).findFirst().orElse(null); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param type the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType}, {@code false} otherwise + */ + public final boolean hasAbnormalType(AbnormalType type) + { + return _stackedEffects.contains(type); + } + + /** + * Check if any active {@code BuffInfo} of this {@code AbnormalType} exists.
+ * @param types the abnormal skill type + * @return {@code true} if there is any {@code BuffInfo} matching one of the specified {@code AbnormalType}s, {@code false} otherwise + */ + public boolean hasAbnormalType(Collection types) + { + return _stackedEffects.stream().anyMatch(types::contains); + } + + /** + * @param type the {@code AbnormalType} to match for. + * @param filter any additional filters to match for once a {@code BuffInfo} of this {@code AbnormalType} is found. + * @return {@code true} if there is any {@code BuffInfo} matching the specified {@code AbnormalType} and given filter, {@code false} otherwise + */ + public boolean hasAbnormalType(AbnormalType type, Predicate filter) + { + return hasAbnormalType(type) && _actives.stream().filter(i -> i.isAbnormalType(type)).anyMatch(filter); + } + + /** + * Gets the first {@code BuffInfo} found by the given {@code AbnormalType}.
+ * There are some cases where there are multiple {@code BuffInfo} per single {@code AbnormalType}. + * @param type the abnormal skill type + * @return the {@code BuffInfo} if it's present, {@code null} otherwise + */ + public BuffInfo getFirstBuffInfoByAbnormalType(AbnormalType type) + { + return hasAbnormalType(type) ? _actives.stream().filter(i -> i.isAbnormalType(type)).findFirst().orElse(null) : null; + } + + /** + * Adds {@code AbnormalType}s to the blocked buff slot set. + * @param blockedAbnormalTypes the blocked buff slot set to add + */ + public void addBlockedAbnormalTypes(Set blockedAbnormalTypes) + { + _blockedAbnormalTypes.addAll(blockedAbnormalTypes); + } + + /** + * Removes {@code AbnormalType}s from the blocked buff slot set. + * @param blockedBuffSlots the blocked buff slot set to remove + * @return {@code true} if the blocked buff slots set has been modified, {@code false} otherwise + */ + public boolean removeBlockedAbnormalTypes(Set blockedBuffSlots) + { + return _blockedAbnormalTypes.removeAll(blockedBuffSlots); + } + + /** + * Gets all the blocked {@code AbnormalType}s for this creature effect list. + * @return the current blocked {@code AbnormalType}s set in unmodifiable view. + */ + public Set getBlockedAbnormalTypes() + { + return Collections.unmodifiableSet(_blockedAbnormalTypes); + } + + /** + * Sets the Short Buff data and sends an update if the effected is a player. + * @param info the {@code BuffInfo} + */ + public void shortBuffStatusUpdate(BuffInfo info) + { + if (_owner.isPlayer()) + { + _shortBuff = info; + if (info == null) + { + _owner.sendPacket(ShortBuffStatusUpdate.RESET_SHORT_BUFF); + } + else + { + _owner.sendPacket(new ShortBuffStatusUpdate(info.getSkill().getId(), info.getSkill().getLevel(), info.getSkill().getSubLevel(), info.getTime())); + } + } + } + + /** + * Gets the buffs count without including the hidden buffs (after getting an Herb buff).
+ * Prevents initialization. + * @return the number of buffs in this creature effect list + */ + public int getBuffCount() + { + return !_actives.isEmpty() ? (_buffCount.get() - _hiddenBuffs.get()) : 0; + } + + /** + * Gets the Songs/Dances count.
+ * Prevents initialization. + * @return the number of Songs/Dances in this creature effect list + */ + public int getDanceCount() + { + return _danceCount.get(); + } + + /** + * Gets the triggered buffs count.
+ * Prevents initialization. + * @return the number of triggered buffs in this creature effect list + */ + public int getTriggeredBuffCount() + { + return _triggerBuffCount.get(); + } + + /** + * Gets the toggled skills count.
+ * Prevents initialization. + * @return the number of toggle skills in this creature effect list + */ + public int getToggleCount() + { + return _toggleCount.get(); + } + + /** + * Gets the debuff skills count.
+ * Prevents initialization. + * @return the number of debuff effects in this creature effect list + */ + public int getDebuffCount() + { + return _debuffCount.get(); + } + + /** + * Gets the hidden buff count. + * @return the number of hidden buffs + */ + public int getHiddenBuffsCount() + { + return _hiddenBuffs.get(); + } + + /** + * Exits all effects in this effect list.
+ * Stops all the effects, clear the effect lists and updates the effect flags and icons. + * @param broadcast {@code true} to broadcast update packets, {@code false} otherwise. + */ + public void stopAllEffects(boolean broadcast) + { + stopEffects(b -> !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, broadcast); + } + + /** + * Stops all effects in this effect list except those that last through death. + */ + public void stopAllEffectsExceptThoseThatLastThroughDeath() + { + stopEffects(info -> !info.getSkill().isStayAfterDeath(), true, true); + } + + /** + * Stops all active toggle skills. + */ + public void stopAllToggles() + { + if (_toggleCount.get() > 0) + { + // Ignore necessary toggles. + stopEffects(b -> b.getSkill().isToggle() && !b.getSkill().isNecessaryToggle() && !b.getSkill().isIrreplacableBuff(), true, true); + } + } + + public void stopAllTogglesOfGroup(int toggleGroup) + { + if (_toggleCount.get() > 0) + { + stopEffects(b -> b.getSkill().isToggle() && (b.getSkill().getToggleGroupId() == toggleGroup), true, true); + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllPassives(boolean update, boolean broadcast) + { + if (!_passives.isEmpty()) + { + _passives.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Stops all active dances/songs skills. + * @param update set to true to update the effect flags and icons + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopAllOptions(boolean update, boolean broadcast) + { + if (!_options.isEmpty()) + { + _options.forEach(this::remove); + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exit all effects having a specified flag.
+ * @param effectFlag the flag of the effect to stop + */ + public void stopEffects(EffectFlag effectFlag) + { + if (isAffected(effectFlag)) + { + stopEffects(info -> info.getEffects().stream().anyMatch(effect -> (effect != null) && ((effect.getEffectFlags() & effectFlag.getMask()) != 0)), true, true); + } + } + + /** + * Exits all effects created by a specific skill ID.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, Skill)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skillId the skill ID + */ + public void stopSkillEffects(boolean removed, int skillId) + { + final BuffInfo info = getBuffInfoBySkillId(skillId); + if (info != null) + { + remove(info, removed, true, true); + } + } + + /** + * Exits all effects created by a specific skill.
+ * Removes the effects from the effect list.
+ * Removes the stats from the creature.
+ * Updates the effect flags and icons.
+ * Presents overload:
+ * {@link #stopSkillEffects(boolean, int)}
+ * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param skill the skill + */ + public void stopSkillEffects(boolean removed, Skill skill) + { + stopSkillEffects(removed, skill.getId()); + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}.
+ * This function should not be used recursively, because it updates on every execute. + * @param type the skill {@code AbnormalType} + * @return {@code true} if there was any {@code BuffInfo} with the given {@code AbnormalType}, {@code false} otherwise + */ + public boolean stopEffects(AbnormalType type) + { + if (hasAbnormalType(type)) + { + stopEffects(i -> i.isAbnormalType(type), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects created by a specific skill {@code AbnormalType}s.
+ * @param types the skill {@code AbnormalType}s to be checked and removed. + * @return {@code true} if there was any {@code BuffInfo} with one of the given {@code AbnormalType}s, {@code false} otherwise + */ + public boolean stopEffects(Collection types) + { + if (hasAbnormalType(types)) + { + stopEffects(i -> types.contains(i.getSkill().getAbnormalType()), true, true); + return true; + } + + return false; + } + + /** + * Exits all effects matched by a specific filter.
+ * @param filter any filter to apply when selecting which {@code BuffInfo}s to be removed. + * @param update update effect flags and icons after the operation finishes. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void stopEffects(Predicate filter, boolean update, boolean broadcast) + { + if (!_actives.isEmpty()) + { + _actives.stream().filter(filter).forEach(this::remove); + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + } + + /** + * Exits all buffs effects of the skills with "removedOnAnyAction" set.
+ * Called on any action except movement (attack, cast). + */ + public void stopEffectsOnAction() + { + if (_hasBuffsRemovedOnAnyAction.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnAnyActionExceptMove(), true, true); + } + } + + public void stopEffectsOnDamage() + { + if (_hasBuffsRemovedOnDamage.get() > 0) + { + stopEffects(info -> info.getSkill().isRemovedOnDamage(), true, true); + } + } + + /** + * Checks if a given effect limitation is exceeded. + * @param buffTypes the {@code SkillBuffType} of the skill. + * @return {@code true} if the current effect count for any of the given types is greater than the limit, {@code false} otherwise. + */ + private boolean isLimitExceeded(SkillBuffType... buffTypes) + { + for (SkillBuffType buffType : buffTypes) + { + switch (buffType) + { + case TRIGGER: + { + if (_triggerBuffCount.get() > Config.TRIGGERED_BUFFS_MAX_AMOUNT) + { + return true; + } + } + case DANCE: + { + if (_danceCount.get() > Config.DANCES_MAX_AMOUNT) + { + return true; + } + } + // case TOGGLE: Do toggles have limit? + case DEBUFF: + { + if (_debuffCount.get() > 24) + { + return true; + } + } + case BUFF: + { + if (getBuffCount() > _owner.getStat().getMaxBuffCount()) + { + return true; + } + } + } + } + + return false; + } + + /** + * @param info the {@code BuffInfo} whose buff category will be increased/decreased in count. + * @param increase {@code true} to increase the category count of this {@code BuffInfo}, {@code false} to decrease. + * @return the new count of the given {@code BuffInfo}'s category. + */ + private int increaseDecreaseCount(BuffInfo info, boolean increase) + { + // If it's a hidden buff, manage hidden buff count. + if (!info.isInUse()) + { + if (increase) + { + _hiddenBuffs.incrementAndGet(); + } + else + { + _hiddenBuffs.decrementAndGet(); + } + } + + // Update flag for skills being removed on action or damage. + if (info.getSkill().isRemovedOnAnyActionExceptMove()) + { + if (increase) + { + _hasBuffsRemovedOnAnyAction.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnAnyAction.decrementAndGet(); + } + } + if (info.getSkill().isRemovedOnDamage()) + { + if (increase) + { + _hasBuffsRemovedOnDamage.incrementAndGet(); + } + else + { + _hasBuffsRemovedOnDamage.decrementAndGet(); + } + } + + // Increase specific buff count + switch (info.getSkill().getBuffType()) + { + case TRIGGER: + { + return increase ? _triggerBuffCount.incrementAndGet() : _triggerBuffCount.decrementAndGet(); + } + case DANCE: + { + return increase ? _danceCount.incrementAndGet() : _danceCount.decrementAndGet(); + } + case TOGGLE: + { + return increase ? _toggleCount.incrementAndGet() : _toggleCount.decrementAndGet(); + } + case DEBUFF: + { + return increase ? _debuffCount.incrementAndGet() : _debuffCount.decrementAndGet(); + } + case BUFF: + { + return increase ? _buffCount.incrementAndGet() : _buffCount.decrementAndGet(); + } + } + + return 0; + } + + /** + * Removes a set of effects from this effect list.
+ * Does NOT update effect icons and flags. + * @param info the effects to remove + */ + private void remove(BuffInfo info) + { + remove(info, true, false, false); + } + + /** + * Removes a set of effects from this effect list. + * @param info the effects to remove + * @param removed {@code true} if the effect is removed, {@code false} otherwise + * @param update {@code true} if effect flags and icons should be updated after this removal, {@code false} otherwise. + * @param broadcast {@code true} to broadcast update packets if updating, {@code false} otherwise. + */ + public void remove(BuffInfo info, boolean removed, boolean update, boolean broadcast) + { + if (info == null) + { + return; + } + + if (info.getOption() != null) + { + // Remove separately if its an option. + removeOption(info, removed); + } + else if (info.getSkill().isPassive()) + { + // Remove Passive effect. + removePassive(info, removed); + } + else + { + // Remove active effect. + removeActive(info, removed); + if (_owner.isNpc()) // Fix for all NPC debuff animations removed. + { + updateEffectList(broadcast); + } + } + + // Update stats, effect flags and icons. + if (update) + { + updateEffectList(broadcast); + } + } + + /** + * @param info + * @param removed + */ + private synchronized void removeActive(BuffInfo info, boolean removed) + { + if (!_actives.isEmpty()) + { + // Removes the buff from the given effect list. + _actives.remove(info); + + // Remove short buff. + if (info == _shortBuff) + { + shortBuffStatusUpdate(null); + } + + // Stop the buff effects. + info.stopAllEffects(removed); + + // Decrease specific buff count + increaseDecreaseCount(info, false); + + info.getSkill().applyEffectScope(EffectScope.END, info, true, false); + } + } + + private void removePassive(BuffInfo info, boolean removed) + { + if (!_passives.isEmpty()) + { + _passives.remove(info); + info.stopAllEffects(removed); + } + } + + private void removeOption(BuffInfo info, boolean removed) + { + if (!_options.isEmpty()) + { + _options.remove(info); + info.stopAllEffects(removed); + } + } + + /** + * Adds a set of effects to this effect list. + * @param info the {@code BuffInfo} + */ + public void add(BuffInfo info) + { + if (info == null) + { + return; + } + + // Prevent adding and initializing buffs/effects on dead creatures. + if (info.getEffected().isDead()) + { + return; + } + + if (info.getSkill() == null) + { + // Only options are without skills. + addOption(info); + } + else if (info.getSkill().isPassive()) + { + // Passive effects are treated specially + addPassive(info); + } + else + { + // Add active effect + addActive(info); + } + + // Update stats, effect flags and icons. + updateEffectList(true); + } + + private synchronized void addActive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Cannot add active buff to dead creature. Even in retail if you are dead with Lv. 3 Shillien's Breath, it will disappear instead of going 1 level down. + if (info.getEffected().isDead()) + { + return; + } + + if ((_blockedAbnormalTypes != null) && _blockedAbnormalTypes.contains(skill.getAbnormalType())) + { + return; + } + + // Fix for stacking trigger skills + if (skill.isTriggeredSkill()) + { + final BuffInfo triggerInfo = info.getEffected().getEffectList().getBuffInfoBySkillId(skill.getId()); + if (triggerInfo != null) + { + if (triggerInfo.getSkill().getLevel() >= skill.getLevel()) + { + return; + } + } + } + + if (info.getEffector() != null) + { + // Check for debuffs against target. + if ((info.getEffector() != info.getEffected()) && skill.isBad()) + { + // Check if effected is debuff blocked. + if ((info.getEffected().isDebuffBlocked() || (info.getEffector().isGM() && !info.getEffector().getAccessLevel().canGiveDamage()))) + { + return; + } + + if (info.getEffector().isPlayer() && info.getEffected().isPlayer() && info.getEffected().isAffected(EffectFlag.DUELIST_FURY) && !info.getEffector().isAffected(EffectFlag.DUELIST_FURY)) + { + return; + } + } + + // Check if buff skills are blocked. + if (info.getEffected().isBuffBlocked() && !skill.isBad()) + { + return; + } + } + + // Manage effect stacking. + if (hasAbnormalType(skill.getAbnormalType())) + { + for (BuffInfo existingInfo : _actives) + { + final Skill existingSkill = existingInfo.getSkill(); + // Check if existing effect should be removed due to stack. + // Effects with no abnormal don't stack if their ID is the same. Effects of the same abnormal type don't stack. + if ((skill.getAbnormalType().isNone() && (existingSkill.getId() == skill.getId())) || (!skill.getAbnormalType().isNone() && (existingSkill.getAbnormalType() == skill.getAbnormalType()))) + { + // Check if there is subordination abnormal. Skills with subordination abnormal stack with each other, unless the caster is the same. + if (!skill.getSubordinationAbnormalType().isNone() && (skill.getSubordinationAbnormalType() == existingSkill.getSubordinationAbnormalType())) + { + if ((info.getEffectorObjectId() == 0) || (existingInfo.getEffectorObjectId() == 0) || (info.getEffectorObjectId() != existingInfo.getEffectorObjectId())) + { + continue; + } + } + + // The effect we are adding overrides the existing effect. Delete or disable the existing effect. + if (skill.getAbnormalLvl() >= existingSkill.getAbnormalLvl()) + { + // If it is an herb, set as not in use the lesser buff, unless it is the same skill. + if ((skill.isAbnormalInstant() || existingSkill.isIrreplacableBuff()) && (skill.getId() != existingSkill.getId())) + { + existingInfo.setInUse(false); + _hiddenBuffs.incrementAndGet(); + } + else + { + // Remove effect that gets overridden. + remove(existingInfo); + } + } + else if (skill.isIrreplacableBuff()) // The effect we try to add should be hidden. + { + info.setInUse(false); + } + else // The effect we try to add should be overridden. + { + return; + } + } + } + } + + // Increase buff count. + increaseDecreaseCount(info, true); + + // Check if any effect limit is exceeded. + if (isLimitExceeded(SkillBuffType.values())) + { + // Check for each category. + for (BuffInfo existingInfo : _actives) + { + if (existingInfo.isInUse() && !skill.is7Signs() && isLimitExceeded(existingInfo.getSkill().getBuffType())) + { + remove(existingInfo); + } + + // Break further loops if there is no any other limit exceeding. + if (!isLimitExceeded(SkillBuffType.values())) + { + break; + } + } + } + + // After removing old buff (same ID) or stacked buff (same abnormal type), + // Add the buff to the end of the effect list. + _actives.add(info); + // Initialize effects. + info.initializeEffects(); + } + + private void addPassive(BuffInfo info) + { + final Skill skill = info.getSkill(); + + // Passive effects don't need stack type! + if (!skill.getAbnormalType().isNone()) + { + LOGGER.warning("Passive " + skill + " with abnormal type: " + skill.getAbnormalType() + "!"); + } + + // Check for passive skill conditions. + if (!skill.checkCondition(info.getEffector(), info.getEffected())) + { + return; + } + + // Remove previous passives of this id. + _passives.stream().filter(Objects::nonNull).filter(b -> b.getSkill().getId() == skill.getId()).forEach(b -> + { + b.setInUse(false); + _passives.remove(b); + }); + + _passives.add(info); + + // Initialize effects. + info.initializeEffects(); + } + + private void addOption(BuffInfo info) + { + if (info.getOption() != null) + { + // Remove previous options of this id. + _options.stream().filter(Objects::nonNull).filter(b -> b.getOption().getId() == info.getOption().getId()).forEach(b -> + { + b.setInUse(false); + _options.remove(b); + }); + + _options.add(info); + + // Initialize effects. + info.initializeEffects(); + } + } + + /** + * Update effect icons.
+ * Prevents initialization. + * @param partyOnly {@code true} only party icons need to be updated. + */ + public void updateEffectIcons(boolean partyOnly) + { + final PlayerInstance player = _owner.getActingPlayer(); + if (player != null) + { + final Party party = player.getParty(); + final Optional asu = (_owner.isPlayer() && !partyOnly) ? Optional.of(new AbnormalStatusUpdate()) : Optional.empty(); + final Optional ps = ((party != null) || _owner.isSummon()) ? Optional.of(new PartySpelled(_owner)) : Optional.empty(); + final Optional os = (player.isInOlympiadMode() && player.isOlympiadStart()) ? Optional.of(new ExOlympiadSpelledInfo(player)) : Optional.empty(); + + if (!_actives.isEmpty()) + { + //@formatter:off + _actives.stream() + .filter(Objects::nonNull) + .filter(BuffInfo::isInUse) + .forEach(info -> + { + if (info.getSkill().isHealingPotionSkill()) + { + shortBuffStatusUpdate(info); + } + else + { + asu.ifPresent(a -> a.addSkill(info)); + ps.filter(p -> !info.getSkill().isToggle()).ifPresent(p -> p.addSkill(info)); + os.ifPresent(o -> o.addSkill(info)); + } + }); + //@formatter:on + } + + // Send icon update for player buff bar. + asu.ifPresent(_owner::sendPacket); + + // Player or summon is in party. Broadcast packet to everyone in the party. + if (party != null) + { + ps.ifPresent(party::broadcastPacket); + } + else // Not in party, then its a summon info for its owner. + { + ps.ifPresent(player::sendPacket); + } + + // Send icon update to all olympiad observers. + if (os.isPresent()) + { + final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(player.getOlympiadGameId()); + if ((game != null) && game.isBattleStarted()) + { + os.ifPresent(game.getStadium()::broadcastPacketToObservers); + } + } + } + + // Update effect icons for everyone targeting this owner. + final ExAbnormalStatusUpdateFromTarget upd = new ExAbnormalStatusUpdateFromTarget(_owner); + + // @formatter:off + _owner.getStatus().getStatusListener().stream() + .filter(Objects::nonNull) + .filter(WorldObject::isPlayer) + .map(Creature::getActingPlayer) + .forEach(upd::sendTo); + // @formatter:on + + if (_owner.isPlayer() && (_owner.getTarget() == _owner)) + { + _owner.sendPacket(upd); + } + } + + /** + * Gets the currently applied abnormal visual effects. + * @return the abnormal visual effects + */ + public Set getCurrentAbnormalVisualEffects() + { + return _abnormalVisualEffects; + } + + /** + * Checks if the creature has the abnormal visual effect. + * @param ave the abnormal visual effect + * @return {@code true} if the creature has the abnormal visual effect, {@code false} otherwise + */ + public boolean hasAbnormalVisualEffect(AbnormalVisualEffect ave) + { + return _abnormalVisualEffects.contains(ave); + } + + /** + * Adds the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void startAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.add(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Removes the abnormal visual and sends packet for updating them in client. + * @param aves the abnormal visual effects + */ + public final void stopAbnormalVisualEffect(AbnormalVisualEffect... aves) + { + for (AbnormalVisualEffect ave : aves) + { + _abnormalVisualEffects.remove(ave); + } + _owner.updateAbnormalVisualEffects(); + } + + /** + * Wrapper to update abnormal icons and effect flags. + * @param broadcast {@code true} sends update packets to observing players, {@code false} doesn't send any packets. + */ + private void updateEffectList(boolean broadcast) + { + // Create new empty flags. + long flags = 0; + final Set abnormalTypeFlags = EnumSet.noneOf(AbnormalType.class); + final Set abnormalVisualEffectFlags = EnumSet.noneOf(AbnormalVisualEffect.class); + final Set unhideBuffs = new HashSet<>(); + + // Recalculate new flags + for (BuffInfo info : _actives) + { + if (info != null) + { + final Skill skill = info.getSkill(); + + // Handle hidden buffs. Check if there was such abnormal before so we can continue. + if ((_hiddenBuffs.get() > 0) && _stackedEffects.contains(skill.getAbnormalType())) + { + // If incoming buff isnt hidden, remove any hidden buffs with its abnormal type. + if (info.isInUse()) + { + unhideBuffs.removeIf(b -> b.isAbnormalType(skill.getAbnormalType())); + } + // If this incoming buff is hidden and its first of its abnormal, or it removes any previous hidden buff with the same or lower abnormal level and add this instead. + else if (!abnormalTypeFlags.contains(skill.getAbnormalType()) || unhideBuffs.removeIf(b -> (b.isAbnormalType(skill.getAbnormalType())) && (b.getSkill().getAbnormalLvl() <= skill.getAbnormalLvl()))) + { + unhideBuffs.add(info); + } + } + + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + + // Add the AbnormalType flag. + abnormalTypeFlags.add(skill.getAbnormalType()); + + // Add AbnormalVisualEffect flag. + if (skill.hasAbnormalVisualEffects()) + { + for (AbnormalVisualEffect ave : skill.getAbnormalVisualEffects()) + { + abnormalVisualEffectFlags.add(ave); + _abnormalVisualEffects.add(ave); + } + if (broadcast) + { + _owner.updateAbnormalVisualEffects(); + } + } + } + } + // Add passive effect flags. + for (BuffInfo info : _passives) + { + if (info != null) + { + // Add the EffectType flag. + for (AbstractEffect e : info.getEffects()) + { + flags |= e.getEffectFlags(); + } + } + } + + // Replace the old flags with the new flags. + _effectFlags = flags; + _stackedEffects = abnormalTypeFlags; + + // Unhide the selected buffs. + unhideBuffs.forEach(b -> + { + b.setInUse(true); + _hiddenBuffs.decrementAndGet(); + }); + + // Recalculate all stats + _owner.getStat().recalculateStats(broadcast); + + if (broadcast) + { + // Check if there is change in AbnormalVisualEffect + if (!abnormalVisualEffectFlags.containsAll(_abnormalVisualEffects)) + { + _abnormalVisualEffects = abnormalVisualEffectFlags; + _owner.updateAbnormalVisualEffects(); + } + + // Send updates to the client + updateEffectIcons(false); + } + } + + /** + * Check if target is affected with special buff + * @param flag of special buff + * @return boolean true if affected + */ + public boolean isAffected(EffectFlag flag) + { + return (_effectFlags & flag.getMask()) != 0; + } +} diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/Creature.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/Creature.java index 7da13a6397..ee4d4e9fd8 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/Creature.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/Creature.java @@ -68,7 +68,7 @@ import com.l2jmobius.gameserver.instancemanager.QuestManager; import com.l2jmobius.gameserver.instancemanager.TimersManager; import com.l2jmobius.gameserver.instancemanager.ZoneManager; import com.l2jmobius.gameserver.model.AccessLevel; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.CreatureContainer; import com.l2jmobius.gameserver.model.Hit; import com.l2jmobius.gameserver.model.Location; @@ -232,7 +232,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe private volatile Map _ignoreSkillEffects; /** Creatures effect list. */ - private final CharEffectList _effectList = new CharEffectList(this); + private final EffectList _effectList = new EffectList(this); /** The creature that summons this character. */ private Creature _summoner = null; @@ -345,7 +345,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe setIsInvul(true); } - public final CharEffectList getEffectList() + public final EffectList getEffectList() { return _effectList; } @@ -4675,7 +4675,7 @@ public abstract class Creature extends WorldObject implements ISkillsHolder, IDe * Check if target is affected with special buff * @param flag int * @return boolean - * @see CharEffectList#isAffected(EffectFlag) + * @see EffectList#isAffected(EffectFlag) */ public boolean isAffected(EffectFlag flag) { diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java index a9aa49811d..23d246b634 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/actor/stat/CreatureStat.java @@ -36,7 +36,7 @@ import java.util.stream.Stream; import com.l2jmobius.Config; import com.l2jmobius.gameserver.enums.AttributeType; import com.l2jmobius.gameserver.enums.Position; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.items.instance.ItemInstance; import com.l2jmobius.gameserver.model.skills.AbnormalType; @@ -773,7 +773,7 @@ public class CreatureStat resetStats(); // Collect all necessary effects - final CharEffectList effectList = _creature.getEffectList(); + final EffectList effectList = _creature.getEffectList(); final Stream passives = effectList.getPassives().stream().filter(BuffInfo::isInUse).filter(info -> info.getSkill().checkConditions(SkillConditionScope.PASSIVE, _creature, _creature)); final Stream options = effectList.getOptions().stream().filter(BuffInfo::isInUse); final Stream effectsStream = Stream.concat(effectList.getEffects().stream().filter(BuffInfo::isInUse), Stream.concat(passives, options)); diff --git a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java index b4d8f0cf74..4f6d8ad8c1 100644 --- a/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java +++ b/L2J_Mobius_Classic_2.3_SevenSigns/java/com/l2jmobius/gameserver/model/skills/BuffInfo.java @@ -25,7 +25,7 @@ import java.util.concurrent.ScheduledFuture; import com.l2jmobius.Config; import com.l2jmobius.commons.concurrent.ThreadPool; import com.l2jmobius.gameserver.GameTimeController; -import com.l2jmobius.gameserver.model.CharEffectList; +import com.l2jmobius.gameserver.model.EffectList; import com.l2jmobius.gameserver.model.actor.Creature; import com.l2jmobius.gameserver.model.actor.Summon; import com.l2jmobius.gameserver.model.effects.AbstractEffect; @@ -290,7 +290,7 @@ public final class BuffInfo * Stops all the effects for this buff info.
* Removes effects stats.
* It will not remove the buff info from the effect list.
- * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)} + * Instead call {@link EffectList#stopSkillEffects(boolean, Skill)} * @param removed if {@code true} the skill will be handled as removed */ public void stopAllEffects(boolean removed)