Bladeren bron

BETA: Cubic fixes:
* Fixed cubic not being removed.
* Moved cubic related tasks to own class/package.
* Reported by: Twist, Aristos
* Fixed wrong cubic count.
* Reported by: Midas, Twist


'''Note:''' More work is required, unhardcoding for example.

Zoey76 11 jaren geleden
bovenliggende
commit
d9ef46921b

+ 48 - 284
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2CubicInstance.java

@@ -18,42 +18,41 @@
  */
 package com.l2jserver.gameserver.model.actor.instance;
 
+import java.util.ArrayList;
 import java.util.List;
 import java.util.concurrent.Future;
 import java.util.logging.Level;
 import java.util.logging.Logger;
 
-import javolution.util.FastList;
-
 import com.l2jserver.Config;
 import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.ai.CtrlEvent;
 import com.l2jserver.gameserver.datatables.SkillTable;
-import com.l2jserver.gameserver.handler.ISkillHandler;
-import com.l2jserver.gameserver.handler.SkillHandler;
 import com.l2jserver.gameserver.instancemanager.DuelManager;
 import com.l2jserver.gameserver.model.L2Object;
 import com.l2jserver.gameserver.model.L2Party;
 import com.l2jserver.gameserver.model.actor.L2Attackable;
 import com.l2jserver.gameserver.model.actor.L2Character;
 import com.l2jserver.gameserver.model.actor.L2Playable;
+import com.l2jserver.gameserver.model.actor.tasks.cubics.CubicAction;
+import com.l2jserver.gameserver.model.actor.tasks.cubics.CubicDisappear;
+import com.l2jserver.gameserver.model.actor.tasks.cubics.CubicHeal;
 import com.l2jserver.gameserver.model.effects.L2Effect;
 import com.l2jserver.gameserver.model.effects.L2EffectType;
 import com.l2jserver.gameserver.model.entity.TvTEvent;
 import com.l2jserver.gameserver.model.entity.TvTEventTeam;
+import com.l2jserver.gameserver.model.interfaces.IIdentifiable;
 import com.l2jserver.gameserver.model.skills.L2Skill;
 import com.l2jserver.gameserver.model.skills.L2SkillType;
 import com.l2jserver.gameserver.model.stats.Formulas;
 import com.l2jserver.gameserver.model.stats.Stats;
 import com.l2jserver.gameserver.model.zone.ZoneId;
 import com.l2jserver.gameserver.network.SystemMessageId;
-import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
-import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
 import com.l2jserver.util.Rnd;
 
-public final class L2CubicInstance
+public final class L2CubicInstance implements IIdentifiable
 {
-	protected static final Logger _log = Logger.getLogger(L2CubicInstance.class.getName());
+	private static final Logger _log = Logger.getLogger(L2CubicInstance.class.getName());
 	
 	// Type of Cubics
 	public static final int STORM_CUBIC = 1;
@@ -79,38 +78,36 @@ public final class L2CubicInstance
 	public static final int SKILL_CUBIC_HEAL = 4051;
 	public static final int SKILL_CUBIC_CURE = 5579;
 	
-	protected L2PcInstance _owner;
-	protected L2Character _target;
+	private final L2PcInstance _owner;
+	private L2Character _target;
 	
-	protected int _id;
-	protected int _cubicPower;
-	protected int _cubicDuration;
-	protected int _cubicDelay;
-	protected int _cubicSkillChance;
-	protected int _cubicMaxCount;
-	protected int _currentcount;
-	protected boolean _active;
+	private final int _cubicId;
+	private final int _cubicPower;
+	private final int _cubicDuration;
+	private final int _cubicDelay;
+	private final int _cubicSkillChance;
+	private final int _cubicMaxCount;
+	private boolean _active;
 	private final boolean _givenByOther;
 	
-	protected List<L2Skill> _skills = new FastList<>();
+	private final List<L2Skill> _skills = new ArrayList<>();
 	
 	private Future<?> _disappearTask;
 	private Future<?> _actionTask;
 	
-	public L2CubicInstance(L2PcInstance owner, int id, int level, int cubicPower, int cubicDelay, int cubicSkillChance, int cubicMaxCount, int cubicDuration, boolean givenByOther)
+	public L2CubicInstance(L2PcInstance owner, int cubicId, int level, int cubicPower, int cubicDelay, int cubicSkillChance, int cubicMaxCount, int cubicDuration, boolean givenByOther)
 	{
 		_owner = owner;
-		_id = id;
+		_cubicId = cubicId;
 		_cubicPower = cubicPower;
 		_cubicDuration = cubicDuration * 1000;
 		_cubicDelay = cubicDelay * 1000;
 		_cubicSkillChance = cubicSkillChance;
 		_cubicMaxCount = cubicMaxCount;
-		_currentcount = 0;
 		_active = false;
 		_givenByOther = givenByOther;
 		
-		switch (_id)
+		switch (_cubicId)
 		{
 			case STORM_CUBIC:
 				_skills.add(SkillTable.getInstance().getInfo(4049, level));
@@ -164,7 +161,7 @@ public final class L2CubicInstance
 				_skills.add(SkillTable.getInstance().getInfo(5115, 4));
 				break;
 		}
-		_disappearTask = ThreadPoolManager.getInstance().scheduleGeneral(new Disappear(), _cubicDuration); // disappear
+		_disappearTask = ThreadPoolManager.getInstance().scheduleGeneral(new CubicDisappear(this), _cubicDuration); // disappear
 	}
 	
 	public synchronized void doAction()
@@ -175,7 +172,7 @@ public final class L2CubicInstance
 		}
 		_active = true;
 		
