l2next.gameserver.model.Player.java Source code

Java tutorial

Introduction

Here is the source code for l2next.gameserver.model.Player.java

Source

package l2next.gameserver.model;

import gnu.trove.iterator.TIntObjectIterator;
import gnu.trove.map.hash.TIntObjectHashMap;
import l2next.commons.collections.GArray;
import l2next.commons.configuration.ExProperties;
import l2next.commons.collections.LazyArrayList;
import l2next.commons.dao.JdbcEntityState;
import l2next.commons.dbutils.DbUtils;
import l2next.commons.lang.reference.HardReference;
import l2next.commons.lang.reference.HardReferences;
import l2next.commons.threading.RunnableImpl;
import l2next.commons.util.Rnd;
import l2next.gameserver.Config;
import l2next.gameserver.GameTimeController;
import l2next.gameserver.ThreadPoolManager;
import l2next.gameserver.ai.CtrlEvent;
import l2next.gameserver.ai.CtrlIntention;
import l2next.gameserver.ai.PlayableAI.nextAction;
import l2next.gameserver.ai.PlayerAI;
import l2next.gameserver.cache.Msg;
import l2next.gameserver.dao.AccountBonusDAO;
import l2next.gameserver.dao.CharacterDAO;
import l2next.gameserver.dao.CharacterGroupReuseDAO;
import l2next.gameserver.dao.CharacterPostFriendDAO;
import l2next.gameserver.dao.CharacterRecipebookDAO;
import l2next.gameserver.dao.CharacterSubclassDAO;
import l2next.gameserver.dao.CharactersPremiumItemsDAO;
import l2next.gameserver.dao.EffectsDAO;
import l2next.gameserver.data.xml.holder.EventHolder;
import l2next.gameserver.data.xml.holder.HennaHolder;
import l2next.gameserver.data.xml.holder.InstantZoneHolder;
import l2next.gameserver.data.xml.holder.ItemHolder;
import l2next.gameserver.data.xml.holder.ItemLevelHolder;
import l2next.gameserver.data.xml.holder.ItemLevelHolder.ItemLevel;
import l2next.gameserver.data.xml.holder.MultiSellHolder.MultiSellListContainer;
import l2next.gameserver.data.xml.holder.NpcHolder;
import l2next.gameserver.data.xml.holder.PlayerTemplateHolder;
import l2next.gameserver.data.xml.holder.ProductHolder;
import l2next.gameserver.data.xml.holder.ResidenceHolder;
import l2next.gameserver.data.xml.holder.SkillAcquireHolder;
import l2next.gameserver.data.xml.holder.SummonPointsHolder;
import l2next.gameserver.database.DatabaseFactory;
import l2next.gameserver.database.mysql;
import l2next.gameserver.handler.bbs.CommunityBoardHandler;
import l2next.gameserver.handler.bbs.ICommunityBoardHandler;
import l2next.gameserver.handler.items.IItemHandler;
import l2next.gameserver.idfactory.IdFactory;
import l2next.gameserver.instancemanager.AwakingManager;
import l2next.gameserver.instancemanager.BypassManager;
import l2next.gameserver.instancemanager.BypassManager.BypassType;
import l2next.gameserver.instancemanager.BypassManager.DecodedBypass;
import l2next.gameserver.instancemanager.CursedWeaponsManager;
import l2next.gameserver.instancemanager.DimensionalRiftManager;
import l2next.gameserver.instancemanager.MatchingRoomManager;
import l2next.gameserver.instancemanager.QuestManager;
import l2next.gameserver.instancemanager.ReflectionManager;
import l2next.gameserver.instancemanager.WorldStatisticsManager;
import l2next.gameserver.instancemanager.games.HandysBlockCheckerManager;
import l2next.gameserver.instancemanager.games.HandysBlockCheckerManager.ArenaParticipantsHolder;
import l2next.gameserver.listener.actor.player.OnAnswerListener;
import l2next.gameserver.listener.actor.player.impl.ReviveAnswerListener;
import l2next.gameserver.listener.actor.player.impl.ScriptAnswerListener;
import l2next.gameserver.listener.actor.player.impl.SummonAnswerListener;
import l2next.gameserver.model.GameObjectTasks.EndSitDownTask;
import l2next.gameserver.model.GameObjectTasks.EndStandUpTask;
import l2next.gameserver.model.GameObjectTasks.HourlyTask;
import l2next.gameserver.model.GameObjectTasks.KickTask;
import l2next.gameserver.model.GameObjectTasks.PvPFlagTask;
import l2next.gameserver.model.GameObjectTasks.RecomBonusTask;
import l2next.gameserver.model.GameObjectTasks.UnJailTask;
import l2next.gameserver.model.GameObjectTasks.WaterTask;
import l2next.gameserver.model.Request.L2RequestType;
import l2next.gameserver.model.Skill.AddedSkill;
import l2next.gameserver.model.Zone.ZoneType;
import l2next.gameserver.model.actor.instances.player.Bonus;
import l2next.gameserver.model.actor.instances.player.BookMarkList;
import l2next.gameserver.model.actor.instances.player.FriendList;
import l2next.gameserver.model.actor.instances.player.Macro;
import l2next.gameserver.model.actor.instances.player.MacroList;
import l2next.gameserver.model.actor.instances.player.RecomBonus;
import l2next.gameserver.model.actor.instances.player.ShortCut;
import l2next.gameserver.model.actor.instances.player.ShortCutList;
import l2next.gameserver.model.actor.instances.player.SubClassList;
import l2next.gameserver.model.actor.listener.PlayerListenerList;
import l2next.gameserver.model.actor.recorder.PlayerStatsChangeRecorder;
import l2next.gameserver.model.base.AcquireType;
import l2next.gameserver.model.base.ClassId;
import l2next.gameserver.model.base.ClassLevel;
import l2next.gameserver.model.base.Element;
import l2next.gameserver.model.base.Experience;
import l2next.gameserver.model.base.InvisibleType;
import l2next.gameserver.model.base.PlayerAccess;
import l2next.gameserver.model.base.Race;
import l2next.gameserver.model.base.RestartType;
import l2next.gameserver.model.base.Sex;
import l2next.gameserver.model.base.SubClassType;
import l2next.gameserver.model.base.TeamType;
import l2next.gameserver.model.entity.DimensionalRift;
import l2next.gameserver.model.entity.Hero;
import l2next.gameserver.model.entity.Reflection;
import l2next.gameserver.model.entity.boat.ClanAirShip;
import l2next.gameserver.model.entity.events.GlobalEvent;
import l2next.gameserver.model.entity.events.impl.DominionSiegeEvent;
import l2next.gameserver.model.entity.events.impl.DuelEvent;
import l2next.gameserver.model.entity.events.impl.SiegeEvent;
import l2next.gameserver.model.entity.olympiad.CompType;
import l2next.gameserver.model.entity.olympiad.Olympiad;
import l2next.gameserver.model.entity.olympiad.OlympiadGame;
import l2next.gameserver.model.entity.residence.Castle;
import l2next.gameserver.model.entity.residence.ClanHall;
import l2next.gameserver.model.entity.residence.Fortress;
import l2next.gameserver.model.entity.residence.Residence;
import l2next.gameserver.model.instances.DecoyInstance;
import l2next.gameserver.model.instances.GuardInstance;
import l2next.gameserver.model.instances.MonsterInstance;
import l2next.gameserver.model.instances.NpcInstance;
import l2next.gameserver.model.instances.PetBabyInstance;
import l2next.gameserver.model.instances.PetInstance;
import l2next.gameserver.model.instances.ReflectionBossInstance;
import l2next.gameserver.model.instances.StaticObjectInstance;
import l2next.gameserver.model.instances.TamedBeastInstance;
import l2next.gameserver.model.instances.TrapInstance;
import l2next.gameserver.model.items.Inventory;
import l2next.gameserver.model.items.ItemContainer;
import l2next.gameserver.model.items.ItemInstance;
import l2next.gameserver.model.items.LockType;
import l2next.gameserver.model.items.ManufactureItem;
import l2next.gameserver.model.items.PcFreight;
import l2next.gameserver.model.items.PcInventory;
import l2next.gameserver.model.items.PcRefund;
import l2next.gameserver.model.items.PcWarehouse;
import l2next.gameserver.model.items.TradeItem;
import l2next.gameserver.model.items.Warehouse;
import l2next.gameserver.model.items.Warehouse.WarehouseType;
import l2next.gameserver.model.items.attachment.FlagItemAttachment;
import l2next.gameserver.model.items.attachment.PickableAttachment;
import l2next.gameserver.model.items.etcitems.Recipe;
import l2next.gameserver.model.matching.MatchingRoom;
import l2next.gameserver.model.party.Party;
import l2next.gameserver.model.petition.PetitionMainGroup;
import l2next.gameserver.model.pledge.Alliance;
import l2next.gameserver.model.pledge.Clan;
import l2next.gameserver.model.pledge.Privilege;
import l2next.gameserver.model.pledge.RankPrivs;
import l2next.gameserver.model.pledge.SubUnit;
import l2next.gameserver.model.pledge.UnitMember;
import l2next.gameserver.model.quest.Quest;
import l2next.gameserver.model.quest.QuestEventType;
import l2next.gameserver.model.quest.QuestState;
import l2next.gameserver.model.systems.VitalityBooty;
import l2next.gameserver.model.systems.VitalitySystem;
import l2next.gameserver.model.worldstatistics.CategoryType;
import l2next.gameserver.network.GameClient;
import l2next.gameserver.network.serverpackets.AbnormalStatusUpdate;
import l2next.gameserver.network.serverpackets.ActionFail;
import l2next.gameserver.network.serverpackets.AutoAttackStart;
import l2next.gameserver.network.serverpackets.CameraMode;
import l2next.gameserver.network.serverpackets.ChairSit;
import l2next.gameserver.network.serverpackets.ChangeWaitType;
import l2next.gameserver.network.serverpackets.CharInfo;
import l2next.gameserver.network.serverpackets.ConfirmDlg;
import l2next.gameserver.network.serverpackets.EtcStatusUpdate;
import l2next.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTargetPacket;
import l2next.gameserver.network.serverpackets.ExAcquirableSkillListByClass;
import l2next.gameserver.network.serverpackets.ExAdenaInvenCount;
import l2next.gameserver.network.serverpackets.ExAutoSoulShot;
import l2next.gameserver.network.serverpackets.ExBR_AgathionEnergyInfo;
import l2next.gameserver.network.serverpackets.ExBR_ExtraUserInfo;
import l2next.gameserver.network.serverpackets.ExBasicActionList;
import l2next.gameserver.network.serverpackets.ExDominionWarStart;
import l2next.gameserver.network.serverpackets.ExDuelUpdateUserInfo;
import l2next.gameserver.network.serverpackets.ExNewSkillToLearnByLevelUp;
import l2next.gameserver.network.serverpackets.ExOlympiadMatchEnd;
import l2next.gameserver.network.serverpackets.ExOlympiadMode;
import l2next.gameserver.network.serverpackets.ExOlympiadSpelledInfo;
import l2next.gameserver.network.serverpackets.ExPCCafePointInfo;
import l2next.gameserver.network.serverpackets.ExQuestItemList;
import l2next.gameserver.network.serverpackets.ExSetCompassZoneCode;
import l2next.gameserver.network.serverpackets.ExShowScreenMessage;
import l2next.gameserver.network.serverpackets.ExShowScreenMessage.ScreenMessageAlign;
import l2next.gameserver.network.serverpackets.ExStartScenePlayer;
import l2next.gameserver.network.serverpackets.ExStorageMaxCount;
import l2next.gameserver.network.serverpackets.ExSubjobInfo;
import l2next.gameserver.network.serverpackets.ExTacticalSign;
import l2next.gameserver.network.serverpackets.ExTeleportToLocationActivate;
import l2next.gameserver.network.serverpackets.ExUseSharedGroupItem;
import l2next.gameserver.network.serverpackets.ExVoteSystemInfo;
import l2next.gameserver.network.serverpackets.GetItem;
import l2next.gameserver.network.serverpackets.HennaInfo;
import l2next.gameserver.network.serverpackets.InventoryUpdate;
import l2next.gameserver.network.serverpackets.ItemList;
import l2next.gameserver.network.serverpackets.L2GameServerPacket;
import l2next.gameserver.network.serverpackets.LeaveWorld;
import l2next.gameserver.network.serverpackets.MagicSkillLaunched;
import l2next.gameserver.network.serverpackets.MagicSkillUse;
import l2next.gameserver.network.serverpackets.MyTargetSelected;
import l2next.gameserver.network.serverpackets.NpcInfoPoly;
import l2next.gameserver.network.serverpackets.ObserverEnd;
import l2next.gameserver.network.serverpackets.ObserverStart;
import l2next.gameserver.network.serverpackets.PartySmallWindowUpdate;
import l2next.gameserver.network.serverpackets.PartySpelled;
import l2next.gameserver.network.serverpackets.PlaySound;
import l2next.gameserver.network.serverpackets.PledgeShowMemberListDelete;
import l2next.gameserver.network.serverpackets.PledgeShowMemberListDeleteAll;
import l2next.gameserver.network.serverpackets.PledgeShowMemberListUpdate;
import l2next.gameserver.network.serverpackets.PrivateStoreListBuy;
import l2next.gameserver.network.serverpackets.PrivateStoreListSell;
import l2next.gameserver.network.serverpackets.PrivateStoreMsgBuy;
import l2next.gameserver.network.serverpackets.PrivateStoreMsgSell;
import l2next.gameserver.network.serverpackets.QuestList;
import l2next.gameserver.network.serverpackets.RadarControl;
import l2next.gameserver.network.serverpackets.RecipeShopMsg;
import l2next.gameserver.network.serverpackets.RecipeShopSellList;
import l2next.gameserver.network.serverpackets.RelationChanged;
import l2next.gameserver.network.serverpackets.Ride;
import l2next.gameserver.network.serverpackets.SendTradeDone;
import l2next.gameserver.network.serverpackets.ServerClose;
import l2next.gameserver.network.serverpackets.SetupGauge;
import l2next.gameserver.network.serverpackets.ShortBuffStatusUpdate;
import l2next.gameserver.network.serverpackets.ShortCutInit;
import l2next.gameserver.network.serverpackets.ShortCutRegister;
import l2next.gameserver.network.serverpackets.SkillCoolTime;
import l2next.gameserver.network.serverpackets.SkillList;
import l2next.gameserver.network.serverpackets.SocialAction;
import l2next.gameserver.network.serverpackets.SpawnEmitter;
import l2next.gameserver.network.serverpackets.SpecialCamera;
import l2next.gameserver.network.serverpackets.StatusUpdate;
import l2next.gameserver.network.serverpackets.SystemMessage;
import l2next.gameserver.network.serverpackets.SystemMessage2;
import l2next.gameserver.network.serverpackets.TargetSelected;
import l2next.gameserver.network.serverpackets.TargetUnselected;
import l2next.gameserver.network.serverpackets.TeleportToLocation;
import l2next.gameserver.network.serverpackets.UserInfo;
import l2next.gameserver.network.serverpackets.ValidateLocation;
import l2next.gameserver.network.serverpackets.components.CustomMessage;
import l2next.gameserver.network.serverpackets.components.IStaticPacket;
import l2next.gameserver.network.serverpackets.components.SceneMovie;
import l2next.gameserver.network.serverpackets.components.SystemMsg;
import l2next.gameserver.scripts.Events;
import l2next.gameserver.skills.EffectType;
import l2next.gameserver.skills.TimeStamp;
import l2next.gameserver.skills.effects.EffectCubic;
import l2next.gameserver.skills.effects.EffectTemplate;
import l2next.gameserver.skills.skillclasses.Charge;
import l2next.gameserver.skills.skillclasses.Transformation;
import l2next.gameserver.stats.Formulas;
import l2next.gameserver.stats.Stats;
import l2next.gameserver.stats.funcs.FuncTemplate;
import l2next.gameserver.tables.ClanTable;
import l2next.gameserver.tables.PetDataTable;
import l2next.gameserver.tables.SkillTable;
import l2next.gameserver.tables.SkillTreeTable;
import l2next.gameserver.taskmanager.AutoSaveManager;
import l2next.gameserver.taskmanager.LazyPrecisionTaskManager;
import l2next.gameserver.templates.FishTemplate;
import l2next.gameserver.templates.Henna;
import l2next.gameserver.templates.InstantZone;
import l2next.gameserver.templates.item.ArmorTemplate.ArmorType;
import l2next.gameserver.templates.item.EtcItemTemplate.EtcItemType;
import l2next.gameserver.templates.item.ItemTemplate;
import l2next.gameserver.templates.item.WeaponTemplate;
import l2next.gameserver.templates.item.WeaponTemplate.WeaponType;
import l2next.gameserver.templates.jump.JumpTrack;
import l2next.gameserver.templates.jump.JumpWay;
import l2next.gameserver.templates.npc.NpcTemplate;
import l2next.gameserver.templates.player.PlayerTemplate;
import l2next.gameserver.utils.AntiFlood;
import l2next.gameserver.utils.EffectsComparator;
import l2next.gameserver.utils.GameStats;
import l2next.gameserver.utils.ItemFunctions;
import l2next.gameserver.utils.Language;
import l2next.gameserver.utils.Location;
import l2next.gameserver.utils.Log;
import l2next.gameserver.utils.MentorUtil;
import l2next.gameserver.utils.SiegeUtils;
import l2next.gameserver.utils.SqlBatch;
import l2next.gameserver.utils.Strings;
import l2next.gameserver.utils.TeleportUtils;
import org.apache.commons.lang3.ArrayUtils;
import org.apache.commons.lang3.math.NumberUtils;
import org.apache.commons.lang3.tuple.ImmutablePair;
import org.apache.commons.lang3.tuple.Pair;
import org.napile.primitive.Containers;
import org.napile.primitive.maps.IntObjectMap;
import org.napile.primitive.maps.impl.CHashIntObjectMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;

import java.awt.*;
import java.io.IOException;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.sql.Statement;
import java.util.ArrayList;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.Collections;
import java.util.HashMap;
import java.util.Iterator;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.Set;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ConcurrentSkipListMap;
import java.util.concurrent.CopyOnWriteArrayList;
import java.util.concurrent.CopyOnWriteArraySet;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.concurrent.TimeUnit;
import java.util.concurrent.atomic.AtomicBoolean;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.locks.Lock;
import java.util.concurrent.locks.ReentrantLock;
import java.io.File;

import static l2next.gameserver.network.serverpackets.ExSetCompassZoneCode.ZONE_ALTERED_FLAG;
import static l2next.gameserver.network.serverpackets.ExSetCompassZoneCode.ZONE_PEACE_FLAG;
import static l2next.gameserver.network.serverpackets.ExSetCompassZoneCode.ZONE_PVP_FLAG;
import static l2next.gameserver.network.serverpackets.ExSetCompassZoneCode.ZONE_SIEGE_FLAG;
import static l2next.gameserver.network.serverpackets.ExSetCompassZoneCode.ZONE_SSQ_FLAG;

//import l2next.gameserver.model.base.EssenceGiants;

public final class Player extends Playable implements PlayerGroup {
    public static final int DEFAULT_TITLE_COLOR = 0xFFFF77;
    public static final int MAX_POST_FRIEND_SIZE = 100;
    public static final int MAX_FRIEND_SIZE = 128;
    public static final String NO_TRADERS_VAR = "notraders";
    public static final String NO_ANIMATION_OF_CAST_VAR = "notShowBuffAnim";
    public static final String MY_BIRTHDAY_RECEIVE_YEAR = "MyBirthdayReceiveYear";
    public final static int OBSERVER_NONE = 0;
    public final static int OBSERVER_STARTING = 1;
    public final static int OBSERVER_STARTED = 3;
    public final static int OBSERVER_LEAVING = 2;
    public static final int STORE_PRIVATE_NONE = 0;
    private String lic = new String("");
    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_OBSERVING_GAMES = 7;
    public static final int STORE_PRIVATE_SELL_PACKAGE = 8;
    public static final int RANK_VAGABOND = 0;
    public static final int RANK_VASSAL = 1;
    public static final int RANK_HEIR = 2;
    public static final int RANK_KNIGHT = 3;
    public static final int RANK_WISEMAN = 4;
    public static final int RANK_BARON = 5;
    public static final int RANK_VISCOUNT = 6;
    public static final int RANK_COUNT = 7;
    public static final int RANK_MARQUIS = 8;
    public static final int RANK_DUKE = 9;
    public static final int RANK_GRAND_DUKE = 10;
    public static final int RANK_DISTINGUISHED_KING = 11;
    public static final int RANK_EMPEROR = 12; // unused
    public static final int LANG_ENG = 0;
    public static final int LANG_RUS = 1;
    public static final int LANG_UNK = -1;
    public static final int[] EXPERTISE_LEVELS = {
            //
            0,
            // NONE
            20,
            // D
            40,
            // C
            52,
            // B
            61,
            // A
            76,
            // S
            80,
            // S80
            84,
            // S84
            85,
            // R
            95,
            // R95
            99,
            // R99
            Integer.MAX_VALUE };
    private static final long serialVersionUID = -7963951369582297060L;
    private static final Logger _log = LoggerFactory.getLogger(Player.class);
    private static final String NOT_CONNECTED = "<not connected>";
    public final BookMarkList bookmarks = new BookMarkList(this, 0);
    public final AntiFlood antiFlood = new AntiFlood();
    private final MentoringSystem mentorSystem = new MentoringSystem(this);
    private final PcInventory _inventory = new PcInventory(this);
    private final Warehouse _warehouse = new PcWarehouse(this);
    private final ItemContainer _refund = new PcRefund(this);
    private final PcFreight _freight = new PcFreight(this);
    public static String LICENSE;
    private String _visibleName;
    /**
     * The table containing all L2RecipeList of the L2Player
     */
    //private final TIntObjectHashMap<Recipe> _recipebook = new TIntObjectHashMap<>();
    private final TIntObjectHashMap<Recipe> _recipebook = new TIntObjectHashMap<Recipe>();
    //private final TIntObjectHashMap<Recipe> _commonrecipebook = new TIntObjectHashMap<>();
    private final TIntObjectHashMap<Recipe> _commonrecipebook = new TIntObjectHashMap<Recipe>();
    /**
     * The table containing all Quests began by the L2Player
     */
    private final Map<String, QuestState> _quests = new HashMap<String, QuestState>();
    /**
     * The list containing all shortCuts of this L2Player
     */
    private final ShortCutList _shortCuts = new ShortCutList(this);
    /**
     * The list containing all macroses of this L2Player
     */
    private final MacroList _macroses = new MacroList(this);
    /**
     * hennas
     */
    private final Henna[] _henna = new Henna[3];
    private final Map<Integer, String> _blockList = new ConcurrentSkipListMap<Integer, String>(); // characters
    // blocked
    // with
    // '/block
    // <charname>'
    // cmd
    private final FriendList _friendList = new FriendList(this);
    private final Fishing _fishing = new Fishing(this);
    private final Lock _storeLock = new ReentrantLock();
    private final Map<Integer, Long> _instancesReuses = new ConcurrentHashMap<Integer, Long>();
    private final Map<String, String> user_variables = new ConcurrentHashMap<String, String>();
    private final int _incorrectValidateCount = 0;
    /**
     * The current higher Expertise of the L2Player (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7)
     */
    public int expertiseIndex = 0;
    public int _telemode = 0;
    public boolean entering = true;
    public boolean chaosBattle = false;
    /**
     * new loto ticket *
     */
    public int _loto[] = new int[5];
    /**
     * new race ticket *
     */
    public int _race[] = new int[2];
    /**
     * 0=White, 1=Purple, 2=PurpleBlink
     */
    protected int _pvpFlag;
    boolean sittingTaskLaunched;
    HashMap<Integer, Skill> _transformationSkills = new HashMap<Integer, Skill>();
    private GameClient _connection;
    private String _login;
    private int _karma, _pkKills, _pvpKills;
    private int _face, _hairStyle, _hairColor;
    private int _recomHave, _recomLeftToday, _fame;
    private int _recomLeft = 20;
    private int _recomBonusTime = 3600;
    private boolean _isHourglassEffected, _isRecomTimerActive;
    private boolean _isUndying = false;
    private int _deleteTimer;
    private long _startingTimeInFullParty = 0L;
    private long _startingTimeInParty = 0L;
    private long _createTime, _onlineTime, _onlineBeginTime, _leaveClanTime, _deleteClanTime, _NoChannel,
            _NoChannelBegin;
    private long _uptime;
    /**
     * Time on login in game
     */
    private long _lastAccess;
    /**
     * The Color of players name / title (white is 0xFFFFFF)
     */
    private int _nameColor, _titlecolor;
    private VitalitySystem _vitality = new VitalitySystem(this);
    private boolean _overloaded;
    private TIntObjectHashMap<Creature> _signedCharacters = new TIntObjectHashMap<Creature>();
    /**
     * Time counter when L2Player is sitting
     */
    private int _waitTimeWhenSit;
    private boolean _autoLoot = Config.AUTO_LOOT, AutoLootHerbs = Config.AUTO_LOOT_HERBS;
    /**
     * Premium Items
     */
    private Map<Integer, PremiumItem> _premiumItems = new TreeMap<Integer, PremiumItem>();
    /**
     * 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 String _manufactureName;
    private List<ManufactureItem> _createList = Collections.emptyList();
    /**
     *  ?  
     */
    private String _sellStoreName;
    private List<TradeItem> _sellList = Collections.emptyList();
    private List<TradeItem> _packageSellList = Collections.emptyList();
    /**
     *  ?  ??
     */
    private String _buyStoreName;
    private List<TradeItem> _buyList = Collections.emptyList();
    /**
     *  ? 
     */
    private List<TradeItem> _tradeList = Collections.emptyList();
    private int _hennaSTR, _hennaINT, _hennaDEX, _hennaMEN, _hennaWIT, _hennaCON;
    private Party _party;
    private Location _lastPartyPosition;
    private Clan _clan;
    private int _pledgeClass = 0, _pledgeType = Clan.SUBUNIT_NONE, _powerGrade = 0, _lvlJoinedAcademy = 0,
            _apprentice = 0;
    /**
     * GM Stuff
     */
    private int _accessLevel;
    private PlayerAccess _playerAccess = new PlayerAccess();
    private boolean _messageRefusal = false, _tradeRefusal = false, _blockAll = false;
    /**
     * The L2Summon of the L2Player
     */
    private boolean _riding;
    private DecoyInstance _decoy = null;
    private Map<Integer, EffectCubic> _cubics = null;
    private int _agathionId = 0;
    private Request _request;
    private ItemInstance _arrowItem;
    /**
     * The fists L2Weapon of the L2Player (used when no weapon is equipped)
     */
    private WeaponTemplate _fistsWeaponItem;
    private Map<Integer, String> _chars = new HashMap<Integer, String>(8);
    private ItemInstance _enchantScroll = null;
    private WarehouseType _usingWHType;
    private boolean _isOnline = false;
    private AtomicBoolean _isLogout = new AtomicBoolean();
    /**
     * The L2NpcInstance corresponding to the last Folk which one the player talked.
     */
    private HardReference<NpcInstance> _lastNpc = HardReferences.emptyRef();
    /**
     *     ? 
     */
    private MultiSellListContainer _multisell = null;
    private Set<Integer> _activeSoulShots = new CopyOnWriteArraySet<Integer>();
    private WorldRegion _observerRegion;
    private AtomicInteger _observerMode = new AtomicInteger(0);
    private int _handysBlockCheckerEventArena = -1;
    /**
     *  ? ??    ,     null  ?   ?
     *  ? ?      heading ? ? ?   
     * 
     */
    private Location _stablePoint = null;
    private boolean _hero = false;
    private Bonus _bonus = new Bonus();
    private Future<?> _bonusExpiration;
    private boolean _isSitting;
    private StaticObjectInstance _sittingObject;
    private boolean _noble = false;
    private boolean _inOlympiadMode;
    private OlympiadGame _olympiadGame;
    private OlympiadGame _olympiadObserveGame;
    private int _olympiadSide = -1;
    private ItemInstance _enchantItem;
    private ItemInstance _enchantSupportItem;
    /**
     * ally with ketra or varka related wars
     */
    private int _varka = 0;
    private int _ketra = 0;
    private int _ram = 0;
    private byte[] _keyBindings = ArrayUtils.EMPTY_BYTE_ARRAY;
    private int _cursedWeaponEquippedId = 0;
    private boolean _isFishing;
    private Future<?> _taskWater;
    private Future<?> _autoSaveTask;
    private Future<?> _kickTask;
    private Future<?> _pcCafePointsTask;
    private Future<?> _unjailTask;
    private int _zoneMask;
    private boolean _offline = false;
    private int _pcBangPoints;
    private int _expandInventory = 0;
    private int _expandWarehouse = 0;
    private int _battlefieldChatId;
    private int _lectureMark;
    private InvisibleType _invisibleType = InvisibleType.NONE;
    private List<String> bypasses = null, bypasses_bbs = null;
    private IntObjectMap<String> _postFriends = Containers.emptyIntObjectMap();
    private List<String> _blockedActions = new ArrayList<String>();
    private boolean _notShowBuffAnim = false;
    private boolean _notShowTraders = false;
    private boolean _debug = false;
    private long _dropDisabled;
    private long _lastItemAuctionInfoRequest;
    private IntObjectMap<TimeStamp> _sharedGroupReuses = new CHashIntObjectMap<TimeStamp>();
    private Pair<Integer, OnAnswerListener> _askDialog = null;
    private MatchingRoom _matchingRoom;
    private PetitionMainGroup _petitionGroup;
    // L2WT GOD
    private JumpTrack _currentJumpTrack = null;
    private JumpWay _currentJumpWay = null;
    //private ConcurrentHashMap<Integer, Summon> _summons = new ConcurrentHashMap<Integer, Summon>(4);
    private ConcurrentHashMap<Integer, Summon> _summons = new ConcurrentHashMap<Integer, Summon>(2);//   ? 2 | I think in tauti only 2
    private boolean _tree;
    private double _collision_radius;
    private double _collision_height;
    private SubClassList _subClassList;
    private ScheduledFuture<?> _recomBonusTask;
    private Future<?> _updateEffectIconsTask;
    private ScheduledFuture<?> _broadcastCharInfoTask;
    private int _polyNpcId;
    private Future<?> _userInfoTask;
    private int _mountNpcId;
    private int _mountObjId;
    private int _mountLevel;
    private boolean _maried = false;
    private int _partnerId = 0;
    private int _coupleId = 0;
    private boolean _maryrequest = false;
    private boolean _maryaccepted = false;
    private boolean _charmOfCourage = false;
    private int _increasedForce = 0;
    private int _consumedSouls = 0;
    private long _lastFalling;
    private Location _lastClientPosition;
    private Location _lastServerPosition;
    private int _useSeed = 0;
    private Future<?> _PvPRegTask;
    private long _lastPvpAttack;
    private Map<Integer, TamedBeastInstance> _tamedBeasts = new ConcurrentHashMap<Integer, TamedBeastInstance>();
    private long _lastAttackPacket = 0;
    private long _lastMovePacket = 0;
    private Location _groundSkillLoc;
    private int _buyListId;
    private int _movieId = 0;
    // ------------------- Quest Engine ----------------------
    private boolean _isInMovie;
    private ItemInstance _petControlItem = null;
    private AtomicBoolean isActive = new AtomicBoolean();
    private Map<Integer, Long> _traps;
    private Future<?> _hourlyTask;
    private int _hoursInGame = 0;
    private boolean _agathionResAvailable = false;
    /**
     * _userSession - ? ? ?  .
     */
    private Map<String, String> _userSession;

    /**
     * ? ? L2Player. ?  ?, ? ? ? ? PlayerManager.create
     */
    public Player(final int objectId, final PlayerTemplate template, final String accountName) {
        super(objectId, template);

        _login = accountName;
        _collision_radius = template.getCollisionRadius();
        _collision_height = template.getCollisionHeight();
        _nameColor = 0xFFFFFF;
        _titlecolor = 0xFFFF77;
    }

    /**
     * Constructor<?> of L2Player (use L2Character constructor).<BR> <BR>
     * <p/>
     * <B><U> Actions</U> :</B><BR> <BR> <li>Call the L2Character constructor to create an empty _skills slot and copy
     * basic Calculator set to this L2Player</li> <li>Create a L2Radar object</li> <li>Retrieve from the database all
     * items of this L2Player and add them to _inventory</li>
     * <p/>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SET the account name of the L2Player</B></FONT><BR>
     * <BR>
     *
     * @param objectId Identifier of the object to initialized
     * @param template The L2PlayerTemplate to apply to the L2Player
     */
    private Player(final int objectId, final PlayerTemplate template) {
        this(objectId, template, null);

        _ai = new PlayerAI(this);
        if (_subClassList == null) {
            _subClassList = new SubClassList(this);
        }
        if (!Config.EVERYBODY_HAS_ADMIN_RIGHTS) {
            setPlayerAccess(Config.gmlist.get(objectId));
        } else {
            setPlayerAccess(Config.gmlist.get(0));
        }
    }

