428 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
			
		
		
	
	
			428 lines
		
	
	
		
			11 KiB
		
	
	
	
		
			Java
		
	
	
	
	
	
| /*
 | |
|  * Copyright (C) 2004-2015 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.skills;
 | |
| 
 | |
| import java.util.ArrayList;
 | |
| import java.util.List;
 | |
| import java.util.Map;
 | |
| import java.util.concurrent.ConcurrentHashMap;
 | |
| import java.util.concurrent.ScheduledFuture;
 | |
| 
 | |
| import com.l2jserver.Config;
 | |
| import com.l2jserver.gameserver.GameTimeController;
 | |
| import com.l2jserver.gameserver.ThreadPoolManager;
 | |
| import com.l2jserver.gameserver.model.CharEffectList;
 | |
| import com.l2jserver.gameserver.model.actor.L2Character;
 | |
| import com.l2jserver.gameserver.model.actor.L2Summon;
 | |
| import com.l2jserver.gameserver.model.effects.AbstractEffect;
 | |
| import com.l2jserver.gameserver.model.effects.EffectTaskInfo;
 | |
| import com.l2jserver.gameserver.model.effects.EffectTickTask;
 | |
| import com.l2jserver.gameserver.model.stats.Formulas;
 | |
| import com.l2jserver.gameserver.network.SystemMessageId;
 | |
| import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
 | |
| 
 | |
| /**
 | |
|  * Buff Info.<br>
 | |
|  * Complex DTO that holds all the information for a given buff (or debuff or dance/song) set of effects issued by an skill.
 | |
|  * @author Zoey76
 | |
|  */
 | |
| public final class BuffInfo
 | |
