Bläddra i källkod

Effectlist rework.
Still not perfect, but we will continue to develop and optimize it.
Required DP update.

_DS_ 16 år sedan
förälder
incheckning
db142cbd00

+ 617 - 332
L2_GameServer/java/net/sf/l2j/gameserver/model/CharEffectList.java

@@ -14,6 +14,8 @@
  */
 package net.sf.l2j.gameserver.model;
 
+import java.util.concurrent.LinkedBlockingQueue;
+import java.util.concurrent.locks.ReentrantLock;
 import java.util.Iterator;
 import java.util.List;
 import java.util.Map;
@@ -22,12 +24,16 @@ import javolution.util.FastList;
 import javolution.util.FastMap;
 import net.sf.l2j.Config;
 import net.sf.l2j.gameserver.model.actor.L2Character;
+import net.sf.l2j.gameserver.model.actor.L2Playable;
+import net.sf.l2j.gameserver.model.actor.L2Summon;
 import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
+import net.sf.l2j.gameserver.model.olympiad.Olympiad;
 import net.sf.l2j.gameserver.network.SystemMessageId;
+import net.sf.l2j.gameserver.network.serverpackets.AbnormalStatusUpdate;
+import net.sf.l2j.gameserver.network.serverpackets.ExOlympiadSpelledInfo;
+import net.sf.l2j.gameserver.network.serverpackets.PartySpelled;
 import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
-import net.sf.l2j.gameserver.skills.effects.EffectCharmOfCourage;
 import net.sf.l2j.gameserver.templates.skills.L2EffectType;
-import net.sf.l2j.gameserver.templates.skills.L2SkillType;
 
 public class CharEffectList
 {
@@ -39,6 +45,14 @@ public class CharEffectList
 	// The table containing the List of all stacked effect in progress for each Stack group Identifier
 	protected Map<String, List<L2Effect>> _stackedEffects;
 
+	private boolean _queuesInitialized = false;
+	private LinkedBlockingQueue<L2Effect> _addQueue;
+	private LinkedBlockingQueue<L2Effect> _removeQueue;
+	private final ReentrantLock queueLock = new ReentrantLock();
+
+	// only party icons need to be updated
+	private boolean _partyOnly = false;
+
 	// Owner of this list
 	private L2Character _owner;
 
@@ -47,31 +61,6 @@ public class CharEffectList
 		_owner = owner;
 	}
 
