com.l2jfree.gameserver.gameobjects.L2Creature.java Source code

Java tutorial

Introduction

Here is the source code for com.l2jfree.gameserver.gameobjects.L2Creature.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 com.l2jfree.gameserver.gameobjects;

import static com.l2jfree.gameserver.gameobjects.ai.CtrlIntention.AI_INTENTION_ACTIVE;
import static com.l2jfree.gameserver.gameobjects.ai.CtrlIntention.AI_INTENTION_FOLLOW;
import static com.l2jfree.gameserver.gameobjects.ai.CtrlIntention.AI_INTENTION_IDLE;
import static com.l2jfree.gameserver.gameobjects.ai.CtrlIntention.AI_INTENTION_MOVE_TO;

import java.util.ArrayList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;

import javolution.util.FastMap;

import org.apache.commons.logging.Log;
import org.apache.commons.logging.LogFactory;

import com.l2jfree.Config;
import com.l2jfree.gameserver.Shutdown;
import com.l2jfree.gameserver.Shutdown.DisableType;
import com.l2jfree.gameserver.ThreadPoolManager;
import com.l2jfree.gameserver.datatables.SkillTable;
import com.l2jfree.gameserver.gameobjects.ai.CtrlEvent;
import com.l2jfree.gameserver.gameobjects.ai.CtrlIntention;
import com.l2jfree.gameserver.gameobjects.ai.L2CreatureAI;
import com.l2jfree.gameserver.gameobjects.effects.CreatureEffects;
import com.l2jfree.gameserver.gameobjects.instance.L2AirShipInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2BoatInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2DoorInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2MinionInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2NpcWalkerInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2RiftInvaderInstance;
import com.l2jfree.gameserver.gameobjects.itemcontainer.Inventory;
import com.l2jfree.gameserver.gameobjects.knownlist.CreatureKnownList;
import com.l2jfree.gameserver.gameobjects.shot.CreatureShots;
import com.l2jfree.gameserver.gameobjects.stat.CreatureStat;
import com.l2jfree.gameserver.gameobjects.status.CreatureStatus;
import com.l2jfree.gameserver.gameobjects.templates.L2CreatureTemplate;
import com.l2jfree.gameserver.gameobjects.templates.L2NpcTemplate;
import com.l2jfree.gameserver.gameobjects.view.ICreatureView;
import com.l2jfree.gameserver.geodata.GeoData;
import com.l2jfree.gameserver.geodata.pathfinding.Node;
import com.l2jfree.gameserver.geodata.pathfinding.PathFinding;
import com.l2jfree.gameserver.handler.SkillHandler;
import com.l2jfree.gameserver.instancemanager.GameTimeManager;
import com.l2jfree.gameserver.instancemanager.InstanceManager;
import com.l2jfree.gameserver.model.L2CharPosition;
import com.l2jfree.gameserver.model.Location;
import com.l2jfree.gameserver.model.items.L2ItemInstance;
import com.l2jfree.gameserver.model.items.templates.L2Weapon;
import com.l2jfree.gameserver.model.items.templates.L2WeaponType;
import com.l2jfree.gameserver.model.party.L2Party;
import com.l2jfree.gameserver.model.quest.Quest;
import com.l2jfree.gameserver.model.quest.Quest.QuestEventType;
import com.l2jfree.gameserver.model.quest.QuestState;
import com.l2jfree.gameserver.model.restriction.global.GlobalRestrictions;
import com.l2jfree.gameserver.model.skills.Calculator;
import com.l2jfree.gameserver.model.skills.ChanceSkillList;
import com.l2jfree.gameserver.model.skills.Formulas;
import com.l2jfree.gameserver.model.skills.FusionSkill;
import com.l2jfree.gameserver.model.skills.IChanceSkillTrigger;
import com.l2jfree.gameserver.model.skills.L2Skill;
import com.l2jfree.gameserver.model.skills.L2Skill.SkillTargetType;
import com.l2jfree.gameserver.model.skills.SkillUsageRequest;
import com.l2jfree.gameserver.model.skills.Stats;
import com.l2jfree.gameserver.model.skills.effects.AbnormalEffect;
import com.l2jfree.gameserver.model.skills.effects.L2Effect;
import com.l2jfree.gameserver.model.skills.effects.SpecialEffect;
import com.l2jfree.gameserver.model.skills.funcs.Func;
import com.l2jfree.gameserver.model.skills.funcs.FuncOwner;
import com.l2jfree.gameserver.model.skills.l2skills.L2SkillAgathion;
import com.l2jfree.gameserver.model.skills.l2skills.L2SkillFusion;
import com.l2jfree.gameserver.model.skills.l2skills.L2SkillMount;
import com.l2jfree.gameserver.model.skills.l2skills.L2SkillSummon;
import com.l2jfree.gameserver.model.skills.templates.L2EffectType;
import com.l2jfree.gameserver.model.skills.templates.L2SkillType;
import com.l2jfree.gameserver.model.world.L2World;
import com.l2jfree.gameserver.model.world.L2WorldRegion;
import com.l2jfree.gameserver.model.zone.L2Zone;
import com.l2jfree.gameserver.network.Disconnection;
import com.l2jfree.gameserver.network.SystemMessageId;
import com.l2jfree.gameserver.network.packets.L2ServerPacket;
import com.l2jfree.gameserver.network.packets.server.ActionFailed;
import com.l2jfree.gameserver.network.packets.server.Attack;
import com.l2jfree.gameserver.network.packets.server.ChangeMoveType;
import com.l2jfree.gameserver.network.packets.server.ChangeWaitType;
import com.l2jfree.gameserver.network.packets.server.DeleteObject;
import com.l2jfree.gameserver.network.packets.server.FlyToLocation;
import com.l2jfree.gameserver.network.packets.server.MagicSkillCanceled;
import com.l2jfree.gameserver.network.packets.server.MagicSkillLaunched;
import com.l2jfree.gameserver.network.packets.server.MagicSkillUse;
import com.l2jfree.gameserver.network.packets.server.MoveToLocation;
import com.l2jfree.gameserver.network.packets.server.Revive;
import com.l2jfree.gameserver.network.packets.server.SetupGauge;
import com.l2jfree.gameserver.network.packets.server.StaticPacket;
import com.l2jfree.gameserver.network.packets.server.StatusUpdate;
import com.l2jfree.gameserver.network.packets.server.StopMove;
import com.l2jfree.gameserver.network.packets.server.SystemMessage;
import com.l2jfree.gameserver.network.packets.server.TeleportToLocation;
import com.l2jfree.gameserver.network.packets.server.ValidateLocation;
import com.l2jfree.gameserver.taskmanager.CoordRevalidator;
import com.l2jfree.gameserver.taskmanager.MovementController;
import com.l2jfree.gameserver.taskmanager.PacketBroadcaster;
import com.l2jfree.gameserver.taskmanager.PacketBroadcaster.BroadcastMode;
import com.l2jfree.gameserver.threadmanager.ExclusiveTask;
import com.l2jfree.gameserver.util.Broadcast;
import com.l2jfree.gameserver.util.Util;
import com.l2jfree.gameserver.util.Util.Direction;
import com.l2jfree.lang.L2System;
import com.l2jfree.tools.geometry.Point3D;
import com.l2jfree.tools.random.Rnd;
import com.l2jfree.util.L2Arrays;
import com.l2jfree.util.LazyFastSet;

/**
 * Mother class of all character objects of the world (PC, NPC...)<BR>
 * <BR>
 * L2Creature :<BR>
 * <BR>
 * <li>L2CastleGuardInstance</li>
 * <li>L2DoorInstance</li>
 * <li>L2Npc</li>
 * <li>L2Playable </li>
 * <BR>
 * <BR>
 * <B><U> Concept of L2CreatureTemplate</U> :</B><BR>
 * <BR>
 * Each L2Creature owns generic and static properties (ex : all Keltir have the same number of HP...). All of those properties are stored in a different
 * template for each type of L2Creature. Each template is loaded once in the server cache memory (reduce memory use). When a new instance of L2Creature is
 * spawned, server just create a link between the instance and the template. This link is stored in <B>_template</B><BR>
 * <BR>
 *
 * @version $Revision: 1.53.2.45.2.34 $ $Date: 2005/04/11 10:06:08 $
 */
public abstract class L2Creature extends L2Object {
    public final static Log _log = LogFactory.getLog(L2Creature.class);

    // =========================================================
    // Data Field
    private Set<L2Creature> _attackByList;
    //private L2Creature            _attackingChar;
    private volatile boolean _isCastingNow = false;
    private volatile boolean _isCastingSimultaneouslyNow = false;
    private L2Skill _lastSimultaneousSkillCast;
    private boolean _block_buffs = false;
    private boolean _isAfraid = false; // Flee in a random direction
    private boolean _isConfused = false; // Attack anyone randomly
    private boolean _isFakeDeath = false; // Fake death
    private boolean _isFallsdown = false; // Falls down [L2J_JP_ADD]
    private boolean _isMuted = false; // Cannot use magic
    private boolean _isPhysicalMuted = false; // Cannot use physical attack
    private boolean _isPhysicalAttackMuted = false; // Cannot use attack
    private volatile boolean _isDead = false;
    private byte _isDying = DEATH_ANIMATION_NONE;
    private boolean _isImmobilized = false;
    private boolean _isParalyzed = false; // cannot do anything

    private boolean _isPendingRevive = false;
    private boolean _isRooted = false; // Cannot move until root timed out
    private boolean _isRunning = true;
    private boolean _isNoRndWalk = false; // Is no random walk
    private boolean _isImmobileUntilAttacked = false;
    private boolean _isSleeping = false; // Cannot move/attack until sleep
    // timed out or monster is attacked
    private boolean _isBlessedByNoblesse = false;
    private boolean _isLuckByNoblesse = false;
    private boolean _isBetrayed = false;
    private boolean _isStunned = false; // Cannot move/attack until stun
    // timed out
    protected boolean _isTeleporting = false;
    protected boolean _isInvul = false;
    protected L2Effect _invulEffect = null;
    protected boolean _isDisarmed = false;
    protected boolean _isMarked = false;
    protected boolean _isEradicated = false;
    private final int[] lastPosition = { 0, 0, 0 };
    protected final ICreatureView _view;
    protected final CreatureStat _stat;
    protected final CreatureStatus _status;
    private L2CreatureTemplate _template; // The link on the L2CreatureTemplate
    protected boolean _showSummonAnimation = false;
    // object containing generic and
    // static properties of this
    // L2Creature type (ex : Max HP,
    // Speed...)
    private String _title;
    private double _hpUpdateIncCheck = .0;
    private double _hpUpdateDecCheck = .0;
    private double _hpUpdateInterval = .0;

    /** Table of Calculators containing all used calculator */
    private Calculator[] _calculators;

    /** FastMap(Integer, L2Skill) containing all skills of the L2Creature */
    private Map<Integer, L2Skill> _skills;
    private ChanceSkillList _chanceSkills;
    /** Current force buff this caster is casting to a target */
    protected FusionSkill _fusionSkill;

    private boolean _isRaid = false;
    private boolean _isFlying;

    /**
     * Objects known by this object
     */
    protected final CreatureKnownList _knownList;

    // =========================================================
    // Constructor
    /**
     * Constructor of L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * Each L2Creature owns generic and static properties (ex : all Keltir have the same number of HP...). All of those properties are stored in a different
     * template for each type of L2Creature. Each template is loaded once in the server cache memory (reduce memory use). When a new instance of L2Creature is
     * spawned, server just create a link between the instance and the template This link is stored in <B>_template</B><BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Set the _template of the L2Creature </li>
     * <li>Set _overloaded to false (the charcater can take more items)</li>
     * <BR>
     * <BR>
     * <li>If L2Creature is a L2Npc, copy skills from template to object</li>
     * <li>If L2Creature is a L2Npc, link _calculators to NPC_STD_CALCULATOR</li>
     * <BR>
     * <BR>
     * <li>If L2Creature is NOT a L2Npc, create an empty _skills slot</li>
     * <li>If L2Creature is a L2Player or L2Summon, copy basic Calculator set to object</li>
     * <BR>
     * <BR>
     *
     * @param objectId
     *            Identifier of the object to initialized
     * @param template
     *            The L2CreatureTemplate to apply to the object
     */
    public L2Creature(int objectId, L2CreatureTemplate template) {
        super(objectId);

        // Set its template to the new L2Creature
        _template = template;

        _view = initView();
        _stat = initStat();
        _status = initStatus();
        _knownList = initKnownList();
        _effects = initEffects();
        _shots = initShots();

        if (this instanceof L2DoorInstance)
            _calculators = Formulas.getStdDoorCalculators();
        else if (this instanceof L2Npc)
            _calculators = NPC_STD_CALCULATOR;
        else
            _calculators = new Calculator[Stats.NUM_STATS];

        Formulas.addFuncsToNewCharacter(this);

        if (_template instanceof L2NpcTemplate) {
            _skills = ((L2NpcTemplate) _template).getSkills();

            if (_skills != null)
                for (L2Skill skill : _skills.values())
                    skillChanged(null, skill);
        } else
            _skills = new FastMap<Integer, L2Skill>().setShared(true);

        setIsInvul(true);
    }

    private final byte[] _currentZones = new byte[L2Zone.FLAG_SIZE];

    public final boolean isInsideZone(byte zone) {
        switch (zone) {
        case L2Zone.FLAG_PVP: {
            if (InstanceManager.getInstance().getInstance(getInstanceId()).isPvPInstance())
                return true;
            if (isInsideZone(L2Zone.FLAG_PEACE))
                return false;
            break;
        }
        case L2Zone.FLAG_PEACE: {
            if (InstanceManager.getInstance().getInstance(getInstanceId()).isPvPInstance())
                return false;
        }
        }

        final Boolean value = GlobalRestrictions.isInsideZone(this, zone);

        if (value != null)
            return value.booleanValue();

        return _currentZones[zone] > 0;
    }

    public final void setInsideZone(byte zone, boolean state) {
        final boolean oldState = isInsideZone(zone);

        _currentZones[zone] = (byte) Math.max(0, _currentZones[zone] + (state ? 1 : -1));

        final boolean newState = isInsideZone(zone);

        if (oldState != newState)
            GlobalRestrictions.isInsideZoneStateChanged(this, zone, newState);
    }

    /**
     * Returns character inventory, default null, overridden in L2Playable types and in L2Npc
     */
    public Inventory getInventory() {
        return null;
    }