| {
 | |
| 	// Data
 | |
| 	/** Data. */
 | |
| 	private final L2Character _effector;
 | |
| 	private final L2Character _effected;
 | |
| 	private final Skill _skill;
 | |
| 	/** The effects. */
 | |
| 	private final List<AbstractEffect> _effects = new ArrayList<>(1);
 | |
| 	// Tasks
 | |
| 	/** Effect tasks for ticks. */
 | |
| 	private volatile Map<AbstractEffect, EffectTaskInfo> _tasks;
 | |
| 	/** Scheduled future. */
 | |
| 	private ScheduledFuture<?> _scheduledFutureTimeTask;
 | |
| 	// Time and ticks
 | |
| 	/** Abnormal time. */
 | |
| 	private int _abnormalTime;
 | |
| 	/** The game ticks at the start of this effect. */
 | |
| 	private final int _periodStartTicks;
 | |
| 	// Misc
 | |
| 	/** If {@code true} then this effect has been cancelled. */
 | |
| 	private boolean _isRemoved = false;
 | |
| 	/** If {@code true} then this effect is in use (or has been stop because an Herb took place). */
 | |
| 	private boolean _isInUse = true;
 | |
| 	
 | |
| 	/**
 | |
| 	 * Buff Info constructor.
 | |
| 	 * @param effector
 | |
| 	 * @param effected
 | |
| 	 * @param skill
 | |
| 	 */
 | |
| 	public BuffInfo(L2Character effector, L2Character effected, Skill skill)
 | |
| 	{
 | |
| 		_effector = effector;
 | |
| 		_effected = effected;
 | |
| 		_skill = skill;
 | |
| 		_abnormalTime = Formulas.calcEffectAbnormalTime(effector, effected, skill);
 | |
| 		_periodStartTicks = GameTimeController.getInstance().getGameTicks();
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the effects on this buff info.
 | |
| 	 * @return the effects
 | |
| 	 */
 | |
| 	public List<AbstractEffect> getEffects()
 | |
| 	{
 | |
| 		return _effects;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Adds an effect to this buff info.
 | |
| 	 * @param effect the effect to add
 | |
| 	 */
 | |
| 	public void addEffect(AbstractEffect effect)
 | |
| 	{
 | |
| 		_effects.add(effect);
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Adds an effect task to this buff info.<br>
 | |
| 	 * Uses double-checked locking to initialize the map if it's necessary.
 | |
| 	 * @param effect the effect that owns the task
 | |
| 	 * @param effectTaskInfo the task info
 | |
| 	 */
 | |
| 	private void addTask(AbstractEffect effect, EffectTaskInfo effectTaskInfo)
 | |
| 	{
 | |
| 		if (_tasks == null)
 | |
| 		{
 | |
| 			synchronized (this)
 | |
| 			{
 | |
| 				if (_tasks == null)
 | |
| 				{
 | |
| 					_tasks = new ConcurrentHashMap<>();
 | |
| 				}
 | |
| 			}
 | |
| 		}
 | |
| 		_tasks.put(effect, effectTaskInfo);
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the task for the given effect.
 | |
| 	 * @param effect the effect
 | |
| 	 * @return the task
 | |
| 	 */
 | |
| 	private EffectTaskInfo getEffectTask(AbstractEffect effect)
 | |
| 	{
 | |
| 		return (_tasks == null) ? null : _tasks.get(effect);
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the skill that created this buff info.
 | |
| 	 * @return the skill
 | |
| 	 */
 | |
| 	public Skill getSkill()
 | |
| 	{
 | |
| 		return _skill;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the calculated abnormal time.
 | |
| 	 * @return the abnormal time
 | |
| 	 */
 | |
| 	public int getAbnormalTime()
 | |
| 	{
 | |
| 		return _abnormalTime;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Sets the abnormal time.
 | |
| 	 * @param abnormalTime the abnormal time to set
 | |
| 	 */
 | |
| 	public void setAbnormalTime(int abnormalTime)
 | |
| 	{
 | |
| 		_abnormalTime = abnormalTime;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the period start ticks.
 | |
| 	 * @return the period start
 | |
| 	 */
 | |
| 	public int getPeriodStartTicks()
 | |
| 	{
 | |
| 		return _periodStartTicks;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Get the remaining time in seconds for this buff info.
 | |
| 	 * @return the elapsed time
 | |
| 	 */
 | |
| 	public int getTime()
 | |
| 	{
 | |
| 		return _abnormalTime - ((GameTimeController.getInstance().getGameTicks() - _periodStartTicks) / GameTimeController.TICKS_PER_SECOND);
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Verify if this buff info has been cancelled.
 | |
| 	 * @return {@code true} if this buff info has been cancelled, {@code false} otherwise
 | |
| 	 */
 | |
| 	public boolean isRemoved()
 | |
| 	{
 | |
| 		return _isRemoved;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Set the buff info to removed.
 | |
| 	 * @param val the value to set
 | |
| 	 */
 | |
| 	public void setRemoved(boolean val)
 | |
| 	{
 | |
| 		_isRemoved = val;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Verify if this buff info is in use.
 | |
| 	 * @return {@code true} if this buff info is in use, {@code false} otherwise
 | |
| 	 */
 | |
| 	public boolean isInUse()
 | |
| 	{
 | |
| 		return _isInUse;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Set the buff info to in use.
 | |
| 	 * @param val the value to set
 | |
| 	 */
 | |
| 	public void setInUse(boolean val)
 | |
| 	{
 | |
| 		_isInUse = val;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the character that launched the buff.
 | |
| 	 * @return the effector
 | |
| 	 */
 | |
| 	public L2Character getEffector()
 | |
| 	{
 | |
| 		return _effector;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the target of the skill.
 | |
| 	 * @return the effected
 | |
| 	 */
 | |
| 	public L2Character getEffected()
 | |
| 	{
 | |
| 		return _effected;
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Stops all the effects for this buff info.<br>
 | |
| 	 * Removes effects stats.<br>
 | |
| 	 * <b>It will not remove the buff info from the effect list</b>.<br>
 | |
| 	 * Instead call {@link CharEffectList#stopSkillEffects(boolean, Skill)}
 | |
| 	 * @param removed if {@code true} the skill will be handled as removed
 | |
| 	 */
 | |
| 	public void stopAllEffects(boolean removed)
 | |
| 	{
 | |
| 		setRemoved(removed);
 | |
| 		// Cancels the task that will end this buff info
 | |
| 		if ((_scheduledFutureTimeTask != null) && !_scheduledFutureTimeTask.isCancelled())
 | |
| 		{
 | |
| 			_scheduledFutureTimeTask.cancel(true);
 | |
| 		}
 | |
| 		finishEffects();
 | |
| 	}
 | |
| 	
 | |
| 	public void initializeEffects()
 | |
| 	{
 | |
| 		if ((_effected == null) || (_skill == null))
 | |
| 		{
 | |
| 			return;
 | |
| 		}
 | |
| 		
 | |
| 		// When effects are initialized, the successfully landed.
 | |
| 		if (_effected.isPlayer() && !_skill.isPassive())
 | |
| 		{
 | |
| 			final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_S_EFFECT_CAN_BE_FELT);
 | |
| 			sm.addSkillName(_skill);
 | |
| 			_effected.sendPacket(sm);
 | |
| 		}
 | |
| 		
 | |
| 		// Creates a task that will stop all the effects.
 | |
| 		if (_abnormalTime > 0)
 | |
| 		{
 | |
| 			_scheduledFutureTimeTask = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new BuffTimeTask(this), 0, 1000L);
 | |
| 		}
 | |
| 		
 | |
| 		// Reset abnormal visual effects.
 | |
| 		resetAbnormalVisualEffects();
 | |
| 		
 | |
| 		for (AbstractEffect effect : _effects)
 | |
| 		{
 | |
| 			if (effect.isInstant() || (_effected.isDead() && !_skill.isPassive()))
 | |
| 			{
 | |
| 				continue;
 | |
| 			}
 | |
| 			
 | |
| 			// Call on start.
 | |
| 			effect.onStart(this);
 | |
| 			
 | |
| 			// If it's a continuous effect, if has ticks schedule a task with period, otherwise schedule a simple task to end it.
 | |
| 			if (effect.getTicks() > 0)
 | |
| 			{
 | |
| 				// The task for the effect ticks.
 | |
| 				final EffectTickTask effectTask = new EffectTickTask(this, effect);
 | |
| 				final ScheduledFuture<?> scheduledFuture = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(effectTask, effect.getTicks() * Config.EFFECT_TICK_RATIO, effect.getTicks() * Config.EFFECT_TICK_RATIO);
 | |
| 				// Adds the task for ticking.
 | |
| 				addTask(effect, new EffectTaskInfo(effectTask, scheduledFuture));
 | |
| 			}
 | |
| 			
 | |
| 			// Add stats.
 | |
| 			_effected.addStatFuncs(effect.getStatFuncs(_effector, _effected, _skill));
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Called on each tick.<br>
 | |
| 	 * Verify if the effect should end and the effect task should be cancelled.
 | |
| 	 * @param effect the effect that is ticking
 | |
| 	 * @param tickCount the tick count
 | |
| 	 */
 | |
| 	public void onTick(AbstractEffect effect, int tickCount)
 | |
| 	{
 | |
| 		boolean continueForever = false;
 | |
| 		// If the effect is in use, allow it to affect the effected.
 | |
| 		if (_isInUse)
 | |
| 		{
 | |
| 			// Callback for on action time event.
 | |
| 			continueForever = effect.onActionTime(this);
 | |
| 		}
 | |
| 		
 | |
| 		if (!continueForever && _skill.isToggle())
 | |
| 		{
 | |
| 			final EffectTaskInfo task = getEffectTask(effect);
 | |
| 			if (task != null)
 | |
| 			{
 | |
| 				task.getScheduledFuture().cancel(true); // Don't allow to finish current run.
 | |
| 				_effected.getEffectList().stopSkillEffects(true, getSkill()); // Remove the buff from the effect list.
 | |
| 			}
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	public void finishEffects()
 | |
| 	{
 | |
| 		// Cancels the ticking task.
 | |
| 		if (_tasks != null)
 | |
| 		{
 | |
| 			for (EffectTaskInfo effectTask : _tasks.values())
 | |
| 			{
 | |
| 				effectTask.getScheduledFuture().cancel(true); // Don't allow to finish current run.
 | |
| 			}
 | |
| 		}
 | |
| 		// Remove stats
 | |
| 		removeStats();
 | |
| 		// Notify on exit.
 | |
| 		for (AbstractEffect effect : _effects)
 | |
| 		{
 | |
| 			// Instant effects shouldn't call onExit(..).
 | |
| 			if ((effect != null) && !effect.isInstant())
 | |
| 			{
 | |
| 				effect.onExit(this);
 | |
| 			}
 | |
| 		}
 | |
| 		// Remove abnormal visual effects.
 | |
| 		resetAbnormalVisualEffects();
 | |
| 		// Set the proper system message.
 | |
| 		if (!(_effected.isSummon() && !((L2Summon) _effected).getOwner().hasSummon()))
 | |
| 		{
 | |
| 			SystemMessageId smId = null;
 | |
| 			if (_skill.isToggle())
 | |
| 			{
 | |
| 				smId = SystemMessageId.S1_HAS_BEEN_ABORTED;
 | |
| 			}
 | |
| 			else if (isRemoved())
 | |
| 			{
 | |
| 				smId = SystemMessageId.THE_EFFECT_OF_S1_HAS_BEEN_REMOVED;
 | |
| 			}
 | |
| 			else if (!_skill.isPassive())
 | |
| 			{
 | |
| 				smId = SystemMessageId.S1_HAS_WORN_OFF;
 | |
| 			}
 | |
| 			
 | |
| 			if (smId != null)
 | |
| 			{
 | |
| 				final SystemMessage sm = SystemMessage.getSystemMessage(smId);
 | |
| 				sm.addSkillName(_skill);
 | |
| 				_effected.sendPacket(sm);
 | |
| 			}
 | |
| 		}
 | |
| 		// Remove short buff.
 | |
| 		if (this == _effected.getEffectList().getShortBuff())
 | |
| 		{
 | |
| 			_effected.getEffectList().shortBuffStatusUpdate(null);
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Applies all the abnormal visual effects to the effected.<br>
 | |
| 	 * Prevents multiple updates.
 | |
| 	 */
 | |
| 	private void resetAbnormalVisualEffects()
 | |
| 	{
 | |
| 		if (_skill.hasAbnormalVisualEffects())
 | |
| 		{
 | |
| 			_effected.resetCurrentAbnormalVisualEffects();
 | |
| 		}
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Adds the buff stats.
 | |
| 	 */
 | |
| 	public void addStats()
 | |
| 	{
 | |
| 		_effects.forEach(effect -> _effected.addStatFuncs(effect.getStatFuncs(_effector, _effected, _skill)));
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Removes the buff stats.
 | |
| 	 */
 | |
| 	public void removeStats()
 | |
| 	{
 | |
| 		_effects.forEach(_effected::removeStatsOwner);
 | |
| 		_effected.removeStatsOwner(_skill);
 | |
| 	}
 | |
| 	
 | |
| 	/**
 | |
| 	 * Gets the effect tick count.
 | |
| 	 * @param effect the effect
 | |
| 	 * @return the current tick count
 | |
| 	 */
 | |
| 	public int getTickCount(AbstractEffect effect)
 | |
| 	{
 | |
| 		if (_tasks != null)
 | |
| 		{
 | |
| 			final EffectTaskInfo effectTaskInfo = _tasks.get(effect);
 | |
| 			if (effectTaskInfo != null)
 | |
| 			{
 | |
| 				return effectTaskInfo.getEffectTask().getTickCount();
 | |
| 			}
 | |
| 		}
 | |
| 		return 0;
 | |
| 	}
 | |
| }
 | 
