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

Java tutorial

Introduction

Here is the source code for com.l2jfree.gameserver.gameobjects.L2Player.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 java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Comparator;
import java.util.HashMap;
import java.util.HashSet;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.locks.ReentrantLock;

import javolution.util.FastList;
import javolution.util.FastMap;

import org.apache.commons.lang3.ArrayUtils;

import com.l2jfree.Config;
import com.l2jfree.L2DatabaseFactory;
import com.l2jfree.gameserver.Announcements;
import com.l2jfree.gameserver.GameServer;
import com.l2jfree.gameserver.LoginServerThread;
import com.l2jfree.gameserver.Shutdown;
import com.l2jfree.gameserver.Shutdown.DisableType;
import com.l2jfree.gameserver.ThreadPoolManager;
import com.l2jfree.gameserver.cache.HtmCache;
import com.l2jfree.gameserver.cache.WarehouseCacheManager;
import com.l2jfree.gameserver.communitybbs.Manager.ForumsBBSManager;
import com.l2jfree.gameserver.communitybbs.Manager.RegionBBSManager;
import com.l2jfree.gameserver.communitybbs.Manager.RegionBBSManager.PlayerStateOnCommunity;
import com.l2jfree.gameserver.communitybbs.bb.Forum;
import com.l2jfree.gameserver.datatables.CharNameTable;
import com.l2jfree.gameserver.datatables.CharTemplateTable;
import com.l2jfree.gameserver.datatables.ClanTable;
import com.l2jfree.gameserver.datatables.FishTable;
import com.l2jfree.gameserver.datatables.GmListTable;
import com.l2jfree.gameserver.datatables.HennaTable;
import com.l2jfree.gameserver.datatables.HeroSkillTable;
import com.l2jfree.gameserver.datatables.ItemTable;
import com.l2jfree.gameserver.datatables.NobleSkillTable;
import com.l2jfree.gameserver.datatables.NpcTable;
import com.l2jfree.gameserver.datatables.PetDataTable;
import com.l2jfree.gameserver.datatables.RecipeTable;
import com.l2jfree.gameserver.datatables.RecordTable;
import com.l2jfree.gameserver.datatables.SkillTable;
import com.l2jfree.gameserver.datatables.SkillTreeTable;
import com.l2jfree.gameserver.gameobjects.ai.CtrlIntention;
import com.l2jfree.gameserver.gameobjects.ai.L2CreatureAI;
import com.l2jfree.gameserver.gameobjects.ai.L2PlayerAI;
import com.l2jfree.gameserver.gameobjects.ai.L2SummonAI;
import com.l2jfree.gameserver.gameobjects.appearance.PlayerAppearance;
import com.l2jfree.gameserver.gameobjects.base.ClassId;
import com.l2jfree.gameserver.gameobjects.base.ClassLevel;
import com.l2jfree.gameserver.gameobjects.base.Experience;
import com.l2jfree.gameserver.gameobjects.base.Race;
import com.l2jfree.gameserver.gameobjects.base.SubClass;
import com.l2jfree.gameserver.gameobjects.effects.PlayerEffects;
import com.l2jfree.gameserver.gameobjects.instance.L2AirShipInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2BoatInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2ClassMasterInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2CubicInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2DoorInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2FestivalMonsterInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2FortSiegeGuardInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2GuardInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2MonsterInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2NpcInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2PetInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2SiegeGuardInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2StaticObjectInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2SummonInstance;
import com.l2jfree.gameserver.gameobjects.instance.L2TamedBeastInstance;
import com.l2jfree.gameserver.gameobjects.itemcontainer.Inventory;
import com.l2jfree.gameserver.gameobjects.itemcontainer.ItemContainer;
import com.l2jfree.gameserver.gameobjects.itemcontainer.PetInventory;
import com.l2jfree.gameserver.gameobjects.itemcontainer.PlayerFreight;
import com.l2jfree.gameserver.gameobjects.itemcontainer.PlayerInventory;
import com.l2jfree.gameserver.gameobjects.itemcontainer.PlayerWarehouse;
import com.l2jfree.gameserver.gameobjects.knownlist.CreatureKnownList;
import com.l2jfree.gameserver.gameobjects.knownlist.PlayerKnownList;
import com.l2jfree.gameserver.gameobjects.reference.ClearableReference;
import com.l2jfree.gameserver.gameobjects.reference.ImmutableReference;
import com.l2jfree.gameserver.gameobjects.shot.CreatureShots;
import com.l2jfree.gameserver.gameobjects.shot.PlayerShots;
import com.l2jfree.gameserver.gameobjects.skills.PlayerSkills;
import com.l2jfree.gameserver.gameobjects.stat.CreatureStat;
import com.l2jfree.gameserver.gameobjects.stat.PlayerStat;
import com.l2jfree.gameserver.gameobjects.status.CreatureStatus;
import com.l2jfree.gameserver.gameobjects.status.PlayerStatus;
import com.l2jfree.gameserver.gameobjects.templates.L2PlayerTemplate;
import com.l2jfree.gameserver.gameobjects.view.ICreatureView;
import com.l2jfree.gameserver.gameobjects.view.PlayerView;
import com.l2jfree.gameserver.geodata.GeoData;
import com.l2jfree.gameserver.handler.ItemHandler;
import com.l2jfree.gameserver.handler.SkillHandler;
import com.l2jfree.gameserver.handler.admincommands.AdminEditChar;
import com.l2jfree.gameserver.handler.skills.TakeCastle;
import com.l2jfree.gameserver.handler.skills.TakeFort;
import com.l2jfree.gameserver.instancemanager.CastleManager;
import com.l2jfree.gameserver.instancemanager.CursedWeaponsManager;
import com.l2jfree.gameserver.instancemanager.DimensionalRiftManager;
import com.l2jfree.gameserver.instancemanager.DuelManager;
import com.l2jfree.gameserver.instancemanager.FactionManager;
import com.l2jfree.gameserver.instancemanager.FortManager;
import com.l2jfree.gameserver.instancemanager.FortSiegeManager;
import com.l2jfree.gameserver.instancemanager.FourSepulchersManager;
import com.l2jfree.gameserver.instancemanager.GameTimeManager;
import com.l2jfree.gameserver.instancemanager.InstanceManager;
import com.l2jfree.gameserver.instancemanager.ItemsAutoDestroyManager;
import com.l2jfree.gameserver.instancemanager.MapRegionManager;
import com.l2jfree.gameserver.instancemanager.PartyRoomManager;
import com.l2jfree.gameserver.instancemanager.QuestManager;
import com.l2jfree.gameserver.instancemanager.RecommendationManager;
import com.l2jfree.gameserver.instancemanager.SiegeManager;
import com.l2jfree.gameserver.instancemanager.ZoneManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.AntharasManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.BaiumManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.BaylorManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.FrintezzaManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.SailrenManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.ValakasManager;
import com.l2jfree.gameserver.instancemanager.grandbosses.VanHalterManager;
import com.l2jfree.gameserver.instancemanager.lastimperialtomb.LastImperialTombManager;
import com.l2jfree.gameserver.instancemanager.leaderboards.ArenaManager;
import com.l2jfree.gameserver.model.BlockList;
import com.l2jfree.gameserver.model.CursedWeapon;
import com.l2jfree.gameserver.model.Elementals;
import com.l2jfree.gameserver.model.FishData;
import com.l2jfree.gameserver.model.L2Fishing;
import com.l2jfree.gameserver.model.L2FriendList;
import com.l2jfree.gameserver.model.L2Macro;
import com.l2jfree.gameserver.model.L2Marker;
import com.l2jfree.gameserver.model.L2PetData;
import com.l2jfree.gameserver.model.L2Request;
import com.l2jfree.gameserver.model.L2ShortCut;
import com.l2jfree.gameserver.model.L2Transformation;
import com.l2jfree.gameserver.model.Location;
import com.l2jfree.gameserver.model.MacroList;
import com.l2jfree.gameserver.model.ShortCuts;
import com.l2jfree.gameserver.model.TradeList;
import com.l2jfree.gameserver.model.clan.L2Clan;
import com.l2jfree.gameserver.model.clan.L2ClanMember;
import com.l2jfree.gameserver.model.clan.L2SiegeClan;
import com.l2jfree.gameserver.model.entity.Castle;
import com.l2jfree.gameserver.model.entity.Duel;
import com.l2jfree.gameserver.model.entity.Fort;
import com.l2jfree.gameserver.model.entity.FortSiege;
import com.l2jfree.gameserver.model.entity.GrandBossState;
import com.l2jfree.gameserver.model.entity.Instance;
import com.l2jfree.gameserver.model.entity.Siege;
import com.l2jfree.gameserver.model.entity.events.AbstractFunEventPlayerInfo;
import com.l2jfree.gameserver.model.entity.events.AutomatedTvT;
import com.l2jfree.gameserver.model.entity.events.CTF.CTFPlayerInfo;
import com.l2jfree.gameserver.model.entity.faction.FactionMember;
import com.l2jfree.gameserver.model.items.L2ItemInstance;
import com.l2jfree.gameserver.model.items.manufacture.L2ManufactureList;
import com.l2jfree.gameserver.model.items.recipe.L2RecipeList;
import com.l2jfree.gameserver.model.items.templates.L2Armor;
import com.l2jfree.gameserver.model.items.templates.L2ArmorType;
import com.l2jfree.gameserver.model.items.templates.L2EtcItemType;
import com.l2jfree.gameserver.model.items.templates.L2Henna;
import com.l2jfree.gameserver.model.items.templates.L2Item;
import com.l2jfree.gameserver.model.items.templates.L2Weapon;
import com.l2jfree.gameserver.model.items.templates.L2WeaponType;
import com.l2jfree.gameserver.model.mapregion.TeleportWhereType;
import com.l2jfree.gameserver.model.olympiad.Olympiad;
import com.l2jfree.gameserver.model.party.L2Party;
import com.l2jfree.gameserver.model.party.L2PartyRoom;
import com.l2jfree.gameserver.model.quest.Quest;
import com.l2jfree.gameserver.model.quest.QuestState;
import com.l2jfree.gameserver.model.quest.State;
import com.l2jfree.gameserver.model.restriction.AvailableRestriction;
import com.l2jfree.gameserver.model.restriction.ObjectRestrictions;
import com.l2jfree.gameserver.model.restriction.global.DuelRestriction;
import com.l2jfree.gameserver.model.restriction.global.GlobalRestrictions;
import com.l2jfree.gameserver.model.sevensigns.SevenSigns;
import com.l2jfree.gameserver.model.sevensigns.SevenSignsFestival;
import com.l2jfree.gameserver.model.skills.Env;
import com.l2jfree.gameserver.model.skills.Formulas;
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.conditions.ConditionGameTime;
import com.l2jfree.gameserver.model.skills.conditions.ConditionPlayerHp;
import com.l2jfree.gameserver.model.skills.effects.L2Effect;
import com.l2jfree.gameserver.model.skills.funcs.Func;
import com.l2jfree.gameserver.model.skills.l2skills.L2SkillSummon;
import com.l2jfree.gameserver.model.skills.learn.L2CertificationSkillsLearn;
import com.l2jfree.gameserver.model.skills.learn.L2SkillLearn;
import com.l2jfree.gameserver.model.skills.learn.L2TransformSkillLearn;
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.L2JailZone;
import com.l2jfree.gameserver.model.zone.L2Zone;
import com.l2jfree.gameserver.network.Disconnection;
import com.l2jfree.gameserver.network.L2Client;
import com.l2jfree.gameserver.network.SystemChatChannelId;
import com.l2jfree.gameserver.network.SystemMessageId;
import com.l2jfree.gameserver.network.packets.L2ServerPacket;
import com.l2jfree.gameserver.network.packets.client.ConfirmDlgAnswer.AnswerHandler;
import com.l2jfree.gameserver.network.packets.server.AbstractNpcInfo;
import com.l2jfree.gameserver.network.packets.server.ActionFailed;
import com.l2jfree.gameserver.network.packets.server.CameraMode;
import com.l2jfree.gameserver.network.packets.server.ChangeWaitType;
import com.l2jfree.gameserver.network.packets.server.CharInfo;
import com.l2jfree.gameserver.network.packets.server.ConfirmDlg;
import com.l2jfree.gameserver.network.packets.server.EffectInfoPacket.EffectInfoPacketList;
import com.l2jfree.gameserver.network.packets.server.EtcStatusUpdate;
import com.l2jfree.gameserver.network.packets.server.ExBasicActionList;
import com.l2jfree.gameserver.network.packets.server.ExDuelUpdateUserInfo;
import com.l2jfree.gameserver.network.packets.server.ExFishingEnd;
import com.l2jfree.gameserver.network.packets.server.ExFishingStart;
import com.l2jfree.gameserver.network.packets.server.ExGetBookMarkInfoPacket;
import com.l2jfree.gameserver.network.packets.server.ExGetOnAirShip;
import com.l2jfree.gameserver.network.packets.server.ExManagePartyRoomMember;
import com.l2jfree.gameserver.network.packets.server.ExOlympiadMode;
import com.l2jfree.gameserver.network.packets.server.ExOlympiadUserInfo;
import com.l2jfree.gameserver.network.packets.server.ExPrivateStoreSetWholeMsg;
import com.l2jfree.gameserver.network.packets.server.ExSetCompassZoneCode;
import com.l2jfree.gameserver.network.packets.server.ExSpawnEmitter;
import com.l2jfree.gameserver.network.packets.server.ExStorageMaxCount;
import com.l2jfree.gameserver.network.packets.server.ExVitalityPointInfo;
import com.l2jfree.gameserver.network.packets.server.FriendList;
import com.l2jfree.gameserver.network.packets.server.GMHide;
import com.l2jfree.gameserver.network.packets.server.GameGuardQuery;
import com.l2jfree.gameserver.network.packets.server.GetOnVehicle;
import com.l2jfree.gameserver.network.packets.server.HennaInfo;
import com.l2jfree.gameserver.network.packets.server.InventoryUpdate;
import com.l2jfree.gameserver.network.packets.server.ItemList;
import com.l2jfree.gameserver.network.packets.server.MagicEffectIcons;
import com.l2jfree.gameserver.network.packets.server.MagicSkillUse;
import com.l2jfree.gameserver.network.packets.server.MyTargetSelected;
import com.l2jfree.gameserver.network.packets.server.NicknameChanged;
import com.l2jfree.gameserver.network.packets.server.NpcHtmlMessage;
import com.l2jfree.gameserver.network.packets.server.ObservationMode;
import com.l2jfree.gameserver.network.packets.server.ObservationReturn;
import com.l2jfree.gameserver.network.packets.server.PartySmallWindowUpdate;
import com.l2jfree.gameserver.network.packets.server.PartySpelled;
import com.l2jfree.gameserver.network.packets.server.PetInventoryUpdate;
import com.l2jfree.gameserver.network.packets.server.PledgeShowMemberListDelete;
import com.l2jfree.gameserver.network.packets.server.PledgeShowMemberListUpdate;
import com.l2jfree.gameserver.network.packets.server.PledgeSkillList;
import com.l2jfree.gameserver.network.packets.server.PrivateStoreListBuy;
import com.l2jfree.gameserver.network.packets.server.PrivateStoreListSell;
import com.l2jfree.gameserver.network.packets.server.PrivateStoreManageListBuy;
import com.l2jfree.gameserver.network.packets.server.PrivateStoreManageListSell;
import com.l2jfree.gameserver.network.packets.server.PrivateStoreMsgBuy;
import com.l2jfree.gameserver.network.packets.server.PrivateStoreMsgSell;
import com.l2jfree.gameserver.network.packets.server.QuestList;
import com.l2jfree.gameserver.network.packets.server.RecipeShopMsg;
import com.l2jfree.gameserver.network.packets.server.RecipeShopSellList;
import com.l2jfree.gameserver.network.packets.server.RelationChanged;
import com.l2jfree.gameserver.network.packets.server.Ride;
import com.l2jfree.gameserver.network.packets.server.SetupGauge;
import com.l2jfree.gameserver.network.packets.server.ShortBuffStatusUpdate;
import com.l2jfree.gameserver.network.packets.server.ShortCutInit;
import com.l2jfree.gameserver.network.packets.server.SkillCoolTime;
import com.l2jfree.gameserver.network.packets.server.SkillList;
import com.l2jfree.gameserver.network.packets.server.Snoop;
import com.l2jfree.gameserver.network.packets.server.SocialAction;
import com.l2jfree.gameserver.network.packets.server.SpecialCamera;
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.TargetSelected;
import com.l2jfree.gameserver.network.packets.server.TargetUnselected;
import com.l2jfree.gameserver.network.packets.server.TradeDone;
import com.l2jfree.gameserver.network.packets.server.TradeOtherDone;
import com.l2jfree.gameserver.network.packets.server.TradeStart;
import com.l2jfree.gameserver.network.packets.server.TutorialCloseHtml;
import com.l2jfree.gameserver.network.packets.server.TutorialShowHtml;
import com.l2jfree.gameserver.network.packets.server.UserInfo;
import com.l2jfree.gameserver.network.packets.server.ValidateLocation;
import com.l2jfree.gameserver.taskmanager.AbstractIterativePeriodicTaskManager;
import com.l2jfree.gameserver.taskmanager.AttackStanceTaskManager;
import com.l2jfree.gameserver.taskmanager.LeakTaskManager;
import com.l2jfree.gameserver.taskmanager.MovementController;
import com.l2jfree.gameserver.taskmanager.PacketBroadcaster.BroadcastMode;
import com.l2jfree.gameserver.taskmanager.SQLQueue;
import com.l2jfree.gameserver.util.Broadcast;
import com.l2jfree.gameserver.util.FloodProtector;
import com.l2jfree.gameserver.util.Util;
import com.l2jfree.lang.L2Math;
import com.l2jfree.lang.L2System;
import com.l2jfree.lang.Replaceable;
import com.l2jfree.mmocore.network.InvalidPacketException;
import com.l2jfree.sql.SQLQuery;
import com.l2jfree.tools.geometry.Point3D;
import com.l2jfree.tools.random.Rnd;
import com.l2jfree.tools.util.HexUtil;
import com.l2jfree.util.ArrayBunch;
import com.l2jfree.util.L2Arrays;
import com.l2jfree.util.L2Collections;
import com.l2jfree.util.LazyFastList;
import com.l2jfree.util.LazyFastMap;

/**
 * This class represents all player characters in the world.
 * There is always a client-thread connected to this (except if a player-store is activated upon logout).<BR><BR>
 *
 * @version $Revision: 1.66.2.41.2.33 $ $Date: 2005/04/11 10:06:09 $
 */
public final class L2Player extends L2Playable {
    @SuppressWarnings("hiding")
    public static final L2Player[] EMPTY_ARRAY = new L2Player[0];

    // Character Skill Reuse SQL String Definitions:
    private static final String RESTORE_SKILL_REUSES = "SELECT skillId,reuseDelay,expiration FROM character_skill_reuses WHERE charId=?";
    private static final String ADD_SKILL_REUSE = "INSERT INTO character_skill_reuses (charId,skillId,reuseDelay,expiration) VALUES (?,?,?,?)";
    private static final String DELETE_SKILL_REUSES = "DELETE FROM character_skill_reuses WHERE charId=?";

    // Character Character SQL String Definitions:
    private static final String UPDATE_CHARACTER = "UPDATE characters SET level=?,maxHp=?,curHp=?,maxCp=?,curCp=?,maxMp=?,curMp=?,face=?,hairStyle=?,hairColor=?,sex=?,heading=?,x=?,y=?,z=?,exp=?,expBeforeDeath=?,sp=?,karma=?,fame=?,pvpkills=?,pkkills=?,clanid=?,race=?,classid=?,deletetime=?,title=?,accesslevel=?,online=?,isin7sdungeon=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,in_jail=?,jail_timer=?,newbie=?,nobless=?,pledge_rank=?,subpledge=?,lvl_joined_academy=?,apprentice=?,sponsor=?,varka_ketra_ally=?,clan_join_expiry_time=?,clan_create_expiry_time=?,banchat_timer=?,char_name=?,death_penalty_level=?,vitality_points=?,bookmarkslot=? WHERE charId=?";
    private static final String RESTORE_CHARACTER = "SELECT account_name, charId, char_name, level, maxHp, curHp, maxCp, curCp, maxMp, curMp, face, hairStyle, hairColor, sex, heading, x, y, z, exp, expBeforeDeath, sp, karma, fame, pvpkills, pkkills, clanid, race, classid, deletetime, cancraft, title, accesslevel, online, char_slot, lastAccess, clan_privs, wantspeace, base_class, onlinetime, isin7sdungeon, in_jail, jail_timer, banchat_timer, newbie, nobless, pledge_rank, subpledge, lvl_joined_academy, apprentice, sponsor, varka_ketra_ally, clan_join_expiry_time,clan_create_expiry_time,charViP,death_penalty_level,vitality_points,bookmarkslot FROM characters WHERE charId=?";

    // Character Subclass SQL String Definitions:
    private static final String RESTORE_CHAR_SUBCLASSES = "SELECT class_id,exp,sp,level,class_index FROM character_subclasses WHERE charId=? ORDER BY class_index ASC";
    private static final String ADD_CHAR_SUBCLASS = "INSERT INTO character_subclasses (charId,class_id,exp,sp,level,class_index) VALUES (?,?,?,?,?,?)";
    private static final String UPDATE_CHAR_SUBCLASS = "UPDATE character_subclasses SET exp=?,sp=?,level=?,class_id=? WHERE charId=? AND class_index =?";
    private static final String DELETE_CHAR_SUBCLASS = "DELETE FROM character_subclasses WHERE charId=? AND class_index=?";

    // Character Henna SQL String Definitions:
    private static final String RESTORE_CHAR_HENNAS = "SELECT slot,symbol_id FROM character_hennas WHERE charId=? AND class_index=?";
    private static final String ADD_CHAR_HENNA = "INSERT INTO character_hennas (charId,symbol_id,slot,class_index) VALUES (?,?,?,?)";
    private static final String DELETE_CHAR_HENNA = "DELETE FROM character_hennas WHERE charId=? AND slot=? AND class_index=?";
    private static final String DELETE_CHAR_HENNAS = "DELETE FROM character_hennas WHERE charId=? AND class_index=?";

    // Character Shortcut SQL String Definitions:
    private static final String DELETE_CHAR_SHORTCUTS = "DELETE FROM character_shortcuts WHERE charId=? AND class_index=?";

    // Character Transformation SQL String Definitions:
    private static final String SELECT_CHAR_TRANSFORM = "SELECT transform_id FROM characters WHERE charId=?";
    private static final String UPDATE_CHAR_TRANSFORM = "UPDATE characters SET transform_id=? WHERE charId=?";

    // Character Teleport Bookmark:
    private static final String INSERT_TP_BOOKMARK = "INSERT INTO character_tpbookmark (charId,Id,x,y,z,icon,tag,name) values (?,?,?,?,?,?,?,?)";
    private static final String UPDATE_TP_BOOKMARK = "UPDATE character_tpbookmark SET icon=?,tag=?,name=? where charId=? AND Id=?";
    private static final String RESTORE_TP_BOOKMARK = "SELECT Id,x,y,z,icon,tag,name FROM character_tpbookmark WHERE charId=?";
    private static final String DELETE_TP_BOOKMARK = "DELETE FROM character_tpbookmark WHERE charId=? AND Id=?";

    // Subclass certification
    public static final String STORE_CHAR_CERTIFICATION = "INSERT INTO character_subclass_certification (charId,class_index,certif_level) VALUES (?,?,?)";
    public static final String UPDATE_CHAR_CERTIFICATION = "UPDATE character_subclass_certification SET certif_level=? WHERE charId=? AND class_index=?";
    private static final String DELETE_CHAR_CERTIFICATION = "DELETE FROM character_subclass_certification WHERE charId=?";
    private static final String GET_CHAR_CERTIFICATION = "SELECT certif_level FROM character_subclass_certification WHERE charId=? AND class_index=?";

    // Creation day
    private static final String GET_CREATION_DATE = "SELECT lastClaim,birthDate FROM character_birthdays WHERE charId=?";
    private static final String CLAIM_CREATION_DAY = "UPDATE character_birthdays SET lastClaim=? WHERE charId=?";

    // Name / Title Colors
    private static final String RESTORE_COLORS = "SELECT name_color, title_color FROM character_name_title_colors WHERE char_id=?";
    private static final String UPDATE_COLORS = "REPLACE INTO character_name_title_colors VALUES(?,?,?)";

    public static final int REQUEST_TIMEOUT = 15;

    public static final int STORE_PRIVATE_NONE = 0;
    public static final int STORE_PRIVATE_SELL = 1;
    public static final int STORE_PRIVATE_BUY = 3;
    public static final int STORE_PRIVATE_MANUFACTURE = 5;
    public static final int STORE_PRIVATE_PACKAGE_SELL = 8;

    /** The table containing all minimum level needed for each Expertise (None, D, C, B, A, S, S80, S84)*/
    private static final int[] EXPERTISE_LEVELS = { SkillTreeTable.getInstance().getExpertiseLevel(0), // NONE
            SkillTreeTable.getInstance().getExpertiseLevel(1), // D
            SkillTreeTable.getInstance().getExpertiseLevel(2), // C
            SkillTreeTable.getInstance().getExpertiseLevel(3), // B
            SkillTreeTable.getInstance().getExpertiseLevel(4), // A
            SkillTreeTable.getInstance().getExpertiseLevel(5), // S
            SkillTreeTable.getInstance().getExpertiseLevel(6), // S80
            SkillTreeTable.getInstance().getExpertiseLevel(7) //S84
    };

    private static final int[] COMMON_CRAFT_LEVELS = { 5, 20, 28, 36, 43, 49, 55, 62 };

    public class AIAccessor extends L2Creature.AIAccessor {
        protected AIAccessor() {
        }

        public L2Player getPlayer() {
            return L2Player.this;
        }

        public void doPickupItem(L2Object object) {
            L2Player.this.doPickupItem(object);
        }

        public void doInteract(L2Creature target) {
            L2Player.this.doInteract(target);
        }

        @Override
        public void doAttack(L2Creature target) {
            super.doAttack(target);

            // Cancel the recent fake-death protection instantly if the player attacks or casts spells
            setRecentFakeDeath(false);
        }

        @Override
        public void doCast(L2Skill skill) {
            super.doCast(skill);

            // Cancel the recent fake-death protection instantly if the player attacks or casts spells
            setRecentFakeDeath(false);
            if (skill == null)
                return;
            if (!skill.isOffensive())
                return;

            switch (skill.getTargetType()) {
            case TARGET_GROUND:
                return;
            default: {
                for (L2CubicInstance cubic : getCubics().values())
                    if (cubic.getId() != L2CubicInstance.LIFE_CUBIC)
                        cubic.doAction();
            }
                break;
            }
        }
    }

    private L2Client _client;

    private final PlayerAppearance _appearance;

    /** Sitting down and Standing up fix */
    private long _lastSitStandRequest = 0;

    /** The Identifier of the L2Player */
    private int _charId = 0x00030b7a;

    /** The Experience of the L2Player before the last Death Penalty */
    private long _expBeforeDeath;

    /** The Karma of the L2Player (if higher than 0, the name of the L2Player appears in red) */
    private int _karma;

    /** The number of player killed during a PvP (the player killed was PvP Flagged) */
    private int _pvpKills;

    /** The PK counter of the L2Player (= Number of non PvP Flagged player killed) */
    private int _pkKills;

    /** The Siege state of the L2Player */
    private byte _siegeState = SIEGE_STATE_NOT_INVOLVED;
    private boolean _isInSiege = false;

    private int _lastCompassZone; // The last compass zone update send to the client

    private boolean _isIn7sDungeon = false;

    public int _bookmarkslot = 0; // The Teleport Bookmark Slot
    public final List<TeleportBookmark> tpbookmark = new LazyFastList<TeleportBookmark>();

    private int _subPledgeType = 0;

    /** L2Player's pledge rank*/
    private int _pledgeRank;

    /** Level at which the player joined the clan as an accedemy member*/
    private int _lvlJoinedAcademy = 0;

    /** The random number of the L2Player */
    //private static final Random _rnd = new Random();
    private int _curWeightPenalty = 0;

    private long _deleteTimer;
    private PlayerInventory _inventory;
    private PlayerWarehouse _warehouse;
    private PlayerFreight _freight;
    private List<PlayerFreight> _depositedFreight;
    private final PlayerSkills _pcSkills = new PlayerSkills(this);

    /** True if the L2Player is sitting */
    private boolean _waitTypeSitting;

    /** True if the L2Player is using the relax skill */
    private boolean _relax;

    /** AirShip */
    private L2AirShipInstance _airShip;
    private Point3D _inAirShipPosition;

    public ScheduledFuture<?> _taskforfish;

    /** Last NPC Id talked on a quest */
    private int _questNpcObject = 0;

    /** Bitmask used to keep track of one-time/newbie quest rewards */
    private int _newbie;

    /** The table containing all Quests began by the L2Player */
    private final Map<String, QuestState> _quests = new LazyFastMap<String, QuestState>();

    /** The list containing all shortCuts of this L2Player */
    private ShortCuts _shortCuts;

    /** The list containing all macroses of this L2Player */
    private MacroList _macroses;

    private TradeList _activeTradeList;
    private ItemContainer _activeWarehouse;
    private L2ManufactureList _createList;
    private TradeList _sellList;
    private TradeList _buyList;

    private L2Player[] _snoopers = L2Player.EMPTY_ARRAY; // List of GMs snooping this player
    private L2Player[] _snoopedPlayers = L2Player.EMPTY_ARRAY; // List of players being snooped by this GM

    /** The Private Store type of the L2Player (STORE_PRIVATE_NONE=0, STORE_PRIVATE_SELL=1, sellmanage=2, STORE_PRIVATE_BUY=3, buymanage=4, STORE_PRIVATE_MANUFACTURE=5) */
    private int _privatestore;
    private ClassId _skillLearningClassId;

    // Hennas
    private final L2Henna[] _henna = new L2Henna[3];
    private int _hennaSTR;
    private int _hennaINT;
    private int _hennaDEX;
    private int _hennaMEN;
    private int _hennaWIT;
    private int _hennaCON;

    private boolean _isRidingStrider = false;
    private boolean _isRidingRedStrider = false;
    private boolean _isRidingHorse = false;
    private boolean _isFlyingMounted = false;

    /** The L2Summon of the L2Player */
    private L2Summon _summon = null;
    /** The L2Decoy of the L2Player */
    private L2Decoy _decoy = null;
    /** The L2Trap of the L2Player */
    private L2Trap _trap = null;
    /** The L2Agathion of the L2Player */
    private int _agathionId = 0;
    // Apparently, a L2Player CAN have both a summon AND a tamed beast at the same time!!
    private L2TamedBeastInstance _tamedBeast = null;

    // Client radar
    private L2Marker _radar;

    // These values are only stored temporarily
    private boolean _lookingForParty;
    private boolean _partyMatchingAllLevels;
    private int _partyMatchingRegion;
    private L2PartyRoom _partyRoom;
    private L2Party _party;
    // Clan related attributes

    /** The Clan Identifier of the L2Player */
    private int _clanId;

    /** The Clan object of the L2Player */
    private L2Clan _clan;

    /** Apprentice and Sponsor IDs */
    private int _apprentice = 0;
    private int _sponsor = 0;

    private long _clanJoinExpiryTime;
    private long _clanCreateExpiryTime;

    private long _onlineTime;
    private long _onlineBeginTime;

    // GM Stuff
    private boolean _isGm;
    private int _accessLevel;

    private boolean _messageRefusal = false; // Message refusal mode
    private boolean _dietMode = false; // Ignore weight penalty
    private boolean _tradeRefusal = false; // Trade refusal
    private boolean _exchangeRefusal = false; // Exchange refusal

    // This is needed to find the inviting player for Party response
    // There can only be one active party request at once
    private L2Player _activeRequester;
    private long _requestExpireTime = 0;
    private L2Request _request;
    private L2ItemInstance _arrowItem;
    private L2ItemInstance _boltItem;

    // Used for protection after teleport
    private long _protectEndTime = 0;

    // Protects a char from agro mobs when getting up from fake death
    private long _recentFakeDeathEndTime = 0;

    /** The fists L2Weapon of the L2Player (used when no weapon is equipped) */
    private L2Weapon _fistsWeaponItem;

    private long _uptime;
    private final String _accountName;

    private Map<Integer, String> _chars;

    /** The table containing all L2RecipeList of the L2Player */
    private final Map<Integer, L2RecipeList> _dwarvenRecipeBook = new LazyFastMap<Integer, L2RecipeList>();
    private final Map<Integer, L2RecipeList> _commonRecipeBook = new LazyFastMap<Integer, L2RecipeList>();

    private int _mountType;
    private int _mountNpcId;
    private int _mountLevel;

    /** The current higher Expertise of the L2Player (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7)*/
    private int _expertiseIndex; // Index in EXPERTISE_LEVELS
    private int _weaponGradePenalty;
    private int _armorPenalty;

    private boolean _isEnchanting = false;
    private L2ItemInstance _activeEnchantItem = null;
    private L2ItemInstance _activeEnchantSupportItem = null;
    private L2ItemInstance _activeEnchantAttrItem = null;
    private long _activeEnchantTimestamp = 0;

    public static final byte ONLINE_STATE_LOADED = 0;
    public static final byte ONLINE_STATE_ONLINE = 1;
    public static final byte ONLINE_STATE_DELETED = 2;

    private byte _isOnline = ONLINE_STATE_LOADED;

    protected boolean _inventoryDisabled = false;

    protected Map<Integer, L2CubicInstance> _cubics = new LazyFastMap<Integer, L2CubicInstance>().setShared();

    /** The L2NpcInstance corresponding to the last Folk wich one the player talked. */
    private L2Npc _lastFolkNpc = null;

    private int _clanPrivileges = 0;

    /** L2Player's pledge class (knight, Baron, etc.)*/
    private int _pledgeClass = 0;

    /** Location before entering Observer Mode */
    private int _obsX;
    private int _obsY;
    private int _obsZ;
    private boolean _observerMode = false;

    /** TvT Instanced Engine parameters */
    public int _originalNameColorTvTi, _originalKarmaTvTi, _countTvTiKills = 0, _countTvTITeamKills = 0;
    public boolean _inEventTvTi = false, _isSitForcedTvTi = false, _joiningTvTi = false;

    public int _telemode = 0;

    /** new loto ticket **/
    private final int _loto[] = new int[5];
    /** new race ticket **/
    private final int _race[] = new int[2];

    private BlockList _blockList;
    private L2FriendList _friendList;

    private boolean _fishing = false;
    private int _fishx = 0;
    private int _fishy = 0;
    private int _fishz = 0;

    private final List<Integer> _transformAllowedSkills = new LazyFastList<Integer>();

    private int _team = 0;
    private int _wantsPeace = 0;

    // Death Penalty Buff Level
    private int _deathPenaltyBuffLevel = 0;

    // Self resurrect during siege
    private boolean _charmOfCourage = false;

    private boolean _hero = false;
    private boolean _noble = false;
    private boolean _inOlympiadMode = false;
    private boolean _olympiadStart = false;
    private int _olympiadGameId = -1;
    private int _olympiadSide = -1;
    private int _olympiadOpponentId = 0;
    public int olyBuff = 0;

    /** Duel */
    private int _duelState = Duel.DUELSTATE_NODUEL;
    private boolean _isInDuel = false;
    private int _duelId = 0;
    private int _noDuelReason = 0;

    /** ally with ketra or varka related vars*/
    private int _alliedVarkaKetra = 0;

    /**
     * IMO we don't need it, as we have FIFO packet execution.
     */
    private final ReentrantLock _subclassLock = new ReentrantLock();
    /** The list of sub-classes this character has. */
    private Map<Integer, SubClass> _subClasses;
    protected int _baseClass;
    protected int _activeClass;
    protected int _classIndex = 0;

    /** data for mounted pets */
    private int _controlItemId;
    private L2PetData _data;
    private int _curFeed;
    protected Future<?> _mountFeedTask;
    private ScheduledFuture<?> _dismountTask;

    private long _lastAccess;
    private int _boatId;

    private ScheduledFuture<?> _taskRentPet;
    private ScheduledFuture<?> _taskWater;
    private L2BoatInstance _boat;
    private Point3D _inBoatPosition;

    /** Bypass validations */
    private List<String> _validBypass;
    private List<String> _validBypass2;

    private Forum _forumMail;
    private Forum _forumMemo;

    private L2Fishing _fishCombat;

    /** The number of evaluation points obtained by this player */
    private int _evalPoints;

    /** The number of evaluations this player can give */
    private int _evaluations;

    /** List of players this player already evaluated */
    private final List<Integer> _evaluated = new LazyFastList<Integer>();

    private boolean _inCrystallize;

    private boolean _inCraftMode;

    /** Store object used to summon the strider you are mounting **/
    private int _mountObjectID = 0;

    /** character VIP **/
    private boolean _charViP = false;

    private boolean _inJail = false;
    private long _jailTimer = 0;

    private boolean _maried = false;
    private int _partnerId = 0;
    private int _coupleId = 0;
    private boolean _maryrequest = false;
    private boolean _maryaccepted = false;

    private int _clientRevision = 0;

    private FactionMember _faction;

    /* Flag to disable equipment/skills while wearing formal wear **/
    private boolean _IsWearingFormalWear = false;

    private L2Transformation _transformation;

    private int _transformationId = 0;

    private L2StaticObjectInstance _throne;

    // Absorbed Souls
    private int _souls = 0;
    private ScheduledFuture<?> _soulTask = null;
    private int _lastSoulConsume = 0;

    // Force charges
    private int _charges = 0;
    private ScheduledFuture<?> _chargeTask = null;

    public int _fame = 0; // The Fame of this L2Player
    private ScheduledFuture<?> _fameTask;

    /** Vitality recovery task */
    private ScheduledFuture<?> _vitalityTask;

    private ScheduledFuture<?> _teleportWatchdog;

    // Id of the afro hair cut
    private int _afroId = 0;

    // Creation day
    private int _lastClaim;
    private Calendar _createdOn;

    private long _lastTargetChange;
    private int _lastTargetId;

    private boolean _illegalWaiting;

    private long _nextJumpTime;

    private class VitalityTask implements Runnable {
        private L2Player _player = null;

        public VitalityTask(L2Player player) {
            _player = player;
        }

        @Override
        public void run() {
            if (_player == null)
                return;

            if (!_player.isInsideZone(L2Zone.FLAG_PEACE))
                return;

            if (_player.getVitalityPoints() >= PlayerStat.MAX_VITALITY_POINTS)
                return;

            _player.updateVitalityPoints(Config.RATE_RECOVERY_VITALITY_PEACE_ZONE, false, false);
            _player.sendPacket(new ExVitalityPointInfo(getVitalityPoints()));
        }
    }

    @Override
    public final boolean isAllSkillsDisabled() {
        return super.isAllSkillsDisabled() || isTryingToSitOrStandup();
    }

    @Override
    public final boolean isAttackingDisabled() {
        return super.isAttackingDisabled() || isTryingToSitOrStandup();
    }

    @Override
    public boolean isInProtectedAction() {
        return super.isInProtectedAction() || isTryingToSitOrStandup();
    }

    /**
     * Create a new L2Player and add it in the characters table of the database.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Create a new L2Player with an account name </li>
     * <li>Set the name, the Hair Style, the Hair Color and  the Face type of the L2Player</li>
     * <li>Add the player in the characters table of the database</li><BR><BR>
     *
     * @param objectId Identifier of the object to initialized
     * @param template The L2PlayerTemplate to apply to the L2Player
     * @param accountName The name of the L2Player
     * @param name The name of the L2Player
     * @param hairStyle The hair style Identifier of the L2Player
     * @param hairColor The hair color Identifier of the L2Player
     * @param face The face type Identifier of the L2Player
     *
     * @return The L2Player added to the database or null
     *
     */
    public static L2Player create(int objectId, L2PlayerTemplate template, String accountName, String name,
            byte hairStyle, byte hairColor, byte face, boolean sex) {
        // Create a new L2Player with an account name
        PlayerAppearance app = new PlayerAppearance(face, hairColor, hairStyle, sex);
        L2Player player = new L2Player(objectId, template, accountName, app);

        // Set the name of the L2Player
        player.setName(name);

        // Set the base class ID to that of the actual class ID.
        player.setBaseClass(player.getClassId());

        // Kept for backwards compabitility.
        player.setNewbie(1);

        // Add the player in the characters table of the database
        boolean ok = player.createDb();

        if (!ok)
            return null;

        return player;
    }

    public String getAccountName() {
        if (getClient() == null)
            return _accountName;
        return getClient().getAccountName();
    }

    public int getRelation(L2Player target) {
        int result = 0;

        if (getClan() != null)
            result |= RelationChanged.RELATION_CLAN_MEMBER;

        if (isClanLeader())
            result |= RelationChanged.RELATION_LEADER;

        L2Party party = getParty();
        if (party != null && party == target.getParty()) {
            result |= RelationChanged.RELATION_HAS_PARTY;

            switch (party.getPartyMembers().indexOf(this)) {
            case 0:
                result |= RelationChanged.RELATION_PARTYLEADER; // 0x10
                break;
            case 1:
                result |= RelationChanged.RELATION_PARTY4; // 0x8
                break;
            case 2:
                result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2
                        + RelationChanged.RELATION_PARTY1; // 0x7
                break;
            case 3:
                result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2; // 0x6
                break;
            case 4:
                result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY1; // 0x5
                break;
            case 5:
                result |= RelationChanged.RELATION_PARTY3; // 0x4
                break;
            case 6:
                result |= RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x3
                break;
            case 7:
                result |= RelationChanged.RELATION_PARTY2; // 0x2
                break;
            case 8:
                result |= RelationChanged.RELATION_PARTY1; // 0x1
                break;
            }
        }

        if (getSiegeState() != SIEGE_STATE_NOT_INVOLVED) {
            result |= RelationChanged.RELATION_INSIEGE;
            if (getSiegeState() != target.getSiegeState())
                result |= RelationChanged.RELATION_ENEMY;
            else
                result |= RelationChanged.RELATION_ALLY;
            if (getSiegeState() == SIEGE_STATE_ATTACKER)
                result |= RelationChanged.RELATION_ATTACKER;
        }

        if (getClan() != null && target.getClan() != null) {
            if (target.getSubPledgeType() != L2Clan.SUBUNIT_ACADEMY && getSubPledgeType() != L2Clan.SUBUNIT_ACADEMY
                    && target.getClan().isAtWarWith(getClan().getClanId())) {
                result |= RelationChanged.RELATION_1SIDED_WAR;
                if (getClan().isAtWarWith(target.getClan().getClanId()))
                    result |= RelationChanged.RELATION_MUTUAL_WAR;
            }
        }
        return result;
    }

    public Map<Integer, String> getAccountChars() {
        if (_chars == null) {
            Connection con = null;
            try {
                con = L2DatabaseFactory.getInstance().getConnection();

                // Retrieve the name and ID of the other characters assigned to this account.
                PreparedStatement statement = con.prepareStatement(
                        "SELECT charId, char_name FROM characters WHERE account_name=? AND charId<>?");
                statement.setString(1, getAccountName());
                statement.setInt(2, getObjectId());
                ResultSet rset = statement.executeQuery();

                while (rset.next()) {
                    if (_chars == null)
                        _chars = new HashMap<Integer, String>();

                    _chars.put(rset.getInt("charId"), rset.getString("char_name"));
                }

                rset.close();
                statement.close();
            } catch (SQLException e) {
                _log.warn("", e);
            } finally {
                L2DatabaseFactory.close(con);
            }

            if (_chars == null)
                _chars = L2Collections.emptyMap();
        }

        return _chars;
    }

    private void initPlayerStatusUpdateValues() {
        _cpUpdateInterval = getMaxCp() / 352.0;
        _cpUpdateIncCheck = getMaxCp();
        _cpUpdateDecCheck = getMaxCp() - _cpUpdateInterval;
        _mpUpdateInterval = getMaxMp() / 352.0;
        _mpUpdateIncCheck = getMaxMp();
        _mpUpdateDecCheck = getMaxMp() - _mpUpdateInterval;
    }

    /**
     * Constructor of L2Player (use L2Creature constructor).<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Call the L2Creature constructor to create an empty _skills slot and copy basic Calculator set to this L2Player </li>
     * <li>Set the name of the L2Player</li><BR><BR>
     *
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method SET the level of the L2Player to 1</B></FONT><BR><BR>
     *
     * @param objectId Identifier of the object to initialized
     * @param template The L2PlayerTemplate to apply to the L2Player
     * @param accountName The name of the account including this L2Player
     *
     */
    private L2Player(int objectId, L2PlayerTemplate template, String accountName, PlayerAppearance app) {
        super(objectId, template);
        getKnownList(); // Init knownlist
        getStat(); // Init stats
        getStatus(); // Init status
        super.initCreatureStatusUpdateValues();
        initPlayerStatusUpdateValues();

        _accountName = accountName;
        app.setOwner(this);
        _appearance = app;

        // Create an AI
        getAI();

        // Retrieve from the database all items of this L2Player and add them to _inventory
        getInventory().restore();
        if (!Config.WAREHOUSE_CACHE)
            getWarehouse();
        getFreight();
        startVitalityTask();
    }

    @Override
    protected CreatureKnownList initKnownList() {
        return new PlayerKnownList(this);
    }

    @Override
    public final PlayerKnownList getKnownList() {
        return (PlayerKnownList) _knownList;
    }

    @Override
    protected ICreatureView initView() {
        return new PlayerView(this);
    }

    @Override
    public PlayerView getView() {
        return (PlayerView) _view;
    }

    @Override
    protected CreatureStat initStat() {
        return new PlayerStat(this);
    }

    @Override
    public final PlayerStat getStat() {
        return (PlayerStat) _stat;
    }

    @Override
    protected CreatureStatus initStatus() {
        return new PlayerStatus(this);
    }

    @Override
    public final PlayerStatus getStatus() {
        return (PlayerStatus) _status;
    }

    @Override
    protected PlayerEffects initEffects() {
        return new PlayerEffects(this);
    }

    @Override
    public PlayerEffects getEffects() {
        return (PlayerEffects) _effects;
    }

    public final PlayerAppearance getAppearance() {
        return _appearance;
    }

    @Override
    public void setTitle(String value) {
        if (Config.FACTION_ENABLED) {
            if (FactionManager.getInstance().getFactionTitles().contains(value.toLowerCase()) && !value.isEmpty()) {
                sendMessage("Title protected by Faction System");
                return;
            }
        }

        if (value.length() > 16)
            value = value.substring(0, 15);

        super.setTitle(value);
    }

    /**
     * Return the base L2PlayerTemplate link to the L2Player.<BR><BR>
     */
    public final L2PlayerTemplate getBaseTemplate() {
        return CharTemplateTable.getInstance().getTemplate(_baseClass);
    }

    /** Return the L2PlayerTemplate link to the L2Player. */
    @Override
    public final L2PlayerTemplate getTemplate() {
        return (L2PlayerTemplate) super.getTemplate();
    }

    public void setTemplate(ClassId newclass) {
        super.setTemplate(CharTemplateTable.getInstance().getTemplate(newclass));
    }

    @Override
    protected L2CreatureAI initAI() {
        return new L2PlayerAI(new L2Player.AIAccessor());
    }

    /** Return the Level of the L2Player. */
    @Override
    public final int getLevel() {
        return getStat().getLevel();
    }

    /**
     * Return the _newbie rewards state of the L2Player.<BR><BR>
     */
    public int getNewbie() {
        return _newbie;
    }

    /**
     * Set the _newbie rewards state of the L2Player.<BR><BR>
     *
     * @param newbieRewards The Identifier of the _newbie state<BR><BR>
     *
     */
    public void setNewbie(int newbieRewards) {
        _newbie = newbieRewards;
    }

    public void setBaseClass(int baseClass) {
        _baseClass = baseClass;
    }

    public void setBaseClass(ClassId classId) {
        _baseClass = classId.ordinal();
    }

    public boolean isInStoreMode() {
        return (getPrivateStoreType() > 0);
    }

    public boolean isInCraftMode() {
        return _inCraftMode;
    }

    public void isInCraftMode(boolean b) {
        _inCraftMode = b;
    }

    /**
     * Return a table containing all Common L2Recipe of the L2Player.<BR><BR>
     */
    public L2RecipeList[] getCommonRecipeBook() {
        return _commonRecipeBook.values().toArray(new L2RecipeList[_commonRecipeBook.values().size()]);
    }

    /**
     * Return a table containing all Dwarf L2Recipe of the L2Player.<BR><BR>
     */
    public L2RecipeList[] getDwarvenRecipeBook() {
        return _dwarvenRecipeBook.values().toArray(new L2RecipeList[_dwarvenRecipeBook.values().size()]);
    }

    /**
     * Add a new L2Recipe to the table _commonrecipebook containing all L2Recipe of the L2Player <BR><BR>
     *
     * @param recipe The L2RecipeList to add to the _recipebook
     * @param saveToDb true to save infos into the DB
     */
    public void registerCommonRecipeList(L2RecipeList recipe, boolean saveToDb) {
        _commonRecipeBook.put(recipe.getId(), recipe);

        if (saveToDb)
            insertNewRecipeData(recipe.getId(), false);
    }

    /**
     * Add a new L2Recipe to the table _recipebook containing all L2Recipe of the L2Player <BR><BR>
     *
     * @param recipe The L2Recipe to add to the _recipebook
     * @param saveToDb true to save infos into the DB
     */
    public void registerDwarvenRecipeList(L2RecipeList recipe, boolean saveToDb) {
        _dwarvenRecipeBook.put(recipe.getId(), recipe);

        if (saveToDb)
            insertNewRecipeData(recipe.getId(), true);
    }

    /**
     * @param recipeId The Identifier of the L2Recipe to check in the player's recipe books
     *
     * @return
     * <b>TRUE</b> if player has the recipe on Common or Dwarven Recipe book else returns <b>FALSE</b>
     */
    public boolean hasRecipeList(int recipeId) {
        if (_dwarvenRecipeBook.containsKey(recipeId))
            return true;
        else
            return _commonRecipeBook.containsKey(recipeId);
    }

    /**
     * Tries to remove a L2Recipe from the table _DwarvenRecipeBook or from table _CommonRecipeBook, those table contain all L2Recipe of the L2Player <BR><BR>
     *
     * @param recipeId The Identifier of the L2Recipe to remove from the _recipebook
     *
     */
    public void unregisterRecipeList(int recipeId) {
        if (_dwarvenRecipeBook.remove(recipeId) != null)
            deleteRecipeData(recipeId, true);
        else if (_commonRecipeBook.remove(recipeId) != null)
            deleteRecipeData(recipeId, false);
        else
            _log.warn("Attempted to remove unknown RecipeList: " + recipeId);

        L2ShortCut[] allShortCuts = getAllShortCuts();
        for (L2ShortCut sc : allShortCuts) {
            if (sc != null && sc.getId() == recipeId && sc.getType() == L2ShortCut.TYPE_RECIPE)
                deleteShortCut(sc.getSlot(), sc.getPage());
        }
    }

    private void insertNewRecipeData(int recipeId, boolean isDwarf) {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con.prepareStatement(
                    "INSERT INTO character_recipebook (charId, id, classIndex, type) values(?,?,?,?)");
            statement.setInt(1, getObjectId());
            statement.setInt(2, recipeId);
            statement.setInt(3, isDwarf ? _classIndex : 0);
            statement.setInt(4, isDwarf ? 1 : 0);
            statement.execute();
            statement.close();
        } catch (SQLException e) {
            _log.error("SQL exception while inserting recipe: " + recipeId + " from character " + getObjectId(), e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    private void deleteRecipeData(int recipeId, boolean isDwarf) {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con
                    .prepareStatement("DELETE FROM character_recipebook WHERE charId=? AND id=? AND classIndex=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, recipeId);
            statement.setInt(3, isDwarf ? _classIndex : 0);
            statement.execute();
            statement.close();
        } catch (SQLException e) {
            _log.error("SQL exception while deleting recipe: " + recipeId + " from character " + getObjectId(), e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    /**
     * Returns the Id for the last talked quest NPC.<BR><BR>
     */
    public int getLastQuestNpcObject() {
        return _questNpcObject;
    }

    public void setLastQuestNpcObject(int npcId) {
        _questNpcObject = npcId;
    }

    /**
     * Return the QuestState object corresponding to the quest name.<BR><BR>
     *
     * @param quest The name of the quest
     *
     */
    public QuestState getQuestState(String quest) {
        return _quests.get(quest);
    }

    /**
     * Add a QuestState to the table _quest containing all quests began by the L2Player.<BR><BR>
     *
     * @param qs The QuestState to add to _quest
     *
     */
    public void setQuestState(QuestState qs) {
        _quests.put(qs.getQuestName(), qs);
    }

    /**
     * Remove a QuestState from the table _quest containing all quests began by the L2Player.<BR><BR>
     *
     * @param quest The name of the quest
     *
     */
    public void delQuestState(String quest) {
        _quests.remove(quest);
    }

    private QuestState[] addToQuestStateArray(QuestState[] questStateArray, QuestState state) {
        int len = questStateArray.length;
        QuestState[] tmp = new QuestState[len + 1];
        System.arraycopy(questStateArray, 0, tmp, 0, len);
        tmp[len] = state;
        return tmp;
    }

    /**
     * Return a table containing all Quest in progress from the table _quests.<BR><BR>
     */
    public Quest[] getAllActiveQuests() {
        ArrayBunch<Quest> quests = new ArrayBunch<Quest>();

        for (QuestState qs : _quests.values()) {
            if (qs == null)
                continue;

            int questId = qs.getQuest().getQuestIntId();
            if ((questId > 19999) || (questId < 1))
                continue;

            if (!qs.isStarted() && !Config.DEVELOPER)
                continue;

            quests.add(qs.getQuest());
        }

        return quests.moveToArray(new Quest[quests.size()]);
    }

    /**
     * Return a table containing all QuestState to modify after a L2Attackable killing.<BR><BR>
     *
     * @param npc The Identifier of the L2Attackable attacked
     *
     */
    public QuestState[] getQuestsForAttacks(L2NpcInstance npc) {
        // Create a QuestState table that will contain all QuestState to modify
        QuestState[] states = null;

        // Go through the QuestState of the L2Player quests
        for (Quest quest : npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_ATTACK)) {
            // Check if the Identifier of the L2Attackable attck is needed for the current quest
            if (getQuestState(quest.getName()) != null) {
                // Copy the current L2Player QuestState in the QuestState table
                if (states == null)
                    states = new QuestState[] { getQuestState(quest.getName()) };
                else
                    states = addToQuestStateArray(states, getQuestState(quest.getName()));
            }
        }

        // Return a table containing all QuestState to modify
        return states;
    }

    /**
     * Return a table containing all QuestState to modify after a L2Attackable killing.<BR><BR>
     *
     * @param npc The Identifier of the L2Attackable killed
     *
     */
    public QuestState[] getQuestsForKills(L2NpcInstance npc) {
        // Create a QuestState table that will contain all QuestState to modify
        QuestState[] states = null;

        // Go through the QuestState of the L2Player quests
        for (Quest quest : npc.getTemplate().getEventQuests(Quest.QuestEventType.ON_KILL)) {
            // Check if the Identifier of the L2Attackable killed is needed for the current quest
            if (getQuestState(quest.getName()) != null) {
                // Copy the current L2Player QuestState in the QuestState table
                if (states == null)
                    states = new QuestState[] { getQuestState(quest.getName()) };
                else
                    states = addToQuestStateArray(states, getQuestState(quest.getName()));
            }
        }

        // Return a table containing all QuestState to modify
        return states;
    }

    /**
     * Return a table containing all QuestState from the table _quests in which the L2Player must talk to the NPC.<BR><BR>
     *
     * @param npcId The Identifier of the NPC
     *
     */
    public QuestState[] getQuestsForTalk(int npcId) {
        // Create a QuestState table that will contain all QuestState to modify
        QuestState[] states = null;

        // Go through the QuestState of the L2Player quests
        Quest[] quests = NpcTable.getInstance().getTemplate(npcId).getEventQuests(Quest.QuestEventType.ON_TALK);
        if (quests != null) {
            for (Quest quest : quests) {
                // Copy the current L2Player QuestState in the QuestState table
                if (quest != null) {
                    // Copy the current L2Player QuestState in the QuestState table
                    if (getQuestState(quest.getName()) != null) {
                        if (states == null)
                            states = new QuestState[] { getQuestState(quest.getName()) };
                        else
                            states = addToQuestStateArray(states, getQuestState(quest.getName()));
                    }
                }
            }
        }

        // Return a table containing all QuestState to modify
        return states;
    }

    public QuestState processQuestEvent(String quest, String event) {
        QuestState retval = null;
        if (event == null)
            event = "";
        QuestState qs = getQuestState(quest);
        if (qs == null && event.isEmpty())
            return retval;
        if (qs == null) {
            Quest q = QuestManager.getInstance().getQuest(quest);
            if (q == null)
                return retval;
            qs = q.newQuestState(this);
        }
        if (qs != null) {
            if (getLastQuestNpcObject() > 0) {
                L2Object object = getKnownList().getKnownObject(getLastQuestNpcObject());
                if (object instanceof L2Npc && isInsideRadius(object, L2Npc.INTERACTION_DISTANCE, false, false)) {
                    L2Npc npc = (L2Npc) object;
                    QuestState[] states = getQuestsForTalk(npc.getNpcId());

                    if (states != null) {
                        for (QuestState state : states) {
                            if ((state.getQuest().getQuestIntId() == qs.getQuest().getQuestIntId()))// && !qs.isCompleted())
                            {
                                if (qs.getQuest().notifyEvent(event, npc, this))
                                    showQuestWindow(quest, State.getStateName(qs.getState()));

                                retval = qs;
                            }
                        }
                        sendPacket(new QuestList(this));
                    }
                }
            }
        }

        return retval;
    }

    /**
     * FIXME: move this from L2Player, there is no reason to have this here
     * @param questId
     * @param stateId
     */
    private void showQuestWindow(String questId, String stateId) {
        String path = "data/scripts/quests/" + questId + "/" + stateId + ".htm";
        String content = HtmCache.getInstance().getHtm(path);

        if (content != null) {
            if (_log.isDebugEnabled())
                _log.debug(
                        "Showing quest window for quest " + questId + " state " + stateId + " html path: " + path);

            NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
            npcReply.setHtml(content);
            sendPacket(npcReply);
        }

        sendPacket(ActionFailed.STATIC_PACKET);
    }

    /** List of all QuestState instance that needs to be notified of this L2Player's or its pet's death */
    private final List<QuestState> _NotifyQuestOfDeathList = new LazyFastList<QuestState>();

    /**
     * Add QuestState instance that is to be notified of L2Player's death.<BR>
     * <BR>
     *
     * @param qs The QuestState that subscribe to this event
     */
    public void addNotifyQuestOfDeath(QuestState qs) {
        if (qs == null || _NotifyQuestOfDeathList.contains(qs))
            return;

        _NotifyQuestOfDeathList.add(qs);
    }

    /**
     * Remove QuestState instance that is to be notified of L2Player's death.<BR>
     * <BR>
     *
     * @param qs The QuestState that subscribe to this event
     */
    public void removeNotifyQuestOfDeath(QuestState qs) {
        if (qs == null || !_NotifyQuestOfDeathList.contains(qs))
            return;

        _NotifyQuestOfDeathList.remove(qs);
    }

    /**
     * Return a list of QuestStates which registered for notify of death of this L2Player.<BR>
     * <BR>
     */
    public final List<QuestState> getNotifyQuestOfDeath() {
        return _NotifyQuestOfDeathList;
    }

    private ShortCuts getShortCuts() {
        if (_shortCuts == null)
            _shortCuts = new ShortCuts(this);

        return _shortCuts;
    }

    /**
     * Return a table containing all L2ShortCut of the L2Player.<BR><BR>
     */
    public L2ShortCut[] getAllShortCuts() {
        return getShortCuts().getAllShortCuts();
    }

    /**
     * Add a L2shortCut to the L2Player shortCuts<BR><BR>
     */
    public void registerShortCut(L2ShortCut shortcut) {
        getShortCuts().registerShortCut(shortcut);
    }

    /**
     * Delete the L2ShortCut corresponding to the position (page-slot) from the L2Player shortCuts.<BR><BR>
     */
    public void deleteShortCut(int slot, int page) {
        getShortCuts().deleteShortCut(slot, page);
    }

    /**
     * Add a L2Macro to the L2Player macroses<BR><BR>
     */
    public void registerMacro(L2Macro macro) {
        getMacroses().registerMacro(macro);
    }

    /**
     * Delete the L2Macro corresponding to the Identifier from the L2Player macroses.<BR><BR>
     */
    public void deleteMacro(int id) {
        getMacroses().deleteMacro(id);
    }

    /**
     * Return all L2Macro of the L2Player.<BR><BR>
     */
    public MacroList getMacroses() {
        if (_macroses == null)
            _macroses = new MacroList(this);

        return _macroses;
    }

    /**
     * Set the siege state of the L2Player.<BR><BR>
     * 1 = attacker, 2 = defender, 0 = not involved
     */
    public void setSiegeState(byte siegeState) {
        _siegeState = siegeState;
        broadcastRelationChanged();
    }

    /**
     * Get the siege state of the L2Player.<BR><BR>
     * 1 = attacker, 2 = defender, 0 = not involved
     */
    public byte getSiegeState() {
        return _siegeState;
    }

    public static final byte SIEGE_STATE_NOT_INVOLVED = 0;
    public static final byte SIEGE_STATE_ATTACKER = 1;
    public static final byte SIEGE_STATE_DEFENDER = 2;

    @Override
    public boolean revalidateZone(boolean force) {
        if (!super.revalidateZone(force))
            return false;

        if (Config.ALLOW_WATER)
            checkWaterState();

        if (isInsideZone(L2Zone.FLAG_SIEGE)) {
            setLastCompassZone(ExSetCompassZoneCode.SIEGE_WAR);
        } else if (isInsideZone(L2Zone.FLAG_PVP)) {
            setLastCompassZone(ExSetCompassZoneCode.PVP);
        } else if (isIn7sDungeon()) {
            setLastCompassZone(ExSetCompassZoneCode.SEVEN_SIGNS);
        } else if (isInsideZone(L2Zone.FLAG_PEACE)) {
            setLastCompassZone(ExSetCompassZoneCode.PEACEFUL);
        } else {
            if (_lastCompassZone == ExSetCompassZoneCode.SIEGE_WAR.getZoneCode())
                updatePvPStatus();

            setLastCompassZone(ExSetCompassZoneCode.GENERAL);
        }

        return true;
    }

    private void setLastCompassZone(ExSetCompassZoneCode packet) {
        if (_lastCompassZone == packet.getZoneCode())
            return;

        _lastCompassZone = packet.getZoneCode();
        sendPacket(packet);
    }

    /**
     * Return True if the L2Player can Craft Dwarven Recipes.<BR><BR>
     */
    public boolean hasDwarvenCraft() {
        return getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN) >= 1;
    }

    public int getDwarvenCraft() {
        return getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN);
    }

    /**
     * Return True if the L2Player can Craft Dwarven Recipes.<BR><BR>
     */
    public boolean hasCommonCraft() {
        return getSkillLevel(L2Skill.SKILL_CREATE_COMMON) >= 1;
    }

    public int getCommonCraft() {
        return getSkillLevel(L2Skill.SKILL_CREATE_COMMON);
    }

    /**
     * Return the PK counter of the L2Player.<BR><BR>
     */
    public int getPkKills() {
        return _pkKills;
    }

    /**
     * Set the PK counter of the L2Player.<BR><BR>
     */
    public void setPkKills(int pkKills) {
        _pkKills = pkKills;
    }

    /**
     * Return the _deleteTimer of the L2Player.<BR><BR>
     */
    public long getDeleteTimer() {
        return _deleteTimer;
    }

    /**
     * Set the _deleteTimer of the L2Player.<BR><BR>
     */
    public void setDeleteTimer(long deleteTimer) {
        _deleteTimer = deleteTimer;
    }

    /**
     * Return the current weight of the L2Player.<BR><BR>
     */
    @Override
    public int getCurrentLoad() {
        return getInventory().getTotalWeight();
    }

    /** @return the number of evaluation points obtained by player. */
    public int getEvalPoints() {
        return _evalPoints;
    }

    /**
     * Set the number of evaluation points obtained by player.
     * @param value Evaluation point count
     */
    public void setEvalPoints(int value) {
        _evalPoints = value;
    }

    /** @return the number of evaluations this player can give away. */
    public int getEvaluations() {
        return _evaluations;
    }

    /**
     * Set the number of available evaluations.
     * @param value New available evaluation count
     */
    public void setEvaluationCount(int value) {
        _evaluations = value;
    }

    /**
     * Add a player that has been evaluated by this player.
     * @param charId evaluated player's ID
     */
    public void addEvalRestriction(int charId) {
        _evaluated.add(charId);
    }

    /**   Removes all session evaluation restrictions for this player. */
    public void cleanEvalRestrictions() {
        _evaluated.clear();
    }

    /**
     * @param target Player being evaluated
     * @return whether this player hasn't evaluated the given player
     */
    public boolean canEvaluate(L2Player target) {
        return !_evaluated.contains(target.getObjectId());
    }

    /**
     * Set the exp of the L2Player before a death
     * @param exp
     */
    public void setExpBeforeDeath(long exp) {
        _expBeforeDeath = exp;
    }

    public long getExpBeforeDeath() {
        return _expBeforeDeath;
    }

    /**
     * Return the Karma of the L2Player.<BR><BR>
     */
    public int getKarma() {
        return _karma;
    }

    /**
     * Set the Karma of the L2Player and send a Server->Client packet StatusUpdate (broadcast).<BR><BR>
     */
    public void setKarma(int karma) {
        if (karma < 0)
            karma = 0;
        if (_karma == 0 && karma > 0) {
            for (L2Object object : getKnownList().getKnownObjects().values()) {
                if (!(object instanceof L2GuardInstance))
                    continue;

                if (((L2GuardInstance) object).getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
                    ((L2GuardInstance) object).getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null);
            }
        } else if (_karma > 0 && karma == 0) {
            // Send a Server->Client StatusUpdate packet with Karma and PvP Flag to the L2Player and all L2Player to inform (broadcast)
            setKarmaFlag(0);
        }
        _karma = karma;
        broadcastKarma();
        RegionBBSManager.changeCommunityBoard(this, PlayerStateOnCommunity.KARMA_OWNER);
    }

    /**
     * Return the max weight that the L2Player can load.<BR><BR>
     */
    @Override
    public int getMaxLoad() {
        return (int) (calcStat(Stats.MAX_LOAD, 69000, this, null) * Config.ALT_WEIGHT_LIMIT);
    }

    public int getWeaponPenalty() {
        return _weaponGradePenalty;
    }

    private void setWeaponPenalty(int level) {
        _weaponGradePenalty = level;
    }

    public int getArmorPenalty() {
        return _armorPenalty;
    }

    private void setArmorPenalty(int level) {
        _armorPenalty = level;
    }

    public boolean getExpertisePenalty() {
        return getWeaponPenalty() > 0 || getArmorPenalty() > 0;
    }

    @Override
    public int getWeightPenalty() {
        return _curWeightPenalty;
    }

    @Override
    public void setWeightPenalty(int value) {
        _curWeightPenalty = value;
    }

    public void refreshExpertisePenalty() {
        if (!Config.ALT_GRADE_PENALTY)
            return;

        int wpnPenalty = 0;
        int armorPenalty = 0;
        boolean sendUpdate = false;

        for (L2ItemInstance item : getInventory().getItems()) {
            if (!item.isEquipped() || item.getItem() == null
                    || item.getItem().getCrystalType() <= getExpertiseIndex())
                continue;

            if (item.getItem().getType2() == L2Item.TYPE2_WEAPON)
                wpnPenalty = (item.getItem().getCrystalType() - getExpertiseIndex());
            else
                armorPenalty++;
        }

        L2Skill skill = getKnownSkill(9009);
        int skillLevel = skill == null ? 0 : skill.getLevel();
        if (wpnPenalty > 4)
            wpnPenalty = 4;
        if (getWeaponPenalty() != wpnPenalty || skillLevel != wpnPenalty) {
            setWeaponPenalty(wpnPenalty);
            if (wpnPenalty > 0)
                super.addSkill(9009, wpnPenalty);
            else
                super.removeSkill(skill);
            sendUpdate = true;
        }

        skill = getKnownSkill(9010);
        skillLevel = skill == null ? 0 : skill.getLevel();
        if (armorPenalty > 5)
            armorPenalty = 5;
        if (getArmorPenalty() != armorPenalty || skillLevel != armorPenalty) {
            setArmorPenalty(armorPenalty);
            if (armorPenalty > 0)
                super.addSkill(9010, armorPenalty);
            else
                super.removeSkill(skill);
            sendUpdate = true;
        }

        if (sendUpdate)
            sendEtcStatusUpdate();
    }

    /**
     * Return the the PvP Kills of the L2Player (Number of player killed during a PvP).<BR><BR>
     */
    public int getPvpKills() {
        return _pvpKills;
    }

    /**
     * Set the the PvP Kills of the L2Player (Number of player killed during a PvP).<BR><BR>
     */
    public void setPvpKills(int pvpKills) {
        _pvpKills = pvpKills;
    }

    /**
     * Return the ClassId object of the L2Player contained in L2PlayerTemplate.<BR><BR>
     */
    public ClassId getClassId() {
        return getTemplate().getClassId();
    }

    public void academyCheck(int Id) {
        if ((getSubPledgeType() == -1 || getLvlJoinedAcademy() != 0) && _clan != null
                && ClassId.values()[Id].getLevel() == ClassLevel.Third) {
            if (getLvlJoinedAcademy() <= 16)
                _clan.setReputationScore(_clan.getReputationScore() + Config.JOIN_ACADEMY_MAX_REP_SCORE, true);
            else if (getLvlJoinedAcademy() >= 39)
                _clan.setReputationScore(_clan.getReputationScore() + Config.JOIN_ACADEMY_MIN_REP_SCORE, true);
            else
                _clan.setReputationScore(_clan.getReputationScore()
                        + (Config.JOIN_ACADEMY_MAX_REP_SCORE - (getLvlJoinedAcademy() - 16) * 20), true);
            setLvlJoinedAcademy(0);

            // Oust pledge member from the academy, cuz he has finished his 2nd class transfer
            SystemMessage msg = new SystemMessage(SystemMessageId.CLAN_MEMBER_S1_EXPELLED);
            msg.addString(getName());
            _clan.broadcastToOnlineMembers(msg);
            _clan.broadcastToOnlineMembers(new PledgeShowMemberListDelete(getName()));

            _clan.removeClanMember(getObjectId(), 0);
            sendPacket(SystemMessageId.ACADEMY_MEMBERSHIP_TERMINATED);
            // Receive graduation gift
            getInventory().addItem("Gift", 8181, 1, this, null); // Give academy circlet
        }
    }

    /**
     * Set the template of the L2Player.<BR><BR>
     *
     * @param Id The Identifier of the L2PlayerTemplate to set to the L2Player
     *
     */
    public void setClassId(int Id) {
        if (!_subclassLock.tryLock())
            return;

        try {
            academyCheck(Id);

            if (isSubClassActive()) {
                getSubClasses().get(_classIndex).setClassId(Id);
            }
            setClassTemplate(Id);

            setTarget(this);
            // Animation: Production - Clan / Transfer
            MagicSkillUse msu = new MagicSkillUse(this, this, 5103, 1, 1196, 0);
            broadcastPacket(msu);

            // Update class icon in party and clan
            broadcastClassIcon();

            rewardSkills();
        } finally {
            _subclassLock.unlock();
        }
    }

    public void useEquippableItem(L2ItemInstance item, boolean abortAttack) {
        // Equip or unEquip
        L2ItemInstance[] items = null;
        final boolean isEquiped = item.isEquipped();
        final int oldInvLimit = getInventoryLimit();
        SystemMessage sm = null;
        L2ItemInstance old = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LRHAND);
        if (old == null)
            old = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);

        int bodyPart = item.getItem().getBodyPart();
        if (isEquiped) {
            if (item.getEnchantLevel() > 0) {
                sm = new SystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
                sm.addNumber(item.getEnchantLevel());
                sm.addItemName(item);
            } else {
                sm = new SystemMessage(SystemMessageId.S1_DISARMED);
                sm.addItemName(item);
            }
            sendPacket(sm);

            switch (bodyPart) {
            case L2Item.SLOT_L_EAR:
            case L2Item.SLOT_LR_EAR:
            case L2Item.SLOT_L_FINGER:
            case L2Item.SLOT_LR_FINGER:
                getInventory().setPaperdollItem(item.getLocationSlot(), null);
                sendPacket(new ItemList(this, false));
            }

            // We can't unequip talisman by body slot
            if (bodyPart == L2Item.SLOT_DECO)
                items = getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot());
            else
                items = getInventory().unEquipItemInBodySlotAndRecord(bodyPart);
        } else {
            L2ItemInstance tempItem = getInventory().getPaperdollItemByL2ItemId(bodyPart);

            // Check if the item replaces a wear-item
            if (tempItem != null && tempItem.isWear()) {
                // Don't allow an item to replace a wear-item
                return;
            } else if (bodyPart == L2Item.SLOT_LR_HAND) {
                // This may not remove left OR right hand equipment
                tempItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
                if (tempItem != null && tempItem.isWear())
                    return;

                tempItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
                if (tempItem != null && tempItem.isWear())
                    return;
            } else if (bodyPart == L2Item.SLOT_FULL_ARMOR) {
                // This may not remove chest or leggings
                tempItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
                if (tempItem != null && tempItem.isWear())
                    return;

                tempItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS);
                if (tempItem != null && tempItem.isWear())
                    return;
            }

            if (item.getEnchantLevel() > 0) {
                sm = new SystemMessage(SystemMessageId.S1_S2_EQUIPPED);
                sm.addNumber(item.getEnchantLevel());
                sm.addItemName(item);
            } else {
                sm = new SystemMessage(SystemMessageId.S1_EQUIPPED);
                sm.addItemName(item);
            }
            sendPacket(sm);

            if ((bodyPart & L2Item.SLOT_HEAD) > 0 || (bodyPart & L2Item.SLOT_NECK) > 0
                    || (bodyPart & L2Item.SLOT_L_EAR) > 0 || (bodyPart & L2Item.SLOT_R_EAR) > 0
                    || (bodyPart & L2Item.SLOT_L_FINGER) > 0 || (bodyPart & L2Item.SLOT_R_FINGER) > 0
                    || (bodyPart & L2Item.SLOT_R_BRACELET) > 0 || (bodyPart & L2Item.SLOT_L_BRACELET) > 0
                    || (bodyPart & L2Item.SLOT_DECO) > 0) {
                // must be sent explicitly before IU
                sendPacket(new UserInfo(this));
            }

            items = getInventory().equipItemAndRecord(item);

            // Consume mana - will start a task if required; returns if item is not a shadow item
            item.decreaseMana(false);
        }

        refreshExpertisePenalty();

        InventoryUpdate iu = new InventoryUpdate();
        iu.addEquipItems(items);
        sendPacket(iu);

        // must be sent explicitly after IU
        sendPacket(new UserInfo(this));
        // send 3rd time, just like retail
        broadcastUserInfo();

        if (abortAttack)
            abortAttack();

        if (getInventoryLimit() != oldInvLimit)
            sendPacket(new ExStorageMaxCount(this));
    }

    /** Return the Experience of the L2Player. */
    public long getExp() {
        return getStat().getExp();
    }

    public void setActiveEnchantItem(L2ItemInstance scroll) {
        // If we dont have a Enchant Item, we are not enchanting.
        if (scroll == null) {
            setActiveEnchantSupportItem(null);
            setActiveEnchantTimestamp(0);
            setIsEnchanting(false);
        }

        _activeEnchantItem = scroll;
    }

    public L2ItemInstance getActiveEnchantItem() {
        return _activeEnchantItem;
    }

    public void setActiveEnchantSupportItem(L2ItemInstance item) {
        _activeEnchantSupportItem = item;
    }

    public L2ItemInstance getActiveEnchantSupportItem() {
        return _activeEnchantSupportItem;
    }

    public long getActiveEnchantTimestamp() {
        return _activeEnchantTimestamp;
    }

    public void setActiveEnchantTimestamp(long val) {
        _activeEnchantTimestamp = val;
    }

    public void setActiveEnchantAttrItem(L2ItemInstance stone) {
        _activeEnchantAttrItem = stone;
    }

    public L2ItemInstance getActiveEnchantAttrItem() {
        return _activeEnchantAttrItem;
    }

    public void setIsEnchanting(boolean val) {
        _isEnchanting = val;
    }

    public boolean isEnchanting() {
        return _isEnchanting;
    }

    /**
     * Set the fists weapon of the L2Player (used when no weapon is equipped).<BR><BR>
     *
     * @param weaponItem The fists L2Weapon to set to the L2Player
     *
     */
    public void setFistsWeaponItem(L2Weapon weaponItem) {
        _fistsWeaponItem = weaponItem;
    }

    /**
     * Return the fists weapon of the L2Player (used when no weapon is equipped).<BR><BR>
     */
    public L2Weapon getFistsWeaponItem() {
        return _fistsWeaponItem;
    }

    /**
     * Return the fists weapon of the L2Player Class (used when no weapon is equipped).<BR><BR>
     */
    public L2Weapon findFistsWeaponItem(int classId) {
        L2Weapon weaponItem = null;
        if ((classId >= 0x00) && (classId <= 0x09)) {
            // Human fighter fists
            L2Item temp = ItemTable.getInstance().getTemplate(246);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x0a) && (classId <= 0x11)) {
            // Human mage fists
            L2Item temp = ItemTable.getInstance().getTemplate(251);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x12) && (classId <= 0x18)) {
            // Elven fighter fists
            L2Item temp = ItemTable.getInstance().getTemplate(244);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x19) && (classId <= 0x1e)) {
            // Elven mage fists
            L2Item temp = ItemTable.getInstance().getTemplate(249);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x1f) && (classId <= 0x25)) {
            // Dark elven fighter fists
            L2Item temp = ItemTable.getInstance().getTemplate(245);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x26) && (classId <= 0x2b)) {
            // Dark elven mage fists
            L2Item temp = ItemTable.getInstance().getTemplate(250);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x2c) && (classId <= 0x30)) {
            // Orc fighter fists
            L2Item temp = ItemTable.getInstance().getTemplate(248);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x31) && (classId <= 0x34)) {
            // Orc mage fists
            L2Item temp = ItemTable.getInstance().getTemplate(252);
            weaponItem = (L2Weapon) temp;
        } else if ((classId >= 0x35) && (classId <= 0x39)) {
            // Dwarven fists
            L2Item temp = ItemTable.getInstance().getTemplate(247);
            weaponItem = (L2Weapon) temp;
        }

        return weaponItem;
    }

    /**
     * Give Expertise skill of this level and remove beginner Lucky skill.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Get the Level of the L2Player </li>
     * <li>If L2Player Level is 5, remove beginner Lucky skill </li>
     * <li>Add the Expertise skill corresponding to its Expertise level</li>
     * <li>Update the overloaded status of the L2Player</li><BR><BR>
     *
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T give other free skills (SP needed = 0)</B></FONT><BR><BR>
     *
     */
    public void rewardSkills() {
        // Get the Level of the L2Player
        int lvl = getLevel();

        // Remove beginner Lucky skill
        if (lvl > 9) {
            L2Skill skill = SkillTable.getInstance().getInfo(194, 1);
            skill = removeSkill(skill);

            if (_log.isDebugEnabled() && skill != null)
                _log.debug("removed skill 'Lucky' from " + getName());
        }

        // Calculate the current higher Expertise of the L2Player
        for (int i = 0; i < EXPERTISE_LEVELS.length; i++) {
            if (lvl >= EXPERTISE_LEVELS[i])
                setExpertiseIndex(i);
        }

        // Add the Expertise skill corresponding to its Expertise level
        if (getExpertiseIndex() > 0) {
            L2Skill skill = SkillTable.getInstance().getInfo(239, getExpertiseIndex());
            addSkill(skill);

            if (_log.isDebugEnabled())
                _log.debug("awarded " + getName() + " with new expertise.");

        } else {
            if (_log.isDebugEnabled())
                _log.debug("No skills awarded at lvl: " + lvl);
        }

        // Active skill dwarven craft
        if (getSkillLevel(1321) < 1 && getRace() == Race.Dwarf) {
            L2Skill skill = SkillTable.getInstance().getInfo(1321, 1);
            addSkill(skill);
        }

        // Active skill common craft
        if (getSkillLevel(1322) < 1) {
            L2Skill skill = SkillTable.getInstance().getInfo(1322, 1);
            addSkill(skill);
        }

        for (int i = 0; i < COMMON_CRAFT_LEVELS.length; i++) {
            if (lvl >= COMMON_CRAFT_LEVELS[i] && getSkillLevel(1320) < (i + 1)) {
                L2Skill skill = SkillTable.getInstance().getInfo(1320, (i + 1));
                addSkill(skill);
            }
        }

        // Auto-Learn skills if activated
        if (Config.ALT_AUTO_LEARN_SKILLS && !isCursedWeaponEquipped())
            sendMessage("You have learned " + SkillTreeTable.getInstance().giveAvailableSkills(this) + ".");

        if (isGM() && !hasSkill(7029)) {
            addSkill(SkillTable.getInstance().getInfo(7029, 4));
        }
    }

    /** Set the Experience value of the L2Player. */
    public void setExp(long exp) {
        getStat().setExp(exp);
    }

    /**
     * Regive all skills which aren't saved to database, like Noble, Hero, Clan Skills<BR><BR>
     *
     */
    public void regiveTemporarySkills() {
        // Do not call this on enterworld or char load

        // Add noble skills if noble
        if (isNoble())
            setNoble(true);

        // Add Hero skills if hero
        if (isHero())
            setHero(true);

        if (getClan() != null) {
            setPledgeClass(L2ClanMember.getCurrentPledgeClass(this));
            getClan().addSkillEffects(this, false);
            PledgeSkillList psl = new PledgeSkillList(getClan());
            sendPacket(psl);
            if (getClan().getLevel() >= Config.SIEGE_CLAN_MIN_LEVEL && isClanLeader())
                SiegeManager.getInstance().addSiegeSkills(this);
            enableResidentialSkills(true);
        }

        // Reload passive skills from armors / jewels / weapons
        getInventory().reloadEquippedItems();

        // Add Death Penalty Buff Level
        restoreDeathPenaltyBuffLevel();
    }

    public void enableResidentialSkills(boolean enable) {
        if (getClan() == null)
            return;

        if (enable) {
            if (getClan().getHasCastle() > 0)
                CastleManager.getInstance().getCastleByOwner(getClan()).giveResidentialSkills(this);

            if (getClan().getHasFort() > 0)
                FortManager.getInstance().getFortByOwner(getClan()).giveResidentialSkills(this);
        } else {
            if (getClan().getHasCastle() > 0)
                CastleManager.getInstance().getCastleByOwner(getClan()).removeResidentialSkills(this);
            if (getClan().getHasFort() > 0)
                FortManager.getInstance().getFortByOwner(getClan()).removeResidentialSkills(this);
        }
    }

    /**
     * Return the Race object of the L2Player.<BR><BR>
     */
    public Race getRace() {
        if (!isSubClassActive())
            return getTemplate().getRace();

        L2PlayerTemplate charTemp = CharTemplateTable.getInstance().getTemplate(_baseClass);
        return charTemp.getRace();
    }

    public L2Marker getRadar() {
        if (_radar == null)
            _radar = new L2Marker(this);

        return _radar;
    }

    /** Return the SP amount of the L2Player. */
    public int getSp() {
        return getStat().getSp();
    }

    /** Set the SP amount of the L2Player. */
    public void setSp(int sp) {
        getStat().setSp(sp);
    }

    /**
     * Return true if this L2Player is a clan leader in
     * ownership of the passed castle
     */
    public boolean isCastleLord(int castleId) {
        L2Clan clan = getClan();
        // Player has clan and is the clan leader, check the castle info
        if ((clan != null) && (clan.getLeader().getPlayerInstance() == this)) {
            // If the clan has a castle and it is actually the queried castle, return true
            Castle castle = CastleManager.getInstance().getCastleByOwner(clan);
            if ((castle != null) && (castle == CastleManager.getInstance().getCastleById(castleId)))
                return true;
        }
        return false;
    }

    /**
     * Return the Clan Identifier of the L2Player.<BR><BR>
     */
    public int getClanId() {
        return _clanId;
    }

    /**
     * Return the Clan Crest Identifier of the L2Player or 0.<BR><BR>
     */
    public int getClanCrestId() {
        if (_clan != null && _clan.hasCrest()) {
            return _clan.getCrestId();
        }
        return 0;
    }

    /**
     * @return The Clan CrestLarge Identifier or 0
     */
    public int getClanCrestLargeId() {
        if (_clan != null && _clan.hasCrestLarge()) {
            return _clan.getCrestLargeId();
        }
        return 0;
    }

    public long getClanJoinExpiryTime() {
        return _clanJoinExpiryTime;
    }

    public void setClanJoinExpiryTime(long time) {
        _clanJoinExpiryTime = time;
    }

    public long getClanCreateExpiryTime() {
        return _clanCreateExpiryTime;
    }

    public void setClanCreateExpiryTime(long time) {
        _clanCreateExpiryTime = time;
    }

    public void setOnlineTime(long time) {
        _onlineTime = time;
        _onlineBeginTime = System.currentTimeMillis();
    }

    /**
     * Return the PlayerInventory Inventory of the L2Player contained in _inventory.<BR><BR>
     */
    @Override
    public PlayerInventory getInventory() {
        if (_inventory == null)
            _inventory = new PlayerInventory(this);

        return _inventory;
    }

    /**
     * Delete a ShortCut of the L2Player shortCuts.<BR><BR>
     */
    public void removeItemFromShortCut(int objectId) {
        getShortCuts().deleteShortCutByObjectId(objectId);
    }

    /**
     * Return True if the L2Player is sitting.<BR><BR>
     */
    public boolean isSitting() {
        return _waitTypeSitting;
    }

    public void sitDown() {
        sitDown(true);
    }

    /**
     * Sit down the L2Player, set the AI Intention to AI_INTENTION_REST and send a Server->Client ChangeWaitType packet (broadcast)<BR><BR>
     */
    public void sitDown(boolean force) {
        if ((isCastingNow() || isCastingSimultaneouslyNow()) && !_relax) {
            sendMessage("Cannot sit while casting");
            return;
        }
        if (!(_waitTypeSitting || super.isAttackingDisabled() || isOutOfControl() || isImmobilized()
                || (!force && isTryingToSitOrStandup()))) {
            breakAttack();
            _waitTypeSitting = true;
            getAI().setIntention(CtrlIntention.AI_INTENTION_REST);
            broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING));
            _lastSitStandRequest = System.currentTimeMillis();
        }
    }

    public void standUp() {
        standUp(true);
    }

    /**
     * Stand up the L2Player, set the AI Intention to AI_INTENTION_IDLE and send a Server->Client ChangeWaitType packet (broadcast)<BR><BR>
     */
    public void standUp(boolean force) {
        if (!GlobalRestrictions.canStandUp(this))
            return;

        if (_waitTypeSitting && !isInStoreMode() && !isAlikeDead() && (!isTryingToSitOrStandup() || force)) {
            if (_relax) {
                setRelax(false);
                stopEffects(L2EffectType.RELAXING);
            }
            _waitTypeSitting = false;
            getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
            broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING));
            _lastSitStandRequest = System.currentTimeMillis();
        }
    }

    /**
     * Set the value of the _relax value. Must be True if using skill Relax and False if not.
     */
    public void setRelax(boolean val) {
        _relax = val;
    }

    /**
     * Return the PlayerWarehouse object of the L2Player.<BR><BR>
     */
    public PlayerWarehouse getWarehouse() {
        if (_warehouse == null) {
            _warehouse = new PlayerWarehouse(this);
            _warehouse.restore();
        }

        if (Config.WAREHOUSE_CACHE)
            WarehouseCacheManager.getInstance().add(this);

        return _warehouse;
    }

    /**
     * Free memory used by Warehouse
     */
    public void clearWarehouse() {
        if (_warehouse != null)
            _warehouse.deleteMe();
        _warehouse = null;
    }

    /**
     * Return the PcFreight object of the L2Player.<BR><BR>
     */
    public PlayerFreight getFreight() {
        if (_freight == null) {
            _freight = new PlayerFreight(this);
            _freight.restore();
        }

        return _freight;
    }

    /**
     * Free memory used by Freight
     */
    public void clearFreight() {
        if (_freight != null)
            _freight.deleteMe();
        _freight = null;
    }

    /**
     * Return deposited PcFreight object for the objectId
     * or create new if not exist
     */
    public PlayerFreight getDepositedFreight(int objectId) {
        if (_depositedFreight == null)
            _depositedFreight = new FastList<PlayerFreight>();
        else {
            for (PlayerFreight freight : _depositedFreight) {
                if (freight != null && freight.getOwnerId() == objectId)
                    return freight;
            }
        }

        PlayerFreight freight = new PlayerFreight(null);
        freight.doQuickRestore(objectId);
        _depositedFreight.add(freight);
        return freight;
    }

    /**
     * Clear memory used by deposited freight
     */
    public void clearDepositedFreight() {
        if (_depositedFreight == null)
            return;

        for (PlayerFreight freight : _depositedFreight) {
            if (freight != null) {
                try {
                    freight.deleteMe();
                } catch (Exception e) {
                    _log.fatal("clearDepositedFreight()", e);
                }
            }
        }
        _depositedFreight.clear();
        _depositedFreight = null;
    }

    /**
     * Return the Identifier of the L2Player.<BR><BR>
     */
    public int getCharId() {
        return _charId;
    }

    /**
     * Set the Identifier of the L2Player.<BR><BR>
     */
    public void setCharId(int charId) {
        _charId = charId;
    }

    /**
     * Return the Adena amount of the L2Player.<BR><BR>
     */
    public long getAdena() {
        return getInventory().getAdena();
    }

    /**
     * Return the Ancient Adena amount of the L2Player.<BR><BR>
     */
    public long getAncientAdena() {
        return getInventory().getAncientAdena();
    }

    /**
     * Add adena to Inventory of the L2Player and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param count : long Quantity of adena to be added
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     */
    public void addAdena(String process, long count, L2Object reference, boolean sendMessage) {
        if (count > 0) {
            getInventory().addAdena(process, count, this, reference);

            if (sendMessage) {
                SystemMessage sm = new SystemMessage(SystemMessageId.EARNED_S1_ADENA);
                sm.addItemNumber(count);
                sendPacket(sm);
            }

            // Send update packet
            getInventory().updateInventory(getInventory().getAdenaInstance());
        }
    }

    /**
     * Reduce adena in Inventory of the L2Player and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param count : long Quantity of adena to be reduced
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public boolean reduceAdena(String process, long count, L2Object reference, boolean sendMessage) {
        if (count > getAdena()) {
            if (sendMessage)
                sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
            return false;
        }

        if (count > 0) {
            L2ItemInstance adenaItem = getInventory().getAdenaInstance();
            getInventory().reduceAdena(process, count, this, reference);

            // Send update packet
            getInventory().updateInventory(adenaItem);

            if (sendMessage) {
                SystemMessage sm = new SystemMessage(SystemMessageId.S1_ADENA_DISAPPEARED);
                sm.addItemNumber(count);
                sendPacket(sm);
            }
        }

        return true;
    }

    /**
     * Add ancient adena to Inventory of the L2Player and send a Server->Client InventoryUpdate packet to the L2Player.
     *
     * @param process : String Identifier of process triggering this action
     * @param count : long Quantity of ancient adena to be added
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     */
    public void addAncientAdena(String process, long count, L2Object reference, boolean sendMessage) {
        if (sendMessage) {
            SystemMessage sm = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
            sm.addItemName(PlayerInventory.ANCIENT_ADENA_ID);
            sm.addItemNumber(count);
            sendPacket(sm);
        }

        if (count > 0) {
            getInventory().addAncientAdena(process, count, this, reference);
            getInventory().updateInventory(getInventory().getAncientAdenaInstance());
        }
    }

    /**
     * Reduce ancient adena in Inventory of the L2Player and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param count : long Quantity of ancient adena to be reduced
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public boolean reduceAncientAdena(String process, long count, L2Object reference, boolean sendMessage) {
        if (count > getAncientAdena()) {
            if (sendMessage)
                sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);

            return false;
        }

        if (count > 0) {
            getInventory().reduceAncientAdena(process, count, this, reference);
            getInventory().updateInventory(getInventory().getAncientAdenaInstance());
            if (sendMessage) {
                SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
                sm.addItemName(PlayerInventory.ANCIENT_ADENA_ID);
                sm.addItemNumber(count);
                sendPacket(sm);
            }
        }

        return true;
    }

    /**
     * Adds item to inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param item : L2ItemInstance to be added
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     */
    public L2ItemInstance addItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage,
            boolean UpdateIL) {
        if (item.getCount() > 0) {
            // Sends message to client if requested
            if (sendMessage) {
                if (item.getCount() > 1) {
                    SystemMessage sm = new SystemMessage(SystemMessageId.YOU_PICKED_UP_S1_S2);
                    sm.addItemName(item);
                    sm.addItemNumber(item.getCount());
                    sendPacket(sm);
                } else if (item.getEnchantLevel() > 0) {
                    SystemMessage sm = new SystemMessage(SystemMessageId.YOU_PICKED_UP_A_S1_S2);
                    sm.addNumber(item.getEnchantLevel());
                    sm.addItemName(item);
                    sendPacket(sm);
                } else {
                    SystemMessage sm = new SystemMessage(SystemMessageId.YOU_PICKED_UP_S1);
                    sm.addItemName(item);
                    sendPacket(sm);
                }
            }

            // Add the item to inventory
            L2ItemInstance newitem = getInventory().addItem(process, item, this, reference);

            // Do treatments after adding this item
            processAddItem(UpdateIL, newitem);
            return newitem;
        }
        return null;
    }

    /**
     * Adds item to Inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param itemId : int Item Identifier of the item to be added
     * @param count : long Quantity of items to be added
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     */
    public L2ItemInstance addItem(String process, int itemId, long count, L2Object reference, boolean sendMessage,
            boolean UpdateIL) {
        if (count > 0) {
            // Add the item to inventory
            L2ItemInstance newItem = getInventory().addItem(process, itemId, count, this, reference);

            // Sends message to client if requested
            if (sendMessage) {
                sendMessageForNewItem(newItem, count, process);
            }

            processAddItem(UpdateIL, newItem);
            return newItem;
        }
        return null;
    }

    /**
     * @param UpdateIL
     * @param newitem
     */
    private void processAddItem(boolean UpdateIL, L2ItemInstance newitem) {
        // If over capacity, drop the item
        if (!isGM() && !getInventory().validateCapacity(0) && newitem.isDropable()) {
            dropItem("InvDrop", newitem, null, true);
        }
        // Cursed Weapon
        else if (CursedWeaponsManager.getInstance().isCursed(newitem.getItemId())) {
            if (!CursedWeaponsManager.getInstance().activate(this, newitem))
                dropItem("CwDrop", newitem, null, true);
        }
        // Combat Flag
        else if (FortSiegeManager.getInstance().isCombat(newitem.getItemId())) {
            if (FortSiegeManager.getInstance().activateCombatFlag(this, newitem)) {
                Fort fort = FortManager.getInstance().getFort(this);
                if (fort != null)
                    fort.getSiege().announceToPlayer(new SystemMessage(SystemMessageId.C1_ACQUIRED_THE_FLAG),
                            getName());
            }
            //else // FIXME: i'm not sure about this
            //   dropItem("CombatFlagDrop", newitem, null, true);
        }

        // Auto use herbs - autoloot
        else if (newitem.getItemType() == L2EtcItemType.HERB) {
            ItemHandler.getInstance().useItem(newitem.getItemId(), this, newitem);
        }

        // Update current load as well
        if (UpdateIL) {
            StatusUpdate su = new StatusUpdate(getObjectId());
            su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
            sendPacket(su);
        }

        // Send inventory update packet
        if (!Config.FORCE_INVENTORY_UPDATE) {
            InventoryUpdate playerIU = new InventoryUpdate();
            playerIU.addItem(newitem);
            sendPacket(playerIU);
        } else
            sendPacket(new ItemList(this, false));
    }

    /**
     * @param item : L2ItemInstance Item Identifier of the item to be added
     * @param count : long Quantity of items to be added
     * @param process : String Identifier of process triggering this action
     */
    private void sendMessageForNewItem(L2ItemInstance item, long count, String process) {
        if (count > 1) {
            if (process.equalsIgnoreCase("sweep") || process.equalsIgnoreCase("Quest")) {
                SystemMessage sm = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
                sm.addItemName(item);
                sm.addItemNumber(count);
                sendPacket(sm);
            } else {
                SystemMessage sm = new SystemMessage(SystemMessageId.YOU_PICKED_UP_S1_S2);
                sm.addItemName(item);
                sm.addItemNumber(count);
                sendPacket(sm);
            }
        } else {
            if (process.equalsIgnoreCase("sweep") || process.equalsIgnoreCase("Quest")) {
                SystemMessage sm = new SystemMessage(SystemMessageId.EARNED_S1);
                sm.addItemName(item);
                sendPacket(sm);
            } else {
                SystemMessage sm = new SystemMessage(SystemMessageId.YOU_PICKED_UP_S1);
                sm.addItemName(item);
                sendPacket(sm);
            }
        }
    }

    public void addItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) {
        addItem(process, item, reference, sendMessage, true);
    }

    public void addItem(String process, int itemId, long count, L2Object reference, boolean sendMessage) {
        addItem(process, itemId, count, reference, sendMessage, true);
    }

    /**
     * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param item : L2ItemInstance to be destroyed
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public boolean destroyItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) {
        return this.destroyItem(process, item, item.getCount(), reference, sendMessage);
    }

    /**
     * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param item : L2ItemInstance to be destroyed
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public boolean destroyItem(String process, L2ItemInstance item, long count, L2Object reference,
            boolean sendMessage) {
        item = getInventory().destroyItem(process, item, count, this, reference);

        if (item == null) {
            if (sendMessage) {
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
            }
            return false;
        }

        // Send inventory update packet
        if (!Config.FORCE_INVENTORY_UPDATE) {
            InventoryUpdate playerIU = new InventoryUpdate();
            playerIU.addItem(item);
            sendPacket(playerIU);
        } else {
            sendPacket(new ItemList(this, false));
        }

        // Update current load as well
        StatusUpdate su = new StatusUpdate(getObjectId());
        su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
        sendPacket(su);

        // Sends message to client if requested
        if (sendMessage) {
            SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
            sm.addItemName(item);
            sm.addItemNumber(count);
            sendPacket(sm);
        }

        return true;
    }

    /**
     * Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param objectId : int Item Instance identifier of the item to be destroyed
     * @param count : long Quantity of items to be destroyed
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    @Override
    public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage) {
        L2ItemInstance item = getInventory().getItemByObjectId(objectId);
        if (item == null) {
            if (sendMessage)
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
            return false;
        }

        return destroyItem(process, item, count, reference, sendMessage);
    }

    /**
     * Destroys shots from inventory without logging and only occasional saving to database.
     * Sends a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param objectId : int Item Instance identifier of the item to be destroyed
     * @param count : long Quantity of items to be destroyed
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public boolean destroyItemWithoutTrace(String process, int objectId, long count, L2Object reference,
            boolean sendMessage) {
        L2ItemInstance item = getInventory().getItemByObjectId(objectId);

        if (item == null || item.getCount() < count) {
            if (sendMessage)
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
            return false;
        }

        return destroyItem(null, item, count, reference, sendMessage);
    }

    /**
     * Destroy item from inventory by using its <B>itemId</B> and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param itemId : int Item identifier of the item to be destroyed
     * @param count : long Quantity of items to be destroyed
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    @Override
    public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference,
            boolean sendMessage) {
        L2ItemInstance item = getInventory().getItemByItemId(itemId);
        if (item == null || item.getCount() < count
                || getInventory().destroyItemByItemId(process, itemId, count, this, reference) == null) {
            if (sendMessage)
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);

            return false;
        }

        // Send inventory update packet
        getInventory().updateInventory(item);

        // Sends message to client if requested
        if (sendMessage) {
            SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
            sm.addItemName(item);
            sm.addItemNumber(count);
            sendPacket(sm);
        }
        return true;
    }

    /**
     * Destroy all weared items from inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public void destroyWearedItems(String process, L2Object reference, boolean sendMessage) {

        // Go through all Items of the inventory
        for (L2ItemInstance item : getInventory().getItems()) {
            // Check if the item is a Try On item in order to remove it
            if (item.isWear()) {
                if (item.isEquipped())
                    getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot());

                if (getInventory().destroyItem(process, item, this, reference) == null) {
                    _log.warn("Player " + getName() + " can't destroy weared item: " + item.getName() + "[ "
                            + item.getObjectId() + " ]");
                    continue;
                }

                // Send an Unequipped Message in system window of the player for each Item
                SystemMessage sm = new SystemMessage(SystemMessageId.S1_DISARMED);
                sm.addItemName(item);
                sendPacket(sm);

            }
        }

        // Send the ItemList Server->Client Packet to the player in order to refresh its Inventory
        ItemList il = new ItemList(getInventory().getItems(), true);
        sendPacket(il);

        // Send a Server->Client packet UserInfo to this L2Player and CharInfo to all L2Player in its _knownPlayers
        broadcastUserInfo();

        // Sends message to client if requested
        sendMessage("Trying-on mode has ended.");

    }

    /**
     * Transfers item to another ItemContainer and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param objectId : int Item Identifier of the item to be transfered
     * @param count : long Quantity of items to be transfered
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @return L2ItemInstance corresponding to the new item or the updated item in inventory
     */
    public L2ItemInstance transferItem(String process, int objectId, long count, Inventory target,
            L2Object reference) {
        L2ItemInstance oldItem = checkItemManipulation(objectId, count, "transfer");
        if (oldItem == null)
            return null;
        L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, this, reference);
        if (newItem == null)
            return null;

        // Send inventory update packet
        if (!Config.FORCE_INVENTORY_UPDATE) {
            InventoryUpdate playerIU = new InventoryUpdate();

            if (oldItem.getCount() > 0 && oldItem != newItem)
                playerIU.addModifiedItem(oldItem);
            else
                playerIU.addRemovedItem(oldItem);

            sendPacket(playerIU);
        } else
            sendPacket(new ItemList(this, false));

        // Update current load as well
        StatusUpdate playerSU = new StatusUpdate(getObjectId());
        playerSU.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
        sendPacket(playerSU);

        // Send target update packet
        if (target instanceof PlayerInventory) {
            L2Player targetPlayer = ((PlayerInventory) target).getOwner();

            if (!Config.FORCE_INVENTORY_UPDATE) {
                InventoryUpdate playerIU = new InventoryUpdate();

                if (newItem.getCount() > count)
                    playerIU.addModifiedItem(newItem);
                else
                    playerIU.addNewItem(newItem);

                targetPlayer.sendPacket(playerIU);
            } else
                targetPlayer.sendPacket(new ItemList(targetPlayer, false));

            // Update current load as well
            playerSU = new StatusUpdate(targetPlayer.getObjectId());
            playerSU.addAttribute(StatusUpdate.CUR_LOAD, targetPlayer.getCurrentLoad());
            targetPlayer.sendPacket(playerSU);
        } else if (target instanceof PetInventory) {
            PetInventoryUpdate petIU = new PetInventoryUpdate();

            if (newItem.getCount() > count)
                petIU.addModifiedItem(newItem);
            else
                petIU.addNewItem(newItem);

            ((PetInventory) target).getOwner().getOwner().sendPacket(petIU);
        }

        return newItem;
    }

    /**
     * Drop item from inventory and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param item : L2ItemInstance to be dropped
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return boolean informing if the action was successfull
     */
    public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) {
        item = getInventory().dropItem(process, item, this, reference);

        if (item == null) {
            if (sendMessage)
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
            return false;
        }

        item.dropMe(this, getX() + Rnd.get(50) - 25, getY() + Rnd.get(50) - 25, getZ() + 20);

        if (Config.DESTROY_DROPPED_PLAYER_ITEM) {
            ItemsAutoDestroyManager.tryAddItem(item);

            if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM))
                item.setProtected(false);
            else
                item.setProtected(true);
        } else
            item.setProtected(true);

        // Send inventory update packet
        getInventory().updateInventory(item);

        // Sends message to client if requested
        if (sendMessage) {
            SystemMessage sm = new SystemMessage(SystemMessageId.YOU_DROPPED_S1);
            sm.addItemName(item);
            sendPacket(sm);
        }

        return true;
    }

    /**
     * Drop item from inventory by using its <B>objectID</B> and send a Server->Client InventoryUpdate packet to the L2Player.
     * @param process : String Identifier of process triggering this action
     * @param objectId : int Item Instance identifier of the item to be dropped
     * @param count : long Quantity of items to be dropped
     * @param x : int coordinate for drop X
     * @param y : int coordinate for drop Y
     * @param z : int coordinate for drop Z
     * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
     * @param sendMessage : boolean Specifies whether to send message to Client about this action
     * @return L2ItemInstance corresponding to the new item or the updated item in inventory
     */
    public L2ItemInstance dropItem(String process, int objectId, long count, int x, int y, int z,
            L2Object reference, boolean sendMessage) {
        L2ItemInstance olditem = getInventory().getItemByObjectId(objectId);
        L2ItemInstance item = getInventory().dropItem(process, objectId, count, this, reference);

        if (item == null) {
            if (sendMessage)
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);

            return null;
        }

        item.dropMe(this, x, y, z);
        // Destroy item droped from inventory by player when DESTROY_PLAYER_INVENTORY_DROP is set to true
        if (Config.DESTROY_PLAYER_INVENTORY_DROP) {
            ItemsAutoDestroyManager.tryAddItem(item);

            item.setProtected(false);
        }
        // Avoids it from beeing removed by the auto item destroyer
        else
            item.setDropTime(0);

        // Send inventory update packet
        getInventory().updateInventory(olditem);

        // Sends message to client if requested
        if (sendMessage) {
            SystemMessage sm = new SystemMessage(SystemMessageId.YOU_DROPPED_S1);
            sm.addItemName(item);
            sendPacket(sm);
        }
        return item;
    }

    public L2ItemInstance checkItemManipulation(int objectId, long count, String action) {
        L2ItemInstance item = getInventory().getItemByObjectId(objectId);
        if (item == null) {
            _log.debug(getObjectId() + ": player tried to " + action + " item he is not owner of");
            return null;
        }

        if (count < 0 || (count > 1 && !item.isStackable())) {
            _log.debug(getObjectId() + ": player tried to " + action + " item with invalid count: " + count);
            return null;
        }

        if (count > item.getCount()) {
            _log.debug(getObjectId() + ": player tried to " + action + " more items than he owns");
            return null;
        }

        // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting
        if (getPet() != null && getPet().getControlItemId() == objectId || getMountObjectID() == objectId) {
            if (_log.isDebugEnabled())
                _log.debug(getObjectId() + ": player tried to " + action + " item controling pet");
            return null;
        }

        if (getActiveEnchantItem() != null && getActiveEnchantItem().getObjectId() == objectId) {
            if (_log.isDebugEnabled())
                _log.debug(getObjectId() + ":player tried to " + action + " an enchant scroll he was using");
            return null;
        }

        if (item.isWear()) {
            // Cannot drop/trade wear-items
            return null;
        }

        // We cannot put a Weapon with Augmention in WH while casting (Possible Exploit)
        if (item.isAugmented() && (isCastingNow() || isCastingSimultaneouslyNow()))
            return null;

        return item;
    }

    /**
     * Set _protectEndTime according settings.
     */
    public void setProtection(boolean protect) {
        int proTime = AutomatedTvT.isPlaying(this) ? Config.AUTO_TVT_SPAWN_PROTECT : Config.PLAYER_SPAWN_PROTECTION;
        if (protect && (proTime == 0 || isInOlympiadMode()))
            return;

        if (_log.isDebugEnabled() && (protect || _protectEndTime > 0))
            _log.debug(getName() + ": Protection "
                    + (protect
                            ? "ON " + (GameTimeManager.getGameTicks() + proTime * GameTimeManager.TICKS_PER_SECOND)
                            : "OFF")
                    + " (currently " + GameTimeManager.getGameTicks() + ")");

        _protectEndTime = protect ? GameTimeManager.getGameTicks() + proTime * GameTimeManager.TICKS_PER_SECOND : 0;
    }

    public long getProtection() {
        return _protectEndTime;
    }

    /**
     * Set protection from agro mobs when getting up from fake death, according settings.
     */
    public void setRecentFakeDeath(boolean protect) {
        _recentFakeDeathEndTime = protect
                ? GameTimeManager.getGameTicks()
                        + Config.PLAYER_FAKEDEATH_UP_PROTECTION * GameTimeManager.TICKS_PER_SECOND
                : 0;
    }

    public boolean isRecentFakeDeath() {
        return _recentFakeDeathEndTime > GameTimeManager.getGameTicks();
    }

    /**
     * Get the client owner of this char.<BR><BR>
     */
    public L2Client getClient() {
        return _client;
    }

    /**
     * Set the active connection with the client.<BR><BR>
     */
    public void setClient(L2Client client) {
        _client = client;
    }

    public Point3D getCurrentSkillWorldPosition() {
        SkillUsageRequest currentSkill = getCurrentSkill();

        return currentSkill == null ? null : currentSkill.getSkillWorldPosition();
    }

    public boolean canBeTargetedByAtSiege(L2Player player) {
        Siege siege = SiegeManager.getInstance().getSiege(this);
        if (siege != null && siege.getIsInProgress()) {
            L2Clan selfClan = getClan();
            L2Clan oppClan = player.getClan();
            if (selfClan != null && oppClan != null) {
                boolean self = false;
                for (L2SiegeClan clan : siege.getAttackerClans()) {
                    L2Clan cl = ClanTable.getInstance().getClan(clan.getClanId());

                    if (cl == selfClan || cl.getAllyId() == getAllyId()) {
                        self = true;
                        break;
                    }
                }

                for (L2SiegeClan clan : siege.getDefenderClans()) {
                    L2Clan cl = ClanTable.getInstance().getClan(clan.getClanId());

                    if (cl == selfClan || cl.getAllyId() == getAllyId()) {
                        self = true;
                        break;
                    }
                }

                boolean opp = false;
                for (L2SiegeClan clan : siege.getAttackerClans()) {
                    L2Clan cl = ClanTable.getInstance().getClan(clan.getClanId());

                    if (cl == oppClan || cl.getAllyId() == player.getAllyId()) {
                        opp = true;
                        break;
                    }
                }

                for (L2SiegeClan clan : siege.getDefenderClans()) {
                    L2Clan cl = ClanTable.getInstance().getClan(clan.getClanId());

                    if (cl == oppClan || cl.getAllyId() == player.getAllyId()) {
                        opp = true;
                        break;
                    }
                }

                return self && opp;
            }

            return false;
        }

        return true;
    }

    /**
     * Manage actions when a player click on this L2Player.<BR><BR>
     *
     * <B><U> Actions on first click on the L2Player (Select it)</U> :</B><BR><BR>
     * <li>Set the target of the player</li>
     * <li>Send a Server->Client packet MyTargetSelected to the player (display the select window)</li><BR><BR>
     *
     * <B><U> Actions on second click on the L2Player (Follow it/Attack it/Intercat with it)</U> :</B><BR><BR>
     * <li>Send a Server->Client packet MyTargetSelected to the player (display the select window)</li>
     * <li>If this L2Player has a Private Store, notify the player AI with AI_INTENTION_INTERACT</li>
     * <li>If this L2Player is autoAttackable, notify the player AI with AI_INTENTION_ATTACK</li><BR><BR>
     * <li>If this L2Player is NOT autoAttackable, notify the player AI with AI_INTENTION_FOLLOW</li><BR><BR>
     *
     * <B><U> Example of use </U> :</B><BR><BR>
     * <li> Client packet : Action, AttackRequest</li><BR><BR>
     *
     * @param player The player that start an action on this L2Player
     *
     */
    @Override
    public void onAction(L2Player player) {
        if (player == null)
            return;
        // Restrict interactions during restart/shutdown
        if (Shutdown.isActionDisabled(DisableType.PC_ITERACTION)) {
            sendMessage("Player interaction disabled during restart/shutdown.");
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        if (!GlobalRestrictions.canTarget(player, this, true)) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        // Check if the L2Player is confused
        if (player.isOutOfControl()) {
            // Send a Server->Client packet ActionFailed to the player
            player.sendPacket(ActionFailed.STATIC_PACKET);
        }
        // Aggression target lock effect
        if (!player.canChangeLockedTarget(this))
            return;

        // Check if the player already target this L2Player
        if (player.getTarget() != this) {
            // Set the target of the player
            player.setTarget(this);
        } else {
            if (player != this)
                player.sendPacket(new ValidateLocation(this));
            // Check if this L2Player has a Private Store
            if (getPrivateStoreType() != 0) {
                player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this);
            } else {
                // Check if this L2Player is autoAttackable
                if (isAutoAttackable(player)) {
                    // Player with lvl < 21 can't attack a cursed weapon holder
                    // And a cursed weapon holder  can't attack players with lvl < 21
                    if ((isCursedWeaponEquipped() && player.getLevel() < 21)
                            || (player.isCursedWeaponEquipped() && getLevel() < 21)) {
                        player.sendPacket(ActionFailed.STATIC_PACKET);
                    } else {
                        if (GeoData.getInstance().canSeeTarget(player, this)) {
                            player.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, this);
                            player.onActionRequest();
                        }
                    }
                } else {
                    // This Action Failed packet avoids player getting stuck when clicking three or more times
                    player.sendPacket(ActionFailed.STATIC_PACKET);

                    if (GeoData.getInstance().canSeeTarget(player, this))
                        player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this);
                }
            }
        }
    }

    @Override
    public void onActionShift(L2Player gm) {
        gm.sendPacket(ActionFailed.STATIC_PACKET);
        if (gm.isGM()) {
            if (this != gm.getTarget()) {
                gm.setTarget(this);
            } else {
                AdminEditChar.gatherCharacterInfo(gm, this, "charinfo.htm");
            }
        }
    }

    @Override
    public boolean isInFunEvent() {
        final AbstractFunEventPlayerInfo info = _playerInfoForEvents;

        return (info != null ? info.isInFunEvent() : false) || _inEventTvTi;
    }

    /**
     * Returns true if cp update should be done, false if not
     * @return boolean
     */
    private boolean needCpUpdate(int barPixels) {
        double currentCp = getStatus().getCurrentCp();

        if (currentCp <= 1.0 || getMaxCp() < barPixels)
            return true;

        if (currentCp <= _cpUpdateDecCheck || currentCp >= _cpUpdateIncCheck) {
            if (currentCp == getMaxCp()) {
                _cpUpdateIncCheck = currentCp + 1;
                _cpUpdateDecCheck = currentCp - _cpUpdateInterval;
            } else {
                double doubleMulti = currentCp / _cpUpdateInterval;
                int intMulti = (int) doubleMulti;

                _cpUpdateDecCheck = _cpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
                _cpUpdateIncCheck = _cpUpdateDecCheck + _cpUpdateInterval;
            }

            return true;
        }

        return false;
    }

    /**
     * Returns true if mp update should be done, false if not
     * @return boolean
     */
    private boolean needMpUpdate(int barPixels) {
        double currentMp = getStatus().getCurrentMp();

        if (currentMp <= 1.0 || getMaxMp() < barPixels)
            return true;

        if (currentMp <= _mpUpdateDecCheck || currentMp >= _mpUpdateIncCheck) {
            if (currentMp == getMaxMp()) {
                _mpUpdateIncCheck = currentMp + 1;
                _mpUpdateDecCheck = currentMp - _mpUpdateInterval;
            } else {
                double doubleMulti = currentMp / _mpUpdateInterval;
                int intMulti = (int) doubleMulti;

                _mpUpdateDecCheck = _mpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
                _mpUpdateIncCheck = _mpUpdateDecCheck + _mpUpdateInterval;
            }

            return true;
        }

        return false;
    }

    /**
     * Send packet StatusUpdate with current HP,MP and CP to the L2Player and only current HP, MP and Level to all other L2Player of the Party.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Send the Server->Client packet StatusUpdate with current HP, MP and CP to this L2Player </li><BR>
     * <li>Send the Server->Client packet PartySmallWindowUpdate with current HP, MP and Level to all other L2Player of the Party </li><BR><BR>
     *
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND current HP and MP to all L2Player of the _statusListener</B></FONT><BR><BR>
     *
     */
    @Override
    public final void broadcastStatusUpdateImpl() {
        StatusUpdate su = new StatusUpdate(getObjectId());
        su.addAttribute(StatusUpdate.CUR_HP, (int) getStatus().getCurrentHp());
        su.addAttribute(StatusUpdate.CUR_MP, (int) getStatus().getCurrentMp());
        su.addAttribute(StatusUpdate.CUR_CP, (int) getStatus().getCurrentCp());
        sendPacket(su);

        final boolean needCpUpdate = needCpUpdate(352);
        final boolean needHpUpdate = needHpUpdate(352);
        if (isInParty() && (needCpUpdate || needHpUpdate || needMpUpdate(352)))
            getParty().broadcastToPartyMembers(this, new PartySmallWindowUpdate(this));

        if (isInOlympiadMode() && isOlympiadStart() && (needCpUpdate || needHpUpdate)) {
            Collection<L2Player> players = getKnownList().getKnownPlayers().values();
            if (!players.isEmpty()) {
                ExOlympiadUserInfo olyInfo = new ExOlympiadUserInfo(this, 2);
                for (L2Player player : players)
                    if (player != null && player.isInOlympiadMode()
                            && player.getOlympiadGameId() == _olympiadGameId)
                        player.sendPacket(olyInfo);
            }

            players = Olympiad.getInstance().getSpectators(_olympiadGameId);
            if (players != null && !players.isEmpty()) {
                ExOlympiadUserInfo olyInfo = new ExOlympiadUserInfo(this, getOlympiadSide());
                for (L2Player spectator : players)
                    if (spectator != null)
                        spectator.sendPacket(olyInfo);
            }
        }

        if (isInDuel() && (needCpUpdate || needHpUpdate))
            DuelManager.getInstance().broadcastToOppositTeam(this, new ExDuelUpdateUserInfo(this));
    }

    @Override
    public void updateEffectIconsImpl() {
        final EffectInfoPacketList list = new EffectInfoPacketList(this);

        sendPacket(new MagicEffectIcons(list));

        if (isInParty())
            getParty().broadcastToPartyMembers(this, new PartySpelled(list));
    }

    /**
     * Send a Server->Client packet UserInfo to this L2Player and CharInfo to all L2Player in its _knownPlayers.<BR><BR>
     *
     * <B><U> Concept</U> :</B><BR><BR>
     * Others L2Player in the detection area of the L2Player are identified in <B>_knownPlayers</B>.
     * In order to inform other players of this L2Player state modifications, server just need to go through _knownPlayers to send Server->Client Packet<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Send a Server->Client packet UserInfo to this L2Player (Public and Private Data)</li>
     * <li>Send a Server->Client packet CharInfo to all L2Player in _knownPlayers of the L2Player (Public data only)</li><BR><BR>
     *
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : DON'T SEND UserInfo packet to other players instead of CharInfo packet.
     * Indeed, UserInfo packet contains PRIVATE DATA as MaxHP, STR, DEX...</B></FONT><BR><BR>
     *
     */
    public final void broadcastUserInfo() {
        broadcastFullInfo();
    }

    public final void broadcastTitleInfo() {
        // Send a Server->Client packet UserInfo to this L2Player
        sendPacket(new UserInfo(this));

        // Send a Server->Client packet NicknameChanged to all L2Player in _KnownPlayers of the L2Player
        if (_log.isDebugEnabled())
            _log.debug("players to notify:" + getKnownList().getKnownPlayers().size()
                    + " packet: [S] cc NicknameChanged");

        Broadcast.toKnownPlayers(this, new NicknameChanged(this));
    }

    /**
     * Return the Alliance Identifier of the L2Player.<BR><BR>
     */
    public int getAllyId() {
        return (_clan == null) ? 0 : _clan.getAllyId();
    }

    public int getAllyCrestId() {
        if (getClanId() == 0) {
            return 0;
        }
        if (getClan().getAllyId() == 0) {
            return 0;
        }
        return getClan().getAllyCrestId();
    }

    public void queryGameGuard() {
        getClient().setGameGuardOk(false);
        sendPacket(GameGuardQuery.STATIC_PACKET);
        if (Config.GAMEGUARD_ENFORCE) {
            ThreadPoolManager.getInstance().scheduleGeneral(new GameGuardCheck(), 30 * 1000);
        }
    }

    class GameGuardCheck implements Runnable {

        /**
         * @see java.lang.Runnable#run()
         */
        @Override
        public void run() {
            L2Client client = getClient();
            if (client != null && !client.isAuthedGG() && isOnline() == 1) {
                //GmListTable.broadcastMessageToGMs("Client "+client+" failed to reply GameGuard query and is being kicked!");
                _log.info("Client " + client + " failed to reply GameGuard query and is being kicked!");
                new Disconnection(client, L2Player.this).defaultSequence(false);
            }
        }
    }

    /**
     * Send a Server->Client packet to the L2Player.<BR>
     * <BR>
     */
    @Override
    @SuppressWarnings("deprecation")
    public void sendPacket(L2ServerPacket packet) {
        final L2Client client = _client;
        if (client != null)
            client.sendPacket(packet);
    }

    /**
     * Sends a SystemMessage without any parameter added. No instancing at all!
     */
    @Override
    public void sendPacket(SystemMessageId sm) {
        sendPacket(sm.getSystemMessage());
    }

    @Override
    public void sendPacket(StaticPacket packet) {
        sendPacket((L2ServerPacket) packet);
    }

    @Override
    @SuppressWarnings("deprecation")
    public void sendMessage(String message) {
        sendPacket(SystemMessage.sendString(message));
    }

    /**
     * Manage Interact Task with another L2Player.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>If the private store is a STORE_PRIVATE_SELL, send a Server->Client PrivateBuyListSell packet to the L2Player</li>
     * <li>If the private store is a STORE_PRIVATE_BUY, send a Server->Client PrivateBuyListBuy packet to the L2Player</li>
     * <li>If the private store is a STORE_PRIVATE_MANUFACTURE, send a Server->Client RecipeShopSellList packet to the L2Player</li><BR><BR>
     *
     * @param target The L2Creature targeted
     *
     */
    public void doInteract(L2Creature target) {
        if (target instanceof L2Player) {
            L2Player temp = (L2Player) target;
            sendPacket(ActionFailed.STATIC_PACKET);

            if (temp.getPrivateStoreType() == STORE_PRIVATE_SELL
                    || temp.getPrivateStoreType() == STORE_PRIVATE_PACKAGE_SELL)
                sendPacket(new PrivateStoreListSell(this, temp));
            else if (temp.getPrivateStoreType() == STORE_PRIVATE_BUY)
                sendPacket(new PrivateStoreListBuy(this, temp));
            else if (temp.getPrivateStoreType() == STORE_PRIVATE_MANUFACTURE)
                sendPacket(new RecipeShopSellList(this, temp));
        } else {
            // _interactTarget=null should never happen but one never knows ^^;
            if (target != null)
                target.onAction(this);
        }
    }

    /**
     * Manage AutoLoot Task.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Send a System Message to the L2Player : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2</li>
     * <li>Add the Item to the L2Player inventory</li>
     * <li>Send a Server->Client packet InventoryUpdate to this L2Player with NewItem (use a new slot) or ModifiedItem (increase amount)</li>
     * <li>Send a Server->Client packet StatusUpdate to this L2Player with current weight</li><BR><BR>
     *
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : If a Party is in progress, distribute Items between party members</B></FONT><BR><BR>
     *
     * @param target The L2ItemInstance dropped
     *
     */
    public void doAutoLoot(L2Attackable target, L2Attackable.RewardItem item) {
        if (!tryAutoLoot(target, item.getItemId()))
            target.dropItem(this, item);
        else if (isInParty())
            getParty().distributeItem(this, item, false, target);
        else if (item.getItemId() == PlayerInventory.ADENA_ID)
            addAdena("Loot", item.getCount(), target, true);
        else
            addItem("Loot", item.getItemId(), item.getCount(), target, true, false);
    }

    private boolean tryAutoLoot(L2Attackable target, int itemId) {
        if (target.isFlying())
            return true;

        if (ItemTable.isAdenaLikeItem(itemId))
            return Config.ALT_AUTO_LOOT_ADENA;

        if (ItemTable.getInstance().getTemplate(itemId).getItemType() == L2EtcItemType.HERB)
            return Config.ALT_AUTO_LOOT_HERBS;

        if (target.isRaid())
            return Config.ALT_AUTO_LOOT_RAID;

        return Config.ALT_AUTO_LOOT;
    }

    /**
     * Manage Pickup Task.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Send a Server->Client packet StopMove to this L2Player </li>
     * <li>Remove the L2ItemInstance from the world and send server->client GetItem packets </li>
     * <li>Send a System Message to the L2Player : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2</li>
     * <li>Add the Item to the L2Player inventory</li>
     * <li>Send a Server->Client packet InventoryUpdate to this L2Player with NewItem (use a new slot) or ModifiedItem (increase amount)</li>
     * <li>Send a Server->Client packet StatusUpdate to this L2Player with current weight</li><BR><BR>
     *
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : If a Party is in progress, distribute Items between party members</B></FONT><BR><BR>
     *
     * @param object The L2ItemInstance to pick up
     *
     */
    protected void doPickupItem(L2Object object) {
        if (isAlikeDead() || isFakeDeath())
            return;

        // Set the AI Intention to AI_INTENTION_IDLE
        getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);

        // Check if the L2Object to pick up is a L2ItemInstance
        if (!(object instanceof L2ItemInstance)) {
            // Dont try to pickup anything that is not an item :)
            _log.warn("trying to pickup wrong target." + getTarget());
            return;
        }

        L2ItemInstance target = (L2ItemInstance) object;

        // Send a Server->Client packet ActionFailed to this L2Player
        sendPacket(ActionFailed.STATIC_PACKET);

        // Send a Server->Client packet StopMove to this L2Player
        StopMove sm = new StopMove(getObjectId(), getX(), getY(), getZ(), getHeading());
        if (_log.isDebugEnabled())
            _log.debug("pickup pos: " + target.getX() + " " + target.getY() + " " + target.getZ());
        sendPacket(sm);

        synchronized (target) {
            // Check if the target to pick up is visible
            if (!target.isVisible()) {
                // Send a Server->Client packet ActionFailed to this L2Player
                sendPacket(ActionFailed.STATIC_PACKET);
                return;

            }

            if (((isInParty() && getParty().getLootDistribution() == L2Party.ITEM_LOOTER) || !isInParty())
                    && !getInventory().validateCapacity(target)) {
                sendPacket(SystemMessageId.SLOTS_FULL);
                sendPacket(ActionFailed.STATIC_PACKET);
                return;
            }

            if (isInvul() && !isGM()) {
                SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1);
                smsg.addItemName(target);
                sendPacket(smsg);
                sendPacket(ActionFailed.STATIC_PACKET);
                return;
            }

            if (getActiveTradeList() != null) {
                sendPacket(SystemMessageId.CANNOT_PICKUP_OR_USE_ITEM_WHILE_TRADING);
                sendPacket(ActionFailed.STATIC_PACKET);
                return;
            }

            if (target.getOwnerId() != 0 && target.getOwnerId() != getObjectId()
                    && !isInLooterParty(target.getOwnerId())) {
                sendPacket(ActionFailed.STATIC_PACKET);

                if (target.getItemId() == PlayerInventory.ADENA_ID) {
                    SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1_ADENA);
                    smsg.addItemNumber(target.getCount());
                    sendPacket(smsg);
                } else if (target.getCount() > 1) {
                    SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S2_S1_S);
                    smsg.addItemName(target);
                    smsg.addItemNumber(target.getCount());
                    sendPacket(smsg);
                } else {
                    SystemMessage smsg = new SystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1);
                    smsg.addItemName(target);
                    sendPacket(smsg);
                }

                return;
            }

            // Cursed Weapons
            if (CursedWeaponsManager.getInstance().isCursed(target.getItemId()) && isCursedWeaponEquipped()) {
                ItemTable.getInstance().destroyItem("Pickup CW", target, this, null);
                CursedWeapon cw = CursedWeaponsManager.getInstance().getCursedWeapon(getCursedWeaponEquippedId());
                cw.increaseKills(cw.getStageKills());
                return;
            }

            // You can pickup only 1 combat flag
            if (FortSiegeManager.getInstance().isCombat(target.getItemId())) {
                if (!FortSiegeManager.getInstance().checkIfCanPickup(this))
                    return;
            }

            if (target.getItemLootShedule() != null
                    && (target.getOwnerId() == getObjectId() || isInLooterParty(target.getOwnerId())))
                target.resetOwnerTimer();

            // Remove the L2ItemInstance from the world and send server->client GetItem packets
            target.pickupMe(this);
        }
        // Auto use herbs - pick up
        if (target.getItemType() == L2EtcItemType.HERB) {
            ItemHandler.getInstance().useItem(target.getItemId(), this, target);
        }
        // Cursed Weapons are not distributed
        else if (CursedWeaponsManager.getInstance().isCursed(target.getItemId())) {
            addItem("Pickup", target, null, true);
        } else if (FortSiegeManager.getInstance().isCombat(target.getItemId())) {
            addItem("Pickup", target, null, true);
        } else {
            // If item is instance of L2ArmorType or L2WeaponType broadcast an "Attention" system message
            if (target.getItemType() instanceof L2ArmorType || target.getItemType() instanceof L2WeaponType) {
                if (target.getEnchantLevel() > 0) {
                    SystemMessage msg = new SystemMessage(SystemMessageId.ANNOUNCEMENT_C1_PICKED_UP_S2_S3);
                    msg.addPcName(this);
                    msg.addNumber(target.getEnchantLevel());
                    msg.addItemName(target);
                    broadcastPacket(msg, 1400);
                } else {
                    SystemMessage msg = new SystemMessage(SystemMessageId.ANNOUNCEMENT_C1_PICKED_UP_S2);
                    msg.addPcName(this);
                    msg.addItemName(target);
                    broadcastPacket(msg, 1400);
                }
            }

            // Check if a Party is in progress
            if (isInParty())
                getParty().distributeItem(this, target);
            // Target is adena
            else if (target.getItemId() == PlayerInventory.ADENA_ID && getInventory().getAdenaInstance() != null) {
                addAdena("Pickup", target.getCount(), null, true);
                ItemTable.getInstance().destroyItem("Pickup", target, this, null);
            }
            // Target is regular item
            else
                addItem("Pickup", target, null, true);
        }
    }

    /**
     * Set a target.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Remove the L2Player from the _statusListener of the old target if it was a L2Creature </li>
     * <li>Add the L2Player to the _statusListener of the new target if it's a L2Creature </li>
     * <li>Target the new L2Object (add the target to the L2Player _target, _knownObject and L2Player to _KnownObject of the L2Object)</li><BR><BR>
     *
     * @param newTarget The L2Object to target
     *
     */
    @Override
    public void setTarget(L2Object newTarget) {
        if (newTarget != null) {
            if (this != newTarget && newTarget instanceof L2Creature)
                sendPacket(new ValidateLocation((L2Creature) newTarget));

            if (!isGM()) {
                if (newTarget instanceof L2FestivalMonsterInstance && !isFestivalParticipant())
                    return;

                if (isInParty() && getParty().isInDimensionalRift()) {
                    byte riftType = getParty().getDimensionalRift().getType();
                    byte riftRoom = getParty().getDimensionalRift().getCurrentRoom();

                    if (!DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(newTarget))
                        return;
                }
            }

            if (!(newTarget instanceof L2Player) || !isInParty()
                    || getParty() != ((L2Player) newTarget).getParty()) {
                if (!newTarget.isVisible())
                    return;

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

        super.setTarget(newTarget);
    }

    @Override
    protected void refreshTarget(L2Object newTarget) {
        final L2Object oldTarget = getTarget();

        if (oldTarget instanceof L2Creature)
            ((L2Creature) oldTarget).getStatus().removeStatusListener(this);

        if (newTarget instanceof L2Creature)
            ((L2Creature) newTarget).getStatus().addStatusListener(this);

        if (newTarget != null) {
            broadcastPacket(new TargetSelected(this, newTarget));

            sendPacket(new MyTargetSelected(this, newTarget));
        } else if (oldTarget != null) {
            broadcastPacket(new TargetUnselected(this));
        }

        super.refreshTarget(newTarget);

        saveLastTarget(oldTarget != null ? oldTarget.getObjectId() : 0);
    }

    /**
     * Return the active weapon instance (always equipped in the right hand).<BR><BR>
     */
    @Override
    public L2ItemInstance getActiveWeaponInstance() {
        return getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
    }

    /**
     * Return the active weapon item (always equipped in the right hand).<BR><BR>
     */
    @Override
    public L2Weapon getActiveWeaponItem() {
        L2ItemInstance weapon = getActiveWeaponInstance();

        if (weapon == null)
            return getFistsWeaponItem();

        return (L2Weapon) weapon.getItem();
    }

    public L2ItemInstance getChestArmorInstance() {
        return getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
    }

    public L2ItemInstance getLegsArmorInstance() {
        return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS);
    }

    public L2Armor getActiveChestArmorItem() {
        L2ItemInstance armor = getChestArmorInstance();

        if (armor == null)
            return null;

        return (L2Armor) armor.getItem();
    }

    public L2Armor getActiveLegsArmorItem() {
        L2ItemInstance legs = getLegsArmorInstance();

        if (legs == null)
            return null;

        return (L2Armor) legs.getItem();
    }

    public boolean isWearingHeavyArmor() {
        if ((getChestArmorInstance() != null) && getLegsArmorInstance() != null) {
            L2ItemInstance legs = getLegsArmorInstance();
            L2ItemInstance armor = getChestArmorInstance();
            if (legs.getItemType() == L2ArmorType.HEAVY && (armor.getItemType() == L2ArmorType.HEAVY))
                return true;
        }
        if (getChestArmorInstance() != null) {
            L2ItemInstance armor = getChestArmorInstance();

            if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem()
                    .getBodyPart() == L2Item.SLOT_FULL_ARMOR && armor.getItemType() == L2ArmorType.HEAVY)
                return true;
        }

        return false;
    }

    public boolean isWearingLightArmor() {
        if ((getChestArmorInstance() != null) && getLegsArmorInstance() != null) {
            L2ItemInstance legs = getLegsArmorInstance();
            L2ItemInstance armor = getChestArmorInstance();
            if (legs.getItemType() == L2ArmorType.LIGHT && (armor.getItemType() == L2ArmorType.LIGHT))
                return true;
        }
        if (getChestArmorInstance() != null) {
            L2ItemInstance armor = getChestArmorInstance();

            if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem()
                    .getBodyPart() == L2Item.SLOT_FULL_ARMOR && armor.getItemType() == L2ArmorType.LIGHT)
                return true;
        }

        return false;
    }

    public boolean isWearingMagicArmor() {
        if ((getChestArmorInstance() != null) && getLegsArmorInstance() != null) {
            L2ItemInstance legs = getLegsArmorInstance();
            L2ItemInstance armor = getChestArmorInstance();
            if (legs.getItemType() == L2ArmorType.MAGIC && (armor.getItemType() == L2ArmorType.MAGIC))
                return true;
        }
        if (getChestArmorInstance() != null) {
            L2ItemInstance armor = getChestArmorInstance();

            if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem()
                    .getBodyPart() == L2Item.SLOT_FULL_ARMOR && armor.getItemType() == L2ArmorType.MAGIC)
                return true;
        }

        return false;
    }

    public boolean isWearingFormalWear() {
        return _IsWearingFormalWear;
    }

    public void setIsWearingFormalWear(boolean value) {
        _IsWearingFormalWear = value;
    }

    /**
     * Return the secondary weapon instance (always equipped in the left hand).<BR><BR>
     */
    @Override
    public L2ItemInstance getSecondaryWeaponInstance() {
        return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
    }

    /**
     * Return the secondary weapon item (always equipped in the left hand) or the fists weapon.<BR><BR>
     */
    @Override
    public L2Weapon getSecondaryWeaponItem() {
        L2ItemInstance weapon = getSecondaryWeaponInstance();

        if (weapon == null)
            return getFistsWeaponItem();

        L2Item item = weapon.getItem();

        if (item instanceof L2Weapon)
            return (L2Weapon) item;

        return null;
    }

    /**
     * Kill the L2Creature, Apply Death Penalty, Manage gain/loss Karma and Item Drop.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Reduce the Experience of the L2Player in function of the calculated Death Penalty </li>
     * <li>If necessary, unsummon the Pet of the killed L2Player </li>
     * <li>Manage Karma gain for attacker and Karma loss for the killed L2Player </li>
     * <li>If the killed L2Player has Karma, manage Drop Item</li>
     * <li>Kill the L2Player </li><BR><BR>
     *
     *
     * @param killer The L2Creature who attacks
     *
     */
    @Override
    public boolean doDie(L2Creature killer) {
        // is the dying in duel? if so, change his duel state to dead
        if (isInDuel()) // pets can die as usual
        {
            disableAllSkills();
            getStatus().setCurrentHp(1);
            getStatus().stopHpMpRegeneration();
            killer.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
            killer.sendPacket(ActionFailed.STATIC_PACKET);

            // let the DuelManager know of his defeat
            DuelManager.getInstance().onPlayerDefeat(this);
            return false;
        }

        if (isInOlympiadMode()) {
            getStatus().stopHpMpRegeneration();
            setIsDead(true);
            setIsPendingRevive(true);
            if (getPet() != null)
                getPet().getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null);
            return false;
        }

        /* Since L2Creature.doDie() calls stopAllEffects(), which includes
         * setting charm of curage and other blessings as false, this stores value
         * before calling superclass method
         */
        boolean charmOfCourage = getCharmOfCourage();

        // Kill the L2Player
        if (!super.doDie(killer))
            return false;

        if (isMounted())
            stopFeed();

        synchronized (this) {
            if (isFakeDeath())
                stopFakeDeath(true);
        }

        // Clear resurrect xp calculation
        setExpBeforeDeath(0);

        // Issues drop of Cursed Weapon.
        if (isCursedWeaponEquipped()) {
            CursedWeaponsManager.getInstance().drop(_cursedWeaponEquippedId, killer);
        } else if (isCombatFlagEquipped()) {
            FortSiegeManager.getInstance().dropCombatFlag(this);
        }

        Castle castle = null;
        if (getClan() != null) {
            castle = CastleManager.getInstance().getCastleByOwner(getClan());
            if (castle != null)
                castle.destroyClanGate();
        }

        if (killer != null) {
            L2Player pk = killer.getActingPlayer();

            boolean bothWayClanWarKill = false;
            boolean clanWarKill = false;
            boolean playerKill = false;

            if (GlobalRestrictions.playerKilled(killer, this)) {
            }

            else if (pk != null) {
                if (pk.getClan() != null && getClan() != null && !isAcademyMember() && !pk.isAcademyMember()) {
                    if ((_clan.isAtWarWith(pk.getClanId()) && pk.getClan().isAtWarWith(getClanId()))
                            || (isInSiege() && pk.isInSiege())) {
                        bothWayClanWarKill = true;
                        clanWarKill = true;
                    } else if (_clan.isAtWarWith(pk.getClanId()))
                        clanWarKill = true;
                }
                playerKill = true;
            }

            boolean srcInPvP = isInsideZone(L2Zone.FLAG_PVP) && !isInSiege();

            if (killer instanceof L2Player && srcInPvP && Config.ARENA_ENABLED) {
                ArenaManager.getInstance().onKill(killer.getObjectId(), killer.getName());
                ArenaManager.getInstance().onDeath(getObjectId(), getName());
            }

            if (bothWayClanWarKill && pk != null) {
                // When your reputation score is 0 or below, the other clan cannot acquire any reputation points
                if (getClan().getReputationScore() > 0)
                    pk.getClan().setReputationScore(
                            pk.getClan().getReputationScore() + Config.REPUTATION_SCORE_PER_KILL, true);
                // When the opposing sides reputation score is 0 or below, your clans reputation score does not decrease
                if (pk.getClan().getReputationScore() > 0)
                    _clan.setReputationScore(_clan.getReputationScore() - Config.REPUTATION_SCORE_PER_KILL, true);
            }

            if (!srcInPvP) {
                if (pk == null || !pk.isCursedWeaponEquipped()) {
                    onDieDropItem(killer); // Check if any item should be dropped

                    if (!srcInPvP) {
                        if (Config.ALT_GAME_DELEVEL) {
                            // Reduce the Experience of the L2Player in function of the calculated Death Penalty
                            // NOTE: deathPenalty +- Exp will update karma
                            // Penalty is lower if the player is at war with the pk (war has to be declared)
                            if (getSkillLevel(L2Skill.SKILL_LUCKY) < 0 || getStat().getLevel() > 9)
                                deathPenalty(clanWarKill, playerKill, charmOfCourage,
                                        killer instanceof L2SiegeGuard);
                        } else {
                            if (!(isInsideZone(L2Zone.FLAG_PVP) && !isInsideZone(L2Zone.FLAG_PVP)) || pk == null)
                                onDieUpdateKarma(); // Update karma if delevel is not allowed
                        }
                    }
                }
                if (pk != null) {
                    if (Config.ALT_ANNOUNCE_PK) {
                        String announcetext = "";
                        // Build announce text
                        if (getPvpFlag() == 0)
                            announcetext = pk.getName() + " has slaughtered " + getName();
                        else
                            announcetext = pk.getName() + " has defeated " + getName();

                        // Announce to player
                        if (Config.ALT_ANNOUNCE_PK_NORMAL_MESSAGE)
                            Announcements.getInstance().announceToPlayers(announcetext);
                        else
                            Announcements.getInstance().announceToAll(announcetext);
                    }
                }
            } else if (pk != null && Config.ALT_ANNOUNCE_PK) {
                if (Config.ALT_ANNOUNCE_PK_NORMAL_MESSAGE)
                    Announcements.getInstance().announceToPlayers(pk.getName() + " has defeated " + getName());
                else
                    Announcements.getInstance().announceToAll(pk.getName() + " has defeated " + getName());
            }
        }

        // Force Charges
        clearCharges(); // Empty charges

        //updatePvPFlag(0); // Clear the pvp flag
        // Pet shouldn't get unsummoned after masters death.
        // Unsummon the Pet
        //if (getPet() != null) getPet().unSummon(this);

        // Unsummon Cubics
        if (!_cubics.isEmpty()) {
            for (L2CubicInstance cubic : _cubics.values()) {
                cubic.stopAction();
                cubic.cancelDisappear();
            }
            _cubics.clear();
        }

        if (_fusionSkill != null)
            abortCast();

        for (L2Creature character : getKnownList().getKnownCharacters())
            if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
                character.abortCast();

        if (isInParty() && getParty().isInDimensionalRift()) {
            getParty().getDimensionalRift().memberDead(this);
        }

        // Calculate death penalty buff
        calculateDeathPenaltyBuffLevel(killer);

        // [L2J_JP ADD SANDMAN]
        // When the player has been annihilated, the player is banished from the Four Sepulcher.
        if (FourSepulchersManager.getInstance().checkIfInZone(this) && (getZ() >= -7250 && getZ() <= -6841))
            FourSepulchersManager.getInstance().checkAnnihilated(this);
        // When the player has been annihilated, the player is banished from the lair.
        else if (SailrenManager.getInstance().checkIfInZone(this))
            SailrenManager.getInstance().checkAnnihilated();
        else if (AntharasManager.getInstance().checkIfInZone(this))
            AntharasManager.getInstance().checkAnnihilated();
        else if (ValakasManager.getInstance().checkIfInZone(this))
            ValakasManager.getInstance().checkAnnihilated();
        else if (BaiumManager.getInstance().checkIfInZone(this))
            BaiumManager.getInstance().checkAnnihilated();
        else if (BaylorManager.getInstance().checkIfInZone(this))
            BaylorManager.getInstance().checkAnnihilated();
        else if (FrintezzaManager.getInstance().checkIfInZone(this))
            FrintezzaManager.getInstance().checkAnnihilated();
        else if (LastImperialTombManager.getInstance().checkIfInZone(this))
            LastImperialTombManager.getInstance().checkAnnihilated();

        QuestState qs = getQuestState("255_Tutorial");
        if (qs != null)
            qs.getQuest().notifyEvent("CE30", null, this);

        return true;
    }

    /** UnEnquip on skills with disarm effect **/
    public void onDisarm(L2Player target) {
        target.getInventory().unEquipItemInBodySlotAndRecord(14);
    }

    private void onDieDropItem(L2Creature killer) {
        if (killer == null)
            return;

        L2Player pk = killer.getActingPlayer();
        if (pk != null && getKarma() <= 0 && pk.getClan() != null && getClan() != null
                && (pk.getClan().isAtWarWith(getClanId())
                //|| this.getClan().isAtWarWith(((L2Player)killer).getClanId())
                ))
            return;

        if ((!isInsideZone(L2Zone.FLAG_PVP) || pk == null) && (!isGM() || Config.KARMA_DROP_GM)) {
            boolean isKarmaDrop = false;
            boolean isKillerNpc = (killer instanceof L2Npc);
            int pkLimit = Config.KARMA_PK_LIMIT;

            int dropEquip = 0;
            int dropEquipWeapon = 0;
            int dropItem = 0;
            int dropLimit = 0;
            int dropPercent = 0;

            if (getKarma() > 0 && getPkKills() >= pkLimit) {
                isKarmaDrop = true;
                dropPercent = Config.KARMA_RATE_DROP;
                dropEquip = Config.KARMA_RATE_DROP_EQUIP;
                dropEquipWeapon = Config.KARMA_RATE_DROP_EQUIP_WEAPON;
                dropItem = Config.KARMA_RATE_DROP_ITEM;
                dropLimit = Config.KARMA_DROP_LIMIT;
            } else if (isKillerNpc && getLevel() > 4 && !isFestivalParticipant()) {
                dropPercent = Config.PLAYER_RATE_DROP;
                dropEquip = Config.PLAYER_RATE_DROP_EQUIP;
                dropEquipWeapon = Config.PLAYER_RATE_DROP_EQUIP_WEAPON;
                dropItem = Config.PLAYER_RATE_DROP_ITEM;
                dropLimit = Config.PLAYER_DROP_LIMIT;
            }

            if (dropPercent > 0 && Rnd.get(100) < dropPercent) {
                int dropCount = 0;
                int itemDropPercent = 0;
                for (L2ItemInstance itemDrop : getInventory().getItems()) {
                    // Don't drop
                    if (!itemDrop.isDropable() || itemDrop.getItemId() == PlayerInventory.ADENA_ID
                    // Dont drop Shadow Items
                            || itemDrop.isShadowItem()
                            // Dont drop Time Limited Items
                            || itemDrop.isTimeLimitedItem()
                            // Quest Items
                            || itemDrop.getItem().getType2() == L2Item.TYPE2_QUEST
                            // Control Item of active pet
                            || getPet() != null && getPet().getControlItemId() == itemDrop.getItemId()
                            // Item listed in the non droppable item list
                            || Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_ITEMS, itemDrop.getItemId()) >= 0
                            // Item listed in the non droppable pet item list
                            || Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_PET_ITEMS,
                                    itemDrop.getItemId()) >= 0)
                        continue;

                    if (itemDrop.isEquipped())
                        // Set proper chance according to Item type of equipped Item
                        itemDropPercent = itemDrop.getItem().getType2() == L2Item.TYPE2_WEAPON ? dropEquipWeapon
                                : dropEquip;
                    else
                        itemDropPercent = dropItem; // Item in inventory

                    // NOTE: Each time an item is dropped, the chance of another item being dropped gets lesser (dropCount * 2)
                    if (Rnd.get(100) < itemDropPercent) {
                        if (itemDrop.isEquipped()) {
                            getInventory().unEquipItemInSlotAndRecord(itemDrop.getLocationSlot());
                            // must be sent explicitly to avoid visible garbage
                            sendPacket(new UserInfo(this));
                        }
                        dropItem("DieDrop", itemDrop, killer, true);

                        if (isKarmaDrop)
                            _log.info(getName() + " has karma and dropped " + itemDrop);
                        else
                            _log.info(getName() + " dropped " + itemDrop);

                        if (++dropCount >= dropLimit)
                            break;
                    }
                }
            }
            // Player can drop adena against other player
            if (Config.ALT_PLAYER_CAN_DROP_ADENA && !isKillerNpc && Config.PLAYER_RATE_DROP_ADENA > 0
                    && 100 >= Config.PLAYER_RATE_DROP_ADENA
                    && !(killer instanceof L2Player && ((L2Player) killer).isGM())) {
                L2ItemInstance itemDrop = getInventory().getAdenaInstance();
                long iCount = getInventory().getAdena();
                // Adena count depends on config
                iCount = iCount * Config.PLAYER_RATE_DROP_ADENA / 100;
                // Drop only adena this time
                if (itemDrop != null && itemDrop.getItemId() == PlayerInventory.ADENA_ID) // Adena
                {
                    dropItem("DieDrop", itemDrop.getObjectId(), iCount, getPosition().getX() + Rnd.get(50) - 25,
                            getPosition().getY() + Rnd.get(50) - 25, getPosition().getZ() + 20, killer, true);
                }
            }
        }
    }

    private void onDieUpdateKarma() {
        // Karma lose for server that does not allow delevel
        if (getKarma() > 0) {
            // This formula seems to work relatively well:
            // baseKarma * thisLVL * (thisLVL/100)
            // Calculate the new Karma of the attacker : newKarma = baseKarma*pkCountMulti*lvlDiffMulti
            double karmaLost = Config.KARMA_LOST_BASE;
            karmaLost *= getLevel(); // Multiply by char lvl
            karmaLost *= (getLevel() / 100.0); // Divide by 0.charLVL
            karmaLost = Math.round(karmaLost);
            if (karmaLost < 0)
                karmaLost = 1;

            // Decrease Karma of the L2Player and Send it a Server->Client StatusUpdate packet with Karma and PvP Flag if necessary
            setKarma(getKarma() - (int) karmaLost);
        }
    }

    public void onKillUpdatePvPKarma(L2Creature target) {
        if (target == null)
            return;
        if (!(target instanceof L2Playable))
            return;

        L2Player targetPlayer = target.getActingPlayer();

        if (targetPlayer == null)
            return; // Target player is null
        if (targetPlayer == this)
            return; // Target player is self

        if (isCursedWeaponEquipped()) {
            CursedWeaponsManager.getInstance().increaseKills(_cursedWeaponEquippedId);
            // Custom message for time left
            // CursedWeapon cw = CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquippedId);
            // SystemMessage msg = new SystemMessage(SystemMessageId.THERE_IS_S1_HOUR_AND_S2_MINUTE_LEFT_OF_THE_FIXED_USAGE_TIME);
            // int timeLeftInHours = (int)(((cw.getTimeLeft()/60000)/60));
            // msg.addItemName(_cursedWeaponEquippedId);
            // msg.addNumber(timeLeftInHours);
            // sendPacket(msg);
            return;
        }

        // If in duel and you kill (only can kill l2summon), do nothing
        if (isInDuel() && targetPlayer.isInDuel())
            return;

        // If in Arena, do nothing
        if (isInsideZone(L2Zone.FLAG_PVP))
            return;

        // Check if it's pvp
        if ((checkIfPvP(target) && //  Can pvp and
                targetPlayer.getPvpFlag() != 0 // Target player has pvp flag set
        ) || // or
                (isInsideZone(L2Zone.FLAG_PVP) && // Player is inside pvp zone and
                        targetPlayer.isInsideZone(L2Zone.FLAG_PVP) // Target player is inside pvp zone
                )) {
            if (target instanceof L2Player)
                increasePvpKills();
            // Give faction pvp points
            if (Config.FACTION_ENABLED && targetPlayer.getSide() != getSide() && targetPlayer.getSide() != 0
                    && getSide() != 0 && Config.FACTION_KILL_REWARD)
                increaseFactionKillPoints(targetPlayer.getLevel(), false);
        } else
        // Target player doesn't have pvp flag set
        {
            // Check factions
            if (Config.FACTION_ENABLED && targetPlayer.getSide() != getSide() && targetPlayer.getSide() != 0
                    && getSide() != 0 && Config.FACTION_KILL_REWARD) {
                // Give faction pk points
                increaseFactionKillPoints(targetPlayer.getLevel(), true);
                // No karma
                return;
            }

            // Check about wars
            boolean clanWarKill = (targetPlayer.getClan() != null && getClan() != null && !isAcademyMember()
                    && !(targetPlayer.isAcademyMember()) && _clan.isAtWarWith(targetPlayer.getClanId())
                    && targetPlayer.getClan().isAtWarWith(_clan.getClanId()));
            if (clanWarKill) {
                // 'Both way war' -> 'PvP Kill'
                if (target instanceof L2Player)
                    increasePvpKills();
                return;
            }

            // 'No war' or 'One way war' -> 'Normal PK'
            if (targetPlayer.getKarma() > 0) // Target player has karma
            {
                if (Config.KARMA_AWARD_PK_KILL) {
                    if (target instanceof L2Player)
                        increasePvpKills();
                }
            } else if (targetPlayer.getPvpFlag() == 0) // Target player doesn't have karma
            {
                increasePkKillsAndKarma(targetPlayer.getLevel(), target instanceof L2Player);
                // Unequip adventurer items
                if (getInventory().getPaperdollItemId(7) >= 7816 && getInventory().getPaperdollItemId(7) <= 7831) {
                    L2ItemInstance invItem = getInventory().getItemByItemId(getInventory().getPaperdollItemId(7));
                    if (invItem.isEquipped()) {
                        L2ItemInstance[] unequiped = getInventory()
                                .unEquipItemInSlotAndRecord(invItem.getLocationSlot());
                        InventoryUpdate iu = new InventoryUpdate();
                        for (L2ItemInstance itm : unequiped)
                            iu.addModifiedItem(itm);
                        sendPacket(iu);
                    }
                    refreshExpertisePenalty();
                    sendPacket(SystemMessageId.CANT_EQUIP_WITH_PK_POINTS);
                }
            }
        }
    }

    /**
     * Increase the faction points depending on level
     * PK Kills give half the points of a PVP Kill
     */
    public void increaseFactionKillPoints(int level, boolean pk) {
        int points;
        points = (level / getLevel()) * (Config.FACTION_KILL_RATE / 100);
        if (pk)
            points /= 2;
        _faction.addFactionPoints(points);
        sendMessage("You earned " + String.valueOf(points) + " Facion Points");
    }

    /**
     * Increase the pvp kills count and send the info to the player
     *
     */
    private void increasePvpKills() {
        // Add karma to attacker and increase its PK counter
        setPvpKills(getPvpKills() + 1);

        // Send a Server->Client UserInfo packet to attacker with its Karma and PK Counter
        sendPacket(new UserInfo(this));
    }

    /**
     * Increase pk count, karma and send the info to the player
     *
     * @param targLVL : level of the killed player
     * @param increasePk : true if PK counter should be increased too
     */
    private void increasePkKillsAndKarma(int targLVL, boolean increasePk) {
        int baseKarma = (int) (Config.KARMA_MIN_KARMA * Config.KARMA_RATE);
        int newKarma = baseKarma;
        int karmaLimit = (int) (Config.KARMA_MAX_KARMA * Config.KARMA_RATE);

        int pkLVL = getLevel();
        int pkPKCount = getPkKills();

        int lvlDiffMulti = 0;
        int pkCountMulti = 0;

        // Check if the attacker has a PK counter greater than 0
        if (pkPKCount > 0)
            pkCountMulti = pkPKCount / 2;
        else
            pkCountMulti = 1;
        if (pkCountMulti < 1)
            pkCountMulti = 1;

        // Calculate the level difference Multiplier between attacker and killed L2Player
        if (pkLVL > targLVL)
            lvlDiffMulti = pkLVL / targLVL;
        else
            lvlDiffMulti = 1;
        if (lvlDiffMulti < 1)
            lvlDiffMulti = 1;

        // Calculate the new Karma of the attacker : newKarma = baseKarma*pkCountMulti*lvlDiffMulti
        newKarma = (int) (newKarma * pkCountMulti * lvlDiffMulti * Config.KARMA_RATE);

        // Make sure newKarma is less than karmaLimit and higher than baseKarma
        if (newKarma < baseKarma)
            newKarma = baseKarma;
        if (newKarma > karmaLimit)
            newKarma = karmaLimit;

        // Fix to prevent overflow (=> karma has a  max value of 2 147 483 647)
        if (getKarma() > (Integer.MAX_VALUE - newKarma))
            newKarma = Integer.MAX_VALUE - getKarma();

        // Add karma to attacker and increase its PK counter
        if (increasePk)
            setPkKills(getPkKills() + 1);
        setKarma(getKarma() + newKarma);

        // Send a Server->Client UserInfo packet to attacker with its Karma and PK Counter
        sendPacket(new UserInfo(this));
    }

    public int calculateKarmaLost(long exp) {
        // KARMA LOSS
        // When a Player Killer gets killed by another player or a L2MonsterInstance, it loses a certain amount of Karma based on their level.
        // this (with defaults) results in a level 1 losing about ~2 karma per death, and a lvl 70 loses about 11760 karma per death...
        // You lose karma as long as you were not in a pvp zone and you did not kill urself.
        // NOTE: exp for death (if delevel is allowed) is based on the players level

        long expGained = Math.abs(exp);
        expGained /= Config.KARMA_XP_DIVIDER;

        int karmaLost = 0;
        if (expGained > Integer.MAX_VALUE)
            karmaLost = Integer.MAX_VALUE;
        else
            karmaLost = (int) expGained;

        if (karmaLost < Config.KARMA_LOST_BASE)
            karmaLost = Config.KARMA_LOST_BASE;
        if (karmaLost > getKarma())
            karmaLost = getKarma();

        return karmaLost;
    }

    private static final class PvPFlagManager extends AbstractIterativePeriodicTaskManager<L2Player> {
        private static final PvPFlagManager _instance = new PvPFlagManager();

        private static PvPFlagManager getInstance() {
            return _instance;
        }

        private PvPFlagManager() {
            super(1000);
        }

        @Override
        protected void callTask(L2Player task) {
            if (System.currentTimeMillis() > task.getPvpFlagLasts()) {
                task.stopPvPFlag();
            } else if (System.currentTimeMillis() > (task.getPvpFlagLasts() - 20000)) {
                task.updatePvPFlag(2);
            } else {
                task.updatePvPFlag(1);
            }
        }

        @Override
        protected String getCalledMethodName() {
            return "updatePvPFlag()";
        }
    }

    /** The PvP Flag state of the L2Player (0=White, 1=Purple) */
    private byte _pvpFlag;
    private long _pvpFlagLasts;

    /**
     * Set the PvP Flag of the L2Player.<BR>
     * <BR>
     */
    private void setPvpFlag(int pvpFlag) {
        _pvpFlag = (byte) pvpFlag;
    }

    public byte getPvpFlag() {
        return _pvpFlag;
    }

    public void updatePvPFlag(int value) {
        if (getPvpFlag() == value)
            return;

        setPvpFlag((byte) value);

        if (getPvpFlag() == 0)
            PvPFlagManager.getInstance().stopTask(this);
        else
            PvPFlagManager.getInstance().startTask(this);

        sendPacket(new UserInfo(this));
        broadcastRelationChanged();
    }

    private void setPvpFlagLasts(long time) {
        _pvpFlagLasts = time;
    }

    private long getPvpFlagLasts() {
        return _pvpFlagLasts;
    }

    private void startPvPFlag() {
        updatePvPFlag(1);
    }

    private void stopPvPFlag() {
        updatePvPFlag(0);
    }

    public void updatePvPStatus() {
        if (isInsideZone(L2Zone.FLAG_PVP))
            return;

        setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME);

        if (getPvpFlag() == 0)
            startPvPFlag();
    }

    public void updatePvPStatus(L2Creature target) {
        L2Player player_target = target.getActingPlayer();

        if (player_target == null)
            return;

        if ((isInDuel() && player_target.getDuelId() == getDuelId()))
            return;
        if ((!isInsideZone(L2Zone.FLAG_PVP) || !player_target.isInsideZone(L2Zone.FLAG_PVP))
                && player_target.getKarma() == 0) {
            if (checkIfPvP(player_target))
                setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_PVP_TIME);
            else
                setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME);
            if (getPvpFlag() == 0)
                startPvPFlag();
        }
    }

    /**
     * Restore the specified % of experience this L2Player has
     * lost and sends a Server->Client StatusUpdate packet.<BR><BR>
     */
    public void restoreExp(double restorePercent) {
        if (getExpBeforeDeath() > 0) {
            // Restore the specified % of lost experience.
            getStat().addExp((int) Math.round((getExpBeforeDeath() - getExp()) * restorePercent / 100));
            setExpBeforeDeath(0);
        }
    }

    /**
     * Reduce the Experience (and level if necessary) of the L2Player in function of the calculated Death Penalty.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Calculate the Experience loss </li>
     * <li>Set the value of _expBeforeDeath </li>
     * <li>Set the new Experience value of the L2Player and Decrease its level if necessary </li>
     * <li>Send a Server->Client StatusUpdate packet with its new Experience </li><BR><BR>
     *
     */
    public void deathPenalty(boolean atwar, boolean killed_by_pc, boolean charmOfCourage,
            boolean killed_by_siege_npc) {
        if (charmOfCourage && isInSiege())
            return;

        if ((killed_by_pc || killed_by_siege_npc)
                && ((isInsideZone(L2Zone.FLAG_PVP) && !isInSiege()) || isInSiege()))
            return;

        // FIXME: Need Correct Penalty

        // Get the level of the L2Player
        final int lvl = getLevel();

        byte level = (byte) getLevel();

        int clan_luck = getSkillLevel(L2Skill.SKILL_CLAN_LUCK);

        double clan_luck_modificator = 1.0;

        if (!killed_by_pc) {
            switch (clan_luck) {
            case 3:
                clan_luck_modificator = 0.8;
                break;
            case 2:
                clan_luck_modificator = 0.8;
                break;
            case 1:
                clan_luck_modificator = 0.88;
                break;
            default:
                clan_luck_modificator = 1.0;
                break;
            }
        } else {
            switch (clan_luck) {
            case 3:
                clan_luck_modificator = 0.5;
                break;
            case 2:
                clan_luck_modificator = 0.5;
                break;
            case 1:
                clan_luck_modificator = 0.5;
                break;
            default:
                clan_luck_modificator = 1.0;
                break;
            }
        }

        // The death steal you some Exp
        double percentLost = Config.PLAYER_XP_PERCENT_LOST[getLevel()] * clan_luck_modificator;

        switch (level) {
        case 78:
            percentLost = (1.5 * clan_luck_modificator);
            break;
        case 77:
            percentLost = (2.0 * clan_luck_modificator);
            break;
        case 76:
            percentLost = (2.5 * clan_luck_modificator);
            break;
        default:
            if (level < 40)
                percentLost = (7.0 * clan_luck_modificator);
            else if (level >= 40 && level <= 75)
                percentLost = (4.0 * clan_luck_modificator);
            break;
        }

        if (getKarma() > 0)
            percentLost *= Config.RATE_KARMA_EXP_LOST;

        if (isFestivalParticipant() || atwar)
            percentLost /= 4.0;

        // Calculate the Experience loss
        final long lostExp;

        if (lvl < Experience.MAX_LEVEL)
            lostExp = Math
                    .round((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost / 100);
        else
            lostExp = Math.round((getStat().getExpForLevel(Experience.MAX_LEVEL)
                    - getStat().getExpForLevel(Experience.MAX_LEVEL - 1)) * percentLost / 100);

        if (_log.isDebugEnabled())
            _log.debug(getName() + " died and lost " + lostExp + " experience.");

        // Get the Experience before applying penalty
        setExpBeforeDeath(getExp());

        // Set the new Experience value of the L2Player
        getStat().addExp(-lostExp);
    }

    public void deathPenalty(boolean atwar, boolean killed_by_pc, boolean killed_by_siege_npc) {
        deathPenalty(atwar, killed_by_pc, getCharmOfCourage(), killed_by_siege_npc);
    }

    public boolean isLookingForParty() {
        return _lookingForParty;
    }

    public boolean getPartyMatchingLevelRestriction() {
        return !_partyMatchingAllLevels;
    }

    public int getPartyMatchingRegion() {
        return _partyMatchingRegion;
    }

    public void setLookingForParty(boolean matching) {
        _lookingForParty = matching;
    }

    public void setPartyMatchingLevelRestriction(boolean off) {
        _partyMatchingAllLevels = off;
    }

    public void setPartyMatchingRegion(int region) {
        _partyMatchingRegion = region;
    }

    public L2PartyRoom getPartyRoom() {
        return _partyRoom;
    }

    /**
     * Set the _partyRoom object of the L2Player (without joining it).
     * @param room new party room
     */
    public void setPartyRoom(L2PartyRoom room) {
        _partyRoom = room;
    }

    /**
     * Stop the HP/MP/CP Regeneration task.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Set the RegenActive flag to False </li>
     * <li>Stop the HP/MP/CP Regeneration task </li><BR><BR>
     *
     */
    public void stopAllTimers() {
        getStatus().stopHpMpRegeneration();
        stopWarnUserTakeBreak();
        stopAutoSaveTask();
        stopWaterTask();

        stopFeed();
        clearPetData();
        storePetFood(_mountNpcId);

        stopSoulTask();
        stopChargeTask();
        stopFameTask();
        stopVitalityTask();

        stopPvPFlag();
        stopJailTask(true);
    }

    /**
     * Return the L2Summon of the L2Player or null.<BR><BR>
     */
    @Override
    public L2Summon getPet() {
        return _summon;
    }

    /**
     * Return the L2Decoy of the L2Player or null.<BR><BR>
     */
    public L2Decoy getDecoy() {
        return _decoy;
    }

    /**
     * Return the L2Trap of the L2Player or null.<BR><BR>
     */
    public L2Trap getTrap() {
        return _trap;
    }

    /**
     * Set the L2Summon of the L2Player.<BR><BR>
     */
    public void setPet(L2Summon summon) {
        _summon = summon;
        // update attack element value display
        if ((_summon == null || _summon instanceof L2SummonInstance) && getClassId().isSummoner()
                && getAttackElement() != Elementals.NONE)
            sendPacket(new UserInfo(this));
    }

    /**
     * Set the L2Decoy of the L2Player.<BR><BR>
     */
    public void setDecoy(L2Decoy decoy) {
        _decoy = decoy;
    }

    /**
     * Set the L2Trap of this L2Player<BR><BR>
     * @param trap
     */
    public void setTrap(L2Trap trap) {
        _trap = trap;
    }

    /**
     * Return the L2Summon of the L2Player or null.<BR><BR>
     */
    public L2TamedBeastInstance getTrainedBeast() {
        return _tamedBeast;
    }

    /**
     * Set the L2Summon of the L2Player.<BR><BR>
     */
    public void setTrainedBeast(L2TamedBeastInstance tamedBeast) {
        _tamedBeast = tamedBeast;
    }

    /**
     * Return the L2Player requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).<BR><BR>
     */
    public L2Request getRequest() {
        if (_request == null)
            _request = new L2Request(this);

        return _request;
    }

    /**
     * Set the L2Player requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).<BR><BR>
     */
    public synchronized void setActiveRequester(L2Player requester) {
        _activeRequester = requester;
    }

    /**
     * Return true if last request is expired.
     * @return
     */
    public boolean isRequestExpired() {
        return !(_requestExpireTime > GameTimeManager.getGameTicks());
    }

    /**
     * Return the L2Player requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).<BR><BR>
     */
    public L2Player getActiveRequester() {
        return _activeRequester;
    }

    /**
     * Return True if a transaction is in progress.<BR><BR>
     */
    public boolean isProcessingRequest() {
        return _activeRequester != null || _requestExpireTime > GameTimeManager.getGameTicks();
    }

    /**
     * Return True if a transaction is in progress.<BR><BR>
     */
    public boolean isProcessingTransaction() {
        return _activeRequester != null || _activeTradeList != null
                || _requestExpireTime > GameTimeManager.getGameTicks();
    }

    /**
     * Select the Warehouse to be used in next activity.<BR><BR>
     */
    public void onTransactionRequest(L2Player partner) {
        _requestExpireTime = GameTimeManager.getGameTicks() + REQUEST_TIMEOUT * GameTimeManager.TICKS_PER_SECOND;
        partner.setActiveRequester(this);
    }

    /**
     * Select the Warehouse to be used in next activity.<BR><BR>
     */
    public void onTransactionResponse() {
        _requestExpireTime = 0;
    }

    /**
     * Select the Warehouse to be used in next activity.<BR><BR>
     */
    public void setActiveWarehouse(ItemContainer warehouse) {
        _activeWarehouse = warehouse;
    }

    /**
     * Return active Warehouse.<BR><BR>
     */
    public ItemContainer getActiveWarehouse() {
        return _activeWarehouse;
    }

    /**
     * Select the TradeList to be used in next activity.<BR><BR>
     */
    public void setActiveTradeList(TradeList tradeList) {
        _activeTradeList = tradeList;
    }

    /**
     * Return active TradeList.<BR><BR>
     */
    public TradeList getActiveTradeList() {
        return _activeTradeList;
    }

    public void onTradeStart(L2Player partner) {
        _activeTradeList = new TradeList(this);
        _activeTradeList.setPartner(partner);

        SystemMessage msg = new SystemMessage(SystemMessageId.BEGIN_TRADE_WITH_C1);
        msg.addPcName(partner);
        sendPacket(msg);
        sendPacket(new TradeStart(this));
    }

    public void onTradeConfirm(L2Player partner) {
        SystemMessage msg = new SystemMessage(SystemMessageId.C1_CONFIRMED_TRADE);
        msg.addPcName(partner);
        sendPacket(msg);
        sendPacket(TradeOtherDone.STATIC_PACKET);
    }

    public void onTradeCancel(L2Player partner) {
        if (_activeTradeList == null)
            return;

        _activeTradeList.lock();
        _activeTradeList = null;
        sendPacket(TradeDone.CANCELLED);
        SystemMessage msg = new SystemMessage(SystemMessageId.C1_CANCELED_TRADE);
        msg.addPcName(partner);
        sendPacket(msg);
    }

    public void onTradeFinish(boolean successfull) {
        _activeTradeList = null;
        sendPacket(TradeDone.COMPLETED);
        if (successfull)
            sendPacket(SystemMessageId.TRADE_SUCCESSFUL);
    }

    public void startTrade(L2Player partner) {
        onTradeStart(partner);
        partner.onTradeStart(this);
    }

    public void cancelActiveTrade() {
        if (_activeTradeList == null)
            return;

        L2Player partner = _activeTradeList.getPartner();
        if (partner != null)
            partner.onTradeCancel(this);
        onTradeCancel(this);
    }

    /**
     * Return the _createList object of the L2Player.<BR><BR>
     */
    public L2ManufactureList getCreateList() {
        return _createList;
    }

    /**
     * Set the _createList object of the L2Player.<BR><BR>
     */
    public void setCreateList(L2ManufactureList x) {
        _createList = x;
    }

    /**
     * Return the _buyList object of the L2Player.<BR><BR>
     */
    public TradeList getSellList() {
        if (_sellList == null)
            _sellList = new TradeList(this);
        return _sellList;
    }

    /**
     * Return the _buyList object of the L2Player.<BR><BR>
     */
    public TradeList getBuyList() {
        if (_buyList == null)
            _buyList = new TradeList(this);
        return _buyList;
    }

    /**
     * Set the Private Store type of the L2Player.<BR><BR>
     *
     * <B><U> Values </U> :</B><BR><BR>
     * <li>0 : STORE_PRIVATE_NONE</li>
     * <li>1 : STORE_PRIVATE_SELL</li>
     * <li>2 : sellmanage</li><BR>
     * <li>3 : STORE_PRIVATE_BUY</li><BR>
     * <li>4 : buymanage</li><BR>
     * <li>5 : STORE_PRIVATE_MANUFACTURE</li><BR>
     *
     */
    public void setPrivateStoreType(int type) {
        _privatestore = type;
    }

    /**
     * Return the Private Store type of the L2Player.<BR><BR>
     *
     * <B><U> Values </U> :</B><BR><BR>
     * <li>0 : STORE_PRIVATE_NONE</li>
     * <li>1 : STORE_PRIVATE_SELL</li>
     * <li>2 : sellmanage</li><BR>
     * <li>3 : STORE_PRIVATE_BUY</li><BR>
     * <li>4 : buymanage</li><BR>
     * <li>5 : STORE_PRIVATE_MANUFACTURE</li><BR>
     *
     */
    public int getPrivateStoreType() {
        return _privatestore;
    }

    /**
     * Set the _skillLearningClassId object of the L2Player.<BR><BR>
     */
    public void setSkillLearningClassId(ClassId classId) {
        _skillLearningClassId = classId;
    }

    /**
     * Return the _skillLearningClassId object of the L2Player.<BR><BR>
     */
    public ClassId getSkillLearningClassId() {
        return _skillLearningClassId;
    }

    /**
     * Set the _clan object, _clanId, _clanLeader Flag and title of the L2Player.<BR><BR>
     */
    public void setClan(L2Clan clan) {
        _clan = clan;
        setTitle("");

        if (clan == null) {
            _clanId = 0;
            _clanPrivileges = 0;
            _subPledgeType = 0;
            _pledgeRank = 0;
            _lvlJoinedAcademy = 0;
            _apprentice = 0;
            _sponsor = 0;
            return;
        }

        if (!clan.isMember(getObjectId())) {
            // Char has been kicked from clan
            setClan(null);
            return;
        }

        _clanId = clan.getClanId();
    }

    /**
     * Return the _clan object of the L2Player.<BR><BR>
     */
    public L2Clan getClan() {
        return _clan;
    }

    /**
     * Return True if the L2Player is the leader of its clan.<BR><BR>
     */
    public boolean isClanLeader() {
        return (getClan() != null) && getObjectId() == getClan().getLeaderId();
    }

    /**
     * Disarm the player's weapon and shield.<BR><BR>
     */
    public boolean disarmWeapons(boolean shield) {
        // Don't allow disarming a cursed weapon
        if (isCursedWeaponEquipped())
            return false;

        // Unequip the weapon
        L2ItemInstance wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
        if (wpn == null)
            wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
        if (wpn != null) {
            if (wpn.isWear())
                return false;

            L2ItemInstance[] unequipped = getInventory()
                    .unEquipItemInBodySlotAndRecord(wpn.getItem().getBodyPart());
            InventoryUpdate iu = new InventoryUpdate();
            for (L2ItemInstance element : unequipped)
                iu.addModifiedItem(element);
            sendPacket(iu);

            abortAttack();
            refreshExpertisePenalty();

            // This can be 0 if the user pressed the right mousebutton twice very fast
            if (unequipped.length > 0) {
                SystemMessage sm = null;
                if (unequipped[0].getEnchantLevel() > 0) {
                    sm = new SystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
                    sm.addNumber(unequipped[0].getEnchantLevel());
                    sm.addItemName(unequipped[0]);
                } else {
                    sm = new SystemMessage(SystemMessageId.S1_DISARMED);
                    sm.addItemName(unequipped[0]);
                }
                sendPacket(sm);
            }
            broadcastFullInfoImpl();
        }

        if (!shield)
            return true;

        // Unequip the shield
        L2ItemInstance sld = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
        if (sld != null) {
            if (sld.isWear())
                return false;

            L2ItemInstance[] unequipped = getInventory()
                    .unEquipItemInBodySlotAndRecord(sld.getItem().getBodyPart());
            InventoryUpdate iu = new InventoryUpdate();
            for (L2ItemInstance element : unequipped)
                iu.addModifiedItem(element);
            sendPacket(iu);

            abortAttack();
            refreshExpertisePenalty();

            // This can be 0 if the user pressed the right mousebutton twice very fast
            if (unequipped.length > 0) {
                SystemMessage sm = null;
                if (unequipped[0].getEnchantLevel() > 0) {
                    sm = new SystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
                    sm.addNumber(unequipped[0].getEnchantLevel());
                    sm.addItemName(unequipped[0]);
                } else {
                    sm = new SystemMessage(SystemMessageId.S1_DISARMED);
                    sm.addItemName(unequipped[0]);
                }
                sendPacket(sm);
            }
            broadcastFullInfoImpl();
        }

        return true;
    }

    /**
     * Reduce the number of arrows/bolts owned by the L2Player and send it Server->Client Packet InventoryUpdate or ItemList (to unequip if the last arrow was consummed).<BR><BR>
     */
    @Override
    protected void reduceArrowCount(boolean bolts) {
        L2ItemInstance arrows = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);

        if (arrows == null) {
            getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
            if (bolts)
                _boltItem = null;
            else
                _arrowItem = null;
            sendPacket(new ItemList(this, false));
            return;
        }

        // Adjust item quantity
        if (arrows.getCount() > 1) {
            synchronized (arrows) {
                arrows.changeCountWithoutTrace(-1, this, null);
                arrows.setLastChange(L2ItemInstance.MODIFIED);

                // Could do also without saving, but let's save approx 1 of 10
                if (GameTimeManager.getGameTicks() % 10 == 0)
                    arrows.updateDatabase();
                getInventory().refreshWeight();
            }
        } else {
            // Destroy entire item and save to database
            getInventory().destroyItem("Consume", arrows, this, null);

            getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
            if (bolts)
                _boltItem = null;
            else
                _arrowItem = null;

            if (_log.isDebugEnabled())
                _log.debug("removed arrows count");
            sendPacket(new ItemList(this, false));
            return;
        }

        if (!Config.FORCE_INVENTORY_UPDATE) {
            InventoryUpdate iu = new InventoryUpdate();
            iu.addModifiedItem(arrows);
            sendPacket(iu);
        } else
            sendPacket(new ItemList(this, false));
    }

    /**
     * Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2Player then return True.<BR><BR>
     */
    @Override
    protected boolean checkAndEquipArrows() {
        // Check if nothing is equipped in left hand
        if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) {
            // Get the L2ItemInstance of the arrows needed for this bow
            _arrowItem = getInventory().findArrowForBow(getActiveWeaponItem());

            if (_arrowItem != null) {
                // Equip arrows needed in left hand
                getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _arrowItem);
                // Send inventory update packet
                getInventory().updateInventory(_arrowItem);
            }
        } else {
            // Get the L2ItemInstance of arrows equipped in left hand
            _arrowItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
        }

        return _arrowItem != null;
    }

    /**
     * Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2Player then return True.<BR><BR>
     */
    @Override
    protected boolean checkAndEquipBolts() {
        // Check if nothing is equipped in left hand
        if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) {
            // Get the L2ItemInstance of the arrows needed for this bow
            _boltItem = getInventory().findBoltForCrossBow(getActiveWeaponItem());

            if (_boltItem != null) {
                // Equip arrows needed in left hand
                getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _boltItem);

                // Send a Server->Client packet ItemList to this L2Player to update left hand equipement
                ItemList il = new ItemList(this, false);
                sendPacket(il);
            }
        } else {
            // Get the L2ItemInstance of arrows equipped in left hand
            _boltItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
        }

        return _boltItem != null;
    }

    public boolean mount(L2Summon pet) {
        // TODO: all checks from usercommandhandler mount and requestactionuse should be handled in ONE place, and this is not L2Player
        // so this is temporary
        if (!isInsideRadius(pet, 80, true, false)) {
            sendPacket(SystemMessageId.TOO_FAR_AWAY_FROM_FENRIR_TO_MOUNT);
            return false;
        } else if (!GeoData.getInstance().canSeeTarget(this, pet)) {
            sendPacket(SystemMessageId.CANT_SEE_TARGET);
            return false;
        }

        if (!disarmWeapons(true))
            return false;
        if (isTransformed())
            return false;

        for (L2Effect e : getAllEffects()) {
            if (e != null && e.getSkill().isToggle())
                e.exit();
        }

        Ride mount = new Ride(this, true, pet.getTemplate().getNpcId());
        setMount(pet.getNpcId(), pet.getLevel(), mount.getMountType());
        setMountObjectID(pet.getControlItemId());
        clearPetData();
        startFeed(pet.getNpcId());
        broadcastPacket(mount);

        // Notify self and others about speed change
        broadcastUserInfo();

        pet.unSummon(this);

        return true;
    }

    public boolean remount(L2Player player) {
        Ride dismount = new Ride(this, false, 0);
        Ride mount = new Ride(this, true, getMountNpcId());

        player.sendPacket(dismount);
        player.sendPacket(mount);
        return true;
    }

    public boolean mount(int npcId, int controlItemObjId, boolean useFood) {
        if (!disarmWeapons(true))
            return false;
        if (isTransformed())
            return false;

        for (L2Effect e : getAllEffects()) {
            if (e != null && e.getSkill().isToggle())
                e.exit();
        }

        Ride mount = new Ride(this, true, npcId);
        if (setMount(npcId, getLevel(), mount.getMountType())) {
            clearPetData();
            setMountObjectID(controlItemObjId);
            broadcastPacket(mount);
            // Notify self and others about speed change
            broadcastFullInfoImpl();
            if (useFood)
                startFeed(npcId);
            return true;
        }
        return false;
    }

    public boolean mountPlayer(L2Summon pet) {
        if (pet != null && pet.isMountable() && !isMounted() && !isBetrayed() && !pet.isOutOfControl()) {
            if (pet.getNpcId() == 16030 && pet.getLevel() < Config.GREAT_WOLF_MOUNT_LEVEL) {
                sendMessage("Your Wolf needs minimum level " + Config.GREAT_WOLF_MOUNT_LEVEL);
                return false;
            } else if (isInEvent(CTFPlayerInfo.class) && as(CTFPlayerInfo.class)._haveFlagCTF) {
                // You cannot mount a steed while holding a flag.
                sendPacket(SystemMessageId.YOU_CANNOT_MOUNT_A_STEED_WHILE_HOLDING_A_FLAG);
                return false;
            } else if (isParalyzed()) {
                // You cannot mount a steed while petrified.
                sendPacket(SystemMessageId.YOU_CANNOT_MOUNT_A_STEED_WHILE_PETRIFIED);
                return false;
            } else if (isDead()) {
                //A strider cannot be ridden when dead
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_DEAD);
                return false;
            } else if (pet.isDead()) {
                //A dead strider cannot be ridden.
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.DEAD_STRIDER_CANT_BE_RIDDEN);
                return false;
            } else if (pet.isInCombat() || pet.isRooted() || pet.isParalyzed()) {
                //A strider in battle cannot be ridden
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.STRIDER_IN_BATLLE_CANT_BE_RIDDEN);
                return false;

            } else if (isInCombat()) {
                //A strider cannot be ridden while in battle
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_IN_BATTLE);
                return false;
            } else if (isSitting() || isInsideZone(L2Zone.FLAG_WATER)) {
                //A strider can be ridden only when standing
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.STRIDER_CAN_BE_RIDDEN_ONLY_WHILE_STANDING);
                return false;
            } else if (isFishing()) {
                //You can't mount, dismount, break and drop items while fishing
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.CANNOT_DO_WHILE_FISHING_2);
                return false;
            } else if (isInDuel()) {
                // You cannot mount a steed while in a duel.
                sendPacket(SystemMessageId.YOU_CANNOT_MOUNT_A_STEED_WHILE_IN_A_DUEL);
                return false;
            } else if (isTransformed() || isCursedWeaponEquipped()) {
                // no message needed, player while transformed doesn't have mount action
                sendPacket(ActionFailed.STATIC_PACKET);
                return false;
            } else if (getInventory().getItemByItemId(9819) != null) {
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.YOU_CANNOT_MOUNT_A_STEED_WHILE_HOLDING_A_FLAG); // TODO: confirm this message
                return false;
            } else if (isCastingNow()) {
                // You cannot mount a steed while skill casting.
                sendPacket(SystemMessageId.YOU_CANNOT_MOUNT_A_STEED_WHILE_SKILL_CASTING);
                return false;
            } else if (pet.isHungry()) {
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.HUNGRY_STRIDER_NOT_MOUNT);
                return false;
            } else if (!Util.checkIfInRange(200, this, pet, true)) {
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.TOO_FAR_AWAY_FROM_FENRIR_TO_MOUNT);
                return false;
            } else if (!pet.isDead() && !isMounted()) {
                mount(pet);
            }
        } else if (isRentedPet()) {
            stopRentPet();
        } else if (isMounted()) {
            if (getMountType() == 2 && isInsideZone(L2Zone.FLAG_NOWYVERN)) {
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.NO_DISMOUNT_HERE);
                return false;
            } else if (isHungry()) {
                sendPacket(ActionFailed.STATIC_PACKET);
                sendPacket(SystemMessageId.HUNGRY_STRIDER_NOT_MOUNT);
                return false;
            } else if (ObjectRestrictions.getInstance().checkRestriction(this,
                    AvailableRestriction.PlayerUnmount)) {
                sendPacket(ActionFailed.STATIC_PACKET);
                sendMessage("You cannot dismount due to a restriction.");
                return false;
            } else
                dismount();
        }
        return true;
    }

    public boolean dismount() {
        boolean wasFlying = isFlying();

        sendPacket(new SetupGauge(3, 0, 0));
        int petId = _mountNpcId;
        if (setMount(0, 0, 0)) {
            stopFeed();
            clearPetData();

            if (wasFlying)
                removeSkill(SkillTable.getInstance().getInfo(4289, 1));
            Ride dismount = new Ride(this, false, 0);
            broadcastPacket(dismount);
            setMountObjectID(0);
            storePetFood(petId);

            // Notify self and others about speed change
            broadcastUserInfo();
            return true;
        }
        return false;
    }

    /**
     * Return True if the L2Player use a dual weapon.<BR><BR>
     */
    @Override
    public boolean isUsingDualWeapon() {
        L2Weapon weaponItem = getActiveWeaponItem();

        if (weaponItem == null)
            return false;
        if (weaponItem.getItemType() == L2WeaponType.DUAL) {
            return true;
        } else if (weaponItem.getItemType() == L2WeaponType.DUAL_DAGGER) {
            return true;
        } else if (weaponItem.getItemType() == L2WeaponType.DUALFIST) {
            return true;
        } else if (weaponItem.getItemId() == 248) // Orc fighter fists
        {
            return true;
        } else
            return weaponItem.getItemId() == 252;
    }

    public void setUptime(long time) {
        _uptime = time;
    }

    public long getUptime() {
        return System.currentTimeMillis() - _uptime;
    }

    public long getOnlineTime() {
        long totalOnlineTime = _onlineTime;

        if (_onlineBeginTime > 0)
            totalOnlineTime += (System.currentTimeMillis() - _onlineBeginTime) / 1000;

        return totalOnlineTime;
    }

    /**
     * Return True if the L2Player is invulnerable.<BR><BR>
     */
    @Override
    public boolean isInvul() {
        return super.isInvul() || _protectEndTime > GameTimeManager.getGameTicks();
    }

    /**
     * Return True if the L2Player has a Party in progress.<BR><BR>
     */
    @Override
    public boolean isInParty() {
        return _party != null;
    }

    /**
     * Set the _party object of the L2Player (without joining it).<BR><BR>
     */
    public void setParty(L2Party party) {
        _party = party;
    }

    /**
     * Set the _party object of the L2Player AND join it.<BR><BR>
     */
    public void joinParty(L2Party party) {
        if (party != null) {
            // First set the party otherwise this wouldn't be considered
            // as in a party into the L2Creature.updateEffectIcons() call.
            setParty(party);
            if (!party.addPartyMember(this))
                setParty(null);
        }
    }

    /**
     * Manage the Leave Party task of the L2Player.<BR><BR>
     */
    public void leaveParty() {
        if (isInParty()) {
            _party.removePartyMember(this, false);
            _party = null;
        }
    }

    /**
     * Return the _party object of the L2Player.<BR><BR>
     */
    @Override
    public L2Party getParty() {
        return _party;
    }

    /**
     * Set the _isGm Flag of the L2Player.<BR><BR>
     */
    public void setIsGM(boolean status) {
        _isGm = status;
    }

    /**
     * Return True if the L2Player is a GM.<BR><BR>
     */
    public boolean isGM() {
        return _isGm;
    }

    /**
     * Set the _accessLevel of the L2Player.<BR><BR>
     */
    public void setAccessLevel(int level) {
        _accessLevel = level;
        if (_accessLevel >= Config.GM_MIN)
            setIsGM(true);
    }

    public void setAccountAccesslevel(int level) {
        LoginServerThread.getInstance().sendAccessLevel(getAccountName(), level);
    }

    /**
     * Return the _accessLevel of the L2Player.<BR><BR>
     */
    public int getAccessLevel() {
        return _accessLevel;
    }

    @Override
    public double getLevelMod() {
        return (89 + getLevel()) / 100.0;
    }

    /**
     * Send a Server->Client StatusUpdate packet with Karma and PvP Flag to the L2Player and all L2Player to inform (broadcast).<BR><BR>
     * @param flag
     */
    public void setKarmaFlag(int flag) {
        sendPacket(new UserInfo(this));
        broadcastRelationChanged();
    }

    /**
     * Send a Server->Client StatusUpdate packet with Karma to the L2Player and all L2Player to inform (broadcast).<BR><BR>
     */
    public void broadcastKarma() {
        StatusUpdate su = new StatusUpdate(getObjectId());
        su.addAttribute(StatusUpdate.KARMA, getKarma());
        sendPacket(su);
        broadcastRelationChanged();
    }

    /**
     * Set the online Flag to True or False and update the characters table of the database with online status and lastAccess (called when login and logout).<BR><BR>
     */
    public void setOnlineStatus(boolean isOnline) {
        final byte value = isOnline ? ONLINE_STATE_ONLINE : ONLINE_STATE_DELETED;

        if (_isOnline != value) {
            _isOnline = value;

            // Update the characters table of the database with online status and lastAccess (called when login and logout)
            updateOnlineStatusInDb();
        }
    }

    public void setIsIn7sDungeon(boolean isIn7sDungeon) {
        _isIn7sDungeon = isIn7sDungeon;
    }

    /**
     * Update the characters table of the database with online status and lastAccess of this L2Player (called when
     * login and logout).<BR>
     * <BR>
     */
    private void updateOnlineStatusInDb() {
        RecordTable.getInstance().update();

        SQLQueue.getInstance().add(new SQLQuery() {
            @Override
            public void execute(Connection con) {
                try {
                    PreparedStatement statement = con
                            .prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE charId=?");
                    statement.setInt(1, isOnline());
                    statement.setLong(2, System.currentTimeMillis());
                    statement.setInt(3, getObjectId());
                    statement.execute();
                    statement.close();
                } catch (Exception e) {
                    _log.error("Failed updating character online status.", e);
                }
            }
        });
    }

    /**
     * Create a new player in the characters table of the database.<BR><BR>
     */
    private boolean createDb() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement("INSERT INTO characters "
                    + "(account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,"
                    + "face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,"
                    + "classid,deletetime,cancraft,title,accesslevel,online,isin7sdungeon,clan_privs,"
                    + "wantspeace,base_class,newbie,nobless,pledge_rank) "
                    + "values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
            statement.setString(1, _accountName);
            statement.setInt(2, getObjectId());
            statement.setString(3, getName());
            statement.setInt(4, getLevel());
            statement.setInt(5, getMaxHp());
            statement.setDouble(6, getStatus().getCurrentHp());
            statement.setInt(7, getMaxCp());
            statement.setDouble(8, getStatus().getCurrentCp());
            statement.setInt(9, getMaxMp());
            statement.setDouble(10, getStatus().getCurrentMp());
            statement.setInt(11, getAppearance().getFace());
            statement.setInt(12, getAppearance().getHairStyle());
            statement.setInt(13, getAppearance().getHairColor());
            statement.setInt(14, getAppearance().getSex() ? 1 : 0);
            statement.setLong(15, getExp());
            statement.setInt(16, getSp());
            statement.setInt(17, getKarma());
            statement.setInt(18, getFame());
            statement.setInt(19, getPvpKills());
            statement.setInt(20, getPkKills());
            statement.setInt(21, getClanId());
            statement.setInt(22, getRace().ordinal());
            statement.setInt(23, getClassId().getId());
            statement.setLong(24, getDeleteTimer());
            statement.setInt(25, hasDwarvenCraft() ? 1 : 0);
            statement.setString(26, getTitle());
            statement.setInt(27, getAccessLevel());
            statement.setInt(28, isOnline());
            statement.setInt(29, isIn7sDungeon() ? 1 : 0);
            statement.setInt(30, getClanPrivileges());
            statement.setInt(31, getWantsPeace());
            statement.setInt(32, getBaseClass());
            statement.setInt(33, getNewbie());
            statement.setInt(34, isNoble() ? 1 : 0);
            statement.setLong(35, 0);

            statement.executeUpdate();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not insert char data: ", e);
            return false;
        } finally {
            L2DatabaseFactory.close(con);
        }

        return true;
    }

    public static void disconnectIfOnline(int objectId) {
        L2Player onlinePlayer = L2World.getInstance().findPlayer(objectId);

        if (onlinePlayer == null)
            onlinePlayer = L2World.getInstance().getPlayer(CharNameTable.getInstance().getByObjectId(objectId));

        if (onlinePlayer == null)
            return;

        if (!onlinePlayer.isInOfflineMode())
            _log.warn("Avoiding duplicate character! Disconnecting online character (" + onlinePlayer.getName()
                    + ")");

        new Disconnection(onlinePlayer).defaultSequence(true);
    }

    public static void disconnectIfOnline(String accountName) {
        for (int objectId : CharNameTable.getInstance().getObjectIdsForAccount(accountName))
            disconnectIfOnline(objectId);
    }

    /**
     * Retrieve a L2Player from the characters table of the database and add it in _allObjects of the L2world.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Retrieve the L2Player from the characters table of the database </li>
     * <li>Add the L2Player object in _allObjects </li>
     * <li>Set the x,y,z position of the L2Player and make it invisible</li>
     * <li>Update the overloaded status of the L2Player</li><BR><BR>
     *
     * @param objectId Identifier of the object to initialized
     *
     * @return The L2Player loaded from the database
     *
     */
    public static L2Player load(int objectId) {
        disconnectIfOnline(objectId);

        L2Player player = null;
        Connection con = null;

        try {
            // Retrieve the L2Player from the characters table of the database
            con = L2DatabaseFactory.getInstance().getConnection(con);

            PreparedStatement statement = con.prepareStatement(RESTORE_CHARACTER);
            statement.setInt(1, objectId);
            ResultSet rset = statement.executeQuery();

            double currentHp = 1, currentMp = 1, currentCp = 1;
            if (rset.next()) {
                final int activeClassId = rset.getInt("classid");
                final boolean female = rset.getInt("sex") != 0;
                final L2PlayerTemplate template = CharTemplateTable.getInstance().getTemplate(activeClassId);
                PlayerAppearance app = new PlayerAppearance(rset.getByte("face"), rset.getByte("hairColor"),
                        rset.getByte("hairStyle"), female);

                player = new L2Player(objectId, template, rset.getString("account_name"), app);
                player.setName(rset.getString("char_name"));
                player._lastAccess = rset.getLong("lastAccess");

                player.getStat().setExp(rset.getLong("exp"));
                player.setExpBeforeDeath(rset.getLong("expBeforeDeath"));
                player.getStat().setLevel(rset.getByte("level"));
                player.getStat().setSp(rset.getInt("sp"));

                player.setWantsPeace(rset.getInt("wantspeace"));

                player.setHeading(rset.getInt("heading"));

                player.setKarma(rset.getInt("karma"));
                player.setFame(rset.getInt("fame"));
                player.setPvpKills(rset.getInt("pvpkills"));
                player.setPkKills(rset.getInt("pkkills"));

                player.setClanJoinExpiryTime(rset.getLong("clan_join_expiry_time"));
                if (player.getClanJoinExpiryTime() < System.currentTimeMillis()) {
                    player.setClanJoinExpiryTime(0);
                }
                player.setClanCreateExpiryTime(rset.getLong("clan_create_expiry_time"));
                if (player.getClanCreateExpiryTime() < System.currentTimeMillis()) {
                    player.setClanCreateExpiryTime(0);
                }

                int clanId = rset.getInt("clanid");

                if (clanId > 0) {
                    player.setClan(ClanTable.getInstance().getClan(clanId));
                }

                player.setDeleteTimer(rset.getLong("deletetime"));
                player.setOnlineTime(rset.getLong("onlinetime"));
                player.setNewbie(rset.getInt("newbie"));
                player.setNoble(rset.getInt("nobless") == 1);

                player.setTitle(rset.getString("title"));
                player.setAccessLevel(rset.getInt("accesslevel"));
                player.setFistsWeaponItem(player.findFistsWeaponItem(activeClassId));
                player.setUptime(System.currentTimeMillis());

                // Only 1 line needed for each and their values only have to be set once as long as you don't die before it's set.
                currentHp = rset.getDouble("curHp");
                currentMp = rset.getDouble("curMp");
                currentCp = rset.getDouble("curCp");

                player._classIndex = 0;
                try {
                    player.setBaseClass(rset.getInt("base_class"));
                } catch (Exception e) {
                    player.setBaseClass(activeClassId);
                }

                // Restore Subclass Data (cannot be done earlier in function)
                if (restoreSubClassData(player)) {
                    if (activeClassId != player.getBaseClass()) {
                        for (SubClass subClass : player.getSubClasses().values())
                            if (subClass.getClassId() == activeClassId)
                                player._classIndex = subClass.getClassIndex();
                    }
                }
                if (player.getClassIndex() == 0 && activeClassId != player.getBaseClass()) {
                    // Subclass in use but doesn't exist in DB -
                    // a possible restart-while-modifysubclass cheat has been attempted.
                    // Switching to use base class
                    player.setClassId(player.getBaseClass());
                    _log.warn("Player " + player.getName()
                            + " reverted to base class. Possibly has tried a relogin exploit while subclassing.");
                } else
                    player._activeClass = activeClassId;

                player.setIsIn7sDungeon(rset.getInt("isin7sdungeon") == 1);
                player.setInJail(rset.getInt("in_jail") == 1);
                player.setJailTimer(rset.getLong("jail_timer"));
                player.setBanChatTimer(rset.getLong("banchat_timer"));
                if (player.isInJail())
                    player.setJailTimer(rset.getLong("jail_timer"));
                else
                    player.setJailTimer(0);

                CursedWeaponsManager.getInstance().onEnter(player);

                player.setNoble(rset.getBoolean("nobless"));
                player.setCharViP((rset.getInt("charViP") == 1));
                player.setSubPledgeType(rset.getInt("subpledge"));
                player.setPledgeRank(rset.getInt("pledge_rank"));
                player.setApprentice(rset.getInt("apprentice"));
                player.setSponsor(rset.getInt("sponsor"));
                if (player.getClan() != null) {
                    if (player.getClan().getLeaderId() != player.getObjectId()) {
                        if (player.getPledgeRank() == 0) {
                            player.setPledgeRank(5);
                        }
                        player.setClanPrivileges(player.getClan().getRankPrivs(player.getPledgeRank()));
                    } else {
                        player.setClanPrivileges(L2Clan.CP_ALL);
                        player.setPledgeRank(1);
                    }
                } else {
                    player.setClanPrivileges(L2Clan.CP_NOTHING);
                }
                player.setLvlJoinedAcademy(rset.getInt("lvl_joined_academy"));
                player.setAllianceWithVarkaKetra(rset.getInt("varka_ketra_ally"));
                player.setDeathPenaltyBuffLevel(rset.getInt("death_penalty_level"));
                player.setVitalityPoints(rset.getInt("vitality_points"), true);

                // Add the L2Player object in _allObjects
                // L2World.getInstance().storeObject(player);

                // Set the x,y,z position of the L2Player and make it invisible
                player.getPosition().setXYZInvisible(rset.getInt("x"), rset.getInt("y"), rset.getInt("z"));

                // Set Teleport Bookmark Slot
                player.setBookMarkSlot(rset.getInt("BookmarkSlot"));
            }

            rset.close();
            statement.close();

            if (player == null)
                return null;

            // Retrieve from the database all secondary data of this L2Player
            // and reward expertise/lucky skills if necessary.
            player.restoreCharData();
            player.rewardSkills();

            // Buff and status icons
            player.getEffects().restoreEffects();
            player.restoreSkillReuses();

            player.stopEffects(L2EffectType.HEAL_OVER_TIME);
            player.stopEffects(L2EffectType.COMBAT_POINT_HEAL_OVER_TIME);

            // Restore current Cp, HP and MP values
            player.getStatus().setCurrentCp(currentCp);
            player.getStatus().setCurrentHp(currentHp);
            player.getStatus().setCurrentMp(currentMp);

            if (currentHp < 0.5) {
                player.setIsDead(true);
                player.getStatus().stopHpMpRegeneration();
            }

            // Restore pet if exists in the world
            player.setPet(L2World.getInstance().getPet(player.getObjectId()));
            if (player.getPet() != null)
                player.getPet().setOwner(player);

            // refresh overloaded already done when loading inventory
            // Update the expertise status of the L2Player
            player.refreshExpertisePenalty();
        } catch (Exception e) {
            _log.error("Failed loading character.", e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        return player;
    }

    /**
     * @return
     */
    public Forum getMail() {
        if (_forumMail == null) {
            setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName()));

            if (_forumMail == null) {
                ForumsBBSManager.getInstance().createNewForum(getName(),
                        ForumsBBSManager.getInstance().getForumByName("MailRoot"), Forum.MAIL, Forum.OWNERONLY,
                        getObjectId());
                setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName()));
            }
        }

        return _forumMail;
    }

    /**
     * @param forum
     */
    public void setMail(Forum forum) {
        _forumMail = forum;
    }

    /**
     * @return
     */
    public Forum getMemo() {
        if (_forumMemo == null) {
            setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName));

            if (_forumMemo == null) {
                ForumsBBSManager.getInstance().createNewForum(_accountName,
                        ForumsBBSManager.getInstance().getForumByName("MemoRoot"), Forum.MEMO, Forum.OWNERONLY,
                        getObjectId());
                setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName));
            }
        }

        return _forumMemo;
    }

    /**
     * @param forum
     */
    public void setMemo(Forum forum) {
        _forumMemo = forum;
    }

    /**
     * Restores sub-class data for the L2Player, used to check the current
     * class index for the character.
     */
    private static boolean restoreSubClassData(L2Player player) {
        Connection con = null;

        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_SUBCLASSES);
            statement.setInt(1, player.getObjectId());

            ResultSet rset = statement.executeQuery();

            while (rset.next()) {
                SubClass subClass = new SubClass();
                subClass.setClassId(rset.getInt("class_id"));
                subClass.setLevel(rset.getByte("level"));
                subClass.setExp(rset.getLong("exp"));
                subClass.setSp(rset.getInt("sp"));
                subClass.setClassIndex(rset.getInt("class_index"));

                // Enforce the correct indexing of _subClasses against their class indexes.
                player.getSubClasses().put(subClass.getClassIndex(), subClass);
            }

            statement.close();
        } catch (Exception e) {
            _log.error("Could not restore classes for " + player.getName() + ": ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        return true;
    }

    /**
     * Restores secondary data for the L2Player, based on the current class index.
     */
    private void restoreCharData() {
        // Retrieve from the database all skills of this L2Player and add them to _skills.
        restoreSkills();

        // Retrieve from the database all macroses of this L2Player and add them to macroses.
        getMacroses().restore();

        // Retrieve from the database all shortCuts of this L2Player and add them to shortCuts.
        getShortCuts().restore();

        // Retrieve from the database all henna of this L2Player and add them to _henna.
        restoreHenna();

        // Retrieve from the database all teleport bookmark of this L2Player and add them to _tpbookmark.
        restoreTeleportBookmark();

        // Retrieve from the database all recom data of this L2Player and add to _recomChars.
        RecommendationManager.getInstance().onJoin(this);

        // Retrieve from the database the recipe book of this L2Player.
        restoreRecipeBook(true);

        restoreCreationDate();

        restoreNameTitleColors();
    }

    /**
     * Restore recipe book data for this L2Player.
     */
    private void restoreRecipeBook(boolean loadCommon) {
        Connection con = null;

        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            String sql = loadCommon ? "SELECT id, type, classIndex FROM character_recipebook WHERE charId=?"
                    : "SELECT id FROM character_recipebook WHERE charId=? AND classIndex=? AND type = 1";
            PreparedStatement statement = con.prepareStatement(sql);
            statement.setInt(1, getObjectId());
            if (!loadCommon)
                statement.setInt(2, _classIndex);
            ResultSet rset = statement.executeQuery();

            _dwarvenRecipeBook.clear();

            L2RecipeList recipe;
            while (rset.next()) {
                recipe = RecipeTable.getInstance().getRecipeList(rset.getInt("id"));

                if (loadCommon) {
                    if (rset.getInt(2) == 1) {
                        if (rset.getInt(3) == _classIndex)
                            registerDwarvenRecipeList(recipe, false);
                    } else
                        registerCommonRecipeList(recipe, false);
                } else
                    registerDwarvenRecipeList(recipe, false);
            }

            rset.close();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not restore recipe book data:", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    /** player coords from client */
    private int _clientX;
    private int _clientY;
    private int _clientZ;
    private int _clientHeading;

    @Override
    public int getClientX() {
        return _clientX;
    }

    @Override
    public int getClientY() {
        return _clientY;
    }

    @Override
    public int getClientZ() {
        return _clientZ;
    }

    @Override
    public int getClientHeading() {
        return _clientHeading;
    }

    public void setClientX(int val) {
        _clientX = val;
    }

    public void setClientY(int val) {
        _clientY = val;
    }

    public void setClientZ(int val) {
        _clientZ = val;
    }

    public void setClientHeading(int val) {
        _clientHeading = val;
    }

    /**
     * Update L2Player stats in the characters table of the database.<BR><BR>
     */
    private long _lastStore;

    public void store() {
        store(false, true);
    }

    public void store(boolean storeActiveEffects) {
        store(false, storeActiveEffects);
    }

    public synchronized void store(boolean items, boolean storeActiveEffects) {
        _lastStore = System.currentTimeMillis();

        if (getOnlineState() == ONLINE_STATE_DELETED)
            return;

        // Update client coords, if these look like true
        // if (isInsideRadius(getClientX(), getClientY(), 1000, true))
        //   getPosition().setXYZ(getClientX(), getClientY(), getClientZ());

        storeCharBase();
        storeCharSub();
        storePet();
        getEffects().storeEffects(storeActiveEffects);
        storeSkillReuses();
        transformInsertInfo();
        storeNameTitleColors();

        if (Config.UPDATE_ITEMS_ON_CHAR_STORE || items)
            getInventory().updateDatabase();
    }

    private void storeCharBase() {
        Connection con = null;

        try {
            // Get the exp, level, and sp of base class to store in base table
            int currentClassIndex = getClassIndex();
            _classIndex = 0;
            long exp = getStat().getExp();
            int level = getStat().getLevel();
            int sp = getStat().getSp();
            _classIndex = currentClassIndex;

            con = L2DatabaseFactory.getInstance().getConnection(con);

            // Update base class
            PreparedStatement statement = con.prepareStatement(UPDATE_CHARACTER);
            statement.setInt(1, level);
            statement.setInt(2, getMaxHp());
            statement.setDouble(3, getStatus().getCurrentHp());
            statement.setInt(4, getMaxCp());
            statement.setDouble(5, getStatus().getCurrentCp());
            statement.setInt(6, getMaxMp());
            statement.setDouble(7, getStatus().getCurrentMp());
            statement.setInt(8, getAppearance().getFace());
            statement.setInt(9, getAppearance().getHairStyle());
            statement.setInt(10, getAppearance().getHairColor());
            statement.setInt(11, getAppearance().getSex() ? 1 : 0);
            statement.setInt(12, getHeading());
            statement.setInt(13, _observerMode ? _obsX : getX());
            statement.setInt(14, _observerMode ? _obsY : getY());
            statement.setInt(15, _observerMode ? _obsZ : getZ());
            statement.setLong(16, exp);
            statement.setLong(17, getExpBeforeDeath());
            statement.setInt(18, sp);
            statement.setInt(19, getKarma());
            statement.setInt(20, getFame());
            statement.setInt(21, getPvpKills());
            statement.setInt(22, getPkKills());
            statement.setInt(23, getClanId());
            statement.setInt(24, getRace().ordinal());
            statement.setInt(25, getClassId().getId());
            statement.setLong(26, getDeleteTimer());
            statement.setString(27, getTitle());
            statement.setInt(28, getAccessLevel());
            statement.setInt(29, isOnline());
            statement.setInt(30, isIn7sDungeon() ? 1 : 0);
            statement.setInt(31, getClanPrivileges());
            statement.setInt(32, getWantsPeace());
            statement.setInt(33, getBaseClass());

            statement.setLong(34, getOnlineTime());
            statement.setInt(35, isInJail() ? 1 : 0);
            statement.setLong(36, getJailTimer());
            statement.setInt(37, getNewbie());
            statement.setInt(38, isNoble() ? 1 : 0);
            statement.setLong(39, getPledgeRank());
            statement.setInt(40, getSubPledgeType());
            statement.setInt(41, getLvlJoinedAcademy());
            statement.setLong(42, getApprentice());
            statement.setLong(43, getSponsor());
            statement.setInt(44, getAllianceWithVarkaKetra());
            statement.setLong(45, getClanJoinExpiryTime());
            statement.setLong(46, getClanCreateExpiryTime());
            statement.setLong(47, getBanChatTimer());
            statement.setString(48, getName());
            statement.setLong(49, getDeathPenaltyBuffLevel());
            statement.setInt(50, getVitalityPoints());
            statement.setInt(51, getBookMarkSlot());
            statement.setInt(52, getObjectId());
            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not store char base data: ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    private void storeCharSub() {
        Connection con = null;

        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_SUBCLASS);

            if (getTotalSubClasses() > 0) {
                for (SubClass subClass : getSubClasses().values()) {
                    statement.setLong(1, subClass.getExp());
                    statement.setInt(2, subClass.getSp());
                    statement.setInt(3, subClass.getLevel());

                    statement.setInt(4, subClass.getClassId());
                    statement.setInt(5, getObjectId());
                    statement.setInt(6, subClass.getClassIndex());

                    statement.execute();
                }
            }
            statement.close();
        } catch (Exception e) {
            _log.error("Could not store sub class data for " + getName() + ": ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    private void storeSkillReuses() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();

            PreparedStatement statement = con.prepareStatement(DELETE_SKILL_REUSES);
            statement.setInt(1, getObjectId());
            statement.execute();
            statement.close();

            statement = con.prepareStatement(ADD_SKILL_REUSE);

            for (TimeStamp t : getReuseTimeStamps().values()) {
                if (t.getRemaining() > 10000) // store only over 10s
                {
                    statement.setInt(1, getObjectId());
                    statement.setInt(2, t.getSkillId());
                    statement.setInt(3, t.getReuseDelay());
                    statement.setLong(4, t.getExpiration());
                    statement.execute();
                }
            }

            statement.close();
        } catch (Exception e) {
            _log.error("", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    private void storePet() {
        L2Summon pet = getPet();
        if (pet != null)
            pet.store();
    }

    /**
     * Return True if the L2Player is on line.<BR><BR>
     */
    public int isOnline() {
        return (getOnlineState() == ONLINE_STATE_ONLINE) ? 1 : 0;
    }

    public byte getOnlineState() {
        return _isOnline;
    }

    public boolean isIn7sDungeon() {
        return _isIn7sDungeon;
    }

    /**
     * Add a skill to the L2Player _skills and its Func objects to the calculator set of the L2Player and save update in the character_skills table of the database.<BR><BR>
     *
     * <B><U> Concept</U> :</B><BR><BR>
     * All skills own by a L2Player 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>
     *
     * @param newSkill The L2Skill to add to the L2Creature
     *
     * @return The L2Skill replaced or null if just added a new L2Skill
     *
     */
    public L2Skill addSkill(L2Skill newSkill, boolean save) {
        // Add a skill to the L2Player _skills and its Func objects to the calculator set of the L2Player
        final L2Skill oldSkill = addSkill(newSkill);

        // Add or update a L2Player skill in the character_skills table of the database
        if (save)
            _pcSkills.storeSkill(newSkill, getClassIndex());

        return oldSkill;
    }

    @Override
    protected void skillChanged(L2Skill removed, L2Skill added) {
        super.skillChanged(removed, added);

        if (!L2System.equals(removed, added))
            sendSkillList();
    }

    public L2Skill removeSkill(L2Skill skill, boolean store) {
        return store ? removeSkill(skill) : super.removeSkill(skill);
    }

    /**
     * Remove a skill from the L2Creature and its Func objects from calculator set of the L2Creature and save update in the character_skills table of the database.<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
     *
     */
    @Override
    public L2Skill removeSkill(L2Skill skill) {
        if (skill == null)
            return null;

        // Remove a skill from the L2Creature and its Func objects from calculator set of the L2Creature
        L2Skill oldSkill = super.removeSkill(skill);

        _pcSkills.deleteSkill(skill);

        if (transformId() > 0 || isCursedWeaponEquipped())
            return oldSkill;

        for (L2ShortCut sc : getAllShortCuts()) {
            if (sc != null && sc.getId() == skill.getId() && sc.getType() == L2ShortCut.TYPE_SKILL
                    && !skill.isItemSkill()) {
                deleteShortCut(sc.getSlot(), sc.getPage());
            }
        }

        return oldSkill;
    }

    /**
     * check player skills and remove unlegit ones (excludes hero, noblesse and cursed weapon skills)
     */
    public void checkAllowedSkills() {
        if (isGM() || !Config.CHECK_SKILLS_ON_ENTER || Config.ALT_GAME_SKILL_LEARN)
            return;

        Set<Integer> skillTreeUIDs = SkillTreeTable.getInstance().getAllowedSkillUIDs(getClassId());

        skill_loop: for (L2Skill skill : getAllSkills()) {
            int skillid = skill.getId();

            if (isStoredSkill(skill, skillTreeUIDs))
                continue;

            if (isTemporarySkill(skill))
                continue;

            // Exclude Skills from AllowedSkills in options.properties
            if (Config.ALLOWED_SKILLS_LIST.contains(skillid))
                continue skill_loop;
            // Exclude VIP character
            if (isCharViP() && Config.CHAR_VIP_SKIP_SKILLS_CHECK)
                continue skill_loop;

            // Remove skill from ingame, but not from the database to avoid accidentally removal of skills
            // if something failed loading and do a lil log message
            removeSkill(skill, false);
            sendMessage("Skill " + skill.getName() + " removed and GM informed!");
            _log.fatal("Cheater?! " + skill + " removed from " + getName() + " (" + getAccountName() + ")");
        }
    }

    public boolean isStoredSkill(L2Skill skill) {
        return isStoredSkill(skill, SkillTreeTable.getInstance().getAllowedSkillUIDs(getClassId()));
    }

    private boolean isStoredSkill(L2Skill skill, Set<Integer> skillTreeUIDs) {
        int skillid = skill.getId();

        // Loop through all skills in players skilltree
        if (skillTreeUIDs.contains(SkillTable.getSkillUID(skillid, SkillTable.getInstance().getNormalLevel(skill))))
            return true;

        // skills learned by L2SkillType.LEARN_SKILL
        if (SkillTable.getInstance().isLearnedSkill(skill))
            return true;

        // Exclude fishing skills and common skills + dwarfen craft
        if (skillid >= 1312 && skillid <= 1322)
            return true;
        if (skillid >= 1368 && skillid <= 1373)
            return true;

        if (L2CertificationSkillsLearn.isCertificationSkill(skillid))
            return true;
        if (L2TransformSkillLearn.isTransformSkill(skillid))
            return true;
        if (L2SkillLearn.isSpecialSkill(skillid))
            return true;

        return false;
    }

    public boolean isTemporarySkill(L2Skill skill) {
        int skillid = skill.getId();

        if (getTransformation() != null && containsAllowedTransformSkill(skillid))
            return true;
        // Exclude noble skills
        if (isNoble() && NobleSkillTable.isNobleSkill(skillid))
            return true;
        // Exclude hero skills
        if (isHero() && HeroSkillTable.isHeroSkill(skillid))
            return true;
        // Exclude cursed weapon skills
        if (isCursedWeaponEquipped() && skillid == CursedWeaponsManager.getInstance()
                .getCursedWeapon(_cursedWeaponEquippedId).getSkillId())
            return true;
        // Exclude clan skills
        if (getClan() != null && (skillid >= 370 && skillid <= 391))
            return true;
        // Exclude residential skills
        if (getClan() != null && (getClan().getHasCastle() > 0 || getClan().getHasFort() > 0))
            if (590 <= skillid && skillid <= 610)
                return true;
        // Exclude seal of ruler / build siege hq
        if (getClan() != null && getClan().getLeaderId() == getObjectId() && (skillid == 246 || skillid == 247))
            return true;
        // Exclude sa / enchant bonus / penality etc. skills
        if (skillid >= 3000 && skillid < 7000)
            return true;
        // Exclude Armor Set skills
        if (skillid >= 8100 && skillid < 8400)
            return true;

        return false;
    }

    /**
     * Retrieve from the database all skills of this L2Player and add them to _skills.<BR><BR>
     */
    private void restoreSkills() {
        _pcSkills.restoreSkills();

        // Restore clan skills
        if (_clan != null)
            _clan.addSkillEffects(this, false);
    }

    private void restoreSkillReuses() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();

            PreparedStatement statement = con.prepareStatement(RESTORE_SKILL_REUSES);
            statement.setInt(1, getObjectId());
            ResultSet rset = statement.executeQuery();

            while (rset.next()) {
                final int skillId = rset.getInt("skillId");
                final int reuseDelay = rset.getInt("reuseDelay");
                final long expiration = rset.getLong("expiration");

                final int remaining = L2Math.limit(0, expiration - System.currentTimeMillis(), Integer.MAX_VALUE);

                disableSkill(skillId, reuseDelay, remaining);
            }

            rset.close();
            statement.close();
        } catch (Exception e) {
            _log.error("", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    /**
     * Retrieve from the database all Henna of this L2Player, add them to _henna and calculate stats of the L2Player.<BR><BR>
     */
    private void restoreHenna() {
        Connection con = null;

        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_HENNAS);
            statement.setInt(1, getObjectId());
            statement.setInt(2, getClassIndex());
            ResultSet rset = statement.executeQuery();

            for (int i = 0; i < 3; i++)
                _henna[i] = null;

            while (rset.next()) {
                int slot = rset.getInt("slot");

                if (slot < 1 || slot > 3)
                    continue;

                _henna[slot - 1] = HennaTable.getInstance().getTemplate(rset.getInt("symbol_id"));
            }

            rset.close();
            statement.close();
        } catch (Exception e) {
            _log.error("Failed restoing character hennas.", e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        // Calculate Henna modifiers of this L2Player
        recalcHennaStats();
    }

    /**
     * Return the number of Henna empty slot of the L2Player.<BR><BR>
     */
    public int getHennaEmptySlots() {
        int totalSlots;
        if (getClassId().level() == 1)
            totalSlots = 2;
        else
            totalSlots = 3;

        for (int i = 0; i < 3; i++)
            if (_henna[i] != null)
                totalSlots--;

        if (totalSlots <= 0)
            return 0;

        return totalSlots;
    }

    /**
     * Remove a Henna of the L2Player, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2Player.<BR><BR>
     */
    public boolean removeHenna(int slot) {
        if (slot < 1 || slot > 3)
            return false;

        slot--;

        if (_henna[slot] == null)
            return false;

        L2Henna henna = _henna[slot];
        _henna[slot] = null;

        Connection con = null;

        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA);
            statement.setInt(1, getObjectId());
            statement.setInt(2, slot + 1);
            statement.setInt(3, getClassIndex());
            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Failed removing character henna.", e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        // Calculate Henna modifiers of this L2Player
        recalcHennaStats();

        // Send Server->Client HennaInfo packet to this L2Player
        sendPacket(new HennaInfo(this));

        // Send Server->Client UserInfo packet to this L2Player
        sendPacket(new UserInfo(this));

        // Add the recovered dyes to the player's inventory and notify them.
        L2ItemInstance dye = getInventory().addItem("Henna", henna.getItemId(), henna.getAmount() / 2, this, null);
        getInventory().updateInventory(dye);

        reduceAdena("Henna", henna.getPrice() / 5, this, false);

        SystemMessage sm = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
        sm.addItemName(henna.getItemId());
        sm.addItemNumber(henna.getAmount() / 2);
        sendPacket(sm);

        sendPacket(SystemMessageId.SYMBOL_DELETED);

        return true;
    }

    /**
     * Add a Henna to the L2Player, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2Player.<BR><BR>
     * <B>Does not do <U>any</U> validation!</B>
     */
    public void addHenna(L2Henna henna) {
        for (int i = 0; i < 3; i++) {
            if (_henna[i] == null) {
                _henna[i] = henna;

                // Calculate Henna modifiers of this L2Player
                recalcHennaStats();

                Connection con = null;
                try {
                    con = L2DatabaseFactory.getInstance().getConnection(con);
                    PreparedStatement statement = con.prepareStatement(ADD_CHAR_HENNA);
                    statement.setInt(1, getObjectId());
                    statement.setInt(2, henna.getSymbolId());
                    statement.setInt(3, i + 1);
                    statement.setInt(4, getClassIndex());
                    statement.execute();
                    statement.close();
                } catch (Exception e) {
                    _log.error("Failed saving character henna.", e);
                } finally {
                    L2DatabaseFactory.close(con);
                }

                // Send Server->Client HennaInfo packet to this L2Player
                sendPacket(new HennaInfo(this));
                // Send Server->Client UserInfo packet to this L2Player
                sendPacket(new UserInfo(this));
                return;
            }
        }
    }

    /**
     * Calculate Henna modifiers of this L2Player.<BR><BR>
     */
    private void recalcHennaStats() {
        _hennaINT = 0;
        _hennaSTR = 0;
        _hennaCON = 0;
        _hennaMEN = 0;
        _hennaWIT = 0;
        _hennaDEX = 0;

        for (int i = 0; i < 3; i++) {
            if (_henna[i] == null)
                continue;
            _hennaINT += _henna[i].getStatINT();
            _hennaSTR += _henna[i].getStatSTR();
            _hennaMEN += _henna[i].getStatMEM();
            _hennaCON += _henna[i].getStatCON();
            _hennaWIT += _henna[i].getStatWIT();
            _hennaDEX += _henna[i].getStatDEX();
        }

        if (_hennaINT > 5)
            _hennaINT = 5;
        if (_hennaSTR > 5)
            _hennaSTR = 5;
        if (_hennaMEN > 5)
            _hennaMEN = 5;
        if (_hennaCON > 5)
            _hennaCON = 5;
        if (_hennaWIT > 5)
            _hennaWIT = 5;
        if (_hennaDEX > 5)
            _hennaDEX = 5;
    }

    /**
     * Return the Henna of this L2Player corresponding to the selected slot.<BR><BR>
     */
    public L2Henna getHenna(int slot) {
        if (slot < 1 || slot > 3)
            return null;

        return _henna[slot - 1];
    }

    /**
     * Return the INT Henna modifier of this L2Player.<BR><BR>
     */
    public int getHennaStatINT() {
        return _hennaINT;
    }

    /**
     * Return the STR Henna modifier of this L2Player.<BR><BR>
     */
    public int getHennaStatSTR() {
        return _hennaSTR;
    }

    /**
     * Return the CON Henna modifier of this L2Player.<BR><BR>
     */
    public int getHennaStatCON() {
        return _hennaCON;
    }

    /**
     * Return the MEN Henna modifier of this L2Player.<BR><BR>
     */
    public int getHennaStatMEN() {
        return _hennaMEN;
    }

    /**
     * Return the WIT Henna modifier of this L2Player.<BR><BR>
     */
    public int getHennaStatWIT() {
        return _hennaWIT;
    }

    /**
     * Return the DEX Henna modifier of this L2Player.<BR><BR>
     */
    public int getHennaStatDEX() {
        return _hennaDEX;
    }

    /**
     * Return True if the L2Player is autoAttackable.<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>Check if the attacker isn't the L2Player Pet </li>
     * <li>Check if the attacker is L2MonsterInstance</li>
     * <li>If the attacker is a L2Player, check if it is not in the same party </li>
     * <li>Check if the L2Player has Karma </li>
     * <li>If the attacker is a L2Player, check if it is not in the same siege clan (Attacker, Defender) </li><BR><BR>
     *
     */
    @Override
    public boolean isAutoAttackable(L2Creature attacker) {
        // Check if the attacker isn't the L2Player Pet
        if (attacker == this || attacker == getPet())
            return false;

        // Check if the attacker is a L2MonsterInstance
        if (attacker instanceof L2MonsterInstance)
            return true;

        switch (GlobalRestrictions.getCombatState(L2Object.getActingPlayer(attacker), this)) {
        case ENEMY:
            return true;
        case FRIEND:
            return false;
        }

        // Check if the attacker is not in the same party
        if (getParty() != null && getParty().getPartyMembers().contains(attacker))
            return false;

        if (isCursedWeaponEquipped())
            return true;

        // Check if the attacker is in olympia and olympia start
        if (attacker instanceof L2Player && ((L2Player) attacker).isInOlympiadMode()) {
            return isInOlympiadMode() && isOlympiadStart()
                    && ((L2Player) attacker).getOlympiadGameId() == getOlympiadGameId();
        }

        // Check if the attacker is not in the same clan
        if (getClan() != null && attacker != null && getClan().isMember(attacker.getObjectId()))
            return false;

        if (attacker instanceof L2Playable && isInsideZone(L2Zone.FLAG_PEACE))
            return false;

        // Check if the L2Player has Karma
        if (getKarma() > 0 || getPvpFlag() > 0)
            return true;

        // Check if the attacker is a L2Playable
        if (attacker instanceof L2Playable) {
            // Is AutoAttackable if both playables are in the same duel and the duel is still going on
            if (getDuelState() == Duel.DUELSTATE_DUELLING && getDuelId() == attacker.getActingPlayer().getDuelId())
                return true;
        }

        // Check if the attacker is a L2Player
        if (attacker instanceof L2Player) {
            // Check if the L2Player is in an arena or a siege area
            if (isInsideZone(L2Zone.FLAG_PVP) && attacker.isInsideZone(L2Zone.FLAG_PVP))
                return true;

            // Check if the L2Player holds a cursed weapon
            if (((L2Player) attacker).isCursedWeaponEquipped())
                return true;

            if (getClan() != null) {
                Siege siege = SiegeManager.getInstance().getSiege(getX(), getY(), getZ());
                if (siege != null) {
                    // Check if a siege is in progress and if attacker and the L2Player aren't in the Defender clan
                    if (siege.checkIsDefender(((L2Player) attacker).getClan()) && siege.checkIsDefender(getClan()))
                        return false;

                    // Check if a siege is in progress and if attacker and the L2Player aren't in the Attacker clan
                    if (siege.checkIsAttacker(((L2Player) attacker).getClan()) && siege.checkIsAttacker(getClan()))
                        return false;
                }

                // Check if clan is at war
                if (getClan() != null && ((L2Player) attacker).getClan() != null
                        && (getClan().isAtWarWith(((L2Player) attacker).getClanId())
                                && ((L2Player) attacker).getClan().isAtWarWith(getClanId()) && getWantsPeace() == 0
                                && ((L2Player) attacker).getWantsPeace() == 0 && !isAcademyMember()))
                    return true;
            }
        } else if (attacker instanceof L2SiegeGuardInstance) {
            if (getClan() != null) {
                Siege siege = SiegeManager.getInstance().getSiege(this);
                return (siege != null && siege.checkIsAttacker(getClan()));
            }
        } else if (attacker instanceof L2FortSiegeGuardInstance) {
            if (getClan() != null) {
                FortSiege siege = FortSiegeManager.getInstance().getSiege(this);
                return (siege != null && siege.checkIsAttacker(getClan()));
            }
        }

        return false;
    }

    /**
     * Checks if the client was allowed to call that skill at all, or not.
     */
    public boolean canUseMagic(L2Skill skill) {
        if (skill == null || skill.getSkillType() == L2SkillType.NOTDONE)
            return false;

        // players mounted on pets cannot use any toggle skills
        if (skill.isToggle() && isMounted())
            return false;

        // Check if the skill is active
        if (skill.isPassive())
            return false;

        if (isTransformationDisabledSkill(skill) && !skill.isPotion())
            return false;

        // Failfast as in retail
        if (isSkillDisabled(skill.getId())) {
            sendReuseMessage(skill);
            return false;
        }

        return true;
    }

    private boolean isTransformationDisabledSkill(L2Skill skill) {
        if (_transformation != null && !containsAllowedTransformSkill(skill.getId()) && !skill.allowOnTransform())
            return true;

        return false;
    }

    @Override
    public void doCast(L2Skill skill) {
        if (!canUseMagic(skill)) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        super.doCast(skill);
    }

    @Override
    public void doSimultaneousCast(L2Skill skill) {
        if (!canUseMagic(skill)) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return;
        }

        super.doSimultaneousCast(skill);
    }

    public void sendReuseMessage(L2Skill skill) {
        SystemMessage sm = null;

        TimeStamp timeStamp = getReuseTimeStamps().get(skill.getId());
        int remainingTime = (timeStamp == null ? 0 : timeStamp.getRemaining() / 1000);
        int hours = remainingTime / 3600;
        int minutes = (remainingTime % 3600) / 60;
        int seconds = (remainingTime % 60);
        if (hours > 0) {
            sm = new SystemMessage(SystemMessageId.S2_HOURS_S3_MINUTES_S4_SECONDS_REMAINING_FOR_REUSE_S1);
            sm.addSkillName(skill);
            sm.addNumber(hours);
            sm.addNumber(minutes);
            sm.addNumber(seconds);
        } else if (minutes > 0) {
            sm = new SystemMessage(SystemMessageId.S2_MINUTES_S3_SECONDS_REMAINING_FOR_REUSE_S1);
            sm.addSkillName(skill);
            sm.addNumber(minutes);
            sm.addNumber(seconds);
        } else if (seconds > 0) {
            sm = new SystemMessage(SystemMessageId.S2_SECONDS_REMAINING_FOR_REUSE_S1);
            sm.addSkillName(skill);
            sm.addNumber(seconds);
        } else {
            sm = new SystemMessage(SystemMessageId.S1_PREPARED_FOR_REUSE);
            sm.addSkillName(skill);
        }
        sendPacket(sm);
    }

    @Override
    protected boolean checkUseMagicConditions(L2Skill skill, boolean forceUse) {
        L2SkillType sklType = skill.getSkillType();

        //************************************* Check Player State *******************************************

        // Abnormal effects(ex : Stun, Sleep...) are checked in L2Creature useMagic()

        if (!SkillHandler.getInstance().checkConditions(this, skill)) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        if (isOutOfControl()) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if the player is dead
        if (isDead()) {
            abortCast();
            // Send a Server->Client packet ActionFailed to the L2Player
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        if (isFishing() && (sklType != L2SkillType.PUMPING && sklType != L2SkillType.REELING
                && sklType != L2SkillType.FISHING)) {
            // Only fishing skills are available
            sendPacket(SystemMessageId.ONLY_FISHING_SKILLS_NOW);
            return false;
        }

        if (inObserverMode()) {
            sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
            abortCast();
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if the caster is sitting
        if (isSitting() && !skill.isPotion()) {
            // Send a System Message to the caster
            sendPacket(SystemMessageId.CANT_MOVE_SITTING);

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

        // Check if the skill type is TOGGLE
        if (skill.isToggle()) {
            // Get effects of the skill
            L2Effect effect = getFirstEffect(skill);

            if (effect != null) {
                effect.exit();

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

        // Check if the player uses "Fake Death" skill
        // Note: do not check this before TOGGLE reset
        if (isFakeDeath()) {
            // Send a Server->Client packet ActionFailed to the L2Player
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if it's ok to summon
        // Siege Golem (13), Wild Hog Cannon (299), Swoop Cannon (448)
        switch (skill.getId()) {
        case 13:
        case 299:
        case 448:
            if ((!SiegeManager.getInstance().checkIfOkToSummon(this, false)
                    && !FortSiegeManager.getInstance().checkIfOkToSummon(this, false))
                    || SevenSigns.getInstance().checkSummonConditions(this)) {
                return false;
            }
        }

        //************************************* Check Target *******************************************

        // Create and set a L2Object containing the target of the skill

        SkillTargetType sklTargetType = skill.getTargetType();
        Point3D worldPosition = getCurrentSkillWorldPosition();

        if (sklTargetType == SkillTargetType.TARGET_GROUND && worldPosition == null) {
            _log.info("WorldPosition is null for skill: " + skill.getName() + ", player: " + getName() + ".");
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        final L2Creature target = skill.getFirstOfTargetList(this);

        // Check the validity of the target
        if (target == null) {
            //sendPacket(SystemMessageId.TARGET_CANT_FOUND);
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Skills can be used on walls and doors only during siege
        if (target instanceof L2DoorInstance) {
            L2DoorInstance door = (L2DoorInstance) target;
            boolean isCastleDoor = (door.getCastle() != null && door.getCastle().getSiege().getIsInProgress());
            boolean isFortDoor = (door.getFort() != null && door.getFort().getSiege().getIsInProgress()
                    && !door.isCommanderDoor());
            if (!isCastleDoor && !isFortDoor && (door.isUnlockable() && skill.getSkillType() != L2SkillType.UNLOCK))
                return false;
        }

        // Are the target and the player in the same duel?
        if (isInDuel()) {
            if (!(target instanceof L2Playable && target.getActingPlayer().getDuelId() == getDuelId())) {
                sendMessage("You cannot do this while duelling.");
                sendPacket(ActionFailed.STATIC_PACKET);
                return false;
            }
        }

        //************************************* Check skill availability *******************************************

        // Check if this skill is enabled (ex : reuse time)
        if (isSkillDisabled(skill.getId())) {
            sendReuseMessage(skill);
            return false;
        }

        //************************************* Check Consumables *******************************************

        if (skill.getTransformId() > 0) {
            if (getPet() != null)
                getPet().unSummon(this); // Unsummon pets

            if (getEffects().hasEffect(L2EffectType.TRANSFORMATION) || getPet() != null || isMounted()
                    || isFlying()) {
                sendPacket(new SystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill));
                return false;
            }
        }

        //************************************* Check Consumables *******************************************

        // Check if spell consumes a Soul
        // Most kamael skills have only optional soul consumption to empower skills!
        if (getSouls() < skill.getSoulConsumeCount()) {
            sendPacket(SystemMessageId.THERE_IS_NOT_ENOUGH_SOUL);
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if spell consumes charges
        if (_charges < skill.getNeededCharges()) {
            SystemMessage sm = new SystemMessage(SystemMessageId.S1_CANNOT_BE_USED);
            sm.addSkillName(skill);
            sendPacket(sm);
            return false;
        }

        // Check if spell adds charges
        if (!skill.isOffensive() && skill.getGiveCharges() > 0 && _charges >= skill.getMaxCharges()) {
            sendPacket(SystemMessageId.FORCE_MAXLEVEL_REACHED);
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        //************************************* Check Casting Conditions *******************************************

        // Check if all casting conditions are completed
        if (!skill.checkCondition(this, target)) {
            // Send a Server->Client packet ActionFailed to the L2Player
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        //************************************* Check Skill Type *******************************************

        // Check if this is offensive magic skill
        if (skill.isOffensive()) {
            if (L2Creature.isInsidePeaceZone(this, target)) {
                // If L2Creature or target is in a peace zone, send a system message TARGET_IN_PEACEZONE a Server->Client packet ActionFailed
                sendPacket(SystemMessageId.TARGET_IN_PEACEZONE);
                sendPacket(ActionFailed.STATIC_PACKET);
                return false;
            }

            if (isInOlympiadMode() && !isOlympiadStart()) {
                // If the L2Player is in Olympia and the match isn't already start, send a Server->Client packet ActionFailed
                sendPacket(ActionFailed.STATIC_PACKET);
                return false;
            }

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

            // Check if a Forced ATTACK is in progress on non-attackable target
            if (!target.isAutoAttackable(this) && !forceUse) {
                switch (sklTargetType) {
                case TARGET_AURA:
                case TARGET_FRONT_AURA:
                case TARGET_BEHIND_AURA:
                case TARGET_SERVITOR_AURA:
                case TARGET_CLAN:
                case TARGET_PARTY_CLAN:
                case TARGET_ALLY:
                case TARGET_PARTY:
                case TARGET_SELF:
                case TARGET_GROUND:
                    // Everything okay
                    break;
                default:
                    // Send a Server->Client packet ActionFailed to the L2Player
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return false;
                }
            }
        }

        // Check if the skill is defensive
        if (skill.isPositive() && target instanceof L2MonsterInstance && !forceUse) {
            switch (sklTargetType) {
            case TARGET_PET:
            case TARGET_SUMMON:
            case TARGET_AURA:
            case TARGET_FRONT_AURA:
            case TARGET_BEHIND_AURA:
            case TARGET_SERVITOR_AURA:
            case TARGET_CLAN:
            case TARGET_PARTY_CLAN:
            case TARGET_SELF:
            case TARGET_PARTY:
            case TARGET_ALLY:
            case TARGET_CORPSE_MOB:
            case TARGET_AREA_CORPSE_MOB:
            case TARGET_GROUND:
                // Everything okay
                break;
            default:
                switch (sklType) {
                case BEAST_FEED:
                case DELUXE_KEY_UNLOCK:
                case UNLOCK:
                case MAKE_KILLABLE:
                    // Everything okay
                    break;
                default:
                    // send the action failed so that the skill doens't go off.
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return false;
                }
            }
        }

        if (!SkillHandler.getInstance().checkConditions(this, skill, L2Object.getActingCharacter(target))) {
            sendPacket(ActionFailed.STATIC_PACKET);
            return false;
        }

        // Check if this is a Pvp skill and target isn't a non-flagged/non-karma player
        switch (sklTargetType) {
        case TARGET_PARTY:
        case TARGET_ALLY: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList()
        case TARGET_CLAN: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList()
        case TARGET_PARTY_CLAN:// For such skills, checkPvpSkill() is called from L2Skill.getTargetList()
        case TARGET_AURA:
        case TARGET_FRONT_AURA:
        case TARGET_BEHIND_AURA:
        case TARGET_SERVITOR_AURA:
        case TARGET_GROUND:
        case TARGET_SELF:
            break;
        default:
            if (!checkPvpSkill(target, skill) && (getAccessLevel() < Config.GM_PEACEATTACK)) {
                // Send a System Message to the L2Player
                sendPacket(SystemMessageId.TARGET_IS_INCORRECT);

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

        if ((sklTargetType == SkillTargetType.TARGET_HOLY && (!TakeCastle.checkIfOkToCastSealOfRule(this)))
                || (sklTargetType == SkillTargetType.TARGET_FLAGPOLE
                        && !TakeFort.checkIfOkToCastFlagDisplay(this, false, skill, getTarget()))) {
            sendPacket(ActionFailed.STATIC_PACKET);
            abortCast();
            return false;
        }

        // GeoData Los Check here
        if (skill.getCastRange() > 0) {
            if (sklTargetType == SkillTargetType.TARGET_GROUND) {
                if (!GeoData.getInstance().canSeeTarget(this, worldPosition)) {
                    sendPacket(SystemMessageId.CANT_SEE_TARGET);
                    sendPacket(ActionFailed.STATIC_PACKET);
                    return false;
                }
            } else if (!GeoData.getInstance().canSeeTarget(this, target)) {
                sendPacket(SystemMessageId.CANT_SEE_TARGET);
                sendPacket(ActionFailed.STATIC_PACKET);
                return false;
            }
        }

        // Finally, after passing all conditions
        return true;
    }

    public boolean isInLooterParty(int LooterId) {
        L2Player looter = (L2Player) L2World.getInstance().findObject(LooterId);

        // If L2Player is in a CommandChannel
        if (isInParty() && getParty().isInCommandChannel() && looter != null)
            return getParty().getCommandChannel().getMembers().contains(looter);

        if (isInParty() && looter != null)
            return getParty().getPartyMembers().contains(looter);

        return false;
    }

    /**
     * Check if the requested casting is a Pc->Pc skill cast and if it's a valid pvp condition
     * @param obj L2Object instance containing the target
     * @param skill L2Skill instance with the skill being casted
     * @return False if the skill is a pvpSkill and target is not a valid pvp target
     */
    public boolean checkPvpSkill(L2Object obj, L2Skill skill) {
        return checkPvpSkill(obj, skill, false);
    }

    /**
     * Check if the requested casting is a Pc->Pc skill cast and if it's a valid pvp condition
     * @param obj L2Object instance containing the target
     * @param skill L2Skill instance with the skill being casted
     * @param srcIsSummon is L2Summon - caster?
     * @return False if the skill is a pvpSkill and target is not a valid pvp target
     */
    public boolean checkPvpSkill(L2Object obj, L2Skill skill, boolean srcIsSummon) {
        if (obj instanceof L2Creature)
            if (GlobalRestrictions.isProtected(this, (L2Creature) obj, skill, false))
                return false;

        // Check for PC->PC Pvp status
        if (obj != this && // Target is not self and
                obj instanceof L2Player && // Target is L2Player and
                !(isInDuel() && ((L2Player) obj).getDuelId() == getDuelId()) && // Self is not in a duel and attacking opponent
                !isInsideZone(L2Zone.FLAG_PVP) && // Pc is not in PvP zone
                !((L2Player) obj).isInsideZone(L2Zone.FLAG_PVP) // Target is not in PvP zone
        ) {
            L2Player target = (L2Player) obj;

            if (skill.isPvpSkill()) // Pvp skill
            {
                if (getClan() != null && target.getClan() != null) {
                    if (getClan().isAtWarWith(target.getClan().getClanId())
                            && target.getClan().isAtWarWith(getClan().getClanId()))
                        return true; // In clan war player can attack whites even with sleep etc.
                }
                if (target.getPvpFlag() == 0 && // Target's pvp flag is not set and
                        target.getKarma() == 0 // Target has no karma
                )
                    return false;
            } else if ((getCurrentSkill() != null && !getCurrentSkill().isCtrlPressed() && skill.isOffensive()
                    && !srcIsSummon)
                    || (getCurrentPetSkill() != null && !getCurrentPetSkill().isCtrlPressed() && skill.isOffensive()
                            && srcIsSummon)) {
                if (getClan() != null && target.getClan() != null) {
                    if (getClan().isAtWarWith(target.getClan().getClanId())
                            && target.getClan().isAtWarWith(getClan().getClanId()))
                        return true; // In clan war player can attack whites even without ctrl
                }
                if (target.getPvpFlag() == 0 && // Target's pvp flag is not set and
                        target.getKarma() == 0 // Target has no karma
                )
                    return false;
            }
        }
        return true;
    }

    /**
     * Return True if the L2Player is a Mage.<BR><BR>
     */
    public boolean isMageClass() {
        return getClassId().isMage();
    }

    public boolean isMounted() {
        return _mountType > 0;
    }

    public boolean checkCanLand() {
        // Check if char is in a no landing zone
        if (isInsideZone(L2Zone.FLAG_NOWYVERN))
            return false;

        // If this is a castle that is currently being sieged, and the rider is NOT a castle owner
        // he cannot land.
        // Castle owner is the leader of the clan that owns the castle where the pc is
        return !(SiegeManager.getInstance().checkIfInZone(this)
                && !(getClan() != null && CastleManager.getInstance().getCastle(this) == CastleManager.getInstance()
                        .getCastleByOwner(getClan()) && this == getClan().getLeader().getPlayerInstance()));
    }

    /**
     * Set the type of Pet mounted (0 : none, 1 : Stridder, 2 : Wyvern) and send a Server->Client packet InventoryUpdate to the L2Player.<BR><BR>
     * @return false if the change of mount type false
     */
    public boolean setMount(int npcId, int npcLevel, int mountType) {
        switch (mountType) {
        case 0:
            setIsRidingStrider(false);
            setIsRidingRedStrider(false);
            setIsRidingHorse(false);
            setIsFlying(false);
            isFalling(false, 0); // Initialize the fall just incase dismount was made while in-air
            break; // Dismounted
        case 1:
            if (npcId >= 12526 && npcId <= 12528) {
                setIsRidingStrider(true);
            } else if (npcId >= 16038 && npcId <= 16040) {
                setIsRidingRedStrider(true);
            }
            if (isNoble()) {
                L2Skill striderAssaultSkill = SkillTable.getInstance().getInfo(325, 1);
                addSkill(striderAssaultSkill); // Not saved to DB
            }
            break;
        case 2:
            setIsFlying(true);
            break; // Flying Wyvern
        case 4:
            setIsRidingHorse(true);
            break;
        }

        _mountType = mountType;
        _mountNpcId = npcId;
        _mountLevel = npcLevel;

        return true;
    }

    /**
     * @return the type of Pet mounted (0 : none, 1 : Strider, 2 : Wyvern, 3: Wolf).
     */
    public int getMountType() {
        return _mountType;
    }

    /**
     * Disable the Inventory and create a new task to enable it after 1.5s.<BR><BR>
     */
    public void tempInventoryDisable() {
        _inventoryDisabled = true;

        ThreadPoolManager.getInstance().scheduleGeneral(new InventoryEnable(), 1500);
    }

    /**
     * Return True if the Inventory is disabled.<BR><BR>
     */
    public boolean isInventoryDisabled() {
        return _inventoryDisabled;
    }

    class InventoryEnable implements Runnable {
        @Override
        public void run() {
            _inventoryDisabled = false;
        }
    }

    public Map<Integer, L2CubicInstance> getCubics() {
        return _cubics;
    }

    /**
     * Add a L2CubicInstance to the L2Player _cubics.<BR><BR>
     */
    public void addCubic(int id, int level, double matk, int activationtime, int activationchance,
            int totalLifeTime) {
        if (_log.isDebugEnabled())
            _log.info("L2Player(" + getName() + "): addCubic(" + id + "|" + level + "|" + matk + ")");
        L2CubicInstance cubic = new L2CubicInstance(this, id, level, (int) matk, activationtime, activationchance,
                totalLifeTime);
        _cubics.put(id, cubic);
    }

    public void addCubic(L2SkillSummon skill) {
        addCubic(skill.getNpcId(), skill.getLevel(), skill.getPower(), skill.getActivationTime(),
                skill.getActivationChance(), skill.getTotalLifeTime());
    }

    /**
     * Remove a L2CubicInstance from the L2Player _cubics.<BR><BR>
     */
    public void delCubic(int id) {
        _cubics.remove(id);
    }

    /**
     * Return the L2CubicInstance corresponding to the Identifier of the L2Player _cubics.<BR><BR>
     */
    public L2CubicInstance getCubic(int id) {
        return _cubics.get(id);
    }

    @Override
    public String toString() {
        return "player " + getName();
    }

    /**
     * Return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127).<BR><BR>
     */
    public int getEnchantEffect() {
        L2ItemInstance wpn = getActiveWeaponInstance();

        if (wpn == null)
            return 0;

        return Math.min(127, wpn.getEnchantLevel());
    }

    /**
     * Set the _lastFolkNpc of the L2Player corresponding to the last Folk wich one the player talked.<BR><BR>
     */
    public void setLastFolkNPC(L2Npc folkNpc) {
        _lastFolkNpc = folkNpc;
    }

    /**
     * Return the _lastFolkNpc of the L2Player corresponding to the last Folk wich one the player talked.<BR><BR>
     */
    public L2Npc getLastFolkNPC() {
        return _lastFolkNpc;
    }

    /**
     * Return True if L2Player is a participant in the Festival of Darkness.<BR><BR>
     */
    public boolean isFestivalParticipant() {
        return SevenSignsFestival.getInstance().isParticipant(this);
    }

    private ScheduledFuture<?> _taskWarnUserTakeBreak;

    class WarnUserTakeBreak implements Runnable {
        @Override
        public void run() {
            sendPacket(SystemMessageId.PLAYING_FOR_LONG_TIME);
        }
    }

    class RentPetTask implements Runnable {
        @Override
        public void run() {
            stopRentPet();
        }
    }

    class WaterTask implements Runnable {
        @Override
        @SuppressWarnings("deprecation")
        public void run() {
            double reduceHp = getMaxHp() / 100.0;

            if (reduceHp < 1)
                reduceHp = 1;

            reduceCurrentHp(reduceHp, L2Player.this, false);
            // Reduced hp, because not rest
            SystemMessage sm = new SystemMessage(SystemMessageId.DROWN_DAMAGE_S1);
            sm.addNumber((int) reduceHp);
            sendPacket(sm);

        }
    }

    class LookingForFishTask implements Runnable {
        boolean _isNoob, _isUpperGrade;
        int _fishType, _fishGutsCheck, _gutsCheckTime;
        long _endTaskTime;

        protected LookingForFishTask(int fishWaitTime, int fishGutsCheck, int fishType, boolean isNoob,
                boolean isUpperGrade) {
            _fishGutsCheck = fishGutsCheck;
            _endTaskTime = System.currentTimeMillis() + fishWaitTime + 10000;
            _fishType = fishType;
            _isNoob = isNoob;
            _isUpperGrade = isUpperGrade;
        }

        @Override
        public void run() {
            if (System.currentTimeMillis() >= _endTaskTime) {
                endFishing(false);
                return;
            }

            if (!GameTimeManager.getInstance().isNowNight() && _lure.isNightLure())
                return;

            int check = Rnd.get(1000);
            if (_fishGutsCheck > check) {
                stopLookingForFishTask();
                startFishCombat(_isNoob, _isUpperGrade);
            }
        }
    }

    public int getClanPrivileges() {
        return _clanPrivileges;
    }

    public void setClanPrivileges(int n) {
        _clanPrivileges = n;
    }

    public int getBookMarkSlot() {
        return _bookmarkslot;
    }

    public void setBookMarkSlot(int slot) {
        _bookmarkslot = slot;
        sendPacket(new ExGetBookMarkInfoPacket(this));
    }

    public void enterObserverMode(int x, int y, int z) {
        _obsX = getX();
        _obsY = getY();
        _obsZ = getZ();

        setTarget(null);
        stopMove(null);
        startParalyze();
        setIsInvul(true);
        getAppearance().setInvisible();
        sendPacket(GMHide.ENABLE);
        sendPacket(new ObservationMode(x, y, z));
        getPosition().setXYZ(x, y, z);

        _observerMode = true;

        updateInvisibilityStatus();
    }

    public void enterOlympiadObserverMode(Location loc, int id, boolean storeCoords) {
        if (getPet() != null)
            getPet().unSummon(this);

        if (!getCubics().isEmpty()) {
            for (L2CubicInstance cubic : getCubics().values()) {
                cubic.stopAction();
                cubic.cancelDisappear();
            }

            getCubics().clear();
        }

        if (getParty() != null)
            getParty().removePartyMember(this);

        _olympiadGameId = id;
        if (isSitting())
            standUp();
        if (storeCoords) {
            _obsX = getX();
            _obsY = getY();
            _obsZ = getZ();
        }
        setTarget(null);
        setIsInvul(true);
        getAppearance().setInvisible();
        teleToLocation(loc, false);
        sendPacket(GMHide.ENABLE);
        sendPacket(ExOlympiadMode.SPECTATE);
        _observerMode = true;

        updateInvisibilityStatus();
    }

    // [L2J_JP ADD SANDMAN]
    public void enterMovieMode() {
        setTarget(null);
        stopMove(null);
        setIsInvul(true);
        setIsImmobilized(true);
        sendPacket(CameraMode.FIRST_PERSON);
    }

    public void leaveMovieMode() {
        if (!isGM())
            setIsInvul(false);
        setIsImmobilized(false);
        sendPacket(CameraMode.THIRD_PERSON);
    }

    /**
     * yaw:North=90, south=270, east=0, west=180<BR>
     * pitch > 0:looks up,pitch < 0:looks down<BR>
     * time:faster that small value is.<BR>
     */
    public void specialCamera(L2Object target, int dist, int yaw, int pitch, int time, int duration) {
        sendPacket(new SpecialCamera(target.getObjectId(), dist, yaw, pitch, time, duration));
    }

    // L2JJP END

    public void leaveObserverMode() {
        setTarget(null);
        getPosition().setXYZ(_obsX, _obsY, _obsZ);
        sendPacket(GMHide.DISABLE);
        stopParalyze(false);

        if (!isGM()) {
            getAppearance().setVisible();
            setIsInvul(false);
        }

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

        teleToLocation(_obsX, _obsY, _obsZ);
        sendPacket(new ObservationReturn(this));
        _observerMode = false;
        broadcastUserInfo();
    }

    public void leaveOlympiadObserverMode() {
        setTarget(null);
        sendPacket(ExOlympiadMode.RETURN);
        teleToLocation(_obsX, _obsY, _obsZ);
        sendPacket(GMHide.DISABLE);
        if (!isGM()) {
            getAppearance().setVisible();
            setIsInvul(false);
        }
        if (getAI() != null) {
            getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
        }
        Olympiad.removeSpectator(_olympiadGameId, this);
        _olympiadGameId = -1;
        _observerMode = false;
        broadcastUserInfo();
    }

    public void updateNameTitleColor() {
        int nameColor = getAppearance().getNameColor();
        int titleColor = getAppearance().getTitleColor();

        if (isClanLeader() && Config.CLAN_LEADER_COLOR_ENABLED
                && getClan().getLevel() >= Config.CLAN_LEADER_COLOR_CLAN_LEVEL) {
            if (Config.CLAN_LEADER_COLORED == Config.ClanLeaderColored.name)
                nameColor = Config.CLAN_LEADER_COLOR;
            else
                titleColor = Config.CLAN_LEADER_COLOR;
        }

        if (Config.CHAR_VIP_COLOR_ENABLED) {
            if (isCharViP())
                nameColor = Config.CHAR_VIP_COLOR;
        }

        if (Config.ALLOW_OFFLINE_TRADE_COLOR_NAME) {
            if (isInOfflineMode())
                nameColor = Config.OFFLINE_TRADE_COLOR_NAME;
        }

        if (isGM()) {
            if (Config.GM_NAME_COLOR_ENABLED) {
                if (getAccessLevel() >= 100)
                    nameColor = Config.ADMIN_NAME_COLOR;
                else if (getAccessLevel() >= 75)
                    nameColor = Config.GM_NAME_COLOR;
            }

            if (Config.GM_TITLE_COLOR_ENABLED) {
                if (getAccessLevel() >= 100)
                    titleColor = Config.ADMIN_TITLE_COLOR;
                else if (getAccessLevel() >= 75)
                    titleColor = Config.GM_TITLE_COLOR;
            }
        }

        getAppearance().setTitleColor(titleColor);
        getAppearance().setNameColor(nameColor);

        broadcastUserInfo();
    }

    public void setOlympiadSide(int i) {
        _olympiadSide = i;
    }

    public int getOlympiadSide() {
        return _olympiadSide;
    }

    public void setOlympiadGameId(int id) {
        _olympiadGameId = id;
    }

    public int getOlympiadGameId() {
        return _olympiadGameId;
    }

    public int getObsX() {
        return _obsX;
    }

    public int getObsY() {
        return _obsY;
    }

    public int getObsZ() {
        return _obsZ;
    }

    public boolean inObserverMode() {
        return _observerMode;
    }

    public int getTeleMode() {
        return _telemode;
    }

    public void setTeleMode(int mode) {
        _telemode = mode;
    }

    public void setLoto(int i, int val) {
        _loto[i] = val;
    }

    public int getLoto(int i) {
        return _loto[i];
    }

    public void setRace(int i, int val) {
        _race[i] = val;
    }

    public int getRace(int i) {
        return _race[i];
    }

    @Deprecated
    private void setBanChatTimer(long timer) {
    }

    @Deprecated
    private long getBanChatTimer() {
        return 0;
    }

    public boolean isChatBanned() {
        return ObjectRestrictions.getInstance().checkRestriction(this, AvailableRestriction.PlayerChat);
    }

    public boolean getMessageRefusal() {
        return _messageRefusal;
    }

    public void setMessageRefusal(boolean mode) {
        _messageRefusal = mode;
        sendEtcStatusUpdate();
    }

    public void setDietMode(boolean mode) {
        _dietMode = mode;
    }

    public boolean getDietMode() {
        return _dietMode;
    }

    public void setTradeRefusal(boolean mode) {
        _tradeRefusal = mode;
    }

    public boolean getTradeRefusal() {
        return _tradeRefusal;
    }

    public void setExchangeRefusal(boolean mode) {
        _exchangeRefusal = mode;
    }

    public boolean getExchangeRefusal() {
        return _exchangeRefusal;
    }

    public BlockList getBlockList() {
        if (_blockList == null)
            _blockList = new BlockList(this);

        return _blockList;
    }

    public L2FriendList getFriendList() {
        if (_friendList == null)
            _friendList = new L2FriendList(this);

        return _friendList;
    }

    public void setHero(boolean hero) {
        if (hero && _baseClass == _activeClass)
            for (L2Skill s : HeroSkillTable.getHeroSkills())
                addSkill(s, false); // Dont Save Hero skills to Sql
        else
            for (L2Skill s : HeroSkillTable.getHeroSkills())
                super.removeSkill(s); // Just Remove skills without deleting from Sql
        _hero = hero;
    }

    public void setIsInOlympiadMode(boolean b) {
        _inOlympiadMode = b;
    }

    public void setIsOlympiadStart(boolean b) {
        _olympiadStart = b;
    }

    public boolean isOlympiadStart() {
        return _olympiadStart;
    }

    public boolean isHero() {
        return _hero;
    }

    public boolean isInOlympiadMode() {
        return _inOlympiadMode;
    }

    public void setNoble(boolean val) {
        if (val)
            for (L2Skill s : NobleSkillTable.getNobleSkills())
                addSkill(s, false); // Dont Save Noble skills to Sql
        else
            for (L2Skill s : NobleSkillTable.getNobleSkills())
                super.removeSkill(s); // Just Remove skills without deleting from Sql
        _noble = val;
    }

    public boolean isInDuel() {
        return _isInDuel;
    }

    public int getDuelId() {
        return _duelId;
    }

    public void setDuelState(int mode) {
        _duelState = mode;
    }

    public int getDuelState() {
        return _duelState;
    }

    /**
     * Sets up the duel state using a non 0 duelId.
     * @param duelId 0=not in a duel
     */
    public void setIsInDuel(int duelId) {
        if (duelId > 0) {
            _isInDuel = true;
            _duelState = Duel.DUELSTATE_DUELLING;
            _duelId = duelId;
        } else {
            if (_duelState == Duel.DUELSTATE_DEAD) {
                enableAllSkills();
                getStatus().startHpMpRegeneration();
            }
            _isInDuel = false;
            _duelState = Duel.DUELSTATE_NODUEL;
            _duelId = 0;
        }
    }

    /**
     * This returns a SystemMessage stating why
     * the player is not available for duelling.
     * @return S1_CANNOT_DUEL... message
     */
    public SystemMessage getNoDuelReason() {
        // This is somewhat hacky - but that case should never happen anyway...
        if (_noDuelReason == 0)
            _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL.getId();

        SystemMessage sm = new SystemMessage(SystemMessageId.getSystemMessageId(_noDuelReason));
        sm.addPcName(this);
        _noDuelReason = 0;
        return sm;
    }

    /**
     * Checks if this player might join / start a duel.
     * To get the reason use getNoDuelReason() after calling this function.
     * @return true if the player might join/start a duel.
     */
    public boolean canDuel() {
        if (isInCombat() || isInJail()) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_BATTLE.getId();
            return false;
        }
        if (isDead() || isAlikeDead() || (getStatus().getCurrentHp() < getStat().getMaxHp() / 2
                || getStatus().getCurrentMp() < getStat().getMaxMp() / 2)) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_HP_OR_MP_IS_BELOW_50_PERCENT.getId();
            return false;
        }
        if (isInDuel()) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_ALREADY_ENGAGED_IN_A_DUEL.getId();
            return false;
        }
        if (isInOlympiadMode()) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_PARTICIPATING_IN_THE_OLYMPIAD.getId();
            return false;
        }
        if (isCursedWeaponEquipped()) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_IN_A_CHAOTIC_STATE.getId();
            return false;
        }
        if (getPrivateStoreType() != STORE_PRIVATE_NONE) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_C1_IS_IN_A_PRIVATE_STORE_OR_MANUFACTURE.getId();
            return false;
        }
        if (isMounted() || isInBoat()) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_RIDING_A_BOAT_WYVERN_OR_STRIDER
                    .getId();
            return false;
        }
        if (isFishing()) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_FISHING.getId();
            return false;
        }
        if (isInsideZone(L2Zone.FLAG_PVP) || isInsideZone(L2Zone.FLAG_PEACE)
                || SiegeManager.getInstance().checkIfInZone(this)) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_IN_A_DUEL_PROHIBITED_AREA.getId();
            return false;
        }
        if (GlobalRestrictions.isRestricted(this, DuelRestriction.class)) {
            _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_BATTLE.getId(); // TODO
            return false;
        }
        return true;
    }

    public boolean isNoble() {
        /**_noble = false;
            
         if (isSubClassActive())
         {
         if (getTotalSubClasses() > 1)
         {
         _noble = true;
         return _noble;
         }
         if (getLevel() >= 75)
         {
         _noble = true;
         return _noble;
         }
         }
         else
         {
         if (getSubClasses() == null)
         return _noble;
            
         if (getLevel() >= 76 && getClassId().getId() >= 88)
         {
         for (SubClass sub : getSubClasses().values())
         {
         if (sub.getLevel() >= 75)
         {
         _noble = true;
         return _noble;
         }
         }
         }
         }*/

        return _noble;
    }

    public int getSubLevel() {
        if (isSubClassActive()) {
            int lvl = getLevel();
            return lvl;
        }
        return 0;
    }

    // Baron, Wise Man etc, calculated on EnterWorld and when rank is changing
    public void setPledgeClass(int classId) {
        _pledgeClass = classId;
    }

    public int getPledgeClass() {
        return _pledgeClass;
    }

    public void setSubPledgeType(int typeId) {
        _subPledgeType = typeId;
    }

    public int getSubPledgeType() {
        return _subPledgeType;
    }

    public int getPledgeRank() {
        return _pledgeRank;
    }

    public void setPledgeRank(int rank) {
        _pledgeRank = rank;
    }

    public int getApprentice() {
        return _apprentice;
    }

    public void setApprentice(int apprentice_id) {
        _apprentice = apprentice_id;
    }

    public int getSponsor() {
        return _sponsor;
    }

    public void setSponsor(int sponsor_id) {
        _sponsor = sponsor_id;
    }

    public void setLvlJoinedAcademy(int lvl) {
        _lvlJoinedAcademy = lvl;
    }

    public int getLvlJoinedAcademy() {
        return _lvlJoinedAcademy;
    }

    public boolean isAcademyMember() {
        return _lvlJoinedAcademy > 0;
    }

    public void setTeam(int team) {
        _team = team;
        if (getPet() != null)
            getPet().broadcastStatusUpdate();
    }

    public int getTeam() {
        return _team;
    }

    public void setWantsPeace(int wantsPeace) {
        _wantsPeace = wantsPeace;
    }

    public int getWantsPeace() {
        return _wantsPeace;
    }

    public boolean isFishing() {
        return _fishing;
    }

    public void setFishing(boolean fishing) {
        _fishing = fishing;
    }

    public void setAllianceWithVarkaKetra(int sideAndLvlOfAlliance) {
        // [-5,-1] varka, 0 neutral, [1,5] ketra
        _alliedVarkaKetra = sideAndLvlOfAlliance;
    }

    public int getAllianceWithVarkaKetra() {
        return _alliedVarkaKetra;
    }

    public boolean isAlliedWithVarka() {
        return (_alliedVarkaKetra < 0);
    }

    public boolean isAlliedWithKetra() {
        return (_alliedVarkaKetra > 0);
    }

    public final Comparator<L2Skill> SKILL_LIST_COMPARATOR = new Comparator<L2Skill>() {
        @Override
        public int compare(L2Skill s1, L2Skill s2) {
            int o1 = getOrder(s1);
            int o2 = getOrder(s2);

            if (o1 != o2)
                return (o1 < o2) ? -1 : 1;

            int so1 = getSubOrder(s1);
            int so2 = getSubOrder(s2);

            if (so1 != so2)
                return (so1 < so2) ? -1 : 1;

            return s1.getId().compareTo(s2.getId());
        }

        private int getOrder(L2Skill s) {
            if (s.getSkillType() == L2SkillType.NOTDONE)
                return 10;

            if (isTransformationDisabledSkill(s))
                return 9;

            // TODO: add other ordering conditions, if there is any other useful :)

            return 0;
        }

        private int getSubOrder(L2Skill s) {
            if (s.isPositive())
                return 1;
            else if (s.isNeutral())
                return 0;
            else
                // s.isOffensive()
                return -1;
        }
    };

    public L2Skill[] getSortedAllSkills(boolean isGM) {
        L2Skill[] array = getAllSkills();

        for (int i = 0; i < array.length; i++) {
            L2Skill s = array[i];

            if (s == null)
                continue;

            if (!isGM) {
                if (!s.canSendToClient()) {
                    array[i] = null;
                    continue;
                }

                // Hide skills when transformed if they are not passive
                if (isTransformationDisabledSkill(s)) {
                    array[i] = null;
                    continue;
                }
            }

            if (s.getSkillType() == L2SkillType.NOTDONE) {
                switch (Config.SEND_NOTDONE_SKILLS) {
                case 1: {
                    array[i] = null;
                    continue;
                }
                case 2: {
                    if (!isGM) {
                        array[i] = null;
                        continue;
                    }
                }
                }
            }
        }

        array = L2Arrays.compact(array);

        Arrays.sort(array, SKILL_LIST_COMPARATOR);

        return array;
    }

    public void sendSkillList() {
        addPacketBroadcastMask(BroadcastMode.SEND_SKILL_LIST);
    }

    public void sendSkillListImpl() {
        sendPacket(new SkillList(this));
    }

    public void setTransformAllowedSkills(int[] ids) {
        _transformAllowedSkills.clear();
        for (int id : ids)
            addTransformAllowedSkill(id);
    }

    public void addTransformAllowedSkill(int[] ids) {
        for (int id : ids)
            addTransformAllowedSkill(id);
    }

    public void addTransformAllowedSkill(int id) {
        _transformAllowedSkills.add(id);
    }

    public boolean containsAllowedTransformSkill(int id) {
        return _transformAllowedSkills.contains(id);
    }

    /** Section for mounted pets */
    class FeedTask implements Runnable {
        @Override
        public void run() {
            try {
                if (!isMounted()) {
                    stopFeed();
                    return;
                }

                if (getCurrentFeed() > getFeedConsume()) {
                    // eat
                    setCurrentFeed(getCurrentFeed() - getFeedConsume());
                } else {
                    // go back to pet control item, or simply said, unsummon it
                    setCurrentFeed(0);
                    stopFeed();
                    dismount();
                    sendPacket(SystemMessageId.OUT_OF_FEED_MOUNT_CANCELED);
                }

                int[] foodIds = PetDataTable.getFoodItemId(getMountNpcId());
                if (foodIds[0] == 0)
                    return;
                L2ItemInstance food = null;
                food = getInventory().getItemByItemId(foodIds[0]);

                // use better strider food if exists
                if (PetDataTable.isStrider(getMountNpcId())) {
                    if (getInventory().getItemByItemId(foodIds[1]) != null)
                        food = getInventory().getItemByItemId(foodIds[1]);
                }
                if (food != null && isHungry()) {
                    if (ItemHandler.getInstance().useItem(food.getItemId(), L2Player.this, food)) {
                        SystemMessage sm = new SystemMessage(SystemMessageId.PET_TOOK_S1_BECAUSE_HE_WAS_HUNGRY);
                        sm.addItemName(food.getItemId());
                        sendPacket(sm);
                    }
                }
            } catch (Exception e) {
                _log.fatal("Mounted Pet [NpcId: " + getMountNpcId() + "] a feed task error has occurred", e);
            }
        }
    }

    protected synchronized void startFeed(int npcId) {
        _canFeed = npcId > 0;
        if (!isMounted())
            return;
        if (getPet() != null) {
            setCurrentFeed(((L2PetInstance) getPet()).getCurrentFed());
            _controlItemId = getPet().getControlItemId();
            sendPacket(new SetupGauge(3, getCurrentFeed() * 10000 / getFeedConsume(),
                    getMaxFeed() * 10000 / getFeedConsume()));
            if (!isDead()) {
                _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000,
                        10000);
            }
        } else if (_canFeed) {
            setCurrentFeed(getMaxFeed());
            SetupGauge sg = new SetupGauge(3, getCurrentFeed() * 10000 / getFeedConsume(),
                    getMaxFeed() * 10000 / getFeedConsume());
            sendPacket(sg);
            if (!isDead()) {
                _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000,
                        10000);
            }
        }
    }

    protected synchronized void stopFeed() {
        if (_mountFeedTask != null) {
            _mountFeedTask.cancel(false);
            _mountFeedTask = null;
            if (_log.isDebugEnabled())
                _log.info("Pet [#" + _mountNpcId + "] feed task stop");
        }
    }

    protected final void clearPetData() {
        _data = null;
    }

    protected final L2PetData getPetData(int npcId) {
        if (_data == null && getPet() != null) {
            _data = PetDataTable.getInstance().getPetData(getPet().getNpcId(), getPet().getLevel());
        } else if (_data == null && npcId > 0) {
            _data = PetDataTable.getInstance().getPetData(npcId, getLevel());
        }

        return _data;
    }

    public int getCurrentFeed() {
        return _curFeed;
    }

    protected int getFeedConsume() {
        // if pet is attacking
        if (isAttackingNow())
            return getPetData(_mountNpcId).getPetFeedBattle();
        else
            return getPetData(_mountNpcId).getPetFeedNormal();
    }

    public void setCurrentFeed(int num) {
        _curFeed = num > getMaxFeed() ? getMaxFeed() : num;
        SetupGauge sg = new SetupGauge(3, getCurrentFeed() * 10000 / getFeedConsume(),
                getMaxFeed() * 10000 / getFeedConsume());
        sendPacket(sg);
    }

    protected int getMaxFeed() {
        return getPetData(_mountNpcId).getPetMaxFeed();
    }

    protected boolean isHungry() {
        return _canFeed ? (getCurrentFeed() < (0.55 * getPetData(getMountNpcId()).getPetMaxFeed())) : false;
    }

    public class dismount implements Runnable {
        @Override
        public void run() {
            dismount();
        }
    }

    public void enteredNoWyvernZone() {
        sendPacket(SystemMessageId.AREA_CANNOT_BE_ENTERED_WHILE_MOUNTED_WYVERN);

        _dismountTask = ThreadPoolManager.getInstance().scheduleGeneral(new L2Player.dismount(), 5000);
    }

    public void exitedNoWyvernZone() {
        if (_dismountTask != null) {
            _dismountTask.cancel(false);
            _dismountTask = null;
        }
    }

    public void storePetFood(int petId) {
        if (_controlItemId != 0 && petId != 0) {
            String req = "UPDATE pets SET fed=? WHERE item_obj_id = ?";
            Connection con = null;
            try {
                con = L2DatabaseFactory.getInstance().getConnection();
                PreparedStatement statement = con.prepareStatement(req);
                statement.setInt(1, getCurrentFeed());
                statement.setInt(2, _controlItemId);
                statement.executeUpdate();
                statement.close();
                _controlItemId = 0;
            } catch (Exception e) {
                _log.fatal("Failed to store Pet [NpcId: " + petId + "] data", e);
            } finally {
                L2DatabaseFactory.close(con);
            }
        }
    }

    /** End of section for mounted pets */

    /**
     * 1. Add the specified class ID as a subclass (up to the maximum number of <b>three</b>)
     * for this character.<BR>
     * 2. This method no longer changes the active _classIndex of the player. This is only
     * done by the calling of setActiveClass() method as that should be the only way to do so.
     *
     * @param classId
     * @param classIndex
     * @return subclassAdded
     */
    public boolean addSubClass(int classId, int classIndex) {
        if (!_subclassLock.tryLock())
            return false;

        try {
            if (getTotalSubClasses() == Config.ALT_MAX_SUBCLASS || classIndex == 0)
                return false;

            if (getSubClasses().containsKey(classIndex))
                return false;

            // Note: Never change _classIndex in any method other than setActiveClass().

            SubClass newClass = new SubClass();
            newClass.setClassId(classId);
            newClass.setClassIndex(classIndex);

            Connection con = null;
            try {
                // Store the basic info about this new sub-class.
                con = L2DatabaseFactory.getInstance().getConnection();
                PreparedStatement statement = con.prepareStatement(ADD_CHAR_SUBCLASS);
                statement.setInt(1, getObjectId());
                statement.setInt(2, newClass.getClassId());
                statement.setLong(3, newClass.getExp());
                statement.setInt(4, newClass.getSp());
                statement.setInt(5, newClass.getLevel());
                statement.setInt(6, newClass.getClassIndex()); // <-- Added
                statement.execute();
                statement.close();
            } catch (Exception e) {
                _log.warn("Could not add character sub class for " + getName() + ": ", e);
                return false;
            } finally {
                L2DatabaseFactory.close(con);
            }

            // Commit after database INSERT incase exception is thrown.
            getSubClasses().put(newClass.getClassIndex(), newClass);

            if (_log.isDebugEnabled())
                _log.info(
                        getName() + " added class ID " + classId + " as a sub class at index " + classIndex + ".");

            ClassId subTemplate = ClassId.values()[classId];
            Iterable<L2SkillLearn> skillTree = SkillTreeTable.getInstance().getAllowedSkills(subTemplate);

            if (skillTree == null)
                return true;

            final Map<Integer, L2Skill> skills = new FastMap<Integer, L2Skill>();

            for (L2SkillLearn skillInfo : skillTree) {
                if (skillInfo.getMinLevel() <= 40) {
                    final L2Skill prevSkill = skills.get(skillInfo.getId());
                    final L2Skill newSkill = SkillTable.getInstance().getInfo(skillInfo.getId(),
                            skillInfo.getLevel());

                    if (prevSkill != null && prevSkill.getLevel() >= newSkill.getLevel())
                        continue;

                    skills.put(newSkill.getId(), newSkill);
                }
            }

            for (L2Skill skill : skills.values())
                _pcSkills.storeSkill(skill, classIndex);

            if (_log.isDebugEnabled())
                _log.info(getName() + " was given " + getAllSkills().length + " skills for their new sub class.");

            return true;
        } finally {
            _subclassLock.unlock();
        }
    }

    /**
     * 1. Completely erase all existance of the subClass linked to the classIndex.<BR>
     * 2. Send over the newClassId to addSubClass()to create a new instance on this classIndex.<BR>
     * 3. Upon Exception, revert the player to their BaseClass to avoid further problems.<BR>
     * 
     * @param classIndex
     * @param newClassId
     * @return subclassAdded
     */
    public boolean modifySubClass(int classIndex, int newClassId) {
        if (!_subclassLock.tryLock())
            return false;

        try {
            int oldClassId = getSubClasses().get(classIndex).getClassId();

            if (_log.isDebugEnabled())
                _log.info(getName() + " has requested to modify sub class index " + classIndex + " from class ID "
                        + oldClassId + " to " + newClassId + ".");

            Connection con = null;
            try {
                con = L2DatabaseFactory.getInstance().getConnection();

                // Remove all henna info stored for this sub-class.
                PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNAS);
                statement.setInt(1, getObjectId());
                statement.setInt(2, classIndex);
                statement.execute();
                statement.close();

                // Remove all shortcuts info stored for this sub-class.
                statement = con.prepareStatement(DELETE_CHAR_SHORTCUTS);
                statement.setInt(1, getObjectId());
                statement.setInt(2, classIndex);
                statement.execute();
                statement.close();

                // Remove all effects info stored for this sub-class.
                getEffects().deleteEffects(con, classIndex);

                // Remove all skill info stored for this sub-class.
                _pcSkills.deleteSkills(con, classIndex);

                // Remove all basic info stored about this sub-class.
                statement = con.prepareStatement(DELETE_CHAR_SUBCLASS);
                statement.setInt(1, getObjectId());
                statement.setInt(2, classIndex);
                statement.execute();
                statement.close();
            } catch (Exception e) {
                _log.warn("Could not modify sub class for " + getName() + " to class index " + classIndex + ": ",
                        e);
                // This must be done in order to maintain data consistency.
                getSubClasses().remove(classIndex);
                return false;
            } finally {
                L2DatabaseFactory.close(con);
            }

            getSubClasses().remove(classIndex);
        } finally {
            _subclassLock.unlock();
        }

        return addSubClass(newClassId, classIndex);
    }

    public boolean isSubClassActive() {
        return _classIndex > 0;
    }

    public Map<Integer, SubClass> getSubClasses() {
        if (_subClasses == null)
            _subClasses = new LazyFastMap<Integer, SubClass>();

        return _subClasses;
    }

    public int getTotalSubClasses() {
        return getSubClasses().size();
    }

    public int getBaseClass() {
        return _baseClass;
    }

    public int getActiveClass() {
        return _activeClass;
    }

    public int getClassIndex() {
        return _classIndex;
    }

    private void setClassTemplate(int classId) {
        _activeClass = classId;

        L2PlayerTemplate t = CharTemplateTable.getInstance().getTemplate(classId);

        if (t == null) {
            _log.fatal("Missing template for classId: " + classId);
            throw new Error();
        }

        // Set the template of the L2Player
        setTemplate(t);

        L2PartyRoom room = getPartyRoom();
        if (room != null)
            room.broadcastPacket(new ExManagePartyRoomMember(ExManagePartyRoomMember.MODIFIED, this));
    }

    /**
     * Changes the character's class based on the given class index.
     * <BR><BR>
     * An index of zero specifies the character's original (base) class,
     * while indexes 1-3 specifies the character's sub-classes respectively.
     * <br><br>
     * <font color="00FF00"/>WARNING: Use only on subclass change</font>
     *
     * @param classIndex
     */
    public boolean setActiveClass(int classIndex) {
        if (!_subclassLock.tryLock())
            return false;

        try {
            //  Cannot switch or change subclasses while transformed
            if (_transformation != null)
                return false;

            // Remove active item skills before saving char to database
            // because next time when choosing this class, weared items can be different

            for (L2ItemInstance temp : getInventory().getAugmentedItems())
                if (temp != null && temp.isEquipped())
                    temp.getAugmentation().removeBonus(this);

            // Remove class circlets (can't equip circlets while being in subclass)
            L2ItemInstance circlet = getInventory().getPaperdollItem(Inventory.PAPERDOLL_HAIRALL);
            if (circlet != null) {
                if (((circlet.getItemId() >= 9397 && circlet.getItemId() <= 9408) || circlet.getItemId() == 10169)
                        && circlet.isEquipped()) {
                    L2ItemInstance[] unequipped = getInventory()
                            .unEquipItemInBodySlotAndRecord(circlet.getItem().getBodyPart());
                    InventoryUpdate iu = new InventoryUpdate();
                    for (L2ItemInstance element : unequipped)
                        iu.addModifiedItem(element);
                    sendPacket(iu);
                }
            }

            // Delete a force buff upon class change.
            if (_fusionSkill != null)
                abortCast();

            // Stop casting for any player that may be casting a force buff on this L2Player.
            for (L2Creature character : getKnownList().getKnownCharacters()) {
                if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
                    character.abortCast();
            }

            /*
             * 1. Call store() before modifying _classIndex to avoid skill effects rollover.
             * 2. Register the correct _classId against applied 'classIndex'.
             */

            store(Config.STORE_EFFECTS_ON_SUBCLASS_CHANGE);

            // clear charges
            clearCharges();

            if (classIndex == 0) {
                setClassTemplate(getBaseClass());
            } else {
                try {
                    setClassTemplate(getSubClasses().get(classIndex).getClassId());
                } catch (Exception e) {
                    _log.info("Could not switch " + getName() + "'s sub class to class index " + classIndex + ": ",
                            e);
                    return false;
                }
            }
            _classIndex = classIndex;

            if (isInParty()) {
                if (Config.MAX_PARTY_LEVEL_DIFFERENCE > 0) {
                    for (L2Player p : getParty().getPartyMembers()) {
                        if (Math.abs(p.getLevel() - getLevel()) > Config.MAX_PARTY_LEVEL_DIFFERENCE) {
                            getParty().removePartyMember(this);
                            sendMessage(
                                    "You have been removed from your party, because the level difference is too big.");
                            break;
                        }
                    }
                } else
                    getParty().recalculatePartyLevel();
            }

            /*
             * Update the character's change in class status.
             *
             * 1. Remove any active cubics from the player.
             * 2. Renovate the characters table in the database with the new class info, storing also buff/effect data.
             * 3. Remove all existing skills.
             * 4. Restore all the learned skills for the current class from the database.
             * 5. Restore effect/buff data for the new class.
             * 6. Restore henna data for the class, applying the new stat modifiers while removing existing ones.
             * 7. Reset HP/MP/CP stats and send Server->Client character status packet to reflect changes.
             * 8. Restore shortcut data related to this class.
             * 9. Resend a class change animation effect to broadcast to all nearby players.
             * 10.Unsummon any active servitor from the player.
             */

            if (getPet() instanceof L2SummonInstance)
                getPet().unSummon(this);

            if (!getCubics().isEmpty()) {
                for (L2CubicInstance cubic : getCubics().values()) {
                    cubic.stopAction();
                    cubic.cancelDisappear();
                }

                getCubics().clear();
            }

            abortCast();

            for (L2Skill oldSkill : getAllSkills())
                super.removeSkill(oldSkill);

            stopAllEffectsExceptThoseThatLastThroughDeath();

            restoreRecipeBook(false);

            // Restore any Death Penalty Buff
            restoreDeathPenaltyBuffLevel();

            restoreSkills();
            regiveTemporarySkills();
            rewardSkills();

            getEffects().restoreEffects();
            updateEffectIcons();

            // If player has quest "422: Repent Your Sins", remove it
            QuestState st = getQuestState("422_RepentYourSins");
            if (st != null) {
                st.exitQuest(true);
            }

            for (int i = 0; i < 3; i++)
                _henna[i] = null;

            restoreHenna();
            sendPacket(new HennaInfo(this));

            checkItemRestriction();

            if (getCurrentHp() > getMaxHp())
                getStatus().setCurrentHp(getMaxHp());
            if (getCurrentMp() > getMaxMp())
                getStatus().setCurrentMp(getMaxMp());
            if (getCurrentCp() > getMaxCp())
                getStatus().setCurrentCp(getMaxCp());

            getInventory().restoreEquipedItemsPassiveSkill();
            getInventory().restoreArmorSetPassiveSkill();

            refreshOverloaded();
            refreshExpertisePenalty();
            broadcastUserInfo();

            // Clear resurrect xp calculation
            setExpBeforeDeath(0);

            //getMacroses().restore();
            //getMacroses().sendUpdate();

            getShortCuts().restore();
            sendPacket(new ShortCutInit(this));

            broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP));
            sendSkillCoolTime();
            sendPacket(new ExStorageMaxCount(this));

            broadcastClassIcon();

            return true;
        } finally {
            _subclassLock.unlock();
        }
    }

    public boolean isLocked() {
        return _subclassLock.isLocked();
    }

    public void broadcastClassIcon() {
        // Update class icon in party and clan
        if (isInParty())
            getParty().broadcastToPartyMembers(new PartySmallWindowUpdate(this));

        if (getClan() != null)
            getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
    }

    public void stopWarnUserTakeBreak() {
        if (_taskWarnUserTakeBreak != null) {
            _taskWarnUserTakeBreak.cancel(false);
            _taskWarnUserTakeBreak = null;
        }
    }

    public void startWarnUserTakeBreak() {
        if (_taskWarnUserTakeBreak == null)
            _taskWarnUserTakeBreak = ThreadPoolManager.getInstance()
                    .scheduleGeneralAtFixedRate(new WarnUserTakeBreak(), 7200000, 7200000);
    }

    public void stopRentPet() {
        if (_taskRentPet != null) {
            // If the rent of a wyvern expires while over a flying zone, tp to down before unmounting
            if (getMountType() == 2 && !checkCanLand())
                teleToLocation(TeleportWhereType.Town);

            if (dismount()) // This should always be true now, since we teleported already
            {
                _taskRentPet.cancel(true);
                _taskRentPet = null;
            }
        }
    }

    public void startRentPet(int seconds) {
        if (_taskRentPet == null)
            _taskRentPet = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new RentPetTask(),
                    seconds * 1000L, seconds * 1000);
    }

    public boolean isRentedPet() {
        return _taskRentPet != null;
    }

    public void stopWaterTask() {
        if (_taskWater != null) {
            _taskWater.cancel(false);

            _taskWater = null;
            sendPacket(new SetupGauge(SetupGauge.CYAN, 0));
            // Added to sync fall when swimming stops:
            // (e.g. catacombs players swim down and then they fell when they got out of the water).
            isFalling(false, 0);
        }

        broadcastUserInfo();
    }

    public void startWaterTask() {
        // Temp fix here
        if (isMounted())
            dismount();

        if (isTransformed() && !isCursedWeaponEquipped()) {
            untransform();
        }
        // TODO: update to only send speed status when that packet is known
        else
            broadcastUserInfo();

        if (!isDead() && _taskWater == null) {
            int timeinwater = (int) calcStat(Stats.BREATH, 60000, this, null);

            sendPacket(new SetupGauge(2, timeinwater));
            _taskWater = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new WaterTask(), timeinwater,
                    1000);
        }
    }

    public boolean isInWater() {
        return _taskWater != null;
    }

    public void onPlayerEnter() {
        startWarnUserTakeBreak();
        startAutoSaveTask();

        if (SevenSigns.getInstance().isSealValidationPeriod() || SevenSigns.getInstance().isCompResultsPeriod()) {
            if (!isGM() && isIn7sDungeon() && Config.ALT_STRICT_SEVENSIGNS && SevenSigns.getInstance()
                    .getPlayerCabal(this) != SevenSigns.getInstance().getCabalHighestScore()) {
                teleToLocation(TeleportWhereType.Town);
                setIsIn7sDungeon(false);
                sendPacket(SystemMessage.sendString(
                        "You have been teleported to the nearest town due to the beginning of the Seal Validation period."));
            }
        } else {
            if (!isGM() && isIn7sDungeon() && Config.ALT_STRICT_SEVENSIGNS
                    && SevenSigns.getInstance().getPlayerCabal(this) == SevenSigns.CABAL_NULL) {
                teleToLocation(TeleportWhereType.Town);
                setIsIn7sDungeon(false);
                sendPacket(SystemMessage.sendString(
                        "You have been teleported to the nearest town because you have not signed for any cabal."));
            }
        }

        // Jail task
        updateJailState();

        if (_isInvul) // isInvul() is always true on login if login protection is activated...
            sendPacket(SystemMessage.sendString("Entering world in Invulnerable mode."));
        if (getAppearance().isInvisible())
            sendPacket(SystemMessage.sendString("Entering world in Invisible mode."));
        if (getMessageRefusal())
            sendPacket(SystemMessage.sendString("Entering world in Message Refusal mode."));

        revalidateZone(true);

        // [L2J_JP ADD SANDMAN] Check of a restart prohibition area.
        if (!isGM()) {
            // Four-Sepulcher,It is less than 5 minutes.
            if (FourSepulchersManager.getInstance().checkIfInZone(this)
                    && (System.currentTimeMillis() - getLastAccess() >= 300000)) {
                int driftX = Rnd.get(-80, 80);
                int driftY = Rnd.get(-80, 80);
                teleToLocation(178293 + driftX, -84607 + driftY, -7216);
            }

            // It is less than a time limit from player restarting.
            // TODO write code for restart fight against bosses.
            // Lair of bosses,It is less than 30 minutes from server starting.
            // It is only for Antharas and Valakas that I know it now.
            // Thanks a lot Serafiel of L2J_JP.

            // 10 minutes
            // Antharas
            else if (AntharasManager.getInstance().checkIfInZone(this)) {
                // Lair of bosses,It is less than 30 minutes from server starting.
                // Player can restart inside lair, but Antharas do not respawn.
                if (System.currentTimeMillis()
                        - GameServer.getStartedTime().getTimeInMillis() <= Config.ALT_TIMELIMITOFINVADE) {
                    if (getQuestState("antharas") != null)
                        getQuestState("antharas").exitQuest(true);
                } else if (System.currentTimeMillis() - getLastAccess() >= 600000) {
                    if (getQuestState("antharas") != null)
                        getQuestState("antharas").exitQuest(true);
                    teleToLocation(TeleportWhereType.Town);
                }
            }

            // Baium
            else if (BaiumManager.getInstance().checkIfInZone(this)) {
                if (System.currentTimeMillis() - getLastAccess() >= 600000) {
                    if (getQuestState("baium") != null)
                        getQuestState("baium").exitQuest(true);
                    teleToLocation(TeleportWhereType.Town);
                } else {
                    // Player can restart inside lair, but can not awake Baium.
                    if (getQuestState("baium") != null)
                        getQuestState("baium").exitQuest(true);
                }
            }

            // 10 minutes
            // Last Imperial Tomb (includes Frintezza's room)
            else if (LastImperialTombManager.getInstance().checkIfInZone(this)) {
                // Lair of bosses,It is less than 30 minutes from server starting.
                // Player can restart inside lair, but Antharas do not respawn.
                if (System.currentTimeMillis()
                        - GameServer.getStartedTime().getTimeInMillis() <= Config.ALT_TIMELIMITOFINVADE) {
                    if (getQuestState("lastimperialtomb") != null)
                        getQuestState("lastimperialtomb").exitQuest(true);
                } else if (System.currentTimeMillis() - getLastAccess() >= 600000) {
                    if (getQuestState("lastimperialtomb") != null)
                        getQuestState("lastimperialtomb").exitQuest(true);
                    teleToLocation(TeleportWhereType.Town);
                }
            }

            // 10 minutes
            // Not sure if Imperial tomb includes Frintezza's room
            // So just incase lets clear that room from intruders till all FrintezzaManager will work like retail
            else if (FrintezzaManager.getInstance().checkIfInZone(this)) {
                if (System.currentTimeMillis() - getLastAccess() >= 600000
                        && !FrintezzaManager.getInstance().getState().equals(GrandBossState.StateEnum.ALIVE))
                    teleToLocation(TeleportWhereType.Town);
            }

            // Lilith
            /*else if (LilithManager.getInstance().checkIfInZone(this))
            {
               if (System.currentTimeMillis() - getLastAccess() >= 600000)
                  teleToLocation(TeleportWhereType.Town);
            }
                
            // Anakim
            else if (AnakimManager.getInstance().checkIfInZone(this))
            {
               if (System.currentTimeMillis() - getLastAccess() >= 600000)
                  teleToLocation(TeleportWhereType.Town);
            }
                
            // Zaken
            else if (ZakenManager.getInstance().checkIfInZone(this))
            {
               if (System.currentTimeMillis() - getLastAccess() >= 600000)
                  teleToLocation(TeleportWhereType.Town);
            }*/

            // High Priestess van Halter
            else if (VanHalterManager.getInstance().checkIfInZone(this)) {
                if (System.currentTimeMillis() - getLastAccess() >= 600000)
                    teleToLocation(TeleportWhereType.Town);
                else
                    VanHalterManager.getInstance().intruderDetection(this);
            }

            // 30 minutes
            // Valakas
            else if (ValakasManager.getInstance().checkIfInZone(this)) {
                // Lair of bosses,It is less than 30 minutes from server starting.
                // Player can restart inside lair, and begin fight against Valakas 30min later.
                if (System.currentTimeMillis()
                        - GameServer.getStartedTime().getTimeInMillis() <= Config.ALT_TIMELIMITOFINVADE
                        && ValakasManager.getInstance().getState() == GrandBossState.StateEnum.ALIVE) {
                } else {
                    if (getQuestState("valakas") != null)
                        getQuestState("valakas").exitQuest(true);
                    teleToLocation(TeleportWhereType.Town);
                }
            }
            // Baylor
            else if (BaylorManager.getInstance().checkIfInZone(this)) {
                if (getQuestState("baylor") != null)
                    getQuestState("baylor").exitQuest(true);
                teleToLocation(TeleportWhereType.Town);
            }
            // Sailren
            else if (SailrenManager.getInstance().checkIfInZone(this)) {
                if (getQuestState("sailren") != null)
                    getQuestState("sailren").exitQuest(true);
                teleToLocation(TeleportWhereType.Town);
            }
        }
    }

    public void checkWaterState() {
        if (isInsideZone(L2Zone.FLAG_WATER)) {
            startWaterTask();
        } else {
            stopWaterTask();
        }
    }

    public long getLastAccess() {
        return _lastAccess;
    }

    public int getBoatId() {
        return _boatId;
    }

    public void setBoatId(int boatId) {
        _boatId = boatId;
    }

    @Override
    public void doRevive() {
        super.doRevive();
        stopEffects(L2EffectType.CHARMOFCOURAGE);
        updateEffectIcons();
        _reviveRequested = false;

        if (isMounted())
            startFeed(_mountNpcId);

        if (isInParty() && getParty().isInDimensionalRift()) {
            if (!DimensionalRiftManager.getInstance().checkIfInPeaceZone(getX(), getY(), getZ()))
                getParty().getDimensionalRift().memberRessurected(this);
        }

        GlobalRestrictions.playerRevived(this);
    }

    @Override
    public void doRevive(double revivePower) {
        // Restore the player's lost experience,
        // depending on the % return of the skill used (based on its power).
        restoreExp(revivePower);
        doRevive();
    }

    public void reviveRequest(L2Player reviver, L2Skill skill) {
        if (_reviveRequested || _revivePetRequested) {
            reviver.sendPacket(SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED); // Resurrection is already been proposed.
            return;
        }
        if (isDead()) {
            _reviveRequested = true;

            final double revivePower;
            if (isPhoenixBlessed())
                revivePower = 100;
            else if (skill != null)
                revivePower = Formulas.calculateSkillResurrectRestorePercent(skill, reviver);
            else
                revivePower = 0;

            int restoreExp = (int) Math.round((getExpBeforeDeath() - getExp()) * revivePower / 100);

            if (getCharmOfCourage()) {
                ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.RESURRECT_USING_CHARM_OF_COURAGE);
                dlg.addTime(60000);
                dlg.addAnswerHandler(new AnswerHandler() {
                    @Override
                    public void handle(boolean answer) {
                        reviveAnswer(answer, revivePower);
                    }
                });
                sendPacket(dlg);
                return;
            }

            ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.RESSURECTION_REQUEST_BY_C1_FOR_S2_XP);
            dlg.addPcName(reviver);
            dlg.addString(String.valueOf(restoreExp));
            dlg.addAnswerHandler(new AnswerHandler() {
                @Override
                public void handle(boolean answer) {
                    reviveAnswer(answer, revivePower);
                }
            });
            sendPacket(dlg);
        }
    }

    public void revivePetRequest(L2Player reviver, L2Skill skill) {
        if (_reviveRequested || _revivePetRequested) {
            reviver.sendPacket(SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED); // Resurrection is already been proposed.
            return;
        }

        if (getPet().isDead() && getPet() instanceof L2PetInstance) {
            _revivePetRequested = true;

            final double revivePower;
            if (skill != null)
                revivePower = Formulas.calculateSkillResurrectRestorePercent(skill, reviver);
            else
                revivePower = 0;

            int restoreExp = (int) Math
                    .round((((L2PetInstance) getPet()).getExpBeforeDeath() - getPet().getStat().getExp())
                            * revivePower / 100);

            ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.RESSURECTION_REQUEST_BY_C1_FOR_S2_XP);
            dlg.addAnswerHandler(new AnswerHandler() {
                @Override
                public void handle(boolean answer) {
                    reviveAnswer(answer, revivePower);
                }
            });
            sendPacket(dlg.addPcName(reviver).addString("" + restoreExp));
        }
    }

    public void reviveAnswer(boolean answer, double revivePower) {
        if (!(_reviveRequested && isDead() || _revivePetRequested && getPet() != null && getPet().isDead()))
            return;
        // If character refuses a PhoenixBless autoress, cancel all buffs he had
        if (!answer && isPhoenixBlessed() && isDead() && _reviveRequested) {
            stopPhoenixBlessing(true);
            stopAllEffectsExceptThoseThatLastThroughDeath();
        }

        if (answer) {
            if (_reviveRequested) {
                if (revivePower != 0)
                    doRevive(revivePower);
                else
                    doRevive();
            } else if (_revivePetRequested && getPet() != null) {
                if (revivePower != 0)
                    getPet().doRevive(revivePower);
                else
                    getPet().doRevive();
            }
        }
        _reviveRequested = false;
        _revivePetRequested = false;
    }

    public boolean isReviveRequested() {
        return _reviveRequested;
    }

    public boolean isPetReviveRequested() {
        return _revivePetRequested;
    }

    public void removeReviving() {
        _reviveRequested = false;
    }

    public void removePetReviving() {
        _revivePetRequested = false;
    }

    public void onActionRequest() {
        setProtection(false);
    }

    /**
     * @param expertiseIndex The expertiseIndex to set.
     */
    public void setExpertiseIndex(int expertiseIndex) {
        _expertiseIndex = expertiseIndex;
    }

    /**
     * @return Returns the expertiseIndex.
     */
    public int getExpertiseIndex() {
        return _expertiseIndex;
    }

    @Override
    public boolean onTeleported() {
        if (!super.onTeleported())
            return false;

        getKnownList().updateKnownObjects();

        setProtection(true);

        // Trained beast is after teleport lost
        if (getTrainedBeast() != null) {
            getTrainedBeast().decayMe();
            setTrainedBeast(null);
        }
        // Modify the position of the pet if necessary
        L2Summon pet = getPet();
        if (pet != null) {
            pet.setFollowStatus(false);
            getPet().teleToLocation(getPosition().getX(), getPosition().getY(), getPosition().getZ(), false);
            ((L2SummonAI) getPet().getAI()).setStartFollowController(true);
            pet.setFollowStatus(true);
            getPet().broadcastFullInfoImpl(0);
        }

        sendPacket(new UserInfo(this));
        return true;
    }

    @Override
    public void setIsTeleporting(boolean teleport) {
        super.setIsTeleporting(teleport);
        if (teleport) {
            if (Config.TELEPORT_WATCHDOG_TIMEOUT > 0 && _teleportWatchdog == null)
                _teleportWatchdog = ThreadPoolManager.getInstance().scheduleGeneral(new TeleportWatchdog(),
                        Config.TELEPORT_WATCHDOG_TIMEOUT * 1000);
        } else if (_teleportWatchdog != null) {
            _teleportWatchdog.cancel(false);
            _teleportWatchdog = null;
        }
    }

    private class TeleportWatchdog implements Runnable {
        @Override
        public void run() {
            if (!isTeleporting() || getOnlineState() == L2Player.ONLINE_STATE_DELETED)
                return;

            if (_log.isDebugEnabled())
                _log.debug("Player " + getName() + " teleport timeout expired");
            onTeleported();
        }
    }

    public void teleToLocation(TeleportWhereType teleportWhere) {
        teleToLocation(MapRegionManager.getInstance().getTeleToLocation(this, teleportWhere), true);
    }

    @Override
    public void addExpAndSp(long addToExp, int addToSp) {
        getStat().addExpAndSp(addToExp, addToSp, false);
    }

    public void addExpAndSp(long addToExp, int addToSp, boolean useVitality) {
        getStat().addExpAndSp(addToExp, addToSp, useVitality);
    }

    public void removeExpAndSp(long removeExp, int removeSp) {
        getStat().removeExpAndSp(removeExp, removeSp);
    }

    public void removeExpAndSp(long removeExp, int removeSp, boolean sendMessage) {
        getStat().removeExpAndSp(removeExp, removeSp, sendMessage);
    }

    /**
     * Function is used in the PLAYER, calls snoop for all GMs listening to this player speak.
     *
     * @param channel - msg channel of the snooped player
     * @param name - name of snooped player
     * @param text - the msg the snooped player sent/received
     */
    public void broadcastSnoop(SystemChatChannelId channel, String name, String text) {
        if (_snoopers.length == 0)
            return;

        final Snoop sn = new Snoop(this, channel, name, text);

        for (L2Player snooper : _snoopers)
            snooper.sendPacket(sn);
    }

    /**
     * Adds a spy ^^ GM to the player listener.
     *
     * @param pci - GM char that listens to the conversation
     */
    public void addSnooper(L2Player snooper) {
        if (!ArrayUtils.contains(_snoopers, snooper))
            _snoopers = ArrayUtils.add(_snoopers, snooper);
    }

    public void removeSnooper(L2Player snooper) {
        _snoopers = ArrayUtils.removeElement(_snoopers, snooper);
    }

    public void removeSnooped(L2Player snooped) {
        _snoopedPlayers = ArrayUtils.removeElement(_snoopedPlayers, snooped);
    }

    /**
     * Adds a player to the GM queue for being listened.
     * @param pci - player we listen to
     */
    public void addSnooped(L2Player snooped) {
        if (!ArrayUtils.contains(_snoopedPlayers, snooped)) {
            _snoopedPlayers = ArrayUtils.add(_snoopedPlayers, snooped);

            sendPacket(new Snoop(snooped, SystemChatChannelId.Chat_Normal, "",
                    "*** Starting snooping of player " + snooped.getName() + " ***"));
        }
    }

    public synchronized void buildBypassCache(final Replaceable replaceable) {
        if (_validBypass != null)
            _validBypass.clear();

        if (_validBypass2 != null)
            _validBypass2.clear();

        for (int i = 0; i < replaceable.length(); i++) {
            int start = replaceable.indexOf("bypass -h", i);
            int finish = replaceable.indexOf("\"", start);

            if (start < 0 || finish < 0)
                break;

            start += 10;
            i = start;
            int finish2 = replaceable.indexOf("$", start);

            if (0 < finish2 && finish2 < finish) {
                if (_validBypass2 == null)
                    _validBypass2 = new ArrayList<String>();

                _validBypass2.add(replaceable.substring(start, finish2).trim());
            } else {
                if (_validBypass == null)
                    _validBypass = new ArrayList<String>();

                _validBypass.add(replaceable.substring(start, finish).trim());
            }
        }
    }

    public synchronized void validateBypass(String cmd) throws InvalidPacketException {
        if (_validBypass != null)
            for (String bp : _validBypass)
                if (bp != null && cmd.equals(bp))
                    return;

        if (_validBypass2 != null)
            for (String bp : _validBypass2)
                if (bp != null && cmd.startsWith(bp))
                    return;

        throw new InvalidPacketException("[" + this + "] sent invalid bypass '" + cmd + "'!");
    }

    /**
     * Performs following tests:<br>
     * <li> Inventory contains item
     * <li> Item owner id == this.owner id
     * <li> It isnt pet control item while mounting pet or pet summoned
     * <li> It isnt active enchant item
     * <li> It isnt cursed weapon/item
     * <li> It isnt wear item
     * <br>
     * 
     * @param objectId item object id
     * @param action just for logging
     * @return
     */
    public boolean validateItemManipulation(int objectId, String action) {
        L2ItemInstance item = getInventory().getItemByObjectId(objectId);

        if (item == null || item.getOwnerId() != getObjectId()) {
            _log.debug(getObjectId() + ": player tried to " + action + " item he is not owner of");
            return false;
        }

        // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting
        if (getPet() != null && getPet().getControlItemId() == objectId || getMountObjectID() == objectId) {
            if (_log.isDebugEnabled())
                _log.debug(getObjectId() + ": player tried to " + action + " item controling pet");

            return false;
        }

        if (getActiveEnchantItem() != null && getActiveEnchantItem().getObjectId() == objectId) {
            if (_log.isDebugEnabled())
                _log.debug(getObjectId() + ":player tried to " + action + " an enchant scroll he was using");

            return false;
        }

        if (CursedWeaponsManager.getInstance().isCursed(item.getItemId())) {
            // Cannot trade a cursed weapon
            return false;
        }

        return !item.isWear();
    }

    /**
     * @return Returns the inBoat.
     */
    public boolean isInBoat() {
        return getBoat() != null;
    }

    /**
     * @return
     */
    public L2BoatInstance getBoat() {
        return _boat;
    }

    /**
     * @param boat
     */
    public void setBoat(L2BoatInstance boat) {
        _boat = boat;
    }

    /**
     * @return Returns the inAirShip.
     */
    public boolean isInAirShip() {
        return getAirShip() != null;
    }

    /**
     * @return
     */
    public L2AirShipInstance getAirShip() {
        return _airShip;
    }

    /**
     * @param airShip
     */
    public void setAirShip(L2AirShipInstance airShip) {
        _airShip = airShip;
    }

    public void setInCrystallize(boolean inCrystallize) {
        _inCrystallize = inCrystallize;
    }

    public boolean isInCrystallize() {
        return _inCrystallize;
    }

    /**
     * @return
     */
    public Point3D getInBoatPosition() {
        return _inBoatPosition;
    }

    public void setInBoatPosition(Point3D pt) {
        _inBoatPosition = pt;
    }

    /**
     * @return
     */
    public Point3D getInAirShipPosition() {
        return _inAirShipPosition;
    }

    public void setInAirShipPosition(Point3D pt) {
        _inAirShipPosition = pt;
    }

    /**
     * Manage the delete task of a L2Player (Leave Party, Unsummon pet, Save its inventory in the database, Remove it from the world...).<BR><BR>
     *
     * <B><U> Actions</U> :</B><BR><BR>
     * <li>If the L2Player is in observer mode, set its position to its position before entering in observer mode </li>
     * <li>Set the online Flag to True or False and update the characters table of the database with online status and lastAccess </li>
     * <li>Stop the HP/MP/CP Regeneration task </li>
     * <li>Cancel Crafting, Attak or Cast </li>
     * <li>Remove the L2Player from the world </li>
     * <li>Stop Party and Unsummon Pet </li>
     * <li>Update database with items in its inventory and remove them from the world </li>
     * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2Creature then cancel Attak or Cast and notify AI </li>
     * <li>Close the connection with the client </li><BR><BR>
     *
     */
    public void deleteMe() {
        final HashSet<L2Zone> before = getZonesPlayerIn();

        if (getOnlineState() == ONLINE_STATE_DELETED)
            return;

        // Pause restrictions
        ObjectRestrictions.getInstance().pauseTasks(getObjectId());

        abortCast();
        abortAttack();

        try {
            if (isFlying())
                removeSkill(SkillTable.getInstance().getInfo(4289, 1));
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        try {
            L2ItemInstance flag = getInventory().getItemByItemId(9819);
            if (flag != null) {
                Fort fort = FortManager.getInstance().getFort(this);
                if (fort != null)
                    FortSiegeManager.getInstance().dropCombatFlag(this);
                else {
                    int slot = flag.getItem().getBodyPart();
                    getInventory().unEquipItemInBodySlotAndRecord(slot);
                    destroyItem("CombatFlag", flag, null, true);
                }
            }
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        // If the L2Player has Pet, unsummon it
        if (getPet() != null) {
            try {
                getPet().unSummon(this);
                // dead pet wasnt unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
                if (getPet() != null)
                    getPet().broadcastFullInfoImpl(0);
            } catch (Exception e) {
                _log.error(e.getMessage(), e);
            } // Return pet to the control item
        }

        // Cancel trade
        if (getActiveRequester() != null) {
            getActiveRequester().onTradeCancel(this);
            onTradeCancel(getActiveRequester());

            cancelActiveTrade();

            setActiveRequester(null);
        }

        // Check if the L2Player is in observer mode to set its position to its position before entering in observer mode
        if (inObserverMode())
            getPosition().setXYZ(_obsX, _obsY, _obsZ);
        else if (isInAirShip())
            getAirShip().oustPlayer(this);

        Castle castle = null;
        if (getClan() != null) {
            castle = CastleManager.getInstance().getCastleByOwner(getClan());
            if (castle != null)
                castle.destroyClanGate();
        }

        // Set the online Flag to True or False and update the characters table of the database with online status and lastAccess (called when login and logout)
        try {
            setOnlineStatus(false);
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        // Stop the HP/MP/CP Regeneration task (scheduled tasks)
        try {
            stopAllTimers();
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        GlobalRestrictions.playerDisconnected(this);

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

        // Stop crafting, if in progress
        try {
            RecipeTable.getInstance().requestMakeItemAbort(this);
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

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

        if (_throne != null)
            _throne.setOccupier(null);
        _throne = 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.getMessage(), e);
        }

        getEffects().stopAllEffects(true);

        // Remove from world regions zones
        L2WorldRegion oldRegion = getWorldRegion();

        // Remove the L2Player from the world
        if (isVisible()) {
            try {
                decayMe();
            } catch (Exception e) {
                _log.fatal(e.getMessage(), e);
            }
        }

        if (oldRegion != null)
            oldRegion.removeFromZones(this);

        // If a Party is in progress, leave it (and festival party)
        if (isInParty()) {
            try {
                // If player is festival participant and it is in progress
                // notify party members that the player is not longer a participant.
                if (isFestivalParticipant() && SevenSignsFestival.getInstance().isFestivalInitialized()) {
                    getParty().broadcastToPartyMembers(
                            SystemMessage.sendString(getName() + " has been removed from the upcoming festival."));
                }

                leaveParty();
            } catch (Exception e) {
                _log.fatal(e.getMessage(), e);
            }
        } else
        // if in party, already taken care of
        {
            L2PartyRoom room = getPartyRoom();
            if (room != null)
                room.removeMember(this, false);
        }
        PartyRoomManager.getInstance().removeFromWaitingList(this);

        if (getClanId() != 0 && getClan() != null) {
            // Set the status for pledge member list to OFFLINE
            try {
                L2ClanMember clanMember = getClan().getClanMember(getName());
                if (clanMember != null)
                    clanMember.setPlayerInstance(null);
            } catch (Exception e) {
                _log.fatal(e.getMessage(), e);
            }
        }

        if (getActiveRequester() != null) {
            // Deals with sudden exit in the middle of transaction
            setActiveRequester(null);
        }

        // If the L2Player is a GM, remove it from the GM List
        if (isGM()) {
            try {
                GmListTable.deleteGm(this);
            } catch (Exception e) {
                _log.fatal(e.getMessage(), e);
            }
        }

        // remove player from instance and set spawn location if any
        try {
            if (isInInstance()) {
                final Instance inst = InstanceManager.getInstance().getInstance(getInstanceId());
                if (inst != null) {
                    inst.removePlayer(getObjectId());
                    final Location spawn = inst.getSpawnLoc();
                    if (spawn != null) {
                        final int x = spawn.getX() + Rnd.get(-30, 30);
                        final int y = spawn.getY() + Rnd.get(-30, 30);
                        getPosition().setXYZ(x, y, spawn.getZ());
                        if (getPet() != null) // dead pet
                        {
                            getPet().teleToLocation(x, y, spawn.getZ());
                            // ??? unset pet's instance id, but not players
                            getPet().decayMe();
                            getPet().spawnMe();
                        }
                    }
                }
            }
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        // Update database with items in its inventory and remove them from the world
        try {
            getInventory().deleteMe();
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        // Update database with items in its warehouse and remove them from the world
        try {
            clearWarehouse();
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        if (Config.WAREHOUSE_CACHE)
            WarehouseCacheManager.getInstance().remove(this);

        // Update database with items in its freight and remove them from the world
        try {
            clearFreight();
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

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

        // Remove all L2Object from _knownObjects and _knownPlayer of the L2Creature then cancel Attak or Cast and notify AI
        try {
            getKnownList().removeAllKnownObjects();
        } catch (Exception e) {
            _log.fatal(e.getMessage(), e);
        }

        untransform();

        if (getClanId() > 0)
            getClan().broadcastToOtherOnlineMembers(new PledgeShowMemberListUpdate(this), this);

        if (_snoopedPlayers.length > 0) {
            for (L2Player snooped : _snoopedPlayers)
                snooped.removeSnooper(this);
            _snoopedPlayers = L2Player.EMPTY_ARRAY;
        }

        if (_snoopers.length > 0) {
            broadcastSnoop(SystemChatChannelId.Chat_Normal, "", "*** Player " + getName() + " logged off ***");
            for (L2Player snooper : _snoopers)
                snooper.removeSnooped(this);
            _snoopers = L2Player.EMPTY_ARRAY;
        }

        for (Integer objId : getFriendList().getFriendIds()) {
            L2Player friend = L2World.getInstance().findPlayer(objId);
            if (friend != null) {
                friend.sendPacket(new FriendList(friend));
                friend.sendMessage("Friend: " + getName() + " has logged off.");
            }
        }

        MovementController.getInstance().remove(this);

        // Remove L2Object object from _allObjects of L2World, if still in it
        L2World.getInstance().removeObject(this);

        try {
            // To delete the player from L2World on crit during teleport ;)
            setIsTeleporting(false);

            L2World.getInstance().removeOnlinePlayer(this);
        } catch (RuntimeException e) {
            _log.fatal("deleteMe()", e);
        }

        RegionBBSManager.changeCommunityBoard(this, PlayerStateOnCommunity.NONE);

        //getClearableReference().clear();
        LeakTaskManager.getInstance().add(this);

        SQLQueue.getInstance().run();

        final HashSet<L2Zone> after = getZonesPlayerIn();

        if (!after.isEmpty()) {
            _log.warn("Leaking zones before L2Player.deleteMe(): " + before.toString());
            _log.warn("Leaking zones after L2Player.deleteMe(): " + after.toString());
        }
    }

    private HashSet<L2Zone> getZonesPlayerIn() {
        final HashSet<L2Zone> set = new HashSet<L2Zone>();

        for (L2Zone[] zones : ZoneManager.getInstance().getZones())
            if (zones != null)
                for (L2Zone zone : zones)
                    if (zone.getCharactersInside().contains(this))
                        set.add(zone);

        return set;
    }

    public boolean canLogout() {
        return canLogout(false);
    }

    public boolean canLogout(boolean restart) {
        if (!isGM() || !Config.GM_RESTART_FIGHTING) {
            if (AttackStanceTaskManager.getInstance().getAttackStanceTask(this)) {
                if (restart)
                    sendPacket(SystemMessageId.CANT_RESTART_WHILE_FIGHTING);
                else
                    sendPacket(SystemMessageId.CANT_LOGOUT_WHILE_FIGHTING);
                return false;
            }
        }

        if (isFlying()) {
            sendPacket(SystemMessageId.CANNOT_DO_WHILE_MOUNTED);
            return false;
        }

        L2Summon summon = getPet();

        if (summon != null && summon instanceof L2PetInstance && !summon.isBetrayed() && summon.isAttackingNow()) {
            sendPacket(SystemMessageId.PET_CANNOT_SENT_BACK_DURING_BATTLE);
            return false;
        }

        if (isInFunEvent()) {
            sendMessage("A superior power doesn't allow you to leave the event.");
            return false;
        }

        // Prevent player from restarting if they are a festival participant
        if (isFestivalParticipant() /* && SevenSignsFestival.getInstance().isFestivalInitialized()*/) {
            sendMessage("You can't logout while you are a participant in a festival.");
            return false;
        }

        if (getPrivateStoreType() != 0) {
            sendMessage("You can't logout while trading.");
            return false;
        }

        if (getActiveEnchantItem() != null || getActiveEnchantAttrItem() != null)
            return false;

        if (isLocked()) {
            sendMessage("You can't logout while changing class.");
            return false;
        }

        if (!isGM()) {
            if (isInsideZone(L2Zone.FLAG_NOESCAPE)) {
                sendPacket(SystemMessageId.NO_LOGOUT_HERE);
                teleToLocation(TeleportWhereType.Town);
            }
        }

        return true;
    }

    private FishData _fish;

    public void startFishing(int x, int y, int z) {
        _fishx = x;
        _fishy = y;
        _fishz = z;

        stopMove(null);
        setIsImmobilized(true);
        _fishing = true;
        broadcastUserInfo();
        // Start Fishing
        int lvl = getRandomFishLvl();
        int group = getRandomGroup();
        int type = getRandomFishType(group);
        List<FishData> fishs = FishTable.getInstance().getFish(lvl, type, group);
        if (fishs == null || fishs.size() == 0) {
            endFishing(false);
            return;
        }
        int check = Rnd.get(fishs.size());
        _fish = fishs.get(check);
        fishs.clear();
        fishs = null;
        sendPacket(SystemMessageId.CAST_LINE_AND_START_FISHING);
        ExFishingStart efs = null;
        efs = new ExFishingStart(this, _fish.getType(), x, y, z, _lure.isNightLure());
        broadcastPacket(efs);
        startLookingForFishTask();
    }

    public void stopLookingForFishTask() {
        if (_taskforfish != null) {
            _taskforfish.cancel(false);
            _taskforfish = null;
        }
    }

    public void startLookingForFishTask() {
        if (!isDead() && _taskforfish == null) {
            int checkDelay = 0;
            boolean isNoob = false;
            boolean isUpperGrade = false;

            if (_lure != null) {
                int lureid = _lure.getItemId();
                isNoob = _fish.getGroup() == 0;
                isUpperGrade = _fish.getGroup() == 2;
                if (lureid == 6519 || lureid == 6522 || lureid == 6525 || lureid == 8505 || lureid == 8508
                        || lureid == 8511) // Low Grade
                    checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (1.33)));
                else if (lureid == 6520 || lureid == 6523 || lureid == 6526 || (lureid >= 8505 && lureid <= 8513)
                        || (lureid >= 7610 && lureid <= 7613) || (lureid >= 7807 && lureid <= 7809)
                        || (lureid >= 8484 && lureid <= 8486)) // Medium Grade, Beginner, Prize-Winning & Quest Special Bait
                    checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (1.00)));
                else if (lureid == 6521 || lureid == 6524 || lureid == 6527 || lureid == 8507 || lureid == 8510
                        || lureid == 8513) // High grade
                    checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (0.66)));
            }
            _taskforfish = ThreadPoolManager.getInstance()
                    .scheduleEffectAtFixedRate(new LookingForFishTask(_fish.getWaitTime(), _fish.getFishGuts(),
                            _fish.getType(), isNoob, isUpperGrade), 10000, checkDelay);
        }
    }

    private int getRandomGroup() {
        switch (_lure.getItemId()) {
        case 7807: // Green for Beginners
        case 7808: // Purple for Beginners
        case 7809: // Yellow for Beginners
        case 8486: // Prize-Winning for Beginners
            return 0;
        case 8485: // Prize-Winning Luminous
        case 8506: // Green Luminous
        case 8509: // Purple Luminous
        case 8512: // Yellow Luminous
            return 2;
        default:
            return 1;
        }
    }

    private int getRandomFishType(int group) {
        int check = Rnd.get(100);
        int type = 1;
        switch (group) {
        case 0: // Fish for Novices
            switch (_lure.getItemId()) {
            case 7807: // Green Lure, preferred by fast-moving (nimble) fish (type 5)
                if (check <= 54)
                    type = 5;
                else if (check <= 77)
                    type = 4;
                else
                    type = 6;
                break;
            case 7808: // Purple Lure, preferred by fat fish (type 4)
                if (check <= 54)
                    type = 4;
                else if (check <= 77)
                    type = 6;
                else
                    type = 5;
                break;
            case 7809: // Yellow Lure, preferred by ugly fish (type 6)
                if (check <= 54)
                    type = 6;
                else if (check <= 77)
                    type = 5;
                else
                    type = 4;
                break;
            case 8486: // Prize-Winning Fishing Lure for Beginners
                if (check <= 33)
                    type = 4;
                else if (check <= 66)
                    type = 5;
                else
                    type = 6;
                break;
            }
            break;
        case 1: // Normal Fish
            switch (_lure.getItemId()) {
            case 7610:
            case 7611:
            case 7612:
            case 7613:
                type = 3;
                break;
            case 6519: // All theese lures (green) are prefered by fast-moving (nimble) fish (type 1)
            case 8505:
            case 6520:
            case 6521:
            case 8507:
                if (check <= 54)
                    type = 1;
                else if (check <= 74)
                    type = 0;
                else if (check <= 94)
                    type = 2;
                else
                    type = 3;
                break;
            case 6522: // All theese lures (purple) are prefered by fat fish (type 0)
            case 8508:
            case 6523:
            case 6524:
            case 8510:
                if (check <= 54)
                    type = 0;
                else if (check <= 74)
                    type = 1;
                else if (check <= 94)
                    type = 2;
                else
                    type = 3;
                break;
            case 6525: // All theese lures (yellow) are prefered by ugly fish (type 2)
            case 8511:
            case 6526:
            case 6527:
            case 8513:
                if (check <= 55)
                    type = 2;
                else if (check <= 74)
                    type = 1;
                else if (check <= 94)
                    type = 0;
                else
                    type = 3;
                break;
            case 8484: // Prize-Winning Fishing Lure
                if (check <= 33)
                    type = 0;
                else if (check <= 66)
                    type = 1;
                else
                    type = 2;
                break;
            }
            break;
        case 2: // Upper Grade Fish, Luminous Lure
            switch (_lure.getItemId()) {
            case 8506: // Green Lure, preferred by fast-moving (nimble) fish (type 8)
                if (check <= 54)
                    type = 8;
                else if (check <= 77)
                    type = 7;
                else
                    type = 9;
                break;
            case 8509: // Purple Lure, preferred by fat fish (type 7)
                if (check <= 54)
                    type = 7;
                else if (check <= 77)
                    type = 9;
                else
                    type = 8;
                break;
            case 8512: // Yellow Lure, preferred by ugly fish (type 9)
                if (check <= 54)
                    type = 9;
                else if (check <= 77)
                    type = 8;
                else
                    type = 7;
                break;
            case 8485: // Prize-Winning Fishing Lure
                if (check <= 33)
                    type = 7;
                else if (check <= 66)
                    type = 8;
                else
                    type = 9;
                break;
            }
        }
        return type;
    }

    private int getRandomFishLvl() {
        L2Effect[] effects = getAllEffects();
        int skilllvl = getSkillLevel(1315);
        for (L2Effect e : effects) {
            if (e.getSkill().getId() == 2274)
                skilllvl = (int) e.getSkill().getPower();
        }
        if (skilllvl <= 0)
            return 1;
        int randomlvl;
        int check = Rnd.get(100);

        if (check <= 50) {
            randomlvl = skilllvl;
        } else if (check <= 85) {
            randomlvl = skilllvl - 1;
            if (randomlvl <= 0) {
                randomlvl = 1;
            }
        } else {
            randomlvl = skilllvl + 1;
            if (randomlvl > 27)
                randomlvl = 27;
        }

        return randomlvl;
    }

    public void startFishCombat(boolean isNoob, boolean isUpperGrade) {
        _fishCombat = new L2Fishing(this, _fish, isNoob, isUpperGrade);
    }

    public void endFishing(boolean win) {
        ExFishingEnd efe = new ExFishingEnd(win, this);
        broadcastPacket(efe);
        _fishing = false;
        _fishx = 0;
        _fishy = 0;
        _fishz = 0;
        broadcastUserInfo();
        if (_fishCombat == null)
            sendPacket(SystemMessageId.BAIT_LOST_FISH_GOT_AWAY);
        _fishCombat = null;

        _lure = null;
        // End Fishing
        sendPacket(SystemMessageId.REEL_LINE_AND_STOP_FISHING);
        setIsImmobilized(false);
        stopLookingForFishTask();
    }

    public L2Fishing getFishCombat() {
        return _fishCombat;
    }

    public int getFishx() {
        return _fishx;
    }

    public int getFishy() {
        return _fishy;
    }

    public int getFishz() {
        return _fishz;
    }

    public void setLure(L2ItemInstance lure) {
        _lure = lure;
    }

    public L2ItemInstance getLure() {
        return _lure;
    }

    public int getInventoryLimit() {
        int ivlim;
        if (isGM()) {
            ivlim = Config.INVENTORY_MAXIMUM_GM;
        } else if (getRace() == Race.Dwarf) {
            ivlim = Config.INVENTORY_MAXIMUM_DWARF;
        } else {
            ivlim = Config.INVENTORY_MAXIMUM_NO_DWARF;
        }
        ivlim += (int) getStat().calcStat(Stats.INV_LIM, 0, null, null);

        return ivlim;
    }

    public int getWareHouseLimit() {
        int whlim;
        if (getRace() == Race.Dwarf) {
            whlim = Config.WAREHOUSE_SLOTS_DWARF;
        } else {
            whlim = Config.WAREHOUSE_SLOTS_NO_DWARF;
        }
        whlim += (int) getStat().calcStat(Stats.WH_LIM, 0, null, null);

        return whlim;
    }

    public int getPrivateSellStoreLimit() {
        int pslim;
        if (getRace() == Race.Dwarf) {
            pslim = Config.MAX_PVTSTORESELL_SLOTS_DWARF;
        } else {
            pslim = Config.MAX_PVTSTORESELL_SLOTS_OTHER;
        }
        pslim += (int) getStat().calcStat(Stats.P_SELL_LIM, 0, null, null);

        return pslim;
    }

    public int getPrivateBuyStoreLimit() {
        int pblim;
        if (getRace() == Race.Dwarf) {
            pblim = Config.MAX_PVTSTOREBUY_SLOTS_DWARF;
        } else {
            pblim = Config.MAX_PVTSTOREBUY_SLOTS_OTHER;
        }
        pblim += (int) getStat().calcStat(Stats.P_BUY_LIM, 0, null, null);

        return pblim;
    }

    public int getFreightLimit() {
        return Config.FREIGHT_SLOTS + (int) getStat().calcStat(Stats.FREIGHT_LIM, 0, null, null);
    }

    public int getDwarfRecipeLimit() {
        int recdlim = Config.ALT_DWARF_RECIPE_LIMIT;
        recdlim += (int) getStat().calcStat(Stats.REC_D_LIM, 0, null, null);
        return recdlim;
    }

    public int getCommonRecipeLimit() {
        int recclim = Config.ALT_COMMON_RECIPE_LIMIT;
        recclim += (int) getStat().calcStat(Stats.REC_C_LIM, 0, null, null);
        return recclim;
    }

    /**
     * @return Returns the mountNpcId.
     */
    public int getMountNpcId() {
        return _mountNpcId;
    }

    /**
     * @return Returns the mountLevel.
     */
    public int getMountLevel() {
        return _mountLevel;
    }

    public void setMountObjectID(int newID) {
        _mountObjectID = newID;
    }

    public int getMountObjectID() {
        return _mountObjectID;
    }

    private L2ItemInstance _lure = null;

    public SkillUsageRequest getCurrentPetSkill() {
        final L2Summon pet = getPet();

        return pet == null ? null : pet.getCurrentSkill();
    }

    public boolean isMaried() {
        return _maried;
    }

    public void setMaried(boolean state) {
        _maried = state;
    }

    public void setMaryRequest(boolean state) {
        _maryrequest = state;
    }

    public boolean isMary() {
        return _maryrequest;
    }

    public void setMaryAccepted(boolean state) {
        _maryaccepted = state;
    }

    public boolean isMaryAccepted() {
        return _maryaccepted;
    }

    public int getPartnerId() {
        return _partnerId;
    }

    public void setPartnerId(int partnerid) {
        _partnerId = partnerid;
    }

    public int getCoupleId() {
        return _coupleId;
    }

    public void setCoupleId(int coupleId) {
        _coupleId = coupleId;
    }

    public void setClientRevision(int clientrev) {
        _clientRevision = clientrev;
    }

    public int getClientRevision() {
        return _clientRevision;
    }

    public boolean isInJail() {
        return _inJail;
    }

    public void setInJail(boolean state) {
        //setInJail(state, 0);
        _inJail = state;
    }

    public void setInJail(boolean state, int delayInMinutes) {
        _inJail = state;
        // Remove the task if any
        stopJailTask(false);

        if (_inJail) {
            if (delayInMinutes > 0) {
                _jailTimer = delayInMinutes * 60000L; // In millisec

                // Start the countdown
                _jailTask = ThreadPoolManager.getInstance().scheduleGeneral(new JailTask(), _jailTimer);
                sendMessage("You are in jail for " + delayInMinutes + " minutes.");
            }

            // Open a Html message to inform the player
            NpcHtmlMessage htmlMsg = new NpcHtmlMessage(0);
            String jailInfos = HtmCache.getInstance().getHtm("data/html/jail_in.htm");
            if (jailInfos != null)
                htmlMsg.setHtml(jailInfos);
            else
                htmlMsg.setHtml("<html><body>You have been put in jail by an admin.</body></html>");
            sendPacket(htmlMsg);

            if (isFlyingMounted())
                untransform();
            setInstanceId(0);
            setIsIn7sDungeon(false);

            teleToLocation(L2JailZone.JAIL_LOCATION, false); // Jail
        } else {
            // Open a Html message to inform the player
            NpcHtmlMessage htmlMsg = new NpcHtmlMessage(0);
            String jailInfos = HtmCache.getInstance().getHtm("data/html/jail_out.htm");
            if (jailInfos != null)
                htmlMsg.setHtml(jailInfos);
            else
                htmlMsg.setHtml("<html><body>You are free for now, respect server rules!</body></html>");
            sendPacket(htmlMsg);

            teleToLocation(17836, 170178, -3507); // Floran
        }

        // Store in database
        storeCharBase();

        RegionBBSManager.changeCommunityBoard(this, PlayerStateOnCommunity.IN_JAIL);
    }

    public long getJailTimer() {
        if (_jailTask != null)
            return _jailTask.getDelay(TimeUnit.MILLISECONDS);

        return _jailTimer;
    }

    public void setJailTimer(long time) {
        _jailTimer = time;
    }

    private void updateJailState() {
        if (isInJail()) {
            // If jail time is elapsed, free the player
            if (_jailTimer > 0) {
                // Restart the countdown
                _jailTask = ThreadPoolManager.getInstance().scheduleGeneral(new JailTask(), _jailTimer);
                sendMessage("You are still in jail for " + Math.round(_jailTimer / 60000) + " minutes.");
            }

            // If player escaped, put him back in jail
            if (!isInsideZone(L2Zone.FLAG_JAIL))
                teleToLocation(L2JailZone.JAIL_LOCATION, false);
        }
    }

    public void stopJailTask(boolean save) {
        if (_jailTask != null) {
            if (save) {
                long delay = _jailTask.getDelay(TimeUnit.MILLISECONDS);
                if (delay < 0)
                    delay = 0;
                setJailTimer(delay);
            }
            _jailTask.cancel(false);
            _jailTask = null;
        }
    }

    /**
     * Return True if the L2Player is a ViP.<BR><BR>
     */
    public boolean isCharViP() {
        return _charViP;
    }

    /**
     * Set the _charViP Flag of the L2Player.<BR><BR>
     */
    public void setCharViP(boolean status) {
        _charViP = status;
    }

    private ScheduledFuture<?> _jailTask;
    private int _cursedWeaponEquippedId = 0;
    private boolean _combatFlagEquipped = false;

    private boolean _reviveRequested = false;
    private boolean _revivePetRequested = false;

    private double _cpUpdateIncCheck = .0;
    private double _cpUpdateDecCheck = .0;
    private double _cpUpdateInterval = .0;
    private double _mpUpdateIncCheck = .0;
    private double _mpUpdateDecCheck = .0;
    private double _mpUpdateInterval = .0;

    private class JailTask implements Runnable {
        @Override
        public void run() {
            setInJail(false, 0);
        }
    }

    public void restoreHPMP() {
        getStatus().setCurrentHpMp(getMaxHp(), getMaxMp());
    }

    public boolean isCursedWeaponEquipped() {
        return _cursedWeaponEquippedId != 0;
    }

    public void setCursedWeaponEquippedId(int value) {
        _cursedWeaponEquippedId = value;

        RegionBBSManager.changeCommunityBoard(this, PlayerStateOnCommunity.CURSED_WEAPON_OWNER);
    }

    public int getCursedWeaponEquippedId() {
        return _cursedWeaponEquippedId;
    }

    public boolean isCombatFlagEquipped() {
        return _combatFlagEquipped;
    }

    public void setCombatFlagEquipped(boolean value) {
        _combatFlagEquipped = value;
    }

    public void setNPCFaction(FactionMember fm) {
        _faction = fm;
    }

    public FactionMember getNPCFaction() {
        return _faction;
    }

    public boolean removeNPCFactionPoints(int factionPoints) {
        if (_faction != null) {
            if (_faction.getFactionPoints() < factionPoints)
                return false;
            _faction.reduceFactionPoints(factionPoints);
            return true;
        }
        return false;
    }

    public int getNPCFactionPoints() {
        return _faction.getFactionPoints();
    }

    public int getSide() {
        return _faction.getSide();
    }

    public void quitNPCFaction() {
        if (_faction != null) {
            _faction.quitFaction();
            _faction = null;
        }
    }

    public boolean getCharmOfCourage() {
        return _charmOfCourage;
    }

    public void setCharmOfCourage(boolean val) {
        _charmOfCourage = val;
        sendEtcStatusUpdate();
    }

    /** Return True if the L2Creature is riding. */
    public final boolean isRidingStrider() {
        return _isRidingStrider;
    }

    public final boolean isRidingRedStrider() {
        return _isRidingRedStrider;
    }

    public final boolean isRidingHorse() {
        return _isRidingHorse;
    }

    /** Set the L2Creature riding mode to True. */
    public final void setIsRidingStrider(boolean mode) {
        _isRidingStrider = mode;
    }

    public final void setIsRidingRedStrider(boolean mode) {
        _isRidingRedStrider = mode;
    }

    public final void setIsRidingHorse(boolean mode) {
        _isRidingHorse = mode;
    }

    public int getCharges() {
        return _charges;
    }

    private static final int[] CHARGE_SKILLS = { 570, 8, 50 }; // Transformation skill is checked first

    public L2Skill getChargeSkill() {
        for (int id : L2Player.CHARGE_SKILLS) {
            L2Skill skill = getKnownSkill(id);
            if (skill != null && skill.getMaxCharges() > 0) {
                return skill;
            }
        }
        return null;
    }

    public void increaseCharges(int count, int max) {
        if (count <= 0) // Wrong usage
            return;

        // Checking charges maximum
        if (_charges >= max) {
            sendPacket(SystemMessageId.FORCE_MAXLEVEL_REACHED);
            return;
        }

        // Increase charges
        setCharges(Math.min(_charges + count, max));

        SystemMessage sm = new SystemMessage(SystemMessageId.FORCE_INCREASED_TO_S1);
        sm.addNumber(_charges);
        sendPacket(sm);
    }

    public void increaseChargesBySkill(L2Skill skill) {
        if (skill.getGiveCharges() > 0)
            increaseCharges(skill.getGiveCharges(), skill.getMaxCharges());
    }

    private void setCharges(int charges) {
        _charges = Math.max(0, charges);

        sendEtcStatusUpdate();
        if (_charges == 0)
            stopChargeTask();
        else
            restartChargeTask();
    }

    public void decreaseCharges(int count) {
        if (count < 0) // Wrong usage
            return;
        setCharges(_charges - count);
    }

    public class ChargeTask implements Runnable {
        @Override
        public void run() {
            clearCharges();
        }
    }

    /**
     * Clear out all charges from this L2Player
     */
    public void clearCharges() {
        setCharges(0);
    }

    /**
     * Starts/Restarts the SoulTask to Clear Charges after 10 Mins.
     */
    private void restartChargeTask() {
        if (_chargeTask != null) {
            _chargeTask.cancel(false);
            _chargeTask = null;
        }
        _chargeTask = ThreadPoolManager.getInstance().scheduleGeneral(new ChargeTask(), 600000);
    }

    /**
     * Stops the Clearing Task.
     */
    public void stopChargeTask() {
        if (_chargeTask != null) {
            _chargeTask.cancel(false);
            _chargeTask = null;
        }
    }

    /**
     * Returns the Number of Souls this L2Player got.
     *
     * @return
     */
    public int getSouls() {
        return _souls;
    }

    public int getLastSoulConsume() {
        return _lastSoulConsume;
    }

    public void resetLastSoulConsume() {
        _lastSoulConsume = 0;
    }

    /**
     * Absorbs a Soul from a Npc.
     *
     * @param skill
     * @param target
     */
    public void absorbSoulFromNpc(L2Skill soulMastery, L2Creature target) {
        if (_souls >= soulMastery.getNumSouls()) {
            sendPacket(SystemMessageId.SOUL_CANNOT_BE_INCREASED_ANYMORE);
            return;
        }

        increaseSouls(1);

        // Npc -> Player absorb animation
        if (target != null)
            broadcastPacket(new ExSpawnEmitter(getObjectId(), target.getObjectId()), 500);
    }

    /**
     * Increase Souls
     *
     * @param count
     */
    private void increaseSouls(int count) // By skill or mob kill
    {
        setSouls(_souls + count);

        SystemMessage sm = new SystemMessage(SystemMessageId.YOUR_SOUL_HAS_INCREASED_BY_S1_SO_IT_IS_NOW_AT_S2);
        sm.addNumber(count);
        sm.addNumber(_souls);
        sendPacket(sm);
    }

    public void increaseSoulsBySkill(L2Skill skill) {
        if (skill.getNumSouls() == 0)
            return;

        final L2Skill soulmastery = getKnownSkill(L2Skill.SKILL_SOUL_MASTERY);
        if (soulmastery == null)
            return;

        if (_souls >= soulmastery.getNumSouls()) {
            sendPacket(SystemMessageId.SOUL_CANNOT_BE_INCREASED_ANYMORE);
            return;
        }

        increaseSouls(Math.min(skill.getNumSouls(), soulmastery.getNumSouls() - getSouls()));
    }

    /**
     * Decreases existing Souls.
     *
     * @param skill
     */
    public void decreaseSouls(L2Skill skill) {
        if (_souls == 0)
            return;

        // Calculation part
        int souls = _souls;
        if (skill.getSoulConsumeCount() > 0) {
            souls -= skill.getSoulConsumeCount();
        } else if (skill.getMaxSoulConsumeCount() > 0) {
            int consume = Math.min(_souls, skill.getMaxSoulConsumeCount());
            souls -= consume;
            _lastSoulConsume = consume; // Store for PDAM/MDAM boosting
        }

        setSouls(souls);
    }

    public void setSouls(int count) {
        _souls = L2Math.limit(0, count, 45); // Client can't display more

        if (_souls > 0)
            restartSoulTask();
        else
            stopSoulTask();

        sendEtcStatusUpdate();
    }

    private class SoulTask implements Runnable {
        @Override
        public void run() {
            clearSouls();
        }
    }

    /**
     * Clear out all Souls from this L2Player
     */
    public void clearSouls() {
        setSouls(0);
    }

    /**
     * Starts/Restarts the SoulTask to Clear Souls after 10 Mins.
     */
    private void restartSoulTask() {
        if (_soulTask != null) {
            _soulTask.cancel(false);
            _soulTask = null;
        }
        _soulTask = ThreadPoolManager.getInstance().scheduleGeneral(new SoulTask(), 600000);
    }

    /**
     * Stops the Clearing Task.
     */
    public void stopSoulTask() {
        if (_soulTask != null) {
            _soulTask.cancel(false);
            _soulTask = null;
        }
    }

    public void startFameTask(long delay, int fameFixRate) {
        if (getLevel() < 40 || getClassId().level() < 2)
            return;
        if (_fameTask == null)
            _fameTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FameTask(fameFixRate), delay,
                    delay);
    }

    public void stopFameTask() {
        if (_fameTask != null) {
            _fameTask.cancel(false);
            _fameTask = null;
        }
    }

    public class FameTask implements Runnable {
        protected int _value;

        protected FameTask(int value) {
            _value = value;
        }

        @Override
        public void run() {
            if (isDead() && !Config.ALT_FAME_FOR_DEAD_PLAYERS)
                return;

            setFame(getFame() + _value);
            SystemMessage sm = new SystemMessage(SystemMessageId.ACQUIRED_S1_REPUTATION_SCORE);
            sm.addNumber(_value);
            sendPacket(sm);
            sendPacket(new UserInfo(L2Player.this));
        }
    }

    private L2Effect _shortBuff;

    public void startShortBuffStatusUpdate(L2Effect effect) {
        if (ShortBuffStatusUpdate.getPriority(_shortBuff) > ShortBuffStatusUpdate.getPriority(effect))
            return;

        _shortBuff = effect;

        sendPacket(
                new ShortBuffStatusUpdate(effect.getId(), effect.getLevel(), (int) effect.getRemainingTaskTime()));
    }

    public void stopShortBuffStatusUpdate(L2Effect effect) {
        if (_shortBuff != effect)
            return;

        _shortBuff = null;

        sendPacket(new ShortBuffStatusUpdate(0, 0, 0));
    }

    public int getDeathPenaltyBuffLevel() {
        return _deathPenaltyBuffLevel;
    }

    public void setDeathPenaltyBuffLevel(int level) {
        _deathPenaltyBuffLevel = level;
    }

    public void calculateDeathPenaltyBuffLevel(L2Creature killer) {
        if (Config.DEATH_PENALTY_CHANCE < 1)
            return;

        if (!(killer instanceof L2Playable) && !isGM() && !(getCharmOfLuck() && killer.isRaid())
                && !isPhoenixBlessed() && !isInFunEvent()
                && (getKarma() > 0 || Rnd.get(100) < Config.DEATH_PENALTY_CHANCE))
            increaseDeathPenaltyBuffLevel();
    }

    public void increaseDeathPenaltyBuffLevel() {
        if (getDeathPenaltyBuffLevel() >= 15) // Maximum level reached
            return;

        if (getDeathPenaltyBuffLevel() != 0) {
            L2Skill skill = SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel());

            if (skill != null)
                removeSkill(skill, true);
        }

        _deathPenaltyBuffLevel++;

        addSkill(SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()), false);
        sendEtcStatusUpdate();
        SystemMessage sm = new SystemMessage(SystemMessageId.DEATH_PENALTY_LEVEL_S1_ADDED);
        sm.addNumber(getDeathPenaltyBuffLevel());
        sendPacket(sm);
    }

    public void reduceDeathPenaltyBuffLevel() {
        if (getDeathPenaltyBuffLevel() <= 0)
            return;

        L2Skill skill = SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel());

        if (skill != null)
            removeSkill(skill, true);

        _deathPenaltyBuffLevel--;

        if (getDeathPenaltyBuffLevel() > 0) {
            addSkill(SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()), false);
            SystemMessage sm = new SystemMessage(SystemMessageId.DEATH_PENALTY_LEVEL_S1_ADDED);
            sm.addNumber(getDeathPenaltyBuffLevel());
            sendPacket(sm);
        } else {
            sendPacket(SystemMessageId.DEATH_PENALTY_LIFTED);
        }

        sendEtcStatusUpdate();
    }

    public void restoreDeathPenaltyBuffLevel() {
        L2Skill skill = SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel());

        if (skill != null)
            removeSkill(skill, true);

        if (getDeathPenaltyBuffLevel() > 0) {
            addSkill(SkillTable.getInstance().getInfo(5076, getDeathPenaltyBuffLevel()), false);
        }
    }

    private boolean _canFeed;

    private final Map<Integer, TimeStamp> _reuseTimeStamps = new FastMap<Integer, TimeStamp>().setShared(true);

    public Map<Integer, TimeStamp> getReuseTimeStamps() {
        return _reuseTimeStamps;
    }

    public void sendSkillCoolTime() {
        addPacketBroadcastMask(BroadcastMode.SEND_SKILL_COOL_TIME);
    }

    public void sendSkillCoolTimeImpl() {
        sendPacket(SkillCoolTime.STATIC_PACKET);
    }

    /**
     * Simple class containing all neccessary information to maintain
     * valid timestamps and reuse for skills upon relog. Filter this
     * carefully as it becomes redundant to store reuse for small delays.
     * @author  Yesod
     */
    public static final class TimeStamp {
        private final int _skillId;
        private final int _reuseDelay;
        private final long _expiration;

        public TimeStamp(int skillId, int reuseDelay, int remaining) {
            _skillId = skillId;
            _reuseDelay = reuseDelay;
            _expiration = System.currentTimeMillis() + remaining;
        }

        public long getExpiration() {
            return _expiration;
        }

        public int getSkillId() {
            return _skillId;
        }

        public int getReuseDelay() {
            return _reuseDelay;
        }

        public int getRemaining() {
            return L2Math.limit(0, _expiration - System.currentTimeMillis(), Integer.MAX_VALUE);
        }
    }

    public void disableSkill(TimeStamp ts) {
        disableSkill(ts.getSkillId(), ts.getReuseDelay(), ts.getRemaining());
    }

    public boolean disableSkill(int skillId, int delay, int remaining) {
        if (!super.disableSkill(skillId, remaining))
            return false;

        //if (remaining < 10000)
        //   return true;

        final TimeStamp ts = getReuseTimeStamps().put(skillId, new TimeStamp(skillId, delay, remaining));
        final SkillUsageRequest request = getCurrentSkill();

        if (ts == null || Math.abs(ts.getReuseDelay() - delay) > 500
                || Math.abs(ts.getRemaining() - remaining) > 500)
            if (request == null || request.getSkillId() != skillId)
                sendSkillCoolTime();
        return true;
    }

    @Override
    public boolean disableSkill(int skillId, int delay) {
        return disableSkill(skillId, delay, delay);
    }

    @Override
    public void enableSkill(int skillId) {
        super.enableSkill(skillId);

        final TimeStamp ts = getReuseTimeStamps().remove(skillId);

        if (ts != null && ts.getRemaining() > 500)
            sendSkillCoolTime();
    }

    public boolean isKamaelic() {
        return getRace() == Race.Kamael;
    }

    public boolean canOpenPrivateStore() {
        return !isAlikeDead() && !isInOlympiadMode() && !isMounted();
    }

    public void tryOpenPrivateBuyStore() {
        // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death)
        if (canOpenPrivateStore()) {
            if (getPrivateStoreType() == L2Player.STORE_PRIVATE_BUY
                    || getPrivateStoreType() == L2Player.STORE_PRIVATE_BUY + 1) {
                setPrivateStoreType(L2Player.STORE_PRIVATE_NONE);
            } else if (isInsideZone(L2Zone.FLAG_NOSTORE)) {
                sendPacket(SystemMessageId.NO_PRIVATE_STORE_HERE);
                sendPacket(ActionFailed.STATIC_PACKET);
                return;
            }
            if (getPrivateStoreType() == L2Player.STORE_PRIVATE_NONE) {
                if (isSitting()) {
                    this.standUp();
                }
                setPrivateStoreType(L2Player.STORE_PRIVATE_BUY + 1);
                this.sendPacket(new PrivateStoreManageListBuy(this));
            }
        } else {
            sendPacket(ActionFailed.STATIC_PACKET);
        }
    }

    public void tryOpenPrivateSellStore(boolean isPackageSale) {
        // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death)
        if (canOpenPrivateStore()) {
            if (getPrivateStoreType() == L2Player.STORE_PRIVATE_SELL
                    || getPrivateStoreType() == L2Player.STORE_PRIVATE_SELL + 1
                    || getPrivateStoreType() == L2Player.STORE_PRIVATE_PACKAGE_SELL) {
                setPrivateStoreType(L2Player.STORE_PRIVATE_NONE);
            } else if (isInsideZone(L2Zone.FLAG_NOSTORE)) {
                sendPacket(SystemMessageId.NO_PRIVATE_STORE_HERE);
                sendPacket(ActionFailed.STATIC_PACKET);
                return;
            }

            if (getPrivateStoreType() == L2Player.STORE_PRIVATE_NONE) {
                if (isSitting()) {
                    this.standUp();
                }
                setPrivateStoreType(L2Player.STORE_PRIVATE_SELL + 1);
                this.sendPacket(new PrivateStoreManageListSell(this, isPackageSale));
            }
        } else {
            sendPacket(ActionFailed.STATIC_PACKET);
        }
    }

    public boolean isTransformed() {
        return _transformation != null && !_transformation.isStance();
    }

    public boolean isInStance() {
        return _transformation != null && _transformation.isStance();
    }

    public void transform(L2Transformation transformation) {
        if (_transformation != null) {
            // You already polymorphed and cannot polymorph again.
            sendPacket(SystemMessageId.YOU_ALREADY_POLYMORPHED_AND_CANNOT_POLYMORPH_AGAIN);
            return;
        }
        if (isMounted()) {
            // Get off the strider or something else if character is mounted
            dismount();
        }
        if (getPet() != null) {
            // Unsummon pets
            getPet().unSummon(this);
        }
        _transformation = transformation;
        for (L2Effect e : getAllEffects()) {
            if (e != null && e.getSkill().isToggle())
                e.exit();
        }
        transformation.onTransform(this);
        sendSkillList();
        sendSkillCoolTime();
        ExBasicActionList.sendTo(this);
        broadcastUserInfo();
    }

    public void untransform() {
        if (_transformation != null) {
            _transformAllowedSkills.clear();
            _transformation.onUntransform(this);
            _transformation = null;
            stopEffects(L2EffectType.TRANSFORMATION);
            broadcastUserInfo();
            ExBasicActionList.sendTo(this);
            sendSkillList();
            sendSkillCoolTime();
        }
    }

    public L2Transformation getTransformation() {
        return _transformation;
    }

    /**
     * This returns the transformation Id of the current transformation.
     * For example, if a player is transformed as a Buffalo, and then picks up the Zariche,
     * the transform Id returned will be that of the Zariche, and NOT the Buffalo.
     * @return Transformation Id
     */
    public int getTransformationId() {
        L2Transformation transformation = getTransformation();
        if (transformation == null)
            return 0;
        return transformation.getId();
    }

    /**
     * This returns the transformation Id stored inside the character table, selected by the method: transformSelectInfo()
     * For example, if a player is transformed as a Buffalo, and then picks up the Zariche,
     * the transform Id returned will be that of the Buffalo, and NOT the Zariche.
     * @return Transformation Id
     */
    public int transformId() {
        return _transformationId;
    }

    /**
     * This is a simple query that inserts the transform Id into the character table for future reference.
     */
    public void transformInsertInfo() {

        if (getTransformationId() == L2Transformation.TRANSFORM_AKAMANAH
                || getTransformationId() == L2Transformation.TRANSFORM_ZARICHE)
            return;

        _transformationId = getTransformationId();

        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_TRANSFORM);
            statement.setInt(1, _transformationId);
            statement.setInt(2, getObjectId());

            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.fatal("Transformation insert info: " + e, e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    /**
     * This selects the current
     * @return transformation Id
     */
    public int transformSelectInfo() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(SELECT_CHAR_TRANSFORM);
            statement.setInt(1, getObjectId());

            ResultSet rset = statement.executeQuery();
            if (rset.next())
                _transformationId = rset.getInt("transform_id");

            rset.close();
            statement.close();
        } catch (Exception e) {
            _log.fatal("Transformation select info error:" + e.getMessage(), e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        return _transformationId;
    }

    /**
     *
     * @param npcId
     */
    public void setAgathionId(int npcId) {
        _agathionId = npcId;
    }

    /**
     *
     * @return
     */
    public int getAgathionId() {
        return _agathionId;
    }

    private void startVitalityTask() {
        if (Config.ENABLE_VITALITY && _vitalityTask == null) {
            _vitalityTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new VitalityTask(this), 1000,
                    60000);
        }
    }

    private void stopVitalityTask() {
        if (_vitalityTask != null) {
            _vitalityTask.cancel(true);
            _vitalityTask = null;
        }
    }

    public int getVitalityPoints() {
        return getStat().getVitalityPoints();
    }

    public void setVitalityPoints(int points, boolean quiet) {
        getStat().setVitalityPoints(points, quiet);
    }

    public synchronized void updateVitalityPoints(float points, boolean useRates, boolean quiet) {
        getStat().updateVitalityPoints(points, useRates, quiet);
    }

    public L2StaticObjectInstance getObjectSittingOn() {
        return _throne;
    }

    public void setObjectSittingOn(L2StaticObjectInstance throne) {
        //prevent misuse
        if (throne == null)
            resetThrone();
        else
            _throne = throne;
    }

    public void resetThrone() {
        if (_throne == null)
            return;
        _throne.setOccupier(null);
        _throne = null;
    }

    public int getOlympiadOpponentId() {
        return _olympiadOpponentId;
    }

    public void setOlympiadOpponentId(int value) {
        _olympiadOpponentId = value;
    }

    private ImmutableReference<L2Player> _immutableReference;
    private ClearableReference<L2Player> _clearableReference;

    public ImmutableReference<L2Player> getImmutableReference() {
        if (_immutableReference == null)
            _immutableReference = new ImmutableReference<L2Player>(this);

        return _immutableReference;
    }

    public ClearableReference<L2Player> getClearableReference() {
        if (_clearableReference == null)
            _clearableReference = new ClearableReference<L2Player>(this);

        return _clearableReference;
    }

    @Override
    public final L2Player getActingPlayer() {
        return this;
    }

    @Override
    public final L2Summon getActingSummon() {
        return getPet();
    }

    /**
     * Set the Fame of this L2PcInstane <BR><BR>
     * @param fame
     */
    public void setFame(int fame) {
        if (fame > Config.MAX_PERSONAL_FAME_POINTS)
            _fame = Config.MAX_PERSONAL_FAME_POINTS;
        else
            _fame = fame;
    }

    /**
     * Return the Fame of this L2Player <BR><BR>
     * @return
     */
    public int getFame() {
        return _fame;
    }

    private ScheduledFuture<?> _autoSaveTask;

    private void startAutoSaveTask() {
        if (_autoSaveTask == null && Config.CHAR_STORE_INTERVAL > 0)
            _autoSaveTask = ThreadPoolManager.getInstance().schedule(new AutoSave(), 300000);
    }

    private void stopAutoSaveTask() {
        if (_autoSaveTask != null) {
            _autoSaveTask.cancel(false);
            _autoSaveTask = null;
        }
    }

    private final class AutoSave implements Runnable {
        @Override
        public void run() {
            long period = Config.CHAR_STORE_INTERVAL * 60000L;
            long delay = _lastStore + period - System.currentTimeMillis();

            if (delay <= 0) {
                try {
                    store();
                } catch (RuntimeException e) {
                    _log.fatal("", e);
                }

                delay = period;
            }

            if (Config.CHAR_STORE_INTERVAL > 0)
                _autoSaveTask = ThreadPoolManager.getInstance().schedule(this, delay);
        }
    }

    public void checkItemRestriction() {
        for (int i = 0; i < Inventory.PAPERDOLL_TOTALSLOTS; i++) {
            L2ItemInstance equippedItem = getInventory().getPaperdollItem(i);
            if (equippedItem != null && !equippedItem.getItem().checkCondition(this, false)) {
                getInventory().unEquipItemInSlotAndRecord(i);
                if (equippedItem.isWear())
                    continue;

                SystemMessage sm = null;
                if (equippedItem.getItem().getBodyPart() == L2Item.SLOT_BACK)
                    sm = SystemMessageId.CLOAK_REMOVED_BECAUSE_ARMOR_SET_REMOVED.getSystemMessage();
                else if (equippedItem.getEnchantLevel() > 0) {
                    sm = new SystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
                    sm.addNumber(equippedItem.getEnchantLevel());
                    sm.addItemName(equippedItem);
                } else {
                    sm = new SystemMessage(SystemMessageId.S1_DISARMED);
                    sm.addItemName(equippedItem);
                }
                sendPacket(sm);
            }
        }
    }

    @Override
    public void setName(String name) {
        super.setName(name);

        CharNameTable.getInstance().update(getObjectId(), getAccountName(), getName());
    }

    // =========================================================================================
    // Condition listeners
    // TODO: wrapper conditions - ConditionLogicAnd, ConditionLogicOr, etc
    // TODO: make it more similar to conventional java listeners
    // TODO: add every listener which makes sense - currently listens for hp percent and game time only

    public enum ConditionListenerDependency {
        PLAYER_HP, GAME_TIME,;
    }

    private abstract class ConditionListener {
        private final Map<Func, Boolean> _values = new LazyFastMap<Func, Boolean>().setShared();
        private final Env _env;

        protected ConditionListener() {
            _env = new Env();
            _env.player = L2Player.this;
        }

        protected void refresh(ConditionListenerDependency dependency) {
            for (Entry<Func, Boolean> entry : _values.entrySet()) {
                boolean newValue = entry.getKey().isAllowed(_env);
                boolean oldValue = entry.setValue(newValue);

                if (newValue != oldValue)
                    onChange(entry.getKey(), newValue);
            }
        }

        protected void onChange(Func f, boolean newValue) {
            sendMessage(f.funcOwner.getFuncOwnerName() + (newValue ? " activated." : " deactivated."));
        }

        protected void onFuncAddition(Func f) {
            final boolean newValue = f.isAllowed(_env);

            _values.put(f, newValue);

            if (newValue)
                onChange(f, true);
        }

        protected void onFuncRemoval(Func f) {
            _values.remove(f);
        }
    }

    private final class ConditionPlayerHpListener extends ConditionListener {
        @Override
        protected void onFuncAddition(Func f) {
            if (f.condition instanceof ConditionPlayerHp)
                super.onFuncAddition(f);
        }

        @Override
        protected void refresh(ConditionListenerDependency dependency) {
            if (dependency == ConditionListenerDependency.PLAYER_HP)
                super.refresh(dependency);
        }

        @Override
        protected void onChange(Func f, boolean newValue) {
            final SystemMessage sm;

            if (newValue)
                sm = new SystemMessage(SystemMessageId.S1_HP_DECREASED_EFFECT_APPLIES);
            else
                sm = new SystemMessage(SystemMessageId.S1_HP_DECREASED_EFFECT_DISAPPEARS);

            if (f.funcOwner.getFuncOwnerSkill() != null)
                sm.addSkillName(f.funcOwner.getFuncOwnerSkill());
            else
                sm.addString(f.funcOwner.getFuncOwnerName());

            sendPacket(sm);

            broadcastUserInfo();
        }
    }

    private final class ConditionGameTimeListener extends ConditionListener {
        @Override
        protected void onFuncAddition(Func f) {
            if (f.condition instanceof ConditionGameTime)
                super.onFuncAddition(f);
        }

        @Override
        protected void refresh(ConditionListenerDependency dependency) {
            if (dependency == ConditionListenerDependency.GAME_TIME)
                super.refresh(dependency);
        }

        @Override
        protected void onChange(Func f, boolean newValue) {
            final SystemMessage sm;

            if (newValue)
                sm = new SystemMessage(SystemMessageId.S1_NIGHT_EFFECT_APPLIES);
            else
                sm = new SystemMessage(SystemMessageId.S1_NIGHT_EFFECT_DISAPPEARS);

            if (f.funcOwner.getFuncOwnerSkill() != null)
                sm.addSkillName(f.funcOwner.getFuncOwnerSkill());
            else
                sm.addString(f.funcOwner.getFuncOwnerName());

            sendPacket(sm);

            broadcastUserInfo();
        }
    }

    private ConditionListener[] _conditionListeners;

    private ConditionListener[] getConditionListeners() {
        if (_conditionListeners == null)
            _conditionListeners = new ConditionListener[] { new ConditionPlayerHpListener(),
                    new ConditionGameTimeListener() };

        return _conditionListeners;
    }

    public void onFuncAddition(Func f) {
        for (ConditionListener listener : getConditionListeners())
            listener.onFuncAddition(f);
    }

    public void onFuncRemoval(Func f) {
        for (ConditionListener listener : getConditionListeners())
            listener.onFuncRemoval(f);
    }

    public void refreshConditionListeners(ConditionListenerDependency dependency) {
        for (ConditionListener listener : getConditionListeners())
            listener.refresh(dependency);
    }

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

    public void sendEtcStatusUpdate() {
        addPacketBroadcastMask(BroadcastMode.SEND_ETC_STATUS_UPDATE);
    }

    public void sendEtcStatusUpdateImpl() {
        sendPacket(EtcStatusUpdate.STATIC_PACKET);
    }

    public void broadcastRelationChanged() {
        addPacketBroadcastMask(BroadcastMode.BROADCAST_RELATION_CHANGED);
    }

    public void broadcastRelationChangedImpl() {
        if (!getKnownList().getKnownPlayers().isEmpty())
            for (L2Player player : getKnownList().getKnownPlayers().values())
                RelationChanged.sendRelationChanged(this, player);
    }

    @Override
    public void sendInfo(L2Player activeChar) {
        if (isInBoat())
            getPosition().setWorldPosition(getBoat().getPosition());
        else if (isInAirShip())
            getPosition().setWorldPosition(getAirShip().getPosition());

        if (getPoly().isMorphed()) {
            activeChar.sendPacket(new AbstractNpcInfo.PlayerMorphInfo(this, getPoly().getNpcTemplate()));
        } else {
            activeChar.sendPacket(new CharInfo(this));
            //activeChar.sendPacket(new ExBrExtraUserInfo(this));
        }

        if (isInBoat())
            activeChar.sendPacket(new GetOnVehicle(this, getBoat(), getInBoatPosition().getX(),
                    getInBoatPosition().getY(), getInBoatPosition().getZ()));
        else if (isInAirShip())
            activeChar.sendPacket(new ExGetOnAirShip(this, getAirShip()));

        if (getMountType() == 4) {
            // TODO: Remove when horse mounts fixed
            activeChar.sendPacket(new Ride(this, false, 0));
            activeChar.sendPacket(new Ride(this, true, getMountNpcId()));
        }
        switch (getPrivateStoreType()) {
        case L2Player.STORE_PRIVATE_SELL: {
            activeChar.sendPacket(new PrivateStoreMsgSell(this));
            break;
        }
        case L2Player.STORE_PRIVATE_PACKAGE_SELL: {
            activeChar.sendPacket(new ExPrivateStoreSetWholeMsg(this));
            break;
        }
        case L2Player.STORE_PRIVATE_BUY: {
            activeChar.sendPacket(new PrivateStoreMsgBuy(this));
            break;
        }
        case L2Player.STORE_PRIVATE_MANUFACTURE: {
            activeChar.sendPacket(new RecipeShopMsg(this));
            break;
        }
        }
    }

    @Override
    public void broadcastFullInfoImpl() {
        sendPacket(new UserInfo(this));
        if (getPoly().isMorphed()) {
            Broadcast.toKnownPlayers(this, new AbstractNpcInfo.PlayerMorphInfo(this, getPoly().getNpcTemplate()));
        } else {
            Broadcast.toKnownPlayers(this, new CharInfo(this));
        }
    }

    @Override
    protected boolean shouldAddPacketBroadcastMask() {
        return getOnlineState() != L2Player.ONLINE_STATE_LOADED;
    }

    /**
     * @return afro haircut id
     */
    public int getAfroHaircutId() {
        return _afroId;
    }

    public void setAfroHaircutId(int id) {
        _afroId = id;
        broadcastUserInfo();
    }

    public void setIsInSiege(boolean b) {
        _isInSiege = b;
    }

    public boolean isInSiege() {
        return _isInSiege;
    }

    @Override
    protected CreatureShots initShots() {
        return new PlayerShots(this);
    }

    @Override
    public PlayerShots getShots() {
        return (PlayerShots) _shots;
    }

    public boolean isTryingToSitOrStandup() {
        return ((_lastSitStandRequest + 2333) > System.currentTimeMillis());
    }

    public static enum TeleportMode {
        SCROLL_OF_ESCAPE, RECALL, UNSTUCK, GOTOLOVE,
    }

    public boolean canTeleport(TeleportMode mode) {
        return canTeleport(mode, false);
    }

    public boolean canTeleport(TeleportMode mode, boolean bySkill) {
        if (mode != TeleportMode.RECALL && !bySkill) {
            if (isCastingNow() || isMuted() || isAlikeDead() || isMovementDisabled() || isAllSkillsDisabled())
                return false;
        }

        if (mode == TeleportMode.SCROLL_OF_ESCAPE) {
            if (isSitting()) {
                sendPacket(SystemMessageId.CANT_MOVE_SITTING);
                return false;
            }
        }

        if (!GlobalRestrictions.canTeleport(this))
            return false;

        // [L2J_JP ADD]
        if (isInsideZone(L2Zone.FLAG_NOESCAPE)) {
            sendMessage("You can't teleport from here.");
            return false;
        }

        if (isInOlympiadMode()) {
            if (mode == TeleportMode.SCROLL_OF_ESCAPE)
                sendPacket(SystemMessageId.THIS_ITEM_IS_NOT_AVAILABLE_FOR_THE_OLYMPIAD_EVENT);
            else if (mode == TeleportMode.RECALL)
                sendPacket(SystemMessageId.THIS_SKILL_IS_NOT_AVAILABLE_FOR_THE_OLYMPIAD_EVENT);
            else
                sendMessage("You can't teleport during Olympiad.");
            return false;
        }

        // Check to see if the player is in a festival.
        if (isFestivalParticipant()) {
            sendMessage("You can't teleport in a festival.");
            return false;
        }

        if (inObserverMode()) {
            sendMessage("You can't teleport during Observation Mode.");
            return false;
        }

        if (isAfraid())
            return false;

        return true;
    }

    public boolean canSee(L2Creature cha) {
        if (isGM())
            return true;

        final L2Player player = cha.getActingPlayer();

        if (player == this)
            return true;

        if (player != null) {
            if (player.getAppearance().isInvisible())
                return false;

            if (player.inObserverMode())
                return false;
        }

        // Are traps invisible for other chars than owner?
        if (cha instanceof L2Trap) {
            final L2Trap trap = (L2Trap) cha;

            if (!trap.isDetected())
                return false;
        }

        return true;
    }

    public static class TeleportBookmark {
        public int _id, _x, _y, _z, _icon;
        public String _name, _tag;

        public TeleportBookmark(int id, int x, int y, int z, int icon, String tag, String name) {
            _id = id;
            _x = x;
            _y = y;
            _z = z;
            _icon = icon;
            _name = name;
            _tag = tag;
        }
    }

    public void teleportBookmarkModify(int Id, int icon, String tag, String name) {
        int count = 0;
        int size = tpbookmark.size();
        while (size > count) {
            if (tpbookmark.get(count)._id == Id) {
                tpbookmark.get(count)._icon = icon;
                tpbookmark.get(count)._tag = tag;
                tpbookmark.get(count)._name = name;

                Connection con = null;
                try {
                    con = L2DatabaseFactory.getInstance().getConnection();
                    PreparedStatement statement = con.prepareStatement(UPDATE_TP_BOOKMARK);

                    statement.setInt(1, icon);
                    statement.setString(2, tag);
                    statement.setString(3, name);
                    statement.setInt(4, getObjectId());
                    statement.setInt(5, Id);

                    statement.execute();
                    statement.close();
                } catch (Exception e) {
                    _log.error("Could not update character teleport bookmark data.", e);
                } finally {
                    L2DatabaseFactory.close(con);
                }
            }
            count++;
        }
        sendPacket(new ExGetBookMarkInfoPacket(this));
    }

    public void teleportBookmarkDelete(int Id) {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con.prepareStatement(DELETE_TP_BOOKMARK);

            statement.setInt(1, getObjectId());
            statement.setInt(2, Id);

            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not delete character teleport bookmark data.", e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        int count = 0;
        int size = tpbookmark.size();

        while (size > count) {
            if (tpbookmark.get(count)._id == Id) {
                tpbookmark.remove(count);
                break;
            }
            count++;
        }

        sendPacket(new ExGetBookMarkInfoPacket(this));
    }

    public void teleportBookmarkGo(int Id) {
        if (!teleportBookmarkCondition(0))
            return;
        if (getInventory().getInventoryItemCount(20025, 0) == 0) {
            sendPacket(SystemMessageId.CANNOT_TELEPORT_WITHOUT_TELEPORT_ITEM);
            return;
        }
        SystemMessage sm = new SystemMessage(SystemMessageId.S1_DISAPPEARED);
        sm.addItemName(20025);
        sendPacket(sm);

        int count = 0;
        int size = tpbookmark.size();
        while (size > count) {
            if (tpbookmark.get(count)._id == Id) {
                destroyItem("Consume", getInventory().getItemByItemId(20025).getObjectId(), 1, null, false);
                teleToLocation(tpbookmark.get(count)._x, tpbookmark.get(count)._y, tpbookmark.get(count)._z);
                break;
            }
            count++;
        }
        sendPacket(new ExGetBookMarkInfoPacket(this));
    }

    public boolean teleportBookmarkCondition(int type) {
        if (isInCombat()) {
            sendPacket(SystemMessageId.UNNAMED_2348);
            return false;
        } else if (isInSiege()) {
            sendPacket(SystemMessageId.UNNAMED_2349);
            return false;
        } else if (isInDuel()) {
            sendPacket(SystemMessageId.UNNAMED_2350);
            return false;
        } else if (isFlying()) {
            sendPacket(SystemMessageId.UNNAMED_2351);
            return false;
        } else if (isInOlympiadMode()) {
            sendPacket(SystemMessageId.UNNAMED_2352);
            return false;
        } else if (isParalyzed()) {
            sendPacket(SystemMessageId.UNNAMED_2353);
            return false;
        } else if (isDead()) {
            sendPacket(SystemMessageId.UNNAMED_2354);
            return false;
        } else if (isInBoat() || isInAirShip() || isInJail() || isInsideZone(L2Zone.FLAG_NOSUMMON)) {
            if (type == 0)
                sendPacket(SystemMessageId.UNNAMED_2355);
            else if (type == 1)
                sendPacket(SystemMessageId.UNNAMED_2410);
            return false;
        } else if (isInWater()) {
            sendPacket(SystemMessageId.UNNAMED_2356);
            return false;
        }
        /* TODO: Instant Zone still not implement
        else if (this.isInsideZone(ZONE_INSTANT))
        {
           sendPacket(SystemMessageId.UNNAMED_2357);
           return;
        }
         */
        else
            return true;
    }

    public void teleportBookmarkAdd(int x, int y, int z, int icon, String tag, String name) {
        if (!teleportBookmarkCondition(1))
            return;

        if (tpbookmark.size() >= _bookmarkslot) {
            sendPacket(SystemMessageId.UNNAMED_2358);
            return;
        }

        if (getInventory().getInventoryItemCount(20033, 0) == 0) {
            sendPacket(
                    SystemMessageId.YOU_CANNOT_BOOKMARK_THIS_LOCATION_BECAUSE_YOU_DO_NOT_HAVE_A_MY_TELEPORT_FLAG);
            return;
        }

        int count = 0;
        int id = 1;
        List<Integer> idlist = new ArrayList<Integer>();

        int size = tpbookmark.size();
        while (size > count) {
            idlist.add(tpbookmark.get(count)._id);
            count++;
        }

        for (int i = 1; i < 10; i++) {
            if (!idlist.contains(i)) {
                id = i;
                break;
            }
        }

        TeleportBookmark tpadd = new TeleportBookmark(id, x, y, z, icon, tag, name);
        tpbookmark.add(tpadd);

        destroyItem("Consume", getInventory().getItemByItemId(20033).getObjectId(), 1, null, false);

        SystemMessage sm = new SystemMessage(SystemMessageId.S1_DISAPPEARED);
        sm.addItemName(20033);
        sendPacket(sm);

        Connection con = null;
        try {

            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con.prepareStatement(INSERT_TP_BOOKMARK);

            statement.setInt(1, getObjectId());
            statement.setInt(2, id);
            statement.setInt(3, x);
            statement.setInt(4, y);
            statement.setInt(5, z);
            statement.setInt(6, icon);
            statement.setString(7, tag);
            statement.setString(8, name);

            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not insert character teleport bookmark data.", e);
        } finally {
            L2DatabaseFactory.close(con);
        }

        sendPacket(new ExGetBookMarkInfoPacket(this));
    }

    public void restoreTeleportBookmark() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con.prepareStatement(RESTORE_TP_BOOKMARK);
            statement.setInt(1, getObjectId());
            ResultSet rset = statement.executeQuery();

            while (rset.next()) {
                tpbookmark.add(new TeleportBookmark(rset.getInt("Id"), rset.getInt("x"), rset.getInt("y"),
                        rset.getInt("z"), rset.getInt("icon"), rset.getString("tag"), rset.getString("name")));
            }

            rset.close();
            statement.close();
        } catch (Exception e) {
            _log.fatal("Failed restoring character teleport bookmark.", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    public boolean isFlyingMounted() {
        return _isFlyingMounted;
    }

    public void setIsFlyingMounted(boolean val) {
        _isFlyingMounted = val;
        setIsFlying(val);
    }

    public synchronized boolean enterOfflineMode() {
        if (isInOfflineMode())
            return false;

        leaveParty();

        if (getPet() != null)
            getPet().unSummon(this);

        // set name color if enabled
        updateNameTitleColor();

        new Disconnection(this).store().close(false);

        RegionBBSManager.changeCommunityBoard(this, PlayerStateOnCommunity.OFFLINE);
        return true;
    }

    public boolean isInOfflineMode() {
        return getOnlineState() == ONLINE_STATE_ONLINE && getClient() == null;
    }

    public boolean isChaotic() {
        return getKarma() > 0 || isCursedWeaponEquipped();
    }

    public int getCertificationLevel(int classIndex) {
        Connection con = null;
        int certificationLevel = -1;

        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(GET_CHAR_CERTIFICATION);

            statement.setInt(1, getObjectId());
            statement.setInt(2, classIndex);

            ResultSet rset = statement.executeQuery();
            while (rset.next()) {
                certificationLevel = rset.getInt("certif_level");
            }
            rset.close();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not get subclass certification level: ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
        return certificationLevel;
    }

    public void storeCertificationLevel(int classIndex) {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(STORE_CHAR_CERTIFICATION);

            statement.setInt(1, getObjectId());
            statement.setInt(2, classIndex);
            statement.setInt(3, 0);
            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not store character subclass certification: ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    public void updateCertificationLevel(int classIndex, int level) {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_CERTIFICATION);

            statement.setInt(1, level);
            statement.setInt(2, getObjectId());
            statement.setInt(3, classIndex);
            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not update character subclass certification: ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    public void deleteSubclassCertifications() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection(con);
            PreparedStatement statement = con.prepareStatement(DELETE_CHAR_CERTIFICATION);

            statement.setInt(1, getObjectId());
            statement.execute();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not delete character subclass certifications: ", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    public int getSubClassType() {
        /*
         * 1: Warrior
         * 2: Rogue
         * 3: Knight
         * 4: Summoner
         * 5: Wizard
         * 6: Healer
         * 7: Enchanter
         */

        int classId = getActiveClass();
        int subClassType = 0;

        if (classId == 3 || classId == 2 || classId == 46 || classId == 48 || classId == 55 || classId == 57
                || classId == 127 || classId == 128 || classId == 129 || classId == 89 || classId == 88
                || classId == 113 || classId == 114 || classId == 117 || classId == 118 || classId == 131
                || classId == 132 || classId == 133)
            subClassType = 1;
        else if (classId == 9 || classId == 24 || classId == 37 || classId == 130 || classId == 8 || classId == 23
                || classId == 36 || classId == 92 || classId == 102 || classId == 109 || classId == 134
                || classId == 93 || classId == 101 || classId == 108)
            subClassType = 2;
        else if (classId == 5 || classId == 6 || classId == 20 || classId == 33 || classId == 90 || classId == 91
                || classId == 99 || classId == 106)
            subClassType = 3;
        else if (classId == 14 || classId == 28 || classId == 41 || classId == 96 || classId == 104
                || classId == 111)
            subClassType = 4;
        else if (classId == 12 || classId == 13 || classId == 27 || classId == 40 || classId == 94 || classId == 95
                || classId == 103 || classId == 110)
            subClassType = 5;
        else if (classId == 16 || classId == 30 || classId == 43 || classId == 97 || classId == 105
                || classId == 112)
            subClassType = 6;
        else if (classId == 17 || classId == 21 || classId == 34 || classId == 52 || classId == 51 || classId == 135
                || classId == 98 || classId == 100 || classId == 107 || classId == 116 || classId == 115
                || classId == 136)
            subClassType = 7;

        return subClassType;
    }

    private final long[] _floodProtectors = new long[FloodProtector.Protected.VALUES_LENGTH];

    public long[] getFloodProtectors() {
        return _floodProtectors;
    }

    private AbstractFunEventPlayerInfo _playerInfoForEvents;

    public <T extends AbstractFunEventPlayerInfo> void setPlayerInfo(T info) {
        if (_playerInfoForEvents != null && info != null)
            throw new IllegalStateException();

        _playerInfoForEvents = info;
    }

    public <T extends AbstractFunEventPlayerInfo> T getPlayerInfo(Class<T> clazz) {
        final AbstractFunEventPlayerInfo info = _playerInfoForEvents;

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

    public <T extends AbstractFunEventPlayerInfo> boolean isInEvent(Class<T> clazz) {
        return clazz.isInstance(_playerInfoForEvents);
    }

    public <T extends AbstractFunEventPlayerInfo> T as(Class<T> clazz) {
        final AbstractFunEventPlayerInfo info = _playerInfoForEvents;

        if (clazz.isInstance(info))
            return clazz.cast(info);
        else
            throw new IllegalStateException();
    }

    private final void restoreNameTitleColors() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con.prepareStatement(RESTORE_COLORS);
            statement.setInt(1, getObjectId());
            ResultSet result = statement.executeQuery();
            if (result.next()) {
                getAppearance().setNameColor(Util.reverseRGBChanels(Integer.decode("0x" + result.getString(1))));
                getAppearance().setTitleColor(Util.reverseRGBChanels(Integer.decode("0x" + result.getString(2))));
            } else {
                getAppearance().setNameColor(PlayerAppearance.DEFAULT_NAME_COLOR);
                getAppearance().setTitleColor(PlayerAppearance.DEFAULT_TITLE_COLOR);
            }
            result.close();
            statement.close();
        } catch (Exception e) {
            getAppearance().setNameColor(PlayerAppearance.DEFAULT_NAME_COLOR);
            getAppearance().setTitleColor(PlayerAppearance.DEFAULT_TITLE_COLOR);

            _log.error("Could not load character name/title colors!", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
        updateNameTitleColor();
    }

    private final void storeNameTitleColors() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement statement = con.prepareStatement(UPDATE_COLORS);
            statement.setInt(1, getObjectId());
            statement.setString(2, HexUtil.fillHex(Util.reverseRGBChanels(getAppearance().getNameColor()), 6));
            statement.setString(3, HexUtil.fillHex(Util.reverseRGBChanels(getAppearance().getTitleColor()), 6));
            statement.executeUpdate();
            statement.close();
        } catch (Exception e) {
            _log.error("Could not store character name/title colors!", e);
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    private final void restoreCreationDate() {
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement ps = con.prepareStatement(GET_CREATION_DATE);
            ps.setInt(1, getObjectId());
            ResultSet rs = ps.executeQuery();
            rs.next();
            _lastClaim = rs.getInt("lastClaim");
            _createdOn = Calendar.getInstance();
            _createdOn.setTimeInMillis(rs.getDate("birthDate").getTime());
            rs.close();
            ps.close();
        } catch (Exception e) {
            _log.error("Could not load character creation date!", e);
            _lastClaim = Calendar.getInstance().get(Calendar.YEAR);
            _createdOn = Calendar.getInstance();
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    /**
     * Update the database indicating that the player received this year's
     * birthday gift. Should be called before giving items.
     * @return whether updating gift receival status succeeds
     */
    public final boolean claimCreationPrize() {
        _lastClaim = Calendar.getInstance().get(Calendar.YEAR);
        Connection con = null;
        try {
            con = L2DatabaseFactory.getInstance().getConnection();
            PreparedStatement ps = con.prepareStatement(CLAIM_CREATION_DAY);
            ps.setInt(1, _lastClaim);
            ps.setInt(2, getObjectId());
            ps.executeUpdate();
            ps.close();
            return true;
        } catch (Exception e) {
            _log.error(this + " could not claim creation day prize!", e);
            return false;
        } finally {
            L2DatabaseFactory.close(con);
        }
    }

    /** @return Calendar object representing the exact creation date. */
    public final Calendar getCreationDate() {
        if (_createdOn == null) // this cannot happen, but just in-case
        {
            _log.error("Illegal method call: before load() -ing the actual player instance!", new Exception());
            restoreCreationDate();
        }
        return _createdOn;
    }

    /** @return true if birthday is on Feb 29th, false otherwise */
    public final boolean isBirthdayIllegal() {
        return (getCreationDate().get(Calendar.MONTH) == Calendar.FEBRUARY
                && getCreationDate().get(Calendar.DAY_OF_MONTH) == 29);
    }

    private final int getDaysUntilAnniversary() {
        Calendar now = Calendar.getInstance();
        Calendar birth = getCreationDate();
        int day = birth.get(Calendar.DAY_OF_MONTH);
        if (isBirthdayIllegal())
            day = 28;
        if (birth.get(Calendar.MONTH) == now.get(Calendar.MONTH) && day == now.get(Calendar.DAY_OF_MONTH)) {
            if (now.get(Calendar.YEAR) > _lastClaim)
                return 0;
            else
                return 365;
        }

        Calendar anno = Calendar.getInstance();
        anno.setTimeInMillis(birth.getTimeInMillis());
        anno.set(Calendar.DAY_OF_MONTH, day);
        anno.set(Calendar.YEAR, now.get(Calendar.YEAR));

        if (anno.compareTo(now) < 0)
            anno.add(Calendar.YEAR, 1);
        long diff = (anno.getTimeInMillis() - now.getTimeInMillis());
        return (int) (diff / 86400000) + 1;
    }

    /**
     * Returns -1 if this day is the anniversary day, but the player
     * has already received the gift. Otherwise returns how many days
     * are left until the birthday.<BR>
     * 0 indicates that player can receive the gift.
     * @return days until anniversary or -1
     */
    public final int canReceiveAnnualPresent() {
        if (_lastClaim < Calendar.getInstance().get(Calendar.YEAR))
            return getDaysUntilAnniversary();
        else
            return -1;
    }

    public final void saveLastTarget(int objectId) {
        _lastTargetId = objectId;
        _lastTargetChange = System.currentTimeMillis();
    }

    public final int getLastTargetId() {
        return _lastTargetId;
    }

    public final long getLastTargetTime() {
        return _lastTargetChange;
    }

    public final boolean isIllegalWaiting() {
        return _illegalWaiting;
    }

    public final void setIllegalWaiting(boolean iw) {
        _illegalWaiting = iw;
    }

    @Override
    public void sendResistedMyEffectMessage(L2Creature target, L2Skill skill) {
        SystemMessage sm = new SystemMessage(SystemMessageId.C1_RESISTED_YOUR_S2_EFFECT);
        sm.addCharName(target);
        sm.addSkillName(skill);
        sendPacket(sm);
    }

    public boolean tryJump() {
        long time = System.currentTimeMillis();
        if (_nextJumpTime < time) {
            _nextJumpTime = time + 3000;
            return true;
        } else
            return false;
    }

    public boolean destroyItems(String process, int[] itemId, long[] count, L2Object ref, boolean sendMessage) {
        if (itemId == null || itemId.length == 0 || count == null || count.length == 0
                || itemId.length != count.length) {
            _log.error("Invalid mass item destruction call!", new IllegalArgumentException());
            return false;
        }

        int fail = -1;
        L2ItemInstance[] items = new L2ItemInstance[itemId.length];
        for (int i = 0; i < itemId.length; i++) {
            L2ItemInstance item = getInventory().getItemByItemId(itemId[i]);
            items[i] = item;
            if (item == null || item.getCount() < count[i]
                    || getInventory().destroyItem(process, item, count[i], this, ref) == null) {
                fail = i;
                break;
            }
        }

        if (fail > -1) {
            if (fail > 0)
                for (int i = 0; i < fail; i++)
                    getInventory().addItem(process, itemId[i], count[i], this, ref);
            if (sendMessage)
                sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
            return false;
        } else {
            for (int i = 0; i < itemId.length; i++) {
                getInventory().updateInventory(items[i]);
                if (sendMessage) {
                    SystemMessage sm = new SystemMessage(SystemMessageId.S2_S1_DISAPPEARED);
                    sm.addItemName(itemId[i]);
                    sm.addItemNumber(count[i]);
                    sendPacket(sm);
                }
            }
            return true;
        }
    }

    public void onTutorialQuestionMark(int number) {
        showTutorialHtml(number);
    }

    public void onTutorialLink(String request) {
        if (request.equals("close"))
            sendPacket(new TutorialCloseHtml());
    }

    private void showTutorialHtml(int number) {
        String msg = "";

        switch (number) {
        case 1001:
            L2ClassMasterInstance.onTutorialQuestionMark(this, number);
            break;
        case 1002:
            msg = HtmCache.getInstance().getHtm("data/html/level28.htm");
            sendPacket(new TutorialShowHtml(msg));
            break;
        }
    }

    private AnswerHandler _answerHandler;

    public AnswerHandler setAnswerHandler(AnswerHandler nextHandler) {
        final AnswerHandler previousHandler = _answerHandler;

        _answerHandler = nextHandler;

        return previousHandler;
    }

    public boolean hasActiveConfirmDlg() {
        return _answerHandler != null;
    }
}