-		switch (_id)
+		switch (_cubicId)
 		{
 			case AQUA_CUBIC:
 			case BINDING_CUBIC:
@@ -190,17 +187,18 @@ public final class L2CubicInstance
 			case SMART_CUBIC_SPECTRALMASTER:
 			case SMART_CUBIC_EVATEMPLAR:
 			case SMART_CUBIC_SHILLIENTEMPLAR:
-				_actionTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new Action(_cubicSkillChance), 0, _cubicDelay);
+				_actionTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new CubicAction(this, _cubicSkillChance), 0, _cubicDelay);
 				break;
 			case LIFE_CUBIC:
-				_actionTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new Heal(), 0, _cubicDelay);
+				_actionTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new CubicHeal(this), 0, _cubicDelay);
 				break;
 		}
 	}
 	
+	@Override
 	public int getId()
 	{
-		return _id;
+		return _cubicId;
 	}
 	
 	public L2PcInstance getOwner()
@@ -213,6 +211,26 @@ public final class L2CubicInstance
 		return _cubicPower;
 	}
 	
+	public L2Character getTarget()
+	{
+		return _target;
+	}
+	
+	public void setTarget(L2Character target)
+	{
+		_target = target;
+	}
+	
+	public List<L2Skill> getSkills()
+	{
+		return _skills;
+	}
+	
+	public int getCubicMaxCount()
+	{
+		return _cubicMaxCount;
+	}
+	
 	public void stopAction()
 	{
 		_target = null;
@@ -447,184 +465,6 @@ public final class L2CubicInstance
 		}
 	}
 	
