lineage2.gameserver.model.Creature.java Source code

Java tutorial

Introduction

Here is the source code for lineage2.gameserver.model.Creature.java

Source

/*
 * 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 lineage2.gameserver.model;

import static lineage2.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import gnu.trove.set.hash.TIntHashSet;

import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReadWriteLock;
import java.util.concurrent.locks.ReentrantLock;
import java.util.concurrent.locks.ReentrantReadWriteLock;

import javolution.util.FastList;
import lineage2.commons.collections.LazyArrayList;
import lineage2.commons.lang.reference.HardReference;
import lineage2.commons.lang.reference.HardReferences;
import lineage2.commons.listener.Listener;
import lineage2.commons.threading.RunnableImpl;
import lineage2.commons.util.Rnd;
import lineage2.commons.util.concurrent.atomic.AtomicState;
import lineage2.gameserver.Config;
import lineage2.gameserver.ThreadPoolManager;
import lineage2.gameserver.ai.CharacterAI;
import lineage2.gameserver.ai.CtrlEvent;
import lineage2.gameserver.ai.CtrlIntention;
import lineage2.gameserver.ai.PlayableAI.nextAction;
import lineage2.gameserver.cache.Msg;
import lineage2.gameserver.geodata.GeoEngine;
import lineage2.gameserver.geodata.GeoMove;
import lineage2.gameserver.instancemanager.ReflectionManager;
import lineage2.gameserver.instancemanager.WorldStatisticsManager;
import lineage2.gameserver.model.GameObjectTasks.AltMagicUseTask;
import lineage2.gameserver.model.GameObjectTasks.CastEndTimeTask;
import lineage2.gameserver.model.GameObjectTasks.HitTask;
import lineage2.gameserver.model.GameObjectTasks.MagicLaunchedTask;
import lineage2.gameserver.model.GameObjectTasks.MagicUseTask;
import lineage2.gameserver.model.GameObjectTasks.NotifyAITask;
import lineage2.gameserver.model.Skill.SkillTargetType;
import lineage2.gameserver.model.Skill.SkillType;
import lineage2.gameserver.model.Zone.ZoneType;
import lineage2.gameserver.model.actor.listener.CharListenerList;
import lineage2.gameserver.model.actor.recorder.CharStatsChangeRecorder;
import lineage2.gameserver.model.base.InvisibleType;
import lineage2.gameserver.model.base.TeamType;
import lineage2.gameserver.model.entity.Reflection;
import lineage2.gameserver.model.instances.MinionInstance;
import lineage2.gameserver.model.instances.MonsterInstance;
import lineage2.gameserver.model.instances.NpcInstance;
import lineage2.gameserver.model.items.ItemInstance;
import lineage2.gameserver.model.pledge.Clan;
import lineage2.gameserver.model.quest.QuestEventType;
import lineage2.gameserver.model.quest.QuestState;
import lineage2.gameserver.model.reference.L2Reference;
import lineage2.gameserver.model.worldstatistics.CategoryType;
import lineage2.gameserver.network.serverpackets.ActionFail;
import lineage2.gameserver.network.serverpackets.Attack;
import lineage2.gameserver.network.serverpackets.AutoAttackStart;
import lineage2.gameserver.network.serverpackets.AutoAttackStop;
import lineage2.gameserver.network.serverpackets.ChangeMoveType;
import lineage2.gameserver.network.serverpackets.CharMoveToLocation;
import lineage2.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget;
import lineage2.gameserver.network.serverpackets.ExTeleportToLocationActivate;
import lineage2.gameserver.network.serverpackets.FlyToLocation;
import lineage2.gameserver.network.serverpackets.FlyToLocation.FlyType;
import lineage2.gameserver.network.serverpackets.L2GameServerPacket;
import lineage2.gameserver.network.serverpackets.MagicSkillCanceled;
import lineage2.gameserver.network.serverpackets.MagicSkillLaunched;
import lineage2.gameserver.network.serverpackets.MagicSkillUse;
import lineage2.gameserver.network.serverpackets.SetupGauge;
import lineage2.gameserver.network.serverpackets.StatusUpdate;
import lineage2.gameserver.network.serverpackets.StatusUpdate.StatusUpdateField;
import lineage2.gameserver.network.serverpackets.StopMove;
import lineage2.gameserver.network.serverpackets.SystemMessage;
import lineage2.gameserver.network.serverpackets.TeleportToLocation;
import lineage2.gameserver.network.serverpackets.ValidateLocation;
import lineage2.gameserver.network.serverpackets.components.CustomMessage;
import lineage2.gameserver.network.serverpackets.components.IStaticPacket;
import lineage2.gameserver.network.serverpackets.components.SystemMsg;
import lineage2.gameserver.skills.AbnormalEffect;
import lineage2.gameserver.skills.EffectType;
import lineage2.gameserver.skills.TimeStamp;
import lineage2.gameserver.skills.effects.EffectTemplate;
import lineage2.gameserver.stats.Calculator;
import lineage2.gameserver.stats.Env;
import lineage2.gameserver.stats.Formulas;
import lineage2.gameserver.stats.Formulas.AttackInfo;
import lineage2.gameserver.stats.StatFunctions;
import lineage2.gameserver.stats.StatTemplate;
import lineage2.gameserver.stats.Stats;
import lineage2.gameserver.stats.funcs.Func;
import lineage2.gameserver.stats.triggers.TriggerInfo;
import lineage2.gameserver.stats.triggers.TriggerType;
import lineage2.gameserver.taskmanager.LazyPrecisionTaskManager;
import lineage2.gameserver.taskmanager.RegenTaskManager;
import lineage2.gameserver.templates.CharTemplate;
import lineage2.gameserver.templates.item.WeaponTemplate;
import lineage2.gameserver.templates.item.WeaponTemplate.WeaponType;
import lineage2.gameserver.templates.spawn.WalkerRouteTemplate;
import lineage2.gameserver.utils.Location;
import lineage2.gameserver.utils.Log;
import lineage2.gameserver.utils.PositionUtils;

import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.StringUtils;
import org.napile.primitive.maps.IntObjectMap;
import org.napile.primitive.maps.impl.CHashIntObjectMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

/**
 * @author Mobius
 * @version $Revision: 1.0 $
 */
public abstract class Creature extends GameObject {
    /**
     * 
     */
    private static final long serialVersionUID = 1L;

    /**
     * @author Mobius
     */
    public class MoveNextTask extends RunnableImpl {
        /**
         * Field donedist.
         */
        /**
         * Field alldist.
         */
        private double alldist, donedist;

        /**
         * Method setDist.
         * @param dist double
         * @return MoveNextTask
         */
        public MoveNextTask setDist(double dist) {
            alldist = dist;
            donedist = 0.;
            return this;
        }

        /**
         * Method runImpl.
         */
        @Override
        public void runImpl() {
            if (!isMoving) {
                return;
            }
            moveLock.lock();
            try {
                if (!isMoving) {
                    return;
                }
                if (isMovementDisabled()) {
                    stopMove();
                    return;
                }
                Creature follow = null;
                int speed = getMoveSpeed();
                if (speed <= 0) {
                    stopMove();
                    return;
                }
                long now = System.currentTimeMillis();
                if (isFollow) {
                    follow = getFollowTarget();
                    if (follow == null) {
                        stopMove();
                        return;
                    }
                    if (isInRangeZ(follow, _offset) && GeoEngine.canSeeTarget(Creature.this, follow, false)) {
                        stopMove();
                        ThreadPoolManager.getInstance()
                                .execute(new NotifyAITask(Creature.this, CtrlEvent.EVT_ARRIVED_TARGET));
                        return;
                    }
                }
                if (alldist <= 0) {
                    moveNext(false);
                    return;
                }
                donedist += ((now - _startMoveTime) * _previousSpeed) / 1000.;
                double done = donedist / alldist;
                if (done < 0) {
                    done = 0;
                }
                if (done >= 1) {
                    moveNext(false);
                    return;
                }
                if (isMovementDisabled()) {
                    stopMove();
                    return;
                }
                Location loc = null;
                int index = (int) (moveList.size() * done);
                if (index >= moveList.size()) {
                    index = moveList.size() - 1;
                }
                if (index < 0) {
                    index = 0;
                }
                loc = moveList.get(index).clone().geo2world();
                if (!isFlying() && !isInBoat() && !isInWater() && !isBoat()) {
                    if ((loc.z - getZ()) > 256) {
                        String bug_text = "geo bug 1 at: " + getLoc() + " => " + loc.x + "," + loc.y + "," + loc.z
                                + "\tAll path: " + moveList.get(0) + " => " + moveList.get(moveList.size() - 1);
                        Log.add(bug_text, "geo");
                        stopMove();
                        return;
                    }
                }
                if ((loc == null) || isMovementDisabled()) {
                    stopMove();
                    return;
                }
                setLoc(loc, true);
                if (isMovementDisabled()) {
                    stopMove();
                    return;
                }
                if (isFollow && ((now - _followTimestamp) > (_forestalling ? 500 : 1000)) && (follow != null)
                        && !follow.isInRange(movingDestTempPos, Math.max(100, _offset))) {
                    if ((Math.abs(getZ() - loc.z) > 1000) && !isFlying()) {
                        sendPacket(SystemMsg.CANNOT_SEE_TARGET);
                        stopMove();
                        return;
                    }
                    if (buildPathTo(follow.getX(), follow.getY(), follow.getZ(), _offset, follow, true, true)) {
                        movingDestTempPos.set(follow.getX(), follow.getY(), follow.getZ());
                    } else {
                        stopMove();
                        return;
                    }
                    moveNext(true);
                    return;
                }
                _previousSpeed = speed;
                _startMoveTime = now;
                _moveTask = ThreadPoolManager.getInstance().schedule(this, getMoveTickInterval());
            } catch (Exception e) {
                _log.error("", e);
            } finally {
                moveLock.unlock();
            }
        }
    }

    /**
     * Field _log.
     */
    private static final Logger _log = LoggerFactory.getLogger(Creature.class);
    /**
     * Field HEADINGS_IN_PI. (value is 10430.378350470453)
     */
    public static final double HEADINGS_IN_PI = 10430.378350470452724949566316381;
    /**
     * Field INTERACTION_DISTANCE. (value is 200)
     */
    public static final int INTERACTION_DISTANCE = 200;
    /**
     * Field _castingSkill.
     */
    private Skill _castingSkill;
    /**
     * Field _castInterruptTime.
     */
    private long _castInterruptTime;
    /**
     * Field _animationEndTime.
     */
    private long _animationEndTime;
    /**
     * Field _scheduledCastCount.
     */
    public int _scheduledCastCount;
    /**
     * Field _scheduledCastInterval.
     */
    public int _scheduledCastInterval;
    /**
     * Field _skillTask.
     */
    public Future<?> _skillTask;
    /**
     * Field _skillLaunchedTask.
     */
    public Future<?> _skillLaunchedTask;
    /**
     * Field _skillDoubleTask.
     */
    public Future<?> _skillDoubleTask;
    /**
     * Field _skillDoubleLaunchedTask.
     */
    public Future<?> _skillDoubleLaunchedTask;
    /**
     * Field _stanceTask.
     */
    private Future<?> _stanceTask;
    /**
     * Field _stanceTaskRunnable.
     */
    private Runnable _stanceTaskRunnable;
    /**
     * Field _stanceEndTime.
     */
    private long _stanceEndTime;
    /**
     * Field CLIENT_BAR_SIZE. (value is 352)
     */
    public final static int CLIENT_BAR_SIZE = 352;
    /**
     * Field _lastCpBarUpdate.
     */
    private int _lastCpBarUpdate = -1;
    /**
     * Field _lastHpBarUpdate.
     */
    private int _lastHpBarUpdate = -1;
    /**
     * Field _lastMpBarUpdate.
     */
    private int _lastMpBarUpdate = -1;
    /**
     * Field _currentCp.
     */
    protected double _currentCp = 0;
    /**
     * Field _currentHp.
     */
    protected double _currentHp = 1;
    /**
     * Field _currentMp.
     */
    protected double _currentMp = 1;
    /**
     * Field _abnormalEffects.
     */
    private int _abnormalEffects;
    /**
     * Field _abnormalEffects2.
     */
    private int _abnormalEffects2;
    /**
     * Field _abnormalEffects3.
     */
    private int _abnormalEffects3;
    /**
     * Field _isAttackAborted.
     */

    private final FastList<Integer> _aveList = new FastList<Integer>();

    protected boolean _isAttackAborted;
    /**
     * Field _attackEndTime.
     */
    protected long _attackEndTime;
    /**
     * Field _attackReuseEndTime.
     */
    protected long _attackReuseEndTime;
    /**
     * Field _poleAttackCount.
     */
    private int _poleAttackCount = 0;
    /**
     * Field POLE_VAMPIRIC_MOD.
     */
    private static final double[] POLE_VAMPIRIC_MOD = { 1, 0.9, 0, 7, 0.2, 0.01 };
    /**
     * Field _skills.
     */
    protected final Map<Integer, Skill> _skills = new ConcurrentSkipListMap<Integer, Skill>();
    /**
     * Field _triggers.
     */
    protected Map<TriggerType, Set<TriggerInfo>> _triggers;
    /**
     * Field _skillReuses.
     */
    protected final IntObjectMap<TimeStamp> _skillReuses = new CHashIntObjectMap<TimeStamp>();
    /**
     * Field _effectList.
     */
    protected volatile EffectList _effectList;
    /**
     * Field _statsRecorder.
     */
    protected volatile CharStatsChangeRecorder<? extends Creature> _statsRecorder;
    /**
     * Field _blockedStats.
     */
    private List<Stats> _blockedStats;
    /**
     * Field isDead.
     */
    protected AtomicBoolean isDead = new AtomicBoolean();
    /**
     * Field isTeleporting.
     */
    protected AtomicBoolean isTeleporting = new AtomicBoolean();
    /**
     * Field _skillMastery.
     */
    private Map<Integer, Integer> _skillMastery;
    /**
     * Field _isInvul.
     */
    protected boolean _isInvul;
    /**
     * Field _fakeDeath.
     */
    private boolean _fakeDeath;
    /**
     * Field _isBlessedByNoblesse.
     */
    private boolean _isBlessedByNoblesse;
    /**
     * Field _isSalvation.
     */
    private boolean _isSalvation;
    /**
     * Field _meditated.
     */
    private boolean _meditated;
    /**
     * Field _lockedTarget.
     */
    private boolean _lockedTarget;
    /**
     * Field _isTargetable.
     */
    private boolean _isTargetable = true;
    /**
     * Field _blocked.
     */
    private boolean _blocked;
    /**
     * Field _afraid.
     */
    private final AtomicState _afraid = new AtomicState();
    /**
     * Field _muted.
     */
    private final AtomicState _muted = new AtomicState();
    /**
     * Field _pmuted.
     */
    private final AtomicState _pmuted = new AtomicState();
    /**
     * Field _amuted.
     */
    private final AtomicState _amuted = new AtomicState();
    /**
     * Field _pulled.
     */
    private final AtomicState _pulled = new AtomicState();
    /**
     * Field _airbinded.
     */
    private final AtomicState _airbinded = new AtomicState();
    /**
     * Field _knockbacked.
     */
    private final AtomicState _knockedback = new AtomicState();
    /**
     * Field _knockdowned.
     */
    private final AtomicState _knockeddown = new AtomicState();
    /**
     * Field _paralyzed.
     */
    private final AtomicState _paralyzed = new AtomicState();
    /**
     * Field _rooted.
     */
    private final AtomicState _rooted = new AtomicState();
    /**
     * Field _sleeping.
     */
    private final AtomicState _sleeping = new AtomicState();
    /**
     * Field _stunned.
     */
    private final AtomicState _stunned = new AtomicState();
    /**
     * Field _immobilized.
     */
    private final AtomicState _immobilized = new AtomicState();
    /**
     * Field _confused.
     */
    private final AtomicState _confused = new AtomicState();
    /**
     * Field _frozen.
     */
    private final AtomicState _frozen = new AtomicState();
    /**
     * Field _healBlocked.
     */
    private final AtomicState _healBlocked = new AtomicState();
    /**
     * Field _damageBlocked.
     */
    private final AtomicState _damageBlocked = new AtomicState();
    /**
     * Field _buffImmunity.
     */
    private final AtomicState _buffImmunity = new AtomicState();
    /**
     * Field _debuffImmunity.
     */
    private final AtomicState _debuffImmunity = new AtomicState();
    /**
     * Field _effectImmunity.
     */
    private final AtomicState _effectImmunity = new AtomicState();
    /**
     * Field _weaponEquipBlocked.
     */
    private final AtomicState _weaponEquipBlocked = new AtomicState();
    /**
     * Field _flying.
     */
    private boolean _flying;
    /**
     * Field _running.
     */
    private boolean _running;
    /**
     * Field isMoving.
     */
    public boolean isMoving;
    /**
     * Field isFollow.
     */
    public boolean isFollow;
    /**
     * Field moveLock.
     */
    final Lock moveLock = new ReentrantLock();
    /**
     * Field _moveTask.
     */
    Future<?> _moveTask;
    /**
     * Field _moveTaskRunnable.
     */
    private MoveNextTask _moveTaskRunnable;
    /**
     * Field moveList.
     */
    List<Location> moveList;
    /**
     * Field destination.
     */
    private Location destination;
    /**
     * Field movingDestTempPos.
     */
    final Location movingDestTempPos = new Location();
    /**
     * Field _offset.
     */
    int _offset;
    /**
     * Field _forestalling.
     */
    boolean _forestalling;
    /**
     * Field target.
     */
    private volatile HardReference<? extends GameObject> target = HardReferences.emptyRef();
    /**
     * Field castingTarget.
     */
    private volatile HardReference<? extends Creature> castingTarget = HardReferences.emptyRef();
    /**
     * Field followTarget.
     */
    private volatile HardReference<? extends Creature> followTarget = HardReferences.emptyRef();
    /**
     * Field _aggressionTarget.
     */
    private volatile HardReference<? extends Creature> _aggressionTarget = HardReferences.emptyRef();
    /**
     * Field _targetRecorder.
     */
    private final List<List<Location>> _targetRecorder = new ArrayList<List<Location>>();
    /**
     * Field _followTimestamp.
     */
    long _followTimestamp;
    /**
     * Field _startMoveTime.
     */
    long _startMoveTime;
    /**
     * Field _previousSpeed.
     */
    int _previousSpeed = 0;
    /**
     * Field _heading.
     */
    private int _heading;
    /**
     * Field _calculators.
     */
    private final Calculator[] _calculators;
    /**
     * Field _template.
     */
    protected CharTemplate _template;
    /**
     * Field _ai.
     */
    protected volatile CharacterAI _ai;
    /**
     * Field _name.
     */
    protected String _name;
    /**
     * Field _title.
     */
    protected String _title;
    /**
     * Field _team.
     */
    protected TeamType _team = TeamType.NONE;
    /**
     * Field _isRegenerating.
     */
    private boolean _isRegenerating;
    /**
     * Field regenLock.
     */
    final Lock regenLock = new ReentrantLock();
    /**
     * Field _regenTask.
     */
    private Future<?> _regenTask;
    /**
     * Field _regenTaskRunnable.
     */
    private Runnable _regenTaskRunnable;
    /**
     * Field _isEnabledDoubleCast.
     */
    public boolean _isEnabledDoubleCast = false;
    /**
     * Field _isKnockedDown.
     */
    public boolean _isKnockedDown = false;
    /**
     * Field _isKnockedDown.
     */
    public boolean _isAirBind = false;
    /**
     * Field _zones.
     */
    private final List<Zone> _zones = new LazyArrayList<Zone>();
    /**
     * Field zonesLock.
     */
    private final ReadWriteLock zonesLock = new ReentrantReadWriteLock();
    /**
     * Field zonesRead.
     */
    private final Lock zonesRead = zonesLock.readLock();
    /**
     * Field zonesWrite.
     */
    private final Lock zonesWrite = zonesLock.writeLock();
    /**
     * Field listeners.
     */
    protected volatile CharListenerList listeners;
    /**
     * Field _deathImmune.
     */
    protected boolean _deathImmune = false;

    private List<Player> _statusListeners;
    private final Lock statusListenersLock = new ReentrantLock();

    /**
     * Field _walkerRoutesTemplate.
     */
    protected WalkerRouteTemplate _walkerRoutesTemplate = null;
    /**
     * Field _storedId.
     */
    protected Long _storedId;

    /**
     * Method getStoredId.
     * @return Long
     */
    public final Long getStoredId() {
        return _storedId;
    }

    /**
     * Field reference.
     */
    protected HardReference<? extends Creature> reference;
    /**
     * Field _transformationId.
     */
    private int _transformationId;
    /**
     * Field _transformationTemplate.
     */
    private int _transformationTemplate;
    /**
     * Field _transformationName.
     */
    private String _transformationName;

    /**
     * Constructor for Creature.
     * @param objectId int
     * @param template CharTemplate
     */
    public Creature(int objectId, CharTemplate template) {
        super(objectId);
        _template = template;
        _calculators = new Calculator[Stats.NUM_STATS];
        StatFunctions.addPredefinedFuncs(this);
        reference = new L2Reference<>(this);
        _storedId = GameObjectsStorage.put(this);
    }

    /**
     * Method getRef.
     * @return HardReference<? extends Creature>
     */
    @Override
    public HardReference<? extends Creature> getRef() {
        return reference;
    }

    /**
     * Method isAttackAborted.
     * @return boolean
     */
    public boolean isAttackAborted() {
        return _isAttackAborted;
    }

    /**
     * Method abortAttack.
     * @param force boolean
     * @param message boolean
     */
    public final void abortAttack(boolean force, boolean message) {
        if (isAttackingNow()) {
            _attackEndTime = 0;
            if (force) {
                _isAttackAborted = true;
            }
            getAI().setIntention(AI_INTENTION_ACTIVE);
            if (isPlayer() && message) {
                sendActionFailed();
                sendPacket(new SystemMessage(SystemMessage.C1S_ATTACK_FAILED).addName(this));
            }
        }
    }