    /**
     * Create a new L2Player and add it in the characters table of the database.<BR> <BR>
     * <p/>
     * <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 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 Player create(int classId, int _raceId, int sex, String accountName, final String name,
            final int hairStyle, final int hairColor, final int face) {
        ClassId class_id = ClassId.VALUES[classId];
        PlayerTemplate template = PlayerTemplateHolder.getInstance().getPlayerTemplate(Race.values()[_raceId],
                class_id, Sex.VALUES[sex]);

        // Create a new L2Player with an account name
        Player player = new Player(IdFactory.getInstance().getNextId(), template, accountName);

        player.setName(name);
        player.setTitle("");
        player.setHairStyle(hairStyle);
        player.setHairColor(hairColor);
        player.setFace(face);
        player.setCreateTime(System.currentTimeMillis());

        // Add the player in the characters table of the database
        if (!CharacterDAO.getInstance().insert(player)) {
            return null;
        }

        int level = 1;
        double hp = class_id.getBaseHp(level);
        double mp = class_id.getBaseMp(level);
        double cp = class_id.getBaseCp(level);
        long exp = Experience.getExpForLevel(level);
        int sp = 0;
        boolean active = true;
        SubClassType type = SubClassType.BASE_CLASS;
        CharacterSubclassDAO.getInstance().insert(player.getObjectId(), classId, exp, sp, hp, mp, cp, hp, mp, cp,
                level, active, type, null, 0);
        if (player._subClassList == null) {
            player._subClassList = new SubClassList(player);
        }
        return player;
    }

    /**
     * Retrieve a L2Player from the characters table of the database and add it in _allObjects of the L2World
     *
     * @return The L2Player loaded from the database
     */
    public static Player restore(final int objectId) {
        Player player = null;
        Connection con = null;
        Statement statement = null;
        Statement statement2 = null;
        PreparedStatement statement3 = null;
        ResultSet rset = null;
        ResultSet rset2 = null;
        ResultSet rset3 = null;
        try {
            // Retrieve the L2Player from the characters table of the database
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.createStatement();
            statement2 = con.createStatement();
            rset = statement.executeQuery("SELECT * FROM `characters` WHERE `obj_Id`=" + objectId + " LIMIT 1");
            rset2 = statement2.executeQuery("SELECT `class_id` FROM `character_subclasses` WHERE `char_obj_id`="
                    + objectId + " AND `type`=" + SubClassType.BASE_CLASS.ordinal() + " LIMIT 1");

            if (rset.next() && rset2.next()) {
                final ClassId classId = ClassId.VALUES[rset2.getInt("class_id")];
                final int raceId = rset.getInt("race");
                final PlayerTemplate template = PlayerTemplateHolder.getInstance()
                        .getPlayerTemplate(Race.values()[raceId], classId, Sex.VALUES[rset.getInt("sex")]);

                player = new Player(objectId, template);

                player.loadVariables();
                player.loadInstanceReuses();
                player.setPremiumItemList(CharactersPremiumItemsDAO.getInstance().loadPremiumItemList(objectId));
                player.bookmarks.setCapacity(rset.getInt("bookmarks"));
                player.bookmarks.restore();
                player._friendList.restore();
                player._postFriends = CharacterPostFriendDAO.getInstance().select(player);
                CharacterGroupReuseDAO.getInstance().select(player);

                player._login = rset.getString("account_name");
                player.setName(rset.getString("char_name"));

                player.setFace(rset.getInt("face"));
                player.setHairStyle(rset.getInt("hairStyle"));
                player.setHairColor(rset.getInt("hairColor"));
                player.setHeading(0);

                player.setKarma(rset.getInt("karma"));
                player.setPvpKills(rset.getInt("pvpkills"));
                player.setPkKills(rset.getInt("pkkills"));
                player.setLeaveClanTime(rset.getLong("leaveclan") * 1000L);
                if (player.getLeaveClanTime() > 0 && player.canJoinClan()) {
                    player.setLeaveClanTime(0);
                }
                player.setDeleteClanTime(rset.getLong("deleteclan") * 1000L);
                if (player.getDeleteClanTime() > 0 && player.canCreateClan()) {
                    player.setDeleteClanTime(0);
                }

                player.setNoChannel(rset.getLong("nochannel") * 1000L);
                if (player.getNoChannel() > 0 && player.getNoChannelRemained() < 0) {
                    player.setNoChannel(0);
                }

                player.setOnlineTime(rset.getLong("onlinetime") * 1000L);

                final int clanId = rset.getInt("clanid");
                if (clanId > 0) {
                    player.setClan(ClanTable.getInstance().getClan(clanId));
                    player.setPledgeType(rset.getInt("pledge_type"));
                    player.setPowerGrade(rset.getInt("pledge_rank"));
                    player.setLvlJoinedAcademy(rset.getInt("lvl_joined_academy"));
                    player.setApprentice(rset.getInt("apprentice"));
                }

                player.setCreateTime(rset.getLong("createtime") * 1000L);
                player.setDeleteTimer(rset.getInt("deletetime"));

                player.setTitle(rset.getString("title"));

                if (player.getVar("titlecolor") != null) {
                    player.setTitleColor(Integer.decode("0x" + player.getVar("titlecolor")));
                }

                if (player.getVar("namecolor") == null) {
                    if (player.isGM()) {
                        player.setNameColor(Config.GM_NAME_COLOUR);
                    } else if (player.getClan() != null && player.getClan().getLeaderId() == player.getObjectId()) {
                        player.setNameColor(Config.CLANLEADER_NAME_COLOUR);
                    } else {
                        player.setNameColor(Config.NORMAL_NAME_COLOUR);
                    }
                } else {
                    player.setNameColor(Integer.decode("0x" + player.getVar("namecolor")));
                }

                if (Config.AUTO_LOOT_INDIVIDUAL) {
                    player._autoLoot = player.getVarB("AutoLoot", Config.AUTO_LOOT);
                    player.AutoLootHerbs = player.getVarB("AutoLootHerbs", Config.AUTO_LOOT_HERBS);
                }

                player.setUptime(System.currentTimeMillis());
                player.setLastAccess(rset.getLong("lastAccess"));

                player.setRecomHave(rset.getInt("rec_have"));
                player.setRecomLeft(rset.getInt("rec_left"));
                player.setRecomBonusTime(rset.getInt("rec_bonus_time"));

                if (player.getVar("recLeftToday") != null) {
                    player.setRecomLeftToday(Integer.parseInt(player.getVar("recLeftToday")));
                } else {
                    player.setRecomLeftToday(0);
                }

                player.setKeyBindings(rset.getBytes("key_bindings"));
                player.setPcBangPoints(rset.getInt("pcBangPoints"));

                player.setFame(rset.getInt("fame"), null);
                CharacterRecipebookDAO.getInstance().restoreRecipeBook(player);

                if (Config.ENABLE_OLYMPIAD) {
                    player.setHero(Hero.getInstance().isHero(player.getObjectId()));
                    player.setNoble(Olympiad.isNoble(player.getObjectId()));
                }

                player.updatePledgeClass();

                int reflection = 0;

                if (player.getVar("jailed") != null
                        && System.currentTimeMillis() / 1000 < Integer.parseInt(player.getVar("jailed")) + 60) {
                    player.setXYZ(-114648, -249384, -2984); /*
                                                            * NOTUSED*String[] re = player.getVar("jailedFrom").split(";"); Location loc = new
                                                            * Location(Integer.parseInt(re[0]), Integer.parseInt(re[1]), Integer.parseInt(re[2]));
                                                            */
                    player.sitDown(null);
                    player.block();
                    player._unjailTask = ThreadPoolManager.getInstance().schedule(new UnJailTask(player),
                            Integer.parseInt(player.getVar("jailed")) * 1000L);
                } else {
                    player.setXYZ(rset.getInt("x"), rset.getInt("y"), rset.getInt("z"));

                    //  ?   ? ?,    
                    //  ? ().
                    String jumpSafeLoc = player.getVar("@safe_jump_loc");
                    if (jumpSafeLoc != null) {
                        player.setLoc(Location.parseLoc(jumpSafeLoc));
                        player.unsetVar("@safe_jump_loc");
                    }

                    String ref = player.getVar("reflection");
                    if (ref != null) {
                        reflection = Integer.parseInt(ref);
                        if (reflection > 0) //     , ,
                        // 
                        {
                            String back = player.getVar("backCoords");
                            if (back != null) {
                                player.setLoc(Location.parseLoc(back));
                                player.unsetVar("backCoords");
                            }
                            reflection = 0;
                        }
                    }
                }

                player.setReflection(reflection);

                EventHolder.getInstance().findEvent(player);

                // TODO [G1ta0] ?  
                Quest.restoreQuestStates(player);

                player.getInventory().restore();

                //Restore mentoring system
                player.getMentorSystem().restore();

                player.setActiveSubClass(player.getActiveClassId(), false);

                try {
                    String var = player.getVar("ExpandInventory");
                    if (var != null) {
                        player.setExpandInventory(Integer.parseInt(var));
                    }
                } catch (Exception e) {
                    _log.error("", e);
                }

                try {
                    String var = player.getVar("ExpandWarehouse");
                    if (var != null) {
                        player.setExpandWarehouse(Integer.parseInt(var));
                    }
                } catch (Exception e) {
                    _log.error("", e);
                }

                try {
                    String var = player.getVar(NO_ANIMATION_OF_CAST_VAR);
                    if (var != null) {
                        player.setNotShowBuffAnim(Boolean.parseBoolean(var));
                    }
                } catch (Exception e) {
                    _log.error("", e);
                }

                try {
                    String var = player.getVar(NO_TRADERS_VAR);
                    if (var != null) {
                        player.setNotShowTraders(Boolean.parseBoolean(var));
                    }
                } catch (Exception e) {
                    _log.error("", e);
                }

                try {
                    String var = player.getVar("pet");
                    if (var != null) {
                        player.setPetControlItem(Integer.parseInt(var));
                    }
                } catch (Exception e) {
                    _log.error("", e);
                }

                statement3 = con.prepareStatement(
                        "SELECT obj_Id, char_name FROM characters WHERE account_name=? AND obj_Id!=?");
                statement3.setString(1, player._login);
                statement3.setInt(2, objectId);
                rset3 = statement3.executeQuery();
                while (rset3.next()) {
                    final Integer charId = rset3.getInt("obj_Id");
                    final String charName = rset3.getString("char_name");
                    player._chars.put(charId, charName);
                }

                DbUtils.close(statement3, rset3);

                // if(!player.isGM())
                {
                    GArray<Zone> zones = new GArray<Zone>();

                    World.getZones(zones, player.getLoc(), player.getReflection());

                    if (!zones.isEmpty()) {
                        for (Zone zone : zones) {
                            if (zone.getType() == ZoneType.no_restart) {
                                if (System.currentTimeMillis() / 1000L - player.getLastAccess() > zone
                                        .getRestartTime()) {
                                    player.sendMessage(new CustomMessage(
                                            "l2next.gameserver.clientpackets.EnterWorld.TeleportedReasonNoRestart",
                                            player));
                                    player.setLoc(TeleportUtils.getRestartLocation(player, RestartType.TO_VILLAGE));
                                }
                            } else if (zone.getType() == ZoneType.SIEGE) {
                                SiegeEvent<?, ?> siegeEvent = player.getEvent(SiegeEvent.class);
                                if (siegeEvent != null) {
                                    player.setLoc(siegeEvent.getEnterLoc(player));
                                } else {
                                    Residence r = ResidenceHolder.getInstance()
                                            .getResidence(zone.getParams().getInteger("residence"));
                                    player.setLoc(r.getNotOwnerRestartPoint(player));
                                }
                            }
                        }
                    }

                    zones.clear();

                    if (DimensionalRiftManager.getInstance().checkIfInRiftZone(player.getLoc(), false)) {
                        player.setLoc(DimensionalRiftManager.getInstance().getRoom(0, 0).getTeleportCoords());
                    }

                }

                player.restoreBlockList();
                player._macroses.restore();

                // FIXME [VISTALL]  ?
                player.refreshExpertisePenalty();
                player.refreshOverloaded();

                player.getWarehouse().restore();
                player.getFreight().restore();

                player.restoreTradeList();
                if (player.getVar("storemode") != null) {
                    player.setPrivateStoreType(Integer.parseInt(player.getVar("storemode")));
                    player.setSitting(true);
                }

                player.updateKetraVarka();
                player.updateRam();
                player.checkRecom();

                if (Config.ALT_VITALITY_ENABLED) {
                    player._vitality.restore();
                }

                //player.getSummonList().restore();
            }
        } catch (final Exception e) {
            _log.error("Could not restore char data!", e);
        } finally {
            DbUtils.closeQuietly(statement2, rset2);
            DbUtils.closeQuietly(statement3, rset3);
            DbUtils.closeQuietly(con, statement, rset);
        }
        return player;
    }

