Package l2p.gameserver.model

Source Code of l2p.gameserver.model.L2Player

package l2p.gameserver.model;

import javolution.text.TextBuilder;
import javolution.util.FastList;
import javolution.util.FastMap;
import l2p.Config;
import l2p.common.ThreadPoolManager;
import l2p.database.DatabaseUtils;
import l2p.database.FiltredPreparedStatement;
import l2p.database.FiltredStatement;
import l2p.database.L2DatabaseFactory;
import l2p.database.ThreadConnection;
import l2p.database.mysql;
import l2p.extensions.Bonus;
import l2p.extensions.Stat;
import l2p.extensions.multilang.CustomMessage;
import l2p.extensions.network.SendablePacket;
import l2p.extensions.scripts.Events;
import l2p.extensions.scripts.Functions;
import l2p.extensions.scripts.ScriptManager;
import l2p.extensions.scripts.ScriptManager.ScriptClassAndMethod;
import l2p.gameserver.GameTimeController;
import l2p.gameserver.RecipeController;
import l2p.gameserver.ai.CtrlEvent;
import l2p.gameserver.ai.CtrlIntention;
import l2p.gameserver.ai.DefaultAI;
import l2p.gameserver.ai.L2CharacterAI;
import l2p.gameserver.ai.L2PlayableAI;
import l2p.gameserver.ai.L2PlayableAI.nextAction;
import l2p.gameserver.ai.L2PlayerAI;
import l2p.gameserver.cache.Msg;
import l2p.gameserver.clientpackets.EnterWorld;
import l2p.gameserver.handler.IItemHandler;
import l2p.gameserver.handler.ItemHandler;
import l2p.gameserver.idfactory.IdFactory;
import l2p.gameserver.instancemanager.CastleManager;
import l2p.gameserver.instancemanager.CastleSiegeManager;
import l2p.gameserver.instancemanager.ClanHallManager;
import l2p.gameserver.instancemanager.CoupleManager;
import l2p.gameserver.instancemanager.CursedWeaponsManager;
import l2p.gameserver.instancemanager.DimensionalRiftManager;
import l2p.gameserver.instancemanager.FortressManager;
import l2p.gameserver.instancemanager.FortressSiegeManager;
import l2p.gameserver.instancemanager.PartyRoomManager;
import l2p.gameserver.instancemanager.PlayerManager;
import l2p.gameserver.instancemanager.QuestManager;
import l2p.gameserver.instancemanager.SiegeManager;
import l2p.gameserver.instancemanager.ZoneManager;
import l2p.gameserver.loginservercon.LSConnection;
import l2p.gameserver.loginservercon.gspackets.ChangeAccessLevel;
import l2p.gameserver.model.L2Clan.RankPrivs;
import l2p.gameserver.model.L2Multisell.MultiSellListContainer;
import l2p.gameserver.model.L2ObjectTasks.BonusTask;
import l2p.gameserver.model.L2ObjectTasks.BroadcastCharInfoTask;
import l2p.gameserver.model.L2ObjectTasks.EndSitDownTask;
import l2p.gameserver.model.L2ObjectTasks.EndStandUpTask;
import l2p.gameserver.model.L2ObjectTasks.InventoryEnableTask;
import l2p.gameserver.model.L2ObjectTasks.KickTask;
import l2p.gameserver.model.L2ObjectTasks.LookingForFishTask;
import l2p.gameserver.model.L2ObjectTasks.PvPFlagTask;
import l2p.gameserver.model.L2ObjectTasks.ReturnTerritoryFlagTask;
import l2p.gameserver.model.L2ObjectTasks.TeleportTask;
import l2p.gameserver.model.L2ObjectTasks.UserInfoTask;
import l2p.gameserver.model.L2ObjectTasks.WaterTask;
import l2p.gameserver.model.L2Skill.AddedSkill;
import l2p.gameserver.model.L2Skill.SkillType;
import l2p.gameserver.model.L2Zone.ZoneType;
import l2p.gameserver.model.base.ClassId;
import l2p.gameserver.model.base.Experience;
import l2p.gameserver.model.base.PlayerAccess;
import l2p.gameserver.model.base.Race;
import l2p.gameserver.model.base.Transaction;
import l2p.gameserver.model.base.Transaction.TransactionType;
import l2p.gameserver.model.entity.DimensionalRift;
import l2p.gameserver.model.entity.Duel;
import l2p.gameserver.model.entity.Duel.DuelState;
import l2p.gameserver.model.entity.Hero;
import l2p.gameserver.model.entity.SevenSignsFestival.DarknessFestival;
import l2p.gameserver.model.entity.olympiad.CompType;
import l2p.gameserver.model.entity.olympiad.Olympiad;
import l2p.gameserver.model.entity.olympiad.OlympiadGame;
import l2p.gameserver.model.entity.residence.Castle;
import l2p.gameserver.model.entity.residence.ClanHall;
import l2p.gameserver.model.entity.residence.Fortress;
import l2p.gameserver.model.entity.residence.Residence;
import l2p.gameserver.model.entity.residence.ResidenceType;
import l2p.gameserver.model.entity.siege.Siege;
import l2p.gameserver.model.entity.siege.territory.TerritorySiege;
import l2p.gameserver.model.entity.vehicle.L2AirShip;
import l2p.gameserver.model.entity.vehicle.L2Ship;
import l2p.gameserver.model.entity.vehicle.L2Vehicle;
import l2p.gameserver.model.instances.L2AgathionInstance;
import l2p.gameserver.model.instances.L2ClanHallManagerInstance;
import l2p.gameserver.model.instances.L2CubicInstance;
import l2p.gameserver.model.instances.L2CubicInstance.CubicType;
import l2p.gameserver.model.instances.L2DecoyInstance;
import l2p.gameserver.model.instances.L2DoorInstance;
import l2p.gameserver.model.instances.L2FestivalMonsterInstance;
import l2p.gameserver.model.instances.L2GuardInstance;
import l2p.gameserver.model.instances.L2HennaInstance;
import l2p.gameserver.model.instances.L2MinionInstance;
import l2p.gameserver.model.instances.L2MonsterInstance;
import l2p.gameserver.model.instances.L2NpcInstance;
import l2p.gameserver.model.instances.L2PetInstance;
import l2p.gameserver.model.instances.L2ReflectionBossInstance;
import l2p.gameserver.model.instances.L2StaticObjectInstance;
import l2p.gameserver.model.instances.L2TamedBeastInstance;
import l2p.gameserver.model.instances.L2TerritoryFlagInstance;
import l2p.gameserver.model.instances.L2TrapInstance;
import l2p.gameserver.model.items.Inventory;
import l2p.gameserver.model.items.L2ItemInstance;
import l2p.gameserver.model.items.PcFreight;
import l2p.gameserver.model.items.PcInventory;
import l2p.gameserver.model.items.PcWarehouse;
import l2p.gameserver.model.items.Warehouse;
import l2p.gameserver.model.items.Warehouse.WarehouseType;
import l2p.gameserver.model.quest.Quest;
import l2p.gameserver.model.quest.QuestEventType;
import l2p.gameserver.model.quest.QuestState;
import l2p.gameserver.modules.FloodProtector;
import l2p.gameserver.modules.community.buffer.Buff;
import l2p.gameserver.modules.community.buffer.OneScheme;
import l2p.gameserver.network.L2GameClient;
import l2p.gameserver.serverpackets.*;
import l2p.gameserver.skills.EffectType;
import l2p.gameserver.skills.Env;
import l2p.gameserver.skills.SkillTimeStamp;
import l2p.gameserver.skills.Stats;
import l2p.gameserver.skills.effects.EffectTemplate;
import l2p.gameserver.skills.skillclasses.Charge;
import l2p.gameserver.skills.skillclasses.Transformation;
import l2p.gameserver.tables.CharTemplateTable;
import l2p.gameserver.tables.ClanTable;
import l2p.gameserver.tables.HennaTable;
import l2p.gameserver.tables.ItemTable;
import l2p.gameserver.tables.MapRegion;
import l2p.gameserver.tables.NpcTable;
import l2p.gameserver.tables.PetDataTable;
import l2p.gameserver.tables.ReflectionTable;
import l2p.gameserver.tables.SkillTable;
import l2p.gameserver.tables.SkillTreeTable;
import l2p.gameserver.taskmanager.AutoSaveManager;
import l2p.gameserver.taskmanager.BreakWarnManager;
import l2p.gameserver.taskmanager.VitalityManager;
import l2p.gameserver.templates.L2Armor;
import l2p.gameserver.templates.L2Armor.ArmorType;
import l2p.gameserver.templates.L2Henna;
import l2p.gameserver.templates.L2Item;
import l2p.gameserver.templates.L2PlayerTemplate;
import l2p.gameserver.templates.L2Weapon;
import l2p.gameserver.templates.L2Weapon.WeaponType;
import l2p.util.EffectsComparator;
import l2p.util.GArray;
import l2p.util.GCSArray;
import l2p.util.Location;
import l2p.util.Log;
import l2p.util.Rnd;
import l2p.util.SqlBatch;
import l2p.util.Strings;
import l2p.util.Util;

import java.awt.Color;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.Arrays;
import java.util.Calendar;
import java.util.Collection;
import java.util.ConcurrentModificationException;
import java.util.HashMap;
import java.util.Map;
import java.util.Map.Entry;
import java.util.TreeMap;
import java.util.concurrent.ConcurrentLinkedQueue;
import java.util.concurrent.ConcurrentSkipListSet;
import java.util.concurrent.Future;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.Logger;

import static l2p.gameserver.model.L2Zone.ZoneType.Siege;
import static l2p.gameserver.model.L2Zone.ZoneType.damage;
import static l2p.gameserver.model.L2Zone.ZoneType.instant_skill;
import static l2p.gameserver.model.L2Zone.ZoneType.no_landing;
import static l2p.gameserver.model.L2Zone.ZoneType.no_restart;
import static l2p.gameserver.model.L2Zone.ZoneType.peace_zone;
import static l2p.gameserver.model.L2Zone.ZoneType.poison;
import static l2p.gameserver.model.L2Zone.ZoneType.ssq_zone;
import static l2p.gameserver.model.L2Zone.ZoneType.swamp;

public final class L2Player extends L2Playable
{
  static final Logger _log = Logger.getLogger(L2Player.class.getName());
  public HashMap<Integer, L2SubClass> _classlist = new HashMap<Integer, L2SubClass>(4);
  public static final short STORE_PRIVATE_NONE = 0;
  public static final short STORE_PRIVATE_SELL = 1;
  public static final short STORE_PRIVATE_BUY = 3;
  public static final short STORE_PRIVATE_MANUFACTURE = 5;
  public static final short STORE_OBSERVING_GAMES = 7;
  public static final short 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;
  /**
   * The table containing all minimum level needed for each Expertise (None, D, C, B, A, S, S80, S84)
   */
  public static final int[] EXPERTISE_LEVELS =
  {
    //
    0, //NONE
    20, //D
    40, //C
    52, //B
    61, //A
    76, //S
    80, //S80
    84, //S84
    Integer.MAX_VALUE, // затычка
  };
  private int expertiseIndex;
  private ClassId _skillLearningClassId;
  private L2GameClient _connection;
  private String _accountName;
  private int _karma, _pkKills, _pvpKills;
  private int _face, _hairStyle, _hairColor;
  private int _recomHave, _recomLeft, _fame;
  private int _deleteTimer;
  private int _partyMatchingLevels, _partyMatchingRegion;
  private Integer _partyRoom = 0;
  private long _createTime, _onlineTime, _onlineBeginTime, _leaveClanTime, _deleteClanTime, _NoChannel,
  _NoChannelBegin;
  /**
   * The Color of players name / title (white is 0xFFFFFF)
   */
  private int _nameColor, _titlecolor;
  private int _vitalityLevel = -1;
  private double _vitality = 10000;
  private int _curWeightPenalty = 0;
  private boolean _relax;
  boolean sittingTaskLaunched;
  /**
   * Time counter when L2Player is sitting
   */
  private int _waitTimeWhenSit;
  private boolean AutoLoot = Config.AUTO_LOOT, AutoLootHerbs = Config.AUTO_LOOT_HERBS;
  private final GArray<Integer> _recomChars = new GArray<Integer>();
  private final PcInventory _inventory = new PcInventory(this);
  private PcWarehouse _warehouse = new PcWarehouse(this);
  private PcFreight _freight = new PcFreight(this);
  public final BookMarkList bookmarks = new BookMarkList(this, 0);
  /**
   * The table containing all L2RecipeList of the L2Player
   */
  private final Map<Integer, L2Recipe> _recipebook = new TreeMap<Integer, L2Recipe>();
  private final Map<Integer, L2Recipe> _commonrecipebook = new TreeMap<Integer, L2Recipe>();
  /**
   * The table containing all Quests began by the L2Player
   */
  private final HashMap<String, QuestState> _quests = new HashMap<String, QuestState>();
  /**
   * The list containing all shortCuts of this L2Player
   */
  private final ShortCuts _shortCuts = new ShortCuts(this);
  /**
   * The list containing all macroses of this L2Player
   */
  private final MacroList _macroses = new MacroList(this);
  private final StatsChangeRecorder _statsChangeRecorder = new StatsChangeRecorder(this);
  public L2Radar radar;
  private L2TradeList _tradeList;
  private L2ManufactureList _createList;
  private ConcurrentLinkedQueue<TradeItem> _sellList, _buyList;
  // hennas
  private final L2HennaInstance[] _henna = new L2HennaInstance[3];
  private short _hennaSTR, _hennaINT, _hennaDEX, _hennaMEN, _hennaWIT, _hennaCON;
  private L2Party _party;
  private L2Clan _clan;
  private int _pledgeClass = 0, _pledgeType = 0, _powerGrade = 0, _lvlJoinedAcademy = 0, _apprentice = 0;
  //GM Stuff
  private int _accessLevel;
  private PlayerAccess _playerAccess = new PlayerAccess();
  private boolean _messageRefusal = false, _tradeRefusal = false, _exchangeRefusal = false, _invisible = false,
  _blockAll = false;
  /**
   * 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 short _privatestore;
  /**
   * The L2Summon of the L2Player
   */
  private L2Summon _summon = null;
  private L2DecoyInstance _decoy = null;
  private GCSArray<L2CubicInstance> _cubics = null;
  private L2AgathionInstance _agathion = null;
  private Transaction _transaction;
  private L2ItemInstance _arrowItem;
  /**
   * The fists L2Weapon of the L2Player (used when no weapon is equipped)
   */
  private L2Weapon _fistsWeaponItem;
  private long _uptime;
  private HashMap<Integer, String> _chars = new HashMap<Integer, String>(8);
  public byte updateKnownCounter = 0;
  /**
   * The current higher Expertise of the L2Player (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7)
   */
  private int weaponPenalty = 0, armorPenalty = 0;
  private L2ItemInstance _enchantScroll = null;
  private WarehouseType _usingWHType;
  private boolean _isOnline = false;
  private boolean _isDeleting = false;
  protected boolean _inventoryDisable = false;
  /**
   * The L2NpcInstance corresponding to the last Folk which one the player talked.
   */
  private L2NpcInstance _lastNpc = null;
  /**
   * тут храним мультиселл с которым работаем, полезно...
   */
  private MultiSellListContainer _multisell = null;
  protected ConcurrentSkipListSet<Integer> _activeSoulShots = new ConcurrentSkipListSet<Integer>();
  /**
   * Location before entering Observer Mode
   */
  private Location _obsLoc = new Location();
  private L2WorldRegion _observNeighbor;
  private byte _observerMode = 0;
  public int _telemode = 0;
  /**
   * Эта точка проверяется при нештатном выходе чара, и если не равна null чар возвращается в нее
   * Используется например для возвращения при падении с виверны
   * Поле heading используется для хранения денег возвращаемых при сбое
   */
  public Location _stablePoint = null;
  /**
   * new loto ticket *
   */
  public int _loto[] = new int[5];
  /**
   * new race ticket *
   */
  public int _race[] = new int[2];
  private final FastMap<Integer, String> _blockList = new FastMap<Integer, String>().setShared(true); // characters blocked with '/block <charname>' cmd
  private boolean _isConnected = true;
  private boolean _hero = false;
  private int _team = 0;
  private boolean _checksForTeam = false;
  // time on login in game
  private long _lastAccess;
  /**
   * True if the L2Player is in a boat
   */
  private L2Vehicle _vehicle;
  private Location _inVehiclePosition;
  protected int _baseClass = -1;
  protected L2SubClass _activeClass = null;
  private Bonus _bonus;
  private Future<?> _bonusExpiration;
  public boolean _isSitting = false;
  private boolean _noble = false;
  private boolean _inOlympiadMode = false;
  private int _olympiadGameId = -1;
  private int _olympiadSide = -1;
  private int _olympiadObserveId = -1;
  /**
   * ally with ketra or varka related wars
   */
  private int _varka = 0;
  private int _ketra = 0;
  private int _ram = 0;
  /**
   * The Siege state
   */
  private int _siegeState = 0;
  private byte[] _keyBindings;
  public ScheduledFuture<?> _taskWater;
  protected HashMap<Integer, Long> _StatKills;
  protected HashMap<Integer, Long> _StatDrop;
  protected HashMap<Integer, Long> _StatCraft;
  private int _cursedWeaponEquippedId = 0;
  private L2Fishing _fishCombat;
  private boolean _fishing = false;
  private Location _fishLoc = new Location();
  private L2ItemInstance _lure = null;
  public ScheduledFuture<?> _taskforfish;
  private Future<?> _kickTask;
  private boolean _isInCombatZone;
  private boolean _isOnSiegeField;
  private boolean _isInPeaceZone;
  private boolean _isInSSZone;
  private boolean _offline = false;
  /**
   * Трансформация
   */
  private int _transformationId;
  private int _transformationTemplate;
  private String _transformationName;
  private int pcBangPoints;
  /**
   * Коллекция для временного хранения скилов данной трансформации
   */
  public HashMap<Integer, L2Skill> _transformationSkills = new HashMap<Integer, L2Skill>();
  private int _expandInventory = 0;
  private int _expandWarehouse = 0;
  private boolean _notShowBuffAnim = false;
  private GArray<String> bypasses = null, bypasses_bbs = null;
  private static final String NOT_CONNECTED = "<not connected>";

  /**
   * Конструктор для L2Player. Напрямую не вызывается, для создания игрока используется PlayerManager.create
   */
  public L2Player(final int objectId, final L2PlayerTemplate template, final String accountName)
  {
    super(objectId, template);
    _accountName = accountName;
    _nameColor = 0xFFFFFF;
    _titlecolor = 0xFFFF77;
    _baseClass = getClassId().getId();
  }

  /**
   * 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 L2Player(final int objectId, final L2PlayerTemplate template)
  {
    this(objectId, template, null);
    getInventory().restore();
    // Create an AI
    setAI(new L2PlayerAI(this));
    // Create a L2Radar object
    radar = new L2Radar(this);
    if(!Config.EVERYBODY_HAS_ADMIN_RIGHTS)
    {
      setPlayerAccess(Config.gmlist.get(objectId));
    }
    else
    {
      setPlayerAccess(Config.gmlist.get(new Integer(0)));
    }
    // Retrieve from the database all macroses of this L2Player and add them to _macroses
    _macroses.restore();
  }

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

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

  /**
   * Возвращает список персонажей на аккаунте, за исключением текущего
   *
   * @return Список персонажей
   */
  public HashMap<Integer, String> getAccountChars()
  {
    return _chars;
  }

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

  @Override
  public L2PlayerTemplate getBaseTemplate()
  {
    return (L2PlayerTemplate) _baseTemplate;
  }

  public void changeSex()
  {
    boolean male = true;
    if(getSex() == 1)
    {
      male = false;
    }
    _template = CharTemplateTable.getInstance().getTemplate(getClassId(), !male);
  }

  @Override
  public L2PlayableAI getAI()
  {
    if(_ai == null)
    {
      _ai = new L2PlayerAI(this);
    }
    return (L2PlayableAI) _ai;
  }

  @Override
  public void doAttack(final L2Character target)
  {
    super.doAttack(target);
    if(_cubics != null)
    {
      for(L2CubicInstance cubic : _cubics)
      {
        if(cubic.getType() != CubicType.LIFE_CUBIC)
        {
          cubic.doAction(target);
        }
      }
    }
    if(_agathion != null)
    {
      _agathion.doAction(target);
    }
  }

