package handlers.effecthandlers;
import java.util.ArrayList;
import java.util.Iterator;
import java.util.List;
import java.util.logging.Level;
import l2j.commons.util.Rnd;
import l2j.gameserver.data.xml.SkillData;
import l2j.gameserver.handler.TargetHandler;
import l2j.gameserver.model.StatSet;
import l2j.gameserver.model.WorldObject;
import l2j.gameserver.model.actor.Creature;
import l2j.gameserver.model.actor.enums.creature.InstanceType;
import l2j.gameserver.model.effects.AbstractEffect;
import l2j.gameserver.model.events.EventType;
import l2j.gameserver.model.events.holders.actor.creature.OnCreatureDamageReceived;
import l2j.gameserver.model.events.listeners.ConsumerEventListener;
import l2j.gameserver.model.item.instance.Item;
import l2j.gameserver.model.skill.BuffInfo;
import l2j.gameserver.model.skill.Skill;
import l2j.gameserver.model.skill.SkillCaster;
import l2j.gameserver.model.skill.enums.SkillFinishType;
import l2j.gameserver.model.skill.holders.SkillHolder;
import l2j.gameserver.model.skill.targets.TargetType;
/**
* Trigger skill by damage received effect implementation.
* @author UnAfraid
*/
public class TriggerSkillByDamageReceived extends AbstractEffect
{
private final int _minAttackerLevel;
private final int _maxAttackerLevel;
private final int _minDamage;
private final int _chance;
private final int _hpPercent;
private final SkillHolder _skill;
private final TargetType _targetType;
private final InstanceType _attackerType;
private final int _skillLevelScaleTo;
private final List<SkillHolder> _triggerSkills;
public TriggerSkillByDamageReceived(StatSet params)
{
_minAttackerLevel = params.getInt("minAttackerLevel", 1);
_maxAttackerLevel = params.getInt("maxAttackerLevel", Integer.MAX_VALUE);
_minDamage = params.getInt("minDamage", 1);
_chance = params.getInt("chance", 100);
_hpPercent = params.getInt("hpPercent", 100);
_skill = new SkillHolder(params.getInt("skillId", 0), params.getInt("skillLevel", 1));
_targetType = params.getEnum("targetType", TargetType.class, TargetType.SELF);
_attackerType = params.getEnum("attackerType", InstanceType.class, InstanceType.Creature);
_skillLevelScaleTo = params.getInt("skillLevelScaleTo", 0);
// Specific skills by level.
final String triggerSkills = params.getString("triggerSkills", "");
if (triggerSkills.isEmpty())
{
_triggerSkills = null;
}
else
{
final String[] split = triggerSkills.split(";");
_triggerSkills = new ArrayList<>(split.length);
for (String skill : split)
{
final String[] splitSkill = skill.split(",");
_triggerSkills.add(new SkillHolder(Integer.parseInt(splitSkill[0]), Integer.parseInt(splitSkill[1])));
}
}
}
private void onDamageReceivedEvent(OnCreatureDamageReceived event)
{
LOGGER.info("🎯 TRIGGER SKILL BY DAMAGE RECEIVED - EVENT CAUGHT");
LOGGER.info("🎯 Effect Skill ID: " + _skill.getSkillId());
LOGGER.info("🎯 MinDamage: " + _minDamage + ", Chance: " + _chance);
LOGGER.info("🎯 AttackerType: " + _attackerType);
LOGGER.info("🎯 Actual Damage: " + event.getDamage());
if (event.isDamageOverTime() || (_chance == 0) || ((_triggerSkills == null) && ((_skill.getSkillId() == 0) || (_skill.getSkillLevel() == 0))))
{
LOGGER.info("❌ FAIL: DOT or invalid chance/skill");
return;
}
if (event.getAttacker() == event.getTarget())
{
LOGGER.info("❌ FAIL: self damage");
return;
}
if ((event.getAttacker().getLevel() < _minAttackerLevel) || (event.getAttacker().getLevel() > _maxAttackerLevel))
{
LOGGER.info("❌ FAIL: attacker level mismatch");
return;
}
if (_minDamage > 0 && event.getDamage() < _minDamage)
{
LOGGER.info("❌ FAIL: Damage too low - " + event.getDamage() + " < " + _minDamage);
return;
}
if ((_chance < 100) && (Rnd.get(100) > _chance))
{
return;
}
if ((_hpPercent < 100) && (event.getTarget().getCurrentHpPercent() > _hpPercent))
{
return;
}
if (!event.getAttacker().getInstanceType().isType(_attackerType))
{
return;
}
LOGGER.info("✅ ALL CHECKS PASSED for skill " + _skill.getSkillId());
WorldObject target = null;
try
{
target = TargetHandler.getInstance().getHandler(_targetType).getTarget(event.getTarget(), event.getAttacker(), _triggerSkills == null ? _skill.getSkill() : _triggerSkills.get(0).getSkill(), false, false, false);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Exception in ITargetTypeHandler.getTarget(): " + e.getMessage(), e);
}
if ((target == null) || !target.isCreature())
{
LOGGER.info("❌ FAIL: target is null or not creature");
return;
}
Skill triggerSkill = null;
if (_triggerSkills == null)
{
final BuffInfo buffInfo = target.asCreature().getEffectList().getBuffInfoBySkillId(_skill.getSkillId());
if ((_skillLevelScaleTo <= 0) || (buffInfo == null))
{
triggerSkill = _skill.getSkill();
}
else
{
triggerSkill = SkillData.getInstance().getSkill(_skill.getSkillId(), Math.min(_skillLevelScaleTo, buffInfo.getSkill().getLevel() + 1));
}
if ((buffInfo == null) || (buffInfo.getSkill().getLevel() < triggerSkill.getLevel()))
{
SkillCaster.triggerCast(event.getAttacker(), target.asCreature(), triggerSkill);
}
}
else // Multiple trigger skills.
{
final Iterator<SkillHolder> iterator = _triggerSkills.iterator();
while (iterator.hasNext())
{
final Skill nextSkill = iterator.next().getSkill();
if (target.asCreature().isAffectedBySkill(nextSkill.getId()))
{
if (iterator.hasNext())
{
target.asCreature().stopSkillEffects(SkillFinishType.SILENT, nextSkill.getId());
triggerSkill = iterator.next().getSkill();
break;
}
// Already at last skill.
return;
}
}
if (triggerSkill == null)
{
triggerSkill = _triggerSkills.get(0).getSkill();
}
SkillCaster.triggerCast(event.getAttacker(), target.asCreature(), triggerSkill);
}
}
@Override
public void onExit(Creature effector, Creature effected, Skill skill)
{
effected.removeListenerIf(EventType.ON_CREATURE_DAMAGE_RECEIVED, listener -> listener.getOwner() == this);
}
@Override
public void onStart(Creature effector, Creature effected, Skill skill, Item item)
{
effected.addListener(new ConsumerEventListener(effected, EventType.ON_CREATURE_DAMAGE_RECEIVED, (OnCreatureDamageReceived event) -> onDamageReceivedEvent(event), this));
}
}