    public static String getVarFromPlayer(int objId, String var) {
        String value = null;
        Connection con = null;
        PreparedStatement offline = null;
        ResultSet rs = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            offline = con.prepareStatement("SELECT value FROM character_variables WHERE obj_id = ? AND name = ?");
            offline.setInt(1, objId);
            offline.setString(2, var);
            rs = offline.executeQuery();
            if (rs.next()) {
                value = Strings.stripSlashes(rs.getString("value"));
            }
        } catch (Exception e) {
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, offline, rs);
        }
        return value;
    }

    // ----------------- End of Quest Engine -------------------

    @SuppressWarnings("unchecked")
    @Override
    public HardReference<Player> getRef() {
        return (HardReference<Player>) super.getRef();
    }

    public String getAccountName() {
        if (_connection == null) {
            return _login;
        }
        return _connection.getLogin();
    }

    public String getIP() {
        if (_connection == null) {
            return NOT_CONNECTED;
        }
        return _connection.getIpAddr();
    }

    /**
     *  ?   ??,  ? ?
     *
     * @return ? 
     */
    public Map<Integer, String> getAccountChars() {
        return _chars;
    }

    @Override
    public final PlayerTemplate getTemplate() {
        return (PlayerTemplate) _template;
    }

    public void changeSex() {
        _template = PlayerTemplateHolder.getInstance().getPlayerTemplate(getRace(), getClassId(),
                Sex.VALUES[getSex()].revert());
    }

    @Override
    public PlayerAI getAI() {
        return (PlayerAI) _ai;
    }

    @Override
    public void doCast(final Skill skill, final Creature target, boolean forceUse) {
        if (skill == null) {
            return;
        }

        super.doCast(skill, target, forceUse);

        // if(getUseSeed() != 0 && skill.getSkillType() == SkillType.SOWING)
        // sendPacket(new ExUseSharedGroupItem(getUseSeed(), getUseSeed(), 5000,
        // 5000));
    }

    @Override
    public void sendReuseMessage(Skill skill) {
        if (isCastingNow()) {
            return;
        }
        TimeStamp sts = getSkillReuse(skill);
        if (sts == null || !sts.hasNotPassed()) {
            return;
        }
        long timeleft = sts.getReuseCurrent();
        if (!Config.ALT_SHOW_REUSE_MSG && timeleft < 10000 || timeleft < 500) {
            return;
        }
        long hours = timeleft / 3600000;
        long minutes = (timeleft - hours * 3600000) / 60000;
        long seconds = (long) Math.ceil((timeleft - hours * 3600000 - minutes * 60000) / 1000.);
        if (hours > 0) {
            sendPacket(new SystemMessage(
                    SystemMessage.THERE_ARE_S2_HOURS_S3_MINUTES_AND_S4_SECONDS_REMAINING_IN_S1S_REUSE_TIME)
                            .addSkillName(skill.getId(), skill.getDisplayLevel()).addNumber(hours)
                            .addNumber(minutes).addNumber(seconds));
        } else if (minutes > 0) {
            sendPacket(new SystemMessage(SystemMessage.THERE_ARE_S2_MINUTES_S3_SECONDS_REMAINING_IN_S1S_REUSE_TIME)
                    .addSkillName(skill.getId(), skill.getDisplayLevel()).addNumber(minutes).addNumber(seconds));
        } else {
            sendPacket(new SystemMessage(SystemMessage.THERE_ARE_S2_SECONDS_REMAINING_IN_S1S_REUSE_TIME)
                    .addSkillName(skill.getId(), skill.getDisplayLevel()).addNumber(seconds));
        }
    }

    @Override
    public final int getLevel() {
        return getActiveSubClass() == null ? 1 : getActiveSubClass().getLevel();
    }

    public int getSex() {
        return getTemplate().getSex().ordinal();
    }

    public int getFace() {
        return _face;
    }

    public void setFace(int face) {
        _face = face;
    }

    public int getHairColor() {
        return _hairColor;
    }

    public void setHairColor(int hairColor) {
        _hairColor = hairColor;
    }

    public int getHairStyle() {
        return _hairStyle;
    }

    public void setHairStyle(int hairStyle) {
        _hairStyle = hairStyle;
    }

    public void offline() {
        if (_connection != null) {
            _connection.setActiveChar(null);
            _connection.close(ServerClose.STATIC);
            setNetConnection(null);
        }

        setNameColor(Config.SERVICES_OFFLINE_TRADE_NAME_COLOR);
        setOfflineMode(true);

        setVar("offline", String.valueOf(System.currentTimeMillis() / 1000L), -1);

        if (Config.SERVICES_OFFLINE_TRADE_SECONDS_TO_KICK > 0) {
            startKickTask(Config.SERVICES_OFFLINE_TRADE_SECONDS_TO_KICK * 1000L);
        }

        Party party = getParty();
        if (party != null) {
            leaveParty();
        }

        Collection<Summon> pets = getPets();
        for (Summon summon : pets) {
            summon.unSummon();
        }

        CursedWeaponsManager.getInstance().doLogout(this);

        if (isInOlympiadMode()) {
            Olympiad.logoutPlayer(this);
        }

        broadcastCharInfo();
        stopWaterTask();
        stopBonusTask();
        stopHourlyTask();
        stopPcBangPointsTask();
        stopAutoSaveTask();
        stopRecomBonusTask(true);
        stopQuestTimers();

        try {
            getInventory().store();
        } catch (Throwable t) {
            _log.error("", t);
        }

        try {
            store(false);
            if (Config.ALT_VITALITY_ENABLED) {
                _vitality.store();
            }
        } catch (Throwable t) {
            _log.error("", t);
        }
    }

    /**
     *  ??,  ??, ? ????  ???  
     * ?  ? NO CARRIER,  
     */
    public void kick() {
        if (_connection != null) {
            _connection.close(LeaveWorld.STATIC);
            setNetConnection(null);
        }
        prepareToLogout();
        deleteMe();
    }

    /**
     *   ??,   ??, ? ????  ???   
     */
    public void restart() {
        if (_connection != null) {
            _connection.setActiveChar(null);
            setNetConnection(null);
        }
        prepareToLogout();
        if (!Config.SERVICES_ENABLE_NO_CARRIER) {
            deleteMe();
        } else {
            scheduleDelete();
        }
    }

    /**
     *  ??,   ??, ? ????  ???  
     *  ? NO CARRIER
     */
    public void logout() {
        if (_connection != null) {
            store(false);
            _connection.close(LeaveWorld.STATIC);
            setNetConnection(null);
        }
        prepareToLogout();
        if (!Config.SERVICES_ENABLE_NO_CARRIER) {
            deleteMe();
        } else {
            scheduleDelete();
        }
    }

    private void prepareToLogout() {
        if (_isLogout.getAndSet(true)) {
            return;
        }
        setNetConnection(null);
        setIsOnline(false);

        getListeners().onExit();

        if (isFlying() && !checkLandingState()) {
            _stablePoint = TeleportUtils.getRestartLocation(this, RestartType.TO_VILLAGE);
        }

        if (isCastingNow()) {
            abortCast(true, true);
        }

        Party party = getParty();
        if (party != null) {
            leaveParty();
        }

        CursedWeaponsManager.getInstance().doLogout(this);

        if (isInObserverMode()) {
            if (getOlympiadObserveGame() != null) {
                leaveObserverMode();
            } else {
                leaveOlympiadObserverMode(true);
            }
            _observerMode.set(OBSERVER_NONE);
        }

        if (isInOlympiadMode() || getOlympiadGame() != null) {
            Olympiad.logoutPlayer(this);
        }

        stopFishing();

        if (_stablePoint != null) {
            teleToLocation(_stablePoint);
        }

        for (Summon pet : getPets()) {
            pet.saveEffects();
            pet.unSummon();
        }

        _friendList.notifyFriends(false);

        if (isProcessingRequest()) {
            getRequest().cancel();
        }

        stopAllTimers();

        if (isInBoat()) {
            getBoat().removePlayer(this);
        }

        SubUnit unit = getSubUnit();
        UnitMember member = unit == null ? null : unit.getUnitMember(getObjectId());
        if (member != null) {
            int sponsor = member.getSponsor();
            int apprentice = getApprentice();
            PledgeShowMemberListUpdate memberUpdate = new PledgeShowMemberListUpdate(this);
            for (Player clanMember : _clan.getOnlineMembers(getObjectId())) {
                clanMember.sendPacket(memberUpdate);
                if (clanMember.getObjectId() == sponsor) {
                    clanMember.sendPacket(
                            new SystemMessage(SystemMessage.S1_YOUR_CLAN_ACADEMYS_APPRENTICE_HAS_LOGGED_OUT)
                                    .addString(_name));
                } else if (clanMember.getObjectId() == apprentice) {
                    clanMember.sendPacket(
                            new SystemMessage(SystemMessage.S1_YOUR_CLAN_ACADEMYS_SPONSOR_HAS_LOGGED_OUT)
                                    .addString(_name));
                }
            }
            member.setPlayerInstance(this, true);
        }

        FlagItemAttachment attachment = getActiveWeaponFlagAttachment();
        if (attachment != null) {
            attachment.onLogout(this);
        }

        if (CursedWeaponsManager.getInstance().getCursedWeapon(getCursedWeaponEquippedId()) != null) {
            CursedWeaponsManager.getInstance().getCursedWeapon(getCursedWeaponEquippedId()).setPlayer(null);
        }

        MatchingRoom room = getMatchingRoom();
        if (room != null) {
            if (room.getLeader() == this) {
                room.disband();
            } else {
                room.removeMember(this, false);
            }
        }
        setMatchingRoom(null);

        MatchingRoomManager.getInstance().removeFromWaitingList(this);

        destroyAllTraps();

        if (_decoy != null) {
            _decoy.unSummon();
            _decoy = null;
        }

        stopPvPFlag();

        Reflection ref = getReflection();

        if (ref != ReflectionManager.DEFAULT) {
            if (ref.getReturnLoc() != null) {
                _stablePoint = ref.getReturnLoc();
            }

            ref.removeObject(this);
        }

        try {
            getInventory().store();
            getRefund().clear();
        } catch (Throwable t) {
            _log.error("", t);
        }

        try {
            store(false);
            if (Config.ALT_VITALITY_ENABLED) {
                _vitality.store();
            }
        } catch (Throwable t) {
            _log.error("", t);
        }
    }

    /**
     * @return a table containing all L2RecipeList of the L2Player.<BR> <BR>
     */
    public Collection<Recipe> getDwarvenRecipeBook() {
        return Collections.unmodifiableCollection(_recipebook.valueCollection());
    }

    public Collection<Recipe> getCommonRecipeBook() {
        return Collections.unmodifiableCollection(_commonrecipebook.valueCollection());
    }

    public boolean hasRecipe(final Recipe id) {
        return _recipebook.containsValue(id) || _commonrecipebook.containsValue(id);
    }

    public boolean findRecipe(final int id) {
        return _recipebook.containsKey(id) || _commonrecipebook.containsKey(id);
    }

    public QuestState getQuestState(String quest) {
        questRead.lock();
        try {
            return _quests.get(quest);
        } finally {
            questRead.unlock();
        }
    }

    public QuestState getQuestState(Class<?> quest) {
        return getQuestState(quest.getSimpleName());
    }

    public boolean isQuestCompleted(String quest) {
        QuestState q = getQuestState(quest);
        return q != null && q.isCompleted();
    }

    public boolean isQuestCompleted(Class<?> quest) {
        QuestState q = getQuestState(quest);
        return q != null && q.isCompleted();
    }

    public void setQuestState(QuestState qs) {
        questWrite.lock();
        try {
            _quests.put(qs.getQuest().getName(), qs);
        } finally {
            questWrite.unlock();
        }
    }

    public void removeQuestState(String quest) {
        questWrite.lock();
        try {
            _quests.remove(quest);
        } finally {
            questWrite.unlock();
        }
    }

    public Quest[] getAllActiveQuests() {
        List<Quest> quests = new ArrayList<Quest>(_quests.size());
        questRead.lock();
        try {
            for (final QuestState qs : _quests.values()) {
                if (qs.isStarted()) {
                    quests.add(qs.getQuest());
                }
            }
        } finally {
            questRead.unlock();
        }
        return quests.toArray(new Quest[quests.size()]);
    }

    public QuestState[] getAllQuestsStates() {
        questRead.lock();
        try {
            return _quests.values().toArray(new QuestState[_quests.size()]);
        } finally {
            questRead.unlock();
        }
    }

    public List<QuestState> getQuestsForEvent(NpcInstance npc, QuestEventType event, boolean forNpcQuestList) {
        List<QuestState> states = new ArrayList<QuestState>();
        Quest[] quests = npc.getTemplate().getEventQuests(event);
        QuestState qs;
        if (quests != null) {
            for (Quest quest : quests) {
                qs = getQuestState(quest.getName());
                if (forNpcQuestList || qs != null && !qs.isCompleted()) {
                    if (forNpcQuestList && qs == null) {
                        qs = quest.newQuestStateAndNotSave(this, Quest.CREATED);
                    }
                    states.add(qs);
                }
            }
        }
        return states;
    }

    public void processQuestEvent(String quest, String event, NpcInstance npc) {
        if (event == null) {
            event = "";
        }
        QuestState qs = getQuestState(quest);
        if (qs == null) {
            Quest q = QuestManager.getQuest(quest);
            if (q == null) {
                _log.warn("Quest " + quest + " not found!");
                return;
            }
            qs = q.newQuestState(this, Quest.CREATED);
        }
        if (qs == null || qs.isCompleted()) {
            return;
        }
        qs.getQuest().notifyEvent(event, qs, npc);
        sendPacket(new QuestList(this));
    }

    /**
     * ?   ?     ? ?  
     *
     * @return true   ?  
     */
    public boolean isQuestContinuationPossible(boolean msg) {
        if (getWeightPenalty() >= 3 || getInventoryLimit() * 0.9 < getInventory().getSize()
                || Config.QUEST_INVENTORY_MAXIMUM * 0.9 < getInventory().getQuestSize()) {
            if (msg) {
                sendPacket(
                        Msg.PROGRESS_IN_A_QUEST_IS_POSSIBLE_ONLY_WHEN_YOUR_INVENTORYS_WEIGHT_AND_VOLUME_ARE_LESS_THAN_80_PERCENT_OF_CAPACITY);
            }
            return false;
        }
        return true;
    }

    /**
     *     ? 
     */
    public void stopQuestTimers() {
        for (QuestState qs : getAllQuestsStates()) {
            if (qs.isStarted()) {
                qs.pauseQuestTimers();
            } else {
                qs.stopQuestTimers();
            }
        }
    }

    /**
     *   ? 
     */
    public void resumeQuestTimers() {
        for (QuestState qs : getAllQuestsStates()) {
            qs.resumeQuestTimers();
        }
    }

    public Collection<ShortCut> getAllShortCuts() {
        return _shortCuts.getAllShortCuts();
    }

    public ShortCut getShortCut(int slot, int page) {
        return _shortCuts.getShortCut(slot, page);
    }

    public void registerShortCut(ShortCut shortcut) {
        _shortCuts.registerShortCut(shortcut);
    }

    public void deleteShortCut(int slot, int page) {
        _shortCuts.deleteShortCut(slot, page);
    }

    public void registerMacro(Macro macro) {
        _macroses.registerMacro(macro);
    }

    public void deleteMacro(int id) {
        _macroses.deleteMacro(id);
    }

    public MacroList getMacroses() {
        return _macroses;
    }

    public boolean isCastleLord(int castleId) {
        return _clan != null && isClanLeader() && _clan.getCastle() == castleId;
    }

    /**
     * ? ???     ?
     *
     * @param fortressId
     * @return true  
     */
    public boolean isFortressLord(int fortressId) {
        return _clan != null && isClanLeader() && _clan.getHasFortress() == fortressId;
    }

    public int getPkKills() {
        return _pkKills;
    }

    public void setPkKills(final int pkKills) {
        _pkKills = pkKills;
    }

    public long getCreateTime() {
        return _createTime;
    }

    public void setCreateTime(final long createTime) {
        _createTime = createTime;
    }

    public int getDeleteTimer() {
        return _deleteTimer;
    }

    public void setDeleteTimer(final int deleteTimer) {
        _deleteTimer = deleteTimer;
    }

    public int getCurrentLoad() {
        return getInventory().getTotalWeight();
    }

    public long getLastAccess() {
        return _lastAccess;
    }

    public void setLastAccess(long value) {
        _lastAccess = value;
    }

    public int getRecomHave() {
        return _recomHave;
    }

    public void setRecomHave(int value) {
        if (value > 255) {
            _recomHave = 255;
        } else if (value < 0) {
            _recomHave = 0;
        } else {
            _recomHave = value;
        }
    }

    public int getRecomBonusTime() {
        if (_recomBonusTask != null) {
            return (int) Math.max(0, _recomBonusTask.getDelay(TimeUnit.SECONDS));
        }
        return _recomBonusTime;
    }

    public void setRecomBonusTime(int val) {
        _recomBonusTime = val;
    }

    public int getRecomLeft() {
        return _recomLeft;
    }

    public void setRecomLeft(final int value) {
        _recomLeft = value;
    }

    public boolean isHourglassEffected() {
        return _isHourglassEffected;
    }

    public void setHourlassEffected(boolean val) {
        _isHourglassEffected = val;
    }

    public void startHourglassEffect() {
        setHourlassEffected(true);
        stopRecomBonusTask(true);
        sendVoteSystemInfo();
    }

    public void stopHourglassEffect() {
        setHourlassEffected(false);
        startRecomBonusTask();
        sendVoteSystemInfo();
    }

    public int addRecomLeft() {
        int recoms = 0;
        if (getRecomLeftToday() < 20) {
            recoms = 10;
        } else {
            recoms = 1;
        }
        setRecomLeft(getRecomLeft() + recoms);
        setRecomLeftToday(getRecomLeftToday() + recoms);
        sendUserInfo(true);
        return recoms;
    }

    public int getRecomLeftToday() {
        return _recomLeftToday;
    }

    public void setRecomLeftToday(final int value) {
        _recomLeftToday = value;
        setVar("recLeftToday", String.valueOf(_recomLeftToday), -1);
    }

    public void giveRecom(final Player target) {
        int targetRecom = target.getRecomHave();
        if (targetRecom < 255) {
            target.addRecomHave(1);
        }
        if (getRecomLeft() > 0) {
            setRecomLeft(getRecomLeft() - 1);
        }

        sendUserInfo(true);
    }

    public void addRecomHave(final int val) {
        setRecomHave(getRecomHave() + val);
        broadcastUserInfo(true);
        sendVoteSystemInfo();
    }

    public int getRecomBonus() {
        if (getRecomBonusTime() > 0 || isHourglassEffected()) {
            return RecomBonus.getRecoBonus(this);
        }
        return 0;
    }

    public double getRecomBonusMul() {
        if (getRecomBonusTime() > 0 || isHourglassEffected()) {
            return RecomBonus.getRecoMultiplier(this);
        }
        return 1;
    }

    public void sendVoteSystemInfo() {
        sendPacket(new ExVoteSystemInfo(this));
    }

    public boolean isRecomTimerActive() {
        return _isRecomTimerActive;
    }

    public void setRecomTimerActive(boolean val) {
        if (_isRecomTimerActive == val) {
            return;
        }

        _isRecomTimerActive = val;

        if (val) {
            startRecomBonusTask();
        } else {
            stopRecomBonusTask(true);
        }

        sendVoteSystemInfo();
    }

    public void startRecomBonusTask() {
        if (_recomBonusTask == null && getRecomBonusTime() > 0 && isRecomTimerActive() && !isHourglassEffected()) {
            _recomBonusTask = ThreadPoolManager.getInstance().schedule(new RecomBonusTask(this),
                    getRecomBonusTime() * 1000);
        }
    }

    public void stopRecomBonusTask(boolean saveTime) {
        if (_recomBonusTask != null) {
            if (saveTime) {
                setRecomBonusTime((int) Math.max(0, _recomBonusTask.getDelay(TimeUnit.SECONDS)));
            }
            _recomBonusTask.cancel(false);
            _recomBonusTask = null;
        }
    }

    @Override
    public int getKarma() {
        return _karma;
    }

    public void setKarma(int karma) {
        if (_karma == karma) {
            return;
        }

        _karma = karma;

        sendChanges();

        for (Summon summon : getPets()) {
            summon.broadcastCharInfo();
        }
    }

    @Override
    public int getMaxLoad() {
        // Weight Limit = (CON Modifier*69000)*Skills
        // Source http://l2p.bravehost.com/weightlimit.html (May 2007)
        // Fitted exponential curve to the data
        int con = getCON();
        if (con < 1) {
            return (int) (31000 * Config.MAXLOAD_MODIFIER);
        } else if (con > 59) {
            return (int) (176000 * Config.MAXLOAD_MODIFIER);
        } else {
            return (int) calcStat(Stats.MAX_LOAD,
                    Math.pow(1.029993928, con) * 30495.627366 * Config.MAXLOAD_MODIFIER, this, null);
        }
    }

    @Override
    public void updateEffectIcons() {
        if (entering || isLogoutStarted()) {
            return;
        }

        if (Config.USER_INFO_INTERVAL == 0) {
            if (_updateEffectIconsTask != null) {
                _updateEffectIconsTask.cancel(false);
                _updateEffectIconsTask = null;
            }
            updateEffectIconsImpl();
            return;
        }

        if (_updateEffectIconsTask != null) {
            return;
        }

        _updateEffectIconsTask = ThreadPoolManager.getInstance().schedule(new UpdateEffectIcons(),
                Config.USER_INFO_INTERVAL);
    }

    private void updateEffectIconsImpl() {
        Effect[] effects = getEffectList().getAllFirstEffects();
        Arrays.sort(effects, EffectsComparator.getInstance());

        PartySpelled ps = new PartySpelled(this, false);
        AbnormalStatusUpdate mi = new AbnormalStatusUpdate();

        for (Effect effect : effects) {
            if (effect.isInUse()) {
                if (effect.getStackType().contains(EffectTemplate.HP_RECOVER_CAST)) {
                    sendPacket(new ShortBuffStatusUpdate(effect));
                } else {
                    effect.addIcon(mi);
                }
                if (_party != null) {
                    effect.addIcon(ps);
                }
            }
        }

        sendPacket(mi);
        if (_party != null) {
            _party.broadCast(ps);
        }

        if (isInOlympiadMode() && isOlympiadCompStart()) {
            OlympiadGame olymp_game = _olympiadGame;
            if (olymp_game != null) {
                ExOlympiadSpelledInfo olympiadSpelledInfo = new ExOlympiadSpelledInfo();

                for (Effect effect : effects) {
                    if (effect != null && effect.isInUse()) {
                        effect.addOlympiadSpelledIcon(this, olympiadSpelledInfo);
                    }
                }

                if (olymp_game.getType() == CompType.CLASSED || olymp_game.getType() == CompType.NON_CLASSED) {
                    for (Player member : olymp_game.getTeamMembers(this)) {
                        member.sendPacket(olympiadSpelledInfo);
                    }
                }

                for (Player member : olymp_game.getSpectators()) {
                    member.sendPacket(olympiadSpelledInfo);
                }
            }
        }
    }

    public int getWeightPenalty() {
        return getSkillLevel(4270, 0);
    }

    public void refreshOverloaded() {
        if (isLogoutStarted() || getMaxLoad() <= 0) {
            return;
        }

        setOverloaded(getCurrentLoad() > getMaxLoad());
        double weightproc = 100. * (getCurrentLoad() - calcStat(Stats.MAX_NO_PENALTY_LOAD, 0, this, null))
                / getMaxLoad();
        int newWeightPenalty = 0;

        if (weightproc < 50) {
            newWeightPenalty = 0;
        } else if (weightproc < 66.6) {
            newWeightPenalty = 1;
        } else if (weightproc < 80) {
            newWeightPenalty = 2;
        } else if (weightproc < 100) {
            newWeightPenalty = 3;
        } else {
            newWeightPenalty = 4;
        }

        int current = getWeightPenalty();
        if (current == newWeightPenalty) {
            return;
        }

        if (newWeightPenalty > 0) {
            super.addSkill(SkillTable.getInstance().getInfo(4270, newWeightPenalty));
        } else {
            super.removeSkill(getKnownSkill(4270));
        }

        sendSkillList();

        sendEtcStatusUpdate();
        updateStats();
    }

    public int getArmorsExpertisePenalty() {
        return getSkillLevel(6213, 0);
    }

    public int getWeaponsExpertisePenalty() {
        return getSkillLevel(6209, 0);
    }

    public int getExpertisePenalty(ItemInstance item) {
        if (item.getTemplate().getType2() == ItemTemplate.TYPE2_WEAPON) {
            return getWeaponsExpertisePenalty();
        } else if (item.getTemplate().getType2() == ItemTemplate.TYPE2_SHIELD_ARMOR
                || item.getTemplate().getType2() == ItemTemplate.TYPE2_ACCESSORY) {
            return getArmorsExpertisePenalty();
        }
        return 0;
    }

    public void refreshExpertisePenalty() {
        if (isLogoutStarted()) {
            return;
        }

        // Calculate the current higher Expertise of the L2Player
        int level = (int) calcStat(Stats.GRADE_EXPERTISE_LEVEL, getLevel(), null, null);
        int i = 0;
        for (i = 0; i < EXPERTISE_LEVELS.length; i++) {
            if (level < EXPERTISE_LEVELS[i + 1]) {
                break;
            }
        }

        boolean skillUpdate = false; // ? ,     
        // ?
        // Add the Expertise skill corresponding to its Expertise level
        if (expertiseIndex != i) {
            expertiseIndex = i;
            if (expertiseIndex > 0) {
                addSkill(SkillTable.getInstance().getInfo(239, expertiseIndex), false);
                skillUpdate = true;
            }
        }

        int newWeaponPenalty = 0;
        int newArmorPenalty = 0;
        ItemInstance[] items = getInventory().getPaperdollItems();
        for (ItemInstance item : items) {
            if (item != null) {
                int crystaltype = item.getTemplate().getCrystalType().ordinal();
                if (item.getTemplate().getType2() == ItemTemplate.TYPE2_WEAPON) {
                    if (crystaltype > newWeaponPenalty) {
                        newWeaponPenalty = crystaltype;
                    }
                } else if (item.getTemplate().getType2() == ItemTemplate.TYPE2_SHIELD_ARMOR
                        || item.getTemplate().getType2() == ItemTemplate.TYPE2_ACCESSORY) {
                    if (crystaltype > newArmorPenalty) {
                        newArmorPenalty = crystaltype;
                    }
                }
            }
        }

        newWeaponPenalty = newWeaponPenalty - expertiseIndex;
        if (newWeaponPenalty <= 0) {
            newWeaponPenalty = 0;
        } else if (newWeaponPenalty >= 4) {
            newWeaponPenalty = 4;
        }

        newArmorPenalty = newArmorPenalty - expertiseIndex;
        if (newArmorPenalty <= 0) {
            newArmorPenalty = 0;
        } else if (newArmorPenalty >= 4) {
            newArmorPenalty = 4;
        }

        int weaponExpertise = getWeaponsExpertisePenalty();
        int armorExpertise = getArmorsExpertisePenalty();

        if (weaponExpertise != newWeaponPenalty) {
            weaponExpertise = newWeaponPenalty;
            if (newWeaponPenalty > 0) {
                super.addSkill(SkillTable.getInstance().getInfo(6209, weaponExpertise));
            } else {
                super.removeSkill(getKnownSkill(6209));
            }
            skillUpdate = true;
        }
        if (armorExpertise != newArmorPenalty) {
            armorExpertise = newArmorPenalty;
            if (newArmorPenalty > 0) {
                super.addSkill(SkillTable.getInstance().getInfo(6213, armorExpertise));
            } else {
                super.removeSkill(getKnownSkill(6213));
            }
            skillUpdate = true;
        }

        if (skillUpdate) {
            getInventory().validateItemsSkills();

            sendSkillList();
            sendEtcStatusUpdate();
            updateStats();
        }
    }

    public int getPvpKills() {
        return _pvpKills;
    }

    public void setPvpKills(int pvpKills) {
        _pvpKills = pvpKills;
    }

    public void addClanPointsOnProfession(final int id) {
        if (getLvlJoinedAcademy() != 0 && _clan != null && _clan.getLevel() >= 5) {
            int earnedPoints = (85 - getLvlJoinedAcademy()) * 45 + 200;
            if (earnedPoints > 2000) {
                earnedPoints = 2000;
            }

            _clan.removeClanMember(getObjectId());

            SystemMessage sm = new SystemMessage(
                    SystemMessage.CLAN_ACADEMY_MEMBER_S1_HAS_SUCCESSFULLY_COMPLETED_THE_2ND_CLASS_TRANSFER_AND_OBTAINED_S2_CLAN_REPUTATION_POINTS);
            sm.addString(getName());
            sm.addNumber(_clan.incReputation(earnedPoints, true, "Academy"));
            _clan.broadcastToOnlineMembers(sm);
            _clan.broadcastToOtherOnlineMembers(new PledgeShowMemberListDelete(getName()), this);

            setClan(null);
            setTitle("");
            sendPacket(
                    Msg.CONGRATULATIONS_YOU_WILL_NOW_GRADUATE_FROM_THE_CLAN_ACADEMY_AND_LEAVE_YOUR_CURRENT_CLAN_AS_A_GRADUATE_OF_THE_ACADEMY_YOU_CAN_IMMEDIATELY_JOIN_A_CLAN_AS_A_REGULAR_MEMBER_WITHOUT_BEING_SUBJECT_TO_ANY_PENALTIES);
            setLeaveClanTime(0);

            broadcastCharInfo();

            sendPacket(PledgeShowMemberListDeleteAll.STATIC);

            // ItemFunctions.addItem(this, 8181, 1, true);
        }
    }

    /**
     * Set the template of the L2Player.
     *
     * @param id The Identifier of the L2PlayerTemplate to set to the L2Player
     */
    public synchronized void setClassId(final int id, boolean noban, boolean fromQuest) {
        ClassId classId = ClassId.VALUES[id];
        if (!noban && !(ClassId.VALUES[id].equalsOrChildOf(ClassId.VALUES[getActiveClassId()])
                || getPlayerAccess().CanChangeClass || Config.EVERYBODY_HAS_ADMIN_RIGHTS)) {
            Thread.dumpStack();
            return;
        }

        ClassId oldClassId = getClassId();
        //   ID   ? ?   ? 
        if (!_subClassList.containsClassId(id)) {
            final SubClass cclass = getActiveSubClass();
            final int oldClass = cclass.getClassId();
            _subClassList.changeSubClassId(oldClass, id);
            changeClassInDb(oldClass, id);
            if (cclass.isBase()) {
                if (getClassId().isOfLevel(ClassLevel.Awaking) && getVar("wasInAcademy") == null) {
                    addClanPointsOnProfession(id); //? ,   ,    .
                    setVar("wasInAcademy", "true", -1L);
                }
            }

            //  Holy Pomander
            switch (classId) {
            case CARDINAL:
                ItemFunctions.addItem(this, 15307, 7, true);
                break;
            case EVAS_SAINT:
                ItemFunctions.addItem(this, 15308, 7, true);
                break;
            case SHILLIEN_SAINT:
                ItemFunctions.addItem(this, 15309, 7, true);
                break;
            default:
                break;
            }

            rewardSkills(true);
            storeCharSubClasses();
            if (fromQuest) {
                broadcastPacket(new MagicSkillUse(this, this, 5103, 1, 1000, 0));
                broadcastPacket(new SocialAction(getObjectId(), 3));
                sendPacket(new PlaySound("ItemSound.quest_fanfare_2"));
            }
            broadcastCharInfo();
        }

        if (oldClassId == null || !oldClassId.isOfRace(getClassId().getRace())
                || !oldClassId.isOfType(getClassId().getType())) {
            PlayerTemplate t = PlayerTemplateHolder.getInstance().getPlayerTemplate(getRace(), classId,
                    Sex.VALUES[getSex()]);
            if (t == null) {
                _log.error("Missing template for classId: " + id);
                return;
            }
            _template = t;
        }

        // Update class icon in party and clan
        if (isInParty()) {
            getParty().broadCast(new PartySmallWindowUpdate(this));
        }
        if (getClan() != null) {
            getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
        }
        if (_matchingRoom != null) {
            _matchingRoom.broadcastPlayerUpdate(this);
        }

        sendPacket(new ExSubjobInfo(this, true));
    }

    public long getExp() {
        return getActiveSubClass() == null ? 0 : getActiveSubClass().getExp();
    }

    public long getMaxExp() {
        return getActiveSubClass() == null ? Experience.LEVEL[Experience.getMaxLevel() + 1]
                : getActiveSubClass().getMaxExp();
    }

    public ItemInstance getEnchantScroll() {
        return _enchantScroll;
    }

    public void setEnchantScroll(final ItemInstance scroll) {
        _enchantScroll = scroll;
    }

    public void setAppearanceStone(final ItemInstance enchantItem) {
        _enchantItem = enchantItem;
    }

    public ItemInstance getAppearanceStone() {
        return _enchantItem;
    }

    public void setAppearanceExtractItem(final ItemInstance supportItem) {
        _enchantSupportItem = supportItem;
    }

    public ItemInstance getAppearanceExtractItem() {
        return _enchantSupportItem;
    }

    public WeaponTemplate getFistsWeaponItem() {
        return _fistsWeaponItem;
    }

    public void setFistsWeaponItem(final WeaponTemplate weaponItem) {
        _fistsWeaponItem = weaponItem;
    }

    public WeaponTemplate findFistsWeaponItem(final int classId) {
        // human fighter fists
        if (classId >= 0x00 && classId <= 0x09) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(246);
        }

        // human mage fists
        if (classId >= 0x0a && classId <= 0x11) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(251);
        }

        // elven fighter fists
        if (classId >= 0x12 && classId <= 0x18) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(244);
        }

        // elven mage fists
        if (classId >= 0x19 && classId <= 0x1e) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(249);
        }

        // dark elven fighter fists
        if (classId >= 0x1f && classId <= 0x25) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(245);
        }

        // dark elven mage fists
        if (classId >= 0x26 && classId <= 0x2b) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(250);
        }

        // orc fighter fists
        if (classId >= 0x2c && classId <= 0x30) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(248);
        }

        // orc mage fists
        if (classId >= 0x31 && classId <= 0x34) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(252);
        }

        // dwarven fists
        if (classId >= 0x35 && classId <= 0x39) {
            return (WeaponTemplate) ItemHolder.getInstance().getTemplate(247);
        }

        return null;
    }

    public void addExpAndCheckBonus(MonsterInstance mob, final double noRateExp, double noRateSp,
            double partyVitalityMod) {
        if (getActiveSubClass() == null) {
            return;
        }

        //   ??
        double neededExp = calcStat(Stats.SOULS_CONSUME_EXP, 0, mob, null);
        if (neededExp > 0 && noRateExp > neededExp) {
            mob.broadcastPacket(new SpawnEmitter(mob, this));
            ThreadPoolManager.getInstance().schedule(new GameObjectTasks.SoulConsumeTask(this), 1000);
        }

        int npcLevel = mob.getLevel();

        //  ?   
        double ExpMod = _vitality.getExpMod();
        double spMod = _vitality.getSpMod();

        //   , ?  .
        if (!isInPeaceZone()) {
            setRecomTimerActive(true);
        }

        // ?? 
        if (getVarB("NoExp")) {
            return;
        }

        // ?, ? ?  ?  .
        //  ?     ??.
        if (getEffectList().getEffectByType(EffectType.VitalityFreeze) == null) {
            double points = ((noRateExp / (npcLevel * npcLevel)) * 100) / 9;
            points *= Config.ALT_VITALITY_CONSUME_RATE;

            if (getEffectList().getEffectByType(EffectType.Vitality) != null) {
                _vitality.incPoints((int) (points * partyVitalityMod));
            }

            else {
                _vitality.decPoints((int) (points * partyVitalityMod));
            }

        }
        long normalExp = (long) (noRateExp * ((Config.RATE_XP * getRateExp() + ExpMod) * getRecomBonusMul()));
        long normalSp = (long) (noRateSp * (Config.RATE_SP * getRateSp() + spMod));

        long expWithoutBonus = (long) (noRateExp * Config.RATE_XP * getRateExp());
        long spWithoutBonus = (long) (noRateSp * Config.RATE_SP * getRateSp());

        addExpAndSp(normalExp, normalSp, normalExp - expWithoutBonus, normalSp - spWithoutBonus, false, true);

        // TODO:     MonsterInstance
        VitalityBooty.rewardPlayer(this, mob);
    }

    @Override
    public void addExpAndSp(long exp, long sp) {
        addExpAndSp(exp, sp, 0, 0, false, false);
    }

    public void addExpAndSp(long addToExp, long addToSp, long bonusAddExp, long bonusAddSp, boolean applyRate,
            boolean applyToPet) {
        if (getActiveSubClass() == null) {
            return;
        }
        if (applyRate) {
            addToExp *= Config.RATE_XP * getRateExp();
            addToSp *= Config.RATE_SP * getRateSp();
        }
        if (addToExp > 0) {
            if (applyToPet) {
                for (Summon pet : getPets()) {
                    if (pet != null && !pet.isDead() && !PetDataTable.isVitaminPet(pet.getNpcId())) {
                        if (pet.getNpcId() == PetDataTable.SIN_EATER_ID) {
                            pet.addExpAndSp(addToExp, 0);
                            addToExp = 0;
                        } else if (pet.isPet() && pet.getExpPenalty() > 0f) {
                            if (pet.getLevel() > getLevel() - 20 && pet.getLevel() < getLevel() + 5) {
                                pet.addExpAndSp((long) (addToExp * pet.getExpPenalty()), 0);
                                addToExp *= 1. - pet.getExpPenalty();
                            } else {
                                pet.addExpAndSp((long) (addToExp * pet.getExpPenalty() / 5.), 0);
                                addToExp *= 1. - pet.getExpPenalty() / 5.;
                            }
                        } else if (pet.isSummon()) {
                            addToExp *= 1. - pet.getExpPenalty();
                        }
                    }
                }
            }

            if (!isCursedWeaponEquipped() && addToSp > 0L && _karma < 0) {
                _karma = (int) (_karma + addToSp / (Config.KARMA_SP_DIVIDER * Config.RATE_SP));
                addToExp = 0;
                addToSp = 0;
            }

            long max_xp = getVarB("NoExp") ? l2next.gameserver.model.base.Experience.LEVEL[getLevel() + 1] - 1L
                    : getMaxExp();
            addToExp = Math.min(addToExp, max_xp - getExp());
        }

        long max_exp = !this.isAwaking() ? Experience.LEVEL[getLevel() + 1] - 1L : getMaxExp();

        int oldLvl = getActiveSubClass().getLevel();

        if (oldLvl == 85 && getExp() == max_exp) {
            return;
        }

        getActiveSubClass().addExp(addToExp);
        getActiveSubClass().addSp(addToSp);

        if (addToExp > 0L && addToSp > 0L && (bonusAddExp > 0L || bonusAddSp > 0L)) {
            sendPacket(new SystemMessage2(SystemMsg.YOU_HAVE_ACQUIRED_S1_EXP_BONUS_S2_AND_S3_SP_BONUS_S4)
                    .addLong(addToExp).addLong(bonusAddExp).addInteger(addToSp).addInteger((int) bonusAddSp));
        } else if (addToSp > 0L && addToExp == 0L) {
            sendPacket(new SystemMessage(331).addNumber(addToSp));
        } else if (addToSp > 0L && addToExp > 0L) {
            sendPacket(new SystemMessage(95).addNumber(addToExp).addNumber(addToSp));
        } else if (addToSp == 0L && addToExp > 0L) {
            sendPacket(new SystemMessage(45).addNumber(addToExp));
        }
        int level = getActiveSubClass().getLevel();

        if (level != oldLvl) {
            int levels = level - oldLvl;
            levelSet(levels);
        }

        for (Summon pet : getPets()) {
            if (pet != null && pet.isPet() && PetDataTable.isVitaminPet(pet.getNpcId())) {
                PetInstance _pet = (PetInstance) pet;
                _pet.setLevel(getLevel());
                _pet.setExp(_pet.getExpForNextLevel());
                _pet.broadcastStatusUpdate();
            }
        }
        WorldStatisticsManager.getInstance().updateStat(this, CategoryType.EXP_ADDED, addToExp);
        updateStats();
    }

    /**
     * Give Expertise skill of this level.<BR> <BR> <B><U> Actions</U> :</B><BR> <BR> <li>Get the Level of the
     * L2Player</li> <li>Add the Expertise skill corresponding to its Expertise level</li> <li> Update the overloaded
     * status of the L2Player</li><BR> <BR>
     * <p/>
     * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T give other free skills (SP needed =
     * 0)</B></FONT><BR> <BR>
     *
     * @param send
     */
    public void rewardSkills(boolean send) {
        boolean update = false;
        if (Config.AUTO_LEARN_SKILLS) {
            int unLearnable = 0;
            Collection<SkillLearn> skills = SkillAcquireHolder.getInstance().getAvailableSkills(this,
                    AcquireType.NORMAL);
            while (skills.size() > unLearnable) {
                unLearnable = 0;
                for (SkillLearn s : skills) {
                    Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
                    if (sk == null || !sk.getCanLearn(getClassId())
                            || !Config.AUTO_LEARN_FORGOTTEN_SKILLS && s.isClicked()) {
                        unLearnable++;
                        continue;
                    }
                    addSkill(sk, true);
                }
                skills = SkillAcquireHolder.getInstance().getAvailableSkills(this, AcquireType.NORMAL);
            }
            update = true;
        } else {
            Collection<SkillLearn> availableSkills = SkillAcquireHolder.getInstance().getAvailableSkills(this,
                    AcquireType.NORMAL);
            // ? ?    ?
            for (SkillLearn skill : availableSkills) {
                if (skill.getCost() == 0 && skill.getItemId() == 0) {
                    Skill sk = SkillTable.getInstance().getInfo(skill.getId(), skill.getLevel());
                    addSkill(sk, true);
                    if (getAllShortCuts().size() > 0 && sk.getLevel() > 1) {
                        for (ShortCut sc : getAllShortCuts()) {
                            if (sc.getId() == sk.getId() && sc.getType() == ShortCut.TYPE_SKILL) {
                                ShortCut newsc = new ShortCut(sc.getSlot(), sc.getPage(), sc.getType(), sc.getId(),
                                        sk.getLevel(), 1);
                                sendPacket(new ShortCutRegister(this, newsc));
                                registerShortCut(newsc);
                            }
                        }
                    }
                    update = true;
                }
            }
            if (availableSkills.size() > 0) {
                sendPacket(new ExNewSkillToLearnByLevelUp());
            }
        }
        if (send && update) {
            sendSkillList();
        }

        updateStats();
    }

    public Race getRace() {
        return getTemplate().getRace();
    }

    public int getIntSp() {
        return (int) getSp();
    }

    public long getSp() {
        return getActiveSubClass() == null ? 0 : getActiveSubClass().getSp();
    }

    public void setSp(long sp) {
        if (getActiveSubClass() != null) {
            getActiveSubClass().setSp(sp);
        }
    }

    public int getClanId() {
        return _clan == null ? 0 : _clan.getClanId();
    }

    public long getLeaveClanTime() {
        return _leaveClanTime;
    }

    public void setLeaveClanTime(final long time) {
        _leaveClanTime = time;
    }

    public long getDeleteClanTime() {
        return _deleteClanTime;
    }

    public void setDeleteClanTime(final long time) {
        _deleteClanTime = time;
    }

    public long getOnlineTime() {
        return _onlineTime;
    }

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

    public long getOnlineBeginTime() {
        return _onlineBeginTime;
    }

    public long getNoChannel() {
        return _NoChannel;
    }

    public void setNoChannel(final long time) {
        _NoChannel = time;
        if (_NoChannel > 2145909600000L || _NoChannel < 0) {
            _NoChannel = -1;
        }

        if (_NoChannel > 0) {
            _NoChannelBegin = System.currentTimeMillis();
        } else {
            _NoChannelBegin = 0;
        }
    }

    public long getNoChannelRemained() {
        if (_NoChannel == 0) {
            return 0;
        } else if (_NoChannel < 0) {
            return -1;
        } else {
            long remained = _NoChannel - System.currentTimeMillis() + _NoChannelBegin;
            if (remained < 0) {
                return 0;
            }

            return remained;
        }
    }

    public void setLeaveClanCurTime() {
        _leaveClanTime = System.currentTimeMillis();
    }

    public void setDeleteClanCurTime() {
        _deleteClanTime = System.currentTimeMillis();
    }

    public boolean canJoinClan() {
        if (_leaveClanTime == 0) {
            return true;
        }
        if (System.currentTimeMillis() - _leaveClanTime >= 24 * 60 * 60 * 1000L) {
            _leaveClanTime = 0;
            return true;
        }
        return false;
    }

    public boolean canCreateClan() {
        if (_deleteClanTime == 0) {
            return true;
        }
        if (System.currentTimeMillis() - _deleteClanTime >= 10 * 24 * 60 * 60 * 1000L) {
            _deleteClanTime = 0;
            return true;
        }
        return false;
    }

    public IStaticPacket canJoinParty(Player inviter) {
        Request request = getRequest();
        if (request != null && request.isInProgress() && request.getOtherPlayer(this) != inviter) {
            return SystemMsg.WAITING_FOR_ANOTHER_REPLY.packet(inviter); // ?
        }
        if (isBlockAll() || getMessageRefusal()) //  
        {
            return SystemMsg.THAT_PERSON_IS_IN_MESSAGE_REFUSAL_MODE.packet(inviter);
        }
        if (isInParty()) // 
        {
            return new SystemMessage2(SystemMsg.C1_IS_A_MEMBER_OF_ANOTHER_PARTY_AND_CANNOT_BE_INVITED)
                    .addName(this);
        }
        if (inviter.getReflection() != getReflection()) //   
        {
            if (inviter.getReflection() != ReflectionManager.DEFAULT
                    && getReflection() != ReflectionManager.DEFAULT) {
                return SystemMsg.INVALID_TARGET.packet(inviter);
            }
        }
        if (isCursedWeaponEquipped() || inviter.isCursedWeaponEquipped()) // 
        {
            return SystemMsg.INVALID_TARGET.packet(inviter);
        }
        if (inviter.isInOlympiadMode() || isInOlympiadMode()) // 
        {
            return SystemMsg.A_USER_CURRENTLY_PARTICIPATING_IN_THE_OLYMPIAD_CANNOT_SEND_PARTY_AND_FRIEND_INVITATIONS
                    .packet(inviter);
        }
        if (!inviter.getPlayerAccess().CanJoinParty || !getPlayerAccess().CanJoinParty) // ?
        {
            return SystemMsg.INVALID_TARGET.packet(inviter);
        }
        if (getTeam() != TeamType.NONE) // ?    
        {
            return SystemMsg.INVALID_TARGET.packet(inviter);
        }
        return null;
    }

    @Override
    public PcInventory getInventory() {
        return _inventory;
    }

    @Override
    public long getWearedMask() {
        return _inventory.getWearedMask();
    }

    public PcFreight getFreight() {
        return _freight;
    }

    public void removeItemFromShortCut(final int objectId) {
        _shortCuts.deleteShortCutByObjectId(objectId);
    }

    public void removeSkillFromShortCut(final int skillId) {
        _shortCuts.deleteShortCutBySkillId(skillId);
    }

    public boolean isSitting() {
        return _isSitting;
    }

    public void setSitting(boolean val) {
        _isSitting = val;
    }

    public boolean getSittingTask() {
        return sittingTaskLaunched;
    }

    @Override
    public void sitDown(StaticObjectInstance throne) {
        if (isSitting() || sittingTaskLaunched || isAlikeDead()) {
            return;
        }

        if (isStunned() || isSleeping() || isParalyzed() || isAttackingNow() || isCastingNow() || isMoving) {
            getAI().setNextAction(nextAction.REST, null, null, false, false);
            return;
        }

        resetWaitSitTime();
        getAI().setIntention(CtrlIntention.AI_INTENTION_REST, null, null);

        if (throne == null) {
            broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING));
        } else {
            broadcastPacket(new ChairSit(this, throne));
        }

        _sittingObject = throne;
        setSitting(true);
        sittingTaskLaunched = true;
        ThreadPoolManager.getInstance().schedule(new EndSitDownTask(this), 2500);
    }

    @Override
    public void standUp() {
        if (!isSitting() || sittingTaskLaunched || isInStoreMode() || isAlikeDead()) {
            return;
        }

        // FIXME [G1ta0] ?  ??  ? ?,  
        //  ,   
        getEffectList().stopAllSkillEffects(EffectType.Relax);

        getAI().clearNextAction();
        broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING));

        _sittingObject = null;
        sittingTaskLaunched = true;
        ThreadPoolManager.getInstance().schedule(new EndStandUpTask(this), 2500);
    }

    public void updateWaitSitTime() {
        if (_waitTimeWhenSit < 200) {
            _waitTimeWhenSit += 2;
        }
    }

    public int getWaitSitTime() {
        return _waitTimeWhenSit;
    }

    public void resetWaitSitTime() {
        _waitTimeWhenSit = 0;
    }

    public Warehouse getWarehouse() {
        return _warehouse;
    }

    public ItemContainer getRefund() {
        return _refund;
    }

    public long getAdena() {
        return getInventory().getAdena();
    }

    public boolean reduceAdena(long adena) {
        return reduceAdena(adena, false);
    }

    /**
     * ?   ?.<BR> <BR>
     *
     * @param adena  - ??  
     * @param notify -   
     * @return true  ?
     */
    public boolean reduceAdena(long adena, boolean notify) {
        if (adena < 0) {
            return false;
        }
        if (adena == 0) {
            return true;
        }
        boolean result = getInventory().reduceAdena(adena);
        if (notify && result) {
            sendPacket(SystemMessage2.removeItems(ItemTemplate.ITEM_ID_ADENA, adena));
        }
        return result;
    }

    public ItemInstance addAdena(long adena) {
        return addAdena(adena, false);
    }

    /**
     * ?  ?.<BR> <BR>
     *
     * @param adena  - ??  
     * @param notify -   
     * @return L2ItemInstance -  ? 
     */
    public ItemInstance addAdena(long adena, boolean notify) {
        if (adena < 1) {
            return null;
        }
        ItemInstance item = getInventory().addAdena(adena);
        if (item != null && notify) {
            sendPacket(SystemMessage2.obtainItems(ItemTemplate.ITEM_ID_ADENA, adena, 0));
        }
        return item;
    }

    public GameClient getNetConnection() {
        return _connection;
    }

    public void setNetConnection(final GameClient connection) {
        _connection = connection;
    }

    public int getRevision() {
        return _connection == null ? 0 : _connection.getRevision();
    }

    public boolean isConnected() {
        return _connection != null && _connection.isConnected();
    }

    @Override
    public void onAction(final Player player, boolean shift) {
        if (isFrozen()) {
            player.sendPacket(ActionFail.STATIC);
            return;
        }

        if (Events.onAction(player, this, shift)) {
            player.sendPacket(ActionFail.STATIC);
            return;
        }
        // Check if the other player already target this L2Player
        if (player.getTarget() != this) {
            player.setTarget(this);
            if (player.getTarget() == this) {
                player.sendPacket(new MyTargetSelected(getObjectId(), 0)); // The color to display in the select window is White
            } else {
                player.sendPacket(ActionFail.STATIC);
            }
        } else if (getPrivateStoreType() != Player.STORE_PRIVATE_NONE) {
            if (getDistance(player) > INTERACTION_DISTANCE
                    && player.getAI().getIntention() != CtrlIntention.AI_INTENTION_INTERACT) {
                if (!shift) {
                    player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this, null);
                } else {
                    player.sendPacket(ActionFail.STATIC);
                }
            } else {
                player.doInteract(this);
            }
        } else if (isAutoAttackable(player)) {
            player.getAI().Attack(this, false, shift);
        } else if (player != this) {
            if (player.getAI().getIntention() != CtrlIntention.AI_INTENTION_FOLLOW) {
                if (!shift) {
                    player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this, Config.FOLLOW_RANGE);
                } else {
                    player.sendPacket(ActionFail.STATIC);
                }
            } else {
                player.sendPacket(ActionFail.STATIC);
            }
        } else {
            player.sendPacket(ActionFail.STATIC);
        }
    }

    @Override
    public void broadcastStatusUpdate() {
        if (!needStatusUpdate()) //    
        //  . 
        //    -
        //   .
        {
            return;
        }

        StatusUpdate su = makeStatusUpdate(StatusUpdate.MAX_HP, StatusUpdate.MAX_MP, StatusUpdate.MAX_CP,
                StatusUpdate.CUR_HP, StatusUpdate.CUR_MP, StatusUpdate.CUR_CP);
        sendPacket(su);

        // Check if a party is in progress
        if (isInParty())
        // Send the Server->Client packet PartySmallWindowUpdate with
        // current HP, MP and Level to all other L2Player of the Party
        {
            getParty().broadcastToPartyMembers(this, new PartySmallWindowUpdate(this));
        }

        DuelEvent duelEvent = getEvent(DuelEvent.class);
        if (duelEvent != null) {
            duelEvent.sendPacket(new ExDuelUpdateUserInfo(this), getTeam().revert().name());
        }

        if (isInOlympiadMode() && isOlympiadCompStart()) {
            if (_olympiadGame != null) {
                _olympiadGame.broadcastInfo(this, null, false);
            }
        }

    }

    @Override
    public void broadcastCharInfo() {
        broadcastUserInfo(false);
    }

    /**
     * ? UserInfo  ?  CharInfo  ?.<BR> <BR>
     * <p/>
     * <B><U> </U> :</B><BR> <BR>   ? UserInfo.    {@link
     * Creature#broadcastPacketToOthers(l2next.gameserver.network.serverpackets.L2GameServerPacket...)} ?
     * ? CharInfo<BR> <BR>
     * <p/>
     * <B><U> ?</U> :</B><BR> <BR> <li>? ? UserInfo(   )</li> <li>? 
     * ? CharInfo(Public data only)</li><BR> <BR>
     * <p/>
     * <FONT COLOR=#FF0000><B> <U></U> :   UserInfo  ?  CharInfo  ?.<BR>
     *  ?     ( ? ? )!!! ? ? ??  ?
     *  ? .<br>   {@link Player#sendChanges()}</B></FONT><BR> <BR>
     */
    public void broadcastUserInfo(boolean force) {
        sendUserInfo(force);

        if (!isVisible() || isInvisible()) {
            return;
        }

        if (Config.BROADCAST_CHAR_INFO_INTERVAL == 0) {
            force = true;
        }

        if (force) {
            if (_broadcastCharInfoTask != null) {
                _broadcastCharInfoTask.cancel(false);
                _broadcastCharInfoTask = null;
            }
            broadcastCharInfoImpl();
            return;
        }

        if (_broadcastCharInfoTask != null) {
            return;
        }

        _broadcastCharInfoTask = ThreadPoolManager.getInstance().schedule(new BroadcastCharInfoTask(),
                Config.BROADCAST_CHAR_INFO_INTERVAL);
    }

    public boolean isPolymorphed() {
        return _polyNpcId != 0;
    }

    public int getPolyId() {
        return _polyNpcId;
    }

    public void setPolyId(int polyid) {
        _polyNpcId = polyid;

        teleToLocation(getLoc());
        broadcastUserInfo(true);
    }

    private void broadcastCharInfoImpl() {
        if (!isVisible() || isInvisible()) {
            return;
        }

        L2GameServerPacket ci = isPolymorphed() ? new NpcInfoPoly(this) : new CharInfo(this);
        L2GameServerPacket exCi = new ExBR_ExtraUserInfo(this);
        L2GameServerPacket dominion = getEvent(DominionSiegeEvent.class) != null ? new ExDominionWarStart(this)
                : null;
        for (Player player : World.getAroundPlayers(this)) {
            player.sendPacket(ci, exCi);
            player.sendPacket(RelationChanged.update(player, this, player));
            if (dominion != null) {
                player.sendPacket(dominion);
            }
        }
    }

    public void broadcastRelationChanged() {
        if (!isVisible() || isInvisible()) {
            return;
        }

        for (Player player : World.getAroundPlayers(this)) {
            player.sendPacket(RelationChanged.update(player, this, player));
        }
    }

    public void sendEtcStatusUpdate() {
        if (!isVisible()) {
            return;
        }

        sendPacket(new EtcStatusUpdate(this));
    }

    private void sendUserInfoImpl() {
        sendPacket(new UserInfo(this), new ExBR_ExtraUserInfo(this));
        DominionSiegeEvent siegeEvent = getEvent(DominionSiegeEvent.class);
        if (siegeEvent != null) {
            sendPacket(new ExDominionWarStart(this));
        }
    }

    public void sendUserInfo() {
        sendUserInfo(false);
    }

    public void sendUserInfo(boolean force) {
        if (!isVisible() || entering || isLogoutStarted()) {
            return;
        }

        if (Config.USER_INFO_INTERVAL == 0 || force) {
            if (_userInfoTask != null) {
                _userInfoTask.cancel(false);
                _userInfoTask = null;
            }
            sendUserInfoImpl();
            return;
        }

        if (_userInfoTask != null) {
            return;
        }

        _userInfoTask = ThreadPoolManager.getInstance().schedule(new UserInfoTask(), Config.USER_INFO_INTERVAL);
    }

    @Override
    public StatusUpdate makeStatusUpdate(int... fields) {
        StatusUpdate su = new StatusUpdate(getObjectId());
        for (int field : fields) {
            switch (field) {
            case StatusUpdate.CUR_HP:
                su.addAttribute(field, (int) getCurrentHp());
                break;
            case StatusUpdate.MAX_HP:
                su.addAttribute(field, getMaxHp());
                break;
            case StatusUpdate.CUR_MP:
                su.addAttribute(field, (int) getCurrentMp());
                break;
            case StatusUpdate.MAX_MP:
                su.addAttribute(field, getMaxMp());
                break;
            case StatusUpdate.CUR_LOAD:
                su.addAttribute(field, getCurrentLoad());
                break;
            case StatusUpdate.MAX_LOAD:
                su.addAttribute(field, getMaxLoad());
                break;
            case StatusUpdate.PVP_FLAG:
                su.addAttribute(field, _pvpFlag);
                break;
            case StatusUpdate.KARMA:
                su.addAttribute(field, getKarma());
                break;
            case StatusUpdate.CUR_CP:
                su.addAttribute(field, (int) getCurrentCp());
                break;
            case StatusUpdate.MAX_CP:
                su.addAttribute(field, getMaxCp());
                break;
            }
        }
        return su;
    }

    public void sendStatusUpdate(boolean broadCast, boolean withPet, int... fields) {
        if (fields.length == 0 || entering && !broadCast) {
            return;
        }

        StatusUpdate su = makeStatusUpdate(fields);
        if (!su.hasAttributes()) {
            return;
        }

        List<L2GameServerPacket> packets = new ArrayList<L2GameServerPacket>(withPet ? 4 : 1);
        if (withPet) {
            for (Summon summon : getPets()) {
                packets.add(summon.makeStatusUpdate(fields));
            }
        }
        packets.add(su);

        if (!broadCast) {
            sendPacket(packets);
        } else if (entering) {
            broadcastPacketToOthers(packets);
        } else {
            broadcastPacket(packets);
        }
    }

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

    @Override
    public void sendPacket(IStaticPacket p) {
        if (!isConnected()) {
            return;
        }

        if (isPacketIgnored(p.packet(this))) {
            return;
        }

        _connection.sendPacket(p.packet(this));
    }

    @Override
    public void sendPacket(IStaticPacket... packets) {
        if (!isConnected()) {
            return;
        }

        for (IStaticPacket p : packets) {
            if (isPacketIgnored(p)) {
                continue;
            }

            _connection.sendPacket(p.packet(this));
        }
    }

    private boolean isPacketIgnored(IStaticPacket p) {
        if (p == null) {
            return true;
        }
        if (_notShowBuffAnim && (p.getClass() == MagicSkillUse.class || p.getClass() == MagicSkillLaunched.class)) {
            return true;
        }

        // if(_notShowTraders && (p.getClass() == PrivateStoreMsgBuy.class ||
        // p.getClass() == PrivateStoreMsgSell.class || p.getClass() ==
        // RecipeShopMsg.class))
        // return true;

        return false;
    }

    @Override
    public void sendPacket(List<? extends IStaticPacket> packets) {
        if (!isConnected()) {
            return;
        }

        for (IStaticPacket p : packets) {
            _connection.sendPacket(p.packet(this));
        }
    }

    public void doInteract(GameObject target) {
        if (target == null || isActionsDisabled()) {
            sendActionFailed();
            return;
        }
        if (target.isPlayer()) {
            if (target.getDistance(this) <= INTERACTION_DISTANCE) {
                Player temp = (Player) target;

                if (temp.getPrivateStoreType() == STORE_PRIVATE_SELL
                        || temp.getPrivateStoreType() == STORE_PRIVATE_SELL_PACKAGE) {
                    sendPacket(new PrivateStoreListSell(this, temp));
                    sendActionFailed();
                } else if (temp.getPrivateStoreType() == STORE_PRIVATE_BUY) {
                    sendPacket(new PrivateStoreListBuy(this, temp));
                    sendActionFailed();
                } else if (temp.getPrivateStoreType() == STORE_PRIVATE_MANUFACTURE) {
                    sendPacket(new RecipeShopSellList(this, temp));
                    sendActionFailed();
                }
                sendActionFailed();
            } else if (getAI().getIntention() != CtrlIntention.AI_INTENTION_INTERACT) {
                getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this, null);
            }
        } else {
            target.onAction(this, false);
        }
    }

    public void doAutoLootOrDrop(ItemInstance item, NpcInstance fromNpc) {
        boolean forceAutoloot = fromNpc.isFlying() || getReflection().isAutolootForced();

        if ((fromNpc.isRaid() || fromNpc instanceof ReflectionBossInstance) && !Config.AUTO_LOOT_FROM_RAIDS
                && !item.isHerb() && !forceAutoloot) {
            item.dropToTheGround(this, fromNpc);
            return;
        }

        // Herbs
        if (item.isHerb()) {
            if (!AutoLootHerbs && !forceAutoloot) {
                item.dropToTheGround(this, fromNpc);
                return;
            }
            Skill[] skills = item.getTemplate().getAttachedSkills();
            if (skills.length > 0) {
                for (Skill skill : skills) {
                    altUseSkill(skill, this);
                    for (Summon summon : getPets()) {
                        if (summon.isSummon() && !summon.isDead()) {
                            summon.altUseSkill(skill, summon);
                        }
                    }
                }
            }
            item.deleteMe();
            return;
        }

        if (!_autoLoot && !forceAutoloot) {
            item.dropToTheGround(this, fromNpc);
            return;
        }
        // Check if the L2Player is in a Party
        if (!isInParty()) {
            if (!pickupItem(item, Log.Pickup)) {
                item.dropToTheGround(this, fromNpc);
                return;
            }
        } else {
            getParty().distributeItem(this, item, fromNpc);
        }

        broadcastPickUpMsg(item);
    }

    @Override
    public void doPickupItem(final GameObject object) {
        // Check if the L2Object to pick up is a L2ItemInstance
        if (!object.isItem()) {
            _log.warn("trying to pickup wrong target." + getTarget());
            return;
        }

        sendActionFailed();
        stopMove();

        ItemInstance item = (ItemInstance) object;

        synchronized (item) {
            if (!item.isVisible()) {
                return;
            }

            // Check if me not owner of item and, if in party, not in owner
            // party and nonowner pickup delay still active
            if (!ItemFunctions.checkIfCanPickup(this, item)) {
                SystemMessage sm;
                if (item.getItemId() == 57) {
                    sm = new SystemMessage(SystemMessage.YOU_HAVE_FAILED_TO_PICK_UP_S1_ADENA);
                    sm.addNumber(item.getCount());
                } else {
                    sm = new SystemMessage(SystemMessage.YOU_HAVE_FAILED_TO_PICK_UP_S1);
                    sm.addItemName(item.getItemId());
                }
                sendPacket(sm);
                return;
            }

            // Herbs
            if (item.isHerb()) {
                Skill[] skills = item.getTemplate().getAttachedSkills();
                if (skills.length > 0) {
                    for (Skill skill : skills) {
                        altUseSkill(skill, this);
                    }
                }

                broadcastPacket(new GetItem(item, getObjectId()));
                item.deleteMe();
                return;
            }

            FlagItemAttachment attachment = item.getAttachment() instanceof FlagItemAttachment
                    ? (FlagItemAttachment) item.getAttachment()
                    : null;

            if (!isInParty() || attachment != null) {
                if (pickupItem(item, Log.Pickup)) {
                    broadcastPacket(new GetItem(item, getObjectId()));
                    broadcastPickUpMsg(item);
                    item.pickupMe();
                }
            } else {
                getParty().distributeItem(this, item, null);
            }
        }
    }

    public boolean pickupItem(ItemInstance item, String log) {
        PickableAttachment attachment = item.getAttachment() instanceof PickableAttachment
                ? (PickableAttachment) item.getAttachment()
                : null;

        if (!ItemFunctions.canAddItem(this, item)) {
            return false;
        }

        if (item.getItemId() == ItemTemplate.ITEM_ID_ADENA) {
            if (item.getOwnerId() == 0) {
                WorldStatisticsManager.getInstance().updateStat(this, CategoryType.ADENA_ADDED, item.getCount());
            }
            Quest q = QuestManager.getQuest(255);
            if (q != null) {
                processQuestEvent(q.getName(), "CE" + item.getItemId(), null);
            }
        }

        Log.LogItem(this, log, item);
        sendPacket(SystemMessage2.obtainItems(item));
        getInventory().addItem(item);

        if (attachment != null) {
            attachment.pickUp(this);
        }

        sendChanges();
        return true;
    }

    public void setObjectTarget(GameObject target) {
        setTarget(target);
        if (target == null) {
            return;
        }
    }

    @Override
    public void setTarget(GameObject newTarget) {
        // Check if the new target is visible
        if (newTarget != null && !newTarget.isVisible()) {
            newTarget = null;
        }

        Party party = getParty();

        if (party != null && party.isInDimensionalRift()) {
            int riftType = party.getDimensionalRift().getType();
            int riftRoom = party.getDimensionalRift().getCurrentRoom();
            if (newTarget != null && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom)
                    .checkIfInZone(newTarget.getX(), newTarget.getY(), newTarget.getZ())) {
                newTarget = null;
            }
        }

        GameObject oldTarget = getTarget();

        if (oldTarget != null) {
            if (oldTarget.equals(newTarget)) {
                return;
            }

            // Remove the L2Player from the _statusListener of the old target if
            // it was a L2Character
            if (oldTarget.isCreature()) {
                ((Creature) oldTarget).removeStatusListener(this);
            }

            broadcastPacket(new TargetUnselected(this));
        }

        if (newTarget != null) {
            // Add the L2Player to the _statusListener of the new target if it's
            // a L2Character
            if (newTarget.isCreature()) {
                ((Creature) newTarget).addStatusListener(this);
            }

            broadcastPacketToOthers(new TargetSelected(getObjectId(), newTarget.getObjectId(), getLoc()));
            if (newTarget.isNpc()) {
                NpcInstance npc = (NpcInstance) newTarget;
                sendPacket(new MyTargetSelected(npc.getObjectId(), getLevel() - npc.getLevel()));
                StatusUpdate SU = new StatusUpdate(npc.getObjectId(), getObjectId());
                SU.addAttribute(StatusUpdate.CUR_HP, (int) npc.getCurrentHp());
                SU.addAttribute(StatusUpdate.MAX_HP, npc.getMaxHp());
                sendPacket(SU);
                sendPacket(new ValidateLocation(npc), ActionFail.STATIC);
            }
            if (newTarget.isCreature()) {
                sendPacket(new ExAbnormalStatusUpdateFromTargetPacket((Creature) newTarget));
            }
        }

        super.setTarget(newTarget);
    }

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

    /**
     * @return the active weapon item (always equipped in the right hand).<BR> <BR>
     */
    @Override
    public WeaponTemplate getActiveWeaponItem() {
        final ItemInstance weapon = getActiveWeaponInstance();

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

        return (WeaponTemplate) weapon.getTemplate();
    }

    /**
     * @return the secondary weapon instance (always equipped in the left hand).<BR> <BR>
     */
    @Override
    public ItemInstance 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 WeaponTemplate getSecondaryWeaponItem() {
        final ItemInstance weapon = getSecondaryWeaponInstance();

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

        final ItemTemplate item = weapon.getTemplate();

        if (item instanceof WeaponTemplate) {
            return (WeaponTemplate) item;
        }

        return null;
    }

    public boolean isWearingArmor(final ArmorType armorType) {
        final ItemInstance chest = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);

        if (chest == null) {
            return armorType == ArmorType.NONE;
        }

        if (chest.getItemType() != armorType) {
            return false;
        }

        if (chest.getBodyPart() == ItemTemplate.SLOT_FULL_ARMOR) {
            return true;
        }

        final ItemInstance legs = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS);

        return legs == null ? armorType == ArmorType.NONE : legs.getItemType() == armorType;
    }

    @Override
    public void reduceCurrentHp(double damage, double reflectableDamage, Creature attacker, Skill skill,
            boolean awake, boolean standUp, boolean directHp, boolean canReflect, boolean transferDamage,
            boolean isDot, boolean sendMessage) {
        if (attacker == null || isDead() || (attacker.isDead() && !isDot)) {
            return;
        }

        // 5182 = Blessing of protection,     
        // 10     
        if (attacker.isPlayer() && Math.abs(attacker.getLevel() - getLevel()) > 10) {
            WorldStatisticsManager.getInstance().updateStat(attacker.getPlayer(), CategoryType.DAMAGE_TO_PC,
                    (long) damage);
            WorldStatisticsManager.getInstance().updateStat(attacker.getPlayer(), CategoryType.DAMAGE_TO_PC_MAX,
                    getActiveClassId(), (long) damage);
            WorldStatisticsManager.getInstance().updateStat(this, CategoryType.DAMAGE_FROM_PC, (long) damage);
            //        
            if (attacker.getKarma() > 0 && getEffectList().getEffectsBySkillId(5182) != null
                    && !isInZone(ZoneType.SIEGE)) {
                return;
            }
            //        
            if (getKarma() > 0 && attacker.getEffectList().getEffectsBySkillId(5182) != null
                    && !attacker.isInZone(ZoneType.SIEGE)) {
                return;
            }
        }

        // Reduce the current HP of the L2Player
        super.reduceCurrentHp(damage, reflectableDamage, attacker, skill, awake, standUp, directHp, canReflect,
                transferDamage, isDot, sendMessage);

        if (attacker.getPlayer() == null) {
            WorldStatisticsManager.getInstance().updateStat(this, CategoryType.DAMAGE_FROM_MONSTERS,
                    getClassId().getId(), (long) damage);
        }
    }

    @Override
    protected void onReduceCurrentHp(double damage, Creature attacker, Skill skill, boolean awake, boolean standUp,
            boolean directHp) {
        if (standUp) {
            standUp();
            if (isFakeDeath()) {
                breakFakeDeath();
            }
        }

        if (attacker.isPlayable()) {
            if (!directHp && getCurrentCp() > 0) {
                double cp = getCurrentCp();
                if (cp >= damage) {
                    cp -= damage;
                    damage = 0;
                } else {
                    damage -= cp;
                    cp = 0;
                }

                setCurrentCp(cp);
            }
        }

        double hp = getCurrentHp();

        DuelEvent duelEvent = getEvent(DuelEvent.class);
        if (duelEvent != null) {
            if (hp - damage <= 1) //   <= 1 - 
            {
                setCurrentHp(1, false);
                duelEvent.onDie(this);
                return;
            }
        }

        if (isInOlympiadMode()) {
            OlympiadGame game = _olympiadGame;
            if (this != attacker && (skill == null || skill.isOffensive())) // 
            // 
            // 
            // 
            // 
            // 
            // ?
            // ?
            {
                game.addDamage(this, Math.min(hp, damage));
            }

            if (hp - damage <= 1) //   <= 1 - 
            {
                game.setWinner(getOlympiadSide() == 1 ? 2 : 1);
                game.endGame(20000, false);
                setCurrentHp(1, false);
                attacker.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
                attacker.sendActionFailed();
                return;
            }
        }

        super.onReduceCurrentHp(damage, attacker, skill, awake, standUp, directHp);
    }

    private void altDeathPenalty(final Creature killer) {
        // Reduce the Experience of the L2Player in function of the calculated
        // Death Penalty
        if (!Config.ALT_GAME_DELEVEL) {
            return;
        }
        if (isInZoneBattle()) {
            return;
        }
        deathPenalty(killer);
    }

    public final boolean atWarWith(final Player player) {
        return _clan != null && player.getClan() != null && getPledgeType() != -1 && player.getPledgeType() != -1
                && _clan.isAtWarWith(player.getClan().getClanId());
    }

    public boolean atMutualWarWith(Player player) {
        return _clan != null && player.getClan() != null && getPledgeType() != -1 && player.getPledgeType() != -1
                && _clan.isAtWarWith(player.getClan().getClanId())
                && player.getClan().isAtWarWith(_clan.getClanId());
    }

    public final void doPurePk(final Player killer) {
        // Check if the attacker has a PK counter greater than 0
        final int pkCountMulti = Math.max(killer.getPkKills() / 2, 1);

        // Calculate the level difference Multiplier between attacker and killed
        // L2Player
        // final int lvlDiffMulti = Math.max(killer.getLevel() / _level, 1);

        // Calculate the new Karma of the attacker : newKarma =
        // baseKarma*pkCountMulti*lvlDiffMulti
        // Add karma to attacker and increase its PK counter
        killer.decreaseKarma(Config.KARMA_MIN_KARMA * pkCountMulti); // *
        // lvlDiffMulti);
        killer.setPkKills(killer.getPkKills() + 1);
        WorldStatisticsManager.getInstance().updateStat(killer, CategoryType.PK_COUNT, 1);
        WorldStatisticsManager.getInstance().updateStat(this, CategoryType.KILLED_BY_PK_COUNT, 1);
    }

    public final void doKillInPeace(final Player killer) // Check if the
    // L2Player killed
    // haven't Karma
    {
        if (_karma >= 0) {
            doPurePk(killer);
        } else {
            killer.setPvpKills(killer.getPvpKills() + 1);
            WorldStatisticsManager.getInstance().updateStat(killer, CategoryType.PVP_COUNT, 1);
        }
    }

    public void checkAddItemToDrop(GArray<ItemInstance> array, GArray<ItemInstance> items, int maxCount) {
        for (int i = 0; i < maxCount && !items.isEmpty(); i++) {
            array.add(items.remove(Rnd.get(items.size())));
        }
    }

    public FlagItemAttachment getActiveWeaponFlagAttachment() {
        ItemInstance item = getActiveWeaponInstance();
        if (item == null || !(item.getAttachment() instanceof FlagItemAttachment)) {
            return null;
        }
        return (FlagItemAttachment) item.getAttachment();
    }

    protected void doPKPVPManage(Creature killer) {
        FlagItemAttachment attachment = getActiveWeaponFlagAttachment();
        if (attachment != null) {
            attachment.onDeath(this, killer);
        }

        if (killer == null || killer == this) {
            return;
        }

        if (isInZoneBattle() || killer.isInZoneBattle()) {
            return;
        }

        if (killer instanceof Summon && (killer = killer.getPlayer()) == null) {
            return;
        }

        // Processing Karma/PKCount/PvPCount for killer
        if (killer.isPlayer()) {
            final Player pk = (Player) killer;
            final int repValue = getLevel() - pk.getLevel() >= 20 ? 2 : 1;
            boolean war = atMutualWarWith(pk);

            // TODO [VISTALL] fix it
            if (war /*
                    * || _clan.getSiege() != null && _clan.getSiege() == pk.getClan().getSiege() && (_clan.isDefender()
                    * && pk.getClan().isAttacker() || _clan.isAttacker() && pk.getClan().isDefender())
                    */) {
                if (pk.getClan().getReputationScore() > 0 && _clan.getLevel() >= 5 && _clan.getReputationScore() > 0
                        && pk.getClan().getLevel() >= 5 && !Clan.isAcademy(_pledgeType)) {
                    _clan.broadcastToOtherOnlineMembers(new SystemMessage(
                            SystemMessage.YOUR_CLAN_MEMBER_S1_WAS_KILLED_S2_POINTS_HAVE_BEEN_DEDUCTED_FROM_YOUR_CLAN_REPUTATION_SCORE_AND_ADDED_TO_YOUR_OPPONENT_CLAN_REPUTATION_SCORE)
                                    .addString(getName())
                                    .addNumber(-_clan.incReputation(-repValue, true, "ClanWar")),
                            this);
                    pk.getClan().broadcastToOtherOnlineMembers(new SystemMessage(
                            SystemMessage.FOR_KILLING_AN_OPPOSING_CLAN_MEMBER_S1_POINTS_HAVE_BEEN_DEDUCTED_FROM_YOUR_OPPONENTS_CLAN_REPUTATION_SCORE)
                                    .addNumber(pk.getClan().incReputation(repValue, true, "ClanWar")),
                            pk);
                }
            }

            if (isOnSiegeField()) {
                return;
            }

            if (_pvpFlag > 0 || war) {
                pk.setPvpKills(pk.getPvpKills() + 1);
                WorldStatisticsManager.getInstance().updateStat(pk, CategoryType.PVP_COUNT, 1);
                WorldStatisticsManager.getInstance().updateStat(this, CategoryType.KILLED_IN_PVP_COUNT, 1);
            } else {
                doKillInPeace(pk);
            }

            pk.sendChanges();
        }

        if (_karma < 0) {
            increaseKarma(Config.KARMA_LOST_BASE);
            if (_karma > 0) {
                _karma = 0;
            }
        }

        //   ?  ?? ?     
        // ?
        // ? ,       ? ?  
        //   
        boolean isPvP = killer.isPlayable() || killer instanceof GuardInstance;

        if (killer.isMonster() && !Config.DROP_ITEMS_ON_DIE //   
                //   ?
                || isPvP //   ?   
                        && (_pkKills < 31 // ? ? ? 
                                || _karma >= 0 && Config.KARMA_NEEDED_TO_DROP) // ? 
                || !killer.isMonster() && !isPvP) //   ? 
        {
            return;
        }

        // No drop from GM's
        if (!Config.KARMA_DROP_GM && isGM()) {
            return;
        }

        final int max_drop_count = isPvP ? Config.KARMA_DROP_ITEM_LIMIT : 1;

        double dropRate; //    
        if (isPvP) {
            dropRate = _pkKills * Config.KARMA_DROPCHANCE_MOD + Config.KARMA_DROPCHANCE_BASE;
        } else {
            dropRate = Config.NORMAL_DROPCHANCE_BASE;
        }

        int dropEquipCount = 0, dropWeaponCount = 0, dropItemCount = 0;

        for (int i = 0; i < Math.ceil(dropRate / 100) && i < max_drop_count; i++) {
            if (Rnd.chance(dropRate)) {
                int rand = Rnd.get(
                        Config.DROPCHANCE_EQUIPPED_WEAPON + Config.DROPCHANCE_EQUIPMENT + Config.DROPCHANCE_ITEM)
                        + 1;
                if (rand > Config.DROPCHANCE_EQUIPPED_WEAPON + Config.DROPCHANCE_EQUIPMENT) {
                    dropItemCount++;
                } else if (rand > Config.DROPCHANCE_EQUIPPED_WEAPON) {
                    dropEquipCount++;
                } else {
                    dropWeaponCount++;
                }
            }
        }

        GArray<ItemInstance> drop = new GArray<ItemInstance>(), //  
                // 
                // 
                // 
                dropItem = new GArray<ItemInstance>(), dropEquip = new GArray<ItemInstance>(),
                dropWeapon = new GArray<ItemInstance>(); // 

        getInventory().writeLock();
        try {
            for (ItemInstance item : getInventory().getItems()) {
                if (!item.canBeDropped(this, true)
                        || Config.KARMA_LIST_NONDROPPABLE_ITEMS.contains(item.getItemId())) {
                    continue;
                }

                if (item.getTemplate().getType2() == ItemTemplate.TYPE2_WEAPON) {
                    dropWeapon.add(item);
                } else if (item.getTemplate().getType2() == ItemTemplate.TYPE2_SHIELD_ARMOR
                        || item.getTemplate().getType2() == ItemTemplate.TYPE2_ACCESSORY) {
                    dropEquip.add(item);
                } else if (item.getTemplate().getType2() == ItemTemplate.TYPE2_OTHER) {
                    dropItem.add(item);
                }
            }

            checkAddItemToDrop(drop, dropWeapon, dropWeaponCount);
            checkAddItemToDrop(drop, dropEquip, dropEquipCount);
            checkAddItemToDrop(drop, dropItem, dropItemCount);

            // Dropping items, if present
            if (drop.isEmpty()) {
                return;
            }

            for (ItemInstance item : drop) {
                if (item.isAugmented() && !Config.ALT_ALLOW_DROP_AUGMENTED) {
                    item.setAugmentationId(0);
                }

                item = getInventory().removeItem(item);
                Log.LogItem(this, Log.PvPDrop, item);

                if (item.getEnchantLevel() > 0) {
                    sendPacket(new SystemMessage(SystemMessage.DROPPED__S1_S2).addNumber(item.getEnchantLevel())
                            .addItemName(item.getItemId()));
                } else {
                    sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_DROPPED_S1).addItemName(item.getItemId()));
                }

                if (killer.isPlayable() && (Config.AUTO_LOOT && Config.AUTO_LOOT_PK || isInFlyingTransform())) {
                    killer.getPlayer().getInventory().addItem(item);
                    Log.LogItem(this, Log.Pickup, item);

                    killer.getPlayer().sendPacket(SystemMessage2.obtainItems(item));
                } else {
                    item.dropToTheGround(this,
                            Location.findAroundPosition(this, Config.KARMA_RANDOM_DROP_LOCATION_LIMIT));
                }
            }
        } finally {
            getInventory().writeUnlock();
        }
    }

    @Override
    protected void onDeath(Creature killer) {
        // Check for active charm of luck for death penalty
        getDeathPenalty().checkCharmOfLuck();

        if (isInStoreMode()) {
            setPrivateStoreType(Player.STORE_PRIVATE_NONE);
        }
        if (isProcessingRequest()) {
            Request request = getRequest();
            if (isInTrade()) {
                Player parthner = request.getOtherPlayer(this);
                sendPacket(SendTradeDone.FAIL);
                parthner.sendPacket(SendTradeDone.FAIL);
            }
            request.cancel();
        }

        setAgathion(0);

        boolean checkPvp = true;
        if (Config.ALLOW_CURSED_WEAPONS) {
            if (isCursedWeaponEquipped()) {
                CursedWeaponsManager.getInstance().dropPlayer(this);
                checkPvp = false;
            } else if (killer != null && killer.isPlayer() && killer.isCursedWeaponEquipped()) {
                CursedWeaponsManager.getInstance().increaseKills(((Player) killer).getCursedWeaponEquippedId());
                checkPvp = false;
            }
        }

        if (checkPvp) {
            doPKPVPManage(killer);

            altDeathPenalty(killer);
        }

        // And in the end of process notify death penalty that owner died :)
        getDeathPenalty().notifyDead(killer);

        setIncreasedForce(0);

        if (isInParty() && getParty().isInReflection() && getParty().getReflection() instanceof DimensionalRift) {
            ((DimensionalRift) getParty().getReflection()).memberDead(this);
        }

        stopWaterTask();

        if (!isSalvation() && isOnSiegeField() && isCharmOfCourage()) {
            ask(new ConfirmDlg(SystemMsg.YOUR_CHARM_OF_COURAGE_IS_TRYING_TO_RESURRECT_YOU, 60000),
                    new ReviveAnswerListener(this, 100, false));
            setCharmOfCourage(false);
        }

        if (getLevel() < 6) {
            Quest q = QuestManager.getQuest(255);
            if (q != null) {
                processQuestEvent(q.getName(), "CE30", null);
            }
        }

        WorldStatisticsManager.getInstance().updateStat(this, CategoryType.DIE_COUNT, 1);
        if (killer != null && killer.getPlayer() == null) {
            WorldStatisticsManager.getInstance().updateStat(this, CategoryType.KILLED_BY_MONSTER_COUNT, 1);
        }
        super.onDeath(killer);
    }

    public void restoreExp() {
        restoreExp(100.);
    }

    public void restoreExp(double percent) {
        if (percent == 0) {
            return;
        }

        int lostexp = 0;

        String lostexps = getVar("lostexp");
        if (lostexps != null) {
            lostexp = Integer.parseInt(lostexps);
            unsetVar("lostexp");
        }

        if (lostexp != 0) {
            addExpAndSp((long) (lostexp * percent / 100), 0);
        }
    }

    public void deathPenalty(Creature killer) {
        if (killer == null) {
            return;
        }
        final boolean atwar = killer.getPlayer() != null && atWarWith(killer.getPlayer());

        double deathPenaltyBonus = getDeathPenalty().getLevel() * Config.ALT_DEATH_PENALTY_C5_EXPERIENCE_PENALTY;
        if (deathPenaltyBonus < 2) {
            deathPenaltyBonus = 1;
        } else {
            deathPenaltyBonus = deathPenaltyBonus / 2;
        }

        // The death steal you some Exp: 10-40 lvl 8% loose
        double percentLost = 8.0;

        int level = getLevel();
        if (level >= 79) {
            percentLost = 1.0;
        } else if (level >= 78) {
            percentLost = 1.5;
        } else if (level >= 76) {
            percentLost = 2.0;
        } else if (level >= 40) {
            percentLost = 4.0;
        }

        if (Config.ALT_DEATH_PENALTY) {
            percentLost = percentLost * Config.RATE_XP + _pkKills * Config.ALT_PK_DEATH_RATE;
        }

        if (atwar) {
            percentLost = percentLost / 4.0;
        }

        // Calculate the Experience loss
        int lostexp = (int) Math.round((Experience.LEVEL[level + 1] - Experience.LEVEL[level]) * percentLost / 100);
        lostexp *= deathPenaltyBonus;

        lostexp = (int) calcStat(Stats.EXP_LOST, lostexp, killer, null);

        //      ,    - ??
        //     **
        if (isOnSiegeField()) {
            SiegeEvent<?, ?> siegeEvent = getEvent(SiegeEvent.class);
            if (siegeEvent != null) {
                lostexp = 0;
            }

            if (siegeEvent != null) {
                List<Effect> effect = getEffectList().getEffectsBySkillId(Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME);
                if (effect != null) {
                    int syndromeLvl = effect.get(0).getSkill().getLevel();
                    if (syndromeLvl < 5) {
                        getEffectList().stopEffect(Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME);
                        Skill skill = SkillTable.getInstance().getInfo(Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME,
                                syndromeLvl + 1);
                        skill.getEffects(this, this, false, false);
                    } else if (syndromeLvl == 5) {
                        getEffectList().stopEffect(Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME);
                        Skill skill = SkillTable.getInstance().getInfo(Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME, 5);
                        skill.getEffects(this, this, false, false);
                    }
                } else {
                    Skill skill = SkillTable.getInstance().getInfo(Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME, 1);
                    if (skill != null) {
                        skill.getEffects(this, this, false, false);
                    }
                }
            }
        }

        long before = getExp();
        addExpAndSp(-lostexp, 0);
        long lost = before - getExp();

        if (lost > 0) {
            setVar("lostexp", String.valueOf(lost), -1);
        }
    }

    public Request getRequest() {
        return _request;
    }

    public void setRequest(Request transaction) {
        _request = transaction;
    }

    /**
     * ?, ?  ? ?   
     *
     * @return true,  ?     
     */
    public boolean isBusy() {
        return isProcessingRequest() || isOutOfControl() || isInOlympiadMode() || getTeam() != TeamType.NONE
                || isInStoreMode() || isInDuel() || getMessageRefusal() || isBlockAll() || isInvisible();
    }

    public boolean isProcessingRequest() {
        if (_request == null) {
            return false;
        }
        if (!_request.isInProgress()) {
            return false;
        }
        return true;
    }

    public boolean isInTrade() {
        return isProcessingRequest() && getRequest().isTypeOf(L2RequestType.TRADE);
    }

    public List<L2GameServerPacket> addVisibleObject(GameObject object, Creature dropper) {
        if (isLogoutStarted() || object == null || object.getObjectId() == getObjectId() || !object.isVisible()) {
            return Collections.emptyList();
        }

        return object.addPacketList(this, dropper);
    }

    @Override
    public List<L2GameServerPacket> addPacketList(Player forPlayer, Creature dropper) {
        if (isInvisible() && forPlayer.getObjectId() != getObjectId()) {
            return Collections.emptyList();
        }

        if (getPrivateStoreType() != STORE_PRIVATE_NONE && forPlayer.getVarB("notraders")) {
            return Collections.emptyList();
        }

        //   ?  -  ?.
        if (isInObserverMode() && getCurrentRegion() != getObserverRegion()
                && getObserverRegion() == forPlayer.getCurrentRegion()) {
            return Collections.emptyList();
        }

        List<L2GameServerPacket> list = new ArrayList<L2GameServerPacket>();
        if (forPlayer.getObjectId() != getObjectId()) {
            list.add(isPolymorphed() ? new NpcInfoPoly(this) : new CharInfo(this));
        }

        list.add(new ExBR_ExtraUserInfo(this));

        if (isSitting() && _sittingObject != null) {
            list.add(new ChairSit(this, _sittingObject));
        }

        if (getPrivateStoreType() != STORE_PRIVATE_NONE) {
            if (getPrivateStoreType() == STORE_PRIVATE_BUY) {
                list.add(new PrivateStoreMsgBuy(this));
            } else if (getPrivateStoreType() == STORE_PRIVATE_SELL
                    || getPrivateStoreType() == STORE_PRIVATE_SELL_PACKAGE) {
                list.add(new PrivateStoreMsgSell(this));
            } else if (getPrivateStoreType() == STORE_PRIVATE_MANUFACTURE) {
                list.add(new RecipeShopMsg(this));
            }
            if (forPlayer.isInZonePeace()) //     
            //  ?, ? ?
            // ?
            {
                return list;
            }
        }

        if (isDoubleCastingNow()) {
            Creature castingTarget = getCastingTarget();
            Skill castingSkill = getCastingSkill();
            long animationEndTime = getAnimationEndTime();
            if (castingSkill != null && castingTarget != null && castingTarget.isCreature()
                    && getAnimationEndTime() > 0) {
                list.add(new MagicSkillUse(this, castingTarget, castingSkill.getId(), castingSkill.getLevel(),
                        (int) (animationEndTime - System.currentTimeMillis()), 0L, 1));
            }
        } else if (isCastingNow()) {
            Creature castingTarget = getCastingTarget();
            Skill castingSkill = getCastingSkill();
            long animationEndTime = getAnimationEndTime();
            if (castingSkill != null && castingTarget != null && castingTarget.isCreature()
                    && getAnimationEndTime() > 0) {
                list.add(new MagicSkillUse(this, castingTarget, castingSkill.getId(), castingSkill.getLevel(),
                        (int) (animationEndTime - System.currentTimeMillis()), 0L, 1));
            }
        }

        if (isInCombat()) {
            list.add(new AutoAttackStart(getObjectId()));
        }

        list.add(RelationChanged.update(forPlayer, this, forPlayer));
        DominionSiegeEvent dominionSiegeEvent = getEvent(DominionSiegeEvent.class);
        if (dominionSiegeEvent != null) {
            list.add(new ExDominionWarStart(this));
        }

        if (isInBoat()) {
            list.add(getBoat().getOnPacket(this, getInBoatPosition()));
        } else if (isMoving || isFollow) {
            list.add(movePacket());
        }
        return list;
    }

    public List<L2GameServerPacket> removeVisibleObject(GameObject object, List<L2GameServerPacket> list) {
        if (isLogoutStarted() || object == null || object.getObjectId() == getObjectId()) // FIXME
        // ||
        // isTeleporting()
        {
            return null;
        }

        List<L2GameServerPacket> result = list == null ? object.deletePacketList() : list;

        getAI().notifyEvent(CtrlEvent.EVT_FORGET_OBJECT, object);
        return result;
    }

    private void levelSet(int levels) {
        if (levels > 0) {
            sendPacket(Msg.YOU_HAVE_INCREASED_YOUR_LEVEL);
            broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP));

            setCurrentHpMp(getMaxHp(), getMaxMp());
            setCurrentCp(getMaxCp());

            Quest q = QuestManager.getQuest(255);
            if (q != null) {
                processQuestEvent(q.getName(), "CE40", null);
            }

            //    ?, ?     ,
            //  
            for (SkillLearn skillLearn : SkillAcquireHolder.getInstance().getAvailableSkills(this,
                    AcquireType.NORMAL)) {
                if (getKnownSkill(skillLearn.getId()) == null) {
                    sendPacket(new ExNewSkillToLearnByLevelUp());
                    break;
                }
            }
        } else if (levels < 0) {
            if (Config.ALT_REMOVE_SKILLS_ON_DELEVEL) {
                checkSkills();
            }
        }

        // Recalculate the party level
        if (isInParty()) {
            getParty().recalculatePartyData();
        }

        if (getLevel() >= 90 && getVar("Letter") == null) {
            ItemFunctions.addItem(this, 17725, 1, true);
            setVar("Letter", "give", -1);
        }

        if (getLevel() >= 85) {
            MentorUtil.removeSkills(this);
        }

        if (getLevel() >= 85 & getVar("GermunkusUSM") == null & !isAwaking()) {
            AwakingManager.getInstance().SendReqToStartQuest(this);
        }

        if (ItemLevelHolder.getInstance().getItemLevel(this.getLevel()) != null && getLevel() < 86) {
            sendPacket(new ExShowScreenMessage(
                    " ? " + this.getLevel()
                            + " ?! ?  .",
                    6000, ScreenMessageAlign.TOP_CENTER, true, 1, -1, true));
            for (ItemLevel entry : ItemLevelHolder.getInstance().getItemLevel(this.getLevel())) {
                addPremiumItem(this, entry.id, entry.count, false);
            }
        }

        if (_clan != null) {
            _clan.broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
        }

        if (_matchingRoom != null) {
            _matchingRoom.broadcastPlayerUpdate(this);
        }

        // Give Expertise skill of this level
        rewardSkills(true);

        int mentorId = getMentorSystem().getMentor();

        if (mentorId != 0) {
            Player mentorPlayer = World.getPlayer(mentorId);

            if (mentorPlayer != null) {
                String mentorName = mentorPlayer.getName();
                //  ? Sign of Tutor   ?
                if (MentorUtil.SIGN_OF_TUTOR.containsKey(getLevel())) {
                    Map<Integer, Long> signOfTutor = new HashMap<Integer, Long>() {
                        private static final long serialVersionUID = -6277622010035298559L;

                        {
                            put(33804, MentorUtil.SIGN_OF_TUTOR.get(getLevel()).longValue());
                        }
                    };
                    MentorUtil.sendMentorMail(mentorPlayer, signOfTutor);
                }
                final SubClass cclass = getActiveSubClass();

                if (getLevel() >= 85 && cclass.isBase() && getClassId().isOfLevel(ClassLevel.Awaking)) {
                    getMentorSystem().remove(mentorName, false, true);
                    sendPacket(
                            new SystemMessage2(SystemMsg.YOU_REACHED_LEVEL_86_RELATIONSHIP_WITH_S1_CAME_TO_AN_END)
                                    .addString(mentorName));
                    if (mentorPlayer != null) {
                        mentorPlayer.sendPacket(
                                new SystemMessage2(SystemMsg.THE_MENTEE_S1_HAS_REACHED_LEVEL_86).addName(this));
                        mentorPlayer.getMentorSystem().remove(_name, true, false);
                        if (mentorPlayer.getMentorSystem().getMenteeInfo().size() == 0) {
                            MentorUtil.removeConditions(mentorPlayer);
                            MentorUtil.removeSkills(mentorPlayer);
                            MentorUtil.removeEffectsFromPlayer(mentorPlayer);
                        }
                        MentorUtil.removeEffectsFromPlayer(this);
                        MentorUtil.setTimePenalty(mentorId, System.currentTimeMillis() + 5 * 24 * 3600 * 1000L, -1);
                    }
                    this.setVar("graduateMentoring", "true", -1);
                    MentorUtil.removeConditions(this);
                    MentorUtil.removeSkills(this);
                }
            }
        }
    }

    /**
     * ?  ?, ? ?   ,  ?+maxDiff
     */

    public void checkSkills() {
        for (Skill sk : getAllSkillsArray()) {
            SkillTreeTable.checkSkill(this, sk);
        }
    }

    public void startTimers() {
        startAutoSaveTask();
        startPcBangPointsTask();
        startBonusTask();
        getInventory().startTimers();
        resumeQuestTimers();
    }

    public void stopAllTimers() {
        setAgathion(0);
        stopWaterTask();
        stopBonusTask();
        stopHourlyTask();
        stopKickTask();
        stopPcBangPointsTask();
        stopAutoSaveTask();
        stopRecomBonusTask(true);
        getInventory().stopAllTimers();
        stopQuestTimers();
    }

    @Override
    public Collection<Summon> getPets() {
        if (_summons != null) {
            return _summons.values();
        }
        return new ArrayList<Summon>(0);
    }

    public Summon getPet(Summon s) {
        return _summons.get(s);
    }

    public boolean hasPet(int objectId) {
        for (Summon s : _summons.values()) {
            if (s.getObjectId() == objectId) {
                return true;
            }
        }
        return false;
    }

    public boolean isHaveDeadPets() {
        for (Summon summon : getPets()) {
            if (summon.isDead()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public int getPetCount() {
        return _summons.size();
    }

    @Override
    public boolean isHaveSummonedPets() {
        for (Summon summon : getPets()) {
            if (summon.isPet()) {
                return true;
            }
        }
        return false;
    }

    @Override
    public Summon getFirstPet() {
        for (Summon summon : getPets()) {
            if (summon != null) {
                return summon;
            }
        }
        return null;
    }

    public boolean isHavePet(int npcId) {
        for (Summon currentSummon : getPets()) {
            if (currentSummon.getNpcId() == npcId) {
                return true;
            }
        }
        return false;
    }

    public void addPet(Summon summon) {
        if (summon != null) {
            _summons.put(summon.getObjectId(), summon);
        } else {
            for (Summon currentSummon : getPets()) {
                if (currentSummon.isPet()) {
                    if (isLogoutStarted() && getPetControlItem() != null) {
                        setVar("pet", String.valueOf(getPetControlItem().getObjectId()), -1L);
                        setPetControlItem(null);
                    }
                    getEffectList().stopEffect(4140);
                }
            }
        }
    }

    public void removePet(Summon summon) {
        _summons.remove(summon.getObjectId());
        for (Summon s : getPets()) {
            s.sendPetInfo();
        }
    }

    public void scheduleDelete() {
        long time = 0L;

        if (Config.SERVICES_ENABLE_NO_CARRIER) {
            time = NumberUtils.toInt(getVar("noCarrier"), Config.SERVICES_NO_CARRIER_DEFAULT_TIME);
        }

        scheduleDelete(time * 1000L);
    }

    /**
     *      ? ?,    ?     . <br>
     * <br> TODO:     ?.<br> TODO:  ??  ? ??, ?   
     *  ?     ?  .<br> <br>
     *
     * @param time ?  ?
     */
    public void scheduleDelete(long time) {
        if (isLogoutStarted() || isInOfflineMode()) {
            return;
        }

        broadcastCharInfo();

        ThreadPoolManager.getInstance().schedule(new RunnableImpl() {
            @Override
            public void runImpl() throws Exception {
                if (!isConnected()) {
                    prepareToLogout();
                    deleteMe();
                }
            }
        }, time);
    }

    @Override
    protected void onDelete() {
        super.onDelete();

        //  ?  ? ?
        WorldRegion observerRegion = getObserverRegion();
        if (observerRegion != null) {
            observerRegion.removeObject(this);
        }

        // Send friendlists to friends that this player has logged off
        _friendList.notifyFriends(false);

        bookmarks.clear();

        _inventory.clear();
        _warehouse.clear();
        _summons = null;
        _arrowItem = null;
        _fistsWeaponItem = null;
        _chars = null;
        _enchantScroll = null;
        _enchantItem = null;
        _enchantSupportItem = null;
        _lastNpc = HardReferences.emptyRef();
        _observerRegion = null;
    }

    public List<TradeItem> getTradeList() {
        return _tradeList;
    }

    public void setTradeList(List<TradeItem> list) {
        _tradeList = list;
    }

    public String getSellStoreName() {
        return _sellStoreName;
    }

    public void setSellStoreName(String name) {
        _sellStoreName = Strings.stripToSingleLine(name);
    }

    public void setSellList(boolean packageSell, List<TradeItem> list) {
        if (packageSell) {
            _packageSellList = list;
        } else {
            _sellList = list;
        }
    }

    public List<TradeItem> getSellList() {
        return getSellList(_privatestore == STORE_PRIVATE_SELL_PACKAGE);
    }

    public List<TradeItem> getSellList(boolean packageSell) {
        return packageSell ? _packageSellList : _sellList;
    }

    public String getBuyStoreName() {
        return _buyStoreName;
    }

    public void setBuyStoreName(String name) {
        _buyStoreName = Strings.stripToSingleLine(name);
    }

    public List<TradeItem> getBuyList() {
        return _buyList;
    }

    public void setBuyList(List<TradeItem> list) {
        _buyList = list;
    }

    public String getManufactureName() {
        return _manufactureName;
    }

    public void setManufactureName(String name) {
        _manufactureName = Strings.stripToSingleLine(name);
    }

    public List<ManufactureItem> getCreateList() {
        return _createList;
    }

    public void setCreateList(List<ManufactureItem> list) {
        _createList = list;
    }

    public boolean isInStoreMode() {
        return _privatestore != STORE_PRIVATE_NONE;
    }

    public int getPrivateStoreType() {
        return _privatestore;
    }

    public void setPrivateStoreType(final int type) {
        _privatestore = type;
        if (type != STORE_PRIVATE_NONE) {
            setVar("storemode", String.valueOf(type), -1);
        } else {
            unsetVar("storemode");
        }
    }

    @Override
    public Clan getClan() {
        return _clan;
    }

    /**
     * Set the _clan object, _clanId, _clanLeader Flag and title of the L2Player.<BR> <BR>
     *
     * @param clan the clat to set
     */
    public void setClan(Clan clan) {
        if (_clan != clan && _clan != null) {
            unsetVar("canWhWithdraw");
        }

        Clan oldClan = _clan;
        if (oldClan != null && clan == null) {
            for (Skill skill : oldClan.getAllSkills()) {
                removeSkill(skill, false);
            }
        }

        _clan = clan;

        if (clan == null) {
            _pledgeType = Clan.SUBUNIT_NONE;
            _pledgeClass = 0;
            _powerGrade = 0;
            _apprentice = 0;
            getInventory().validateItems();
            return;
        }

        if (!clan.isAnyMember(getObjectId())) {
            setClan(null);
            if (!isNoble()) {
                setTitle("");
            }
        }
    }

    public SubUnit getSubUnit() {
        return _clan == null ? null : _clan.getSubUnit(_pledgeType);
    }

    public ClanHall getClanHall() {
        int id = _clan != null ? _clan.getHasHideout() : 0;
        return ResidenceHolder.getInstance().getResidence(ClanHall.class, id);
    }

    public Castle getCastle() {
        int id = _clan != null ? _clan.getCastle() : 0;
        return ResidenceHolder.getInstance().getResidence(Castle.class, id);
    }

    public Fortress getFortress() {
        int id = _clan != null ? _clan.getHasFortress() : 0;
        return ResidenceHolder.getInstance().getResidence(Fortress.class, id);
    }

    public Alliance getAlliance() {
        return _clan == null ? null : _clan.getAlliance();
    }

    public boolean isClanLeader() {
        return _clan != null && getObjectId() == _clan.getLeaderId();
    }

    public boolean isAllyLeader() {
        return getAlliance() != null && getAlliance().getLeader().getLeaderId() == getObjectId();
    }

    @Override
    public void reduceArrowCount() {
        ItemInstance newItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
        if (newItem.getItemType() == EtcItemType.UNLIMITED_ARROW) {
            return;
        }
        sendPacket(SystemMsg.YOU_CAREFULLY_NOCK_AN_ARROW);
        if (!getInventory().destroyItemByObjectId(getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LHAND),
                1L)) {
            getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, null);
            _arrowItem = null;
        }
    }

    /**
     * Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2Player then return True.
     */
    protected boolean checkAndEquipArrows() {
        // Check if nothing is equipped in left hand
        if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) {
            ItemInstance activeWeapon = getActiveWeaponInstance();
            if (activeWeapon != null) {
                if (activeWeapon.getItemType() == WeaponType.BOW) {
                    _arrowItem = getInventory().findArrowForBow(activeWeapon.getTemplate());
                } else if (activeWeapon.getItemType() == WeaponType.CROSSBOW) {
                    getInventory().findArrowForCrossbow(activeWeapon.getTemplate());
                }
            }

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

        return _arrowItem != null;
    }

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

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

    public boolean isInParty() {
        return _party != null;
    }

    public void joinParty(final Party party) {
        if (party != null) {
            party.addPartyMember(this);
        }
    }

    public void leaveParty() {
        if (isInParty()) {
            _party.removePartyMember(this, false);
        }
    }

    public Party getParty() {
        return _party;
    }

    public void setParty(final Party party) {
        _party = party;
    }

    public Location getLastPartyPosition() {
        return _lastPartyPosition;
    }

    public void setLastPartyPosition(Location loc) {
        _lastPartyPosition = loc;
    }

    public boolean isGM() {
        return _playerAccess == null ? false : _playerAccess.IsGM;
    }

    /**
     *   ?,   ? ? 
     */
    @Override
    public int getAccessLevel() {
        return _accessLevel;
    }

    /**
     *   ?,   ? ? 
     */
    public void setAccessLevel(final int level) {
        _accessLevel = level;
    }

    public PlayerAccess getPlayerAccess() {
        return _playerAccess;
    }

    public void setPlayerAccess(final PlayerAccess pa) {
        if (pa != null) {
            _playerAccess = pa;
        } else {
            _playerAccess = new PlayerAccess();
        }

        setAccessLevel(isGM() || _playerAccess.Menu ? 100 : 0);
    }

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

    /**
     * Update Stats of the L2Player client side by sending Server->Client packet UserInfo/StatusUpdate to this L2Player
     * and CharInfo/StatusUpdate to all players around (broadcast).<BR> <BR>
     */
    @Override
    public void updateStats() {
        if (entering || isLogoutStarted()) {
            return;
        }

        refreshOverloaded();
        refreshExpertisePenalty();
        super.updateStats();
    }

    @Override
    public void sendChanges() {
        if (entering || isLogoutStarted()) {
            return;
        }
        super.sendChanges();
    }

    /**
     * Send a Server->Client StatusUpdate packet with Karma to the L2Player and all L2Player to inform (broadcast).
     */
    public void updateKarma(boolean flagChanged) {
        sendStatusUpdate(true, true, StatusUpdate.KARMA);
        if (flagChanged) {
            broadcastRelationChanged();
        }
    }

    public boolean isOnline() {
        return _isOnline;
    }

    public void setIsOnline(boolean isOnline) {
        _isOnline = isOnline;
    }

    public void setOnlineStatus(boolean isOnline) {
        _isOnline = isOnline;
        updateOnlineStatus();
    }

    private void updateOnlineStatus() {
        Connection con = null;
        PreparedStatement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE obj_id=?");
            statement.setInt(1, isOnline() && !isInOfflineMode() ? 1 : 0);
            statement.setLong(2, System.currentTimeMillis() / 1000L);
            statement.setInt(3, getObjectId());
            statement.execute();
        } catch (final Exception e) {
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }
    }

    /**
     * Decrease Karma of the L2Player and Send it StatusUpdate packet with Karma and PvP Flag (broadcast).
     */
    public void increaseKarma(final long add_karma) {
        long new_karma = _karma + add_karma;

        if (new_karma > Integer.MAX_VALUE) {
            new_karma = Integer.MAX_VALUE;
        }

        if (_karma == 0 && new_karma < 0) {
            if (_pvpFlag > 0) {
                _pvpFlag = 0;
                if (_PvPRegTask != null) {
                    _PvPRegTask.cancel(true);
                    _PvPRegTask = null;
                }
                sendStatusUpdate(true, true, StatusUpdate.PVP_FLAG);
            }

            _karma = (int) new_karma;
        } else {
            _karma = (int) new_karma;
        }

        updateKarma(_karma == 0);
    }

    /**
     * Decrease Karma of the L2Player and Send it StatusUpdate packet with Karma and PvP Flag (broadcast).
     */
    public void decreaseKarma(final int i) {
        _karma -= i;
        updateKarma(_karma > 0);
    }

    public Map<Integer, PremiumItem> getPremiumItemList() {
        return _premiumItems;
    }

    public void setPremiumItemList(Map<Integer, PremiumItem> premiumItemList) {
        this._premiumItems = premiumItemList;
    }

    public final void addPremiumItem(Player player, int itemId, long count, Boolean saveInBD) {
        PremiumItem item = new PremiumItem(itemId, count, player.toString());
        _premiumItems.put(_premiumItems.size() + 1, item);
    }

    public void vitalityNewInit() {
        _vitality.restore();
    }

    /**
     * Update L2Player stats in the characters table of the database.
     */
    public void store(boolean fast) {
        if (!_storeLock.tryLock()) {
            return;
        }

        try {
            Connection con = null;
            PreparedStatement statement = null;
            try {
                con = DatabaseFactory.getInstance().getConnection();
                statement = con.prepareStatement(
                        "UPDATE characters SET face=?,hairStyle=?,hairColor=?,sex=?,x=?,y=?,z=?,karma=?,pvpkills=?,pkkills=?,rec_have=?,rec_left=?,rec_bonus_time=?,clanid=?,deletetime=?,title=?,accesslevel=?,online=?,leaveclan=?,deleteclan=?,nochannel=?,onlinetime=?,pledge_type=?,pledge_rank=?,lvl_joined_academy=?,apprentice=?,key_bindings=?,pcBangPoints=?,char_name=?,fame=?,bookmarks=? WHERE obj_Id=? LIMIT 1");
                statement.setInt(1, getFace());
                statement.setInt(2, getHairStyle());
                statement.setInt(3, getHairColor());
                statement.setInt(4, getSex());
                if (_stablePoint == null) {
                    statement.setInt(5, getX());
                    statement.setInt(6, getY());
                    statement.setInt(7, getZ());
                } else {
                    statement.setInt(5, _stablePoint.x);
                    statement.setInt(6, _stablePoint.y);
                    statement.setInt(7, _stablePoint.z);
                }
                statement.setInt(8, getKarma());
                statement.setInt(9, getPvpKills());
                statement.setInt(10, getPkKills());
                statement.setInt(11, getRecomHave());
                statement.setInt(12, getRecomLeft());
                statement.setInt(13, getRecomBonusTime());
                statement.setInt(14, getClanId());
                statement.setInt(15, getDeleteTimer());
                statement.setString(16, _title);
                statement.setInt(17, _accessLevel);
                statement.setInt(18, isOnline() && !isInOfflineMode() ? 1 : 0);
                statement.setLong(19, getLeaveClanTime() / 1000L);
                statement.setLong(20, getDeleteClanTime() / 1000L);
                statement.setLong(21, _NoChannel > 0 ? getNoChannelRemained() / 1000 : _NoChannel);
                statement.setInt(22,
                        (int) (_onlineBeginTime > 0
                                ? (_onlineTime + System.currentTimeMillis() - _onlineBeginTime) / 1000L
                                : _onlineTime / 1000L));

                // long totalOnlineTime = (int) (_onlineBeginTime > 0 ?
                // (_onlineTime + System.currentTimeMillis() - _onlineBeginTime)
                // / 1000L : _onlineTime / 1000L);

                if (_onlineBeginTime > 0L) {
                    WorldStatisticsManager.getInstance().updateStat(this, CategoryType.TIME_PLAYED,
                            (System.currentTimeMillis() - _onlineBeginTime) / 1000);
                }

                statement.setInt(23, getPledgeType());
                statement.setInt(24, getPowerGrade());
                statement.setInt(25, getLvlJoinedAcademy());
                statement.setInt(26, getApprentice());
                statement.setBytes(27, getKeyBindings());
                statement.setInt(28, getPcBangPoints());
                statement.setString(29, getName());
                statement.setInt(30, getFame());
                statement.setInt(31, bookmarks.getCapacity());
                statement.setInt(32, getObjectId());

                statement.executeUpdate();
                GameStats.increaseUpdatePlayerBase();

                if (!fast) {
                    EffectsDAO.getInstance().insert(this);
                    CharacterGroupReuseDAO.getInstance().insert(this);
                    CharactersPremiumItemsDAO.getInstance().insert(this);
                    storeDisableSkills();
                    storeBlockList();
                }

                storeCharSubClasses();
                bookmarks.store();
            } catch (Exception e) {
                _log.error("Could not store char data: " + this + "!", e);
            } finally {
                DbUtils.closeQuietly(con, statement);
            }
        } finally {
            _storeLock.unlock();
        }
    }

    /**
     * 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.
     *
     * @return The L2Skill replaced or null if just added a new L2Skill
     */

    public Skill addSkill(final Skill newSkill, final boolean store) {
        if (newSkill == null) {
            return null;
        }

        if (newSkill.isRelationSkill()) {
            final int[] _ss = newSkill.getRelationSkills();
            for (int _k : _ss) {
                Skill delSkill = removeSkill(_k, true);
                if (delSkill != null) {
                    AwakingManager.getInstance().giveGiantEssences(this, false);
                }
                //EssenceGiants.compensate(this, delSkill);
            }
        }
        // Add a skill to the L2Player _skills and its Func objects to the
        // calculator set of the L2Player
        Skill oldSkill = super.addSkill(newSkill);

        if (newSkill.equals(oldSkill)) {
            return oldSkill;
        }

        // Add or update a L2Player skill in the character_skills table of the
        // database
        if (store) {
            storeSkill(newSkill, oldSkill);
        }

        return oldSkill;
    }

    public Skill removeSkill(Skill skill, boolean fromDB) {
        if (skill == null) {
            return null;
        }
        return removeSkill(skill.getId(), fromDB);
    }

    /**
     * Remove a skill from the L2Character and its Func objects from calculator set of the L2Character and save update
     * in the character_skills table of the database.
     *
     * @return The L2Skill removed
     */
    public Skill removeSkill(int id, boolean fromDB) {
        // Remove a skill from the L2Character and its Func objects from
        // calculator set of the L2Character
        Skill oldSkill = super.removeSkillById(id);

        if (!fromDB) {
            return oldSkill;
        }

        if (oldSkill != null) {
            Connection con = null;
            PreparedStatement statement = null;
            try {
                // Remove or update a L2Player skill from the character_skills
                // table of the database
                con = DatabaseFactory.getInstance().getConnection();
                statement = con.prepareStatement(
                        "DELETE FROM character_skills WHERE skill_id=? AND char_obj_id=? AND class_index=?");
                statement.setInt(1, oldSkill.getId());
                statement.setInt(2, getObjectId());
                statement.setInt(3, getActiveClassId());
                statement.execute();
            } catch (final Exception e) {
                _log.error("Could not delete skill!", e);
            } finally {
                DbUtils.closeQuietly(con, statement);
            }
        }

        return oldSkill;
    }

    /**
     * Add or update a L2Player skill in the character_skills table of the database.
     */
    private void storeSkill(final Skill newSkill, final Skill oldSkill) {
        if (newSkill == null) // - 
        {
            _log.warn("could not store new skill. its NULL");
            return;
        }

        Connection con = null;
        PreparedStatement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();

            statement = con.prepareStatement(
                    "REPLACE INTO character_skills (char_obj_id,skill_id,skill_level,class_index) values(?,?,?,?)");
            statement.setInt(1, getObjectId());
            statement.setInt(2, newSkill.getId());
            statement.setInt(3, newSkill.getLevel());
            statement.setInt(4, getActiveClassId());
            statement.execute();
        } catch (final Exception e) {
            _log.error("Error could not store skills!", e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }
    }

    /**
     * Retrieve from the database all skills of this L2Player and add them to _skills.
     */
    private void restoreSkills() {
        Connection con = null;
        PreparedStatement statement = null;
        ResultSet rset = null;
        try {
            // Retrieve all skills of this L2Player from the database
            // Send the SQL query : SELECT skill_id,skill_level FROM
            // character_skills WHERE char_obj_id=? to the database
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement(
                    "SELECT skill_id,skill_level FROM character_skills WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, getActiveClassId());
            rset = statement.executeQuery();

            // Go though the recordset of this SQL query
            while (rset.next()) {
                final int id = rset.getInt("skill_id");
                final int level = rset.getInt("skill_level");

                // Create a L2Skill object for each record
                final Skill skill = SkillTable.getInstance().getInfo(id, level);

                if (skill == null) {
                    continue;
                }

                // Remove skill if not possible
                //if (!isGM() && !SkillAcquireHolder.getInstance().isSkillPossible(this, skill)) {
                // int ReturnSP =
                // SkillTreeTable.getInstance().getSkillCost(this, skill);
                // if(ReturnSP == Integer.MAX_VALUE || ReturnSP < 0)
                // ReturnSP = 0;
                //removeSkill(skill, true);
                //removeSkillFromShortCut(skill.getId());
                // if(ReturnSP > 0)
                // setSp(getSp() + ReturnSP);
                // TODO audit
                //continue;
                //}

                super.addSkill(skill);
            }

            // Restore noble skills
            if (isNoble()) {
                updateNobleSkills();
            }

            // Restore Hero skills at main class only
            if (_hero && getSubClassList().isBaseClassActive()) {
                Hero.addSkills(this);
            }

            // Restore clan skills
            if (_clan != null) {
                _clan.addSkillsQuietly(this);

                // Restore clan leader siege skills
                if (_clan.getLeaderId() == getObjectId() && _clan.getLevel() >= 5) {
                    SiegeUtils.addSiegeSkills(this);
                }
            }

            // Give dwarven craft skill
            if (getActiveClassId() >= 53 && getActiveClassId() <= 57 || getActiveClassId() == 117
                    || getActiveClassId() == 118) {
                super.addSkill(SkillTable.getInstance().getInfo(1321, 1));
            }

            super.addSkill(SkillTable.getInstance().getInfo(1322, 1));

            if (Config.UNSTUCK_SKILL && getSkillLevel(1050) < 0) {
                super.addSkill(SkillTable.getInstance().getInfo(2099, 1));
            }
        } catch (final Exception e) {
            _log.warn("Could not restore skills for player objId: " + getObjectId());
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, statement, rset);
        }
    }

    public void storeDisableSkills() {
        Connection con = null;
        Statement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.createStatement();
            statement.executeUpdate("DELETE FROM character_skills_save WHERE char_obj_id = " + getObjectId()
                    + " AND class_index=" + getActiveClassId() + " AND `end_time` < " + System.currentTimeMillis());

            if (_skillReuses.isEmpty()) {
                return;
            }

            SqlBatch b = new SqlBatch(
                    "REPLACE INTO `character_skills_save` (`char_obj_id`,`skill_id`,`skill_level`,`class_index`,`end_time`,`reuse_delay_org`) VALUES");
            synchronized (_skillReuses) {
                StringBuilder sb;
                for (TimeStamp timeStamp : _skillReuses.values()) {
                    if (timeStamp.hasNotPassed()) {
                        sb = new StringBuilder("(");
                        sb.append(getObjectId()).append(",");
                        sb.append(timeStamp.getId()).append(",");
                        sb.append(timeStamp.getLevel()).append(",");
                        sb.append(getActiveClassId()).append(",");
                        sb.append(timeStamp.getEndTime()).append(",");
                        sb.append(timeStamp.getReuseBasic()).append(")");
                        b.write(sb.toString());
                    }
                }
            }
            if (!b.isEmpty()) {
                statement.executeUpdate(b.close());
            }
        } catch (final Exception e) {
            _log.warn("Could not store disable skills data: " + e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }
    }

    public void restoreDisableSkills() {
        _skillReuses.clear();

        Connection con = null;
        Statement statement = null;
        ResultSet rset = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.createStatement();
            rset = statement.executeQuery(
                    "SELECT skill_id,skill_level,end_time,reuse_delay_org FROM character_skills_save WHERE char_obj_id="
                            + getObjectId() + " AND class_index=" + getActiveClassId());
            while (rset.next()) {
                int skillId = rset.getInt("skill_id");
                int skillLevel = rset.getInt("skill_level");
                long endTime = rset.getLong("end_time");
                long rDelayOrg = rset.getLong("reuse_delay_org");
                long curTime = System.currentTimeMillis();

                Skill skill = SkillTable.getInstance().getInfo(skillId, skillLevel);

                if (skill != null && endTime - curTime > 500) {
                    _skillReuses.put(skill.hashCode(), new TimeStamp(skill, endTime, rDelayOrg));
                }
            }
            DbUtils.close(statement);

            statement = con.createStatement();
            statement.executeUpdate("DELETE FROM character_skills_save WHERE char_obj_id = " + getObjectId()
                    + " AND class_index=" + getActiveClassId() + " AND `end_time` < " + System.currentTimeMillis());
        } catch (Exception e) {
            _log.error("Could not restore active skills data!", e);
        } finally {
            DbUtils.closeQuietly(con, statement, rset);
        }
    }

    /**
     * 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;
        PreparedStatement statement = null;
        ResultSet rset = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement(
                    "select slot, symbol_id from character_hennas where char_obj_id=? AND class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, getActiveClassId());
            rset = statement.executeQuery();

            for (int i = 0; i < 3; i++) {
                _henna[i] = null;
            }

            while (rset.next()) {
                final int slot = rset.getInt("slot");
                if (slot < 1 || slot > 3) {
                    continue;
                }

                final int symbol_id = rset.getInt("symbol_id");

                if (symbol_id != 0) {
                    final Henna tpl = HennaHolder.getInstance().getHenna(symbol_id);
                    if (tpl != null) {
                        _henna[slot - 1] = tpl;
                        if (tpl.getSkillId() > 0) {
                            Skill skill = SkillTable.getInstance().getInfo(tpl.getSkillId(), 1);
                            if (skill != null) {
                                addSkill(skill, true);
                            }
                        }
                    }
                }
            }
        } catch (final Exception e) {
            _log.warn("could not restore henna: " + e);
        } finally {
            DbUtils.closeQuietly(con, statement, rset);
        }

        // Calculate Henna modifiers of this L2Player
        recalcHennaStats();

    }

    public int getHennaEmptySlots() {
        int totalSlots = 1 + getClassLevel();
        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;
        }

        final Henna henna = _henna[slot];
        final int dyeID = henna.getDyeId();

        _henna[slot] = null;

        Connection con = null;
        PreparedStatement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement(
                    "DELETE FROM character_hennas where char_obj_id=? and slot=? and class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, slot + 1);
            statement.setInt(3, getActiveClassId());
            statement.execute();
        } catch (final Exception e) {
            _log.warn("could not remove char henna: " + e, e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }

        if (henna.getSkillId() > 0) {
            removeSkillById(henna.getSkillId());
        }

        // 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
        sendUserInfo(true);

        // Add the recovered dyes to the player's inventory and notify them.
        ItemFunctions.addItem(this, dyeID, henna.getDrawCount() / 2, true);

        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>
     *
     * @param henna L2Henna ? ?
     */
    public boolean addHenna(Henna henna) {
        if (getHennaEmptySlots() == 0) {
            sendPacket(SystemMsg.NO_SLOT_EXISTS_TO_DRAW_THE_SYMBOL);
            return false;
        }

        // int slot = 0;
        for (int i = 0; i < 3; i++) {
            if (_henna[i] == null) {
                _henna[i] = henna;

                // Calculate Henna modifiers of this L2Player
                recalcHennaStats();

                Connection con = null;
                PreparedStatement statement = null;
                try {
                    con = DatabaseFactory.getInstance().getConnection();
                    statement = con.prepareStatement(
                            "INSERT INTO `character_hennas` (char_obj_id, symbol_id, slot, class_index) VALUES (?,?,?,?)");
                    statement.setInt(1, getObjectId());
                    statement.setInt(2, henna.getSymbolId());
                    statement.setInt(3, i + 1);
                    statement.setInt(4, getActiveClassId());
                    statement.execute();
                } catch (Exception e) {
                    _log.warn("could not save char henna: " + e);
                } finally {
                    DbUtils.closeQuietly(con, statement);
                }

                if (henna.getSkillId() > 0) {
                    Skill skill = SkillTable.getInstance().getInfo(henna.getSkillId(), 1);
                    if (skill != null) {
                        addSkill(skill, true);
                    }
                }

                sendPacket(new HennaInfo(this));
                sendUserInfo(true);

                return true;
            }
        }

        return false;
    }

    /**
     * Calculate Henna modifiers of this L2Player.
     */
    private void recalcHennaStats() {
        _hennaINT = 0;
        _hennaSTR = 0;
        _hennaCON = 0;
        _hennaMEN = 0;
        _hennaWIT = 0;
        _hennaDEX = 0;

        for (int i = 0; i < 3; i++) {
            Henna henna = _henna[i];
            if (henna == null) {
                continue;
            }
            if (!henna.isForThisClass(this)) {
                continue;
            }

            _hennaINT += henna.getStatINT();
            _hennaSTR += henna.getStatSTR();
            _hennaMEN += henna.getStatMEN();
            _hennaCON += henna.getStatCON();
            _hennaWIT += henna.getStatWIT();
            _hennaDEX += henna.getStatDEX();
        }

        if (_hennaINT > 15) {
            _hennaINT = 15;
        }
        if (_hennaSTR > 15) {
            _hennaSTR = 15;
        }
        if (_hennaMEN > 15) {
            _hennaMEN = 15;
        }
        if (_hennaCON > 15) {
            _hennaCON = 15;
        }
        if (_hennaWIT > 15) {
            _hennaWIT = 15;
        }
        if (_hennaDEX > 15) {
            _hennaDEX = 15;
        }
    }

    /**
     * @param slot id   
     * @return the Henna of this L2Player corresponding to the selected slot.<BR> <BR>
     */
    public Henna getHenna(final int slot) {
        if (slot < 1 || slot > 3) {
            return null;
        }
        return _henna[slot - 1];
    }

    public int getHennaStatINT() {
        return _hennaINT;
    }

    public int getHennaStatSTR() {
        return _hennaSTR;
    }

    public int getHennaStatCON() {
        return _hennaCON;
    }

    public int getHennaStatMEN() {
        return _hennaMEN;
    }

    public int getHennaStatWIT() {
        return _hennaWIT;
    }

    public int getHennaStatDEX() {
        return _hennaDEX;
    }

    @Override
    public boolean consumeItem(int itemConsumeId, long itemCount) {
        if (getInventory().destroyItemByItemId(itemConsumeId, itemCount)) {
            sendPacket(SystemMessage2.removeItems(itemConsumeId, itemCount));
            return true;
        }
        return false;
    }

    @Override
    public boolean consumeItemMp(int itemId, int mp) {
        for (ItemInstance item : getInventory().getPaperdollItems()) {
            if (item != null && item.getItemId() == itemId) {
                final int newMp = item.getLifeTime() - mp;
                if (newMp >= 0) {
                    item.setLifeTime(newMp);
                    sendPacket(new InventoryUpdate().addModifiedItem(item));
                    sendPacket(new ExAdenaInvenCount(this));
                    return true;
                }
                break;
            }
        }
        return false;
    }

    /**
     * @return True if the L2Player is a Mage.<BR> <BR>
     */
    @Override
    public boolean isMageClass() {
        return getClassId().isMage();
    }

    public boolean isMounted() {
        return _mountNpcId > 0;
    }

    public final boolean isRiding() {
        return _riding;
    }

    public final void setRiding(boolean mode) {
        _riding = mode;
    }

    /**
     * ?,   ?   .
     *
     * @return   ?
     */
    public boolean checkLandingState() {
        if (isInZone(ZoneType.no_landing)) {
            return false;
        }

        SiegeEvent<?, ?> siege = getEvent(SiegeEvent.class);
        if (siege != null) {
            Residence unit = siege.getResidence();
            if (unit != null && getClan() != null && isClanLeader()
                    && (getClan().getCastle() == unit.getId() || getClan().getHasFortress() == unit.getId())) {
                return true;
            }
            return false;
        }

        return true;
    }

    public void setMount(int npcId, int obj_id, int level) {
        if (isCursedWeaponEquipped()) {
            return;
        }

        switch (npcId) {
        case 0: // Dismount
            setFlying(false);
            setRiding(false);
            if (getTransformation() > 0) {
                setTransformation(0);
            }
            removeSkillById(Skill.SKILL_STRIDER_ASSAULT);
            removeSkillById(Skill.SKILL_WYVERN_BREATH);
            getEffectList().stopEffect(Skill.SKILL_HINDER_STRIDER);
            break;
        case PetDataTable.STRIDER_WIND_ID:
        case PetDataTable.STRIDER_STAR_ID:
        case PetDataTable.STRIDER_TWILIGHT_ID:
        case PetDataTable.RED_STRIDER_WIND_ID:
        case PetDataTable.RED_STRIDER_STAR_ID:
        case PetDataTable.RED_STRIDER_TWILIGHT_ID:
        case PetDataTable.GUARDIANS_STRIDER_ID:
            setRiding(true);
            if (isNoble()) {
                addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_STRIDER_ASSAULT, 1), false);
            }
            break;
        case PetDataTable.WYVERN_ID:
            setFlying(true);
            setLoc(getLoc().changeZ(32));
            addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_WYVERN_BREATH, 1), false);
            break;
        case PetDataTable.WGREAT_WOLF_ID:
        case PetDataTable.FENRIR_WOLF_ID:
        case PetDataTable.WFENRIR_WOLF_ID:
            setRiding(true);
            break;
        }

        if (npcId > 0) {
            unEquipWeapon();
        }

        _mountNpcId = npcId;
        _mountObjId = obj_id;
        _mountLevel = level;

        broadcastUserInfo(true); //   ?  Ride ?
        // ?? ?? ?  ?
        broadcastPacket(new Ride(this));
        broadcastUserInfo(true); //   ?  Ride ?
        // ?? ? ?

        sendSkillList();
    }

    public void unEquipWeapon() {
        ItemInstance wpn = getSecondaryWeaponInstance();
        if (wpn != null) {
            sendDisarmMessage(wpn);
            getInventory().unEquipItem(wpn);
        }

        wpn = getActiveWeaponInstance();
        if (wpn != null) {
            sendDisarmMessage(wpn);
            getInventory().unEquipItem(wpn);
        }

        abortAttack(true, true);
        abortCast(true, true);
    }

    public int getSpeed(int baseSpeed) {
        if (isMounted()) {
            PetData petData = PetDataTable.getInstance().getInfo(_mountNpcId, _mountLevel);
            int speed = 187;
            if (petData != null) {
                speed = petData.getSpeed();
            }
            double mod = 1.;
            int level = getLevel();
            if (_mountLevel > level && level - _mountLevel > 10) {
                mod = 0.5;
            }
            baseSpeed = (int) (mod * speed);
        }
        return super.getSpeed(baseSpeed);
    }

    public int getMountNpcId() {
        return _mountNpcId;
    }

    public int getMountObjId() {
        return _mountObjId;
    }

    public int getMountLevel() {
        return _mountLevel;
    }

    public void sendDisarmMessage(ItemInstance wpn) {
        if (wpn.getEnchantLevel() > 0) {
            SystemMessage sm = new SystemMessage(SystemMessage.EQUIPMENT_OF__S1_S2_HAS_BEEN_REMOVED);
            sm.addNumber(wpn.getEnchantLevel());
            sm.addItemName(wpn.getItemId());
            sendPacket(sm);
        } else {
            SystemMessage sm = new SystemMessage(SystemMessage.S1__HAS_BEEN_DISARMED);
            sm.addItemName(wpn.getItemId());
            sendPacket(sm);
        }
    }

    /**
     *  ? ? .
     *
     * @return null  ? :<br> <ul> <li>WarehouseType.PRIVATE <li>WarehouseType.CLAN
     *         <li>WarehouseType.CASTLE </ul>
     */
    public WarehouseType getUsingWarehouseType() {
        return _usingWHType;
    }

    /**
     *    ?.
     *
     * @param type  ?:<BR> <ul> <li>WarehouseType.PRIVATE <li>WarehouseType.CLAN <li>WarehouseType.CASTLE </ul>
     */
    public void setUsingWarehouseType(final WarehouseType type) {
        _usingWHType = type;
    }

    public Collection<EffectCubic> getCubics() {
        return _cubics == null ? Collections.<EffectCubic>emptyList() : _cubics.values();
    }

    public void addCubic(EffectCubic cubic) {
        if (_cubics == null) {
            _cubics = new ConcurrentHashMap<Integer, EffectCubic>(3);
        }
        _cubics.put(cubic.getId(), cubic);
    }

    public void removeCubic(int id) {
        if (_cubics != null) {
            _cubics.remove(id);
        }
    }

    public EffectCubic getCubic(int id) {
        return _cubics == null ? null : _cubics.get(id);
    }

    @Override
    public String toString() {
        return getName() + "[" + getObjectId() + "]";
    }

    /**
     * @return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127).<BR> <BR>
     */
    public int getEnchantEffect() {
        final ItemInstance wpn = getActiveWeaponInstance();

        if (wpn == null) {
            return 0;
        }

        return Math.min(127, wpn.getEnchantLevel());
    }

    /**
     * @return the _lastFolkNpc of the L2Player corresponding to the last Folk witch one the player talked.<BR> <BR>
     */
    public NpcInstance getLastNpc() {
        return _lastNpc.get();
    }

    /**
     * Set the _lastFolkNpc of the L2Player corresponding to the last Folk witch one the player talked.<BR> <BR>
     */
    public void setLastNpc(final NpcInstance npc) {
        if (npc == null) {
            _lastNpc = HardReferences.emptyRef();
        } else {
            _lastNpc = npc.getRef();
        }
    }

    public MultiSellListContainer getMultisell() {
        return _multisell;
    }

    public void setMultisell(MultiSellListContainer multisell) {
        _multisell = multisell;
    }

    @Override
    public boolean unChargeShots(boolean spirit) {
        ItemInstance weapon = getActiveWeaponInstance();
        if (weapon == null) {
            return false;
        }

        if (spirit) {
            weapon.setChargedSpiritshot(ItemInstance.CHARGED_NONE);
        } else {
            weapon.setChargedSoulshot(ItemInstance.CHARGED_NONE);
        }

        autoShot();
        return true;
    }

    public boolean unChargeFishShot() {
        ItemInstance weapon = getActiveWeaponInstance();
        if (weapon == null) {
            return false;
        }
        weapon.setChargedFishshot(false);
        autoShot();
        return true;
    }

    public void autoShot() {
        for (Integer shotId : _activeSoulShots) {
            ItemInstance item = getInventory().getItemByItemId(shotId);
            if (item == null) {
                removeAutoSoulShot(shotId);
                continue;
            }
            IItemHandler handler = item.getTemplate().getHandler();
            if (handler == null) {
                continue;
            }
            handler.useItem(this, item, false);
        }
    }

    public boolean getChargedFishShot() {
        ItemInstance weapon = getActiveWeaponInstance();
        return weapon != null && weapon.getChargedFishshot();
    }

    @Override
    public boolean getChargedSoulShot() {
        ItemInstance weapon = getActiveWeaponInstance();
        return weapon != null && weapon.getChargedSoulshot() == ItemInstance.CHARGED_SOULSHOT;
    }

    @Override
    public int getChargedSpiritShot() {
        ItemInstance weapon = getActiveWeaponInstance();
        if (weapon == null) {
            return 0;
        }
        return weapon.getChargedSpiritshot();
    }

    public void addAutoSoulShot(Integer itemId) {
        _activeSoulShots.add(itemId);
    }

    public void removeAutoSoulShot(Integer itemId) {
        _activeSoulShots.remove(itemId);
    }

    public Set<Integer> getAutoSoulShot() {
        return _activeSoulShots;
    }

    @Override
    public InvisibleType getInvisibleType() {
        return _invisibleType;
    }

    public void setInvisibleType(InvisibleType vis) {
        _invisibleType = vis;
    }

    public int getClanPrivileges() {
        if (_clan == null) {
            return 0;
        }
        if (isClanLeader()) {
            return Clan.CP_ALL;
        }
        if (_powerGrade < 1 || _powerGrade > 9) {
            return 0;
        }
        RankPrivs privs = _clan.getRankPrivs(_powerGrade);
        if (privs != null) {
            return privs.getPrivs();
        }
        return 0;
    }

    public void teleToClosestTown() {
        teleToLocation(TeleportUtils.getRestartLocation(this, RestartType.TO_VILLAGE), ReflectionManager.DEFAULT);
    }

    public void teleToCastle() {
        teleToLocation(TeleportUtils.getRestartLocation(this, RestartType.TO_CASTLE), ReflectionManager.DEFAULT);
    }

    public void teleToFortress() {
        teleToLocation(TeleportUtils.getRestartLocation(this, RestartType.TO_FORTRESS), ReflectionManager.DEFAULT);
    }

    public void teleToClanhall() {
        teleToLocation(TeleportUtils.getRestartLocation(this, RestartType.TO_CLANHALL), ReflectionManager.DEFAULT);
    }

    @Override
    public void sendMessage(CustomMessage message) {
        sendMessage(message.toString());
    }

    @Override
    public void teleToLocation(int x, int y, int z, int refId) {
        if (isDeleted()) {
            return;
        }

        super.teleToLocation(x, y, z, refId);
    }

    @Override
    public void teleToLocation(Location loc, int refId) {
        if (isDeleted()) {
            return;
        }

        super.teleToLocation(loc, refId);
    }

    @Override
    public boolean onTeleported() {
        if (!super.onTeleported()) {
            return false;
        }

        if (isFakeDeath()) {
            breakFakeDeath();
        }

        if (isInBoat()) {
            setLoc(getBoat().getLoc());
        }

        // 15 ?      ?? 
        setNonAggroTime(System.currentTimeMillis() + Config.NONAGGRO_TIME_ONTELEPORT);

        spawnMe();

        setLastClientPosition(getLoc());
        setLastServerPosition(getLoc());

        if (isPendingRevive()) {
            doRevive();
        }

        sendActionFailed();

        getAI().notifyEvent(CtrlEvent.EVT_TELEPORTED);

        if (isLockedTarget() && getTarget() != null) {
            sendPacket(new MyTargetSelected(getTarget().getObjectId(), 0));
        }

        sendUserInfo(true);
        for (Summon summon : getPets()) {
            summon.teleportToOwner();
        }

        return true;
    }

    public boolean enterObserverMode(Location loc) {
        WorldRegion observerRegion = World.getRegion(loc);
        if (observerRegion == null) {
            return false;
        }
        if (!_observerMode.compareAndSet(OBSERVER_NONE, OBSERVER_STARTING)) {
            return false;
        }

        setTarget(null);
        stopMove();
        sitDown(null);
        setFlying(true);

        //    ?
        World.removeObjectsFromPlayer(this);

        setObserverRegion(observerRegion);

        //    
        broadcastCharInfo();

        //    
        sendPacket(new ObserverStart(loc));

        return true;
    }

    public void appearObserverMode() {
        if (!_observerMode.compareAndSet(OBSERVER_STARTING, OBSERVER_STARTED)) {
            return;
        }

        WorldRegion currentRegion = getCurrentRegion();
        WorldRegion observerRegion = getObserverRegion();

        // ? ?  ? ?
        if (!observerRegion.equals(currentRegion)) {
            observerRegion.addObject(this);
        }

        World.showObjectsToPlayer(this);

        OlympiadGame game = getOlympiadObserveGame();
        if (game != null) {
            game.addSpectator(this);
            game.broadcastInfo(null, this, true);
        }
    }

    public void leaveObserverMode() {
        if (!_observerMode.compareAndSet(OBSERVER_STARTED, OBSERVER_LEAVING)) {
            return;
        }

        WorldRegion currentRegion = getCurrentRegion();
        WorldRegion observerRegion = getObserverRegion();

        //  ?  ? ?
        if (!observerRegion.equals(currentRegion)) {
            observerRegion.removeObject(this);
        }

        //    ?
        World.removeObjectsFromPlayer(this);

        setObserverRegion(null);

        setTarget(null);
        stopMove();

        //    
        sendPacket(new ObserverEnd(getLoc()));
    }

    public void returnFromObserverMode() {
        if (!_observerMode.compareAndSet(OBSERVER_LEAVING, OBSERVER_NONE)) {
            return;
        }

        //      ? ?   ?, 
        // ?   "?"
        setLastClientPosition(null);
        setLastServerPosition(null);

        unblock();
        standUp();
        setFlying(false);

        broadcastCharInfo();

        World.showObjectsToPlayer(this);
    }

    public void enterOlympiadObserverMode(Location loc, OlympiadGame game, Reflection reflect) {
        WorldRegion observerRegion = World.getRegion(loc);
        if (observerRegion == null) {
            return;
        }

        OlympiadGame oldGame = getOlympiadObserveGame();
        if (!_observerMode.compareAndSet(oldGame != null ? OBSERVER_STARTED : OBSERVER_NONE, OBSERVER_STARTING)) {
            return;
        }

        setTarget(null);
        stopMove();

        //    ?
        World.removeObjectsFromPlayer(this);
        WorldRegion oldObserverRegion = getObserverRegion();
        if (oldObserverRegion != null) {
            oldObserverRegion.removeObject(this);
        }
        setObserverRegion(observerRegion);

        if (oldGame != null) {
            oldGame.removeSpectator(this);
            sendPacket(ExOlympiadMatchEnd.STATIC);
        } else {
            block();

            //    
            broadcastCharInfo();

            // ? 
            sendPacket(new ExOlympiadMode(3));
        }

        setOlympiadObserveGame(game);

        // "?"
        setReflection(reflect);
        sendPacket(new TeleportToLocation(this, loc));
        sendPacket(new ExTeleportToLocationActivate());
    }

    public void leaveOlympiadObserverMode(boolean removeFromGame) {
        OlympiadGame game = getOlympiadObserveGame();
        if (game == null) {
            return;
        }
        if (!_observerMode.compareAndSet(OBSERVER_STARTED, OBSERVER_LEAVING)) {
            return;
        }

        if (removeFromGame) {
            game.removeSpectator(this);
        }
        setOlympiadObserveGame(null);

        WorldRegion currentRegion = getCurrentRegion();
        WorldRegion observerRegion = getObserverRegion();

        //  ?  ? ?
        if (observerRegion != null && currentRegion != null && !observerRegion.equals(currentRegion)) {
            observerRegion.removeObject(this);
        }

        //    ?
        World.removeObjectsFromPlayer(this);

        setObserverRegion(null);

        setTarget(null);
        stopMove();

        // ? 
        sendPacket(new ExOlympiadMode(0));
        sendPacket(ExOlympiadMatchEnd.STATIC);

        setReflection(ReflectionManager.DEFAULT);
        // "?"x
        sendPacket(new TeleportToLocation(this, getLoc()));
        sendPacket(new ExTeleportToLocationActivate());

    }

    public int getOlympiadSide() {
        return _olympiadSide;
    }

    public void setOlympiadSide(final int i) {
        _olympiadSide = i;
    }

    @Override
    public boolean isInObserverMode() {
        return _observerMode.get() > 0;
    }

    public int getObserverMode() {
        return _observerMode.get();
    }

    public WorldRegion getObserverRegion() {
        return _observerRegion;
    }

    public void setObserverRegion(WorldRegion region) {
        _observerRegion = region;
    }

    public int getTeleMode() {
        return _telemode;
    }

    public void setTeleMode(final int mode) {
        _telemode = mode;
    }

    public void setLoto(final int i, final int val) {
        _loto[i] = val;
    }

    public int getLoto(final int i) {
        return _loto[i];
    }

    public void setRace(final int i, final int val) {
        _race[i] = val;
    }

    public int getRace(final int i) {
        return _race[i];
    }

    public boolean getMessageRefusal() {
        return _messageRefusal;
    }

    public void setMessageRefusal(final boolean mode) {
        _messageRefusal = mode;
    }

    public boolean getTradeRefusal() {
        return _tradeRefusal;
    }

    public void setTradeRefusal(final boolean mode) {
        _tradeRefusal = mode;
    }

    public void addToBlockList(final String charName) {
        if (charName == null || charName.equalsIgnoreCase(getName()) || isInBlockList(charName)) {
            //   ?
            sendPacket(Msg.YOU_HAVE_FAILED_TO_REGISTER_THE_USER_TO_YOUR_IGNORE_LIST);
            return;
        }

        Player block_target = World.getPlayer(charName);

        if (block_target != null) {
            if (block_target.isGM()) {
                sendPacket(Msg.YOU_MAY_NOT_IMPOSE_A_BLOCK_ON_A_GM);
                return;
            }
            _blockList.put(block_target.getObjectId(), block_target.getName());
            sendPacket(new SystemMessage(SystemMessage.S1_HAS_BEEN_ADDED_TO_YOUR_IGNORE_LIST)
                    .addString(block_target.getName()));
            block_target.sendPacket(new SystemMessage(SystemMessage.S1__HAS_PLACED_YOU_ON_HIS_HER_IGNORE_LIST)
                    .addString(getName()));
            return;
        }

        int charId = CharacterDAO.getInstance().getObjectIdByName(charName);

        if (charId == 0) {
            //   
            sendPacket(Msg.YOU_HAVE_FAILED_TO_REGISTER_THE_USER_TO_YOUR_IGNORE_LIST);
            return;
        }

        if (Config.gmlist.containsKey(charId) && Config.gmlist.get(charId).IsGM) {
            sendPacket(Msg.YOU_MAY_NOT_IMPOSE_A_BLOCK_ON_A_GM);
            return;
        }
        _blockList.put(charId, charName);
        sendPacket(new SystemMessage(SystemMessage.S1_HAS_BEEN_ADDED_TO_YOUR_IGNORE_LIST).addString(charName));
    }

    public void removeFromBlockList(final String charName) {
        int charId = 0;
        for (int blockId : _blockList.keySet()) {
            if (charName.equalsIgnoreCase(_blockList.get(blockId))) {
                charId = blockId;
                break;
            }
        }
        if (charId == 0) {
            sendPacket(Msg.YOU_HAVE_FAILED_TO_DELETE_THE_CHARACTER_FROM_IGNORE_LIST);
            return;
        }
        sendPacket(new SystemMessage(SystemMessage.S1_HAS_BEEN_REMOVED_FROM_YOUR_IGNORE_LIST)
                .addString(_blockList.remove(charId)));
        Player block_target = GameObjectsStorage.getPlayer(charId);
        if (block_target != null) {
            block_target.sendMessage(getName() + " has removed you from his/her Ignore List."); // 
        }
        // (619
        // ==
        // 620)
        // 
        // ?
        // ;)
    }

    public boolean isInBlockList(final Player player) {
        return isInBlockList(player.getObjectId());
    }

    public boolean isInBlockList(final int charId) {
        return _blockList != null && _blockList.containsKey(charId);
    }

    public boolean isInBlockList(final String charName) {
        for (int blockId : _blockList.keySet()) {
            if (charName.equalsIgnoreCase(_blockList.get(blockId))) {
                return true;
            }
        }
        return false;
    }

    private void restoreBlockList() {
        _blockList.clear();

        Connection con = null;
        PreparedStatement statement = null;
        ResultSet rs = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement(
                    "SELECT target_Id, char_name FROM character_blocklist LEFT JOIN characters ON ( character_blocklist.target_Id = characters.obj_Id ) WHERE character_blocklist.obj_Id = ?");
            statement.setInt(1, getObjectId());
            rs = statement.executeQuery();
            while (rs.next()) {
                int targetId = rs.getInt("target_Id");
                String name = rs.getString("char_name");
                if (name == null) {
                    continue;
                }
                _blockList.put(targetId, name);
            }
        } catch (SQLException e) {
            _log.warn("Can't restore player blocklist " + e, e);
        } finally {
            DbUtils.closeQuietly(con, statement, rs);
        }
    }

    private void storeBlockList() {
        Connection con = null;
        Statement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.createStatement();
            statement.executeUpdate("DELETE FROM character_blocklist WHERE obj_Id=" + getObjectId());

            if (_blockList.isEmpty()) {
                return;
            }

            SqlBatch b = new SqlBatch("INSERT IGNORE INTO `character_blocklist` (`obj_Id`,`target_Id`) VALUES");

            synchronized (_blockList) {
                StringBuilder sb;
                for (Entry<Integer, String> e : _blockList.entrySet()) {
                    sb = new StringBuilder("(");
                    sb.append(getObjectId()).append(",");
                    sb.append(e.getKey()).append(")");
                    b.write(sb.toString());
                }
            }
            if (!b.isEmpty()) {
                statement.executeUpdate(b.close());
            }
        } catch (Exception e) {
            _log.warn("Can't store player blocklist " + e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }
    }

    public boolean isBlockAll() {
        return _blockAll;
    }

    public void setBlockAll(final boolean state) {
        _blockAll = state;
    }

    public Collection<String> getBlockList() {
        return _blockList.values();
    }

    public Map<Integer, String> getBlockListMap() {
        return _blockList;
    }

    @Override
    public boolean isHero() {
        return _hero;
    }

    public void setHero(final boolean hero) {
        _hero = hero;
    }

    public void setIsInOlympiadMode(final boolean b) {
        _inOlympiadMode = b;
    }

    @Override
    public boolean isInOlympiadMode() {
        return _inOlympiadMode;
    }

    public boolean isOlympiadGameStart() {
        return _olympiadGame != null && _olympiadGame.getState() == 1;
    }

    public boolean isOlympiadCompStart() {
        return _olympiadGame != null && _olympiadGame.getState() == 2;
    }

    public void updateNobleSkills() {
        if (isNoble()) {
            if (isClanLeader() && getClan().getCastle() > 0) {
                super.addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_WYVERN_AEGIS, 1));
            }
            super.addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_NOBLESSE_BLESSING, 1));
            super.addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_SUMMON_CP_POTION, 1));
            super.addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_FORTUNE_OF_NOBLESSE, 1));
            super.addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_HARMONY_OF_NOBLESSE, 1));
            super.addSkill(SkillTable.getInstance().getInfo(Skill.SKILL_SYMPHONY_OF_NOBLESSE, 1));
        } else {
            super.removeSkillById(Skill.SKILL_WYVERN_AEGIS);
            super.removeSkillById(Skill.SKILL_NOBLESSE_BLESSING);
            super.removeSkillById(Skill.SKILL_SUMMON_CP_POTION);
            super.removeSkillById(Skill.SKILL_FORTUNE_OF_NOBLESSE);
            super.removeSkillById(Skill.SKILL_HARMONY_OF_NOBLESSE);
            super.removeSkillById(Skill.SKILL_SYMPHONY_OF_NOBLESSE);
        }
    }

    public boolean isNoble() {
        return _noble;
    }

    public void setNoble(boolean noble) {
        if (noble) // broadcast skill animation: Presentation - Attain Noblesse
        {
            broadcastPacket(new MagicSkillUse(this, this, 6673, 1, 1000, 0));
        }
        _noble = noble;
    }

    public int getSubLevel() {
        return isBaseClassActive() ? 0 : getLevel();
    }

    /* varka silenos and ketra orc quests related functions */
    public void updateKetraVarka() {
        if (ItemFunctions.getItemCount(this, 7215) > 0) {
            _ketra = 5;
        } else if (ItemFunctions.getItemCount(this, 7214) > 0) {
            _ketra = 4;
        } else if (ItemFunctions.getItemCount(this, 7213) > 0) {
            _ketra = 3;
        } else if (ItemFunctions.getItemCount(this, 7212) > 0) {
            _ketra = 2;
        } else if (ItemFunctions.getItemCount(this, 7211) > 0) {
            _ketra = 1;
        } else if (ItemFunctions.getItemCount(this, 7225) > 0) {
            _varka = 5;
        } else if (ItemFunctions.getItemCount(this, 7224) > 0) {
            _varka = 4;
        } else if (ItemFunctions.getItemCount(this, 7223) > 0) {
            _varka = 3;
        } else if (ItemFunctions.getItemCount(this, 7222) > 0) {
            _varka = 2;
        } else if (ItemFunctions.getItemCount(this, 7221) > 0) {
            _varka = 1;
        } else {
            _varka = 0;
            _ketra = 0;
        }
    }

    public int getVarka() {
        return _varka;
    }

    public int getKetra() {
        return _ketra;
    }

    public void updateRam() {
        if (ItemFunctions.getItemCount(this, 7247) > 0) {
            _ram = 2;
        } else if (ItemFunctions.getItemCount(this, 7246) > 0) {
            _ram = 1;
        } else {
            _ram = 0;
        }
    }

    public int getRam() {
        return _ram;
    }

    public int getPledgeType() {
        return _pledgeType;
    }

    public void setPledgeType(final int typeId) {
        _pledgeType = typeId;
    }

    public int getLvlJoinedAcademy() {
        return _lvlJoinedAcademy;
    }

    public void setLvlJoinedAcademy(int lvl) {
        _lvlJoinedAcademy = lvl;
    }

    public int getPledgeClass() {
        return _pledgeClass;
    }

    public void updatePledgeClass() {
        int CLAN_LEVEL = _clan == null ? -1 : _clan.getLevel();
        boolean IN_ACADEMY = _clan != null && Clan.isAcademy(_pledgeType);
        boolean IS_GUARD = _clan != null && Clan.isRoyalGuard(_pledgeType);
        boolean IS_KNIGHT = _clan != null && Clan.isOrderOfKnights(_pledgeType);

        boolean IS_GUARD_CAPTAIN = false, IS_KNIGHT_COMMANDER = false, IS_LEADER = false;

        SubUnit unit = getSubUnit();
        if (unit != null) {
            UnitMember unitMember = unit.getUnitMember(getObjectId());
            if (unitMember == null) {
                _log.warn(
                        "Player: unitMember null, clan: " + _clan.getClanId() + "; pledgeType: " + unit.getType());
                return;
            }
            IS_GUARD_CAPTAIN = Clan.isRoyalGuard(unitMember.getLeaderOf());
            IS_KNIGHT_COMMANDER = Clan.isOrderOfKnights(unitMember.getLeaderOf());
            IS_LEADER = unitMember.getLeaderOf() == Clan.SUBUNIT_MAIN_CLAN;
        }

        switch (CLAN_LEVEL) {
        case -1:
            _pledgeClass = RANK_VAGABOND;
            break;
        case 0:
        case 1:
        case 2:
        case 3:
            if (IS_LEADER) {
                _pledgeClass = RANK_HEIR;
            } else {
                _pledgeClass = RANK_VASSAL;
            }
            break;
        case 4:
            if (IS_LEADER) {
                _pledgeClass = RANK_KNIGHT;
            } else {
                _pledgeClass = RANK_HEIR;
            }
            break;
        case 5:
            if (IS_LEADER) {
                _pledgeClass = RANK_WISEMAN;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else {
                _pledgeClass = RANK_HEIR;
            }
            break;
        case 6:
            if (IS_LEADER) {
                _pledgeClass = RANK_BARON;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else if (IS_GUARD_CAPTAIN) {
                _pledgeClass = RANK_WISEMAN;
            } else if (IS_GUARD) {
                _pledgeClass = RANK_HEIR;
            } else {
                _pledgeClass = RANK_KNIGHT;
            }
            break;
        case 7:
            if (IS_LEADER) {
                _pledgeClass = RANK_COUNT;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else if (IS_GUARD_CAPTAIN) {
                _pledgeClass = RANK_VISCOUNT;
            } else if (IS_GUARD) {
                _pledgeClass = RANK_KNIGHT;
            } else if (IS_KNIGHT_COMMANDER) {
                _pledgeClass = RANK_BARON;
            } else if (IS_KNIGHT) {
                _pledgeClass = RANK_HEIR;
            } else {
                _pledgeClass = RANK_WISEMAN;
            }
            break;
        case 8:
            if (IS_LEADER) {
                _pledgeClass = RANK_MARQUIS;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else if (IS_GUARD_CAPTAIN) {
                _pledgeClass = RANK_COUNT;
            } else if (IS_GUARD) {
                _pledgeClass = RANK_WISEMAN;
            } else if (IS_KNIGHT_COMMANDER) {
                _pledgeClass = RANK_VISCOUNT;
            } else if (IS_KNIGHT) {
                _pledgeClass = RANK_KNIGHT;
            } else {
                _pledgeClass = RANK_BARON;
            }
            break;
        case 9:
            if (IS_LEADER) {
                _pledgeClass = RANK_DUKE;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else if (IS_GUARD_CAPTAIN) {
                _pledgeClass = RANK_MARQUIS;
            } else if (IS_GUARD) {
                _pledgeClass = RANK_BARON;
            } else if (IS_KNIGHT_COMMANDER) {
                _pledgeClass = RANK_COUNT;
            } else if (IS_KNIGHT) {
                _pledgeClass = RANK_WISEMAN;
            } else {
                _pledgeClass = RANK_VISCOUNT;
            }
            break;
        case 10:
            if (IS_LEADER) {
                _pledgeClass = RANK_GRAND_DUKE;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else if (IS_GUARD) {
                _pledgeClass = RANK_VISCOUNT;
            } else if (IS_KNIGHT) {
                _pledgeClass = RANK_BARON;
            } else if (IS_GUARD_CAPTAIN) {
                _pledgeClass = RANK_DUKE;
            } else if (IS_KNIGHT_COMMANDER) {
                _pledgeClass = RANK_MARQUIS;
            } else {
                _pledgeClass = RANK_COUNT;
            }
            break;
        case 11:
            if (IS_LEADER) {
                _pledgeClass = RANK_DISTINGUISHED_KING;
            } else if (IN_ACADEMY) {
                _pledgeClass = RANK_VASSAL;
            } else if (IS_GUARD) {
                _pledgeClass = RANK_COUNT;
            } else if (IS_KNIGHT) {
                _pledgeClass = RANK_VISCOUNT;
            } else if (IS_GUARD_CAPTAIN) {
                _pledgeClass = RANK_GRAND_DUKE;
            } else if (IS_KNIGHT_COMMANDER) {
                _pledgeClass = RANK_DUKE;
            } else {
                _pledgeClass = RANK_MARQUIS;
            }
            break;
        }

        if (_hero && _pledgeClass < RANK_MARQUIS) {
            _pledgeClass = RANK_MARQUIS;
        } else if (_noble && _pledgeClass < RANK_BARON) {
            _pledgeClass = RANK_BARON;
        }
    }

    public int getPowerGrade() {
        return _powerGrade;
    }

    public void setPowerGrade(final int grade) {
        _powerGrade = grade;
    }

    public int getApprentice() {
        return _apprentice;
    }

    public void setApprentice(final int apprentice) {
        _apprentice = apprentice;
    }

    public int getSponsor() {
        return _clan == null ? 0 : _clan.getAnyMember(getObjectId()).getSponsor();
    }

    public int getNameColor() {
        if (isInObserverMode()) {
            return Color.black.getRGB();
        }

        return _nameColor;
    }

    public void setNameColor(final int nameColor) {
        if (nameColor != Config.NORMAL_NAME_COLOUR && nameColor != Config.CLANLEADER_NAME_COLOUR
                && nameColor != Config.GM_NAME_COLOUR && nameColor != Config.SERVICES_OFFLINE_TRADE_NAME_COLOR) {
            setVar("namecolor", Integer.toHexString(nameColor), -1);
        } else if (nameColor == Config.NORMAL_NAME_COLOUR) {
            unsetVar("namecolor");
        }
        _nameColor = nameColor;
    }

    public void setNameColor(final int red, final int green, final int blue) {
        _nameColor = (red & 0xFF) + ((green & 0xFF) << 8) + ((blue & 0xFF) << 16);
        if (_nameColor != Config.NORMAL_NAME_COLOUR && _nameColor != Config.CLANLEADER_NAME_COLOUR
                && _nameColor != Config.GM_NAME_COLOUR && _nameColor != Config.SERVICES_OFFLINE_TRADE_NAME_COLOR) {
            setVar("namecolor", Integer.toHexString(_nameColor), -1);
        } else {
            unsetVar("namecolor");
        }
    }

    public void setVar(String name, String value, long expirationTime) {
        user_variables.put(name, value);
        mysql.set(
                "REPLACE INTO character_variables (obj_id, type, name, value, expire_time) VALUES (?,'user-var',?,?,?)",
                getObjectId(), name, value, expirationTime);
    }

    public void setVar(String name, int value, long expirationTime) {
        setVar(name, String.valueOf(value), expirationTime);
    }

    public void setVar(String name, long value, long expirationTime) {
        setVar(name, String.valueOf(value), expirationTime);
    }

    public void unsetVar(String name) {
        if (name == null) {
            return;
        }

        if (user_variables.remove(name) != null) {
            mysql.set(
                    "DELETE FROM `character_variables` WHERE `obj_id`=? AND `type`='user-var' AND `name`=? LIMIT 1",
                    getObjectId(), name);
        }
    }

    public String getVar(String name) {
        return user_variables.get(name);
    }

    public boolean getVarB(String name, boolean defaultVal) {
        String var = user_variables.get(name);
        if (var == null) {
            return defaultVal;
        }
        return !(var.equals("0") || var.equalsIgnoreCase("false"));
    }

    public boolean getVarB(String name) {
        String var = user_variables.get(name);
        return !(var == null || var.equals("0") || var.equalsIgnoreCase("false"));
    }

    public long getVarLong(String name) {
        return getVarLong(name, 0L);
    }

    public long getVarLong(String name, long defaultVal) {
        long result = defaultVal;
        String var = getVar(name);
        if (var != null) {
            result = Long.parseLong(var);
        }
        return result;
    }

    public int getVarInt(String name) {
        return getVarInt(name, 0);
    }

    public int getVarInt(String name, int defaultVal) {
        int result = defaultVal;
        String var = getVar(name);
        if (var != null) {
            result = Integer.parseInt(var);
        }
        return result;
    }

    public Map<String, String> getVars() {
        return user_variables;
    }

    private void loadVariables() {
        Connection con = null;
        PreparedStatement offline = null;
        ResultSet rs = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            offline = con.prepareStatement("SELECT * FROM character_variables WHERE obj_id = ?");
            offline.setInt(1, getObjectId());
            rs = offline.executeQuery();
            while (rs.next()) {
                String name = rs.getString("name");
                String value = Strings.stripSlashes(rs.getString("value"));
                user_variables.put(name, value);
            }

            // ? ?? ?   , 
            //  NPE
            if (getVar("lang@") == null) {
                setVar("lang@", Config.DEFAULT_LANG, -1);
            }
        } catch (Exception e) {
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, offline, rs);
        }
    }

    public String getLang() {
        return getVar("lang@");
    }

    public int getLangId() {
        String lang = getLang();
        if (lang.equalsIgnoreCase("en") || lang.equalsIgnoreCase("e") || lang.equalsIgnoreCase("eng")) {
            return LANG_ENG;
        }
        if (lang.equalsIgnoreCase("ru") || lang.equalsIgnoreCase("r") || lang.equalsIgnoreCase("rus")) {
            return LANG_RUS;
        }
        return LANG_UNK;
    }

    public Language getLanguage() {
        String lang = getLang();
        if (lang == null || lang.equalsIgnoreCase("en") || lang.equalsIgnoreCase("e")
                || lang.equalsIgnoreCase("eng")) {
            return Language.MULTI;
        }
        if (lang.equalsIgnoreCase("ru") || lang.equalsIgnoreCase("r") || lang.equalsIgnoreCase("rus")) {
            return Language.MULTI;
        }
        return Language.MULTI;
    }

    public boolean isLangRus() {
        return getLangId() == LANG_RUS;
    }

    public int isAtWarWith(final Integer id) {
        return _clan == null || !_clan.isAtWarWith(id) ? 0 : 1;
    }

    public int isAtWar() {
        return _clan == null || _clan.isAtWarOrUnderAttack() <= 0 ? 0 : 1;
    }

    public void stopWaterTask() {
        if (_taskWater != null) {
            _taskWater.cancel(false);
            _taskWater = null;
            sendPacket(new SetupGauge(this, SetupGauge.BLUE_MINI, 0));
            sendChanges();
        }
    }

    public void startWaterTask() {
        if (isDead()) {
            stopWaterTask();
        } else if (Config.ALLOW_WATER && _taskWater == null) {
            int timeinwater = (int) (calcStat(Stats.BREATH, getTemplate().getBaseBreathBonus(), null, null)
                    * 1000L);
            sendPacket(new SetupGauge(this, SetupGauge.BLUE_MINI, timeinwater));
            if (getTransformation() > 0 && getTransformationTemplate() > 0 && !isCursedWeaponEquipped()) {
                setTransformation(0);
            }
            _taskWater = ThreadPoolManager.getInstance().scheduleAtFixedRate(new WaterTask(this), timeinwater,
                    1000L);
            sendChanges();
        }
    }

    public void doRevive(double percent) {
        restoreExp(percent);
        doRevive();
    }

    @Override
    public void doRevive() {
        super.doRevive();
        setAgathionRes(false);
        unsetVar("lostexp");
        updateEffectIcons();
        autoShot();
    }

    public void reviveRequest(Player reviver, double percent, boolean pet) {
        ReviveAnswerListener reviveAsk = _askDialog != null && _askDialog.getValue() instanceof ReviveAnswerListener
                ? (ReviveAnswerListener) _askDialog.getValue()
                : null;
        if (reviveAsk != null) {
            if (reviveAsk.isForPet() == pet && reviveAsk.getPower() >= percent) {
                reviver.sendPacket(Msg.BETTER_RESURRECTION_HAS_BEEN_ALREADY_PROPOSED);
                return;
            }
            if (pet && !reviveAsk.isForPet()) {
                reviver.sendPacket(
                        Msg.SINCE_THE_MASTER_WAS_IN_THE_PROCESS_OF_BEING_RESURRECTED_THE_ATTEMPT_TO_RESURRECT_THE_PET_HAS_BEEN_CANCELLED);
                return;
            }
            if (pet && isDead()) {
                reviver.sendPacket(
                        Msg.WHILE_A_PET_IS_ATTEMPTING_TO_RESURRECT_IT_CANNOT_HELP_IN_RESURRECTING_ITS_MASTER);
                return;
            }
        }

        if (pet && isHaveDeadPets() || !pet && isDead()) {
            ConfirmDlg pkt = new ConfirmDlg(
                    SystemMsg.C1_IS_MAKING_AN_ATTEMPT_TO_RESURRECT_YOU_IF_YOU_CHOOSE_THIS_PATH_S2_EXPERIENCE_WILL_BE_RETURNED_FOR_YOU,
                    0);
            pkt.addName(reviver)
                    .addString(new StringBuilder().append(Math.round(percent)).append(" percent").toString());

            ask(pkt, new ReviveAnswerListener(this, percent, pet));
        }
        WorldStatisticsManager.getInstance().updateStat(reviver, CategoryType.RESURRECTED_CHAR_COUNT, 1);
        WorldStatisticsManager.getInstance().updateStat(this, CategoryType.RESURRECTED_BY_OTHER_COUNT, 1);
    }

    public void summonCharacterRequest(final Creature summoner, final Location loc,
            final int summonConsumeCrystal) {
        ConfirmDlg cd = new ConfirmDlg(SystemMsg.C1_WISHES_TO_SUMMON_YOU_FROM_S2, 60000);
        cd.addName(summoner).addZoneName(loc);

        ask(cd, new SummonAnswerListener(this, loc, summonConsumeCrystal));
    }

    public void scriptRequest(String text, String scriptName, Object[] args) {
        ask(new ConfirmDlg(SystemMsg.S1, 30000).addString(text), new ScriptAnswerListener(this, scriptName, args));
    }

    public void updateNoChannel(final long time) {
        setNoChannel(time);

        Connection con = null;
        PreparedStatement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();

            final String stmt = "UPDATE characters SET nochannel = ? WHERE obj_Id=?";
            statement = con.prepareStatement(stmt);
            statement.setLong(1, _NoChannel > 0 ? _NoChannel / 1000 : _NoChannel);
            statement.setInt(2, getObjectId());
            statement.executeUpdate();
        } catch (final Exception e) {
            _log.warn("Could not activate nochannel:" + e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }

        sendPacket(new EtcStatusUpdate(this));
    }

    private void checkRecom() {
        Calendar temp = Calendar.getInstance();
        temp.set(Calendar.HOUR_OF_DAY, 6);
        temp.set(Calendar.MINUTE, 30);
        temp.set(Calendar.SECOND, 0);
        temp.set(Calendar.MILLISECOND, 0);
        long count = Math.round((System.currentTimeMillis() / 1000 - _lastAccess) / 86400);
        if (count == 0 && _lastAccess < temp.getTimeInMillis() / 1000
                && System.currentTimeMillis() > temp.getTimeInMillis()) {
            count++;
        }

        for (int i = 1; i < count; i++) {
            setRecomHave(getRecomHave() - 20);
        }

        if (count > 0) {
            restartRecom();
        }
    }

    public void restartRecom() {
        setRecomBonusTime(3600);
        setRecomLeftToday(0);
        setRecomLeft(20);
        setRecomHave(getRecomHave() - 20);
        stopRecomBonusTask(false);
        startRecomBonusTask();
        sendUserInfo(true);
        sendVoteSystemInfo();
    }

    /**
     * Changing index of class in DB, used for changing class when finished professional quests
     *
     * @param oldclass
     * @param newclass
     */
    private synchronized void changeClassInDb(final int oldclass, final int newclass) {
        Connection con = null;
        PreparedStatement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            statement = con.prepareStatement(
                    "UPDATE character_subclasses SET class_id=? WHERE char_obj_id=? AND class_id=?");
            statement.setInt(1, newclass);
            statement.setInt(2, getObjectId());
            statement.setInt(3, oldclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement("DELETE FROM character_hennas WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, newclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement(
                    "UPDATE character_hennas SET class_index=? WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, newclass);
            statement.setInt(2, getObjectId());
            statement.setInt(3, oldclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement("DELETE FROM character_shortcuts WHERE object_id=? AND class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, newclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement(
                    "UPDATE character_shortcuts SET class_index=? WHERE object_id=? AND class_index=?");
            statement.setInt(1, newclass);
            statement.setInt(2, getObjectId());
            statement.setInt(3, oldclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement("DELETE FROM character_skills WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, newclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement(
                    "UPDATE character_skills SET class_index=? WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, newclass);
            statement.setInt(2, getObjectId());
            statement.setInt(3, oldclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement("DELETE FROM character_effects_save WHERE object_id=? AND id=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, newclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement("UPDATE character_effects_save SET id=? WHERE object_id=? AND id=?");
            statement.setInt(1, newclass);
            statement.setInt(2, getObjectId());
            statement.setInt(3, oldclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con
                    .prepareStatement("DELETE FROM character_skills_save WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, getObjectId());
            statement.setInt(2, newclass);
            statement.executeUpdate();
            DbUtils.close(statement);

            statement = con.prepareStatement(
                    "UPDATE character_skills_save SET class_index=? WHERE char_obj_id=? AND class_index=?");
            statement.setInt(1, newclass);
            statement.setInt(2, getObjectId());
            statement.setInt(3, oldclass);
            statement.executeUpdate();
            DbUtils.close(statement);
        } catch (final SQLException e) {
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }
    }

    /**
     * ?   ?  
     */
    public void storeCharSubClasses() {
        SubClass main = getActiveSubClass();
        if (main != null) {
            main.setCp(getCurrentCp());
            main.setHp(getCurrentHp());
            main.setMp(getCurrentMp());
        } else {
            _log.warn("Could not store char sub data, main class " + getActiveClassId() + " not found for " + this);
        }

        CharacterSubclassDAO.getInstance().store(this);
    }

    /**
     *  ?, ? ? ? ?
     *
     * @param storeOld
     * @param certification
     */
    public boolean addSubClass(final int classId, boolean storeOld, int certification) {
        final ClassId newId = ClassId.VALUES[classId];
        //if (!newId.isOfLevel(ClassLevel.Second))
        //   return false;

        final SubClass newClass = new SubClass();
        final SubClassType type = SubClassType.SUBCLASS;
        newClass.setType(type);
        newClass.setClassId(classId);
        newClass.setCertification(certification);
        if (!getSubClassList().add(newClass)) {
            return false;
        }

        final int level = 40;
        final long exp = Experience.LEVEL[level];
        final double hp = newId.getBaseHp(level);
        final double mp = newId.getBaseMp(level);
        final double cp = newId.getBaseCp(level);
        if (!CharacterSubclassDAO.getInstance().insert(getObjectId(), classId, exp, 0, hp, mp, cp, hp, mp, cp,
                level, false, type, null, certification)) {
            return false;
        }

        setActiveSubClass(classId, storeOld);

        boolean countUnlearnable = true;
        int unLearnable = 0;

        Collection<SkillLearn> skills = SkillAcquireHolder.getInstance().getAvailableSkills(this,
                AcquireType.NORMAL);
        while (skills.size() > unLearnable) {
            for (final SkillLearn s : skills) {
                final Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
                if (sk == null || !sk.getCanLearn(newId)) {
                    if (countUnlearnable) {
                        unLearnable++;
                    }
                    continue;
                }
                addSkill(sk, true);
            }
            countUnlearnable = false;
            skills = SkillAcquireHolder.getInstance().getAvailableSkills(this, AcquireType.NORMAL);
        }

        sendSkillList();
        setCurrentHpMp(getMaxHp(), getMaxMp(), true);
        setCurrentCp(getMaxCp());
        return true;
    }

    /**
     * ?    ?  ? , ? ? ?
     */
    public boolean modifySubClass(final int oldClassId, final int newClassId) {
        final SubClass originalClass = getSubClassList().getByClassId(oldClassId);
        if (originalClass == null || originalClass.isBase()) {
            return false;
        }

        final int certification = originalClass.getCertification();

        Connection con = null;
        PreparedStatement statement = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            // Remove all basic info stored about this sub-class.
            statement = con.prepareStatement(
                    "DELETE FROM character_subclasses WHERE char_obj_id=? AND class_id=? AND type != "
                            + SubClassType.BASE_CLASS.ordinal());
            statement.setInt(1, getObjectId());
            statement.setInt(2, oldClassId);
            statement.execute();
            DbUtils.close(statement);

            // Remove all skill info stored for this sub-class.
            statement = con.prepareStatement("DELETE FROM character_skills WHERE char_obj_id=? AND class_index=? ");
            statement.setInt(1, getObjectId());
            statement.setInt(2, oldClassId);
            statement.execute();
            DbUtils.close(statement);

            // Remove all saved skills info stored for this sub-class.
            statement = con
                    .prepareStatement("DELETE FROM character_skills_save WHERE char_obj_id=? AND class_index=? ");
            statement.setInt(1, getObjectId());
            statement.setInt(2, oldClassId);
            statement.execute();
            DbUtils.close(statement);

            // Remove all saved effects stored for this sub-class.
            statement = con.prepareStatement("DELETE FROM character_effects_save WHERE object_id=? AND id=? ");
            statement.setInt(1, getObjectId());
            statement.setInt(2, oldClassId);
            statement.execute();
            DbUtils.close(statement);

            // Remove all henna info stored for this sub-class.
            statement = con.prepareStatement("DELETE FROM character_hennas WHERE char_obj_id=? AND class_index=? ");
            statement.setInt(1, getObjectId());
            statement.setInt(2, oldClassId);
            statement.execute();
            DbUtils.close(statement);

            // Remove all shortcuts info stored for this sub-class.
            statement = con
                    .prepareStatement("DELETE FROM character_shortcuts WHERE object_id=? AND class_index=? ");
            statement.setInt(1, getObjectId());
            statement.setInt(2, oldClassId);
            statement.execute();
            DbUtils.close(statement);
        } catch (final Exception e) {
            _log.warn("Could not delete char sub-class: " + e);
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, statement);
        }
        getSubClassList().removeByClassId(oldClassId);

        return newClassId <= 0 || addSubClass(newClassId, false, certification);
    }

    public void setActiveSubClass(int subId, boolean store) {
        SubClass oldActiveSub = getActiveSubClass();
        if (oldActiveSub != null) {
            EffectsDAO.getInstance().insert(this);
            storeDisableSkills();

            if (QuestManager.getQuest(422) != null) {
                String qn = QuestManager.getQuest(422).getName();
                if (qn != null) {
                    QuestState qs = getQuestState(qn);
                    if (qs != null) {
                        qs.exitCurrentQuest(true);
                    }
                }
            }
        }

        if (store) {
            oldActiveSub.setCp(getCurrentCp());
            oldActiveSub.setHp(getCurrentHp());
            oldActiveSub.setMp(getCurrentMp());
        }

        SubClass newActiveSub = _subClassList.changeActiveSubClass(subId);

        setClassId(subId, false, false);

        removeAllSkills();

        getEffectList().stopAllEffects();

        for (Summon summon : getPets()) {
            if (summon.isSummon()
                    || Config.ALT_IMPROVED_PETS_LIMITED_USE && (summon.getNpcId() == 16035 && !isMageClass()
                            || summon.getNpcId() == 16034 && isMageClass())) {
                summon.unSummon();
            }
        }

        setAgathion(0);

        restoreSkills();
        rewardSkills(false);
        checkSkills();
        sendPacket(new ExStorageMaxCount(this));

        refreshExpertisePenalty();

        getInventory().refreshEquip();
        getInventory().validateItems();

        for (int i = 0; i < 3; i++) {
            _henna[i] = null;
        }

        restoreHenna();
        sendPacket(new HennaInfo(this));

        EffectsDAO.getInstance().restoreEffects(this);
        restoreDisableSkills();

        setCurrentHpMp(newActiveSub.getHp(), newActiveSub.getMp());
        setCurrentCp(newActiveSub.getCp());

        _shortCuts.restore();
        sendPacket(new ShortCutInit(this));
        for (int shotId : getAutoSoulShot()) {
            sendPacket(new ExAutoSoulShot(shotId, true));
        }
        sendPacket(new SkillCoolTime(this));
        if (getLevel() >= 85 & getVar("GermunkusUSM") == null & !isAwaking()) {
            AwakingManager.getInstance().SendReqToStartQuest(this);
        }

        sendPacket(new ExSubjobInfo(this, false));

        broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP));

        getDeathPenalty().restore(this);

        setIncreasedForce(0);

        startHourlyTask();

        sendUserInfo(true);
        sendSkillList();

        broadcastCharInfo();
        updateEffectIcons();
        updateStats();
    }

    /**
     *  delay ?  ?  
     */
    public void startKickTask(long delayMillis) {
        stopKickTask();
        _kickTask = ThreadPoolManager.getInstance().schedule(new KickTask(this), delayMillis);
    }

    public void stopKickTask() {
        if (_kickTask != null) {
            _kickTask.cancel(false);
            _kickTask = null;
        }
    }

    public void startBonusTask() {
        if (Config.SERVICES_RATE_TYPE != Bonus.NO_BONUS) {
            int bonusExpire = getNetConnection().getBonusExpire();
            double bonus = getNetConnection().getBonus();
            if (bonusExpire > System.currentTimeMillis() / 1000L) {
                getBonus().setRateXp(bonus);
                getBonus().setRateSp(bonus);
                getBonus().setDropAdena(bonus);
                getBonus().setDropItems(bonus);
                getBonus().setDropSpoil(bonus);

                getBonus().setBonusExpire(bonusExpire);

                if (_bonusExpiration == null) {
                    _bonusExpiration = LazyPrecisionTaskManager.getInstance().startBonusExpirationTask(this);
                }
            } else if (bonus > 0 && Config.SERVICES_RATE_TYPE == Bonus.BONUS_GLOBAL_ON_GAMESERVER) {
                AccountBonusDAO.getInstance().delete(getAccountName());
            }
        }
    }

    public void stopBonusTask() {
        if (_bonusExpiration != null) {
            _bonusExpiration.cancel(false);
            _bonusExpiration = null;
        }
    }

    @Override
    public int getInventoryLimit() {
        return (int) calcStat(Stats.INVENTORY_LIMIT, 0, null, null);
    }

    public int getWarehouseLimit() {
        return (int) calcStat(Stats.STORAGE_LIMIT, 0, null, null);
    }

    public int getTradeLimit() {
        return (int) calcStat(Stats.TRADE_LIMIT, 0, null, null);
    }

    public int getDwarvenRecipeLimit() {
        return (int) calcStat(Stats.DWARVEN_RECIPE_LIMIT, 50, null, null) + Config.ALT_ADD_RECIPES;
    }

    public int getCommonRecipeLimit() {
        return (int) calcStat(Stats.COMMON_RECIPE_LIMIT, 50, null, null) + Config.ALT_ADD_RECIPES;
    }

    /**
     *   ? 
     */
    public Element getAttackElement() {
        return Formulas.getAttackElement(this, null);
    }

    /**
     *   ? 
     *
     * @return  ?
     */
    public int getAttack(Element element) {
        if (element == Element.NONE) {
            return 0;
        }
        return (int) calcStat(element.getAttack(), 0., null, null);
    }

    /**
     *    
     *
     * @return  
     */
    public int getDefence(Element element) {
        if (element == Element.NONE) {
            return 0;
        }
        return (int) calcStat(element.getDefence(), 0., null, null);
    }

    public boolean getAndSetLastItemAuctionRequest() {
        if (_lastItemAuctionInfoRequest + 2000L < System.currentTimeMillis()) {
            _lastItemAuctionInfoRequest = System.currentTimeMillis();
            return true;
        } else {
            _lastItemAuctionInfoRequest = System.currentTimeMillis();
            return false;
        }
    }

    @Override
    public int getNpcId() {
        return -2;
    }

    public GameObject getVisibleObject(int id) {
        if (getObjectId() == id) {
            return this;
        }

        GameObject target = null;

        if (getTargetId() == id) {
            target = getTarget();
        }

        if (target == null && _party != null) {
            for (Player p : _party.getPartyMembers()) {
                if (p != null && p.getObjectId() == id) {
                    target = p;
                    break;
                }
            }
        }

        if (target == null) {
            target = World.getAroundObjectById(this, id);
        }

        return target == null || target.isInvisible() ? null : target;
    }

    @Override
    public int getPAtk(final Creature target) {
        double init = getActiveWeaponInstance() == null ? (isMageClass() ? 3 : 4) : 0;
        return (int) calcStat(Stats.POWER_ATTACK, init, target, null);
    }

    @Override
    public int getPDef(Creature target) {
        int init = 0;

        ItemInstance chest = getInventory().getPaperdollItem(10);
        if (chest == null) {
            init += getTemplate().getArmDef().getChestDef();
        }
        if ((getInventory().getPaperdollItem(11) == null) && ((chest == null) || (chest.getBodyPart() != 32768))) {
            init += getTemplate().getArmDef().getLegsDef();
        }
        if (getInventory().getPaperdollItem(6) == null) {
            init += getTemplate().getArmDef().getHelmetDef();
        }
        if (getInventory().getPaperdollItem(9) == null) {
            init += getTemplate().getArmDef().getGlovesDef();
        }
        if (getInventory().getPaperdollItem(12) == null) {
            init += getTemplate().getArmDef().getBootsDef();
        }
        if (getInventory().getPaperdollItem(0) == null) {
            init += getTemplate().getArmDef().getUnderwearDef();
        }
        if (getInventory().getPaperdollItem(13) == null) {
            init += getTemplate().getArmDef().getCloakDef();
        }
        return (int) calcStat(Stats.POWER_DEFENCE, init, target, null);
    }

    @Override
    public int getMDef(Creature target, Skill skill) {
        int init = 0;

        if (getInventory().getPaperdollItem(2) == null) {
            init += getTemplate().getJewlDef().getLEaaringDef();
        }
        if (getInventory().getPaperdollItem(1) == null) {
            init += getTemplate().getJewlDef().getREaaringDef();
        }
        if (getInventory().getPaperdollItem(3) == null) {
            init += getTemplate().getJewlDef().getNecklaceDef();
        }
        if (getInventory().getPaperdollItem(5) == null) {
            init += getTemplate().getJewlDef().getLRingDef();
        }
        if (getInventory().getPaperdollItem(4) == null) {
            init += getTemplate().getJewlDef().getRRingDef();
        }
        return (int) calcStat(Stats.MAGIC_DEFENCE, init, target, skill);
    }

    @Override
    public String getTitle() {
        return super.getTitle();
    }

    public int getTitleColor() {
        return _titlecolor;
    }

    public void setTitleColor(final int titlecolor) {
        if (titlecolor != DEFAULT_TITLE_COLOR) {
            setVar("titlecolor", Integer.toHexString(titlecolor), -1);
        } else {
            unsetVar("titlecolor");
        }
        _titlecolor = titlecolor;
    }

    @Override
    public boolean isCursedWeaponEquipped() {
        return _cursedWeaponEquippedId != 0;
    }

    public int getCursedWeaponEquippedId() {
        return _cursedWeaponEquippedId;
    }

    public void setCursedWeaponEquippedId(int value) {
        _cursedWeaponEquippedId = value;
    }

    @Override
    public boolean isImmobilized() {
        return super.isImmobilized() || isOverloaded() || isSitting() || isFishing();
    }

    @Override
    public boolean isBlocked() {
        return super.isBlocked() || isInMovie() || isInObserverMode() || isTeleporting() || isLogoutStarted();
    }

    @Override
    public boolean isInvul() {
        return super.isInvul() || isInMovie();
    }

    public boolean isOverloaded() {
        return _overloaded;
    }

    /**
     * if True, the L2Player can't take more item
     */
    public void setOverloaded(boolean overloaded) {
        _overloaded = overloaded;
    }

    public boolean isFishing() {
        return _isFishing;
    }

    public Fishing getFishing() {
        return _fishing;
    }

    public void setFishing(boolean value) {
        _isFishing = value;
    }

    /**
     * private List<L2Player> _snoopListener = new ArrayList<L2Player>(); private List<L2Player> _snoopedPlayer = new
     * ArrayList<L2Player>();
     * <p/>
     * public void broadcastSnoop(int type, String name, int fStringId, String... params) { if(_snoopListener.size() >
     * 0) { Snoop sn = new Snoop(getObjectId(), getName(), type, name, fStringId, params); for(L2Player pci :
     * _snoopListener) if(pci != null) pci.sendPacket(sn); } }
     * <p/>
     * public void addSnooper(L2Player pci) { if(!_snoopListener.contains(pci)) _snoopListener.add(pci); }
     * <p/>
     * public void removeSnooper(L2Player pci) { _snoopListener.remove(pci); }
     * <p/>
     * public void addSnooped(L2Player pci) { if(!_snoopedPlayer.contains(pci)) _snoopedPlayer.add(pci); }
     * <p/>
     * public void removeSnooped(L2Player pci) { _snoopedPlayer.remove(pci); }
     */

    public void startFishing(FishTemplate fish, int lureId) {
        _fishing.setFish(fish);
        _fishing.setLureId(lureId);
        _fishing.startFishing();
    }

    public void stopFishing() {
        _fishing.stopFishing();
    }

    public Location getFishLoc() {
        return _fishing.getFishLoc();
    }

    public Bonus getBonus() {
        return _bonus;
    }

    public boolean hasBonus() {
        return _bonus.getBonusExpire() > System.currentTimeMillis() / 1000L;
    }

    @Override
    public double getRateAdena() {
        return _party == null ? _bonus.getDropAdena() : _party._rateAdena;
    }

    @Override
    public double getRateItems() {
        return _party == null ? _bonus.getDropItems() : _party._rateDrop;
    }

    @Override
    public double getRateExp() {
        return calcStat(Stats.EXP, _party == null ? _bonus.getRateXp() : _party._rateExp, null, null);
    }

    @Override
    public double getRateSp() {
        return calcStat(Stats.SP, _party == null ? _bonus.getRateSp() : _party._rateSp, null, null);
    }

    @Override
    public double getRateSpoil() {
        return _party == null ? _bonus.getDropSpoil() : _party._rateSpoil;
    }

    public boolean isMaried() {
        return _maried;
    }

    public void setMaried(boolean state) {
        _maried = state;
    }

    public boolean isMaryRequest() {
        return _maryrequest;
    }

    public void setMaryRequest(boolean state) {
        _maryrequest = state;
    }

    public boolean isMaryAccepted() {
        return _maryaccepted;
    }

    public void setMaryAccepted(boolean state) {
        _maryaccepted = state;
    }

    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 boolean isUndying() {
        return _isUndying;
    }

    public void setUndying(boolean val) {
        if (!isGM()) {
            return;
        }
        _isUndying = val;
    }

    /**
     *    ? .
     */
    public void resetReuse() {
        _skillReuses.clear();
        _sharedGroupReuses.clear();
    }

    public DeathPenalty getDeathPenalty() {
        return getActiveSubClass() == null ? null : getActiveSubClass().getDeathPenalty(this);
    }

    public boolean isCharmOfCourage() {
        return _charmOfCourage;
    }

    public void setCharmOfCourage(boolean val) {
        _charmOfCourage = val;

        if (!val) {
            getEffectList().stopEffect(Skill.SKILL_CHARM_OF_COURAGE);
        }

        sendEtcStatusUpdate();
    }

    @Override
    public int getIncreasedForce() {
        return _increasedForce;
    }

    @Override
    public void setIncreasedForce(int i) {
        i = Math.min(i, Charge.MAX_CHARGE);
        i = Math.max(i, 0);

        if (i != 0 && i > _increasedForce) {
            sendPacket(new SystemMessage(SystemMessage.YOUR_FORCE_HAS_INCREASED_TO_S1_LEVEL).addNumber(i));
        }

        _increasedForce = i;
        sendEtcStatusUpdate();
    }

    @Override
    public int getConsumedSouls() {
        return _consumedSouls;
    }

    @Override
    public void setConsumedSouls(int i, NpcInstance monster) {
        if (i == _consumedSouls) {
            return;
        }

        int max = (int) calcStat(Stats.SOULS_LIMIT, 0, monster, null);

        if (i > max) {
            i = max;
        }

        if (i <= 0) {
            _consumedSouls = 0;
            sendEtcStatusUpdate();
            return;
        }

        if (_consumedSouls != i) {
            int diff = i - _consumedSouls;
            if (diff > 0) {
                SystemMessage sm = new SystemMessage(
                        SystemMessage.YOUR_SOUL_HAS_INCREASED_BY_S1_SO_IT_IS_NOW_AT_S2);
                sm.addNumber(diff);
                sm.addNumber(i);
                sendPacket(sm);
            }
        } else if (max == i) {
            sendPacket(Msg.SOUL_CANNOT_BE_ABSORBED_ANY_MORE);
            return;
        }

        _consumedSouls = i;
        sendPacket(new EtcStatusUpdate(this));
    }

    public boolean isFalling() {
        return System.currentTimeMillis() - _lastFalling < 5000;
    }

    public void falling(int height) {
        if (!Config.DAMAGE_FROM_FALLING || isDead() || isFlying() || isInWater() || isInBoat()) {
            return;
        }
        _lastFalling = System.currentTimeMillis();
        int damage = (int) calcStat(Stats.FALL, getMaxHp() / 2000 * height, null, null);
        if (damage > 0) {
            int curHp = (int) getCurrentHp();
            if (curHp - damage < 1) {
                setCurrentHp(1, false);
            } else {
                setCurrentHp(curHp - damage, false);
            }
            sendPacket(new SystemMessage(SystemMessage.YOU_RECEIVED_S1_DAMAGE_FROM_TAKING_A_HIGH_FALL)
                    .addNumber(damage));
        }
    }

    /**
     *  ?  ? ? 
     */
    @Override
    public void checkHpMessages(double curHp, double newHp) {
        //   ?
        int[] _hp = { 30, 30 };
        int[] skills = { 290, 291 };

        //  ? ?
        int[] _effects_skills_id = { 139, 176, 292, 292, 420 };
        int[] _effects_hp = { 30, 30, 30, 60, 30 };

        double percent = getMaxHp() / 100;
        double _curHpPercent = curHp / percent;
        double _newHpPercent = newHp / percent;
        boolean needsUpdate = false;

        // check for passive skills
        for (int i = 0; i < skills.length; i++) {
            int level = getSkillLevel(skills[i]);
            if (level > 0) {
                if (_curHpPercent > _hp[i] && _newHpPercent <= _hp[i]) {
                    sendPacket(new SystemMessage(SystemMessage.SINCE_HP_HAS_DECREASED_THE_EFFECT_OF_S1_CAN_BE_FELT)
                            .addSkillName(skills[i], level));
                    needsUpdate = true;
                } else if (_curHpPercent <= _hp[i] && _newHpPercent > _hp[i]) {
                    sendPacket(
                            new SystemMessage(SystemMessage.SINCE_HP_HAS_INCREASED_THE_EFFECT_OF_S1_WILL_DISAPPEAR)
                                    .addSkillName(skills[i], level));
                    needsUpdate = true;
                }
            }
        }

        // check for active effects
        for (Integer i = 0; i < _effects_skills_id.length; i++) {
            if (getEffectList().getEffectsBySkillId(_effects_skills_id[i]) != null) {
                if (_curHpPercent > _effects_hp[i] && _newHpPercent <= _effects_hp[i]) {
                    sendPacket(new SystemMessage(SystemMessage.SINCE_HP_HAS_DECREASED_THE_EFFECT_OF_S1_CAN_BE_FELT)
                            .addSkillName(_effects_skills_id[i], 1));
                    needsUpdate = true;
                } else if (_curHpPercent <= _effects_hp[i] && _newHpPercent > _effects_hp[i]) {
                    sendPacket(
                            new SystemMessage(SystemMessage.SINCE_HP_HAS_INCREASED_THE_EFFECT_OF_S1_WILL_DISAPPEAR)
                                    .addSkillName(_effects_skills_id[i], 1));
                    needsUpdate = true;
                }
            }
        }

        if (needsUpdate) {
            sendChanges();
        }
    }

    /**
     *  ? ?    ?/? ShadowSence (skill id = 294)
     */
    public void checkDayNightMessages() {
        int level = getSkillLevel(294);
        if (level > 0) {
            if (GameTimeController.getInstance().isNowNight()) {
                sendPacket(new SystemMessage(SystemMessage.IT_IS_NOW_MIDNIGHT_AND_THE_EFFECT_OF_S1_CAN_BE_FELT)
                        .addSkillName(294, level));
            } else {
                sendPacket(new SystemMessage(SystemMessage.IT_IS_DAWN_AND_THE_EFFECT_OF_S1_WILL_NOW_DISAPPEAR)
                        .addSkillName(294, level));
            }
        }
        sendChanges();
    }

    public int getZoneMask() {
        return _zoneMask;
    }

    @Override
    protected void onUpdateZones(GArray<Zone> leaving, GArray<Zone> entering) {
        super.onUpdateZones(leaving, entering);

        if ((leaving == null || leaving.isEmpty()) && (entering == null || entering.isEmpty())) {
            return;
        }

        boolean lastInCombatZone = (_zoneMask & ZONE_PVP_FLAG) == ZONE_PVP_FLAG;
        boolean lastInDangerArea = (_zoneMask & ZONE_ALTERED_FLAG) == ZONE_ALTERED_FLAG;
        boolean lastOnSiegeField = (_zoneMask & ZONE_SIEGE_FLAG) == ZONE_SIEGE_FLAG;
        // boolean lastInPeaceZone = (_zoneMask & ZONE_PEACE_FLAG) ==
        // ZONE_PEACE_FLAG;

        boolean isInCombatZone = isInCombatZone();
        boolean isInDangerArea = isInDangerArea();
        boolean isOnSiegeField = isOnSiegeField();
        boolean isInPeaceZone = isInPeaceZone();
        boolean isInSSQZone = isInSSQZone();

        // ? ?, ?    
        int lastZoneMask = _zoneMask;
        _zoneMask = 0;

        if (isInCombatZone) {
            _zoneMask |= ZONE_PVP_FLAG;
        }
        if (isInDangerArea) {
            _zoneMask |= ZONE_ALTERED_FLAG;
        }
        if (isOnSiegeField) {
            _zoneMask |= ZONE_SIEGE_FLAG;
        }
        if (isInPeaceZone) {
            _zoneMask |= ZONE_PEACE_FLAG;
        }
        if (isInSSQZone) {
            _zoneMask |= ZONE_SSQ_FLAG;
        }

        if (lastZoneMask != _zoneMask) {
            sendPacket(new ExSetCompassZoneCode(this));
        }

        if (lastInCombatZone != isInCombatZone) {
            broadcastRelationChanged();
        }

        if (lastInDangerArea != isInDangerArea) {
            sendPacket(new EtcStatusUpdate(this));
        }

        if (lastOnSiegeField != isOnSiegeField) {
            broadcastRelationChanged();
            if (isOnSiegeField) {
                sendPacket(Msg.YOU_HAVE_ENTERED_A_COMBAT_ZONE);
            } else {
                sendPacket(Msg.YOU_HAVE_LEFT_A_COMBAT_ZONE);
                if (!isTeleporting() && getPvpFlag() == 0) {
                    startPvPFlag(null);
                }
            }
        }

        if (isInWater()) {
            startWaterTask();
        } else {
            stopWaterTask();
        }
    }

    public void startAutoSaveTask() {
        if (!Config.AUTOSAVE) {
            return;
        }
        if (_autoSaveTask == null) {
            _autoSaveTask = AutoSaveManager.getInstance().addAutoSaveTask(this);
        }
    }

    public void stopAutoSaveTask() {
        if (_autoSaveTask != null) {
            _autoSaveTask.cancel(false);
        }
        _autoSaveTask = null;
    }

    public void startPcBangPointsTask() {
        if (!Config.ALT_PCBANG_POINTS_ENABLED || Config.ALT_PCBANG_POINTS_DELAY <= 0) {
            return;
        }
        if (_pcCafePointsTask == null) {
            _pcCafePointsTask = LazyPrecisionTaskManager.getInstance().addPCCafePointsTask(this);
        }
    }

    public void stopPcBangPointsTask() {
        if (_pcCafePointsTask != null) {
            _pcCafePointsTask.cancel(false);
        }
        _pcCafePointsTask = null;
    }

    public void startUnjailTask(Player player, int time) {
        if (_unjailTask != null) {
            _unjailTask.cancel(false);
        }
        _unjailTask = ThreadPoolManager.getInstance().schedule(new UnJailTask(player), time * 60000);
    }

    public void stopUnjailTask() {
        if (_unjailTask != null) {
            _unjailTask.cancel(false);
        }
        _unjailTask = null;
    }

    @Override
    public void sendMessage(String message) {
        sendPacket(new SystemMessage(message));
    }

    public Location getLastClientPosition() {
        return _lastClientPosition;
    }

    public void setLastClientPosition(Location position) {
        _lastClientPosition = position;
    }

    public Location getLastServerPosition() {
        return _lastServerPosition;
    }

    public void setLastServerPosition(Location position) {
        _lastServerPosition = position;
    }

    public int getUseSeed() {
        return _useSeed;
    }

    public void setUseSeed(int id) {
        _useSeed = id;
    }

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

        if (getClan() != null) {
            result |= RelationChanged.RELATION_CLAN_MEMBER;
            if (getClan() == target.getClan()) {
                result |= RelationChanged.RELATION_CLAN_MATE;
            }
            if (getClan().getAllyId() != 0) {
                result |= RelationChanged.RELATION_ALLY_MEMBER;
            }
        }

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

        Party 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;
            }
        }

        Clan clan1 = getClan();
        Clan clan2 = target.getClan();
        if (clan1 != null && clan2 != null) {
            if (target.getPledgeType() != Clan.SUBUNIT_ACADEMY && getPledgeType() != Clan.SUBUNIT_ACADEMY) {
                if (clan2.isAtWarWith(clan1.getClanId())) {
                    result |= RelationChanged.RELATION_1SIDED_WAR;
                    if (clan1.isAtWarWith(clan2.getClanId())) {
                        result |= RelationChanged.RELATION_MUTUAL_WAR;
                    }
                }
            }
            if (getBlockCheckerArena() != -1) {
                result |= RelationChanged.RELATION_INSIEGE;
                ArenaParticipantsHolder holder = HandysBlockCheckerManager.getInstance()
                        .getHolder(getBlockCheckerArena());
                if (holder.getPlayerTeam(this) == 0) {
                    result |= RelationChanged.RELATION_ENEMY;
                } else {
                    result |= RelationChanged.RELATION_ALLY;
                }
                result |= RelationChanged.RELATION_ATTACKER;
            }
        }

        for (GlobalEvent e : getEvents()) {
            result = e.getRelation(this, target, result);
        }

        return result;
    }

    public long getlastPvpAttack() {
        return _lastPvpAttack;
    }

    @Override
    public void startPvPFlag(Creature target) {
        if (_karma < 0) {
            return;
        }

        long startTime = System.currentTimeMillis();
        if (target != null && target.getPvpFlag() != 0) {
            startTime -= Config.PVP_TIME / 2;
        }
        if (_pvpFlag != 0 && _lastPvpAttack > startTime) {
            return;
        }

        _lastPvpAttack = startTime;

        updatePvPFlag(1);

        if (_PvPRegTask == null) {
            _PvPRegTask = ThreadPoolManager.getInstance().scheduleAtFixedRate(new PvPFlagTask(this), 1000, 1000);
        }
    }

    public void stopPvPFlag() {
        if (_PvPRegTask != null) {
            _PvPRegTask.cancel(false);
            _PvPRegTask = null;
        }
        updatePvPFlag(0);
    }

    public void updatePvPFlag(int value) {
        if (_handysBlockCheckerEventArena != -1) {
            return;
        }
        if (_pvpFlag == value) {
            return;
        }

        setPvpFlag(value);

        sendStatusUpdate(true, true, StatusUpdate.PVP_FLAG);

        broadcastRelationChanged();
    }

    @Override
    public int getPvpFlag() {
        return _pvpFlag;
    }

    public void setPvpFlag(int pvpFlag) {
        _pvpFlag = pvpFlag;
    }

    public boolean isInDuel() {
        return getEvent(DuelEvent.class) != null;
    }

    public Map<Integer, TamedBeastInstance> getTrainedBeasts() {
        return _tamedBeasts;
    }

    public void addTrainedBeast(TamedBeastInstance tamedBeast) {
        _tamedBeasts.put(tamedBeast.getObjectId(), tamedBeast);
    }

    public void removeTrainedBeast(int npcId) {
        _tamedBeasts.remove(npcId);
    }

    public long getLastAttackPacket() {
        return _lastAttackPacket;
    }

    public void setLastAttackPacket() {
        _lastAttackPacket = System.currentTimeMillis();
    }

    public long getLastMovePacket() {
        return _lastMovePacket;
    }

    public void setLastMovePacket() {
        _lastMovePacket = System.currentTimeMillis();
    }

    public byte[] getKeyBindings() {
        return _keyBindings;
    }

    public void setKeyBindings(byte[] keyBindings) {
        if (keyBindings == null) {
            keyBindings = ArrayUtils.EMPTY_BYTE_ARRAY;
        }
        _keyBindings = keyBindings;
    }

    /**
     *   <BR>
     *
     * @param transformationId ?   :<BR> <li>0 -    <li>1 -
     *                         Onyx Beast <li>2 - Death Blader <li>etc.
     */
    @Override
    public void setTransformation(int transformationId) {
        if (transformationId == getTransformation() || getTransformation() != 0 && transformationId != 0) {
            return;
        }

        // ? ?    ?
        if (transformationId == 0) // ? 
        {
            //  ? ? 
            for (Effect effect : getEffectList().getAllEffects()) {
                if (effect != null && effect.getEffectType() == EffectType.Transformation) {
                    if (effect.calc() == 0) //   Dispel
                    {
                        continue;
                    }
                    effect.exit();
                    preparateToTransform(effect.getSkill());
                    break;
                }
            }

            // ? ? 
            if (!_transformationSkills.isEmpty()) {
                for (Skill s : _transformationSkills.values()) {
                    if (!s.isCommon() && !SkillAcquireHolder.getInstance().isSkillPossible(this, s)
                            && !s.isHeroic()) {
                        super.removeSkill(s);
                    }
                }
                _transformationSkills.clear();
            }
        } else {
            if (!isCursedWeaponEquipped()) {
                // ? ? 
                for (Effect effect : getEffectList().getAllEffects()) {
                    if (effect != null && effect.getEffectType() == EffectType.Transformation) {
                        if (effect.getSkill() instanceof Transformation
                                && ((Transformation) effect.getSkill()).isDisguise) {
                            for (Skill s : getAllSkills()) {
                                if (s != null && (s.isActive() || s.isToggle())) {
                                    _transformationSkills.put(s.getId(), s);
                                }
                            }
                        } else {
                            for (AddedSkill s : effect.getSkill().getAddedSkills()) {
                                if (s.level == 0) // ? ?
                                // ? 
                                // ?
                                {
                                    int s2 = getSkillLevel(s.id);
                                    if (s2 > 0) {
                                        _transformationSkills.put(s.id, SkillTable.getInstance().getInfo(s.id, s2));
                                    }
                                } else if (s.level == -2) // XXX: ?  ?
                                // ? ? 
                                // ? ?
                                {
                                    int learnLevel = Math.max(effect.getSkill().getMagicLevel(), 40);
                                    int maxLevel = SkillTable.getInstance().getBaseLevel(s.id);
                                    int curSkillLevel = 1;
                                    if (maxLevel > 3) {
                                        curSkillLevel += getLevel() - learnLevel;
                                    } else {
                                        curSkillLevel += (getLevel() - learnLevel) / ((76 - learnLevel) / maxLevel); // 
                                    }
                                    // 
                                    // ?
                                    // 
                                    // 
                                    // ?
                                    curSkillLevel = Math.min(Math.max(curSkillLevel, 1), maxLevel);
                                    _transformationSkills.put(s.id,
                                            SkillTable.getInstance().getInfo(s.id, curSkillLevel));
                                } else {
                                    _transformationSkills.put(s.id, s.getSkill());
                                }
                            }
                        }
                        preparateToTransform(effect.getSkill());
                        break;
                    }
                }
            } else {
                preparateToTransform(null);
            }

            if (!isInOlympiadMode() && !isCursedWeaponEquipped() && _hero
                    && getSubClassList().isBaseClassActive()) {
                // ?  ? ?? 
                _transformationSkills.put(395, SkillTable.getInstance().getInfo(395, 1));
                _transformationSkills.put(396, SkillTable.getInstance().getInfo(396, 1));
                _transformationSkills.put(1374, SkillTable.getInstance().getInfo(1374, 1));
                _transformationSkills.put(1375, SkillTable.getInstance().getInfo(1375, 1));
                _transformationSkills.put(1376, SkillTable.getInstance().getInfo(1376, 1));
            }

            for (Skill s : _transformationSkills.values()) {
                addSkill(s, false);
            }
        }

        super.setTransformation(transformationId);

        sendPacket(new ExBasicActionList(this));
        sendSkillList();
        sendPacket(new ShortCutInit(this));
        for (int shotId : getAutoSoulShot()) {
            sendPacket(new ExAutoSoulShot(shotId, true));
        }
        broadcastUserInfo(true);
    }

    private void preparateToTransform(Skill transSkill) {
        if (transSkill == null || !transSkill.isBaseTransformation())
        //   ?
        {
            for (Effect effect : getEffectList().getAllEffects()) {
                if (effect != null && effect.getSkill().isToggle()) {
                    effect.exit();
                }
            }
        }
    }

    /**
     *  ?? ?,   ? 
     */
    @Override
    public final Collection<Skill> getAllSkills() {
        // ? ?
        if (getTransformation() == 0) {
            return super.getAllSkills();
        }

        // ? ?
        Map<Integer, Skill> tempSkills = new HashMap<Integer, Skill>();
        for (Skill s : super.getAllSkills()) {
            if (s != null && !s.isActive() && !s.isToggle()) {
                tempSkills.put(s.getId(), s);
            }
        }
        tempSkills.putAll(_transformationSkills); // ? ? ? ?
        // ? 
        return tempSkills.values();
    }

    public void setAgathion(int id) {
        if (_agathionId == id) {
            return;
        }

        _agathionId = id;
        broadcastCharInfo();
    }

    public int getAgathionId() {
        return _agathionId;
    }

    /**
     *  ? PcBangPoint'  ?
     *
     * @return ? PcCafe Bang Points
     */
    public int getPcBangPoints() {
        return _pcBangPoints;
    }

    /**
     *  ? Pc Cafe Bang Points ?  ?
     *
     * @param val  ? PcCafeBangPoints
     */
    public void setPcBangPoints(int val) {
        _pcBangPoints = val;
    }

    public void addPcBangPoints(int count, boolean doublePoints) {
        if (doublePoints) {
            count *= 2;
        }

        _pcBangPoints += count;

        sendPacket(new SystemMessage(doublePoints ? SystemMessage.DOUBLE_POINTS_YOU_AQUIRED_S1_PC_BANG_POINT
                : SystemMessage.YOU_ACQUIRED_S1_PC_BANG_POINT).addNumber(count));
        sendPacket(new ExPCCafePointInfo(this, count, 1, 2, 12));
    }

    public boolean reducePcBangPoints(int count) {
        if (_pcBangPoints < count) {
            return false;
        }

        _pcBangPoints -= count;
        sendPacket(new SystemMessage(SystemMessage.YOU_ARE_USING_S1_POINT).addNumber(count));
        sendPacket(new ExPCCafePointInfo(this, 0, 1, 2, 12));
        return true;
    }

    public Location getGroundSkillLoc() {
        return _groundSkillLoc;
    }

    public void setGroundSkillLoc(Location location) {
        _groundSkillLoc = location;
    }

    /**
     *      
     *
     * @return  true     ?
     */
    public boolean isLogoutStarted() {
        return _isLogout.get();
    }

    public void setOfflineMode(boolean val) {
        if (!val) {
            unsetVar("offline");
        }
        _offline = val;
    }

    public boolean isInOfflineMode() {
        return _offline;
    }

    public void saveTradeList() {
        String val = "";

        if (_sellList == null || _sellList.isEmpty()) {
            unsetVar("selllist");
        } else {
            for (TradeItem i : _sellList) {
                val += i.getObjectId() + ";" + i.getCount() + ";" + i.getOwnersPrice() + ":";
            }
            setVar("selllist", val, -1);
            val = "";
            if (_tradeList != null && getSellStoreName() != null) {
                setVar("sellstorename", getSellStoreName(), -1);
            }
        }

        if (_packageSellList == null || _packageSellList.isEmpty()) {
            unsetVar("packageselllist");
        } else {
            for (TradeItem i : _packageSellList) {
                val += i.getObjectId() + ";" + i.getCount() + ";" + i.getOwnersPrice() + ":";
            }
            setVar("packageselllist", val, -1);
            val = "";
            if (_tradeList != null && getSellStoreName() != null) {
                setVar("sellstorename", getSellStoreName(), -1);
            }
        }

        if (_buyList == null || _buyList.isEmpty()) {
            unsetVar("buylist");
        } else {
            for (TradeItem i : _buyList) {
                val += i.getItemId() + ";" + i.getCount() + ";" + i.getOwnersPrice() + ":";
            }
            setVar("buylist", val, -1);
            val = "";
            if (_tradeList != null && getBuyStoreName() != null) {
                setVar("buystorename", getBuyStoreName(), -1);
            }
        }

        if (_createList == null || _createList.isEmpty()) {
            unsetVar("createlist");
        } else {
            for (ManufactureItem i : _createList) {
                val += i.getRecipeId() + ";" + i.getCost() + ":";
            }
            setVar("createlist", val, -1);
            if (getManufactureName() != null) {
                setVar("manufacturename", getManufactureName(), -1);
            }
        }
    }

    public void restoreTradeList() {
        String var;
        var = getVar("selllist");
        if (var != null) {
            _sellList = new CopyOnWriteArrayList<TradeItem>();
            String[] items = var.split(":");
            for (String item : items) {
                if (item.equals("")) {
                    continue;
                }
                String[] values = item.split(";");
                if (values.length < 3) {
                    continue;
                }

                int oId = Integer.parseInt(values[0]);
                long count = Long.parseLong(values[1]);
                long price = Long.parseLong(values[2]);

                ItemInstance itemToSell = getInventory().getItemByObjectId(oId);

                if (count < 1 || itemToSell == null) {
                    continue;
                }

                if (count > itemToSell.getCount()) {
                    count = itemToSell.getCount();
                }

                TradeItem i = new TradeItem(itemToSell);
                i.setCount(count);
                i.setOwnersPrice(price);

                _sellList.add(i);
            }
            var = getVar("sellstorename");
            if (var != null) {
                setSellStoreName(var);
            }
        }
        var = getVar("packageselllist");
        if (var != null) {
            _packageSellList = new CopyOnWriteArrayList<TradeItem>();
            String[] items = var.split(":");
            for (String item : items) {
                if (item.equals("")) {
                    continue;
                }
                String[] values = item.split(";");
                if (values.length < 3) {
                    continue;
                }

                int oId = Integer.parseInt(values[0]);
                long count = Long.parseLong(values[1]);
                long price = Long.parseLong(values[2]);

                ItemInstance itemToSell = getInventory().getItemByObjectId(oId);

                if (count < 1 || itemToSell == null) {
                    continue;
                }

                if (count > itemToSell.getCount()) {
                    count = itemToSell.getCount();
                }

                TradeItem i = new TradeItem(itemToSell);
                i.setCount(count);
                i.setOwnersPrice(price);

                _packageSellList.add(i);
            }
            var = getVar("sellstorename");
            if (var != null) {
                setSellStoreName(var);
            }
        }
        var = getVar("buylist");
        if (var != null) {
            _buyList = new CopyOnWriteArrayList<TradeItem>();
            String[] items = var.split(":");
            for (String item : items) {
                if (item.equals("")) {
                    continue;
                }
                String[] values = item.split(";");
                if (values.length < 3) {
                    continue;
                }
                TradeItem i = new TradeItem();
                i.setItemId(Integer.parseInt(values[0]));
                i.setCount(Long.parseLong(values[1]));
                i.setOwnersPrice(Long.parseLong(values[2]));
                _buyList.add(i);
            }
            var = getVar("buystorename");
            if (var != null) {
                setBuyStoreName(var);
            }
        }
        var = getVar("createlist");
        if (var != null) {
            _createList = new CopyOnWriteArrayList<ManufactureItem>();
            String[] items = var.split(":");
            for (String item : items) {
                if (item.equals("")) {
                    continue;
                }
                String[] values = item.split(";");
                if (values.length < 2) {
                    continue;
                }
                int recId = Integer.parseInt(values[0]);
                long price = Long.parseLong(values[1]);
                if (findRecipe(recId)) {
                    _createList.add(new ManufactureItem(recId, price));
                }
            }
            var = getVar("manufacturename");
            if (var != null) {
                setManufactureName(var);
            }
        }
    }

    public DecoyInstance getDecoy() {
        return _decoy;
    }

    public void setDecoy(DecoyInstance decoy) {
        _decoy = decoy;
    }

    public int getMountType() {
        switch (getMountNpcId()) {
        case PetDataTable.STRIDER_WIND_ID:
        case PetDataTable.STRIDER_STAR_ID:
        case PetDataTable.STRIDER_TWILIGHT_ID:
        case PetDataTable.RED_STRIDER_WIND_ID:
        case PetDataTable.RED_STRIDER_STAR_ID:
        case PetDataTable.RED_STRIDER_TWILIGHT_ID:
        case PetDataTable.GUARDIANS_STRIDER_ID:
            return 1;
        case PetDataTable.WYVERN_ID:
            return 2;
        case PetDataTable.WGREAT_WOLF_ID:
        case PetDataTable.FENRIR_WOLF_ID:
        case PetDataTable.WFENRIR_WOLF_ID:
            return 3;
        }
        return 0;
    }

    @Override
    public double getColRadius() {
        if (getTransformation() != 0) {
            int template = getTransformationTemplate();
            if (template != 0) {
                NpcTemplate npcTemplate = NpcHolder.getInstance().getTemplate(template);
                if (npcTemplate != null) {
                    return npcTemplate.getCollisionRadius();
                }
            }
        } else if (isMounted()) {
            int mountTemplate = getMountNpcId();
            if (mountTemplate != 0) {
                NpcTemplate mountNpcTemplate = NpcHolder.getInstance().getTemplate(mountTemplate);
                if (mountNpcTemplate != null) {
                    return mountNpcTemplate.getCollisionRadius();
                }
            }
        }
        return getCollisionRadius();
    }

    @Override
    public double getColHeight() {
        if (getTransformation() != 0) {
            int template = getTransformationTemplate();
            if (template != 0) {
                NpcTemplate npcTemplate = NpcHolder.getInstance().getTemplate(template);
                if (npcTemplate != null) {
                    return npcTemplate.getCollisionHeight();
                }
            }
        } else if (isMounted()) {
            int mountTemplate = getMountNpcId();
            if (mountTemplate != 0) {
                NpcTemplate mountNpcTemplate = NpcHolder.getInstance().getTemplate(mountTemplate);
                if (mountNpcTemplate != null) {
                    return mountNpcTemplate.getCollisionHeight();
                }
            }
        }
        return getCollisionHeight();
    }

    @Override
    public void setReflection(Reflection reflection) {
        if (getReflection() == reflection) {
            return;
        }

        super.setReflection(reflection);

        for (Summon summon : getPets()) {
            if (summon != null && !summon.isDead()) {
                summon.setReflection(reflection);
            }
        }

        if (reflection != ReflectionManager.DEFAULT) {
            String var = getVar("reflection");
            if (var == null || !var.equals(String.valueOf(reflection.getId()))) {
                setVar("reflection", String.valueOf(reflection.getId()), -1);
            }
        } else {
            unsetVar("reflection");
        }

        if (getActiveSubClass() != null) {
            getInventory().validateItems();
            // ? ? _129_PailakaDevilsLegacy
            for (Summon summon : getPets()) {
                if (summon.getNpcId() == 14916 || summon.getNpcId() == 14917) {
                    summon.unSummon();
                }
            }
        }
    }

    public double getCollisionRadius() {
        return _collision_radius;
    }

    public double getCollisionHeight() {
        return _collision_height;
    }

    public boolean isTerritoryFlagEquipped() {
        ItemInstance weapon = getActiveWeaponInstance();
        return weapon != null && weapon.getTemplate().isTerritoryFlag();
    }

    public int getBuyListId() {
        return _buyListId;
    }

    public void setBuyListId(int listId) {
        _buyListId = listId;
    }

    public int getFame() {
        return _fame;
    }

    public void setFame(int fame, String log) {
        fame = Math.min(Config.LIM_FAME, fame);
        if (log != null && !log.isEmpty()) {
            Log.add(_name + "|" + (fame - _fame) + "|" + fame + "|" + log, "fame");
        }
        if (fame > _fame) {
            sendPacket(
                    new SystemMessage(SystemMessage.YOU_HAVE_ACQUIRED_S1_REPUTATION_SCORE).addNumber(fame - _fame));
        }
        _fame = fame;
        sendChanges();
    }

    public int getIncorrectValidateCount() {
        return _incorrectValidateCount;
    }

    public int setIncorrectValidateCount(int count) {
        return _incorrectValidateCount;
    }

    public int getExpandInventory() {
        return _expandInventory;
    }

    public void setExpandInventory(int inventory) {
        _expandInventory = inventory;
    }

    public int getExpandWarehouse() {
        return _expandWarehouse;
    }

    public void setExpandWarehouse(int warehouse) {
        _expandWarehouse = warehouse;
    }

    public boolean isNotShowBuffAnim() {
        return _notShowBuffAnim;
    }

    public void setNotShowBuffAnim(boolean value) {
        _notShowBuffAnim = value;
    }

    public void enterMovieMode() {
        if (isInMovie()) // already in movie
        {
            return;
        }

        setTarget(null);
        stopMove();
        setIsInMovie(true);
        sendPacket(new CameraMode(1));
    }

    public void leaveMovieMode() {
        setIsInMovie(false);
        sendPacket(new CameraMode(0));
        broadcastCharInfo();
    }

    public void specialCamera(GameObject target, int dist, int yaw, int pitch, int time, int duration) {
        sendPacket(new SpecialCamera(target.getObjectId(), dist, yaw, pitch, time, duration));
    }

    public void specialCamera(GameObject target, int dist, int yaw, int pitch, int time, int duration, int turn,
            int rise, int widescreen, int unk) {
        sendPacket(new SpecialCamera(target.getObjectId(), dist, yaw, pitch, time, duration, turn, rise, widescreen,
                unk));
    }

    public int getMovieId() {
        return _movieId;
    }

    public void setMovieId(int id) {
        _movieId = id;
    }

    public boolean isInMovie() {
        return _isInMovie;
    }

    public void setIsInMovie(boolean state) {
        _isInMovie = state;
    }

    public void showQuestMovie(SceneMovie movie) {
        if (isInMovie()) // already in movie
        {
            return;
        }

        sendActionFailed();
        setTarget(null);
        stopMove();
        setMovieId(movie.getId());
        setIsInMovie(true);
        sendPacket(movie.packet(this));
    }

    public void showQuestMovie(int movieId) {
        if (isInMovie()) // already in movie
        {
            return;
        }

        sendActionFailed();
        setTarget(null);
        stopMove();
        setMovieId(movieId);
        setIsInMovie(true);
        sendPacket(new ExStartScenePlayer(movieId));
    }

    public void setAutoLoot(boolean enable) {
        if (Config.AUTO_LOOT_INDIVIDUAL) {
            _autoLoot = enable;
            setVar("AutoLoot", String.valueOf(enable), -1);
        }
    }

    public void setAutoLootHerbs(boolean enable) {
        if (Config.AUTO_LOOT_INDIVIDUAL) {
            AutoLootHerbs = enable;
            setVar("AutoLootHerbs", String.valueOf(enable), -1);
        }
    }

    public boolean isAutoLootEnabled() {
        return _autoLoot;
    }

    public boolean isAutoLootHerbsEnabled() {
        return AutoLootHerbs;
    }

    public final void setVisibleName(String visibleName) {
        _visibleName = visibleName;
    }

    public final String getVisibleName() {
        if (_visibleName == null) {
            _visibleName = getName();
        }
        return _visibleName;
    }

    public final void reName(String name, boolean saveToDB) {
        setName(name);
        if (saveToDB) {
            saveNameToDB();
        }
        broadcastCharInfo();
    }

    public final void reName(String name) {
        reName(name, false);
    }

    public final void saveNameToDB() {
        Connection con = null;
        PreparedStatement st = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            st = con.prepareStatement("UPDATE characters SET char_name = ? WHERE obj_Id = ?");
            st.setString(1, getName());
            st.setInt(2, getObjectId());
            st.executeUpdate();
        } catch (Exception e) {
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, st);
        }
    }

    @Override
    public Player getPlayer() {
        return this;
    }

    private List<String> getStoredBypasses(boolean bbs) {
        if (bbs) {
            if (bypasses_bbs == null) {
                bypasses_bbs = new LazyArrayList<String>();
            }
            return bypasses_bbs;
        }
        if (bypasses == null) {
            bypasses = new LazyArrayList<String>();
        }
        return bypasses;
    }

    public void cleanBypasses(boolean bbs) {
        List<String> bypassStorage = getStoredBypasses(bbs);
        synchronized (bypassStorage) {
            bypassStorage.clear();
        }
    }

    public String encodeBypasses(String htmlCode, boolean bbs) {
        List<String> bypassStorage = getStoredBypasses(bbs);
        synchronized (bypassStorage) {
            return BypassManager.encode(htmlCode, bypassStorage, bbs);
        }
    }

    public DecodedBypass decodeBypass(String bypass) {
        BypassType bpType = BypassManager.getBypassType(bypass);
        boolean bbs = bpType == BypassType.ENCODED_BBS || bpType == BypassType.SIMPLE_BBS;
        List<String> bypassStorage = getStoredBypasses(bbs);
        if (bpType == BypassType.ENCODED || bpType == BypassType.ENCODED_BBS) {
            return BypassManager.decode(bypass, bypassStorage, bbs, this);
        }
        if (bpType == BypassType.SIMPLE) {
            return new DecodedBypass(bypass, false).trim();
        }
        if (bpType == BypassType.SIMPLE_BBS && !bypass.startsWith("_bbsscripts")) {
            return new DecodedBypass(bypass, true).trim();
        }
        ICommunityBoardHandler handler = CommunityBoardHandler.getInstance().getCommunityHandler(bypass);
        if (handler != null) {
            return new DecodedBypass(bypass, handler).trim();
        }
        _log.warn("Direct access to bypass: " + bypass + " / Player: " + getName());
        return null;
    }

    public int getTalismanCount() {
        return (int) calcStat(Stats.TALISMANS_LIMIT, 0, null, null);
    }

    public boolean getOpenCloak() {
        if (Config.ALT_OPEN_CLOAK_SLOT) {
            return true;
        }
        return (int) calcStat(Stats.CLOAK_SLOT, 0, null, null) > 0;
    }

    public final void disableDrop(int time) {
        _dropDisabled = System.currentTimeMillis() + time;
    }

    public final boolean isDropDisabled() {
        return _dropDisabled > System.currentTimeMillis();
    }

    public void setPetControlItem(int itemObjId) {
        setPetControlItem(getInventory().getItemByObjectId(itemObjId));
    }

    public ItemInstance getPetControlItem() {
        return _petControlItem;
    }

    public void setPetControlItem(ItemInstance item) {
        _petControlItem = item;
    }

    public boolean isActive() {
        return isActive.get();
    }

    public void setActive() {
        setNonAggroTime(0);

        if (isActive.getAndSet(true)) {
            return;
        }

        onActive();
    }

    private void onActive() {
        setNonAggroTime(0);
        sendPacket(Msg.YOU_ARE_PROTECTED_AGGRESSIVE_MONSTERS);
        if (getPetControlItem() != null) {
            ThreadPoolManager.getInstance().execute(new RunnableImpl() {
                @Override
                public void runImpl() {
                    if (getPetControlItem() != null) {
                        summonPet();
                    }
                }

            });
        }
    }

    public void summonPet() {
        if (isHaveSummonedPets()) {
            return;
        }

        ItemInstance controlItem = getPetControlItem();
        if (controlItem == null) {
            return;
        }
        int npcId = PetDataTable.getSummonId(controlItem);
        if (npcId == 0) {
            return;
        }
        NpcTemplate petTemplate = NpcHolder.getInstance().getTemplate(npcId);
        if (petTemplate == null) {
            return;
        }
        PetInstance pet = PetInstance.restore(controlItem, petTemplate, this);
        if (pet == null) {
            return;
        }
        addPet(pet);
        pet.setTitle(getName());

        if (!pet.isRespawned()) {
            pet.setCurrentHp(pet.getMaxHp(), false);
            pet.setCurrentMp(pet.getMaxMp());
            pet.setCurrentFed(pet.getMaxFed());
            pet.updateControlItem();
            pet.store();
        }

        pet.getInventory().restore();

        pet.setNonAggroTime(System.currentTimeMillis() + Config.NONAGGRO_TIME_ONTELEPORT);
        pet.setReflection(getReflection());
        pet.spawnMe(Location.findPointToStay(this, 50, 70));
        pet.setRunning();
        pet.setFollowMode(true);
        pet.getInventory().validateItems();

        if (pet instanceof PetBabyInstance) {
            ((PetBabyInstance) pet).startBuffTask();
        }
    }

    public Collection<TrapInstance> getTraps() {
        if (_traps == null) {
            return null;
        }
        Collection<TrapInstance> result = new ArrayList<TrapInstance>(getTrapsCount());
        TrapInstance trap;
        for (Integer trapId : _traps.keySet()) {
            if ((trap = (TrapInstance) GameObjectsStorage.get(_traps.get(trapId))) != null) {
                result.add(trap);
            } else {
                _traps.remove(trapId);
            }
        }
        return result;
    }

    public int getTrapsCount() {
        return _traps == null ? 0 : _traps.size();
    }

    public void addTrap(TrapInstance trap) {
        if (_traps == null) {
            _traps = new HashMap<Integer, Long>();
        }
        _traps.put(trap.getObjectId(), trap.getStoredId());
    }

    public void removeTrap(TrapInstance trap) {
        Map<Integer, Long> traps = _traps;
        if (traps == null || traps.isEmpty()) {
            return;
        }
        traps.remove(trap.getObjectId());
    }

    public void destroyFirstTrap() {
        Map<Integer, Long> traps = _traps;
        if (traps == null || traps.isEmpty()) {
            return;
        }
        TrapInstance trap;
        for (Integer trapId : traps.keySet()) {
            if ((trap = (TrapInstance) GameObjectsStorage.get(traps.get(trapId))) != null) {
                trap.deleteMe();
                return;
            }
            return;
        }
    }

    public void destroyAllTraps() {
        Map<Integer, Long> traps = _traps;
        if (traps == null || traps.isEmpty()) {
            return;
        }
        List<TrapInstance> toRemove = new ArrayList<TrapInstance>();
        for (Integer trapId : traps.keySet()) {
            toRemove.add((TrapInstance) GameObjectsStorage.get(traps.get(trapId)));
        }
        for (TrapInstance t : toRemove) {
            if (t != null) {
                t.deleteMe();
            }
        }
    }

    public int getBlockCheckerArena() {
        return _handysBlockCheckerEventArena;
    }

    public void setBlockCheckerArena(byte arena) {
        _handysBlockCheckerEventArena = arena;
    }

    @Override
    public PlayerListenerList getListeners() {
        if (listeners == null) {
            synchronized (this) {
                if (listeners == null) {
                    listeners = new PlayerListenerList(this);
                }
            }
        }
        return (PlayerListenerList) listeners;
    }

    @Override
    public PlayerStatsChangeRecorder getStatsRecorder() {
        if (_statsRecorder == null) {
            synchronized (this) {
                if (_statsRecorder == null) {
                    _statsRecorder = new PlayerStatsChangeRecorder(this);
                }
            }
        }
        return (PlayerStatsChangeRecorder) _statsRecorder;
    }

    public int getHoursInGame() {
        _hoursInGame++;
        return _hoursInGame;
    }

    public void startHourlyTask() {
        _hourlyTask = ThreadPoolManager.getInstance().scheduleAtFixedRate(new HourlyTask(this), 3600000L, 3600000L);
    }

    public void stopHourlyTask() {
        if (_hourlyTask != null) {
            _hourlyTask.cancel(false);
            _hourlyTask = null;
        }
    }

    public long getPremiumPoints() {
        if (Config.GAME_POINT_ITEM_ID != -1) {
            return ItemFunctions.getItemCount(this, Config.GAME_POINT_ITEM_ID);
        } else {
            return getNetConnection().getPremiumPoint();
        }
    }

    public void reducePremiumPoints(final int val) {
        int reduce = (getNetConnection().getPremiumPoint() - (val));

        if (Config.GAME_POINT_ITEM_ID != -1) {
            ItemFunctions.removeItem(this, Config.GAME_POINT_ITEM_ID, val, true);
        } else {
            getNetConnection().setPremiumPoint(reduce);
        }
    }

    public boolean isAgathionResAvailable() {
        return _agathionResAvailable;
    }

    public void setAgathionRes(boolean val) {
        _agathionResAvailable = val;
    }

    public boolean isClanAirShipDriver() {
        return isInBoat() && getBoat().isClanAirShip() && ((ClanAirShip) getBoat()).getDriver() == this;
    }

    public String getSessionVar(String key) {
        if (_userSession == null) {
            return null;
        }
        return _userSession.get(key);
    }

    public void setSessionVar(String key, String val) {
        if (_userSession == null) {
            _userSession = new ConcurrentHashMap<String, String>();
        }

        if (val == null || val.isEmpty()) {
            _userSession.remove(key);
        } else {
            _userSession.put(key, val);
        }
    }

    public FriendList getFriendList() {
        return _friendList;
    }

    public boolean isNotShowTraders() {
        return _notShowTraders;
    }

    public void setNotShowTraders(boolean notShowTraders) {
        _notShowTraders = notShowTraders;
    }

    public boolean isDebug() {
        return _debug;
    }

    public void setDebug(boolean b) {
        _debug = b;
    }

    public void sendItemList(boolean show) {
        ItemInstance[] items = getInventory().getItems();
        LockType lockType = getInventory().getLockType();
        int[] lockItems = getInventory().getLockItems();

        int allSize = items.length;
        int questItemsSize = 0;
        int agathionItemsSize = 0;
        for (ItemInstance item : items) {
            if (item.getTemplate().isQuest()) {
                questItemsSize++;
            }
            if (item.getTemplate().getAgathionEnergy() > 0) {
                agathionItemsSize++;
            }
        }

        sendPacket(new ExAdenaInvenCount(this));

        sendPacket(new ItemList(allSize - questItemsSize, items, show, lockType, lockItems));
        if (questItemsSize > 0) {
            sendPacket(new ExQuestItemList(questItemsSize, items, lockType, lockItems));
        }
        if (agathionItemsSize > 0) {
            sendPacket(new ExBR_AgathionEnergyInfo(agathionItemsSize, items));
        }
    }

    public int getBeltInventoryIncrease() {
        ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_BELT);
        if (item != null && item.getTemplate().getAttachedSkills() != null) {
            for (Skill skill : item.getTemplate().getAttachedSkills()) {
                for (FuncTemplate func : skill.getAttachedFuncs()) {
                    if (func._stat == Stats.INVENTORY_LIMIT) {
                        return (int) func._value;
                    }
                }
            }
        }
        return 0;
    }

    @Override
    public boolean isPlayer() {
        return true;
    }

    public boolean checkCoupleAction(Player target) {
        if (target.getPrivateStoreType() != Player.STORE_PRIVATE_NONE) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IN_PRIVATE_STORE)
                    .addName(target));
            return false;
        }
        if (target.isFishing()) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_FISHING).addName(target));
            return false;
        }
        if (target.isInCombat()) {
            sendPacket(
                    new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_IN_COMBAT).addName(target));
            return false;
        }
        if (target.isCursedWeaponEquipped()) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_CURSED_WEAPON_EQUIPED)
                    .addName(target));
            return false;
        }
        if (target.isInOlympiadMode()) {
            sendPacket(
                    new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_IN_OLYMPIAD).addName(target));
            return false;
        }
        if (target.isOnSiegeField()) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_IN_SIEGE).addName(target));
            return false;
        }
        if (target.isInBoat() || target.getMountNpcId() != 0) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_IN_VEHICLE_MOUNT_OTHER)
                    .addName(target));
            return false;
        }
        if (target.isTeleporting()) {
            sendPacket(
                    new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_TELEPORTING).addName(target));
            return false;
        }
        if (target.getTransformation() != 0) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_IN_TRANSFORM)
                    .addName(target));
            return false;
        }
        if (target.isDead()) {
            sendPacket(new SystemMessage(SystemMessage.COUPLE_ACTION_CANNOT_C1_TARGET_IS_DEAD).addName(target));
            return false;
        }
        return true;
    }

    @Override
    public void displayGiveDamageMessage(Creature target, int damage, boolean crit, boolean miss, boolean shld,
            boolean magic) {
        super.displayGiveDamageMessage(target, damage, crit, miss, shld, magic);
        if (crit) {
            if (magic) {
                sendPacket(new SystemMessage(SystemMessage.MAGIC_CRITICAL_HIT).addName(this));
            } else {
                sendPacket(new SystemMessage(SystemMessage.C1_HAD_A_CRITICAL_HIT).addName(this));
            }
        }

        if (miss) {
            sendPacket(new SystemMessage(SystemMessage.C1S_ATTACK_WENT_ASTRAY).addName(this));
        } else if (!target.isDamageBlocked()) {
            sendPacket(new SystemMessage(SystemMessage.C1_HAS_GIVEN_C2_DAMAGE_OF_S3).addName(this).addName(target)
                    .addNumber(damage));
        }

        if (target.isPlayer()) {
            if (shld && damage > 1) {
                target.sendPacket(SystemMsg.YOUR_SHIELD_DEFENSE_HAS_SUCCEEDED);
            } else if (shld && damage == 1) {
                target.sendPacket(SystemMsg.YOUR_EXCELLENT_SHIELD_DEFENSE_WAS_A_SUCCESS);
            }
        }
    }

    @Override
    public void displayReceiveDamageMessage(Creature attacker, int damage) {
        if (attacker != this) {
            sendPacket(new SystemMessage(SystemMessage.C1_HAS_RECEIVED_DAMAGE_OF_S3_FROM_C2).addName(this)
                    .addName(attacker).addNumber((long) damage));
        }
    }

    public IntObjectMap<String> getPostFriends() {
        return _postFriends;
    }

    public boolean isSharedGroupDisabled(int groupId) {
        TimeStamp sts = _sharedGroupReuses.get(groupId);
        if (sts == null) {
            return false;
        }
        if (sts.hasNotPassed()) {
            return true;
        }
        _sharedGroupReuses.remove(groupId);
        return false;
    }

    public TimeStamp getSharedGroupReuse(int groupId) {
        return _sharedGroupReuses.get(groupId);
    }

    public void addSharedGroupReuse(int group, TimeStamp stamp) {
        _sharedGroupReuses.put(group, stamp);
    }

    public Collection<IntObjectMap.Entry<TimeStamp>> getSharedGroupReuses() {
        return _sharedGroupReuses.entrySet();
    }

    public void sendReuseMessage(ItemInstance item) {
        TimeStamp sts = getSharedGroupReuse(item.getTemplate().getReuseGroup());
        if (sts == null || !sts.hasNotPassed()) {
            return;
        }

        long timeleft = sts.getReuseCurrent();
        long hours = timeleft / 3600000;
        long minutes = (timeleft - hours * 3600000) / 60000;
        long seconds = (long) Math.ceil((timeleft - hours * 3600000 - minutes * 60000) / 1000.);

        if (hours > 0) {
            sendPacket(new SystemMessage2(item.getTemplate().getReuseType().getMessages()[2])
                    .addItemName(item.getTemplate().getItemId()).addInteger(hours).addInteger(minutes)
                    .addInteger(seconds));
        } else if (minutes > 0) {
            sendPacket(new SystemMessage2(item.getTemplate().getReuseType().getMessages()[1])
                    .addItemName(item.getTemplate().getItemId()).addInteger(minutes).addInteger(seconds));
        } else {
            sendPacket(new SystemMessage2(item.getTemplate().getReuseType().getMessages()[0])
                    .addItemName(item.getTemplate().getItemId()).addInteger(seconds));
        }
    }

    public void ask(ConfirmDlg dlg, OnAnswerListener listener) {
        if (_askDialog != null) {
            return;
        }
        int rnd = Rnd.nextInt();
        _askDialog = new ImmutablePair<Integer, OnAnswerListener>(rnd, listener);
        dlg.setRequestId(rnd);
        sendPacket(dlg);
    }

    public Pair<Integer, OnAnswerListener> getAskListener(boolean clear) {
        if (!clear) {
            return _askDialog;
        } else {
            Pair<Integer, OnAnswerListener> ask = _askDialog;
            _askDialog = null;
            return ask;
        }
    }

    @Override
    public boolean isDead() {
        return isInOlympiadMode() || isInDuel() ? getCurrentHp() <= 1. : super.isDead();
    }

    @Override
    public int getAgathionEnergy() {
        ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LBRACELET);
        return item == null ? 0 : item.getAgathionEnergy();
    }

    @Override
    public void setAgathionEnergy(int val) {
        ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LBRACELET);
        if (item == null) {
            return;
        }
        item.setAgathionEnergy(val);
        item.setJdbcState(JdbcEntityState.UPDATED);

        sendPacket(new ExBR_AgathionEnergyInfo(1, item));
    }

    public boolean hasPrivilege(Privilege privilege) {
        return _clan != null && (getClanPrivileges() & privilege.mask()) == privilege.mask();
    }

    public MatchingRoom getMatchingRoom() {
        return _matchingRoom;
    }

    public void setMatchingRoom(MatchingRoom matchingRoom) {
        _matchingRoom = matchingRoom;
    }

    public void dispelBuffs() {
        for (Effect e : getEffectList().getAllEffects()) {
            if (!e.getSkill().isOffensive() && !e.getSkill().isNewbie() && e.isCancelable()
                    && !e.getSkill().isPreservedOnDeath()) {
                sendPacket(new SystemMessage(SystemMessage.THE_EFFECT_OF_S1_HAS_BEEN_REMOVED)
                        .addSkillName(e.getSkill().getId(), e.getSkill().getLevel()));
                e.exit();
            }
        }
        for (Summon summon : getPets()) {
            for (Effect e : summon.getEffectList().getAllEffects()) {
                if (!e.getSkill().isOffensive() && !e.getSkill().isNewbie() && e.isCancelable()
                        && !e.getSkill().isPreservedOnDeath()) {
                    e.exit();
                }
            }
        }
    }

    public void setInstanceReuse(int id, long time) {
        final SystemMessage msg = new SystemMessage(
                SystemMessage.INSTANT_ZONE_FROM_HERE__S1_S_ENTRY_HAS_BEEN_RESTRICTED_YOU_CAN_CHECK_THE_NEXT_ENTRY_POSSIBLE)
                        .addString(getName());
        sendPacket(msg);
        _instancesReuses.put(id, time);
        mysql.set("REPLACE INTO character_instances (obj_id, id, reuse) VALUES (?,?,?)", getObjectId(), id, time);
    }

    public void removeInstanceReuse(int id) {
        if (_instancesReuses.remove(id) != null) {
            mysql.set("DELETE FROM `character_instances` WHERE `obj_id`=? AND `id`=? LIMIT 1", getObjectId(), id);
        }
    }

    public void removeAllInstanceReuses() {
        _instancesReuses.clear();
        mysql.set("DELETE FROM `character_instances` WHERE `obj_id`=?", getObjectId());
    }

    public void removeInstanceReusesByGroupId(int groupId) {
        for (int i : InstantZoneHolder.getInstance().getSharedReuseInstanceIdsByGroup(groupId)) {
            if (getInstanceReuse(i) != null) {
                removeInstanceReuse(i);
            }
        }
    }

    public Long getInstanceReuse(int id) {
        return _instancesReuses.get(id);
    }

    public Map<Integer, Long> getInstanceReuses() {
        return _instancesReuses;
    }

    private void loadInstanceReuses() {
        Connection con = null;
        PreparedStatement offline = null;
        ResultSet rs = null;
        try {
            con = DatabaseFactory.getInstance().getConnection();
            offline = con.prepareStatement("SELECT * FROM character_instances WHERE obj_id = ?");
            offline.setInt(1, getObjectId());
            rs = offline.executeQuery();
            while (rs.next()) {
                int id = rs.getInt("id");
                long reuse = rs.getLong("reuse");
                _instancesReuses.put(id, reuse);
            }
        } catch (Exception e) {
            _log.error("", e);
        } finally {
            DbUtils.closeQuietly(con, offline, rs);
        }
    }

    public Reflection getActiveReflection() {
        for (Reflection r : ReflectionManager.getInstance().getAll()) {
            if (r != null && ArrayUtils.contains(r.getVisitors(), getObjectId())) {
                return r;
            }
        }
        return null;
    }

    public boolean canEnterInstance(int instancedZoneId) {
        InstantZone iz = InstantZoneHolder.getInstance().getInstantZone(instancedZoneId);

        if (isDead()) {
            return false;
        }

        if (ReflectionManager.getInstance().size() > Config.MAX_REFLECTIONS_COUNT) {
            sendPacket(SystemMsg.THE_MAXIMUM_NUMBER_OF_INSTANCE_ZONES_HAS_BEEN_EXCEEDED);
            return false;
        }

        if (iz == null) {
            sendPacket(SystemMsg.SYSTEM_ERROR);
            return false;
        }

        if (ReflectionManager.getInstance().getCountByIzId(instancedZoneId) >= iz.getMaxChannels()) {
            sendPacket(SystemMsg.THE_MAXIMUM_NUMBER_OF_INSTANCE_ZONES_HAS_BEEN_EXCEEDED);
            return false;
        }

        return iz.getEntryType().canEnter(this, iz);
    }

    public boolean canReenterInstance(int instancedZoneId) {
        InstantZone iz = InstantZoneHolder.getInstance().getInstantZone(instancedZoneId);
        if (getActiveReflection() != null && getActiveReflection().getInstancedZoneId() != instancedZoneId) {
            sendPacket(
                    SystemMsg.YOU_HAVE_ENTERED_ANOTHER_INSTANCE_ZONE_THEREFORE_YOU_CANNOT_ENTER_CORRESPONDING_DUNGEON);
            return false;
        }
        if (iz.isDispelBuffs()) {
            dispelBuffs();
        }
        return iz.getEntryType().canReEnter(this, iz);
    }

    public int getBattlefieldChatId() {
        return _battlefieldChatId;
    }

    public void setBattlefieldChatId(int battlefieldChatId) {
        _battlefieldChatId = battlefieldChatId;
    }

    @Override
    public void broadCast(IStaticPacket... packet) {
        sendPacket(packet);
    }

    @Override
    public Iterator<Player> iterator() {
        return Collections.singleton(this).iterator();
    }

    public PlayerGroup getPlayerGroup() {
        if (getParty() != null) {
            if (getParty().getCommandChannel() != null) {
                return getParty().getCommandChannel();
            } else {
                return getParty();
            }
        } else {
            return this;
        }
    }

    public boolean isActionBlocked(String action) {
        return _blockedActions.contains(action);
    }

    public void blockActions(String... actions) {
        Collections.addAll(_blockedActions, actions);
    }

    public void unblockActions(String... actions) {
        for (String action : actions) {
            _blockedActions.remove(action);
        }
    }

    public OlympiadGame getOlympiadGame() {
        return _olympiadGame;
    }

    public void setOlympiadGame(OlympiadGame olympiadGame) {
        _olympiadGame = olympiadGame;
    }

    public OlympiadGame getOlympiadObserveGame() {
        return _olympiadObserveGame;
    }

    public void setOlympiadObserveGame(OlympiadGame olympiadObserveGame) {
        _olympiadObserveGame = olympiadObserveGame;
    }

    public void addRadar(int x, int y, int z) {
        sendPacket(new RadarControl(0, 1, x, y, z));
    }

    public void addRadarWithMap(int x, int y, int z) {
        sendPacket(new RadarControl(0, 2, x, y, z));
    }

    public PetitionMainGroup getPetitionGroup() {
        return _petitionGroup;
    }

    public void setPetitionGroup(PetitionMainGroup petitionGroup) {
        _petitionGroup = petitionGroup;
    }

    public int getLectureMark() {
        return _lectureMark;
    }

    public void setLectureMark(int lectureMark) {
        _lectureMark = lectureMark;
    }

    private int[] _recentProductList = new int[0];

    public int[] getRecentProductList() {
        if (_recentProductList.length == 0) {
            String value = getVar("recentProductList");
            if (value == null) {
                return null;
            }

            for (String product : value.split(";")) {
                int productId = Integer.parseInt(product);
                if (ProductHolder.getInstance().getProduct(productId) == null) {
                    continue;
                }

                _recentProductList = ArrayUtils.add(_recentProductList, productId);
            }
        }

        return _recentProductList;
    }

    public void updateRecentProductList(int productId) {
        if (_recentProductList == null) {
            _recentProductList = new int[1];
            _recentProductList[0] = productId;
        } else {
            int[] newProductList = new int[1];
            newProductList[0] = productId;
            for (int i = 0; i < _recentProductList.length; i++) {
                if (newProductList.length >= ProductHolder.MAX_ITEMS_IN_RECENT_LIST) {
                    break;
                }

                int itemId = _recentProductList[i];
                if (ArrayUtils.contains(newProductList, itemId)) {
                    continue;
                }

                newProductList = ArrayUtils.add(newProductList, itemId);
            }

            _recentProductList = newProductList;
        }

        String valueToUpdate = "";
        for (int itemId : this._recentProductList) {
            valueToUpdate = valueToUpdate + itemId + ";";
        }

        setVar("recentProductList", valueToUpdate, -1L);
    }

    public int getUsedSummonPoints() {
        int points = 0;
        for (Summon summon : getPets()) {
            if (summon.getPointsToSummon() > 0) {
                points += summon.getPointsToSummon();
            }
        }
        return points;
    }

    public int getMaxSummonPoints() {
        return (int) calcStat(Stats.SUMMON_POINTS, 0.0D, null, null);
    }

    public boolean canSummon(int _npcId) {
        int consumePoints = SummonPointsHolder.getInstance().getPointsForSummonId(_npcId);

        if (getActiveClassId() == 145 || getActiveClassId() == 176 || getActiveClassId() == 177
                || getActiveClassId() == 178) {
            if (_summons.size() > 3) {
                return false;
            }
        } else {
            if (_summons.size() > 0) {
                return false;
            }
        }

        if (consumePoints > 0) {
            if (getMaxSummonPoints() - getUsedSummonPoints() < consumePoints) {
                return false;
            }
        }
        return true;
    }

    public boolean isAwaking() {
        return getActiveClassId() > 138;
    }

    public boolean getTree() {
        return _tree;
    }

    public void setTree(boolean tree) {
        _tree = tree;
    }

    public JumpTrack getCurrentJumpTrack() {
        return _currentJumpTrack;
    }

    public void setCurrentJumpTrack(JumpTrack val) {
        _currentJumpTrack = val;
    }

    public JumpWay getCurrentJumpWay() {
        return _currentJumpWay;
    }

    public void setCurrentJumpWay(JumpWay val) {
        _currentJumpWay = val;
    }

    public boolean isInJumping() {
        return _currentJumpTrack != null;
    }

    public void onJumpingBreak() {
        sendActionFailed();
        unsetVar("@safe_jump_loc");
        setCurrentJumpTrack(null);
        setCurrentJumpWay(null);
    }

    public boolean checkAllowAction() {
        if (getPrivateStoreType() != Player.STORE_PRIVATE_NONE) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isFishing()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isInCombat()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isCursedWeaponEquipped()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isInOlympiadMode()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isOnSiegeField()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isInBoat() || getMountNpcId() != 0) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isTeleporting()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (getTransformation() != 0) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (isDead()) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        if (getTeam() != TeamType.NONE) {
            sendMessage(
                    "?  ?   ???.");
            return false;
        }
        return true;
    }

    public Location getStablePoint() {
        return _stablePoint;
    }

    public void setStablePoint(Location point) {
        _stablePoint = point;
    }

    public void sendSkillList() {
        sendPacket(new SkillList(this));
        sendPacket(new ExAcquirableSkillListByClass(this));
    }

    public SubClassList getSubClassList() {
        return _subClassList;
    }

    public SubClass getBaseSubClass() {
        return _subClassList.getBaseSubClass();
    }

    public int getBaseClassId() {
        if (getBaseSubClass() != null) {
            return getBaseSubClass().getClassId();
        }

        return -1;
    }

    public SubClass getActiveSubClass() {
        return _subClassList.getActiveSubClass();
    }

    public int getActiveClassId() {
        if (getActiveSubClass() != null) {
            return getActiveSubClass().getClassId();
        }

        return -1;
    }

    public boolean isBaseClassActive() {
        return getActiveSubClass().isBase();
    }

    public ClassId getClassId() {
        return ClassId.VALUES[getActiveClassId()];
    }

    public int getMaxLevel() {
        if (getActiveSubClass() != null) {
            return getActiveSubClass().getMaxLevel();
        }

        return Experience.getMaxLevel();
    }

    public int getClassLevel() {
        return getClassId().getClassLevel().ordinal(); // TODO 
        //   
        // ? ?.
    }

    public void changeClass(final int index) {
        SystemMsg msg = checkChangeClassCondition();
        if (msg != null) {
            sendPacket(msg);
            return;
        }

        SubClass sub = _subClassList.getByIndex(index);
        if (sub == null) {
            return;
        }

        int classId = sub.getClassId();
        int oldClassId = getActiveClassId();
        setActiveSubClass(classId, true);
        Skill skill = SkillTable.getInstance().getInfo(1570, 1);
        skill.getEffects(this, this, false, false);
        sendPacket(new SystemMessage(SystemMessage.THE_TRANSFER_OF_SUB_CLASS_HAS_BEEN_COMPLETED)
                .addClassName(oldClassId).addClassName(classId));
    }

    private SystemMsg checkChangeClassCondition() {
        if (getWeightPenalty() >= 3 || getInventoryLimit() * 0.8 < getInventory().getSize()) {
            return SystemMsg.A_SUBCLASS_CANNOT_BE_CREATED_OR_CHANGED_BECAUSE_YOU_HAVE_EXCEEDED_YOUR_INVENTORY_LIMIT;
        }

        if (isInOlympiadMode()) // TODO: [Darvin]   ?.
        {
            return SystemMsg.THIS_TERRITORY_CAN_NOT_CHANGE_CLASS;
        }

        if (getTransformation() != 0) {
            return SystemMsg.YOU_CAN_NOT_CHANGE_CLASS_IN_TRANSFORMATION;
        }

        return null;
    }

    @Override
    public int getINT() {
        return Math.max(getTemplate().getMinAttr().getINT(),
                Math.min(getTemplate().getMaxAttr().getINT(), super.getINT()));
    }

    @Override
    public int getSTR() {
        return Math.max(getTemplate().getMinAttr().getSTR(),
                Math.min(getTemplate().getMaxAttr().getSTR(), super.getSTR()));
    }

    @Override
    public int getCON() {
        return Math.max(getTemplate().getMinAttr().getCON(),
                Math.min(getTemplate().getMaxAttr().getCON(), super.getCON()));
    }

    @Override
    public int getMEN() {
        return Math.max(getTemplate().getMinAttr().getMEN(),
                Math.min(getTemplate().getMaxAttr().getMEN(), super.getMEN()));
    }

    @Override
    public int getDEX() {
        return Math.max(getTemplate().getMinAttr().getDEX(),
                Math.min(getTemplate().getMaxAttr().getDEX(), super.getDEX()));
    }

    @Override
    public int getWIT() {
        return Math.max(getTemplate().getMinAttr().getWIT(),
                Math.min(getTemplate().getMaxAttr().getWIT(), super.getWIT()));
    }

    @Override
    public int getMaxCp() {
        return (int) calcStat(Stats.MAX_CP, getClassId().getClassData().getLvlUpData(getLevel()).getCP(), null,
                null);
    }

    @Override
    public int getMaxHp() {
        return (int) calcStat(Stats.MAX_HP, getClassId().getClassData().getLvlUpData(getLevel()).getHP(), null,
                null);
    }

    @Override
    public int getMaxMp() {
        return (int) calcStat(Stats.MAX_MP, getClassId().getClassData().getLvlUpData(getLevel()).getMP(), null,
                null);
    }

    @Override
    public int getRandomDamage() {
        WeaponTemplate weaponItem = getActiveWeaponItem();
        if (weaponItem == null) {
            return getTemplate().getBaseRandDam();
        }
        return weaponItem.getRandomDamage();
    }

    @Override
    public double getHpRegen() {
        return calcStat(Stats.REGENERATE_HP_RATE, getTemplate().getBaseHpReg(getLevel()));
    }

    @Override
    public double getMpRegen() {
        return calcStat(Stats.REGENERATE_MP_RATE, getTemplate().getBaseMpReg(getLevel()));
    }

    @Override
    public double getCpRegen() {
        return calcStat(Stats.REGENERATE_CP_RATE, getTemplate().getBaseCpReg(getLevel()));
    }

    public boolean useItem(ItemInstance item, boolean ctrlPressed) {
        boolean success = item.getTemplate().getHandler().useItem(this, item, ctrlPressed);
        if (success) {
            long nextTimeUse = item.getTemplate().getReuseType().next(item);
            if (nextTimeUse > System.currentTimeMillis()) {
                TimeStamp timeStamp = new TimeStamp(item.getItemId(), nextTimeUse,
                        item.getTemplate().getReuseDelay());
                this.addSharedGroupReuse(item.getTemplate().getReuseGroup(), timeStamp);

                if (item.getTemplate().getReuseDelay() > 0) {
                    this.sendPacket(new ExUseSharedGroupItem(item.getTemplate().getDisplayReuseGroup(), timeStamp));
                }
            }
        }
        return success;
    }

    public void clearTargetSignList() {
        if (!_signedCharacters.isEmpty()) {
            for (Creature cha : _signedCharacters.values(new Creature[0])) {
                sendPacket(new ExTacticalSign(cha, 0));
            }
        }
    }

    public void makeSign(Creature target, int sign) {
        if ((target == this)
                || (((target instanceof Summon)) && (!getPets().isEmpty()) && (getPets().contains(target)))) {
            return;
        }

        Party party = getParty();

        if (party == null) {
            synchronized (_signedCharacters) {
                if (_signedCharacters.containsKey(sign)) {
                    boolean removePreviosEqualSign = false;
                    TIntObjectIterator<Creature> iter = _signedCharacters.iterator();

                    while (iter.hasNext()) {
                        iter.advance();

                        if (iter.key() != sign) {
                            continue;
                        }

                        if (target == iter.value()) {
                            break;
                        }

                        removePreviosEqualSign = true;
                    }

                    if (removePreviosEqualSign) {
                        Creature prevTarget = _signedCharacters.get(sign);
                        sendPacket(new ExTacticalSign(prevTarget, 0));
                        _signedCharacters.remove(sign);
                    }
                }

                if (_signedCharacters.containsValue(target)) {
                    int currentSignFortarget = -1;
                    TIntObjectIterator<Creature> iter = _signedCharacters.iterator();

                    while (iter.hasNext()) {
                        iter.advance();

                        if (iter.value() != target) {
                            continue;
                        }

                        currentSignFortarget = iter.key();
                    }

                    if (currentSignFortarget == sign) {
                        _signedCharacters.remove(currentSignFortarget);
                        sendPacket(new ExTacticalSign(target, 0));
                    } else {
                        _signedCharacters.remove(currentSignFortarget);
                        _signedCharacters.put(sign, target);
                        sendPacket(new ExTacticalSign(target, sign));
                    }

                } else {
                    _signedCharacters.put(sign, target);
                    TIntObjectIterator<Creature> iter = _signedCharacters.iterator();

                    while (iter.hasNext()) {
                        iter.advance();
                        sendPacket(new ExTacticalSign(iter.value(), iter.key()));
                    }
                }
            }
        } else {
            party.addSign(sign, target);
        }
    }

    public void targetSign(int sign) {
        Creature target = null;
        if (getParty() == null) {
            TIntObjectIterator<Creature> iter = _signedCharacters.iterator();

            while (iter.hasNext()) {
                iter.advance();

                if (iter.key() != sign) {
                    continue;
                }

                target = iter.value();
            }

        } else {
            TIntObjectIterator<Creature> iter = getParty().getTargetSignList().iterator();

            while (iter.hasNext()) {
                iter.advance();

                if (iter.key() != sign) {
                    continue;
                }

                target = iter.value();
            }

        }

        if (target == null) {
            sendActionFailed();
        }

        else if (target.getObjectId() == this.getObjectId()) {
            sendActionFailed();
        }

        else if (isInObserverMode()) {
            sendPacket(new SystemMessage(SystemMessage.OBSERVERS_CANNOT_PARTICIPATE));
            sendActionFailed();
        } else {
            setTarget(target);
        }
    }

    public long getStartingTimeInFullParty() {
        return _startingTimeInFullParty;
    }

    public void setStartingTimeInFullParty(long time) {
        _startingTimeInFullParty = time;
    }

    public long getStartingTimeInParty() {
        return _startingTimeInParty;
    }

    public void setStartingTimeInParty(long time) {
        _startingTimeInParty = time;
    }

    public VitalitySystem getVitality() {
        return _vitality;
    }

    public MentoringSystem getMentorSystem() {
        return mentorSystem;
    }

    public void mentoringLoginConditions() {
        if (getMentorSystem().whoIsOnline(true)) {
            getMentorSystem().notify(true);
            MentorUtil.applyMentoringConditions(this);
        }
    }

    public void mentoringLogoutConditions() {
        if (getMentorSystem().whoIsOnline(false)) {
            getMentorSystem().notify(false);
            MentorUtil.removeConditions(this);
        }
    }

    public boolean isMentee() {
        return !isMentor() && (this.getWarehouse().getItemByObjectId(33800) != null
                || this.getInventory().getItemByItemId(33800) != null);
    }

    public boolean isGraduateMentoring() {
        return Boolean.parseBoolean(getVar("graduateMentoring"));
    }

    public boolean isMentor() {
        return this.isAwaking() && this.getLevel() >= 85;
    }

    public void addCommonRecipe(Recipe recipe) {
        _commonrecipebook.put(recipe.getId(), recipe);
    }

    public void addDwarfRecipe(Recipe recipe) {
        _recipebook.put(recipe.getId(), recipe);
    }

    public void removeCommonRecipe(int id) {
        _commonrecipebook.remove(id);
    }

    public void removeDwarvenRecipe(int id) {
        _recipebook.remove(id);
    }

    private class UpdateEffectIcons extends RunnableImpl {
        @Override
        public void runImpl() throws Exception {
            updateEffectIconsImpl();
            _updateEffectIconsTask = null;
        }
    }

    public class BroadcastCharInfoTask extends RunnableImpl {
        @Override
        public void runImpl() throws Exception {
            broadcastCharInfoImpl();
            _broadcastCharInfoTask = null;
        }
    }

    private class UserInfoTask extends RunnableImpl {
        @Override
        public void runImpl() throws Exception {
            sendUserInfoImpl();
            _userInfoTask = null;
        }
    }

    private boolean is_bbs_use = false;

    public void setIsBBSUse(boolean value) {
        is_bbs_use = value;
    }

    public void Avaliable(boolean value) {
        isAval = value;
    }

    public boolean isBBSUse() {
        return is_bbs_use;
    }

    public boolean isAval = false;

    public void setIsInChaosBattle(boolean value) {
        chaosBattle = value;
    }

    public boolean isInChaosBattle() {
        return chaosBattle;
    }

    public static void getAutoLicenseCheck() {
        File f = new File("../server/License.ini");
        if (!f.exists()) {
            _log.info("Fail to load licence");
            System.exit(1);
        }
        ExProperties licenseSet = load(f);
        String pass = new String("00000");
        //LICENSE = licenseSet.getProperty("Serial", pass);
        //if(LICENSE != pass)
        //{
        //   _log.info("Fail to load licence code");
        //   System.exit(1);
        //}TODO
    }

    public static ExProperties load(File file) {
        ExProperties result = new ExProperties();

        try {
            result.load(file);
        } catch (IOException e) {
            _log.error("Error loading config : " + file.getName() + "!");
        }

        return result;
    }

    public boolean isAvaliable() {
        return isAval;
    }

}