-	private class Action implements Runnable
-	{
-		private final int _chance;
-		
-		protected Action(int chance)
-		{
-			_chance = chance;
-		}
-		
-		@Override
-		public void run()
-		{
-			try
-			{
-				if (_owner.isDead() || !_owner.isOnline())
-				{
-					stopAction();
-					_owner.getCubics().remove(this);
-					_owner.broadcastUserInfo();
-					cancelDisappear();
-					return;
-				}
-				if (!AttackStanceTaskManager.getInstance().hasAttackStanceTask(_owner))
-				{
-					if (_owner.hasSummon())
-					{
-						if (!AttackStanceTaskManager.getInstance().hasAttackStanceTask(_owner.getSummon()))
-						{
-							stopAction();
-							return;
-						}
-					}
-					else
-					{
-						stopAction();
-						return;
-					}
-				}
-				// The cubic has already reached its limit and it will stay idle until its duration ends.
-				if ((_cubicMaxCount > -1) && (_currentcount >= _cubicMaxCount))
-				{
-					stopAction();
-					return;
-				}
-				// Smart Cubic debuff cancel is 100%
-				boolean UseCubicCure = false;
-				L2Skill skill = null;
-				
-				if ((_id >= SMART_CUBIC_EVATEMPLAR) && (_id <= SMART_CUBIC_SPECTRALMASTER))
-				{
-					for (L2Effect e : _owner.getAllEffects())
-					{
-						if ((e != null) && e.getSkill().isDebuff() && e.getSkill().canBeDispeled())
-						{
-							UseCubicCure = true;
-							e.exit();
-						}
-					}
-				}
-				
-				if (UseCubicCure)
-				{
-					// Smart Cubic debuff cancel is needed, no other skill is used in this
-					// activation period
-					MagicSkillUse msu = new MagicSkillUse(_owner, _owner, SKILL_CUBIC_CURE, 1, 0, 0);
-					_owner.broadcastPacket(msu);
-					
-					// The cubic has done an action, increase the currentcount
-					_currentcount++;
-				}
-				else if (Rnd.get(1, 100) < _chance)
-				{
-					skill = _skills.get(Rnd.get(_skills.size()));
-					if (skill != null)
-					{
-						if (skill.getId() == SKILL_CUBIC_HEAL)
-						{
-							// friendly skill, so we look a target in owner's party
-							cubicTargetForHeal();
-						}
-						else
-						{
-							// offensive skill, we look for an enemy target
-							getCubicTarget();
-							if (!isInCubicRange(_owner, _target))
-							{
-								_target = null;
-							}
-						}
-						L2Character target = _target; // copy to avoid npe
-						if ((target != null) && (!target.isDead()))
-						{
-							if (Config.DEBUG)
-							{
-								_log.info("L2CubicInstance: Action.run();");
-								_log.info("Cubic Id: " + _id + " Target: " + target.getName() + " distance: " + Math.sqrt(target.getDistanceSq(_owner.getX(), _owner.getY(), _owner.getZ())));
-							}
-							
-							_owner.broadcastPacket(new MagicSkillUse(_owner, target, skill.getId(), skill.getLevel(), 0, 0));
-							
-							L2SkillType type = skill.getSkillType();
-							ISkillHandler handler = SkillHandler.getInstance().getHandler(skill.getSkillType());
-							L2Character[] targets =
-							{
-								target
-							};
-							
-							if (type == L2SkillType.DEBUFF)
-							{
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run() handler " + type);
-								}
-								useCubicContinuous(L2CubicInstance.this, skill, targets);
-							}
-							else
-							{
-								handler.useSkill(_owner, skill, targets);
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run(); other handler");
-								}
-							}
-							
-							if (skill.hasEffectType(L2EffectType.MAGICAL_ATTACK))
-							{
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run() handler " + type);
-								}
-								useCubicMdam(L2CubicInstance.this, skill, targets);
-							}
-							else if (skill.hasEffectType(L2EffectType.HP_DRAIN))
-							{
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run() skill " + type);
-								}
-								useCubicDrain(L2CubicInstance.this, skill, targets);
-							}
-							else if (skill.hasEffectType(L2EffectType.STUN, L2EffectType.ROOT, L2EffectType.PARALYZE))
-							{
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run() handler " + type);
-								}
-								useCubicDisabler(type, L2CubicInstance.this, skill, targets);
-							}
-							else if (skill.hasEffectType(L2EffectType.DMG_OVER_TIME, L2EffectType.DMG_OVER_TIME_PERCENT))
-							{
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run() handler " + type);
-								}
-								useCubicContinuous(L2CubicInstance.this, skill, targets);
-							}
-							else if (skill.hasEffectType(L2EffectType.AGGRESSION))
-							{
-								if (Config.DEBUG)
-								{
-									_log.info("L2CubicInstance: Action.run() handler " + type);
-								}
-								useCubicDisabler(type, L2CubicInstance.this, skill, targets);
-							}
-							
-							// The cubic has done an action, increase the currentcount
-							_currentcount++;
-						}
-					}
-				}
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "", e);
-			}
-		}
-	}
-	
 	public void useCubicContinuous(L2CubicInstance activeCubic, L2Skill skill, L2Object[] targets)
 	{
 		for (L2Character target : (L2Character[]) targets)
@@ -855,7 +695,7 @@ public final class L2CubicInstance
 	 * @param target
 	 * @return true if the target is inside of the owner's max Cubic range
 	 */
-	public boolean isInCubicRange(L2Character owner, L2Character target)
+	public static boolean isInCubicRange(L2Character owner, L2Character target)
 	{
 		if ((owner == null) || (target == null))
 		{
@@ -899,7 +739,7 @@ public final class L2CubicInstance
 			{
 				if (!partyMember.isDead())
 				{
-					// if party member not dead, check if he is in castrange of heal cubic
+					// if party member not dead, check if he is in cast range of heal cubic
 					if (isInCubicRange(_owner, partyMember))
 					{
 						// member is in cubic casting range, check if he need heal and if he have
@@ -963,80 +803,4 @@ public final class L2CubicInstance
 	{
 		return _givenByOther;
 	}
-	
-	protected class Heal implements Runnable
-	{
-		@Override
-		public void run()
-		{
-			if (_owner.isDead() || !_owner.isOnline())
-			{
-				stopAction();
-				_owner.getCubics().remove(this);
-				_owner.broadcastUserInfo();
-				cancelDisappear();
-				return;
-			}
-			try
-			{
-				L2Skill skill = null;
-				for (L2Skill sk : _skills)
-				{
-					if (sk.getId() == SKILL_CUBIC_HEAL)
-					{
-						skill = sk;
-						break;
-					}
-				}
-				
-				if (skill != null)
-				{
-					cubicTargetForHeal();
-					L2Character target = _target;
-					if ((target != null) && !target.isDead())
-					{
-						if ((target.getMaxHp() - target.getCurrentHp()) > skill.getPower())
-						{
-							L2Character[] targets =
-							{
-								target
-							};
-							ISkillHandler handler = SkillHandler.getInstance().getHandler(skill.getSkillType());
-							if (handler != null)
-							{
-								handler.useSkill(_owner, skill, targets);
-							}
-							else
-							{
-								skill.useSkill(_owner, targets);
-							}
-							
-							MagicSkillUse msu = new MagicSkillUse(_owner, target, skill.getId(), skill.getLevel(), 0, 0);
-							_owner.broadcastPacket(msu);
-						}
-					}
-				}
-			}
-			catch (Exception e)
-			{
-				_log.log(Level.SEVERE, "", e);
-			}
-		}
-	}
-	
-	private class Disappear implements Runnable
-	{
-		Disappear()
-		{
-			// run task
-		}
-		
-		@Override
-		public void run()
-		{
-			stopAction();
-			_owner.getCubics().remove(this);
-			_owner.broadcastUserInfo();
-		}
-	}
 }

+ 25 - 27
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/instance/L2PcInstance.java

@@ -36,7 +36,7 @@ import java.util.List;
 import java.util.Map;
 import java.util.Map.Entry;
 import java.util.Set;
-import java.util.concurrent.CopyOnWriteArrayList;
+import java.util.concurrent.ConcurrentSkipListMap;
 import java.util.concurrent.Future;
 import java.util.concurrent.ScheduledFuture;
 import java.util.concurrent.TimeUnit;
@@ -765,7 +765,7 @@ public final class L2PcInstance extends L2Playable
 	
 	protected boolean _inventoryDisable = false;
 	
-	private final List<L2CubicInstance> _cubics = new CopyOnWriteArrayList<>();
+	private final Map<Integer, L2CubicInstance> _cubics = new ConcurrentSkipListMap<>();
 	
 	/** Active shots. */
 	protected FastSet<Integer> _activeSoulShots = new FastSet<Integer>().shared();
@@ -5590,7 +5590,7 @@ public final class L2PcInstance extends L2Playable
 		// Unsummon Cubics
 		if (!_cubics.isEmpty())
 		{
-			for (L2CubicInstance cubic : _cubics)
+			for (L2CubicInstance cubic : _cubics.values())
 			{
 				cubic.stopAction();
 				cubic.cancelDisappear();
@@ -9483,7 +9483,7 @@ public final class L2PcInstance extends L2Playable
 	{
 		if (!_cubics.isEmpty())
 		{
-			for (L2CubicInstance cubic : _cubics)
+			for (L2CubicInstance cubic : _cubics.values())
 			{
 				cubic.stopAction();
 				cubic.cancelDisappear();
@@ -9498,13 +9498,13 @@ public final class L2PcInstance extends L2Playable
 		if (!_cubics.isEmpty())
 		{
 			boolean broadcast = false;
-			for (L2CubicInstance cubic : _cubics)
+			for (L2CubicInstance cubic : _cubics.values())
 			{
 				if (cubic.givenByOther())
 				{
 					cubic.stopAction();
 					cubic.cancelDisappear();
-					_cubics.remove(cubic);
+					_cubics.remove(cubic.getId());
 					broadcast = true;
 				}
 			}
@@ -9554,14 +9554,9 @@ public final class L2PcInstance extends L2Playable
 		return _inventoryDisable;
 	}
 	
-	public List<L2CubicInstance> getCubics()
-	{
-		return _cubics;
-	}
-	
 	/**
 	 * Add a cubic to this player.
-	 * @param id
+	 * @param cubicId the cubic ID
 	 * @param level
 	 * @param cubicPower
 	 * @param cubicDelay
@@ -9569,27 +9564,30 @@ public final class L2PcInstance extends L2Playable
 	 * @param cubicMaxCount
 	 * @param cubicDuration
 	 * @param givenByOther
+	 * @return the old cubic for this cubic ID if any, otherwise {@code null}
 	 */
-	public void addCubic(int id, int level, double cubicPower, int cubicDelay, int cubicSkillChance, int cubicMaxCount, int cubicDuration, boolean givenByOther)
+	public L2CubicInstance addCubic(int cubicId, int level, double cubicPower, int cubicDelay, int cubicSkillChance, int cubicMaxCount, int cubicDuration, boolean givenByOther)
 	{
-		_cubics.add(new L2CubicInstance(this, id, level, (int) cubicPower, cubicDelay, cubicSkillChance, cubicMaxCount, cubicDuration, givenByOther));
+		return _cubics.put(cubicId, new L2CubicInstance(this, cubicId, level, (int) cubicPower, cubicDelay, cubicSkillChance, cubicMaxCount, cubicDuration, givenByOther));
 	}
 	
 	/**
-	 * Get the player cubic by NPC Id, if any.
-	 * @param id the NPC id
-	 * @return the cubic with the given NPC id, {@code null} otherwise
+	 * Get the player's cubics.
+	 * @return the cubics
 	 */
-	public L2CubicInstance getCubicById(int id)
+	public Map<Integer, L2CubicInstance> getCubics()
 	{
-		for (L2CubicInstance c : _cubics)
-		{
-			if (c.getId() == id)
-			{
-				return c;
-			}
-		}
-		return null;
+		return _cubics;
+	}
+	
+	/**
+	 * Get the player cubic by cubic ID, if any.
+	 * @param cubicId the cubic ID
+	 * @return the cubic with the given cubic ID, {@code null} otherwise
+	 */
+	public L2CubicInstance getCubicById(int cubicId)
+	{
+		return _cubics.get(cubicId);
 	}
 	
 	/**
@@ -9861,7 +9859,7 @@ public final class L2PcInstance extends L2Playable
 		
 		if (!_cubics.isEmpty())
 		{
-			for (L2CubicInstance cubic : _cubics)
+			for (L2CubicInstance cubic : _cubics.values())
 			{
 				cubic.stopAction();
 				cubic.cancelDisappear();

+ 226 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/tasks/cubics/CubicAction.java

@@ -0,0 +1,226 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.tasks.cubics;
+
+import java.util.concurrent.atomic.AtomicInteger;
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.l2jserver.Config;
+import com.l2jserver.gameserver.handler.ISkillHandler;
+import com.l2jserver.gameserver.handler.SkillHandler;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2CubicInstance;
+import com.l2jserver.gameserver.model.effects.L2Effect;
+import com.l2jserver.gameserver.model.effects.L2EffectType;
+import com.l2jserver.gameserver.model.skills.L2Skill;
+import com.l2jserver.gameserver.model.skills.L2SkillType;
+import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
+import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
+import com.l2jserver.util.Rnd;
+
+/**
+ * Cubic action task.
+ * @author Zoey76
+ */
+public final class CubicAction implements Runnable
+{
+	private static final Logger _log = Logger.getLogger(CubicAction.class.getName());
+	private final L2CubicInstance _cubic;
+	private final AtomicInteger _currentCount = new AtomicInteger();
+	private final int _chance;
+	
+	public CubicAction(L2CubicInstance cubic, int chance)
+	{
+		_cubic = cubic;
+		_chance = chance;
+	}
+	
+	@Override
+	public void run()
+	{
+		if (_cubic == null)
+		{
+			return;
+		}
+		
+		try
+		{
+			if (_cubic.getOwner().isDead() || !_cubic.getOwner().isOnline())
+			{
+				_cubic.stopAction();
+				_cubic.getOwner().getCubics().remove(_cubic.getId());
+				_cubic.getOwner().broadcastUserInfo();
+				_cubic.cancelDisappear();
+				return;
+			}
+			if (!AttackStanceTaskManager.getInstance().hasAttackStanceTask(_cubic.getOwner()))
+			{
+				if (_cubic.getOwner().hasSummon())
+				{
+					if (!AttackStanceTaskManager.getInstance().hasAttackStanceTask(_cubic.getOwner().getSummon()))
+					{
+						_cubic.stopAction();
+						return;
+					}
+				}
+				else
+				{
+					_cubic.stopAction();
+					return;
+				}
+			}
+			// The cubic has already reached its limit and it will stay idle until its duration ends.
+			if ((_cubic.getCubicMaxCount() > -1) && (_currentCount.get() >= _cubic.getCubicMaxCount()))
+			{
+				_cubic.stopAction();
+				return;
+			}
+			// Smart Cubic debuff cancel is 100%
+			boolean UseCubicCure = false;
+			L2Skill skill = null;
+			
+			if ((_cubic.getId() >= L2CubicInstance.SMART_CUBIC_EVATEMPLAR) && (_cubic.getId() <= L2CubicInstance.SMART_CUBIC_SPECTRALMASTER))
+			{
+				for (L2Effect e : _cubic.getOwner().getAllEffects())
+				{
+					if ((e != null) && e.getSkill().isDebuff() && e.getSkill().canBeDispeled())
+					{
+						UseCubicCure = true;
+						e.exit();
+					}
+				}
+			}
+			
+			if (UseCubicCure)
+			{
+				// Smart Cubic debuff cancel is needed, no other skill is used in this activation period
+				MagicSkillUse msu = new MagicSkillUse(_cubic.getOwner(), _cubic.getOwner(), L2CubicInstance.SKILL_CUBIC_CURE, 1, 0, 0);
+				_cubic.getOwner().broadcastPacket(msu);
+				
+				// The cubic has done an action, increase the current count
+				_currentCount.incrementAndGet();
+			}
+			else if (Rnd.get(1, 100) < _chance)
+			{
+				skill = _cubic.getSkills().get(Rnd.get(_cubic.getSkills().size()));
+				if (skill != null)
+				{
+					if (skill.getId() == L2CubicInstance.SKILL_CUBIC_HEAL)
+					{
+						// friendly skill, so we look a target in owner's party
+						_cubic.cubicTargetForHeal();
+					}
+					else
+					{
+						// offensive skill, we look for an enemy target
+						_cubic.getCubicTarget();
+						if (!L2CubicInstance.isInCubicRange(_cubic.getOwner(), _cubic.getTarget()))
+						{
+							_cubic.setTarget(null);
+						}
+					}
+					L2Character target = _cubic.getTarget();
+					if ((target != null) && !target.isDead())
+					{
+						if (Config.DEBUG)
+						{
+							_log.info("L2CubicInstance: Action.run();");
+							_log.info("Cubic ID: " + _cubic.getId() + " Target: " + target.getName() + " distance: " + Math.sqrt(target.getDistanceSq(_cubic.getOwner().getX(), _cubic.getOwner().getY(), _cubic.getOwner().getZ())));
+						}
+						
+						_cubic.getOwner().broadcastPacket(new MagicSkillUse(_cubic.getOwner(), target, skill.getId(), skill.getLevel(), 0, 0));
+						
+						L2SkillType type = skill.getSkillType();
+						ISkillHandler handler = SkillHandler.getInstance().getHandler(skill.getSkillType());
+						L2Character[] targets =
+						{
+							target
+						};
+						
+						if (type == L2SkillType.DEBUFF)
+						{
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run() handler " + type);
+							}
+							_cubic.useCubicContinuous(_cubic, skill, targets);
+						}
+						else
+						{
+							handler.useSkill(_cubic.getOwner(), skill, targets);
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run(); other handler");
+							}
+						}
+						
+						if (skill.hasEffectType(L2EffectType.MAGICAL_ATTACK))
+						{
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run() handler " + type);
+							}
+							_cubic.useCubicMdam(_cubic, skill, targets);
+						}
+						else if (skill.hasEffectType(L2EffectType.HP_DRAIN))
+						{
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run() skill " + type);
+							}
+							_cubic.useCubicDrain(_cubic, skill, targets);
+						}
+						else if (skill.hasEffectType(L2EffectType.STUN, L2EffectType.ROOT, L2EffectType.PARALYZE))
+						{
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run() handler " + type);
+							}
+							_cubic.useCubicDisabler(type, _cubic, skill, targets);
+						}
+						else if (skill.hasEffectType(L2EffectType.DMG_OVER_TIME, L2EffectType.DMG_OVER_TIME_PERCENT))
+						{
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run() handler " + type);
+							}
+							_cubic.useCubicContinuous(_cubic, skill, targets);
+						}
+						else if (skill.hasEffectType(L2EffectType.AGGRESSION))
+						{
+							if (Config.DEBUG)
+							{
+								_log.info("L2CubicInstance: Action.run() handler " + type);
+							}
+							_cubic.useCubicDisabler(type, _cubic, skill, targets);
+						}
+						
+						// The cubic has done an action, increase the current count
+						_currentCount.incrementAndGet();
+					}
+				}
+			}
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "", e);
+		}
+	}
+}

+ 46 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/tasks/cubics/CubicDisappear.java

@@ -0,0 +1,46 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.tasks.cubics;
+
+import com.l2jserver.gameserver.model.actor.instance.L2CubicInstance;
+
+/**
+ * Cubic disappear task.
+ * @author Zoey76
+ */
+public final class CubicDisappear implements Runnable
+{
+	private final L2CubicInstance _cubic;
+	
+	public CubicDisappear(L2CubicInstance cubic)
+	{
+		_cubic = cubic;
+	}
+	
+	@Override
+	public void run()
+	{
+		if (_cubic != null)
+		{
+			_cubic.stopAction();
+			_cubic.getOwner().getCubics().remove(_cubic.getId());
+			_cubic.getOwner().broadcastUserInfo();
+		}
+	}
+}

+ 105 - 0
L2J_Server_BETA/java/com/l2jserver/gameserver/model/actor/tasks/cubics/CubicHeal.java

@@ -0,0 +1,105 @@
+/*
+ * Copyright (C) 2004-2013 L2J Server
+ * 
+ * This file is part of L2J Server.
+ * 
+ * L2J Server 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.
+ * 
+ * L2J Server 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 <http://www.gnu.org/licenses/>.
+ */
+package com.l2jserver.gameserver.model.actor.tasks.cubics;
+
+import java.util.logging.Level;
+import java.util.logging.Logger;
+
+import com.l2jserver.gameserver.handler.ISkillHandler;
+import com.l2jserver.gameserver.handler.SkillHandler;
+import com.l2jserver.gameserver.model.actor.L2Character;
+import com.l2jserver.gameserver.model.actor.instance.L2CubicInstance;
+import com.l2jserver.gameserver.model.skills.L2Skill;
+import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
+
+/**
+ * Cubic heal task.
+ * @author Zoey76
+ */
+public class CubicHeal implements Runnable
+{
+	private static final Logger _log = Logger.getLogger(CubicHeal.class.getName());
+	private final L2CubicInstance _cubic;
+	
+	public CubicHeal(L2CubicInstance cubic)
+	{
+		_cubic = cubic;
+	}
+	
+	@Override
+	public void run()
+	{
+		if (_cubic == null)
+		{
+			return;
+		}
+		
+		if (_cubic.getOwner().isDead() || !_cubic.getOwner().isOnline())
+		{
+			_cubic.stopAction();
+			_cubic.getOwner().getCubics().remove(_cubic.getId());
+			_cubic.getOwner().broadcastUserInfo();
+			_cubic.cancelDisappear();
+			return;
+		}
+		try
+		{
+			L2Skill skill = null;
+			for (L2Skill sk : _cubic.getSkills())
+			{
+				if (sk.getId() == L2CubicInstance.SKILL_CUBIC_HEAL)
+				{
+					skill = sk;
+					break;
+				}
+			}
+			
+			if (skill != null)
+			{
+				_cubic.cubicTargetForHeal();
+				final L2Character target = _cubic.getTarget();
+				if ((target != null) && !target.isDead())
+				{
+					if ((target.getMaxHp() - target.getCurrentHp()) > skill.getPower())
+					{
+						L2Character[] targets =
+						{
+							target
+						};
+						ISkillHandler handler = SkillHandler.getInstance().getHandler(skill.getSkillType());
+						if (handler != null)
+						{
+							handler.useSkill(_cubic.getOwner(), skill, targets);
+						}
+						else
+						{
+							skill.useSkill(_cubic.getOwner(), targets);
+						}
+						
+						_cubic.getOwner().broadcastPacket(new MagicSkillUse(_cubic.getOwner(), target, skill.getId(), skill.getLevel(), 0, 0));
+					}
+				}
+			}
+		}
+		catch (Exception e)
+		{
+			_log.log(Level.SEVERE, "", e);
+		}
+	}
+}

+ 2 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/CharInfo.java

@@ -23,7 +23,6 @@ import com.l2jserver.gameserver.datatables.NpcTable;
 import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
 import com.l2jserver.gameserver.model.PcCondOverride;
 import com.l2jserver.gameserver.model.actor.L2Decoy;
-import com.l2jserver.gameserver.model.actor.instance.L2CubicInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
 import com.l2jserver.gameserver.model.effects.AbnormalEffect;
@@ -278,9 +277,9 @@ public class CharInfo extends L2GameServerPacket
 			writeC(_activeChar.getPrivateStoreType()); // 1 - sellshop
 			
 			writeH(_activeChar.getCubics().size());
-			for (L2CubicInstance c : _activeChar.getCubics())
+			for (int cubicId : _activeChar.getCubics().keySet())
 			{
-				writeH(c.getId());
+				writeH(cubicId);
 			}
 			
 			writeC(_activeChar.isInPartyMatchRoom() ? 1 : 0);

+ 2 - 3
L2J_Server_BETA/java/com/l2jserver/gameserver/network/serverpackets/UserInfo.java

@@ -25,7 +25,6 @@ import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
 import com.l2jserver.gameserver.instancemanager.TerritoryWarManager;
 import com.l2jserver.gameserver.model.Elementals;
 import com.l2jserver.gameserver.model.PcCondOverride;
-import com.l2jserver.gameserver.model.actor.instance.L2CubicInstance;
 import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
 import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
 import com.l2jserver.gameserver.model.effects.AbnormalEffect;
@@ -202,9 +201,9 @@ public final class UserInfo extends L2GameServerPacket
 		writeD(_activeChar.getPvpKills());
 		
 		writeH(_activeChar.getCubics().size());
-		for (L2CubicInstance c : _activeChar.getCubics())
+		for (int cubicId : _activeChar.getCubics().keySet())
 		{
-			writeH(c.getId());
+			writeH(cubicId);
 		}
 		
 		writeC(_activeChar.isInPartyMatchRoom() ? 1 : 0);

+ 1 - 1
L2J_Server_BETA/java/com/l2jserver/gameserver/taskmanager/AttackStanceTaskManager.java

@@ -58,7 +58,7 @@ public class AttackStanceTaskManager
 		if ((actor != null) && actor.isPlayable())
 		{
 			final L2PcInstance player = actor.getActingPlayer();
-			for (L2CubicInstance cubic : player.getCubics())
+			for (L2CubicInstance cubic : player.getCubics().values())
 			{
 				if (cubic.getId() != L2CubicInstance.LIFE_CUBIC)
 				{