/*
* 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 <http://www.gnu.org/licenses/>.
*/
package silentium.gameserver.model;
import javolution.util.FastList;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import silentium.gameserver.configs.MainConfig;
import silentium.gameserver.data.xml.SkillTreeData;
import silentium.gameserver.geo.GeoData;
import silentium.gameserver.model.actor.*;
import silentium.gameserver.model.actor.instance.*;
import silentium.gameserver.model.entity.TvTEvent;
import silentium.gameserver.network.SystemMessageId;
import silentium.gameserver.network.serverpackets.SystemMessage;
import silentium.gameserver.skills.Env;
import silentium.gameserver.skills.Formulas;
import silentium.gameserver.skills.Stats;
import silentium.gameserver.skills.basefuncs.Func;
import silentium.gameserver.skills.basefuncs.FuncTemplate;
import silentium.gameserver.skills.conditions.Condition;
import silentium.gameserver.skills.effects.EffectTemplate;
import silentium.gameserver.tables.ItemTable;
import silentium.gameserver.tables.SkillTable;
import silentium.gameserver.taskmanager.DecayTaskManager;
import silentium.gameserver.templates.StatsSet;
import silentium.gameserver.templates.item.L2Armor;
import silentium.gameserver.templates.item.L2ArmorType;
import silentium.gameserver.templates.skills.L2SkillType;
import silentium.gameserver.utils.Util;
import java.util.ArrayList;
import java.util.Collection;
import java.util.List;
import java.util.StringTokenizer;
public abstract class L2Skill implements IChanceSkillTrigger
{
protected static final Logger _log = LoggerFactory.getLogger(L2Skill.class.getName());
private static final L2Object[] _emptyTargetList = new L2Object[0];
public static final int SKILL_LUCKY = 194;
public static final int SKILL_CREATE_COMMON = 1320;
public static final int SKILL_CREATE_DWARVEN = 172;
public static final int SKILL_CRYSTALLIZE = 248;
public static final int SKILL_DIVINE_INSPIRATION = 1405;
public static final int SKILL_NPC_RACE = 4416;
public static final boolean geoEnabled = MainConfig.GEODATA > 0;
public static enum SkillOpType
{
OP_PASSIVE, OP_ACTIVE, OP_TOGGLE
}
/** Target types of skills : SELF, PARTY, CLAN, PET... */
public static enum SkillTargetType
{
TARGET_NONE, TARGET_SELF, TARGET_ONE, TARGET_PARTY, TARGET_ALLY, TARGET_CLAN, TARGET_PET, TARGET_AREA, TARGET_FRONT_AREA, TARGET_BEHIND_AREA, TARGET_AURA, TARGET_FRONT_AURA, TARGET_BEHIND_AURA, TARGET_CORPSE, TARGET_UNDEAD, TARGET_AREA_UNDEAD, TARGET_CORPSE_ALLY, TARGET_CORPSE_CLAN, TARGET_CORPSE_PLAYER, TARGET_CORPSE_PET, TARGET_ITEM, TARGET_AREA_CORPSE_MOB, TARGET_CORPSE_MOB, TARGET_UNLOCKABLE, TARGET_HOLY, TARGET_PARTY_MEMBER, TARGET_PARTY_OTHER, TARGET_SUMMON, TARGET_AREA_SUMMON, TARGET_ENEMY_SUMMON, TARGET_OWNER_PET, TARGET_GROUND
}
// conditional values
public static final int COND_RUNNING = 0x0001;
public static final int COND_WALKING = 0x0002;
public static final int COND_SIT = 0x0004;
public static final int COND_BEHIND = 0x0008;
public static final int COND_CRIT = 0x0010;
public static final int COND_LOWHP = 0x0020;
public static final int COND_ROBES = 0x0040;
public static final int COND_CHARGES = 0x0080;
public static final int COND_SHIELD = 0x0100;
private static final Func[] _emptyFunctionSet = new Func[0];
private static final L2Effect[] _emptyEffectSet = new L2Effect[0];
private final int _id;
private final int _level;
private int _displayId;
private final String _name;
private final SkillOpType _operateType;
private final boolean _magic;
private final int _mpConsume;
private final int _mpInitialConsume;
private final int _hpConsume;
private final int _targetConsume;
private final int _targetConsumeId;
private final int _itemConsume; // items consumption
private final int _itemConsumeId;
private final int _castRange;
private final int _effectRange;
private final int _abnormalLvl; // Abnormal levels for skills and their canceling
private final int _effectAbnormalLvl;
private final int _hitTime; // all times in milliseconds
private final int _coolTime;
private final int _reuseDelay;
private final int _equipDelay;
private final int _buffDuration;
/** Target type of the skill : SELF, PARTY, CLAN, PET... */
private final SkillTargetType _targetType;
private final double _power;
private final int _magicLevel;
private final int _negateLvl; // abnormalLvl is negated with negateLvl
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 int _levelDepend;
private final int _skillRadius; // Effecting area of the skill, in radius.
private final L2SkillType _skillType;
private final L2SkillType _effectType;
private final int _effectId;
private final int _effectPower;
private final int _effectLvl;
private final boolean _ispotion;
private final byte _element;
private final boolean _ignoreResists;
private final boolean _staticReuse;
private final boolean _staticHitTime;
private final int _reuseHashCode;
private final Stats _stat;
private final int _condition;
private final int _conditionValue;
private final boolean _overhit;
private final boolean _killByDOT;
private final boolean _isSuicideAttack;
private final boolean _isDemonicSkill;
private final boolean _isFlyingSkill;
private final boolean _isStriderSkill;
private final boolean _isSiegeSummonSkill;
private final int _weaponsAllowed;
private final boolean _nextActionIsAttack;
private final int _minPledgeClass;
private final boolean _isOffensive;
private final int _maxCharges;
private final int _numCharges;
private final int _triggeredId;
private final int _triggeredLevel;
protected ChanceCondition _chanceCondition = null;
private final String _chanceType;
private final String _flyType;
private final int _flyRadius;
private final float _flyCourse;
private final int _feed;
private final boolean _isHeroSkill; // If true the skill is a Hero Skill
private final int _baseCritRate; // percent of success for skill critical hit (especially for PDAM & BLOW - they're not
// affected by rCrit values or buffs). Default loads -1 for all other skills but 0 to PDAM
// & BLOW
private final int _lethalEffect1; // percent of success for lethal 1st effect (hit cp to 1 or if mob hp to 50%) (only for PDAM
// skills)
private final int _lethalEffect2; // percent of success for lethal 2nd effect (hit cp,hp to 1 or if mob hp to 1) (only for
// PDAM skills)
private final boolean _directHpDmg; // If true then dmg is being make directly
private final boolean _isDance; // If true then casting more dances will cost more MP
private final int _nextDanceCost;
private final float _sSBoost; // If true skill will have SoulShot boost (power*2)
private final int _aggroPoints;
protected List<Condition> _preCondition;
protected List<Condition> _itemPreCondition;
protected FuncTemplate[] _funcTemplates;
protected EffectTemplate[] _effectTemplates;
protected EffectTemplate[] _effectTemplatesSelf;
private final String _attribute;
private final boolean _isDebuff;
private final boolean _stayAfterDeath; // skill should stay after death
private final boolean _removedOnAnyActionExceptMove;
private final boolean _removedOnDamage;
private final boolean _canBeReflected;
private final boolean _canBeDispeled;
private final boolean _isClanSkill;
private final boolean _ignoreShield;
private final boolean _simultaneousCast;
private L2ExtractableSkill _extractableItems = null;
protected L2Skill(StatsSet set)
{
_id = set.getInteger("skill_id");
_level = set.getInteger("level");
_displayId = set.getInteger("displayId", _id);
_name = set.getString("name");
_operateType = set.getEnum("operateType", SkillOpType.class);
_magic = set.getBool("isMagic", false);
_ispotion = set.getBool("isPotion", false);
_mpConsume = set.getInteger("mpConsume", 0);
_mpInitialConsume = set.getInteger("mpInitialConsume", 0);
_hpConsume = set.getInteger("hpConsume", 0);
_targetConsume = set.getInteger("targetConsumeCount", 0);
_targetConsumeId = set.getInteger("targetConsumeId", 0);
_itemConsume = set.getInteger("itemConsumeCount", 0);
_itemConsumeId = set.getInteger("itemConsumeId", 0);
_castRange = set.getInteger("castRange", 0);
_effectRange = set.getInteger("effectRange", -1);
_abnormalLvl = set.getInteger("abnormalLvl", -1);
_effectAbnormalLvl = set.getInteger("effectAbnormalLvl", -1); // support for a separate effect abnormal lvl, e.g. poison
// inside a different skill
_negateLvl = set.getInteger("negateLvl", -1);
_hitTime = set.getInteger("hitTime", 0);
_coolTime = set.getInteger("coolTime", 0);
_reuseDelay = set.getInteger("reuseDelay", 0);
_equipDelay = set.getInteger("equipDelay", 0);
_buffDuration = set.getInteger("buffDuration", 0);
_skillRadius = set.getInteger("skillRadius", 80);
_targetType = set.getEnum("target", SkillTargetType.class);
_power = set.getFloat("power", 0.f);
_attribute = set.getString("attribute", "");
String str = set.getString("negateStats", "");
if (str.isEmpty())
_negateStats = new L2SkillType[0];
else
{
String[] stats = str.split(" ");
L2SkillType[] array = new L2SkillType[stats.length];
for (int i = 0; i < stats.length; i++)
{
L2SkillType type = null;
try
{
type = Enum.valueOf(L2SkillType.class, stats[i]);
}
catch (Exception e)
{
throw new IllegalArgumentException("SkillId: " + _id + "Enum value of type " + L2SkillType.class.getName() + " required, but found: " + stats[i]);
}
array[i] = type;
}
_negateStats = array;
}
String negateId = set.getString("negateId", null);
if (negateId != null)
{
String[] valuesSplit = negateId.split(",");
_negateId = new int[valuesSplit.length];
for (int i = 0; i < valuesSplit.length; i++)
{
_negateId[i] = Integer.parseInt(valuesSplit[i]);
}
}
else
_negateId = new int[0];
_maxNegatedEffects = set.getInteger("maxNegated", 0);
_magicLevel = set.getInteger("magicLvl", SkillTreeData.getInstance().getMinSkillLevel(_id, _level));
_levelDepend = set.getInteger("lvlDepend", 0);
_ignoreResists = set.getBool("ignoreResists", false);
_staticReuse = set.getBool("staticReuse", false);
_staticHitTime = set.getBool("staticHitTime", false);
String reuseHash = set.getString("sharedReuse", null);
if (reuseHash != null)
{
try
{
String[] valuesSplit = reuseHash.split("-");
_reuseHashCode = SkillTable.getSkillHashCode(Integer.parseInt(valuesSplit[0]), Integer.parseInt(valuesSplit[1]));
}
catch (Exception e)
{
throw new IllegalArgumentException("SkillId: " + _id + " invalid sharedReuse value: " + reuseHash + ", \"skillId-skillLvl\" required");
}
}
else
_reuseHashCode = SkillTable.getSkillHashCode(_id, _level);
_stat = set.getEnum("stat", Stats.class, null);
_ignoreShield = set.getBool("ignoreShld", false);
_skillType = set.getEnum("skillType", L2SkillType.class);
_effectType = set.getEnum("effectType", L2SkillType.class, null);
_effectId = set.getInteger("effectId", 0);
_effectPower = set.getInteger("effectPower", 0);
_effectLvl = set.getInteger("effectLevel", 0);
_element = set.getByte("element", (byte) -1);
_condition = set.getInteger("condition", 0);
_conditionValue = set.getInteger("conditionValue", 0);
_overhit = set.getBool("overHit", false);
_killByDOT = set.getBool("killByDOT", false);
_isSuicideAttack = set.getBool("isSuicideAttack", false);
_isDemonicSkill = set.getBool("isDemonicSkill", false);
_isFlyingSkill = set.getBool("isFlyingSkill", false);
_isStriderSkill = set.getBool("isStriderSkill", false);
_isSiegeSummonSkill = set.getBool("isSiegeSummonSkill", false);
String weaponsAllowedString = set.getString("weaponsAllowed", null);
if (weaponsAllowedString != null && !weaponsAllowedString.trim().isEmpty())
{
int mask = 0;
StringTokenizer st = new StringTokenizer(weaponsAllowedString, ",");
while (st.hasMoreTokens())
{
int old = mask;
String item = st.nextToken().trim();
if (ItemTable._weaponTypes.containsKey(item))
mask |= ItemTable._weaponTypes.get(item).mask();
if (ItemTable._armorTypes.containsKey(item)) // for shield
mask |= ItemTable._armorTypes.get(item).mask();
if (old == mask)
_log.info("[weaponsAllowed] Unknown item type name: " + item);
}
_weaponsAllowed = mask;
}
else
_weaponsAllowed = 0;
_nextActionIsAttack = set.getBool("nextActionAttack", false);
_minPledgeClass = set.getInteger("minPledgeClass", 0);
_triggeredId = set.getInteger("triggeredId", 0);
_triggeredLevel = set.getInteger("triggeredLevel", 0);
_chanceType = set.getString("chanceType", "");
if (!_chanceType.isEmpty())
_chanceCondition = ChanceCondition.parse(set);
_isOffensive = set.getBool("offensive", isSkillTypeOffensive());
_maxCharges = set.getInteger("maxCharges", 0);
_numCharges = set.getInteger("numCharges", 0);
_isHeroSkill = SkillTable.isHeroSkill(_id);
_baseCritRate = set.getInteger("baseCritRate", (_skillType == L2SkillType.PDAM || _skillType == L2SkillType.BLOW) ? 0 : -1);
_lethalEffect1 = set.getInteger("lethal1", 0);
_lethalEffect2 = set.getInteger("lethal2", 0);
_directHpDmg = set.getBool("dmgDirectlyToHp", false);
_isDance = set.getBool("isDance", false);
_nextDanceCost = set.getInteger("nextDanceCost", 0);
_sSBoost = set.getFloat("SSBoost", 0.f);
_aggroPoints = set.getInteger("aggroPoints", 0);
_isDebuff = set.getBool("isDebuff", false);
_stayAfterDeath = set.getBool("stayAfterDeath", false);
_removedOnAnyActionExceptMove = set.getBool("removedOnAnyActionExceptMove", false);
_removedOnDamage = set.getBool("removedOnDamage", _skillType == L2SkillType.SLEEP);
_flyType = set.getString("flyType", null);
_flyRadius = set.getInteger("flyRadius", 0);
_flyCourse = set.getFloat("flyCourse", 0);
_feed = set.getInteger("feed", 0);
_canBeReflected = set.getBool("canBeReflected", true);
_canBeDispeled = set.getBool("canBeDispeled", true);
_isClanSkill = set.getBool("isClanSkill", false);
_simultaneousCast = set.getBool("simultaneousCast", false);
String capsuled_items = set.getString("capsuled_items_skill", null);
if (capsuled_items != null)
{
if (capsuled_items.isEmpty())
_log.warn("Empty extractable data for skill: " + _id);
_extractableItems = parseExtractableSkill(_id, _level, capsuled_items);
}
}
public abstract void useSkill(L2Character caster, L2Object[] targets);
public final boolean isPotion()
{
return _ispotion;
}
public final int getConditionValue()
{
return _conditionValue;
}
public final L2SkillType getSkillType()
{
return _skillType;
}
public final byte getElement()
{
return _element;
}
/**
* @return the target type of the skill : SELF, PARTY, CLAN, PET...
*/
public final SkillTargetType getTargetType()
{
return _targetType;
}
public final int getCondition()
{
return _condition;
}
public final boolean isOverhit()
{
return _overhit;
}
public final boolean killByDOT()
{
return _killByDOT;
}
public final boolean isSuicideAttack()
{
return _isSuicideAttack;
}
public final boolean isDemonicSkill()
{
return _isDemonicSkill;
}
public final boolean isFlyingSkill()
{
return _isFlyingSkill;
}
public final boolean isStriderSkill()
{
return _isStriderSkill;
}
public final boolean isSiegeSummonSkill()
{
return _isSiegeSummonSkill;
}
/**
* @param activeChar
* @param target
* @return the power of the skill.
*/
public final double getPower(L2Character activeChar, L2Character target)
{
if (activeChar == null)
return getPower();
switch (_skillType)
{
case DEATHLINK:
return getPower() * Math.pow(1.7165 - activeChar.getCurrentHp() / activeChar.getMaxHp(), 2) * 0.577;
case FATAL:
return getPower() * 3.5 * (1 - target.getCurrentHp() / target.getMaxHp());
default:
return getPower();
}
}
public final double getPower()
{
return _power;
}
public final L2SkillType[] getNegateStats()
{
return _negateStats;
}
public final int getAbnormalLvl()
{
return _abnormalLvl;
}
public final int getNegateLvl()
{
return _negateLvl;
}
public final int[] getNegateId()
{
return _negateId;
}
public final int getMagicLevel()
{
return _magicLevel;
}
public final int getMaxNegatedEffects()
{
return _maxNegatedEffects;
}
public final int getLevelDepend()
{
return _levelDepend;
}
/**
* @return true if skill should ignore all resistances.
*/
public final boolean ignoreResists()
{
return _ignoreResists;
}
public int getTriggeredId()
{
return _triggeredId;
}
public int getTriggeredLevel()
{
return _triggeredLevel;
}
public boolean triggerAnotherSkill()
{
return _triggeredId > 1;
}
/**
* @return true if skill effects should be removed on any action except movement
*/
public final boolean isRemovedOnAnyActionExceptMove()
{
return _removedOnAnyActionExceptMove;
}
/**
* @return true if skill effects should be removed on damage
*/
public final boolean isRemovedOnDamage()
{
return _removedOnDamage;
}
/**
* @return the additional effect power or base probability.
*/
public final double getEffectPower()
{
if (_effectTemplates != null)
for (EffectTemplate et : _effectTemplates)
if (et.effectPower > 0)
return et.effectPower;
if (_effectPower > 0)
return _effectPower;
// to let damage dealing skills having proper resist even without specified effectPower
switch (_skillType)
{
case PDAM:
return 20;
case MDAM:
return 20;
default:
// to let debuffs succeed even without specified power
return (_power <= 0 || 100 < _power) ? 20 : _power;
}
}
/**
* @return the additional effect Id.
*/
public final int getEffectId()
{
return _effectId;
}
/**
* @return the additional effect level.
*/
public final int getEffectLvl()
{
return _effectLvl;
}
public final int getEffectAbnormalLvl()
{
return _effectAbnormalLvl;
}
/**
* @return the additional effect skill type (ex : STUN, PARALYZE,...).
*/
public final L2SkillType getEffectType()
{
if (_effectTemplates != null)
for (EffectTemplate et : _effectTemplates)
if (et.effectType != null)
return et.effectType;
if (_effectType != null)
return _effectType;
// to let damage dealing skills having proper resist even without specified effectType
switch (_skillType)
{
case PDAM:
return L2SkillType.STUN;
case MDAM:
return L2SkillType.PARALYZE;
default:
return _skillType;
}
}
/**
* @return true if character should attack target after skill
*/
public final boolean nextActionIsAttack()
{
return _nextActionIsAttack;
}
/**
* @return Returns the buffDuration.
*/
public final int getBuffDuration()
{
return _buffDuration;
}
/**
* @return Returns the castRange.
*/
public final int getCastRange()
{
return _castRange;
}
/**
* @return Returns the effectRange.
*/
public final int getEffectRange()
{
return _effectRange;
}
/**
* @return Returns the hpConsume.
*/
public final int getHpConsume()
{
return _hpConsume;
}
/**
* @return Returns the boolean _isDebuff.
*/
public final boolean isDebuff()
{
return _isDebuff;
}
/**
* @return the skill id.
*/
public final int getId()
{
return _id;
}
public int getDisplayId()
{
return _displayId;
}
public void setDisplayId(int id)
{
_displayId = id;
}
public final Stats getStat()
{
return _stat;
}
/**
* @return the _targetConsumeId.
*/
public final int getTargetConsumeId()
{
return _targetConsumeId;
}
/**
* @return the targetConsume.
*/
public final int getTargetConsume()
{
return _targetConsume;
}
/**
* @return the itemConsume.
*/
public final int getItemConsume()
{
return _itemConsume;
}
/**
* @return the itemConsumeId.
*/
public final int getItemConsumeId()
{
return _itemConsumeId;
}
/**
* @return the level.
*/
public final int getLevel()
{
return _level;
}
/**
* @return the magic.
*/
public final boolean isMagic()
{
return _magic;
}
/**
* @return true to set static reuse.
*/
public final boolean isStaticReuse()
{
return _staticReuse;
}
/**
* @return true to set static hittime.
*/
public final boolean isStaticHitTime()
{
return _staticHitTime;
}
/**
* @return Returns the mpConsume.
*/
public final int getMpConsume()
{
return _mpConsume;
}
/**
* @return Returns the mpInitialConsume.
*/
public final int getMpInitialConsume()
{
return _mpInitialConsume;
}
/**
* @return Returns the name.
*/
public final String getName()
{
return _name;
}
/**
* @return Returns the reuseDelay.
*/
public final int getReuseDelay()
{
return _reuseDelay;
}
public final int getEquipDelay()
{
return _equipDelay;
}
public final int getReuseHashCode()
{
return _reuseHashCode;
}
public final int getHitTime()
{
return _hitTime;
}
/**
* @return Returns the coolTime.
*/
public final int getCoolTime()
{
return _coolTime;
}
public final int getSkillRadius()
{
return _skillRadius;
}
public final boolean isActive()
{
return _operateType == SkillOpType.OP_ACTIVE;
}
public final boolean isPassive()
{
return _operateType == SkillOpType.OP_PASSIVE;
}
public final boolean isToggle()
{
return _operateType == SkillOpType.OP_TOGGLE;
}
public boolean isChance()
{
return _chanceCondition != null && isPassive();
}
public final boolean isDance()
{
return _isDance;
}
public final int getNextDanceMpCost()
{
return _nextDanceCost;
}
public final float getSSBoost()
{
return _sSBoost;
}
public final int getAggroPoints()
{
return _aggroPoints;
}
public final boolean useSoulShot()
{
return ((getSkillType() == L2SkillType.PDAM) || (getSkillType() == L2SkillType.STUN) || (getSkillType() == L2SkillType.CHARGEDAM));
}
public final boolean useSpiritShot()
{
return isMagic();
}
public final boolean useFishShot()
{
return ((getSkillType() == L2SkillType.PUMPING) || (getSkillType() == L2SkillType.REELING));
}
public final int getWeaponsAllowed()
{
return _weaponsAllowed;
}
public boolean isSimultaneousCast()
{
return _simultaneousCast;
}
public int getMinPledgeClass()
{
return _minPledgeClass;
}
public String getAttributeName()
{
return _attribute;
}
public boolean ignoreShield()
{
return _ignoreShield;
}
public boolean canBeReflected()
{
return _canBeReflected;
}
public boolean canBeDispeled()
{
return _canBeDispeled;
}
public boolean isClanSkill()
{
return _isClanSkill;
}
public final String getFlyType()
{
return _flyType;
}
public final int getFlyRadius()
{
return _flyRadius;
}
public int getFeed()
{
return _feed;
}
public final float getFlyCourse()
{
return _flyCourse;
}
public final int getMaxCharges()
{
return _maxCharges;
}
@Override
public boolean triggersChanceSkill()
{
return _triggeredId > 0 && isChance();
}
@Override
public int getTriggeredChanceId()
{
return _triggeredId;
}
@Override
public int getTriggeredChanceLevel()
{
return _triggeredLevel;
}
@Override
public ChanceCondition getTriggeredChanceCondition()
{
return _chanceCondition;
}
public final boolean isPvpSkill()
{
switch (_skillType)
{
case DOT:
case BLEED:
case POISON:
case DEBUFF:
case AGGDEBUFF:
case STUN:
case ROOT:
case FEAR:
case SLEEP:
case MDOT:
case MUTE:
case WEAKNESS:
case PARALYZE:
case CANCEL:
case MAGE_BANE:
case WARRIOR_BANE:
case BETRAY:
case AGGDAMAGE:
case AGGREDUCE_CHAR:
case MANADAM:
return true;
default:
return false;
}
}
public final boolean is7Signs()
{
if (_id > 4360 && _id < 4367)
return true;
return false;
}
public final boolean isStayAfterDeath()
{
return _stayAfterDeath;
}
public final boolean isOffensive()
{
return _isOffensive;
}
public final boolean isHeroSkill()
{
return _isHeroSkill;
}
public final int getNumCharges()
{
return _numCharges;
}
public final int getBaseCritRate()
{
return _baseCritRate;
}
public final int getLethalChance1()
{
return _lethalEffect1;
}
public final int getLethalChance2()
{
return _lethalEffect2;
}
public final boolean getDmgDirectlyToHP()
{
return _directHpDmg;
}
public final boolean isSkillTypeOffensive()
{
switch (_skillType)
{
case PDAM:
case MDAM:
case CPDAMPERCENT:
case DOT:
case BLEED:
case POISON:
case AGGDAMAGE:
case DEBUFF:
case AGGDEBUFF:
case STUN:
case ROOT:
case CONFUSION:
case ERASE:
case BLOW:
case FATAL:
case FEAR:
case DRAIN:
case SLEEP:
case CHARGEDAM:
case DEATHLINK:
case DETECT_WEAKNESS:
case MANADAM:
case MDOT:
case MUTE:
case SOULSHOT:
case SPIRITSHOT:
case SPOIL:
case WEAKNESS:
case SWEEP:
case PARALYZE:
case DRAIN_SOUL:
case AGGREDUCE:
case CANCEL:
case MAGE_BANE:
case WARRIOR_BANE:
case AGGREMOVE:
case AGGREDUCE_CHAR:
case BETRAY:
case DELUXE_KEY_UNLOCK:
case SOW:
case HARVEST:
case INSTANT_JUMP:
return true;
default:
return isDebuff();
}
}
public final boolean getWeaponDependancy(L2Character activeChar)
{
int weaponsAllowed = getWeaponsAllowed();
// check to see if skill has a weapon dependency.
if (weaponsAllowed == 0)
return true;
int mask = 0;
if (activeChar.getActiveWeaponItem() != null)
mask |= activeChar.getActiveWeaponItem().getItemType().mask();
if (activeChar.getSecondaryWeaponItem() != null && activeChar.getSecondaryWeaponItem() instanceof L2Armor)
mask |= ((L2ArmorType) activeChar.getSecondaryWeaponItem().getItemType()).mask();
if ((mask & weaponsAllowed) != 0)
return true;
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(this));
return false;
}
public boolean checkCondition(L2Character activeChar, L2Object target, boolean itemOrWeapon)
{
List<Condition> preCondition = _preCondition;
if (itemOrWeapon)
preCondition = _itemPreCondition;
if (preCondition == null || preCondition.isEmpty())
return true;
for (Condition cond : preCondition)
{
Env env = new Env();
env.player = activeChar;
if (target instanceof L2Character)
env.target = (L2Character) target;
env.skill = this;
if (!cond.test(env))
{
String msg = cond.getMessage();
int msgId = cond.getMessageId();
if (msgId != 0)
{
SystemMessage sm = SystemMessage.getSystemMessage(msgId);
if (cond.isAddName())
sm.addSkillName(_id);
activeChar.sendPacket(sm);
}
else if (msg != null)
{
activeChar.sendMessage(msg);
}
return false;
}
}
return true;
}
public final L2Object[] getTargetList(L2Character activeChar, boolean onlyFirst)
{
// Init to null the target of the skill
L2Character target = null;
// Get the L2Objcet targeted by the user of the skill at this moment
L2Object objTarget = activeChar.getTarget();
// If the L2Object targeted is a L2Character, it becomes the L2Character target
if (objTarget instanceof L2Character)
{
target = (L2Character) objTarget;
}
return getTargetList(activeChar, onlyFirst, target);
}
/**
* @param activeChar
* The L2Character who use the skill
* @param onlyFirst
* @param target
* @return all targets of the skill in a table in function of the skill type.
*/
public final L2Object[] getTargetList(L2Character activeChar, boolean onlyFirst, L2Character target)
{
List<L2Character> targetList = new FastList<>();
// Get the target type of the skill
// (ex : ONE, SELF, HOLY, PET, AURA, AURA_CLOSE, AREA, MULTIFACE, PARTY, CLAN, CORPSE_PLAYER, CORPSE_MOB, CORPSE_CLAN,
// UNLOCKABLE, ITEM, UNDEAD)
SkillTargetType targetType = getTargetType();
// Get the type of the skill
// (ex : PDAM, MDAM, DOT, BLEED, POISON, HEAL, HOT, MANAHEAL, MANARECHARGE, AGGDAMAGE, BUFF, DEBUFF, STUN, ROOT,
// RESURRECT, PASSIVE...)
L2SkillType skillType = getSkillType();
switch (targetType)
{
// The skill can only be used on the L2Character targeted, or on the caster itself
case TARGET_ONE:
{
boolean canTargetSelf = false;
switch (skillType)
{
case BUFF:
case HEAL:
case HOT:
case HEAL_PERCENT:
case MANARECHARGE:
case MANAHEAL:
case NEGATE:
case CANCEL_DEBUFF:
case REFLECT:
case COMBATPOINTHEAL:
case SEED:
case BALANCE_LIFE:
canTargetSelf = true;
break;
}
// Check for null target or any other invalid target
if (target == null || target.isDead() || (target == activeChar && !canTargetSelf))
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
// If a target is found, return it in a table else send a system message TARGET_IS_INCORRECT
return new L2Character[] { target };
}
case TARGET_SELF:
case TARGET_GROUND:
{
return new L2Character[] { activeChar };
}
case TARGET_HOLY:
{
if (activeChar instanceof L2PcInstance)
{
if (target instanceof L2ArtefactInstance)
return new L2Character[] { target };
}
return _emptyTargetList;
}
case TARGET_PET:
{
target = activeChar.getPet();
if (target != null && !target.isDead())
return new L2Character[] { target };
return _emptyTargetList;
}
case TARGET_SUMMON:
{
target = activeChar.getPet();
if (target != null && !target.isDead() && target instanceof L2SummonInstance)
return new L2Character[] { target };
return _emptyTargetList;
}
case TARGET_OWNER_PET:
{
if (activeChar instanceof L2Summon)
{
target = ((L2Summon) activeChar).getOwner();
if (target != null && !target.isDead())
return new L2Character[] { target };
}
return _emptyTargetList;
}
case TARGET_CORPSE_PET:
{
if (activeChar instanceof L2PcInstance)
{
target = activeChar.getPet();
if (target != null && target.isDead())
return new L2Character[] { target };
}
return _emptyTargetList;
}
case TARGET_AURA:
case TARGET_FRONT_AURA:
case TARGET_BEHIND_AURA:
{
final boolean srcInArena = (activeChar.isInsideZone(L2Character.ZONE_PVP) && !activeChar.isInsideZone(L2Character.ZONE_SIEGE));
final L2PcInstance sourcePlayer = activeChar.getActingPlayer();
// Go through the L2Character _knownList
final Collection<L2Character> objs = activeChar.getKnownList().getKnownCharactersInRadius(getSkillRadius());
if (getSkillType() == L2SkillType.DUMMY)
{
if (onlyFirst)
return new L2Character[] { activeChar };
targetList.add(activeChar);
for (L2Character obj : objs)
{
if (!(obj == activeChar || obj == sourcePlayer || obj instanceof L2Npc || obj instanceof L2Attackable))
continue;
targetList.add(obj);
}
}
else
{
for (L2Character obj : objs)
{
if (obj instanceof L2Attackable || obj instanceof L2Playable)
{
switch (targetType)
{
case TARGET_FRONT_AURA:
if (!obj.isInFrontOf(activeChar))
continue;
break;
case TARGET_BEHIND_AURA:
if (!obj.isBehind(activeChar))
continue;
break;
}
if (!checkForAreaOffensiveSkills(activeChar, obj, this, srcInArena))
continue;
if (onlyFirst)
return new L2Character[] { obj };
targetList.add(obj);
}
}
}
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_AREA_SUMMON:
{
target = activeChar.getPet();
if (target == null || !(target instanceof L2SummonInstance) || target.isDead())
return _emptyTargetList;
if (onlyFirst)
return new L2Character[] { target };
final boolean srcInArena = (activeChar.isInsideZone(L2Character.ZONE_PVP) && !activeChar.isInsideZone(L2Character.ZONE_SIEGE));
final Collection<L2Character> objs = target.getKnownList().getKnownCharacters();
final int radius = getSkillRadius();
for (L2Character obj : objs)
{
if (obj == null || obj == target || obj == activeChar)
continue;
if (!Util.checkIfInRange(radius, target, obj, true))
continue;
if (!(obj instanceof L2Attackable || obj instanceof L2Playable))
continue;
if (!checkForAreaOffensiveSkills(activeChar, obj, this, srcInArena))
continue;
targetList.add(obj);
}
if (targetList.isEmpty())
return _emptyTargetList;
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_AREA:
case TARGET_FRONT_AREA:
case TARGET_BEHIND_AREA:
{
if (((target == null || target == activeChar || target.isAlikeDead()) && getCastRange() >= 0) || (!(target instanceof L2Attackable || target instanceof L2Playable)))
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
final L2Character origin;
final boolean srcInArena = (activeChar.isInsideZone(L2Character.ZONE_PVP) && !activeChar.isInsideZone(L2Character.ZONE_SIEGE));
final int radius = getSkillRadius();
if (getCastRange() >= 0)
{
if (!checkForAreaOffensiveSkills(activeChar, target, this, srcInArena))
return _emptyTargetList;
if (onlyFirst)
return new L2Character[] { target };
origin = target;
targetList.add(origin); // Add target to target list
}
else
origin = activeChar;
final Collection<L2Character> objs = activeChar.getKnownList().getKnownCharacters();
for (L2Character obj : objs)
{
if (!(obj instanceof L2Attackable || obj instanceof L2Playable))
continue;
if (obj == origin)
continue;
if (Util.checkIfInRange(radius, origin, obj, true))
{
switch (targetType)
{
case TARGET_FRONT_AREA:
if (!obj.isInFrontOf(activeChar))
continue;
break;
case TARGET_BEHIND_AREA:
if (!obj.isBehind(activeChar))
continue;
break;
}
if (!checkForAreaOffensiveSkills(activeChar, obj, this, srcInArena))
continue;
targetList.add(obj);
}
}
if (targetList.isEmpty())
return _emptyTargetList;
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_PARTY:
{
if (onlyFirst)
return new L2Character[] { activeChar };
targetList.add(activeChar);
final int radius = getSkillRadius();
L2PcInstance player = activeChar.getActingPlayer();
if (activeChar instanceof L2Summon)
{
if (addCharacter(activeChar, player, radius, false))
targetList.add(player);
}
else if (activeChar instanceof L2PcInstance)
{
if (addSummon(activeChar, player, radius, false))
targetList.add(player.getPet());
}
if (activeChar.isInParty())
{
// Get a list of Party Members
for (L2PcInstance partyMember : activeChar.getParty().getPartyMembers())
{
if (partyMember == null || partyMember == player)
continue;
if (addCharacter(activeChar, partyMember, radius, false))
targetList.add(partyMember);
if (addSummon(activeChar, partyMember, radius, false))
targetList.add(partyMember.getPet());
}
}
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_PARTY_MEMBER:
{
if (target != null && (target == activeChar || (activeChar.isInParty() && target.isInParty() && activeChar.getParty().getPartyLeaderOID() == target.getParty().getPartyLeaderOID()) || (activeChar instanceof L2PcInstance && target instanceof L2Summon && activeChar.getPet() == target) || (activeChar instanceof L2Summon && target instanceof L2PcInstance && activeChar == target.getPet())))
{
if (!target.isDead())
{
// If a target is found, return it in a table else send a system message TARGET_IS_INCORRECT
return new L2Character[] { target };
}
return _emptyTargetList;
}
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
case TARGET_PARTY_OTHER:
{
if (target != null && target != activeChar && activeChar.isInParty() && target.isInParty() && activeChar.getParty().getPartyLeaderOID() == target.getParty().getPartyLeaderOID())
{
if (!target.isDead())
{
if (target instanceof L2PcInstance)
{
switch (getId())
{
// FORCE BUFFS may cancel here but there should be a proper condition
case 426:
if (!((L2PcInstance) target).isMageClass())
return new L2Character[] { target };
return _emptyTargetList;
case 427:
if (((L2PcInstance) target).isMageClass())
return new L2Character[] { target };
return _emptyTargetList;
}
}
return new L2Character[] { target };
}
return _emptyTargetList;
}
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
case TARGET_CORPSE_ALLY:
case TARGET_ALLY:
{
if (activeChar instanceof L2Playable)
{
final L2PcInstance player = activeChar.getActingPlayer();
if (player == null)
return _emptyTargetList;
if (player.isInOlympiadMode())
return new L2Character[] { player };
final boolean isCorpseType = targetType == SkillTargetType.TARGET_CORPSE_ALLY;
if (!isCorpseType)
{
if (onlyFirst)
return new L2Character[] { player };
targetList.add(player);
}
final int radius = getSkillRadius();
if (addSummon(activeChar, player, radius, isCorpseType))
targetList.add(player.getPet());
if (player.getClan() != null)
{
// Get all visible objects in a spherical area near the L2Character
final Collection<L2PcInstance> objs = activeChar.getKnownList().getKnownPlayersInRadius(radius);
for (L2PcInstance obj : objs)
{
if (obj == null)
continue;
if ((obj.getAllyId() == 0 || obj.getAllyId() != player.getAllyId()) && (obj.getClan() == null || obj.getClanId() != player.getClanId()))
continue;
if (player.isInDuel())
{
if (player.getDuelId() != obj.getDuelId())
continue;
if (player.isInParty() && obj.isInParty() && player.getParty().getPartyLeaderOID() != obj.getParty().getPartyLeaderOID())
continue;
}
// Don't add this target if this is a Pc->Pc pvp
// casting and pvp condition not met
if (!player.checkPvpSkill(obj, this))
continue;
if (!TvTEvent.checkForTvTSkill(player, obj, this))
continue;
if (!onlyFirst && addSummon(activeChar, obj, radius, isCorpseType))
targetList.add(obj.getPet());
if (!addCharacter(activeChar, obj, radius, isCorpseType))
continue;
if (isCorpseType)
{
// Siege battlefield resurrect has been made possible for participants
if (getSkillType() == L2SkillType.RESURRECT)
{
if (obj.isInsideZone(L2Character.ZONE_SIEGE) && !obj.isInSiege())
continue;
}
}
if (onlyFirst)
return new L2Character[] { obj };
targetList.add(obj);
}
}
}
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_CORPSE_CLAN:
case TARGET_CLAN:
{
if (activeChar instanceof L2Playable)
{
final L2PcInstance player = activeChar.getActingPlayer();
if (player == null)
return _emptyTargetList;
if (player.isInOlympiadMode())
return new L2Character[] { player };
final boolean isCorpseType = targetType == SkillTargetType.TARGET_CORPSE_CLAN;
if (!isCorpseType)
{
if (onlyFirst)
return new L2Character[] { player };
targetList.add(player);
}
final int radius = getSkillRadius();
final L2Clan clan = player.getClan();
if (addSummon(activeChar, player, radius, isCorpseType))
targetList.add(player.getPet());
if (clan != null)
{
L2PcInstance obj;
// Get Clan Members
for (L2ClanMember member : clan.getMembers())
{
obj = member.getPlayerInstance();
if (obj == null || obj == player)
continue;
if (player.isInDuel())
{
if (player.getDuelId() != obj.getDuelId())
continue;
if (player.isInParty() && obj.isInParty() && player.getParty().getPartyLeaderOID() != obj.getParty().getPartyLeaderOID())
continue;
}
// Don't add this target if this is a Pc->Pc pvp casting and pvp condition not met
if (!player.checkPvpSkill(obj, this))
continue;
if (!TvTEvent.checkForTvTSkill(player, obj, this))
continue;
if (!onlyFirst && addSummon(activeChar, obj, radius, isCorpseType))
targetList.add(obj.getPet());
if (!addCharacter(activeChar, obj, radius, isCorpseType))
continue;
if (isCorpseType)
{
if (getSkillType() == L2SkillType.RESURRECT)
{
// check target is not in a active siege zone
if (obj.isInsideZone(L2Character.ZONE_SIEGE) && !obj.isInSiege())
continue;
}
}
if (onlyFirst)
return new L2Character[] { obj };
targetList.add(obj);
}
}
}
else if (activeChar instanceof L2Npc)
{
// for buff purposes, returns friendly mobs nearby and mob itself
final L2Npc npc = (L2Npc) activeChar;
if (npc.getClan() == null || npc.getClan().isEmpty())
return new L2Character[] { activeChar };
targetList.add(activeChar);
final Collection<L2Object> objs = activeChar.getKnownList().getKnownObjects().values();
for (L2Object newTarget : objs)
{
if (newTarget instanceof L2Npc && npc.getClan().equals(((L2Npc) newTarget).getClan()))
{
// Bypass buff if target is dead
if (((L2Npc) newTarget).isDead())
continue;
if (!Util.checkIfInRange(getCastRange(), activeChar, newTarget, true))
continue;
targetList.add((L2Npc) newTarget);
}
}
}
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_CORPSE_PLAYER:
{
if (target != null && target.isDead())
{
final L2PcInstance player;
if (activeChar instanceof L2PcInstance)
player = (L2PcInstance) activeChar;
else
player = null;
final L2PcInstance targetPlayer;
if (target instanceof L2PcInstance)
targetPlayer = (L2PcInstance) target;
else
targetPlayer = null;
final L2PetInstance targetPet;
if (target instanceof L2PetInstance)
targetPet = (L2PetInstance) target;
else
targetPet = null;
if (player != null && (targetPlayer != null || targetPet != null))
{
boolean condGood = true;
if (getSkillType() == L2SkillType.RESURRECT)
{
if (targetPlayer != null)
{
// check target is not in a active siege zone
if (targetPlayer.isInsideZone(L2Character.ZONE_SIEGE) && !targetPlayer.isInSiege())
{
condGood = false;
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CANNOT_BE_RESURRECTED_DURING_SIEGE));
}
if (targetPlayer.isFestivalParticipant()) // Check to see if the current player target is in a
// festival.
{
condGood = false;
activeChar.sendMessage("You may not resurrect participants in a festival.");
}
if (targetPlayer.isReviveRequested())
{
if (targetPlayer.isRevivingPet())
player.sendPacket(SystemMessageId.MASTER_CANNOT_RES); // While a pet is attempting to
// resurrect, it cannot help in
// resurrecting its master.
else
player.sendPacket(SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED); // Resurrection is
// already been
// proposed.
condGood = false;
}
}
else if (targetPet != null)
{
if (targetPet.getOwner() != player)
{
if (targetPet.getOwner().isReviveRequested())
{
if (targetPet.getOwner().isRevivingPet())
player.sendPacket(SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED); // Resurrection is
// already been
// proposed.
else
player.sendPacket(SystemMessageId.CANNOT_RES_PET2); // A pet cannot be resurrected
// while it's owner is in the
// process of resurrecting.
condGood = false;
}
}
}
}
if (condGood)
{
if (!onlyFirst)
{
targetList.add(target);
return targetList.toArray(new L2Object[targetList.size()]);
}
return new L2Character[] { target };
}
}
}
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
case TARGET_CORPSE_MOB:
{
final boolean isSummon = target instanceof L2SummonInstance;
if (!(isSummon || target instanceof L2Attackable) || !target.isDead())
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
// Corpse mob only available for half time
switch (getSkillType())
{
case SUMMON:
{
if (isSummon && ((L2SummonInstance) target).getOwner() != null && ((L2SummonInstance) target).getOwner().getObjectId() == activeChar.getObjectId())
return _emptyTargetList;
}
case DRAIN:
{
if (DecayTaskManager.getInstance().getTasks().containsKey(target) && (System.currentTimeMillis() - DecayTaskManager.getInstance().getTasks().get(target)) > DecayTaskManager.DEFAULT_DECAY_TIME / 2)
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.CORPSE_TOO_OLD_SKILL_NOT_USED));
return _emptyTargetList;
}
}
}
if (!onlyFirst)
{
targetList.add(target);
return targetList.toArray(new L2Object[targetList.size()]);
}
return new L2Character[] { target };
}
case TARGET_AREA_CORPSE_MOB:
{
if ((!(target instanceof L2Attackable)) || !target.isDead())
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
if (onlyFirst)
return new L2Character[] { target };
targetList.add(target);
final boolean srcInArena = (activeChar.isInsideZone(L2Character.ZONE_PVP) && !activeChar.isInsideZone(L2Character.ZONE_SIEGE));
final int radius = getSkillRadius();
final Collection<L2Character> objs = activeChar.getKnownList().getKnownCharacters();
for (L2Character obj : objs)
{
if (!(obj instanceof L2Attackable || obj instanceof L2Playable) || !Util.checkIfInRange(radius, target, obj, true))
continue;
if (!checkForAreaOffensiveSkills(activeChar, obj, this, srcInArena))
continue;
targetList.add(obj);
}
if (targetList.isEmpty())
return _emptyTargetList;
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_UNLOCKABLE:
{
if (!(target instanceof L2DoorInstance) && !(target instanceof L2ChestInstance))
return _emptyTargetList;
if (!onlyFirst)
{
targetList.add(target);
return targetList.toArray(new L2Object[targetList.size()]);
}
return new L2Character[] { target };
}
case TARGET_UNDEAD:
{
if (target instanceof L2Npc || target instanceof L2SummonInstance)
{
if (!target.isUndead() || target.isDead())
{
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
if (!onlyFirst)
targetList.add(target);
else
return new L2Character[] { target };
return targetList.toArray(new L2Object[targetList.size()]);
}
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
return _emptyTargetList;
}
case TARGET_AREA_UNDEAD:
{
final L2Character cha;
final int radius = getSkillRadius();
if (getCastRange() >= 0 && (target instanceof L2Npc || target instanceof L2SummonInstance) && target.isUndead() && !target.isAlikeDead())
{
cha = target;
if (!onlyFirst)
targetList.add(cha); // Add target to target list
else
return new L2Character[] { cha };
}
else
cha = activeChar;
final Collection<L2Character> objs = activeChar.getKnownList().getKnownCharacters();
for (L2Character obj : objs)
{
if (!Util.checkIfInRange(radius, cha, obj, true))
continue;
if (obj instanceof L2Npc)
target = obj;
else if (obj instanceof L2SummonInstance)
target = obj;
else
continue;
if (!target.isAlikeDead()) // If target is not dead/fake death
{
if (!target.isUndead())
continue;
if (geoEnabled && !GeoData.getInstance().canSeeTarget(activeChar, target))
continue;
if (!onlyFirst)
targetList.add(obj);
else
return new L2Character[] { obj };
}
}
if (targetList.isEmpty())
return _emptyTargetList;
return targetList.toArray(new L2Character[targetList.size()]);
}
case TARGET_ENEMY_SUMMON:
{
if (target instanceof L2Summon)
{
L2Summon targetSummon = (L2Summon) target;
if (activeChar instanceof L2PcInstance && activeChar.getPet() != targetSummon && !targetSummon.isDead() && (targetSummon.getOwner().getPvpFlag() != 0 || targetSummon.getOwner().getKarma() > 0) || (targetSummon.getOwner().isInsideZone(L2Character.ZONE_PVP) && ((L2PcInstance) activeChar).isInsideZone(L2Character.ZONE_PVP)) || (targetSummon.getOwner().isInDuel() && ((L2PcInstance) activeChar).isInDuel() && targetSummon.getOwner().getDuelId() == ((L2PcInstance) activeChar).getDuelId()))
return new L2Character[] { targetSummon };
}
return _emptyTargetList;
}/*
* // npc only for now - untested case TARGET_CLAN_MEMBER: { if (activeChar instanceof L2Npc) { // for buff purposes, returns
* friendly mobs nearby and mob itself final L2Npc npc = (L2Npc) activeChar; if (npc.getClan() == null || npc.getClan().isEmpty()) {
* return new L2Character[]{activeChar}; } final Collection<L2Object> objs = activeChar.getKnownList().getKnownObjects().values();
* for (L2Object newTarget : objs) { if (newTarget instanceof L2Npc && npc.getClan().equals(((L2Npc) newTarget).getClan())) { if
* (!Util.checkIfInRange(getCastRange(), activeChar, newTarget, true)) continue; if (((L2Npc) newTarget).getFirstEffect(this) !=
* null) continue; targetList.add((L2Npc) newTarget); break; // found } } if (targetList.isEmpty()) targetList.add(npc); } else
* return _emptyTargetList; return targetList.toArray(new L2Character[targetList.size()]); }
*/
default:
{
activeChar.sendMessage("Target type of skill is not currently handled");
return _emptyTargetList;
}
}// end switch
}
public final L2Object[] getTargetList(L2Character activeChar)
{
return getTargetList(activeChar, false);
}
public final L2Object getFirstOfTargetList(L2Character activeChar)
{
L2Object[] targets = getTargetList(activeChar, true);
if (targets.length == 0)
return null;
return targets[0];
}
/*
* Check if should be target added to the target list false if target is dead, target same as caster, target inside peace zone, target in the
* same party with caster, caster can see target. Additional checks if not in PvP zones (arena, siege): target in not the same clan and
* alliance with caster, and usual skill PvP check. Caution: distance is not checked.
*/
public static final boolean checkForAreaOffensiveSkills(L2Character caster, L2Character target, L2Skill skill, boolean sourceInArena)
{
if (target == null || target.isDead() || target == caster)
return false;
final L2PcInstance player = caster.getActingPlayer();
final L2PcInstance targetPlayer = target.getActingPlayer();
if (player != null)
{
if (targetPlayer != null)
{
if (targetPlayer == caster || targetPlayer == player)
return false;
if (targetPlayer.inObserverMode())
return false;
if (skill.isOffensive() && player.getSiegeState() > 0 && player.isInsideZone(L2Character.ZONE_SIEGE) && player.getSiegeState() == targetPlayer.getSiegeState())
return false;
if (target.isInsideZone(L2Character.ZONE_PEACE))
return false;
if (player.isInParty() && targetPlayer.isInParty())
{
// Same party
if (player.getParty().getPartyLeaderOID() == targetPlayer.getParty().getPartyLeaderOID())
return false;
// Same commandchannel
if (player.getParty().getCommandChannel() != null && player.getParty().getCommandChannel() == targetPlayer.getParty().getCommandChannel())
return false;
}
if (!TvTEvent.checkForTvTSkill(player, targetPlayer, skill))
return false;
if (!sourceInArena && !(targetPlayer.isInsideZone(L2Character.ZONE_PVP) && !targetPlayer.isInsideZone(L2Character.ZONE_SIEGE)))
{
if (player.getAllyId() != 0 && player.getAllyId() == targetPlayer.getAllyId())
return false;
if (player.getClanId() != 0 && player.getClanId() == targetPlayer.getClanId())
return false;
if (!player.checkPvpSkill(targetPlayer, skill, (caster instanceof L2Summon)))
return false;
}
}
}
else
{
// target is mob
if (targetPlayer == null && target instanceof L2Attackable && caster instanceof L2Attackable)
{
String casterEnemyClan = ((L2Attackable) caster).getEnemyClan();
if (casterEnemyClan == null || casterEnemyClan.isEmpty())
return false;
String targetClan = ((L2Attackable) target).getClan();
if (targetClan == null || targetClan.isEmpty())
return false;
if (!casterEnemyClan.equals(targetClan))
return false;
}
}
if (geoEnabled && !GeoData.getInstance().canSeeTarget(caster, target))
return false;
return true;
}
public static final boolean addSummon(L2Character caster, L2PcInstance owner, int radius, boolean isDead)
{
final L2Summon summon = owner.getPet();
if (summon == null)
return false;
return addCharacter(caster, summon, radius, isDead);
}
public static final boolean addCharacter(L2Character caster, L2Character target, int radius, boolean isDead)
{
if (isDead != target.isDead())
return false;
if (radius > 0 && !Util.checkIfInRange(radius, caster, target, true))
return false;
return true;
}
public final Func[] getStatFuncs(L2Effect effect, L2Character player)
{
if (_funcTemplates == null)
return _emptyFunctionSet;
if (!(player instanceof L2Playable) && !(player instanceof L2Attackable))
return _emptyFunctionSet;
ArrayList<Func> funcs = new ArrayList<>(_funcTemplates.length);
Env env = new Env();
env.player = player;
env.skill = this;
Func f;
for (FuncTemplate t : _funcTemplates)
{
f = t.getFunc(env, this); // skill is owner
if (f != null)
funcs.add(f);
}
if (funcs.isEmpty())
return _emptyFunctionSet;
return funcs.toArray(new Func[funcs.size()]);
}
public boolean hasEffects()
{
return (_effectTemplates != null && _effectTemplates.length > 0);
}
public EffectTemplate[] getEffectTemplates()
{
return _effectTemplates;
}
public boolean hasSelfEffects()
{
return (_effectTemplatesSelf != null && _effectTemplatesSelf.length > 0);
}
/**
* @param effector
* @param effected
* @param env
* parameters for secondary effects (shield and ss/bss/bsss)
* @return an array with the effects that have been added to effector
*/
public final L2Effect[] getEffects(L2Character effector, L2Character effected, Env env)
{
if (!hasEffects() || isPassive())
return _emptyEffectSet;
// doors and siege flags cannot receive any effects
if (effected instanceof L2DoorInstance || effected instanceof L2SiegeFlagInstance)
return _emptyEffectSet;
if (effector != effected)
{
if (isOffensive() || isDebuff())
{
if (effected.isInvul())
return _emptyEffectSet;
if (effector instanceof L2PcInstance && ((L2PcInstance) effector).isGM())
{
if (!((L2PcInstance) effector).getAccessLevel().canGiveDamage())
return _emptyEffectSet;
}
}
}
ArrayList<L2Effect> effects = new ArrayList<>(_effectTemplates.length);
if (env == null)
env = new Env();
env.skillMastery = Formulas.calcSkillMastery(effector, this);
env.player = effector;
env.target = effected;
env.skill = this;
for (EffectTemplate et : _effectTemplates)
{
boolean success = true;
if (et.effectPower > -1)
success = Formulas.calcEffectSuccess(effector, effected, et, this, env.shld, env.ss, env.sps, env.bss);
if (success)
{
L2Effect e = et.getEffect(env);
if (e != null)
{
e.scheduleEffect();
effects.add(e);
}
}
// display fail message only for effects with icons
else if (et.icon && effector instanceof L2PcInstance)
{
SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2);
sm.addCharName(effected);
sm.addSkillName(this);
((L2PcInstance) effector).sendPacket(sm);
}
}
if (effects.isEmpty())
return _emptyEffectSet;
return effects.toArray(new L2Effect[effects.size()]);
}
/**
* Warning: this method doesn't consider modifier (shield, ss, sps, bss) for secondary effects
*
* @param effector
* @param effected
* @return An array of L2Effect.
*/
public final L2Effect[] getEffects(L2Character effector, L2Character effected)
{
return getEffects(effector, effected, null);
}
/**
* This method has suffered some changes in CT2.2 ->CT2.3<br>
* Effect engine is now supporting secondary effects with independent success/fail calculus from effect skill. Env parameter has been added
* to pass parameters like soulshot, spiritshots, blessed spiritshots or shield deffence. Some other optimizations have been done <br>
* <br>
* This new feature works following next rules: <li>To enable feature, effectPower must be over -1 (check DocumentSkill#attachEffect for
* further information)</li> <li>If main skill fails, secondary effect always fail</li>
*
* @param effector
* @param effected
* @param env
* parameters for secondary effects (shield and ss/bss/bsss)
* @return An array of L2Effect.
*/
public final L2Effect[] getEffects(L2CubicInstance effector, L2Character effected, Env env)
{
if (!hasEffects() || isPassive())
return _emptyEffectSet;
if (effector.getOwner() != effected)
{
if (isDebuff() || isOffensive())
{
if (effected.isInvul())
return _emptyEffectSet;
if (effector.getOwner().isGM() && !effector.getOwner().getAccessLevel().canGiveDamage())
return _emptyEffectSet;
}
}
ArrayList<L2Effect> effects = new ArrayList<>(_effectTemplates.length);
if (env == null)
env = new Env();
env.player = effector.getOwner();
env.cubic = effector;
env.target = effected;
env.skill = this;
for (EffectTemplate et : _effectTemplates)
{
boolean success = true;
if (et.effectPower > -1)
success = Formulas.calcEffectSuccess(effector.getOwner(), effected, et, this, env.shld, env.ss, env.sps, env.bss);
if (success)
{
L2Effect e = et.getEffect(env);
if (e != null)
{
e.scheduleEffect();
effects.add(e);
}
}
}
if (effects.isEmpty())
return _emptyEffectSet;
return effects.toArray(new L2Effect[effects.size()]);
}
public final L2Effect[] getEffectsSelf(L2Character effector)
{
if (!hasSelfEffects() || isPassive())
return _emptyEffectSet;
List<L2Effect> effects = new ArrayList<>(_effectTemplatesSelf.length);
Env env = new Env();
env.player = effector;
env.target = effector;
env.skill = this;
for (EffectTemplate et : _effectTemplatesSelf)
{
L2Effect e = et.getEffect(env);
if (e != null)
{
e.setSelfEffect();
e.scheduleEffect();
effects.add(e);
}
}
if (effects.isEmpty())
return _emptyEffectSet;
return effects.toArray(new L2Effect[effects.size()]);
}
public final void attach(FuncTemplate f)
{
if (_funcTemplates == null)
_funcTemplates = new FuncTemplate[] { f };
else
{
int len = _funcTemplates.length;
FuncTemplate[] tmp = new FuncTemplate[len + 1];
System.arraycopy(_funcTemplates, 0, tmp, 0, len);
tmp[len] = f;
_funcTemplates = tmp;
}
}
public final void attach(EffectTemplate effect)
{
if (_effectTemplates == null)
_effectTemplates = new EffectTemplate[] { effect };
else
{
int len = _effectTemplates.length;
EffectTemplate[] tmp = new EffectTemplate[len + 1];
System.arraycopy(_effectTemplates, 0, tmp, 0, len);
tmp[len] = effect;
_effectTemplates = tmp;
}
}
public final void attachSelf(EffectTemplate effect)
{
if (_effectTemplatesSelf == null)
{
_effectTemplatesSelf = new EffectTemplate[] { effect };
}
else
{
int len = _effectTemplatesSelf.length;
EffectTemplate[] tmp = new EffectTemplate[len + 1];
System.arraycopy(_effectTemplatesSelf, 0, tmp, 0, len);
tmp[len] = effect;
_effectTemplatesSelf = tmp;
}
}
public final void attach(Condition c, boolean itemOrWeapon)
{
if (itemOrWeapon)
{
if (_itemPreCondition == null)
_itemPreCondition = new FastList<>();
_itemPreCondition.add(c);
}
else
{
if (_preCondition == null)
_preCondition = new FastList<>();
_preCondition.add(c);
}
}
/**
* @param skillId
* @param skillLvl
* @param values
* @return L2ExtractableSkill
* @author Zoey76
*/
private L2ExtractableSkill parseExtractableSkill(int skillId, int skillLvl, String values)
{
String[] lineSplit = values.split(";");
final List<L2ExtractableProductItem> product_temp = new ArrayList<>();
for (int i = 0; i <= (lineSplit.length - 1); i++)
{
final String[] lineSplit2 = lineSplit[i].split(",");
if (lineSplit2.length < 3)
_log.warn("Extractable skills data: Error in Skill Id: " + skillId + " Level: " + skillLvl + " -> wrong seperator!");
int[] production = null;
int[] amount = null;
double chance = 0;
int prodId = 0;
int quantity = 0;
try
{
int k = 0;
production = new int[(lineSplit2.length - 1) / 2];
amount = new int[(lineSplit2.length - 1) / 2];
for (int j = 0; j < (lineSplit2.length - 1); j++)
{
prodId = Integer.parseInt(lineSplit2[j]);
quantity = Integer.parseInt(lineSplit2[j += 1]);
if ((prodId <= 0) || (quantity <= 0))
_log.warn("Extractable skills data: Error in Skill Id: " + skillId + " Level: " + skillLvl + " wrong production Id: " + prodId + " or wrond quantity: " + quantity + "!");
production[k] = prodId;
amount[k] = quantity;
k++;
}
chance = Double.parseDouble(lineSplit2[lineSplit2.length - 1]);
}
catch (Exception e)
{
_log.warn("Extractable skills data: Error in Skill Id: " + skillId + " Level: " + skillLvl + " -> incomplete/invalid production data or wrong seperator!");
}
product_temp.add(new L2ExtractableProductItem(production, amount, chance));
}
if (product_temp.isEmpty())
_log.warn("Extractable skills data: Error in Skill Id: " + skillId + " Level: " + skillLvl + " -> There are no production items!");
return new L2ExtractableSkill(SkillTable.getSkillHashCode(this), product_temp);
}
public L2ExtractableSkill getExtractableSkill()
{
return _extractableItems;
}
@Override
public String toString()
{
return "" + _name + "[id=" + _id + ",lvl=" + _level + "]";
}
}