    /**
     * Method abortCast.
     * @param force boolean
     * @param message boolean
     */
    public final void abortCast(boolean force, boolean message) {
        if (isCastingNow() && (force || canAbortCast())) {
            final Skill castingSkill = _castingSkill;
            final Future<?> skillTask = _skillTask;
            final Future<?> skillLaunchedTask = _skillLaunchedTask;
            final Future<?> skillDoubleTask = _skillDoubleTask;
            final Future<?> skillDoubleLaunchedTask = _skillDoubleLaunchedTask;
            finishFly();
            clearCastVars();
            if (skillTask != null) {
                skillTask.cancel(false);
            }
            if (skillLaunchedTask != null) {
                skillLaunchedTask.cancel(false);
            }
            if (skillDoubleTask != null) {
                skillDoubleTask.cancel(false);
            }
            if (skillDoubleLaunchedTask != null) {
                skillDoubleLaunchedTask.cancel(false);
            }
            if (castingSkill != null) {
                if (castingSkill.isUsingWhileCasting()) {
                    Creature target = getAI().getAttackTarget();
                    if (target != null) {
                        target.getEffectList().stopEffect(castingSkill.getId());
                    }
                }
                removeSkillMastery(castingSkill.getId());
            }
            broadcastPacket(new MagicSkillCanceled(getObjectId()));
            getAI().setIntention(AI_INTENTION_ACTIVE);
            if (isPlayer() && message) {
                sendPacket(Msg.CASTING_HAS_BEEN_INTERRUPTED);
            }
        }
    }

    /**
     * Method canAbortCast.
     * @return boolean
     */
    public final boolean canAbortCast() {
        return _castInterruptTime > System.currentTimeMillis();
    }

    /**
     * Method absorbAndReflect.
     * @param target Creature
     * @param skill Skill
     * @param damage double
     * @return boolean
     */
    public boolean absorbAndReflect(Creature target, Skill skill, double damage) {
        if (target.isDead()) {
            return false;
        }
        boolean bow = (getActiveWeaponItem() != null) && ((getActiveWeaponItem().getItemType() == WeaponType.BOW)
                || (getActiveWeaponItem().getItemType() == WeaponType.CROSSBOW));
        double value = 0;
        if ((skill != null) && skill.isMagic()) {
            value = target.calcStat(Stats.REFLECT_AND_BLOCK_MSKILL_DAMAGE_CHANCE, 0, this, skill);
        } else if ((skill != null) && (skill.getCastRange() <= 200)) {
            value = target.calcStat(Stats.REFLECT_AND_BLOCK_PSKILL_DAMAGE_CHANCE, 0, this, skill);
        } else if ((skill == null) && !bow) {
            value = target.calcStat(Stats.REFLECT_AND_BLOCK_DAMAGE_CHANCE, 0, this, null);
        }
        if ((value > 0) && Rnd.chance(value)) {
            reduceCurrentHp(damage, 0, target, null, true, true, false, false, false, false, true);
            return true;
        }
        if ((skill != null) && skill.isMagic()) {
            value = target.calcStat(Stats.REFLECT_MSKILL_DAMAGE_PERCENT, 0, this, skill)
                    - this.calcStat(Stats.REFLECT_RESISTANCE_PERCENT, 0., target, skill);
        } else if ((skill != null) && (skill.getCastRange() <= 200)) {
            value = target.calcStat(Stats.REFLECT_PSKILL_DAMAGE_PERCENT, 0, this, skill)
                    - this.calcStat(Stats.REFLECT_RESISTANCE_PERCENT, 0., target, skill);
        } else if ((skill == null) && !bow) {
            value = target.calcStat(Stats.REFLECT_DAMAGE_PERCENT, 0, this, null)
                    - this.calcStat(Stats.REFLECT_RESISTANCE_PERCENT, 0., target, null);
        }
        if (value > 0) {
            if ((target.getCurrentHp() + target.getCurrentCp()) > damage) {
                reduceCurrentHp((value / 100.) * damage, 0, target, null, true, true, false, false, false, false,
                        true);
            }
        }
        if ((skill != null || bow) && Config.ALT_ABSORB_DAMAGE_ONLY_MEELE) {
            return false;
        }
        damage = (int) (damage - target.getCurrentCp());
        if (damage <= 0) {
            return false;
        }
        final double poleMod = _poleAttackCount < POLE_VAMPIRIC_MOD.length ? POLE_VAMPIRIC_MOD[_poleAttackCount]
                : 0;
        double absorb = poleMod * calcStat(Stats.ABSORB_DAMAGE_PERCENT, 0, target, null);
        double limit;
        if ((absorb > 0) && !target.isDamageBlocked()) {
            limit = (calcStat(Stats.HP_LIMIT, null, null) * getMaxHp()) / 100.;
            if (getCurrentHp() < limit) {
                setCurrentHp(Math.min(_currentHp + ((damage * absorb * Config.ALT_ABSORB_DAMAGE_MODIFIER) / 100.),
                        limit), false);
            }
        }
        absorb = poleMod * calcStat(Stats.ABSORB_DAMAGEMP_PERCENT, 0, target, null);
        if ((absorb > 0) && !target.isDamageBlocked()) {
            limit = (calcStat(Stats.MP_LIMIT, null, null) * getMaxMp()) / 100.;
            if (getCurrentMp() < limit) {
                setCurrentMp(Math.min(_currentMp + ((damage * absorb * Config.ALT_ABSORB_DAMAGE_MODIFIER) / 100.),
                        limit));
            }
        }
        return false;
    }

    /**
     * Method absorbToEffector.
     * @param attacker Creature
     * @param damage double
     * @return double
     */
    public double absorbToEffector(Creature attacker, double damage) {
        double transferToEffectorDam = calcStat(Stats.TRANSFER_TO_EFFECTOR_DAMAGE_PERCENT, 0.);
        if (transferToEffectorDam > 0) {
            Effect effect = getEffectList().getEffectByType(EffectType.AbsorbDamageToEffector);
            if (effect == null) {
                return damage;
            }
            Creature effector = effect.getEffector();
            if ((effector == this) || effector.isDead() || !isInRange(effector, 1200)) {
                return damage;
            }
            Player thisPlayer = getPlayer();
            Player effectorPlayer = effector.getPlayer();
            if ((thisPlayer != null) && (effectorPlayer != null)) {
                if ((thisPlayer != effectorPlayer) && (!thisPlayer.isOnline() || !thisPlayer.isInParty()
                        || (thisPlayer.getParty() != effectorPlayer.getParty()))) {
                    return damage;
                }
            } else {
                return damage;
            }
            double transferDamage = (damage * transferToEffectorDam) * .01;
            damage -= transferDamage;
            effector.reduceCurrentHp(transferDamage, 0, effector, null, false, false, !attacker.isPlayable(), false,
                    true, false, true);
        }
        return damage;
    }

    /**
     * Method absorbToMp.
     * @param attacker Creature
     * @param damage double
     * @return double
     */
    public double absorbToMp(Creature attacker, double damage) {
        double transferToMpDamPercent = calcStat(Stats.TRANSFER_TO_MP_DAMAGE_PERCENT, 0.);
        if (transferToMpDamPercent > 0) {
            double transferDamage = (damage * transferToMpDamPercent) * .01;
            double currentMp = getCurrentMp();
            if (currentMp > transferDamage) {
                setCurrentMp(getCurrentMp() - transferDamage);
                return 0;
            }
            if (currentMp > 0) {
                damage -= currentMp;
                setCurrentMp(0);
                sendPacket(SystemMsg.MP_BECAME_0_AND_THE_ARCANE_SHIELD_IS_DISAPPEARING);
            }
            getEffectList().stopEffects(EffectType.AbsorbDamageToMp);
            return damage;
        }
        return damage;
    }

    /**
     * Method absorbToSummon.
     * @param attacker Creature
     * @param damage double
     * @return double
     */
    public double absorbToSummon(Creature attacker, double damage) {
        if (!isPlayer()) {
            return damage;
        }
        double transferToSummonDam = calcStat(Stats.TRANSFER_TO_SUMMON_DAMAGE_PERCENT, 0.);
        if (transferToSummonDam > 0) {
            //TRANSFER DAMAGE ALWAYS TO THE FIRST SUMMON
            Summon summon = null;
            List<Summon> servitors = ((Player) this).getSummonList().getServitors();
            double transferDamage = (damage * transferToSummonDam) * .01;
            if (servitors.size() > 0) {
                summon = servitors.get(0);
            }
            if (summon != null && !summon.isDead() && summon.getCurrentHp() > transferDamage
                    && summon.isInRangeZ(this, 1200)) {
                damage -= transferDamage;
                summon.reduceCurrentHp(transferDamage, 0, summon, null, false, false, false, false, true, false,
                        true);
            } else {
                getEffectList().stopEffects(EffectType.AbsorbDamageToSummon);
                return damage;
            }
            /* 
            List<Summon> servitors = ((Player) this).getSummonList().getServitors();
            double transferDamage = (damage * transferToSummonDam) * .01;
            for (Iterator<Summon> it = servitors.iterator(); it.hasNext();)
            {
               Summon summon = it.next();
               if ((summon == null) || summon.isDead() || (summon.getCurrentHp() < (transferDamage / servitors.size())))
               {
                  it.remove();
               }
            }
            if (servitors.size() > 0)
            {
               for (Summon summon : servitors)
               {
                  if (summon.isInRangeZ(this, 1200))
                  {
              damage -= transferDamage;
              summon.reduceCurrentHp(transferDamage, 0, summon, null, false, false, false, false, true, false, true);
                  }
               }
            }
            else
            {
               getEffectList().stopEffects(EffectType.AbsorbDamageToSummon);
               return damage;
            }
            */
        }
        return damage;
    }

    /**
     * Method addBlockStats.
     * @param stats List<Stats>
     */
    public void addBlockStats(List<Stats> stats) {
        if (_blockedStats == null) {
            _blockedStats = new ArrayList<Stats>();
        }
        _blockedStats.addAll(stats);
    }

    /**
     * Method addSkill.
     * @param newSkill Skill
     * @return Skill
     */
    public Skill addSkill(Skill newSkill) {
        if (newSkill == null) {
            return null;
        }
        Skill oldSkill = _skills.get(newSkill.getId());
        if ((oldSkill != null) && (oldSkill.getLevel() == newSkill.getLevel())) {
            return newSkill;
        }
        _skills.put(newSkill.getId(), newSkill);
        if (oldSkill != null) {
            removeStatsOwner(oldSkill);
            removeTriggers(oldSkill);
        }
        addTriggers(newSkill);
        addStatFuncs(newSkill.getStatFuncs());
        return oldSkill;
    }

    /**
     * Method getCalculators.
     * @return Calculator[]
     */
    public Calculator[] getCalculators() {
        return _calculators;
    }

    /**
     * Method addStatFunc.
     * @param f Func
     */
    public final void addStatFunc(Func f) {
        if (f == null) {
            return;
        }
        int stat = f.stat.ordinal();
        synchronized (_calculators) {
            if (_calculators[stat] == null) {
                _calculators[stat] = new Calculator(f.stat, this);
            }
            _calculators[stat].addFunc(f);
        }
    }

    /**
     * Method addStatFuncs.
     * @param funcs Func[]
     */
    public final void addStatFuncs(Func[] funcs) {
        for (Func f : funcs) {
            addStatFunc(f);
        }
    }

    /**
     * Method removeStatFunc.
     * @param f Func
     */
    public final void removeStatFunc(Func f) {
        if (f == null) {
            return;
        }
        int stat = f.stat.ordinal();
        synchronized (_calculators) {
            if (_calculators[stat] != null) {
                _calculators[stat].removeFunc(f);
            }
        }
    }

    /**
     * Method removeStatFuncs.
     * @param funcs Func[]
     */
    public final void removeStatFuncs(Func[] funcs) {
        for (Func f : funcs) {
            removeStatFunc(f);
        }
    }

    /**
     * Method removeStatsOwner.
     * @param owner Object
     */
    public final void removeStatsOwner(Object owner) {
        synchronized (_calculators) {
            for (Calculator _calculator : _calculators) {
                if (_calculator != null) {
                    _calculator.removeOwner(owner);
                }
            }
        }
    }

    /**
     * Method altOnMagicUseTimer.
     * @param aimingTarget Creature
     * @param skill Skill
     */
    public void altOnMagicUseTimer(Creature aimingTarget, Skill skill) {
        if (isAlikeDead()) {
            return;
        }
        int magicId = skill.getDisplayId();
        int level = Math.max(1, getSkillDisplayLevel(skill.getId()));
        List<Creature> targets = skill.getTargets(this, aimingTarget, true);
        broadcastPacket(new MagicSkillLaunched(getObjectId(), magicId, level, targets));
        double mpConsume2 = skill.getMpConsume2();
        if (mpConsume2 > 0) {
            if (_currentMp < mpConsume2) {
                sendPacket(Msg.NOT_ENOUGH_MP);
                return;
            }
            if (skill.isMagic()) {
                reduceCurrentMp(calcStat(Stats.MP_MAGIC_SKILL_CONSUME, mpConsume2, aimingTarget, skill), null);
            } else {
                reduceCurrentMp(calcStat(Stats.MP_PHYSICAL_SKILL_CONSUME, mpConsume2, aimingTarget, skill), null);
            }
        }
        callSkill(skill, targets, false);
    }

    /**
     * Method altUseSkill.
     * @param skill Skill
     * @param target Creature
     */
    public void altUseSkill(Skill skill, Creature target) {
        if (skill == null) {
            return;
        }
        int magicId = skill.getId();
        if (isUnActiveSkill(magicId)) {
            return;
        }
        if (isSkillDisabled(skill)) {
            sendReuseMessage(skill);
            return;
        }
        if (target == null) {
            target = skill.getAimingTarget(this, getTarget());
            if (target == null) {
                return;
            }
        }
        getListeners().onMagicUse(skill, target, true);
        int itemConsume[] = skill.getItemConsume();
        if (itemConsume[0] > 0) {
            for (int i = 0; i < itemConsume.length; i++) {
                if (!consumeItem(skill.getItemConsumeId()[i], itemConsume[i])) {
                    sendPacket(skill.isHandler() ? SystemMsg.INCORRECT_ITEM_COUNT
                            : SystemMsg.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL);
                    return;
                }
            }
        }
        if (skill.getReferenceItemId() > 0) {
            if (!consumeItemMp(skill.getReferenceItemId(), skill.getReferenceItemMpConsume())) {
                return;
            }
        }
        if (skill.getSoulsConsume() > getConsumedSouls()) {
            sendPacket(Msg.THERE_IS_NOT_ENOUGHT_SOUL);
            return;
        }
        if (skill.getEnergyConsume() > getAgathionEnergy()) {
            sendPacket(SystemMsg.THE_SKILL_HAS_BEEN_CANCELED_BECAUSE_YOU_HAVE_INSUFFICIENT_ENERGY);
            return;
        }
        if (skill.getSoulsConsume() > 0) {
            setConsumedSouls(getConsumedSouls() - skill.getSoulsConsume(), null);
        }
        if (skill.getEnergyConsume() > 0) {
            setAgathionEnergy(getAgathionEnergy() - skill.getEnergyConsume());
        }
        int level = Math.max(1, getSkillDisplayLevel(magicId));
        Formulas.calcSkillMastery(skill, this);
        long reuseDelay = Formulas.calcSkillReuseDelay(this, skill);
        if (!skill.isToggle() || skill.isAwakeningToggle()) {
            broadcastPacket(
                    new MagicSkillUse(this, target, skill.getDisplayId(), level, skill.getHitTime(), reuseDelay));
        }
        if (!skill.isHideUseMessage()) {
            if (skill.getSkillType() == SkillType.PET_SUMMON) {
                sendPacket(new SystemMessage(SystemMessage.SUMMON_A_PET));
            } else if (!skill.isHandler()) {
                sendPacket(new SystemMessage(SystemMessage.YOU_USE_S1).addSkillName(magicId, level));
            } else {
                sendPacket(new SystemMessage(SystemMessage.YOU_USE_S1).addItemName(skill.getItemConsumeId()[0]));
            }
        }
        if (!skill.isHandler()) {
            disableSkill(skill, reuseDelay);
        }
        ThreadPoolManager.getInstance().schedule(new AltMagicUseTask(this, target, skill), skill.getHitTime());
    }

    /**
     * Method sendReuseMessage.
     * @param skill Skill
     */
    public void sendReuseMessage(Skill skill) {
    }

    /**
     * Method broadcastPacket.
     * @param packets L2GameServerPacket[]
     */
    public void broadcastPacket(L2GameServerPacket... packets) {
        sendPacket(packets);
        broadcastPacketToOthers(packets);
    }

    /**
     * Method broadcastPacket.
     * @param packets List<L2GameServerPacket>
     */
    public void broadcastPacket(List<L2GameServerPacket> packets) {
        sendPacket(packets);
        broadcastPacketToOthers(packets);
    }

    /**
     * Method broadcastPacketToOthers.
     * @param packets L2GameServerPacket[]
     */
    public void broadcastPacketToOthers(L2GameServerPacket... packets) {
        if (!isVisible() || (packets.length == 0)) {
            return;
        }
        List<Player> players = World.getAroundPlayers(this);
        Player target;
        for (int i = 0; i < players.size(); i++) {
            target = players.get(i);
            target.sendPacket(packets);
        }
    }

    /**
     * Method broadcastPacketToOthers.
     * @param packets List<L2GameServerPacket>
     */
    public void broadcastPacketToOthers(List<L2GameServerPacket> packets) {
        if (!isVisible() || packets.isEmpty()) {
            return;
        }
        List<Player> players = World.getAroundPlayers(this);
        Player target;
        for (int i = 0; i < players.size(); i++) {
            target = players.get(i);
            target.sendPacket(packets);
        }
    }

    public void broadcastToStatusListeners(L2GameServerPacket... packets) {
        if (!isVisible() || (packets.length == 0)) {
            return;
        }

        statusListenersLock.lock();

        try {
            if ((_statusListeners == null) || _statusListeners.isEmpty()) {
                return;
            }

            Player player;

            for (int i = 0; i < _statusListeners.size(); i++) {
                player = _statusListeners.get(i);

                player.sendPacket(packets);
            }
        } finally {
            statusListenersLock.unlock();
        }
    }

    public void addStatusListener(Player cha) {
        if (cha == this) {
            return;
        }

        statusListenersLock.lock();

        try {
            if (_statusListeners == null) {
                _statusListeners = new LazyArrayList<Player>();
            }

            if (!_statusListeners.contains(cha)) {
                _statusListeners.add(cha);
            }
        } finally {
            statusListenersLock.unlock();
        }
    }

    public void removeStatusListener(Creature cha) {
        statusListenersLock.lock();

        try {
            if (_statusListeners == null) {
                return;
            }

            _statusListeners.remove(cha);
        } finally {
            statusListenersLock.unlock();
        }
    }

    public void clearStatusListeners() {
        statusListenersLock.lock();

        try {
            if (_statusListeners == null) {
                return;
            }

            _statusListeners.clear();
        } finally {
            statusListenersLock.unlock();
        }
    }

    /**
     * Method broadcastStatusUpdate.
     */
    public void broadcastStatusUpdate() {
        if (!needStatusUpdate()) {
            return;
        }
        StatusUpdate statusUpdatePacket = new StatusUpdate(this).addAttribute(StatusUpdateField.CUR_HP,
                StatusUpdateField.MAX_HP);

        for (final Player pla : World.getAroundPlayers(this)) {
            if (pla == null) {
                continue;
            }
            pla.sendPacket(statusUpdatePacket);
        }
    }

    /**
     * Method calcHeading.
     * @param x_dest int
     * @param y_dest int
     * @return int
     */
    public int calcHeading(int x_dest, int y_dest) {
        return (int) (Math.atan2(getY() - y_dest, getX() - x_dest) * HEADINGS_IN_PI) + 32768;
    }

    /**
     * Method calcStat.
     * @param stat Stats
     * @param init double
     * @return double
     */
    public final double calcStat(Stats stat, double init) {
        return calcStat(stat, init, null, null);
    }

    /**
     * Method calcStat.
     * @param stat Stats
     * @param init double
     * @param target Creature
     * @param skill Skill
     * @return double
     */
    public final double calcStat(Stats stat, double init, Creature target, Skill skill) {
        int id = stat.ordinal();
        Calculator c = _calculators[id];
        if (c == null) {
            return init;
        }
        Env env = new Env();
        env.character = this;
        env.target = target;
        env.skill = skill;
        env.value = init;
        c.calc(env);
        return env.value;
    }

    /**
     * Method calcStat.
     * @param stat Stats
     * @param target Creature
     * @param skill Skill
     * @return double
     */
    public final double calcStat(Stats stat, Creature target, Skill skill) {
        Env env = new Env(this, target, skill);
        env.value = stat.getInit();
        int id = stat.ordinal();
        Calculator c = _calculators[id];
        if (c != null) {
            c.calc(env);
        }
        return env.value;
    }

    /**
     * Method calculateAttackDelay.
     * @return int
     */
    public int calculateAttackDelay() {
        return Formulas.calcPAtkSpd(getPAtkSpd());
    }