    /**
     * @param process
     * @param itemId
     * @param count
     * @param reference
     * @param sendMessage
     */
    public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference,
            boolean sendMessage) {
        // Default: NPCs consume virtual items for their skills
        if (_log.isDebugEnabled())
            _log.warn("destroyItem called for L2Creature!", new IllegalStateException());

        return true;
    }

    /**
     * @param process
     * @param objectId
     * @param count
     * @param reference
     * @param sendMessage
     */
    public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage) {
        // Default: NPCs consume virtual items for their skills
        if (_log.isDebugEnabled())
            _log.warn("destroyItem called for L2Creature!", new IllegalStateException());

        return true;
    }

    protected void initCreatureStatusUpdateValues() {
        _hpUpdateInterval = getMaxHp() / 352.0; // MAX_HP div MAX_HP_BAR_PX
        _hpUpdateIncCheck = getMaxHp();
        _hpUpdateDecCheck = getMaxHp() - _hpUpdateInterval;
    }

    // =========================================================
    // Event - Public
    /**
     * Remove the L2Creature from the world when the decay task is launched.<BR>
     * <BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT><BR>
     * <BR>
     */
    public void onDecay() {
        L2WorldRegion reg = getWorldRegion();
        decayMe();
        if (reg != null)
            reg.removeFromZones(this);
    }

    @Override
    public void onSpawn() {
        super.onSpawn();
        // Force a revalidation
        revalidateZone(true);
    }

    public boolean onTeleported() {
        if (!isTeleporting())
            return false;

        if (this instanceof L2Summon) {
            ((L2Summon) this).getOwner().sendPacket(new TeleportToLocation(this, getPosition().getX(),
                    getPosition().getY(), getPosition().getZ(), getPosition().getHeading()));
        }

        setIsTeleporting(false);
        spawnMe();
        if (_isPendingRevive)
            doRevive();
        return true;
    }

    // =========================================================
    // Method - Public
    /**
     * Add L2Creature instance that is attacking to the attacker list.<BR>
     * <BR>
     *
     * @param player
     *            The L2Creature that attacks this one
     */
    public void addAttackerToAttackByList(L2Creature player) {
        if (player == null || player == this)
            return;
        getAttackByList().add(player);
    }

    /**
     * Send a packet to the L2Creature AND to all L2Player in the _knownPlayers of the L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * L2Player in the detection area of the L2Creature are identified in <B>_knownPlayers</B>. In order to inform other players of state modification on
     * the L2Creature, server just need to go through _knownPlayers to send Server->Client Packet<BR>
     * <BR>
     */
    public final void broadcastPacket(L2ServerPacket mov) {
        Broadcast.toSelfAndKnownPlayers(this, mov);
    }

    /**
     * Send a packet to the L2Creature AND to all L2Player in the radius (max knownlist radius) from the L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * L2Player in the detection area of the L2Creature are identified in <B>_knownPlayers</B>. In order to inform other players of state modification on
     * the L2Creature, server just need to go through _knownPlayers to send Server->Client Packet<BR>
     * <BR>
     */
    public final void broadcastPacket(L2ServerPacket mov, int radiusInKnownlist) {
        Broadcast.toSelfAndKnownPlayersInRadius(this, mov, radiusInKnownlist);
    }

    /**
     * Returns true if hp update should be done, false if not
     *
     * @return boolean
     */
    protected boolean needHpUpdate(int barPixels) {
        double currentHp = getStatus().getCurrentHp();

        if (currentHp <= 1.0 || getMaxHp() < barPixels)
            return true;

        if (currentHp <= _hpUpdateDecCheck || currentHp >= _hpUpdateIncCheck) {
            if (currentHp == getMaxHp()) {
                _hpUpdateIncCheck = currentHp + 1;
                _hpUpdateDecCheck = currentHp - _hpUpdateInterval;
            } else {
                double doubleMulti = currentHp / _hpUpdateInterval;
                int intMulti = (int) doubleMulti;

                _hpUpdateDecCheck = _hpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
                _hpUpdateIncCheck = _hpUpdateDecCheck + _hpUpdateInterval;
            }
            return true;
        }

        return false;
    }

    /**
     * Send the Server->Client packet StatusUpdate with current HP and MP to all other L2Player to inform.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Create the Server->Client packet StatusUpdate with current HP and MP </li>
     * <li>Send the Server->Client packet StatusUpdate with current HP and MP to all L2Creature called _statusListener that must be informed of HP/MP updates
     * of this L2Creature </li>
     * <BR>
     * <BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND CP information</B></FONT><BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player : Send current HP,MP and CP to the L2Player and only current HP, MP and Level to all other L2Player of the Party</li>
     * <BR>
     * <BR>
     */
    public final void broadcastStatusUpdate() {
        addPacketBroadcastMask(BroadcastMode.BROADCAST_STATUS_UPDATE);
    }

    public void broadcastStatusUpdateImpl() {
        if (getStatus().getStatusListeners().isEmpty() || !needHpUpdate(352))
            return;

        StatusUpdate su = new StatusUpdate(getObjectId());
        su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp());
        su.addAttribute(StatusUpdate.CUR_MP, (int) getStatus().getCurrentMp());

        synchronized (getStatus().getStatusListeners()) {
            for (L2Player player : getStatus().getStatusListeners())
                player.sendPacket(su);
        }
    }

    /**
     * Not Implemented.<BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player</li>
     * <BR>
     * <BR>
     * @param gsp
     */
    @Deprecated
    public void sendPacket(L2ServerPacket gsp) {
    }

    /**
     * @param sm
     */
    public void sendPacket(SystemMessageId sm) {
    }

    public void sendPacket(StaticPacket packet) {
    }

    @Deprecated
    public void sendMessage(String message) {
    }

    /**
     * Teleport a L2Creature and its pet if necessary.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Stop the movement of the L2Creature</li>
     * <li>Set the x,y,z position of the L2Object and if necessary modify its _worldRegion</li>
     * <li>Send a Server->Client packet TeleportToLocationt to the L2Creature AND to all L2Player in its _knownPlayers</li>
     * <li>Modify the position of the pet if necessary</li>
     * <BR>
     * <BR>
     */
    public final void teleToLocation(int x, int y, int z, boolean allowRandomOffset) {
        teleToLocation(x, y, z, getHeading(), allowRandomOffset);
    }

    public final void teleToLocation(int x, int y, int z, int heading, boolean allowRandomOffset) {
        // Restrict teleport during restart/shutdown
        if (Shutdown.isActionDisabled(DisableType.TELEPORT)) {
            sendMessage("Teleport is not allowed during restart/shutdown.");
            return;
        }

        // Stop movement
        setTarget(this);
        abortAttack();
        abortCast();
        isFalling(false, 0);
        getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
        setIsTeleporting(true);

        if (Config.RESPAWN_RANDOM_ENABLED && allowRandomOffset) {
            x += Rnd.get(-Config.RESPAWN_RANDOM_MAX_OFFSET, Config.RESPAWN_RANDOM_MAX_OFFSET);
            y += Rnd.get(-Config.RESPAWN_RANDOM_MAX_OFFSET, Config.RESPAWN_RANDOM_MAX_OFFSET);
        }

        z += 5;

        if (_log.isDebugEnabled())
            _log.debug("Teleporting to: " + x + ", " + y + ", " + z);

        // remove the object from its old location
        decayMe();

        // Send a Server->Client packet TeleportToLocationt to the L2Creature AND to all L2Player in the _knownPlayers of the L2Creature
        broadcastPacket(new TeleportToLocation(this, x, y, z, heading));

        // Set the x, y, z coords of the object, but do not update it's world region yet - onTeleported() will do it
        getPosition().setWorldPosition(x, y, z);
        // temporary fix for heading on teleports
        if (heading != 0)
            getPosition().setHeading(heading);

        isFalling(false, 0);

        if (this instanceof L2Player && !((L2Player) this).isInOfflineMode()) {
        } else
            onTeleported();

        revalidateZone(true);
    }

    public final void teleToLocation(int x, int y, int z) {
        teleToLocation(x, y, z, true);
    }

    public final void teleToLocation(Location loc) {
        teleToLocation(loc, true);
    }

    public final void teleToLocation(Location loc, boolean allowRandomOffset) {
        int x = loc.getX();
        int y = loc.getY();
        int z = loc.getZ();
        int heading = loc.getHeading();

        if (heading == 0)
            teleToLocation(x, y, z, allowRandomOffset);
        else
            teleToLocation(x, y, z, heading, allowRandomOffset);
    }

    /** ************************************-+ Fall Damage +-************************************** */

    /**
     * @author Darki699 Calculates if a L2Creature is falling or not. If the character falls, it returns the fall height.
     * @param  falling: if false no checks are made, but last position is set to the current one
     * @param  fallHeight: an integer value of the fall already calculated before.
     * @return A positive integer of the fall height, if not falling returns -1
     */
    public int isFalling(boolean falling, int fallHeight) {

        if (isFallsdown() && fallHeight == 0) // Avoid double checks -> let him fall only 1 time =P
            return -1;

        // If the boolean falling is set to false, just initialize this fall
        if (!falling || (lastPosition[0] == 0 && lastPosition[1] == 0 && lastPosition[2] == 0)) {
            lastPosition[0] = getPosition().getX();
            lastPosition[1] = getPosition().getY();
            lastPosition[2] = getPosition().getZ();
            setIsFallsdown(false);
            return -1;
        }

        int moveChangeX = Math.abs(lastPosition[0] - getPosition().getX()),
                moveChangeY = Math.abs(lastPosition[1] - getPosition().getY()),
                // Z has a Positive value ONLY if the L2Creature is moving down!
                moveChangeZ = Math.max(lastPosition[2] - getPosition().getZ(), lastPosition[2] - getZ());

        // Add acumulated damage to this fall, calling this function at a short delay while the fall is in progress
        if (moveChangeZ > fallSafeHeight() && moveChangeY < moveChangeZ && moveChangeX < moveChangeZ
                && !isFlying()) {

            setIsFallsdown(true);
            // Calculate the acumulated fall height for a total fall calculation
            fallHeight += moveChangeZ;

            // set the last position to the current one for the next future calculation
            lastPosition[0] = getPosition().getX();
            lastPosition[1] = getPosition().getY();
            lastPosition[2] = getPosition().getZ();
            getPosition().setXYZ(lastPosition[0], lastPosition[1], lastPosition[2]);

            // Call this function for further checks in the short future (next time we either keep falling, or finalize the fall)
            // This "next time" check is a rough estimate on how much time is needed to calculate the next check, and it is based on the current fall height.
            ThreadPoolManager.getInstance().scheduleGeneral(new CheckFalling(fallHeight),
                    Math.min(1200, moveChangeZ));

            // Value returned but not currently used. Maybe useful for future features.
            return fallHeight;
        }

        // Stopped falling or is not falling at all.
        lastPosition[0] = getPosition().getX();
        lastPosition[1] = getPosition().getY();
        lastPosition[2] = getPosition().getZ();
        getPosition().setXYZ(lastPosition[0], lastPosition[1], lastPosition[2]);

        if (fallHeight > fallSafeHeight()) {
            doFallDamage(fallHeight);
            return fallHeight;
        }

        return -1;
    }

    /**
     * <font color="ff0000"><b>Needs to be completed!</b></font> Add to safeFallHeight the buff resist values which increase the fall resistance.
     *
     * @author Darki699
     * @return integer safeFallHeight is the value from which above it this L2Creature suffers a fall damage.
     */
    private int fallSafeHeight() {

        int safeFallHeight = Config.ALT_MINIMUM_FALL_HEIGHT;

        try {
            if (this instanceof L2Player) {
                safeFallHeight = ((L2Player) this).getTemplate()
                        .getBaseFallSafeHeight(((L2Player) this).getAppearance().getSex());
            }
        }

        catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        return safeFallHeight;
    }

    private int getFallDamage(int fallHeight) {
        int damage = (fallHeight - fallSafeHeight()) * 2; // Needs verification for actual damage
        damage = (int) (damage / getStat().calcStat(Stats.FALL_VULN, 1, this, null));

        if (damage >= getStatus().getCurrentHp()) {
            damage = (int) (getStatus().getCurrentHp() - 1);
        }

        broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_START_FAKEDEATH));
        disableAllSkills();

        ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() {
            @Override
            public void run() {
                L2Creature.this.enableAllSkills();
                broadcastPacket(new ChangeWaitType(L2Creature.this, ChangeWaitType.WT_STOP_FAKEDEATH));
                setIsFallsdown(false);

                // For some reason this is needed since the client side changes back to last airborn position after 1 second
                lastPosition[0] = getPosition().getX();
                lastPosition[1] = getPosition().getY();
                lastPosition[2] = getPosition().getZ();
            }
        }, 1100);

        return damage;
    }

    /**
     * Receives a integer fallHeight and finalizes the damage effect from the fall.
     *
     * @author Darki699
     */
    private void doFallDamage(int fallHeight) {
        isFalling(false, 0);

        if (isInvul()) {
            setIsFallsdown(false);
            return;
        }

        int damage = getFallDamage(fallHeight);

        if (damage < 1)
            return;

        if (this instanceof L2Player) {
            SystemMessage sm = new SystemMessage(SystemMessageId.FALL_DAMAGE_S1);
            sm.addNumber(damage);
            sendPacket(sm);
        }

        getStatus().reduceHp(damage, this);
        getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, this);
    }

    /**
     * @author Darki699 Once a character is falling, we call this to run in order to see when he is not falling down any more. Constructor receives the int
     *         fallHeight already calculated, and function isFalling(boolean,int) will be called again to terminate the fall and calculate the damage.
     */
    public class CheckFalling implements Runnable {
        private final int _fallHeight;

        public CheckFalling(int fallHeight) {
            _fallHeight = fallHeight;
        }

        @Override
        public void run() {
            isFalling(true, _fallHeight);
        }
    }

    // =========================================================
    // Method - Private
    /**
     * Launch a physical attack against a target (Simple, Bow, Pole or Dual).<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Get the active weapon (always equipped in the right hand) </li>
     * <BR>
     * <BR>
     * <li>If weapon is a bow, check for arrows, MP and bow re-use delay (if necessary, equip the L2Player with arrows in left hand)</li>
     * <li>If weapon is a bow, consume MP and set the new period of bow non re-use </li>
     * <BR>
     * <BR>
     * <li>Get the Attack Speed of the L2Creature (delay (in milliseconds) before next attack) </li>
     * <li>Select the type of attack to start (Simple, Bow, Pole or Dual) and verify if SoulShot are charged then start calculation</li>
     * <li>If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack to the L2Creature AND to all L2Player in the
     * _knownPlayers of the L2Creature</li>
     * <li>Notify AI with EVT_READY_TO_ACT</li>
     * <BR>
     * <BR>
     *
     * @param target
     *            The L2Creature targeted
     */
    protected void doAttack(final L2Creature target) {
        if (_log.isDebugEnabled())
            _log.debug(getName() + " doAttack: target=" + target);

        if (isAlikeDead() || target == null || (this instanceof L2Npc && target.isAlikeDead())
                || (this instanceof L2Player && target.isDead() && !target.isFakeDeath())) {
            // If L2Player is dead or the target is dead, the action is stoped
            getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);

            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        if (isAttackingDisabled())
            return;

        // GeoData Los Check here (or dz > 1000)
        if (!(target instanceof L2DoorInstance) && !GeoData.getInstance().canSeeTarget(this, target)) {
            sendPacket(SystemMessageId.CANT_SEE_TARGET);
            getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        boolean transformed = false;

        if (this instanceof L2Player) {
            L2Player actor = (L2Player) this;
            if ((actor.isMounted() && actor.getMountNpcId() == 12621)
                    || (actor.isTransformed() && !actor.getTransformation().canDoMeleeAttack())) {
                sendPacket(ActionFailed.STATIC_PACKET);
                return;
            }

            transformed = ((L2Player) this).isTransformed();
        }

        if (GlobalRestrictions.isProtected(this, target, null, true)) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        // Get the active weapon instance (always equipped in the right hand)
        L2ItemInstance weaponInst = getActiveWeaponInstance();

        // TODO: unhardcode this to support boolean if with that weapon u can attack or not (for ex transform weapons)
        if (weaponInst != null && weaponInst.getItemId() == 9819) {
            sendPacket(SystemMessageId.THAT_WEAPON_CANT_ATTACK);
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        // Get the active weapon item corresponding to the active weapon instance (always equipped in the right hand)
        L2Weapon weaponItem = getActiveWeaponItem();

        if ((weaponItem != null && weaponItem.getItemType() == L2WeaponType.ROD)) {
            // You can't make an attack with a fishing pole.
            sendPacket(SystemMessageId.CANNOT_ATTACK_WITH_FISHING_POLE);
            getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        // BOW and CROSSBOW checks
        if (weaponItem != null && !transformed && this instanceof L2Player) {
            // Check for arrows and MP
            if (weaponItem.getItemType() == L2WeaponType.BOW) {
                // Verify if the bow can be used
                if (getBowReuseEndEvtReadyToAct().isScheduled()) {
                    // Cancel the action because the bow can't be re-used at this moment
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return;
                }

                // Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2Player then return True
                if (!checkAndEquipArrows()) {
                    // Cancel the action because the L2Player have no arrow
                    getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                    sendPacket(ActionFailed.STATIC_PACKET);
                    sendPacket(SystemMessageId.NOT_ENOUGH_ARROWS);
                    return;
                }

                // Verify if L2Player owns enough MP
                int saMpConsume = (int) getStat().calcStat(Stats.MP_CONSUME, 0, null, null);
                int mpConsume = saMpConsume == 0 ? weaponItem.getMpConsume() : saMpConsume;
                mpConsume = (int) calcStat(Stats.BOW_MP_CONSUME_RATE, mpConsume, null, null);

                if (getStatus().getCurrentMp() < mpConsume) {
                    // If L2Player doesn't have enough MP, stop the attack
                    getBowReuseEndEvtReadyToAct().schedule(1000);
                    sendPacket(ActionFailed.STATIC_PACKET);
                    sendPacket(SystemMessageId.NOT_ENOUGH_MP);
                    return;
                }

                // If L2Player have enough MP, the bow consumes it
                if (mpConsume > 0)
                    getStatus().reduceMp(mpConsume);
            }
            // Check for bolts
            else if (weaponItem.getItemType() == L2WeaponType.CROSSBOW) {
                // Verify if the crossbow can be used
                if (getBowReuseEndEvtReadyToAct().isScheduled()) {
                    // Cancel the action because the crossbow can't be re-used at this moment
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return;
                }

                // Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2Player then return True
                if (!checkAndEquipBolts()) {
                    // Cancel the action because the L2Player have no arrow
                    getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                    sendPacket(ActionFailed.STATIC_PACKET);
                    sendPacket(SystemMessageId.NOT_ENOUGH_BOLTS);
                    return;
                }
            }
        }

        // Add the L2Player to _knownObjects and _knownPlayer of the target
        target.getKnownList().addKnownObject(this);

        // Reduce the current CP if TIREDNESS configuration is activated
        if (Config.ALT_GAME_TIREDNESS)
            getStatus().setCurrentCp(getStatus().getCurrentCp() - 10);

        rechargeShot();

        // Verify if soulshots are charged.
        final boolean wasSSCharged = isSoulshotCharged();

        // Get the Attack Speed of the L2Creature (delay (in milliseconds) before next attack)
        int timeAtk = calculateTimeBetweenAttacks(target, weaponItem);
        // the hit is calculated to happen halfway to the animation - might need further tuning e.g. in bow, dual case
        int timeToHit = timeAtk / 2;
        // Get the Attack Reuse Delay of the L2Weapon
        int reuse = calculateReuseTime(target, weaponItem);

        // Notify AI with EVT_READY_TO_ACT
        getAttackEndEvtReadyToAct().schedule(timeAtk); // normal attacks and bow attacks with queued intentions
        if (reuse > 0)
            getBowReuseEndEvtReadyToAct().schedule(timeAtk + reuse); // bow attack without queued intentions

        int ssGrade = 0;

        if (weaponItem != null)
            ssGrade = weaponItem.getItemGradeSPlus();

        // Create a Server->Client packet Attack
        final Attack attack = new Attack(this, target, wasSSCharged, ssGrade);

        // Set the Attacking Body part to CHEST
        setAttackingBodypart();
        // Make sure that char is facing selected target
        // also works: setHeading(Util.convertDegreeToClientHeading(Util.calculateAngleFrom(this, target)));
        setHeading(Util.calculateHeadingFrom(this, target));

        boolean hitted;
        // Select the type of attack to start
        if (weaponItem == null || transformed)
            hitted = doAttackHitSimple(attack, target, timeToHit);

        else if (weaponItem.getItemType() == L2WeaponType.BOW)
            hitted = doAttackHitByBow(attack, target, timeAtk, reuse);

        else if (weaponItem.getItemType() == L2WeaponType.CROSSBOW)
            hitted = doAttackHitByCrossBow(attack, target, timeAtk, reuse);

        else if (weaponItem.getItemType() == L2WeaponType.POLE)
            hitted = doAttackHitByPole(attack, target, timeToHit);

        else if (isUsingDualWeapon())
            hitted = doAttackHitByDual(attack, target, timeToHit);

        else
            hitted = doAttackHitSimple(attack, target, timeToHit);

        // Flag the attacker if it's a L2Player outside a PvP area
        L2Player player = getActingPlayer();

        if (player != null && player.getPet() != target) {
            player.updatePvPStatus(target);
        }

        // Check if hit isn't missed
        if (!hitted)
            // Abort the attack of the L2Creature and send Server->Client ActionFailed packet
            abortAttack();
        else {
            /*
             * ADDED BY nexus - 2006-08-17
             *
             * As soon as we know that our hit landed, we must discharge any active soulshots. This must be done so to avoid unwanted soulshot consumption.
             */

            // If we didn't miss the hit, discharge the shoulshots, if any
            if (wasSSCharged) {
                useSoulshotCharge();

                double shotTime = 0.4 * timeAtk;

                if (weaponItem != null && weaponItem.getItemType() == L2WeaponType.BOW)
                    shotTime = 0.7 * timeAtk;
                else if (isUsingDualWeapon())
                    shotTime = 0.5 * timeAtk;

                scheduleShotRecharge((int) shotTime);
            }

            if (player != null) {
                if (player.isCursedWeaponEquipped()) {
                    if (!target.isInvul())
                        target.getStatus().setCurrentCp(0);
                } else if (player.isHero()) {
                    if (target instanceof L2Player && ((L2Player) target).isCursedWeaponEquipped())
                        target.getStatus().setCurrentCp(0); // If Zariche is hitted by a Hero, Cp is reduced to 0
                }
            }
        }

        // If the Server->Client packet Attack contains at least 1 hit, send the Server->Client packet Attack
        // to the L2Creature AND to all L2Player in the _knownPlayers of the L2Creature
        if (attack.hasHits())
            broadcastPacket(attack);
    }

    private EvtReadyToAct _attackEndEvtReadyToAct;
    private EvtReadyToAct _bowReuseEndEvtReadyToAct;

    private EvtReadyToAct getAttackEndEvtReadyToAct() {
        if (_attackEndEvtReadyToAct == null)
            _attackEndEvtReadyToAct = new EvtReadyToAct();

        return _attackEndEvtReadyToAct;
    }

    private EvtReadyToAct getBowReuseEndEvtReadyToAct() {
        if (_bowReuseEndEvtReadyToAct == null)
            _bowReuseEndEvtReadyToAct = new EvtReadyToAct();

        return _bowReuseEndEvtReadyToAct;
    }

    private final class EvtReadyToAct extends ExclusiveTask {
        @Override
        protected void onElapsed() {
            cancel();

            getAI().notifyEvent(CtrlEvent.EVT_READY_TO_ACT);
        }
    }

    /**
     * Launch a Bow attack.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Calculate if hit is missed or not </li>
     * <li>Consume arrows </li>
     * <li>If hit isn't missed, calculate if shield defense is efficient </li>
     * <li>If hit isn't missed, calculate if hit is critical </li>
     * <li>If hit isn't missed, calculate physical damages </li>
     * <li>If the L2Creature is a L2Player, Send a Server->Client packet SetupGauge </li>
     * <li>Create a new hit task with Medium priority</li>
     * <li>Calculate and set the disable delay of the bow in function of the Attack Speed</li>
     * <li>Add this hit to the Server-Client packet Attack </li>
     * <BR>
     * <BR>
     *
     * @param attack
     *            Server->Client packet Attack in which the hit will be added
     * @param target
     *            The L2Creature targeted
     * @param sAtk
     *            The Attack Speed of the attacker
     * @return True if the hit isn't missed
     */
    private boolean doAttackHitByBow(Attack attack, L2Creature target, int sAtk, int reuse) {
        int damage1 = 0;
        byte shld1 = 0;
        boolean crit1 = false;

        // Calculate if hit is missed or not
        boolean miss1 = Formulas.calcHitMiss(this, target);

        // Consume arrows
        reduceArrowCount(false);

        _move = null;

        // Check if hit isn't missed
        if (!miss1) {
            // Calculate if shield defense is efficient
            shld1 = Formulas.calcShldUse(this, target);

            // Calculate if hit is critical
            crit1 = Formulas.calcCriticalHit(this, target);

            // Calculate physical damages
            damage1 = (int) Formulas.calcPhysDam(this, target, null, shld1, crit1, attack.soulshot);
        }

        // Check if the L2Creature is a L2Player
        if (this instanceof L2Player) {
            // Send a system message
            sendPacket(SystemMessageId.GETTING_READY_TO_SHOOT_AN_ARROW);

            // Send a Server->Client packet SetupGauge
            SetupGauge sg = new SetupGauge(SetupGauge.RED, sAtk + reuse);
            sendPacket(sg);
        }

        // Create a new hit task with Medium priority
        ThreadPoolManager.getInstance().scheduleAi(new HitTask(target, damage1, crit1, miss1), sAtk);

        // Add this hit to the Server-Client packet Attack
        attack.hit(attack.createHit(target, damage1, miss1, crit1, shld1));

        // Return true if hit isn't missed
        return !miss1;
    }

    /**
    * Launch a CrossBow attack.<BR><BR>
    *
    * <B><U> Actions</U> :</B><BR><BR>
    * <li>Calculate if hit is missed or not </li>
    * <li>Consume bolts </li>
    * <li>If hit isn't missed, calculate if shield defense is efficient </li>
    * <li>If hit isn't missed, calculate if hit is critical </li>
    * <li>If hit isn't missed, calculate physical damages </li>
    * <li>If the L2Creature is a L2Player, Send a Server->Client packet SetupGauge </li>
    * <li>Create a new hit task with Medium priority</li>
    * <li>Calculate and set the disable delay of the crossbow in function of the Attack Speed</li>
    * <li>Add this hit to the Server-Client packet Attack </li><BR><BR>
    *
    * @param attack Server->Client packet Attack in which the hit will be added
    * @param target The L2Creature targeted
    * @param sAtk The Attack Speed of the attacker
    *
    * @return True if the hit isn't missed
    *
    */
    private boolean doAttackHitByCrossBow(Attack attack, L2Creature target, int sAtk, int reuse) {
        int damage1 = 0;
        byte shld1 = 0;
        boolean crit1 = false;

        // Calculate if hit is missed or not
        boolean miss1 = Formulas.calcHitMiss(this, target);

        // Consume bows
        reduceArrowCount(true);

        _move = null;

        // Check if hit isn't missed
        if (!miss1) {
            // Calculate if shield defense is efficient
            shld1 = Formulas.calcShldUse(this, target);

            // Calculate if hit is critical
            crit1 = Formulas.calcCriticalHit(this, target);

            // Calculate physical damages
            damage1 = (int) Formulas.calcPhysDam(this, target, null, shld1, crit1, attack.soulshot);
        }

        // Check if the L2Creature is a L2Player
        if (this instanceof L2Player) {
            // Send a system message
            sendPacket(SystemMessageId.CROSSBOW_PREPARING_TO_FIRE);

            // Send a Server->Client packet SetupGauge
            SetupGauge sg = new SetupGauge(SetupGauge.RED, sAtk + reuse);
            sendPacket(sg);
        }

        // Create a new hit task with Medium priority
        ThreadPoolManager.getInstance().scheduleAi(new HitTask(target, damage1, crit1, miss1), sAtk);

        // Add this hit to the Server-Client packet Attack
        attack.hit(attack.createHit(target, damage1, miss1, crit1, shld1));

        // Return true if hit isn't missed
        return !miss1;
    }

    /**
     * Launch a Dual attack.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Calculate if hits are missed or not </li>
     * <li>If hits aren't missed, calculate if shield defense is efficient </li>
     * <li>If hits aren't missed, calculate if hit is critical </li>
     * <li>If hits aren't missed, calculate physical damages </li>
     * <li>Create 2 new hit tasks with Medium priority</li>
     * <li>Add those hits to the Server-Client packet Attack </li>
     * <BR>
     * <BR>
     *
     * @param attack
     *            Server->Client packet Attack in which the hit will be added
     * @param target
     *            The L2Creature targeted
     * @return True if hit 1 or hit 2 isn't missed
     */
    private boolean doAttackHitByDual(Attack attack, L2Creature target, int sAtk) {
        int damage1 = 0;
        int damage2 = 0;
        byte shld1 = 0;
        byte shld2 = 0;
        boolean crit1 = false;
        boolean crit2 = false;

        // Calculate if hits are missed or not
        boolean miss1 = Formulas.calcHitMiss(this, target);
        boolean miss2 = Formulas.calcHitMiss(this, target);

        // Check if hit 1 isn't missed
        if (!miss1) {
            // Calculate if shield defense is efficient against hit 1
            shld1 = Formulas.calcShldUse(this, target);

            // Calculate if hit 1 is critical
            crit1 = Formulas.calcCriticalHit(this, target);

            // Calculate physical damages of hit 1
            damage1 = (int) Formulas.calcPhysDam(this, target, null, shld1, crit1, attack.soulshot);
            damage1 /= 2;
        }

        // Check if hit 2 isn't missed
        if (!miss2) {
            // Calculate if shield defense is efficient against hit 2
            shld2 = Formulas.calcShldUse(this, target);

            // Calculate if hit 2 is critical
            crit2 = Formulas.calcCriticalHit(this, target);

            // Calculate physical damages of hit 2
            damage2 = (int) Formulas.calcPhysDam(this, target, null, shld2, crit2, attack.soulshot);
            damage2 /= 2;
        }

        // Create a new hit task with Medium priority for hit 1
        ThreadPoolManager.getInstance().scheduleAi(new HitTask(target, damage1, crit1, miss1), sAtk / 2);

        // Create a new hit task with Medium priority for hit 2 with a higher delay
        ThreadPoolManager.getInstance().scheduleAi(new HitTask(target, damage2, crit2, miss2), sAtk);

        // Add those hits to the Server-Client packet Attack
        attack.hit(attack.createHit(target, damage1, miss1, crit1, shld1),
                attack.createHit(target, damage2, miss2, crit2, shld2));

        // Return true if hit 1 or hit 2 isn't missed
        return (!miss1 || !miss2);
    }

    /**
     * Launch a Pole attack.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Get all visible objects in a spherical area near the L2Creature to obtain possible targets </li>
     * <li>If possible target is the L2Creature targeted, launch a simple attack against it </li>
     * <li>If possible target isn't the L2Creature targeted but is attackable, launch a simple attack against it </li>
     * <BR>
     * <BR>
     *
     * @param attack
     *            Server->Client packet Attack in which the hit will be added
     * @return True if one hit isn't missed
     */
    private boolean doAttackHitByPole(Attack attack, L2Creature target, int sAtk) {
        final int maxRadius = getPhysicalAttackRange();
        final int maxAngleDiff = (int) getStat().calcStat(Stats.POWER_ATTACK_ANGLE, 120, null, null) / 2;
        final int attackCountMax = (int) getStat().calcStat(Stats.ATTACK_COUNT_MAX, 1, null, null);

        // ===========================================================

        final ArrayList<HitTask> hitTasks = new ArrayList<HitTask>();
        double attackMulti = 1.0;

        hitTasks.add(doAttackHitSimple(attack, target, attackMulti));
        attackMulti *= Config.ALT_POLEARM_DAMAGE_MULTI;

        for (L2Object obj : getKnownList().getKnownObjects().values()) {
            if (hitTasks.size() >= attackCountMax)
                break;

            if (obj == target)
                continue; // do not hit twice

            // Check if the L2Object is a L2Creature
            if (!(obj instanceof L2Creature))
                continue;

            if (obj instanceof L2Summon && ((L2Summon) obj).getOwner() == this)
                continue;

            if (!Util.checkIfInRange(maxRadius, this, obj, false))
                continue;
            if (!GeoData.getInstance().canSeeTarget(this, obj))
                continue;

            // otherwise hit too high/low. 650 because mob z coord sometimes wrong on hills
            if (Math.abs(obj.getZ() - getZ()) > 650)
                continue;
            if (!target.isInFrontOf(this, maxAngleDiff))
                continue;

            L2Creature cha = (L2Creature) obj;

            // Launch a simple attack against the L2Creature targeted
            if (cha.isAlikeDead())
                continue;

            if (cha != getAI().getAttackTarget() && !cha.isAutoAttackable(this))
                continue;

            if (GlobalRestrictions.isProtected(this, cha, null, false))
                continue;

            hitTasks.add(doAttackHitSimple(attack, cha, attackMulti));
            attackMulti *= Config.ALT_POLEARM_DAMAGE_MULTI;
        }

        // Create a new hit task with Medium priority
        ThreadPoolManager.getInstance().schedule(new PolearmHitTask(hitTasks), sAtk);

        // Return true if one hit isn't missed
        for (int i = 0; i < hitTasks.size(); i++)
            if (!hitTasks.get(i)._miss)
                return true;

        return false;
    }

    /**
     * Launch a simple attack.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Calculate if hit is missed or not </li>
     * <li>If hit isn't missed, calculate if shield defense is efficient </li>
     * <li>If hit isn't missed, calculate if hit is critical </li>
     * <li>If hit isn't missed, calculate physical damages </li>
     * <li>Create a new hit task with Medium priority</li>
     * <li>Add this hit to the Server-Client packet Attack </li>
     * <BR>
     * <BR>
     *
     * @param attack
     *            Server->Client packet Attack in which the hit will be added
     * @param target
     *            The L2Creature targeted
     * @return True if the hit isn't missed
     */
    private boolean doAttackHitSimple(Attack attack, L2Creature target, int sAtk) {
        HitTask hitTask = doAttackHitSimple(attack, target, 1.0);

        // Create a new hit task with Medium priority
        ThreadPoolManager.getInstance().scheduleAi(hitTask, sAtk);

        // Return true if hit isn't missed
        return !hitTask._miss;
    }

    private HitTask doAttackHitSimple(Attack attack, L2Creature target, double attackMulti) {
        int damage1 = 0;
        byte shld1 = 0;
        boolean crit1 = false;

        // Calculate if hit is missed or not
        boolean miss1 = Formulas.calcHitMiss(this, target);

        // Check if hit isn't missed
        if (!miss1) {
            // Calculate if shield defense is efficient
            shld1 = Formulas.calcShldUse(this, target);

            // Calculate if hit is critical
            crit1 = Formulas.calcCriticalHit(this, target);

            // Calculate physical damages
            damage1 = (int) Formulas.calcPhysDam(this, target, null, shld1, crit1, attack.soulshot);
            damage1 *= attackMulti;
        }

        // Add this hit to the Server-Client packet Attack
        attack.hit(attack.createHit(target, damage1, miss1, crit1, shld1));

        // Create a new hit task
        return new HitTask(target, damage1, crit1, miss1);
    }

    /**
     * Manage the casting task (casting and interrupt time, re-use delay...) and display the casting bar and animation on client.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Verify the possibilty of the the cast : skill is a spell, caster isn't muted... </li>
     * <li>Get the list of all targets (ex : area effects) and define the L2Charcater targeted (its stats will be used in calculation)</li>
     * <li>Calculate the casting time (base + modifier of MAtkSpd), interrupt time and re-use delay</li>
     * <li>Send a Server->Client packet MagicSkillUse (to diplay casting animation), a packet SetupGauge (to display casting bar) and a system message </li>
     * <li>Disable all skills during the casting time (create a task EnableAllSkills)</li>
     * <li>Disable the skill during the re-use delay (create a task EnableSkill)</li>
     * <li>Create a task MagicUseTask (that will call method onMagicUseTimer) to launch the Magic Skill at the end of the casting time</li>
     * <BR>
     * <BR>
     *
     * @param skill
     *            The L2Skill to use
     */
    public void doCast(L2Skill skill) {
        beginCast(skill, false);
    }

    public void doSimultaneousCast(L2Skill skill) {
        // queue herbs and potions
        if (isCastingSimultaneouslyNow()) {
            ThreadPoolManager.getInstance().scheduleAi(new UsePotionTask(skill), 100);
            return;
        }

        beginCast(skill, true);
    }

    private void beginCast(L2Skill skill, boolean simultaneously) {
        if (!checkDoCastConditions(skill)) {
            if (this instanceof L2Player)
                getAI().setIntention(AI_INTENTION_ACTIVE);
            return;
        }

        // Get all possible targets of the skill in a table in function of the skill target type
        final L2Creature[] targets = skill.getTargetList(this);

        // Set the target of the skill in function of Skill Type and Target Type
        final L2Creature target = skill.getFirstOfTargetList(this, targets);

        if (target == null || GlobalRestrictions.isProtected(this, target, skill, true)) {
            if (this instanceof L2Player) {
                sendPacket(ActionFailed.STATIC_PACKET);
                getAI().setIntention(AI_INTENTION_ACTIVE);
            }

            return;
        }

        //setAttackingChar(this);
        // setLastSkillCast(skill);

        // Get the casting time of the skill (base)
        int hitTime = skill.getHitTime();
        int coolTime = skill.getCoolTime();
        int skillInterruptTime = skill.getSkillInterruptTime();
        // Seems this is the indicator in retail
        if (!skill.isMagic())
            skillInterruptTime = 0;

        final boolean effectWhileCasting = skill.hasEffectWhileCasting();

        // Calculate the casting time of the skill (base + modifier of MAtkSpd)
        // Don't modify the skill time for FORCE_BUFF skills. The skill time for those skills represent the buff time.
        if (!effectWhileCasting && !skill.isStaticHitTime()) {
            double multi = Formulas.calcCastingRelatedTimeMulti(this, skill);

            rechargeShot();

            // Calculate altered Cast Speed due to BSpS/SpS
            if (skill.useSpiritShot() && isAnySpiritshotCharged())
                multi *= 0.7;

            // if basic hitTime is higher or equal to 500 than the min hitTime is 500
            if (hitTime >= 500)
                multi = Math.max(multi, 500.0 / hitTime);

            hitTime *= multi;
            coolTime *= multi;
            skillInterruptTime *= multi;
        }

        // Set the _castInterruptTime and casting status (L2Player already has this true)
        if (simultaneously) {
            setIsCastingSimultaneouslyNow(true);
            setLastSimultaneousSkillCast(skill);
        } else {
            // Note: _castEndTime = GameTimeController.getGameTicks() + (coolTime + hitTime) / GameTimeController.MILLIS_IN_TICK;
            setIsCastingNow(true, skillInterruptTime);
        }

        // Init the reuse time of the skill
        int reuseDelay = skill.getReuseDelay();

        if (Formulas.calcSkillMastery(this, skill)) {
            reuseDelay = 0;
            sendPacket(SystemMessageId.SKILL_READY_TO_USE_AGAIN);
        } else if (!skill.isStaticReuse()) {
            reuseDelay *= skill.isMagic() ? getStat().getMReuseRate(skill) : getStat().getPReuseRate(skill);
        }

        // Check if this skill consume mp on start casting
        int initmpcons = getStat().getMpInitialConsume(skill);
        if (initmpcons > 0)
            getStatus().reduceMp(initmpcons);

        // Disable the skill during the re-use delay and create a task EnableSkill with Medium priority to enable it at the end of the re-use delay
        disableSkill(skill.getId(), reuseDelay);

        // Make sure that char is facing selected target
        if (target != this)
            setHeading(Util.calculateHeadingFrom(this, target));

        // For force buff skills, start the effect as long as the player is casting.
        if (effectWhileCasting) {
            // Consume Items if necessary and Send the Server->Client packet InventoryUpdate with Item modification to all the L2Creature
            if (skill.getItemConsume() > 0) {
                if (!destroyItemByItemId("Consume", skill.getItemConsumeId(), skill.getItemConsume(), null,
                        false)) {
                    sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);

                    if (simultaneously)
                        setIsCastingSimultaneouslyNow(false);
                    else
                        setIsCastingNow(false);

                    if (this instanceof L2Player)
                        getAI().setIntention(AI_INTENTION_ACTIVE);
                    return;
                }
            }

            if (this instanceof L2Player) {
                L2Player player = (L2Player) this;
                // Reset soul bonus for skills
                player.resetLastSoulConsume();

                // Consume Souls if necessary
                if (skill.getSoulConsumeCount() > 0 || skill.getMaxSoulConsumeCount() > 0) {
                    player.decreaseSouls(skill);
                }

                // Consume Charges if necessary ... L2SkillChargeDmg does the consume by itself.
                if (skill.getNeededCharges() > 0 && skill.getSkillType() != L2SkillType.CHARGEDAM) {
                    player.decreaseCharges(skill.getNeededCharges());
                }
            }

            if (skill.getSkillType() == L2SkillType.FUSION)
                startFusionSkill(target, skill);
            else
                callSkill(skill, targets);
        }

        if (!skill.isToggle()) // otherwise stops movement client side
        {
            broadcastPacket(new MagicSkillUse(this, target, skill, hitTime, reuseDelay));
            // The correct time to send this packet?!!
            broadcastPacket(new MagicSkillLaunched(this, skill, targets));
        }

        if (this instanceof L2Playable) {
            long protTime = hitTime + coolTime;

            if (reuseDelay < protTime)
                protTime /= 2;

            ((L2Playable) this).setSkillQueueProtectionTime(System.currentTimeMillis() + protTime);
        }

        // Send a system message USE_S1 to the L2Creature
        if (this instanceof L2Player && skill.getId() != 1312) {
            SystemMessage sm = new SystemMessage(SystemMessageId.USE_S1);
            sm.addSkillName(skill);
            getActingPlayer().sendPacket(sm);
        }

        //      switch (skill.getTargetType())
        //      {
        //         case TARGET_AURA:
        //         case TARGET_FRONT_AURA:
        //         case TARGET_BEHIND_AURA:
        //         case TARGET_GROUND:
        //         {
        //            if (targets.length == 0)
        //            {
        //               // now cancels both, simultaneous and normal
        //               getAI().notifyEvent(CtrlEvent.EVT_CANCEL);
        //               return;
        //            }
        //            break;
        //         }
        //         default:
        //            break;
        //      }

        // Before start AI Cast Broadcast Fly Effect is Need
        if (skill.getFlyType() != null && this instanceof L2Player)
            ThreadPoolManager.getInstance().schedule(new FlyToLocationTask(target, skill), 50);

        final MagicEnv magicEnv = new MagicEnv(skill, targets, getTarget(), target, coolTime, simultaneously);

        // launch the magic in hitTime milliseconds
        if (hitTime < 10) {
            if (effectWhileCasting)
                onMagicHitTimer(magicEnv);
            else
                onMagicLaunchedTimer(magicEnv);
        } else {
            if (this instanceof L2Player && !effectWhileCasting)
                getActingPlayer().sendPacket(new SetupGauge(SetupGauge.BLUE, hitTime));

            // Create a task MagicUseTask to launch the MagicSkill at the end of the casting time (hitTime)
            if (simultaneously) {
                if (_skillCast2 != null) {
                    _skillCast2.cancel(true);
                    _skillCast2 = null;
                }

                if (effectWhileCasting)
                    _skillCast2 = ThreadPoolManager.getInstance().schedule(new MagicHitTimer(magicEnv), hitTime);
                else
                    _skillCast2 = ThreadPoolManager.getInstance().schedule(new MagicLaunchedTimer(magicEnv),
                            hitTime);
            } else {
                if (_skillCast != null) {
                    _skillCast.cancel(true);
                    _skillCast = null;
                }

                if (effectWhileCasting)
                    _skillCast = ThreadPoolManager.getInstance().schedule(new MagicHitTimer(magicEnv), hitTime);
                else
                    _skillCast = ThreadPoolManager.getInstance().schedule(new MagicLaunchedTimer(magicEnv),
                            hitTime);
            }
        }
    }

    private boolean checkDoCastConditions(L2Skill skill) {
        if (skill == null || isSkillDisabled(skill.getId()) || skill.getSkillType() == L2SkillType.NOTDONE
                || (skill.getFlyType() != null && isRooted())) {
            // Send a Server->Client packet ActionFailed to the L2Player
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if the caster has enough MP
        if (getStatus().getCurrentMp() < getStat().getMpConsume(skill) + getStat().getMpInitialConsume(skill)) {
            if (this instanceof L2Player) {
                // Send a System Message to the caster
                sendPacket(SystemMessageId.NOT_ENOUGH_MP);

                // Send a Server->Client packet ActionFailed to the L2Player
                sendPacket(ActionFailed.STATIC_PACKET);
            }
            return false;
        }

        // Check if the caster has enough HP
        if (getStatus().getCurrentHp() <= skill.getHpConsume()) {
            if (this instanceof L2Player) {
                // Send a System Message to the caster
                sendPacket(SystemMessageId.NOT_ENOUGH_HP);

                // Send a Server->Client packet ActionFailed to the L2Player
                sendPacket(ActionFailed.STATIC_PACKET);
            }
            return false;
        }

        switch (skill.getSkillType()) {
        case SUMMON_TRAP: {
            if (isInsideZone(L2Zone.FLAG_PEACE)) {
                if (this instanceof L2Player)
                    sendPacket(SystemMessageId.A_MALICIOUS_SKILL_CANNOT_BE_USED_IN_PEACE_ZONE);
                return false;
            }
            if (this instanceof L2Player && ((L2Player) this).getTrap() != null) {
                // Send a Server->Client packet ActionFailed to the L2Player
                sendPacket(ActionFailed.STATIC_PACKET);
                return false;
            }

            break;
        }
        case SUMMON: {
            if (!((L2SkillSummon) skill).isCubic() && this instanceof L2Player
                    && (getPet() != null || ((L2Player) this).isMounted())) {
                if (_log.isDebugEnabled())
                    _log.info("player has a pet already. ignore summon skill");

                sendPacket(SystemMessageId.YOU_ALREADY_HAVE_A_PET);
                return false;
            }
            break;
        }
        case HEAL: {
            if (isInsideZone(L2Zone.FLAG_NOHEAL)) {
                sendPacket(new SystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill));
                return false;
            }
        }
        }

        if (!skill.isPotion()) {
            // Check if the skill is a magic spell and if the L2Creature is not muted
            if (skill.isMagic()) {
                if (isMuted()) {
                    // Send a Server->Client packet ActionFailed to the L2Player
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return false;
                }
            } else {
                // Check if the skill is physical and if the L2Creature is not physical_muted
                if (isPhysicalMuted()) {
                    // Send a Server->Client packet ActionFailed to the L2Player
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return false;
                } else if (isPhysicalAttackMuted()) // Prevent use attack
                {
                    // Send a Server->Client packet ActionFailed to the L2Player
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return false;
                }
            }
        }

        // prevent casting signets to peace zone
        if (skill.getSkillType() == L2SkillType.SIGNET || skill.getSkillType() == L2SkillType.SIGNET_CASTTIME) {
            L2WorldRegion region = getWorldRegion();
            if (region == null)
                return false;
            boolean canCast = true;
            if (skill.getTargetType() == SkillTargetType.TARGET_GROUND && this instanceof L2Player) {
                Point3D wp = ((L2Player) this).getCurrentSkillWorldPosition();
                if (!region.checkEffectRangeInsidePeaceZone(skill, wp.getX(), wp.getY(), wp.getZ()))
                    canCast = false;
            } else if (!region.checkEffectRangeInsidePeaceZone(skill, getX(), getY(), getZ()))
                canCast = false;
            if (!canCast) {
                SystemMessage sm = new SystemMessage(SystemMessageId.S1_CANNOT_BE_USED);
                sm.addSkillName(skill);
                sendPacket(sm);
                return false;
            }
        }

        // Check if the caster owns the weapon needed
        if (!skill.getWeaponDependancy(this, true)) {
            // Send a Server->Client packet ActionFailed to the L2Player
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if the spell consumes an Item
        // TODO: combine check and consume
        if (skill.getItemConsume() > 0 && getInventory() != null) {
            // Get the L2ItemInstance consumed by the spell
            L2ItemInstance requiredItems = getInventory().getItemByItemId(skill.getItemConsumeId());

            // Check if the caster owns enough consumed Item to cast
            if (requiredItems == null || requiredItems.getCount() < skill.getItemConsume()) {
                // Checked: when a summon skill failed, server show required consume item count
                if (skill.getSkillType() == L2SkillType.SUMMON) {
                    SystemMessage sm = new SystemMessage(SystemMessageId.SUMMONING_SERVITOR_COSTS_S2_S1);
                    sm.addItemName(skill.getItemConsumeId());
                    sm.addNumber(skill.getItemConsume());
                    sendPacket(sm);
                    return false;
                }

                // Send a System Message to the caster
                sendPacket(SystemMessageId.THERE_ARE_NOT_ENOUGH_NECESSARY_ITEMS_TO_USE_THE_SKILL);
                return false;
            }
        }
        return true;
    }

    public void startFusionSkill(L2Creature target, L2Skill skill) {
        if (skill.getSkillType() != L2SkillType.FUSION)
            return;

        if (_fusionSkill == null)
            _fusionSkill = new FusionSkill(this, target, (L2SkillFusion) skill);
    }

    /**
     * Kill the L2Creature.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Set target to null and cancel Attack or Cast </li>
     * <li>Stop movement </li>
     * <li>Stop HP/MP/CP Regeneration task </li>
     * <li>Stop all active skills effects in progress on the L2Creature </li>
     * <li>Send the Server->Client packet StatusUpdate with current HP and MP to all other L2Player to inform </li>
     * <li>Notify L2Creature AI </li>
     * <BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Npc : Create a DecayTask to remove the corpse of the L2Npc after 7 seconds </li>
     * <li> L2Attackable : Distribute rewards (EXP, SP, Drops...) and notify Quest Engine </li>
     * <li> L2Player : Apply Death Penalty, Manage gain/loss Karma and Item Drop </li>
     * <BR>
     * <BR>
     *
     * @param killer
     *            The L2Creature who killed it
     */
    public boolean doDie(L2Creature killer) {
        // starts the protection to make sure that the death animation finishes normally
        startDying();

        // killing is only possible one time
        synchronized (this) {
            if (isDead())
                return false;
            // now reset currentHp to zero
            getStatus().setCurrentHp(0);
            if (isFakeDeath())
                stopFakeDeath(true);
            setIsDead(true);
        }
        // Set target to null and cancel Attack or Cast
        setTarget(null);

        // Stop movement
        stopMove(null);

        // Stop HP/MP/CP Regeneration task
        getStatus().stopHpMpRegeneration();

        // Stop all active skills effects in progress on the L2Creature,
        // if the Character isn't affected by Soul of The Phoenix or Salvation
        if (this instanceof L2Playable) {
            final L2Playable pl = (L2Playable) this;

            if (pl.isPhoenixBlessed()) {
                if (pl.getCharmOfLuck()) //remove Lucky Charm if player has SoulOfThePhoenix/Salvation buff
                    pl.stopCharmOfLuck(true);
                if (pl.isNoblesseBlessed())
                    pl.stopNoblesseBlessing(true);
            }
            // Same thing if the Character isn't a Noblesse Blessed L2Playable
            else if (pl.isNoblesseBlessed()) {
                pl.stopNoblesseBlessing(true);
                if (pl.getCharmOfLuck()) // remove Lucky Charm if player have Nobless blessing buff
                    pl.stopCharmOfLuck(true);
            } else
                stopAllEffectsExceptThoseThatLastThroughDeath();
        } else
            stopAllEffectsExceptThoseThatLastThroughDeath();

        if (this instanceof L2Player && ((L2Player) this).getAgathionId() != 0)
            ((L2Player) this).setAgathionId(0);

        calculateRewards(killer);

        // Send the Server->Client packet StatusUpdate with current HP and MP to all other L2Player to inform
        broadcastStatusUpdate();

        if (getWorldRegion() != null)
            getWorldRegion().onDie(this);

        // Notify Quest of L2Playable's death
        L2Player actingPlayer = getActingPlayer();
        if (actingPlayer != null) {
            for (QuestState qs : actingPlayer.getNotifyQuestOfDeath()) {
                qs.getQuest().notifyDeath((killer == null ? this : killer), this, qs);
            }
        }

        // Notify L2Creature AI
        getAI().notifyEvent(CtrlEvent.EVT_DEAD, null);

        // If character is PhoenixBlessed
        // or has charm of courage inside siege battlefield (exact operation to be confirmed)
        // a resurrection popup will show up
        if (this instanceof L2Summon) {
            if (((L2Summon) this).isPhoenixBlessed() && ((L2Summon) this).getOwner() != null)
                ((L2Summon) this).getOwner().revivePetRequest(((L2Summon) this).getOwner(), null);
        } else if (this instanceof L2Player) {
            if (((L2Playable) this).isPhoenixBlessed())
                ((L2Player) this).reviveRequest(((L2Player) this), null);
            else if (((L2Player) this).getCharmOfCourage() && ((L2Player) this).isInSiege()) {
                ((L2Player) this).reviveRequest(((L2Player) this), null);
            }
        }

        try {
            if (_fusionSkill != null)
                abortCast();

            for (L2Creature character : getKnownList().getKnownCharacters())
                if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
                    character.abortCast();
        } catch (Exception e) {
            _log.fatal("", e);
        }

        getAttackByList().clear();
        return true;
    }

    /**
     * @param killer
     */
    protected void calculateRewards(L2Creature killer) {
    }

    /** Sets HP, MP and CP and revives the L2Creature. */
    public void doRevive() {
        if (!isDead())
            return;
        if (!isTeleporting()) {
            setIsPendingRevive(false);
            setIsDead(false);

            boolean restorefull = false;

            if (this instanceof L2Playable && ((L2Playable) this).isPhoenixBlessed()) {
                restorefull = true;
                ((L2Playable) this).stopPhoenixBlessing(true);
            }

            if (restorefull) {
                //_status.setCurrentCp(getMaxCp()); //this is not confirmed...
                _status.setCurrentHp(getMaxHp()); //confirmed
                _status.setCurrentMp(getMaxMp()); //and also confirmed
            } else {
                _status.setCurrentHp(getMaxHp() * Config.RESPAWN_RESTORE_HP);
                //_status.setCurrentCp(getMaxCp() * Config.RESPAWN_RESTORE_CP);
                //_status.setCurrentMp(getMaxMp() * Config.RESPAWN_RESTORE_MP);
            }

            // Start broadcast status
            broadcastPacket(new Revive(this));

            if (getWorldRegion() != null)
                getWorldRegion().onRevive(this);
        } else
            setIsPendingRevive(true);
    }

    /** Revives the L2Creature using skill.
     * @param revivePower */
    public void doRevive(double revivePower) {
        doRevive();
    }

    // =========================================================
    // Property - Public
    /**
     * Return the L2CreatureAI of the L2Creature and if its null create a new one.
     */
    public L2CreatureAI getAI() {
        if (_ai == null) {
            synchronized (this) {
                if (_ai == null)
                    _ai = initAI();
            }
        }

        return _ai;
    }

    protected L2CreatureAI initAI() {
        return new L2CreatureAI(new AIAccessor());
    }

    public final void setAI(L2CreatureAI newAI) {
        if (!canReplaceAI())
            return;

        final L2CreatureAI oldAI = _ai;

        if (oldAI == newAI)
            return;

        if (oldAI != null)
            oldAI.stopAITask();

        _ai = newAI;
    }

    /** Return True if the L2Creature has a L2CreatureAI. */
    public boolean hasAI() {
        return _ai != null;
    }

    protected boolean canReplaceAI() {
        return true;
    }

    /** Return True if the L2Creature is RaidBoss or his minion. */
    public boolean isRaid() {
        return _isRaid;
    }

    /**
     * Set this Npc as a Raid instance.<BR><BR>
     * @param isRaid
     */
    public void setIsRaid(boolean isRaid) {
        _isRaid = isRaid;
    }

    /** Return a list of L2Creature that attacked. */
    public final Set<L2Creature> getAttackByList() {
        if (_attackByList == null)
            _attackByList = new LazyFastSet<L2Creature>();

        return _attackByList;
    }

    /*
    public final L2Creature getAttackingChar()
    {
       return _attackingChar;
    }
    */

    /**
     * Set _attackingChar to the L2Creature that attacks this one.<BR>
     * <BR>
     *
     * @param player
     *            The L2Creature that attcks this one
     */
    /*
    public final void setAttackingChar(L2Creature player)
    {
       if (player == null || player == this)
     return;
       _attackingChar = player;
       addAttackerToAttackByList(player);
    }
    */

    public final L2Skill getLastSimultaneousSkillCast() {
        return _lastSimultaneousSkillCast;
    }

    public void setLastSimultaneousSkillCast(L2Skill skill) {
        _lastSimultaneousSkillCast = skill;
    }

    public final boolean isNoRndWalk() {
        return _isNoRndWalk;
    }

    public final void setIsNoRndWalk(boolean value) {
        _isNoRndWalk = value;
    }

    public final boolean isAfraid() {
        return _isAfraid;
    }

    public final void setIsAfraid(boolean value) {
        _isAfraid = value;
        updateAbnormalEffect();
    }

    /** Return True if the L2Creature can't use its skills (ex : stun, sleep...). */
    public boolean isAllSkillsDisabled() {
        return _allSkillsDisabled || isStunned() || isSleeping() || isImmobileUntilAttacked() || isParalyzed();
    }

    /** Return True if the L2Creature can't attack (stun, sleep, attackEndTime, fakeDeath, paralyse). */
    public boolean isAttackingDisabled() {
        return isStunned() || isSleeping() || isImmobileUntilAttacked() || isAttackingNow() || isAlikeDead()
                || isParalyzed() || isFallsdown() || isPhysicalAttackMuted() || isCoreAIDisabled() || isFlying();
    }

    public boolean isInProtectedAction() {
        return isAllSkillsDisabled() || isCastingNow() || isAttackingNow();
    }

    public final Calculator[] getCalculators() {
        return _calculators;
    }

    public final boolean isConfused() {
        return _isConfused;
    }

    public final void setIsConfused(boolean value) {
        _isConfused = value;
        updateAbnormalEffect();
    }

    private static final byte DEATH_ANIMATION_NONE = 0;
    private static final byte DEATH_ANIMATION_RUNNING = 1;
    private static final byte DEATH_ANIMATION_RUNNING_AND_BROADCAST_NEEDED = 2;

    /**
     * Starts the protection to make sure that the death animation finishes normally.
     */
    public final void startDying() {
        _isDying = DEATH_ANIMATION_RUNNING;

        // broadcast status now just to be sure
        broadcastFullInfoImpl();

        ThreadPoolManager.getInstance().schedule(new Runnable() {
            @Override
            public void run() {
                final byte isDying = _isDying;

                _isDying = DEATH_ANIMATION_NONE;

                if (isDying == DEATH_ANIMATION_RUNNING_AND_BROADCAST_NEEDED)
                    broadcastFullInfo();
            }
        }, 2000);
    }

    /**
     * @return indicates that the {@link PacketBroadcaster} should broadcast full info about the character, or not
     *         because of the death animation
     */
    public final boolean isDying() {
        // normal state
        if (_isDying == DEATH_ANIMATION_NONE)
            return false;

        // death animation is in progress
        _isDying = DEATH_ANIMATION_RUNNING_AND_BROADCAST_NEEDED;
        return true;
    }

    public final boolean isDead() {
        return _isDead;
    }

    public final void setIsDead(boolean value) {
        _isDead = value;
    }

    /** Return True if the L2Creature is dead or use fake death.  */
    public final boolean isAlikeDead() {
        return isFakeDeath() || _isDead;
    }

    public final boolean isFakeDeath() {
        return _isFakeDeath;
    }

    public final void setIsFakeDeath(boolean value) {
        _isFakeDeath = value;
    }

    // [L2J_JP_ADD START]
    public final boolean isFallsdown() {
        return _isFallsdown;
    }

    public final void setIsFallsdown(boolean value) {
        _isFallsdown = value;
    }

    public boolean isImmobilized() {
        return _isImmobilized;
    }

    public void setIsImmobilized(boolean value) {
        _isImmobilized = value;
    }

    public final boolean isMuted() {
        return _isMuted;
    }

    public final void setIsMuted(boolean value) {
        _isMuted = value;
        updateAbnormalEffect();
    }

    public final boolean isPhysicalMuted() {
        return _isPhysicalMuted;
    }

    public final void setIsPhysicalMuted(boolean value) {
        _isPhysicalMuted = value;
        updateAbnormalEffect();
    }

    public final boolean isPhysicalAttackMuted() {
        return _isPhysicalAttackMuted;
    }

    public final void setIsPhysicalAttackMuted(boolean value) {
        _isPhysicalAttackMuted = value;
    }

    public void disableCoreAI(boolean val) {
        _AIdisabled = val;
    }

    public boolean isCoreAIDisabled() {
        return _AIdisabled;
    }

    /** Return True if the L2Creature can't move (stun, root, sleep, overload, paralyzed). */
    public boolean isMovementDisabled() {
        // check for isTeleporting to prevent teleport cheating (if appear packet not received)
        return isStunned() || isRooted() || isSleeping() || isTeleporting() || isImmobileUntilAttacked()
                || isOverloaded() || isParalyzed() || isImmobilized() || isFakeDeath() || isFallsdown();
    }

    /** Return True if the L2Creature can not be controlled by the player (confused, afraid). */
    public boolean isOutOfControl() {
        return isConfused() || isAfraid();
    }

    public boolean isOverloaded() {
        return false;
    }

    public final boolean isParalyzed() {
        return _isParalyzed;
    }

    public final void setIsParalyzed(boolean value) {
        _isParalyzed = value;
        updateAbnormalEffect();
    }

    public final boolean isPendingRevive() {
        return isDead() && _isPendingRevive;
    }

    public final void setIsPendingRevive(boolean value) {
        _isPendingRevive = value;
    }

    public final boolean isDisarmed() {
        return _isDisarmed;
    }

    public final void setIsDisarmed(boolean value) {
        _isDisarmed = value;
    }

    public final boolean isEradicated() {
        return _isEradicated;
    }

    public final void setIsEradicated(boolean value) {
        _isEradicated = value;
    }

    /**
     * Return the L2Summon of the L2Creature.<BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player</li>
     * <BR>
     * <BR>
     */
    public L2Summon getPet() {
        return null;
    }

    public final boolean isRooted() {
        return _isRooted;
    }

    public final void setIsRooted(boolean value) {
        _isRooted = value;
        updateAbnormalEffect();
    }

    /** Return True if the L2Creature is running. */
    public boolean isRunning() {
        return _isRunning;
    }

    public final void setIsRunning(boolean value) {
        _isRunning = value;
        if (getRunSpeed() != 0)
            broadcastPacket(new ChangeMoveType(this));

        broadcastFullInfo();
    }

    /** Set the L2Creature movement type to run and send Server->Client packet ChangeMoveType to all others L2Player. */
    public final void setRunning() {
        if (!isRunning())
            setIsRunning(true);
    }

    public final boolean isSleeping() {
        return _isSleeping;
    }

    public final void setIsImmobileUntilAttacked(boolean value) {
        _isImmobileUntilAttacked = value;
        updateAbnormalEffect();
    }

    public final boolean isImmobileUntilAttacked() {
        return _isImmobileUntilAttacked;
    }

    public final void setIsSleeping(boolean value) {
        _isSleeping = value;
        updateAbnormalEffect();
    }

    public final boolean isBlessedByNoblesse() {
        return _isBlessedByNoblesse;
    }

    public final void setIsBlessedByNoblesse(boolean value) {
        _isBlessedByNoblesse = value;
    }

    public final boolean isLuckByNoblesse() {
        return _isLuckByNoblesse;
    }

    public final void setIsLuckByNoblesse(boolean value) {
        _isLuckByNoblesse = value;
    }

    public final boolean isStunned() {
        return _isStunned;
    }

    public final void setIsStunned(boolean value) {
        _isStunned = value;
        updateAbnormalEffect();
    }

    public final void setIsPetrified(boolean value) {
        if (value) {
            startParalyze();
            setIsInvul(true);
        } else {
            stopParalyze(false);
            setIsInvul(false);
        }
    }

    public final boolean isBetrayed() {
        return _isBetrayed;
    }

    public final void setIsBetrayed(boolean value) {
        _isBetrayed = value;
    }

    public final boolean isTeleporting() {
        return _isTeleporting;
    }

    public void setIsTeleporting(boolean value) {
        _isTeleporting = value;
    }

    public void setIsInvul(boolean b) {
        _isInvul = b;
    }

    public void setIsInvulByEffect(L2Effect effect) {
        _invulEffect = effect;
    }

    public boolean isInvul() {
        return _isInvul || _invulEffect != null || _isTeleporting
                || GlobalRestrictions.isInvul(null, this, null, false);
    }

    public L2Effect getInvulEffect() {
        return _invulEffect;
    }

    public boolean isUndead() {
        return _template.isUndead();
    }

    public final boolean isFlying() {
        return _isFlying;
    }

    public void setIsFlying(boolean mode) {
        _isFlying = mode;
    }

    protected CreatureKnownList initKnownList() {
        return new CreatureKnownList(this);
    }

    @Override
    public CreatureKnownList getKnownList() {
        return _knownList;
    }

    protected ICreatureView initView() {
        return null;
    }

    public ICreatureView getView() {
        return _view;
    }

    protected CreatureStat initStat() {
        return new CreatureStat(this);
    }

    public CreatureStat getStat() {
        return _stat;
    }

    protected CreatureStatus initStatus() {
        return new CreatureStatus(this);
    }

    public CreatureStatus getStatus() {
        return _status;
    }

    public L2CreatureTemplate getTemplate() {
        return _template;
    }

    /**
     * Set the template of the L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * Each L2Creature owns generic and static properties (ex : all Keltir have the same number of HP...). All of those properties are stored in a different
     * template for each type of L2Creature. Each template is loaded once in the server cache memory (reduce memory use). When a new instance of L2Creature is
     * spawned, server just create a link between the instance and the template This link is stored in <B>_template</B><BR>
     * <BR>
     * <B><U> Assert </U> :</B><BR>
     * <BR>
     * <li> this instanceof L2Creature</li>
     * <BR>
     * <BR
     */
    protected final void setTemplate(L2CreatureTemplate template) {
        _template = template;
    }

    /** Return the Title of the L2Creature. */
    public final String getTitle() {
        return _title;
    }

    /** Set the Title of the L2Creature. */
    public void setTitle(String value) {
        _title = value;
    }

    /** Set the L2Creature movement type to walk and send Server->Client packet ChangeMoveType to all others L2Player. */
    public final void setWalking() {
        if (isRunning())
            setIsRunning(false);
    }

    /**
     * Task launching the function onHitTimer().<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>If the attacker/target is dead or use fake death, notify the AI with EVT_CANCEL and send a Server->Client packet ActionFailed (if attacker is a
     * L2Player)</li>
     * <li>If attack isn't aborted, send a message system (critical hit, missed...) to attacker/target if they are L2Player </li>
     * <li>If attack isn't aborted and hit isn't missed, reduce HP of the target and calculate reflection damage to reduce HP of attacker if necessary </li>
     * <li>if attack isn't aborted and hit isn't missed, manage attack or cast break of the target (calculating rate, sending message...) </li>
     * <BR>
     * <BR>
     */
    private final class HitTask implements Runnable {
        private final L2Creature _hitTarget;
        private final int _damage;
        private final boolean _crit;
        private final boolean _miss;

        public HitTask(L2Creature target, int damage, boolean crit, boolean miss) {
            _hitTarget = target;
            _damage = damage;
            _crit = crit;
            _miss = miss;
        }

        @Override
        public void run() {
            onHitTimer(_hitTarget, _damage, _crit, _miss, 1.0);
        }

        public void hitByPole(double vampiricMulti) {
            onHitTimer(_hitTarget, _damage, _crit, _miss, vampiricMulti);
        }
    }

    private static final class PolearmHitTask implements Runnable {
        private final ArrayList<HitTask> _hitTasks;

        public PolearmHitTask(ArrayList<HitTask> hitTasks) {
            _hitTasks = hitTasks;
        }

        @Override
        public void run() {
            double vampiricMulti = 1.0;

            for (int i = 0; i < _hitTasks.size(); i++) {
                final HitTask hitTask = _hitTasks.get(i);

                hitTask.hitByPole(vampiricMulti);

                vampiricMulti *= Config.ALT_POLEARM_VAMPIRIC_MULTI;
            }
        }
    }

    private static final class MagicEnv {
        private final L2Skill _skill;
        private final List<L2Creature> _targets;
        private final L2Creature _originalTarget;
        private final L2Creature _originalSkillTarget;
        private final int _coolTime;
        private final boolean _simultaneously;

        private MagicEnv(L2Skill skill, L2Creature[] targets, L2Object originalTarget,
                L2Creature originalSkillTarget, int coolTime, boolean simultaneously) {
            if (skill == null)
                throw new NullPointerException();

            _skill = skill;
            _targets = L2Arrays.asForeachSafeList(targets);
            _originalTarget = L2Object.getActingCharacter(originalTarget);
            _originalSkillTarget = originalSkillTarget;
            _coolTime = coolTime;
            _simultaneously = simultaneously;
        }
    }

    private final class MagicLaunchedTimer implements Runnable {
        private final MagicEnv _magicEnv;

        private MagicLaunchedTimer(MagicEnv magicEnv) {
            _magicEnv = magicEnv;
        }

        @Override
        public void run() {
            try {
                onMagicLaunchedTimer(_magicEnv);
            } catch (Exception e) {
                _log.error(e.getMessage(), e);

                if (_magicEnv._simultaneously)
                    setIsCastingSimultaneouslyNow(false);
                else
                    setIsCastingNow(false);
            }
        }
    }

    private final class MagicHitTimer implements Runnable {
        private final MagicEnv _magicEnv;

        private MagicHitTimer(MagicEnv magicEnv) {
            _magicEnv = magicEnv;
        }

        @Override
        public void run() {
            try {
                onMagicHitTimer(_magicEnv);
            } catch (Exception e) {
                _log.error(e.getMessage(), e);

                if (_magicEnv._simultaneously)
                    setIsCastingSimultaneouslyNow(false);
                else
                    setIsCastingNow(false);
            }
        }
    }

    private final class MagicFinalizer implements Runnable {
        private final MagicEnv _magicEnv;

        private MagicFinalizer(MagicEnv magicEnv) {
            _magicEnv = magicEnv;
        }

        @Override
        public void run() {
            try {
                onMagicFinalizer(_magicEnv);
            } catch (Exception e) {
                _log.error(e.getMessage(), e);

                if (_magicEnv._simultaneously)
                    setIsCastingSimultaneouslyNow(false);
                else
                    setIsCastingNow(false);
            }
        }
    }

    /** Task of AI notification */
    public class NotifyAITask implements Runnable {
        private final CtrlEvent _evt;

        NotifyAITask(CtrlEvent evt) {
            _evt = evt;
        }

        @Override
        public void run() {
            getAI().notifyEvent(_evt, null);
        }
    }

    // =========================================================

    // =========================================================
    // Abnormal Effect - NEED TO REMOVE ONCE L2CHARABNORMALEFFECT IS COMPLETE
    // Data Field
    /** Map 32 bits (0x0000) containing all abnormal effect in progress */
    private int _AbnormalEffects;

    protected final CreatureEffects _effects;

    protected CreatureEffects initEffects() {
        return new CreatureEffects(this);
    }

    public CreatureEffects getEffects() {
        return _effects;
    }

    private int _SpecialEffects;

    // Method - Public
    /**
     * Active abnormal effects flags in the binary mask and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startAbnormalEffect(AbnormalEffect mask) {
        startAbnormalEffect(mask.getMask());
    }

    /**
     * Active special effects flags in the binary mask and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startSpecialEffect(SpecialEffect mask) {
        startSpecialEffect(mask.getMask());
    }

    public final void startAbnormalEffect(int mask) {
        if (_AbnormalEffects != (_AbnormalEffects |= mask))
            updateAbnormalEffect();
    }

    public final void startSpecialEffect(int mask) {
        if (_SpecialEffects != (_SpecialEffects |= mask))
            updateAbnormalEffect();
    }

    /**
     * Active the abnormal effect Confused flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startConfused() {
        setIsConfused(true);
        getAI().notifyEvent(CtrlEvent.EVT_CONFUSED);
    }

    /**
     * Active the abnormal effect Fake Death flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startFakeDeath() {
        // [L2J_JP ADD START]
        setIsFallsdown(true);

        /*
        if (Config.ALT_FAIL_FAKEDEATH)
        {
           // It fails in Fake Death at the probability
           setIsFakeDeath(true);
           if (_attackingChar != null)
           {
        int _diff;
        _diff = _attackingChar.getLevel() - getLevel();
        switch (_diff)
        {
        case 1:
        case 2:
        case 3:
        case 4:
        case 5:
           if (Rnd.nextInt(100) >= 95) // fails at 5%.
              setIsFakeDeath(false);
           break;
        case 6:
           if (Rnd.nextInt(100) >= 90) // fails at 10%.
              setIsFakeDeath(false);
           break;
        case 7:
           if (Rnd.nextInt(100) >= 85) // fails at 15%.
              setIsFakeDeath(false);
           break;
        case 8:
           if (Rnd.nextInt(100) >= 80) // fails at 20%.
              setIsFakeDeath(false);
           break;
        case 9:
           if (Rnd.nextInt(100) >= 75) // fails at 25%.
              setIsFakeDeath(false);
           break;
        default:
           if (_diff > 9)
           {
              if (Rnd.nextInt(100) >= 50) // fails at 50%.
                 setIsFakeDeath(false);
           }
           else
           {
              setIsFakeDeath(true);
           }
        }
        // If _attackingChar is L2RaidBoss, Fake Death will have failed.
        if (_attackingChar.isRaid())
        {
           setIsFakeDeath(false);
        }
           }
           else
           // attacked from aggressive monster
           {
        if (Rnd.nextInt(100) >= 75) // fails at 25%.
           setIsFakeDeath(false);
           }
        }
        else
        {
           setIsFakeDeath(true);
        }
        // [L2J_JP ADD END]
        */

        setIsFakeDeath(true);
        /* Aborts any attacks/casts if fake dead */
        abortAttack();
        abortCast();
        stopMove(null);
        sendPacket(ActionFailed.STATIC_PACKET);
        getAI().notifyEvent(CtrlEvent.EVT_FAKE_DEATH, null);
        broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_START_FAKEDEATH));
    }

    /**
     * Active the abnormal effect Fear flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startFear() {
        setIsAfraid(true);
        getAI().notifyEvent(CtrlEvent.EVT_AFRAID);
    }

    /**
     * Active the abnormal effect Muted flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startMuted() {
        setIsMuted(true);
        /* Aborts any casts if muted */
        abortCast();
        getAI().notifyEvent(CtrlEvent.EVT_MUTED);
    }

    /**
     * Active the abnormal effect Physical_Muted flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startPhysicalMuted() {
        setIsPhysicalMuted(true);
        getAI().notifyEvent(CtrlEvent.EVT_MUTED);
    }

    /**
     * Active the abnormal effect Root flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startRooted() {
        setIsRooted(true);
        stopMove(null);
        getAI().notifyEvent(CtrlEvent.EVT_ROOTED, null);
    }

    /**
     * Active the abnormal effect Sleep flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet.<BR>
     * <BR>
     */
    public final void startSleeping() {
        setIsSleeping(true);
        /* Aborts any attacks/casts if sleeped */
        abortAttack();
        abortCast();
        stopMove(null);
        getAI().notifyEvent(CtrlEvent.EVT_SLEEPING, null);
    }

    public final void startImmobileUntilAttacked() {
        setIsImmobileUntilAttacked(true);
        abortAttack();
        abortCast();
        stopMove(null);
        getAI().notifyEvent(CtrlEvent.EVT_SLEEPING, null);
    }

    public final void startLuckNoblesse() {
        setIsBlessedByNoblesse(true);
        getAI().notifyEvent(CtrlEvent.EVT_LUCKNOBLESSE, null);
    }

    public final void stopLuckNoblesse() {
        setIsBlessedByNoblesse(false);
        getAI().notifyEvent(CtrlEvent.EVT_LUCKNOBLESSE, null);
    }

    /**
     * Launch a Stun Abnormal Effect on the L2Creature.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Calculate the success rate of the Stun Abnormal Effect on this L2Creature</li>
     * <li>If Stun succeed, active the abnormal effect Stun flag, notify the L2Creature AI and send Server->Client UserInfo/CharInfo packet</li>
     * <li>If Stun NOT succeed, send a system message Failed to the L2Player attacker</li>
     * <BR>
     * <BR>
     */
    public final void startStunning() {
        setIsStunned(true);
        /* Aborts any attacks/casts if stunned */
        abortAttack();
        abortCast();
        stopMove(null);
        getAI().notifyEvent(CtrlEvent.EVT_STUNNED, null);
    }

    public final void startParalyze() {
        setIsParalyzed(true);
        /* Aborts any attacks/casts if paralyzed */
        abortAttack();
        abortCast();
        stopMove(null);
        getAI().notifyEvent(CtrlEvent.EVT_PARALYZED, null);
    }

    public final void startBetray() {
        setIsBetrayed(true);
        getAI().notifyEvent(CtrlEvent.EVT_BETRAYED, null);
        updateAbnormalEffect();
    }

    public final void stopBetray() {
        stopEffects(L2EffectType.BETRAY);
        setIsBetrayed(false);
        updateAbnormalEffect();
    }

    /**
     * Modify the abnormal effect map according to the mask.<BR>
     * <BR>
     */
    public final void stopAbnormalEffect(AbnormalEffect mask) {
        stopAbnormalEffect(mask.getMask());
    }

    /**
     * Modify the special effect map according to the mask.<BR>
     * <BR>
     */
    public final void stopSpecialEffect(SpecialEffect mask) {
        stopSpecialEffect(mask.getMask());
    }

    public final void stopAbnormalEffect(int mask) {
        if (_AbnormalEffects != (_AbnormalEffects &= ~mask))
            updateAbnormalEffect();
    }

    public final void stopSpecialEffect(int mask) {
        if (_SpecialEffects != (_SpecialEffects &= ~mask))
            updateAbnormalEffect();
    }

    /**
     * Stop all active skills effects in progress on the L2Creature.<BR>
     * <BR>
     */
    public final void stopAllEffects() {
        getEffects().stopAllEffects();

        broadcastFullInfo();
    }

    public final void stopAllEffects(boolean stopEffectsThatLastThroughDeathToo) {
        getEffects().stopAllEffects(stopEffectsThatLastThroughDeathToo);

        broadcastFullInfo();
    }

    public final void stopAllEffectsExceptThoseThatLastThroughDeath() {
        getEffects().stopAllEffectsExceptThoseThatLastThroughDeath();

        broadcastFullInfo();
    }

    /**
     * Stop a specified/all Confused abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Confused abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _confused to False </li>
     * <li>Notify the L2Creature AI</li>
     * <li>Send Server->Client UserInfo/CharInfo packet</li>
     * <BR>
     * <BR>
     */
    public final void stopConfused(boolean all) {
        if (all)
            stopEffects(L2EffectType.CONFUSION);

        setIsConfused(false);
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    public final void startPhysicalAttackMuted() {
        setIsPhysicalAttackMuted(true);
        abortAttack();
    }

    public final void stopPhysicalAttackMuted(boolean all) {
        if (all)
            stopEffects(L2EffectType.PHYSICAL_ATTACK_MUTE);

        setIsPhysicalAttackMuted(false);
    }

    /**
     * Stop and remove the L2Effects corresponding to the L2Skill Identifier and update client magic icon.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All active skills effects in progress on the L2Creature are identified in ConcurrentHashMap(Integer,L2Effect) <B>_effects</B>. The Integer key of
     * _effects is the L2Skill Identifier that has created the L2Effect.<BR>
     * <BR>
     *
     * @param skillId
     *            The L2Skill Identifier of the L2Effect to remove from _effects
     */
    public final void stopSkillEffects(int skillId) {
        getEffects().stopEffects(skillId);
    }

    /**
     * Stop and remove all L2Effect of the selected type (ex : BUFF, DMG_OVER_TIME...) from the L2Creature and update client magic icon.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All active skills effects in progress on the L2Creature are identified in ConcurrentHashMap(Integer,L2Effect) <B>_effects</B>. The Integer key of
     * _effects is the L2Skill Identifier that has created the L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Remove Func added by this effect from the L2Creature Calculator (Stop L2Effect)</li>
     * <li>Remove the L2Effect from _effects of the L2Creature</li>
     * <li>Update active skills in progress icons on player client</li>
     * <BR>
     * <BR>
     *
     * @param type
     *            The type of effect to stop ((ex : BUFF, DMG_OVER_TIME...)
     */
    public final void stopEffects(L2EffectType type) {
        getEffects().stopEffects(type);
    }

    /**
     * Stop a specified/all Fake Death abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Fake Death abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _fake_death to False </li>
     * <li>Notify the L2Creature AI</li>
     * <BR>
     * <BR>
     */
    public final void stopFakeDeath(boolean all) {
        if (all)
            stopEffects(L2EffectType.FAKE_DEATH);

        setIsFakeDeath(false);
        setIsFallsdown(false); // [L2J_JP_ADD]
        // if this is a player instance, start the grace period for this character (grace from mobs only)!
        if (this instanceof L2Player) {
            ((L2Player) this).setRecentFakeDeath(true);
        }
        ChangeWaitType revive = new ChangeWaitType(this, ChangeWaitType.WT_STOP_FAKEDEATH);
        broadcastPacket(revive);
        //TODO: Temp hack: players see FD on ppl that are moving: Teleport to someone who uses FD - if he gets up he will fall down again for that client -
        // even tho he is actually standing... Probably bad info in CharInfo packet?
        broadcastPacket(new Revive(this));
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    /**
     * Stop a specified/all Fear abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Fear abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _afraid to False </li>
     * <li>Notify the L2Creature AI</li>
     * <li>Send Server->Client UserInfo/CharInfo packet</li>
     * <BR>
     * <BR>
     */
    public final void stopFear(boolean all) {
        if (all)
            stopEffects(L2EffectType.FEAR);

        setIsAfraid(false);
    }

    /**
     * Stop a specified/all Muted abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Muted abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _muted to False </li>
     * <li>Notify the L2Creature AI</li>
     * <li>Send Server->Client UserInfo/CharInfo packet</li>
     * <BR>
     * <BR>
     */
    public final void stopMuted(boolean all) {
        if (all)
            stopEffects(L2EffectType.MUTE);

        setIsMuted(false);
    }

    public final void stopPhysicalMuted(boolean all) {
        if (all)
            stopEffects(L2EffectType.PHYSICAL_MUTE);

        setIsPhysicalMuted(false);
    }

    /**
     * Stop a specified/all Root abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Root abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _rooted to False </li>
     * <li>Notify the L2Creature AI</li>
     * <li>Send Server->Client UserInfo/CharInfo packet</li>
     * <BR>
     * <BR>
     */
    public final void stopRooting(boolean all) {
        if (all)
            stopEffects(L2EffectType.ROOT);

        setIsRooted(false);
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    /**
     * Stop a specified/all Sleep abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Sleep abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _sleeping to False </li>
     * <li>Notify the L2Creature AI</li>
     * <li>Send Server->Client UserInfo/CharInfo packet</li>
     * <BR>
     * <BR>
     */
    public final void stopSleeping(boolean all) {
        if (all)
            stopEffects(L2EffectType.SLEEP);

        setIsSleeping(false);
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    public final void stopImmobileUntilAttacked(boolean all) {
        if (all)
            stopEffects(L2EffectType.IMMOBILEUNTILATTACKED);

        setIsImmobileUntilAttacked(false);
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    public final void stopNoblesse() {
        stopEffects(L2EffectType.NOBLESSE_BLESSING);
        stopEffects(L2EffectType.LUCKNOBLESSE);
        setIsBlessedByNoblesse(false);
        setIsLuckByNoblesse(false);
        getAI().notifyEvent(CtrlEvent.EVT_LUCKNOBLESSE, null);
    }

    /**
     * Stop a specified/all Stun abnormal L2Effect.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete a specified/all (if effect=null) Stun abnormal L2Effect from L2Creature and update client magic icon </li>
     * <li>Set the abnormal effect flag _stuned to False </li>
     * <li>Notify the L2Creature AI</li>
     * <li>Send Server->Client UserInfo/CharInfo packet</li>
     * <BR>
     * <BR>
     */
    public final void stopStunning(boolean all) {
        if (all)
            stopEffects(L2EffectType.STUN);

        setIsStunned(false);
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    public final void stopParalyze(boolean all) {
        if (all)
            stopEffects(L2EffectType.PARALYZE);

        setIsParalyzed(false);
        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
    }

    /**
    * Stop L2Effect: Transformation<BR><BR>
    *
    * <B><U> Actions</U> :</B><BR><BR>
    * <li>Remove Transformation Effect</li>
    * <li>Notify the L2Creature AI</li>
    * <li>Send Server->Client UserInfo/CharInfo packet</li><BR><BR>
    *
    */
    public final void stopTransformation(boolean all) {
        if (all)
            stopEffects(L2EffectType.TRANSFORMATION);

        // if this is a player instance, then untransform, also set the transform_id column equal to 0 if not cursed.
        if (this instanceof L2Player) {
            if (((L2Player) this).getTransformation() != null) {
                ((L2Player) this).untransform();
            }
        }

        getAI().notifyEvent(CtrlEvent.EVT_THINK, null);
        updateAbnormalEffect();
    }

    /**
     * Not Implemented.<BR>
     * <BR>
     * <B><U> Overridden in</U> :</B><BR>
     * <BR>
     * <li>L2Npc</li>
     * <li>L2Player</li>
     * <li>L2Summon</li>
     * <li>L2DoorInstance</li>
     * <BR>
     * <BR>
     */
    public final void updateAbnormalEffect() {
        broadcastFullInfo();
    }

    // Property - Public
    /**
     * Return a map of 16 bits (0x0000) containing all abnormal effect in progress for this L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * In Server->Client packet, each effect is represented by 1 bit of the map (ex : BLEEDING = 0x0001 (bit 1), SLEEP = 0x0080 (bit 8)...). The map is
     * calculated by applying a BINARY OR operation on each effect.<BR>
     * <BR>
     * <B><U> Example of use </U> :</B><BR>
     * <BR>
     * <li> Server Packet : CharInfo, NpcInfo, NpcInfoPoly, UserInfo...</li>
     * <BR>
     * <BR>
     */
    public int getAbnormalEffect() {
        int ae = _AbnormalEffects;
        if (!isFlying() && isStunned())
            ae |= AbnormalEffect.STUN.getMask();
        if (!isFlying() && isRooted())
            ae |= AbnormalEffect.ROOT.getMask();
        if (isSleeping())
            ae |= AbnormalEffect.SLEEP.getMask();
        if (isConfused())
            ae |= AbnormalEffect.CONFUSED.getMask();
        if (isMuted())
            ae |= AbnormalEffect.MUTED.getMask();
        if (isPhysicalMuted())
            ae |= AbnormalEffect.MUTED.getMask();
        return ae;
    }

    /**
    * Return a map of 32 bits (0x00000000) containing all special effect in progress for this L2Creature.<BR><BR>
    *
    * <B><U> Concept</U> :</B><BR><BR>
    * In Server->Client packet, each effect is represented by 1 bit of the map (ex : INVULNERABLE = 0x0001 (bit 1), PINK_AFFRO = 0x0020 (bit 6)...).
    * The map is calculated by applying a BINARY OR operation on each effect.<BR><BR>
    *
    * <B><U> Example of use </U> :</B><BR><BR>
    * <li> Server Packet : CharInfo, UserInfo...</li><BR><BR>
    */
    public int getSpecialEffect() {
        int se = _SpecialEffects;
        if (isFlying() && isStunned())
            se |= SpecialEffect.S_AIR_STUN.getMask();
        if (isFlying() && isRooted())
            se |= SpecialEffect.S_AIR_ROOT.getMask();
        return se;
    }

    /**
     * Return all active skills effects in progress on the L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All active skills effects in progress on the L2Creature are identified in <B>_effects</B>. The Integer key of _effects is the L2Skill Identifier that
     * has created the effect.<BR>
     * <BR>
     *
     * @return A table containing all active skills effect in progress on the L2Creature
     */
    public final L2Effect[] getAllEffects() {
        return getEffects().getAllEffects();
    }

    /**
     * Return L2Effect in progress on the L2Creature corresponding to the L2Skill Identifier.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All active skills effects in progress on the L2Creature are identified in <B>_effects</B>.
     *
     * @param index
     *            The L2Skill Identifier of the L2Effect to return from the _effects
     * @return The L2Effect corresponding to the L2Skill Identifier
     */
    public final L2Effect getFirstEffect(int skillId) {
        return getEffects().getFirstEffect(skillId);
    }

    /**
     * Return the first L2Effect in progress on the L2Creature created by the L2Skill.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All active skills effects in progress on the L2Creature are identified in <B>_effects</B>.
     *
     * @param skill
     *            The L2Skill whose effect must be returned
     * @return The first L2Effect created by the L2Skill
     */
    public final L2Effect getFirstEffect(L2Skill skill) {
        return getEffects().getFirstEffect(skill);
    }

    /**
     * Return the first L2Effect in progress on the L2Creature corresponding to the Effect Type (ex : BUFF, STUN, ROOT...).<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All active skills effects in progress on the L2Creature are identified in <B>_effects</B>.
     * <BR>
     *
     * @param tp
     *            The Effect Type of skills whose effect must be returned
     * @return The first L2Effect corresponding to the Effect Type
     */
    public final L2Effect getFirstEffect(L2EffectType tp) {
        return getEffects().getFirstEffect(tp);
    }

    // =========================================================

    // =========================================================
    // NEED TO ORGANIZE AND MOVE TO PROPER PLACE
    /** This class permit to the L2Creature AI to obtain informations and uses L2Creature method */
    public class AIAccessor {
        public AIAccessor() {
        }

        /**
         * Return the L2Creature managed by this Accessor AI.<BR>
         * <BR>
         */
        public L2Creature getActor() {
            return L2Creature.this;
        }

        /**
         * Accessor to L2Creature moveToLocation() method with an interaction area.<BR>
         * <BR>
         */
        public void moveTo(int x, int y, int z, int offset) {
            moveToLocation(x, y, z, offset);
        }

        /**
         * Accessor to L2Creature moveToLocation() method without interaction area.<BR>
         * <BR>
         */
        public void moveTo(int x, int y, int z) {
            moveToLocation(x, y, z, 0);
        }

        /**
         * Accessor to L2Creature stopMove() method.<BR>
         * <BR>
         */
        public void stopMove(L2CharPosition pos) {
            L2Creature.this.stopMove(pos);
        }

        /**
         * Accessor to L2Creature doAttack() method.<BR>
         * <BR>
         */
        public void doAttack(L2Creature target) {
            getEffects().dispelOnAction();
            getEffects().dispelOnAttack();

            if (L2Creature.this != target)
                L2Creature.this.doAttack(target);
        }

        /**
         * Accessor to L2Creature doCast() method.<BR>
         * <BR>
         */
        public void doCast(L2Skill skill) {
            getEffects().dispelOnAction();
            getEffects().dispelOnAttack();

            L2Creature.this.doCast(skill);
        }

        /**
         * Cancel the AI.<BR>
         * <BR>
         */
        public final void detachAI() {
            if (!canReplaceAI())
                return;

            _ai = null;
        }
    }

    /**
     * This class group all mouvement data.<BR>
     * <BR>
     * <B><U> Data</U> :</B><BR>
     * <BR>
     * <li>_moveTimestamp : Last time position update</li>
     * <li>_xDestination, _yDestination, _zDestination : Position of the destination</li>
     * <li>_xMoveFrom, _yMoveFrom, _zMoveFrom : Position of the origin</li>
     * <li>_moveStartTime : Start time of the movement</li>
     * <li>_ticksToMove : Nb of ticks between the start and the destination</li>
     * <li>_xSpeedTicks, _ySpeedTicks : Speed in unit/ticks</li>
     * <BR>
     * <BR>
     */
    public static class MoveData {
        // when we retrieve x/y/z we use GameTimeControl.getGameTicks()
        // if we are moving, but move timestamp==gameticks, we don't need
        // to recalculate position
        public int _moveStartTime;
        public int _moveTimestamp; // last update
        public int _xDestination;
        public int _yDestination;
        public int _zDestination;
        public double _xAccurate; // otherwise there would be rounding errors
        public double _yAccurate;
        public double _zAccurate;
        public int _yMoveFrom;
        public int _zMoveFrom;
        public int _heading;

        public boolean disregardingGeodata;
        public int onGeodataPathIndex;
        public Node[] geoPath;
        public int geoPathAccurateTx;
        public int geoPathAccurateTy;
        public int geoPathGtx;
        public int geoPathGty;
    }

    /** Table containing all skillId that are disabled */
    private Map<Integer, ScheduledFuture<?>> _disabledSkills;
    private boolean _allSkillsDisabled;

    // private int _flyingRunSpeed;
    // private int _floatingWalkSpeed;
    // private int _flyingWalkSpeed;
    // private int _floatingRunSpeed;

    /** Movement data of this L2Creature */
    protected MoveData _move;

    /** L2Charcater targeted by the L2Creature */
    private L2Object _target = null;

    // set by the start of attack, in game ticks
    private int _attacking;

    private long _castInterruptTime;

    /** Table of calculators containing all standard NPC calculator (ex : ACCURACY_COMBAT, EVASION_RATE */
    private static final Calculator[] NPC_STD_CALCULATOR;
    static {
        NPC_STD_CALCULATOR = Formulas.getStdNPCCalculators();
    }

    private L2CreatureAI _ai;

    /** Future Skill Cast */
    protected Future<?> _skillCast;
    protected Future<?> _skillCast2;

    /**
     * Return True if the L2Creature is avoiding a geodata obstacle.<BR>
     * <BR>
     */
    public final boolean isOnGeodataPath() {
        MoveData m = _move;
        if (m == null)
            return false;

        if (m.onGeodataPathIndex == -1)
            return false;

        return m.onGeodataPathIndex != m.geoPath.length - 1;
    }

    public final void addStatFunc(Func f) {
        if (f == null)
            return;

        synchronized (_calculators) {
            // Check if Calculator set is linked to the standard Calculator set of NPC
            if (_calculators == NPC_STD_CALCULATOR) {
                // Create a copy of the standard NPC Calculator set
                _calculators = new Calculator[Stats.NUM_STATS];

                for (int i = 0; i < Stats.NUM_STATS; i++) {
                    if (NPC_STD_CALCULATOR[i] != null)
                        _calculators[i] = new Calculator(NPC_STD_CALCULATOR[i]);
                }
            }

            // Select the Calculator of the affected state in the Calculator set
            int stat = f.stat.ordinal();

            if (_calculators[stat] == null)
                _calculators[stat] = new Calculator();

            // Add the Func to the calculator corresponding to the state
            _calculators[stat].addFunc(f);

            if (this instanceof L2Player)
                ((L2Player) this).onFuncAddition(f);
        }

        broadcastFullInfo();
    }

    public final void addStatFuncs(Func[] funcs) {
        for (Func f : funcs)
            addStatFunc(f);
    }

    public final void removeStatsOwner(FuncOwner owner) {
        // Go through the Calculator set
        synchronized (_calculators) {
            for (int i = 0; i < _calculators.length; i++) {
                if (_calculators[i] != null) {
                    // Delete all Func objects of the selected owner
                    _calculators[i].removeOwner(owner, this);

                    if (_calculators[i].size() == 0)
                        _calculators[i] = null;
                }
            }

            // If possible, free the memory and just create a link on NPC_STD_CALCULATOR
            if (this instanceof L2Npc) {
                int i = 0;
                for (; i < Stats.NUM_STATS; i++) {
                    if (!Calculator.equalsCals(_calculators[i], NPC_STD_CALCULATOR[i]))
                        break;
                }

                if (i >= Stats.NUM_STATS)
                    _calculators = NPC_STD_CALCULATOR;
            }
        }

        broadcastFullInfo();
    }

    public int getClientX() {
        return getX();
    }

    public int getClientY() {
        return getY();
    }

    public int getClientZ() {
        return getZ();
    }

    public int getClientHeading() {
        return getHeading();
    }

    public final int getXdestination() {
        MoveData m = _move;

        if (m != null)
            return m._xDestination;

        return getX();
    }

    /**
     * Return the Y destination of the L2Creature or the Y position if not in movement.<BR>
     * <BR>
     */
    public final int getYdestination() {
        MoveData m = _move;

        if (m != null)
            return m._yDestination;

        return getY();
    }

    /**
     * Return the Z destination of the L2Creature or the Z position if not in movement.<BR>
     * <BR>
     */
    public final int getZdestination() {
        MoveData m = _move;

        if (m != null)
            return m._zDestination;

        return getZ();
    }

    /**
     * Return True if the L2Creature is in combat.<BR>
     * <BR>
     */
    public boolean isInCombat() {
        return (getAI().getAttackTarget() != null || getAI().isAutoAttacking());
    }

    /**
     * Return True if the L2Creature is moving.<BR>
     * <BR>
     */
    @Override
    public final boolean isMoving() {
        return _move != null;
    }

    /**
     * Return True if the L2Creature is casting.<BR>
     * <BR>
     */
    public final boolean isCastingNow() {
        return _isCastingNow;
    }

    public final void setIsCastingNow(boolean value) {
        setIsCastingNow(value, 0);
    }

    public final void setIsCastingNow(boolean value, int interruptTime) {
        _isCastingNow = value;

        if (!value) {
            // safeguard for cannot be interrupt any more
            _castInterruptTime = 0;

            if (_skillCast != null) {
                _skillCast.cancel(false);
                _skillCast = null;
            }
        } else {
            _castInterruptTime = L2System.milliTime() + interruptTime;
        }
    }

    public final boolean isCastingSimultaneouslyNow() {
        return _isCastingSimultaneouslyNow;
    }

    public final void setIsCastingSimultaneouslyNow(boolean value) {
        _isCastingSimultaneouslyNow = value;

        if (!value) {
            if (_skillCast2 != null) {
                _skillCast2.cancel(false);
                _skillCast2 = null;
            }
        }
    }

    /**
     * Return True if the cast of the L2Creature can be aborted.<BR>
     * <BR>
     */
    public final boolean canAbortCast() {
        return _castInterruptTime > L2System.milliTime();
    }

    /**
     * Return True if the L2Creature is attacking.<BR>
     * <BR>
     */
    public boolean isAttackingNow() {
        return getAttackEndEvtReadyToAct().isScheduled();
    }

    /**
     * Return True if the L2Creature has aborted its attack.<BR>
     * <BR>
     */
    public final boolean isAttackAborted() {
        return _attacking <= 0;
    }

    /**
     * Abort the attack of the L2Creature and send Server->Client ActionFailed packet.<BR>
     * <BR>
     */
    public final void abortAttack() {
        if (isAttackingNow()) {
            _attacking = 0;
            sendPacket(ActionFailed.STATIC_PACKET);
        }
    }

    /**
     * Returns body part (paperdoll slot) we are targeting right now
     */
    public final int getAttackingBodyPart() {
        return _attacking;
    }

    /**
     * Abort the cast of the L2Creature and send Server->Client MagicSkillCanceld/ActionFailed packet.<BR>
     * <BR>
     */
    public final void abortCast() {
        if (isCastingNow() || isCastingSimultaneouslyNow()) {
            if (getFusionSkill() != null)
                getFusionSkill().onCastAbort();

            stopEffects(L2EffectType.SIGNET_GROUND);

            if (_allSkillsDisabled)
                enableAllSkills(); // this remains for forced skill use, e.g. scroll of escape
            setIsCastingNow(false);
            setIsCastingSimultaneouslyNow(false);
            //if (this instanceof L2Player)
            getAI().notifyEvent(CtrlEvent.EVT_FINISH_CASTING); // setting back previous intention
            broadcastPacket(new MagicSkillCanceled(getObjectId())); // broadcast packet to stop animations client-side
            sendPacket(ActionFailed.STATIC_PACKET); // send an "action failed" packet to the caster
        }
    }

    /**
     * Update the position of the L2Creature during a movement and return True if the movement is finished.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * At the beginning of the move action, all properties of the movement are stored in the MoveData object called <B>_move</B> of the L2Creature. The
     * position of the start point and of the destination permit to estimated in function of the movement speed the time to achieve the destination.<BR>
     * <BR>
     * When the movement is started (ex : by MovetoLocation), this method will be called each 0.1 sec to estimate and update the L2Creature position on the
     * server. Note, that the current server position can differe from the current client position even if each movement is straight foward. That's why, client
     * send regularly a Client->Server ValidatePosition packet to eventually correct the gap on the server. But, it's always the server position that is used in
     * range calculation.<BR>
     * <BR>
     * At the end of the estimated movement time, the L2Creature position is automatically set to the destination position even if the movement is not
     * finished.<BR>
     * <BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : The current Z position is obtained FROM THE CLIENT by the Client->Server ValidatePosition Packet. But x and y
     * positions must be calculated to avoid that players try to modify their movement speed.</B></FONT><BR>
     * <BR>
     *
     * @param gameTicks
     *            Nb of ticks since the server start
     * @return True if the movement is finished
     */
    public boolean updatePosition(int gameTicks) {
        // Get movement data
        MoveData m = _move;

        if (m == null)
            return true;

        if (!isVisible()) {
            _move = null;
            return true;
        }

        // Check if this is the first update
        if (m._moveTimestamp == 0) {
            m._moveTimestamp = m._moveStartTime;
            m._xAccurate = getX();
            m._yAccurate = getY();
        }

        // Check if the position has already been calculated
        if (m._moveTimestamp == gameTicks)
            return false;

        int xPrev = getX();
        int yPrev = getY();
        int zPrev = getZ(); // the z coordinate may be modified by coordinate synchronizations

        double dx, dy, dz, distFraction;
        if (Config.COORD_SYNCHRONIZE == 1)
        // the only method that can modify x,y while moving (otherwise _move would/should be set null)
        {
            dx = m._xDestination - xPrev;
            dy = m._yDestination - yPrev;
        } else
        // otherwise we need saved temporary values to avoid rounding errors
        {
            dx = m._xDestination - m._xAccurate;
            dy = m._yDestination - m._yAccurate;
        }
        // Z coordinate will follow geodata or client values
        if (Config.GEODATA > 0 && Config.COORD_SYNCHRONIZE == 2 && !isFlying() && !isInsideZone(L2Zone.FLAG_WATER)
                && !m.disregardingGeodata && GameTimeManager.getGameTicks() % 10 == 0
                && !(this instanceof L2BoatInstance) // once a second to reduce possible cpu load
                && !(this instanceof L2AirShipInstance)) {
            short geoHeight = GeoData.getInstance().getSpawnHeight(xPrev, yPrev, zPrev - 30, zPrev + 30,
                    getObjectId());
            dz = m._zDestination - geoHeight;
            // quite a big difference, compare to validatePosition packet
            if (this instanceof L2Player && Math.abs(getClientZ() - geoHeight) > 200
                    && Math.abs(getClientZ() - geoHeight) < 1500) {
                dz = m._zDestination - zPrev; // allow diff
            } else if (isInCombat() && Math.abs(dz) > 200 && (dx * dx + dy * dy) < 40000) // allow mob to climb up to pcinstance
            {
                dz = m._zDestination - zPrev; // climbing
            } else {
                zPrev = geoHeight;
            }
        } else
            dz = m._zDestination - zPrev;

        double distPassed = getStat().getMoveSpeed() * (gameTicks - m._moveTimestamp)
                / GameTimeManager.TICKS_PER_SECOND;
        if ((dx * dx + dy * dy) < 10000 && (dz * dz > 2500)) // close enough, allows error between client and server geodata if it cannot be avoided
        {
            distFraction = distPassed / Math.sqrt(dx * dx + dy * dy);
        } else
            distFraction = distPassed / Math.sqrt(dx * dx + dy * dy + dz * dz);

        if (distFraction > 1) // already there
        {
            // Set the position of the L2Creature to the destination
            super.getPosition().setXYZ(m._xDestination, m._yDestination, m._zDestination);
        } else {
            m._xAccurate += dx * distFraction;
            m._yAccurate += dy * distFraction;

            // Set the position of the L2Creature to estimated after parcial move
            super.getPosition().setXYZ((int) (m._xAccurate), (int) (m._yAccurate),
                    zPrev + (int) (dz * distFraction + 0.5));
        }

        // Set the timer of last position update to now
        m._moveTimestamp = gameTicks;

        return (distFraction > 1);
    }

    public boolean revalidateZone(boolean force) {
        if (!force) {
            CoordRevalidator.getInstance().add(this);
            return false;
        }

        final L2WorldRegion region = getWorldRegion();

        if (region == null)
            return false;

        region.revalidateZones(this);
        return true;
    }

    /**
     * Stop movement of the L2Creature (Called by AI Accessor only).<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Delete movement data of the L2Creature </li>
     * <li>Set the current position (x,y,z), its current L2WorldRegion if necessary and its heading </li>
     * <li>Remove the L2Object object from _gmList** of GmListTable </li>
     * <li>Remove object from _knownObjects and _knownPlayer* of all surrounding L2WorldRegion L2Creatures </li>
     * <BR>
     * <BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T send Server->Client packet StopMove/StopRotation </B></FONT><BR>
     * <BR>
     */
    public void stopMove(L2CharPosition pos) {
        stopMove(pos, false);
    }

    public void stopMove(L2CharPosition pos, boolean updateKnownObjects) {
        // Delete movement data of the L2Creature
        _move = null;

        // if (getAI() != null)
        // getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

        // Set the current position (x,y,z), its current L2WorldRegion if necessary and its heading
        // All data are contained in a L2CharPosition object
        if (pos != null) {
            getPosition().setXYZ(pos.x, pos.y, pos.z);
            setHeading(pos.heading);
            revalidateZone(true);
        }
        broadcastPacket(new StopMove(this));
        if (updateKnownObjects)
            getKnownList().updateKnownObjects();
    }

    /**
     * @return Returns the showSummonAnimation.
     */
    public boolean isShowSummonAnimation() {
        return _showSummonAnimation;
    }

    /**
     * @param showSummonAnimation The showSummonAnimation to set.
     */
    public void setShowSummonAnimation(boolean showSummonAnimation) {
        _showSummonAnimation = showSummonAnimation;
    }

    /**
     * Target a L2Object (add the target to the L2Creature _target, _knownObject and L2Creature to _KnownObject of the L2Object).<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * The L2Object (including L2Creature) targeted is identified in <B>_target</B> of the L2Creature<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Set the _target of L2Creature to L2Object </li>
     * <li>If necessary, add L2Object to _knownObject of the L2Creature </li>
     * <li>If necessary, add L2Creature to _KnownObject of the L2Object </li>
     * <li>If object==null, cancel Attak or Cast </li>
     * <BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player : Remove the L2Player from the old target _statusListener and add it to the new target if it was a L2Creature</li>
     * <BR>
     * <BR>
     *
     * @param object
     *            L2object to target
     */
    public void setTarget(L2Object newTarget) {
        if (newTarget == getTarget())
            return;

        if (newTarget != null && !(this instanceof L2Player)) {
            if (!newTarget.isVisible())
                return;

            if (Math.abs(newTarget.getZ() - getZ()) > 500)
                return;
        }

        refreshTarget(newTarget);
    }

    protected void refreshTarget(L2Object newTarget) {
        if (newTarget != null) {
            getKnownList().addKnownObject(newTarget);
            newTarget.getKnownList().addKnownObject(this);
        }

        _target = newTarget;
    }

    /**
     * Return the identifier of the L2Object targeted or -1.<BR>
     * <BR>
     */
    public final int getTargetId() {
        if (_target != null) {
            return _target.getObjectId();
        }

        return -1;
    }

    /**
     * Return the L2Object targeted or null.<BR>
     * <BR>
     */
    public final L2Object getTarget() {
        return _target;
    }

    public final <T> T getTarget(Class<T> clazz) {
        L2Object target = getTarget();

        if (clazz.isInstance(target))
            return clazz.cast(target);
        else
            return null;
    }

    public final <T> T getTarget(Class<T> clazz, int objectId) {
        L2Object target = getTarget();

        if (target == null)
            target = L2World.getInstance().findObject(objectId);

        if (clazz.isInstance(target) && target.getObjectId() == objectId)
            return clazz.cast(target);
        else
            return null;
    }

    // called from AIAccessor only
    /**
     * Calculate movement data for a move to location action and add the L2Creature to movingObjects of GameTimeController (only called by AI Accessor).<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * At the beginning of the move action, all properties of the movement are stored in the MoveData object called <B>_move</B> of the L2Creature. The
     * position of the start point and of the destination permit to estimated in function of the movement speed the time to achieve the destination.<BR>
     * <BR>
     * All L2Creature in movement are identified in <B>movingObjects</B> of GameTimeController that will call the updatePosition method of those L2Creature
     * each 0.1s.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Get current position of the L2Creature </li>
     * <li>Calculate distance (dx,dy) between current position and destination including offset </li>
     * <li>Create and Init a MoveData object </li>
     * <li>Set the L2Creature _move object to MoveData object </li>
     * <li>Add the L2Creature to movingObjects of the GameTimeController </li>
     * <li>Create a task to notify the AI that L2Creature arrives at a check point of the movement </li>
     * <BR>
     * <BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T send Server->Client packet MoveToPawn/MoveToLocation </B></FONT><BR>
     * <BR>
     * <B><U> Example of use </U> :</B><BR>
     * <BR>
     * <li> AI : onIntentionMoveTo(L2CharPosition), onIntentionPickUp(L2Object), onIntentionInteract(L2Object) </li>
     * <li> FollowTask </li>
     * <BR>
     * <BR>
     *
     * @param x
     *            The X position of the destination
     * @param y
     *            The Y position of the destination
     * @param z
     *            The Y position of the destination
     * @param offset
     *            The size of the interaction area of the L2Creature targeted
     */
    protected void moveToLocation(int x, int y, int z, int offset) {
        // Get the Move Speed of the L2Charcater
        float speed = getStat().getMoveSpeed();
        if (speed <= 0 || isMovementDisabled())
            return;

        // Get current position of the L2Creature
        final int curX = super.getX();
        final int curY = super.getY();
        final int curZ = super.getZ();

        // Calculate distance (dx,dy) between current position and destination
        // TODO: improve Z axis move/follow support when dx,dy are small compared to dz
        double dx = (x - curX);
        double dy = (y - curY);
        double dz = (z - curZ);
        double distance = Math.sqrt(dx * dx + dy * dy);

        // make water move short and use no geodata checks for swimming chars
        // distance in a click can easily be over 3000
        if (Config.GEODATA > 0 && isInsideZone(L2Zone.FLAG_WATER) && distance > 700) {
            double divider = 700 / distance;
            x = curX + (int) (divider * dx);
            y = curY + (int) (divider * dy);
            z = curZ + (int) (divider * dz);
            dx = (x - curX);
            dy = (y - curY);
            dz = (z - curZ);
            distance = Math.sqrt(dx * dx + dy * dy);
        }

        if (_log.isDebugEnabled())
            _log.info("distance to target:" + distance);

        // Define movement angles needed
        // ^
        // |     X (x,y)
        // |   /
        // |  /distance
        // | /
        // |/ angle
        // X ---------->
        // (curx,cury)

        double cos;
        double sin;

        // Check if a movement offset is defined or no distance to go through
        if (offset != 0 || distance < 1) {
            // approximation for moving closer when z coordinates are different
            // TODO: handle Z axis movement better
            offset -= Math.abs(dz);
            //if (offset < 5) offset = 5;

            // If no distance to go through, the movement is canceled
            if (distance < 1 || distance - offset <= 0) {
                if (_log.isDebugEnabled())
                    _log.info("already in range, no movement needed.");

                // Notify the AI that the L2Creature is arrived at destination
                getAI().notifyEvent(CtrlEvent.EVT_ARRIVED);

                return;
            }
            // Calculate movement angles needed
            sin = dy / distance;
            cos = dx / distance;

            distance -= (offset - 5); // due to rounding error, we have to move a bit closer to be in range

            // Calculate the new destination with offset included
            x = curX + (int) (distance * cos);
            y = curY + (int) (distance * sin);

        } else {
            // Calculate movement angles needed
            sin = dy / distance;
            cos = dx / distance;
        }

        // Create and Init a MoveData object
        MoveData m = new MoveData();

        // GEODATA MOVEMENT CHECKS AND PATHFINDING
        m.onGeodataPathIndex = -1; // Initialize not on geodata path
        m.disregardingGeodata = false;

        if (Config.GEODATA > 0 && !isFlying() // flying chars not checked - even canSeeTarget doesn't work yet
                && (!isInsideZone(L2Zone.FLAG_WATER) || isInsideZone(L2Zone.FLAG_SIEGE)) // swimming also not checked unless in siege zone - but distance is limited
                && !(this instanceof L2NpcWalkerInstance)) // npc walkers not checked
        {
            double originalDistance = distance;
            int originalX = x;
            int originalY = y;
            int originalZ = z;
            int gtx = (originalX - L2World.MAP_MIN_X) >> 4;
            int gty = (originalY - L2World.MAP_MIN_Y) >> 4;

            // Movement checks:
            // when geodata == 2, for all characters except mobs returning home (could be changed later to teleport if pathfinding fails)
            // when geodata == 1, for L2Playable and l2riftinstance only
            if ((Config.GEODATA == 2
                    && !(this instanceof L2Attackable && ((L2Attackable) this).isReturningToSpawnPoint()))
                    || this instanceof L2Player
                    || (this instanceof L2Summon && !(getAI().getIntention() == AI_INTENTION_FOLLOW)) // assuming intention_follow only when following owner
                    || isAfraid() || this instanceof L2RiftInvaderInstance) {
                if (isOnGeodataPath()) {
                    try {
                        if (gtx == _move.geoPathGtx && gty == _move.geoPathGty)
                            return;
                        else
                            _move.onGeodataPathIndex = -1; // Set not on geodata path
                    } catch (NullPointerException e) {
                        // nothing
                    }
                }

                if (curX < L2World.MAP_MIN_X || curX > L2World.MAP_MAX_X || curY < L2World.MAP_MIN_Y
                        || curY > L2World.MAP_MAX_Y) {
                    // Temporary fix for character outside world region errors
                    _log.warn("Character " + getName() + " outside world area, in coordinates x:" + curX + " y:"
                            + curY);
                    getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                    if (this instanceof L2Player)
                        new Disconnection((L2Player) this).defaultSequence(true);
                    else if (!(this instanceof L2Summon))
                        onDecay(); // preventation when summon get out of world coords, player will not loose it, unsummon handled from pcinstance
                    return;
                }
                Location destiny = GeoData.getInstance().moveCheck(curX, curY, curZ, x, y, z, getInstanceId());
                // location different if destination wasn't reached (or just z coord is different)
                x = destiny.getX();
                y = destiny.getY();
                z = destiny.getZ();
                distance = Math.sqrt((x - curX) * (x - curX) + (y - curY) * (y - curY));

            }
            // Pathfinding checks. Only when geodata setting is 2, the LoS check gives shorter result
            // than the original movement was and the LoS gives a shorter distance than 2000
            // This way of detecting need for pathfinding could be changed.
            if (Config.GEODATA == 2 && originalDistance - distance > 100 && distance < 2000 && !isAfraid()) {
                // Path calculation
                // Overrides previous movement check
                if (this instanceof L2Playable || isInCombat() || this instanceof L2MinionInstance) {

                    m.geoPath = PathFinding.getInstance().findPath(curX, curY, curZ, originalX, originalY,
                            originalZ, getInstanceId());
                    if (m.geoPath == null || m.geoPath.length < 2) // No path found
                    {
                        // * Even though there's no path found (remember geonodes aren't perfect),
                        // the mob is attacking and right now we set it so that the mob will go
                        // after target anyway, is dz is small enough.
                        // * With cellpathfinding this approach could be changed but would require taking
                        // off the geonodes and some more checks.
                        // * Summons will follow their masters no matter what.
                        // * Currently minions also must move freely since L2AttackableAI commands
                        // them to move along with their leader
                        if (this instanceof L2Player
                                || (!(this instanceof L2Playable) && !(this instanceof L2MinionInstance)
                                        && Math.abs(z - curZ) > 140)
                                || (this instanceof L2Summon && !((L2Summon) this).getFollowStatus())) {
                            getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                            return;
                        }

                        m.disregardingGeodata = true;
                        x = originalX;
                        y = originalY;
                        z = originalZ;
                        distance = originalDistance;
                    } else {
                        m.onGeodataPathIndex = 0; // on first segment
                        m.geoPathGtx = gtx;
                        m.geoPathGty = gty;
                        m.geoPathAccurateTx = originalX;
                        m.geoPathAccurateTy = originalY;

                        x = m.geoPath[m.onGeodataPathIndex].getX();
                        y = m.geoPath[m.onGeodataPathIndex].getY();
                        z = m.geoPath[m.onGeodataPathIndex].getZ();

                        dx = (x - curX);
                        dy = (y - curY);
                        distance = Math.sqrt(dx * dx + dy * dy);
                        sin = dy / distance;
                        cos = dx / distance;
                    }
                }
            }
            // If no distance to go through, the movement is canceled
            if (distance < 1 && (Config.GEODATA == 2 || this instanceof L2Playable || isAfraid()
                    || this instanceof L2RiftInvaderInstance)) {
                if (this instanceof L2Summon)
                    ((L2Summon) this).setFollowStatus(false);
                getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
                return;
            }
        }

        // Caclulate the Nb of ticks between the current position and the destination
        // One tick added for rounding reasons
        int ticksToMove = 1 + (int) (GameTimeManager.TICKS_PER_SECOND * distance / speed);
        m._xDestination = x;
        m._yDestination = y;
        m._zDestination = z; // this is what was requested from client

        // Calculate and set the heading of the L2Creature
        m._heading = 0; // initial value for coordinate sync
        setHeading(Util.calculateHeadingFrom(cos, sin));

        if (_log.isDebugEnabled())
            _log.info("dist:" + distance + "speed:" + speed + " ttt:" + ticksToMove + " heading:" + getHeading());

        m._moveStartTime = GameTimeManager.getGameTicks();

        // Set the L2Creature _move object to MoveData object
        _move = m;

        MovementController.getInstance().add(this, ticksToMove);
    }

    public boolean moveToNextRoutePoint() {
        if (!isOnGeodataPath()) {
            // Cancel the move action
            _move = null;
            return false;
        }

        // Get the Move Speed of the L2Charcater
        float speed = getStat().getMoveSpeed();
        if (speed <= 0 || isMovementDisabled()) {
            // Cancel the move action
            _move = null;
            return false;
        }

        // Create and Init a MoveData object
        MoveData m = new MoveData();
        MoveData md = _move;
        if (md == null)
            return false;

        // Update MoveData object
        m.onGeodataPathIndex = md.onGeodataPathIndex + 1; // next segment
        m.geoPath = md.geoPath;
        m.geoPathGtx = md.geoPathGtx;
        m.geoPathGty = md.geoPathGty;
        m.geoPathAccurateTx = md.geoPathAccurateTx;
        m.geoPathAccurateTy = md.geoPathAccurateTy;

        if (md.onGeodataPathIndex == md.geoPath.length - 2) {
            m._xDestination = md.geoPathAccurateTx;
            m._yDestination = md.geoPathAccurateTy;
            m._zDestination = md.geoPath[m.onGeodataPathIndex].getZ();
        } else {
            m._xDestination = md.geoPath[m.onGeodataPathIndex].getX();
            m._yDestination = md.geoPath[m.onGeodataPathIndex].getY();
            m._zDestination = md.geoPath[m.onGeodataPathIndex].getZ();
        }
        double dx = (m._xDestination - super.getX());
        double dy = (m._yDestination - super.getY());

        double distance = Math.sqrt(dx * dx + dy * dy);
        double sin = dy / distance;
        double cos = dx / distance;

        // Caclulate the Nb of ticks between the current position and the destination
        // One tick added for rounding reasons
        int ticksToMove = 1 + (int) (GameTimeManager.TICKS_PER_SECOND * distance / speed);

        // Calculate and set the heading of the L2Creature
        int heading = (int) (Math.atan2(-sin, -cos) * 10430.378);
        heading += 32768;
        setHeading(heading);
        m._heading = 0; // initial value for coordinate sync

        m._moveStartTime = GameTimeManager.getGameTicks();

        if (_log.isDebugEnabled())
            _log.info("time to target:" + ticksToMove);

        // Set the L2Creature _move object to MoveData object
        _move = m;

        MovementController.getInstance().add(this, ticksToMove);

        // Send a Server->Client packet MoveToLocation to the actor and all L2Player in its _knownPlayers
        MoveToLocation msg = new MoveToLocation(this);
        broadcastPacket(msg);

        return true;
    }

    public boolean validateMovementHeading(int heading) {
        MoveData md = _move;
        if (md == null)
            return true;

        boolean result = true;
        // if (_move._heading < heading - 5 || _move._heading > heading 5)
        if (md._heading != heading) {
            result = (md._heading == 0);
            md._heading = heading;
        }

        return result;
    }

    /**
     * Return the distance between the current position of the L2Creature and the target (x,y).<BR>
     * <BR>
     *
     * @param x
     *            X position of the target
     * @param y
     *            Y position of the target
     * @return the plan distance
     * @deprecated use getPlanDistanceSq(int x, int y, int z)
     */
    @Deprecated
    public final double getDistance(int x, int y) {
        double dx = x - getX();
        double dy = y - getY();

        return Math.sqrt(dx * dx + dy * dy);
    }

    /**
     * Return the distance between the current position of the L2Creature and the target (x,y).<BR>
     * <BR>
     *
     * @param x
     *            X position of the target
     * @param y
     *            Y position of the target
     * @return the plan distance
     * @deprecated use getPlanDistanceSq(int x, int y, int z)
     */
    @Deprecated
    public final double getDistance(int x, int y, int z) {
        double dx = x - getX();
        double dy = y - getY();
        double dz = z - getZ();

        return Math.sqrt(dx * dx + dy * dy + dz * dz);
    }

    /**
     * Return the squared distance between the current position of the L2Creature and the given object.<BR>
     * <BR>
     *
     * @param object
     *            L2Object
     * @return the squared distance
     */
    public final double getDistanceSq(L2Object object) {
        return getDistanceSq(object.getX(), object.getY(), object.getZ());
    }

    /**
     * Return the squared distance between the current position of the L2Creature and the given x, y, z.<BR>
     * <BR>
     *
     * @param x
     *            X position of the target
     * @param y
     *            Y position of the target
     * @param z
     *            Z position of the target
     * @return the squared distance
     */
    public final double getDistanceSq(int x, int y, int z) {
        double dx = x - getX();
        double dy = y - getY();
        double dz = z - getZ();

        return (dx * dx + dy * dy + dz * dz);
    }

    /**
     * Return the squared plan distance between the current position of the L2Creature and the given object.<BR>
     * (check only x and y, not z)<BR>
     * <BR>
     *
     * @param object
     *            L2Object
     * @return the squared plan distance
     */
    public final double getPlanDistanceSq(L2Object object) {
        return getPlanDistanceSq(object.getX(), object.getY());
    }

    /**
     * Return the squared plan distance between the current position of the L2Creature and the given x, y, z.<BR>
     * (check only x and y, not z)<BR>
     * <BR>
     *
     * @param x
     *            X position of the target
     * @param y
     *            Y position of the target
     * @return the squared plan distance
     */
    public final double getPlanDistanceSq(int x, int y) {
        double dx = x - getX();
        double dy = y - getY();

        return (dx * dx + dy * dy);
    }

    /**
     * Check if this object is inside the given radius around the given object. Warning: doesn't cover collision radius!<BR>
     * <BR>
     * If the target is null, we consider that this object is not inside radius
     *
     * @param object
     *            the target
     * @param radius
     *            the radius around the target
     * @param checkZ
     *            should we check Z axis also
     * @param strictCheck
     *            true if (distance < radius), false if (distance <= radius)
     * @return true is the L2Creature is inside the radius.
     */
    public final boolean isInsideRadius(L2Object object, int radius, boolean checkZ, boolean strictCheck) {
        if (object == null)
            return false;

        return isInsideRadius(object.getX(), object.getY(), object.getZ(), radius, checkZ, strictCheck);
    }

    /**
     * Check if this object is inside the given plan radius around the given point. Warning: doesn't cover collision radius!<BR>
     * <BR>
     *
     * @param x
     *            X position of the target
     * @param y
     *            Y position of the target
     * @param radius
     *            the radius around the target
     * @param strictCheck
     *            true if (distance < radius), false if (distance <= radius)
     * @return true is the L2Creature is inside the radius.
     */
    public final boolean isInsideRadius(int x, int y, int radius, boolean strictCheck) {
        return isInsideRadius(x, y, 0, radius, false, strictCheck);
    }

    /**
     * Check if this object is inside the given radius around the given point.<BR>
     * <BR>
     *
     * @param x
     *            X position of the target
     * @param y
     *            Y position of the target
     * @param z
     *            Z position of the target
     * @param radius
     *            the radius around the target
     * @param checkZ
     *            should we check Z axis also
     * @param strictCheck
     *            true if (distance < radius), false if (distance <= radius)
     * @return true is the L2Creature is inside the radius.
     */
    public final boolean isInsideRadius(int x, int y, int z, int radius, boolean checkZ, boolean strictCheck) {
        double dx = x - getX();
        double dy = y - getY();
        double dz = z - getZ();

        if (strictCheck) {
            if (checkZ)
                return (dx * dx + dy * dy + dz * dz) < radius * radius;

            return (dx * dx + dy * dy) < radius * radius;
        }

        if (checkZ)
            return (dx * dx + dy * dy + dz * dz) <= radius * radius;

        return (dx * dx + dy * dy) <= radius * radius;
    }

    public final boolean isOutsideRadius(int x, int y, int radius) {
        double dx = x - getX();
        double dy = y - getY();

        return (dx * dx + dy * dy) >= radius * radius;
    }

    /**
     * Return the Weapon Expertise Penalty of the L2Creature.<BR>
     * <BR>
     */
    @Deprecated
    public final float getWeaponExpertisePenalty() {
        return 1.f;
    }

    /**
     * Return the Armour Expertise Penalty of the L2Creature.<BR>
     * <BR>
     */
    @Deprecated
    public final float getArmourExpertisePenalty() {
        return 1.f;
    }

    /**
     * Set _attacking corresponding to Attacking Body part to CHEST.<BR>
     * <BR>
     */
    public void setAttackingBodypart() {
        _attacking = Inventory.PAPERDOLL_CHEST;
    }

    /**
     * Retun True if arrows are available.<BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player</li>
     * <BR>
     * <BR>
     */
    protected boolean checkAndEquipArrows() {
        return true;
    }

    /**
    * Retun True if bolts are available.<BR><BR>
    *
    * <B><U> Overridden in </U> :</B><BR><BR>
    * <li> L2Player</li><BR><BR>
    *
    */
    protected boolean checkAndEquipBolts() {
        return true;
    }

    /**
     * Add Exp and Sp to the L2Creature.<BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player</li>
     * <li> L2PetInstance</li>
     * <BR>
     * <BR>
     */
    public void addExpAndSp(long addToExp, int addToSp) {
        // Dummy method (overridden by players and pets)
    }

    /**
     * Return the active weapon instance (always equipped in the right hand).<BR>
     */
    public L2ItemInstance getActiveWeaponInstance() {
        return null;
    }

    /**
     * Return the active weapon item (always equipped in the right hand).<BR>
     */
    public L2Weapon getActiveWeaponItem() {
        return null;
    }

    /**
     * Return the secondary weapon instance (always equipped in the left hand).<BR>
     */
    public L2ItemInstance getSecondaryWeaponInstance() {
        return null;
    }

    /**
     * Return the secondary weapon item (always equipped in the left hand).<BR>
     */
    public L2Weapon getSecondaryWeaponItem() {
        return null;
    }

    /**
     * Manage hit process (called by Hit Task).<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>If the attacker/target is dead or use fake death, notify the AI with EVT_CANCEL and send a Server->Client packet ActionFailed (if attacker is a
     * L2Player)</li>
     * <li>If attack isn't aborted, send a message system (critical hit, missed...) to attacker/target if they are L2Player </li>
     * <li>If attack isn't aborted and hit isn't missed, reduce HP of the target and calculate reflection damage to reduce HP of attacker if necessary </li>
     * <li>if attack isn't aborted and hit isn't missed, manage attack or cast break of the target (calculating rate, sending message...) </li>
     * <BR>
     * <BR>
     *
     * @param target
     *            The L2Creature targeted
     * @param damage
     *            Nb of HP to reduce
     * @param crit
     *            True if hit is critical
     * @param miss
     *            True if hit is missed
     * @param soulshot
     *            True if SoulShot are charged
     * @param shld
     *            True if shield is efficient
     */
    protected void onHitTimer(L2Creature target, int damage, boolean crit, boolean miss, double vampiricMulti) {
        // If the attacker/target is dead or use fake death, notify the AI with EVT_CANCEL
        // and send a Server->Client packet ActionFailed (if attacker is a L2Player)
        if (target == null || isAlikeDead()) {
            getAI().notifyEvent(CtrlEvent.EVT_CANCEL);
            return;
        }

        if ((this instanceof L2Npc && target.isAlikeDead()) || target.isDead()
                || (!getKnownList().knowsObject(target) && !(this instanceof L2DoorInstance))) {
            // getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);
            getAI().notifyEvent(CtrlEvent.EVT_CANCEL);

            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        if (miss) {
            // ON_EVADED_HIT
            if (target.getChanceSkills() != null)
                target.getChanceSkills().onEvadedHit(this);
        }

        sendDamageMessage(target, damage, false, crit, miss);

        // If attack isn't aborted, send a message system (critical hit, missed...) to attacker/target if they are L2Player
        if (!isAttackAborted()) {
            // Check Raidboss attack
            // Character will be petrified if attacking a raid that's more
            // than 8 levels lower
            if (target.isRaid() && !Config.ALT_DISABLE_RAIDBOSS_PETRIFICATION
                    && getSkillLevel(L2Boss.BOSS_PENALTY_RESISTANCE) == -1) {
                int level = 0;
                if (this instanceof L2Player)
                    level = getLevel();
                else if (this instanceof L2Summon)
                    level = ((L2Summon) this).getOwner().getLevel();

                if (level > target.getLevel() + 8) {
                    L2Skill skill = SkillTable.getInstance().getInfo(L2Boss.BOSS_PENALTY_PETRIFICATION, 1);

                    if (skill != null)
                        skill.getEffects(target, this);
                    else
                        _log.warn("Skill " + L2Boss.BOSS_PENALTY_PETRIFICATION + " at level 1 is missing in DP.");

                    damage = 0; // prevents messing up drop calculation
                }
            }

            // If L2Creature target is a L2Player, send a system message
            if (target instanceof L2Player) {
                L2Player enemy = (L2Player) target;
                enemy.getAI().clientStartAutoAttack();

                // Check if shield is efficient
                /*if (shld && 100 - Config.ALT_PERFECT_SHLD_BLOCK < Rnd.get(100))
                   enemy.sendPacket(SystemMessageId.SHIELD_DEFENCE_SUCCESSFULL);
                // else if (!miss && damage < 1)
                // enemy.sendMessage("You hit the target's armor.");*/
            } else if (target instanceof L2Summon) {
                ((L2Summon) target).getOwner().getAI().clientStartAutoAttack();
            }

            if (!miss && damage > 0) {
                L2Weapon weapon = getActiveWeaponItem();
                boolean isRangeWeapon = (weapon != null && (weapon.getItemType() == L2WeaponType.BOW
                        || weapon.getItemType() == L2WeaponType.CROSSBOW));

                int reflectedDamage = 0;
                if (!isRangeWeapon && !target.isInvul()) // Do not reflect if weapon is of type bow/crossbow or target is invulnerable
                {
                    // quick fix for no drop from raid if boss attack high-level char with damage reflection
                    if (!isRaid() || target.getActingPlayer() == null
                            || target.getActingPlayer().getLevel() <= getLevel() + 8) {
                        // Reduce HP of the target and calculate reflection damage to reduce HP of attacker if necessary
                        double reflectPercent = target.getStat().calcStat(Stats.REFLECT_DAMAGE_PERCENT, 0, null,
                                null);

                        if (reflectPercent > 0) {
                            reflectedDamage = (int) (reflectPercent / 100. * damage);
                            damage -= reflectedDamage;

                            if (reflectedDamage > target.getMaxHp()) // to prevent extreme damage when hitting a low lvl char...
                                reflectedDamage = target.getMaxHp();
                        }
                    }
                }

                // Reduce targets HP
                target.reduceCurrentHp(damage, this);

                if (reflectedDamage > 0) {
                    reduceCurrentHp(reflectedDamage, target);

                    // Custom messages - nice but also more network load
                    /*
                     * if (target instanceof L2Player) ((L2Player)target).sendMessage("You reflected " + reflectedDamage + " damage."); else if
                     * (target instanceof L2Summon) ((L2Summon)target).getOwner().sendMessage("Summon reflected " + reflectedDamage + " damage.");
                     *
                     * if (this instanceof L2Player) ((L2Player)this).sendMessage("Target reflected to you " + reflectedDamage + " damage."); else
                     * if (this instanceof L2Summon) ((L2Summon)this).getOwner().sendMessage("Target reflected to your summon " + reflectedDamage + "
                     * damage.");
                     */
                }

                if (!isRangeWeapon) // Do not absorb if weapon is of type bow
                {
                    // Absorb HP from the damage inflicted
                    double absorbPercent = getStat().calcStat(Stats.ABSORB_DAMAGE_PERCENT, 0, null, null)
                            * vampiricMulti;

                    if (absorbPercent > 0) {
                        int maxCanAbsorb = (int) (getMaxHp() - getStatus().getCurrentHp());
                        int absorbDamage = (int) (absorbPercent / 100. * damage);

                        if (absorbDamage > maxCanAbsorb)
                            absorbDamage = maxCanAbsorb; // Can't absorb more than max hp

                        if (absorbDamage > 0) {
                            getStatus().increaseHp(absorbDamage);
                        }
                    }

                    // Absorb MP from the damage inflicted
                    absorbPercent = getStat().calcStat(Stats.ABSORB_MANA_DAMAGE_PERCENT, 0, null, null)
                            * vampiricMulti;
                    if (absorbPercent > 0) {
                        int maxCanAbsorb = (int) (getMaxMp() - getCurrentMp());
                        int absorbDamage = (int) (absorbPercent / 100. * damage);

                        if (absorbDamage > maxCanAbsorb)
                            absorbDamage = maxCanAbsorb; // Can't absord more than max hp

                        if (absorbDamage > 0)
                            getStatus().increaseMp(absorbDamage);
                    }

                    /*
                    // Absorb CP from the damage inflicted
                    double absorbCPPercent = getStat().calcStat(Stats.ABSORB_CP_PERCENT, 0, null, null) * vampiricMulti;
                        
                    if (absorbCPPercent > 0)
                    {
                       int maxCanAbsorb = (int) (getMaxCp() - getStatus().getCurrentCp());
                       int absorbDamage = (int) (absorbCPPercent / 100. * damage);
                        
                       if (absorbDamage > maxCanAbsorb)
                          absorbDamage = maxCanAbsorb; // Can't absorb more than max cp
                        
                       getStatus().setCurrentCp(getStatus().getCurrentCp() + absorbDamage);
                    }
                    */
                }

                // Notify AI with EVT_ATTACKED
                if (target.hasAI())
                    target.getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, this);
                getAI().clientStartAutoAttack();
                if (this instanceof L2Summon) {
                    L2Player owner = ((L2Summon) this).getOwner();
                    if (owner != null) {
                        owner.getAI().clientStartAutoAttack();
                    }
                }

                // Manage attack or cast break of the target (calculating rate, sending message...)
                if (Formulas.calcAtkBreak(target, damage)) {
                    target.breakAttack();
                    target.breakCast();
                }

                // Maybe launch chance skills on us
                if (_chanceSkills != null)
                    _chanceSkills.onHit(target, false, crit, isRangeWeapon);

                // Maybe launch chance skills on target
                if (target.getChanceSkills() != null)
                    target.getChanceSkills().onHit(this, true, crit, isRangeWeapon);

                // Launch weapon Special ability effect if available
                L2Weapon activeWeapon = getActiveWeaponItem();

                if (activeWeapon != null && crit)
                    activeWeapon.getSkillEffectsByCrit(this, target);
            }
            return;
        }

        if (!isCastingNow() && !isCastingSimultaneouslyNow())
            getAI().notifyEvent(CtrlEvent.EVT_CANCEL);
    }

    /**
     * Break an attack and send Server->Client ActionFailed packet and a System Message to the L2Creature.<BR>
     * <BR>
     */
    public void breakAttack() {
        if (isAttackingNow()) {
            // Abort the attack of the L2Creature and send Server->Client ActionFailed packet
            abortAttack();

            if (this instanceof L2Player) {
                // not retail, so no system message.
                sendPacket(ActionFailed.STATIC_PACKET);
            }
        }
    }

    /**
     * Break a cast and send Server->Client ActionFailed packet and a System Message to the L2Creature.<BR>
     * <BR>
     */
    public void breakCast() {
        // damage can only cancel magical skills
        if (isCastingNow() && canAbortCast()) {
            // Abort the cast of the L2Creature and send Server->Client MagicSkillCanceld/ActionFailed packet.
            abortCast();

            if (this instanceof L2Player) {
                // Send a system message
                SystemMessage sm = new SystemMessage(SystemMessageId.C1_CASTING_INTERRUPTED);
                sm.addCharName(this);
                getActingPlayer().sendPacket(sm);
            }
        }
    }

    /**
     * Reduce the arrow number of the L2Creature.<BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player</li>
     * <BR>
     * <BR>
     * @param bolts
     */
    protected void reduceArrowCount(boolean bolts) {
        // default is to do nothin
    }

    /**
     * Manage Forced attack (shift + select target).<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>If L2Creature or target is in a town area, send a system message TARGET_IN_PEACEZONE a Server->Client packet ActionFailed </li>
     * <li>If target is confused, send a Server->Client packet ActionFailed </li>
     * <li>If L2Creature is a L2ArtefactInstance, send a Server->Client packet ActionFailed </li>
     * <li>Send a Server->Client packet MyTargetSelected to start attack and Notify AI with AI_INTENTION_ATTACK </li>
     * <BR>
     * <BR>
     *
     * @param player
     *            The L2Player to attack
     */
    @Override
    public void onForcedAttack(L2Player player) {
        if (GlobalRestrictions.isProtected(player, this, null, true)) {
            player.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        if (!isAttackable() && player.getAccessLevel() < Config.GM_PEACEATTACK) {
            // If target is not attackable, send a Server->Client packet ActionFailed
            player.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        if (player.isConfused()) {
            // If target is confused, send a Server->Client packet ActionFailed
            player.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        // GeoData Los Check or dz > 1000
        if (!GeoData.getInstance().canSeeTarget(player, this)) {
            player.sendPacket(SystemMessageId.CANT_SEE_TARGET);
            player.sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        // Notify AI with AI_INTENTION_ATTACK
        player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
    }

    public static final boolean isInsidePeaceZone(L2Creature attacker, L2Object objTarget) {
        final L2Creature target = L2Object.getActingCharacter(objTarget);
        final L2Player attackerPlayer = L2Object.getActingPlayer(attacker);
        final L2Player targetPlayer = L2Object.getActingPlayer(target);

        if (attackerPlayer == null || targetPlayer == null)
            return false;

        if (attackerPlayer.getAccessLevel() >= Config.GM_PEACEATTACK)
            return false;

        if (InstanceManager.getInstance().getInstance(attacker.getInstanceId()).isPvPInstance())
            return false;

        if (Config.ALT_GAME_KARMA_PLAYER_CAN_BE_KILLED_IN_PEACEZONE) {
            // allows red to be attacked and red to attack flagged players
            if (targetPlayer.getKarma() > 0)
                return false;

            if (attackerPlayer.getKarma() > 0 && targetPlayer.getPvpFlag() > 0)
                return false;
        }

        return attacker.isInsideZone(L2Zone.FLAG_PEACE) || target.isInsideZone(L2Zone.FLAG_PEACE);
    }

    /**
     * return true if this character is inside an active grid.
     */
    public boolean isInActiveRegion() {
        L2WorldRegion region = getWorldRegion();
        return ((region != null) && (region.isActive()));
    }

    /**
     * Return True if the L2Creature has a Party in progress.<BR>
     * <BR>
     */
    public boolean isInParty() {
        return false;
    }

    /**
     * Return the L2Party object of the L2Creature.<BR>
     * <BR>
     */
    public L2Party getParty() {
        return null;
    }

    /**
     * Return the Attack Speed of the L2Creature (delay (in milliseconds) before next attack).<BR>
     * <BR>
     * @param target
     * @param weapon
     */
    // FIXME
    public int calculateTimeBetweenAttacks(L2Creature target, L2Weapon weapon) {
        double base = 500000/*468000*/; //1500 * 333.3/*1500 * 312*/

        //if (weapon != null && !(this instanceof L2Player && ((L2Player)this).isTransformed()))
        //{
        //   switch (weapon.getItemType())
        //   {
        //      case BOW:
        //         base = 517500; //1500 * 345
        //      case CROSSBOW:
        //         base = 414000; //1200 * 345
        //   }
        //}

        return Formulas.calcPAtkSpd(this, target, getPAtkSpd(), base);
    }

    public int calculateReuseTime(L2Creature target, L2Weapon weapon) {
        // Source L2P
        // Standing still and with no SA, normal bows and yumi bows shoot the exact same number of shots per second.
        // Normal Bows allow faster use of skills and more kiteability due to having a higher Atk. Spd. while Yumi Bows have significant P.Atk.
        // The SA "Quick Recovery" reduces the red bar Weapon Delay on a bow to the following:
        // Reuse goes from 639 EB QR, to 1500 Normal...

        if (weapon == null || (this instanceof L2Player && ((L2Player) this).isTransformed()))
            return 0;

        double reuse = weapon.getAttackReuseDelay();

        // only bows should continue for now
        if (reuse == 0)
            return 0;

        reuse = calcStat(Stats.BOW_REUSE, reuse, target, null);

        reuse *= 333.3/*345/312*/;

        return Formulas.calcPAtkSpd(this, target, getPAtkSpd(), reuse);
    }

    /**
     * Return True if the L2Creature use a dual weapon.<BR><BR>
     */
    public boolean isUsingDualWeapon() {
        return false;
    }

    /**
     * Add a skill to the L2Creature _skills and its Func objects to the calculator set of the L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All skills own by a L2Creature are identified in <B>_skills</B><BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Replace oldSkill by newSkill or Add the newSkill </li>
     * <li>If an old skill has been replaced, remove all its Func objects of L2Creature calculator set</li>
     * <li>Add Func objects of newSkill to the calculator set of the L2Creature </li>
     * <BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player : Save update in the character_skills table of the database</li>
     * <BR>
     * <BR>
     *
     * @param newSkill
     *            The L2Skill to add to the L2Creature
     * @return The L2Skill replaced or null if just added a new L2Skill
     */
    @Deprecated
    public L2Skill addSkill(L2Skill newSkill) {
        if (newSkill == null)
            return null;

        if (!(_skills instanceof FastMap<?, ?>)) // map returned by L2NpcTemplate.getSkills()
        {
            // L2NpcTemplate.getSkillS() is unmodifiable, so the entrySet() of it can't be used
            FastMap<Integer, L2Skill> skills = new FastMap<Integer, L2Skill>(_skills.size()).setShared(true);

            for (Integer key : _skills.keySet())
                skills.put(key, _skills.get(key));

            _skills = skills;
        }

        // Replace oldSkill by newSkill or Add the newSkill
        final L2Skill oldSkill = _skills.put(newSkill.getId(), newSkill);

        // If an old skill has been replaced, remove all its Func objects
        skillChanged(oldSkill, newSkill);

        return oldSkill;
    }

    /**
     * Remove a skill from the L2Creature and its Func objects from calculator set of the L2Creature.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All skills own by a L2Creature are identified in <B>_skills</B><BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Remove the skill from the L2Creature _skills </li>
     * <li>Remove all its Func objects from the L2Creature calculator set</li>
     * <BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player : Save update in the character_skills table of the database</li>
     * <BR>
     * <BR>
     *
     * @param skill
     *            The L2Skill to remove from the L2Creature
     * @return The L2Skill removed
     */
    @Deprecated
    public L2Skill removeSkill(L2Skill skill) {
        if (skill == null || _skills == null)
            return null;

        // Remove the skill from the L2Creature _skills
        final L2Skill oldSkill = _skills.remove(skill.getId());

        // Remove all its Func objects from the L2Creature calculator set
        if (oldSkill != null) {
            skillChanged(oldSkill, null);

            // does not abort casting of the transformation dispell
            if (oldSkill.getSkillType() != L2SkillType.TRANSFORMDISPEL) {
                // Stop casting if this skill is used right now
                if (isCastingNow() && this instanceof L2Player) {
                    SkillUsageRequest currentSkill = ((L2Player) this).getCurrentSkill();
                    if (currentSkill != null && currentSkill.getSkillId() == oldSkill.getId())
                        abortCast();
                }

                if (isCastingSimultaneouslyNow()) {
                    L2Skill lastSimultaneousSkillCast = getLastSimultaneousSkillCast();
                    if (lastSimultaneousSkillCast != null && lastSimultaneousSkillCast.getId() == oldSkill.getId())
                        abortCast();
                }
            }

            // for now, to support transformations, we have to let their
            // effects stay when skill is removed
            for (L2Effect e : getAllEffects()) {
                if (e == null || e.getSkill().getId() != oldSkill.getId())
                    continue; // remove only effects with the same id

                if (e.getEffectType() == L2EffectType.TRANSFORMATION)
                    continue; // remove only non-transformation effects

                if (e.getSkill().isChance())
                    continue; // don't remove triggered effects

                if (Config.ALT_KEEP_ITEM_BUFFS && e.getSkill().isItemSkill() && e.getSkill().isActive())
                    continue; // skip item/augmentation active/self buffs

                if (e.getSkill().getTargetType() == SkillTargetType.TARGET_SELF)
                    e.exit(); // remove self skills only - there is no reason to remove normal buffs
            }

            if (this instanceof L2Player) {
                L2Player player = (L2Player) this;

                if (oldSkill instanceof L2SkillAgathion && player.getAgathionId() > 0) {
                    player.setAgathionId(0);
                    player.broadcastUserInfo();
                }

                if (oldSkill instanceof L2SkillMount && player.isMounted()) {
                    player.dismount();
                }

                if (oldSkill instanceof L2SkillSummon && oldSkill.getId() == 710) {
                    L2Summon summon = player.getPet();

                    if (summon != null && summon.getNpcId() == 14870)
                        summon.unSummon();
                }
            }
        }

        return oldSkill;
    }

    protected void skillChanged(L2Skill removed, L2Skill added) {
        if (removed != null) {
            removeStatsOwner(removed);

            if (removed.isChance())
                removeChanceSkillTrigger(removed);
        }

        if (added != null) {
            if (added.getSkillType() != L2SkillType.NOTDONE)
                addStatFuncs(added.getStatFuncs(this));

            if (added.isChance())
                addChanceSkillTrigger(added);

            if (added.isActive())
                disableSkill(added.getId(), added.getEquipDelay());
        }
    }

    public synchronized void addChanceSkillTrigger(IChanceSkillTrigger trigger) {
        if (_chanceSkills == null)
            _chanceSkills = new ChanceSkillList(this);

        _chanceSkills.add(trigger);
    }

    public synchronized void removeChanceSkillTrigger(IChanceSkillTrigger trigger) {
        if (_chanceSkills == null)
            return;

        _chanceSkills.remove(trigger);
    }

    /**
     * Return all skills own by the L2Creature in a table of L2Skill.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All skills own by a L2Creature are identified in <B>_skills</B> the L2Creature <BR>
     * <BR>
     */
    public final L2Skill[] getAllSkills() {
        if (_skills == null)
            return new L2Skill[0];

        return _skills.values().toArray(new L2Skill[_skills.size()]);
    }

    /**
     * Return the level of a skill owned by the L2Creature.<BR>
     * <BR>
     *
     * @param skillId
     *            The identifier of the L2Skill whose level must be returned
     * @return The level of the L2Skill identified by skillId
     */
    public int getSkillLevel(int skillId) {
        if (_skills == null)
            return -1;

        L2Skill skill = _skills.get(skillId);

        if (skill == null)
            return -1;
        return skill.getLevel();
    }

    /**
     * Return True if the skill is known by the L2Creature.<BR>
     * <BR>
     *
     * @param skillId
     *            The identifier of the L2Skill to check the knowledge
     */
    public final L2Skill getKnownSkill(int skillId) {
        if (_skills == null)
            return null;

        return _skills.get(skillId);
    }

    public final boolean hasSkill(int skillId) {
        return getKnownSkill(skillId) != null;
    }

    /**
     * Return the number of buffs affecting this L2Creature.<BR><BR>
     *
     * @return The number of Buffs affecting this L2Creature
     */
    public final int getBuffCount() {
        return getEffects().getBuffCount();
    }

    public final int getDanceCount(boolean dances, boolean songs) {
        return getEffects().getDanceCount(dances, songs);
    }

    /**
     * Manage the magic skill launching task (MP, HP, Item consummation...) and display the magic skill animation on client.<BR>
     * <BR>
     * <B><U> Actions</U> :</B><BR>
     * <BR>
     * <li>Send a Server->Client packet MagicSkillLaunched (to display magic skill animation) to all L2Player of L2Charcater _knownPlayers</li>
     * <li>Consume MP, HP and Item if necessary</li>
     * <li>Send a Server->Client packet StatusUpdate with MP modification to the L2Player</li>
     * <li>Launch the magic skill in order to calculate its effects</li>
     * <li>If the skill type is PDAM, notify the AI of the target with AI_INTENTION_ATTACK</li>
     * <li>Notify the AI of the L2Creature with EVT_FINISH_CASTING</li>
     * <BR>
     * <BR>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : A magic skill casting MUST BE in progress</B></FONT><BR>
     * <BR>
     *
     * @param skill
     *            The L2Skill to use
     */
    private final void onMagicLaunchedTimer(MagicEnv magicEnv) {
        final L2Skill skill = magicEnv._skill;
        final List<L2Creature> targets = magicEnv._targets;
        final boolean simultaneously = magicEnv._simultaneously;

        //      if (targets.isEmpty())
        //      {
        //         abortCast();
        //         setAttackingChar(null);
        //         return;
        //      }

        // Escaping from under skill's radius and peace zone check. First version, not perfect in AoE skills.
        int escapeRange = 0;
        if (skill.getEffectRange() > 0)
            escapeRange = skill.getEffectRange();
        else if (skill.getCastRange() < 0 && skill.getSkillRadius() > 80)
            escapeRange = skill.getSkillRadius();

        if (escapeRange > 0) {
            for (L2Creature target : targets) {
                if ((!Util.checkIfInRange(escapeRange, this, target, true)
                        || !GeoData.getInstance().canSeeTarget(this, target))) {
                    targets.remove(target);
                    continue;
                }
                if (skill.isOffensive()) {
                    if (L2Creature.isInsidePeaceZone(this, target)) {
                        targets.remove(target);
                        continue;
                    }
                }
            }

            //         if (targets.isEmpty())
            //         {
            //            abortCast();
            //            return;
            //         }
        }

        if (simultaneously && !isCastingSimultaneouslyNow() // Ensure that a cast is in progress
                || !simultaneously && !isCastingNow() // Ensure that a cast is in progress
                || isAlikeDead() && !skill.isPotion()) // Check if player is using fake death, potions still can be used.
        {
            // now cancels both, simultaneous and normal
            //setAttackingChar(null);
            getAI().notifyEvent(CtrlEvent.EVT_CANCEL);
            return;
        }

        // If this packet is sent here, client may choose not to accept it
        //if (!skill.isPotion())
        //   broadcastPacket(new MagicSkillLaunched(this, skill, targets.toArray(new L2Creature[targets.size()])));

        onMagicHitTimer(magicEnv);
    }

    /**
     * Runs in the end of skill casting
     * @param magicEnv
     */
    private final void onMagicHitTimer(MagicEnv magicEnv) {
        final L2Skill skill = magicEnv._skill;
        final List<L2Creature> targets = magicEnv._targets;
        final boolean simultaneously = magicEnv._simultaneously;

        //      if (targets.isEmpty())
        //      {
        //         abortCast();
        //         setAttackingChar(null);
        //         return;
        //      }

        if (getFusionSkill() != null) {
            if (simultaneously) {
                setIsCastingSimultaneouslyNow(false);
            } else {
                setIsCastingNow(false);
            }
            notifyQuestEventSkillFinished(magicEnv);
            getFusionSkill().onCastAbort();
            return;
        }

        L2Effect mog = getFirstEffect(L2EffectType.SIGNET_GROUND);
        if (mog != null) {
            if (simultaneously) {
                setIsCastingSimultaneouslyNow(false);
            } else {
                setIsCastingNow(false);
            }
            mog.exit();
            notifyQuestEventSkillFinished(magicEnv);
            return;
        }

        try {
            for (L2Creature target : targets) {
                if (target instanceof L2Playable) {
                    if (skill.getSkillType() == L2SkillType.BUFF) {
                        SystemMessage smsg = new SystemMessage(SystemMessageId.YOU_FEEL_S1_EFFECT);
                        smsg.addSkillName(skill);
                        target.sendPacket(smsg);
                    }

                    if (this instanceof L2Player && target instanceof L2Summon) {
                        ((L2Summon) target).broadcastFullInfo();
                    }
                }
            }

            // Consume MP of the L2Creature and Send the Server->Client packet StatusUpdate with current HP and MP to all other L2Player to inform
            double mpConsume = getStat().getMpConsume(skill);
            if (mpConsume > 0)
                getStatus().reduceMp(mpConsume);

            // Consume HP if necessary and Send the Server->Client packet StatusUpdate with current HP and MP to all other L2Player to inform
            if (skill.getHpConsume() > 0) {
                double consumeHp;

                consumeHp = calcStat(Stats.HP_CONSUME_RATE, skill.getHpConsume(), null, null);
                if (consumeHp + 1 >= getStatus().getCurrentHp())
                    consumeHp = getStatus().getCurrentHp() - 1.0;

                reduceCurrentHpByConsume(consumeHp);
            }

            // Consume CP if necessary and Send the Server->Client packet StatusUpdate with current CP/HP and MP to all other L2Player to inform
            if (skill.getCpConsume() > 0) {
                double consumeCp;

                consumeCp = skill.getCpConsume();
                if (consumeCp + 1 >= getStatus().getCurrentHp())
                    consumeCp = getStatus().getCurrentHp() - 1.0;

                getStatus().reduceCp((int) consumeCp);
            }

            // Consume Items if necessary and Send the Server->Client packet InventoryUpdate with Item modification to all the L2Creature
            if (skill.getItemConsume() > 0) {
                if (!destroyItemByItemId("Consume", skill.getItemConsumeId(), skill.getItemConsume(), null,
                        false)) {
                    sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
                    abortCast();
                    return;
                }
            }

            if (this instanceof L2Player) {
                L2Player player = (L2Player) this;
                // Reset soul bonus for skills
                player.resetLastSoulConsume();

                // Consume Souls if necessary
                if (skill.getSoulConsumeCount() > 0 || skill.getMaxSoulConsumeCount() > 0) {
                    player.decreaseSouls(skill);
                }

                // Consume Charges if necessary ... L2SkillChargeDmg does the consume by itself.
                if (skill.getNeededCharges() > 0 && skill.getSkillType() != L2SkillType.CHARGEDAM) {
                    player.decreaseCharges(skill.getNeededCharges());
                }
            }

            // Launch the magic skill in order to calculate its effects
            callSkill(skill, targets.toArray(new L2Creature[targets.size()]));
        } catch (Exception e) {
            _log.error(e.getMessage(), e);
        }

        final int coolTime = magicEnv._coolTime;

        if (coolTime < 10)
            onMagicFinalizer(magicEnv);
        else {
            if (simultaneously)
                _skillCast2 = ThreadPoolManager.getInstance().scheduleEffect(new MagicFinalizer(magicEnv),
                        coolTime);
            else
                _skillCast = ThreadPoolManager.getInstance().scheduleEffect(new MagicFinalizer(magicEnv), coolTime);
        }
    }

    /**
     * Runs after skill hitTime+coolTime
     * @param magicEnv
     */
    private final void onMagicFinalizer(MagicEnv magicEnv) {
        final L2Skill skill = magicEnv._skill;
        final boolean simultaneously = magicEnv._simultaneously;

        if (simultaneously) {
            setIsCastingSimultaneouslyNow(false);
            return;
        } else {
            setIsCastingNow(false);
        }

        // if the skill has changed the character's state to something other than STATE_CASTING
        // then just leave it that way, otherwise switch back to STATE_IDLE.
        // if(isCastingNow())
        // getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);

        switch (skill.getSkillType()) {
        case PDAM:
        case BLOW:
        case CHARGEDAM:
        case SPOIL:
        case STUN: {
            final L2Creature originalTarget = magicEnv._originalTarget;
            final L2Creature originalSkillTarget = magicEnv._originalSkillTarget;
            final L2Object currentTarget = L2Object.getActingCharacter(getTarget());

            L2Object newTarget = null;

            if (originalSkillTarget != null && originalSkillTarget != this && originalSkillTarget == currentTarget)
                newTarget = originalSkillTarget;
            else if (originalTarget != null && originalTarget != this && originalTarget == currentTarget)
                newTarget = originalTarget;

            if (// As far as I remember, you can move away after launching a skill without hitting
            getAI().getIntention() != AI_INTENTION_MOVE_TO && (getAI().getNextCtrlIntention() == null
                    // I see no problems with this...
                    || getAI().getNextCtrlIntention() == AI_INTENTION_IDLE)
            // And you will not auto-attack a non-flagged player after launching a skill
                    && newTarget != null && newTarget.isAutoAttackable(this)) {
                double distance = Util.calculateDistance(this, newTarget, false);

                // if the skill is melee, or almost in the range of a normal attack
                if (getMagicalAttackRange(skill) < 200 || getPhysicalAttackRange() + 200 > distance)
                    getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, newTarget);
            }
        }
        }

        switch (skill.getSkillType()) {
        case UNLOCK:
        case DELUXE_KEY_UNLOCK:
        case MAKE_KILLABLE:
        case MAKE_QUEST_DROPABLE:
            break;
        default: {
            if (skill.isOffensive())
                getAI().clientStartAutoAttack();
        }
        }

        // Notify the AI of the L2Creature with EVT_FINISH_CASTING
        getAI().notifyEvent(CtrlEvent.EVT_FINISH_CASTING);

        notifyQuestEventSkillFinished(magicEnv);
    }

    // Quest event ON_SPELL_FNISHED
    private void notifyQuestEventSkillFinished(MagicEnv magicEnv) {
        if (this instanceof L2Npc) {
            for (L2Creature target : magicEnv._targets) {
                try {
                    final Quest[] quests = ((L2NpcTemplate) getTemplate())
                            .getEventQuests(QuestEventType.ON_SPELL_FINISHED);

                    if (quests != null) {
                        final L2Player player = target.getActingPlayer();

                        for (Quest quest : quests)
                            quest.notifySpellFinished(((L2Npc) this), player, magicEnv._skill);
                    }
                } catch (Exception e) {
                    _log.error(e.getMessage(), e);
                }
            }
        }
    }

    /**
     * Enable a skill (remove it from _disabledSkills of the L2Creature).<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All skills disabled are identified by their skillId in <B>_disabledSkills</B> of the L2Creature <BR>
     * <BR>
     * 
     * @param skillId The identifier of the L2Skill to enable
     */
    public void enableSkill(int skillId) {
        if (_disabledSkills == null)
            return;

        final ScheduledFuture<?> task = _disabledSkills.remove(skillId);

        if (task != null)
            task.cancel(false);
    }

    /**
     * Disable a skill (add it to _disabledSkills of the L2Creature).<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All skills disabled are identified by their skillId in <B>_disabledSkills</B> of the L2Creature <BR>
     * <BR>
     * 
     * @param skillId The identifier of the L2Skill to disable
     * @deprecated
     */
    @Deprecated
    public final void disableSkill(int skillId) {
        disableSkill(skillId, Integer.MAX_VALUE);
    }

    /**
     * Disable this skill id for the duration of the delay in milliseconds.
     * 
     * @param skillId
     * @param delay in milliseconds
     * @return modified
     */
    public boolean disableSkill(int skillId, int delay) {
        if (delay < 100)
            return false;

        if (_disabledSkills == null)
            _disabledSkills = new FastMap<Integer, ScheduledFuture<?>>();

        final ScheduledFuture<?> oldTask = _disabledSkills.get(skillId);

        if (oldTask != null) {
            if (oldTask.getDelay(TimeUnit.MILLISECONDS) + 50 >= delay)
                return false;

            oldTask.cancel(false);
        }

        _disabledSkills.put(skillId, ThreadPoolManager.getInstance().schedule(new EnableSkill(skillId), delay));
        return true;
    }

    /** Task launching the function enableSkill() */
    private final class EnableSkill implements Runnable {
        private final int _skillId;

        public EnableSkill(int skillId) {
            _skillId = skillId;
        }

        @Override
        public void run() {
            enableSkill(_skillId);
        }
    }

    /**
     * Check if a skill is disabled.<BR>
     * <BR>
     * <B><U> Concept</U> :</B><BR>
     * <BR>
     * All skills disabled are identified by their skillId in <B>_disabledSkills</B> of the L2Creature <BR>
     * <BR>
     * 
     * @param skillId The identifier of the L2Skill to disable
     */
    public final boolean isSkillDisabled(int skillId) {
        if (isAllSkillsDisabled())
            return true;

        if (_disabledSkills == null)
            return false;

        return _disabledSkills.containsKey(skillId);
    }

    /**
     * Disable all skills (set _allSkillsDisabled to True).<BR>
     * <BR>
     */
    public final void disableAllSkills() {
        if (_log.isDebugEnabled())
            _log.debug("all skills disabled");
        _allSkillsDisabled = true;
    }

    /**
     * Enable all skills (set _allSkillsDisabled to False).<BR>
     * <BR>
     */
    public final void enableAllSkills() {
        if (_log.isDebugEnabled())
            _log.debug("all skills enabled");
        _allSkillsDisabled = false;
    }

    /**
     * Launch the magic skill and calculate its effects on each target contained in the targets table.<BR>
     * <BR>
     *
     * @param skill
     *            The L2Skill to use
     * @param targets
     *            The table of L2Object targets
     */
    public void callSkill(L2Skill skill, L2Creature... targets) {
        L2Weapon activeWeapon = getActiveWeaponItem();

        L2Player player = getActingPlayer();

        for (L2Object trg : targets) {
            if (player != null && trg instanceof L2Player && Config.SIEGE_ONLY_REGISTERED) {
                if (!((L2Player) trg).canBeTargetedByAtSiege(player)) {
                    //quick fix should be just removed from targetlist
                    return;
                }
            }

            if (trg instanceof L2Creature) {
                // Set some values inside target's instance for later use
                L2Creature target = (L2Creature) trg;

                // Check Raidboss attack and
                // check buffing chars who attack raidboss. Results in mute.
                L2Creature targetsAttackTarget = null;
                L2Creature targetsCastTarget = null;
                if (target.hasAI()) {
                    targetsAttackTarget = target.getAI().getAttackTarget();
                    targetsCastTarget = target.getAI().getCastTarget();
                }

                if (!Config.ALT_DISABLE_RAIDBOSS_PETRIFICATION
                        && getSkillLevel(L2Boss.BOSS_PENALTY_RESISTANCE) == -1
                        && ((target.isRaid() && getLevel() > target.getLevel() + 8)
                                || (!skill.isOffensive() && targetsAttackTarget != null
                                        && targetsAttackTarget.isRaid()
                                        && targetsAttackTarget.getAttackByList().contains(target) // has attacked raid
                                        && getLevel() > targetsAttackTarget.getLevel() + 8)
                                || (!skill.isOffensive() && targetsCastTarget != null && targetsCastTarget.isRaid()
                                        && targetsCastTarget.getAttackByList().contains(target) // has attacked raid
                                        && getLevel() > targetsCastTarget.getLevel() + 8))) {
                    if (skill.isMagic()) {
                        L2Skill tempSkill = SkillTable.getInstance().getInfo(L2Boss.BOSS_PENALTY_SILENCE, 1);
                        if (tempSkill != null)
                            tempSkill.getEffects(target, this);
                        else
                            _log.warn("Skill " + L2Boss.BOSS_PENALTY_SILENCE + " at level 1 is missing in DP.");
                    } else {
                        L2Skill tempSkill = SkillTable.getInstance().getInfo(L2Boss.BOSS_PENALTY_PETRIFICATION, 1);
                        if (tempSkill != null)
                            tempSkill.getEffects(target, this);
                        else
                            _log.warn(
                                    "Skill " + L2Boss.BOSS_PENALTY_PETRIFICATION + " at level 1 is missing in DP.");
                    }
                    return;
                }

                // Check if over-hit is possible
                if (skill.isOverhit()) {
                    if (target instanceof L2Attackable)
                        ((L2Attackable) target).overhitEnabled(true);
                }

                if (ChanceSkillList.canTriggerByCast(this, target, skill)) {
                    // Launch weapon Special ability skill effect if available
                    if (activeWeapon != null)
                        activeWeapon.getSkillEffectsByCast(this, target, skill);

                    // Maybe launch chance skills on us
                    if (_chanceSkills != null)
                        _chanceSkills.onSkillHit(target, false, skill);

                    // Maybe launch chance skills on target
                    if (target.getChanceSkills() != null)
                        target.getChanceSkills().onSkillHit(this, true, skill);
                }
            }
        }

        // Launch the magic skill and calculate its effects
        SkillHandler.getInstance().useSkill(this, skill, targets);

        if (skill.useSpiritShot() && !skill.hasEffectWhileCasting())
            useSpiritshotCharge();

        rechargeShot();

        if (player != null) {
            final boolean isAggroReducingSkill = skill.getSkillType() == L2SkillType.AGGREMOVE
                    || skill.getSkillType() == L2SkillType.AGGREDUCE
                    || skill.getSkillType() == L2SkillType.AGGREDUCE_CHAR;

            for (L2Object target : targets) {
                // EVT_ATTACKED and PvPStatus
                if (target instanceof L2Creature) {
                    if (skill.isNeutral()) {
                        // no flags
                    } else if (skill.isOffensive()) {
                        if (target instanceof L2Player || target instanceof L2Summon || target instanceof L2Trap) {
                            if (skill.getSkillType() != L2SkillType.SIGNET
                                    && skill.getSkillType() != L2SkillType.SIGNET_CASTTIME) {
                                if (!isAggroReducingSkill) {
                                    // notify target AI about the attack
                                    ((L2Creature) target).getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, player);
                                }

                                if (!(target instanceof L2Summon) || player.getPet() != target)
                                    player.updatePvPStatus(target.getActingPlayer());
                            }
                        } else if (target instanceof L2Attackable) {
                            if (!isAggroReducingSkill) {
                                switch (skill.getId()) {
                                case 51:
                                case 511:
                                    break;
                                default:
                                    // add attacker into list
                                    ((L2Creature) target).addAttackerToAttackByList(this);
                                }
                                // notify target AI about the attack
                                ((L2Creature) target).getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, this);
                            }
                        }
                    } else {
                        if (target instanceof L2Player) {
                            // Casting non offensive skill on player with pvp flag set or with karma
                            if (target != this && target != player
                                    && (((L2Player) target).getPvpFlag() > 0 || ((L2Player) target).getKarma() > 0))
                                player.updatePvPStatus();
                        } else if (target instanceof L2Attackable && !(skill.getSkillType() == L2SkillType.SUMMON)
                                && !(skill.getSkillType() == L2SkillType.BEAST_FEED)
                                && !(skill.getSkillType() == L2SkillType.UNLOCK)
                                && !(skill.getSkillType() == L2SkillType.DELUXE_KEY_UNLOCK)
                                && !(skill.getSkillType() == L2SkillType.HEAL_MOB)
                                && !(skill.getSkillType() == L2SkillType.MAKE_KILLABLE)
                                && !(skill.getSkillType() == L2SkillType.MAKE_QUEST_DROPABLE)
                                && (!(target instanceof L2Summon) || player.getPet() != target))
                            player.updatePvPStatus();
                    }
                }
            }

            notifyMobsAboutSkillCast(skill, targets);
        }
    }

    public void notifyMobsAboutSkillCast(L2Skill skill, L2Creature... targets) {
        L2Player player = getActingPlayer();

        if (player == null)
            return;

        // Mobs in range 1000 see spell
        for (L2Object obj : player.getKnownList().getKnownObjects().values()) {
            if (!(obj instanceof L2Npc))
                continue;

            final L2Npc npc = (L2Npc) obj;

            if (!npc.isInsideRadius(player, 1000, true, true))
                continue;

            final Quest[] quests = npc.getTemplate().getEventQuests(QuestEventType.ON_SKILL_SEE);
            if (quests != null)
                for (Quest quest : quests)
                    quest.notifySkillSee(npc, player, skill, targets, this instanceof L2Summon);
        }
    }

    public boolean isBehind(L2Object src) {
        return Direction.getDirection(this, src) == Direction.BACK;
    }

    /**
     * @deprecated should be called with proper target
     */
    @Deprecated
    public boolean isBehindTarget() {
        return isBehind(getTarget());
    }

    public boolean isInFrontOf(L2Object src, double degree) {
        return Util.isInAngle(this, src, degree);
    }

    public boolean isInFrontOf(L2Object src) {
        return Direction.getDirection(this, src) == Direction.FRONT;
    }

    /**
     * @deprecated should be called with proper target
     */
    @Deprecated
    public boolean isInFrontOfTarget() {
        return isInFrontOf(getTarget());
    }

    /**
     * Return 1.<BR>
     * <BR>
     */
    public double getLevelMod() {
        return 1;
    }

    public final void setTeleportSkillCast(final Runnable runnable, int delay) {
        getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

        // SoE Animation section
        setTarget(this);

        broadcastPacket(new MagicSkillUse(this, 1050, 1, delay, 0));
        // End SoE Animation section

        setSkillCast(new Runnable() {
            @Override
            public void run() {
                if (isDead())
                    return;

                runnable.run();
            }
        }, delay);
    }

    public final void setSkillCast(final Runnable runnable, int delay) {
        if (this instanceof L2Player)
            getActingPlayer().sendPacket(new SetupGauge(SetupGauge.BLUE, delay));

        setIsCastingNow(true, delay);

        _skillCast = ThreadPoolManager.getInstance().schedule(new Runnable() {
            @Override
            public void run() {
                setIsCastingNow(false);

                runnable.run();
            }
        }, delay);
    }

    private boolean _AIdisabled = false;

    private boolean _isMinion = false;

    /**
     * Return a Random Damage in function of the weapon.<BR>
     * <BR>
     */
    public final int getRandomDamage(L2Creature target) {
        L2Weapon weaponItem = getActiveWeaponItem();

        if (weaponItem == null)
            return 5 + (int) Math.sqrt(getLevel());

        return weaponItem.getRandomDamage();
    }

    @Override
    public String toString() {
        return "mob " + getObjectId();
    }

    /**
     * Not Implemented.<BR>
     * <BR>
     */
    public abstract int getLevel();

    // =========================================================

    // =========================================================
    // Stat - NEED TO REMOVE ONCE CREATURESTAT IS COMPLETE
    // Property - Public
    public final double calcStat(Stats stat, double init, L2Creature target, L2Skill skill) {
        return getStat().calcStat(stat, init, target, skill);
    }

    // Property - Public
    public final int getAccuracy() {
        return getStat().getAccuracy();
    }

    public final int getCriticalHit() {
        return getStat().getCriticalHit(null);
    }

    public final int getEvasionRate() {
        return getStat().getEvasionRate(null);
    }

    public final int getINT() {
        return getStat().getINT();
    }

    public final int getMagicalAttackRange(L2Skill skill) {
        return getStat().getMagicalAttackRange(skill);
    }

    public final int getMaxCp() {
        return getStat().getMaxCp();
    }

    public final int getMAtk(L2Creature target, L2Skill skill) {
        return getStat().getMAtk(target, skill);
    }

    public final int getMAtkSpd() {
        return getStat().getMAtkSpd();
    }

    public final int getMaxMp() {
        return getStat().getMaxMp();
    }

    public final int getMaxHp() {
        return getStat().getMaxHp();
    }

    public final int getMCriticalHit(L2Creature target, L2Skill skill) {
        return getStat().getMCriticalHit(target, skill);
    }

    public final int getMDef(L2Creature target, L2Skill skill) {
        return getStat().getMDef(target, skill);
    }

    public final int getPAtk(L2Creature target) {
        return getStat().getPAtk(target);
    }

    public final int getPAtkSpd() {
        return getStat().getPAtkSpd();
    }

    public final int getPDef(L2Creature target) {
        return getStat().getPDef(target);
    }

    public final int getShldDef() {
        return getStat().getShldDef();
    }

    public final int getPhysicalAttackRange() {
        return getStat().getPhysicalAttackRange();
    }

    public final int getRunSpeed() {
        return getStat().getRunSpeed();
    }

    // =========================================================

    // =========================================================
    // Status - NEED TO REMOVE ONCE L2CHARTATUS IS COMPLETE
    // Method - Public
    // WRAPPERS ONLY! DO NOT OVERRIDE IT!
    public final void reduceCurrentHp(double i, L2Creature attacker) {
        getStatus().reduceHp(i, attacker);
    }

    public final void reduceCurrentHp(double i, L2Creature attacker, L2Skill skill) {
        getStatus().reduceHp(i, attacker);
    }

    @Deprecated
    @SuppressWarnings("deprecation")
    public final void reduceCurrentHp(double i, L2Creature attacker, boolean awake) {
        getStatus().reduceHp(i, attacker, awake);
    }

    @Deprecated
    @SuppressWarnings("deprecation")
    public final void reduceCurrentHp(double i, L2Creature attacker, boolean awake, L2Skill skill) {
        getStatus().reduceHp(i, attacker, awake);
    }

    public final void reduceCurrentHp(double i, L2Creature attacker, boolean awake, boolean isDOT,
            boolean isConsume) {
        getStatus().reduceHp(i, attacker, awake, isDOT, isConsume);
    }

    public final void reduceCurrentHp(double i, L2Creature attacker, boolean awake, boolean isDOT,
            boolean isConsume, L2Skill skill) {
        getStatus().reduceHp(i, attacker, awake, isDOT, isConsume);
    }

    public final void reduceCurrentHpByDOT(double i, L2Creature attacker, L2Skill skill) {
        getStatus().reduceHpByDOT(i, attacker, skill);
    }

    public final void reduceCurrentHpByConsume(double i) {
        getStatus().reduceHpByConsume(i);
    }

    public final void reduceCurrentMp(double i) {
        getStatus().reduceMp(i);
    }

    // =========================================================
    public boolean isChampion() {
        return false;
    }

    /**
     * Check player max buff count
     * @return max buff count
     */
    public int getMaxBuffCount() {
        return Config.ALT_BUFFS_MAX_AMOUNT + Math.max(0, getSkillLevel(L2Skill.SKILL_DIVINE_INSPIRATION));
    }

    /**
     * Send system message about damage.<BR>
     * <BR>
     * <B><U> Overridden in </U> :</B><BR>
     * <BR>
     * <li> L2Player
     * <li> L2SummonInstance
     * <li> L2PetInstance</li>
     * <BR>
     * <BR>
     * @param target
     * @param damage
     * @param mcrit
     * @param pcrit
     * @param miss
     */
    public void sendDamageMessage(L2Creature target, int damage, boolean mcrit, boolean pcrit, boolean miss) {
        if (miss)
            target.sendAvoidMessage(this);
    }

    public void sendAvoidMessage(L2Creature attacker) {
    }

    public final void sendMissedDamageMessage(L2Creature target) {
        sendDamageMessage(target, -1, false, false, true);
    }

    public FusionSkill getFusionSkill() {
        return _fusionSkill;
    }

    public void setFusionSkill(FusionSkill fs) {
        _fusionSkill = fs;
    }

    public ChanceSkillList getChanceSkills() {
        return _chanceSkills;
    }

    public final byte getAttackElement() {
        return getStat().getAttackElement();
    }

    public final int getDefenseElementValue(byte defenseAttribute) {
        return getStat().getDefenseElementValue(defenseAttribute);
    }

    // Wrapper
    public final double getCurrentHp() {
        return getStatus().getCurrentHp();
    }

    // Wrapper
    public final double getCurrentMp() {
        return getStatus().getCurrentMp();
    }

    // Wrapper
    public final double getCurrentCp() {
        return getStatus().getCurrentCp();
    }

    public final int getAttackElementValue(byte attackAttribute) {
        return getStat().getAttackElementValue(attackAttribute);
    }

    public boolean mustFallDownOnDeath() {
        return isDead();
    }

    public void setPreventedFromReceivingBuffs(boolean value) {
        _block_buffs = value;
    }

    public boolean isPreventedFromReceivingBuffs() {
        return _block_buffs;
    }

    private final class FlyToLocationTask implements Runnable {
        private final L2Creature _flyTarget;
        private final L2Skill _skill;

        public FlyToLocationTask(L2Creature target, L2Skill skill) {
            _flyTarget = target;
            _skill = skill;
        }

        @Override
        public void run() {
            broadcastPacket(new FlyToLocation(L2Creature.this, _flyTarget, _skill.getFlyType()));
            getPosition().setXYZ(_flyTarget.getX(), _flyTarget.getY(), _flyTarget.getZ());
            broadcastPacket(new ValidateLocation(L2Creature.this));
        }
    }

    /** Task for potion and herb queue */
    private final class UsePotionTask implements Runnable {
        private final L2Skill _skill;

        private UsePotionTask(L2Skill skill) {
            _skill = skill;
        }

        @Override
        public void run() {
            doSimultaneousCast(_skill);
        }
    }

    public boolean isRaidMinion() {
        return _isMinion;
    }

    public boolean isRaidBoss() {
        return _isRaid && !_isMinion;
    }

    /**
     * Set this Npc as a Minion instance.<BR><BR>
     * @param val
     */
    public void setIsRaidMinion(boolean val) {
        _isRaid = val;
        _isMinion = val;
    }

    private volatile byte _packetBroadcastMask;

    protected boolean shouldAddPacketBroadcastMask() {
        return !getKnownList().getKnownPlayers().isEmpty();
    }

    public final void addPacketBroadcastMask(BroadcastMode mode) {
        if (!shouldAddPacketBroadcastMask())
            return;

        _packetBroadcastMask |= mode.mask();

        PacketBroadcaster.getInstance().add(this);
    }

    public final void removePacketBroadcastMask(BroadcastMode mode) {
        _packetBroadcastMask &= ~mode.mask();
    }

    public final byte getPacketBroadcastMask() {
        return _packetBroadcastMask;
    }

    public final void broadcastFullInfo() {
        addPacketBroadcastMask(BroadcastMode.BROADCAST_FULL_INFO);
    }

    public abstract void broadcastFullInfoImpl();

    @Override
    public final L2Creature getActingCharacter() {
        return this;
    }

    protected final CreatureShots _shots;

    protected CreatureShots initShots() {
        return CreatureShots.getEmptyInstance();
    }

    public CreatureShots getShots() {
        return _shots;
    }

    public final void rechargeShot() {
        getShots().rechargeShots();
    }

    public final void scheduleShotRecharge(int delay) {
        getShots().scheduleShotRecharge(delay);
    }

    public final boolean isSoulshotCharged() {
        return getShots().isSoulshotCharged();
    }

    public final boolean isSpiritshotCharged() {
        return getShots().isSpiritshotCharged();
    }

    public final boolean isBlessedSpiritshotCharged() {
        return getShots().isBlessedSpiritshotCharged();
    }

    public final boolean isAnySpiritshotCharged() {
        return getShots().isAnySpiritshotCharged();
    }

    public final boolean isFishshotCharged() {
        return getShots().isFishshotCharged();
    }

    public final void useSoulshotCharge() {
        getShots().useSoulshotCharge();
    }

    public final void useSpiritshotCharge() {
        getShots().useSpiritshotCharge();
    }

    public final void useBlessedSpiritshotCharge() {
        getShots().useBlessedSpiritshotCharge();
    }

    public final void useFishshotCharge() {
        getShots().useFishshotCharge();
    }

    public final void clearShotCharges() {
        getShots().clearShotCharges();
    }

    public void updateInvisibilityStatus() {
        // Since knownlist objects are always mutual linked, DeleteObject is used for invisibility.
        // In this case, sending the specific *Info packet is prevented until the object is visible again.
        DeleteObject de = new DeleteObject(this);
        for (L2Player player : getKnownList().getKnownPlayers().values()) {
            if (!player.canSee(this)) {
                if (player.getTarget() == this) {
                    player.setTarget(null);
                    player.abortAttack();
                }
                player.sendPacket(de);
            }
        }
        broadcastFullInfo();
    }

    public void sendResistedMyEffectMessage(L2Creature target, L2Skill skill) {
    }

    public void sendResistedMyMagicMessage(L2Creature target) {
        target.sendResistedAgainstMagicMessage(this);
    }

    public void sendResistedMyMagicSlightlyMessage(L2Creature target) {
        target.sendResistedAgainstMagicWeaklyMessage(this);
    }

    public void sendResistedAgainstMagicMessage(L2Creature attacker) {
    }

    public void sendResistedAgainstMagicWeaklyMessage(L2Creature attacker) {
    }
}