  @Override
  public void doCast(final L2Skill skill, final L2Character 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));
    }
    if(skill.isOffensive() && target != null)
    {
      if(_cubics != null)
      {
        for(L2CubicInstance cubic : _cubics)
        {
          if(cubic.getType() != CubicType.LIFE_CUBIC)
          {
            cubic.doAction(target);
          }
        }
      }
      if(_agathion != null)
      {
        _agathion.doAction(target);
      }
    }
  }

  public void refreshSavedStats()
  {
    _statsChangeRecorder.refreshSaves();
  }

  @Override
  public void sendChanges()
  {
    _statsChangeRecorder.sendChanges();
  }

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

  public final boolean setLevel(final int lvl)
  {
    if(_activeClass != null)
    {
      _activeClass.setLevel((byte) lvl);
    }
    return lvl == getLevel();
  }

  public byte getSex()
  {
    return getTemplate().isMale ? (byte) 0 : (byte) 1;
  }

  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 boolean isInStoreMode()
  {
    return _privatestore != STORE_PRIVATE_NONE && _privatestore != STORE_OBSERVING_GAMES;
  }

  public void offline()
  {
    setNameColor(Config.SERVICES_OFFLINE_TRADE_NAME_COLOR);
    setOfflineMode(true);
    clearHateList(false);
    setVar("offline", String.valueOf(System.currentTimeMillis() / 1000));
    if(Config.SERVICES_OFFLINE_TRADE_SECONDS_TO_KICK > 0)
    {
      startKickTask(Config.SERVICES_OFFLINE_TRADE_SECONDS_TO_KICK * 1000L);
    }
    if(isFestivalParticipant())
    {
      L2Party playerParty = getParty();
      if(playerParty != null)
      {
        playerParty.broadcastMessageToPartyMembers(getName() + " has been removed from the upcoming festival.");
      }
    }
    if(getParty() != null)
    {
      getParty().oustPartyMember(this);
    }
    if(getPet() != null && getPet().getNpcId() != PetDataTable.IMPROVED_BABY_KOOKABURRA_ID && getPet().getNpcId() != PetDataTable.IMPROVED_BABY_COUGAR_ID)
    {
      getPet().unSummon();
    }
    CursedWeaponsManager.getInstance().doLogout(this);
    if(isInOlympiadMode() || getOlympiadGameId() > -1)
    {
      Olympiad.logoutPlayer(this);
    }
    sendPacket(Msg.LeaveWorld);
    setConnected(false);
    setOnlineStatus(false);
    //LSConnection.getInstance().removeAccount(getNetConnection());
    //LSConnection.getInstance().sendPacket(new PlayerLogout(getNetConnection().getLoginName()));
    broadcastUserInfo(true);
    store(false);
    _connection.OnOfflineTrade();
    //TODO освобождать кучу других объектов связанных с игроком не нужных в оффлайне
  }

  /**
   * Сохраняет персонажа в бд и запускает необходимые процедуры.
   *
   * @param shutdown тру при шатдауне
   * @param restart  тру при рестарте. Игнорируется шатдаун.
   * @param kicked   Отобразить у клиента табличку с мессагой о закрытии коннекта, отобрать проклятое оружие.
   * @param instant  Выкидывает моментально, не оставляя чара в игре.
   */
  public void logout(boolean shutdown, boolean restart, boolean kicked, boolean instant)
  {
    if(isLogoutStarted())
    {
      return;
    }
    Log.LogChar(this, Log.Logout, "");
    // Msg.ExRestartClient - 2 таблички появляется (вторая GG Fail), нажатие ок приводит к закрытию клиента
    // Msg.ServerClose - табличка появляется, после нажатия ок переходит к диалогу ввода логина/пароля
    // Msg.LeaveWorld - молча закрывает клиент (используется при выходе из игры)
    if(kicked && Config.ALLOW_CURSED_WEAPONS && Config.DROP_CURSED_WEAPONS_ON_KICK)
    {
      if(isCursedWeaponEquipped())
      {
        _pvpFlag = 0;
        CursedWeaponsManager.getInstance().dropPlayer(this);
      }
    }

        if (restart) {
            // При рестарте просто обнуляем коннект
            if (instant || Config.PLAYER_LOGOUT_INGAME_TIME == 0)
                deleteMe();
            else
                scheduleDelete(Config.PLAYER_LOGOUT_INGAME_TIME);
            if (_connection != null)
                _connection.setActiveChar(null);
        } else {
            L2GameServerPacket sp = shutdown || kicked ? Msg.ServerClose : Msg.LeaveWorld;
            sendPacket(sp);
            if (_connection != null && _connection.getConnection() != null)
                _connection.getConnection().close(sp);
            if (instant || Config.PLAYER_LOGOUT_INGAME_TIME == 0)
                deleteMe();
            else
                scheduleDelete(Config.PLAYER_LOGOUT_INGAME_TIME);
        }
    _connection = null;
    setConnected(false);
    broadcastUserInfo(false);
  }

  public void prepareToLogout()
  {
    if(isFlying() && !checkLandingState())
    {
      setLoc(MapRegion.getTeleToClosestTown(this));
    }
    if(isCastingNow())
    {
      abortCast(true);
    }
    // При логауте автоматом проигрывается дуэль.
    if(getDuel() != null)
    {
      getDuel().onPlayerDefeat(this);
    }
    if(isFestivalParticipant())
    {
      L2Party playerParty = getParty();
      if(playerParty != null)
      {
        playerParty.broadcastMessageToPartyMembers(getName() + " has been removed from the upcoming festival.");
      }
    }
    CursedWeaponsManager.getInstance().doLogout(this);
    if(inObserverMode())
    {
      if(getOlympiadObserveId() == -1)
      {
        leaveObserverMode();
      }
      else
      {
        leaveOlympiadObserverMode();
      }
    }
    if(isInOlympiadMode() || getOlympiadGameId() > -1)
    {
      Olympiad.logoutPlayer(this);
    }
    // Вызов всех хэндлеров, определенных в скриптах
    Object[] script_args = new Object[]
    {
      _objectId
    };
    for(ScriptClassAndMethod handler : ScriptManager.onPlayerExit)
    {
      callScripts(handler.scriptClass, handler.method, script_args);
    }
    if(_stablePoint != null)
    {
      teleToLocation(_stablePoint);
      addAdena(_stablePoint.h);
      unsetVar("wyvern_moneyback");
    }
    if(_recomChars.isEmpty())
    {
      unsetVar("recomChars");
    }
    else
    {
      String recomList = Integer.toHexString(_recomChars.get(0));
      for(int i = 1; i < _recomChars.size(); i++)
      {
        recomList += "," + Integer.toHexString(_recomChars.get(i));
      }
      setVar("recomChars", recomList);
    }
    if(getPet() != null)
    {
      try
      {
        getPet().unSummon();
      }
      catch(Throwable t)
      {
        t.printStackTrace();
        _log.log(Level.WARNING, "prepareToLogout()", t);
      }
    }
    if(isInParty())
    {
      try
      {
        leaveParty();
      }
      catch(Throwable t)
      {
        t.printStackTrace();
        _log.log(Level.WARNING, "prepareToLogout()", t);
      }
    }
  }
  private boolean _logoutStarted = false;

  public boolean isLogoutStarted()
  {
    return _logoutStarted;
  }

  public void setLogoutStarted(boolean logoutStarted)
  {
    _logoutStarted = logoutStarted;
  }

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

  public Collection<L2Recipe> getCommonRecipeBook()
  {
    return _commonrecipebook.values();
  }

  public int recipesCount()
  {
    return _commonrecipebook.size() + _recipebook.size();
  }

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

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

  /**
   * Add a new L2RecipList to the table _recipebook containing all L2RecipeList of the L2Player
   */
  public void registerRecipe(final L2Recipe recipe, boolean saveDB)
  {
    if(recipe.isDwarvenRecipe())
    {
      _recipebook.put(recipe.getId(), recipe);
    }
    else
    {
      _commonrecipebook.put(recipe.getId(), recipe);
    }
    if(saveDB)
    {
      mysql.set("REPLACE INTO character_recipebook (char_id, id) VALUES(?,?)", getObjectId(), recipe.getId());
    }
  }

  /**
   * Remove a L2RecipList from the table _recipebook containing all L2RecipeList of the L2Player
   */
  public void unregisterRecipe(final int RecipeID)
  {
    if(_recipebook.containsKey(RecipeID))
    {
      mysql.set("DELETE FROM `character_recipebook` WHERE `char_id`=? AND `id`=? LIMIT 1", getObjectId(), RecipeID);
      _recipebook.remove(RecipeID);
    }
    else if(_commonrecipebook.containsKey(RecipeID))
    {
      mysql.set("DELETE FROM `character_recipebook` WHERE `char_id`=? AND `id`=? LIMIT 1", getObjectId(), RecipeID);
      _commonrecipebook.remove(RecipeID);
    }
    else
    {
      _log.warning("Attempted to remove unknown RecipeList" + RecipeID);
    }
  }
  // ------------------- Quest Engine ----------------------

  public QuestState getQuestState(String quest)
  {
    return _quests != null ? _quests.get(quest) : null;
  }

  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)
  {
    _quests.put(qs.getQuest().getName(), qs);
  }

  public void delQuestState(String quest)
  {
    _quests.remove(quest);
  }

  public Quest[] getAllActiveQuests()
  {
    GArray<Quest> quests = new GArray<Quest>();
    for(final QuestState qs : _quests.values())
    {
      if(qs != null && qs.isStarted())
      {
        quests.add(qs.getQuest());
      }
    }
    return quests.toArray(new Quest[quests.size()]);
  }

  public QuestState[] getAllQuestsStates()
  {
    return _quests.values().toArray(new QuestState[_quests.size()]);
  }

  public GArray<QuestState> getQuestsForEvent(L2NpcInstance npc, QuestEventType event)
  {
    GArray<QuestState> states = new GArray<QuestState>();
    Quest[] quests = npc.getTemplate().getEventQuests(event);
    if(quests != null)
    {
      for(Quest quest : quests)
      {
        if(getQuestState(quest.getName()) != null && !getQuestState(quest.getName()).isCompleted())
        {
          states.add(getQuestState(quest.getName()));
        }
      }
    }
    return states;
  }

  public void processQuestEvent(String quest, String event, L2NpcInstance npc)
  {
    if(event == null)
    {
      event = "";
    }
    QuestState qs = getQuestState(quest);
    if(qs == null)
    {
      Quest q = QuestManager.getQuest(quest);
      if(q == null)
      {
        System.out.println("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.8 < getInventory().getSize())
    {
      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;
  }
  // ----------------- End of Quest Engine -------------------

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

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

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

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

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

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

  public MacroList getMacroses()
  {
    return _macroses;
  }

  /**
   * Возвращает состояние осады L2Player.<BR>
   * 1 = attacker, 2 = defender, 0 = не учавствует
   *
   * @return состояние осады
   */
  public int getSiegeState()
  {
    return _siegeState;
  }

  /**
   * Устанавливает состояние осады L2Player.<BR>
   * 1 = attacker, 2 = defender, 0 = не учавствует
   */
  public void setSiegeState(int siegeState)
  {
    _siegeState = siegeState;
    broadcastRelationChanged();
  }

  public boolean isCastleLord(int castleId)
  {
    return _clan != null && isClanLeader() && _clan.getHasCastle() == 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 getRecomLeft()
  {
    return _recomLeft;
  }

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

  public void giveRecom(final L2Player target)
  {
    int targetRecom = target.getRecomHave();
    if(targetRecom < 255)
    {
      target.setRecomHave(targetRecom + 1);
    }
    if(_recomLeft > 0)
    {
      _recomLeft--;
    }
    _recomChars.add(target.getObjectId());
  }

  public boolean canRecom(final L2Player target)
  {
    return !_recomChars.contains(target.getObjectId());
  }

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

  public void setKarma(int karma)
  {
    if(karma < 0)
    {
      karma = 0;
    }
    if(_karma == karma)
    {
      return;
    }
    _karma = karma;
    if(karma > 0)
    {
      for(final L2Character object : L2World.getAroundCharacters(this))
      {
        if(object instanceof L2GuardInstance && object.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
        {
          object.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null, null);
        }
      }
    }
    sendChanges();
    if(getPet() != null)
    {
      getPet().broadcastPetInfo();
    }
  }

  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);
    }
  }

  public int getWeightPenalty()
  {
    return _curWeightPenalty;
  }

  @Override
  public void updateEffectIcons()
  {
    if(isMassUpdating())
    {
      return;
    }
    L2Effect[] effects = getEffectList().getAllFirstEffects();
    Arrays.sort(effects, EffectsComparator.getInstance());
    PartySpelled ps = new PartySpelled(this, false);
    AbnormalStatusUpdate mi = new AbnormalStatusUpdate();
    for(L2Effect effect : effects)
    {
      if(effect != null && effect.isInUse())
      {
        if(effect.getStackType().equalsIgnoreCase("HpRecoverCast"))
        {
          sendPacket(new ShortBuffStatusUpdate(effect));
        }
        else
        {
          effect.addIcon(mi);
        }
        if(_party != null)
        {
          effect.addPartySpelledIcon(ps);
        }
      }
    }
    sendPacket(mi);
    if(_party != null)
    {
      _party.broadcastToPartyMembers(ps);
    }
    if(Config.ENABLE_OLYMPIAD && isInOlympiadMode() && isOlympiadCompStart())
    {
      OlympiadGame olymp_game = Olympiad.getOlympiadGame(getOlympiadGameId());
      if(olymp_game != null)
      {
        ExOlympiadSpelledInfo OlympiadSpelledInfo = new ExOlympiadSpelledInfo();
        for(L2Effect effect : effects)
        {
          if(effect != null && effect.isInUse())
          {
            effect.addOlympiadSpelledIcon(this, OlympiadSpelledInfo);
          }
        }
        if(olymp_game.getType() == CompType.CLASSED || olymp_game.getType() == CompType.NON_CLASSED)
        {
          for(L2Player member : olymp_game.getTeamMembers(this))
          {
            member.sendPacket(OlympiadSpelledInfo);
          }
        }
        for(L2Player member : olymp_game.getSpectators())
        {
          member.sendPacket(OlympiadSpelledInfo);
        }
      }
    }
  }

  public void refreshOverloaded()
  {
    if(isMassUpdating() || getMaxLoad() <= 0)
    {
      return;
    }
    setOverloaded(getCurrentLoad() > getMaxLoad());
    double weightproc = 100. * (getCurrentLoad() - calcStat(Stats.MAX_NO_PENALTY_LOAD, 0, this, null)) / getMaxLoad();
    int newWeightPenalty;
    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;
    }
    if(_curWeightPenalty == newWeightPenalty)
    {
      return;
    }
    _curWeightPenalty = newWeightPenalty;
    if(_curWeightPenalty > 0)
    {
      super.addSkill(SkillTable.getInstance().getInfo(4270, _curWeightPenalty));
    }
    else
    {
      super.removeSkill(getKnownSkill(4270));
    }
    sendPacket(new EtcStatusUpdate(this));
  }

  public void refreshExpertisePenalty()
  {
    if(isMassUpdating())
    {
      return;
    }
    int armorPenalty = 0;
    int weaponPenalty = 0;
    // Calculate the current higher Expertise of the L2Player
    int level = (int) calcStat(Stats.GRADE_EXPERTISE_LEVEL, getLevel(), null, null);
    int i;
    for(i = 0; i < EXPERTISE_LEVELS.length; i++)
    {
      if(level < EXPERTISE_LEVELS[i + 1])
      {
        break;
      }
    }
    // Add the Expertise skill corresponding to its Expertise level
    if(expertiseIndex != i)
    {
      expertiseIndex = i;
      if(expertiseIndex > 0)
      {
        addSkill(SkillTable.getInstance().getInfo(239, expertiseIndex), false);
      }
    }
    L2ItemInstance[] items = getInventory().getPaperdollItems();
    for(L2ItemInstance item : items)
    {
      if(item != null)
      {
        int crystaltype = item.getItem().getCrystalType().ordinal();
        if(item.getItem().getType2() == L2Item.TYPE2_WEAPON)
        {
          weaponPenalty = crystaltype;
        }
        else if(crystaltype > armorPenalty)
        {
          armorPenalty = crystaltype;
        }
      }
    }
    boolean changed = false;
    // calc armor penalty
    armorPenalty -= expertiseIndex;
    if(armorPenalty < 0)
    {
      armorPenalty = 0;
    }
    else if(armorPenalty > 4)
    {
      armorPenalty = 4;
    }
    if(getArmorPenalty() != armorPenalty || getSkillLevel(6213) != armorPenalty)
    {
      this.armorPenalty = armorPenalty;
      if(this.armorPenalty > 0)
      {
        super.addSkill(SkillTable.getInstance().getInfo(6213, this.armorPenalty)); // level used to be newPenalty
      }
      else
      {
        super.removeSkill(getKnownSkill(6213));
      }
      changed = true;
    }
    // calc weapon penalty
    weaponPenalty -= expertiseIndex;
    if(weaponPenalty < 0)
    {
      weaponPenalty = 0;
    }
    else if(weaponPenalty > 4)
    {
      weaponPenalty = 4;
    }
    if(getWeaponPenalty() != weaponPenalty || getSkillLevel(6209) != weaponPenalty)
    {
      this.weaponPenalty = weaponPenalty;
      if(this.weaponPenalty > 0)
      {
        super.addSkill(SkillTable.getInstance().getInfo(6209, this.weaponPenalty)); // level used to be newPenalty
      }
      else
      {
        super.removeSkill(getKnownSkill(6209));
      }
      changed = true;
    }
    if(changed)
    {
      sendPacket(new EtcStatusUpdate(this));
    }
  }

  public int getExpertiseIndex()
  {
    return expertiseIndex;
  }

  public boolean getExpertisePenalty()
  {
    return weaponPenalty > 0 || armorPenalty > 0;
  }

  public int getWeaponPenalty()
  {
    return weaponPenalty;
  }

  public int getArmorPenalty()
  {
    return armorPenalty;
  }

  public int getPvpKills()
  {
    return _pvpKills;
  }

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

  public ClassId getClassId()
  {
    return getTemplate().classId;
  }

  public void addClanPointsOnProfession(final int id)
  {
    if(getLvlJoinedAcademy() != 0 && _clan != null && _clan.getLevel() >= 5 && ClassId.values()[id].getLevel() == 2)
    {
      _clan.incReputation(100, true, "Academy");
    }
    else if(getLvlJoinedAcademy() != 0 && _clan != null && _clan.getLevel() >= 5 && ClassId.values()[id].getLevel() == 3)
    {
      int earnedPoints;
      if(getLvlJoinedAcademy() <= 16)
      {
        earnedPoints = 650;
      }
      else if(getLvlJoinedAcademy() >= 39)
      {
        earnedPoints = 190;
      }
      else
      {
        earnedPoints = 650 - (getLvlJoinedAcademy() - 16) * 20;
      }
      _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);
      broadcastUserInfo(true);
      broadcastRelationChanged();
      sendPacket(Msg.PledgeShowMemberListDeleteAll);
      L2ItemInstance academyCirclet = ItemTable.getInstance().createItem(8181);
      getInventory().addItem(academyCirclet);
      sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_EARNED_S1).addItemName(academyCirclet.getItemId()));
    }
  }

  /**
   * 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)
  {
    if(!noban && !(ClassId.values()[id].equalsOrChildOf(ClassId.values()[getActiveClassId()]) || getPlayerAccess().CanChangeClass || Config.EVERYBODY_HAS_ADMIN_RIGHTS))
    {
      Thread.dumpStack();
      Util.handleIllegalPlayerAction(this, "L2Player[1535]", "tried to change class " + getActiveClassId() + " to " + id, 1);
      return;
    }
    //Если новый ID не принадлежит имеющимся классам значит это новая профа
    if(!getSubClasses().containsKey(id))
    {
      final L2SubClass cclass = getActiveClass();
      getSubClasses().remove(getActiveClassId());
      changeClassInDb(cclass.getClassId(), id);
      if(cclass.isBase())
      {
        setBaseClass(id);
        addClanPointsOnProfession(id);
        L2ItemInstance coupons = null;
        if(ClassId.values()[id].getLevel() == 2)
        {
          if(Config.ALT_ALLOW_SHADOW_WEAPONS)
          {
            coupons = ItemTable.getInstance().createItem(8869);
          }
          unsetVar("newbieweapon");
          unsetVar("p1q2");
          unsetVar("p1q3");
          unsetVar("p1q4");
          unsetVar("prof1");
          unsetVar("ng1");
          unsetVar("ng2");
          unsetVar("ng3");
          unsetVar("ng4");
        }
        else if(ClassId.values()[id].getLevel() == 3)
        {
          if(Config.ALT_ALLOW_SHADOW_WEAPONS)
          {
            coupons = ItemTable.getInstance().createItem(8870);
          }
          unsetVar("newbiearmor");
          unsetVar("dd1"); // удаляем отметки о выдаче дименшен даймондов
          unsetVar("dd2");
          unsetVar("dd3");
          unsetVar("prof2.1");
          unsetVar("prof2.2");
          unsetVar("prof2.3");
        }
        if(coupons != null)
        {
          coupons.setCount(15);
          getInventory().addItem(coupons);
          sendPacket(SystemMessage.obtainItems(coupons));
        }
      }
      // Выдача Holy Pomander
      switch(ClassId.values()[id])
      {
        case cardinal:
          Functions.addItem(this, 15307, 1);
          break;
        case evaSaint:
          Functions.addItem(this, 15308, 1);
          break;
        case shillienSaint:
          Functions.addItem(this, 15309, 4);
          break;
      }
      cclass.setClassId(id);
      getSubClasses().put(id, cclass);
      rewardSkills();
      storeCharSubClasses();
      // Социалка при получении профы
      broadcastPacket(new MagicSkillUse(this, this, 5103, 1, 1000, 0));
      //broadcastPacket(new SocialAction(getObjectId(), 16));
      sendPacket(new PlaySound("ItemSound.quest_fanfare_2"));
      broadcastUserInfo(true);
    }
    L2PlayerTemplate t = CharTemplateTable.getInstance().getTemplate(id, getSex() == 1);
    if(t == null)
    {
      _log.severe("Missing template for classId: " + id);
      // do not throw error - only print error
      return;
    }
    // Set the template of the L2Player
    setTemplate(t);
    // Update class icon in party and clan
    if(isInParty())
    {
      getParty().broadcastToPartyMembers(new PartySmallWindowUpdate(this));
    }
    if(getClan() != null)
    {
      getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
    }
  }

  public void setClassId(final int id)
  {
    setClassId(id, false);
  }

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

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

  public void addExp(long val)
  {
    if(_activeClass != null)
    {
      _activeClass.addExp(val);
    }
  }

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

  public L2ItemInstance getEnchantScroll()
  {
    return _enchantScroll;
  }

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

  public L2Weapon getFistsWeaponItem()
  {
    return _fistsWeaponItem;
  }

  public L2Weapon findFistsWeaponItem(final int classId)
  {
    //human fighter fists
    if(classId >= 0x00 && classId <= 0x09)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(246);
    }
    //human mage fists
    if(classId >= 0x0a && classId <= 0x11)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(251);
    }
    //elven fighter fists
    if(classId >= 0x12 && classId <= 0x18)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(244);
    }
    //elven mage fists
    if(classId >= 0x19 && classId <= 0x1e)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(249);
    }
    //dark elven fighter fists
    if(classId >= 0x1f && classId <= 0x25)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(245);
    }
    //dark elven mage fists
    if(classId >= 0x26 && classId <= 0x2b)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(250);
    }
    //orc fighter fists
    if(classId >= 0x2c && classId <= 0x30)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(248);
    }
    //orc mage fists
    if(classId >= 0x31 && classId <= 0x34)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(252);
    }
    //dwarven fists
    if(classId >= 0x35 && classId <= 0x39)
    {
      return (L2Weapon) ItemTable.getInstance().getTemplate(247);
    }
    return null;
  }

  /**
   * Добавляет чару опыт и/или сп с учетом личного бонуса
   */
  @Override
  public void addExpAndSp(long addToExp, long addToSp)
  {
    addExpAndSp(addToExp, addToSp, true, true);
  }

  /**
   * Добавляет чару опыт и/или сп, с учетом личного бонуса или нет
   */
  @Override
  public void addExpAndSp(long addToExp, long addToSp, boolean applyBonus, boolean appyToPet)
  {
    if(addToExp > 0 && getVarB("NoExp"))
    {
      addToExp = 0;
    }
    if(applyBonus)
    {
      addToExp *= Config.RATE_XP * getRateExp();
      addToSp *= Config.RATE_SP * getRateSp();
    }
    if(addToExp > 0)
    {
      if(appyToPet)
      {
        L2Summon pet = getPet();
        if(pet != null && !pet.isDead())
        // Sin Eater забирает всю экспу у персонажа
        {
          if(pet.getNpcId() == PetDataTable.SIN_EATER_ID)
          {
            pet.addExpAndSp(addToExp, 0);
            addToExp = 0;
          }
          else if(pet.isPet() && pet.getLevel() < 85 && pet.getExpPenalty() > 0f)
          {
            if(pet.getLevel() > getLevel() - 20 && pet.getLevel() < getLevel() + 5)
            {
              pet.addExpAndSp((long) (addToExp * pet.getExpPenalty()), 0);
              addToExp *= 1f - pet.getExpPenalty();
            }
            else
            {
              pet.addExpAndSp((long) (addToExp * pet.getExpPenalty() / 5f), 0);
              addToExp *= 1f - pet.getExpPenalty() / 5f;
            }
          }
          else if(pet.isSummon())
          {
            addToExp *= 1f - pet.getExpPenalty();
          }
        }
      }
      // Remove Karma when the player kills L2MonsterInstance
      if(!isCursedWeaponEquipped() && addToSp > 0 && _karma > 0)
      {
        _karma -= addToSp / (Config.KARMA_SP_DIVIDER * Config.RATE_SP);
      }
      if(_karma < 0)
      {
        _karma = 0;
      }
      long max_xp = getMaxExp();
      addToExp = Math.min(addToExp, max_xp - getExp());
    }
    addExp(addToExp);
    addSp(addToSp);
    if(addToSp > 0 && addToExp == 0)
    {
      sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_ACQUIRED_S1_SP).addNumber(addToSp));
    }
    else if(addToSp > 0 || addToExp > 0)
    {
      sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_EARNED_S1_EXPERIENCE_AND_S2_SP).addNumber(addToExp).addNumber(addToSp));
    }
    long exp = getExp();
    int level = getLevel();
    while(exp >= Experience.LEVEL[level + 1] && increaseLevel())
    {
      level = getLevel();
    }
    while(exp < Experience.LEVEL[level] && decreaseLevel())
    {
      level = getLevel();
    }
    sendChanges();
  }

  /**
   * 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>
   */
  private void rewardSkills()
  {
    boolean update = false;
    if(Config.AUTO_LEARN_SKILLS && getLevel() <= Config.AutoLearnSkillsLvl)
    {
      int unLearnable = 0;
      GArray<L2SkillLearn> skills = SkillTreeTable.getInstance().getAvailableSkills(this, getClassId());
      while(skills.size() > unLearnable)
      {
        unLearnable = 0;
        for(L2SkillLearn s : skills)
        {
          L2Skill sk = SkillTable.getInstance().getInfo(s.id, s.skillLevel);
          if(sk == null || !sk.getCanLearn(getClassId()))
          {
            unLearnable++;
            continue;
          }
          addSkill(sk, true);
        }
        skills = SkillTreeTable.getInstance().getAvailableSkills(this, getClassId());
      }
      update = true;
    }
    else
    // Скиллы дающиеся бесплатно не требуют изучения
    {
      for(L2SkillLearn skill : SkillTreeTable.getInstance().getAvailableSkills(this, getClassId()))
      {
        if(skill._repCost == 0 && skill._spCost == 0 && skill.itemCount == 0)
        {
          L2Skill sk = SkillTable.getInstance().getInfo(skill.getId(), skill.getLevel());
          addSkill(sk, true);
          if(getAllShortCuts().size() > 0 && sk.getLevel() > 1)
          {
            for(L2ShortCut sc : getAllShortCuts())
            {
              if(sc.id == sk.getId() && sc.type == L2ShortCut.TYPE_SKILL)
              {
                L2ShortCut newsc = new L2ShortCut(sc.slot, sc.page, sc.type, sc.id, sk.getLevel());
                sendPacket(new ShortCutRegister(newsc));
                registerShortCut(newsc);
              }
            }
          }
          update = true;
        }
      }
    }
    if(update)
    {
      sendPacket(new SkillList(this));
    }
    // This function gets called on login, so not such a bad place to check weight
    // Update the overloaded status of the L2Player
    refreshOverloaded();
    refreshExpertisePenalty();
  }

  public Race getRace()
  {
    return getBaseTemplate().race;
  }

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

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

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

  public void addSp(long val)
  {
    if(_activeClass != null)
    {
      _activeClass.addSp(val);
    }
  }

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

  @Override
  public int getClanCrestId()
  {
    return _clan == null ? 0 : _clan.getCrestId();
  }

  @Override
  public int getClanCrestLargeId()
  {
    return _clan == null ? 0 : _clan.getCrestLargeId();
  }

  public long getLeaveClanTime()
  {
    return _leaveClanTime;
  }

  public long getDeleteClanTime()
  {
    return _deleteClanTime;
  }

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

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

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

  public void setNoChannel(final long time)
  {
    _NoChannel = time;
    if(_NoChannel > 2145909600000L || _NoChannel < 0)
    {
      _NoChannel = -1;
    }
    if(_NoChannel > 0)
    {
      _NoChannelBegin = System.currentTimeMillis();
    }
    else
    {
      _NoChannelBegin = 0;
    }
    sendPacket(new EtcStatusUpdate(this));
  }

  public long getNoChannel()
  {
    return _NoChannel;
  }

  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 SystemMessage canJoinParty(L2Player inviter)
  {
    Transaction transaction = getTransaction();
    if(transaction != null && transaction.isInProgress() && transaction.getOtherPlayer(this) != inviter)
    {
      return Msg.WAITING_FOR_ANOTHER_REPLY;
    } // занят
    if(isBlockAll() || getMessageRefusal()) // всех нафиг
    {
      return Msg.THE_PERSON_IS_IN_A_MESSAGE_REFUSAL_MODE;
    }
    if(isInParty()) // уже
    {
      return new SystemMessage(SystemMessage.S1_IS_A_MEMBER_OF_ANOTHER_PARTY_AND_CANNOT_BE_INVITED).addString(getName());
    }
    if(ReflectionTable.getInstance().findSoloKamaloka(getObjectId()) != null) // в соло каме
    {
      return Msg.INVALID_TARGET;
    }
    if(isCursedWeaponEquipped() || inviter.isCursedWeaponEquipped()) // зарич
    {
      return Msg.INVALID_TARGET;
    }
    if(inviter.isInOlympiadMode() || isInOlympiadMode()) // олимпиада
    {
      return Msg.INVALID_TARGET;
    }
    if(!inviter.getPlayerAccess().CanJoinParty || !getPlayerAccess().CanJoinParty) // низя
    {
      return Msg.INVALID_TARGET;
    }
    if(getTeam() != 0) // участник пвп эвента или дуэли
    {
      return Msg.INVALID_TARGET;
    }
    return null;
  }

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

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

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

  @Override
  public boolean isSitting()
  {
    return inObserverMode() || _isSitting;
  }

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

  public boolean getSittingTask()
  {
    return sittingTaskLaunched;
  }

  @Override
  public void sitDown()
  {
    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);
    broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING));
    sittingTaskLaunched = true;
    _isSitting = true;
    ThreadPoolManager.getInstance().scheduleAi(new EndSitDownTask(this), 2500, true);
  }

  @Override
  public void standUp()
  {
    if(_isSitting && !sittingTaskLaunched && !isInStoreMode() && !isAlikeDead())
    {
      getEffectList().stopAllSkillEffects(EffectType.Relax);
      getEffectList().stopEffect(296);
      getAI().clearNextAction();
      broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING));
      sittingTaskLaunched = true;
      _isSitting = true;
      ThreadPoolManager.getInstance().scheduleAi(new EndStandUpTask(this), 2500, true);
    }
  }

  public void setRelax(final boolean val)
  {
    _relax = val;
  }

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

  public int getWaitSitTime()
  {
    return _waitTimeWhenSit;
  }

  public void resetWaitSitTime()
  {
    _waitTimeWhenSit = 0;
  }

  public Warehouse getWarehouse()
  {
    return _warehouse;
  }

  public Warehouse getFreight()
  {
    return _freight;
  }

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

  /**
   * Забирает адену у игрока.<BR><BR>
   *
   * @param adena  - сколько адены забрать
   * @param notify - отображать системное сообщение
   * @return L2ItemInstance - остаток адены
   */
  public L2ItemInstance reduceAdena(long adena, boolean notify)
  {
    if(notify && adena > 0)
    {
      sendPacket(new SystemMessage(SystemMessage.S1_ADENA_DISAPPEARED).addNumber(adena));
    }
    return getInventory().reduceAdena(adena);
  }

  /**
   * Добавляет адену игроку.<BR><BR>
   *
   * @param adena - сколько адены дать
   * @return L2ItemInstance - новое количество адены
   *         TODO добавить параметр update как в reduceAdena
   */
  public L2ItemInstance addAdena(final long adena)
  {
    return getInventory().addAdena(adena);
  }

  public L2GameClient getNetConnection()
  {
    return _connection;
  }

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

  public void setNetConnection(final L2GameClient connection)
  {
    _connection = connection;
    _isConnected = connection != null && connection.isConnected();
  }

  public void closeNetConnection()
  {
    if(_connection != null)
    {
      _connection.closeNow(false);
    }
  }

  @Override
  public void onAction(final L2Player player, boolean shift)
  {
    if(Events.onAction(player, this, shift))
    {
      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
      {
        sendActionFailed();
      }
    }
    else if(getPrivateStoreType() != L2Player.STORE_PRIVATE_NONE)
    {
      if(getDistance(player) > INTERACTION_DISTANCE && getAI().getIntention() != CtrlIntention.AI_INTENTION_INTERACT)
      {
        if(!shift)
        {
          player.getAI().setIntention(CtrlIntention.AI_INTENTION_INTERACT, this, null);
        }
      }
      else
      {
        player.doInteract(this);
      }
    }
    else if(isAutoAttackable(player))
    {
      // Player with lvl < 21 can't attack a cursed weapon holder, and a cursed weapon holder can't attack players with lvl < 21
      if(isCursedWeaponEquipped() && player.getLevel() < 21 || player.isCursedWeaponEquipped() && getLevel() < 21)
      {
        player.sendActionFailed();
      }
      else
      {
        player.getAI().Attack(this, false, shift);
      }
    }
    else if(player != this && !shift)
    {
      player.getAI().setIntention(CtrlIntention.AI_INTENTION_FOLLOW, this, Config.FOLLOW_RANGE);
    }
    else
    {
      sendActionFailed();
    }
  }

  @Override
  public void broadcastStatusUpdate()
  {
    // Send the Server->Client packet StatusUpdate with current HP and MP to all L2Player that must be informed of HP/MP updates of this L2Player
    if(Config.FORCE_STATUSUPDATE)
    {
      super.broadcastStatusUpdate();
    }
    else if(!needStatusUpdate()) //По идее еше должно срезать траффик. Будут глюки с отображением - убрать это условие.
    {
      return;
    }
    sendStatusUpdate(false, StatusUpdate.CUR_HP, StatusUpdate.CUR_MP, StatusUpdate.CUR_CP);
    // 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));
    }
    if(getDuel() != null)
    {
      getDuel().broadcastToOppositTeam(this, new ExDuelUpdateUserInfo(this));
    }
    if(isInOlympiadMode() && isOlympiadCompStart())
    {
      OlympiadGame game = Olympiad.getOlympiadGame(getOlympiadGameId());
      if(game != null)
      {
        game.broadcastInfo(this, null, false);
      }
    }
  }
  public Future<?> _broadcastCharInfoTask;

  /**
   * Отправляет UserInfo даному игроку и CharInfo всем окружающим.<BR><BR>
   * <p/>
   * <B><U> Концепт</U> :</B><BR><BR>
   * Сервер шлет игроку UserInfo.
   * Сервер вызывает метод {@link L2Player#broadcastPacketToOthers(l2p.gameserver.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 l2p.gameserver.model.L2Player#sendChanges()}</B></FONT><BR><BR>
   */
  @Override
  public void broadcastUserInfo(boolean force)
  {
    sendUserInfo(force);
    if(isInvisible())
    {
      return;
    }
    if(Config.BROADCAST_CHAR_INFO_INTERVAL == 0)
    {
      force = true;
    }
    if(force)
    {
      broadcastCharInfo();
      if(_broadcastCharInfoTask != null)
      {
        _broadcastCharInfoTask.cancel(false);
        _broadcastCharInfoTask = null;
      }
      return;
    }
    if(_broadcastCharInfoTask != null)
    {
      return;
    }
    _broadcastCharInfoTask = ThreadPoolManager.getInstance().scheduleAi(new BroadcastCharInfoTask(this), Config.BROADCAST_CHAR_INFO_INTERVAL, true);
  }

  public L2GameServerPacket newCharInfo()
  {
    if(!isPolymorphed())
    {
      return new CharInfo(this);
    }
    else if(getPolytype() == L2Object.POLY_NPC)
    {
      return new NpcInfoPoly(this);
    }
    else
    {
      return new SpawnItemPoly(this);
    }
  }

  public void broadcastCharInfo()
  {
    if(isInvisible())
    {
      return;
    }
    L2GameServerPacket ci = newCharInfo();
    for(L2Player player : L2World.getAroundPlayers(this))
    {
      if(player != null && player != this)
      {
        player.sendPacket(ci);
      }
    }
  }

  public void broadcastRelationChanged()
  {
    if(isInvisible() || isInOfflineMode())
    {
      return;
    }
    for(L2Player player : L2World.getAroundPlayers(this))
    {
      if(player != null && _objectId != player.getObjectId())
      {
        player.sendPackets(RelationChanged.update(player, this, player));
      }
    }
  }
  public Future<?> _userInfoTask;
  public boolean entering = true;

  public void sendUserInfo(boolean force)
  {
    if(entering || isLogoutStarted())
    {
      return;
    }
    if(Config.USER_INFO_INTERVAL == 0 || force)
    {
      sendPacket(new UserInfo(this), new ExBrExtraUserInfo(this));
      if(_userInfoTask != null)
      {
        _userInfoTask.cancel(false);
        _userInfoTask = null;
      }
      return;
    }
    if(_userInfoTask != null)
    {
      return;
    }
    _userInfoTask = ThreadPoolManager.getInstance().scheduleAi(new UserInfoTask(this), Config.USER_INFO_INTERVAL, true);
  }

  @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;
        default:
          System.out.println("unknown StatusUpdate field: " + field);
          Thread.dumpStack();
          break;
      }
    }
    return su;
  }

  public void sendStatusUpdate(boolean broadCast, int... fields)
  {
    if(fields.length == 0 || entering && !broadCast)
    {
      return;
    }
    StatusUpdate su = makeStatusUpdate(fields);
    if(!su.hasAttributes())
    {
      return;
    }
    if(!broadCast)
    {
      sendPacket(su);
    }
    else if(entering)
    {
      broadcastPacketToOthers(su);
    }
    else
    {
      broadcastPacket(su);
    }
  }

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

  @Override
  public int getAllyCrestId()
  {
    return getAlliance() == null ? 0 : getAlliance().getAllyCrestId();
  }
  public HashMap<String, Integer> packetsStat = null;
  public boolean packetsCount = false;

  protected synchronized void sendPacketStatsUpdate(final L2GameServerPacket... packets)
  {
    if(packetsStat == null)
    {
      packetsStat = new HashMap<String, Integer>();
    }
    String className;
    Integer count;
    for(L2GameServerPacket packet : packets)
    {
      className = packet.getClass().getSimpleName();
      count = packetsStat.get(className);
      if(count == null)
      {
        count = 1;
      }
      else
      {
        count++;
      }
      packetsStat.put(className, count);
    }
  }

  protected synchronized void sendPacketsStatsUpdate(final Collection<L2GameServerPacket> packets)
  {
    if(packetsStat == null)
    {
      packetsStat = new HashMap<String, Integer>();
    }
    String className;
    Integer count;
    for(SendablePacket<?> packet : packets)
    {
      className = packet.getClass().getSimpleName();
      count = packetsStat.get(className);
      if(count == null)
      {
        count = 1;
      }
      else
      {
        count++;
      }
      packetsStat.put(className, count);
    }
  }

  /**
   * Send a Server->Client packet StatusUpdate to the L2Player.<BR><BR>
   */
  @Override
  public void sendPacket(final L2GameServerPacket... packets)
  {
    if(_isConnected && packets.length != 0)
    {
      try
      {
        if(_connection != null)
        {
          _connection.sendPacket(packets);
        }
        if(packetsCount && isGM())
        {
          sendPacketStatsUpdate(packets);
        }
      }
      catch(final Exception e)
      {
        _log.log(Level.INFO, "", e);
        e.printStackTrace();
      }
    }
  }

  public void sendPackets(final Collection<L2GameServerPacket> packets)
  {
    if(_isConnected && packets != null && packets.size() > 0)
    {
      try
      {
        if(_connection != null)
        {
          _connection.sendPackets(packets);
        }
        if(packetsCount && isGM())
        {
          sendPacketsStatsUpdate(packets);
        }
      }
      catch(final Exception e)
      {
        _log.log(Level.INFO, "", e);
        e.printStackTrace();
      }
    }
  }

  public void doInteract(L2Object target)
  {
    if(target == null || isOutOfControl())
    {
      sendActionFailed();
      return;
    }
    if(target.isPlayer())
    {
      if(target.getDistance(this) <= INTERACTION_DISTANCE)
      {
        L2Player temp = (L2Player) 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(L2ItemInstance item, L2NpcInstance fromNpc)
  {
    boolean forceAutoloot = fromNpc.isFlying() || getReflection().isAutolootForced();
    if((fromNpc.isRaid() || fromNpc instanceof L2ReflectionBossInstance) && !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;
      }
      L2Skill[] skills = item.getItem().getAttachedSkills();
      if(skills != null && skills.length > 0)
      {
        for(L2Skill skill : skills)
        {
          altUseSkill(skill, this);
          if(getPet() != null && getPet().isSummon() && !getPet().isDead() && (item.getItemId() <= 8605 || item.getItemId() == 8614))
          {
            getPet().altUseSkill(skill, getPet());
          }
        }
      }
      item.deleteMe();
      broadcastPacket(new GetItem(item, getObjectId()));
      return;
    }
    if(!AutoLoot && !forceAutoloot)
    {
      item.dropToTheGround(this, fromNpc);
      return;
    }
    // Check if the L2Player is in a Party
    if(!isInParty())
    {
      if(!getInventory().validateWeight(item))
      {
        sendActionFailed();
        sendPacket(Msg.YOU_HAVE_EXCEEDED_THE_WEIGHT_LIMIT);
        item.dropToTheGround(this, fromNpc);
        return;
      }
      if(!getInventory().validateCapacity(item))
      {
        sendActionFailed();
        sendPacket(Msg.YOUR_INVENTORY_IS_FULL);
        item.dropToTheGround(this, fromNpc);
        return;
      }
      // Send a System Message to the L2Player
      sendPacket(SystemMessage.obtainItems(item));
      // Add the Item to the L2Player inventory
      L2ItemInstance target2 = getInventory().addItem(item);
      Log.LogItem(this, fromNpc, Log.GetItemByAutoLoot, target2);
      sendChanges();
    }
    else if(item.getItemId() == 57)
    // Distribute Adena between Party members
    {
      getParty().distributeAdena(item, fromNpc, this);
    }
    else
    // Distribute Item between Party members
    {
      getParty().distributeItem(this, item, fromNpc);
    }
    broadcastPickUpMsg(item);
  }

  @Override
  public void doPickupItem(final L2Object object)
  {
    // Check if the L2Object to pick up is a L2ItemInstance
    if(!object.isItem())
    {
      _log.warning("trying to pickup wrong target." + getTarget());
      return;
    }
    sendActionFailed();
    stopMove();
    L2ItemInstance item = (L2ItemInstance) object;
    if(item.getItem().isCombatFlag() && !FortressSiegeManager.checkIfCanPickup(this))
    {
      return;
    }
    synchronized(item)
    {
      // Check if me not owner of item and, if in party, not in owner party and nonowner pickup delay still active
      if(item.getDropTimeOwner() != 0 && item.getItemDropOwner() != null && item.getDropTimeOwner() > System.currentTimeMillis() && this != item.getItemDropOwner() && (!isInParty() || isInParty() && item.getItemDropOwner().isInParty() && getParty() != item.getItemDropOwner().getParty()))
      {
        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;
      }
      if(!item.isVisible())
      {
        return;
      }
      // Herbs
      if(item.isHerb())
      {
        L2Skill[] skills = item.getItem().getAttachedSkills();
        if(skills != null && skills.length > 0)
        {
          for(L2Skill skill : skills)
          {
            altUseSkill(skill, this);
            if(getPet() != null && getPet().isSummon() && !getPet().isDead() && (item.getItemId() <= 8605 || item.getItemId() == 8614))
            {
              getPet().altUseSkill(skill, getPet());
            }
          }
        }
        item.deleteMe();
        broadcastPacket(new GetItem(item, getObjectId()));
        return;
      }
      boolean equip = (item.getCustomFlags() & L2ItemInstance.FLAG_EQUIP_ON_PICKUP) == L2ItemInstance.FLAG_EQUIP_ON_PICKUP;
      if(!isInParty() || equip)
      {
        if(!getInventory().validateWeight(item))
        {
          sendPacket(Msg.YOU_HAVE_EXCEEDED_THE_WEIGHT_LIMIT);
          return;
        }
        if(!getInventory().validateCapacity(item))
        {
          sendPacket(Msg.YOUR_INVENTORY_IS_FULL);
          return;
        }
        if(!item.pickupMe(this))
        {
          return;
        }
        sendPacket(SystemMessage.obtainItems(item));
        Log.LogItem(this, Log.PickupItem, getInventory().addItem(item));
        if(equip)
        {
          getInventory().equipItem(item, true);
        }
        sendChanges();
      }
      else if(item.getItemId() == 57)
      {
        if(!item.pickupMe(this))
        {
          return;
        }
        getParty().distributeAdena(item, this);
      }
      else
      {
        // Нужно обязательно сначало удалить предмет с земли.
        if(!item.pickupMe(null))
        {
          return;
        }
        getParty().distributeItem(this, item);
      }
      broadcastPacket(new GetItem(item, getObjectId()));
      broadcastPickUpMsg(item);
    }
  }

  @Override
  public void setTarget(L2Object newTarget)
  {
    // Check if the new target is visible
    if(newTarget != null && !newTarget.isVisible())
    {
      newTarget = null;
    }
    // Can't target and attack festival monsters if not participant
    if(newTarget instanceof L2FestivalMonsterInstance && !isFestivalParticipant())
    {
      newTarget = null;
    }
    L2Party party = getParty();
    // Can't target and attack rift invaders if not in the same room
    if(party != null && party.isInDimensionalRift())
    {
      Integer riftType = party.getDimensionalRift().getType();
      Integer riftRoom = party.getDimensionalRift().getCurrentRoom();
      if(newTarget != null && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(newTarget.getX(), newTarget.getY(), newTarget.getZ()))
      {
        newTarget = null;
      }
    }
    L2Object 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.isCharacter())
      {
        ((L2Character) 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.isCharacter())
      {
        ((L2Character) newTarget).addStatusListener(this);
      }
      broadcastPacket(new TargetSelected(getObjectId(), newTarget.getObjectId(), getLoc()));
    }
    super.setTarget(newTarget);
  }

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

  /**
   * @return the active weapon item (always equipped in the right hand).<BR><BR>
   */
  @Override
  public L2Weapon getActiveWeaponItem()
  {
    final L2ItemInstance weapon = getActiveWeaponInstance();
    if(weapon == null)
    {
      return getFistsWeaponItem();
    }
    return (L2Weapon) weapon.getItem();
  }

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

  /**
   * @return the secondary weapon item (always equipped in the left hand) or the fists weapon.<BR><BR>
   */
  @Override
  public L2Weapon getSecondaryWeaponItem()
  {
    final L2ItemInstance weapon = getSecondaryWeaponInstance();
    if(weapon == null)
    {
      return getFistsWeaponItem();
    }
    final L2Item item = weapon.getItem();
    if(item instanceof L2Weapon)
    {
      return (L2Weapon) item;
    }
    return null;
  }

  public boolean isWearingArmor(final ArmorType armorType)
  {
    final L2ItemInstance chest = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
    if(chest == null)
    {
      return armorType == ArmorType.NONE;
    }
    if(chest.getItemType() != armorType)
    {
      return false;
    }
    if(chest.getBodyPart() == L2Item.SLOT_FULL_ARMOR)
    {
      return true;
    }
    final L2ItemInstance legs = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS);
    return legs == null ? armorType == ArmorType.NONE : legs.getItemType() == armorType;
  }

  @Override
  public void reduceCurrentHp(double i, L2Character attacker, L2Skill skill, boolean awake, boolean standUp, boolean directHp, boolean canReflect)
  {
    if(attacker == null || isDead() || attacker.isDead())
    {
      return;
    }
    if(isInvul() && attacker != this)
    {
      attacker.sendPacket(Msg.THE_ATTACK_HAS_BEEN_BLOCKED);
      return;
    }
    if(isInOfflineMode() && attacker.getPlayer() != null && !attacker.getPlayer().isGM())
    {
      attacker.sendPacket(Msg.INVALID_TARGET);
      return;
    }
    if(this != attacker && isInOlympiadMode() && !isOlympiadCompStart())
    {
      attacker.getPlayer().sendPacket(Msg.INVALID_TARGET);
      return;
    }
    if(attacker.isPlayable() && isInZoneBattle() != attacker.isInZoneBattle())
    {
      attacker.getPlayer().sendPacket(Msg.INVALID_TARGET);
      return;
    }
    double trans = calcStat(Stats.TRANSFER_PET_DAMAGE_PERCENT, 0, attacker, skill);
    if(trans >= 1)
    {
      if(_summon == null || _summon.isDead())
      {
        getEffectList().stopEffect(L2Skill.SKILL_TRANSFER_PAIN);
        getEffectList().stopEffect(711); // Divine Summoner Transfer Pain
        getEffectList().stopEffect(3667); // Yellow Talisman - Damage Transition
      }
      else if(_summon.isInRange(this, 1200))
      {
        trans *= i * .01;
        i -= trans;
        _summon.reduceCurrentHp(trans, attacker, null, false, false, false, false);
      }
    }
    if(this != attacker && canReflect)
    {
      L2Effect transferDam = getEffectList().getEffectByType(EffectType.TransferDam);
      if(transferDam != null)
      {
        L2Character effector = transferDam.getEffector();
        if(effector == null || effector.isDead())
        {
          getEffectList().stopEffects(EffectType.TransferDam);
        }
        else if(effector.isInRange(this, 1200))
        {
          trans = transferDam.calc();
          trans *= i * .01;
          i -= trans;
          effector.reduceCurrentHp(trans, attacker, null, false, false, false, false);
        }
      }
    }
    if(attacker != this)
    {
      sendPacket(new SystemMessage(SystemMessage.C1_HAS_RECEIVED_DAMAGE_OF_S3_FROM_C2).addName(this).addName(attacker).addNumber((long) i));
    }
    double hp = directHp ? getCurrentHp() : getCurrentHp() + getCurrentCp();
    if(getDuel() != null)
    {
      if(getDuel() != attacker.getDuel())
      {
        getDuel().setDuelState(getStoredId(), DuelState.Interrupted);
      }
      else if(getDuel().getDuelState(getStoredId()) == DuelState.Interrupted)
      {
        attacker.getPlayer().sendPacket(Msg.INVALID_TARGET);
        return;
      }
      else if(i >= hp)
      {
        setCurrentHp(1, false);
        getDuel().onPlayerDefeat(this);
        getDuel().stopFighting(attacker.getPlayer());
        return;
      }
    }
    if(isInOlympiadMode())
    {
      OlympiadGame olymp_game = Olympiad.getOlympiadGame(getOlympiadGameId());
      if(olymp_game != null)
      {
        if(olymp_game.getState() <= 0)
        {
          attacker.getPlayer().sendPacket(Msg.INVALID_TARGET);
          return;
        }
        if(this != attacker)
        {
          olymp_game.addDamage(this, Math.min(hp, i));
        }
        if(i >= hp)
        {
          if(olymp_game.getType() != CompType.TEAM && olymp_game.getType() != CompType.TEAM_RANDOM)
          {
            olymp_game.setWinner(getOlympiadSide() == 1 ? 2 : 1);
            olymp_game.endGame(20000, false);
            setCurrentHp(1, false);
            attacker.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
            attacker.sendActionFailed();
            return;
          }
          else if(olymp_game.doDie(this)) // Все умерли
          {
            olymp_game.setWinner(getOlympiadSide() == 1 ? 2 : 1);
            olymp_game.endGame(20000, false);
          }
        }
      }
      else
      {
        _log.warning("OlympiadGame id = " + getOlympiadGameId() + " is null");
        Thread.dumpStack();
      }
    }
    // Reduce the current HP of the L2Player
    super.reduceCurrentHp(i, attacker, skill, awake, standUp, directHp, canReflect);
    //TODO: переделать на листенер
    if(getLevel() < 6 && getCurrentHpPercents() < 25)
    {
      Quest q = QuestManager.getQuest(255);
      if(q != null)
      {
        processQuestEvent(q.getName(), "CE45", null);
      }
    }
  }

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

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

  public boolean atMutualWarWith(L2Player 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 L2Player 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.increaseKarma(Config.KARMA_MIN_KARMA * pkCountMulti); // * lvlDiffMulti);
    killer.setPkKills(killer.getPkKills() + 1);
  }

  public final void doKillInPeace(final L2Player killer) // Check if the L2Player killed haven't Karma
  {
    if(_karma <= 0)
    {
      doPurePk(killer);
    }
    else
    {
      killer.setPvpKills(killer.getPvpKills() + 1);
    }
  }

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

  protected void doPKPVPManage(L2Character killer)
  {
    if(isCombatFlagEquipped())
    {
      L2ItemInstance flag = getActiveWeaponInstance();
      if(flag != null)
      {
        int customFlags = flag.getCustomFlags();
        flag.setCustomFlags(0, false);
        flag = getInventory().dropItem(flag, flag.getCount(), true);
        flag.setCustomFlags(customFlags, false);
        flag.dropMe(this, getLoc().rnd(0, 100, false));
        sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_DROPPED_S1).addItemName(flag.getItemId()));
      }
    }
    if(isTerritoryFlagEquipped())
    {
      L2ItemInstance flag = getActiveWeaponInstance();
      if(flag != null && flag.getCustomType1() != 77) // 77 это эвентовый флаг
      {
        L2TerritoryFlagInstance flagNpc = TerritorySiege.getNpcFlagByItemId(flag.getItemId());
        flagNpc.drop(this);
        sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_DROPPED_S1).addItemName(flag.getItemId()));
        String terrName = CastleManager.getInstance().getCastleByIndex(flagNpc.getBaseTerritoryId()).getName();
        TerritorySiege.announceToPlayer(new SystemMessage(SystemMessage.THE_CHARACTER_THAT_ACQUIRED_S1_WARD_HAS_BEEN_KILLED).addString(terrName), true);
      }
    }
    for(L2ItemInstance item : getInventory().getItemsList())
    {
      if((item.getCustomFlags() & L2ItemInstance.FLAG_ALWAYS_DROP_ON_DIE) == L2ItemInstance.FLAG_ALWAYS_DROP_ON_DIE)
      {
        item = getInventory().dropItem(item, item.getCount(), false);
        item.dropMe(this, getLoc().rnd(0, 100, false));
        sendPacket(new SystemMessage(SystemMessage.YOU_HAVE_DROPPED_S1).addItemName(item.getItemId()));
      }
    }
    if(killer == null || killer == _summon)
    {
      return;
    }
    if(killer.getObjectId() == _objectId)
    {
      return;
    }
    if(isInZoneBattle() || killer.isInZoneBattle() || isEvent)
    {
      return;
    }
    if(killer instanceof L2Summon && (killer = killer.getPlayer()) == null)
    {
      return;
    }
    // Processing Karma/PKCount/PvPCount for killer
    if(killer.isPlayer())
    {
      final L2Player pk = (L2Player) killer;
      final int repValue = getLevel() - pk.getLevel() >= 20 ? 2 : 1;
      boolean war = atMutualWarWith(pk);
      if(getLevel() > 4 && _clan != null && pk.getClan() != null)
      {
        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.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(isInZone(Siege))
      {
        if(pk.getTerritorySiege() > -1 && getTerritorySiege() > -1 && pk.getTerritorySiege() != getTerritorySiege() && pk.getLevel() - getLevel() < 10 && pk.getLevel() > 61 && getLevel() > 61)
        {
          if(getClanId() > 0 && getClanId() != pk.getClanId() || getAllyId() > 0 && getAllyId() != pk.getAllyId())
          {
            return;
          }
          String var = pk.getVar("badges" + pk.getTerritorySiege());
          int badges = var == null ? 0 : Integer.parseInt(var);
          if(!isInParty())
          {
            badges += Rnd.get(0, 1);
          }
          else if(getParty() != pk.getParty())
          {
            badges += Rnd.get(0, 2);
          }
          pk.setVar("badges" + pk.getTerritorySiege(), "" + badges);
        }
        return;
      }
      if(_pvpFlag > 0 || war)
      {
        pk.setPvpKills(pk.getPvpKills() + 1);
      }
      else
      {
        doKillInPeace(pk);
      }
      // Send a Server->Client UserInfo packet to attacker with its PvP Kills Counter
      pk.sendUserInfo(false);
    }
    int karma = _karma;
    decreaseKarma(Config.KARMA_LOST_BASE);
    // в нормальных условиях вещи теряются только при смерти от гварда или игрока
    // кроме того, альт на потерю вещей при сметри позволяет терять вещи при смтери от монстра
    boolean isPvP = killer.isPlayable() || killer instanceof L2GuardInstance;
    if(killer.isMonster() && !Config.DROP_ITEMS_ON_DIE // если убил монстр и альт выключен
       || isPvP // если убил игрок или гвард и
        && (_pkKills < Config.MIN_PK_TO_ITEMS_DROP // количество пк слишком мало
          || karma == 0 && Config.KARMA_NEEDED_TO_DROP) // кармы нет
       || isFestivalParticipant() // в фестивале вещи не теряются
       || !killer.isMonster() && !isPvP) // в прочих случаях тоже
    {
      return;
    }
    // No drop from GM's
    if(!Config.KARMA_DROP_GM && isGM())
    {
      return;
    }
    // нечего терять
    if(getInventory().getItemsList().isEmpty())
    {
      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<L2ItemInstance> dropped_items = new GArray<L2ItemInstance>(), // общий массив с результатами выбора
    dropItem = new GArray<L2ItemInstance>(), dropEquip = new GArray<L2ItemInstance>(), dropWeapon = new GArray<L2ItemInstance>(); // временные
    for(L2ItemInstance item : getInventory().getItems())
    {
      if(!item.canBeDropped(this, true) || Config.KARMA_LIST_NONDROPPABLE_ITEMS.contains(item.getItemId()))
      {
        continue;
      }
      if(item.getItem().getType2() == L2Item.TYPE2_WEAPON)
      {
        dropWeapon.add(item);
      }
      else if(item.getItem().getType2() == L2Item.TYPE2_SHIELD_ARMOR || item.getItem().getType2() == L2Item.TYPE2_ACCESSORY)
      {
        dropEquip.add(item);
      }
      else if(item.getItem().getType2() == L2Item.TYPE2_OTHER)
      {
        dropItem.add(item);
      }
    }
    checkAddItemToDrop(dropped_items, dropWeapon, dropWeaponCount);
    checkAddItemToDrop(dropped_items, dropEquip, dropEquipCount);
    checkAddItemToDrop(dropped_items, dropItem, dropItemCount);
    // Dropping items, if present
    if(dropped_items.isEmpty())
    {
      return;
    }
    for(L2ItemInstance item : dropped_items)
    {
      if(item.isEquipped())
      {
        getInventory().unEquipItemInSlot(item.getEquipSlot());
      }
      if(item.isAugmented() && !Config.ALT_ALLOW_DROP_AUGMENTED)
      {
        item.removeAugmentation();
      }
      item = getInventory().dropItem(item, item.getCount(), false);
      if(Config.MOBSLOOTERS && killer.isMonster() && !item.isCursed() && _reflection <= 0)
      {
        if(killer.isMinion() && !((L2MinionInstance) killer).getLeader().isDead())
        {
          ((L2MinionInstance) killer).getLeader().giveItem(item, true);
        }
        else
        {
          ((L2MonsterInstance) killer).giveItem(item, true);
        }
      }
      else if(killer.isPlayer() && Config.AUTO_LOOT && Config.AUTO_LOOT_PK)
      {
        ((L2Player) killer).getInventory().addItem(item);
      }
      else if(killer.isSummon() && Config.AUTO_LOOT && Config.AUTO_LOOT_PK)
      {
        killer.getPlayer().getInventory().addItem(item);
      }
      else
      {
        item = getInventory().dropItem(item, item.getCount(), false);
        item.dropMe(this, getLoc().rnd(0, Config.KARMA_RANDOM_DROP_LOCATION_LIMIT, false));
      }
      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()));
      }
    }
    refreshOverloaded();
  }

  @Override
  public void doDie(L2Character killer)
  {
    dieLock.lock();
    try
    {
      if(_killedAlreadyPlayer)
      {
        return;
      }
      _killedAlreadyPlayer = true;
    }
    finally
    {
      dieLock.unlock();
    }
    //Check for active charm of luck for death penalty
    getDeathPenalty().checkCharmOfLuck();
    L2TradeList tl = getTradeList();
    if(tl != null)
    {
      tl.removeAll();
      setTradeList(null);
    }
    if(isInTransaction())
    {
      if(getTransaction().isTypeOf(TransactionType.TRADE))
      {
        sendPacket(new SendTradeDone(0));
      }
      getTransaction().cancel();
    }
    setPrivateStoreType(L2Player.STORE_PRIVATE_NONE);
    // Kill the L2Player
    super.doDie(killer);
    // Dont unsummon a summon, it can kill few enemies. But pet must returned back into its item
    // Unsummon siege summons
    if(_summon != null && (_summon.isPet() || _summon.isSiegeWeapon()))
    {
      _summon.unSummon();
    }
    // Unsummon Cubics and agathion
    if(!isBlessedByNoblesse() && !isSalvation())
    {
      if(_cubics != null)
      {
        for(L2CubicInstance cubic : _cubics)
        {
          cubic.deleteMe(false);
        }
      }
    }
    setAgathion(0);
    if(Config.LOG_KILLS)
    {
      String coords = " at (" + getX() + "," + getY() + "," + getZ() + ")";
      if(killer.isNpc())
      {
        Log.add("" + this + " karma " + _karma + " killed by mob " + killer.getNpcId() + coords, "kills");
      }
      else if(killer instanceof L2Summon && killer.getPlayer() != null)
      {
        Log.add("" + this + " karma " + _karma + " killed by summon of " + killer.getPlayer() + coords, "kills");
      }
      else
      {
        Log.add("" + this + " karma " + _karma + " killed by " + killer + coords, "kills");
      }
    }
    if(Config.ALLOW_CURSED_WEAPONS)
    {
      if(isCursedWeaponEquipped())
      {
        _pvpFlag = 0;
        CursedWeaponsManager.getInstance().dropPlayer(this);
        return;
      }
      else if(killer.isPlayer() && killer.isCursedWeaponEquipped())
      {
        _pvpFlag = 0;
        //noinspection ConstantConditions
        CursedWeaponsManager.getInstance().increaseKills(((L2Player) killer).getCursedWeaponEquippedId());
        return;
      }
    }
    doPKPVPManage(killer);
    // Set the PvP Flag of the L2Player
    _pvpFlag = 0;
    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() && isInZone(Siege) && isCharmOfCourage())
    {
      _reviveRequested = true;
      _revivePower = 100;
      sendPacket(new ConfirmDlg(SystemMessage.RESURRECTION_IS_POSSIBLE_BECAUSE_OF_THE_COURAGE_CHARM_S_EFFECT_WOULD_YOU_LIKE_TO_RESURRECT_NOW, 60000, 2));
      setCharmOfCourage(false);
    }
    if(getLevel() < 6)
    {
      Quest q = QuestManager.getQuest(255);
      if(q != null)
      {
        processQuestEvent(q.getName(), "CE30", null);
      }
    }
  }

  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, false, false);
    }
  }

  public void deathPenalty(L2Character killer)
  {
    final boolean atwar = killer.getPlayer() != null ? atWarWith(killer.getPlayer()) : false;
    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;
    byte 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(isFestivalParticipant() || 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(isInZone(Siege))
    {
      Siege siege = SiegeManager.getSiege(this, true);
      if(siege != null && siege.isParticipant(this))
      {
        lostexp = 0;
      }
      if(getTerritorySiege() > -1 && TerritorySiege.checkIfInZone(this))
      {
        lostexp = 0;
      }
      // Battlefield Death Syndrome
      GArray<L2Effect> effect = getEffectList().getEffectsBySkillId(L2Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME);
      if(effect != null)
      {
        int syndromeLvl = effect.get(0).getSkill().getLevel();
        if(syndromeLvl < 5)
        {
          getEffectList().stopEffect(L2Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME);
          L2Skill skill = SkillTable.getInstance().getInfo(L2Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME, syndromeLvl + 1);
          skill.getEffects(this, this, false, false);
        }
        else if(syndromeLvl == 5)
        {
          getEffectList().stopEffect(L2Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME);
          L2Skill skill = SkillTable.getInstance().getInfo(L2Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME, 5);
          skill.getEffects(this, this, false, false);
        }
      }
      else
      {
        L2Skill skill = SkillTable.getInstance().getInfo(L2Skill.SKILL_BATTLEFIELD_DEATH_SYNDROME, 1);
        if(skill != null)
        {
          skill.getEffects(this, this, false, false);
        }
      }
    }
    _log.fine(_name + "is dead, so exp to remove:" + lostexp);
    long before = getExp();
    addExpAndSp(-lostexp, 0, false, false);
    long lost = before - getExp();
    if(lost > 0)
    {
      setVar("lostexp", String.valueOf(lost));
    }
  }

  public void setPartyMatchingLevels(int levels)
  {
    _partyMatchingLevels = levels;
  }

  public int getPartyMatchingLevels()
  {
    return _partyMatchingLevels;
  }

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

  public int getPartyMatchingRegion()
  {
    return _partyMatchingRegion;
  }

  public Integer getPartyRoom()
  {
    return _partyRoom;
  }

  public void setPartyRoom(Integer partyRoom)
  {
    _partyRoom = partyRoom;
  }

  public void setTransaction(Transaction transaction)
  {
    _transaction = transaction;
  }

  public Transaction getTransaction()
  {
    return _transaction;
  }

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

  public GArray<L2GameServerPacket> addVisibleObject(L2Object object, L2Character dropper)
  {
    GArray<L2GameServerPacket> result = new GArray<L2GameServerPacket>();
    if(isLogoutStarted() || object == null || object.getObjectId() == getObjectId() || !object.isVisible())
    {
      return result;
    }
    if(object.isTrap() && !((L2TrapInstance) object).isDetected() && ((L2TrapInstance) object).getOwner() != this)
    {
      return result;
    }
    if(object.isPolymorphed())
    {
      switch(object.getPolytype())
      {
        case L2Object.POLY_ITEM:
          result.add(new SpawnItemPoly(object));
          showMoves(result, object);
          return result;
        case L2Object.POLY_NPC:
          result.add(new NpcInfoPoly(object));
          showMoves(result, object);
          return result;
      }
    }
    if(object.isItem())
    {
      if(dropper != null)
      {
        result.add(new DropItem((L2ItemInstance) object, dropper.getObjectId()));
      }
      else
      {
        result.add(new SpawnItem((L2ItemInstance) object));
      }
      return result;
    }
    if(object.isDoor())
    {
      result.add(new StaticObject((L2DoorInstance) object));
      return result;
    }
    if(object instanceof L2StaticObjectInstance)
    {
      result.add(new StaticObject((L2StaticObjectInstance) object));
      return result;
    }
    if(object instanceof L2ClanHallManagerInstance)
    {
      ((L2ClanHallManagerInstance) object).sendDecoInfo(this);
    }
    if(object.isNpc())
    {
      result.add(new NpcInfo((L2NpcInstance) object, this));
      showMoves(result, object);
      if(object.getAI() instanceof DefaultAI && !object.getAI().isActive())
      {
        object.getAI().startAITask();
      }
      return result;
    }
    if(object instanceof L2Summon)
    {
      L2Summon summon = (L2Summon) object;
      L2Player owner = summon.getPlayer();
      if(owner == this)
      {
        result.add(new PetInfo(summon, 2));
        result.add(new PartySpelled(summon, true));
        if(summon.isPet())
        {
          result.add(new PetItemList((L2PetInstance) summon));
        }
      }
      else
      {
        L2Party party = getParty();
        if(getReflectionId() == -2 && (owner == null || party == null || party != owner.getParty())) // Чужие петы в GH не показываются для уменьшения лагов.
        {
          return result;
        }
        result.add(new NpcInfo(summon, this, 2));
        if(owner != null && party != null && party == owner.getParty())
        {
          result.add(new PartySpelled(summon, true));
        }
        result.addAll(RelationChanged.update(this, owner, this));
      }
      showMoves(result, object);
      return result;
    }
    if(object.isPlayer())
    {
      final L2Player otherPlayer = (L2Player) object;
      if(otherPlayer.isInvisible() && getObjectId() != otherPlayer.getObjectId())
      {
        return result;
      }
      if(otherPlayer.getPrivateStoreType() != STORE_PRIVATE_NONE && getVarB("notraders"))
      {
        return result;
      }
      if(getObjectId() != otherPlayer.getObjectId())
      {
        result.add(otherPlayer.newCharInfo());
      }
      if(otherPlayer.getPrivateStoreType() != STORE_PRIVATE_NONE)
      {
        if(otherPlayer.getPrivateStoreType() == STORE_PRIVATE_BUY)
        {
          result.add(new PrivateStoreMsgBuy(otherPlayer));
        }
        else if(otherPlayer.getPrivateStoreType() == STORE_PRIVATE_SELL || otherPlayer.getPrivateStoreType() == STORE_PRIVATE_SELL_PACKAGE)
        {
          result.add(new PrivateStoreMsgSell(otherPlayer));
        }
        else if(otherPlayer.getPrivateStoreType() == STORE_PRIVATE_MANUFACTURE)
        {
          result.add(new RecipeShopMsg(otherPlayer));
        }
        if(isInZonePeace()) // Мирным торговцам не нужно посылать больше пакетов, для экономии траффика
        {
          return result;
        }
      }
      if(otherPlayer.isCastingNow())
      {
        L2Character castingTarget = otherPlayer.getCastingTarget();
        L2Skill castingSkill = otherPlayer.getCastingSkill();
        long animationEndTime = otherPlayer.getAnimationEndTime();
        if(castingSkill != null && castingTarget != null && castingTarget.isCharacter() && otherPlayer.getAnimationEndTime() > 0)
        {
          result.add(new MagicSkillUse(otherPlayer, castingTarget, castingSkill.getId(), castingSkill.getLevel(), (int) (animationEndTime - System.currentTimeMillis()), 0));
        }
      }
      result.addAll(RelationChanged.update(this, otherPlayer, this));
      if(otherPlayer.isInVehicle())
      {
        if(otherPlayer.getVehicle().isAirShip())
        {
          result.add(new ExGetOnAirShip(otherPlayer, (L2AirShip) otherPlayer.getVehicle(), otherPlayer.getInVehiclePosition()));
        }
        else
        {
          result.add(new GetOnVehicle(otherPlayer, (L2Ship) otherPlayer.getVehicle(), otherPlayer.getInVehiclePosition()));
        }
      }
      else
      {
        showMoves(result, object);
      }
      return result;
    }
    if(object.isAirShip())
    {
      L2AirShip boat = (L2AirShip) object;
      result.add(new ExAirShipInfo(boat));
      if(isInVehicle() && getVehicle() == boat)
      {
        result.add(new ExGetOnAirShip(this, boat, getInVehiclePosition()));
      }
      if(boat.isMoving)
      {
        result.add(new ExMoveToLocationAirShip(boat, boat.getLoc(), boat.getDestination()));
      }
    }
    else if(object.isShip())
    {
      L2Ship boat = (L2Ship) object;
      result.add(new VehicleInfo(boat));
      if(isInVehicle() && getVehicle() == boat)
      {
        result.add(new GetOnVehicle(this, boat, getInVehiclePosition()));
      }
      if(boat.isMoving)
      {
        result.add(new VehicleDeparture(boat));
      }
    }
    return result;
  }

  public L2GameServerPacket removeVisibleObject(L2Object object, DeleteObject packet, boolean deactivateAI)
  {
    if(isLogoutStarted() || object == null || object.getObjectId() == getObjectId()) // FIXME  || isTeleporting()
    {
      return null;
    }
    if(isInVehicle() && getVehicle() == object)
    {
      return null;
    }
    if(deactivateAI && object.isNpc())
    {
      L2NpcInstance npc = (L2NpcInstance) object;
      L2WorldRegion region = npc.getCurrentRegion();
      L2CharacterAI ai = npc.getAI();
      if(ai instanceof DefaultAI && ai.isActive() && !ai.isGlobalAI() && (region == null || region.areNeighborsEmpty()))
      {
        npc.setTarget(null);
        npc.stopMove();
        npc.getAI().stopAITask();
      }
    }
    L2GameServerPacket result = (packet == null ? new DeleteObject(object) : packet);
    //if(object.isNpc)
    //  removeFromHatelist((L2NpcInstance) object);
    getAI().notifyEvent(CtrlEvent.EVT_FORGET_OBJECT, object);
    return result;
  }

  private void showMoves(GArray<L2GameServerPacket> result, L2Object object)
  {
    if(object != null && object.isCharacter())
    {
      L2Character obj = (L2Character) object;
      if(obj.isMoving || obj.isFollow)
      {
        result.add(new CharMoveToLocation(obj));
      }
    }
  }

  private boolean increaseLevel()
  {
    if(_activeClass == null || !_activeClass.incLevel())
    {
      return false;
    }
    sendPacket(Msg.YOU_HAVE_INCREASED_YOUR_LEVEL);
    broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP));
    setCurrentHpMp(getMaxHp(), getMaxMp());
    setCurrentCp(getMaxCp());
    // Recalculate the party level
    if(isInParty())
    {
      getParty().recalculatePartyData();
    }
    if(_clan != null)
    {
      PledgeShowMemberListUpdate memberUpdate = new PledgeShowMemberListUpdate(this);
      for(L2Player clanMember : _clan.getOnlineMembers(0))
      {
        clanMember.sendPacket(memberUpdate);
      }
    }
    // Give Expertise skill of this level
    rewardSkills();
    Quest q = QuestManager.getQuest(255);
    if(q != null)
    {
      processQuestEvent(q.getName(), "CE40", null);
    }
    return true;
  }

  private boolean decreaseLevel()
  {
    if(_activeClass == null || !_activeClass.decLevel())
    {
      return false;
    }
    // Recalculate the party level
    if(isInParty())
    {
      getParty().recalculatePartyData();
    }
    if(_clan != null)
    {
      PledgeShowMemberListUpdate memberUpdate = new PledgeShowMemberListUpdate(this);
      for(L2Player clanMember : _clan.getOnlineMembers(getObjectId()))
      {
        if(!clanMember.equals(this))
        {
          clanMember.sendPacket(memberUpdate);
        }
      }
    }
    checkSkills();
    // Give Expertise skill of this level
    rewardSkills();
    return true;
  }

  /**
   * Удаляет все скиллы, которые учатся на уровне большем, чем текущий+maxDiff
   */
  public void checkSkills()
  {
    for(L2Skill sk : getAllSkillsArray())
    {
      if(SkillTreeTable.getMinSkillLevel(sk.getId(), getClassId(), sk.getLevel()) > getLevel() + Config.ALT_REMOVE_SKILLS_ON_DELEVEL)
      {
        int id = sk.getId();
        int level = sk.getLevel();
        removeSkill(sk, true);
        if(level > 1)
        {
          L2Skill skill = SkillTable.getInstance().getInfo(id, level - 1);
          addSkill(skill, true);
        }
      }
    }
  }

  public void stopAllTimers()
  {
    if(_cubics != null)
    {
      for(L2CubicInstance cubic : _cubics)
      {
        cubic.deleteMe(false);
      }
    }
    setAgathion(0);
    stopWaterTask();
    stopBonusTask();
    stopKickTask();
  }

  @Override
  public L2Summon getPet()
  {
    return _summon;
  }

  public void setPet(L2Summon summon)
  {
    _summon = summon;
    AutoShot();
    if(summon == null)
    {
      getEffectList().stopEffect(4140);
    }
  }
    public void scheduleDelete(long time) {
        if (time == 0) {
            deleteMe();
            return;
        }

        synchronized (_storeLock) {
            PlayerManager.saveCharToDisk(this);
        }

        ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() {
            public void run() {
                if (getNetConnection() == null || !getNetConnection().isConnected())
                    deleteMe();
            }
        }, time);
    }

  @Override
  public void deleteMe()
  {
    if(isLogoutStarted())
    {
      return;
    }
    setLogoutStarted(true);
    prepareToLogout();
    synchronized(_storeLock)
    {
      PlayerManager.saveCharToDisk(this);
    }
    // Останавливаем и запоминаем все квестовые таймеры
    Quest.pauseQuestTimes(this);
    super.deleteMe();
    _isDeleting = true;
    getEffectList().stopAllEffects();
    setMassUpdating(true);
    //Send friendlists to friends that this player has logged off
    EnterWorld.notifyFriends(this, false);
    if(isInTransaction())
    {
      getTransaction().cancel();
    }
    // Set the online Flag to True or False and update the characters table of the database with online status and lastAccess (called when login and logout)
    try
    {
      setOnlineStatus(false);
    }
    catch(Throwable t)
    {
      _log.log(Level.WARNING, "deletedMe()", t);
    }
    // Stop the HP/MP/CP Regeneration task (scheduled tasks)
    try
    {
      stopAllTimers();
    }
    catch(Throwable t)
    {
      _log.log(Level.WARNING, "deletedMe()", t);
    }
    // Cancel Attak or Cast
    try
    {
      setTarget(null);
    }
    catch(Throwable t)
    {
      _log.log(Level.WARNING, "deletedMe()", t);
    }
    try
    {
      if(getClanId() > 0 && _clan != null && _clan.getClanMember(getObjectId()) != null)
      {
        int sponsor = _clan.getClanMember(getObjectId()).getSponsor();
        int apprentice = getApprentice();
        PledgeShowMemberListUpdate memberUpdate = new PledgeShowMemberListUpdate(this);
        for(L2Player clanMember : _clan.getOnlineMembers(getObjectId()))
        {
          if(clanMember.getObjectId() == getObjectId())
          {
            continue;
          }
          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));
          }
        }
        _clan.getClanMember(getObjectId()).setPlayerInstance(null);
      }
    }
    catch(final Throwable t)
    {
      _log.log(Level.SEVERE, "deletedMe()", t);
    }
    try
    {
      if(isCombatFlagEquipped())
      {
        L2ItemInstance flag = getActiveWeaponInstance();
        if(flag != null)
        {
          int customFlags = flag.getCustomFlags();
          flag.setCustomFlags(0, false);
          flag = getInventory().dropItem(flag, flag.getCount(), true);
          flag.setCustomFlags(customFlags, false);
          flag.dropMe(this, getLoc().rnd(0, 100, false));
        }
      }
      if(isTerritoryFlagEquipped())
      {
        L2ItemInstance flag = getActiveWeaponInstance();
        if(flag != null && flag.getCustomType1() != 77) // 77 это эвентовый флаг
        {
          L2TerritoryFlagInstance flagNpc = TerritorySiege.getNpcFlagByItemId(flag.getItemId());
          flagNpc.drop(this);
        }
      }
      /* TODO
      for(L2ItemInstance item : getInventory().getItemsList())
      if((item.getCustomFlags() & L2ItemInstance.FLAG_ALWAYS_DROP_ON_DIE) == L2ItemInstance.FLAG_DROP_ON_DISCONNECT)
      {
      item = getInventory().dropItem(item, item.getCount());
      item.dropMe(this, getLoc().rnd(0, 100, false));
      }
       */
    }
    catch(Throwable t)
    {
      _log.log(Level.WARNING, "deletedMe()", t);
    }
    if(CursedWeaponsManager.getInstance().getCursedWeapon(getCursedWeaponEquippedId()) != null)
    {
      CursedWeaponsManager.getInstance().getCursedWeapon(getCursedWeaponEquippedId()).setPlayer(null);
    }
    if(getPartyRoom() > 0)
    {
      PartyRoom room = PartyRoomManager.getInstance().getRooms().get(getPartyRoom());
      if(room != null)
      {
        if(room.getLeader() == null || room.getLeader().equals(this))
        {
          PartyRoomManager.getInstance().removeRoom(room.getId());
        }
        else
        {
          room.removeMember(this, false);
        }
      }
    }
    setPartyRoom(0);
    setEffectList(null);
    // Update database with items in its inventory and remove them from the world
    try
    {
      getInventory().deleteMe();
    }
    catch(Throwable t)
    {
      _log.log(Level.WARNING, "deletedMe()", t);
    }
    destroyAllTraps();
    if(_decoy != null)
    {
      _decoy.unSummon();
    }
    stopPvPFlag();
    bookmarks.clear();
    _warehouse = null;
    _freight = null;
    _ai = null;
    _summon = null;
    _arrowItem = null;
    _fistsWeaponItem = null;
    _chars = null;
    _enchantScroll = null;
    _agathion = null;
    _lastNpc = null;
    _obsLoc = null;
    _observNeighbor = null;
  }

  public void setTradeList(final L2TradeList x)
  {
    _tradeList = x;
  }

  public L2TradeList getTradeList()
  {
    return _tradeList;
  }

  public void setSellList(final ConcurrentLinkedQueue<TradeItem> x)
  {
    _sellList = x;
    saveTradeList();
  }

  public ConcurrentLinkedQueue<TradeItem> getSellList()
  {
    return _sellList != null ? _sellList : new ConcurrentLinkedQueue<TradeItem>();
  }

  public L2ManufactureList getCreateList()
  {
    return _createList;
  }

  public void setCreateList(final L2ManufactureList x)
  {
    _createList = x;
    saveTradeList();
  }

  public void setBuyList(final ConcurrentLinkedQueue<TradeItem> x)
  {
    _buyList = x;
    saveTradeList();
  }

  public ConcurrentLinkedQueue<TradeItem> getBuyList()
  {
    return _buyList != null ? _buyList : new ConcurrentLinkedQueue<TradeItem>();
  }

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

  public short getPrivateStoreType()
  {
    if(inObserverMode())
    {
      return STORE_OBSERVING_GAMES;
    }
    return _privatestore;
  }

  public void setSkillLearningClassId(final ClassId classId)
  {
    _skillLearningClassId = classId;
  }

  public ClassId getSkillLearningClassId()
  {
    return _skillLearningClassId;
  }

  /**
   * Set the _clan object, _clanId, _clanLeader Flag and title of the L2Player.<BR><BR>
   *
   * @param clan the clat to set
   */
  public void setClan(L2Clan clan)
  {
    if(_clan != clan && _clan != null)
    {
      unsetVar("canWhWithdraw");
    }
    L2Clan oldClan = _clan;
    if(oldClan != null && clan == null)
    {
      for(L2Skill skill : oldClan.getAllSkills())
      {
        removeSkill(skill, false);
      }
    }
    _clan = clan;
    if(clan == null)
    {
      _pledgeType = 0;
      _pledgeClass = 0;
      _powerGrade = 0;
      _apprentice = 0;
      getInventory().checkAllConditions();
      return;
    }
    if(!clan.isMember(getObjectId()))
    {
      // char has been kicked from clan
      _log.fine("Char " + _name + " is kicked from clan: " + clan.getName());
      setClan(null);
      setTitle("");
      return;
    }
    setTitle("");
  }

  public L2Clan getClan()
  {
    return _clan;
  }

  public ClanHall getClanHall()
  {
    return ClanHallManager.getInstance().getClanHallByOwner(_clan);
  }

  public Castle getCastle()
  {
    return CastleManager.getInstance().getCastleByOwner(_clan);
  }

  public Fortress getFortress()
  {
    return FortressManager.getInstance().getFortressByOwner(_clan);
  }

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

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

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

  @Override
  public void reduceArrowCount()
  {
    sendPacket(Msg.YOU_CAREFULLY_NOCK_AN_ARROW);
    L2ItemInstance arrows = getInventory().destroyItem(getInventory().getPaperdollObjectId(Inventory.PAPERDOLL_LHAND), 1, false);
    if(arrows == null || arrows.getCount() == 0)
    {
      getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
      _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)
    {
      // Get the L2ItemInstance of the arrows needed for this bow
      if(getActiveWeaponItem().getItemType() == WeaponType.BOW)
      {
        _arrowItem = getInventory().findArrowForBow(getActiveWeaponItem());
      }
      else if(getActiveWeaponItem().getItemType() == WeaponType.CROSSBOW)
      {
        getInventory().findArrowForCrossbow(getActiveWeaponItem());
      }
      // 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 void setUptime(final long time)
  {
    _uptime = time;
  }

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

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

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

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

  public void leaveParty()
  {
    if(isInParty())
    {
      _party.oustPartyMember(this);
      _party = null;
    }
  }

  public L2Party getParty()
  {
    return _party;
  }

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

  /**
   * Нигде не используется, но может пригодиться для БД
   */
  public void setAccessLevel(final int level)
  {
    _accessLevel = level;
  }

  /**
   * Нигде не используется, но может пригодиться для БД
   */
  @Override
  public int getAccessLevel()
  {
    return _accessLevel;
  }

  public void setPlayerAccess(final PlayerAccess pa)
  {
    if(pa != null)
    {
      _playerAccess = pa;
    }
    else
    {
      _playerAccess = new PlayerAccess();
    }
    setAccessLevel(isGM() || _playerAccess.Menu ? 100 : 0);
  }

  public PlayerAccess getPlayerAccess()
  {
    return _playerAccess;
  }

  public void setAccountAccesslevel(final int level, final String comments, int banTime)
  {
    LSConnection.getInstance().sendPacket(new ChangeAccessLevel(getAccountName(), level, comments, banTime));
  }

  @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 L2Player in its _KnownPlayers (broadcast).<BR><BR>
   */
  @Override
  public void updateStats()
  {
    refreshOverloaded();
    refreshExpertisePenalty();
    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, StatusUpdate.KARMA);
    if(flagChanged)
    {
      broadcastRelationChanged();
    }
  }

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

  public void updateOnlineStatus()
  {
    boolean online = isOnline();
    if(isInOfflineMode())
    {
      online = false;
    }
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE obj_id=?");
      statement.setInt(1, online ? 1 : 0);
      statement.setLong(2, System.currentTimeMillis() / 1000);
      statement.setInt(3, getObjectId());
      statement.execute();
    }
    catch(final Exception e)
    {
      _log.warning("could not set char online status:" + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(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)
  {
    boolean flagChanged = _karma == 0;
    long new_karma = _karma + add_karma;
    if(new_karma > Integer.MAX_VALUE)
    {
      new_karma = Integer.MAX_VALUE;
    }
    if(_karma == 0 && new_karma > 0)
    {
      _karma = (int) new_karma;
      for(final L2Character cha : L2World.getAroundCharacters(this))
      {
        if(cha instanceof L2GuardInstance && cha.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
        {
          cha.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null, null);
        }
      }
    }
    else
    {
      _karma = (int) new_karma;
    }
    updateKarma(flagChanged);
  }

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

  /**
   * 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 L2Player create(int classId, byte sex, String accountName, final String name, final byte hairStyle, final byte hairColor, final byte face)
  {
    L2PlayerTemplate template = CharTemplateTable.getInstance().getTemplate(classId, sex != 0);
    // Create a new L2Player with an account name
    L2Player player = new L2Player(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(!PlayerManager.createDb(player))
    {
      return null;
    }
    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 L2Player restore(final int objectId)
  {
    L2Player player = null;
    ThreadConnection con = null;
    FiltredStatement statement = null;
    FiltredStatement statement2 = null;
    ResultSet pl_rset = null;
    ResultSet ps_rset = null;
    try
    {
      // Retrieve the L2Player from the characters table of the database
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.createStatement();
      statement2 = con.createStatement();
      pl_rset = statement.executeQuery("SELECT * FROM `characters` WHERE `obj_Id`=" + objectId + " LIMIT 1");
      ps_rset = statement2.executeQuery("SELECT `class_id` FROM `character_subclasses` WHERE `char_obj_id`=" + objectId + " AND `isBase`=1 LIMIT 1");
      if(pl_rset.next() && ps_rset.next())
      {
        final int classId = ps_rset.getInt("class_id");
        final boolean female = pl_rset.getInt("sex") == 1;
        final L2PlayerTemplate template = CharTemplateTable.getInstance().getTemplate(classId, female);
        player = new L2Player(objectId, template);
        player.loadVariables();
        player.bookmarks.setCapacity(pl_rset.getInt("bookmarks"));
        player.setBaseClass(classId);
        player._accountName = pl_rset.getString("account_name");
        player.setName(pl_rset.getString("char_name"));
        player.setFace(pl_rset.getByte("face"));
        player.setHairStyle(pl_rset.getByte("hairStyle"));
        player.setHairColor(pl_rset.getByte("hairColor"));
        player.setHeading(pl_rset.getInt("heading"));
        player.setKarma(pl_rset.getInt("karma"));
        player.setPvpKills(pl_rset.getInt("pvpkills"));
        player.setPkKills(pl_rset.getInt("pkkills"));
        player.setLeaveClanTime(pl_rset.getLong("leaveclan") * 1000L);
        if(player.getLeaveClanTime() > 0 && player.canJoinClan())
        {
          player.setLeaveClanTime(0);
        }
        player.setDeleteClanTime(pl_rset.getLong("deleteclan") * 1000L);
        if(player.getDeleteClanTime() > 0 && player.canCreateClan())
        {
          player.setDeleteClanTime(0);
        }
        player.setNoChannel(pl_rset.getLong("nochannel") * 1000L);
        if(player.getNoChannel() > 0 && player.getNoChannelRemained() < 0)
        {
          player.updateNoChannel(0);
        }
        player.setOnlineTime(pl_rset.getLong("onlinetime") * 1000L);
        final int clanId = pl_rset.getInt("clanid");
        if(clanId > 0)
        {
          player.setClan(ClanTable.getInstance().getClan(clanId));
          player.setPledgeType(pl_rset.getInt("pledge_type"));
          player.setPowerGrade(pl_rset.getInt("pledge_rank"));
          player.setLvlJoinedAcademy(pl_rset.getInt("lvl_joined_academy"));
          player.setApprentice(pl_rset.getInt("apprentice"));
        }
        player.setCreateTime(pl_rset.getLong("createtime") * 1000L);
        player.setDeleteTimer(pl_rset.getInt("deletetime"));
        player.setTitle(pl_rset.getString("title"));
        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);
        }
        String recomList = player.getVar("recomChars");
        if(recomList != null && !recomList.isEmpty())
        {
          for(String recom : recomList.split(","))
          {
            if(!recom.isEmpty())
            {
              player._recomChars.add(Integer.decode("0x" + recom));
            }
          }
        }
        player.setFistsWeaponItem(player.findFistsWeaponItem(classId));
        player.setUptime(System.currentTimeMillis());
        player.setLastAccess(pl_rset.getLong("lastAccess"));
        player.setRecomHave(pl_rset.getInt("rec_have"));
        player.setRecomLeft(pl_rset.getInt("rec_left"));
        player.setKeyBindings(pl_rset.getBytes("key_bindings"));
        player.setPcBangPoints(pl_rset.getInt("pcBangPoints"));
        player.setFame(pl_rset.getInt("fame"), null);
        player.restoreRecipeBook();
        if(Config.ENABLE_OLYMPIAD)
        {
          player.setHero(Hero.getInstance().isHero(player.getObjectId()));
          player.setNoble(Olympiad.isNoble(player.getObjectId()));
        }
        player.updatePledgeClass();
        player.updateKetraVarka();
        player.updateRam();
        // для сервиса виверн - возврат денег если сервер упал во время полета
        String wm = player.getVar("wyvern_moneyback");
        if(wm != null && Integer.parseInt(wm) > 0)
        {
          player.addAdena(Integer.parseInt(wm));
        }
        player.unsetVar("wyvern_moneyback");
        long reflection = 0;
        // Set the x,y,z position of the L2Player and make it invisible
        if(player.getVar("jailed") != null && System.currentTimeMillis() / 1000 < Integer.parseInt(player.getVar("jailed")) + 60)
        {
          player.setXYZInvisible(-114648, -249384, -2984);
          String[] re = player.getVar("jailedFrom").split(";");
          Location loc = new Location(Integer.parseInt(re[0]), Integer.parseInt(re[1]), Integer.parseInt(re[2]));
          reflection = -3;
          player._unjailTask = ThreadPoolManager.getInstance().scheduleGeneral(new TeleportTask(player, loc, re.length > 3 ? Integer.parseInt(re[3]) : 0), Integer.parseInt(player.getVar("jailed")) * 1000L);
        }
        else
        {
          player.setXYZInvisible(pl_rset.getInt("x"), pl_rset.getInt("y"), pl_rset.getInt("z"));
          wm = player.getVar("reflection");
          if(wm != null)
          {
            reflection = Long.parseLong(wm);
            if(reflection > 0)
            {
              if(!Config.RepairPlayerToInstance || ReflectionTable.getInstance().get(reflection) == null)
              {
                String back = player.getVar("backCoords");
                if(back != null)
                {
                  player.setXYZInvisible(new Location(back));
                  player.unsetVar("backCoords");
                }
                reflection = 0;
              }
            }
          }
        }
        player.setReflection(reflection);
        player.restoreTradeList();
        if(player.getVar("storemode") != null)
        {
          if(player.getVar("offline") != null) // оффтрейдеры выбивают других, онтрейдеры нет
          {
            if(Config.SERVICES_TRADE_ONLY_FAR)
            {
              L2WorldRegion currentRegion = L2World.getRegion(player.getLoc(), player.getReflectionId());
              if(currentRegion != null)
              {
                GArray<L2WorldRegion> neighbors = currentRegion.getNeighbors();
                int size = 0;
                for(L2WorldRegion region : neighbors)
                {
                  size += region.getPlayersSize();
                }
                GArray<L2Player> result = new GArray<L2Player>(size);
                for(L2WorldRegion region : neighbors)
                {
                  region.getPlayersList(result, 0, player.getReflection(), player.getX(), player.getY(), player.getZ(), Config.SERVICES_TRADE_RADIUS * Config.SERVICES_TRADE_RADIUS, 200);
                }
                for(L2Player p : result)
                {
                  if(p.isInStoreMode())
                  {
                    if(p.isInOfflineMode())
                    {
                      L2TradeList.cancelStore(p);
                    }
                    else
                    {
                      p.setPrivateStoreType(L2Player.STORE_PRIVATE_NONE);
                      p.broadcastUserInfo(true);
                    }
                  }
                }
              }
            }
            player.setPrivateStoreType(Short.parseShort(player.getVar("storemode")));
            player.setSitting(true);
          }
          else
          {
            short type = Short.parseShort(player.getVar("storemode"));
            if(player.checksForShop(type == STORE_PRIVATE_MANUFACTURE))
            {
              player.setPrivateStoreType(type);
              player.setSitting(true);
            }
            else
            {
              player.unsetVar("storemode");
            }
          }
        }
        if(TerritorySiege.isInProgress())
        {
          player.setTerritorySiege(TerritorySiege.getTerritoryForPlayer(objectId));
        }
        Quest.playerEnter(player);
        player._hidden = true;
        restoreCharSubClasses(player);
        player._hidden = false;
        // 2 очка в минуту оффлайна, на оффе 4, но там очки вдвое легче
        player.setVitality(pl_rset.getInt("vitality") + (int) ((System.currentTimeMillis() / 1000 - pl_rset.getLong("lastAccess")) / 30.));
        // 15 секунд после входа в игру на персонажа не агрятся мобы
        player.setNonAggroTime(System.currentTimeMillis() + 15000);
        try
        {
          String var = player.getVar("ExpandInventory");
          if(var != null)
          {
            player.setExpandInventory(Integer.parseInt(var));
          }
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }
        try
        {
          String var = player.getVar("ExpandWarehouse");
          if(var != null)
          {
            player.setExpandWarehouse(Integer.parseInt(var));
          }
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }
        try
        {
          String var = player.getVar("notShowBuffAnim");
          if(var != null)
          {
            player.setNotShowBuffAnim(Boolean.parseBoolean(var));
          }
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }
        FiltredPreparedStatement stmt = null;
        ResultSet chars = null;
        try
        {
          stmt = con.prepareStatement("SELECT obj_Id, char_name FROM characters WHERE account_name=? AND obj_Id!=?");
          stmt.setString(1, player._accountName);
          stmt.setInt(2, objectId);
          chars = stmt.executeQuery();
          while(chars.next())
          {
            final Integer charId = chars.getInt("obj_Id");
            final String charName = chars.getString("char_name");
            player._chars.put(charId, charName);
          }
        }
        catch(Exception e)
        {
          e.printStackTrace();
        }
        finally
        {
          DatabaseUtils.closeDatabaseSR(stmt, chars);
        }
        if(Config.KILL_COUNTER)
        {
          // Restore kills stat
          FiltredStatement stt = null;
          ResultSet rstkills = null;
          try
          {
            stt = con.createStatement();
            rstkills = stt.executeQuery("SELECT `npc_id`, `count` FROM `killcount` WHERE `char_id`=" + objectId);
            player._StatKills = new HashMap<Integer, Long>(128);
            while(rstkills.next())
            {
              player._StatKills.put(rstkills.getInt("npc_id"), rstkills.getLong("count"));
            }
          }
          catch(Exception e)
          {
            e.printStackTrace();
          }
          finally
          {
            DatabaseUtils.closeDatabaseSR(stt, rstkills);
          }
        }
        //Restore craft stat
        if(Config.CRAFT_COUNTER)
        {
          FiltredStatement stcraft = null;
          ResultSet rstcraft = null;
          try
          {
            stcraft = con.createStatement();
            rstcraft = stcraft.executeQuery("SELECT `item_id`, `count` FROM `craftcount` WHERE `char_id`=" + objectId);
            player._StatCraft = new HashMap<Integer, Long>(32);
            while(rstcraft.next())
            {
              player._StatCraft.put(rstcraft.getInt("item_id"), rstcraft.getLong("count"));
            }
          }
          catch(Exception e)
          {
            e.printStackTrace();
          }
          finally
          {
            DatabaseUtils.closeDatabaseSR(stcraft, rstcraft);
          }
        }
        if(Config.DROP_COUNTER)
        {
          //Restore drop stat
          FiltredStatement stdrop = null;
          ResultSet rstdrop = null;
          try
          {
            stdrop = con.createStatement();
            rstdrop = stdrop.executeQuery("SELECT `item_id`, `count` FROM `dropcount` WHERE `char_id`=" + objectId);
            player._StatDrop = new HashMap<Integer, Long>(128);
            while(rstdrop.next())
            {
              player._StatDrop.put(rstdrop.getInt("item_id"), rstdrop.getLong("count"));
            }
          }
          catch(Exception e)
          {
            e.printStackTrace();
          }
          finally
          {
            DatabaseUtils.closeDatabaseSR(stdrop, rstdrop);
          }
        }
        if(!L2World.validCoords(player.getX(), player.getY()) || player.getX() == 0 && player.getY() == 0)
        {
          player.setXYZInvisible(MapRegion.getTeleToClosestTown(player));
        }
        // Перед началом работы с территориями, выполним их обновление
        player.updateTerritories();
        if(!player.isGM())
        {
          if(Config.ENABLE_OLYMPIAD && player.isInZone(ZoneType.OlympiadStadia))
          {
            player.sendMessage(new CustomMessage("l2p.gameserver.clientpackets.EnterWorld.TeleportedReasonOlympiad", player));
            player.setXYZInvisible(MapRegion.getTeleToClosestTown(player));
          }
          L2Zone noRestartZone = ZoneManager.getInstance().getZoneByTypeAndObject(no_restart, player);
          if(noRestartZone != null && System.currentTimeMillis() / 1000 - player.getLastAccess() > noRestartZone.getRestartTime())
          {
            player.sendMessage(new CustomMessage("l2p.gameserver.clientpackets.EnterWorld.TeleportedReasonNoRestart", player));
            player.setXYZInvisible(MapRegion.getTeleToClosestTown(player));
          }
          if(player.isInZone(Siege))
          {
            Siege siege = SiegeManager.getSiege(player, true);
            if(siege != null && !siege.checkIsDefender(player.getClan()))
            {
              if(siege.getHeadquarter(player.getClan()) == null)
              {
                player.setXYZInvisible(MapRegion.getTeleToClosestTown(player));
              }
              else
              {
                player.setXYZInvisible(MapRegion.getTeleToHeadquarter(player));
              }
            }
            if(TerritorySiege.checkIfInZone(player))
            {
              if(TerritorySiege.getHeadquarter(player.getClan()) == null)
              {
                player.setXYZInvisible(MapRegion.getTeleToClosestTown(player));
              }
              else
              {
                player.setXYZInvisible(MapRegion.getTeleToHeadquarter(player));
              }
            }
          }
          if(DimensionalRiftManager.getInstance().checkIfInRiftZone(player.getLoc(), false))
          {
            player.setXYZInvisible(DimensionalRiftManager.getInstance().getRoom(0, 0).getTeleportCoords());
          }
        }
        player.getInventory().validateItems();
        player.revalidatePenalties();
        player.restoreBlockList();
        BreakWarnManager.getInstance().addWarnTask(player);
        AutoSaveManager.getInstance().addPlayerTask(player);
      }
    }
    catch(final Exception e)
    {
      _log.log(Level.WARNING, "restore: could not restore char data:", e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseSR(statement2, ps_rset);
      DatabaseUtils.closeDatabaseCSR(con, statement, pl_rset);
    }
    return player;
  }
  public Future<?> _unjailTask;

  public void incrementKillsCounter(final Integer Id)
  {
    final Long tmp = _StatKills.containsKey(Id) ? _StatKills.get(Id) + 1 : 1;
    _StatKills.put(Id, tmp);
    sendMessage(new CustomMessage("l2p.gameserver.model.L2Player.KillsCounter", this).addString(tmp.toString()));
  }

  public void incrementDropCounter(final Integer Id, final Long qty)
  {
    _StatDrop.put(Id, _StatDrop.containsKey(Id) ? _StatDrop.get(Id) + qty : qty);
  }

  public void incrementCraftCounter(final Integer Id, final int qty)
  {
    final Long tmp = _StatCraft.containsKey(Id) ? _StatCraft.get(Id) + qty : qty;
    _StatCraft.put(Id, tmp);
    sendMessage(new CustomMessage("l2p.gameserver.model.L2Player.CraftCounter", this).addString(tmp.toString()));
  }
  private final Object _storeLock = new Object();

  /**
   * Update L2Player stats in the characters table of the database.
   */
  public void store(boolean fast)
  {
    synchronized(_storeLock)
    {
      ThreadConnection con = null;
      FiltredPreparedStatement statement = null;
      FiltredStatement fs;
      try
      {
        con = L2DatabaseFactory.getInstance().getConnection();
        statement = con.prepareStatement(//
        "UPDATE characters SET face=?,hairStyle=?,hairColor=?,heading=?,x=?,y=?,z=?" + //
        ",karma=?,pvpkills=?,pkkills=?,rec_have=?,rec_left=?,clanid=?,deletetime=?," + //
        "title=?,accesslevel=?,online=?,leaveclan=?,deleteclan=?,nochannel=?," + //
        "onlinetime=?,pledge_type=?,pledge_rank=?,lvl_joined_academy=?,apprentice=?,key_bindings=?,pcBangPoints=?,char_name=?,vitality=?,fame=?,bookmarks=? WHERE obj_Id=? LIMIT 1");
        statement.setInt(1, getFace());
        statement.setInt(2, getHairStyle());
        statement.setInt(3, getHairColor());
        statement.setInt(4, getHeading() & 0xFFFF);
        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, getClanId());
        statement.setInt(14, getDeleteTimer());
        statement.setString(15, _title);
        statement.setInt(16, _accessLevel);
        statement.setInt(17, isOnline() ? 1 : 0);
        statement.setLong(18, getLeaveClanTime() / 1000);
        statement.setLong(19, getDeleteClanTime() / 1000);
        statement.setLong(20, _NoChannel > 0 ? getNoChannelRemained() / 1000 : _NoChannel);
        statement.setLong(21, _onlineBeginTime > 0 ? (_onlineTime + System.currentTimeMillis() - _onlineBeginTime) / 1000 : _onlineTime / 1000);
        statement.setInt(22, getPledgeType());
        statement.setInt(23, getPowerGrade());
        statement.setInt(24, getLvlJoinedAcademy());
        statement.setInt(25, getApprentice());
        statement.setBytes(26, getKeyBindings());
        statement.setInt(27, getPcBangPoints());
        statement.setString(28, getName());
        statement.setInt(29, (int) getVitality());
        statement.setInt(30, getFame());
        statement.setInt(31, bookmarks.getCapacity());
        statement.setInt(32, getObjectId());
        statement.executeUpdate();
        Stat.increaseUpdatePlayerBase();
        try
        {
          if(!fast && Config.KILL_COUNTER && _StatKills != null)
          {
            TextBuilder sb = TextBuilder.newInstance();
            fs = con.createStatement();
            for(Entry<Integer, Long> tmp : _StatKills.entrySet())
            {
              fs.addBatch(sb.append("REPLACE DELAYED INTO `killcount` SET `npc_id`=").append(tmp.getKey()).append(", `count`=").append(tmp.getValue()).append(", `char_id`=").append(_objectId).toString());
              sb.clear();
            }
            TextBuilder.recycle(sb);
            fs.executeBatch();
            DatabaseUtils.closeStatement(fs);
          }
          if(!fast && Config.CRAFT_COUNTER && _StatCraft != null)
          {
            TextBuilder sb = TextBuilder.newInstance();
            fs = con.createStatement();
            for(Entry<Integer, Long> tmp : _StatCraft.entrySet())
            {
              fs.addBatch(sb.append("REPLACE DELAYED INTO `craftcount` SET `item_id`=").append(tmp.getKey()).append(", `count`=").append(tmp.getValue()).append(", `char_id`=").append(_objectId).toString());
              sb.clear();
            }
            TextBuilder.recycle(sb);
            fs.executeBatch();
            DatabaseUtils.closeStatement(fs);
          }
          if(!fast && Config.DROP_COUNTER && _StatDrop != null)
          {
            TextBuilder sb = TextBuilder.newInstance();
            fs = con.createStatement();
            for(Entry<Integer, Long> tmp : _StatDrop.entrySet())
            {
              fs.addBatch(sb.append("REPLACE DELAYED INTO `craftcount` SET `item_id`=").append(tmp.getKey()).append(", `count`=").append(tmp.getValue()).append(", `char_id`=").append(_objectId).toString());
              sb.clear();
            }
            TextBuilder.recycle(sb);
            fs.executeBatch();
            DatabaseUtils.closeStatement(fs);
          }
        }
        catch(ConcurrentModificationException e)
        {
        }
        if(!fast)
        {
          storeEffects();
          storeDisableSkills();
          storeBlockList();
        }
        storeCharSubClasses();
        bookmarks.store();
      }
      catch(Exception e)
      {
        _log.warning("store: could not store char data: " + e);
        e.printStackTrace();
      }
      finally
      {
        DatabaseUtils.closeDatabaseCS(con, statement);
      }
    }
  }

  public boolean isOnline()
  {
    return _isOnline;
  }

  /**
   * 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 L2Skill addSkill(final L2Skill newSkill, final boolean store)
  {
    if(newSkill == null)
    {
      return null;
    }
    // Add a skill to the L2Player _skills and its Func objects to the calculator set of the L2Player
    L2Skill 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 L2Skill removeSkill(L2Skill 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 L2Skill removeSkill(int id, boolean fromDB)
  {
    // Remove a skill from the L2Character and its Func objects from calculator set of the L2Character
    L2Skill oldSkill = super.removeSkillById(id);
    if(!fromDB)
    {
      return oldSkill;
    }
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      // Remove or update a L2Player skill from the character_skills table of the database
      con = L2DatabaseFactory.getInstance().getConnection();
      if(oldSkill != null)
      {
        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.log(Level.WARNING, "Error could not delete Skill:", e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
    return oldSkill;
  }

  /**
   * Add or update a L2Player skill in the character_skills table of the database.
   */
  private void storeSkill(final L2Skill newSkill, final L2Skill oldSkill)
  {
    if(newSkill == null) // вообще-то невозможно
    {
      _log.warning("could not store new skill. its NULL");
      return;
    }
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.prepareStatement("REPLACE INTO character_skills (char_obj_id,skill_id,skill_level,skill_name,class_index) values(?,?,?,?,?)");
      statement.setInt(1, getObjectId());
      statement.setInt(2, newSkill.getId());
      statement.setInt(3, newSkill.getLevel());
      statement.setString(4, newSkill.getName());
      statement.setInt(5, getActiveClassId());
      statement.execute();
    }
    catch(final Exception e)
    {
      _log.log(Level.WARNING, "Error could not store Skills:", e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  /**
   * Retrieve from the database all skills of this L2Player and add them to _skills.
   */
  private void restoreSkills()
  {
    ThreadConnection con = null;
    FiltredPreparedStatement 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 = L2DatabaseFactory.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");
        if(id > 9000)
        {
          continue;
        } // fake skills for base stats
        // Create a L2Skill object for each record
        final L2Skill skill = SkillTable.getInstance().getInfo(id, level);
        if(skill == null)
        {
          continue;
        }
        // Remove skill if not possible
        if(!isGM() && !skill.isCommon() && !SkillTreeTable.getInstance().isSkillPossible(this, skill.getId(), skill.getLevel()))
        {
          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);
          }
          illegalAction("has skill " + skill.getName() + " / ReturnSP: " + ReturnSP, 0);
          continue;
        }
        // Add the L2Skill object to the L2Character _skills and its Func objects to the calculator set of the L2Character
        super.addSkill(skill);
      }
      // Restore noble skills
      if(isNoble())
      {
        updateNobleSkills();
      }
      // Restore Hero skills at main class only
      if(_hero && getBaseClassId() == getActiveClassId())
      {
        Hero.addSkills(this);
      }
      if(_clan != null)
      {
        // Restore clan leader siege skills
        if(_clan.getLeaderId() == getObjectId() && _clan.getLevel() >= CastleSiegeManager.getSiegeClanMinLevel())
        {
          SiegeManager.addSiegeSkills(this);
        }
        // Restore clan skills
        _clan.addAndShowSkillsToPlayer(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.warning("Could not restore skills for player objId: " + getObjectId());
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rset);
    }
  }

  public void deleteSubclassSkills()
  {
    try
    {
      for(L2SubClass subClass : getSubClasses().values())
      {
        if(!subClass.getSkills().isEmpty())
        {
          for(String i : subClass.getSkills().split(";"))
          {
            super.removeSkillById(Integer.parseInt(i));
          }
        }
      }
    }
    catch(final Exception e)
    {
      _log.warning("Could not delete subclass skills for player objId: " + getObjectId());
      e.printStackTrace();
    }
  }

  public void restoreSubclassSkills()
  {
    if(!getActiveClass().isBase())
    {
      return;
    }
    try
    {
      for(L2SubClass subClass : getSubClasses().values())
      {
        if(!subClass.getSkills().isEmpty())
        {
          for(String i : subClass.getSkills().split(";"))
          {
            int id = Integer.parseInt(i);
            int level = Math.max(1, getSkillLevel(id) + 1);
            L2Skill skill = SkillTable.getInstance().getInfo(id, level);
            if(skill != null)
            {
              super.addSkill(skill);
            }
            else
            {
              System.out.println("Not found skill id: " + id + ", level: " + level);
            }
          }
        }
      }
    }
    catch(final Exception e)
    {
      _log.warning("Could not restore subclass skills for player objId: " + getObjectId());
      e.printStackTrace();
    }
  }

  public void storeDisableSkills()
  {
    ThreadConnection con = null;
    FiltredStatement statement = null;
    try
    {
      con = L2DatabaseFactory.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(skillReuseTimeStamps.isEmpty())
      {
        return;
      }
      SqlBatch b = new SqlBatch("REPLACE INTO `character_skills_save` (`char_obj_id`,`skill_id`,`class_index`,`end_time`,`reuse_delay_org`) VALUES");
      synchronized(skillReuseTimeStamps)
      {
        StringBuilder sb;
        for(Entry<Integer, SkillTimeStamp> tmp : getSkillReuseTimeStamps().entrySet())
        {
          if(tmp.getValue().hasNotPassed())
          {
            sb = new StringBuilder("(");
            sb.append(getObjectId()).append(",");
            sb.append(tmp.getKey()).append(",");
            sb.append(getActiveClassId()).append(",");
            sb.append(tmp.getValue().getEndTime()).append(",");
            sb.append(tmp.getValue().getReuseBasic()).append(")");
            b.write(sb.toString());
          }
        }
      }
      if(!b.isEmpty())
      {
        statement.executeUpdate(b.close());
      }
    }
    catch(final Exception e)
    {
      _log.warning("Could not store disable skills data: " + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  public void storeEffects()
  {
    ThreadConnection con = null;
    FiltredStatement statement = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.createStatement();
      statement.executeUpdate("DELETE FROM character_effects_save WHERE char_obj_id = " + getObjectId() + " AND class_index=" + getActiveClassId());
      if(_effectList == null || _effectList.isEmpty())
      {
        return;
      }
      int order = 0;
      SqlBatch b = new SqlBatch("INSERT IGNORE INTO `character_effects_save` (`char_obj_id`,`skill_id`,`skill_level`,`effect_count`,`effect_cur_time`,`duration`,`order`,`class_index`) VALUES");
      synchronized(getEffectList())
      {
        StringBuilder sb;
        for(L2Effect effect : getEffectList().getAllEffects())
        {
          if(effect != null && effect.isInUse() && !effect.getSkill().isToggle() && effect.getEffectType() != EffectType.HealOverTime && effect.getEffectType() != EffectType.CombatPointHealOverTime)
          {
            if(effect.isSaveable())
            {
              sb = new StringBuilder("(");
              sb.append(getObjectId()).append(",");
              sb.append(effect.getSkill().getId()).append(",");
              sb.append(effect.getSkill().getLevel()).append(",");
              sb.append(effect.getCount()).append(",");
              sb.append(effect.getTime()).append(",");
              sb.append(effect.getPeriod()).append(",");
              sb.append(order).append(",");
              sb.append(getActiveClassId()).append(")");
              b.write(sb.toString());
            }
            while((effect = effect.getNext()) != null && effect.isSaveable())
            {
              sb = new StringBuilder("(");
              sb.append(getObjectId()).append(",");
              sb.append(effect.getSkill().getId()).append(",");
              sb.append(effect.getSkill().getLevel()).append(",");
              sb.append(effect.getCount()).append(",");
              sb.append(effect.getTime()).append(",");
              sb.append(effect.getPeriod()).append(",");
              sb.append(order).append(",");
              sb.append(getActiveClassId()).append(")");
              b.write(sb.toString());
            }
            order++;
          }
        }
        if(Config.ALT_SAVE_UNSAVEABLE && _cubics != null)
        {
          for(L2CubicInstance cubic : _cubics)
          {
            sb = new StringBuilder("(");
            sb.append(getObjectId()).append(",");
            sb.append(cubic.getId() + L2CubicInstance.CUBIC_STORE_OFFSET).append(",");
            sb.append(cubic.getLevel()).append(",1,");
            sb.append(cubic.lifeLeft()).append(",1,");
            sb.append(order++).append(",");
            sb.append(getActiveClassId()).append(")");
            b.write(sb.toString());
          }
        }
      }
      if(!b.isEmpty())
      {
        statement.executeUpdate(b.close());
      }
    }
    catch(final Exception e)
    {
      _log.warning("Could not store active effects data: " + e);
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  public void restoreEffects()
  {
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    ResultSet rset = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.prepareStatement("SELECT `skill_id`,`skill_level`,`effect_count`,`effect_cur_time`,`duration` FROM `character_effects_save` WHERE `char_obj_id`=? AND `class_index`=? ORDER BY `order` ASC");
      statement.setInt(1, getObjectId());
      statement.setInt(2, getActiveClassId());
      rset = statement.executeQuery();
      while(rset.next())
      {
        int skillId = rset.getInt("skill_id");
        int skillLvl = rset.getInt("skill_level");
        int effectCount = rset.getInt("effect_count");
        long effectCurTime = rset.getLong("effect_cur_time");
        long duration = rset.getLong("duration");
        if(skillId >= L2CubicInstance.CUBIC_STORE_OFFSET) // cubic
        {
          skillId -= L2CubicInstance.CUBIC_STORE_OFFSET;
          addCubic(skillId, skillLvl, (int) effectCurTime, true);
          continue;
        }
        L2Skill skill = SkillTable.getInstance().getInfo(skillId, skillLvl);
        if(skill == null)
        {
          _log.warning("Can't restore Effect\tskill: " + skillId + ":" + skillLvl + " " + toFullString());
          Thread.dumpStack();
          continue;
        }
        if(skill.getEffectTemplates() == null)
        {
          _log.warning("Can't restore Effect, EffectTemplates is NULL\tskill: " + skillId + ":" + skillLvl + " " + toFullString());
          Thread.dumpStack();
          continue;
        }
        for(EffectTemplate et : skill.getEffectTemplates())
        {
          if(et == null)
          {
            continue;
          }
          Env env = new Env(this, this, skill);
          L2Effect effect = et.getEffect(env);
          if(effect == null)
          {
            continue;
          }
          if(effectCount == 1)
          {
            effect.setCount(effectCount);
            effect.setPeriod(duration - effectCurTime);
          }
          else
          {
            effect.setPeriod(duration);
            effect.setCount(effectCount);
          }
          getEffectList().addEffect(effect);
        }
      }
    }
    catch(final Exception e)
    {
      _log.warning("Could not restore active effects data [charId: " + getObjectId() + "; ActiveClassId: " + getActiveClassId() + "]: " + e);
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rset);
    }
    updateEffectIcons();
    broadcastUserInfo(true);
  }

  public void restoreDisableSkills()
  {
    ThreadConnection con = null;
    FiltredStatement statement = null;
    ResultSet rset = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.createStatement();
      rset = statement.executeQuery("SELECT skill_id,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 = Math.max(getSkillLevel(skillId), 1);
        long endTime = rset.getLong("end_time");
        long rDelayOrg = rset.getLong("reuse_delay_org");
        long curTime = System.currentTimeMillis();
        L2Skill skill = SkillTable.getInstance().getInfo(skillId, skillLevel);
        if(skill != null && endTime - curTime > 500)
        {
          getSkillReuseTimeStamps().put(skillId, new SkillTimeStamp(skillId, endTime, rDelayOrg));
          disableItem(skill, rDelayOrg, endTime - curTime);
        }
      }
      DatabaseUtils.closeStatement(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.warning("Could not restore active skills data for " + getObjectId() + "/" + getActiveClassId());
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rset);
      updateEffectIcons();
    }
  }

  @Override
  public void disableItem(L2Skill handler, long timeTotal, long timeLeft)
  {
    if(handler.isHandler() && timeLeft > 1000)
    {
      if(handler.getReuseGroupId() > 0)
      {
        GArray<Integer> disabled = new GArray<Integer>();
        for(Integer skill_id : handler.getReuseGroup())
        {
          // TODO: хранить отключенные группы отдельно от скиллов во избежание коллизий и для отсылки при изменении ярлыков
          for(L2Skill sk : SkillTable.getInstance().getAllLevels(skill_id))
          {
            if(sk != null && sk._itemConsumeId[0] != 0 && !disabled.contains(sk._itemConsumeId[0]))
            {
              sendPacket(new ExUseSharedGroupItem(sk._itemConsumeId[0], sk.getReuseGroupId(), (int) timeLeft, (int) timeTotal));
              disabled.add(sk._itemConsumeId[0]);
            }
          }
          if(!isSkillDisabled(skill_id))
          {
            disableSkill(skill_id, timeLeft);
          }
        }
      }
      else
      {
        sendPacket(new ExUseSharedGroupItem(handler._itemConsumeId[0], handler._itemConsumeId[0], (int) timeTotal, (int) timeLeft));
      }
    }
  }

  /**
   * Retrieve from the database all Henna of this L2Player, add them to _henna and calculate stats of the L2Player.<BR><BR>
   */
  private void restoreHenna()
  {
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    ResultSet rset = null;
    try
    {
      con = L2DatabaseFactory.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");
        L2HennaInstance sym;
        if(symbol_id != 0)
        {
          final L2Henna tpl = HennaTable.getInstance().getTemplate(symbol_id);
          if(tpl != null)
          {
            sym = new L2HennaInstance(tpl);
            _henna[slot - 1] = sym;
          }
        }
      }
    }
    catch(final Exception e)
    {
      _log.warning("could not restore henna: " + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rset);
    }
    // Calculate Henna modifiers of this L2Player
    recalcHennaStats();
  }

  public int getHennaEmptySlots()
  {
    int totalSlots = 1 + getClassId().level();
    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 L2HennaInstance henna = _henna[slot];
    final short dyeID = henna.getItemIdDye();
    // Added by Tempy - 10 Aug 05
    // Gives amount equal to half of the dyes needed for the henna back.
    final L2ItemInstance hennaDyes = ItemTable.getInstance().createItem(dyeID);
    hennaDyes.setCount(henna.getAmountDyeRequire() / 2);
    _henna[slot] = null;
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      con = L2DatabaseFactory.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.warning("could not remove char henna: " + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
    // 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(false);
    // Add the recovered dyes to the player's inventory and notify them.
    getInventory().addItem(hennaDyes);
    sendPacket(SystemMessage.obtainItems(henna.getItemIdDye(), henna.getAmountDyeRequire() / 2, 0));
    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 L2HennaInstance для добавления
   */
  public boolean addHenna(L2HennaInstance henna)
  {
    if(getHennaEmptySlots() == 0)
    {
      sendPacket(Msg.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();
        ThreadConnection con = null;
        FiltredPreparedStatement statement = null;
        try
        {
          con = L2DatabaseFactory.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.warning("could not save char henna: " + e);
        }
        finally
        {
          DatabaseUtils.closeDatabaseCS(con, statement);
        }
        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++)
    {
      if(_henna[i] == null)
      {
        continue;
      }
      _hennaINT += _henna[i].getStatINT();
      _hennaSTR += _henna[i].getStatSTR();
      _hennaMEN += _henna[i].getStatMEM();
      _hennaCON += _henna[i].getStatCON();
      _hennaWIT += _henna[i].getStatWIT();
      _hennaDEX += _henna[i].getStatDEX();
    }
    if(_hennaINT > 5)
    {
      _hennaINT = 5;
    }
    if(_hennaSTR > 5)
    {
      _hennaSTR = 5;
    }
    if(_hennaMEN > 5)
    {
      _hennaMEN = 5;
    }
    if(_hennaCON > 5)
    {
      _hennaCON = 5;
    }
    if(_hennaWIT > 5)
    {
      _hennaWIT = 5;
    }
    if(_hennaDEX > 5)
    {
      _hennaDEX = 5;
    }
  }

  /**
   * @param slot id слота у перса
   * @return the Henna of this L2Player corresponding to the selected slot.<BR><BR>
   */
  public L2HennaInstance 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(final int itemConsumeId, final int itemCount)
  {
    L2ItemInstance item = getInventory().getItemByItemId(itemConsumeId);
    if(item == null || item.getCount() < itemCount)
    {
      return false;
    }
    if(getInventory().destroyItem(item, itemCount, false) != null)
    {
      sendPacket(SystemMessage.removeItems(itemConsumeId, itemCount));
      return true;
    }
    return false;
  }

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

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

  /**
   * Проверяет, можно ли приземлиться в этой зоне.
   *
   * @return можно ли приземлится
   */
  public boolean checkLandingState()
  {
    if(isInZone(no_landing))
    {
      return false;
    }
    Siege siege = SiegeManager.getSiege(this, false);
    if(siege != null)
    {
      Residence unit = siege.getSiegeUnit();
      if(unit != null && getClan() != null && isClanLeader() && (getClan().getHasCastle() == unit.getId() || getClan().getHasFortress() == unit.getId()))
      {
        return true;
      }
      return false;
    }
    return true;
  }

  public void unMount()
  {
    setMount(0, 0, 0);
  }

  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(L2Skill.SKILL_STRIDER_ASSAULT);
        removeSkillById(L2Skill.SKILL_WYVERN_BREATH);
        getEffectList().stopEffect(L2Skill.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:
        setRiding(true);
        if(isNoble())
        {
          addSkill(SkillTable.getInstance().getInfo(L2Skill.SKILL_STRIDER_ASSAULT, 1), false);
        }
        break;
      case PetDataTable.WYVERN_ID:
        setFlying(true);
        setLoc(getLoc().changeZ(32));
        addSkill(SkillTable.getInstance().getInfo(L2Skill.SKILL_WYVERN_BREATH, 1), false);
        break;
      case PetDataTable.WGREAT_WOLF_ID:
      case PetDataTable.FENRIR_WOLF_ID:
      case PetDataTable.WFENRIR_WOLF_ID:
      case PetDataTable.LIGHT_PURPLE_MANED_HORSE_ID:
        setRiding(true);
        break;
      case PetDataTable.TAWNY_MANED_LION_ID:
        setRiding(true);
        break;
      case PetDataTable.STEAM_BEATLE_ID:
        setRiding(true);
        break;
      case PetDataTable.AURA_BIRD_FALCON_ID:
        setLoc(getLoc().changeZ(32));
        setFlying(true);
        setTransformation(8);
        break;
      case PetDataTable.AURA_BIRD_OWL_ID:
        setLoc(getLoc().changeZ(32));
        setFlying(true);
        setTransformation(9);
        break;
    }
    if(npcId > 0)
    {
      unEquipWeapon();
    }
    _mountNpcId = npcId;
    _mountObjId = obj_id;
    _mountLevel = level;
    broadcastUserInfo(true); // нужно послать пакет перед Ride для корректного снятия оружия с заточкой
    broadcastPacket(new Ride(this));
    broadcastUserInfo(true); // нужно послать пакет после Ride для корректного отображения скорости
    sendPacket(new SkillList(this));
  }

  public void unEquipWeapon()
  {
    L2ItemInstance wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
    if(wpn != null)
    {
      sendDisarmMessage(wpn);
    }
    getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
    wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
    if(wpn != null)
    {
      sendDisarmMessage(wpn);
    }
    getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_RHAND);
    refreshExpertisePenalty();
    abortAttack(true, true);
    abortCast(true);
  }
  /*
  @Override
  public float getMovementSpeedMultiplier()
  {
  int template_speed = _template.baseRunSpd;
  if(isMounted())
  {
  L2PetData petData = PetDataTable.getInstance().getInfo(_mountNpcId, _mountLevel);
  if(petData != null)
  template_speed = petData.getSpeed();
  }
  return getRunSpeed() * 1f / template_speed;
  }
   */

  @Override
  public int getSpeed(int baseSpeed)
  {
    if(isMounted())
    {
      L2PetData 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);
  }
  private int _mountNpcId;
  private int _mountObjId;
  private int _mountLevel;

  public int getMountNpcId()
  {
    return _mountNpcId;
  }

  public int getMountObjId()
  {
    return _mountObjId;
  }

  public int getMountLevel()
  {
    return _mountLevel;
  }

  public void sendDisarmMessage(L2ItemInstance 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);
    }
  }

  /**
   * Send a Server->Client packet UserInfo to this L2Player and CharInfo to all L2Player in its _KnownPlayers.
   */
  @Override
  public void updateAbnormalEffect()
  {
    sendChanges();
  }

  /**
   * Disable the Inventory and create a new task to enable it after 1.5s.
   */
  public void tempInventoryDisable()
  {
    _inventoryDisable = true;
    ThreadPoolManager.getInstance().scheduleAi(new InventoryEnableTask(this), 1500, true);
  }

  /**
   * @return True if the Inventory is disabled.<BR><BR>
   */
  public boolean isInventoryDisabled()
  {
    return _inventoryDisable;
  }

  /**
   * Устанавливает тип используемого склада.
   *
   * @param type тип склада:<BR>
   *             <ul>
   *             <li>WarehouseType.PRIVATE
   *             <li>WarehouseType.CLAN
   *             <li>WarehouseType.CASTLE
   *             <li>WarehouseType.FREIGHT
   *             </ul>
   */
  public void setUsingWarehouseType(final WarehouseType type)
  {
    _usingWHType = type;
  }

  /**
   * Возвращает тип используемого склада.
   *
   * @return null или тип склада:<br>
   *         <ul>
   *         <li>WarehouseType.PRIVATE
   *         <li>WarehouseType.CLAN
   *         <li>WarehouseType.CASTLE
   *         <li>WarehouseType.FREIGHT
   *         </ul>
   */
  public WarehouseType getUsingWarehouseType()
  {
    return _usingWHType;
  }

  public GCSArray<L2CubicInstance> getCubics()
  {
    return _cubics == null ? new GCSArray<L2CubicInstance>(0) : _cubics;
  }

  public void addCubic(int id, int level, int lifetime, boolean givenByOther)
  {
    if(_cubics != null)
    {
      for(L2CubicInstance old : _cubics)
      {
        if(old.getId() == id)
        {
          old.deleteMe(false);
        }
      }
    }
    if(_cubics == null)
    {
      _cubics = new GCSArray<L2CubicInstance>(4);
    }
    int mastery = Math.max(0, getSkillLevel(L2Skill.SKILL_CUBIC_MASTERY));
    if(_cubics.size() > mastery)
    {
      sendPacket(Msg.CUBIC_SUMMONING_FAILED);
      return;
    }
    _cubics.add(new L2CubicInstance(this, id, level, lifetime, givenByOther));
  }

  public void delCubic(L2CubicInstance cubic)
  {
    if(_cubics != null)
    {
      _cubics.remove(cubic);
    }
  }

  public L2CubicInstance getCubic(int id)
  {
    if(_cubics != null)
    {
      for(L2CubicInstance cubic : _cubics)
      {
        if(cubic.getId() == id)
        {
          return cubic;
        }
      }
    }
    return null;
  }

  @Override
  public String toString()
  {
    return "player '" + getName() + "'";
  }

  /**
   * @return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127).<BR><BR>
   */
  public int getEnchantEffect()
  {
    final L2ItemInstance wpn = getActiveWeaponInstance();
    if(wpn == null)
    {
      return 0;
    }
    return Math.min(127, wpn.getEnchantLevel());
  }

  /**
   * Set the _lastFolkNpc of the L2Player corresponding to the last Folk witch one the player talked.<BR><BR>
   */
  public void setLastNpc(final L2NpcInstance npc)
  {
    _lastNpc = npc;
  }

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

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

  public MultiSellListContainer getMultisell()
  {
    return _multisell;
  }

  /**
   * @return True if L2Player is a participant in the Festival of Darkness.<BR><BR>
   */
  public boolean isFestivalParticipant()
  {
    return getReflection() instanceof DarknessFestival;
  }

  @Override
  public boolean unChargeShots(boolean spirit)
  {
    L2ItemInstance weapon = getActiveWeaponInstance();
    if(weapon == null)
    {
      return false;
    }
    if(spirit)
    {
      weapon.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
    }
    else
    {
      weapon.setChargedSoulshot(L2ItemInstance.CHARGED_NONE);
    }
    AutoShot();
    return true;
  }

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

  public void AutoShot()
  {
    synchronized(_activeSoulShots)
    {
      for(Integer e : _activeSoulShots)
      {
        if(e == null)
        {
          continue;
        }
        L2ItemInstance item = getInventory().getItemByItemId(e);
        if(item == null)
        {
          _activeSoulShots.remove(e);
          continue;
        }
        IItemHandler handler = ItemHandler.getInstance().getItemHandler(e);
        if(handler == null)
        {
          continue;
        }
        handler.useItem(this, item, false);
      }
    }
  }

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

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

  @Override
  public int getChargedSpiritShot()
  {
    L2ItemInstance 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 ConcurrentSkipListSet<Integer> getAutoSoulShot()
  {
    return _activeSoulShots;
  }

  public void setInvisible(boolean vis)
  {
    _invisible = vis;
  }

  @Override
  public boolean isInvisible()
  {
    return _invisible;
  }

  @Override
  public boolean isInvul()
  {
    return super.isInvul();
  }

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

  public boolean enterObserverMode(Location loc)
  {
    _observNeighbor = L2World.getRegion(loc, 0);
    if(_observNeighbor == null)
    {
      return false;
    }
    setTarget(null);
    stopMove();
    sitDown();
    block();
    _observerMode = 1;
    // Отображаем надпись над головой
    broadcastCharInfo();
    // Переходим в режим обсервинга
    sendPacket(new ObserverStart(loc));
    return true;
  }

  public void appearObserverMode()
  {
    L2WorldRegion observNeighbor = _observNeighbor;
    L2WorldRegion currentRegion = getCurrentRegion();
    if(observNeighbor == null || currentRegion == null)
    {
      if(getOlympiadObserveId() == -1)
      {
        leaveObserverMode();
      }
      else
      {
        leaveOlympiadObserverMode();
      }
      return;
    }
    _observerMode = 3;
    // Очищаем все видимые обьекты
    for(L2WorldRegion neighbor : currentRegion.getNeighbors())
    {
      neighbor.removeObjectsFromPlayer(this);
    }
    // Добавляем фэйк в точку наблюдения
    if(!_observNeighbor.equals(currentRegion))
    {
      _observNeighbor.addObject(this);
    }
    // Показываем чару все обьекты, что находятся в точке наблюдения и соседних регионах
    for(L2WorldRegion neighbor : _observNeighbor.getNeighbors())
    {
      neighbor.showObjectsToPlayer(this);
    }
    if(getOlympiadObserveId() > -1)
    {
      OlympiadGame game = Olympiad.getOlympiadGame(getOlympiadObserveId());
      if(game != null)
      {
        game.broadcastInfo(null, this, true);
      }
    }
  }

  public void returnFromObserverMode()
  {
    _observerMode = 0;
    _observNeighbor = null;
    _olympiadObserveId = -1;
    L2WorldRegion currentRegion = getCurrentRegion();
    // Показываем чару все обьекты, что находятся в точке воврата и соседних регионах
    if(currentRegion != null)
    {
      for(L2WorldRegion neighbor : currentRegion.getNeighbors())
      {
        neighbor.showObjectsToPlayer(this);
      }
    }
    broadcastUserInfo(true);
  }

  public void leaveObserverMode()
  {
    L2WorldRegion observNeighbor = _observNeighbor;
    // Удаляем фэйк из точки наблюдения и удаляем у чара все обьекты, что там находятся
    if(observNeighbor != null)
    {
      for(L2WorldRegion neighbor : observNeighbor.getNeighbors())
      {
        neighbor.removeObjectsFromPlayer(this);
        neighbor.removeObject(this, false);
      }
    }
    // Нужно при телепорте с более высокой точки на более низкую, иначе наносится вред от "падения"
    setLastClientPosition(null);
    setLastServerPosition(null);
    _observNeighbor = null;
    _observerMode = 2;
    setTarget(null);
    unblock();
    standUp();
    // Выходим из режима обсервинга
    sendPacket(new ObserverEnd(this));
  }

  public void enterOlympiadObserverMode(Location loc, int id)
  {
    _observNeighbor = L2World.getRegion(loc, 0);
    if(_observNeighbor == null)
    {
      return;
    }
    setTarget(null);
    stopMove();
    block();
    _olympiadObserveId = id;
    _observerMode = 1;
    // Отображаем надпись над головой
    broadcastCharInfo();
    // Меняем интерфейс
    sendPacket(new ExOlympiadMode(3));
    // Нужно при телепорте с более высокой точки на более низкую, иначе наносится вред от "падения"
    setLastClientPosition(null);
    setLastServerPosition(null);
    // "Телепортируемся"
    sendPacket(new TeleportToLocation(this, loc));
  }

  public void switchOlympiadObserverArena(int id)
  {
    L2WorldRegion observNeighbor = _observNeighbor;
    // Удаляем фэйк из точки наблюдения и удаляем у чара все обьекты, что там находятся
    if(observNeighbor != null)
    {
      for(L2WorldRegion neighbor : observNeighbor.getNeighbors())
      {
        neighbor.removeObjectsFromPlayer(this);
        neighbor.removeObject(this, false);
      }
    }
    int oldId = _olympiadObserveId;
    _observNeighbor = null;
    _observerMode = 0;
    _olympiadObserveId = -1;
    setTarget(null);
    stopMove();
    unblock();
    // Меняем интерфейс
    sendPacket(new ExOlympiadMode(0));
    sendPacket(new ExOlympiadMatchEnd());
    Olympiad.removeSpectator(oldId, this);
    Olympiad.addSpectator(id, this);
  }

  public void leaveOlympiadObserverMode()
  {
    L2WorldRegion observNeighbor = _observNeighbor;
    // Удаляем фэйк из точки наблюдения и удаляем у чара все обьекты, что там находятся
    if(observNeighbor != null)
    {
      for(L2WorldRegion neighbor : observNeighbor.getNeighbors())
      {
        neighbor.removeObjectsFromPlayer(this);
        neighbor.removeObject(this, false);
      }
    }
    _observNeighbor = null;
    _observerMode = 2;
    setTarget(null);
    unblock();
    Olympiad.removeSpectator(_olympiadObserveId, this);
    _olympiadObserveId = -1;
    // Меняем интерфейс
    sendPacket(new ExOlympiadMode(0));
    sendPacket(new ExOlympiadMatchEnd());
    // Нужно при телепорте с более высокой точки на более низкую, иначе наносится вред от "падения"
    setLastClientPosition(null);
    setLastServerPosition(null);
    // "Телепортируемся"
    sendPacket(new TeleportToLocation(this, getLoc()));
  }

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

  public int getOlympiadSide()
  {
    return _olympiadSide;
  }

  public void setOlympiadGameId(final int id)
  {
    _olympiadGameId = id;
  }

  public int getOlympiadGameId()
  {
    return _olympiadGameId;
  }

  public int getOlympiadObserveId()
  {
    return _olympiadObserveId;
  }

  public Location getObsLoc()
  {
    return _obsLoc;
  }

  @Override
  public boolean inObserverMode()
  {
    return _observerMode > 0;
  }

  public byte getObserverMode()
  {
    return _observerMode;
  }

  public void setObserverMode(byte mode)
  {
    _observerMode = mode;
  }

  public L2WorldRegion getObservNeighbor()
  {
    return _observNeighbor;
  }

  public void setObservNeighbor(L2WorldRegion region)
  {
    _observNeighbor = 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;
    sendPacket(new EtcStatusUpdate(this));
  }

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

  public boolean getTradeRefusal()
  {
    return _tradeRefusal;
  }

  public void setExchangeRefusal(final boolean mode)
  {
    _exchangeRefusal = mode;
  }

  public boolean getExchangeRefusal()
  {
    return _exchangeRefusal;
  }

  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;
    }
    L2Player block_target = L2World.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 = Util.GetCharIDbyName(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)));
    L2Player block_target = L2ObjectsStorage.getPlayer(charId);
    if(block_target != null)
    {
      block_target.sendMessage(getName() + " has removed you from his/her Ignore List.");
    } //В системных(619 == 620) мессагах ошибка ;)
  }

  public boolean isInBlockList(final L2Player 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();
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    ResultSet rs = null;
    try
    {
      con = L2DatabaseFactory.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())
      {
        _blockList.put(rs.getInt("target_Id"), rs.getString("char_name"));
      }
    }
    catch(SQLException e)
    {
      _log.warning("Can't restore player blocklist " + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rs);
    }
  }

  private void storeBlockList()
  {
    ThreadConnection con = null;
    FiltredStatement statement = null;
    try
    {
      con = L2DatabaseFactory.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.warning("Can't store player blocklist " + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  public boolean isBlockAll()
  {
    return _blockAll;
  }

  public void setBlockAll(final boolean state)
  {
    _blockAll = state;
    sendPacket(new EtcStatusUpdate(this));
  }

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

  public void setConnected(boolean connected)
  {
    _isConnected = connected;
  }

  public boolean isConnected()
  {
    return _isConnected;
  }

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

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

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

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

  public boolean isOlympiadGameStart()
  {
    int id = _olympiadGameId;
    if(id < 0)
    {
      return false;
    }
    OlympiadGame og = Olympiad.getOlympiadGame(id);
    return og != null && og.getState() == 1;
  }

  public boolean isOlympiadCompStart()
  {
    int id = _olympiadGameId;
    if(id < 0)
    {
      return false;
    }
    OlympiadGame og = Olympiad.getOlympiadGame(id);
    return og != null && og.getState() == 2;
  }

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

  public void setNoble(boolean noble)
  {
    _noble = noble;
  }

  public boolean isNoble()
  {
    return _noble;
  }

  public int getSubLevel()
  {
    return isSubClassActive() ? getLevel() : 0;
  }
  /* varka silenos and ketra orc quests related functions */

  public void updateKetraVarka()
  {
    if(Functions.getItemCount(this, 7215) > 0)
    {
      _ketra = 5;
    }
    else if(Functions.getItemCount(this, 7214) > 0)
    {
      _ketra = 4;
    }
    else if(Functions.getItemCount(this, 7213) > 0)
    {
      _ketra = 3;
    }
    else if(Functions.getItemCount(this, 7212) > 0)
    {
      _ketra = 2;
    }
    else if(Functions.getItemCount(this, 7211) > 0)
    {
      _ketra = 1;
    }
    else if(Functions.getItemCount(this, 7225) > 0)
    {
      _varka = 5;
    }
    else if(Functions.getItemCount(this, 7224) > 0)
    {
      _varka = 4;
    }
    else if(Functions.getItemCount(this, 7223) > 0)
    {
      _varka = 3;
    }
    else if(Functions.getItemCount(this, 7222) > 0)
    {
      _varka = 2;
    }
    else if(Functions.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(Functions.getItemCount(this, 7247) > 0)
    {
      _ram = 2;
    }
    else if(Functions.getItemCount(this, 7246) > 0)
    {
      _ram = 1;
    }
    else
    {
      _ram = 0;
    }
  }

  public int getRam()
  {
    return _ram;
  }

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

  public int getPledgeType()
  {
    return _pledgeType;
  }

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

  public int getLvlJoinedAcademy()
  {
    return _lvlJoinedAcademy;
  }

  public void setPledgeClass(final int classId)
  {
    _pledgeClass = classId;
  }

  public int getPledgeClass()
  {
    return _pledgeClass;
  }

  public void updatePledgeClass()
  {
    byte 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;
    boolean IS_KNIGHT_COMMANDER = false;
    if(_clan != null && _pledgeType == 0)
    {
      int leaderOf = _clan.getClanMember(_objectId).isSubLeader();
      if(_clan.isRoyalGuard(leaderOf))
      {
        IS_GUARD_CAPTAIN = true;
      }
      else if(_clan.isOrderOfKnights(leaderOf))
      {
        IS_KNIGHT_COMMANDER = true;
      }
    }
    switch(CLAN_LEVEL)
    {
      case -1:
        _pledgeClass = RANK_VAGABOND;
        break;
      case 0:
      case 1:
      case 2:
      case 3:
        if(isClanLeader())
        {
          _pledgeClass = RANK_HEIR;
        }
        else
        {
          _pledgeClass = RANK_VASSAL;
        }
        break;
      case 4:
        if(isClanLeader())
        {
          _pledgeClass = RANK_KNIGHT;
        }
        else
        {
          _pledgeClass = RANK_HEIR;
        }
        break;
      case 5:
        if(isClanLeader())
        {
          _pledgeClass = RANK_WISEMAN;
        }
        else if(IN_ACADEMY)
        {
          _pledgeClass = RANK_VASSAL;
        }
        else
        {
          _pledgeClass = RANK_HEIR;
        }
        break;
      case 6:
        if(isClanLeader())
        {
          _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(isClanLeader())
        {
          _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(isClanLeader())
        {
          _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(isClanLeader())
        {
          _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(isClanLeader())
        {
          _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(isClanLeader())
        {
          _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 void setPowerGrade(final int grade)
  {
    _powerGrade = grade;
  }

  public int getPowerGrade()
  {
    return _powerGrade;
  }

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

  public int getApprentice()
  {
    return _apprentice;
  }

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

  public void setTeam(final int team, boolean checksForTeam)
  {
    _checksForTeam = checksForTeam;
    if(_team != team)
    {
      _team = team;
      broadcastUserInfo(true);
      if(getPet() != null)
      {
        getPet().broadcastPetInfo();
      }
    }
  }

  @Override
  public int getTeam()
  {
    return _team;
  }

  public boolean isChecksForTeam()
  {
    return _checksForTeam;
  }

  public int getNameColor()
  {
    if(inObserverMode())
    {
      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));
    }
    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));
    }
    else
    {
      unsetVar("namecolor");
    }
  }

  public final void illegalAction(final String msg, final Integer jail_items)
  {
    Log.IllegalPlayerAction(this, msg, jail_items);
  }

  public final String toFullString()
  {
    final StringBuffer sb = new StringBuffer(160);
    sb.append("Player '").append(getName()).append("' [oid=").append(_objectId).append(", account='").append(getAccountName()).append(", ip=").append(getIP()).append("']");
    return sb.toString();
  }
  private final FastMap<String, String> user_variables = new FastMap<String, String>().setShared(true);

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

  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", _objectId, 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 FastMap<String, String> getVars()
  {
    return user_variables;
  }

  private void loadVariables()
  {
    ThreadConnection con = null;
    FiltredPreparedStatement offline = null;
    ResultSet rs = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      offline = con.prepareStatement("SELECT * FROM character_variables WHERE obj_id = ?");
      offline.setInt(1, _objectId);
      rs = offline.executeQuery();
      while(rs.next())
      {
        String name = rs.getString("name");
        String value = Strings.stripSlashes(rs.getString("value"));
        user_variables.put(name, value);
      }
      // TODO Здесь обязятельно выставлять все стандартные параметры, иначе будут NPE
      if(getVar("lang@") == null)
      {
        setVar("lang@", Config.DEFAULT_LANG);
      }
    }
    catch(Exception e)
    {
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(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 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(2, 0));
      sendChanges();
    }
  }

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

  public void checkWaterState()
  {
    if(isInZoneWater())
    {
      startWaterTask();
    }
    else
    {
      stopWaterTask();
    }
  }
  private boolean _reviveRequested = false;
  private double _revivePower = 0;
  private boolean _revivePet = false;

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

  @Override
  public void doRevive()
  {
    super.doRevive();
    unsetVar("lostexp");
    updateEffectIcons();
    AutoShot();
    _reviveRequested = false;
    _revivePower = 0;
  }

  public void reviveRequest(L2Player Reviver, double percent, boolean Pet)
  {
    if(_reviveRequested)
    {
      if(_revivePet == Pet && _revivePower >= percent)
      {
        Reviver.sendPacket(Msg.BETTER_RESURRECTION_HAS_BEEN_ALREADY_PROPOSED);
        return;
      }
      if(Pet && !_revivePet)
      {
        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 && getPet() != null && getPet().isDead() || !Pet && isDead())
    {
      _reviveRequested = true;
      _revivePower = percent;
      _revivePet = Pet;
      ConfirmDlg pkt = new ConfirmDlg(SystemMessage.S1_IS_MAKING_AN_ATTEMPT_AT_RESURRECTION_WITH_$S2_EXPERIENCE_POINTS_DO_YOU_WANT_TO_CONTINUE_WITH_THIS_RESURRECTION, 0, 2);
      pkt.addString(Reviver.getName()).addString(Math.round(_revivePower) + " percent");
      sendPacket(pkt);
    }
  }

  public void reviveAnswer(int answer)
  {
    if(!_reviveRequested || !isDead() && !_revivePet || _revivePet && getPet() != null && !getPet().isDead())
    {
      return;
    }
    if(answer == 1)
    {
      if(!_revivePet)
      {
        doRevive(_revivePower);
      }
      else if(getPet() != null)
      {
        ((L2PetInstance) getPet()).doRevive(_revivePower);
      }
    }
    _reviveRequested = false;
    _revivePower = 0;
  }
  /**
   * Координаты точки призыва персонажа
   */
  private Location _SummonCharacterCoords;
  /**
   * Флаг необходимости потребления Summoning Cystall-а при призыве персонажа
   */
  private int _SummonConsumeCrystall = 0;

  /**
   * Обработчик ответа клиента на призыв персонажа.
   *
   * @param answer Идентификатор запроса
   */
  public void summonCharacterAnswer(int answer)
  {
    int summoningCrystallId = 8615;
    if(answer == 1 && _SummonCharacterCoords != null)
    {
      abortAttack(true, true);
      abortCast(true);
      stopMove();
      if(_SummonConsumeCrystall > 0)
      {
        L2ItemInstance ConsumedItem = getInventory().getItemByItemId(summoningCrystallId);
        if(ConsumedItem != null && ConsumedItem.getCount() >= _SummonConsumeCrystall)
        {
          getInventory().destroyItemByItemId(summoningCrystallId, _SummonConsumeCrystall, false);
          sendPacket(SystemMessage.removeItems(summoningCrystallId, _SummonConsumeCrystall));
          teleToLocation(_SummonCharacterCoords);
        }
        else
        {
          sendPacket(Msg.INCORRECT_ITEM_COUNT);
        }
      }
      else
      {
        teleToLocation(_SummonCharacterCoords);
      }
    }
    _SummonCharacterCoords = null;
  }

  /**
   * Отправляет запрос клиенту на призыв персонажа.
   *
   * @param SummonerName Имя призывающего персонажа
   * @param coords       Координаты точки призыва персонажа
   */
  public void summonCharacterRequest(String SummonerName, Location loc, int SummonConsumeCrystall)
  {
    if(_SummonCharacterCoords == null)
    {
      _SummonConsumeCrystall = SummonConsumeCrystall;
      _SummonCharacterCoords = loc;
      ConfirmDlg cd = new ConfirmDlg(SystemMessage.S1_WISHES_TO_SUMMON_YOU_FROM_S2_DO_YOU_ACCEPT, 60000, 1);
      cd.addString(SummonerName).addZoneName(_SummonCharacterCoords);
      sendPacket(cd);
    }
  }
  String _scriptName = "";
  Object[] _scriptArgs = new Object[0];

  public void scriptAnswer(int answer)
  {
    if(answer == 1 && !_scriptName.equals(""))
    {
      callScripts(_scriptName.split(":")[0], _scriptName.split(":")[1], _scriptArgs);
    }
    _scriptName = "";
  }

  public void scriptRequest(String text, String scriptName, Object[] args)
  {
    if(_scriptName.equals(""))
    {
      _scriptName = scriptName;
      _scriptArgs = args;
      sendPacket(new ConfirmDlg(SystemMessage.S1, 30000, 3).addString(text));
    }
  }

  public boolean isReviveRequested()
  {
    return _reviveRequested;
  }

  public boolean isRevivingPet()
  {
    return _revivePet;
  }

  public void updateNoChannel(final long time)
  {
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    setNoChannel(time);
    try
    {
      con = L2DatabaseFactory.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.warning("Could not activate nochannel:" + e);
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  private void checkRecom()
  {
    Calendar temp = Calendar.getInstance();
    temp.set(Calendar.HOUR_OF_DAY, 13);
    temp.set(Calendar.MINUTE, 0);
    temp.set(Calendar.SECOND, 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++)
    {
      if(_recomHave < 200)
      {
        _recomHave -= 2;
      }
      else
      {
        _recomHave -= 3;
      }
    }
    if(_recomHave < 0)
    {
      _recomHave = 0;
    }
    if(getLevel() < 10)
    {
      return;
    }
    if(count > 0)
    {
      restartRecom();
    }
  }

  public void restartRecom()
  {
    try
    {
      _recomChars.clear();
      if(getLevel() < 20)
      {
        _recomLeft = 3;
      }
      else if(getLevel() < 40)
      {
        _recomLeft = 6;
      }
      else
      {
        _recomLeft = 9;
      }
      if(_recomHave < 200)
      {
        _recomHave -= 2;
      }
      else
      {
        _recomHave -= 3;
      }
      if(_recomHave < 0)
      {
        _recomHave = 0;
      }
    }
    catch(final Exception e)
    {
      e.printStackTrace();
    }
  }

  @Override
  public boolean isInVehicle()
  {
    return _vehicle != null;
  }

  public L2Vehicle getVehicle()
  {
    return _vehicle;
  }

  public void setVehicle(L2Vehicle boat)
  {
    _vehicle = boat;
  }

  public Location getInVehiclePosition()
  {
    return _inVehiclePosition;
  }

  public void setInVehiclePosition(Location loc)
  {
    _inVehiclePosition = loc;
  }

  public HashMap<Integer, L2SubClass> getSubClasses()
  {
    return _classlist;
  }

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

  public int getBaseClassId()
  {
    return _baseClass;
  }

  public void setActiveClass(L2SubClass activeClass)
  {
    if(activeClass == null)
    {
      System.out.print("WARNING! setActiveClass(null);");
      Thread.dumpStack();
    }
    _activeClass = activeClass;
  }

  public L2SubClass getActiveClass()
  {
    return _activeClass;
  }

  public int getActiveClassId()
  {
    return getActiveClass().getClassId();
  }

  /**
   * Changing index of class in DB, used for changing class when finished professional quests
   *
   * @param oldclass
   * @param newclass
   */
  public synchronized void changeClassInDb(final int oldclass, final int newclass)
  {
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      con = L2DatabaseFactory.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();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(statement);
      statement = con.prepareStatement("DELETE FROM character_shortcuts WHERE char_obj_id=? AND class_index=?");
      statement.setInt(1, getObjectId());
      statement.setInt(2, newclass);
      statement.executeUpdate();
      DatabaseUtils.closeStatement(statement);
      statement = con.prepareStatement("UPDATE character_shortcuts SET class_index=? WHERE char_obj_id=? AND class_index=?");
      statement.setInt(1, newclass);
      statement.setInt(2, getObjectId());
      statement.setInt(3, oldclass);
      statement.executeUpdate();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(statement);
      statement = con.prepareStatement("DELETE FROM character_effects_save WHERE char_obj_id=? AND class_index=?");
      statement.setInt(1, getObjectId());
      statement.setInt(2, newclass);
      statement.executeUpdate();
      DatabaseUtils.closeStatement(statement);
      statement = con.prepareStatement("UPDATE character_effects_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();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(statement);
    }
    catch(final SQLException e)
    {
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  /**
   * Сохраняет информацию о классах в БД
   */
  public void storeCharSubClasses()
  {
    L2SubClass main = getActiveClass();
    if(main != null)
    {
      main.setCp(getCurrentCp());
      //main.setExp(getExp());
      //main.setLevel(getLevel());
      //main.setSp(getSp());
      main.setHp(getCurrentHp());
      main.setMp(getCurrentMp());
      main.setActive(true);
      getSubClasses().put(getActiveClassId(), main);
    }
    else
    {
      _log.warning("Could not store char sub data, main class " + getActiveClassId() + " not found for " + this);
    }
    ThreadConnection con = null;
    FiltredStatement statement = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.createStatement();
      StringBuilder sb;
      for(L2SubClass subClass : getSubClasses().values())
      {
        sb = new StringBuilder("UPDATE character_subclasses SET ");
        sb.append("exp=").append(subClass.getExp()).append(",");
        sb.append("sp=").append(subClass.getSp()).append(",");
        sb.append("curHp=").append(subClass.getHp()).append(",");
        sb.append("curMp=").append(subClass.getMp()).append(",");
        sb.append("curCp=").append(subClass.getCp()).append(",");
        sb.append("level=").append(subClass.getLevel()).append(",");
        sb.append("active=").append(subClass.isActive() ? 1 : 0).append(",");
        sb.append("isBase=").append(subClass.isBase() ? 1 : 0).append(",");
        sb.append("death_penalty=").append(subClass.getDeathPenalty().getLevelOnSaveDB()).append(",");
        sb.append("skills='").append(subClass.getSkills()).append("'");
        sb.append(" WHERE char_obj_id=").append(getObjectId()).append(" AND class_id=").append(subClass.getClassId()).append(" LIMIT 1");
        statement.executeUpdate(sb.toString());
      }
      sb = new StringBuilder("UPDATE LOW_PRIORITY character_subclasses SET ");
      sb.append("maxHp=").append(getMaxHp()).append(",");
      sb.append("maxMp=").append(getMaxMp()).append(",");
      sb.append("maxCp=").append(getMaxCp());
      sb.append(" WHERE char_obj_id=").append(getObjectId()).append(" AND active=1 LIMIT 1");
      statement.executeUpdate(sb.toString());
    }
    catch(final Exception e)
    {
      _log.warning("Could not store char sub data: " + e);
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
  }

  /**
   * Restore list of character professions and set up active proof
   * Used when character is loading
   */
  public static void restoreCharSubClasses(final L2Player player)
  {
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    ResultSet rset = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.prepareStatement("SELECT class_id,exp,sp,level,curHp,curCp,curMp,active,isBase,death_penalty,skills FROM character_subclasses WHERE char_obj_id=?");
      statement.setInt(1, player.getObjectId());
      rset = statement.executeQuery();
      while(rset.next())
      {
        final L2SubClass subClass = new L2SubClass();
        subClass.setBase(rset.getInt("isBase") != 0);
        subClass.setClassId(rset.getShort("class_id"));
        subClass.setLevel(rset.getByte("level"));
        subClass.setExp(rset.getLong("exp"));
        subClass.setSp(rset.getInt("sp"));
        subClass.setHp(rset.getDouble("curHp"));
        subClass.setMp(rset.getDouble("curMp"));
        subClass.setCp(rset.getDouble("curCp"));
        subClass.setActive(rset.getInt("active") != 0);
        subClass.setDeathPenalty(new DeathPenalty(player, rset.getByte("death_penalty")));
        subClass.setSkills(rset.getString("skills"));
        subClass.setPlayer(player);
        player.getSubClasses().put(subClass.getClassId(), subClass);
      }
      if(player.getSubClasses().isEmpty())
      {
        throw new Exception("There are no one subclass for player: " + player);
      }
      int BaseClassId = player.getBaseClassId();
      if(BaseClassId == -1)
      {
        throw new Exception("There are no base subclass for player: " + player);
      }
      for(L2SubClass subClass : player.getSubClasses().values())
      {
        if(subClass.isActive())
        {
          player.setActiveSubClass(subClass.getClassId(), false);
          break;
        }
      }
      if(player.getActiveClass() == null)
      {
        //если из-за какого-либо сбоя ни один из сабкласов не отмечен как активный помечаем базовый как активный
        final L2SubClass subClass = player.getSubClasses().get(BaseClassId);
        subClass.setActive(true);
        player.setActiveSubClass(subClass.getClassId(), false);
      }
    }
    catch(final Exception e)
    {
      _log.warning("Could not restore char sub-classes: " + e);
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rset);
    }
  }

  /**
   * Добавить класс, используется только для сабклассов
   *
   * @param storeOld
   */
  public boolean addSubClass(final int classId, boolean storeOld)
  {
    if(_classlist.size() > 3 + Config.ALT_GAME_SUB_ADD)
    {
      return false;
    }
    final ClassId newId = ClassId.values()[classId];
    final L2SubClass newClass = new L2SubClass();
    if(newId.getRace() == null)
    {
      return false;
    }
    newClass.setClassId(classId);
    newClass.setPlayer(this);
    _classlist.put(classId, newClass);
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      // Store the basic info about this new sub-class.
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.prepareStatement("INSERT INTO character_subclasses (char_obj_id, class_id, exp, sp, curHp, curMp, curCp, maxHp, maxMp, maxCp, level, active, isBase, death_penalty, skills) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)");
      statement.setInt(1, getObjectId());
      statement.setInt(2, newClass.getClassId());
      statement.setLong(3, Experience.LEVEL[40]);
      statement.setInt(4, 0);
      statement.setDouble(5, getCurrentHp());
      statement.setDouble(6, getCurrentMp());
      statement.setDouble(7, getCurrentCp());
      statement.setDouble(8, getCurrentHp());
      statement.setDouble(9, getCurrentMp());
      statement.setDouble(10, getCurrentCp());
      statement.setInt(11, 40);
      statement.setInt(12, 0);
      statement.setInt(13, 0);
      statement.setInt(14, 0);
      statement.setString(15, "");
      statement.execute();
    }
    catch(final Exception e)
    {
      _log.warning("Could not add character sub-class: " + e);
      return false;
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
    setActiveSubClass(classId, storeOld);
    // Add all the necessary skills up to level 40 for this new class.
    boolean countUnlearnable = true;
    int unLearnable = 0;
    GArray<L2SkillLearn> skills = SkillTreeTable.getInstance().getAvailableSkills(this, newId);
    while(skills.size() > unLearnable)
    {
      for(final L2SkillLearn s : skills)
      {
        final L2Skill sk = SkillTable.getInstance().getInfo(s.getId(), s.getLevel());
        if(sk == null || !sk.getCanLearn(newId))
        {
          if(countUnlearnable)
          {
            unLearnable++;
          }
          continue;
        }
        addSkill(sk, true);
      }
      countUnlearnable = false;
      skills = SkillTreeTable.getInstance().getAvailableSkills(this, newId);
    }
    restoreSkills();
    rewardSkills();
    sendPacket(new SkillList(this));
    setCurrentHpMp(getMaxHp(), getMaxMp(), true);
    setCurrentCp(getMaxCp());
    return true;
  }

  /**
   * Удаляет всю информацию о классе и добавляет новую, только для сабклассов
   */
  public boolean modifySubClass(final int oldClassId, final int newClassId)
  {
    final L2SubClass originalClass = _classlist.get(oldClassId);
    if(originalClass.isBase())
    {
      return false;
    }
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    try
    {
      con = L2DatabaseFactory.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 isBase = 0");
      statement.setInt(1, getObjectId());
      statement.setInt(2, oldClassId);
      statement.execute();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(statement);
      // Remove all saved effects stored for this sub-class.
      statement = con.prepareStatement("DELETE FROM character_effects_save WHERE char_obj_id=? AND class_index=? ");
      statement.setInt(1, getObjectId());
      statement.setInt(2, oldClassId);
      statement.execute();
      DatabaseUtils.closeStatement(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();
      DatabaseUtils.closeStatement(statement);
      // Remove all shortcuts info stored for this sub-class.
      statement = con.prepareStatement("DELETE FROM character_shortcuts WHERE char_obj_id=? AND class_index=? ");
      statement.setInt(1, getObjectId());
      statement.setInt(2, oldClassId);
      statement.execute();
    }
    catch(final Exception e)
    {
      _log.warning("Could not delete char sub-class: " + e);
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, statement);
    }
    _classlist.remove(oldClassId);
    return newClassId > 0 ? addSubClass(newClassId, false) : true;
  }

  /**
   * Устанавливает активный сабкласс
   * <p/>
   * <li>Retrieve from the database all skills of this L2Player and add them to _skills </li>
   * <li>Retrieve from the database all macroses of this L2Player and add them to _macroses</li>
   * <li>Retrieve from the database all shortCuts of this L2Player and add them to _shortCuts</li><BR><BR>
   */
  public void setActiveSubClass(final int subId, final boolean store)
  {
    final L2SubClass sub = getSubClasses().get(subId);
    if(sub == null)
    {
      System.out.print("WARNING! setActiveSubClass<?> :: sub == null :: subId == " + subId);
      Thread.dumpStack();
      return;
    }
    if(getActiveClass() != null)
    {
      storeEffects();
      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)
    {
      final L2SubClass oldsub = getActiveClass();
      oldsub.setCp(getCurrentCp());
      //oldsub.setExp(getExp());
      //oldsub.setLevel(getLevel());
      //oldsub.setSp(getSp());
      oldsub.setHp(getCurrentHp());
      oldsub.setMp(getCurrentMp());
      oldsub.setActive(false);
      getSubClasses().put(getActiveClassId(), oldsub);
    }
    sub.setActive(true);
    setActiveClass(sub);
    getSubClasses().put(getActiveClassId(), sub);
    setClassId(subId, false);
    removeAllSkills();
    getEffectList().stopAllEffects();
    if(getPet() != null && (getPet().isSummon() || Config.ALT_IMPROVED_PETS_LIMITED_USE && (getPet().getNpcId() == PetDataTable.IMPROVED_BABY_KOOKABURRA_ID && !isMageClass() || getPet().getNpcId() == PetDataTable.IMPROVED_BABY_BUFFALO_ID && isMageClass())))
    {
      getPet().unSummon();
    }
    if(_cubics != null)
    {
      for(L2CubicInstance cubic : _cubics)
      {
        cubic.deleteMe(false);
      }
    }
    setAgathion(0);
    checkRecom();
    restoreSkills();
    restoreSubclassSkills();
    rewardSkills();
    sendPacket(new ExStorageMaxCount(this));
    sendPacket(new SkillList(this));
    getInventory().refreshListeners();
    getInventory().checkAllConditions();
    for(int i = 0; i < 3; i++)
    {
      _henna[i] = null;
    }
    restoreHenna();
    sendPacket(new HennaInfo(this));
    restoreEffects();
    if(isVisible()) // костыль для загрузки чара
    {
      restoreDisableSkills();
    }
    setCurrentHpMp(sub.getHp(), sub.getMp());
    setCurrentCp(sub.getCp());
    broadcastUserInfo(true);
    updateStats();
    _shortCuts.restore();
    sendPacket(new ShortCutInit(this));
    for(int shotId : getAutoSoulShot())
    {
      sendPacket(new ExAutoSoulShot(shotId, true));
    }
    sendPacket(new SkillCoolTime(this));
    broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP));
    getInventory().restoreCursedWeapon();
    getDeathPenalty().restore();
    setIncreasedForce(0);
  }

  /**
   * Через delay миллисекунд выбросит игрока из игры
   */
  public void startKickTask(long delay)
  {
    if(_kickTask != null)
    {
      stopKickTask();
    }
    _kickTask = ThreadPoolManager.getInstance().scheduleAi(new KickTask(this), delay, true);
  }

  public void stopKickTask()
  {
    if(_kickTask != null)
    {
      _kickTask.cancel(false);
      _kickTask = null;
    }
  }

  public void startBonusTask(long time)
  {
    time *= 1000;
    time -= System.currentTimeMillis();
    if(_bonusExpiration != null)
    {
      stopBonusTask();
    }
    _bonusExpiration = ThreadPoolManager.getInstance().scheduleAi(new BonusTask(this), time, true);
  }

  public void stopBonusTask()
  {
    if(_bonusExpiration != null)
    {
      _bonusExpiration.cancel(true);
      _bonusExpiration = null;
    }
  }

  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 getFreightLimit()
  {
    // FIXME Не учитывается количество предметов, уже имеющееся на складе
    return getWarehouseLimit();
  }

  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;
  }

  @Override
  public int getNpcId()
  {
    return -2;
  }

  public L2Object getVisibleObject(int id)
  {
    if(getObjectId() == id)
    {
      return this;
    }
    if(getTargetId() == id)
    {
      return getTarget();
    }
    if(_party != null)
    {
      for(L2Player p : _party.getPartyMembers())
      {
        if(p != null && p.getObjectId() == id)
        {
          return p;
        }
      }
    }
    L2Object obj = L2World.getAroundObjectById(this, id);
    // Руль кланового летающего корабля
    if(obj == null && isInVehicle() && getVehicle().isClanAirShip() && ClanTable.getInstance().getClan(id) != null)
    {
      obj = ((L2AirShip) getVehicle()).getControlKey();
    }
    return obj == null || obj.isInvisible() ? null : obj;
  }

  @Override
  public int getPAtk(final L2Character target)
  {
    double init = getActiveWeaponInstance() == null ? (isMageClass() ? 3 : 4) : 0;
    return (int) calcStat(Stats.POWER_ATTACK, init, target, null);
  }

  @Override
  public int getPDef(final L2Character target)
  {
    double init = 4; //empty cloak and underwear slots
    final L2ItemInstance chest = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
    if(chest == null)
    {
      init += isMageClass() ? L2Armor.EMPTY_BODY_MYSTIC : L2Armor.EMPTY_BODY_FIGHTER;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS) == null && (chest == null || chest.getBodyPart() != L2Item.SLOT_FULL_ARMOR))
    {
      init += isMageClass() ? L2Armor.EMPTY_LEGS_MYSTIC : L2Armor.EMPTY_LEGS_FIGHTER;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_HEAD) == null)
    {
      init += L2Armor.EMPTY_HELMET;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_GLOVES) == null)
    {
      init += L2Armor.EMPTY_GLOVES;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_FEET) == null)
    {
      init += L2Armor.EMPTY_BOOTS;
    }
    return (int) calcStat(Stats.POWER_DEFENCE, init, target, null);
  }

  @Override
  public int getMDef(final L2Character target, final L2Skill skill)
  {
    double init = 0;
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEAR) == null)
    {
      init += L2Armor.EMPTY_EARRING;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_REAR) == null)
    {
      init += L2Armor.EMPTY_EARRING;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_NECK) == null)
    {
      init += L2Armor.EMPTY_NECKLACE;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_LFINGER) == null)
    {
      init += L2Armor.EMPTY_RING;
    }
    if(getInventory().getPaperdollItem(Inventory.PAPERDOLL_RFINGER) == null)
    {
      init += L2Armor.EMPTY_RING;
    }
    return (int) calcStat(Stats.MAGIC_DEFENCE, init, target, skill);
  }

  public boolean isSubClassActive()
  {
    return getBaseClassId() != getActiveClassId();
  }

  @Override
  public String getTitle()
  {
    return super.getTitle();
  }

  public int getTitleColor()
  {
    return _titlecolor;
  }

  public void setTitleColor(final int color)
  {
    _titlecolor = color;
  }

  public void setTitleColor(final int red, final int green, final int blue)
  {
    _titlecolor = (red & 0xFF) + ((green & 0xFF) << 8) + ((blue & 0xFF) << 16);
  }

  @Override
  public boolean isCursedWeaponEquipped()
  {
    return _cursedWeaponEquippedId != 0;
  }

  public void setCursedWeaponEquippedId(int value)
  {
    _cursedWeaponEquippedId = value;
  }

  public int getCursedWeaponEquippedId()
  {
    return _cursedWeaponEquippedId;
  }
  private FishData _fish;

  public void setFish(FishData fish)
  {
    _fish = fish;
  }

  public void stopLookingForFishTask()
  {
    if(_taskforfish != null)
    {
      _taskforfish.cancel(false);
      _taskforfish = null;
    }
  }

  public void startLookingForFishTask()
  {
    if(!isDead() && _taskforfish == null)
    {
      int checkDelay = 0;
      boolean isNoob = false;
      boolean isUpperGrade = false;
      if(_lure != null)
      {
        int lureid = _lure.getItemId();
        isNoob = _fish.getGroup() == 0;
        isUpperGrade = _fish.getGroup() == 2;
        if(lureid == 6519 || lureid == 6522 || lureid == 6525 || lureid == 8505 || lureid == 8508 || lureid == 8511) //low grade
        {
          checkDelay = Math.round((float) (_fish.getGutsCheckTime() * 1.33));
        }
        else if(lureid == 6520 || lureid == 6523 || lureid == 6526 || lureid >= 8505 && lureid <= 8513 || lureid >= 7610 && lureid <= 7613 || lureid >= 7807 && lureid <= 7809 || lureid >= 8484 && lureid <= 8486) //medium grade, beginner, prize-winning & quest special bait
        {
          checkDelay = Math.round((float) (_fish.getGutsCheckTime() * 1.00));
        }
        else if(lureid == 6521 || lureid == 6524 || lureid == 6527 || lureid == 8507 || lureid == 8510 || lureid == 8513) //high grade
        {
          checkDelay = Math.round((float) (_fish.getGutsCheckTime() * 0.66));
        }
      }
      _taskforfish = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new LookingForFishTask(this, _fish.getWaitTime(), _fish.getFishGuts(), _fish.getType(), isNoob, isUpperGrade), 10000, checkDelay, false);
    }
  }

  public void startFishCombat(boolean isNoob, boolean isUpperGrade)
  {
    _fishCombat = new L2Fishing(this, _fish, isNoob, isUpperGrade);
  }

  public void endFishing(boolean win)
  {
    ExFishingEnd efe = new ExFishingEnd(win, this);
    broadcastPacket(efe);
    _fishing = false;
    _fishLoc = new Location();
    broadcastUserInfo(true);
    if(_fishCombat == null)
    {
      sendPacket(Msg.BAITS_HAVE_BEEN_LOST_BECAUSE_THE_FISH_GOT_AWAY);
    }
    _fishCombat = null;
    _lure = null;
    //Ends fishing
    sendPacket(Msg.ENDS_FISHING);
    setImobilised(false);
    stopLookingForFishTask();
  }

  public L2Fishing getFishCombat()
  {
    return _fishCombat;
  }

  public void setFishLoc(Location loc)
  {
    _fishLoc = loc;
  }

  public Location getFishLoc()
  {
    return _fishLoc;
  }

  public void setLure(L2ItemInstance lure)
  {
    _lure = lure;
  }

  public L2ItemInstance getLure()
  {
    return _lure;
  }

  public boolean isFishing()
  {
    return _fishing;
  }

  public void setFishing(boolean fishing)
  {
    _fishing = fishing;
  }

  public Bonus getBonus()
  {
    return _bonus;
  }

  public void restoreBonus()
  {
    _bonus = new Bonus(this);
  }

  @Override
  public float getRateAdena()
  {
    return _party == null ? _bonus.RATE_DROP_ADENA : _party._rateAdena * _bonus.RATE_DROP_ADENA;
  }

  @Override
  public float getRateItems()
  {
    return _party == null ? _bonus.RATE_DROP_ITEMS : _party._rateDrop * _bonus.RATE_DROP_ITEMS;
  }

  @Override
  public double getRateExp()
  {
    return calcStat(Stats.EXP, (_party == null ? _bonus.RATE_XP : _party._rateExp * _bonus.RATE_XP), null, null);
  }

  @Override
  public double getRateSp()
  {
    return calcStat(Stats.SP, (_party == null ? _bonus.RATE_SP : _party._rateSp * _bonus.RATE_SP), null, null);
  }

  @Override
  public float getRateSpoil()
  {
    return _party == null ? _bonus.RATE_DROP_SPOIL : _party._rateSpoil * _bonus.RATE_DROP_SPOIL;
  }
  private boolean _maried = false;
  private int _partnerId = 0;
  private int _coupleId = 0;
  private boolean _engagerequest = false;
  private int _engageid = 0;
  private boolean _maryrequest = false;
  private boolean _maryaccepted = false;

  public boolean isMaried()
  {
    return _maried;
  }

  public void setMaried(boolean state)
  {
    _maried = state;
  }

  public boolean isEngageRequest()
  {
    return _engagerequest;
  }

  public void setEngageRequest(boolean state, int playerid)
  {
    _engagerequest = state;
    _engageid = playerid;
  }

  public void setMaryRequest(boolean state)
  {
    _maryrequest = state;
  }

  public boolean isMaryRequest()
  {
    return _maryrequest;
  }

  public void setMaryAccepted(boolean state)
  {
    _maryaccepted = state;
  }

  public boolean isMaryAccepted()
  {
    return _maryaccepted;
  }

  public int getEngageId()
  {
    return _engageid;
  }

  public int getPartnerId()
  {
    return _partnerId;
  }

  public void setPartnerId(int partnerid)
  {
    _partnerId = partnerid;
  }

  public int getCoupleId()
  {
    return _coupleId;
  }

  public void setCoupleId(int coupleId)
  {
    _coupleId = coupleId;
  }

  public void engageAnswer(int answer)
  {
    if(!_engagerequest || _engageid == 0)
    {
      return;
    }
    L2Player ptarget = L2ObjectsStorage.getPlayer(_engageid);
    setEngageRequest(false, 0);
    if(ptarget != null)
    {
      if(answer == 1)
      {
        CoupleManager.getInstance().createCouple(ptarget, this);
        ptarget.sendMessage(new CustomMessage("l2p.gameserver.model.L2Player.EngageAnswerYes", this));
      }
      else
      {
        ptarget.sendMessage(new CustomMessage("l2p.gameserver.model.L2Player.EngageAnswerNo", this));
      }
    }
  }
  /**
   * private List<L2Player> _snoopListener = new GArray<L2Player>();
   * private List<L2Player> _snoopedPlayer = new GArray<L2Player>();
   * <p/>
   * public void broadcastSnoop(int type, String name, String _text)
   * {
   * if(_snoopListener.size() > 0)
   * {
   * Snoop sn = new Snoop(getObjectId(), getName(), type, name, _text);
   * 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 final FastMap<Integer, SkillTimeStamp> skillReuseTimeStamps = new FastMap<Integer, SkillTimeStamp>().setShared(true);

  public FastMap<Integer, SkillTimeStamp> getSkillReuseTimeStamps()
  {
    return skillReuseTimeStamps;
  }

  private void addSkillTimeStamp(Integer skillId, long reuseDelay)
  {
    synchronized(skillReuseTimeStamps)
    {
      skillReuseTimeStamps.put(skillId, new SkillTimeStamp(skillId, System.currentTimeMillis() + reuseDelay, reuseDelay));
    }
  }

  private void removeSkillTimeStamp(Integer skillId)
  {
    synchronized(skillReuseTimeStamps)
    {
      skillReuseTimeStamps.remove(skillId);
    }
  }

  @Override
  public boolean isSkillDisabled(Integer skillId)
  {
    synchronized(skillReuseTimeStamps)
    {
      SkillTimeStamp sts = skillReuseTimeStamps.get(skillId);
      if(sts == null)
      {
        return false;
      }
      if(sts.hasNotPassed())
      {
        return true;
      }
      skillReuseTimeStamps.remove(skillId);
      return false;
    }
  }

  @Override
  public void disableSkill(int skillId, long delay)
  {
    addSkillTimeStamp(skillId, delay);
  }

  @Override
  public void enableSkill(Integer skillId)
  {
    removeSkillTimeStamp(skillId);
  }

  public ScheduledFuture<?> getWaterTask()
  {
    return _taskWater;
  }

  public DeathPenalty getDeathPenalty()
  {
    return getActiveClass().getDeathPenalty();
  }

  public void setDeathPeanalty(DeathPenalty dp)
  {
    getActiveClass().setDeathPenalty(dp);
  }
  private boolean _charmOfCourage = false;

  public boolean isCharmOfCourage()
  {
    return _charmOfCourage;
  }

  public void setCharmOfCourage(boolean val)
  {
    _charmOfCourage = val;
    if(!val)
    {
      getEffectList().stopEffect(L2Skill.SKILL_CHARM_OF_COURAGE);
    }
    sendPacket(new EtcStatusUpdate(this));
  }

  private void revalidatePenalties()
  {
    _curWeightPenalty = 0;
    weaponPenalty = 0;
    armorPenalty = 0;
    refreshOverloaded();
    refreshExpertisePenalty();
  }
  private int _increasedForce = 0;
  private int _consumedSouls = 0;

  @Override
  public int getIncreasedForce()
  {
    return _increasedForce;
  }

  @Override
  public int getConsumedSouls()
  {
    return _consumedSouls;
  }

  @Override
  public void setConsumedSouls(int i, L2NpcInstance 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;
      sendPacket(new EtcStatusUpdate(this));
      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));
  }

  @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;
    sendPacket(new EtcStatusUpdate(this));
  }
  private long _lastFalling;

  public boolean isFalling()
  {
    return System.currentTimeMillis() - _lastFalling < 5000;
  }

  public void falling(int height)
  {
    if(!Config.DAMAGE_FROM_FALLING || isDead() || isFlying() || isSwimming() || isInVehicle())
    {
      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)
  {
    //сюда пасивные скиллы
    byte[] _hp =
    {
      30, 30
    };
    int[] skills =
    {
      290, 291
    };
    //сюда активные эффекты
    int[] _effects_skills_id =
    {
      139, 176, 292, 292, 420
    };
    byte[] _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++)
    {
      short 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()
  {
    short 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();
  }
  private boolean _isInDangerArea;

  public boolean isInDangerArea()
  {
    return _isInDangerArea;
  }

  public void setInDangerArea(boolean value)
  {
    _isInDangerArea = value;
  }

  public void setInCombatZone(boolean flag)
  {
    _isInCombatZone = flag;
  }

  public void setOnSiegeField(boolean flag)
  {
    _isOnSiegeField = flag;
  }

  public boolean isInPeaceZone()
  {
    return _isInPeaceZone;
  }

  public void setInPeaceZone(boolean b)
  {
    _isInPeaceZone = b;
    if(b)
    {
      VitalityManager.getInstance().addRegenTask(this);
    }
  }

  public boolean isInSSZone()
  {
    return _isInSSZone;
  }

  public void setInSSZone(boolean b)
  {
    _isInSSZone = b;
  }

  public boolean isInCombatZone()
  {
    return _isInCombatZone;
  }

  public boolean isOnSiegeField()
  {
    return _isOnSiegeField;
  }

  public void doZoneCheck(int messageNumber)
  {
    boolean oldIsInDangerArea = isInDangerArea();
    boolean oldIsInCombatZone = isInCombatZone();
    boolean oldIsOnSiegeField = isOnSiegeField();
    boolean oldIsInPeaceZone = isInPeaceZone();
    boolean oldSSQZone = isInSSZone();
    setInDangerArea(isInZone(poison) || isInZone(instant_skill) || isInZone(swamp) || isInZone(damage));
    setInCombatZone(isInZoneBattle());
    setOnSiegeField(isInZone(Siege));
    setInPeaceZone(isInZone(peace_zone));
    setInSSZone(isInZone(ssq_zone));
    if(oldIsInDangerArea != isInDangerArea() || oldIsInCombatZone != isInCombatZone() || oldIsOnSiegeField != isOnSiegeField() || oldIsInPeaceZone != isInPeaceZone() || oldSSQZone != isInSSZone())
    {
      sendPacket(new ExSetCompassZoneCode(this));
      sendPacket(new EtcStatusUpdate(this));
      if(messageNumber != 0)
      {
        sendPacket(new SystemMessage(messageNumber));
      }
    }
    if(oldIsOnSiegeField != isOnSiegeField())
    {
      if(isOnSiegeField())
      {
        sendPacket(Msg.YOU_HAVE_ENTERED_A_COMBAT_ZONE);
      }
      else
      {
        sendPacket(Msg.YOU_HAVE_LEFT_A_COMBAT_ZONE);
      }
    }
    if(oldIsOnSiegeField != isOnSiegeField() && !isOnSiegeField() && !isTeleporting() && getPvpFlag() == 0)
    {
      startPvPFlag(null);
    }
    revalidateInResidence();
  }
  private Future<?> _returnTerritoryFlagTask = null;

  public void checkTerritoryFlag()
  {
    if(isTerritoryFlagEquipped())
    {
      L2Zone siegeZone = ZoneManager.getInstance().getZoneByType(ZoneType.Siege, getX(), getY(), true);
      if(siegeZone == null && (_returnTerritoryFlagTask == null || _returnTerritoryFlagTask.isDone()))
      {
        _returnTerritoryFlagTask = ThreadPoolManager.getInstance().scheduleGeneral(new ReturnTerritoryFlagTask(this), 600000);
        sendMessage("У вас есть 10 минут, чтобы вернуться в осадную зону, иначе флаг вернется в замок. Вы можете использовать форты как помежуточные точки, для сброса таймера.");
      }
      if(siegeZone != null && _returnTerritoryFlagTask != null)
      {
        _returnTerritoryFlagTask.cancel(true);
        _returnTerritoryFlagTask = null;
      }
    }
  }
  private ResidenceType _inResidence = ResidenceType.None;

  public void revalidateInResidence()
  {
    L2Clan clan = _clan;
    if(clan == null)
    {
      return;
    }
    int clanHallIndex = clan.getHasHideout();
    if(clanHallIndex != 0)
    {
      ClanHall clansHall = ClanHallManager.getInstance().getClanHall(clanHallIndex);
      if(clansHall != null && clansHall.checkIfInZone(getX(), getY()))
      {
        setInResidence(ResidenceType.Clanhall);
        return;
      }
    }
    int castleIndex = clan.getHasCastle();
    if(castleIndex != 0)
    {
      Castle castle = CastleManager.getInstance().getCastleByIndex(castleIndex);
      if(castle != null && castle.checkIfInZone(getX(), getY()))
      {
        setInResidence(ResidenceType.Castle);
        return;
      }
    }
    int fortressIndex = clan.getHasFortress();
    if(fortressIndex != 0)
    {
      Fortress fort = FortressManager.getInstance().getFortressByIndex(fortressIndex);
      if(fort != null && fort.checkIfInZone(getX(), getY()))
      {
        setInResidence(ResidenceType.Fortress);
        return;
      }
    }
    setInResidence(ResidenceType.None);
  }

  public ResidenceType getInResidence()
  {
    return _inResidence;
  }

  public void setInResidence(ResidenceType inResidence)
  {
    _inResidence = inResidence;
  }

  @Override
  public void sendMessage(String message)
  {
    sendPacket(new SystemMessage(message));
  }
  private Location _lastClientPosition;
  private Location _lastServerPosition;

  @Override
  public void setLastClientPosition(Location position)
  {
    _lastClientPosition = position;
  }

  public Location getLastClientPosition()
  {
    return _lastClientPosition;
  }

  @Override
  public void setLastServerPosition(Location position)
  {
    _lastServerPosition = position;
  }

  public Location getLastServerPosition()
  {
    return _lastServerPosition;
  }
  private int _useSeed = 0;

  public void setUseSeed(int id)
  {
    _useSeed = id;
  }

  public int getUseSeed()
  {
    return _useSeed;
  }

  public int getRelation(L2Player target)
  {
    int result = 0;
    if(getClan() != null)
    {
      result |= RelationChanged.RELATION_CLAN_MEMBER;
    }
    if(isClanLeader())
    {
      result |= RelationChanged.RELATION_LEADER;
    }
    L2Party party = getParty();
    if(party != null && party == target.getParty())
    {
      result |= RelationChanged.RELATION_HAS_PARTY;
      switch(party.getPartyMembers().indexOf(this))
      {
        case 0:
          result |= RelationChanged.RELATION_PARTYLEADER; // 0x10
          break;
        case 1:
          result |= RelationChanged.RELATION_PARTY4; // 0x8
          break;
        case 2:
          result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x7
          break;
        case 3:
          result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2; // 0x6
          break;
        case 4:
          result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY1; // 0x5
          break;
        case 5:
          result |= RelationChanged.RELATION_PARTY3; // 0x4
          break;
        case 6:
          result |= RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x3
          break;
        case 7:
          result |= RelationChanged.RELATION_PARTY2; // 0x2
          break;
        case 8:
          result |= RelationChanged.RELATION_PARTY1; // 0x1
          break;
      }
    }
    L2Clan clan1 = getClan();
    L2Clan clan2 = target.getClan();
    if(clan1 != null && clan2 != null)
    {
      Siege siege1 = clan1.getSiege();
      Siege siege2 = clan2.getSiege();
      int state1 = getSiegeState();
      int state2 = target.getSiegeState();
      if(siege1 != null && siege2 != null && siege1 == siege2 && siege1.isInProgress() && state1 != 0 && state2 != 0)
      {
        result |= RelationChanged.RELATION_INSIEGE;
        if(state1 != state2 || siege1.isMidVictory() && state1 == 1 && state2 == 1)
        {
          result |= RelationChanged.RELATION_ENEMY;
        }
        else
        {
          result |= RelationChanged.RELATION_ALLY;
        }
        if(state1 == 1)
        {
          result |= RelationChanged.RELATION_ATTACKER;
        }
      }
      if(target.getPledgeType() != L2Clan.SUBUNIT_ACADEMY && getPledgeType() != L2Clan.SUBUNIT_ACADEMY)
      {
        if(clan2.isAtWarWith(clan1.getClanId()))
        {
          result |= RelationChanged.RELATION_1SIDED_WAR;
          if(clan1.isAtWarWith(clan2.getClanId()))
          {
            result |= RelationChanged.RELATION_MUTUAL_WAR;
          }
        }
      }
    }
    int territorySiege1 = getTerritorySiege();
    int territorySiege2 = target.getTerritorySiege();
    if(territorySiege1 > -1 && territorySiege2 > -1)
    /* TODO возможно, что-то из этого тоже нужно отсылать
    result |= RelationChanged.RELATION_INSIEGE;
    if(territorySiege1 != territorySiege2)
    result |= RelationChanged.RELATION_ENEMY;
    else
    result |= RelationChanged.RELATION_ALLY;
     */
    {
      result |= RelationChanged.RELATION_ISINTERRITORYWARS;
    }
    return result;
  }
  /**
   * 0=White, 1=Purple, 2=PurpleBlink
   */
  protected int _pvpFlag;
  private Future<?> _PvPRegTask;
  private long _lastPvpAttack;

  public void setlastPvpAttack(long time)
  {
    _lastPvpAttack = time;
  }

  public long getlastPvpAttack()
  {
    return _lastPvpAttack;
  }

  @Override
  public void startPvPFlag(L2Character target)
  {
    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().scheduleAiAtFixedRate(new PvPFlagTask(this), 1000, 1000, true);
    }
  }

  public void stopPvPFlag()
  {
    if(_PvPRegTask != null)
    {
      _PvPRegTask.cancel(false);
      _PvPRegTask = null;
    }
    updatePvPFlag(0);
  }

  public void updatePvPFlag(int value)
  {
    if(_pvpFlag == value)
    {
      return;
    }
    setPvpFlag(value);
    if(_karma < 1)
    {
      sendStatusUpdate(true, StatusUpdate.PVP_FLAG);
      if(getPet() != null)
      {
        getPet().broadcastPetInfo();
      }
    }
    broadcastRelationChanged();
  }

  public void setPvpFlag(int pvpFlag)
  {
    _pvpFlag = pvpFlag;
  }

  @Override
  public int getPvpFlag()
  {
    return _pvpFlag;
  }
  private Duel _duel;

  public void setDuel(Duel duel)
  {
    _duel = duel;
    broadcastCharInfo();
  }

  @Override
  public Duel getDuel()
  {
    return _duel;
  }

  public boolean isInDuel()
  {
    return _duel != null;
  }
  private L2TamedBeastInstance _tamedBeast = null;

  public L2TamedBeastInstance getTrainedBeast()
  {
    return _tamedBeast;
  }

  public void setTrainedBeast(L2TamedBeastInstance tamedBeast)
  {
    _tamedBeast = tamedBeast;
  }

  public byte[] getKeyBindings()
  {
    return _keyBindings;
  }

  public void setKeyBindings(byte[] keyBindings)
  {
    if(keyBindings == null)
    {
      keyBindings = new byte[0];
    }
    _keyBindings = keyBindings;
  }

  /**
   * Устанавливает режим трансформаии<BR>
   *
   * @param transformationId идентификатор трансформации
   *                         Известные режимы:<BR>
   *                         <li>0 - стандартный вид чара
   *                         <li>1 - Onyx Beast
   *                         <li>2 - Death Blader
   *                         <li>etc.
   */
  public void setTransformation(int transformationId)
  {
    if(transformationId == _transformationId || _transformationId != 0 && transformationId != 0)
    {
      return;
    }
    // Для каждой трансформации свой набор скилов
    if(transformationId == 0) // Обычная форма
    {
      // Останавливаем текущий эффект трансформации
      for(L2Effect effect : getEffectList().getAllEffects())
      {
        if(effect != null && effect.getEffectType() == EffectType.Transformation)
        {
          effect.exit();
          break;
        }
      }
      // Удаляем скилы трансформации
      if(!_transformationSkills.isEmpty())
      {
        for(L2Skill s : _transformationSkills.values())
        {
          if(!s.isCommon() && !SkillTreeTable.getInstance().isSkillPossible(this, s.getId(), s.getLevel()))
          {
            super.removeSkill(s);
          }
        }
        super.removeSkillById(619);
        _transformationSkills.clear();
      }
    }
    else
    {
      // Добавляем скилы трансформации
      for(L2Effect effect : getEffectList().getAllEffects())
      {
        if(effect != null && effect.getEffectType() == EffectType.Transformation)
        {
          if(effect.getSkill() instanceof Transformation && ((Transformation) effect.getSkill()).isDisguise)
          {
            for(L2Skill 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());
              }
            }
          }
          break;
        }
      }
      // Для все трансформаций кроме проклятых добавляем скилы:
      // - обратной трансформации (619)
      // - Decrease Bow/Crossbow Attack Speed (5491)
      if(!isCursedWeaponEquipped())
      {
        _transformationSkills.put(L2Skill.SKILL_TRANSFOR_DISPELL, SkillTable.getInstance().getInfo(L2Skill.SKILL_TRANSFOR_DISPELL, 1));
        _transformationSkills.put(5491, SkillTable.getInstance().getInfo(5491, 1));
      }
      for(L2Skill s : _transformationSkills.values())
      {
        addSkill(s, false);
      }
    }
    _transformationId = transformationId;
    sendPacket(new ExBasicActionList());
    sendPacket(new SkillList(this));
    sendPacket(new ShortCutInit(this));
    for(int shotId : getAutoSoulShot())
    {
      sendPacket(new ExAutoSoulShot(shotId, true));
    }
    broadcastUserInfo(true);
  }

  public boolean isInFlyingTransform()
  {
    return _transformationId == 8 || _transformationId == 9 || _transformationId == 260;
  }

  /**
   * Возвращает режим трансформации
   *
   * @return ID режима трансформации
   */
  public int getTransformation()
  {
    return _transformationId;
  }

  /**
   * Возвращает имя трансформации
   *
   * @return String
   */
  public String getTransformationName()
  {
    return _transformationName;
  }

  /**
   * Устанавливает имя трансформаии
   *
   * @param name имя трансформации
   */
  public void setTransformationName(String name)
  {
    _transformationName = name;
  }

  /**
   * Устанавливает шаблон трансформации, используется для определения коллизий
   *
   * @param template ID шаблона
   */
  public void setTransformationTemplate(int template)
  {
    _transformationTemplate = template;
  }

  /**
   * Возвращает шаблон трансформации, используется для определения коллизий
   *
   * @return NPC ID
   */
  public int getTransformationTemplate()
  {
    return _transformationTemplate;
  }

  /**
   * Возвращает коллекцию скиллов, с учетом текущей трансформации
   */
  @Override
  public final Collection<L2Skill> getAllSkills()
  {
    // Трансформация неактивна
    if(_transformationId == 0)
    {
      return super.getAllSkills();
    }
    // Трансформация активна
    HashMap<Integer, L2Skill> tempSkills = new HashMap<Integer, L2Skill>();
    for(L2Skill 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(id == 0)
    {
      if(_agathion != null)
      {
        _agathion.deleteMe();
      }
      _agathion = null;
    }
    else
    {
      _agathion = new L2AgathionInstance(this, id);
    }
    broadcastUserInfo(true);
    sendPacket(new SkillList(this));
  }

  public L2AgathionInstance getAgathion()
  {
    return _agathion;
  }

  /**
   * Возвращает количество PcBangPoint'ов даного игрока
   *
   * @return количество PcCafe Bang Points
   */
  public int getPcBangPoints()
  {
    return pcBangPoints;
  }

  /**
   * Устанавливает количество Pc Cafe Bang Points для даного игрока
   *
   * @param pcBangPoints новое количество PcCafeBangPoints
   */
  public void setPcBangPoints(int pcBangPoints)
  {
    this.pcBangPoints = pcBangPoints;
  }
  private Location _groundSkillLoc;

  public void setGroundSkillLoc(Location location)
  {
    _groundSkillLoc = location;
  }

  public Location getGroundSkillLoc()
  {
    return _groundSkillLoc;
  }

  public boolean isDeleting()
  {
    return _isDeleting;
  }

  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);
      val = "";
      if(_tradeList != null && _tradeList.getSellStoreName() != null)
      {
        setVar("sellstorename", _tradeList.getSellStoreName());
      }
    }
    if(_buyList == null || _buyList.isEmpty())
    {
      unsetVar("buylist");
    }
    else
    {
      for(TradeItem i : _buyList)
      {
        val += i.getItemId() + ";" + i.getCount() + ";" + i.getOwnersPrice() + ":";
      }
      setVar("buylist", val);
      val = "";
      if(_tradeList != null && _tradeList.getBuyStoreName() != null)
      {
        setVar("buystorename", _tradeList.getBuyStoreName());
      }
    }
    if(_createList == null || _createList.getList().isEmpty())
    {
      unsetVar("createlist");
    }
    else
    {
      for(L2ManufactureItem i : _createList.getList())
      {
        val += i.getRecipeId() + ";" + i.getCost() + ":";
      }
      setVar("createlist", val);
      if(_createList.getStoreName() != null)
      {
        setVar("manufacturename", _createList.getStoreName());
      }
    }
  }

  public void restoreTradeList()
  {
    if(getVar("selllist") != null)
    {
      _sellList = new ConcurrentLinkedQueue<TradeItem>();
      String[] items = getVar("selllist").split(":");
      for(String item : items)
      {
        if(item.equals(""))
        {
          continue;
        }
        String[] values = item.split(";");
        if(values.length < 3)
        {
          continue;
        }
        TradeItem i = new TradeItem();
        int oId = Integer.parseInt(values[0]);
        long count = Long.parseLong(values[1]);
        long price = Long.parseLong(values[2]);
        i.setObjectId(oId);
        L2ItemInstance itemToSell = getInventory().getItemByObjectId(oId);
        if(count < 1 || itemToSell == null)
        {
          continue;
        }
        if(count > itemToSell.getCount())
        {
          count = itemToSell.getCount();
        }
        i.setCount(count);
        i.setOwnersPrice(price);
        i.setItemId(itemToSell.getItemId());
        i.setEnchantLevel(itemToSell.getEnchantLevel());
        i.setAttackElement(itemToSell.getAttackElement());
        i.setDefenceFire(itemToSell.getDefenceFire());
        i.setDefenceWater(itemToSell.getDefenceWater());
        i.setDefenceWind(itemToSell.getDefenceWind());
        i.setDefenceEarth(itemToSell.getDefenceEarth());
        i.setDefenceHoly(itemToSell.getDefenceHoly());
        i.setDefenceUnholy(itemToSell.getDefenceUnholy());
        _sellList.add(i);
      }
      if(_tradeList == null)
      {
        _tradeList = new L2TradeList();
      }
      if(getVar("sellstorename") != null)
      {
        _tradeList.setSellStoreName(getVar("sellstorename"));
      }
    }
    if(getVar("buylist") != null)
    {
      _buyList = new ConcurrentLinkedQueue<TradeItem>();
      String[] items = getVar("buylist").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);
      }
      if(_tradeList == null)
      {
        _tradeList = new L2TradeList();
      }
      if(getVar("buystorename") != null)
      {
        _tradeList.setBuyStoreName(getVar("buystorename"));
      }
    }
    if(getVar("createlist") != null)
    {
      _createList = new L2ManufactureList();
      String[] items = getVar("createlist").split(":");
      for(String item : items)
      {
        if(item.equals(""))
        {
          continue;
        }
        String[] values = item.split(";");
        if(values.length < 2)
        {
          continue;
        }
        _createList.add(new L2ManufactureItem(Integer.parseInt(values[0]), Long.parseLong(values[1])));
      }
      if(getVar("manufacturename") != null)
      {
        _createList.setStoreName(getVar("manufacturename"));
      }
    }
  }

  public void restoreRecipeBook()
  {
    ThreadConnection con = null;
    FiltredPreparedStatement statement = null;
    ResultSet rset = null;
    try
    {
      con = L2DatabaseFactory.getInstance().getConnection();
      statement = con.prepareStatement("SELECT id FROM character_recipebook WHERE char_id=?");
      statement.setInt(1, getObjectId());
      rset = statement.executeQuery();
      while(rset.next())
      {
        int id = rset.getInt("id");
        L2Recipe recipe = RecipeController.getInstance().getRecipeByRecipeId(id);
        registerRecipe(recipe, false);
      }
    }
    catch(Exception e)
    {
      _log.warning("count not recipe skills:" + e);
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCSR(con, statement, rset);
    }
  }

  public L2DecoyInstance getDecoy()
  {
    return _decoy;
  }

  public void setDecoy(L2DecoyInstance 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:
        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;
      case PetDataTable.LIGHT_PURPLE_MANED_HORSE_ID:
      case PetDataTable.TAWNY_MANED_LION_ID:
      case PetDataTable.STEAM_BEATLE_ID:
        return 4;
    }
    return 0;
  }

  @Override
  public float getColRadius()
  {
    if(getTransformation() != 0 && getTransformationTemplate() != 0 && NpcTable.getTemplate(getTransformationTemplate()) != null)
    {
      return NpcTable.getTemplate(getTransformationTemplate()).collisionRadius;
    }
    else if(isMounted() && NpcTable.getTemplate(getMountNpcId()) != null)
    {
      return NpcTable.getTemplate(getMountNpcId()).collisionRadius;
    }
    else
    {
      return getBaseTemplate().collisionRadius;
    }
  }

  @Override
  public float getColHeight()
  {
    if(getTransformation() != 0 && getTransformationTemplate() != 0 && NpcTable.getTemplate(getTransformationTemplate()) != null)
    {
      return NpcTable.getTemplate(getTransformationTemplate()).collisionHeight;
    }
    else if(isMounted() && NpcTable.getTemplate(getMountNpcId()) != null)
    {
      return NpcTable.getTemplate(getMountNpcId()).collisionHeight + getBaseTemplate().collisionHeight;
    }
    else
    {
      return getBaseTemplate().collisionHeight;
    }
  }

  @Override
  public void setReflection(long i)
  {
    if(_reflection == i)
    {
      return;
    }
    super.setReflection(i);
    if(_summon != null && !_summon.isDead())
    {
      _summon.setReflection(i);
    }
    if(i != 0)
    {
      String var = getVar("reflection");
      if(var == null || !var.equals(String.valueOf(i)))
      {
        setVar("reflection", String.valueOf(i));
      }
    }
    else
    {
      unsetVar("reflection");
    }
    if(getActiveClass() != null)
    {
      getInventory().checkAllConditions();
      // Для квеста _129_PailakaDevilsLegacy
      if(getPet() != null && (getPet().getNpcId() == 14916 || getPet().getNpcId() == 14917))
      {
        getPet().unSummon();
      }
    }
  }

  public boolean isCombatFlagEquipped()
  {
    L2ItemInstance weapon = getActiveWeaponInstance();
    return weapon != null && weapon.getItem().isCombatFlag();
  }

  public boolean isTerritoryFlagEquipped()
  {
    L2ItemInstance weapon = getActiveWeaponInstance();
    return weapon != null && weapon.getItem().isTerritoryFlag();
  }
  private int _buyListId;

  public void setBuyListId(int listId)
  {
    _buyListId = listId;
  }

  public int getBuyListId()
  {
    return _buyListId;
  }

  public boolean checksForShop(boolean RequestManufacture)
  {
    if(!getPlayerAccess().UseTrade)
    {
      sendPacket(Msg.THIS_ACCOUNT_CANOT_USE_PRIVATE_STORES);
      return false;
    }
    String tradeBan = getVar("tradeBan");
    if(tradeBan != null && (tradeBan.equals("-1") || Long.parseLong(tradeBan) >= System.currentTimeMillis()))
    {
      sendMessage("Your trade is banned! Expires: " + (tradeBan.equals("-1") ? "never" : Util.formatTime((Long.parseLong(tradeBan) - System.currentTimeMillis()) / 1000)) + ".");
      return false;
    }
    String BLOCK_ZONE = RequestManufacture ? L2Zone.BLOCKED_ACTION_PRIVATE_WORKSHOP : L2Zone.BLOCKED_ACTION_PRIVATE_STORE;
    if(isActionBlocked(BLOCK_ZONE) && !isInStoreMode())
    {
      if(!Config.SERVICES_NO_TRADE_ONLY_OFFLINE || Config.SERVICES_NO_TRADE_ONLY_OFFLINE && isInOfflineMode())
      {
        sendPacket(RequestManufacture ? new SystemMessage(SystemMessage.A_PRIVATE_WORKSHOP_MAY_NOT_BE_OPENED_IN_THIS_AREA) : Msg.A_PRIVATE_STORE_MAY_NOT_BE_OPENED_IN_THIS_AREA);
        return false;
      }
    }
    if(isCastingNow())
    {
      sendPacket(Msg.A_PRIVATE_STORE_MAY_NOT_BE_OPENED_WHILE_USING_A_SKILL);
      return false;
    }
    if(isInCombat())
    {
      sendPacket(Msg.WHILE_YOU_ARE_ENGAGED_IN_COMBAT_YOU_CANNOT_OPERATE_A_PRIVATE_STORE_OR_PRIVATE_WORKSHOP);
      return false;
    }
    if(isOutOfControl() || isActionsDisabled() || isMounted() || isInOlympiadMode() || getDuel() != null)
    {
      return false;
    }
    if(Config.SERVICES_TRADE_ONLY_FAR && !isInStoreMode())
    {
      boolean tradenear = false;
      for(L2Player player : L2World.getAroundPlayers(this, Config.SERVICES_TRADE_RADIUS, 200))
      {
        if(player.isInStoreMode())
        {
          tradenear = true;
          break;
        }
      }
      if(L2World.getAroundNpc(this, Config.SERVICES_TRADE_RADIUS + 100, 200).size() > 0)
      {
        tradenear = true;
      }
      if(tradenear)
      {
        sendMessage(new CustomMessage("trade.OtherTradersNear", this));
        return false;
      }
    }
    return true;
  }

  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 getVitalityLevel()
  {
    return Config.ALT_VITALITY_ENABLED ? _vitalityLevel : 0;
  }

  public float getVitalityBonus()
  {
    float bonus;
    switch(getVitalityLevel())
    {
      case 4:
        bonus = 3;
        break;
      case 3:
        bonus = 2.5f;
        break;
      case 2:
        bonus = 2;
        break;
      case 1:
        bonus = 1.5f;
        break;
      default:
        bonus = 1;
    }
    return bonus;
  }

  public double getVitality()
  {
    return Config.ALT_VITALITY_ENABLED ? _vitality : 0;
  }

  public void setVitality(double newVitality)
  {
    if(!Config.ALT_VITALITY_ENABLED)
    {
      return;
    }
    newVitality = Math.max(Math.min(newVitality, 10000), 0);
    if(newVitality >= _vitality || getLevel() >= 10)
    {
      if(newVitality != _vitality)
      {
        if(newVitality == 0)
        {
          sendPacket(Msg.VITALITY_IS_FULLY_EXHAUSTED);
        }
        else if(newVitality == 10000)
        {
          sendPacket(Msg.YOUR_VITALITY_IS_AT_MAXIMUM);
        }
      }
      _vitality = newVitality;
    }
    int newLevel = 0;
    if(_vitality >= 8500)
    {
      newLevel = 4;
    }
    else if(_vitality >= 6500)
    {
      newLevel = 3;
    }
    else if(_vitality >= 1000)
    {
      newLevel = 2;
    }
    else if(_vitality >= 120)
    {
      newLevel = 1;
    }
    if(_vitalityLevel != newLevel)
    {
      if(_vitalityLevel != -1) // при ините чара сообщения не шлём
      {
        sendPacket(newLevel < _vitalityLevel ? Msg.VITALITY_HAS_DECREASED : Msg.VITALITY_HAS_INCREASED);
      }
      _vitalityLevel = newLevel;
      sendUserInfo(false);
    }
  }

  public double[] applyVitality(L2MonsterInstance monster, double xp, double sp, double partyVitalityMod)
  {
    float vitalitybonus = (monster.isRaid() ? 1 : getVitalityBonus());
    if(xp > 0)
    {
      xp *= Config.RATE_XP * getRateExp() * vitalitybonus;
    }
    if(sp > 0)
    {
      sp *= Config.RATE_SP * getRateSp() * vitalitybonus;
    }
    if(xp > 0)
    {
      if(!monster.isRaid())
      {
        if(!(getVarB("NoExp") && getExp() == Experience.LEVEL[getLevel() + 1] - 1))
        {
          double mod = Experience.baseVitalityMod(getLevel(), getLevel(), monster.getExpReward());
          if(getEffectList().getEffectByType(EffectType.Vitality) != null)
          {
            mod *= -1;
          }
          setVitality(getVitality() - mod * partyVitalityMod);
        }
      }
      else
      {
        setVitality(getVitality() + Config.ALT_VITALITY_RAID_BONUS);
      }
    }
    return new double[]
    {
      xp, sp
    };
  }
  private final int _incorrectValidateCount = 0;

  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()
  {
    setTarget(null);
    stopMove();
    setIsInvul(true);
    setImobilised(true);
    sendPacket(new CameraMode(1));
  }

  public void leaveMovieMode()
  {
    if(!isGM())
    {
      setIsInvul(false);
    }
    setImobilised(false);
    sendPacket(new CameraMode(0));
    broadcastUserInfo(true);
  }

  public void specialCamera(L2Object target, int dist, int yaw, int pitch, int time, int duration)
  {
    sendPacket(new SpecialCamera(target.getObjectId(), dist, yaw, pitch, time, duration));
  }
  private int _movieId = 0;

  public void setMovieId(int id)
  {
    _movieId = id;
  }

  public int getMovieId()
  {
    return _movieId;
  }

  public void showQuestMovie(int id)
  {
    if(_movieId > 0) //already in movie
    {
      return;
    }
    sendActionFailed();
    setTarget(null);
    stopMove();
    _movieId = id;
    sendPacket(new ExStartScenePlayer(id));
  }

  public void setAutoLoot(boolean enable)
  {
    if(Config.AUTO_LOOT_INDIVIDUAL)
    {
      AutoLoot = enable;
      setVar("AutoLoot", String.valueOf(enable));
    }
  }

  public void setAutoLootHerbs(boolean enable)
  {
    if(Config.AUTO_LOOT_INDIVIDUAL)
    {
      AutoLootHerbs = enable;
      setVar("AutoLootHerbs", String.valueOf(enable));
    }
  }

  public boolean isAutoLootEnabled()
  {
    return AutoLoot;
  }

  public boolean isAutoLootHerbsEnabled()
  {
    return AutoLootHerbs;
  }

  public final void reName(String name, boolean saveToDB)
  {
    setName(name);
    if(saveToDB)
    {
      saveNameToDB();
    }
    Olympiad.changeNobleName(getObjectId(), name);
    broadcastUserInfo(true);
  }

  public final void reName(String name)
  {
    reName(name, false);
  }

  public final void saveNameToDB()
  {
    ThreadConnection con = null;
    FiltredPreparedStatement st = null;
    try
    {
      con = L2DatabaseFactory.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)
    {
      e.printStackTrace();
    }
    finally
    {
      DatabaseUtils.closeDatabaseCS(con, st);
    }
  }

  @Override
  public L2Player getPlayer()
  {
    return this;
  }

  private GArray<String> getStoredBypasses(boolean bbs)
  {
    if(bbs)
    {
      if(bypasses_bbs == null)
      {
        bypasses_bbs = new GArray<String>();
      }
      return bypasses_bbs;
    }
    if(bypasses == null)
    {
      bypasses = new GArray<String>();
    }
    return bypasses;
  }

  public void cleanBypasses(boolean bbs)
  {
    GArray<String> bypassStorage = getStoredBypasses(bbs);
    synchronized(bypassStorage)
    {
      bypassStorage.clearSize();
    }
  }

  /**
   * Сброс реюза всех скилов персонажа.
   */
  public void resetSkillsReuse()
  {
    getSkillReuseTimeStamps().clear();
    sendPacket(new SkillCoolTime(this));
  }
  private int _territorySide = -1;

  public void setTerritorySiege(int side)
  {
    _territorySide = side;
  }

  public int getTerritorySiege()
  {
    L2Clan clan = getClan();
    if(clan != null && clan.getTerritorySiege() > -1)
    {
      return clan.getTerritorySiege();
    }
    if(_territorySide > -1)
    {
      return _territorySide;
    }
    return -1;
  }
  //--------------------------------------------------------------------------------------------------------------
  public FastList<OneScheme> schemes;

  public OneScheme getOneSchemeByName(String name)
  {
    for(OneScheme oneScheme : schemes)
    {
      if(oneScheme.getName().equals(name))
      {
        return oneScheme;
      }
    }
    return null;
  }
  public FastList<Buff> temp_schemes;
  private FastMap<String, String> _teleportLocations = new FastMap<String, String>();

  public FastMap<String, String> getTeleportLocations()
  {
    return _teleportLocations;
  }
  private final long[] _actionTimers = new long[FloodProtector.Action.VALUES_LENGTH];

  public long[] getActionTimers()
  {
    return _actionTimers;
  }
  private boolean isEvent = false;

  public void setEvent(boolean isEvent)
  {
    this.isEvent = isEvent;
  }

  public boolean isEvent()
  {
    return isEvent;
  }
}
TOP

Related Classes of l2p.gameserver.model.L2Player

TOP
Copyright © 2018 www.massapi.com. All rights reserved.
All source code are property of their respective owners. Java is a trademark of Sun Microsystems, Inc and owned by ORACLE Inc. Contact coftware#gmail.com.