    /**
     * Method callSkill.
     * @param skill Skill
     * @param targets List<Creature>
     * @param useActionSkills boolean
     */
    public void callSkill(Skill skill, List<Creature> targets, boolean useActionSkills) {
        try {
            if (useActionSkills && !skill.isUsingWhileCasting() && (_triggers != null)) {
                if (skill.isOffensive()) {
                    if (skill.isMagic()) {
                        useTriggers(getTarget(), TriggerType.OFFENSIVE_MAGICAL_SKILL_USE, null, skill, 0);
                    } else {
                        useTriggers(getTarget(), TriggerType.OFFENSIVE_PHYSICAL_SKILL_USE, null, skill, 0);
                    }
                } else if (skill.isMagic()) {
                    final boolean targetSelf = skill.isAoE() || skill.isNotTargetAoE()
                            || (skill.getTargetType() == Skill.SkillTargetType.TARGET_SELF);
                    useTriggers(targetSelf ? this : getTarget(), TriggerType.SUPPORT_MAGICAL_SKILL_USE, null, skill,
                            0);
                }
            }
            Player pl = getPlayer();
            Creature target;
            Iterator<Creature> itr = targets.iterator();
            while (itr.hasNext()) {
                target = itr.next();
                if (skill.isOffensive() && target.isInvul()) {
                    Player pcTarget = target.getPlayer();
                    if ((!skill.isIgnoreInvul() || ((pcTarget != null) && pcTarget.isGM()))
                            && !target.isArtefact()) {
                        itr.remove();
                        continue;
                    }
                }
                Effect ie = target.getEffectList().getEffectByType(EffectType.IgnoreSkill);
                if (ie != null) {
                    if (ArrayUtils.contains(ie.getTemplate().getParam().getIntegerArray("skillId"),
                            skill.getId())) {
                        itr.remove();
                        continue;
                    }
                }
                for (EffectTemplate ef : skill.getEffectTemplates()) {
                    if ((target.isAirBinded() || target.isKnockedDown())
                            && (ef.getEffectType() == EffectType.KnockDown
                                    || ef.getEffectType() == EffectType.HellBinding)) {
                        itr.remove();
                        continue;
                    }
                }
                target.getListeners().onMagicHit(skill, this);
                if (pl != null) {
                    if ((target != null) && target.isNpc()) {
                        NpcInstance npc = (NpcInstance) target;
                        List<QuestState> ql = pl.getQuestsForEvent(npc, QuestEventType.MOB_TARGETED_BY_SKILL,
                                false);
                        if (ql != null) {
                            for (QuestState qs : ql) {
                                qs.getQuest().notifySkillUse(npc, skill, qs);
                            }
                        }
                    }
                }
                if (skill.getNegateSkill() > 0) {
                    for (Effect e : target.getEffectList().getAllEffects()) {
                        Skill efs = e.getSkill();
                        if ((efs.getId() == skill.getNegateSkill()) && e.isCancelable()
                                && ((skill.getNegatePower() <= 0) || (efs.getPower() <= skill.getNegatePower()))) {
                            e.exit();
                        }
                    }
                }
                if (target.getEffectList().getEffectByType(EffectType.DispelOnHit) != null) {
                    target.getEffectList().getEffectByType(EffectType.DispelOnHit).onActionTime();
                }
                if (skill.getCancelTarget() > 0) {
                    if (Rnd.chance(skill.getCancelTarget())) {
                        if (((target.getCastingSkill() == null)
                                || !((target.getCastingSkill().getSkillType() == SkillType.TAKECASTLE)
                                        || (target.getCastingSkill().getSkillType() == SkillType.TAKEFORTRESS)))
                                && !target.isRaid()) {
                            target.abortAttack(true, true);
                            target.abortCast(true, true);
                            target.setTarget(null);
                        }
                    }
                }
            }
            if (skill.isOffensive()) {
                startAttackStanceTask();
            }
            if (!(skill.isNotTargetAoE() && skill.isOffensive() && (targets.size() == 0))) {
                skill.getEffects(this, this, false, true);
            }
            skill.useSkill(this, targets);
        } catch (Exception e) {
            _log.error("", e);
        }
    }

    /**
     * Method useTriggers.
     * @param target GameObject
     * @param type TriggerType
     * @param ex Skill
     * @param owner Skill
     * @param damage double
     */
    public void useTriggers(GameObject target, TriggerType type, Skill ex, Skill owner, double damage) {
        if (_triggers == null) {
            return;
        }
        Set<TriggerInfo> SkillsOnSkillAttack = _triggers.get(type);
        if (SkillsOnSkillAttack != null) {
            for (TriggerInfo t : SkillsOnSkillAttack) {
                if (t.getSkill() != ex) {
                    useTriggerSkill(target == null ? getTarget() : target, null, t, owner, damage);
                }
            }
        }
    }

    /**
     * Method useTriggerSkill.
     * @param target GameObject
     * @param targets List<Creature>
     * @param trigger TriggerInfo
     * @param owner Skill
     * @param damage double
     */
    public void useTriggerSkill(GameObject target, List<Creature> targets, TriggerInfo trigger, Skill owner,
            double damage) {
        Skill skill = trigger.getSkill();
        if ((skill.getReuseDelay() > 0) && isSkillDisabled(skill)) {
            return;
        }
        Creature aimTarget = skill.getAimingTarget(this, target);
        Creature realTarget = (target != null) && target.isCreature() ? (Creature) target : null;
        if (Rnd.chance(trigger.getChance()) && trigger.checkCondition(this, realTarget, aimTarget, owner, damage)
                && skill.checkCondition(this, aimTarget, false, true, true)) {
            if (targets == null) {
                targets = skill.getTargets(this, aimTarget, false);
            }
            int displayId = 0, displayLevel = 0;
            if (skill.hasEffects()) {
                displayId = skill.getEffectTemplates()[0]._displayId;
                displayLevel = skill.getEffectTemplates()[0]._displayLevel;
            }
            if (displayId == 0) {
                displayId = skill.getDisplayId();
            }
            if (displayLevel == 0) {
                displayLevel = skill.getDisplayLevel();
            }
            if (trigger.getType() != TriggerType.SUPPORT_MAGICAL_SKILL_USE) {
                for (Creature cha : targets) {
                    broadcastPacket(new MagicSkillUse(this, cha, displayId, displayLevel, 0, 0));
                }
            }
            Formulas.calcSkillMastery(skill, this);
            callSkill(skill, targets, false);
            disableSkill(skill, skill.getReuseDelay());
        }
    }

    /**
     * Method checkBlockedStat.
     * @param stat Stats
     * @return boolean
     */
    public boolean checkBlockedStat(Stats stat) {
        return (_blockedStats != null) && _blockedStats.contains(stat);
    }

    /**
     * Method checkReflectSkill.
     * @param attacker Creature
     * @param skill Skill
     * @return boolean
     */
    public boolean checkReflectSkill(Creature attacker, Skill skill) {
        if (!skill.isReflectable()) {
            return false;
        }
        if (isInvul() || attacker.isInvul() || !skill.isOffensive()) {
            return false;
        }
        if (skill.isMagic() && (skill.getSkillType() != SkillType.MDAM)) {
            return false;
        }
        if (Rnd.chance(calcStat(skill.isMagic() ? Stats.REFLECT_MAGIC_SKILL : Stats.REFLECT_PHYSIC_SKILL, 0,
                attacker, skill))) {
            sendPacket(new SystemMessage(SystemMessage.YOU_COUNTERED_C1S_ATTACK).addName(attacker));
            attacker.sendPacket(new SystemMessage(SystemMessage.C1_DODGES_THE_ATTACK).addName(this));
            return true;
        }
        return false;
    }

    /**
     * Method doCounterAttack.
     * @param skill Skill
     * @param attacker Creature
     * @param blow boolean
     */
    public void doCounterAttack(Skill skill, Creature attacker, boolean blow) {
        if (isDead()) {
            return;
        }
        if (isDamageBlocked() || attacker.isDamageBlocked()) {
            return;
        }
        if ((skill == null) || skill.hasEffects() || skill.isMagic() || !skill.isOffensive()
                || (skill.getCastRange() > 200)) {
            return;
        }
        if (Rnd.chance(calcStat(Stats.COUNTER_ATTACK, 0, attacker, skill))) {
            double damage = (1189 * getPAtk(attacker)) / Math.max(attacker.getPDef(this), 1);
            attacker.sendPacket(new SystemMessage(SystemMessage.C1S_IS_PERFORMING_A_COUNTERATTACK).addName(this));
            if (blow) {
                sendPacket(new SystemMessage(SystemMessage.C1S_IS_PERFORMING_A_COUNTERATTACK).addName(this));
                sendPacket(new SystemMessage(SystemMessage.C1_HAS_GIVEN_C2_DAMAGE_OF_S3).addName(this)
                        .addName(attacker).addNumber((long) damage));
                attacker.reduceCurrentHp(damage, damage, this, skill, true, true, false, false, false, false, true);
            } else {
                sendPacket(new SystemMessage(SystemMessage.C1S_IS_PERFORMING_A_COUNTERATTACK).addName(this));
            }
            sendPacket(new SystemMessage(SystemMessage.C1_HAS_GIVEN_C2_DAMAGE_OF_S3).addName(this).addName(attacker)
                    .addNumber((long) damage));
            attacker.reduceCurrentHp(damage, damage, this, skill, true, true, false, false, false, false, true);
        }
    }

    /**
     * Method disableSkill.
     * @param skill Skill
     * @param delay long
     */
    public void disableSkill(Skill skill, long delay) {
        _skillReuses.put(skill.hashCode(), new TimeStamp(skill, delay));
    }

    /**
     * Method isAutoAttackable.
     * @param attacker Creature
     * @return boolean
     */
    public abstract boolean isAutoAttackable(Creature attacker);

    /**
     * Method doAttack.
     * @param target Creature
     */
    public void doAttack(Creature target) {
        if ((target == null) || isAMuted() || isAttackingNow() || isAlikeDead() || target.isAlikeDead()
                || !isInRange(target, 2000) || (isPlayer() && getPlayer().isInMountTransform())) {
            return;
        }
        getListeners().onAttack(target);
        int sAtk = Math.max(calculateAttackDelay(), 333);
        int ssGrade = 0;
        WeaponTemplate weaponItem = getActiveWeaponItem();
        if (weaponItem != null) {
            if (isPlayer() && (weaponItem.getAttackReuseDelay() > 0)) {
                int reuse = (int) ((weaponItem.getAttackReuseDelay() * getReuseModifier(target) * 666
                        * calcStat(Stats.ATK_BASE, 0, target, null)) / 293. / getPAtkSpd());
                if (reuse > 0) {
                    sendPacket(new SetupGauge(this, SetupGauge.RED_MINI, reuse));
                    _attackReuseEndTime = (reuse + System.currentTimeMillis()) - 75;
                    if (reuse > sAtk) {
                        ThreadPoolManager.getInstance()
                                .schedule(new NotifyAITask(this, CtrlEvent.EVT_READY_TO_ACT, null, null), reuse);
                    }
                }
            }
            ssGrade = weaponItem.getCrystalType().externalOrdinal;
        }
        _attackEndTime = (sAtk + System.currentTimeMillis()) - 10;
        _isAttackAborted = false;
        Attack attack = new Attack(this, target, getChargedSoulShot(), ssGrade);
        setHeading(PositionUtils.calculateHeadingFrom(this, target));
        if (weaponItem == null) {
            doAttackHitSimple(attack, target, 1., !isPlayer(), sAtk, true);
        } else {
            switch (weaponItem.getItemType()) {
            case BOW:
            case CROSSBOW:
                doAttackHitByBow(attack, target, sAtk);
                break;
            case POLE:
                doAttackHitByPole(attack, target, sAtk);
                break;
            case DUAL:
            case DUALFIST:
            case DUALDAGGER:
                doAttackHitByDual(attack, target, sAtk);
                break;
            default:
                doAttackHitSimple(attack, target, 1., true, sAtk, true);
            }
        }
        if (attack.hasHits()) {
            broadcastPacket(attack);
        }
    }

    /**
     * Method doAttackHitSimple.
     * @param attack Attack
     * @param target Creature
     * @param multiplier double
     * @param unchargeSS boolean
     * @param sAtk int
     * @param notify boolean
     */
    private void doAttackHitSimple(Attack attack, Creature target, double multiplier, boolean unchargeSS, int sAtk,
            boolean notify) {
        int damage1 = 0, reflectableDamage1 = 0;
        boolean shld1 = false;
        boolean crit1 = false;
        boolean miss1 = Formulas.calcHitMiss(this, target);
        if (!miss1) {
            AttackInfo info = Formulas.calcPhysDam(this, target, null, false, false, attack._soulshot, false);
            damage1 = (int) (info.damage * multiplier);
            reflectableDamage1 = (int) (info.reflectableDamage * multiplier);
            shld1 = info.shld;
            crit1 = info.crit;
        }
        ThreadPoolManager.getInstance().schedule(new HitTask(this, target, damage1, reflectableDamage1, crit1,
                miss1, attack._soulshot, shld1, unchargeSS, notify), sAtk);
        attack.addHit(target, damage1, miss1, crit1, shld1);
    }

    /**
     * Method doAttackHitByBow.
     * @param attack Attack
     * @param target Creature
     * @param sAtk int
     */
    private void doAttackHitByBow(Attack attack, Creature target, int sAtk) {
        WeaponTemplate activeWeapon = getActiveWeaponItem();
        if (activeWeapon == null) {
            return;
        }
        int damage1 = 0, damage2 = 0;
        boolean shld1 = false;
        boolean crit1 = false;
        boolean miss1 = Formulas.calcHitMiss(this, target);
        reduceArrowCount();
        if (!miss1) {
            AttackInfo info = Formulas.calcPhysDam(this, target, null, false, false, attack._soulshot, false);
            damage1 = (int) info.damage;
            damage2 = (int) info.reflectableDamage;
            shld1 = info.shld;
            crit1 = info.crit;
            int range = activeWeapon.getAttackRange();
            damage1 *= ((Math.min(range, getDistance(target)) / range) * .4) + 0.8;
        }
        ThreadPoolManager.getInstance().schedule(
                new HitTask(this, target, damage1, damage2, crit1, miss1, attack._soulshot, shld1, true, true),
                sAtk);
        attack.addHit(target, damage2, miss1, crit1, shld1);
    }

    /**
     * Method doAttackHitByDual.
     * @param attack Attack
     * @param target Creature
     * @param sAtk int
     */
    private void doAttackHitByDual(Attack attack, Creature target, int sAtk) {
        int damage1 = 0;
        int damage2 = 0;
        int reflectableDamage1 = 0, reflectableDamage2 = 0;
        boolean shld1 = false;
        boolean shld2 = false;
        boolean crit1 = false;
        boolean crit2 = false;
        boolean miss1 = Formulas.calcHitMiss(this, target);
        boolean miss2 = Formulas.calcHitMiss(this, target);
        if (!miss1) {
            AttackInfo info = Formulas.calcPhysDam(this, target, null, true, false, attack._soulshot, false);
            damage1 = (int) info.damage;
            reflectableDamage1 = (int) info.reflectableDamage;
            shld1 = info.shld;
            crit1 = info.crit;
        }
        if (!miss2) {
            AttackInfo info = Formulas.calcPhysDam(this, target, null, true, false, attack._soulshot, false);
            damage2 = (int) info.damage;
            reflectableDamage2 = (int) info.reflectableDamage;
            shld2 = info.shld;
            crit2 = info.crit;
        }
        ThreadPoolManager.getInstance().schedule(new HitTask(this, target, damage1, reflectableDamage1, crit1,
                miss1, attack._soulshot, shld1, true, false), sAtk / 2);
        ThreadPoolManager.getInstance().schedule(new HitTask(this, target, damage2, reflectableDamage2, crit2,
                miss2, attack._soulshot, shld2, false, true), sAtk);
        attack.addHit(target, damage1, miss1, crit1, shld1);
        attack.addHit(target, damage2, miss2, crit2, shld2);
    }

    /**
     * Method doAttackHitByPole.
     * @param attack Attack
     * @param target Creature
     * @param sAtk int
     */
    private void doAttackHitByPole(Attack attack, Creature target, int sAtk) {
        int angle = (int) calcStat(Stats.POLE_ATTACK_ANGLE, 90, target, null);
        int range = (int) calcStat(Stats.POWER_ATTACK_RANGE, getTemplate().getBaseAtkRange(), target, null);
        int attackcountmax = (int) Math.round(calcStat(Stats.POLE_TARGET_COUNT, 0, target, null));
        if (isBoss()) {
            attackcountmax += 27;
        } else if (isRaid()) {
            attackcountmax += 12;
        } else if (isMonster() && (getLevel() > 0)) {
            attackcountmax += getLevel() / 7.5;
        }
        double mult = 1.;
        _poleAttackCount = 1;
        if (!isInZonePeace()) {
            for (Creature t : getAroundCharacters(range, 200)) {
                if (_poleAttackCount <= attackcountmax) {
                    if ((t == target) || t.isDead() || !PositionUtils.isFacing(this, t, angle)) {
                        continue;
                    }
                    if (t.isAutoAttackable(this)) {
                        doAttackHitSimple(attack, t, mult, false, sAtk, false);
                        mult *= Config.ALT_POLE_DAMAGE_MODIFIER;
                        _poleAttackCount++;
                    }
                } else {
                    break;
                }
            }
        }
        _poleAttackCount = 0;
        doAttackHitSimple(attack, target, 1., true, sAtk, true);
    }

    /**
     * Method getAnimationEndTime.
     * @return long
     */
    public long getAnimationEndTime() {
        return _animationEndTime;
    }