-	public int getElementIdFromEffects()
-	{
-		L2Effect[] effects = getAllEffects();
-		int elementId = -1;
-		for (L2Effect e : effects)
-		{
-			if (e == null)
-				continue;
-			if (e.getSkill().getElement() < 1)
-				continue;
-			// 2nd order - passive skills/toggles
-			if (e.getSkill().isPassive()||e.getSkill().isToggle())
-			{
-				elementId = e.getSkill().getElement()-1;
-				break;
-			}
-			// 3rd order: get first element (if any)
-			else if (elementId < 0)
-			{
-				elementId = e.getSkill().getElement()-1;
-				// do not break; this line, toggle skill can replace element
-			}
-		}
-		return elementId;
-	}
 	/**
 	 * Returns all effects affecting stored in this CharEffectList
 	 * @return
@@ -88,15 +77,21 @@ public class CharEffectList
 		FastList<L2Effect> temp = new FastList<L2Effect>();
 
 		// Add all buffs and all debuffs
-		synchronized (_buffs)
+		if (_buffs != null) 
 		{
-			if (_buffs != null && !_buffs.isEmpty()) 
-				temp.addAll(_buffs);
+			synchronized (_buffs)
+			{
+				if (!_buffs.isEmpty())
+					temp.addAll(_buffs);
+			}
 		}
-		synchronized (_debuffs)
+		if (_debuffs != null)
 		{
-			if (_debuffs != null && !_debuffs.isEmpty()) 
-				temp.addAll(_debuffs);
+			synchronized (_debuffs)
+			{
+				if (!_debuffs.isEmpty())
+					temp.addAll(_debuffs);
+			}
 		}
 
 		// Return all effects in an array
@@ -112,21 +107,45 @@ public class CharEffectList
 	 */
 	public final L2Effect getFirstEffect(L2EffectType tp)
 	{
-		L2Effect[] effects = getAllEffects();
+		L2Effect effectNotInUse = null;
 
-		L2Effect eventNotInUse = null;
-		for (L2Effect e : effects)
+		if (_buffs != null && !_buffs.isEmpty())
 		{
-			if (e == null)
-				continue;
-			
-			if (e.getEffectType() == tp)
+			synchronized (_buffs)
+			{
+				for (L2Effect e: _buffs)
+				{
+					if (e == null)
+						continue;
+					if (e.getEffectType() == tp)
+					{
+						if (e.getInUse())
+							return e;
+						else
+							effectNotInUse = e;
+					}
+				}
+			}
+		}
+		if (effectNotInUse == null && _debuffs != null && !_debuffs.isEmpty())
+		{
+			synchronized (_debuffs)
 			{
-				if (e.getInUse()) return e;
-				else eventNotInUse = e;
+				for (L2Effect e: _debuffs)
+				{
+					if (e == null)
+						continue;
+					if (e.getEffectType() == tp)
+					{
+						if (e.getInUse())
+							return e;
+						else
+							effectNotInUse = e;
+					}
+				}
 			}
 		}
-		return eventNotInUse;
+		return effectNotInUse;
 	}
 
 	/**
@@ -136,22 +155,52 @@ public class CharEffectList
 	 */
 	public final L2Effect getFirstEffect(L2Skill skill)
 	{
-		L2Effect[] effects = getAllEffects();
+		L2Effect effectNotInUse = null;
 
-		L2Effect eventNotInUse = null;
-		
-		if (effects == null || effects.length < 1)
-			return eventNotInUse;
-		
-		for (L2Effect e : effects)
+		if (skill.isDebuff())
+		{
+			if (_debuffs == null || _debuffs.isEmpty())
+				return null;
+
+			synchronized (_debuffs)
+			{
+				for (L2Effect e: _debuffs)
+				{
+					if (e == null)
+						continue;
+					if (e.getSkill() == skill)
+					{
+						if (e.getInUse())
+							return e;
+						else
+							effectNotInUse = e;
+					}
+				}
+			}
+			return effectNotInUse;
+		}
+		else
 		{
-			if (e != null && e.getSkill() == skill)
+			if (_buffs == null || _buffs.isEmpty())
+				return null;
+
+			synchronized (_buffs)
 			{
-				if (e.getInUse()) return e;
-				else eventNotInUse = e;
+				for (L2Effect e: _buffs)
+				{
+					if (e == null)
+						continue;
+					if (e.getSkill() == skill)
+					{
+						if (e.getInUse())
+							return e;
+						else
+							effectNotInUse = e;
+					}
+				}
 			}
+			return effectNotInUse;
 		}
-		return eventNotInUse;
 	}
 
 	/**
@@ -161,22 +210,47 @@ public class CharEffectList
 	 */
 	public final L2Effect getFirstEffect(int skillId)
 	{
-		L2Effect[] effects = getAllEffects();
+		L2Effect effectNotInUse = null;
 
-		L2Effect eventNotInUse = null;
-		
-		if (effects == null || effects.length < 1)
-			return eventNotInUse;
+		if (_buffs != null && !_buffs.isEmpty())
+		{
+			synchronized (_buffs)
+			{
+				for (L2Effect e: _buffs)
+				{
+					if (e == null)
+						continue;
+					if (e.getSkill().getId() == skillId)
+					{
+						if (e.getInUse())
+							return e;
+						else
+							effectNotInUse = e;
+					}
+				}
+			}
+		}
 		
-		for (L2Effect e : effects)
+		if (effectNotInUse == null && _debuffs != null && !_debuffs.isEmpty())
 		{
-			if (e!= null && e.getSkill().getId() == skillId)
+			synchronized (_debuffs)
 			{
-				if (e.getInUse()) return e;
-				else eventNotInUse = e;
+				for (L2Effect e: _debuffs)
+				{
+					if (e == null)
+						continue;
+					if (e.getSkill().getId() == skillId)
+					{
+						if (e.getInUse())
+							return e;
+						else
+							effectNotInUse = e;
+					}
+				}
 			}
 		}
-		return eventNotInUse;
+		
+		return effectNotInUse;
 	}
 
 	/**
@@ -188,19 +262,18 @@ public class CharEffectList
 	 */
 	private boolean doesStack(L2Skill checkSkill)
 	{
-		if ( (_buffs == null || _buffs.isEmpty()) && (_debuffs == null || _debuffs.isEmpty()) ||
+		if ( (_buffs == null || _buffs.isEmpty()) ||
 				checkSkill._effectTemplates == null ||
 				checkSkill._effectTemplates.length < 1 ||
 				checkSkill._effectTemplates[0].stackType == null ||
-				checkSkill._effectTemplates[0].stackType.equals("none") )
+				"none".equals(checkSkill._effectTemplates[0].stackType))
 		{
 			return false;
 		}
 
 		String stackType = checkSkill._effectTemplates[0].stackType;
 
-		L2Effect[] effects = getAllEffects();
-		for (L2Effect e : effects)
+		for (L2Effect e : _buffs)
 		{
 			if (e.getStackType() != null && e.getStackType().equals(stackType))
 			{
@@ -216,22 +289,23 @@ public class CharEffectList
 	 */
 	public int getBuffCount()
 	{
-		if (_buffs == null) return 0;
+		if (_buffs == null || _buffs.isEmpty()) return 0;
 		int buffCount=0;
 		
 		synchronized(_buffs)
 		{
 			for (L2Effect e : _buffs)
 			{
-				if (e != null && e.getShowIcon() && !e.getSkill().isDance() &&
-					!e.getSkill().isDebuff()  &&
-					(e.getSkill().getSkillType() == L2SkillType.BUFF ||
-					e.getSkill().getSkillType() == L2SkillType.REFLECT ||
-					e.getSkill().getSkillType() == L2SkillType.HEAL_PERCENT ||
-					e.getSkill().getSkillType() == L2SkillType.MANAHEAL_PERCENT) &&
-					!(e.getSkill().getId() > 4360  && e.getSkill().getId() < 4367)) // Seven Signs buffs
+				if (e != null && e.getShowIcon() && !e.getSkill().isDance() && !e.getSkill().is7Signs())
 				{
-					buffCount++;
+					switch (e.getSkill().getSkillType())
+					{
+						case BUFF:
+						case REFLECT:
+						case HEAL_PERCENT:
+						case MANAHEAL_PERCENT:
+							buffCount++;
+					}
 				}
 			}
 		}
@@ -244,7 +318,7 @@ public class CharEffectList
 	 */
 	public int getDanceCount()
 	{
-		if (_buffs == null) return 0;
+		if (_buffs == null || _buffs.isEmpty()) return 0;
 		int danceCount = 0;
 
 		synchronized(_buffs)
@@ -269,7 +343,7 @@ public class CharEffectList
 		// Exit them
 		for (L2Effect e : effects)
 		{
-			if (e != null && e.getSkill().getId() != 5660 && (e.getSkill().getId() < 840 || e.getSkill().getId() > 842))
+			if (e != null)
 			{
 				e.exit(true);
 			}
@@ -287,14 +361,8 @@ public class CharEffectList
 		// Exit them
 		for (L2Effect e : effects)
 		{
-			if (e != null)
-			{
-				if (e instanceof EffectCharmOfCourage)
-					continue;
-				if (e.getSkill().getId() >= 840 && e.getSkill().getId() <= 842)
-					continue;
+			if (e != null && !e.getSkill().isStayAfterDeath())
 				e.exit(true);
-			}
 		}
  	}
 
@@ -304,14 +372,33 @@ public class CharEffectList
 	 */
 	public final void stopEffects(L2EffectType type)
 	{
-		// Get all active skills effects from this list
-		L2Effect[] effects = getAllEffects();
-
 		// Go through all active skills effects
-		for(L2Effect e : effects)
+		FastList<L2Effect> temp = new FastList<L2Effect>();
+		if (_buffs != null && !_buffs.isEmpty()) 
+		{
+			synchronized (_buffs)
+			{
+				for (L2Effect e : _buffs)
+					// Get active skills effects of the selected type
+					if (e != null && e.getEffectType() == type)
+						temp.add(e);
+			}
+		}
+		if (_debuffs != null && !_debuffs.isEmpty()) 
+		{
+			synchronized (_debuffs)
+			{
+				for (L2Effect e : _debuffs)
+					// Get active skills effects of the selected type
+					if (e != null && e.getEffectType() == type)
+						temp.add(e);
+			}
+		}
+		if (temp != null && !temp.isEmpty())
 		{
-			// Stop active skills effects of the selected type
-			if (e.getEffectType() == type) e.exit();
+			for (L2Effect e : temp)
+				if (e != null)
+					e.exit();
 		}
 	}
 
@@ -321,370 +408,568 @@ public class CharEffectList
 	 */
 	public final void stopSkillEffects(int skillId)
 	{
-		// Get all skills effects on the L2Character
-		L2Effect[] effects = getAllEffects();
-
-		for(L2Effect e : effects)
+		// Go through all active skills effects
+		FastList<L2Effect> temp = new FastList<L2Effect>();
+		if (_buffs != null && !_buffs.isEmpty()) 
+		{
+			synchronized (_buffs)
+			{
+				for (L2Effect e : _buffs)
+					if (e != null && e.getSkill().getId() == skillId)
+						temp.add(e);
+			}
+		}
+		if (_debuffs != null && !_debuffs.isEmpty()) 
+		{
+			synchronized (_debuffs)
+			{
+				for (L2Effect e : _debuffs)
+					if (e != null && e.getSkill().getId() == skillId)
+						temp.add(e);
+			}
+		}
+		if (temp != null && !temp.isEmpty())
 		{
-			if (e.getSkill().getId() == skillId) e.exit();
+			for (L2Effect e : temp)
+				if (e != null)
+					e.exit();
 		}
 	}
 
+	public void updateEffectIcons(boolean partyOnly)
+	{
+		if (_buffs == null && _debuffs == null)
+			return;
 
+		if (partyOnly)
+			_partyOnly = true;
+		
+		queueRunner();
+	}
 
-	/**
-	 * Removes the first buff of this list.
-	 *
-	 * @param s Is the skill that is being applied.
-	 */
-	private void removeFirstBuff(L2Skill checkSkill)
+	public void queueEffect(L2Effect effect, boolean remove)
 	{
-		boolean danceBuff = false;
-		if (!checkSkill.isDance() && getBuffCount() >= _owner.getMaxBuffCount())
+		if (effect == null) return;
+
+		if (!_queuesInitialized)
+			init();
+
+		try
 		{
-			if (checkSkill.getSkillType() != L2SkillType.BUFF &&
-				checkSkill.getSkillType() != L2SkillType.REFLECT &&
-				checkSkill.getSkillType() != L2SkillType.HEAL_PERCENT &&
-				checkSkill.getSkillType() != L2SkillType.MANAHEAL_PERCENT)
-			{
-				return;
-			}
+			if (remove)
+				_removeQueue.put(effect);
+			else
+				_addQueue.put(effect);
+			
 		}
-		else if (checkSkill.isDance() && getDanceCount() >= Config.DANCES_MAX_AMOUNT)
+		catch (InterruptedException e)
 		{
-			danceBuff = true;
 		}
-		else return;
-		
-		L2Effect[] effects = getAllEffects();
-		L2Effect removeMe = null;
-
-		for (L2Effect e : effects)
+		queueRunner();
+	}
+	
+	synchronized private void init()
+	{
+		if (!_queuesInitialized)
 		{
-			if (e == null)
-				continue;
+			_queuesInitialized = true;
+			_addQueue = new LinkedBlockingQueue<L2Effect>();
+			_removeQueue = new LinkedBlockingQueue<L2Effect>();
+		}
+	}
 
-			switch (e.getSkill().getSkillType())
+	private void queueRunner()
+	{
+		synchronized (this)
+		{
+			if (queueLock.isLocked())
+				return;
+			else
+				queueLock.lock();
+		}
+		try
+		{
+			L2Effect effect;
+			do
 			{
-				case BUFF:
-				case DEBUFF:
-				case REFLECT:
-				case HEAL_PERCENT:
-				case MANAHEAL_PERCENT:
-					break;
-				default:
-					continue;
-			}
+				// remove has more priority than add
+				// so removing all effects from queue first
+				while (!_removeQueue.isEmpty())
+				{
+					effect = _removeQueue.poll();
+					removeEffectFromQueue(effect);
+					_partyOnly = false;
+				}
 
-			if ((danceBuff && e.getSkill().isDance()) || (!danceBuff && !e.getSkill().isDance()))
-			{
-				if (e.getSkill() == checkSkill)
+				if (!_addQueue.isEmpty())
 				{
-					removeMe = e;
-					break;
+					effect = _addQueue.poll();
+					addEffectFromQueue(effect);
+					_partyOnly = false;
 				}
-				else if (removeMe == null)
-					removeMe = e;
 			}
+			while (!_addQueue.isEmpty() || !_removeQueue.isEmpty());
+			
+			updateEffectIcons();
+		}
+		finally
+		{
+			queueLock.unlock();
 		}
-		if (removeMe != null) removeMe.exit();
 	}
-
-	public final void removeEffect(L2Effect effect)
+	
+	private void removeEffectFromQueue(L2Effect effect)
 	{
-		if (effect == null || (_buffs == null && _debuffs == null) ) return;
+		if (effect == null) return;
 
-		FastList<L2Effect> effectList = effect.getSkill().isDebuff() ? _debuffs : _buffs;
-
-		synchronized(effectList)
+		FastList<L2Effect> effectList;
+		
+		if (effect.getSkill().isDebuff())
 		{
+			if (_debuffs == null)
+				return;
+			effectList = _debuffs;
+		}
+		else
+		{
+			if (_buffs == null)
+				return;
+			effectList = _buffs;
+		}
 
-			if ("none".equals(effect.getStackType()))
-			{
-				// Remove Func added by this effect from the L2Character Calculator
-				_owner.removeStatsOwner(effect);
-			}
-			else
-			{
-				if(_stackedEffects == null) return;
-
-				// Get the list of all stacked effects corresponding to the stack type of the L2Effect to add
-				List<L2Effect> stackQueue = _stackedEffects.get(effect.getStackType());
+		if ("none".equals(effect.getStackType()))
+		{
+			// Remove Func added by this effect from the L2Character Calculator
+			_owner.removeStatsOwner(effect);
+		}
+		else
+		{
+			if(_stackedEffects == null) return;
 
-				if (stackQueue == null || stackQueue.isEmpty()) return;
+			// Get the list of all stacked effects corresponding to the stack type of the L2Effect to add
+			List<L2Effect> stackQueue = _stackedEffects.get(effect.getStackType());
 
-				// Get the identifier of the first stacked effect of the stack group selected
-				L2Effect frontEffect = stackQueue.get(0);
+			if (stackQueue == null || stackQueue.isEmpty()) return;
 
-				// Remove the effect from the stack group
-				boolean removed = stackQueue.remove(effect);
+			int index = stackQueue.indexOf(effect);
 
-				if (removed)
+			// Remove the effect from the stack group
+			if (index >= 0)
+			{
+				stackQueue.remove(effect);
+				// Check if the first stacked effect was the effect to remove
+				if (index == 0)
 				{
-					// Check if the first stacked effect was the effect to remove
-					if (frontEffect == effect)
-					{
-						// Remove all its Func objects from the L2Character calculator set
-						_owner.removeStatsOwner(effect);
+					// Remove all its Func objects from the L2Character calculator set
+					_owner.removeStatsOwner(effect);
 
-						// Check if there's another effect in the Stack Group
-						if (!stackQueue.isEmpty())
+					// Check if there's another effect in the Stack Group
+					if (!stackQueue.isEmpty())
+					{
+						L2Effect newStackedEffect = listsContains(stackQueue.get(0));
+						if (newStackedEffect != null)
 						{
 							// Add its list of Funcs to the Calculator set of the L2Character
-							for (L2Effect e : effectList)
-							{
-								if (e == stackQueue.get(0))
-								{
-									// Add its list of Funcs to the Calculator set of the L2Character
-									_owner.addStatFuncs(e.getStatFuncs());
-									// Set the effect to In Use
-									e.setInUse(true);
-									break;
-								}
-							}
+							_owner.addStatFuncs(newStackedEffect.getStatFuncs());
+							// Set the effect to In Use
+							newStackedEffect.setInUse(true);
 						}
 					}
-					if (stackQueue.isEmpty())
-						_stackedEffects.remove(effect.getStackType());
-					else
-						// Update the Stack Group table _stackedEffects of the L2Character
-						_stackedEffects.put(effect.getStackType(), stackQueue);
 				}
+				if (stackQueue.isEmpty())
+					_stackedEffects.remove(effect.getStackType());
+				else
+					// Update the Stack Group table _stackedEffects of the L2Character
+					_stackedEffects.put(effect.getStackType(), stackQueue);
 			}
+		}
 
-			// Remove the active skill L2effect from _effects of the L2Character
-			// The Integer key of _effects is the L2Skill Identifier that has created the effect
-			for (L2Effect e : effectList)
+		// Remove the active skill L2effect from _effects of the L2Character
+		if (effectList.remove(effect) && _owner instanceof L2PcInstance && effect.getShowIcon())
+		{
+			SystemMessage sm;
+			if (effect.getSkill().isToggle())
 			{
-				if (e == effect)
-				{
-					effectList.remove(e);
-					if (_owner instanceof L2PcInstance)
-					{
-						SystemMessage sm;
-						if (effect.getSkill().isToggle())
-							sm = new SystemMessage(SystemMessageId.S1_HAS_BEEN_ABORTED);
-						else
-							sm = new SystemMessage(SystemMessageId.EFFECT_S1_DISAPPEARED);
-						sm.addSkillName(effect);
-						_owner.sendPacket(sm);
-					}
-					break;
-				}
+				sm = new SystemMessage(SystemMessageId.S1_HAS_BEEN_ABORTED);
 			}
-
+			else
+			{
+				sm = new SystemMessage(SystemMessageId.EFFECT_S1_DISAPPEARED);
+			}
+			sm.addSkillName(effect);
+			_owner.sendPacket(sm);
 		}
 	}
 
-	public void addEffect(L2Effect newEffect)
+	private void addEffectFromQueue(L2Effect newEffect)
 	{
 		if (newEffect == null) return;
 
-		synchronized (this)
+		L2Skill newSkill = newEffect.getSkill();
+
+		if (newSkill.isDebuff())
 		{
-			if (_buffs == null) _buffs = new FastList<L2Effect>();
 			if (_debuffs == null) _debuffs = new FastList<L2Effect>();
-			if (_stackedEffects == null) _stackedEffects = new FastMap<String, List<L2Effect>>();
+			for (L2Effect e : _debuffs)
+			{
+				if (e != null
+						&& e.getSkill().getId() == newEffect.getSkill().getId()
+						&& e.getEffectType() == newEffect.getEffectType()
+						&& e.getStackOrder() == newEffect.getStackOrder()
+						&& e.getStackType().equals(newEffect.getStackType()))
+				{
+					// Started scheduled timer needs to be canceled.
+					newEffect.stopEffectTask(); 
+					return; 
+				}
+			}
+			_debuffs.addLast(newEffect);
 		}
-
-		FastList<L2Effect> effectList = newEffect.getSkill().isDebuff() ? _debuffs : _buffs;
-		L2Effect tempEffect = null, tempEffect2 = null;
-		boolean stopNewEffect = false;
-
-		synchronized(effectList)
+		else
 		{
-			// Check for same effects
-			for (L2Effect e : effectList)
+			if (_buffs == null) _buffs = new FastList<L2Effect>();
+
+			for (L2Effect e : _buffs)
 			{
 				if (e != null
 						&& e.getSkill().getId() == newEffect.getSkill().getId()
 						&& e.getEffectType() == newEffect.getEffectType()
-						&& e.getStackOrder() == newEffect.getStackOrder())
+						&& e.getStackOrder() == newEffect.getStackOrder()
+						&& e.getStackType().equals(newEffect.getStackType()))
+				{
+					e.exit(); // exit this
+				}
+			}
+
+			// if max buffs, no herb effects are used, even if they would replace one old
+			if (newEffect.isHerbEffect() && getBuffCount() >= _owner.getMaxBuffCount())
+			{ 
+				newEffect.stopEffectTask(); 
+				return; 
+			}
+
+			// Remove first buff when buff list is full
+			if (!doesStack(newSkill) && !newSkill.is7Signs())
+			{
+				int effectsToRemove;
+				if (newSkill.isDance())
 				{
-					if (!newEffect.getSkill().isDebuff())
+					effectsToRemove = getDanceCount() - Config.DANCES_MAX_AMOUNT;
+					if (effectsToRemove >= 0)
 					{
-						tempEffect = e; // exit this
-						break;
+						for (L2Effect e : _buffs)
+						{
+							if (e == null || !e.getSkill().isDance())
+								continue;
+							
+							// get first dance
+							e.exit();
+							effectsToRemove--;
+							if (effectsToRemove < 0)
+								break;
+						}
 					}
-					else
+				}
+				else
+				{
+					effectsToRemove = getBuffCount() - _owner.getMaxBuffCount();
+					if (effectsToRemove >= 0)
 					{
-						// Started scheduled timer needs to be canceled.
-						stopNewEffect = true;
-						break;
+						switch (newSkill.getSkillType())
+						{
+							case BUFF:
+							case REFLECT:
+							case HEAL_PERCENT:
+							case MANAHEAL_PERCENT:
+								for (L2Effect e : _buffs)
+								{
+									if (e == null || e.getSkill().isDance())
+										continue;
+
+									switch (e.getSkill().getSkillType())
+									{
+										case BUFF:
+										case REFLECT:
+										case HEAL_PERCENT:
+										case MANAHEAL_PERCENT:
+											e.exit();
+											effectsToRemove--;
+											break; // break switch()
+										default:
+											continue; // continue for()
+									}
+									if (effectsToRemove < 0)
+										break; // break for()
+								}
+						}
 					}
 				}
 			}
-		}
 
-		if (tempEffect != null) 
-			tempEffect.exit();
-		
-		// if max buffs, no herb effects are used, even if they would replace one old
-		if (stopNewEffect || (getBuffCount() >= _owner.getMaxBuffCount() && newEffect.isHerbEffect()))
-		{ 
-			newEffect.stopEffectTask(); 
-			return; 
-		}
-			
-		// Remove first buff when buff list is full
-		L2Skill tempSkill = newEffect.getSkill();
-		if (!doesStack(tempSkill) && !tempSkill.isDebuff()  &&
-				!(tempSkill.getId() > 4360 && tempSkill.getId() < 4367))
-		{
-			removeFirstBuff(tempSkill);
-		}
-			
-		synchronized(effectList)
-		{
-			// Add the L2Effect to all effect in progress on the L2Character
-			if (!newEffect.getSkill().isToggle() && !newEffect.getSkill().isDebuff())
+			// Icons order: buffs, 7s, toggles, dances
+			if (newSkill.isDance())
+				_buffs.addLast(newEffect);
+			else
 			{
 				int pos=0;
-				for (L2Effect e : effectList)
+				if (newSkill.isToggle())
+				{
+					// toggle skill - before all dances
+					for (L2Effect e : _buffs)
+					{
+						if (e == null)
+							continue;
+						if (e.getSkill().isDance())
+							break;
+						pos++;
+					}
+				}
+				else
 				{
-					if (e != null)
+					// normal buff - before toggles and 7s and dances
+					for (L2Effect e : _buffs)
 					{
-						int skillid = e.getSkill().getId();
-						if (!e.getSkill().isToggle() && (!(skillid > 4360  && skillid < 4367))) pos++;
+						if (e == null)
+							continue;
+						if (e.getSkill().isToggle() || e.getSkill().is7Signs()
+								|| e.getSkill().isDance())
+							break;
+						pos++;
 					}
-					else break;
 				}
-				effectList.add(pos, newEffect);
+				_buffs.add(pos, newEffect);
 			}
-			else effectList.addLast(newEffect);
 		}
 
 		// Check if a stack group is defined for this effect
-		if (newEffect.getStackType().equals("none"))
+		if ("none".equals(newEffect.getStackType()))
 		{
 			// Set this L2Effect to In Use
 			newEffect.setInUse(true);
 
 			// Add Funcs of this effect to the Calculator set of the L2Character
 			_owner.addStatFuncs(newEffect.getStatFuncs());
-			
-			// Update active skills in progress icons on player client
-			_owner.updateEffectIcons();
 			return;
 		}
 
+		List<L2Effect> stackQueue;
+		L2Effect effectToAdd = null;
+		L2Effect effectToRemove = null;
+		if (_stackedEffects == null) _stackedEffects = new FastMap<String, List<L2Effect>>();
+					
 		// Get the list of all stacked effects corresponding to the stack type of the L2Effect to add
-		List<L2Effect> stackQueue = _stackedEffects.get(newEffect.getStackType());
-		L2Effect[] allEffects = getAllEffects();
-
-		if (stackQueue == null)
-			stackQueue = new FastList<L2Effect>();
-
-		tempEffect = null;
-		if (!stackQueue.isEmpty())
+		stackQueue = _stackedEffects.get(newEffect.getStackType());
+		
+		if (stackQueue != null)
 		{
-			// Get the first stacked effect of the Stack group selected
-			for (L2Effect e : allEffects)
+			int pos = 0;
+			if (!stackQueue.isEmpty())
 			{
-				if (e == stackQueue.get(0))
+				// Get the first stacked effect of the Stack group selected
+				effectToRemove = listsContains(stackQueue.get(0));
+
+				// Create an Iterator to go through the list of stacked effects in progress on the L2Character
+				Iterator<L2Effect> queueIterator = stackQueue.iterator();
+
+				while (queueIterator.hasNext())
 				{
-					tempEffect = e;
-					break;
+		            if (newEffect.getStackOrder() < queueIterator.next().getStackOrder())
+		            	pos++;
+		            else break;
+		        }
+				// Add the new effect to the Stack list in function of its position in the Stack group
+				stackQueue.add(pos, newEffect);
+
+				// skill.exit() could be used, if the users don't wish to see "effect
+				// removed" always when a timer goes off, even if the buff isn't active
+				// any more (has been replaced). but then check e.g. npc hold and raid petrification.
+				if (Config.EFFECT_CANCELING && !newEffect.isHerbEffect() && stackQueue.size() > 1)
+				{
+					if (newSkill.isDebuff())
+					{
+						_debuffs.remove(stackQueue.remove(1));
+					}
+					else
+					{
+						_buffs.remove(stackQueue.remove(1));
+					}
 				}
 			}
+			else
+				stackQueue.add(0, newEffect);
+		}
+		else
+		{
+			stackQueue = new FastList<L2Effect>();
+			stackQueue.add(0, newEffect);
 		}
-		
-		// Add the new effect to the stack group selected at its position
-		stackQueue = effectQueueInsert(newEffect, stackQueue);
-
-		if (stackQueue == null) return;
 
 		// Update the Stack Group table _stackedEffects of the L2Character
 		_stackedEffects.put(newEffect.getStackType(), stackQueue);
 
 		// Get the first stacked effect of the Stack group selected
-		tempEffect2 = null;
-		for (L2Effect e : allEffects)
+		if (stackQueue != null && !stackQueue.isEmpty())
 		{
-			if (e == stackQueue.get(0))
-			{
-				tempEffect2 = e;
-				break;
-			}
+			effectToAdd = listsContains(stackQueue.get(0));
 		}
 
-		if (tempEffect != tempEffect2)
+		if (effectToRemove != effectToAdd)
 		{
-			if (tempEffect != null)
+			if (effectToRemove != null)
 			{
 				// Remove all Func objects corresponding to this stacked effect from the Calculator set of the L2Character
-				_owner.removeStatsOwner(tempEffect);
+				_owner.removeStatsOwner(effectToRemove);
 
 				// Set the L2Effect to Not In Use
-				tempEffect.setInUse(false);
+				effectToRemove.setInUse(false);
 			}
-			if (tempEffect2 != null)
+			if (effectToAdd != null)
 			{
 				// Set this L2Effect to In Use
-				tempEffect2.setInUse(true);
+				effectToAdd.setInUse(true);
 
 				// Add all Func objects corresponding to this stacked effect to the Calculator set of the L2Character
-				_owner.addStatFuncs(tempEffect2.getStatFuncs());
+				_owner.addStatFuncs(effectToAdd.getStatFuncs());
 			}
 		}
 	}
 
-	/**
-	 * Insert an effect at the specified position in a Stack Group.<BR><BR>
-	 *
-	 * <B><U> Concept</U> :</B><BR><BR>
-	 * Several same effect can't be used on a L2Character at the same time.
-	 * Indeed, effects are not stackable and the last cast will replace the previous in progress.
-	 * More, some effects belong to the same Stack Group (ex WindWalk and Haste Potion).
-	 * If 2 effects of a same group are used at the same time on a L2Character, only the more efficient (identified by its priority order) will be preserve.<BR><BR>
-	 *
-	 * @param id The identifier of the stacked effect to add to the Stack Group
-	 * @param stackOrder The position of the effect in the Stack Group
-	 * @param stackQueue The Stack Group in which the effect must be added
-	 *
-	 */
-	private List<L2Effect> effectQueueInsert(L2Effect newStackedEffect, List<L2Effect> stackQueue)
+	private void updateEffectIcons()
 	{
-		// Get the L2Effect corresponding to the effect identifier from the L2Character _effects
-		if (_buffs == null && _debuffs == null) return null;
+		if (_owner == null || !(_owner instanceof L2Playable))
+			return;
 
-		FastList<L2Effect> effectList = newStackedEffect.getSkill().isDebuff() ? _debuffs : _buffs;
+		AbnormalStatusUpdate mi = null;
+		PartySpelled ps = null;
+		ExOlympiadSpelledInfo os = null;
+		
+		if (_owner instanceof L2PcInstance)
+		{
+			if (_partyOnly)
+				_partyOnly = false;
+			else
+				mi = new AbnormalStatusUpdate();
 
-		// Create an Iterator to go through the list of stacked effects in progress on the L2Character
-		Iterator<L2Effect> queueIterator = stackQueue.iterator();
+			if (_owner.isInParty())
+				ps = new PartySpelled(_owner);
 
-		int i = 0;
-		while (queueIterator.hasNext())
+			if(((L2PcInstance)_owner).isInOlympiadMode() && ((L2PcInstance)_owner).isOlympiadStart())
+				os = new ExOlympiadSpelledInfo((L2PcInstance)_owner);
+		}
+		else
+			if (_owner instanceof L2Summon)
+				ps = new PartySpelled(_owner);
+
+		if (_buffs != null && !_buffs.isEmpty())
+		{
+			synchronized (_buffs)
+			{
+				for (L2Effect e : _buffs)
+				{
+					if (e == null || !e.getShowIcon())
+						continue;
+
+					switch (e.getEffectType())
+	                {
+	                	case CHARGE: // handled by EtcStatusUpdate
+	                	case SIGNET_GROUND:
+	                		continue;
+	                }
+					
+					if (e.getInUse())
+					{
+						if (mi != null)
+							e.addIcon(mi);
+						
+						if (ps != null)
+							e.addPartySpelledIcon(ps);
+						
+						if (os != null && !e.getSkill().isToggle())
+							e.addOlympiadSpelledIcon(os);
+					}
+				}
+			}
+		}
+		if (_debuffs != null && !_debuffs.isEmpty())
 		{
-            L2Effect cur = queueIterator.next();
-            if (newStackedEffect.getStackOrder() < cur.getStackOrder())
-            	i++;
-            else break;
-        }
+			synchronized (_debuffs)
+			{
+				for (L2Effect e : _debuffs)
+				{
+					if (e == null || !e.getShowIcon())
+						continue;
+
+					switch (e.getEffectType())
+	                {
+	                	case SIGNET_GROUND:
+	                		continue;
+	                }
+
+					if (e.getInUse())
+					{
+						if (mi != null)
+							e.addIcon(mi);
+						
+						if (ps != null)
+							e.addPartySpelledIcon(ps);
+
+						if (os != null)
+							e.addOlympiadSpelledIcon(os);
+					}
+				}
+			}
+		}
 
-		// Add the new effect to the Stack list in function of its position in the Stack group
-		stackQueue.add(i, newStackedEffect);
+		if (mi != null)
+			_owner.sendPacket(mi);
 
-		// skill.exit() could be used, if the users don't wish to see "effect
-		// removed" always when a timer goes off, even if the buff isn't active
-		// any more (has been replaced). but then check e.g. npc hold and raid petrification.
-		if (Config.EFFECT_CANCELING && !newStackedEffect.isHerbEffect() && stackQueue.size() > 1)
+		if (ps != null)
 		{
-			// only keep the current effect, cancel other effects
-			synchronized(effectList)
+			if (_owner instanceof L2Summon)
 			{
-				for (L2Effect e : effectList)
+				L2PcInstance summonOwner = ((L2Summon)_owner).getOwner();
+
+				if (summonOwner != null)
 				{
-					if (e == stackQueue.get(1))
+					if (summonOwner.isInParty())
+						summonOwner.getParty().broadcastToPartyMembers(ps);
+					else
+						summonOwner.sendPacket(ps);
+				}
+			}
+			else
+				if (_owner instanceof L2PcInstance && _owner.isInParty())
+					_owner.getParty().broadcastToPartyMembers(ps);
+		}
+		
+		if (os != null)
+		{
+			if (Olympiad.getInstance().getSpectators(((L2PcInstance)_owner).getOlympiadGameId()) != null)
+			{
+				for (L2PcInstance spec : Olympiad.getInstance().getSpectators(((L2PcInstance)_owner).getOlympiadGameId()))
+				{
+					if (spec != null)
 					{
-						effectList.remove(e);
-						break;
+						spec.sendPacket(os);
 					}
 				}
 			}
-			stackQueue.remove(1);
 		}
+	}
 
-		return stackQueue;
+	/**
+	 * Returns effect if contains in _buffs or _debuffs and null if not found
+	 * @param effect
+	 * @return
+	 */
+	private L2Effect listsContains(L2Effect effect)
+	{
+		if (_buffs != null && !_buffs.isEmpty()&& _buffs.contains(effect))
+			return effect;
+		if (_debuffs != null && !_debuffs.isEmpty() && _debuffs.contains(effect))
+			return effect;
+		return null;
 	}
+	
+
 }

+ 18 - 2
L2_GameServer/java/net/sf/l2j/gameserver/model/L2Skill.java

@@ -189,7 +189,9 @@ public abstract class L2Skill implements IChanceSkillTrigger
     private final int[] _negateId; 			// cancels the effect of skill ID
     private final L2SkillType[] _negateStats; 	// lists the effect types that are canceled
     private final int _maxNegatedEffects; 	// maximum number of effects to negate
-    
+
+    private final boolean _stayAfterDeath; // skill should stay after death
+
     // kill by damage over time
     private final boolean _killByDOT;
 
@@ -391,7 +393,9 @@ public abstract class L2Skill implements IChanceSkillTrigger
         else
         	_negateId = new int[0];
         _maxNegatedEffects = set.getInteger("maxNegated", 0);
-        
+
+        _stayAfterDeath = set.getBool("stayAfterDeath", false);
+
         _killByDOT = set.getBool("killByDOT", false);
         _isNeutral = set.getBool("neutral",false);
         _hitTime = set.getInteger("hitTime", 0);
@@ -1171,6 +1175,18 @@ public abstract class L2Skill implements IChanceSkillTrigger
         }
     }
 
+    public final boolean is7Signs()
+    {
+    	if (_id > 4360 && _id < 4367)
+    		return true;
+    	return false;
+    }
+
+    public final boolean isStayAfterDeath()
+    {
+    	return _stayAfterDeath;
+    }
+
     //	int weapons[] = {L2Weapon.WEAPON_TYPE_ETC, L2Weapon.WEAPON_TYPE_BOW,
     //	L2Weapon.WEAPON_TYPE_POLE, L2Weapon.WEAPON_TYPE_DUALFIST,
     //	L2Weapon.WEAPON_TYPE_DUAL, L2Weapon.WEAPON_TYPE_BLUNT,

+ 3 - 14
L2_GameServer/java/net/sf/l2j/gameserver/model/actor/L2Character.java

@@ -2650,7 +2650,7 @@ public abstract class L2Character extends L2Object
 	/** Map 32 bits (0x0000) containing all abnormal effect in progress */
 	private int _AbnormalEffects;
 
-	private CharEffectList _effects = new CharEffectList(this);
+	protected CharEffectList _effects = new CharEffectList(this);
 
 	public static final int ABNORMAL_EFFECT_BLEEDING		= 0x000001;
 	public static final int ABNORMAL_EFFECT_POISON 			= 0x000002;
@@ -2708,10 +2708,7 @@ public abstract class L2Character extends L2Object
 	 */
 	public void addEffect(L2Effect newEffect)
 	{
-		_effects.addEffect(newEffect);
-
-		// Update active skills in progress (In Use and Not In Use because stacked) icons on client
-		updateEffectIcons();
+		_effects.queueEffect(newEffect, false);
 	}
 
 	/**
@@ -2735,10 +2732,7 @@ public abstract class L2Character extends L2Object
 	 */
 	public final void removeEffect(L2Effect effect)
 	{
-		_effects.removeEffect(effect);
-
-		// Update active skills in progress (In Use and Not In Use because stacked) icons on client
-		updateEffectIcons();
+		_effects.queueEffect(effect, true);
 	}
 
 	/**
@@ -6954,9 +6948,4 @@ public abstract class L2Character extends L2Object
     	_isRaid = val;
     	_isMinion = val;
     }
-    
-    public int getElementIdFromEffects()
-    {
-    	return _effects.getElementIdFromEffects();
-    }
 }

+ 0 - 38
L2_GameServer/java/net/sf/l2j/gameserver/model/actor/L2Summon.java

@@ -22,7 +22,6 @@ import net.sf.l2j.gameserver.ai.CtrlIntention;
 import net.sf.l2j.gameserver.ai.L2CharacterAI;
 import net.sf.l2j.gameserver.ai.L2SummonAI;
 import net.sf.l2j.gameserver.datatables.SkillTable;
-import net.sf.l2j.gameserver.model.L2Effect;
 import net.sf.l2j.gameserver.model.L2ItemInstance;
 import net.sf.l2j.gameserver.model.L2Object;
 import net.sf.l2j.gameserver.model.L2Party;
@@ -49,7 +48,6 @@ import net.sf.l2j.gameserver.network.serverpackets.ExPartyPetWindowDelete;
 import net.sf.l2j.gameserver.network.serverpackets.ExPartyPetWindowUpdate;
 import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
 import net.sf.l2j.gameserver.network.serverpackets.MyTargetSelected;
-import net.sf.l2j.gameserver.network.serverpackets.PartySpelled;
 import net.sf.l2j.gameserver.network.serverpackets.PetDelete;
 import net.sf.l2j.gameserver.network.serverpackets.PetInfo;
 import net.sf.l2j.gameserver.network.serverpackets.PetStatusShow;
@@ -377,42 +375,6 @@ public abstract class L2Summon extends L2Playable
         updateAndBroadcastStatus(1);
     }
     
-    @Override
-    public void updateEffectIcons(boolean partyOnly)
-    {
-    	if (this instanceof L2MerchantSummonInstance)
-			return;
-        PartySpelled ps = new PartySpelled(this);
-        
-        // Go through all effects if any
-        L2Effect[] effects = getAllEffects();
-        if (effects != null && effects.length > 0)
-        {
-            for (L2Effect effect: effects)
-            {
-                if (effect == null)
-                    continue;
-                
-                if (effect.getInUse() && effect.getShowIcon())
-                {
-                    effect.addPartySpelledIcon(ps);
-                }
-            }
-        }
-        
-        L2Party party = this.getOwner().getParty();
-        if (party != null)
-        {
-            // tell everyone about the summon effect
-            party.broadcastToPartyMembers(ps);
-        }
-        else
-        {
-            // tell only the owner
-            this.getOwner().sendPacket(ps);
-        }
-    }
-
     public void deleteMe(L2PcInstance owner)
     {
     	if (this instanceof L2MerchantSummonInstance)

+ 0 - 85
L2_GameServer/java/net/sf/l2j/gameserver/model/actor/instance/L2PcInstance.java

@@ -142,7 +142,6 @@ import net.sf.l2j.gameserver.model.quest.QuestState;
 import net.sf.l2j.gameserver.model.quest.State;
 import net.sf.l2j.gameserver.network.L2GameClient;
 import net.sf.l2j.gameserver.network.SystemMessageId;
-import net.sf.l2j.gameserver.network.serverpackets.AbnormalStatusUpdate;
 import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
 import net.sf.l2j.gameserver.network.serverpackets.ChangeWaitType;
 import net.sf.l2j.gameserver.network.serverpackets.CharInfo;
@@ -155,7 +154,6 @@ import net.sf.l2j.gameserver.network.serverpackets.ExFishingEnd;
 import net.sf.l2j.gameserver.network.serverpackets.ExFishingStart;
 import net.sf.l2j.gameserver.network.serverpackets.ExGetBookMarkInfoPacket;
 import net.sf.l2j.gameserver.network.serverpackets.ExOlympiadMode;
-import net.sf.l2j.gameserver.network.serverpackets.ExOlympiadSpelledInfo;
 import net.sf.l2j.gameserver.network.serverpackets.ExOlympiadUserInfo;
 import net.sf.l2j.gameserver.network.serverpackets.ExSetCompassZoneCode;
 import net.sf.l2j.gameserver.network.serverpackets.ExSpawnEmitter;
@@ -174,7 +172,6 @@ import net.sf.l2j.gameserver.network.serverpackets.NpcHtmlMessage;
 import net.sf.l2j.gameserver.network.serverpackets.ObservationMode;
 import net.sf.l2j.gameserver.network.serverpackets.ObservationReturn;
 import net.sf.l2j.gameserver.network.serverpackets.PartySmallWindowUpdate;
-import net.sf.l2j.gameserver.network.serverpackets.PartySpelled;
 import net.sf.l2j.gameserver.network.serverpackets.PetInventoryUpdate;
 import net.sf.l2j.gameserver.network.serverpackets.PledgeShowMemberListDelete;
 import net.sf.l2j.gameserver.network.serverpackets.PledgeShowMemberListUpdate;
@@ -4121,88 +4118,6 @@ public final class L2PcInstance extends L2Playable
         }
 	}
     
-    @Override
-    public final void updateEffectIcons(boolean partyOnly)
-    {
-        // Create the main packet if needed
-        AbnormalStatusUpdate mi = null;
-        if (!partyOnly)
-        {
-            mi = new AbnormalStatusUpdate();
-        }
-        
-        PartySpelled ps = null;
-        if (this.isInParty())
-        {
-            ps = new PartySpelled(this);
-        }
-        
-        // Create the olympiad spectator packet if needed
-        ExOlympiadSpelledInfo os = null;
-        if (this.isInOlympiadMode() && this.isOlympiadStart())
-        {
-            os = new ExOlympiadSpelledInfo(this);
-        }
-        
-        // Go through all effects if any
-        L2Effect[] effects = getAllEffects();
-        if (effects != null && effects.length > 0)
-        {
-            for (L2Effect effect : effects)
-            {
-                if (effect == null || !effect.getShowIcon())
-                {
-                    continue;
-                }
-                
-                switch (effect.getEffectType())
-                {
-                	case CHARGE: // handled by EtcStatusUpdate
-                	case SIGNET_GROUND:
-                		continue;
-                }
-                
-                if (effect.getInUse())
-                {
-                    if (mi != null)
-                        effect.addIcon(mi);
-                    if (ps != null)
-                        effect.addPartySpelledIcon(ps);
-                    if (os != null && !effect.getSkill().isToggle())
-                        effect.addOlympiadSpelledIcon(os);
-                }
-            }
-        }
-            
-        // Send the packets if needed
-        if (mi != null)
-            sendPacket(mi);
-        if (ps != null)
-        {
-            // summon info only needs to go to the owner, not to the whole party
-            // player info: if in party, send to all party members except one's self.
-            //              if not in party, send to self.
-            if (this.isInParty())
-            {
-                this.getParty().broadcastToPartyMembers(this, ps);
-            }
-        }
-        
-        if (os != null)
-        {
-            if (Olympiad.getInstance().getSpectators(this.getOlympiadGameId()) != null)
-            {
-                for (L2PcInstance spectator : Olympiad.getInstance().getSpectators(this.getOlympiadGameId()))
-                {
-                    if (spectator != null) 
-                    {
-                        spectator.sendPacket(os);
-                    }
-                }
-            }
-        }
-    }
-
 	/**
 	 * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers.<BR><BR>
 	 *