/*
* 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.scripts.handlers.skill;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import silentium.commons.utils.Rnd;
import silentium.gameserver.ai.AttackableAI;
import silentium.gameserver.ai.CtrlEvent;
import silentium.gameserver.ai.CtrlIntention;
import silentium.gameserver.handler.ISkillHandler;
import silentium.gameserver.handler.SkillHandler;
import silentium.gameserver.model.L2Effect;
import silentium.gameserver.model.L2ItemInstance;
import silentium.gameserver.model.L2Object;
import silentium.gameserver.model.L2Skill;
import silentium.gameserver.model.actor.L2Attackable;
import silentium.gameserver.model.actor.L2Character;
import silentium.gameserver.model.actor.L2Npc;
import silentium.gameserver.model.actor.L2Summon;
import silentium.gameserver.model.actor.instance.L2PcInstance;
import silentium.gameserver.model.actor.instance.L2SiegeSummonInstance;
import silentium.gameserver.model.base.Experience;
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.templates.skills.L2SkillType;
/**
* This Handles Disabler skills
*
* @author _drunk_
*/
public class Disablers implements ISkillHandler {
private static final L2SkillType[] SKILL_IDS = { L2SkillType.STUN, L2SkillType.ROOT, L2SkillType.SLEEP, L2SkillType.CONFUSION, L2SkillType.AGGDAMAGE, L2SkillType.AGGREDUCE, L2SkillType.AGGREDUCE_CHAR, L2SkillType.AGGREMOVE, L2SkillType.MUTE, L2SkillType.FAKE_DEATH, L2SkillType.NEGATE, L2SkillType.CANCEL_DEBUFF, L2SkillType.PARALYZE, L2SkillType.ERASE, L2SkillType.BETRAY };
protected static final Logger _log = LoggerFactory.getLogger(Disablers.class.getName());
@Override
public void useSkill(final L2Character activeChar, final L2Skill skill, final L2Object... targets) {
final L2SkillType type = skill.getSkillType();
byte shld = 0;
boolean ss = false;
boolean sps = false;
boolean bss = false;
final L2ItemInstance weaponInst = activeChar.getActiveWeaponInstance();
if (weaponInst != null) {
if (skill.isMagic()) {
if (weaponInst.getChargedSpiritshot() == L2ItemInstance.CHARGED_BLESSED_SPIRITSHOT) {
bss = true;
if (skill.getId() != 1020) // vitalize
weaponInst.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
} else if (weaponInst.getChargedSpiritshot() == L2ItemInstance.CHARGED_SPIRITSHOT) {
sps = true;
if (skill.getId() != 1020) // vitalize
weaponInst.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
}
} else
ss = true;
}
// If there is no weapon equipped, check for an active summon.
else if (activeChar instanceof L2Summon) {
final L2Summon activeSummon = (L2Summon) activeChar;
if (skill.isMagic()) {
if (activeSummon.getChargedSpiritShot() == L2ItemInstance.CHARGED_BLESSED_SPIRITSHOT) {
bss = true;
activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
} else if (activeSummon.getChargedSpiritShot() == L2ItemInstance.CHARGED_SPIRITSHOT) {
sps = true;
activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
}
} else
ss = true;
} else if (activeChar instanceof L2Npc) {
ss = ((L2Npc) activeChar)._soulshotcharged;
((L2Npc) activeChar)._soulshotcharged = false;
bss = ((L2Npc) activeChar)._spiritshotcharged;
((L2Npc) activeChar)._spiritshotcharged = false;
}
for (final L2Object obj : targets) {
if (!(obj instanceof L2Character))
continue;
L2Character target = (L2Character) obj;
if (target.isDead() || target.isInvul() && !target.isParalyzed()) // bypass if target is null, dead or invul
// (excluding invul from Petrification)
continue;
shld = Formulas.calcShldUse(activeChar, target, skill);
switch (type) {
case BETRAY:
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss))
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
else
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill));
break;
case FAKE_DEATH:
// stun/fakedeath is not mdef dependant, it depends on lvl difference, target CON and power of stun
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
break;
case ROOT:
case STUN:
if (Formulas.calcSkillReflect(target, skill) == Formulas.SKILL_REFLECT_SUCCEED)
target = activeChar;
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss))
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill.getDisplayId()));
}
break;
case SLEEP:
case PARALYZE: // use same as root for now
if (Formulas.calcSkillReflect(target, skill) == Formulas.SKILL_REFLECT_SUCCEED)
target = activeChar;
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss))
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill.getDisplayId()));
}
break;
case MUTE:
if (Formulas.calcSkillReflect(target, skill) == Formulas.SKILL_REFLECT_SUCCEED)
target = activeChar;
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss)) {
// stop same type effect if available
final L2Effect[] effects = target.getAllEffects();
for (final L2Effect e : effects) {
if (e.getSkill().getSkillType() == type)
e.exit();
}
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
} else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill.getDisplayId()));
}
break;
case CONFUSION:
// do nothing if not on mob
if (target instanceof L2Attackable) {
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss)) {
final L2Effect[] effects = target.getAllEffects();
for (final L2Effect e : effects) {
if (e.getSkill().getSkillType() == type)
e.exit();
}
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
} else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill));
}
} else
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.TARGET_IS_INCORRECT));
break;
case AGGDAMAGE:
if (target instanceof L2Attackable)
target.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, activeChar, (int) (150 * skill.getPower() / (target.getLevel() + 7)));
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
break;
case AGGREDUCE:
// these skills needs to be rechecked
if (target instanceof L2Attackable) {
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
final double aggdiff = ((L2Attackable) target).getHating(activeChar) - target.calcStat(Stats.AGGRESSION, ((L2Attackable) target).getHating(activeChar), target, skill);
if (skill.getPower() > 0)
((L2Attackable) target).reduceHate(null, (int) skill.getPower());
else if (aggdiff > 0)
((L2Attackable) target).reduceHate(null, (int) aggdiff);
}
break;
case AGGREDUCE_CHAR:
// these skills needs to be rechecked
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss)) {
if (target instanceof L2Attackable) {
final L2Attackable targ = (L2Attackable) target;
targ.stopHating(activeChar);
if (targ.getMostHated() == null && targ.hasAI() && targ.getAI() instanceof AttackableAI) {
((AttackableAI) targ.getAI()).setGlobalAggro(-25);
targ.clearAggroList();
targ.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
targ.setWalking();
}
}
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
} else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill));
target.getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, activeChar);
}
break;
case AGGREMOVE:
// these skills needs to be rechecked
if (target instanceof L2Attackable && !target.isRaid()) {
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss)) {
if (skill.getTargetType() == L2Skill.SkillTargetType.TARGET_UNDEAD) {
if (target.isUndead())
((L2Attackable) target).reduceHate(null, ((L2Attackable) target).getHating(((L2Attackable) target).getMostHated()));
} else
((L2Attackable) target).reduceHate(null, ((L2Attackable) target).getHating(((L2Attackable) target).getMostHated()));
} else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill));
target.getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, activeChar);
}
} else
target.getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, activeChar);
break;
case ERASE:
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss)
// doesn't affect siege golem or wild hog cannon
&& !(target instanceof L2SiegeSummonInstance)) {
L2PcInstance summonOwner = null;
L2Summon summonPet = null;
summonOwner = ((L2Summon) target).getOwner();
summonPet = summonOwner.getPet();
if (summonPet != null) {
summonPet.unSummon(summonOwner);
summonOwner.sendPacket(SystemMessageId.YOUR_SERVITOR_HAS_VANISHED);
}
} else {
if (activeChar instanceof L2PcInstance)
activeChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_RESISTED_YOUR_S2).addCharName(target).addSkillName(skill));
}
break;
case CANCEL_DEBUFF:
final L2Effect[] effects = target.getAllEffects();
if (effects == null || effects.length == 0)
break;
int count = skill.getMaxNegatedEffects() > 0 ? 0 : -2;
for (final L2Effect e : effects) {
if (e == null || !e.getSkill().isDebuff() || !e.getSkill().canBeDispeled())
continue;
e.exit();
if (count > -1) {
count++;
if (count >= skill.getMaxNegatedEffects())
break;
}
}
break;
case NEGATE:
if (Formulas.calcSkillReflect(target, skill) == Formulas.SKILL_REFLECT_SUCCEED)
target = activeChar;
if (skill.getNegateId().length != 0) {
for (int i = 0; i < skill.getNegateId().length; i++) {
if (skill.getNegateId()[i] != 0)
target.stopSkillEffects(skill.getNegateId()[i]);
}
}
// all others negate type skills
else {
int removedBuffs = skill.getMaxNegatedEffects() > 0 ? 0 : -2;
for (final L2SkillType skillType : skill.getNegateStats()) {
if (removedBuffs > skill.getMaxNegatedEffects())
break;
switch (skillType) {
case BUFF:
int lvlmodifier = 52 + skill.getMagicLevel() * 2;
if (skill.getMagicLevel() == 12)
lvlmodifier = Experience.MAX_LEVEL - 1;
int landrate = 90;
if (target.getLevel() - lvlmodifier > 0)
landrate = 90 - 4 * (target.getLevel() - lvlmodifier);
landrate = (int) activeChar.calcStat(Stats.CANCEL_VULN, landrate, target, null);
if (Rnd.get(100) < landrate)
removedBuffs += negateEffect(target, L2SkillType.BUFF, -1, skill.getMaxNegatedEffects());
break;
case HEAL:
final ISkillHandler Healhandler = SkillHandler.getInstance().getSkillHandler(L2SkillType.HEAL);
if (Healhandler == null) {
_log.error("Couldn't find skill handler for HEAL.");
continue;
}
final L2Character[] tgts = { target };
Healhandler.useSkill(activeChar, skill, tgts);
break;
default:
removedBuffs += negateEffect(target, skillType, skill.getNegateLvl(), skill.getMaxNegatedEffects());
break;
}
}
}
if (Formulas.calcSkillSuccess(activeChar, target, skill, shld, ss, sps, bss))
skill.getEffects(activeChar, target, new Env(shld, ss, sps, bss));
}
}
// self Effect :]
if (skill.hasSelfEffects()) {
final L2Effect effect = activeChar.getFirstEffect(skill.getId());
if (effect != null && effect.isSelfEffect()) {
// Replace old effect with new one.
effect.exit();
}
skill.getEffectsSelf(activeChar);
}
}
private static int negateEffect(final L2Character target, final L2SkillType type, final int negateLvl, final int maxRemoved) {
return negateEffect(target, type, negateLvl, 0, maxRemoved);
}
private static int negateEffect(final L2Character target, final L2SkillType type, final int negateLvl, final int skillId, final int maxRemoved) {
final L2Effect[] effects = target.getAllEffects();
int count = maxRemoved <= 0 ? -2 : 0;
for (final L2Effect e : effects) {
if (negateLvl == -1) // if power is -1 the effect is always removed without power/lvl check ^^
{
if (e.getSkill().getSkillType() == type || e.getSkill().getEffectType() != null && e.getSkill().getEffectType() == type) {
if (skillId != 0) {
if (skillId == e.getSkill().getId() && count < maxRemoved) {
e.exit();
if (count > -1)
count++;
}
} else if (count < maxRemoved) {
e.exit();
if (count > -1)
count++;
}
}
} else {
boolean cancel = false;
if (e.getSkill().getEffectType() != null && e.getSkill().getEffectAbnormalLvl() >= 0) {
if (e.getSkill().getEffectType() == type && e.getSkill().getEffectAbnormalLvl() <= negateLvl)
cancel = true;
} else if (e.getSkill().getSkillType() == type && e.getSkill().getAbnormalLvl() <= negateLvl)
cancel = true;
if (cancel) {
if (skillId != 0) {
if (skillId == e.getSkill().getId() && count < maxRemoved) {
e.exit();
if (count > -1)
count++;
}
} else if (count < maxRemoved) {
e.exit();
if (count > -1)
count++;
}
}
}
}
return maxRemoved <= 0 ? count + 2 : count;
}
@Override
public L2SkillType[] getSkillIds() {
return SKILL_IDS;
}
}