    /**
     * Method doCast.
     * @param skill Skill
     * @param target Creature
     * @param forceUse boolean
     */
    public void doCast(Skill skill, Creature target, boolean forceUse) {
        if (skill == null) {
            return;
        }
        int itemConsume[] = skill.getItemConsume();
        if (itemConsume[0] > 0) {
            for (int i = 0; i < itemConsume.length; i++) {
                if (!consumeItem(skill.getItemConsumeId()[i], itemConsume[i])) {
                    sendPacket(skill.isHandler() ? SystemMsg.INCORRECT_ITEM_COUNT
                            : SystemMsg.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL);
                    return;
                }
            }
        }
        if (skill.getReferenceItemId() > 0) {
            if (!consumeItemMp(skill.getReferenceItemId(), skill.getReferenceItemMpConsume())) {
                return;
            }
        }
        int magicId = skill.getId();
        if (target == null) {
            target = skill.getAimingTarget(this, getTarget());
        }
        if (target == null) {
            return;
        }
        getListeners().onMagicUse(skill, target, false);
        if (this != target) {
            setHeading(PositionUtils.calculateHeadingFrom(this, target));
        }
        int level = Math.max(1, getSkillDisplayLevel(magicId));
        int skillTime = skill.isSkillTimePermanent() ? skill.getHitTime()
                : Formulas.calcMAtkSpd(this, skill, skill.getHitTime());
        int skillInterruptTime = skill.isMagic() ? Formulas.calcMAtkSpd(this, skill, skill.getSkillInterruptTime())
                : 0;
        int minCastTime = Math.min(Config.SKILLS_CAST_TIME_MIN, skill.getHitTime());
        if (skillTime < minCastTime) {
            skillTime = minCastTime;
            skillInterruptTime = 0;
        }
        _animationEndTime = System.currentTimeMillis() + skillTime;
        if (skill.isMagic() && !skill.isSkillTimePermanent() && (getChargedSpiritShot() > 0)) {
            skillTime = (int) (0.70 * skillTime);
            skillInterruptTime = (int) (0.70 * skillInterruptTime);
        }
        Formulas.calcSkillMastery(skill, this);
        long reuseDelay = Math.max(0, Formulas.calcSkillReuseDelay(this, skill));
        broadcastPacket(new MagicSkillUse(this, target, skill.getDisplayId(), level, skillTime, reuseDelay,
                isDoubleCastingNow()));
        if (skill.getFlyType() == FlyType.CHARGE) {
            skillTime = minCastTime;
        }
        if (!skill.isHandler()) {
            disableSkill(skill, reuseDelay);
        }
        if (isPlayer()) {
            if (skill.getSkillType() == SkillType.PET_SUMMON) {
                sendPacket(Msg.SUMMON_A_PET);
            } else if (!skill.isHandler()) {
                if (!skill.isAlterSkill())
                    sendPacket(new SystemMessage(SystemMessage.YOU_USE_S1).addSkillName(magicId, level));
            } else {
                sendPacket(new SystemMessage(SystemMessage.YOU_USE_S1).addItemName(skill.getItemConsumeId()[0]));
            }
        }
        if (skill.getTargetType() == SkillTargetType.TARGET_HOLY) {
            target.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, this, 1);
        }
        double mpConsume1 = skill.isUsingWhileCasting() ? skill.getMpConsume() : skill.getMpConsume1();
        if (mpConsume1 > 0) {
            if (_currentMp < mpConsume1) {
                sendPacket(Msg.NOT_ENOUGH_MP);
                onCastEndTime(false);
                return;
            }
            reduceCurrentMp(mpConsume1, null);
        }
        _castingSkill = skill;
        _castInterruptTime = System.currentTimeMillis() + skillInterruptTime;
        setCastingTarget(target);
        if (skill.isUsingWhileCasting()) {
            callSkill(skill, skill.getTargets(this, target, forceUse), true);
        }
        if (isPlayer()) {
            sendPacket(new SetupGauge(this, SetupGauge.BLUE_DUAL, skillTime));
        }
        _scheduledCastCount = skill.getCastCount();
        _scheduledCastInterval = skill.getCastCount() > 0 ? skillTime / _scheduledCastCount : skillTime;
        if (!isDoubleCastingNow() && IsEnabledDoubleCast()) {
            _skillDoubleLaunchedTask = ThreadPoolManager.getInstance()
                    .schedule(new MagicLaunchedTask(this, forceUse), skillInterruptTime);
            _skillDoubleTask = ThreadPoolManager.getInstance().schedule(new MagicUseTask(this, forceUse),
                    skill.getCastCount() > 0 ? skillTime / skill.getCastCount() : skillTime);
        } else {
            _skillLaunchedTask = ThreadPoolManager.getInstance().schedule(new MagicLaunchedTask(this, forceUse),
                    skillInterruptTime);
            _skillTask = ThreadPoolManager.getInstance().schedule(new MagicUseTask(this, forceUse),
                    skill.getCastCount() > 0 ? skillTime / skill.getCastCount() : skillTime);
        }
    }

    /**
     * Field _flyLoc.
     */
    private Location _flyLoc;

    /**
     * Method getFlyLocation.
     * @param target GameObject
     * @param skill Skill
     * @return Location
     */
    public Location getFlyLocation(GameObject target, Skill skill) {
        if ((target != null) && (target != this)) {
            Location loc;
            double radian = PositionUtils.convertHeadingToRadian(target.getHeading());
            if (skill.isFlyToBack()) {
                loc = new Location(target.getX() + (int) (Math.sin(radian) * 40),
                        target.getY() - (int) (Math.cos(radian) * 40), target.getZ());
            } else {
                loc = new Location(target.getX() - (int) (Math.sin(radian) * 40),
                        target.getY() + (int) (Math.cos(radian) * 40), target.getZ());
            }
            if (isFlying()) {
                if (isPlayer() && ((Player) this).isInFlyingTransform() && ((loc.z <= 0) || (loc.z >= 6000))) {
                    return null;
                }
                if (GeoEngine.moveCheckInAir(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getColRadius(),
                        getGeoIndex()) == null) {
                    return null;
                }
            } else {
                loc.correctGeoZ();
                if (!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex())) {
                    loc = target.getLoc();
                    if (!GeoEngine.canMoveToCoord(getX(), getY(), getZ(), loc.x, loc.y, loc.z, getGeoIndex())) {
                        return null;
                    }
                }
            }
            return loc;
        }
        int x1 = 0;
        int y1 = 0;
        int z1 = 0;
        if (skill.getFlyType() == FlyType.THROW_UP) {
            x1 = 0;
            y1 = 0;
            z1 = getZ() + skill.getFlyRadius();
        } else {
            double radian = PositionUtils.convertHeadingToRadian(getHeading());
            x1 = -(int) (Math.sin(radian) * skill.getFlyRadius());
            y1 = (int) (Math.cos(radian) * skill.getFlyRadius());
        }
        if (isFlying()) {
            return GeoEngine.moveCheckInAir(getX(), getY(), getZ(), getX() + x1, getY() + y1, getZ() + z1,
                    getColRadius(), getGeoIndex());
        }
        return GeoEngine.moveCheck(getX(), getY(), getZ(), getX() + x1, getY() + y1, getGeoIndex());
    }

    /**
     * Method doDie.
     * @param killer Creature
     */
    public final void doDie(Creature killer) {
        if (!isDead.compareAndSet(false, true)) {
            return;
        }
        onDeath(killer);
    }

    /**
     * Method onDeath.
     * @param killer Creature
     */
    protected void onDeath(Creature killer) {
        if (killer != null) {
            Player killerPlayer = killer.getPlayer();
            if (killerPlayer != null) {
                killerPlayer.getListeners().onKillIgnorePetOrSummon(this);
                WorldStatisticsManager.getInstance().updateStat(killerPlayer, CategoryType.MONSTERS_KILLED, 1L);
            }
            killer.getListeners().onKill(this);
            if (isPlayer() && killer.isPlayable()) {
                _currentCp = 0;
            }
        }
        setTarget(null);
        stopMove();
        stopAttackStanceTask();
        stopRegeneration();
        _currentHp = 0;
        if (isBlessedByNoblesse() || isSalvation()) {
            if (isSalvation() && isPlayer() && !getPlayer().isInOlympiadMode()) {
                getPlayer().reviveRequest(getPlayer(), 100, false);
            }
            for (Effect e : getEffectList().getAllEffects()) {
                if ((e.getEffectType() == EffectType.BlessNoblesse)
                        || (e.getSkill().getId() == Skill.SKILL_FORTUNE_OF_NOBLESSE)
                        || (e.getSkill().getId() == Skill.SKILL_RAID_BLESSING)) {
                    e.exit();
                } else if (e.getEffectType() == EffectType.AgathionResurrect) {
                    if (isPlayer()) {
                        getPlayer().setAgathionRes(true);
                    }
                    e.exit();
                }
            }
        } else {
            for (Effect e : getEffectList().getAllEffects()) {
                if ((e.getEffectType() != EffectType.Transformation) && !e.getSkill().isPreservedOnDeath()) {
                    e.exit();
                }
            }
        }
        ThreadPoolManager.getInstance().execute(new NotifyAITask(this, CtrlEvent.EVT_DEAD, killer, null));
        getListeners().onDeath(killer);
        updateEffectIcons();
        updateStats();
        broadcastStatusUpdate();
    }

    /**
     * Method onRevive.
     */
    protected void onRevive() {
    }

    /**
     * Method enableSkill.
     * @param skill Skill
     */
    public void enableSkill(Skill skill) {
        _skillReuses.remove(skill.hashCode());
    }

    /**
     * Method getAbnormalEffect.
     * @return integer
     */
    public int getAbnormalEffect() {
        return _abnormalEffects;
    }

    /**
     * Method getAbnormalEffect2.
     * @return integer
     */
    public int getAbnormalEffect2() {
        return _abnormalEffects2;
    }

    /**
     * Method getAbnormalEffect3.
     * @return integer
     */
    public int getAbnormalEffect3() {
        return _abnormalEffects3;
    }

    /**
     * Method getAveList.
     * @return FastList<Integer>
     */
    public FastList<Integer> getAveList() {
        return _aveList;
    }

    public void addToAveList(int aeId) {
        if (!_aveList.contains(aeId)) {
            _aveList.add(aeId);
        }
    }

    public void removeFromAveList(int aeId) {
        if (_aveList.contains(aeId)) {
            _aveList.remove(_aveList.indexOf(aeId));
        }
    }

    /**
     * Method getAccuracy.
     * @return int
     */
    public int getAccuracy() {
        return (int) calcStat(Stats.ACCURACY_COMBAT, 0, null, null);
    }

    /**
     * Method getMAccuracy.
     * @return int
     */
    public int getMAccuracy() {
        return (int) calcStat(Stats.MACCURACY_COMBAT, 0, null, null);
    }

    /**
     * Method getAllSkills.
     * @return Collection<Skill>
     */
    public Collection<Skill> getAllSkills() {
        return _skills.values();
    }

    /**
     * Method getAllSkillsArray.
     * @return Skill[]
     */
    public final Skill[] getAllSkillsArray() {
        Collection<Skill> vals = _skills.values();
        return vals.toArray(new Skill[vals.size()]);
    }

    /**
     * Method getAttackSpeedMultiplier.
     * @return double
     */
    public final double getAttackSpeedMultiplier() {
        return (1.1 * getPAtkSpd()) / getTemplate().getBasePAtkSpd();
    }

    /**
     * Method getBuffLimit.
     * @return int
     */
    public int getBuffLimit() {
        return (int) calcStat(Stats.BUFF_LIMIT, Config.ALT_BUFF_LIMIT, null, null);
    }

    /**
     * Method getCastingSkill.
     * @return Skill
     */
    public Skill getCastingSkill() {
        return _castingSkill;
    }

    /**
     * Method getCriticalHit.
     * @param target Creature
     * @param skill Skill
     * @return int
     */
    public int getCriticalHit(Creature target, Skill skill) {
        return (int) calcStat(Stats.CRITICAL_BASE, _template.getBaseCritRate(), target, skill);
    }

    /**
     * Method getCriticalDmg.
     * @param target Creature
     * @param skill Skill
     * @return int
     */
    public int getCriticalDmg(Creature target, Skill skill) {
        return (int) calcStat(Stats.CRITICAL_DAMAGE, target, skill);
    }

    /**
     * Method getMagicCriticalRate.
     * @param target Creature
     * @param skill Skill
     * @return double
     */
    public double getMagicCriticalRate(Creature target, Skill skill) {
        return (int) calcStat(Stats.MCRITICAL_RATE, target, skill);
    }

    /**
     * Method getMagicCriticalRate.
     * @param target Creature
     * @param skill Skill
     * @return double
     */
    public double getMagicCriticalDmg(Creature target, Skill skill) {
        return calcStat(Stats.MCRITICAL_DAMAGE, target, skill);
    }

    /**
     * Method getCurrentCp.
     * @return double
     */
    public final double getCurrentCp() {
        return _currentCp;
    }

    /**
     * Method getCurrentCpRatio.
     * @return double
     */
    public final double getCurrentCpRatio() {
        return getCurrentCp() / getMaxCp();
    }

    /**
     * Method getCurrentCpPercents.
     * @return double
     */
    public final double getCurrentCpPercents() {
        return getCurrentCpRatio() * 100.;
    }

    /**
     * Method isCurrentCpFull.
     * @return boolean
     */
    public final boolean isCurrentCpFull() {
        return getCurrentCp() >= getMaxCp();
    }

    /**
     * Method isCurrentCpZero.
     * @return boolean
     */
    public final boolean isCurrentCpZero() {
        return getCurrentCp() < 1;
    }

    /**
     * Method getCurrentHp.
     * @return double
     */
    public final double getCurrentHp() {
        return _currentHp;
    }

    /**
     * Method getCurrentHpRatio.
     * @return double
     */
    public final double getCurrentHpRatio() {
        return getCurrentHp() / getMaxHp();
    }

    /**
     * Method getCurrentHpPercents.
     * @return double
     */
    public final double getCurrentHpPercents() {
        return getCurrentHpRatio() * 100.;
    }

    /**
     * Method isCurrentHpFull.
     * @return boolean
     */
    public final boolean isCurrentHpFull() {
        return getCurrentHp() >= getMaxHp();
    }

    /**
     * Method isCurrentHpZero.
     * @return boolean
     */
    public final boolean isCurrentHpZero() {
        return getCurrentHp() < 1;
    }

    /**
     * Method getCurrentMp.
     * @return double
     */
    public final double getCurrentMp() {
        return _currentMp;
    }

    /**
     * Method getCurrentMpRatio.
     * @return double
     */
    public final double getCurrentMpRatio() {
        return getCurrentMp() / getMaxMp();
    }

    /**
     * Method getCurrentMpPercents.
     * @return double
     */
    public final double getCurrentMpPercents() {
        return getCurrentMpRatio() * 100.;
    }

    /**
     * Method isCurrentMpFull.
     * @return boolean
     */
    public final boolean isCurrentMpFull() {
        return getCurrentMp() >= getMaxMp();
    }

    /**
     * Method isCurrentMpZero.
     * @return boolean
     */
    public final boolean isCurrentMpZero() {
        return getCurrentMp() < 1;
    }

    /**
     * Method getDestination.
     * @return Location
     */
    public Location getDestination() {
        return destination;
    }

    /**
     * Method getDEX.
     * @return int
     */
    public int getDEX() {
        return (int) calcStat(Stats.STAT_DEX, _template.getBaseAttr().getDEX(), null, null);
    }

    /**
     * Method getCON.
     * @return int
     */
    public int getCON() {
        return (int) calcStat(Stats.STAT_CON, _template.getBaseAttr().getCON(), null, null);
    }

    /**
     * Method getINT.
     * @return int
     */
    public int getINT() {
        return (int) calcStat(Stats.STAT_INT, _template.getBaseAttr().getINT(), null, null);
    }

    /**
     * Method getMEN.
     * @return int
     */
    public int getMEN() {
        return (int) calcStat(Stats.STAT_MEN, _template.getBaseAttr().getMEN(), null, null);
    }

    /**
     * Method getSTR.
     * @return int
     */
    public int getSTR() {
        return (int) calcStat(Stats.STAT_STR, _template.getBaseAttr().getSTR(), null, null);
    }

    /**
     * Method getEvasionRate.
     * @param target Creature
     * @return int
     */
    public int getEvasionRate(Creature target) {
        return (int) calcStat(Stats.EVASION_RATE, 0, target, null);
    }

    /**
     * Method getMEvasionRate.
     * @param target Creature
     * @return int
     */
    public int getMEvasionRate(Creature target) {
        return (int) calcStat(Stats.MEVASION_RATE, 0, target, null);
    }

    /**
     * Method getWIT.
     * @return int
     */
    public int getWIT() {
        return (int) calcStat(Stats.STAT_WIT, _template.getBaseAttr().getWIT(), null, null);
    }

    /**
     * Method getAroundCharacters.
     * @param radius int
     * @param height int
     * @return List<Creature>
     */
    public List<Creature> getAroundCharacters(int radius, int height) {
        if (!isVisible()) {
            return Collections.emptyList();
        }
        return World.getAroundCharacters(this, radius, height);
    }

    /**
     * Method getAroundNpc.
     * @param range int
     * @param height int
     * @return List<NpcInstance>
     */
    public List<NpcInstance> getAroundNpc(int range, int height) {
        if (!isVisible()) {
            return Collections.emptyList();
        }
        return World.getAroundNpc(this, range, height);
    }

    /**
     * Method knowsObject.
     * @param obj GameObject
     * @return boolean
     */
    public boolean knowsObject(GameObject obj) {
        return World.getAroundObjectById(this, obj.getObjectId()) != null;
    }

    /**
     * Method getKnownSkill.
     * @param skillId int
     * @return Skill
     */
    public final Skill getKnownSkill(int skillId) {
        return _skills.get(skillId);
    }

    /**
     * Method getMagicalAttackRange.
     * @param skill Skill
     * @return int
     */
    public final int getMagicalAttackRange(Skill skill) {
        if (skill != null) {
            return (int) calcStat(Stats.MAGIC_ATTACK_RANGE, skill.getCastRange(), null, skill);
        }
        return getTemplate().getBaseAtkRange();
    }

    /**
     * Method getMAtk.
     * @param target Creature
     * @param skill Skill
     * @return int
     */
    public int getMAtk(Creature target, Skill skill) {
        if ((skill != null) && (skill.getMatak() > 0)) {
            return skill.getMatak();
        }
        return (int) calcStat(Stats.MAGIC_ATTACK, _template.getBaseMAtk(), target, skill);
    }

    /**
     * Method getMAtkSpd.
     * @return int
     */
    public int getMAtkSpd() {
        return (int) (calcStat(Stats.MAGIC_ATTACK_SPEED, _template.getBaseMAtkSpd(), null, null));
    }

    /**
     * Method getMaxCp.
     * @return int
     */
    public int getMaxCp() {
        return (int) calcStat(Stats.MAX_CP, _template.getBaseCpMax(), null, null);
    }

    /**
     * Method getMaxHp.
     * @return int
     */
    public int getMaxHp() {
        return (int) calcStat(Stats.MAX_HP, _template.getBaseHpMax(), null, null);
    }

    /**
     * Method getMaxMp.
     * @return int
     */
    public int getMaxMp() {
        return (int) calcStat(Stats.MAX_MP, _template.getBaseMpMax(), null, null);
    }

    /**
     * Method getMDef.
     * @param target Creature
     * @param skill Skill
     * @return int
     */
    public int getMDef(Creature target, Skill skill) {
        return Math.max((int) calcStat(Stats.MAGIC_DEFENCE, _template.getBaseMDef(), target, skill), 1);
    }

    /**
     * Method getMinDistance.
     * @param obj GameObject
     * @return double
     */
    public double getMinDistance(GameObject obj) {
        double distance = getTemplate().getCollisionRadius();
        if ((obj != null) && obj.isCreature()) {
            distance += ((Creature) obj).getTemplate().getCollisionRadius();
        }
        return distance;
    }

    /**
     * Method getMovementSpeedMultiplier.
     * @return double
     */
    public double getMovementSpeedMultiplier() {
        return (getRunSpeed() * 1.) / _template.getBaseRunSpd();
    }

    /**
     * Method getMoveSpeed.
     * @return int
     */
    @Override
    public int getMoveSpeed() {
        if (isRunning()) {
            return getRunSpeed();
        }
        return getWalkSpeed();
    }

    /**
     * Method getRunSpeed.
     * @return int
     */
    public int getRunSpeed() {
        if (isInWater()) {
            return getSwimRunSpeed();
        }
        return getSpeed(_template.getBaseRunSpd());
    }

    /**
     * Method getWalkSpeed.
     * @return int
     */
    public int getWalkSpeed() {
        if (isInWater()) {
            return getSwimWalkSpeed();
        }
        return getSpeed(_template.getBaseWalkSpd());
    }

    /**
     * Method getSwimRunSpeed.
     * @return int
     */
    public int getSwimRunSpeed() {
        return getSpeed(_template.getBaseWaterRunSpd());
    }

    /**
     * Method getSwimWalkSpeed.
     * @return int
     */
    public int getSwimWalkSpeed() {
        return getSpeed(_template.getBaseWaterWalkSpd());
    }

    /**
     * Method relativeSpeed.
     * @param target GameObject
     * @return double
     */
    public double relativeSpeed(GameObject target) {
        return getMoveSpeed() - (target.getMoveSpeed()
                * Math.cos(headingToRadians(getHeading()) - headingToRadians(target.getHeading())));
    }

    /**
     * Method getSpeed.
     * @param baseSpeed double
     * @return int
     */
    public int getSpeed(double baseSpeed) {
        return (int) calcStat(Stats.RUN_SPEED, baseSpeed, null, null);
    }

    /**
     * Method getName.
     * @return String
     */
    @Override
    public String getName() {
        return StringUtils.defaultString(_name);
    }

    /**
     * Method getPAtk.
     * @param target Creature
     * @return int
     */
    public int getPAtk(Creature target) {
        return (int) calcStat(Stats.POWER_ATTACK, _template.getBasePAtk(), target, null);
    }

    /**
     * Method getPAtkSpd.
     * @return int
     */
    public int getPAtkSpd() {
        return (int) calcStat(Stats.POWER_ATTACK_SPEED, _template.getBasePAtkSpd(), null, null);
    }

    /**
     * Method getPDef.
     * @param target Creature
     * @return int
     */
    public int getPDef(Creature target) {
        return (int) calcStat(Stats.POWER_DEFENCE, _template.getBasePDef(), target, null);
    }

    /**
     * Method getPhysicalAttackRange.
     * @return int
     */
    public final int getPhysicalAttackRange() {
        return (int) calcStat(Stats.POWER_ATTACK_RANGE, getTemplate().getBaseAtkRange(), null, null);
    }

    /**
     * Method getRandomDamage.
     * @return int
     */
    public int getRandomDamage() {
        WeaponTemplate weaponItem = getActiveWeaponItem();
        if (weaponItem == null) {
            return 5 + (int) Math.sqrt(getLevel());
        }
        return weaponItem.getRandomDamage();
    }

    /**
     * Method getReuseModifier.
     * @param target Creature
     * @return double
     */
    public double getReuseModifier(Creature target) {
        return calcStat(Stats.ATK_REUSE, 1, target, null);
    }

    /**
     * Method getShldDef.
     * @return int
     */
    public final int getShldDef() {
        if (isPlayer()) {
            return (int) calcStat(Stats.SHIELD_DEFENCE, 0, null, null);
        }
        return (int) calcStat(Stats.SHIELD_DEFENCE, _template.getBaseShldDef(), null, null);
    }

    /**
     * Method getSkillDisplayLevel.
     * @param skillId Integer
     * @return int
     */
    public final int getSkillDisplayLevel(Integer skillId) {
        Skill skill = _skills.get(skillId);
        if (skill == null) {
            return -1;
        }
        return skill.getDisplayLevel();
    }

    /**
     * Method getSkillLevel.
     * @param skillId Integer
     * @return int
     */
    public final int getSkillLevel(Integer skillId) {
        switch (skillId) {
        case 1566:
        case 1567:
        case 1568:
        case 1569:
            return 1;
        }
        return getSkillLevel(skillId, -1);
    }

    /**
     * Method getSkillLevel.
     * @param skillId Integer
     * @param def int
     * @return int
     */
    public final int getSkillLevel(Integer skillId, int def) {
        Skill skill = _skills.get(skillId);
        if (skill == null) {
            return def;
        }
        return skill.getLevel();
    }

    /**
     * Method getSkillMastery.
     * @param skillId Integer
     * @return int
     */
    public int getSkillMastery(Integer skillId) {
        if (_skillMastery == null) {
            return 0;
        }
        Integer val = _skillMastery.get(skillId);
        return val == null ? 0 : val.intValue();
    }

    /**
     * Method removeSkillMastery.
     * @param skillId Integer
     */
    public void removeSkillMastery(Integer skillId) {
        if (_skillMastery != null) {
            _skillMastery.remove(skillId);
        }
    }

    /**
     * Method getSwimSpeed.
     * @return int
     */
    public int getSwimSpeed() {
        return (int) calcStat(Stats.RUN_SPEED, Config.SWIMING_SPEED, null, null);
    }

    /**
     * Method getTarget.
     * @return GameObject
     */
    public GameObject getTarget() {
        return target.get();
    }

    /**
     * Method getTargetId.
     * @return int
     */
    public final int getTargetId() {
        GameObject target = getTarget();
        return target == null ? -1 : target.getObjectId();
    }

    /**
     * Method getTemplate.
     * @return CharTemplate
     */
    public CharTemplate getTemplate() {
        return _template;
    }

    /**
     * Method getTitle.
     * @return String
     */
    public String getTitle() {
        return StringUtils.defaultString(_title);
    }

    /**
     * Method headingToRadians.
     * @param heading int
     * @return double
     */
    public double headingToRadians(int heading) {
        return (heading - 32768) / HEADINGS_IN_PI;
    }

    /**
     * Method isAlikeDead.
     * @return boolean
     */
    public boolean isAlikeDead() {
        return _fakeDeath || isDead();
    }

    /**
     * Method isAttackingNow.
     * @return boolean
     */
    public final boolean isAttackingNow() {
        return _attackEndTime > System.currentTimeMillis();
    }

    /**
     * Method isBlessedByNoblesse.
     * @return boolean
     */
    public final boolean isBlessedByNoblesse() {
        return _isBlessedByNoblesse;
    }

    /**
     * Method isSalvation.
     * @return boolean
     */
    public final boolean isSalvation() {
        return _isSalvation;
    }

    /**
     * Method isEffectImmune.
     * @return boolean
     */
    public boolean isEffectImmune() {
        return _effectImmunity.get();
    }

    /**
     * Method isBuffImmune.
     * @return boolean
     */
    public boolean isBuffImmune() {
        return _buffImmunity.get();
    }

    /**
     * Method isDebuffImmune.
     * @return boolean
     */
    public boolean isDebuffImmune() {
        return _debuffImmunity.get();
    }

    /**
     * Method isDead.
     * @return boolean
     */
    public boolean isDead() {
        return (_currentHp < 0.5) || isDead.get();
    }

    /**
     * Method isFlying.
     * @return boolean
     */
    @Override
    public final boolean isFlying() {
        return _flying;
    }

    /**
     * Method isInCombat.
     * @return boolean
     */
    public final boolean isInCombat() {
        return System.currentTimeMillis() < _stanceEndTime;
    }

    /**
     * Method isInvul.
     * @return boolean
     */
    public boolean isInvul() {
        return _isInvul;
    }

    /**
     * Method isMageClass.
     * @return boolean
     */
    public boolean isMageClass() {
        return getTemplate().getBaseMAtk() > 3;
    }

    /**
     * Method isRunning.
     * @return boolean
     */
    public final boolean isRunning() {
        return _running;
    }

    /**
     * Method isSkillDisabled.
     * @param skill Skill
     * @return boolean
     */
    public boolean isSkillDisabled(Skill skill) {
        TimeStamp sts = _skillReuses.get(skill.hashCode());
        if (sts == null) {
            return false;
        }
        if (sts.hasNotPassed()) {
            return true;
        }
        _skillReuses.remove(skill.hashCode());
        return false;
    }

    /**
     * Method isTeleporting.
     * @return boolean
     */
    public final boolean isTeleporting() {
        return isTeleporting.get();
    }

    /**
     * Method getIntersectionPoint.
     * @param target Creature
     * @return Location
     */
    public Location getIntersectionPoint(Creature target) {
        if (!PositionUtils.isFacing(this, target, 90)) {
            return new Location(target.getX(), target.getY(), target.getZ());
        }
        double angle = PositionUtils.convertHeadingToDegree(target.getHeading());
        double radian = Math.toRadians(angle - 90);
        double range = target.getMoveSpeed() / 2;
        return new Location((int) (target.getX() - (range * Math.sin(radian))),
                (int) (target.getY() + (range * Math.cos(radian))), target.getZ());
    }

    /**
     * Method applyOffset.
     * @param point Location
     * @param offset int
     * @return Location
     */
    public Location applyOffset(Location point, int offset) {
        if (offset <= 0) {
            return point;
        }
        long dx = point.x - getX();
        long dy = point.y - getY();
        long dz = point.z - getZ();
        double distance = Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
        if (distance <= offset) {
            point.set(getX(), getY(), getZ());
            return point;
        }
        if (distance >= 1) {
            double cut = offset / distance;
            point.x -= (int) ((dx * cut) + 0.5);
            point.y -= (int) ((dy * cut) + 0.5);
            point.z -= (int) ((dz * cut) + 0.5);
            if (!isFlying() && !isInBoat() && !isInWater() && !isBoat()) {
                point.correctGeoZ();
            }
        }
        return point;
    }

    /**
     * Method applyOffset.
     * @param points List<Location>
     * @param offset int
     * @return List<Location>
     */
    public List<Location> applyOffset(List<Location> points, int offset) {
        offset = offset >> 4;
        if (offset <= 0) {
            return points;
        }
        long dx = points.get(points.size() - 1).x - points.get(0).x;
        long dy = points.get(points.size() - 1).y - points.get(0).y;
        long dz = points.get(points.size() - 1).z - points.get(0).z;
        double distance = Math.sqrt((dx * dx) + (dy * dy) + (dz * dz));
        if (distance <= offset) {
            Location point = points.get(0);
            points.clear();
            points.add(point);
            return points;
        }
        if (distance >= 1) {
            double cut = offset / distance;
            int num = (int) ((points.size() * cut) + 0.5);
            for (int i = 1; (i <= num) && (points.size() > 0); i++) {
                points.remove(points.size() - 1);
            }
        }
        return points;
    }

    /**
     * Method setSimplePath.
     * @param dest Location
     * @return boolean
     */
    private boolean setSimplePath(Location dest) {
        List<Location> moveList = GeoMove.constructMoveList(getLoc(), dest);
        if (moveList.isEmpty()) {
            return false;
        }
        _targetRecorder.clear();
        _targetRecorder.add(moveList);
        return true;
    }

    /**
     * Method buildPathTo.
     * @param x int
     * @param y int
     * @param z int
     * @param offset int
     * @param pathFind boolean
     * @return boolean
     */
    private boolean buildPathTo(int x, int y, int z, int offset, boolean pathFind) {
        return buildPathTo(x, y, z, offset, null, false, pathFind);
    }

    /**
     * Method buildPathTo.
     * @param x int
     * @param y int
     * @param z int
     * @param offset int
     * @param follow Creature
     * @param forestalling boolean
     * @param pathFind boolean
     * @return boolean
     */
    boolean buildPathTo(int x, int y, int z, int offset, Creature follow, boolean forestalling, boolean pathFind) {
        int geoIndex = getGeoIndex();
        Location dest;
        if (forestalling && (follow != null) && follow.isMoving) {
            dest = getIntersectionPoint(follow);
        } else {
            dest = new Location(x, y, z);
        }
        if (isInBoat() || isBoat() || !Config.ALLOW_GEODATA) {
            applyOffset(dest, offset);
            return setSimplePath(dest);
        }
        if (isFlying() || isInWater()) {
            applyOffset(dest, offset);
            Location nextloc;
            if (isFlying()) {
                if (GeoEngine.canSeeCoord(this, dest.x, dest.y, dest.z, true)) {
                    return setSimplePath(dest);
                }
                nextloc = GeoEngine.moveCheckInAir(getX(), getY(), getZ(), dest.x, dest.y, dest.z, getColRadius(),
                        geoIndex);
                if ((nextloc != null) && !nextloc.equals(getX(), getY(), getZ())) {
                    return setSimplePath(nextloc);
                }
            } else {
                int waterZ = getWaterZ();
                nextloc = GeoEngine.moveInWaterCheck(getX(), getY(), getZ(), dest.x, dest.y, dest.z, waterZ,
                        geoIndex);
                if (nextloc == null) {
                    return false;
                }
                List<Location> moveList = GeoMove.constructMoveList(getLoc(), nextloc.clone());
                _targetRecorder.clear();
                if (!moveList.isEmpty()) {
                    _targetRecorder.add(moveList);
                }
                int dz = dest.z - nextloc.z;
                if ((dz > 0) && (dz < 128)) {
                    moveList = GeoEngine.MoveList(nextloc.x, nextloc.y, nextloc.z, dest.x, dest.y, geoIndex, false);
                    if (moveList != null) {
                        if (!moveList.isEmpty()) {
                            _targetRecorder.add(moveList);
                        }
                    }
                }
                return !_targetRecorder.isEmpty();
            }
            return false;
        }
        List<Location> moveList = GeoEngine.MoveList(getX(), getY(), getZ(), dest.x, dest.y, geoIndex, true);
        if (moveList != null) {
            if (moveList.isEmpty()) {
                return false;
            }
            applyOffset(moveList, offset);
            if (moveList.isEmpty()) {
                return false;
            }
            _targetRecorder.clear();
            _targetRecorder.add(moveList);
            return true;
        }
        if (pathFind) {
            List<List<Location>> targets = GeoMove.findMovePath(getX(), getY(), getZ(), dest.clone(), this, true,
                    geoIndex);
            if (!targets.isEmpty()) {
                moveList = targets.remove(targets.size() - 1);
                applyOffset(moveList, offset);
                if (!moveList.isEmpty()) {
                    targets.add(moveList);
                }
                if (!targets.isEmpty()) {
                    _targetRecorder.clear();
                    _targetRecorder.addAll(targets);
                    return true;
                }
            }
        }
        if (follow != null) {
            return false;
        }
        applyOffset(dest, offset);
        moveList = GeoEngine.MoveList(getX(), getY(), getZ(), dest.x, dest.y, geoIndex, false);
        if ((moveList != null) && !moveList.isEmpty()) {
            _targetRecorder.clear();
            _targetRecorder.add(moveList);
            return true;
        }
        return false;
    }

    /**
     * Method getFollowTarget.
     * @return Creature
     */
    public Creature getFollowTarget() {
        return followTarget.get();
    }

    /**
     * Method setFollowTarget.
     * @param target Creature
     */
    public void setFollowTarget(Creature target) {
        followTarget = target == null ? HardReferences.<Creature>emptyRef() : target.getRef();
    }

    /**
     * Method followToCharacter.
     * @param target Creature
     * @param offset int
     * @param forestalling boolean
     * @return boolean
     */
    public boolean followToCharacter(Creature target, int offset, boolean forestalling) {
        return followToCharacter(target.getLoc(), target, offset, forestalling);
    }

    /**
     * Method followToCharacter.
     * @param loc Location
     * @param target Creature
     * @param offset int
     * @param forestalling boolean
     * @return boolean
     */
    public boolean followToCharacter(Location loc, Creature target, int offset, boolean forestalling) {
        moveLock.lock();
        try {
            if (isMovementDisabled() || (target == null) || (isInBoat() && !isInShuttle()) || isInWater()) {
                return false;
            }
            offset = Math.max(offset, 10);
            if (isFollow && (target == getFollowTarget()) && (offset == _offset)) {
                return true;
            }
            if ((Math.abs(getZ() - target.getZ()) > 1000) && !isFlying()) {
                sendPacket(SystemMsg.CANNOT_SEE_TARGET);
                return false;
            }
            getAI().clearNextAction();
            stopMove(false, false);
            if (buildPathTo(loc.x, loc.y, loc.z, offset, target, forestalling, !target.isDoor())) {
                movingDestTempPos.set(loc.x, loc.y, loc.z);
            } else {
                return false;
            }
            isMoving = true;
            isFollow = true;
            _forestalling = forestalling;
            _offset = offset;
            setFollowTarget(target);
            moveNext(true);
            return true;
        } finally {
            moveLock.unlock();
        }
    }

    /**
     * Method moveToLocation.
     * @param loc Location
     * @param offset int
     * @param pathfinding boolean
     * @return boolean
     */
    public boolean moveToLocation(Location loc, int offset, boolean pathfinding) {
        return moveToLocation(loc.x, loc.y, loc.z, offset, pathfinding);
    }

    /**
     * Method moveToLocation.
     * @param x_dest int
     * @param y_dest int
     * @param z_dest int
     * @param offset int
     * @param pathfinding boolean
     * @return boolean
     */
    public boolean moveToLocation(int x_dest, int y_dest, int z_dest, int offset, boolean pathfinding) {
        moveLock.lock();
        try {
            offset = Math.max(offset, 0);
            Location dst_geoloc = new Location(x_dest, y_dest, z_dest).world2geo();
            if (isMoving && !isFollow && movingDestTempPos.equals(dst_geoloc)) {
                sendActionFailed();
                return true;
            }
            if (isMovementDisabled()) {
                getAI().setNextAction(nextAction.MOVE, new Location(x_dest, y_dest, z_dest), offset, pathfinding,
                        false);
                sendActionFailed();
                return false;
            }
            getAI().clearNextAction();
            if (isPlayer()) {
                getAI().changeIntention(AI_INTENTION_ACTIVE, null, null);
            }
            stopMove(false, false);
            if (buildPathTo(x_dest, y_dest, z_dest, offset, pathfinding)) {
                movingDestTempPos.set(dst_geoloc);
            } else {
                sendActionFailed();
                return false;
            }
            isMoving = true;
            moveNext(true);
            return true;
        } finally {
            moveLock.unlock();
        }
    }

    /**
     * Method moveNext.
     * @param firstMove boolean
     */
    void moveNext(boolean firstMove) {
        if (!isMoving || isMovementDisabled()) {
            stopMove();
            return;
        }
        _previousSpeed = getMoveSpeed();
        if (_previousSpeed <= 0) {
            stopMove();
            return;
        }
        if (!firstMove) {
            Location dest = destination;
            if (dest != null) {
                setLoc(dest, true);
            }
        }
        if (_targetRecorder.isEmpty()) {
            CtrlEvent ctrlEvent = isFollow ? CtrlEvent.EVT_ARRIVED_TARGET : CtrlEvent.EVT_ARRIVED;
            stopMove(false, true);
            ThreadPoolManager.getInstance().execute(new NotifyAITask(this, ctrlEvent));
            return;
        }
        moveList = _targetRecorder.remove(0);
        Location begin = moveList.get(0).clone().geo2world();
        Location end = moveList.get(moveList.size() - 1).clone().geo2world();
        destination = end;
        double distance = (isFlying() || isInWater()) ? begin.distance3D(end) : begin.distance(end);
        if (distance != 0) {
            setHeading(PositionUtils.calculateHeadingFrom(getX(), getY(), destination.x, destination.y));
        }
        broadcastMove();
        _startMoveTime = _followTimestamp = System.currentTimeMillis();
        if (_moveTaskRunnable == null) {
            _moveTaskRunnable = new MoveNextTask();
        }
        _moveTask = ThreadPoolManager.getInstance().schedule(_moveTaskRunnable.setDist(distance),
                getMoveTickInterval());
    }

    /**
     * Method getMoveTickInterval.
     * @return int
     */
    public int getMoveTickInterval() {
        return (isPlayer() ? 16000 : 32000) / Math.max(getMoveSpeed(), 1);
    }

    /**
     * Method broadcastMove.
     */
    private void broadcastMove() {
        validateLocation(isPlayer() ? 2 : 1);
        broadcastPacket(movePacket());
    }

    /**
     * Method stopMove.
     */
    public void stopMove() {
        stopMove(true, true);
    }

    /**
     * Method stopMove.
     * @param validate boolean
     */
    public void stopMove(boolean validate) {
        stopMove(true, validate);
    }

    /**
     * Method stopMove.
     * @param stop boolean
     * @param validate boolean
     */
    public void stopMove(boolean stop, boolean validate) {
        if (!isMoving) {
            return;
        }
        moveLock.lock();
        try {
            if (!isMoving) {
                return;
            }
            isMoving = false;
            isFollow = false;
            if (_moveTask != null) {
                _moveTask.cancel(false);
                _moveTask = null;
            }
            destination = null;
            moveList = null;
            _targetRecorder.clear();
            if (validate) {
                validateLocation(isPlayer() ? 2 : 1);
            }
            if (stop) {
                broadcastPacket(stopMovePacket());
            }
        } finally {
            moveLock.unlock();
        }
    }

    /**
     * Method getWaterZ.
     * @return int
     */
    public int getWaterZ() {
        if (!isInWater()) {
            return Integer.MIN_VALUE;
        }
        int waterZ = Integer.MIN_VALUE;
        zonesRead.lock();
        try {
            Zone zone;
            for (int i = 0; i < _zones.size(); i++) {
                zone = _zones.get(i);
                if (zone.getType() == ZoneType.water) {
                    if ((waterZ == Integer.MIN_VALUE) || (waterZ < zone.getTerritory().getZmax())) {
                        waterZ = zone.getTerritory().getZmax();
                    }
                }
            }
        } finally {
            zonesRead.unlock();
        }
        return waterZ;
    }

    /**
     * Method stopMovePacket.
     * @return L2GameServerPacket
     */
    protected L2GameServerPacket stopMovePacket() {
        return new StopMove(this);
    }

    /**
     * Method movePacket.
     * @return L2GameServerPacket
     */
    public L2GameServerPacket movePacket() {
        return new CharMoveToLocation(this);
    }

    /**
     * Method updateZones.
     */
    public void updateZones() {
        if (isInObserverMode()) {
            return;
        }
        Zone[] zones = isVisible() ? getCurrentRegion().getZones() : Zone.EMPTY_L2ZONE_ARRAY;
        LazyArrayList<Zone> entering = null;
        LazyArrayList<Zone> leaving = null;
        Zone zone;
        zonesWrite.lock();
        try {
            if (!_zones.isEmpty()) {
                leaving = LazyArrayList.newInstance();
                for (int i = 0; i < _zones.size(); i++) {
                    zone = _zones.get(i);
                    if (!ArrayUtils.contains(zones, zone)
                            || !zone.checkIfInZone(getX(), getY(), getZ(), getReflection())) {
                        leaving.add(zone);
                    }
                }
                if (!leaving.isEmpty()) {
                    for (int i = 0; i < leaving.size(); i++) {
                        zone = leaving.get(i);
                        _zones.remove(zone);
                    }
                }
            }
            if (zones.length > 0) {
                entering = LazyArrayList.newInstance();
                for (Zone zone2 : zones) {
                    zone = zone2;
                    if (!_zones.contains(zone) && zone.checkIfInZone(getX(), getY(), getZ(), getReflection())) {
                        entering.add(zone);
                    }
                }
                if (!entering.isEmpty()) {
                    for (int i = 0; i < entering.size(); i++) {
                        zone = entering.get(i);
                        _zones.add(zone);
                    }
                }
            }
        } finally {
            zonesWrite.unlock();
        }
        onUpdateZones(leaving, entering);
        if (leaving != null) {
            LazyArrayList.recycle(leaving);
        }
        if (entering != null) {
            LazyArrayList.recycle(entering);
        }
    }

    /**
     * Method onUpdateZones.
     * @param leaving List<Zone>
     * @param entering List<Zone>
     */
    protected void onUpdateZones(List<Zone> leaving, List<Zone> entering) {
        Zone zone;
        if ((leaving != null) && !leaving.isEmpty()) {
            for (int i = 0; i < leaving.size(); i++) {
                zone = leaving.get(i);
                zone.doLeave(this);
            }
        }
        if ((entering != null) && !entering.isEmpty()) {
            for (int i = 0; i < entering.size(); i++) {
                zone = entering.get(i);
                zone.doEnter(this);
            }
        }
    }

    /**
     * Method isInZonePeace.
     * @return boolean
     */
    public boolean isInZonePeace() {
        return isInZone(ZoneType.peace_zone) && !isInZoneBattle();
    }

    /**
     * Method isInZoneBattle.
     * @return boolean
     */
    public boolean isInZoneBattle() {
        return isInZone(ZoneType.battle_zone);
    }

    /**
     * Method isInWater.
     * @return boolean
     */
    public boolean isInWater() {
        return isInZone(ZoneType.water) && !(isInBoat() || isBoat() || isFlying());
    }

    /**
     * Method isInZone.
     * @param type ZoneType
     * @return boolean
     */
    public boolean isInZone(ZoneType type) {
        zonesRead.lock();
        try {
            Zone zone;
            for (Zone _zone : _zones) {
                zone = _zone;
                if (zone.getType() == type) {
                    return true;
                }
            }
        } finally {
            zonesRead.unlock();
        }
        return false;
    }

    /**
     * Method isInZone.
     * @param name String
     * @return boolean
     */
    public boolean isInZone(String name) {
        zonesRead.lock();
        try {
            Zone zone;
            for (int i = 0; i < _zones.size(); i++) {
                zone = _zones.get(i);
                if (zone.getName().equals(name)) {
                    return true;
                }
            }
        } finally {
            zonesRead.unlock();
        }
        return false;
    }

    /**
     * Method isInZone.
     * @param zone Zone
     * @return boolean
     */
    public boolean isInZone(Zone zone) {
        zonesRead.lock();
        try {
            return _zones.contains(zone);
        } finally {
            zonesRead.unlock();
        }
    }

    /**
     * Method getZone.
     * @param type ZoneType
     * @return Zone
     */
    public Zone getZone(ZoneType type) {
        zonesRead.lock();
        try {
            Zone zone;
            for (int i = 0; i < _zones.size(); i++) {
                zone = _zones.get(i);
                if (zone.getType() == type) {
                    return zone;
                }
            }
        } finally {
            zonesRead.unlock();
        }
        return null;
    }

    /**
     * Method getRestartPoint.
     * @return Location
     */
    public Location getRestartPoint() {
        zonesRead.lock();
        try {
            Zone zone;
            for (int i = 0; i < _zones.size(); i++) {
                zone = _zones.get(i);
                if (zone.getRestartPoints() != null) {
                    ZoneType type = zone.getType();
                    if ((type == ZoneType.battle_zone) || (type == ZoneType.peace_zone)
                            || (type == ZoneType.offshore) || (type == ZoneType.dummy)) {
                        return zone.getSpawn();
                    }
                }
            }
        } finally {
            zonesRead.unlock();
        }
        return null;
    }

    /**
     * Method getPKRestartPoint.
     * @return Location
     */
    public Location getPKRestartPoint() {
        zonesRead.lock();
        try {
            Zone zone;
            for (int i = 0; i < _zones.size(); i++) {
                zone = _zones.get(i);
                if (zone.getRestartPoints() != null) {
                    ZoneType type = zone.getType();
                    if ((type == ZoneType.battle_zone) || (type == ZoneType.peace_zone)
                            || (type == ZoneType.offshore) || (type == ZoneType.dummy)) {
                        return zone.getPKSpawn();
                    }
                }
            }
        } finally {
            zonesRead.unlock();
        }
        return null;
    }

    /**
     * Method getGeoZ.
     * @param loc Location
     * @return int
     */
    @Override
    public int getGeoZ(Location loc) {
        if (isFlying() || isInWater() || isInBoat() || isBoat() || isDoor()) {
            return loc.z;
        }
        return super.getGeoZ(loc);
    }

    /**
     * Method needStatusUpdate.
     * @return boolean
     */
    protected boolean needStatusUpdate() {
        if (!isVisible() || !displayHpBar()) {
            return false;
        }

        boolean result = false;
        int bar;

        bar = (int) ((getCurrentHp() * CLIENT_BAR_SIZE) / getMaxHp());

        if ((bar == 0) || (bar != _lastHpBarUpdate)) {
            _lastHpBarUpdate = bar;
            result = true;
        }

        bar = (int) ((getCurrentMp() * CLIENT_BAR_SIZE) / getMaxMp());

        if ((bar == 0) || (bar != _lastMpBarUpdate)) {
            _lastMpBarUpdate = bar;
            result = true;
        }

        if (isPlayer()) {
            bar = (int) ((getCurrentCp() * CLIENT_BAR_SIZE) / getMaxCp());

            if ((bar == 0) || (bar != _lastCpBarUpdate)) {
                _lastCpBarUpdate = bar;
                result = true;
            }
        }
        return result;
    }

    /**
     * Method onForcedAttack.
     * @param player Player
     * @param shift boolean
     */
    @Override
    public void onForcedAttack(Player player, boolean shift) {
        if (player.getTarget() != this) {
            player.setTarget(this);
        }

        if (!isAttackable(player) || player.isConfused() || player.isBlocked()) {
            player.sendActionFailed();
            return;
        }

        player.getAI().Attack(this, true, shift);
    }

    /**
     * Method onHitTimer.
     * @param target Creature
     * @param damage int
     * @param reflectableDamage int
     * @param crit boolean
     * @param miss boolean
     * @param soulshot boolean
     * @param shld boolean
     * @param unchargeSS boolean
     */
    public void onHitTimer(Creature target, int damage, int reflectableDamage, boolean crit, boolean miss,
            boolean soulshot, boolean shld, boolean unchargeSS) {
        if (isAlikeDead()) {
            sendActionFailed();
            return;
        }
        if (target.isDead() || !isInRange(target, 2000)) {
            sendActionFailed();
            return;
        }
        if (isPlayable() && target.isPlayable() && (isInZoneBattle() != target.isInZoneBattle())) {
            Player player = getPlayer();
            if (player != null) {
                player.sendPacket(Msg.INVALID_TARGET);
                player.sendActionFailed();
            }
            return;
        }
        target.getListeners().onAttackHit(this);
        if (!miss && target.isPlayer() && (isCursedWeaponEquipped() || ((getActiveWeaponInstance() != null)
                && getActiveWeaponInstance().isHeroWeapon() && target.isCursedWeaponEquipped()))) {
            target.setCurrentCp(0);
        }
        if (target.isStunned() && Formulas.calcStunBreak(crit)) {
            target.getEffectList().stopEffects(EffectType.Stun);
        }
        if (target.getEffectList().getEffectByType(EffectType.DispelOnHit) != null && !miss) {
            target.getEffectList().getEffectByType(EffectType.DispelOnHit).onActionTime();
        }
        displayGiveDamageMessage(target, damage, crit, miss, shld, false);
        ThreadPoolManager.getInstance().execute(new NotifyAITask(target, CtrlEvent.EVT_ATTACKED, this, damage));
        boolean checkPvP = checkPvP(target, null);
        if (!miss && (damage > 0)) {
            target.reduceCurrentHp(damage, reflectableDamage, this, null, true, true, false, true, false, false,
                    true);
            if (!target.isDead()) {
                if (crit) {
                    useTriggers(target, TriggerType.CRIT, null, null, damage);
                }
                useTriggers(target, TriggerType.ATTACK, null, null, damage);
                if (Formulas.calcCastBreak(target, crit)) {
                    target.abortCast(false, true);
                }
            }
            if (soulshot && unchargeSS) {
                unChargeShots(false);
            }
        }
        if (miss) {
            target.useTriggers(this, TriggerType.UNDER_MISSED_ATTACK, null, null, damage);
        }
        startAttackStanceTask();
        if (checkPvP) {
            startPvPFlag(target);
        }
    }

    /**
     * Method onMagicUseTimer.
     * @param aimingTarget Creature
     * @param skill Skill
     * @param forceUse boolean
     */
    public void onMagicUseTimer(Creature aimingTarget, Skill skill, boolean forceUse) {
        if (skill == null) {
            onCastEndTime(false);
            sendPacket(ActionFail.STATIC);
            return;
        }
        _castInterruptTime = 0;
        if (skill.isUsingWhileCasting()) {
            aimingTarget.getEffectList().stopEffect(skill.getId());
            onCastEndTime(false);
            return;
        }
        if (!skill.isOffensive() && (getAggressionTarget() != null)) {
            forceUse = true;
        }
        if (!skill.checkCondition(this, aimingTarget, forceUse, false, false)) {
            if ((skill.getSkillType() == SkillType.PET_SUMMON) && isPlayer()) {
                getPlayer().setPetControlItem(null);
            }
            onCastEndTime(false);
            return;
        }
        if ((skill.getCastRange() < 32767) && (skill.getSkillType() != SkillType.TAKECASTLE)
                && (skill.getSkillType() != SkillType.TAKEFORTRESS)
                && !GeoEngine.canSeeTarget(this, aimingTarget, isFlying())) {
            sendPacket(SystemMsg.CANNOT_SEE_TARGET);
            broadcastPacket(new MagicSkillCanceled(getObjectId()));
            onCastEndTime(false);
            return;
        }
        List<Creature> targets = skill.getTargets(this, aimingTarget, forceUse);
        int hpConsume = skill.getHpConsume();
        if (hpConsume > 0) {
            setCurrentHp(Math.max(0, _currentHp - hpConsume), false);
        }
        double mpConsume2 = skill.getMpConsume2();
        if (mpConsume2 > 0) {
            if (skill.isMusic()) {
                double inc = mpConsume2 / 2;
                double add = 0;
                for (Effect e : getEffectList().getAllEffects()) {
                    if ((e.getSkill().getId() != skill.getId()) && e.getSkill().isMusic()
                            && (e.getTimeLeft() > 30)) {
                        add += inc;
                    }
                }
                mpConsume2 += add;
                mpConsume2 = calcStat(Stats.MP_DANCE_SKILL_CONSUME, mpConsume2, aimingTarget, skill);
            } else if (skill.isMagic()) {
                mpConsume2 = calcStat(Stats.MP_MAGIC_SKILL_CONSUME, mpConsume2, aimingTarget, skill);
            } else {
                mpConsume2 = calcStat(Stats.MP_PHYSICAL_SKILL_CONSUME, mpConsume2, aimingTarget, skill);
            }
            if ((_currentMp < mpConsume2) && isPlayable()) {
                sendPacket(Msg.NOT_ENOUGH_MP);
                onCastEndTime(false);
                return;
            }
            reduceCurrentMp(mpConsume2, null);
        }
        callSkill(skill, targets, true);
        if (skill.getNumCharges() > 0) {
            setIncreasedForce(getIncreasedForce() - skill.getNumCharges());
        }
        if (skill.getMaxCharges() > 0) {
            setIncreasedForce(getIncreasedForce() - skill.getMaxCharges());
        }
        if (skill.isSoulBoost()) {
            setConsumedSouls(getConsumedSouls() - Math.min(getConsumedSouls(), 5), null);
        } else if (skill.getSoulsConsume() > 0) {
            setConsumedSouls(getConsumedSouls() - skill.getSoulsConsume(), null);
        }
        switch (skill.getFlyType()) {
        // TARGETS FLYTYPE
        case THROW_UP:
        case THROW_HORIZONTAL:
        case PUSH_HORIZONTAL:
        case PUSH_DOWN_HORIZONTAL:
            Location flyLoc;
            for (Creature target : targets) {
                flyLoc = target.getFlyLocation(null, skill);
                if (flyLoc == null)
                    _log.warn(skill.getFlyType() + " have null flyLoc.");
                target.setLoc(flyLoc);
                broadcastPacket(new FlyToLocation(target, flyLoc, skill.getFlyType(), 0));
            }
            break;
        // CASTER FLYTYPE
        case CHARGE:
            Location flyLocCharge;
            for (Creature target : targets) {
                double radian = PositionUtils.convertHeadingToRadian(this.getHeading());
                flyLocCharge = target.getLoc();
                flyLocCharge.set(flyLocCharge.getX() + (int) (Math.sin(radian) * 40),
                        flyLocCharge.getY() - (int) (Math.cos(radian) * 40), flyLocCharge.getZ());
                setLoc(flyLocCharge);
                broadcastPacket(new FlyToLocation(this, flyLocCharge, skill.getFlyType(), 0));
            }
            break;
        case DUMMY:
        case WARP_BACK:
        case WARP_FORWARD:
            flyLoc = getFlyLocation(null, skill);
            if (flyLoc != null) {
                setLoc(flyLoc);
                broadcastPacket(new FlyToLocation(this, flyLoc, skill.getFlyType(), 0));
            } else {
                sendPacket(SystemMsg.CANNOT_SEE_TARGET);
            }
            break;
        default:
            break;
        }
        if (_scheduledCastCount > 0) {
            _scheduledCastCount--;
            if (!isDoubleCastingNow() && IsEnabledDoubleCast()) {
                _skillDoubleLaunchedTask = ThreadPoolManager.getInstance()
                        .schedule(new MagicLaunchedTask(this, forceUse), _scheduledCastInterval);
                _skillDoubleTask = ThreadPoolManager.getInstance().schedule(new MagicUseTask(this, forceUse),
                        _scheduledCastInterval);
            } else {
                _skillLaunchedTask = ThreadPoolManager.getInstance().schedule(new MagicLaunchedTask(this, forceUse),
                        _scheduledCastInterval);
                _skillTask = ThreadPoolManager.getInstance().schedule(new MagicUseTask(this, forceUse),
                        _scheduledCastInterval);
            }
            return;
        }
        int skillCoolTime = 0;
        int chargeAddition = 0;
        if (skill.getFlyType() == FlyType.CHARGE)
            chargeAddition = skill.getHitTime();
        if (!skill.isSkillTimePermanent())
            skillCoolTime = Formulas.calcMAtkSpd(this, skill, skill.getCoolTime() + chargeAddition);
        else
            skillCoolTime = skill.getCoolTime() + chargeAddition;
        if (skillCoolTime > 0) {
            ThreadPoolManager.getInstance().schedule(new CastEndTimeTask(this), skillCoolTime);
        } else {
            onCastEndTime(true);
        }
    }

    /**
     * Method onCastEndTime.
     * @param success boolean
     */
    public void onCastEndTime(boolean success) {
        finishFly();
        int skillId = 0;
        if (getCastingSkill() != null) {
            skillId = getCastingSkill().getId();
            // TODO: CHAIN SKILL MINOR FIX
            if (getCastingSkill().isAlterSkill()) {
                if (getCastingTarget().isAirBinded()) {
                    getCastingTarget().getEffectList().stopEffects(EffectType.HellBinding);
                } else if (getCastingTarget().isKnockedDown()) {
                    getCastingTarget().getEffectList().stopEffects(EffectType.KnockDown);
                }
            }
            //----------------
        }
        clearCastVars();
        getAI().notifyEvent(CtrlEvent.EVT_FINISH_CASTING, skillId, success);
    }

    /**
     * Method clearCastVars.
     */
    public void clearCastVars() {
        _animationEndTime = 0;
        _castInterruptTime = 0;
        _scheduledCastCount = 0;
        _castingSkill = null;
        _skillTask = null;
        _skillLaunchedTask = null;
        _skillDoubleTask = null;
        _skillDoubleLaunchedTask = null;
        _flyLoc = null;
    }

    /**
     * Method finishFly.
     */
    private void finishFly() {
        Location flyLoc = _flyLoc;
        _flyLoc = null;
        if (flyLoc != null) {
            setLoc(flyLoc);
            validateLocation(1);
        }
    }

    /**
     * Method reduceCurrentHp.
     * @param damage double
     * @param reflectableDamage double
     * @param attacker Creature
     * @param skill Skill
     * @param awake boolean
     * @param standUp boolean
     * @param directHp boolean
     * @param canReflect boolean
     * @param transferDamage boolean
     * @param isDot boolean
     * @param sendMessage boolean
     */
    public void reduceCurrentHp(double damage, double reflectableDamage, Creature attacker, Skill skill,
            boolean awake, boolean standUp, boolean directHp, boolean canReflect, boolean transferDamage,
            boolean isDot, boolean sendMessage) {
        if ((attacker == null) || isDead() || (attacker.isDead() && !isDot)) {
            return;
        }
        if (isDamageBlocked() && transferDamage) {
            return;
        }
        if (isDamageBlocked() && (attacker != this)) {
            if (sendMessage) {
                attacker.sendPacket(Msg.THE_ATTACK_HAS_BEEN_BLOCKED);
            }
            return;
        }
        if (isDeathImmune()) {
            damage = Math.min(damage, Math.floor(getCurrentHp() - 1));
        }
        double damageLimit = calcStat(Stats.RECEIVE_DAMAGE_LIMIT, damage);
        if (damageLimit >= 0.) {
            damage = damageLimit;
        }
        if (canReflect) {
            if (attacker.absorbAndReflect(this, skill, reflectableDamage)) {
                return;
            }
            damage = absorbToEffector(attacker, damage);
            damage = absorbToMp(attacker, damage);
            damage = absorbToSummon(attacker, damage);
        }
        getListeners().onCurrentHpDamage(damage, attacker, skill);
        if (attacker != this) {
            if (sendMessage) {
                displayReceiveDamageMessage(attacker, (int) damage);
            }
            if (!isDot) {
                useTriggers(attacker, TriggerType.RECEIVE_DAMAGE, null, null, damage);
            }
        }
        onReduceCurrentHp(damage, attacker, skill, awake, standUp, directHp);

        if (attacker.isPlayer()) {
            WorldStatisticsManager.getInstance().updateStat(attacker.getPlayer(), CategoryType.DAMAGE_TO_MONSTERS,
                    attacker.getPlayer().getClassId().getId(), (long) damage);
            WorldStatisticsManager.getInstance().updateStat(attacker.getPlayer(),
                    CategoryType.DAMAGE_TO_MONSTERS_MAX, attacker.getPlayer().getClassId().getId(), (long) damage);
        }
    }

    /**
     * Method onReduceCurrentHp.
     * @param damage double
     * @param attacker Creature
     * @param skill Skill
     * @param awake boolean
     * @param standUp boolean
     * @param directHp boolean
     */
    protected void onReduceCurrentHp(final double damage, Creature attacker, Skill skill, boolean awake,
            boolean standUp, boolean directHp) {
        if (getTransformation() != 0) {
            List<Effect> effects = getEffectList().getAllEffects();
            for (Effect effect : effects)
                if (effect.getSkill().isDispelOnDamage())
                    getEffectList().stopEffect(effect.getSkill());
        }
        if (awake && isSleeping()) {
            getEffectList().stopEffects(EffectType.Sleep);
        }
        if ((attacker != this) || ((skill != null) && skill.isOffensive())) {
            if (isMeditated()) {
                Effect effect = getEffectList().getEffectByType(EffectType.Meditation);
                if (effect != null) {
                    getEffectList().stopEffect(effect.getSkill());
                }
            }
            startAttackStanceTask();
            checkAndRemoveInvisible();
            if ((getCurrentHp() - damage) < 0.5) {
                useTriggers(attacker, TriggerType.DIE, null, null, damage);
            }
        }
        if (isPlayer() && getPlayer().isGM() && getPlayer().isUndying() && ((damage + 0.5) >= getCurrentHp())) {
            return;
        }
        setCurrentHp(Math.max(getCurrentHp() - damage, 0), false, attacker.getObjectId());
        broadcastStatusUpdate();
        if (getCurrentHp() < 0.5) {
            doDie(attacker);
        }
    }

    /**
     * Method reduceCurrentMp.
     * @param i double
     * @param attacker Creature
     */
    public void reduceCurrentMp(double i, Creature attacker) {
        if ((attacker != null) && (attacker != this)) {
            if (isSleeping()) {
                getEffectList().stopEffects(EffectType.Sleep);
            }
            if (isMeditated()) {
                Effect effect = getEffectList().getEffectByType(EffectType.Meditation);
                if (effect != null) {
                    getEffectList().stopEffect(effect.getSkill());
                }
            }
        }
        if (isDamageBlocked() && (attacker != null) && (attacker != this)) {
            attacker.sendPacket(Msg.THE_ATTACK_HAS_BEEN_BLOCKED);
            return;
        }
        if ((attacker != null) && attacker.isPlayer() && (Math.abs(attacker.getLevel() - getLevel()) > 10)) {
            if ((attacker.getKarma() < 0) && (getEffectList().getEffectsBySkillId(5182) != null)
                    && !isInZone(ZoneType.SIEGE)) {
                return;
            }
            if ((getKarma() < 0) && (attacker.getEffectList().getEffectsBySkillId(5182) != null)
                    && !attacker.isInZone(ZoneType.SIEGE)) {
                return;
            }
        }
        i = _currentMp - i;
        if (i < 0) {
            i = 0;
        }
        setCurrentMp(i);
        if ((attacker != null) && (attacker != this)) {
            startAttackStanceTask();
        }
    }

    /**
     * Method removeAllSkills.
     */
    public void removeAllSkills() {
        for (Skill s : getAllSkillsArray()) {
            removeSkill(s);
        }
    }

    /**
     * Method removeBlockStats.
     * @param stats List<Stats>
     */
    public void removeBlockStats(List<Stats> stats) {
        if (_blockedStats != null) {
            _blockedStats.removeAll(stats);
            if (_blockedStats.isEmpty()) {
                _blockedStats = null;
            }
        }
    }

    /**
     * Method removeSkill.
     * @param skill Skill
     * @return Skill
     */
    public Skill removeSkill(Skill skill) {
        if (skill == null) {
            return null;
        }
        return removeSkillById(skill.getId());
    }

    /**
     * Method removeSkillById.
     * @param id Integer
     * @return Skill
     */
    public Skill removeSkillById(Integer id) {
        Skill oldSkill = _skills.remove(id);
        List<Effect> effects;
        if (oldSkill != null) {
            removeTriggers(oldSkill);
            removeStatsOwner(oldSkill);
            if (Config.ALT_DELETE_SA_BUFFS && (oldSkill.isItemSkill() || oldSkill.isHandler())) {
                effects = getEffectList().getEffectsBySkill(oldSkill);
                if (effects != null) {
                    for (Effect effect : effects) {
                        effect.exit();
                    }
                }
                if (isPlayer()) {
                    for (Summon summon : ((Player) this).getSummonList()) {
                        if (summon != null) {
                            effects = summon.getEffectList().getEffectsBySkill(oldSkill);
                            if (effects != null) {
                                for (Effect effect : effects) {
                                    effect.exit();
                                }
                            }
                        }
                    }
                }
            }
        }
        return oldSkill;
    }

    /**
     * Method addTriggers.
     * @param f StatTemplate
     */
    public void addTriggers(StatTemplate f) {
        if (f.getTriggerList().isEmpty()) {
            return;
        }
        for (TriggerInfo t : f.getTriggerList()) {
            addTrigger(t);
        }
    }

    /**
     * Method addTrigger.
     * @param t TriggerInfo
     */
    public void addTrigger(TriggerInfo t) {
        if (_triggers == null) {
            _triggers = new ConcurrentHashMap<TriggerType, Set<TriggerInfo>>();
        }
        Set<TriggerInfo> hs = _triggers.get(t.getType());
        if (hs == null) {
            hs = new CopyOnWriteArraySet<TriggerInfo>();
            _triggers.put(t.getType(), hs);
        }
        hs.add(t);
        if (t.getType() == TriggerType.ADD) {
            useTriggerSkill(this, null, t, null, 0);
        }
    }

    /**
     * Method removeTriggers.
     * @param f StatTemplate
     */
    public void removeTriggers(StatTemplate f) {
        if ((_triggers == null) || f.getTriggerList().isEmpty()) {
            return;
        }
        for (TriggerInfo t : f.getTriggerList()) {
            removeTrigger(t);
        }
    }

    /**
     * Method removeTrigger.
     * @param t TriggerInfo
     */
    public void removeTrigger(TriggerInfo t) {
        if (_triggers == null) {
            return;
        }
        Set<TriggerInfo> hs = _triggers.get(t.getType());
        if (hs == null) {
            return;
        }
        hs.remove(t);
    }

    /**
     * Method sendActionFailed.
     */
    public void sendActionFailed() {
        sendPacket(ActionFail.STATIC);
    }

    /**
     * Method hasAI.
     * @return boolean
     */
    public boolean hasAI() {
        return _ai != null;
    }

    /**
     * Method getAI.
     * @return CharacterAI
     */
    public CharacterAI getAI() {
        if (_ai == null) {
            synchronized (this) {
                if (_ai == null) {
                    _ai = new CharacterAI(this);
                }
            }
        }
        return _ai;
    }

    /**
     * Method setAI.
     * @param newAI CharacterAI
     */
    public void setAI(CharacterAI newAI) {
        if (newAI == null) {
            return;
        }
        CharacterAI oldAI = _ai;
        synchronized (this) {
            _ai = newAI;
        }
        if (oldAI != null) {
            if (oldAI.isActive()) {
                oldAI.stopAITask();
                newAI.startAITask();
                newAI.setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
            }
        }
    }

    /**
     * Method setCurrentHp.
     * @param newHp double
     * @param canRessurect boolean
     * @param sendInfo boolean
     * @param _attakerId int
     */
    public final void setCurrentHp(double newHp, boolean canRessurect, boolean sendInfo, int _attakerId) {
        int maxHp = getMaxHp();
        newHp = Math.min(maxHp, Math.max(0, newHp));
        if (_currentHp == newHp) {
            return;
        }
        if ((newHp >= 0.5) && isDead() && !canRessurect) {
            return;
        }
        double hpStart = _currentHp;
        _currentHp = newHp;
        if (isDead.compareAndSet(true, false)) {
            onRevive();
        }
        checkHpMessages(hpStart, _currentHp);
        if (sendInfo) {
            broadcastStatusUpdate();
            sendChanges();
        }
        if (_currentHp < maxHp) {
            startRegeneration();
        }
    }

    /**
     * Method setCurrentHp.
     * @param newHp double
     * @param canRessurect boolean
     */
    public final void setCurrentHp(double newHp, boolean canRessurect) {
        setCurrentHp(newHp, canRessurect, true, 0);
    }

    /**
     * Method setCurrentHp.
     * @param newHp double
     * @param canRessurect boolean
     * @param _attakerObjId int
     */
    public final void setCurrentHp(double newHp, boolean canRessurect, int _attakerObjId) {
        setCurrentHp(newHp, canRessurect, true, _attakerObjId);
    }

    /**
     * Method setCurrentMp.
     * @param newMp double
     * @param sendInfo boolean
     */
    public final void setCurrentMp(double newMp, boolean sendInfo) {
        int maxMp = getMaxMp();
        newMp = Math.min(maxMp, Math.max(0, newMp));
        if (_currentMp == newMp) {
            return;
        }
        if ((newMp >= 0.5) && isDead()) {
            return;
        }
        _currentMp = newMp;
        if (sendInfo) {
            broadcastStatusUpdate();
            sendChanges();
        }
        if (_currentMp < maxMp) {
            startRegeneration();
        }
    }

    /**
     * Method setCurrentMp.
     * @param newMp double
     */
    public final void setCurrentMp(double newMp) {
        setCurrentMp(newMp, true);
    }

    /**
     * Method setCurrentCp.
     * @param newCp double
     * @param sendInfo boolean
     */
    public final void setCurrentCp(double newCp, boolean sendInfo) {
        if (!isPlayer()) {
            return;
        }
        int maxCp = getMaxCp();
        newCp = Math.min(maxCp, Math.max(0, newCp));
        if (_currentCp == newCp) {
            return;
        }
        if ((newCp >= 0.5) && isDead()) {
            return;
        }
        _currentCp = newCp;
        if (sendInfo) {
            broadcastStatusUpdate();
            sendChanges();
        }
        if (_currentCp < maxCp) {
            startRegeneration();
        }
    }

    /**
     * Method setCurrentCp.
     * @param newCp double
     */
    public final void setCurrentCp(double newCp) {
        setCurrentCp(newCp, true);
    }

    /**
     * Method setCurrentHpMp.
     * @param newHp double
     * @param newMp double
     * @param canRessurect boolean
     */
    public void setCurrentHpMp(double newHp, double newMp, boolean canRessurect) {
        int maxHp = getMaxHp();
        int maxMp = getMaxMp();
        newHp = Math.min(maxHp, Math.max(0, newHp));
        newMp = Math.min(maxMp, Math.max(0, newMp));
        if ((_currentHp == newHp) && (_currentMp == newMp)) {
            return;
        }
        if ((newHp >= 0.5) && isDead() && !canRessurect) {
            return;
        }
        double hpStart = _currentHp;
        _currentHp = newHp;
        _currentMp = newMp;
        if (isDead.compareAndSet(true, false)) {
            onRevive();
        }
        checkHpMessages(hpStart, _currentHp);
        broadcastStatusUpdate();
        sendChanges();
        if ((_currentHp < maxHp) || (_currentMp < maxMp)) {
            startRegeneration();
        }
    }

    /**
     * Method setCurrentHpMp.
     * @param newHp double
     * @param newMp double
     */
    public void setCurrentHpMp(double newHp, double newMp) {
        setCurrentHpMp(newHp, newMp, false);
    }

    /**
     * Method setFlying.
     * @param mode boolean
     */
    public final void setFlying(boolean mode) {
        _flying = mode;
    }

    /**
     * Method getHeading.
     * @return int
     */
    @Override
    public final int getHeading() {
        return _heading;
    }

    /**
     * Method setHeading.
     * @param heading int
     */
    public void setHeading(int heading) {
        _heading = heading;
    }

    /**
     * Method setIsTeleporting.
     * @param value boolean
     */
    public final void setIsTeleporting(boolean value) {
        isTeleporting.compareAndSet(!value, value);
    }

    /**
     * Method setName.
     * @param name String
     */
    public final void setName(String name) {
        _name = name;
    }

    /**
     * Method getCastingTarget.
     * @return Creature
     */
    public Creature getCastingTarget() {
        return castingTarget.get();
    }

    /**
     * Method setCastingTarget.
     * @param target Creature
     */
    public void setCastingTarget(Creature target) {
        if (target == null) {
            castingTarget = HardReferences.emptyRef();
        } else {
            castingTarget = target.getRef();
        }
    }

    /**
     * Method setRunning.
     */
    public final void setRunning() {
        if (!_running) {
            _running = true;
            broadcastPacket(new ChangeMoveType(this));
        }
    }

    /**
     * Method setSkillMastery.
     * @param skill Integer
     * @param mastery int
     */
    public void setSkillMastery(Integer skill, int mastery) {
        if (_skillMastery == null) {
            _skillMastery = new HashMap<Integer, Integer>();
        }
        _skillMastery.put(skill, mastery);
    }

    /**
     * Method setAggressionTarget.
     * @param target Creature
     */
    public void setAggressionTarget(Creature target) {
        if (target == null) {
            _aggressionTarget = HardReferences.emptyRef();
        } else {
            _aggressionTarget = target.getRef();
        }
    }

    /**
     * Method getAggressionTarget.
     * @return Creature
     */
    public Creature getAggressionTarget() {
        return _aggressionTarget.get();
    }

    /**
     * Method setTarget.
     * @param object GameObject
     */
    public void setTarget(GameObject object) {
        if ((object != null) && !object.isVisible()) {
            object = null;
        }
        if (object == null) {
            target = HardReferences.emptyRef();
        } else {
            target = object.getRef();
        }
    }

    /**
     * Method setTitle.
     * @param title String
     */
    public void setTitle(String title) {
        _title = title;
    }

    /**
     * Method setWalking.
     */
    public void setWalking() {
        if (_running) {
            _running = false;
            broadcastPacket(new ChangeMoveType(this));
        }
    }

    /**
     * Method startAbnormalEffect.
     * @param ae AbnormalEffect
     */
    public void startAbnormalEffect(AbnormalEffect ae) {
        if (ae == AbnormalEffect.NULL) {
            _abnormalEffects = AbnormalEffect.NULL.getMask();
            _abnormalEffects2 = AbnormalEffect.NULL.getMask();
            _abnormalEffects3 = AbnormalEffect.NULL.getMask();
            _aveList.clear();
        } else if (ae.isSpecial()) {
            _abnormalEffects2 |= ae.getMask();
            addToAveList(ae.getId());
        } else if (ae.isEvent()) {
            _abnormalEffects3 |= ae.getMask();
            addToAveList(ae.getId());
        } else {
            _abnormalEffects |= ae.getMask();
            addToAveList(ae.getId());
        }
        sendChanges();
        broadcastCharInfo();
    }

    /**
     * Method startAttackStanceTask.
     */
    public void startAttackStanceTask() {
        startAttackStanceTask0();
    }

    /**
     * Method startAttackStanceTask0.
     */
    protected void startAttackStanceTask0() {
        if (isInCombat()) {
            _stanceEndTime = System.currentTimeMillis() + 15000L;
            return;
        }
        _stanceEndTime = System.currentTimeMillis() + 15000L;
        broadcastPacket(new AutoAttackStart(getObjectId()));
        final Future<?> task = _stanceTask;
        if (task != null) {
            task.cancel(false);
        }
        _stanceTask = LazyPrecisionTaskManager.getInstance().scheduleAtFixedRate(
                _stanceTaskRunnable == null ? _stanceTaskRunnable = new AttackStanceTask() : _stanceTaskRunnable,
                1000L, 1000L);
    }

    /**
     * Method stopAttackStanceTask.
     */
    public void stopAttackStanceTask() {
        _stanceEndTime = 0L;
        final Future<?> task = _stanceTask;
        if (task != null) {
            task.cancel(false);
            _stanceTask = null;
            broadcastPacket(new AutoAttackStop(getObjectId()));
        }
    }

    /**
     * @author Mobius
     */
    private class AttackStanceTask extends RunnableImpl {
        /**
         * Constructor for AttackStanceTask.
         */
        public AttackStanceTask() {
            // TODO Auto-generated constructor stub
        }

        /**
         * Method runImpl.
         */
        @Override
        public void runImpl() {
            if (!isInCombat()) {
                stopAttackStanceTask();
            }
        }
    }

    /**
     * Method stopRegeneration.
     */
    protected void stopRegeneration() {
        regenLock.lock();
        try {
            if (_isRegenerating) {
                _isRegenerating = false;
                if (_regenTask != null) {
                    _regenTask.cancel(false);
                    _regenTask = null;
                }
            }
        } finally {
            regenLock.unlock();
        }
    }

    /**
     * Method startRegeneration.
     */
    protected void startRegeneration() {
        if (!isVisible() || isDead() || (getRegenTick() == 0L)) {
            return;
        }
        if (_isRegenerating) {
            return;
        }
        regenLock.lock();
        try {
            if (!_isRegenerating) {
                _isRegenerating = true;
                _regenTask = RegenTaskManager.getInstance().scheduleAtFixedRate(
                        _regenTaskRunnable == null ? _regenTaskRunnable = new RegenTask() : _regenTaskRunnable, 0,
                        getRegenTick());
            }
        } finally {
            regenLock.unlock();
        }
    }

    /**
     * Method getRegenTick.
     * @return long
     */
    public long getRegenTick() {
        return 3000L;
    }

    /**
     * @author Mobius
     */
    private class RegenTask implements Runnable {
        /**
         * Constructor for RegenTask.
         */
        public RegenTask() {
            // TODO Auto-generated constructor stub
        }

        /**
         * Method run.
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            if (isAlikeDead() || (getRegenTick() == 0L)) {
                return;
            }
            double hpStart = _currentHp;
            int maxHp = getMaxHp();
            int maxMp = getMaxMp();
            int maxCp = isPlayer() ? getMaxCp() : 0;
            double addHp = 0.;
            double addMp = 0.;
            regenLock.lock();
            try {
                if (_currentHp < maxHp) {
                    addHp += Formulas.calcHpRegen(Creature.this);
                }
                if (_currentMp < maxMp) {
                    addMp += Formulas.calcMpRegen(Creature.this);
                }
                if (isPlayer() && Config.REGEN_SIT_WAIT) {
                    Player pl = (Player) Creature.this;
                    if (pl.isSitting()) {
                        pl.updateWaitSitTime();
                        if (pl.getWaitSitTime() > 5) {
                            addHp += pl.getWaitSitTime();
                            addMp += pl.getWaitSitTime();
                        }
                    }
                } else if (isRaid()) {
                    addHp *= Config.RATE_RAID_REGEN;
                    addMp *= Config.RATE_RAID_REGEN;
                }
                _currentHp += Math.max(0,
                        Math.min(addHp, ((calcStat(Stats.HP_LIMIT, null, null) * maxHp) / 100.) - _currentHp));
                _currentMp += Math.max(0,
                        Math.min(addMp, ((calcStat(Stats.MP_LIMIT, null, null) * maxMp) / 100.) - _currentMp));
                _currentHp = Math.min(maxHp, _currentHp);
                _currentMp = Math.min(maxMp, _currentMp);
                if (isPlayer()) {
                    _currentCp += Math.max(0, Math.min(Formulas.calcCpRegen(Creature.this),
                            ((calcStat(Stats.CP_LIMIT, null, null) * maxCp) / 100.) - _currentCp));
                    _currentCp = Math.min(maxCp, _currentCp);
                }
                if ((_currentHp == maxHp) && (_currentMp == maxMp) && (_currentCp == maxCp)) {
                    stopRegeneration();
                }
            } finally {
                regenLock.unlock();
            }
            broadcastStatusUpdate();
            sendChanges();
            checkHpMessages(hpStart, _currentHp);
        }
    }

    /**
     * Method stopAbnormalEffect.
     * @param ae AbnormalEffect
     */
    public void stopAbnormalEffect(AbnormalEffect ae) {
        if (ae.isSpecial()) {
            _abnormalEffects2 &= ~ae.getMask();
            removeFromAveList(ae.getId());
        } else if (ae.isEvent()) {
            _abnormalEffects3 &= ~ae.getMask();
            removeFromAveList(ae.getId());
        } else {
            _abnormalEffects &= ~ae.getMask();
            removeFromAveList(ae.getId());
        }
        sendChanges();
        broadcastCharInfo();
    }

    /**
     * Method block.
     */
    public void block() {
        _blocked = true;
    }

    /**
     * Method unblock.
     */
    public void unblock() {
        _blocked = false;
    }

    /**
     * Method startConfused.
     * @return boolean
     */
    public boolean startConfused() {
        return _confused.getAndSet(true);
    }

    /**
     * Method stopConfused.
     * @return boolean
     */
    public boolean stopConfused() {
        return _confused.setAndGet(false);
    }

    /**
     * Method startFear.
     * @return boolean
     */
    public boolean startFear() {
        return _afraid.getAndSet(true);
    }

    /**
     * Method stopFear.
     * @return boolean
     */
    public boolean stopFear() {
        return _afraid.setAndGet(false);
    }

    /**
     * Method startMuted.
     * @return boolean
     */
    public boolean startMuted() {
        return _muted.getAndSet(true);
    }

    /**
     * Method stopMuted.
     * @return boolean
     */
    public boolean stopMuted() {
        return _muted.setAndGet(false);
    }

    /**
     * Method startPMuted.
     * @return boolean
     */
    public boolean startPMuted() {
        return _pmuted.getAndSet(true);
    }

    /**
     * Method stopPMuted.
     * @return boolean
     */
    public boolean stopPMuted() {
        return _pmuted.setAndGet(false);
    }

    /**
     * Method startAMuted.
     * @return boolean
     */
    public boolean startAMuted() {
        return _amuted.getAndSet(true);
    }

    /**
     * Method stopAMuted.
     * @return boolean
     */
    public boolean stopAMuted() {
        return _amuted.setAndGet(false);
    }

    /**
     * Method startRooted.
     * @return boolean
     */
    public boolean startRooted() {
        return _rooted.getAndSet(true);
    }

    /**
     * Method stopRooted.
     * @return boolean
     */
    public boolean stopRooted() {
        return _rooted.setAndGet(false);
    }

    /**
     * Method startSleeping.
     * @return boolean
     */
    public boolean startSleeping() {
        return _sleeping.getAndSet(true);
    }

    /**
     * Method stopSleeping.
     * @return boolean
     */
    public boolean stopSleeping() {
        return _sleeping.setAndGet(false);
    }

    /**
     * Method startAirbinding.
     * @return boolean
     */
    public boolean startAirbinding() {
        return _airbinded.getAndSet(true);
    }

    /**
     * Method stopAirbinding.
     * @return boolean
     */
    public boolean stopAirbinding() {
        return _airbinded.setAndGet(false);
    }

    /**
     * Method startKnockingback.
     * @return boolean
     */
    public boolean startKnockingback() {
        return _knockedback.getAndSet(true);
    }

    /**
     * Method stopKnockingback.
     * @return boolean
     */
    public boolean stopKnockingback() {
        return _knockedback.setAndGet(false);
    }

    /**
     * Method startKnockingback.
     * @return boolean
     */
    public boolean startKnockingdown() {
        return _knockeddown.getAndSet(true);
    }

    /**
     * Method stopKnockingback.
     * @return boolean
     */
    public boolean stopKnockingdown() {
        return _knockeddown.setAndGet(false);
    }

    /**
     * Method startPulling For chain skills.
     * @return boolean
     */
    public boolean startPulling() {
        return _pulled.getAndSet(true);
    }

    /**
     * Method stopKnockingback for chain skill.
     * @return boolean
     */
    public boolean stopPulling() {
        return _pulled.setAndGet(false);
    }

    /**
     * Method startStunning.
     * @return boolean
     */
    public boolean startStunning() {
        return _stunned.getAndSet(true);
    }

    /**
     * Method stopStunning.
     * @return boolean
     */
    public boolean stopStunning() {
        return _stunned.setAndGet(false);
    }

    /**
     * Method startParalyzed.
     * @return boolean
     */
    public boolean startParalyzed() {
        return _paralyzed.getAndSet(true);
    }

    /**
     * Method stopParalyzed.
     * @return boolean
     */
    public boolean stopParalyzed() {
        return _paralyzed.setAndGet(false);
    }

    /**
     * Method startImmobilized.
     * @return boolean
     */
    public boolean startImmobilized() {
        return _immobilized.getAndSet(true);
    }

    /**
     * Method stopImmobilized.
     * @return boolean
     */
    public boolean stopImmobilized() {
        return _immobilized.setAndGet(false);
    }

    /**
     * Method startHealBlocked.
     * @return boolean
     */
    public boolean startHealBlocked() {
        return _healBlocked.getAndSet(true);
    }

    /**
     * Method stopHealBlocked.
     * @return boolean
     */
    public boolean stopHealBlocked() {
        return _healBlocked.setAndGet(false);
    }

    /**
     * Method startDamageBlocked.
     * @return boolean
     */
    public boolean startDamageBlocked() {
        return _damageBlocked.getAndSet(true);
    }

    /**
     * Method stopDamageBlocked.
     * @return boolean
     */
    public boolean stopDamageBlocked() {
        return _damageBlocked.setAndGet(false);
    }

    /**
     * Method startBuffImmunity.
     * @return boolean
     */
    public boolean startBuffImmunity() {
        return _buffImmunity.getAndSet(true);
    }

    /**
     * Method stopBuffImmunity.
     * @return boolean
     */
    public boolean stopBuffImmunity() {
        return _buffImmunity.setAndGet(false);
    }

    /**
     * Method startDebuffImmunity.
     * @return boolean
     */
    public boolean startDebuffImmunity() {
        return _debuffImmunity.getAndSet(true);
    }

    /**
     * Method stopDebuffImmunity.
     * @return boolean
     */
    public boolean stopDebuffImmunity() {
        return _debuffImmunity.setAndGet(false);
    }

    /**
     * Method startEffectImmunity.
     * @return boolean
     */
    public boolean startEffectImmunity() {
        return _effectImmunity.getAndSet(true);
    }

    /**
     * Method stopEffectImmunity.
     * @return boolean
     */
    public boolean stopEffectImmunity() {
        return _effectImmunity.setAndGet(false);
    }

    /**
     * Method startWeaponEquipBlocked.
     * @return boolean
     */
    public boolean startWeaponEquipBlocked() {
        return _weaponEquipBlocked.getAndSet(true);
    }

    /**
     * Method stopWeaponEquipBlocked.
     * @return boolean
     */
    public boolean stopWeaponEquipBlocked() {
        return _weaponEquipBlocked.getAndSet(false);
    }

    /**
     * Method startFrozen.
     * @return boolean
     */
    public boolean startFrozen() {
        return _frozen.getAndSet(true);
    }

    /**
     * Method stopFrozen.
     * @return boolean
     */
    public boolean stopFrozen() {
        return _frozen.setAndGet(false);
    }

    /**
     * Method setFakeDeath.
     * @param value boolean
     */
    public void setFakeDeath(boolean value) {
        _fakeDeath = value;
    }

    /**
     * Method breakFakeDeath.
     */
    public void breakFakeDeath() {
        getEffectList().stopAllSkillEffects(EffectType.FakeDeath);
    }

    /**
     * Method setMeditated.
     * @param value boolean
     */
    public void setMeditated(boolean value) {
        _meditated = value;
    }

    /**
     * Method setIsBlessedByNoblesse.
     * @param value boolean
     */
    public final void setIsBlessedByNoblesse(boolean value) {
        _isBlessedByNoblesse = value;
    }

    /**
     * Method setIsSalvation.
     * @param value boolean
     */
    public final void setIsSalvation(boolean value) {
        _isSalvation = value;
    }

    /**
     * Method setIsInvul.
     * @param value boolean
     */
    public void setIsInvul(boolean value) {
        _isInvul = value;
    }

    /**
     * Method setLockedTarget.
     * @param value boolean
     */
    public void setLockedTarget(boolean value) {
        _lockedTarget = value;
    }

    /**
     * Method setTargetable.
     * @param value boolean
     */
    public void setTargetable(boolean value) {
        _isTargetable = value;
    }

    /**
     * Method isConfused.
     * @return boolean
     */
    public boolean isConfused() {
        return _confused.get();
    }

    /**
     * Method isFakeDeath.
     * @return boolean
     */
    public boolean isFakeDeath() {
        return _fakeDeath;
    }

    /**
     * Method isAfraid.
     * @return boolean
     */
    public boolean isAfraid() {
        return _afraid.get();
    }

    /**
     * Method isBlocked.
     * @return boolean
     */
    public boolean isBlocked() {
        return _blocked;
    }

    /**
     * Method isMuted.
     * @param skill Skill
     * @return boolean
     */
    public boolean isMuted(Skill skill) {
        if ((skill == null) || skill.isNotAffectedByMute()) {
            return false;
        }
        return (isMMuted() && skill.isMagic()) || (isPMuted() && !skill.isMagic());
    }

    /**
     * Method isPMuted.
     * @return boolean
     */
    public boolean isPMuted() {
        return _pmuted.get();
    }

    /**
     * Method isMMuted.
     * @return boolean
     */
    public boolean isMMuted() {
        return _muted.get();
    }

    /**
     * Method isAMuted.
     * @return boolean
     */
    public boolean isAMuted() {
        return _amuted.get();
    }

    /**
     * Method isRooted.
     * @return boolean
     */
    public boolean isRooted() {
        return _rooted.get();
    }

    /**
     * Method isSleeping.
     * @return boolean
     */
    public boolean isSleeping() {
        return _sleeping.get();
    }

    /**
     * Method isStunned.
     * @return boolean
     */
    public boolean isStunned() {
        return _stunned.get();
    }

    /**
     * Method isAirBinded.
     * @return boolean
     */
    public boolean isAirBinded() {
        return _airbinded.get();
    }

    /**
     * Method isKnockedBack.
     * @return boolean
     */
    public boolean isKnockedBack() {
        return _knockedback.get();
    }

    /**
     * Method isKnockedDown.
     * @return boolean
     */
    public boolean isKnockedDown() {
        return _knockeddown.get();
    }

    /**
     * Method isPulledNow.
     * @return boolean
     */
    public boolean isPulledNow() {
        return _pulled.get();
    }

    /**
     * Method isMeditated.
     * @return boolean
     */
    public boolean isMeditated() {
        return _meditated;
    }

    /**
     * Method isWeaponEquipBlocked.
     * @return boolean
     */
    public boolean isWeaponEquipBlocked() {
        return _weaponEquipBlocked.get();
    }

    /**
     * Method isParalyzed.
     * @return boolean
     */
    public boolean isParalyzed() {
        return _paralyzed.get();
    }

    /**
     * Method isFrozen.
     * @return boolean
     */
    public boolean isFrozen() {
        return _frozen.get();
    }

    /**
     * Method isImmobilized.
     * @return boolean
     */
    public boolean isImmobilized() {
        return _immobilized.get() || (getRunSpeed() < 1);
    }

    /**
     * Method isHealBlocked.
     * @return boolean
     */
    public boolean isHealBlocked() {
        return isAlikeDead() || _healBlocked.get();
    }

    /**
     * Method isDamageBlocked.
     * @return boolean
     */
    public boolean isDamageBlocked() {
        return isInvul() || _damageBlocked.get();
    }

    /**
     * Method isCastingNow.
     * @return boolean
     */
    public boolean isCastingNow() {
        return _skillTask != null;
    }

    /**
     * Method isDoubleCastingNow.
     * @return boolean
     */
    public boolean isDoubleCastingNow() {
        return _skillDoubleTask != null;
    }

    /**
     * Method isLockedTarget.
     * @return boolean
     */
    public boolean isLockedTarget() {
        return _lockedTarget;
    }

    /**
     * Method isTargetable.
     * @return boolean
     */
    public boolean isTargetable() {
        return _isTargetable;
    }

    /**
     * Method isMovementDisabled.
     * @return boolean
     */
    public boolean isMovementDisabled() {
        return isBlocked() || isRooted() || isImmobilized() || isAlikeDead() || isStunned() || isSleeping()
                || isParalyzed() || isAttackingNow() || isCastingNow() || isFrozen() || isAirBinded()
                || isKnockedBack() || isKnockedDown() || isPulledNow();
    }

    /**
     * Method isActionsDisabled.
     * @return boolean
     */
    public boolean isActionsDisabled() {
        return isBlocked() || isAlikeDead() || isStunned() || isSleeping() || isParalyzed() || isAttackingNow()
                || isCastingNow() || isFrozen() || isAirBinded() || isKnockedBack() || isKnockedDown()
                || isPulledNow();
    }

    /**
     * Method isAttackingDisabled.
     * @return boolean
     */
    public final boolean isAttackingDisabled() {
        return _attackReuseEndTime > System.currentTimeMillis();
    }

    /**
     * Method isOutOfControl.
     * @return boolean
     */
    public boolean isOutOfControl() {
        return isBlocked() || isConfused() || isAfraid();
    }

    /**
     * Method teleToLocation.
     * @param loc Location
     */
    public void teleToLocation(Location loc) {
        teleToLocation(loc.x, loc.y, loc.z, getReflection());
    }

    /**
     * Method teleToLocation.
     * @param loc Location
     * @param refId int
     */
    public void teleToLocation(Location loc, int refId) {
        teleToLocation(loc.x, loc.y, loc.z, refId);
    }

    /**
     * Method teleToLocation.
     * @param loc Location
     * @param r Reflection
     */
    public void teleToLocation(Location loc, Reflection r) {
        teleToLocation(loc.x, loc.y, loc.z, r);
    }

    /**
     * Method teleToLocation.
     * @param x int
     * @param y int
     * @param z int
     */
    public void teleToLocation(int x, int y, int z) {
        teleToLocation(x, y, z, getReflection());
    }

    /**
     * Method checkAndRemoveInvisible.
     */
    public void checkAndRemoveInvisible() {
        InvisibleType invisibleType = getInvisibleType();
        if (invisibleType == InvisibleType.EFFECT) {
            getEffectList().stopEffects(EffectType.Invisible);
        }
    }

    /**
     * Method teleToLocation.
     * @param x int
     * @param y int
     * @param z int
     * @param refId int
     */
    public void teleToLocation(int x, int y, int z, int refId) {
        Reflection r = ReflectionManager.getInstance().get(refId);
        if (r == null) {
            return;
        }
        teleToLocation(x, y, z, r);
    }

    /**
     * Method teleToLocation.
     * @param x int
     * @param y int
     * @param z int
     * @param r Reflection
     */
    public void teleToLocation(int x, int y, int z, Reflection r) {
        if (!isTeleporting.compareAndSet(false, true)) {
            return;
        }
        if (isFakeDeath()) {
            breakFakeDeath();
        }
        abortCast(true, false);
        if (!isLockedTarget()) {
            setTarget(null);
        }
        stopMove();
        if (!isBoat() && !isFlying() && !World.isWater(new Location(x, y, z), r)) {
            z = GeoEngine.getHeight(x, y, z, r.getGeoIndex());
        }
        if (isPlayer()) {
            Player player = (Player) this;
            player.getListeners().onTeleport(x, y, z, r);
            decayMe();
            setXYZ(x, y, z);
            setReflection(r);
            player.setLastClientPosition(null);
            player.setLastServerPosition(null);
            player.sendPacket(new TeleportToLocation(player, x, y, z));
            player.sendPacket(new ExTeleportToLocationActivate(player, x, y, z));
        } else {
            setXYZ(x, y, z);
            setReflection(r);
            broadcastPacket(new TeleportToLocation(this, x, y, z));
            onTeleported();
        }
    }

    /**
     * Method onTeleported.
     * @return boolean
     */
    public boolean onTeleported() {
        return isTeleporting.compareAndSet(true, false);
    }

    /**
     * Method sendMessage.
     * @param message CustomMessage
     */
    public void sendMessage(CustomMessage message) {
    }

    /**
     * Method toString.
     * @return String
     */
    @Override
    public String toString() {
        return getClass().getSimpleName() + "[" + getObjectId() + "]";
    }

    /**
     * Method getEffectList.
     * @return EffectList
     */
    public EffectList getEffectList() {
        if (_effectList == null) {
            synchronized (this) {
                if (_effectList == null) {
                    _effectList = new EffectList(this);
                }
            }
        }
        return _effectList;
    }

    /**
     * Method paralizeOnAttack.
     * @param attacker Creature
     * @return boolean
     */
    public boolean paralizeOnAttack(Creature attacker) {
        int max_attacker_level = 0xFFFF;
        MonsterInstance leader;
        if (isRaid()
                || (isMinion() && ((leader = ((MinionInstance) this).getLeader()) != null) && leader.isRaid())) {
            max_attacker_level = getLevel() + Config.RAID_MAX_LEVEL_DIFF;
        } else if (isNpc()) {
            int max_level_diff = ((NpcInstance) this).getParameter("ParalizeOnAttack", -1000);
            if (max_level_diff != -1000) {
                max_attacker_level = getLevel() + max_level_diff;
            }
        }
        if (attacker.getLevel() > max_attacker_level) {
            return true;
        }
        return false;
    }

    /**
     * Method onDelete.
     */
    @Override
    protected void onDelete() {
        GameObjectsStorage.remove(_storedId);
        getEffectList().stopAllEffects();
        super.onDelete();
    }

    /**
     * Method addExpAndSp.
     * @param exp long
     * @param sp long
     */
    public void addExpAndSp(long exp, long sp) {
    }

    /**
     * Method broadcastCharInfo.
     */
    public void broadcastCharInfo() {
    }

    /**
     * Method checkHpMessages.
     * @param currentHp double
     * @param newHp double
     */
    public void checkHpMessages(double currentHp, double newHp) {
    }

    /**
     * Method checkPvP.
     * @param target Creature
     * @param skill Skill
     * @return boolean
     */
    public boolean checkPvP(Creature target, Skill skill) {
        return false;
    }

    /**
     * Method consumeItem.
     * @param itemConsumeId int
     * @param itemCount long
     * @return boolean
     */
    public boolean consumeItem(int itemConsumeId, long itemCount) {
        return true;
    }

    /**
     * Method consumeItemMp.
     * @param itemId int
     * @param mp int
     * @return boolean
     */
    public boolean consumeItemMp(int itemId, int mp) {
        return true;
    }

    /**
     * Method isFearImmune.
     * @return boolean
     */
    public boolean isFearImmune() {
        return false;
    }

    /**
     * Method isLethalImmune.
     * @return boolean
     */
    public boolean isLethalImmune() {
        return getMaxHp() >= 50000;
    }

    /**
     * Method getChargedSoulShot.
     * @return boolean
     */
    public boolean getChargedSoulShot() {
        return false;
    }

    /**
     * Method getChargedSpiritShot.
     * @return int
     */
    public int getChargedSpiritShot() {
        return 0;
    }

    /**
     * Method getIncreasedForce.
     * @return int
     */
    public int getIncreasedForce() {
        return 0;
    }

    /**
     * Method getConsumedSouls.
     * @return int
     */
    public int getConsumedSouls() {
        return 0;
    }

    /**
     * Method getAgathionEnergy.
     * @return int
     */
    public int getAgathionEnergy() {
        return 0;
    }

    /**
     * Method setAgathionEnergy.
     * @param val int
     */
    public void setAgathionEnergy(int val) {
    }

    /**
     * Method getKarma.
     * @return int
     */
    public int getKarma() {
        return 0;
    }

    /**
     * Method getLevelMod.
     * @return double
     */
    public double getLevelMod() {
        return 1;
    }

    /**
     * Method getNpcId.
     * @return int
     */
    public int getNpcId() {
        return 0;
    }

    /**
     * Method getPvpFlag.
     * @return int
     */
    public int getPvpFlag() {
        return 0;
    }

    /**
     * Method setTeam.
     * @param t TeamType
     */
    public void setTeam(TeamType t) {
        _team = t;
        sendChanges();
    }

    /**
     * Method getTeam.
     * @return TeamType
     */
    public TeamType getTeam() {
        return _team;
    }

    /**
     * Method isUndead.
     * @return boolean
     */
    public boolean isUndead() {
        return false;
    }

    /**
     * Method isParalyzeImmune.
     * @return boolean
     */
    public boolean isParalyzeImmune() {
        return false;
    }

    /**
     * Method reduceArrowCount.
     */
    public void reduceArrowCount() {
    }

    /**
     * Method sendChanges.
     */
    public void sendChanges() {
        getStatsRecorder().sendChanges();
    }

    /**
     * Method sendMessage.
     * @param message String
     */
    public void sendMessage(String message) {
    }

    /**
     * Method sendPacket.
     * @param mov IStaticPacket
     */
    public void sendPacket(IStaticPacket mov) {
    }

    /**
     * Method sendPacket.
     * @param mov IStaticPacket[]
     */
    public void sendPacket(IStaticPacket... mov) {
    }

    /**
     * Method sendPacket.
     * @param mov List<? extends IStaticPacket>
     */
    public void sendPacket(List<? extends IStaticPacket> mov) {
    }

    /**
     * Method setIncreasedForce.
     * @param i int
     */
    public void setIncreasedForce(int i) {
    }

    /**
     * Method setConsumedSouls.
     * @param i int
     * @param monster NpcInstance
     */
    public void setConsumedSouls(int i, NpcInstance monster) {
    }

    /**
     * Method startPvPFlag.
     * @param target Creature
     */
    public void startPvPFlag(Creature target) {
    }

    /**
     * Method unChargeShots.
     * @param spirit boolean
     * @return boolean
     */
    public boolean unChargeShots(boolean spirit) {
        return false;
    }

    /**
     * Method updateEffectIcons.
     */
    public void updateEffectIcons() {
    }

    /**
     * Method refreshHpMpCp.
     */
    protected void refreshHpMpCp() {
        final int maxHp = getMaxHp();
        final int maxMp = getMaxMp();
        final int maxCp = isPlayer() ? getMaxCp() : 0;
        if (_currentHp > maxHp) {
            setCurrentHp(maxHp, false);
        }
        if (_currentMp > maxMp) {
            setCurrentMp(maxMp, false);
        }
        if (_currentCp > maxCp) {
            setCurrentCp(maxCp, false);
        }
        if ((_currentHp < maxHp) || (_currentMp < maxMp) || (_currentCp < maxCp)) {
            startRegeneration();
        }
    }

    /**
     * Method updateStats.
     */
    public void updateStats() {
        refreshHpMpCp();
        sendChanges();
    }

    /**
     * Method setOverhitAttacker.
     * @param attacker Creature
     */
    public void setOverhitAttacker(Creature attacker) {
    }

    /**
     * Method setOverhitDamage.
     * @param damage double
     */
    public void setOverhitDamage(double damage) {
    }

    /**
     * Method isCursedWeaponEquipped.
     * @return boolean
     */
    public boolean isCursedWeaponEquipped() {
        return false;
    }

    /**
     * Method isHero.
     * @return boolean
     */
    public boolean isHero() {
        return false;
    }

    /**
     * Method getAccessLevel.
     * @return int
     */
    public int getAccessLevel() {
        return 0;
    }

    /**
     * Method getClan.
     * @return Clan
     */
    public Clan getClan() {
        return null;
    }

    /**
     * Method getRateAdena.
     * @return double
     */
    public double getRateAdena() {
        return 1.;
    }

    /**
     * Method getRateItems.
     * @return double
     */
    public double getRateItems() {
        return 1.;
    }

    /**
     * Method getRateExp.
     * @return double
     */
    public double getRateExp() {
        return 1.;
    }

    /**
     * Method getRateSp.
     * @return double
     */
    public double getRateSp() {
        return 1.;
    }

    /**
     * Method getRateSpoil.
     * @return double
     */
    public double getRateSpoil() {
        return 1.;
    }

    /**
     * Method getFormId.
     * @return int
     */
    public int getFormId() {
        return 0;
    }

    /**
     * Method isNameAbove.
     * @return boolean
     */
    public boolean isNameAbove() {
        return true;
    }

    /**
     * Method setLoc.
     * @param loc Location
     */
    @Override
    public void setLoc(Location loc) {
        setXYZ(loc.x, loc.y, loc.z);
    }

    /**
     * Method setLoc.
     * @param loc Location
     * @param MoveTask boolean
     */
    public void setLoc(Location loc, boolean MoveTask) {
        setXYZ(loc.x, loc.y, loc.z, MoveTask);
    }

    /**
     * Method setXYZ.
     * @param x int
     * @param y int
     * @param z int
     */
    @Override
    public void setXYZ(int x, int y, int z) {
        setXYZ(x, y, z, false);
    }

    /**
     * Method setXYZ.
     * @param x int
     * @param y int
     * @param z int
     * @param MoveTask boolean
     */
    public void setXYZ(int x, int y, int z, boolean MoveTask) {
        if (!MoveTask) {
            stopMove();
        }
        moveLock.lock();
        try {
            super.setXYZ(x, y, z);
        } finally {
            moveLock.unlock();
        }
        updateZones();
    }

    /**
     * Method onSpawn.
     */
    @Override
    protected void onSpawn() {
        super.onSpawn();
        updateStats();
        updateZones();
    }

    /**
     * Method spawnMe.
     * @param loc Location
     */
    @Override
    public void spawnMe(Location loc) {
        if (loc.h > 0) {
            setHeading(loc.h);
        }
        super.spawnMe(loc);
    }

    /**
     * Method onDespawn.
     */
    @Override
    protected void onDespawn() {
        if (!isLockedTarget()) {
            setTarget(null);
        }
        stopMove();
        stopAttackStanceTask();
        stopRegeneration();
        updateZones();
        clearStatusListeners();
        super.onDespawn();
    }

    /**
     * Method doDecay.
     */
    public final void doDecay() {
        if (!isDead()) {
            return;
        }
        onDecay();
    }

    /**
     * Method onDecay.
     */
    protected void onDecay() {
        decayMe();
    }

    /**
     * Method validateLocation.
     * @param broadcast int
     */
    public void validateLocation(int broadcast) {
        L2GameServerPacket sp = new ValidateLocation(this);
        if (broadcast == 0) {
            sendPacket(sp);
        } else if (broadcast == 1) {
            broadcastPacket(sp);
        } else {
            broadcastPacketToOthers(sp);
        }
    }

    /**
     * Field _unActiveSkills.
     */
    private final TIntHashSet _unActiveSkills = new TIntHashSet();

    /**
     * Method addUnActiveSkill.
     * @param skill Skill
     */
    public void addUnActiveSkill(Skill skill) {
        if ((skill == null) || isUnActiveSkill(skill.getId())) {
            return;
        }
        removeStatsOwner(skill);
        removeTriggers(skill);
        _unActiveSkills.add(skill.getId());
    }

    /**
     * Method removeUnActiveSkill.
     * @param skill Skill
     */
    public void removeUnActiveSkill(Skill skill) {
        if ((skill == null) || !isUnActiveSkill(skill.getId())) {
            return;
        }
        addStatFuncs(skill.getStatFuncs());
        addTriggers(skill);
        _unActiveSkills.remove(skill.getId());
    }

    /**
     * Method isUnActiveSkill.
     * @param id int
     * @return boolean
     */
    public boolean isUnActiveSkill(int id) {
        return _unActiveSkills.contains(id);
    }

    /**
     * Method getLevel.
     * @return int
     */
    public abstract int getLevel();

    /**
     * Method getActiveWeaponInstance.
     * @return ItemInstance
     */
    public abstract ItemInstance getActiveWeaponInstance();

    /**
     * Method getActiveWeaponItem.
     * @return WeaponTemplate
     */
    public abstract WeaponTemplate getActiveWeaponItem();

    /**
     * Method getSecondaryWeaponInstance.
     * @return ItemInstance
     */
    public abstract ItemInstance getSecondaryWeaponInstance();

    /**
     * Method getSecondaryWeaponItem.
     * @return WeaponTemplate
     */
    public abstract WeaponTemplate getSecondaryWeaponItem();

    /**
     * Method getListeners.
     * @return CharListenerList
     */
    public CharListenerList getListeners() {
        if (listeners == null) {
            synchronized (this) {
                if (listeners == null) {
                    listeners = new CharListenerList(this);
                }
            }
        }
        return listeners;
    }

    /**
     * Method addListener.
     * @param listener T
     * @return boolean
     */
    public <T extends Listener<Creature>> boolean addListener(T listener) {
        return getListeners().add(listener);
    }

    /**
     * Method removeListener.
     * @param listener T
     * @return boolean
     */
    public <T extends Listener<Creature>> boolean removeListener(T listener) {
        return getListeners().remove(listener);
    }

    /**
     * Method getStatsRecorder.
     * @return CharStatsChangeRecorder<? extends Creature>
     */
    public CharStatsChangeRecorder<? extends Creature> getStatsRecorder() {
        if (_statsRecorder == null) {
            synchronized (this) {
                if (_statsRecorder == null) {
                    _statsRecorder = new CharStatsChangeRecorder<>(this);
                }
            }
        }
        return _statsRecorder;
    }

    /**
     * Method isCreature.
     * @return boolean
     */
    @Override
    public boolean isCreature() {
        return true;
    }

    /**
     * Method displayGiveDamageMessage.
     * @param target Creature
     * @param damage int
     * @param crit boolean
     * @param miss boolean
     * @param shld boolean
     * @param magic boolean
     */
    public void displayGiveDamageMessage(Creature target, int damage, boolean crit, boolean miss, boolean shld,
            boolean magic) {
        if (miss && target.isPlayer() && !target.isDamageBlocked()) {
            target.sendPacket(
                    new SystemMessage(SystemMessage.C1_HAS_EVADED_C2S_ATTACK).addName(target).addName(this));
        }
    }

    /**
     * Method displayReceiveDamageMessage.
     * @param attacker Creature
     * @param damage int
     */
    public void displayReceiveDamageMessage(Creature attacker, int damage) {
    }

    /**
     * Method getSkillReuses.
     * @return Collection<TimeStamp>
     */
    public Collection<TimeStamp> getSkillReuses() {
        return _skillReuses.values();
    }

    /**
     * Method getSkillReuse.
     * @param skill Skill
     * @return TimeStamp
     */
    public TimeStamp getSkillReuse(Skill skill) {
        return _skillReuses.get(skill.hashCode());
    }

    /**
     * Method isInFlyingTransform.
     * @return boolean
     */
    public boolean isInFlyingTransform() {
        return (_transformationId == 8) || (_transformationId == 9) || (_transformationId == 260);
    }

    /**
     * Method isInMountTransform.
     * @return boolean
     */
    public boolean isInMountTransform() {
        return (_transformationId == 106) || (_transformationId == 109) || (_transformationId == 110)
                || (_transformationId == 20001);
    }

    /**
     * Method setTransformation.
     * @param id int
     */
    public void setTransformation(int id) {
        _transformationId = id;
    }

    /**
     * Method getTransformation.
     * @return int
     */
    public int getTransformation() {
        return _transformationId;
    }

    /**
     * Method getTransformationName.
     * @return String
     */
    public String getTransformationName() {
        return _transformationName;
    }

    /**
     * Method setTransformationName.
     * @param name String
     */
    public void setTransformationName(String name) {
        _transformationName = name;
    }

    /**
     * Method setTransformationTemplate.
     * @param template int
     */
    public void setTransformationTemplate(int template) {
        _transformationTemplate = template;
    }

    /**
     * Method getTransformationTemplate.
     * @return int
     */
    public int getTransformationTemplate() {
        return _transformationTemplate;
    }

    /**
     * Method getHpRegen.
     * @return double
     */
    public double getHpRegen() {
        return calcStat(Stats.REGENERATE_HP_RATE, getTemplate().getBaseHpReg());
    }

    /**
     * Method getMpRegen.
     * @return double
     */
    public double getMpRegen() {
        return calcStat(Stats.REGENERATE_MP_RATE, getTemplate().getBaseMpReg());
    }

    /**
     * Method getCpRegen.
     * @return double
     */
    public double getCpRegen() {
        return 0.0D;
    }

    /**
     * Method getColRadius.
     * @return double
     */
    @Override
    public double getColRadius() {
        return getTemplate().getCollisionRadius();
    }

    /**
     * Method getColHeight.
     * @return double
     */
    @Override
    public double getColHeight() {
        return getTemplate().getCollisionHeight();
    }

    /**
     * Method addDeathImmunity.
     */
    public void addDeathImmunity() {
        _deathImmune = true;
    }

    /**
     * Method removeDeathImmunity.
     */
    public void removeDeathImmunity() {
        _deathImmune = false;
    }

    /**
     * Method isDeathImmune.
     * @return boolean
     */
    public boolean isDeathImmune() {
        return _deathImmune;
    }

    /**
     * Method setWalkerRouteTemplate.
     * @param template WalkerRouteTemplate
     */
    public void setWalkerRouteTemplate(WalkerRouteTemplate template) {
        _walkerRoutesTemplate = template;
    }

    /**
     * Method getWalkerRouteTemplate.
     * @return WalkerRouteTemplate
     */
    public WalkerRouteTemplate getWalkerRouteTemplate() {
        return _walkerRoutesTemplate;
    }

    /**
     * Method setIsEnabledDoubleCast.
     * @param doubleCast boolean
     */
    public void setIsEnabledDoubleCast(boolean doubleCast) {
        _isEnabledDoubleCast = doubleCast;
    }

    /**
     * Method IsEnabledDoubleCast.
     * @return boolean
     */
    public boolean IsEnabledDoubleCast() {
        return _isEnabledDoubleCast;
    }

    /**
     * Method getDefaultMaxHp.
     * @return double
     */
    public double getDefaultMaxHp() {
        return 0;
    }

    /**
     * Method getDefaultMaxMp.
     * @return double
     */
    public double getDefaultMaxMp() {
        return 0;
    }

    public Collection<Summon> getPets() {
        return new ArrayList<Summon>(0);
    }

    public void broadcastEffectsStatusToListeners() {
        if (!isVehicle() && !isDoor()) {
            final ExAbnormalStatusUpdateFromTarget exb = new ExAbnormalStatusUpdateFromTarget(this);

            if (isPlayer() && (getTarget() == this)) {
                sendPacket(exb);
            }

            broadcastToStatusListeners(exb);
        }
    }

    @Override
    public void onActionTarget(final Player player, boolean forced) {
        super.onActionTarget(player, forced);

        if ((player != this) && !isDoor()) {
            validateLocation(0);
        }
    }

    @Override
    public void onActionTargeted(final Player player, boolean forced) {
        if (player == this) {
            return;
        }

        if (isAutoAttackable(player)) {
            player.getAI().Attack(this, false, forced);
            return;
        }

        super.onActionTargeted(player, forced);
    }
}