· 9 years ago · Nov 05, 2016, 01:46 AM
1/*
2 * This program is free software: you can redistribute it and/or modify it under
3 * the terms of the GNU General Public License as published by the Free Software
4 * Foundation, either version 3 of the License, or (at your option) any later
5 * version.
6 *
7 * This program is distributed in the hope that it will be useful, but WITHOUT
8 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
9 * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
10 * details.
11 *
12 * You should have received a copy of the GNU General Public License along with
13 * this program. If not, see <http://www.gnu.org/licenses/>.
14 */
15package net.sf.l2j.gameserver.model.actor.instance;
16
17import java.sql.Connection;
18import java.sql.PreparedStatement;
19import java.sql.ResultSet;
20import java.util.ArrayList;
21import java.util.Arrays;
22import java.util.Calendar;
23import java.util.Collection;
24import java.util.HashMap;
25import java.util.LinkedHashMap;
26import java.util.List;
27import java.util.Map;
28import java.util.Set;
29import java.util.concurrent.ConcurrentHashMap;
30import java.util.concurrent.ConcurrentSkipListMap;
31import java.util.concurrent.Future;
32import java.util.concurrent.ScheduledFuture;
33import java.util.concurrent.TimeUnit;
34import java.util.concurrent.atomic.AtomicInteger;
35import java.util.concurrent.locks.ReentrantLock;
36import java.util.logging.Level;
37
38import net.sf.l2j.Config;
39import net.sf.l2j.L2DatabaseFactory;
40import net.sf.l2j.commons.random.Rnd;
41import net.sf.l2j.gameserver.LoginServerThread;
42import net.sf.l2j.gameserver.ThreadPoolManager;
43import net.sf.l2j.gameserver.ai.CtrlEvent;
44import net.sf.l2j.gameserver.ai.CtrlIntention;
45import net.sf.l2j.gameserver.ai.NextAction;
46import net.sf.l2j.gameserver.ai.model.L2CharacterAI;
47import net.sf.l2j.gameserver.ai.model.L2PlayerAI;
48import net.sf.l2j.gameserver.ai.model.L2SummonAI;
49import net.sf.l2j.gameserver.communitybbs.BB.Forum;
50import net.sf.l2j.gameserver.communitybbs.Manager.ForumsBBSManager;
51import net.sf.l2j.gameserver.datatables.AccessLevels;
52import net.sf.l2j.gameserver.datatables.CharNameTable;
53import net.sf.l2j.gameserver.datatables.CharTemplateTable;
54import net.sf.l2j.gameserver.datatables.ClanTable;
55import net.sf.l2j.gameserver.datatables.FishTable;
56import net.sf.l2j.gameserver.datatables.GmListTable;
57import net.sf.l2j.gameserver.datatables.HennaTable;
58import net.sf.l2j.gameserver.datatables.ItemTable;
59import net.sf.l2j.gameserver.datatables.MapRegionTable;
60import net.sf.l2j.gameserver.datatables.PetDataTable;
61import net.sf.l2j.gameserver.datatables.RecipeTable;
62import net.sf.l2j.gameserver.datatables.SkillTable;
63import net.sf.l2j.gameserver.datatables.SkillTable.FrequentSkill;
64import net.sf.l2j.gameserver.datatables.SkillTreeTable;
65import net.sf.l2j.gameserver.geoengine.PathFinding;
66import net.sf.l2j.gameserver.handler.IItemHandler;
67import net.sf.l2j.gameserver.handler.ItemHandler;
68import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminEditChar;
69import net.sf.l2j.gameserver.instancemanager.CastleManager;
70import net.sf.l2j.gameserver.instancemanager.CoupleManager;
71import net.sf.l2j.gameserver.instancemanager.CursedWeaponsManager;
72import net.sf.l2j.gameserver.instancemanager.DimensionalRiftManager;
73import net.sf.l2j.gameserver.instancemanager.DuelManager;
74import net.sf.l2j.gameserver.instancemanager.GrandBossManager;
75import net.sf.l2j.gameserver.instancemanager.SevenSigns;
76import net.sf.l2j.gameserver.instancemanager.SevenSignsFestival;
77import net.sf.l2j.gameserver.instancemanager.SiegeManager;
78import net.sf.l2j.gameserver.model.BlockList;
79import net.sf.l2j.gameserver.model.FishData;
80import net.sf.l2j.gameserver.model.L2AccessLevel;
81import net.sf.l2j.gameserver.model.L2CharPosition;
82import net.sf.l2j.gameserver.model.L2Clan;
83import net.sf.l2j.gameserver.model.L2ClanMember;
84import net.sf.l2j.gameserver.model.L2Effect;
85import net.sf.l2j.gameserver.model.L2Fishing;
86import net.sf.l2j.gameserver.model.L2Macro;
87import net.sf.l2j.gameserver.model.L2ManufactureList;
88import net.sf.l2j.gameserver.model.L2Object;
89import net.sf.l2j.gameserver.model.L2Party;
90import net.sf.l2j.gameserver.model.L2Party.MessageType;
91import net.sf.l2j.gameserver.model.L2PetData;
92import net.sf.l2j.gameserver.model.L2PetData.L2PetLevelData;
93import net.sf.l2j.gameserver.model.L2Radar;
94import net.sf.l2j.gameserver.model.L2Request;
95import net.sf.l2j.gameserver.model.L2ShortCut;
96import net.sf.l2j.gameserver.model.L2Skill;
97import net.sf.l2j.gameserver.model.L2Skill.SkillTargetType;
98import net.sf.l2j.gameserver.model.L2SkillLearn;
99import net.sf.l2j.gameserver.model.L2World;
100import net.sf.l2j.gameserver.model.L2WorldRegion;
101import net.sf.l2j.gameserver.model.Location;
102import net.sf.l2j.gameserver.model.MacroList;
103import net.sf.l2j.gameserver.model.ShortCuts;
104import net.sf.l2j.gameserver.model.ShotType;
105import net.sf.l2j.gameserver.model.actor.L2Attackable;
106import net.sf.l2j.gameserver.model.actor.L2Character;
107import net.sf.l2j.gameserver.model.actor.L2Npc;
108import net.sf.l2j.gameserver.model.actor.L2Playable;
109import net.sf.l2j.gameserver.model.actor.L2Summon;
110import net.sf.l2j.gameserver.model.actor.L2Vehicle;
111import net.sf.l2j.gameserver.model.actor.appearance.PcAppearance;
112import net.sf.l2j.gameserver.model.actor.knownlist.PcKnownList;
113import net.sf.l2j.gameserver.model.actor.position.PcPosition;
114import net.sf.l2j.gameserver.model.actor.stat.PcStat;
115import net.sf.l2j.gameserver.model.actor.status.PcStatus;
116import net.sf.l2j.gameserver.model.actor.template.PcTemplate;
117import net.sf.l2j.gameserver.model.base.ClassId;
118import net.sf.l2j.gameserver.model.base.ClassLevel;
119import net.sf.l2j.gameserver.model.base.Experience;
120import net.sf.l2j.gameserver.model.base.PlayerClass;
121import net.sf.l2j.gameserver.model.base.Race;
122import net.sf.l2j.gameserver.model.base.SubClass;
123import net.sf.l2j.gameserver.model.entity.Castle;
124import net.sf.l2j.gameserver.model.entity.Duel.DuelState;
125import net.sf.l2j.gameserver.model.entity.Hero;
126import net.sf.l2j.gameserver.model.entity.Siege;
127import net.sf.l2j.gameserver.model.holder.IntIntHolder;
128import net.sf.l2j.gameserver.model.holder.SkillUseHolder;
129import net.sf.l2j.gameserver.model.item.Henna;
130import net.sf.l2j.gameserver.model.item.RecipeList;
131import net.sf.l2j.gameserver.model.item.instance.ItemInstance;
132import net.sf.l2j.gameserver.model.item.kind.Armor;
133import net.sf.l2j.gameserver.model.item.kind.Item;
134import net.sf.l2j.gameserver.model.item.kind.Weapon;
135import net.sf.l2j.gameserver.model.item.type.ActionType;
136import net.sf.l2j.gameserver.model.item.type.ArmorType;
137import net.sf.l2j.gameserver.model.item.type.EtcItemType;
138import net.sf.l2j.gameserver.model.item.type.WeaponType;
139import net.sf.l2j.gameserver.model.itemcontainer.Inventory;
140import net.sf.l2j.gameserver.model.itemcontainer.ItemContainer;
141import net.sf.l2j.gameserver.model.itemcontainer.PcFreight;
142import net.sf.l2j.gameserver.model.itemcontainer.PcInventory;
143import net.sf.l2j.gameserver.model.itemcontainer.PcWarehouse;
144import net.sf.l2j.gameserver.model.itemcontainer.PetInventory;
145import net.sf.l2j.gameserver.model.itemcontainer.listeners.ItemPassiveSkillsListener;
146import net.sf.l2j.gameserver.model.memo.PlayerMemo;
147import net.sf.l2j.gameserver.model.olympiad.OlympiadGameManager;
148import net.sf.l2j.gameserver.model.olympiad.OlympiadGameTask;
149import net.sf.l2j.gameserver.model.olympiad.OlympiadManager;
150import net.sf.l2j.gameserver.model.partymatching.PartyMatchRoom;
151import net.sf.l2j.gameserver.model.partymatching.PartyMatchRoomList;
152import net.sf.l2j.gameserver.model.partymatching.PartyMatchWaitingList;
153import net.sf.l2j.gameserver.model.tradelist.TradeList;
154import net.sf.l2j.gameserver.model.zone.ZoneId;
155import net.sf.l2j.gameserver.model.zone.type.L2BossZone;
156import net.sf.l2j.gameserver.network.L2GameClient;
157import net.sf.l2j.gameserver.network.SystemMessageId;
158import net.sf.l2j.gameserver.network.serverpackets.AbstractNpcInfo;
159import net.sf.l2j.gameserver.network.serverpackets.ActionFailed;
160import net.sf.l2j.gameserver.network.serverpackets.ChairSit;
161import net.sf.l2j.gameserver.network.serverpackets.ChangeWaitType;
162import net.sf.l2j.gameserver.network.serverpackets.CharInfo;
163import net.sf.l2j.gameserver.network.serverpackets.ConfirmDlg;
164import net.sf.l2j.gameserver.network.serverpackets.EtcStatusUpdate;
165import net.sf.l2j.gameserver.network.serverpackets.ExAutoSoulShot;
166import net.sf.l2j.gameserver.network.serverpackets.ExDuelUpdateUserInfo;
167import net.sf.l2j.gameserver.network.serverpackets.ExFishingEnd;
168import net.sf.l2j.gameserver.network.serverpackets.ExFishingStart;
169import net.sf.l2j.gameserver.network.serverpackets.ExOlympiadMode;
170import net.sf.l2j.gameserver.network.serverpackets.ExSetCompassZoneCode;
171import net.sf.l2j.gameserver.network.serverpackets.ExStorageMaxCount;
172import net.sf.l2j.gameserver.network.serverpackets.FriendList;
173import net.sf.l2j.gameserver.network.serverpackets.GetOnVehicle;
174import net.sf.l2j.gameserver.network.serverpackets.HennaInfo;
175import net.sf.l2j.gameserver.network.serverpackets.InventoryUpdate;
176import net.sf.l2j.gameserver.network.serverpackets.ItemList;
177import net.sf.l2j.gameserver.network.serverpackets.L2GameServerPacket;
178import net.sf.l2j.gameserver.network.serverpackets.LeaveWorld;
179import net.sf.l2j.gameserver.network.serverpackets.MagicSkillUse;
180import net.sf.l2j.gameserver.network.serverpackets.MoveToPawn;
181import net.sf.l2j.gameserver.network.serverpackets.MyTargetSelected;
182import net.sf.l2j.gameserver.network.serverpackets.NpcHtmlMessage;
183import net.sf.l2j.gameserver.network.serverpackets.ObservationMode;
184import net.sf.l2j.gameserver.network.serverpackets.ObservationReturn;
185import net.sf.l2j.gameserver.network.serverpackets.PartySmallWindowUpdate;
186import net.sf.l2j.gameserver.network.serverpackets.PetInventoryUpdate;
187import net.sf.l2j.gameserver.network.serverpackets.PlaySound;
188import net.sf.l2j.gameserver.network.serverpackets.PledgeShowMemberListDelete;
189import net.sf.l2j.gameserver.network.serverpackets.PledgeShowMemberListUpdate;
190import net.sf.l2j.gameserver.network.serverpackets.PrivateStoreListBuy;
191import net.sf.l2j.gameserver.network.serverpackets.PrivateStoreListSell;
192import net.sf.l2j.gameserver.network.serverpackets.PrivateStoreManageListBuy;
193import net.sf.l2j.gameserver.network.serverpackets.PrivateStoreManageListSell;
194import net.sf.l2j.gameserver.network.serverpackets.PrivateStoreMsgBuy;
195import net.sf.l2j.gameserver.network.serverpackets.PrivateStoreMsgSell;
196import net.sf.l2j.gameserver.network.serverpackets.RecipeShopManageList;
197import net.sf.l2j.gameserver.network.serverpackets.RecipeShopMsg;
198import net.sf.l2j.gameserver.network.serverpackets.RecipeShopSellList;
199import net.sf.l2j.gameserver.network.serverpackets.RelationChanged;
200import net.sf.l2j.gameserver.network.serverpackets.Ride;
201import net.sf.l2j.gameserver.network.serverpackets.SendTradeDone;
202import net.sf.l2j.gameserver.network.serverpackets.ServerClose;
203import net.sf.l2j.gameserver.network.serverpackets.SetupGauge;
204import net.sf.l2j.gameserver.network.serverpackets.ShortBuffStatusUpdate;
205import net.sf.l2j.gameserver.network.serverpackets.ShortCutInit;
206import net.sf.l2j.gameserver.network.serverpackets.SkillCoolTime;
207import net.sf.l2j.gameserver.network.serverpackets.SkillList;
208import net.sf.l2j.gameserver.network.serverpackets.SocialAction;
209import net.sf.l2j.gameserver.network.serverpackets.StaticObject;
210import net.sf.l2j.gameserver.network.serverpackets.StatusUpdate;
211import net.sf.l2j.gameserver.network.serverpackets.StopMove;
212import net.sf.l2j.gameserver.network.serverpackets.SystemMessage;
213import net.sf.l2j.gameserver.network.serverpackets.TargetSelected;
214import net.sf.l2j.gameserver.network.serverpackets.TargetUnselected;
215import net.sf.l2j.gameserver.network.serverpackets.TitleUpdate;
216import net.sf.l2j.gameserver.network.serverpackets.TradePressOtherOk;
217import net.sf.l2j.gameserver.network.serverpackets.TradePressOwnOk;
218import net.sf.l2j.gameserver.network.serverpackets.TradeStart;
219import net.sf.l2j.gameserver.network.serverpackets.UserInfo;
220import net.sf.l2j.gameserver.scripting.EventType;
221import net.sf.l2j.gameserver.scripting.Quest;
222import net.sf.l2j.gameserver.scripting.QuestState;
223import net.sf.l2j.gameserver.scripting.ScriptManager;
224import net.sf.l2j.gameserver.skills.Env;
225import net.sf.l2j.gameserver.skills.Formulas;
226import net.sf.l2j.gameserver.skills.Stats;
227import net.sf.l2j.gameserver.skills.effects.EffectTemplate;
228import net.sf.l2j.gameserver.skills.funcs.FuncHennaCON;
229import net.sf.l2j.gameserver.skills.funcs.FuncHennaDEX;
230import net.sf.l2j.gameserver.skills.funcs.FuncHennaINT;
231import net.sf.l2j.gameserver.skills.funcs.FuncHennaMEN;
232import net.sf.l2j.gameserver.skills.funcs.FuncHennaSTR;
233import net.sf.l2j.gameserver.skills.funcs.FuncHennaWIT;
234import net.sf.l2j.gameserver.skills.funcs.FuncMaxCpMul;
235import net.sf.l2j.gameserver.skills.l2skills.L2SkillSiegeFlag;
236import net.sf.l2j.gameserver.skills.l2skills.L2SkillSummon;
237import net.sf.l2j.gameserver.taskmanager.AttackStanceTaskManager;
238import net.sf.l2j.gameserver.taskmanager.GameTimeTaskManager;
239import net.sf.l2j.gameserver.taskmanager.ItemsOnGroundTaskManager;
240import net.sf.l2j.gameserver.taskmanager.PvpFlagTaskManager;
241import net.sf.l2j.gameserver.taskmanager.ShadowItemTaskManager;
242import net.sf.l2j.gameserver.taskmanager.WaterTaskManager;
243import net.sf.l2j.gameserver.templates.skills.L2EffectFlag;
244import net.sf.l2j.gameserver.templates.skills.L2EffectType;
245import net.sf.l2j.gameserver.templates.skills.L2SkillType;
246import net.sf.l2j.gameserver.util.Broadcast;
247import net.sf.l2j.gameserver.util.Util;
248
249/**
250 * This class represents all player characters in the world. There is always a client-thread connected to this (except if a player-store is activated upon logout).
251 */
252public final class L2PcInstance extends L2Playable
253{
254 public enum PrivateStoreType
255 {
256 NONE(0),
257 SELL(1),
258 SELL_MANAGE(2),
259 BUY(3),
260 BUY_MANAGE(4),
261 MANUFACTURE(5),
262 PACKAGE_SELL(8);
263
264 private int _id;
265
266 private PrivateStoreType(int id)
267 {
268 _id = id;
269 }
270
271 public int getId()
272 {
273 return _id;
274 }
275 }
276
277 public enum PunishLevel
278 {
279 NONE(0, ""),
280 CHAT(1, "chat banned"),
281 JAIL(2, "jailed"),
282 CHAR(3, "banned"),
283 ACC(4, "banned");
284
285 private final int punValue;
286 private final String punString;
287
288 PunishLevel(int value, String string)
289 {
290 punValue = value;
291 punString = string;
292 }
293
294 public int value()
295 {
296 return punValue;
297 }
298
299 public String string()
300 {
301 return punString;
302 }
303 }
304
305 private static final String RESTORE_SKILLS_FOR_CHAR = "SELECT skill_id,skill_level FROM character_skills WHERE char_obj_id=? AND class_index=?";
306 private static final String ADD_NEW_SKILL = "INSERT INTO character_skills (char_obj_id,skill_id,skill_level,class_index) VALUES (?,?,?,?)";
307 private static final String UPDATE_CHARACTER_SKILL_LEVEL = "UPDATE character_skills SET skill_level=? WHERE skill_id=? AND char_obj_id=? AND class_index=?";
308 private static final String DELETE_SKILL_FROM_CHAR = "DELETE FROM character_skills WHERE skill_id=? AND char_obj_id=? AND class_index=?";
309 private static final String DELETE_CHAR_SKILLS = "DELETE FROM character_skills WHERE char_obj_id=? AND class_index=?";
310
311 private static final String ADD_SKILL_SAVE = "INSERT INTO character_skills_save (char_obj_id,skill_id,skill_level,effect_count,effect_cur_time,reuse_delay,systime,restore_type,class_index,buff_index) VALUES (?,?,?,?,?,?,?,?,?,?)";
312 private static final String RESTORE_SKILL_SAVE = "SELECT skill_id,skill_level,effect_count,effect_cur_time, reuse_delay, systime, restore_type FROM character_skills_save WHERE char_obj_id=? AND class_index=? ORDER BY buff_index ASC";
313 private static final String DELETE_SKILL_SAVE = "DELETE FROM character_skills_save WHERE char_obj_id=? AND class_index=?";
314
315 private static final String INSERT_CHARACTER = "INSERT INTO characters (account_name,obj_Id,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,face,hairStyle,hairColor,sex,exp,sp,karma,pvpkills,pkkills,clanid,race,classid,deletetime,cancraft,title,accesslevel,online,isin7sdungeon,clan_privs,wantspeace,base_class,nobless,power_grade,last_recom_date) VALUES (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
316 private static final String UPDATE_CHARACTER = "UPDATE characters SET level=?,maxHp=?,curHp=?,maxCp=?,curCp=?,maxMp=?,curMp=?,face=?,hairStyle=?,hairColor=?,sex=?,heading=?,x=?,y=?,z=?,exp=?,expBeforeDeath=?,sp=?,karma=?,pvpkills=?,pkkills=?,rec_have=?,rec_left=?,clanid=?,race=?,classid=?,deletetime=?,title=?,accesslevel=?,online=?,isin7sdungeon=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,punish_level=?,punish_timer=?,nobless=?,power_grade=?,subpledge=?,last_recom_date=?,lvl_joined_academy=?,apprentice=?,sponsor=?,varka_ketra_ally=?,clan_join_expiry_time=?,clan_create_expiry_time=?,char_name=?,death_penalty_level=?,ffaction=?,sfaction=? WHERE obj_id=?";
317 private static final String RESTORE_CHARACTER = "SELECT account_name, obj_Id, char_name, level, maxHp, curHp, maxCp, curCp, maxMp, curMp, face, hairStyle, hairColor, sex, heading, x, y, z, exp, expBeforeDeath, sp, karma, pvpkills, pkkills, clanid, race, classid, deletetime, cancraft, title, rec_have, rec_left, accesslevel, online, char_slot, lastAccess, clan_privs, wantspeace, base_class, onlinetime, isin7sdungeon, punish_level, punish_timer, nobless, power_grade, subpledge, last_recom_date, lvl_joined_academy, apprentice, sponsor, varka_ketra_ally,clan_join_expiry_time,clan_create_expiry_time,death_penalty_level,ffaction,sfaction FROM characters WHERE obj_id=?";
318
319 private static final String RESTORE_CHAR_SUBCLASSES = "SELECT class_id,exp,sp,level,class_index FROM character_subclasses WHERE char_obj_id=? ORDER BY class_index ASC";
320 private static final String ADD_CHAR_SUBCLASS = "INSERT INTO character_subclasses (char_obj_id,class_id,exp,sp,level,class_index) VALUES (?,?,?,?,?,?)";
321 private static final String UPDATE_CHAR_SUBCLASS = "UPDATE character_subclasses SET exp=?,sp=?,level=?,class_id=? WHERE char_obj_id=? AND class_index =?";
322 private static final String DELETE_CHAR_SUBCLASS = "DELETE FROM character_subclasses WHERE char_obj_id=? AND class_index=?";
323
324 private static final String RESTORE_CHAR_HENNAS = "SELECT slot,symbol_id FROM character_hennas WHERE char_obj_id=? AND class_index=?";
325 private static final String ADD_CHAR_HENNA = "INSERT INTO character_hennas (char_obj_id,symbol_id,slot,class_index) VALUES (?,?,?,?)";
326 private static final String DELETE_CHAR_HENNA = "DELETE FROM character_hennas WHERE char_obj_id=? AND slot=? AND class_index=?";
327 private static final String DELETE_CHAR_HENNAS = "DELETE FROM character_hennas WHERE char_obj_id=? AND class_index=?";
328 private static final String DELETE_CHAR_SHORTCUTS = "DELETE FROM character_shortcuts WHERE char_obj_id=? AND class_index=?";
329
330 private static final String RESTORE_CHAR_RECOMS = "SELECT char_id,target_id FROM character_recommends WHERE char_id=?";
331 private static final String ADD_CHAR_RECOM = "INSERT INTO character_recommends (char_id,target_id) VALUES (?,?)";
332 private static final String DELETE_CHAR_RECOMS = "DELETE FROM character_recommends WHERE char_id=?";
333
334 private static final String UPDATE_NOBLESS = "UPDATE characters SET nobless=? WHERE obj_Id=?";
335
336 public static final int REQUEST_TIMEOUT = 15;
337
338 private static final int[] EXPERTISE_LEVELS =
339 {
340 0, // NONE
341 20, // D
342 40, // C
343 52, // B
344 61, // A
345 76, // S
346 };
347
348 private static final int[] COMMON_CRAFT_LEVELS =
349 {
350 5,
351 20,
352 28,
353 36,
354 43,
355 49,
356 55,
357 62
358 };
359
360 private L2GameClient _client;
361
362 private String _accountName;
363 private long _deleteTimer;
364
365 private boolean _isOnline = false;
366 private long _onlineTime;
367 private long _onlineBeginTime;
368 private long _lastAccess;
369 private long _uptime;
370
371 private final ReentrantLock _subclassLock = new ReentrantLock();
372 protected int _baseClass;
373 protected int _activeClass;
374 protected int _classIndex = 0;
375 private final Map<Integer, SubClass> _subClasses = new ConcurrentSkipListMap<>();
376
377 private PcAppearance _appearance;
378
379 private long _expBeforeDeath;
380 private int _karma;
381 private int _pvpKills;
382 private int _pkKills;
383 private byte _pvpFlag;
384 private byte _siegeState = 0;
385 private int _curWeightPenalty = 0;
386
387 private int _lastCompassZone; // the last compass zone update send to the client
388
389 private boolean _isInWater;
390 private boolean _isIn7sDungeon = false;
391
392 private PunishLevel _punishLevel = PunishLevel.NONE;
393 private long _punishTimer = 0;
394 private ScheduledFuture<?> _punishTask;
395
396 private boolean _inOlympiadMode = false;
397 private boolean _OlympiadStart = false;
398 private int _olympiadGameId = -1;
399 private int _olympiadSide = -1;
400
401 private boolean _isInDuel = false;
402 private DuelState _duelState = DuelState.NO_DUEL;
403 private int _duelId = 0;
404 private SystemMessageId _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL;
405
406 private L2Vehicle _vehicle = null;
407 private Location _inVehiclePosition;
408
409 public ScheduledFuture<?> _taskforfish;
410
411 private int _mountType;
412 private int _mountNpcId;
413 private int _mountLevel;
414 private int _mountObjectID = 0;
415
416 public int _telemode = 0;
417 private boolean _inCrystallize;
418 private boolean _inCraftMode;
419
420 private final Map<Integer, RecipeList> _dwarvenRecipeBook = new HashMap<>();
421 private final Map<Integer, RecipeList> _commonRecipeBook = new HashMap<>();
422
423 private boolean _waitTypeSitting;
424
425 private final Location _savedLocation = new Location(0, 0, 0);
426 private boolean _observerMode = false;
427
428 private int _recomHave;
429 private int _recomLeft;
430 private long _lastRecomUpdate;
431 private final List<Integer> _recomChars = new ArrayList<>();
432
433 private final PcInventory _inventory = new PcInventory(this);
434 private PcWarehouse _warehouse;
435 private PcFreight _freight;
436 private final List<PcFreight> _depositedFreight = new ArrayList<>();
437
438 private PrivateStoreType _privateStoreType = PrivateStoreType.NONE;
439
440 private TradeList _activeTradeList;
441 private ItemContainer _activeWarehouse;
442 private L2ManufactureList _createList;
443 private TradeList _sellList;
444 private TradeList _buyList;
445
446 private boolean _noble = false;
447 private boolean _hero = false;
448
449 private boolean _isffaction = false;
450 private boolean _issfaction = false;
451
452 private L2Npc _currentFolkNpc = null;
453
454 private int _questNpcObject = 0;
455
456 private final List<QuestState> _quests = new ArrayList<>();
457 private final List<QuestState> _notifyQuestOfDeathList = new ArrayList<>();
458
459 private final PlayerMemo _vars = new PlayerMemo(getObjectId());
460
461 private final ShortCuts _shortCuts = new ShortCuts(this);
462
463 private final MacroList _macroses = new MacroList(this);
464
465 private ClassId _skillLearningClassId;
466
467 private final Henna[] _henna = new Henna[3];
468 private int _hennaSTR;
469 private int _hennaINT;
470 private int _hennaDEX;
471 private int _hennaMEN;
472 private int _hennaWIT;
473 private int _hennaCON;
474
475 private L2Summon _summon = null;
476 private L2TamedBeastInstance _tamedBeast = null;
477
478 // TODO: This needs to be better integrated and saved/loaded
479 private L2Radar _radar;
480
481 private int _partyroom = 0;
482
483 private int _clanId;
484 private L2Clan _clan;
485 private int _apprentice = 0;
486 private int _sponsor = 0;
487 private long _clanJoinExpiryTime;
488 private long _clanCreateExpiryTime;
489 private int _powerGrade = 0;
490 private int _clanPrivileges = 0;
491 private int _pledgeClass = 0;
492 private int _pledgeType = 0;
493 private int _lvlJoinedAcademy = 0;
494
495 private boolean _wantsPeace;
496
497 private int _deathPenaltyBuffLevel = 0;
498
499 private final AtomicInteger _charges = new AtomicInteger();
500 private ScheduledFuture<?> _chargeTask = null;
501
502 private Location _currentSkillWorldPosition;
503
504 private L2AccessLevel _accessLevel;
505
506 private boolean _messageRefusal = false; // message refusal mode
507 private boolean _tradeRefusal = false; // Trade refusal
508 private boolean _exchangeRefusal = false; // Exchange refusal
509
510 private L2Party _party;
511
512 private L2PcInstance _activeRequester;
513 private long _requestExpireTime = 0;
514 private final L2Request _request = new L2Request(this);
515
516 private ItemInstance _arrowItem;
517
518 private ScheduledFuture<?> _protectTask = null;
519
520 private long _recentFakeDeathEndTime = 0;
521 private boolean _isFakeDeath;
522
523 private Weapon _fistsWeaponItem;
524
525 private final Map<Integer, String> _chars = new HashMap<>();
526
527 private int _expertiseIndex;
528 private int _expertiseArmorPenalty = 0;
529 private boolean _expertiseWeaponPenalty = false;
530
531 private ItemInstance _activeEnchantItem = null;
532
533 protected boolean _inventoryDisable = false;
534
535 protected Map<Integer, L2CubicInstance> _cubics = new ConcurrentSkipListMap<>();
536
537 protected Set<Integer> _activeSoulShots = ConcurrentHashMap.newKeySet(1);
538
539 private final int _loto[] = new int[5];
540 private final int _race[] = new int[2];
541
542 private final BlockList _blockList = new BlockList(this);
543
544 private int _team = 0;
545
546 private int _alliedVarkaKetra = 0; // lvl of alliance with ketra orcs or varka silenos, used in quests and aggro checks [-5,-1] varka, 0 neutral, [1,5] ketra
547
548 private Location _fishingLoc;
549 private ItemInstance _lure = null;
550 private L2Fishing _fishCombat;
551 private FishData _fish;
552
553 private final List<String> _validBypass = new ArrayList<>();
554 private final List<String> _validBypass2 = new ArrayList<>();
555
556 private Forum _forumMail;
557 private Forum _forumMemo;
558
559 private boolean _canFeed;
560 private L2PetData _data;
561 private L2PetLevelData _leveldata;
562 private int _controlItemId;
563 private int _curFeed;
564 protected Future<?> _mountFeedTask;
565 private ScheduledFuture<?> _dismountTask;
566
567 private boolean _isInSiege;
568
569 private final SkillUseHolder _currentSkill = new SkillUseHolder();
570 private final SkillUseHolder _currentPetSkill = new SkillUseHolder();
571 private final SkillUseHolder _queuedSkill = new SkillUseHolder();
572
573 private int _cursedWeaponEquippedId = 0;
574
575 private int _reviveRequested = 0;
576 private double _revivePower = 0;
577 private boolean _revivePet = false;
578
579 private double _cpUpdateIncCheck = .0;
580 private double _cpUpdateDecCheck = .0;
581 private double _cpUpdateInterval = .0;
582 private double _mpUpdateIncCheck = .0;
583 private double _mpUpdateDecCheck = .0;
584 private double _mpUpdateInterval = .0;
585
586 private volatile int _clientX;
587 private volatile int _clientY;
588 private volatile int _clientZ;
589 private volatile int _clientHeading;
590
591 private int _mailPosition;
592
593 private static final int FALLING_VALIDATION_DELAY = 10000;
594 private volatile long _fallingTimestamp = 0;
595
596 ScheduledFuture<?> _shortBuffTask = null;
597 private int _shortBuffTaskSkillId = 0;
598
599 private boolean _married = false;
600 private int _coupleId = 0;
601 private boolean _marryrequest = false;
602 private int _requesterId = 0;
603
604 private final SummonRequest _summonRequest = new SummonRequest();
605
606 private final GatesRequest _gatesRequest = new GatesRequest();
607
608 protected class ShortBuffTask implements Runnable
609 {
610 @Override
611 public void run()
612 {
613 sendPacket(new ShortBuffStatusUpdate(0, 0, 0));
614 setShortBuffTaskSkillId(0);
615 }
616 }
617
618 protected static class SummonRequest
619 {
620 private L2PcInstance _target = null;
621 private L2Skill _skill = null;
622
623 public void setTarget(L2PcInstance destination, L2Skill skill)
624 {
625 _target = destination;
626 _skill = skill;
627 }
628
629 public L2PcInstance getTarget()
630 {
631 return _target;
632 }
633
634 public L2Skill getSkill()
635 {
636 return _skill;
637 }
638 }
639
640 protected static class GatesRequest
641 {
642 private L2DoorInstance _target = null;
643
644 public void setTarget(L2DoorInstance door)
645 {
646 _target = door;
647 }
648
649 public L2DoorInstance getDoor()
650 {
651 return _target;
652 }
653 }
654
655 public void gatesRequest(L2DoorInstance door)
656 {
657 _gatesRequest.setTarget(door);
658 }
659
660 public void gatesAnswer(int answer, int type)
661 {
662 if (_gatesRequest.getDoor() == null)
663 return;
664
665 if (answer == 1 && getTarget() == _gatesRequest.getDoor() && type == 1)
666 _gatesRequest.getDoor().openMe();
667 else if (answer == 1 && getTarget() == _gatesRequest.getDoor() && type == 0)
668 _gatesRequest.getDoor().closeMe();
669
670 _gatesRequest.setTarget(null);
671 }
672
673 /**
674 * Create a new L2PcInstance and add it in the characters table of the database.
675 * <ul>
676 * <li>Create a new L2PcInstance with an account name</li>
677 * <li>Set the name, the Hair Style, the Hair Color and the Face type of the L2PcInstance</li>
678 * <li>Add the player in the characters table of the database</li>
679 * </ul>
680 * @param objectId Identifier of the object to initialized
681 * @param template The L2PcTemplate to apply to the L2PcInstance
682 * @param accountName The name of the L2PcInstance
683 * @param name The name of the L2PcInstance
684 * @param hairStyle The hair style Identifier of the L2PcInstance
685 * @param hairColor The hair color Identifier of the L2PcInstance
686 * @param face The face type Identifier of the L2PcInstance
687 * @param sex The sex type Identifier of the L2PcInstance
688 * @return The L2PcInstance added to the database or null
689 */
690 public static L2PcInstance create(int objectId, PcTemplate template, String accountName, String name, byte hairStyle, byte hairColor, byte face, boolean sex)
691 {
692 // Create a new L2PcInstance with an account name
693 PcAppearance app = new PcAppearance(face, hairColor, hairStyle, sex);
694 L2PcInstance player = new L2PcInstance(objectId, template, accountName, app);
695
696 // Set the name of the L2PcInstance
697 player.setName(name);
698
699 // Set the base class ID to that of the actual class ID.
700 player.setBaseClass(player.getClassId());
701
702 // Add the player in the characters table of the database
703 boolean ok = player.createDb();
704
705 if (!ok)
706 return null;
707
708 return player;
709 }
710
711 public String getAccountName()
712 {
713 return getClient().getAccountName();
714 }
715
716 public Map<Integer, String> getAccountChars()
717 {
718 return _chars;
719 }
720
721 public int getRelation(L2PcInstance target)
722 {
723 int result = 0;
724
725 // karma and pvp may not be required
726 if (getPvpFlag() != 0)
727 result |= RelationChanged.RELATION_PVP_FLAG;
728 if (getKarma() > 0)
729 result |= RelationChanged.RELATION_HAS_KARMA;
730
731 if (isClanLeader())
732 result |= RelationChanged.RELATION_LEADER;
733
734 if (getSiegeState() != 0)
735 {
736 result |= RelationChanged.RELATION_INSIEGE;
737 if (getSiegeState() != target.getSiegeState())
738 result |= RelationChanged.RELATION_ENEMY;
739 else
740 result |= RelationChanged.RELATION_ALLY;
741 if (getSiegeState() == 1)
742 result |= RelationChanged.RELATION_ATTACKER;
743 }
744
745 if (getClan() != null && target.getClan() != null)
746 {
747 if (target.getPledgeType() != L2Clan.SUBUNIT_ACADEMY && getPledgeType() != L2Clan.SUBUNIT_ACADEMY && target.getClan().isAtWarWith(getClan().getClanId()))
748 {
749 result |= RelationChanged.RELATION_1SIDED_WAR;
750 if (getClan().isAtWarWith(target.getClan().getClanId()))
751 result |= RelationChanged.RELATION_MUTUAL_WAR;
752 }
753 }
754 return result;
755 }
756
757 private void initPcStatusUpdateValues()
758 {
759 _cpUpdateInterval = getMaxCp() / 352.0;
760 _cpUpdateIncCheck = getMaxCp();
761 _cpUpdateDecCheck = getMaxCp() - _cpUpdateInterval;
762 _mpUpdateInterval = getMaxMp() / 352.0;
763 _mpUpdateIncCheck = getMaxMp();
764 _mpUpdateDecCheck = getMaxMp() - _mpUpdateInterval;
765 }
766
767 /**
768 * Constructor of L2PcInstance (use L2Character constructor).
769 * <ul>
770 * <li>Call the L2Character constructor to create an empty _skills slot and copy basic Calculator set to this L2PcInstance</li>
771 * <li>Set the name of the L2PcInstance</li>
772 * </ul>
773 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method SET the level of the L2PcInstance to 1</B></FONT>
774 * @param objectId Identifier of the object to initialized
775 * @param template The L2PcTemplate to apply to the L2PcInstance
776 * @param accountName The name of the account including this L2PcInstance
777 * @param app The PcAppearance of the L2PcInstance
778 */
779 private L2PcInstance(int objectId, PcTemplate template, String accountName, PcAppearance app)
780 {
781 super(objectId, template);
782 super.initCharStatusUpdateValues();
783 initPcStatusUpdateValues();
784
785 _accountName = accountName;
786 _appearance = app;
787
788 // Create an AI
789 _ai = new L2PlayerAI(this);
790
791 // Create a L2Radar object
792 _radar = new L2Radar(this);
793
794 // Retrieve from the database all items of this L2PcInstance and add them to _inventory
795 getInventory().restore();
796 getWarehouse();
797 getFreight();
798 }
799
800 private L2PcInstance(int objectId)
801 {
802 super(objectId, null);
803 super.initCharStatusUpdateValues();
804 initPcStatusUpdateValues();
805 }
806
807 @Override
808 public void addFuncsToNewCharacter()
809 {
810 // Add L2Character functionalities.
811 super.addFuncsToNewCharacter();
812
813 addStatFunc(FuncMaxCpMul.getInstance());
814
815 addStatFunc(FuncHennaSTR.getInstance());
816 addStatFunc(FuncHennaDEX.getInstance());
817 addStatFunc(FuncHennaINT.getInstance());
818 addStatFunc(FuncHennaMEN.getInstance());
819 addStatFunc(FuncHennaCON.getInstance());
820 addStatFunc(FuncHennaWIT.getInstance());
821 }
822
823 @Override
824 public void initKnownList()
825 {
826 setKnownList(new PcKnownList(this));
827 }
828
829 @Override
830 public final PcKnownList getKnownList()
831 {
832 return (PcKnownList) super.getKnownList();
833 }
834
835 @Override
836 public void initCharStat()
837 {
838 setStat(new PcStat(this));
839 }
840
841 @Override
842 public final PcStat getStat()
843 {
844 return (PcStat) super.getStat();
845 }
846
847 @Override
848 public void initCharStatus()
849 {
850 setStatus(new PcStatus(this));
851 }
852
853 @Override
854 public final PcStatus getStatus()
855 {
856 return (PcStatus) super.getStatus();
857 }
858
859 @Override
860 public void initPosition()
861 {
862 setObjectPosition(new PcPosition(this));
863 }
864
865 @Override
866 public PcPosition getPosition()
867 {
868 return (PcPosition) super.getPosition();
869 }
870
871 public final PcAppearance getAppearance()
872 {
873 return _appearance;
874 }
875
876 /**
877 * @return the base L2PcTemplate link to the L2PcInstance.
878 */
879 public final PcTemplate getBaseTemplate()
880 {
881 return CharTemplateTable.getInstance().getTemplate(_baseClass);
882 }
883
884 /** Return the L2PcTemplate link to the L2PcInstance. */
885 @Override
886 public final PcTemplate getTemplate()
887 {
888 return (PcTemplate) super.getTemplate();
889 }
890
891 public void setTemplate(ClassId newclass)
892 {
893 super.setTemplate(CharTemplateTable.getInstance().getTemplate(newclass));
894 }
895
896 /**
897 * Return the AI of the L2PcInstance (create it if necessary).
898 */
899 @Override
900 public L2CharacterAI getAI()
901 {
902 L2CharacterAI ai = _ai;
903 if (ai == null)
904 {
905 synchronized (this)
906 {
907 if (_ai == null)
908 _ai = new L2PlayerAI(this);
909
910 return _ai;
911 }
912 }
913 return ai;
914 }
915
916 /** Return the Level of the L2PcInstance. */
917 @Override
918 public final int getLevel()
919 {
920 return getStat().getLevel();
921 }
922
923 /**
924 * A newbie is a player reaching level 6. He isn't considered newbie at lvl 25.<br>
925 * Since IL newbie isn't anymore the first character of an account reaching that state, but any.
926 * @return True if newbie.
927 */
928 public boolean isNewbie()
929 {
930 return getClassId().level() <= 1 && getLevel() >= 6 && getLevel() <= 25;
931 }
932
933 public void setBaseClass(int baseClass)
934 {
935 _baseClass = baseClass;
936 }
937
938 public void setBaseClass(ClassId classId)
939 {
940 _baseClass = classId.ordinal();
941 }
942
943 public boolean isInStoreMode()
944 {
945 return _privateStoreType != PrivateStoreType.NONE;
946 }
947
948 public boolean isInCraftMode()
949 {
950 return _inCraftMode;
951 }
952
953 public void isInCraftMode(boolean b)
954 {
955 _inCraftMode = b;
956 }
957
958 /**
959 * Manage Logout Task
960 */
961 public void logout()
962 {
963 logout(true);
964 }
965
966 /**
967 * Manage Logout Task
968 * @param closeClient
969 */
970 public void logout(boolean closeClient)
971 {
972 try
973 {
974 closeNetConnection(closeClient);
975 }
976 catch (Exception e)
977 {
978 _log.log(Level.WARNING, "Exception on logout(): " + e.getMessage(), e);
979 }
980 }
981
982 /**
983 * @return a table containing all Common RecipeList of the L2PcInstance.
984 */
985 public Collection<RecipeList> getCommonRecipeBook()
986 {
987 return _commonRecipeBook.values();
988 }
989
990 /**
991 * @return a table containing all Dwarf RecipeList of the L2PcInstance.
992 */
993 public Collection<RecipeList> getDwarvenRecipeBook()
994 {
995 return _dwarvenRecipeBook.values();
996 }
997
998 /**
999 * Add a new L2RecipList to the table _commonrecipebook containing all RecipeList of the L2PcInstance.
1000 * @param recipe The RecipeList to add to the _recipebook
1001 */
1002 public void registerCommonRecipeList(RecipeList recipe)
1003 {
1004 _commonRecipeBook.put(recipe.getId(), recipe);
1005 }
1006
1007 /**
1008 * Add a new L2RecipList to the table _recipebook containing all RecipeList of the L2PcInstance.
1009 * @param recipe The RecipeList to add to the _recipebook
1010 */
1011 public void registerDwarvenRecipeList(RecipeList recipe)
1012 {
1013 _dwarvenRecipeBook.put(recipe.getId(), recipe);
1014 }
1015
1016 /**
1017 * @param recipeId The Identifier of the RecipeList to check in the player's recipe books
1018 * @return <b>TRUE</b> if player has the recipe on Common or Dwarven Recipe book else returns <b>FALSE</b>
1019 */
1020 public boolean hasRecipeList(int recipeId)
1021 {
1022 return _dwarvenRecipeBook.containsKey(recipeId) || _commonRecipeBook.containsKey(recipeId);
1023 }
1024
1025 /**
1026 * Tries to remove a L2RecipList from the table _DwarvenRecipeBook or from table _CommonRecipeBook, those table contain all RecipeList of the L2PcInstance.
1027 * @param recipeId The Identifier of the RecipeList to remove from the _recipebook.
1028 */
1029 public void unregisterRecipeList(int recipeId)
1030 {
1031 if (_dwarvenRecipeBook.containsKey(recipeId))
1032 _dwarvenRecipeBook.remove(recipeId);
1033 else if (_commonRecipeBook.containsKey(recipeId))
1034 _commonRecipeBook.remove(recipeId);
1035 else
1036 _log.warning("Attempted to remove unknown RecipeList: " + recipeId);
1037
1038 for (L2ShortCut sc : getAllShortCuts())
1039 {
1040 if (sc != null && sc.getId() == recipeId && sc.getType() == L2ShortCut.TYPE_RECIPE)
1041 deleteShortCut(sc.getSlot(), sc.getPage());
1042 }
1043 }
1044
1045 /**
1046 * @return the Id for the last talked quest NPC.
1047 */
1048 public int getLastQuestNpcObject()
1049 {
1050 return _questNpcObject;
1051 }
1052
1053 public void setLastQuestNpcObject(int npcId)
1054 {
1055 _questNpcObject = npcId;
1056 }
1057
1058 /**
1059 * @param name The name of the quest.
1060 * @return The QuestState object corresponding to the quest name.
1061 */
1062 public QuestState getQuestState(String name)
1063 {
1064 for (QuestState qs : _quests)
1065 {
1066 if (name.equals(qs.getQuest().getName()))
1067 return qs;
1068 }
1069 return null;
1070 }
1071
1072 /**
1073 * Add a QuestState to the table _quest containing all quests began by the L2PcInstance.
1074 * @param qs The QuestState to add to _quest.
1075 */
1076 public void setQuestState(QuestState qs)
1077 {
1078 _quests.add(qs);
1079 }
1080
1081 /**
1082 * Remove a QuestState from the table _quest containing all quests began by the L2PcInstance.
1083 * @param qs : The QuestState to be removed from _quest.
1084 */
1085 public void delQuestState(QuestState qs)
1086 {
1087 _quests.remove(qs);
1088 }
1089
1090 /**
1091 * @param completed : If true, include completed quests to the list.
1092 * @return list of started and eventually completed quests of the player.
1093 */
1094 public List<Quest> getAllQuests(boolean completed)
1095 {
1096 List<Quest> quests = new ArrayList<>();
1097
1098 for (QuestState qs : _quests)
1099 {
1100 if (qs == null || completed && qs.isCreated() || !completed && !qs.isStarted())
1101 continue;
1102
1103 Quest quest = qs.getQuest();
1104 if (quest == null || !quest.isRealQuest())
1105 continue;
1106
1107 quests.add(quest);
1108 }
1109
1110 return quests;
1111 }
1112
1113 public void processQuestEvent(String questName, String event)
1114 {
1115 Quest quest = ScriptManager.getInstance().getQuest(questName);
1116 if (quest == null)
1117 return;
1118
1119 QuestState qs = getQuestState(questName);
1120 if (qs == null)
1121 return;
1122
1123 L2Object object = L2World.getInstance().getObject(getLastQuestNpcObject());
1124 if (!(object instanceof L2Npc) || !isInsideRadius(object, L2Npc.INTERACTION_DISTANCE, false, false))
1125 return;
1126
1127 L2Npc npc = (L2Npc) object;
1128 List<Quest> quests = npc.getTemplate().getEventQuests(EventType.ON_TALK);
1129 if (quests != null)
1130 {
1131 for (Quest onTalk : quests)
1132 {
1133 if (onTalk == null || !onTalk.equals(quest))
1134 continue;
1135
1136 quest.notifyEvent(event, npc, this);
1137 break;
1138 }
1139 }
1140 }
1141
1142 /**
1143 * Add QuestState instance that is to be notified of L2PcInstance's death.
1144 * @param qs The QuestState that subscribe to this event
1145 */
1146 public void addNotifyQuestOfDeath(QuestState qs)
1147 {
1148 if (qs == null)
1149 return;
1150
1151 if (!_notifyQuestOfDeathList.contains(qs))
1152 _notifyQuestOfDeathList.add(qs);
1153 }
1154
1155 /**
1156 * Remove QuestState instance that is to be notified of L2PcInstance's death.
1157 * @param qs The QuestState that subscribe to this event
1158 */
1159 public void removeNotifyQuestOfDeath(QuestState qs)
1160 {
1161 if (qs == null)
1162 return;
1163
1164 _notifyQuestOfDeathList.remove(qs);
1165 }
1166
1167 /**
1168 * @return A list of QuestStates which registered for notify of death of this L2PcInstance.
1169 */
1170 public final List<QuestState> getNotifyQuestOfDeath()
1171 {
1172 return _notifyQuestOfDeathList;
1173 }
1174
1175 /**
1176 * @return player memos.
1177 */
1178 public PlayerMemo getMemos()
1179 {
1180 return _vars;
1181 }
1182
1183 /**
1184 * @return A table containing all L2ShortCut of the L2PcInstance.
1185 */
1186 public L2ShortCut[] getAllShortCuts()
1187 {
1188 return _shortCuts.getAllShortCuts();
1189 }
1190
1191 /**
1192 * @param slot The slot in wich the shortCuts is equipped
1193 * @param page The page of shortCuts containing the slot
1194 * @return The L2ShortCut of the L2PcInstance corresponding to the position (page-slot).
1195 */
1196 public L2ShortCut getShortCut(int slot, int page)
1197 {
1198 return _shortCuts.getShortCut(slot, page);
1199 }
1200
1201 /**
1202 * Add a L2shortCut to the L2PcInstance _shortCuts
1203 * @param shortcut The shortcut to add.
1204 */
1205 public void registerShortCut(L2ShortCut shortcut)
1206 {
1207 _shortCuts.registerShortCut(shortcut);
1208 }
1209
1210 /**
1211 * Delete the L2ShortCut corresponding to the position (page-slot) from the L2PcInstance _shortCuts.
1212 * @param slot
1213 * @param page
1214 */
1215 public void deleteShortCut(int slot, int page)
1216 {
1217 _shortCuts.deleteShortCut(slot, page);
1218 }
1219
1220 /**
1221 * Add a L2Macro to the L2PcInstance _macroses.
1222 * @param macro The Macro object to add.
1223 */
1224 public void registerMacro(L2Macro macro)
1225 {
1226 _macroses.registerMacro(macro);
1227 }
1228
1229 /**
1230 * Delete the L2Macro corresponding to the Identifier from the L2PcInstance _macroses.
1231 * @param id
1232 */
1233 public void deleteMacro(int id)
1234 {
1235 _macroses.deleteMacro(id);
1236 }
1237
1238 /**
1239 * @return all L2Macro of the L2PcInstance.
1240 */
1241 public MacroList getMacroses()
1242 {
1243 return _macroses;
1244 }
1245
1246 /**
1247 * Set the siege state of the L2PcInstance.
1248 * @param siegeState 1 = attacker, 2 = defender, 0 = not involved
1249 */
1250 public void setSiegeState(byte siegeState)
1251 {
1252 _siegeState = siegeState;
1253 }
1254
1255 /**
1256 * @return the siege state of the L2PcInstance.
1257 */
1258 public byte getSiegeState()
1259 {
1260 return _siegeState;
1261 }
1262
1263 /**
1264 * Set the PvP Flag of the L2PcInstance.
1265 * @param pvpFlag 0 or 1.
1266 */
1267 public void setPvpFlag(int pvpFlag)
1268 {
1269 _pvpFlag = (byte) pvpFlag;
1270 }
1271
1272 @Override
1273 public byte getPvpFlag()
1274 {
1275 return _pvpFlag;
1276 }
1277
1278 public void updatePvPFlag(int value)
1279 {
1280 if (getPvpFlag() == value)
1281 return;
1282
1283 setPvpFlag(value);
1284 sendPacket(new UserInfo(this));
1285
1286 if (getPet() != null)
1287 sendPacket(new RelationChanged(getPet(), getRelation(this), false));
1288
1289 broadcastRelationsChanges();
1290 }
1291
1292 @Override
1293 public void revalidateZone(boolean force)
1294 {
1295 // Cannot validate if not in a world region (happens during teleport)
1296 if (getWorldRegion() == null)
1297 return;
1298
1299 // This function is called very often from movement code
1300 if (force)
1301 _zoneValidateCounter = 4;
1302 else
1303 {
1304 _zoneValidateCounter--;
1305 if (_zoneValidateCounter >= 0)
1306 return;
1307
1308 _zoneValidateCounter = 4;
1309 }
1310
1311 getWorldRegion().revalidateZones(this);
1312
1313 if (Config.ALLOW_WATER)
1314 checkWaterState();
1315
1316 if (isInsideZone(ZoneId.SIEGE))
1317 {
1318 if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2)
1319 return;
1320
1321 _lastCompassZone = ExSetCompassZoneCode.SIEGEWARZONE2;
1322 sendPacket(new ExSetCompassZoneCode(ExSetCompassZoneCode.SIEGEWARZONE2));
1323 }
1324 else if (isInsideZone(ZoneId.PVP))
1325 {
1326 if (_lastCompassZone == ExSetCompassZoneCode.PVPZONE)
1327 return;
1328
1329 _lastCompassZone = ExSetCompassZoneCode.PVPZONE;
1330 sendPacket(new ExSetCompassZoneCode(ExSetCompassZoneCode.PVPZONE));
1331 }
1332 else if (isIn7sDungeon())
1333 {
1334 if (_lastCompassZone == ExSetCompassZoneCode.SEVENSIGNSZONE)
1335 return;
1336
1337 _lastCompassZone = ExSetCompassZoneCode.SEVENSIGNSZONE;
1338 sendPacket(new ExSetCompassZoneCode(ExSetCompassZoneCode.SEVENSIGNSZONE));
1339 }
1340 else if (isInsideZone(ZoneId.PEACE))
1341 {
1342 if (_lastCompassZone == ExSetCompassZoneCode.PEACEZONE)
1343 return;
1344
1345 _lastCompassZone = ExSetCompassZoneCode.PEACEZONE;
1346 sendPacket(new ExSetCompassZoneCode(ExSetCompassZoneCode.PEACEZONE));
1347 }
1348 else
1349 {
1350 if (_lastCompassZone == ExSetCompassZoneCode.GENERALZONE)
1351 return;
1352
1353 if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2)
1354 updatePvPStatus();
1355
1356 _lastCompassZone = ExSetCompassZoneCode.GENERALZONE;
1357 sendPacket(new ExSetCompassZoneCode(ExSetCompassZoneCode.GENERALZONE));
1358 }
1359 }
1360
1361 /**
1362 * @return True if the L2PcInstance can Craft Dwarven Recipes.
1363 */
1364 public boolean hasDwarvenCraft()
1365 {
1366 return getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN) >= 1;
1367 }
1368
1369 public int getDwarvenCraft()
1370 {
1371 return getSkillLevel(L2Skill.SKILL_CREATE_DWARVEN);
1372 }
1373
1374 /**
1375 * @return True if the L2PcInstance can Craft Dwarven Recipes.
1376 */
1377 public boolean hasCommonCraft()
1378 {
1379 return getSkillLevel(L2Skill.SKILL_CREATE_COMMON) >= 1;
1380 }
1381
1382 public int getCommonCraft()
1383 {
1384 return getSkillLevel(L2Skill.SKILL_CREATE_COMMON);
1385 }
1386
1387 /**
1388 * @return the PK counter of the L2PcInstance.
1389 */
1390 public int getPkKills()
1391 {
1392 return _pkKills;
1393 }
1394
1395 /**
1396 * Set the PK counter of the L2PcInstance.
1397 * @param pkKills A number.
1398 */
1399 public void setPkKills(int pkKills)
1400 {
1401 _pkKills = pkKills;
1402 }
1403
1404 /**
1405 * @return The _deleteTimer of the L2PcInstance.
1406 */
1407 public long getDeleteTimer()
1408 {
1409 return _deleteTimer;
1410 }
1411
1412 /**
1413 * Set the _deleteTimer of the L2PcInstance.
1414 * @param deleteTimer Time in ms.
1415 */
1416 public void setDeleteTimer(long deleteTimer)
1417 {
1418 _deleteTimer = deleteTimer;
1419 }
1420
1421 /**
1422 * @return The current weight of the L2PcInstance.
1423 */
1424 public int getCurrentLoad()
1425 {
1426 return _inventory.getTotalWeight();
1427 }
1428
1429 /**
1430 * @return The date of last update of recomPoints.
1431 */
1432 public long getLastRecomUpdate()
1433 {
1434 return _lastRecomUpdate;
1435 }
1436
1437 public void setLastRecomUpdate(long date)
1438 {
1439 _lastRecomUpdate = date;
1440 }
1441
1442 /**
1443 * @return The number of recommandation obtained by the L2PcInstance.
1444 */
1445 public int getRecomHave()
1446 {
1447 return _recomHave;
1448 }
1449
1450 /**
1451 * Increment the number of recommandation obtained by the L2PcInstance (Max : 255).
1452 */
1453 protected void incRecomHave()
1454 {
1455 if (_recomHave < 255)
1456 _recomHave++;
1457 }
1458
1459 /**
1460 * Set the number of recommandations obtained by the L2PcInstance (Max : 255).
1461 * @param value Number of recommandations obtained.
1462 */
1463 public void setRecomHave(int value)
1464 {
1465 if (value > 255)
1466 _recomHave = 255;
1467 else if (value < 0)
1468 _recomHave = 0;
1469 else
1470 _recomHave = value;
1471 }
1472
1473 /**
1474 * @return The number of recommandation that the L2PcInstance can give.
1475 */
1476 public int getRecomLeft()
1477 {
1478 return _recomLeft;
1479 }
1480
1481 /**
1482 * Increment the number of recommandation that the L2PcInstance can give.
1483 */
1484 protected void decRecomLeft()
1485 {
1486 if (_recomLeft > 0)
1487 _recomLeft--;
1488 }
1489
1490 public void giveRecom(L2PcInstance target)
1491 {
1492 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
1493 {
1494 PreparedStatement statement = con.prepareStatement(ADD_CHAR_RECOM);
1495 statement.setInt(1, getObjectId());
1496 statement.setInt(2, target.getObjectId());
1497 statement.execute();
1498 statement.close();
1499 }
1500 catch (Exception e)
1501 {
1502 _log.warning("Could not update char recommendations: " + e);
1503 }
1504
1505 target.incRecomHave();
1506 decRecomLeft();
1507 _recomChars.add(target.getObjectId());
1508 }
1509
1510 public boolean canRecom(L2PcInstance target)
1511 {
1512 return !_recomChars.contains(target.getObjectId());
1513 }
1514
1515 /**
1516 * Set the exp of the L2PcInstance before a death
1517 * @param exp
1518 */
1519 public void setExpBeforeDeath(long exp)
1520 {
1521 _expBeforeDeath = exp;
1522 }
1523
1524 public long getExpBeforeDeath()
1525 {
1526 return _expBeforeDeath;
1527 }
1528
1529 /**
1530 * Return the Karma of the L2PcInstance.
1531 */
1532 @Override
1533 public int getKarma()
1534 {
1535 return _karma;
1536 }
1537
1538 /**
1539 * Set the Karma of the L2PcInstance and send StatusUpdate (broadcast).
1540 * @param karma A value.
1541 */
1542 public void setKarma(int karma)
1543 {
1544 if (karma < 0)
1545 karma = 0;
1546
1547 if (_karma > 0 && karma == 0)
1548 {
1549 sendPacket(new UserInfo(this));
1550 broadcastRelationsChanges();
1551 }
1552
1553 // send message with new karma value
1554 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_KARMA_HAS_BEEN_CHANGED_TO_S1).addNumber(karma));
1555
1556 _karma = karma;
1557 broadcastKarma();
1558 }
1559
1560 /**
1561 * Weight Limit = (CON Modifier*69000)*Skills
1562 * @return The max weight that the L2PcInstance can load.
1563 */
1564 public int getMaxLoad()
1565 {
1566 int con = getCON();
1567 if (con < 1)
1568 return 31000;
1569
1570 if (con > 59)
1571 return 176000;
1572
1573 double baseLoad = Math.pow(1.029993928, con) * 30495.627366;
1574 return (int) calcStat(Stats.MAX_LOAD, baseLoad * Config.ALT_WEIGHT_LIMIT, this, null);
1575 }
1576
1577 public int getExpertiseArmorPenalty()
1578 {
1579 return _expertiseArmorPenalty;
1580 }
1581
1582 public boolean getExpertiseWeaponPenalty()
1583 {
1584 return _expertiseWeaponPenalty;
1585 }
1586
1587 public int getWeightPenalty()
1588 {
1589 return _curWeightPenalty;
1590 }
1591
1592 /**
1593 * Update the overloaded status of the L2PcInstance.
1594 */
1595 public void refreshOverloaded()
1596 {
1597 int maxLoad = getMaxLoad();
1598 if (maxLoad > 0)
1599 {
1600 int weightproc = getCurrentLoad() * 1000 / maxLoad;
1601 int newWeightPenalty;
1602
1603 if (weightproc < 500)
1604 newWeightPenalty = 0;
1605 else if (weightproc < 666)
1606 newWeightPenalty = 1;
1607 else if (weightproc < 800)
1608 newWeightPenalty = 2;
1609 else if (weightproc < 1000)
1610 newWeightPenalty = 3;
1611 else
1612 newWeightPenalty = 4;
1613
1614 if (_curWeightPenalty != newWeightPenalty)
1615 {
1616 _curWeightPenalty = newWeightPenalty;
1617
1618 if (newWeightPenalty > 0)
1619 {
1620 super.addSkill(SkillTable.getInstance().getInfo(4270, newWeightPenalty));
1621 setIsOverloaded(getCurrentLoad() > maxLoad);
1622 }
1623 else
1624 {
1625 super.removeSkill(getKnownSkill(4270));
1626 setIsOverloaded(false);
1627 }
1628
1629 sendPacket(new UserInfo(this));
1630 sendPacket(new EtcStatusUpdate(this));
1631 broadcastCharInfo();
1632 }
1633 }
1634 }
1635
1636 /**
1637 * Refresh expertise level ; weapon got one rank, when armor got 4 ranks.<br>
1638 */
1639 public void refreshExpertisePenalty()
1640 {
1641 int armorPenalty = 0;
1642 boolean weaponPenalty = false;
1643
1644 for (ItemInstance item : getInventory().getItems())
1645 {
1646 if (item != null && item.isEquipped() && item.getItemType() != EtcItemType.ARROW && item.getItem().getCrystalType().getId() > getExpertiseIndex())
1647 {
1648 if (item.isWeapon())
1649 weaponPenalty = true;
1650 else
1651 armorPenalty += (item.getItem().getBodyPart() == Item.SLOT_FULL_ARMOR) ? 2 : 1;
1652 }
1653 }
1654
1655 armorPenalty = Math.min(armorPenalty, 4);
1656
1657 // Found a different state than previous ; update it.
1658 if (_expertiseWeaponPenalty != weaponPenalty || _expertiseArmorPenalty != armorPenalty)
1659 {
1660 _expertiseWeaponPenalty = weaponPenalty;
1661 _expertiseArmorPenalty = armorPenalty;
1662
1663 // Passive skill "Grade Penalty" is either granted or dropped.
1664 if (_expertiseWeaponPenalty || _expertiseArmorPenalty > 0)
1665 super.addSkill(SkillTable.getInstance().getInfo(4267, 1));
1666 else
1667 super.removeSkill(getKnownSkill(4267));
1668
1669 sendSkillList();
1670 sendPacket(new EtcStatusUpdate(this));
1671
1672 final ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
1673 if (weapon != null)
1674 {
1675 if (_expertiseWeaponPenalty)
1676 ItemPassiveSkillsListener.getInstance().onUnequip(0, weapon, this);
1677 else
1678 ItemPassiveSkillsListener.getInstance().onEquip(0, weapon, this);
1679 }
1680 }
1681 }
1682
1683 /**
1684 * Equip or unequip the item.
1685 * <UL>
1686 * <LI>If item is equipped, shots are applied if automation is on.</LI>
1687 * <LI>If item is unequipped, shots are discharged.</LI>
1688 * </UL>
1689 * @param item The item to charge/discharge.
1690 * @param abortAttack If true, the current attack will be aborted in order to equip the item.
1691 */
1692 public void useEquippableItem(ItemInstance item, boolean abortAttack)
1693 {
1694 ItemInstance[] items = null;
1695 final boolean isEquipped = item.isEquipped();
1696 final int oldInvLimit = getInventoryLimit();
1697 SystemMessage sm = null;
1698
1699 if (item.getItem() instanceof Weapon)
1700 item.unChargeAllShots();
1701
1702 if (isEquipped)
1703 {
1704 if (item.getEnchantLevel() > 0)
1705 sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED).addNumber(item.getEnchantLevel()).addItemName(item);
1706 else
1707 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED).addItemName(item);
1708
1709 sendPacket(sm);
1710
1711 int slot = getInventory().getSlotFromItem(item);
1712 items = getInventory().unEquipItemInBodySlotAndRecord(slot);
1713 }
1714 else
1715 {
1716 items = getInventory().equipItemAndRecord(item);
1717
1718 if (item.isEquipped())
1719 {
1720 if (item.getEnchantLevel() > 0)
1721 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_S2_EQUIPPED).addNumber(item.getEnchantLevel()).addItemName(item);
1722 else
1723 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_EQUIPPED).addItemName(item);
1724
1725 sendPacket(sm);
1726
1727 if ((item.getItem().getBodyPart() & Item.SLOT_ALLWEAPON) != 0)
1728 rechargeShots(true, true);
1729 }
1730 else
1731 sendPacket(SystemMessageId.CANNOT_EQUIP_ITEM_DUE_TO_BAD_CONDITION);
1732 }
1733 refreshExpertisePenalty();
1734 broadcastUserInfo();
1735
1736 InventoryUpdate iu = new InventoryUpdate();
1737 iu.addItems(Arrays.asList(items));
1738 sendPacket(iu);
1739
1740 if (abortAttack)
1741 abortAttack();
1742
1743 if (getInventoryLimit() != oldInvLimit)
1744 sendPacket(new ExStorageMaxCount(this));
1745 }
1746
1747 /**
1748 * @return PvP Kills of the L2PcInstance (number of player killed during a PvP).
1749 */
1750 public int getPvpKills()
1751 {
1752 return _pvpKills;
1753 }
1754
1755 /**
1756 * Set PvP Kills of the L2PcInstance (number of player killed during a PvP).
1757 * @param pvpKills A value.
1758 */
1759 public void setPvpKills(int pvpKills)
1760 {
1761 _pvpKills = pvpKills;
1762 }
1763
1764 /**
1765 * @return The ClassId object of the L2PcInstance contained in L2PcTemplate.
1766 */
1767 public ClassId getClassId()
1768 {
1769 return getTemplate().getClassId();
1770 }
1771
1772 /**
1773 * Set the template of the L2PcInstance.
1774 * @param Id The Identifier of the L2PcTemplate to set to the L2PcInstance
1775 */
1776 public void setClassId(int Id)
1777 {
1778 if (!_subclassLock.tryLock())
1779 return;
1780
1781 try
1782 {
1783 if (getLvlJoinedAcademy() != 0 && _clan != null && PlayerClass.values()[Id].getLevel() == ClassLevel.Third)
1784 {
1785 if (getLvlJoinedAcademy() <= 16)
1786 _clan.addReputationScore(400);
1787 else if (getLvlJoinedAcademy() >= 39)
1788 _clan.addReputationScore(170);
1789 else
1790 _clan.addReputationScore((400 - (getLvlJoinedAcademy() - 16) * 10));
1791
1792 setLvlJoinedAcademy(0);
1793
1794 // Oust pledge member from the academy, because he has finished his 2nd class transfer.
1795 _clan.broadcastToOnlineMembers(new PledgeShowMemberListDelete(getName()), SystemMessage.getSystemMessage(SystemMessageId.CLAN_MEMBER_S1_EXPELLED).addString(getName()));
1796 _clan.removeClanMember(getObjectId(), 0);
1797 sendPacket(SystemMessageId.ACADEMY_MEMBERSHIP_TERMINATED);
1798
1799 // receive graduation gift : academy circlet
1800 addItem("Gift", 8181, 1, this, true);
1801 }
1802
1803 if (isSubClassActive())
1804 getSubClasses().get(_classIndex).setClassId(Id);
1805
1806 broadcastPacket(new MagicSkillUse(this, this, 5103, 1, 1000, 0));
1807 setClassTemplate(Id);
1808
1809 if (getClassId().level() == 3)
1810 sendPacket(SystemMessageId.THIRD_CLASS_TRANSFER);
1811 else
1812 sendPacket(SystemMessageId.CLASS_TRANSFER);
1813
1814 // Update class icon in party and clan
1815 if (isInParty())
1816 getParty().broadcastToPartyMembers(new PartySmallWindowUpdate(this));
1817
1818 if (getClan() != null)
1819 getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
1820
1821 if (Config.AUTO_LEARN_SKILLS)
1822 rewardSkills();
1823 }
1824 finally
1825 {
1826 _subclassLock.unlock();
1827 }
1828 }
1829
1830 /**
1831 * @return the Experience of the L2PcInstance.
1832 */
1833 public long getExp()
1834 {
1835 return getStat().getExp();
1836 }
1837
1838 public void setActiveEnchantItem(ItemInstance scroll)
1839 {
1840 _activeEnchantItem = scroll;
1841 }
1842
1843 public ItemInstance getActiveEnchantItem()
1844 {
1845 return _activeEnchantItem;
1846 }
1847
1848 /**
1849 * Set the fists weapon of the L2PcInstance (used when no weapon is equipped).
1850 * @param weaponItem The fists Weapon to set to the L2PcInstance
1851 */
1852 public void setFistsWeaponItem(Weapon weaponItem)
1853 {
1854 _fistsWeaponItem = weaponItem;
1855 }
1856
1857 /**
1858 * @return The fists weapon of the L2PcInstance (used when no weapon is equipped).
1859 */
1860 public Weapon getFistsWeaponItem()
1861 {
1862 return _fistsWeaponItem;
1863 }
1864
1865 /**
1866 * @param classId The classId to test.
1867 * @return The fists weapon of the L2PcInstance Class (used when no weapon is equipped).
1868 */
1869 public static Weapon findFistsWeaponItem(int classId)
1870 {
1871 Weapon weaponItem = null;
1872 if ((classId >= 0x00) && (classId <= 0x09))
1873 {
1874 // human fighter fists
1875 Item temp = ItemTable.getInstance().getTemplate(246);
1876 weaponItem = (Weapon) temp;
1877 }
1878 else if ((classId >= 0x0a) && (classId <= 0x11))
1879 {
1880 // human mage fists
1881 Item temp = ItemTable.getInstance().getTemplate(251);
1882 weaponItem = (Weapon) temp;
1883 }
1884 else if ((classId >= 0x12) && (classId <= 0x18))
1885 {
1886 // elven fighter fists
1887 Item temp = ItemTable.getInstance().getTemplate(244);
1888 weaponItem = (Weapon) temp;
1889 }
1890 else if ((classId >= 0x19) && (classId <= 0x1e))
1891 {
1892 // elven mage fists
1893 Item temp = ItemTable.getInstance().getTemplate(249);
1894 weaponItem = (Weapon) temp;
1895 }
1896 else if ((classId >= 0x1f) && (classId <= 0x25))
1897 {
1898 // dark elven fighter fists
1899 Item temp = ItemTable.getInstance().getTemplate(245);
1900 weaponItem = (Weapon) temp;
1901 }
1902 else if ((classId >= 0x26) && (classId <= 0x2b))
1903 {
1904 // dark elven mage fists
1905 Item temp = ItemTable.getInstance().getTemplate(250);
1906 weaponItem = (Weapon) temp;
1907 }
1908 else if ((classId >= 0x2c) && (classId <= 0x30))
1909 {
1910 // orc fighter fists
1911 Item temp = ItemTable.getInstance().getTemplate(248);
1912 weaponItem = (Weapon) temp;
1913 }
1914 else if ((classId >= 0x31) && (classId <= 0x34))
1915 {
1916 // orc mage fists
1917 Item temp = ItemTable.getInstance().getTemplate(252);
1918 weaponItem = (Weapon) temp;
1919 }
1920 else if ((classId >= 0x35) && (classId <= 0x39))
1921 {
1922 // dwarven fists
1923 Item temp = ItemTable.getInstance().getTemplate(247);
1924 weaponItem = (Weapon) temp;
1925 }
1926
1927 return weaponItem;
1928 }
1929
1930 /**
1931 * This method is kinda polymorph :
1932 * <ul>
1933 * <li>it gives proper Expertise, Dwarven && Common Craft skill level ;</li>
1934 * <li>it controls the Lucky skill (remove at lvl 10) ;</li>
1935 * <li>it finally sends the skill list.</li>
1936 * </ul>
1937 */
1938 public void rewardSkills()
1939 {
1940 // Get the Level of the L2PcInstance
1941 int lvl = getLevel();
1942
1943 // Remove the Lucky skill once reached lvl 10.
1944 if (getSkillLevel(L2Skill.SKILL_LUCKY) > 0 && lvl >= 10)
1945 removeSkill(FrequentSkill.LUCKY.getSkill());
1946
1947 // Calculate the current higher Expertise of the L2PcInstance
1948 for (int i = 0; i < EXPERTISE_LEVELS.length; i++)
1949 {
1950 if (lvl >= EXPERTISE_LEVELS[i])
1951 setExpertiseIndex(i);
1952 }
1953
1954 // Add the Expertise skill corresponding to its Expertise level
1955 if (getExpertiseIndex() > 0)
1956 {
1957 L2Skill skill = SkillTable.getInstance().getInfo(239, getExpertiseIndex());
1958 addSkill(skill, true);
1959 }
1960
1961 // Active skill dwarven craft
1962 if (getSkillLevel(1321) < 1 && getClassId().equalsOrChildOf(ClassId.dwarvenFighter))
1963 {
1964 L2Skill skill = FrequentSkill.DWARVEN_CRAFT.getSkill();
1965 addSkill(skill, true);
1966 }
1967
1968 // Active skill common craft
1969 if (getSkillLevel(1322) < 1)
1970 {
1971 L2Skill skill = FrequentSkill.COMMON_CRAFT.getSkill();
1972 addSkill(skill, true);
1973 }
1974
1975 for (int i = 0; i < COMMON_CRAFT_LEVELS.length; i++)
1976 {
1977 if (lvl >= COMMON_CRAFT_LEVELS[i] && getSkillLevel(1320) < (i + 1))
1978 {
1979 L2Skill skill = SkillTable.getInstance().getInfo(1320, (i + 1));
1980 addSkill(skill, true);
1981 }
1982 }
1983
1984 // Auto-Learn skills if activated
1985 if (Config.AUTO_LEARN_SKILLS)
1986 giveAvailableSkills();
1987
1988 sendSkillList();
1989 }
1990
1991 /**
1992 * Regive all skills which aren't saved to database, like Noble, Hero, Clan Skills.<br>
1993 * <b>Do not call this on enterworld or char load.</b>.
1994 */
1995 private void regiveTemporarySkills()
1996 {
1997 // Add noble skills if noble.
1998 if (isNoble())
1999 setNoble(true, false);
2000
2001 // Add Hero skills if hero.
2002 if (isHero())
2003 setHero(true);
2004
2005 // Add clan skills.
2006 if (getClan() != null)
2007 {
2008 getClan().addSkillEffects(this);
2009
2010 if (getClan().getLevel() >= SiegeManager.MINIMUM_CLAN_LEVEL && isClanLeader())
2011 SiegeManager.addSiegeSkills(this);
2012 }
2013
2014 // Reload passive skills from armors / jewels / weapons
2015 getInventory().reloadEquippedItems();
2016
2017 // Add Death Penalty Buff Level
2018 restoreDeathPenaltyBuffLevel();
2019 }
2020
2021 /**
2022 * Give all available skills to the player.
2023 * @return The number of given skills.
2024 */
2025 public int giveAvailableSkills()
2026 {
2027 int result = 0;
2028 for (L2SkillLearn sl : SkillTreeTable.getInstance().getAllAvailableSkills(this, getClassId()))
2029 {
2030 addSkill(SkillTable.getInstance().getInfo(sl.getId(), sl.getLevel()), true);
2031 result++;
2032 }
2033 return result;
2034 }
2035
2036 /**
2037 * @return The Race object of the L2PcInstance.
2038 */
2039 public Race getRace()
2040 {
2041 if (!isSubClassActive())
2042 return getTemplate().getRace();
2043
2044 return CharTemplateTable.getInstance().getTemplate(_baseClass).getRace();
2045 }
2046
2047 public L2Radar getRadar()
2048 {
2049 return _radar;
2050 }
2051
2052 /**
2053 * @return the SP amount of the L2PcInstance.
2054 */
2055 public int getSp()
2056 {
2057 return getStat().getSp();
2058 }
2059
2060 /**
2061 * @param castleId The castle to check.
2062 * @return True if this L2PcInstance is a clan leader in ownership of the passed castle.
2063 */
2064 public boolean isCastleLord(int castleId)
2065 {
2066 L2Clan clan = getClan();
2067
2068 // player has clan and is the clan leader, check the castle info
2069 if ((clan != null) && (clan.getLeader().getPlayerInstance() == this))
2070 {
2071 // if the clan has a castle and it is actually the queried castle, return true
2072 Castle castle = CastleManager.getInstance().getCastleByOwner(clan);
2073 if ((castle != null) && (castle == CastleManager.getInstance().getCastleById(castleId)))
2074 return true;
2075 }
2076
2077 return false;
2078 }
2079
2080 /**
2081 * @return The Clan Identifier of the L2PcInstance.
2082 */
2083 public int getClanId()
2084 {
2085 return _clanId;
2086 }
2087
2088 /**
2089 * @return The Clan Crest Identifier of the L2PcInstance or 0.
2090 */
2091 public int getClanCrestId()
2092 {
2093 if (_clan != null)
2094 return _clan.getCrestId();
2095
2096 return 0;
2097 }
2098
2099 /**
2100 * @return The Clan CrestLarge Identifier or 0
2101 */
2102 public int getClanCrestLargeId()
2103 {
2104 if (_clan != null)
2105 return _clan.getCrestLargeId();
2106
2107 return 0;
2108 }
2109
2110 public long getClanJoinExpiryTime()
2111 {
2112 return _clanJoinExpiryTime;
2113 }
2114
2115 public void setClanJoinExpiryTime(long time)
2116 {
2117 _clanJoinExpiryTime = time;
2118 }
2119
2120 public long getClanCreateExpiryTime()
2121 {
2122 return _clanCreateExpiryTime;
2123 }
2124
2125 public void setClanCreateExpiryTime(long time)
2126 {
2127 _clanCreateExpiryTime = time;
2128 }
2129
2130 public void setOnlineTime(long time)
2131 {
2132 _onlineTime = time;
2133 _onlineBeginTime = System.currentTimeMillis();
2134 }
2135
2136 /**
2137 * Return the PcInventory Inventory of the L2PcInstance contained in _inventory.
2138 */
2139 @Override
2140 public PcInventory getInventory()
2141 {
2142 return _inventory;
2143 }
2144
2145 /**
2146 * Delete a ShortCut of the L2PcInstance _shortCuts.
2147 * @param objectId The shortcut id.
2148 */
2149 public void removeItemFromShortCut(int objectId)
2150 {
2151 _shortCuts.deleteShortCutByObjectId(objectId);
2152 }
2153
2154 /**
2155 * @return True if the L2PcInstance is sitting.
2156 */
2157 public boolean isSitting()
2158 {
2159 return _waitTypeSitting;
2160 }
2161
2162 /**
2163 * Set _waitTypeSitting to given value.
2164 * @param state A boolean.
2165 */
2166 public void setIsSitting(boolean state)
2167 {
2168 _waitTypeSitting = state;
2169 }
2170
2171 /**
2172 * Sit down the L2PcInstance, set the AI Intention to REST and send ChangeWaitType packet (broadcast)
2173 */
2174 public void sitDown()
2175 {
2176 sitDown(true);
2177 }
2178
2179 public void sitDown(boolean checkCast)
2180 {
2181 if (checkCast && isCastingNow())
2182 return;
2183
2184 if (!_waitTypeSitting && !isAttackingDisabled() && !isOutOfControl() && !isImmobilized())
2185 {
2186 breakAttack();
2187 setIsSitting(true);
2188 broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING));
2189
2190 // Schedule a sit down task to wait for the animation to finish
2191 getAI().setIntention(CtrlIntention.REST);
2192 ThreadPoolManager.getInstance().scheduleGeneral(new SitDownTask(), 2500);
2193 setIsParalyzed(true);
2194 }
2195 }
2196
2197 protected class SitDownTask implements Runnable
2198 {
2199 @Override
2200 public void run()
2201 {
2202 setIsParalyzed(false);
2203 }
2204 }
2205
2206 protected class StandUpTask implements Runnable
2207 {
2208 @Override
2209 public void run()
2210 {
2211 setIsSitting(false);
2212 setIsParalyzed(false);
2213 getAI().setIntention(CtrlIntention.IDLE);
2214 }
2215 }
2216
2217 /**
2218 * Stand up the L2PcInstance, set the AI Intention to IDLE and send ChangeWaitType packet (broadcast)
2219 */
2220 public void standUp()
2221 {
2222 if (_waitTypeSitting && !isInStoreMode() && !isAlikeDead() && !isParalyzed())
2223 {
2224 if (_effects.isAffected(L2EffectFlag.RELAXING))
2225 stopEffects(L2EffectType.RELAXING);
2226
2227 broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING));
2228 // Schedule a stand up task to wait for the animation to finish
2229 ThreadPoolManager.getInstance().scheduleGeneral(new StandUpTask(), 2500);
2230 setIsParalyzed(true);
2231 }
2232 }
2233
2234 /**
2235 * Stands up and close any opened shop window, if any.
2236 */
2237 public void forceStandUp()
2238 {
2239 // Cancels any shop types.
2240 if (isInStoreMode())
2241 {
2242 setPrivateStoreType(PrivateStoreType.NONE);
2243 broadcastUserInfo();
2244 }
2245
2246 // Stand up.
2247 standUp();
2248 }
2249
2250 /**
2251 * Used to sit or stand. If not possible, queue the action.
2252 * @param target The target, used for thrones types.
2253 * @param sittingState The sitting state, inheritated from packet or player status.
2254 */
2255 public void tryToSitOrStand(final L2Object target, final boolean sittingState)
2256 {
2257 if (isFakeDeath())
2258 {
2259 stopFakeDeath(true);
2260 return;
2261 }
2262
2263 final boolean isThrone = target instanceof L2StaticObjectInstance && ((L2StaticObjectInstance) target).getType() == 1;
2264
2265 // Player wants to sit on a throne but is out of radius, move to the throne delaying the sit action.
2266 if (isThrone && !sittingState && !isInsideRadius(target, L2Npc.INTERACTION_DISTANCE, false, false))
2267 {
2268 getAI().setIntention(CtrlIntention.MOVE_TO, new L2CharPosition(target.getX(), target.getY(), target.getZ(), 0));
2269
2270 NextAction nextAction = new NextAction(CtrlEvent.EVT_ARRIVED, CtrlIntention.MOVE_TO, new Runnable()
2271 {
2272 @Override
2273 public void run()
2274 {
2275 if (getMountType() != 0)
2276 return;
2277
2278 sitDown();
2279
2280 if (!((L2StaticObjectInstance) target).isBusy())
2281 {
2282 ((L2StaticObjectInstance) target).setBusy(true);
2283 setMountObjectID(target.getObjectId());
2284 broadcastPacket(new ChairSit(getObjectId(), ((L2StaticObjectInstance) target).getStaticObjectId()));
2285 }
2286 }
2287 });
2288
2289 // Binding next action to AI.
2290 getAI().setNextAction(nextAction);
2291 return;
2292 }
2293
2294 // Player isn't moving, sit directly.
2295 if (!isMoving())
2296 {
2297 if (getMountType() != 0)
2298 return;
2299
2300 if (sittingState)
2301 {
2302 if (getMountObjectID() != 0)
2303 {
2304 final L2Object obj = L2World.getInstance().getObject(getMountObjectID());
2305 ((L2StaticObjectInstance) obj).setBusy(false);
2306
2307 setMountObjectID(0);
2308 }
2309
2310 standUp();
2311 }
2312 else
2313 {
2314 sitDown();
2315
2316 if (isThrone && !((L2StaticObjectInstance) target).isBusy() && isInsideRadius(target, L2Npc.INTERACTION_DISTANCE, false, false))
2317 {
2318 ((L2StaticObjectInstance) target).setBusy(true);
2319 setMountObjectID(target.getObjectId());
2320 broadcastPacket(new ChairSit(getObjectId(), ((L2StaticObjectInstance) target).getStaticObjectId()));
2321 }
2322 }
2323 }
2324 // Player is moving, wait the current action is done, then sit.
2325 else
2326 {
2327 NextAction nextAction = new NextAction(CtrlEvent.EVT_ARRIVED, CtrlIntention.MOVE_TO, new Runnable()
2328 {
2329 @Override
2330 public void run()
2331 {
2332 if (getMountType() != 0)
2333 return;
2334
2335 if (sittingState)
2336 {
2337 if (getMountObjectID() != 0)
2338 {
2339 final L2Object obj = L2World.getInstance().getObject(getMountObjectID());
2340 ((L2StaticObjectInstance) obj).setBusy(false);
2341
2342 setMountObjectID(0);
2343 }
2344
2345 standUp();
2346 }
2347 else
2348 {
2349 sitDown();
2350
2351 if (isThrone && !((L2StaticObjectInstance) target).isBusy() && isInsideRadius(target, L2Npc.INTERACTION_DISTANCE, false, false))
2352 {
2353 ((L2StaticObjectInstance) target).setBusy(true);
2354 setMountObjectID(target.getObjectId());
2355 broadcastPacket(new ChairSit(getObjectId(), ((L2StaticObjectInstance) target).getStaticObjectId()));
2356 }
2357 }
2358 }
2359 });
2360
2361 // Binding next action to AI.
2362 getAI().setNextAction(nextAction);
2363 }
2364 }
2365
2366 /**
2367 * @return The PcWarehouse object of the L2PcInstance.
2368 */
2369 public PcWarehouse getWarehouse()
2370 {
2371 if (_warehouse == null)
2372 {
2373 _warehouse = new PcWarehouse(this);
2374 _warehouse.restore();
2375 }
2376 return _warehouse;
2377 }
2378
2379 /**
2380 * Free memory used by Warehouse
2381 */
2382 public void clearWarehouse()
2383 {
2384 if (_warehouse != null)
2385 _warehouse.deleteMe();
2386
2387 _warehouse = null;
2388 }
2389
2390 /**
2391 * @return The PcFreight object of the L2PcInstance.
2392 */
2393 public PcFreight getFreight()
2394 {
2395 if (_freight == null)
2396 {
2397 _freight = new PcFreight(this);
2398 _freight.restore();
2399 }
2400 return _freight;
2401 }
2402
2403 /**
2404 * Free memory used by Freight
2405 */
2406 public void clearFreight()
2407 {
2408 if (_freight != null)
2409 _freight.deleteMe();
2410
2411 _freight = null;
2412 }
2413
2414 /**
2415 * @param objectId The id of the owner.
2416 * @return deposited PcFreight object for the objectId or create new if not existing.
2417 */
2418 public PcFreight getDepositedFreight(int objectId)
2419 {
2420 for (PcFreight freight : _depositedFreight)
2421 {
2422 if (freight != null && freight.getOwnerId() == objectId)
2423 return freight;
2424 }
2425
2426 PcFreight freight = new PcFreight(null);
2427 freight.doQuickRestore(objectId);
2428 _depositedFreight.add(freight);
2429 return freight;
2430 }
2431
2432 /**
2433 * Clear memory used by deposited freight
2434 */
2435 public void clearDepositedFreight()
2436 {
2437 for (PcFreight freight : _depositedFreight)
2438 {
2439 if (freight != null)
2440 freight.deleteMe();
2441 }
2442 _depositedFreight.clear();
2443 }
2444
2445 /**
2446 * @return The Adena amount of the L2PcInstance.
2447 */
2448 public int getAdena()
2449 {
2450 return _inventory.getAdena();
2451 }
2452
2453 /**
2454 * @return The Ancient Adena amount of the L2PcInstance.
2455 */
2456 public int getAncientAdena()
2457 {
2458 return _inventory.getAncientAdena();
2459 }
2460
2461 /**
2462 * Add adena to Inventory of the L2PcInstance and send InventoryUpdate packet to the L2PcInstance.
2463 * @param process String Identifier of process triggering this action
2464 * @param count int Quantity of adena to be added
2465 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2466 * @param sendMessage boolean Specifies whether to send message to Client about this action
2467 */
2468 public void addAdena(String process, int count, L2Object reference, boolean sendMessage)
2469 {
2470 if (sendMessage)
2471 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA).addNumber(count));
2472
2473 if (count > 0)
2474 {
2475 _inventory.addAdena(process, count, this, reference);
2476
2477 InventoryUpdate iu = new InventoryUpdate();
2478 iu.addItem(_inventory.getAdenaInstance());
2479 sendPacket(iu);
2480 }
2481 }
2482
2483 /**
2484 * Reduce adena in Inventory of the L2PcInstance and send InventoryUpdate packet to the L2PcInstance.
2485 * @param process String Identifier of process triggering this action
2486 * @param count int Quantity of adena to be reduced
2487 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2488 * @param sendMessage boolean Specifies whether to send message to Client about this action
2489 * @return boolean informing if the action was successfull
2490 */
2491 public boolean reduceAdena(String process, int count, L2Object reference, boolean sendMessage)
2492 {
2493 if (count > getAdena())
2494 {
2495 if (sendMessage)
2496 sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
2497
2498 return false;
2499 }
2500
2501 if (count > 0)
2502 {
2503 ItemInstance adenaItem = _inventory.getAdenaInstance();
2504 if (!_inventory.reduceAdena(process, count, this, reference))
2505 return false;
2506
2507 // Send update packet
2508 InventoryUpdate iu = new InventoryUpdate();
2509 iu.addItem(adenaItem);
2510 sendPacket(iu);
2511
2512 if (sendMessage)
2513 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED_ADENA).addNumber(count));
2514 }
2515 return true;
2516 }
2517
2518 /**
2519 * Add ancient adena to Inventory of the L2PcInstance and send InventoryUpdate packet to the L2PcInstance.
2520 * @param process String Identifier of process triggering this action
2521 * @param count int Quantity of ancient adena to be added
2522 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2523 * @param sendMessage boolean Specifies whether to send message to Client about this action
2524 */
2525 public void addAncientAdena(String process, int count, L2Object reference, boolean sendMessage)
2526 {
2527 if (sendMessage)
2528 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S).addItemName(PcInventory.ANCIENT_ADENA_ID).addNumber(count));
2529
2530 if (count > 0)
2531 {
2532 _inventory.addAncientAdena(process, count, this, reference);
2533
2534 InventoryUpdate iu = new InventoryUpdate();
2535 iu.addItem(_inventory.getAncientAdenaInstance());
2536 sendPacket(iu);
2537 }
2538 }
2539
2540 /**
2541 * Reduce ancient adena in Inventory of the L2PcInstance and send InventoryUpdate packet to the L2PcInstance.
2542 * @param process String Identifier of process triggering this action
2543 * @param count int Quantity of ancient adena to be reduced
2544 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2545 * @param sendMessage boolean Specifies whether to send message to Client about this action
2546 * @return boolean informing if the action was successfull
2547 */
2548 public boolean reduceAncientAdena(String process, int count, L2Object reference, boolean sendMessage)
2549 {
2550 if (count > getAncientAdena())
2551 {
2552 if (sendMessage)
2553 sendPacket(SystemMessageId.YOU_NOT_ENOUGH_ADENA);
2554
2555 return false;
2556 }
2557
2558 if (count > 0)
2559 {
2560 ItemInstance ancientAdenaItem = _inventory.getAncientAdenaInstance();
2561 if (!_inventory.reduceAncientAdena(process, count, this, reference))
2562 return false;
2563
2564 InventoryUpdate iu = new InventoryUpdate();
2565 iu.addItem(ancientAdenaItem);
2566 sendPacket(iu);
2567
2568 if (sendMessage)
2569 {
2570 if (count > 1)
2571 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED).addItemName(PcInventory.ANCIENT_ADENA_ID).addItemNumber(count));
2572 else
2573 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED).addItemName(PcInventory.ANCIENT_ADENA_ID));
2574 }
2575 }
2576 return true;
2577 }
2578
2579 /**
2580 * Adds item to inventory and send InventoryUpdate packet to the L2PcInstance.
2581 * @param process String Identifier of process triggering this action
2582 * @param item ItemInstance to be added
2583 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2584 * @param sendMessage boolean Specifies whether to send message to Client about this action
2585 */
2586 public void addItem(String process, ItemInstance item, L2Object reference, boolean sendMessage)
2587 {
2588 if (item.getCount() > 0)
2589 {
2590 // Sends message to client if requested
2591 if (sendMessage)
2592 {
2593 if (item.getCount() > 1)
2594 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S2_S1).addItemName(item).addNumber(item.getCount()));
2595 else if (item.getEnchantLevel() > 0)
2596 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_A_S1_S2).addNumber(item.getEnchantLevel()).addItemName(item));
2597 else
2598 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S1).addItemName(item));
2599 }
2600
2601 // Add the item to inventory
2602 ItemInstance newitem = _inventory.addItem(process, item, this, reference);
2603
2604 // Send inventory update packet
2605 InventoryUpdate playerIU = new InventoryUpdate();
2606 playerIU.addItem(newitem);
2607 sendPacket(playerIU);
2608
2609 // Update current load as well
2610 StatusUpdate su = new StatusUpdate(this);
2611 su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
2612 sendPacket(su);
2613
2614 // Cursed Weapon
2615 if (CursedWeaponsManager.getInstance().isCursed(newitem.getItemId()))
2616 CursedWeaponsManager.getInstance().activate(this, newitem);
2617 // If you pickup arrows and a bow is equipped, try to equip them if no arrows is currently equipped.
2618 else if (item.getItem().getItemType() == EtcItemType.ARROW && getAttackType() == WeaponType.BOW && getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null)
2619 checkAndEquipArrows();
2620 }
2621 }
2622
2623 /**
2624 * Adds item to Inventory and send InventoryUpdate packet to the L2PcInstance.
2625 * @param process String Identifier of process triggering this action
2626 * @param itemId int Item Identifier of the item to be added
2627 * @param count int Quantity of items to be added
2628 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2629 * @param sendMessage boolean Specifies whether to send message to Client about this action
2630 * @return The created ItemInstance.
2631 */
2632 public ItemInstance addItem(String process, int itemId, int count, L2Object reference, boolean sendMessage)
2633 {
2634 if (count > 0)
2635 {
2636 // Retrieve the template of the item.
2637 final Item item = ItemTable.getInstance().getTemplate(itemId);
2638 if (item == null)
2639 {
2640 _log.log(Level.SEVERE, "Item id " + itemId + "doesn't exist, so it can't be added.");
2641 return null;
2642 }
2643
2644 // Sends message to client if requested.
2645 if (sendMessage && ((!isCastingNow() && item.getItemType() == EtcItemType.HERB) || item.getItemType() != EtcItemType.HERB))
2646 {
2647 if (count > 1)
2648 {
2649 if (process.equalsIgnoreCase("Sweep") || process.equalsIgnoreCase("Quest"))
2650 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S).addItemName(itemId).addItemNumber(count));
2651 else
2652 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S2_S1).addItemName(itemId).addItemNumber(count));
2653 }
2654 else
2655 {
2656 if (process.equalsIgnoreCase("Sweep") || process.equalsIgnoreCase("Quest"))
2657 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1).addItemName(itemId));
2658 else
2659 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_PICKED_UP_S1).addItemName(itemId));
2660 }
2661 }
2662
2663 // If the item is herb type, dont add it to inventory.
2664 if (item.getItemType() == EtcItemType.HERB)
2665 {
2666 final ItemInstance herb = new ItemInstance(0, itemId);
2667
2668 final IItemHandler handler = ItemHandler.getInstance().getItemHandler(herb.getEtcItem());
2669 if (handler != null)
2670 handler.useItem(this, herb, false);
2671 }
2672 else
2673 {
2674 // Add the item to inventory
2675 final ItemInstance createdItem = _inventory.addItem(process, itemId, count, this, reference);
2676
2677 // Cursed Weapon
2678 if (CursedWeaponsManager.getInstance().isCursed(createdItem.getItemId()))
2679 CursedWeaponsManager.getInstance().activate(this, createdItem);
2680 // If you pickup arrows and a bow is equipped, try to equip them if no arrows is currently equipped.
2681 else if (item.getItemType() == EtcItemType.ARROW && getAttackType() == WeaponType.BOW && getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null)
2682 checkAndEquipArrows();
2683
2684 return createdItem;
2685 }
2686 }
2687 return null;
2688 }
2689
2690 /**
2691 * Destroy item from inventory and send InventoryUpdate packet to the L2PcInstance.
2692 * @param process String Identifier of process triggering this action
2693 * @param item ItemInstance to be destroyed
2694 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2695 * @param sendMessage boolean Specifies whether to send message to Client about this action
2696 * @return boolean informing if the action was successfull
2697 */
2698 public boolean destroyItem(String process, ItemInstance item, L2Object reference, boolean sendMessage)
2699 {
2700 return this.destroyItem(process, item, item.getCount(), reference, sendMessage);
2701 }
2702
2703 /**
2704 * Destroy item from inventory and send InventoryUpdate packet to the L2PcInstance.
2705 * @param process String Identifier of process triggering this action
2706 * @param item ItemInstance to be destroyed
2707 * @param count int Quantity of ancient adena to be reduced
2708 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2709 * @param sendMessage boolean Specifies whether to send message to Client about this action
2710 * @return boolean informing if the action was successfull
2711 */
2712 public boolean destroyItem(String process, ItemInstance item, int count, L2Object reference, boolean sendMessage)
2713 {
2714 item = _inventory.destroyItem(process, item, count, this, reference);
2715
2716 if (item == null)
2717 {
2718 if (sendMessage)
2719 sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
2720
2721 return false;
2722 }
2723
2724 // Send inventory update packet
2725 InventoryUpdate playerIU = new InventoryUpdate();
2726 playerIU.addItem(item);
2727 sendPacket(playerIU);
2728
2729 // Update current load as well
2730 StatusUpdate su = new StatusUpdate(this);
2731 su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
2732 sendPacket(su);
2733
2734 // Sends message to client if requested
2735 if (sendMessage)
2736 {
2737 if (count > 1)
2738 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED).addItemName(item).addItemNumber(count));
2739 else
2740 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED).addItemName(item));
2741 }
2742 return true;
2743 }
2744
2745 /**
2746 * Destroys item from inventory and send InventoryUpdate packet to the L2PcInstance.
2747 * @param process String Identifier of process triggering this action
2748 * @param objectId int Item Instance identifier of the item to be destroyed
2749 * @param count int Quantity of items to be destroyed
2750 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2751 * @param sendMessage boolean Specifies whether to send message to Client about this action
2752 * @return boolean informing if the action was successfull
2753 */
2754 @Override
2755 public boolean destroyItem(String process, int objectId, int count, L2Object reference, boolean sendMessage)
2756 {
2757 ItemInstance item = _inventory.getItemByObjectId(objectId);
2758
2759 if (item == null)
2760 {
2761 if (sendMessage)
2762 sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
2763
2764 return false;
2765 }
2766 return this.destroyItem(process, item, count, reference, sendMessage);
2767 }
2768
2769 /**
2770 * Destroys shots from inventory without logging and only occasional saving to database. Sends InventoryUpdate packet to the L2PcInstance.
2771 * @param process String Identifier of process triggering this action
2772 * @param objectId int Item Instance identifier of the item to be destroyed
2773 * @param count int Quantity of items to be destroyed
2774 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2775 * @param sendMessage boolean Specifies whether to send message to Client about this action
2776 * @return boolean informing if the action was successfull
2777 */
2778 public boolean destroyItemWithoutTrace(String process, int objectId, int count, L2Object reference, boolean sendMessage)
2779 {
2780 ItemInstance item = _inventory.getItemByObjectId(objectId);
2781
2782 if (item == null || item.getCount() < count)
2783 {
2784 if (sendMessage)
2785 sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
2786
2787 return false;
2788 }
2789
2790 return this.destroyItem(null, item, count, reference, sendMessage);
2791 }
2792
2793 /**
2794 * Destroy item from inventory by using its <B>itemId</B> and send InventoryUpdate packet to the L2PcInstance.
2795 * @param process String Identifier of process triggering this action
2796 * @param itemId int Item identifier of the item to be destroyed
2797 * @param count int Quantity of items to be destroyed
2798 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2799 * @param sendMessage boolean Specifies whether to send message to Client about this action
2800 * @return boolean informing if the action was successfull
2801 */
2802 @Override
2803 public boolean destroyItemByItemId(String process, int itemId, int count, L2Object reference, boolean sendMessage)
2804 {
2805 if (itemId == 57)
2806 return reduceAdena(process, count, reference, sendMessage);
2807
2808 ItemInstance item = _inventory.getItemByItemId(itemId);
2809
2810 if (item == null || item.getCount() < count || _inventory.destroyItemByItemId(process, itemId, count, this, reference) == null)
2811 {
2812 if (sendMessage)
2813 sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
2814
2815 return false;
2816 }
2817
2818 // Send inventory update packet
2819 InventoryUpdate playerIU = new InventoryUpdate();
2820 playerIU.addItem(item);
2821 sendPacket(playerIU);
2822
2823 // Update current load as well
2824 StatusUpdate su = new StatusUpdate(this);
2825 su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
2826 sendPacket(su);
2827
2828 // Sends message to client if requested
2829 if (sendMessage)
2830 {
2831 if (count > 1)
2832 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S2_S1_DISAPPEARED).addItemName(itemId).addItemNumber(count));
2833 else
2834 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED).addItemName(itemId));
2835 }
2836 return true;
2837 }
2838
2839 /**
2840 * Transfers item to another ItemContainer and send InventoryUpdate packet to the L2PcInstance.
2841 * @param process String Identifier of process triggering this action
2842 * @param objectId int Item Identifier of the item to be transfered
2843 * @param count int Quantity of items to be transfered
2844 * @param target Inventory the Inventory target.
2845 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2846 * @return ItemInstance corresponding to the new item or the updated item in inventory
2847 */
2848 public ItemInstance transferItem(String process, int objectId, int count, Inventory target, L2Object reference)
2849 {
2850 final ItemInstance oldItem = checkItemManipulation(objectId, count);
2851 if (oldItem == null)
2852 return null;
2853
2854 final ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, this, reference);
2855 if (newItem == null)
2856 return null;
2857
2858 // Send inventory update packet
2859 InventoryUpdate playerIU = new InventoryUpdate();
2860
2861 if (oldItem.getCount() > 0 && oldItem != newItem)
2862 playerIU.addModifiedItem(oldItem);
2863 else
2864 playerIU.addRemovedItem(oldItem);
2865
2866 sendPacket(playerIU);
2867
2868 // Update current load as well
2869 StatusUpdate playerSU = new StatusUpdate(this);
2870 playerSU.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
2871 sendPacket(playerSU);
2872
2873 // Send target update packet
2874 if (target instanceof PcInventory)
2875 {
2876 final L2PcInstance targetPlayer = ((PcInventory) target).getOwner();
2877
2878 InventoryUpdate playerIU2 = new InventoryUpdate();
2879 if (newItem.getCount() > count)
2880 playerIU2.addModifiedItem(newItem);
2881 else
2882 playerIU2.addNewItem(newItem);
2883 targetPlayer.sendPacket(playerIU2);
2884
2885 // Update current load as well
2886 playerSU = new StatusUpdate(targetPlayer);
2887 playerSU.addAttribute(StatusUpdate.CUR_LOAD, targetPlayer.getCurrentLoad());
2888 targetPlayer.sendPacket(playerSU);
2889 }
2890 else if (target instanceof PetInventory)
2891 {
2892 PetInventoryUpdate petIU = new PetInventoryUpdate();
2893 if (newItem.getCount() > count)
2894 petIU.addModifiedItem(newItem);
2895 else
2896 petIU.addNewItem(newItem);
2897 ((PetInventory) target).getOwner().getOwner().sendPacket(petIU);
2898 }
2899 return newItem;
2900 }
2901
2902 /**
2903 * Drop item from inventory and send InventoryUpdate packet to the L2PcInstance.
2904 * @param process String Identifier of process triggering this action
2905 * @param item ItemInstance to be dropped
2906 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2907 * @param sendMessage boolean Specifies whether to send message to Client about this action
2908 * @param protectItem whether or not dropped item must be protected temporary against other players
2909 * @return boolean informing if the action was successfull
2910 */
2911 public boolean dropItem(String process, ItemInstance item, L2Object reference, boolean sendMessage, boolean protectItem)
2912 {
2913 item = _inventory.dropItem(process, item, this, reference);
2914
2915 if (item == null)
2916 {
2917 if (sendMessage)
2918 sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
2919
2920 return false;
2921 }
2922
2923 item.dropMe(this, getX() + Rnd.get(50) - 25, getY() + Rnd.get(50) - 25, getZ() + 20);
2924
2925 // retail drop protection
2926 if (protectItem)
2927 item.getDropProtection().protect(this);
2928
2929 // Send inventory update packet
2930 InventoryUpdate playerIU = new InventoryUpdate();
2931 playerIU.addItem(item);
2932 sendPacket(playerIU);
2933
2934 // Update current load as well
2935 StatusUpdate su = new StatusUpdate(this);
2936 su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
2937 sendPacket(su);
2938
2939 // Sends message to client if requested
2940 if (sendMessage)
2941 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_DROPPED_S1).addItemName(item));
2942
2943 return true;
2944 }
2945
2946 public boolean dropItem(String process, ItemInstance item, L2Object reference, boolean sendMessage)
2947 {
2948 return dropItem(process, item, reference, sendMessage, false);
2949 }
2950
2951 /**
2952 * Drop item from inventory by using its <B>objectID</B> and send InventoryUpdate packet to the L2PcInstance.
2953 * @param process String Identifier of process triggering this action
2954 * @param objectId int Item Instance identifier of the item to be dropped
2955 * @param count int Quantity of items to be dropped
2956 * @param x int coordinate for drop X
2957 * @param y int coordinate for drop Y
2958 * @param z int coordinate for drop Z
2959 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
2960 * @param sendMessage boolean Specifies whether to send message to Client about this action
2961 * @param protectItem boolean Activates drop protection on that item if true
2962 * @return ItemInstance corresponding to the new item or the updated item in inventory
2963 */
2964 public ItemInstance dropItem(String process, int objectId, int count, int x, int y, int z, L2Object reference, boolean sendMessage, boolean protectItem)
2965 {
2966 ItemInstance invitem = _inventory.getItemByObjectId(objectId);
2967 ItemInstance item = _inventory.dropItem(process, objectId, count, this, reference);
2968
2969 if (item == null)
2970 {
2971 if (sendMessage)
2972 sendPacket(SystemMessageId.NOT_ENOUGH_ITEMS);
2973
2974 return null;
2975 }
2976
2977 item.dropMe(this, x, y, z);
2978
2979 // retail drop protection
2980 if (protectItem)
2981 item.getDropProtection().protect(this);
2982
2983 // Send inventory update packet
2984 InventoryUpdate playerIU = new InventoryUpdate();
2985 playerIU.addItem(invitem);
2986 sendPacket(playerIU);
2987
2988 // Update current load as well
2989 StatusUpdate su = new StatusUpdate(this);
2990 su.addAttribute(StatusUpdate.CUR_LOAD, getCurrentLoad());
2991 sendPacket(su);
2992
2993 // Sends message to client if requested
2994 if (sendMessage)
2995 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_DROPPED_S1).addItemName(item));
2996
2997 return item;
2998 }
2999
3000 public ItemInstance checkItemManipulation(int objectId, int count)
3001 {
3002 if (L2World.getInstance().getObject(objectId) == null)
3003 return null;
3004
3005 final ItemInstance item = getInventory().getItemByObjectId(objectId);
3006
3007 if (item == null || item.getOwnerId() != getObjectId())
3008 return null;
3009
3010 if (count < 1 || (count > 1 && !item.isStackable()))
3011 return null;
3012
3013 if (count > item.getCount())
3014 return null;
3015
3016 // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting
3017 if (getPet() != null && getPet().getControlItemId() == objectId || getMountObjectID() == objectId)
3018 return null;
3019
3020 if (getActiveEnchantItem() != null && getActiveEnchantItem().getObjectId() == objectId)
3021 return null;
3022
3023 // We cannot put a Weapon with Augmention in WH while casting (Possible Exploit)
3024 if (item.isAugmented() && (isCastingNow() || isCastingSimultaneouslyNow()))
3025 return null;
3026
3027 return item;
3028 }
3029
3030 /**
3031 * Launch a task corresponding to Config time.
3032 * @param protect boolean Drop timer or activate it.
3033 */
3034 public void setProtection(boolean protect)
3035 {
3036 if (protect)
3037 {
3038 if (_protectTask == null)
3039 _protectTask = ThreadPoolManager.getInstance().scheduleGeneral(new ProtectTask(), Config.PLAYER_SPAWN_PROTECTION * 1000);
3040 }
3041 else
3042 {
3043 _protectTask.cancel(true);
3044 _protectTask = null;
3045 }
3046 broadcastUserInfo();
3047 }
3048
3049 public boolean isSpawnProtected()
3050 {
3051 return _protectTask != null;
3052 }
3053
3054 protected class ProtectTask implements Runnable
3055 {
3056 @Override
3057 public void run()
3058 {
3059 setProtection(false);
3060 sendMessage("The spawn protection has ended.");
3061 }
3062 }
3063
3064 /**
3065 * Set protection from agro mobs when getting up from fake death, according settings.
3066 */
3067 public void setRecentFakeDeath()
3068 {
3069 _recentFakeDeathEndTime = System.currentTimeMillis() + Config.PLAYER_FAKEDEATH_UP_PROTECTION * 1000;
3070 }
3071
3072 public void clearRecentFakeDeath()
3073 {
3074 _recentFakeDeathEndTime = 0;
3075 }
3076
3077 public boolean isRecentFakeDeath()
3078 {
3079 return _recentFakeDeathEndTime > System.currentTimeMillis();
3080 }
3081
3082 public final boolean isFakeDeath()
3083 {
3084 return _isFakeDeath;
3085 }
3086
3087 public final void setIsFakeDeath(boolean value)
3088 {
3089 _isFakeDeath = value;
3090 }
3091
3092 @Override
3093 public final boolean isAlikeDead()
3094 {
3095 if (super.isAlikeDead())
3096 return true;
3097
3098 return isFakeDeath();
3099 }
3100
3101 /**
3102 * @return The client owner of this char.
3103 */
3104 public L2GameClient getClient()
3105 {
3106 return _client;
3107 }
3108
3109 public void setClient(L2GameClient client)
3110 {
3111 _client = client;
3112 }
3113
3114 /**
3115 * Close the active connection with the client.
3116 * @param closeClient
3117 */
3118 private void closeNetConnection(boolean closeClient)
3119 {
3120 L2GameClient client = _client;
3121 if (client != null)
3122 {
3123 if (client.isDetached())
3124 client.cleanMe(true);
3125 else
3126 {
3127 if (!client.getConnection().isClosed())
3128 {
3129 if (closeClient)
3130 client.close(LeaveWorld.STATIC_PACKET);
3131 else
3132 client.close(ServerClose.STATIC_PACKET);
3133 }
3134 }
3135 }
3136 }
3137
3138 public Location getCurrentSkillWorldPosition()
3139 {
3140 return _currentSkillWorldPosition;
3141 }
3142
3143 public void setCurrentSkillWorldPosition(Location worldPosition)
3144 {
3145 _currentSkillWorldPosition = worldPosition;
3146 }
3147
3148 /**
3149 * @see net.sf.l2j.gameserver.model.actor.L2Character#enableSkill(net.sf.l2j.gameserver.model.L2Skill)
3150 */
3151 @Override
3152 public void enableSkill(L2Skill skill)
3153 {
3154 super.enableSkill(skill);
3155 _reuseTimeStamps.remove(skill.getReuseHashCode());
3156 }
3157
3158 /**
3159 * @see net.sf.l2j.gameserver.model.actor.L2Character#checkDoCastConditions(net.sf.l2j.gameserver.model.L2Skill)
3160 */
3161 @Override
3162 protected boolean checkDoCastConditions(L2Skill skill)
3163 {
3164 if (!super.checkDoCastConditions(skill))
3165 return false;
3166
3167 if (skill.getSkillType() == L2SkillType.SUMMON)
3168 {
3169 if (!((L2SkillSummon) skill).isCubic() && (getPet() != null || isMounted()))
3170 {
3171 sendPacket(SystemMessageId.SUMMON_ONLY_ONE);
3172 return false;
3173 }
3174 }
3175
3176 // Can't use Hero and resurrect skills during Olympiad
3177 if (isInOlympiadMode() && (skill.isHeroSkill() || skill.getSkillType() == L2SkillType.RESURRECT))
3178 {
3179 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THIS_SKILL_IS_NOT_AVAILABLE_FOR_THE_OLYMPIAD_EVENT));
3180 return false;
3181 }
3182
3183 // Check if the spell uses charges
3184 final int charges = getCharges();
3185 if (skill.getMaxCharges() == 0 && charges < skill.getNumCharges())
3186 {
3187 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill));
3188 return false;
3189 }
3190
3191 return true;
3192 }
3193
3194 /**
3195 * Manage actions when a player click on this L2PcInstance.<BR>
3196 * <BR>
3197 * <B><U> Actions on first click on the L2PcInstance (Select it)</U> :</B>
3198 * <ul>
3199 * <li>Set the target of the player</li>
3200 * <li>Send MyTargetSelected to the player (display the select window)</li>
3201 * </ul>
3202 * <B><U> Actions on second click on the L2PcInstance (Follow it/Attack it/Intercat with it)</U> :</B>
3203 * <ul>
3204 * <li>Send MyTargetSelected to the player (display the select window)</li>
3205 * <li>If this L2PcInstance has a Private Store, notify the player AI with INTERACT</li>
3206 * <li>If this L2PcInstance is autoAttackable, notify the player AI with ATTACK</li>
3207 * <li>If this L2PcInstance is NOT autoAttackable, notify the player AI with FOLLOW</li>
3208 * </ul>
3209 * @param player The player that start an action on this L2PcInstance
3210 */
3211 @Override
3212 public void onAction(L2PcInstance player)
3213 {
3214 // Set the target of the player
3215 if (player.getTarget() != this)
3216 player.setTarget(this);
3217 else
3218 {
3219 // Check if this L2PcInstance has a Private Store
3220 if (isInStoreMode())
3221 {
3222 player.getAI().setIntention(CtrlIntention.INTERACT, this);
3223 return;
3224 }
3225
3226 // Check if this L2PcInstance is autoAttackable
3227 if (isAutoAttackable(player))
3228 {
3229 // Player with lvl < 21 can't attack a cursed weapon holder and a cursed weapon holder can't attack players with lvl < 21
3230 if ((isCursedWeaponEquipped() && player.getLevel() < 21) || (player.isCursedWeaponEquipped() && getLevel() < 21))
3231 {
3232 player.sendPacket(ActionFailed.STATIC_PACKET);
3233 return;
3234 }
3235
3236 if (PathFinding.getInstance().canSeeTarget(player, this))
3237 {
3238 player.getAI().setIntention(CtrlIntention.ATTACK, this);
3239 player.onActionRequest();
3240 }
3241 }
3242 else
3243 {
3244 // avoids to stuck when clicking two or more times
3245 player.sendPacket(ActionFailed.STATIC_PACKET);
3246
3247 if (player != this && PathFinding.getInstance().canSeeTarget(player, this))
3248 player.getAI().setIntention(CtrlIntention.FOLLOW, this);
3249 }
3250 }
3251 }
3252
3253 @Override
3254 public void onActionShift(L2PcInstance player)
3255 {
3256 if (player.isGM())
3257 AdminEditChar.showCharacterInfo(player, this);
3258
3259 super.onActionShift(player);
3260 }
3261
3262 /**
3263 * @param barPixels
3264 * @return true if cp update should be done, false if not
3265 */
3266 private boolean needCpUpdate(int barPixels)
3267 {
3268 double currentCp = getCurrentCp();
3269
3270 if (currentCp <= 1.0 || getMaxCp() < barPixels)
3271 return true;
3272
3273 if (currentCp <= _cpUpdateDecCheck || currentCp >= _cpUpdateIncCheck)
3274 {
3275 if (currentCp == getMaxCp())
3276 {
3277 _cpUpdateIncCheck = currentCp + 1;
3278 _cpUpdateDecCheck = currentCp - _cpUpdateInterval;
3279 }
3280 else
3281 {
3282 double doubleMulti = currentCp / _cpUpdateInterval;
3283 int intMulti = (int) doubleMulti;
3284
3285 _cpUpdateDecCheck = _cpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
3286 _cpUpdateIncCheck = _cpUpdateDecCheck + _cpUpdateInterval;
3287 }
3288
3289 return true;
3290 }
3291
3292 return false;
3293 }
3294
3295 /**
3296 * @param barPixels
3297 * @return true if mp update should be done, false if not
3298 */
3299 private boolean needMpUpdate(int barPixels)
3300 {
3301 double currentMp = getCurrentMp();
3302
3303 if (currentMp <= 1.0 || getMaxMp() < barPixels)
3304 return true;
3305
3306 if (currentMp <= _mpUpdateDecCheck || currentMp >= _mpUpdateIncCheck)
3307 {
3308 if (currentMp == getMaxMp())
3309 {
3310 _mpUpdateIncCheck = currentMp + 1;
3311 _mpUpdateDecCheck = currentMp - _mpUpdateInterval;
3312 }
3313 else
3314 {
3315 double doubleMulti = currentMp / _mpUpdateInterval;
3316 int intMulti = (int) doubleMulti;
3317
3318 _mpUpdateDecCheck = _mpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
3319 _mpUpdateIncCheck = _mpUpdateDecCheck + _mpUpdateInterval;
3320 }
3321
3322 return true;
3323 }
3324
3325 return false;
3326 }
3327
3328 /**
3329 * Send packet StatusUpdate with current HP,MP and CP to the L2PcInstance and only current HP, MP and Level to all other L2PcInstance of the Party.
3330 * <ul>
3331 * <li>Send StatusUpdate with current HP, MP and CP to this L2PcInstance</li>
3332 * <li>Send PartySmallWindowUpdate with current HP, MP and Level to all other L2PcInstance of the Party</li>
3333 * </ul>
3334 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND current HP and MP to all L2PcInstance of the _statusListener</B></FONT>
3335 */
3336 @Override
3337 public void broadcastStatusUpdate()
3338 {
3339 // Send StatusUpdate with current HP, MP and CP to this L2PcInstance
3340 StatusUpdate su = new StatusUpdate(this);
3341 su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp());
3342 su.addAttribute(StatusUpdate.CUR_MP, (int) getCurrentMp());
3343 su.addAttribute(StatusUpdate.CUR_CP, (int) getCurrentCp());
3344 su.addAttribute(StatusUpdate.MAX_CP, getMaxCp());
3345 sendPacket(su);
3346
3347 final boolean needCpUpdate = needCpUpdate(352);
3348 final boolean needHpUpdate = needHpUpdate(352);
3349
3350 // Check if a party is in progress and party window update is needed.
3351 if (_party != null && (needCpUpdate || needHpUpdate || needMpUpdate(352)))
3352 _party.broadcastToPartyMembers(this, new PartySmallWindowUpdate(this));
3353
3354 if (isInOlympiadMode() && isOlympiadStart() && (needCpUpdate || needHpUpdate))
3355 {
3356 final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(getOlympiadGameId());
3357 if (game != null && game.isBattleStarted())
3358 game.getZone().broadcastStatusUpdate(this);
3359 }
3360
3361 // In duel, MP updated only with CP or HP
3362 if (isInDuel() && (needCpUpdate || needHpUpdate))
3363 {
3364 ExDuelUpdateUserInfo update = new ExDuelUpdateUserInfo(this);
3365 DuelManager.getInstance().broadcastToOppositeTeam(this, update);
3366 }
3367 }
3368
3369 /**
3370 * Broadcast informations from a user to himself and his knownlist.<BR>
3371 * If player is morphed, it sends informations from the template the player is using.
3372 * <ul>
3373 * <li>Send a UserInfo packet (public and private data) to this L2PcInstance.</li>
3374 * <li>Send a CharInfo packet (public data only) to L2PcInstance's knownlist.</li>
3375 * </ul>
3376 */
3377 public final void broadcastUserInfo()
3378 {
3379 sendPacket(new UserInfo(this));
3380
3381 if (getPoly().isMorphed())
3382 Broadcast.toKnownPlayers(this, new AbstractNpcInfo.PcMorphInfo(this, getPoly().getNpcTemplate()));
3383 else
3384 broadcastCharInfo();
3385 }
3386
3387 public final void broadcastCharInfo()
3388 {
3389 for (L2PcInstance player : getKnownList().getKnownType(L2PcInstance.class))
3390 {
3391 player.sendPacket(new CharInfo(this));
3392
3393 final int relation = getRelation(player);
3394 player.sendPacket(new RelationChanged(this, relation, isAutoAttackable(player)));
3395 if (getPet() != null)
3396 player.sendPacket(new RelationChanged(getPet(), relation, isAutoAttackable(player)));
3397 }
3398 }
3399
3400 /**
3401 * Broadcast player title information.
3402 */
3403 public final void broadcastTitleInfo()
3404 {
3405 sendPacket(new UserInfo(this));
3406 broadcastPacket(new TitleUpdate(this));
3407 }
3408
3409 /**
3410 * @return the Alliance Identifier of the L2PcInstance.
3411 */
3412 public int getAllyId()
3413 {
3414 if (_clan == null)
3415 return 0;
3416
3417 return _clan.getAllyId();
3418 }
3419
3420 public int getAllyCrestId()
3421 {
3422 if (getClanId() == 0)
3423 return 0;
3424
3425 if (getClan().getAllyId() == 0)
3426 return 0;
3427
3428 return getClan().getAllyCrestId();
3429 }
3430
3431 /**
3432 * Send a packet to the L2PcInstance.
3433 */
3434 @Override
3435 public void sendPacket(L2GameServerPacket packet)
3436 {
3437 if (_client != null)
3438 _client.sendPacket(packet);
3439 }
3440
3441 /**
3442 * Send SystemMessage packet.
3443 * @param id SystemMessageId
3444 */
3445 @Override
3446 public void sendPacket(SystemMessageId id)
3447 {
3448 sendPacket(SystemMessage.getSystemMessage(id));
3449 }
3450
3451 /**
3452 * Manage Interact Task with another L2PcInstance.<BR>
3453 * Turn the character in front of the target.<BR>
3454 * In case of private stores, send the related packet.
3455 * @param target The L2Character targeted
3456 */
3457 public void doInteract(L2Character target)
3458 {
3459 if (target instanceof L2PcInstance)
3460 {
3461 L2PcInstance temp = (L2PcInstance) target;
3462 sendPacket(new MoveToPawn(this, temp, L2Npc.INTERACTION_DISTANCE));
3463
3464 switch (temp.getPrivateStoreType())
3465 {
3466 case SELL:
3467 case PACKAGE_SELL:
3468 sendPacket(new PrivateStoreListSell(this, temp));
3469 break;
3470
3471 case BUY:
3472 sendPacket(new PrivateStoreListBuy(this, temp));
3473 break;
3474
3475 case MANUFACTURE:
3476 sendPacket(new RecipeShopSellList(this, temp));
3477 break;
3478 }
3479 }
3480 else
3481 {
3482 // _interactTarget=null should never happen but one never knows ^^;
3483 if (target != null)
3484 target.onAction(this);
3485 }
3486 }
3487
3488 /**
3489 * Manage AutoLoot Task.
3490 * <ul>
3491 * <li>Send a System Message to the L2PcInstance : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2</li>
3492 * <li>Add the Item to the L2PcInstance inventory</li>
3493 * <li>Send InventoryUpdate to this L2PcInstance with NewItem (use a new slot) or ModifiedItem (increase amount)</li>
3494 * <li>Send StatusUpdate to this L2PcInstance with current weight</li>
3495 * </ul>
3496 * <FONT COLOR=#FF0000><B> <U>Caution</U> : If a Party is in progress, distribute Items between party members</B></FONT>
3497 * @param target The reference Object.
3498 * @param item The dropped ItemHolder.
3499 */
3500 public void doAutoLoot(L2Attackable target, IntIntHolder item)
3501 {
3502 if (isInParty())
3503 getParty().distributeItem(this, item, false, target);
3504 else if (item.getId() == 57)
3505 addAdena("Loot", item.getValue(), target, true);
3506 else
3507 addItem("Loot", item.getId(), item.getValue(), target, true);
3508 }
3509
3510 /**
3511 * Manage Pickup Task.
3512 * <ul>
3513 * <li>Send StopMove to this L2PcInstance</li>
3514 * <li>Remove the ItemInstance from the world and send GetItem packets</li>
3515 * <li>Send a System Message to the L2PcInstance : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2</li>
3516 * <li>Add the Item to the L2PcInstance inventory</li>
3517 * <li>Send InventoryUpdate to this L2PcInstance with NewItem (use a new slot) or ModifiedItem (increase amount)</li>
3518 * <li>Send StatusUpdate to this L2PcInstance with current weight</li>
3519 * </ul>
3520 * <FONT COLOR=#FF0000><B> <U>Caution</U> : If a Party is in progress, distribute Items between party members</B></FONT>
3521 * @param object The ItemInstance to pick up
3522 */
3523 @Override
3524 public void doPickupItem(L2Object object)
3525 {
3526 if (isAlikeDead() || isFakeDeath())
3527 return;
3528
3529 // Set the AI Intention to IDLE
3530 getAI().setIntention(CtrlIntention.IDLE);
3531
3532 // Check if the L2Object to pick up is a ItemInstance
3533 if (!(object instanceof ItemInstance))
3534 {
3535 // dont try to pickup anything that is not an item :)
3536 _log.warning(getName() + " tried to pickup a wrong target: " + object);
3537 return;
3538 }
3539
3540 ItemInstance target = (ItemInstance) object;
3541
3542 // Send ActionFailed to this L2PcInstance
3543 sendPacket(ActionFailed.STATIC_PACKET);
3544 sendPacket(new StopMove(this));
3545
3546 synchronized (target)
3547 {
3548 if (!target.isVisible())
3549 return;
3550
3551 if (isInStoreMode())
3552 return;
3553
3554 if (!target.getDropProtection().tryPickUp(this))
3555 {
3556 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1).addItemName(target.getItemId()));
3557 return;
3558 }
3559
3560 if (!_inventory.validateWeight(target.getCount() * target.getItem().getWeight()))
3561 {
3562 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.WEIGHT_LIMIT_EXCEEDED));
3563 return;
3564 }
3565
3566 if (((isInParty() && getParty().getLootDistribution() == L2Party.ITEM_LOOTER) || !isInParty()) && !_inventory.validateCapacity(target))
3567 {
3568 sendPacket(SystemMessageId.SLOTS_FULL);
3569 return;
3570 }
3571
3572 if (getActiveTradeList() != null)
3573 {
3574 sendPacket(SystemMessageId.CANNOT_PICKUP_OR_USE_ITEM_WHILE_TRADING);
3575 return;
3576 }
3577
3578 if (target.getOwnerId() != 0 && target.getOwnerId() != getObjectId() && !isInLooterParty(target.getOwnerId()))
3579 {
3580 if (target.getItemId() == 57)
3581 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1_ADENA).addNumber(target.getCount()));
3582 else if (target.getCount() > 1)
3583 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S2_S1_S).addItemName(target).addNumber(target.getCount()));
3584 else
3585 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FAILED_TO_PICKUP_S1).addItemName(target));
3586
3587 return;
3588 }
3589
3590 if (target.getItemLootShedule() != null && (target.getOwnerId() == getObjectId() || isInLooterParty(target.getOwnerId())))
3591 target.resetOwnerTimer();
3592
3593 // Remove the ItemInstance from the world and send GetItem packets
3594 target.pickupMe(this);
3595
3596 // item must be removed from ItemsOnGroundManager if is active
3597 ItemsOnGroundTaskManager.getInstance().remove(target);
3598 }
3599
3600 // Auto use herbs - pick up
3601 if (target.getItemType() == EtcItemType.HERB)
3602 {
3603 IItemHandler handler = ItemHandler.getInstance().getItemHandler(target.getEtcItem());
3604 if (handler != null)
3605 handler.useItem(this, target, false);
3606
3607 ItemTable.getInstance().destroyItem("Consume", target, this, null);
3608 }
3609 // Cursed Weapons are not distributed
3610 else if (CursedWeaponsManager.getInstance().isCursed(target.getItemId()))
3611 {
3612 addItem("Pickup", target, null, true);
3613 }
3614 else
3615 {
3616 // if item is instance of L2ArmorType or WeaponType broadcast an "Attention" system message
3617 if (target.getItemType() instanceof ArmorType || target.getItemType() instanceof WeaponType)
3618 {
3619 if (target.getEnchantLevel() > 0)
3620 {
3621 SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_S1_PICKED_UP_S2_S3);
3622 msg.addString(getName());
3623 msg.addNumber(target.getEnchantLevel());
3624 msg.addItemName(target.getItemId());
3625 broadcastPacket(msg, 1400);
3626 }
3627 else
3628 {
3629 SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_S1_PICKED_UP_S2);
3630 msg.addString(getName());
3631 msg.addItemName(target.getItemId());
3632 broadcastPacket(msg, 1400);
3633 }
3634 }
3635
3636 // Check if a Party is in progress
3637 if (isInParty())
3638 getParty().distributeItem(this, target);
3639 // Target is adena
3640 else if (target.getItemId() == 57 && getInventory().getAdenaInstance() != null)
3641 {
3642 addAdena("Pickup", target.getCount(), null, true);
3643 ItemTable.getInstance().destroyItem("Pickup", target, this, null);
3644 }
3645 // Target is regular item
3646 else
3647 addItem("Pickup", target, null, true);
3648 }
3649
3650 // Schedule a paralyzed task to wait for the animation to finish
3651 ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()
3652 {
3653 @Override
3654 public void run()
3655 {
3656 setIsParalyzed(false);
3657 }
3658 }, 250);
3659 setIsParalyzed(true);
3660 }
3661
3662 @Override
3663 public void doAttack(L2Character target)
3664 {
3665 super.doAttack(target);
3666 getActingPlayer().clearRecentFakeDeath();
3667 }
3668
3669 @Override
3670 public void doCast(L2Skill skill)
3671 {
3672 super.doCast(skill);
3673 getActingPlayer().clearRecentFakeDeath();
3674 }
3675
3676 public boolean canOpenPrivateStore()
3677 {
3678 if (getActiveTradeList() != null)
3679 cancelActiveTrade();
3680
3681 return !isAlikeDead() && !isInOlympiadMode() && !isMounted() && !isInsideZone(ZoneId.NO_STORE) && !isCastingNow();
3682 }
3683
3684 public void tryOpenPrivateBuyStore()
3685 {
3686 if (canOpenPrivateStore())
3687 {
3688 if (getPrivateStoreType() == PrivateStoreType.BUY || getPrivateStoreType() == PrivateStoreType.BUY_MANAGE)
3689 setPrivateStoreType(PrivateStoreType.NONE);
3690
3691 if (getPrivateStoreType() == PrivateStoreType.NONE)
3692 {
3693 standUp();
3694
3695 setPrivateStoreType(PrivateStoreType.BUY_MANAGE);
3696 sendPacket(new PrivateStoreManageListBuy(this));
3697 }
3698 }
3699 else
3700 {
3701 if (isInsideZone(ZoneId.NO_STORE))
3702 sendPacket(SystemMessageId.NO_PRIVATE_STORE_HERE);
3703
3704 sendPacket(ActionFailed.STATIC_PACKET);
3705 }
3706 }
3707
3708 public void tryOpenPrivateSellStore(boolean isPackageSale)
3709 {
3710 if (canOpenPrivateStore())
3711 {
3712 if (getPrivateStoreType() == PrivateStoreType.SELL || getPrivateStoreType() == PrivateStoreType.SELL_MANAGE || getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL)
3713 setPrivateStoreType(PrivateStoreType.NONE);
3714
3715 if (getPrivateStoreType() == PrivateStoreType.NONE)
3716 {
3717 standUp();
3718
3719 setPrivateStoreType(PrivateStoreType.SELL_MANAGE);
3720 sendPacket(new PrivateStoreManageListSell(this, isPackageSale));
3721 }
3722 }
3723 else
3724 {
3725 if (isInsideZone(ZoneId.NO_STORE))
3726 sendPacket(SystemMessageId.NO_PRIVATE_STORE_HERE);
3727
3728 sendPacket(ActionFailed.STATIC_PACKET);
3729 }
3730 }
3731
3732 public void tryOpenWorkshop(boolean isDwarven)
3733 {
3734 if (canOpenPrivateStore())
3735 {
3736 if (isInStoreMode())
3737 setPrivateStoreType(PrivateStoreType.NONE);
3738
3739 if (getPrivateStoreType() == PrivateStoreType.NONE)
3740 {
3741 standUp();
3742
3743 if (getCreateList() == null)
3744 setCreateList(new L2ManufactureList());
3745
3746 sendPacket(new RecipeShopManageList(this, isDwarven));
3747 }
3748 }
3749 else
3750 {
3751 if (isInsideZone(ZoneId.NO_STORE))
3752 sendPacket(SystemMessageId.NO_PRIVATE_WORKSHOP_HERE);
3753
3754 sendPacket(ActionFailed.STATIC_PACKET);
3755 }
3756 }
3757
3758 /**
3759 * Set a target.
3760 * <ul>
3761 * <li>Remove the L2PcInstance from the _statusListener of the old target if it was a L2Character</li>
3762 * <li>Add the L2PcInstance to the _statusListener of the new target if it's a L2Character</li>
3763 * <li>Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object)</li>
3764 * </ul>
3765 * @param newTarget The L2Object to target
3766 */
3767 @Override
3768 public void setTarget(L2Object newTarget)
3769 {
3770 if (newTarget != null)
3771 {
3772 boolean isParty = (((newTarget instanceof L2PcInstance) && isInParty() && getParty().getPartyMembers().contains(newTarget)));
3773
3774 // Check if the new target is visible
3775 if (!isParty && (!newTarget.isVisible() || Math.abs(newTarget.getZ() - getZ()) > 1000))
3776 newTarget = null;
3777 }
3778
3779 // Can't target and attack festival monsters if not participant
3780 if ((newTarget instanceof L2FestivalMonsterInstance) && !isFestivalParticipant())
3781 newTarget = null;
3782 // Can't target and attack rift invaders if not in the same room
3783 else if (isInParty() && getParty().isInDimensionalRift())
3784 {
3785 byte riftType = getParty().getDimensionalRift().getType();
3786 byte riftRoom = getParty().getDimensionalRift().getCurrentRoom();
3787
3788 if (newTarget != null && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(newTarget.getX(), newTarget.getY(), newTarget.getZ()))
3789 newTarget = null;
3790 }
3791
3792 // Get the current target
3793 L2Object oldTarget = getTarget();
3794
3795 if (oldTarget != null)
3796 {
3797 if (oldTarget.equals(newTarget))
3798 return; // no target change
3799
3800 // Remove the L2PcInstance from the _statusListener of the old target if it was a L2Character
3801 if (oldTarget instanceof L2Character)
3802 ((L2Character) oldTarget).removeStatusListener(this);
3803 }
3804
3805 // Verify if it's a static object.
3806 if (newTarget instanceof L2StaticObjectInstance)
3807 {
3808 sendPacket(new MyTargetSelected(newTarget.getObjectId(), getLevel()));
3809 sendPacket(new StaticObject((L2StaticObjectInstance) newTarget));
3810 }
3811 // Add the L2PcInstance to the _statusListener of the new target if it's a L2Character
3812 else if (newTarget instanceof L2Character)
3813 {
3814 final L2Character target = (L2Character) newTarget;
3815
3816 target.addStatusListener(this);
3817
3818 // Show the client his new target.
3819 if (target.isAutoAttackable(this))
3820 {
3821 // Show the client his new target.
3822 sendPacket(new MyTargetSelected(target.getObjectId(), getLevel() - target.getLevel()));
3823
3824 // Send max/current hp.
3825 final StatusUpdate su = new StatusUpdate(target);
3826 su.addAttribute(StatusUpdate.MAX_HP, target.getMaxHp());
3827 su.addAttribute(StatusUpdate.CUR_HP, (int) target.getCurrentHp());
3828 sendPacket(su);
3829 }
3830 else
3831 sendPacket(new MyTargetSelected(target.getObjectId(), 0));
3832
3833 Broadcast.toKnownPlayers(this, new TargetSelected(getObjectId(), newTarget.getObjectId(), getX(), getY(), getZ()));
3834 }
3835
3836 if (newTarget == null && getTarget() != null)
3837 {
3838 broadcastPacket(new TargetUnselected(this));
3839 setCurrentFolkNPC(null);
3840 }
3841 else
3842 {
3843 // Rehabilitates that useful check.
3844 if (newTarget instanceof L2NpcInstance)
3845 setCurrentFolkNPC((L2Npc) newTarget);
3846 }
3847
3848 // Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object)
3849 super.setTarget(newTarget);
3850 }
3851
3852 /**
3853 * Return the active weapon instance (always equipped in the right hand).
3854 */
3855 @Override
3856 public ItemInstance getActiveWeaponInstance()
3857 {
3858 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
3859 }
3860
3861 /**
3862 * Return the active weapon item (always equipped in the right hand).
3863 */
3864 @Override
3865 public Weapon getActiveWeaponItem()
3866 {
3867 ItemInstance weapon = getActiveWeaponInstance();
3868
3869 if (weapon == null)
3870 return getFistsWeaponItem();
3871
3872 return (Weapon) weapon.getItem();
3873 }
3874
3875 public ItemInstance getChestArmorInstance()
3876 {
3877 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
3878 }
3879
3880 public Armor getActiveChestArmorItem()
3881 {
3882 ItemInstance armor = getChestArmorInstance();
3883
3884 if (armor == null)
3885 return null;
3886
3887 return (Armor) armor.getItem();
3888 }
3889
3890 public boolean isWearingHeavyArmor()
3891 {
3892 ItemInstance armor = getChestArmorInstance();
3893
3894 if ((ArmorType) armor.getItemType() == ArmorType.HEAVY)
3895 return true;
3896
3897 return false;
3898 }
3899
3900 public boolean isWearingLightArmor()
3901 {
3902 ItemInstance armor = getChestArmorInstance();
3903
3904 if ((ArmorType) armor.getItemType() == ArmorType.LIGHT)
3905 return true;
3906
3907 return false;
3908 }
3909
3910 public boolean isWearingMagicArmor()
3911 {
3912 ItemInstance armor = getChestArmorInstance();
3913
3914 if ((ArmorType) armor.getItemType() == ArmorType.MAGIC)
3915 return true;
3916
3917 return false;
3918 }
3919
3920 public boolean isMarried()
3921 {
3922 return _married;
3923 }
3924
3925 public void setMarried(boolean state)
3926 {
3927 _married = state;
3928 }
3929
3930 public void setUnderMarryRequest(boolean state)
3931 {
3932 _marryrequest = state;
3933 }
3934
3935 public boolean isUnderMarryRequest()
3936 {
3937 return _marryrequest;
3938 }
3939
3940 public int getCoupleId()
3941 {
3942 return _coupleId;
3943 }
3944
3945 public void setCoupleId(int coupleId)
3946 {
3947 _coupleId = coupleId;
3948 }
3949
3950 public void setRequesterId(int requesterId)
3951 {
3952 _requesterId = requesterId;
3953 }
3954
3955 public void EngageAnswer(int answer)
3956 {
3957 if (!_marryrequest || _requesterId == 0)
3958 return;
3959
3960 L2PcInstance ptarget = L2World.getInstance().getPlayer(_requesterId);
3961 if (ptarget != null)
3962 {
3963 if (answer == 1)
3964 {
3965 // Create the couple
3966 CoupleManager.getInstance().createCouple(ptarget, this);
3967
3968 // Then "finish the job"
3969 L2WeddingManagerInstance.justMarried(ptarget, this);
3970 }
3971 else
3972 {
3973 setUnderMarryRequest(false);
3974 sendMessage("You declined your partner's marriage request.");
3975
3976 ptarget.setUnderMarryRequest(false);
3977 ptarget.sendMessage("Your partner declined your marriage request.");
3978 }
3979 }
3980 }
3981
3982 /**
3983 * Return the secondary weapon instance (always equipped in the left hand).
3984 */
3985 @Override
3986 public ItemInstance getSecondaryWeaponInstance()
3987 {
3988 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
3989 }
3990
3991 /**
3992 * Return the secondary L2Item item (always equiped in the left hand).
3993 */
3994 @Override
3995 public Item getSecondaryWeaponItem()
3996 {
3997 ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
3998 if (item != null)
3999 return item.getItem();
4000
4001 return null;
4002 }
4003
4004 /**
4005 * Kill the L2Character, Apply Death Penalty, Manage gain/loss Karma and Item Drop.
4006 * <ul>
4007 * <li>Reduce the Experience of the L2PcInstance in function of the calculated Death Penalty</li>
4008 * <li>If necessary, unsummon the Pet of the killed L2PcInstance</li>
4009 * <li>Manage Karma gain for attacker and Karam loss for the killed L2PcInstance</li>
4010 * <li>If the killed L2PcInstance has Karma, manage Drop Item</li>
4011 * <li>Kill the L2PcInstance</li>
4012 * </ul>
4013 * @param killer The L2Character who attacks
4014 */
4015 @Override
4016 public boolean doDie(L2Character killer)
4017 {
4018 // Kill the L2PcInstance
4019 if (!super.doDie(killer))
4020 return false;
4021
4022 if (isMounted())
4023 stopFeed();
4024
4025 synchronized (this)
4026 {
4027 if (isFakeDeath())
4028 stopFakeDeath(true);
4029 }
4030
4031 if (killer != null)
4032 {
4033 L2PcInstance pk = killer.getActingPlayer();
4034
4035 // Clear resurrect xp calculation
4036 setExpBeforeDeath(0);
4037
4038 if (isCursedWeaponEquipped())
4039 CursedWeaponsManager.getInstance().drop(_cursedWeaponEquippedId, killer);
4040 else
4041 {
4042 if (pk == null || !pk.isCursedWeaponEquipped())
4043 {
4044 onDieDropItem(killer); // Check if any item should be dropped
4045
4046 // if the area isn't an arena
4047 if (!isInArena())
4048 {
4049 // if both victim and attacker got clans & aren't academicians
4050 if (pk != null && pk.getClan() != null && getClan() != null && !isAcademyMember() && !pk.isAcademyMember())
4051 {
4052 // if clans got mutual war, then use the reputation calcul
4053 if (_clan.isAtWarWith(pk.getClanId()) && pk.getClan().isAtWarWith(_clan.getClanId()))
4054 {
4055 // when your reputation score is 0 or below, the other clan cannot acquire any reputation points
4056 if (getClan().getReputationScore() > 0)
4057 pk.getClan().addReputationScore(1);
4058 // when the opposing sides reputation score is 0 or below, your clans reputation score doesn't decrease
4059 if (pk.getClan().getReputationScore() > 0)
4060 _clan.takeReputationScore(1);
4061 }
4062 }
4063 }
4064
4065 // Reduce player's xp and karma.
4066 if (Config.ALT_GAME_DELEVEL && (getSkillLevel(L2Skill.SKILL_LUCKY) < 0 || getStat().getLevel() > 9))
4067 deathPenalty(pk != null && getClan() != null && pk.getClan() != null && (getClan().isAtWarWith(pk.getClanId()) || pk.getClan().isAtWarWith(getClanId())), pk != null, killer instanceof L2SiegeGuardInstance);
4068 }
4069 }
4070 }
4071
4072 // Unsummon Cubics
4073 if (!_cubics.isEmpty())
4074 {
4075 for (L2CubicInstance cubic : _cubics.values())
4076 {
4077 cubic.stopAction();
4078 cubic.cancelDisappear();
4079 }
4080
4081 _cubics.clear();
4082 }
4083
4084 if (_fusionSkill != null)
4085 abortCast();
4086
4087 for (L2Character character : getKnownList().getKnownType(L2Character.class))
4088 if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
4089 character.abortCast();
4090
4091 if (isInParty() && getParty().isInDimensionalRift())
4092 getParty().getDimensionalRift().getDeadMemberList().add(this);
4093
4094 // calculate death penalty buff
4095 calculateDeathPenaltyBuffLevel(killer);
4096
4097 stopWaterTask();
4098
4099 if (isPhoenixBlessed() || (isAffected(L2EffectFlag.CHARM_OF_COURAGE) && isInSiege()))
4100 reviveRequest(this, null, false);
4101
4102 // Icons update in order to get retained buffs list
4103 updateEffectIcons();
4104
4105 return true;
4106 }
4107
4108 private void onDieDropItem(L2Character killer)
4109 {
4110 if (killer == null)
4111 return;
4112
4113 L2PcInstance pk = killer.getActingPlayer();
4114 if (getKarma() <= 0 && pk != null && pk.getClan() != null && getClan() != null && pk.getClan().isAtWarWith(getClanId()))
4115 return;
4116
4117 if ((!isInsideZone(ZoneId.PVP) || pk == null) && (!isGM() || Config.KARMA_DROP_GM))
4118 {
4119 boolean isKillerNpc = (killer instanceof L2Npc);
4120 int pkLimit = Config.KARMA_PK_LIMIT;
4121
4122 int dropEquip = 0;
4123 int dropEquipWeapon = 0;
4124 int dropItem = 0;
4125 int dropLimit = 0;
4126 int dropPercent = 0;
4127
4128 if (getKarma() > 0 && getPkKills() >= pkLimit)
4129 {
4130 dropPercent = Config.KARMA_RATE_DROP;
4131 dropEquip = Config.KARMA_RATE_DROP_EQUIP;
4132 dropEquipWeapon = Config.KARMA_RATE_DROP_EQUIP_WEAPON;
4133 dropItem = Config.KARMA_RATE_DROP_ITEM;
4134 dropLimit = Config.KARMA_DROP_LIMIT;
4135 }
4136 else if (isKillerNpc && getLevel() > 4 && !isFestivalParticipant())
4137 {
4138 dropPercent = Config.PLAYER_RATE_DROP;
4139 dropEquip = Config.PLAYER_RATE_DROP_EQUIP;
4140 dropEquipWeapon = Config.PLAYER_RATE_DROP_EQUIP_WEAPON;
4141 dropItem = Config.PLAYER_RATE_DROP_ITEM;
4142 dropLimit = Config.PLAYER_DROP_LIMIT;
4143 }
4144
4145 if (dropPercent > 0 && Rnd.get(100) < dropPercent)
4146 {
4147 int dropCount = 0;
4148 int itemDropPercent = 0;
4149
4150 for (ItemInstance itemDrop : getInventory().getItems())
4151 {
4152 // Don't drop those following things
4153 if (!itemDrop.isDropable() || itemDrop.isShadowItem() || itemDrop.getItemId() == 57 || itemDrop.getItem().getType2() == Item.TYPE2_QUEST || getPet() != null && getPet().getControlItemId() == itemDrop.getItemId() || Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_ITEMS, itemDrop.getItemId()) >= 0 || Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_PET_ITEMS, itemDrop.getItemId()) >= 0)
4154 continue;
4155
4156 if (itemDrop.isEquipped())
4157 {
4158 // Set proper chance according to Item type of equipped Item
4159 itemDropPercent = itemDrop.getItem().getType2() == Item.TYPE2_WEAPON ? dropEquipWeapon : dropEquip;
4160 getInventory().unEquipItemInSlot(itemDrop.getLocationSlot());
4161 }
4162 else
4163 itemDropPercent = dropItem; // Item in inventory
4164
4165 // NOTE: Each time an item is dropped, the chance of another item being dropped gets lesser (dropCount * 2)
4166 if (Rnd.get(100) < itemDropPercent)
4167 {
4168 dropItem("DieDrop", itemDrop, killer, true);
4169
4170 if (++dropCount >= dropLimit)
4171 break;
4172 }
4173 }
4174 }
4175 }
4176 }
4177
4178 public void updateKarmaLoss(long exp)
4179 {
4180 if (!isCursedWeaponEquipped() && getKarma() > 0)
4181 {
4182 int karmaLost = Formulas.calculateKarmaLost(getLevel(), exp);
4183 if (karmaLost > 0)
4184 setKarma(getKarma() - karmaLost);
4185 }
4186 }
4187
4188 /**
4189 * This method is used to update PvP counter, or PK counter / add Karma if necessary.<br>
4190 * It also updates clan kills/deaths counters on siege.
4191 * @param target The L2Playable victim.
4192 */
4193 public void onKillUpdatePvPKarma(L2Playable target)
4194 {
4195 if (target == null)
4196 return;
4197
4198 final L2PcInstance targetPlayer = target.getActingPlayer();
4199 if (targetPlayer == null || targetPlayer == this)
4200 return;
4201
4202 // Don't rank up the CW if it was a summon.
4203 if (isCursedWeaponEquipped() && target instanceof L2PcInstance)
4204 {
4205 CursedWeaponsManager.getInstance().increaseKills(_cursedWeaponEquippedId);
4206 return;
4207 }
4208
4209 if (Config.FACTION_SYSTEM_ALLOW_REWARD_FOR_EACH_PVP && Config.FACTION_SYSTEM_ENABLE)
4210 {
4211 if ((issfaction() && targetPlayer.issfaction()) || (isffaction() && targetPlayer.isffaction()))
4212 {
4213 sendMessage("Cannot Get PvP Reward From The Same Faction.");
4214 sendPacket(ActionFailed.STATIC_PACKET);
4215 return;
4216 }
4217
4218 int itemReward = Config.FACTION_SYSTEM_PVP_ITEM_REWARD_AMOUNT;
4219 int idReward = Config.FACTION_SYSTEM_PVP_ITEM_REWARD_ID;
4220 addItem("Loot", idReward, itemReward, this, true);
4221 sendMessage("Congrats, you win " + itemReward + " Adena From Your Kill.");
4222 }
4223
4224 // pvp?
4225 if (checkIfPvP(target) || (isffaction() && targetPlayer.issfaction()) || (issfaction() && targetPlayer.isffaction()))
4226 {
4227 getPvpKills();
4228 return;
4229 }
4230
4231 if (targetPlayer.issfaction() || targetPlayer.isffaction())
4232 {
4233 return;
4234 }
4235
4236 // If in duel and you kill (only can kill l2summon), do nothing
4237 if (isInDuel() && targetPlayer.isInDuel())
4238 return;
4239
4240 // If in pvp zone, do nothing.
4241 if (isInsideZone(ZoneId.PVP) && targetPlayer.isInsideZone(ZoneId.PVP))
4242 {
4243 // Until the zone was a siege zone. Check also if victim was a player. Randomers aren't counted.
4244 if (target instanceof L2PcInstance && getSiegeState() > 0 && targetPlayer.getSiegeState() > 0 && getSiegeState() != targetPlayer.getSiegeState())
4245 {
4246 // Now check clan relations.
4247 final L2Clan killerClan = getClan();
4248 if (killerClan != null)
4249 killerClan.setSiegeKills(killerClan.getSiegeKills() + 1);
4250
4251 final L2Clan targetClan = targetPlayer.getClan();
4252 if (targetClan != null)
4253 targetClan.setSiegeDeaths(targetClan.getSiegeDeaths() + 1);
4254 }
4255 return;
4256 }
4257
4258 // Check if it's pvp (cases : regular, wars, victim is PKer)
4259 if (checkIfPvP(target) || (targetPlayer.getClan() != null && getClan() != null && getClan().isAtWarWith(targetPlayer.getClanId()) && targetPlayer.getClan().isAtWarWith(getClanId()) && targetPlayer.getPledgeType() != L2Clan.SUBUNIT_ACADEMY && getPledgeType() != L2Clan.SUBUNIT_ACADEMY) || (targetPlayer.getKarma() > 0 && Config.KARMA_AWARD_PK_KILL))
4260 {
4261 if (target instanceof L2PcInstance)
4262 {
4263 // Add PvP point to attacker.
4264 setPvpKills(getPvpKills() + 1);
4265
4266 // Send UserInfo packet to attacker with its Karma and PK Counter
4267 sendPacket(new UserInfo(this));
4268 }
4269 }
4270
4271 if (Config.FACTION_SYSTEM_ENABLE)
4272 {
4273 return;
4274 }
4275
4276 // Otherwise, killer is considered as a PKer.
4277 else if (targetPlayer.getKarma() == 0 && targetPlayer.getPvpFlag() == 0)
4278 {
4279 // PK Points are increased only if you kill a player.
4280 if (target instanceof L2PcInstance)
4281 setPkKills(getPkKills() + 1);
4282
4283 // Calculate new karma.
4284 setKarma(getKarma() + Formulas.calculateKarmaGain(getPkKills(), target instanceof L2Summon));
4285
4286 // Send UserInfo packet to attacker with its Karma and PK Counter
4287 sendPacket(new UserInfo(this));
4288 }
4289 }
4290
4291 public void updatePvPStatus()
4292 {
4293 if (!issfaction() || !isffaction())
4294 {
4295 return;
4296 }
4297
4298 if (isInsideZone(ZoneId.PVP))
4299 return;
4300
4301 PvpFlagTaskManager.getInstance().add(this, Config.PVP_NORMAL_TIME);
4302
4303 if (getPvpFlag() == 0)
4304 updatePvPFlag(1);
4305 }
4306
4307 public void updatePvPStatus(L2Character target)
4308 {
4309 final L2PcInstance player = target.getActingPlayer();
4310 if (player == null)
4311 return;
4312
4313 if (isInDuel() && player.getDuelId() == getDuelId())
4314 return;
4315
4316 if (!issfaction() || !isffaction())
4317 {
4318 return;
4319 }
4320
4321 if ((!isInsideZone(ZoneId.PVP) || !target.isInsideZone(ZoneId.PVP)) && player.getKarma() == 0)
4322 {
4323 PvpFlagTaskManager.getInstance().add(this, checkIfPvP(player) ? Config.PVP_PVP_TIME : Config.PVP_NORMAL_TIME);
4324
4325 if (getPvpFlag() == 0)
4326 updatePvPFlag(1);
4327 }
4328 }
4329
4330 /**
4331 * Restore the experience this L2PcInstance has lost and sends StatusUpdate packet.
4332 * @param restorePercent The specified % of restored experience.
4333 */
4334 public void restoreExp(double restorePercent)
4335 {
4336 if (getExpBeforeDeath() > 0)
4337 {
4338 getStat().addExp((int) Math.round((getExpBeforeDeath() - getExp()) * restorePercent / 100));
4339 setExpBeforeDeath(0);
4340 }
4341 }
4342
4343 /**
4344 * Reduce the Experience (and level if necessary) of the L2PcInstance in function of the calculated Death Penalty.
4345 * <ul>
4346 * <li>Calculate the Experience loss</li>
4347 * <li>Set the value of _expBeforeDeath</li>
4348 * <li>Set the new Experience value of the L2PcInstance and Decrease its level if necessary</li>
4349 * <li>Send StatusUpdate packet with its new Experience</li>
4350 * </ul>
4351 * @param atWar If true, use clan war penalty system instead of regular system.
4352 * @param killedByPlayable Used to see if victim loses XP or not.
4353 * @param killedBySiegeNpc Used to see if victim loses XP or not.
4354 */
4355 public void deathPenalty(boolean atWar, boolean killedByPlayable, boolean killedBySiegeNpc)
4356 {
4357 // No xp loss inside pvp zone unless
4358 // - it's a siege zone and you're NOT participating
4359 // - you're killed by a non-pc whose not belong to the siege
4360 if (isInsideZone(ZoneId.PVP))
4361 {
4362 // No xp loss for siege participants inside siege zone.
4363 if (isInsideZone(ZoneId.SIEGE))
4364 {
4365 if (isInSiege() && (killedByPlayable || killedBySiegeNpc))
4366 return;
4367 }
4368 // No xp loss for arenas participants killed by playable.
4369 else if (killedByPlayable)
4370 return;
4371 }
4372
4373 // Get the level of the L2PcInstance
4374 final int lvl = getLevel();
4375
4376 // The death steal you some Exp
4377 double percentLost = 7.0;
4378 if (getLevel() >= 76)
4379 percentLost = 2.0;
4380 else if (getLevel() >= 40)
4381 percentLost = 4.0;
4382
4383 if (getKarma() > 0)
4384 percentLost *= Config.RATE_KARMA_EXP_LOST;
4385
4386 if (isFestivalParticipant() || atWar || isInsideZone(ZoneId.SIEGE))
4387 percentLost /= 4.0;
4388
4389 // Calculate the Experience loss
4390 long lostExp = 0;
4391
4392 if (lvl < Experience.MAX_LEVEL)
4393 lostExp = Math.round((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost / 100);
4394 else
4395 lostExp = Math.round((getStat().getExpForLevel(Experience.MAX_LEVEL) - getStat().getExpForLevel(Experience.MAX_LEVEL - 1)) * percentLost / 100);
4396
4397 // Get the Experience before applying penalty
4398 setExpBeforeDeath(getExp());
4399
4400 // Set new karma
4401 updateKarmaLoss(lostExp);
4402
4403 // Set the new Experience value of the L2PcInstance
4404 getStat().addExp(-lostExp);
4405 }
4406
4407 public boolean isPartyWaiting()
4408 {
4409 return PartyMatchWaitingList.getInstance().getPlayers().contains(this);
4410 }
4411
4412 public void setPartyRoom(int id)
4413 {
4414 _partyroom = id;
4415 }
4416
4417 public int getPartyRoom()
4418 {
4419 return _partyroom;
4420 }
4421
4422 public boolean isInPartyMatchRoom()
4423 {
4424 return _partyroom > 0;
4425 }
4426
4427 /**
4428 * Stop all timers related to that L2PcInstance.
4429 */
4430 public void stopAllTimers()
4431 {
4432 stopHpMpRegeneration();
4433 stopWaterTask();
4434 stopFeed();
4435 clearPetData();
4436 storePetFood(_mountNpcId);
4437 stopPunishTask(true);
4438 stopChargeTask();
4439
4440 AttackStanceTaskManager.getInstance().remove(this);
4441 PvpFlagTaskManager.getInstance().remove(this);
4442 GameTimeTaskManager.getInstance().remove(this);
4443 ShadowItemTaskManager.getInstance().remove(this);
4444 }
4445
4446 /**
4447 * Return the L2Summon of the L2PcInstance or null.
4448 */
4449 @Override
4450 public L2Summon getPet()
4451 {
4452 return _summon;
4453 }
4454
4455 /**
4456 * @return {@code true} if the player has a pet, {@code false} otherwise
4457 */
4458 public boolean hasPet()
4459 {
4460 return _summon instanceof L2PetInstance;
4461 }
4462
4463 /**
4464 * @return {@code true} if the player has a summon, {@code false} otherwise
4465 */
4466 public boolean hasServitor()
4467 {
4468 return _summon instanceof L2SummonInstance;
4469 }
4470
4471 /**
4472 * Set the L2Summon of the L2PcInstance.
4473 * @param summon The Object.
4474 */
4475 public void setPet(L2Summon summon)
4476 {
4477 _summon = summon;
4478 }
4479
4480 /**
4481 * @return the L2TamedBeast of the L2PcInstance or null.
4482 */
4483 public L2TamedBeastInstance getTrainedBeast()
4484 {
4485 return _tamedBeast;
4486 }
4487
4488 /**
4489 * Set the L2TamedBeast of the L2PcInstance.
4490 * @param tamedBeast The Object.
4491 */
4492 public void setTrainedBeast(L2TamedBeastInstance tamedBeast)
4493 {
4494 _tamedBeast = tamedBeast;
4495 }
4496
4497 /**
4498 * @return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
4499 */
4500 public L2Request getRequest()
4501 {
4502 return _request;
4503 }
4504
4505 /**
4506 * Set the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
4507 * @param requester
4508 */
4509 public void setActiveRequester(L2PcInstance requester)
4510 {
4511 _activeRequester = requester;
4512 }
4513
4514 /**
4515 * @return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
4516 */
4517 public L2PcInstance getActiveRequester()
4518 {
4519 if (_activeRequester != null && _activeRequester.isRequestExpired() && _activeTradeList == null)
4520 _activeRequester = null;
4521
4522 return _activeRequester;
4523 }
4524
4525 /**
4526 * @return True if a request is in progress.
4527 */
4528 public boolean isProcessingRequest()
4529 {
4530 return getActiveRequester() != null || _requestExpireTime > System.currentTimeMillis();
4531 }
4532
4533 /**
4534 * @return True if a transaction <B>(trade OR request)</B> is in progress.
4535 */
4536 public boolean isProcessingTransaction()
4537 {
4538 return getActiveRequester() != null || _activeTradeList != null || _requestExpireTime > System.currentTimeMillis();
4539 }
4540
4541 /**
4542 * Set the _requestExpireTime of that L2PcInstance, and set his partner as the active requester.
4543 * @param partner The partner to make checks on.
4544 */
4545 public void onTransactionRequest(L2PcInstance partner)
4546 {
4547 _requestExpireTime = System.currentTimeMillis() + REQUEST_TIMEOUT * 1000;
4548 partner.setActiveRequester(this);
4549 }
4550
4551 /**
4552 * @return true if last request is expired.
4553 */
4554 public boolean isRequestExpired()
4555 {
4556 return _requestExpireTime <= System.currentTimeMillis();
4557 }
4558
4559 /**
4560 * Select the Warehouse to be used in next activity.
4561 */
4562 public void onTransactionResponse()
4563 {
4564 _requestExpireTime = 0;
4565 }
4566
4567 /**
4568 * Select the Warehouse to be used in next activity.
4569 * @param warehouse An active warehouse.
4570 */
4571 public void setActiveWarehouse(ItemContainer warehouse)
4572 {
4573 _activeWarehouse = warehouse;
4574 }
4575
4576 /**
4577 * @return The active Warehouse.
4578 */
4579 public ItemContainer getActiveWarehouse()
4580 {
4581 return _activeWarehouse;
4582 }
4583
4584 /**
4585 * Set the TradeList to be used in next activity.
4586 * @param tradeList The TradeList to be used.
4587 */
4588 public void setActiveTradeList(TradeList tradeList)
4589 {
4590 _activeTradeList = tradeList;
4591 }
4592
4593 /**
4594 * @return The active TradeList.
4595 */
4596 public TradeList getActiveTradeList()
4597 {
4598 return _activeTradeList;
4599 }
4600
4601 public void onTradeStart(L2PcInstance partner)
4602 {
4603 _activeTradeList = new TradeList(this);
4604 _activeTradeList.setPartner(partner);
4605
4606 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.BEGIN_TRADE_WITH_S1).addString(partner.getName()));
4607 sendPacket(new TradeStart(this));
4608 }
4609
4610 public void onTradeConfirm(L2PcInstance partner)
4611 {
4612 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_CONFIRMED_TRADE).addString(partner.getName()));
4613
4614 partner.sendPacket(TradePressOwnOk.STATIC_PACKET);
4615 sendPacket(TradePressOtherOk.STATIC_PACKET);
4616 }
4617
4618 public void onTradeCancel(L2PcInstance partner)
4619 {
4620 if (_activeTradeList == null)
4621 return;
4622
4623 _activeTradeList.lock();
4624 _activeTradeList = null;
4625
4626 sendPacket(new SendTradeDone(0));
4627 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_CANCELED_TRADE).addString(partner.getName()));
4628 }
4629
4630 public void onTradeFinish(boolean successfull)
4631 {
4632 _activeTradeList = null;
4633 sendPacket(new SendTradeDone(1));
4634 if (successfull)
4635 sendPacket(SystemMessageId.TRADE_SUCCESSFUL);
4636 }
4637
4638 public void startTrade(L2PcInstance partner)
4639 {
4640 onTradeStart(partner);
4641 partner.onTradeStart(this);
4642 }
4643
4644 public void cancelActiveTrade()
4645 {
4646 if (_activeTradeList == null)
4647 return;
4648
4649 L2PcInstance partner = _activeTradeList.getPartner();
4650 if (partner != null)
4651 partner.onTradeCancel(this);
4652
4653 onTradeCancel(this);
4654 }
4655
4656 /**
4657 * @return The _createList object of the L2PcInstance.
4658 */
4659 public L2ManufactureList getCreateList()
4660 {
4661 return _createList;
4662 }
4663
4664 /**
4665 * Set the _createList object of the L2PcInstance.
4666 * @param list
4667 */
4668 public void setCreateList(L2ManufactureList list)
4669 {
4670 _createList = list;
4671 }
4672
4673 /**
4674 * @return The _sellList object of the L2PcInstance.
4675 */
4676 public TradeList getSellList()
4677 {
4678 if (_sellList == null)
4679 _sellList = new TradeList(this);
4680
4681 return _sellList;
4682 }
4683
4684 /**
4685 * @return the _buyList object of the L2PcInstance.
4686 */
4687 public TradeList getBuyList()
4688 {
4689 if (_buyList == null)
4690 _buyList = new TradeList(this);
4691
4692 return _buyList;
4693 }
4694
4695 /**
4696 * Set the Private Store type of the L2PcInstance.
4697 * @param type The value : 0 = none, 1 = sell, 2 = sellmanage, 3 = buy, 4 = buymanage, 5 = manufacture.
4698 */
4699 public void setPrivateStoreType(PrivateStoreType type)
4700 {
4701 _privateStoreType = type;
4702 }
4703
4704 /**
4705 * @return The Private Store type of the L2PcInstance.
4706 */
4707 public PrivateStoreType getPrivateStoreType()
4708 {
4709 return _privateStoreType;
4710 }
4711
4712 /**
4713 * Set the _skillLearningClassId object of the L2PcInstance.
4714 * @param classId The parameter.
4715 */
4716 public void setSkillLearningClassId(ClassId classId)
4717 {
4718 _skillLearningClassId = classId;
4719 }
4720
4721 /**
4722 * @return The _skillLearningClassId object of the L2PcInstance.
4723 */
4724 public ClassId getSkillLearningClassId()
4725 {
4726 return _skillLearningClassId;
4727 }
4728
4729 /**
4730 * Set the _clan object, _clanId, _clanLeader Flag and title of the L2PcInstance.
4731 * @param clan The Clan object which is used to feed L2PcInstance values.
4732 */
4733 public void setClan(L2Clan clan)
4734 {
4735 _clan = clan;
4736 setTitle("");
4737
4738 if (clan == null)
4739 {
4740 _clanId = 0;
4741 _clanPrivileges = 0;
4742 _pledgeType = 0;
4743 _powerGrade = 0;
4744 _lvlJoinedAcademy = 0;
4745 _apprentice = 0;
4746 _sponsor = 0;
4747 return;
4748 }
4749
4750 if (!clan.isMember(getObjectId()))
4751 {
4752 // char has been kicked from clan
4753 setClan(null);
4754 return;
4755 }
4756
4757 _clanId = clan.getClanId();
4758 }
4759
4760 /**
4761 * @return The _clan object of the L2PcInstance.
4762 */
4763 public L2Clan getClan()
4764 {
4765 return _clan;
4766 }
4767
4768 /**
4769 * @return True if the L2PcInstance is the leader of its clan.
4770 */
4771 public boolean isClanLeader()
4772 {
4773 if (getClan() == null)
4774 return false;
4775
4776 return getObjectId() == getClan().getLeaderId();
4777 }
4778
4779 /**
4780 * Reduce the number of arrows owned by the L2PcInstance and send InventoryUpdate or ItemList (to unequip if the last arrow was consummed).
4781 */
4782 @Override
4783 protected void reduceArrowCount()
4784 {
4785 ItemInstance arrows = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
4786
4787 if (arrows == null)
4788 {
4789 getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
4790 _arrowItem = null;
4791 sendPacket(new ItemList(this, false));
4792 return;
4793 }
4794
4795 // Adjust item quantity
4796 if (arrows.getCount() > 1)
4797 {
4798 synchronized (arrows)
4799 {
4800 arrows.changeCountWithoutTrace(-1, this, null);
4801 arrows.setLastChange(ItemInstance.MODIFIED);
4802
4803 // could do also without saving, but let's save approx 1 of 10
4804 if (Rnd.get(10) < 1)
4805 arrows.updateDatabase();
4806 _inventory.refreshWeight();
4807 }
4808 }
4809 else
4810 {
4811 // Destroy entire item and save to database
4812 _inventory.destroyItem("Consume", arrows, this, null);
4813
4814 getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND);
4815 _arrowItem = null;
4816
4817 sendPacket(new ItemList(this, false));
4818 return;
4819 }
4820
4821 InventoryUpdate iu = new InventoryUpdate();
4822 iu.addModifiedItem(arrows);
4823 sendPacket(iu);
4824 }
4825
4826 /**
4827 * Equip arrows needed in left hand and send ItemList to the L2PcInstance then return True.
4828 */
4829 @Override
4830 protected boolean checkAndEquipArrows()
4831 {
4832 // Check if nothing is equipped in left hand
4833 if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null)
4834 {
4835 // Get the ItemInstance of the arrows needed for this bow
4836 _arrowItem = getInventory().findArrowForBow(getActiveWeaponItem());
4837
4838 if (_arrowItem != null)
4839 {
4840 // Equip arrows needed in left hand
4841 getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _arrowItem);
4842
4843 // Send ItemList to this L2PcINstance to update left hand equipement
4844 sendPacket(new ItemList(this, false));
4845 }
4846 }
4847 // Get the ItemInstance of arrows equipped in left hand
4848 else
4849 _arrowItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
4850
4851 return _arrowItem != null;
4852 }
4853
4854 /**
4855 * Disarm the player's weapon and shield.
4856 * @return true if successful, false otherwise.
4857 */
4858 public boolean disarmWeapons()
4859 {
4860 // Don't allow disarming a cursed weapon
4861 if (isCursedWeaponEquipped())
4862 return false;
4863
4864 // Unequip the weapon
4865 ItemInstance wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
4866 if (wpn != null)
4867 {
4868 ItemInstance[] unequipped = getInventory().unEquipItemInBodySlotAndRecord(wpn.getItem().getBodyPart());
4869 InventoryUpdate iu = new InventoryUpdate();
4870 for (ItemInstance itm : unequipped)
4871 iu.addModifiedItem(itm);
4872 sendPacket(iu);
4873
4874 abortAttack();
4875 broadcastUserInfo();
4876
4877 // this can be 0 if the user pressed the right mousebutton twice very fast
4878 if (unequipped.length > 0)
4879 {
4880 SystemMessage sm;
4881 if (unequipped[0].getEnchantLevel() > 0)
4882 sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED).addNumber(unequipped[0].getEnchantLevel()).addItemName(unequipped[0]);
4883 else
4884 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED).addItemName(unequipped[0]);
4885
4886 sendPacket(sm);
4887 }
4888 }
4889
4890 // Unequip the shield
4891 ItemInstance sld = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
4892 if (sld != null)
4893 {
4894 ItemInstance[] unequipped = getInventory().unEquipItemInBodySlotAndRecord(sld.getItem().getBodyPart());
4895 InventoryUpdate iu = new InventoryUpdate();
4896 for (ItemInstance itm : unequipped)
4897 iu.addModifiedItem(itm);
4898 sendPacket(iu);
4899
4900 abortAttack();
4901 broadcastUserInfo();
4902
4903 // this can be 0 if the user pressed the right mousebutton twice very fast
4904 if (unequipped.length > 0)
4905 {
4906 SystemMessage sm;
4907 if (unequipped[0].getEnchantLevel() > 0)
4908 sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED).addNumber(unequipped[0].getEnchantLevel()).addItemName(unequipped[0]);
4909 else
4910 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED).addItemName(unequipped[0]);
4911
4912 sendPacket(sm);
4913 }
4914 }
4915 return true;
4916 }
4917
4918 public boolean mount(L2Summon pet)
4919 {
4920 if (!disarmWeapons())
4921 return false;
4922
4923 stopAllToggles();
4924 Ride mount = new Ride(getObjectId(), Ride.ACTION_MOUNT, pet.getTemplate().getNpcId());
4925 setMount(pet.getNpcId(), pet.getLevel(), mount.getMountType());
4926 setMountObjectID(pet.getControlItemId());
4927 clearPetData();
4928 startFeed(pet.getNpcId());
4929 broadcastPacket(mount);
4930
4931 // Notify self and others about speed change
4932 broadcastUserInfo();
4933
4934 pet.unSummon(this);
4935 return true;
4936 }
4937
4938 public boolean mount(int npcId, int controlItemId, boolean useFood)
4939 {
4940 if (!disarmWeapons())
4941 return false;
4942
4943 stopAllToggles();
4944 Ride mount = new Ride(getObjectId(), Ride.ACTION_MOUNT, npcId);
4945 if (setMount(npcId, getLevel(), mount.getMountType()))
4946 {
4947 clearPetData();
4948 setMountObjectID(controlItemId);
4949 broadcastPacket(mount);
4950
4951 // Notify self and others about speed change
4952 broadcastUserInfo();
4953
4954 if (useFood)
4955 startFeed(npcId);
4956
4957 return true;
4958 }
4959 return false;
4960 }
4961
4962 public boolean mountPlayer(L2Summon summon)
4963 {
4964 if (summon != null && summon.isMountable() && !isMounted() && !isBetrayed())
4965 {
4966 if (isDead()) // A strider cannot be ridden when dead.
4967 {
4968 sendPacket(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_DEAD);
4969 return false;
4970 }
4971
4972 if (summon.isDead()) // A dead strider cannot be ridden.
4973 {
4974 sendPacket(SystemMessageId.DEAD_STRIDER_CANT_BE_RIDDEN);
4975 return false;
4976 }
4977
4978 if (summon.isInCombat() || summon.isRooted()) // A strider in battle cannot be ridden.
4979 {
4980 sendPacket(SystemMessageId.STRIDER_IN_BATLLE_CANT_BE_RIDDEN);
4981 return false;
4982 }
4983
4984 if (isInCombat()) // A strider cannot be ridden while in battle
4985 {
4986 sendPacket(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_IN_BATTLE);
4987 return false;
4988 }
4989
4990 if (isSitting()) // A strider can be ridden only when standing
4991 {
4992 sendPacket(SystemMessageId.STRIDER_CAN_BE_RIDDEN_ONLY_WHILE_STANDING);
4993 return false;
4994 }
4995
4996 if (isFishing()) // You can't mount, dismount, break and drop items while fishing
4997 {
4998 sendPacket(SystemMessageId.CANNOT_DO_WHILE_FISHING_2);
4999 return false;
5000 }
5001
5002 if (isCursedWeaponEquipped()) // You can't mount, dismount, break and drop items while weilding a cursed weapon
5003 {
5004 sendPacket(SystemMessageId.STRIDER_CANT_BE_RIDDEN_WHILE_IN_BATTLE);
5005 return false;
5006 }
5007
5008 if (!Util.checkIfInRange(200, this, summon, true))
5009 {
5010 sendPacket(SystemMessageId.TOO_FAR_AWAY_FROM_STRIDER_TO_MOUNT);
5011 return false;
5012 }
5013
5014 if (summon.isHungry())
5015 {
5016 sendPacket(SystemMessageId.HUNGRY_STRIDER_NOT_MOUNT);
5017 return false;
5018 }
5019
5020 if (!summon.isDead() && !isMounted())
5021 mount(summon);
5022 }
5023 else if (isMounted())
5024 {
5025 if (getMountType() == 2 && isInsideZone(ZoneId.NO_LANDING))
5026 {
5027 sendPacket(SystemMessageId.NO_DISMOUNT_HERE);
5028 return false;
5029 }
5030
5031 if (isHungry())
5032 {
5033 sendPacket(SystemMessageId.HUNGRY_STRIDER_NOT_MOUNT);
5034 return false;
5035 }
5036
5037 dismount();
5038 }
5039 return true;
5040 }
5041
5042 public boolean dismount()
5043 {
5044 sendPacket(new SetupGauge(3, 0, 0));
5045 int petId = _mountNpcId;
5046 if (setMount(0, 0, 0))
5047 {
5048 stopFeed();
5049 clearPetData();
5050
5051 broadcastPacket(new Ride(getObjectId(), Ride.ACTION_DISMOUNT, 0));
5052
5053 setMountObjectID(0);
5054 storePetFood(petId);
5055
5056 // Notify self and others about speed change
5057 broadcastUserInfo();
5058 return true;
5059 }
5060 return false;
5061 }
5062
5063 public void storePetFood(int petId)
5064 {
5065 if (_controlItemId != 0 && petId != 0)
5066 {
5067 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5068 {
5069 PreparedStatement statement = con.prepareStatement("UPDATE pets SET fed=? WHERE item_obj_id = ?");
5070 statement.setInt(1, getCurrentFeed());
5071 statement.setInt(2, _controlItemId);
5072 statement.executeUpdate();
5073 statement.close();
5074 _controlItemId = 0;
5075 }
5076 catch (Exception e)
5077 {
5078 _log.log(Level.SEVERE, "Failed to store Pet [NpcId: " + petId + "] data", e);
5079 }
5080 }
5081 }
5082
5083 protected class FeedTask implements Runnable
5084 {
5085 @Override
5086 public void run()
5087 {
5088 try
5089 {
5090 if (!isMounted())
5091 {
5092 stopFeed();
5093 return;
5094 }
5095
5096 if (getCurrentFeed() > getFeedConsume())
5097 {
5098 // eat
5099 setCurrentFeed(getCurrentFeed() - getFeedConsume());
5100 }
5101 else
5102 {
5103 // go back to pet control item, or simply said, unsummon it
5104 setCurrentFeed(0);
5105 stopFeed();
5106 dismount();
5107 sendPacket(SystemMessageId.OUT_OF_FEED_MOUNT_CANCELED);
5108 }
5109
5110 int[] foodIds = getPetData(getMountNpcId()).getFood();
5111 if (foodIds.length == 0)
5112 return;
5113
5114 ItemInstance food = null;
5115 for (int id : foodIds)
5116 {
5117 food = getInventory().getItemByItemId(id);
5118 if (food != null)
5119 break;
5120 }
5121
5122 if (food != null && isHungry())
5123 {
5124 IItemHandler handler = ItemHandler.getInstance().getItemHandler(food.getEtcItem());
5125 if (handler != null)
5126 {
5127 handler.useItem(L2PcInstance.this, food, false);
5128 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.PET_TOOK_S1_BECAUSE_HE_WAS_HUNGRY).addItemName(food));
5129 }
5130 }
5131 }
5132 catch (Exception e)
5133 {
5134 _log.log(Level.SEVERE, "Mounted Pet [NpcId: " + getMountNpcId() + "] a feed task error has occurred", e);
5135 }
5136 }
5137 }
5138
5139 protected synchronized void startFeed(int npcId)
5140 {
5141 _canFeed = npcId > 0;
5142 if (!isMounted())
5143 return;
5144
5145 if (getPet() != null)
5146 {
5147 setCurrentFeed(((L2PetInstance) getPet()).getCurrentFed());
5148 _controlItemId = getPet().getControlItemId();
5149 sendPacket(new SetupGauge(3, getCurrentFeed() * 10000 / getFeedConsume(), getMaxFeed() * 10000 / getFeedConsume()));
5150 if (!isDead())
5151 _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000, 10000);
5152 }
5153 else if (_canFeed)
5154 {
5155 setCurrentFeed(getMaxFeed());
5156 sendPacket(new SetupGauge(3, getCurrentFeed() * 10000 / getFeedConsume(), getMaxFeed() * 10000 / getFeedConsume()));
5157 if (!isDead())
5158 _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FeedTask(), 10000, 10000);
5159 }
5160 }
5161
5162 protected synchronized void stopFeed()
5163 {
5164 if (_mountFeedTask != null)
5165 {
5166 _mountFeedTask.cancel(false);
5167 _mountFeedTask = null;
5168 }
5169 }
5170
5171 private final void clearPetData()
5172 {
5173 _data = null;
5174 }
5175
5176 protected final L2PetData getPetData(int npcId)
5177 {
5178 if (_data == null)
5179 _data = PetDataTable.getInstance().getPetData(npcId);
5180
5181 return _data;
5182 }
5183
5184 private final L2PetLevelData getPetLevelData(int npcId)
5185 {
5186 if (_leveldata == null)
5187 _leveldata = PetDataTable.getInstance().getPetData(npcId).getPetLevelData(getMountLevel());
5188
5189 return _leveldata;
5190 }
5191
5192 public int getCurrentFeed()
5193 {
5194 return _curFeed;
5195 }
5196
5197 protected int getFeedConsume()
5198 {
5199 return (isAttackingNow()) ? getPetLevelData(_mountNpcId).getPetFeedBattle() : getPetLevelData(_mountNpcId).getPetFeedNormal();
5200 }
5201
5202 public void setCurrentFeed(int num)
5203 {
5204 _curFeed = (num > getMaxFeed()) ? getMaxFeed() : num;
5205 sendPacket(new SetupGauge(3, getCurrentFeed() * 10000 / getFeedConsume(), getMaxFeed() * 10000 / getFeedConsume()));
5206 }
5207
5208 private int getMaxFeed()
5209 {
5210 return getPetLevelData(_mountNpcId).getPetMaxFeed();
5211 }
5212
5213 protected boolean isHungry()
5214 {
5215 return _canFeed ? (getCurrentFeed() < (getPetLevelData(getMountNpcId()).getPetMaxFeed() * 0.55)) : false;
5216 }
5217
5218 /**
5219 * @return the type of attack, depending of the worn weapon.
5220 */
5221 @Override
5222 public WeaponType getAttackType()
5223 {
5224 final Weapon weapon = getActiveWeaponItem();
5225 if (weapon != null)
5226 return weapon.getItemType();
5227
5228 return WeaponType.FIST;
5229 }
5230
5231 public void setUptime(long time)
5232 {
5233 _uptime = time;
5234 }
5235
5236 public long getUptime()
5237 {
5238 return System.currentTimeMillis() - _uptime;
5239 }
5240
5241 /**
5242 * Return True if the L2PcInstance is invulnerable.
5243 */
5244 @Override
5245 public boolean isInvul()
5246 {
5247 return super.isInvul() || isSpawnProtected();
5248 }
5249
5250 /**
5251 * Return True if the L2PcInstance has a Party in progress.
5252 */
5253 @Override
5254 public boolean isInParty()
5255 {
5256 return _party != null;
5257 }
5258
5259 /**
5260 * Set the _party object of the L2PcInstance (without joining it).
5261 * @param party The object.
5262 */
5263 public void setParty(L2Party party)
5264 {
5265 _party = party;
5266 }
5267
5268 /**
5269 * Set the _party object of the L2PcInstance AND join it.
5270 * @param party
5271 */
5272 public void joinParty(L2Party party)
5273 {
5274 if (party != null)
5275 {
5276 _party = party;
5277 party.addPartyMember(this);
5278 }
5279 }
5280
5281 /**
5282 * Manage the Leave Party task of the L2PcInstance.
5283 */
5284 public void leaveParty()
5285 {
5286 if (isInParty())
5287 {
5288 _party.removePartyMember(this, MessageType.Disconnected);
5289 _party = null;
5290 }
5291 }
5292
5293 /**
5294 * Return the _party object of the L2PcInstance.
5295 */
5296 @Override
5297 public L2Party getParty()
5298 {
5299 return _party;
5300 }
5301
5302 /**
5303 * Return True if the L2PcInstance is a GM.
5304 */
5305 @Override
5306 public boolean isGM()
5307 {
5308 return getAccessLevel().isGm();
5309 }
5310
5311 /**
5312 * Set the _accessLevel of the L2PcInstance.
5313 * @param level
5314 */
5315 public void setAccessLevel(int level)
5316 {
5317 if (level == AccessLevels.MASTER_ACCESS_LEVEL_NUMBER)
5318 {
5319 _log.warning(getName() + " has logged in with Master access level.");
5320 _accessLevel = AccessLevels.MASTER_ACCESS_LEVEL;
5321 }
5322 else if (level == AccessLevels.USER_ACCESS_LEVEL_NUMBER)
5323 _accessLevel = AccessLevels.USER_ACCESS_LEVEL;
5324 else
5325 {
5326 L2AccessLevel accessLevel = AccessLevels.getInstance().getAccessLevel(level);
5327
5328 if (accessLevel == null)
5329 {
5330 if (level < 0)
5331 {
5332 AccessLevels.getInstance().addBanAccessLevel(level);
5333 _accessLevel = AccessLevels.getInstance().getAccessLevel(level);
5334 }
5335 else
5336 {
5337 _log.warning("Server tried to set unregistered access level " + level + " to " + getName() + ". His access level have been reseted to user level.");
5338 _accessLevel = AccessLevels.USER_ACCESS_LEVEL;
5339 }
5340 }
5341 else
5342 {
5343 _accessLevel = accessLevel;
5344 setTitle(_accessLevel.getName());
5345 }
5346 }
5347
5348 getAppearance().setNameColor(_accessLevel.getNameColor());
5349 getAppearance().setTitleColor(_accessLevel.getTitleColor());
5350 broadcastUserInfo();
5351
5352 CharNameTable.getInstance().addName(this);
5353 }
5354
5355 public void setAccountAccesslevel(int level)
5356 {
5357 LoginServerThread.getInstance().sendAccessLevel(getAccountName(), level);
5358 }
5359
5360 /**
5361 * @return the _accessLevel of the L2PcInstance.
5362 */
5363 public L2AccessLevel getAccessLevel()
5364 {
5365 if (Config.EVERYBODY_HAS_ADMIN_RIGHTS)
5366 return AccessLevels.MASTER_ACCESS_LEVEL;
5367
5368 if (_accessLevel == null) /* This is here because inventory etc. is loaded before access level on login, so it is not null */
5369 setAccessLevel(AccessLevels.USER_ACCESS_LEVEL_NUMBER);
5370
5371 return _accessLevel;
5372 }
5373
5374 /**
5375 * Update Stats of the L2PcInstance client side by sending UserInfo/StatusUpdate to this L2PcInstance and CharInfo/StatusUpdate to all L2PcInstance in its _KnownPlayers (broadcast).
5376 * @param broadcastType
5377 */
5378 public void updateAndBroadcastStatus(int broadcastType)
5379 {
5380 refreshOverloaded();
5381 refreshExpertisePenalty();
5382
5383 if (broadcastType == 1)
5384 sendPacket(new UserInfo(this));
5385 else if (broadcastType == 2)
5386 broadcastUserInfo();
5387 }
5388
5389 /**
5390 * Send StatusUpdate packet with Karma to the L2PcInstance and all L2PcInstance to inform (broadcast).
5391 */
5392 public void broadcastKarma()
5393 {
5394 StatusUpdate su = new StatusUpdate(this);
5395 su.addAttribute(StatusUpdate.KARMA, getKarma());
5396 sendPacket(su);
5397
5398 if (getPet() != null)
5399 sendPacket(new RelationChanged(getPet(), getRelation(this), false));
5400
5401 broadcastRelationsChanges();
5402 }
5403
5404 /**
5405 * 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).
5406 * @param isOnline
5407 * @param updateInDb
5408 */
5409 public void setOnlineStatus(boolean isOnline, boolean updateInDb)
5410 {
5411 if (_isOnline != isOnline)
5412 _isOnline = isOnline;
5413
5414 // Update the characters table of the database with online status and lastAccess (called when login and logout)
5415 if (updateInDb)
5416 updateOnlineStatus();
5417 }
5418
5419 public void setIsIn7sDungeon(boolean isIn7sDungeon)
5420 {
5421 _isIn7sDungeon = isIn7sDungeon;
5422 }
5423
5424 /**
5425 * Update the characters table of the database with online status and lastAccess of this L2PcInstance (called when login and logout).
5426 */
5427 public void updateOnlineStatus()
5428 {
5429 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5430 {
5431 PreparedStatement statement = con.prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE obj_id=?");
5432 statement.setInt(1, isOnlineInt());
5433 statement.setLong(2, System.currentTimeMillis());
5434 statement.setInt(3, getObjectId());
5435 statement.execute();
5436 statement.close();
5437 }
5438 catch (Exception e)
5439 {
5440 _log.warning("could not set char online status:" + e);
5441 }
5442 }
5443
5444 /**
5445 * Create a new player in the characters table of the database.
5446 * @return true if successful.
5447 */
5448 private boolean createDb()
5449 {
5450 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5451 {
5452 PreparedStatement statement = con.prepareStatement(INSERT_CHARACTER);
5453 statement.setString(1, _accountName);
5454 statement.setInt(2, getObjectId());
5455 statement.setString(3, getName());
5456 statement.setInt(4, getLevel());
5457 statement.setInt(5, getMaxHp());
5458 statement.setDouble(6, getCurrentHp());
5459 statement.setInt(7, getMaxCp());
5460 statement.setDouble(8, getCurrentCp());
5461 statement.setInt(9, getMaxMp());
5462 statement.setDouble(10, getCurrentMp());
5463 statement.setInt(11, getAppearance().getFace());
5464 statement.setInt(12, getAppearance().getHairStyle());
5465 statement.setInt(13, getAppearance().getHairColor());
5466 statement.setInt(14, getAppearance().getSex() ? 1 : 0);
5467 statement.setLong(15, getExp());
5468 statement.setInt(16, getSp());
5469 statement.setInt(17, getKarma());
5470 statement.setInt(18, getPvpKills());
5471 statement.setInt(19, getPkKills());
5472 statement.setInt(20, getClanId());
5473 statement.setInt(21, getRace().ordinal());
5474 statement.setInt(22, getClassId().getId());
5475 statement.setLong(23, getDeleteTimer());
5476 statement.setInt(24, hasDwarvenCraft() ? 1 : 0);
5477 statement.setString(25, getTitle());
5478 statement.setInt(26, getAccessLevel().getLevel());
5479 statement.setInt(27, isOnlineInt());
5480 statement.setInt(28, isIn7sDungeon() ? 1 : 0);
5481 statement.setInt(29, getClanPrivileges());
5482 statement.setInt(30, wantsPeace() ? 1 : 0);
5483 statement.setInt(31, getBaseClass());
5484 statement.setInt(32, isNoble() ? 1 : 0);
5485 statement.setLong(33, 0);
5486 statement.setLong(34, System.currentTimeMillis());
5487 statement.executeUpdate();
5488 statement.close();
5489 }
5490 catch (Exception e)
5491 {
5492 _log.severe("Could not insert char data: " + e);
5493 return false;
5494 }
5495 return true;
5496 }
5497
5498 /**
5499 * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world.
5500 * <ul>
5501 * <li>Retrieve the L2PcInstance from the characters table of the database</li>
5502 * <li>Add the L2PcInstance object in _allObjects</li>
5503 * <li>Set the x,y,z position of the L2PcInstance and make it invisible</li>
5504 * <li>Update the overloaded status of the L2PcInstance</li>
5505 * </ul>
5506 * @param objectId Identifier of the object to initialized
5507 * @return The L2PcInstance loaded from the database
5508 */
5509 public static L2PcInstance restore(int objectId)
5510 {
5511 L2PcInstance player = null;
5512 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5513 {
5514 PreparedStatement statement = con.prepareStatement(RESTORE_CHARACTER);
5515 statement.setInt(1, objectId);
5516 ResultSet rset = statement.executeQuery();
5517
5518 while (rset.next())
5519 {
5520 final int activeClassId = rset.getInt("classid");
5521 final PcTemplate template = CharTemplateTable.getInstance().getTemplate(activeClassId);
5522 final PcAppearance app = new PcAppearance(rset.getByte("face"), rset.getByte("hairColor"), rset.getByte("hairStyle"), rset.getInt("sex") != 0);
5523
5524 player = new L2PcInstance(objectId, template, rset.getString("account_name"), app);
5525 player.setName(rset.getString("char_name"));
5526 player._lastAccess = rset.getLong("lastAccess");
5527
5528 player.getStat().setExp(rset.getLong("exp"));
5529 player.setExpBeforeDeath(rset.getLong("expBeforeDeath"));
5530 player.getStat().setLevel(rset.getByte("level"));
5531 player.getStat().setSp(rset.getInt("sp"));
5532
5533 player.setWantsPeace(rset.getInt("wantspeace") == 1);
5534
5535 player.setHeading(rset.getInt("heading"));
5536
5537 player.setKarma(rset.getInt("karma"));
5538 player.setPvpKills(rset.getInt("pvpkills"));
5539 player.setPkKills(rset.getInt("pkkills"));
5540 player.setOnlineTime(rset.getLong("onlinetime"));
5541 player.setNoble(rset.getInt("nobless") == 1, false);
5542 player.setffaction(rset.getInt("ffaction") == 1 ? true : false);
5543 player.setsfaction(rset.getInt("sfaction") == 1 ? true : false);
5544
5545 player.setClanJoinExpiryTime(rset.getLong("clan_join_expiry_time"));
5546 if (player.getClanJoinExpiryTime() < System.currentTimeMillis())
5547 player.setClanJoinExpiryTime(0);
5548
5549 player.setClanCreateExpiryTime(rset.getLong("clan_create_expiry_time"));
5550 if (player.getClanCreateExpiryTime() < System.currentTimeMillis())
5551 player.setClanCreateExpiryTime(0);
5552
5553 player.setPowerGrade(rset.getInt("power_grade"));
5554 player.setPledgeType(rset.getInt("subpledge"));
5555 player.setLastRecomUpdate(rset.getLong("last_recom_date"));
5556
5557 int clanId = rset.getInt("clanid");
5558 if (clanId > 0)
5559 player.setClan(ClanTable.getInstance().getClan(clanId));
5560
5561 if (player.getClan() != null)
5562 {
5563 if (player.getClan().getLeaderId() != player.getObjectId())
5564 {
5565 if (player.getPowerGrade() == 0)
5566 player.setPowerGrade(5);
5567
5568 player.setClanPrivileges(player.getClan().getRankPrivs(player.getPowerGrade()));
5569 }
5570 else
5571 {
5572 player.setClanPrivileges(L2Clan.CP_ALL);
5573 player.setPowerGrade(1);
5574 }
5575 }
5576 else
5577 player.setClanPrivileges(L2Clan.CP_NOTHING);
5578
5579 player.setDeleteTimer(rset.getLong("deletetime"));
5580
5581 player.setTitle(rset.getString("title"));
5582 player.setAccessLevel(rset.getInt("accesslevel"));
5583 player.setFistsWeaponItem(findFistsWeaponItem(activeClassId));
5584 player.setUptime(System.currentTimeMillis());
5585
5586 // Check recs
5587 player.checkRecom(rset.getInt("rec_have"), rset.getInt("rec_left"));
5588
5589 player._classIndex = 0;
5590 try
5591 {
5592 player.setBaseClass(rset.getInt("base_class"));
5593 }
5594 catch (Exception e)
5595 {
5596 player.setBaseClass(activeClassId);
5597 }
5598
5599 // Restore Subclass Data (cannot be done earlier in function)
5600 if (restoreSubClassData(player))
5601 {
5602 if (activeClassId != player.getBaseClass())
5603 {
5604 for (SubClass subClass : player.getSubClasses().values())
5605 if (subClass.getClassId() == activeClassId)
5606 player._classIndex = subClass.getClassIndex();
5607 }
5608 }
5609 if (player.getClassIndex() == 0 && activeClassId != player.getBaseClass())
5610 {
5611 // Subclass in use but doesn't exist in DB -
5612 // a possible restart-while-modifysubclass cheat has been attempted.
5613 // Switching to use base class
5614 player.setClassId(player.getBaseClass());
5615 _log.warning("Player " + player.getName() + " reverted to base class. Possibly has tried a relogin exploit while subclassing.");
5616 }
5617 else
5618 player._activeClass = activeClassId;
5619
5620 player.setApprentice(rset.getInt("apprentice"));
5621 player.setSponsor(rset.getInt("sponsor"));
5622 player.setLvlJoinedAcademy(rset.getInt("lvl_joined_academy"));
5623 player.setIsIn7sDungeon(rset.getInt("isin7sdungeon") == 1);
5624 player.setPunishLevel(rset.getInt("punish_level"));
5625 if (player.getPunishLevel() != PunishLevel.NONE)
5626 player.setPunishTimer(rset.getLong("punish_timer"));
5627 else
5628 player.setPunishTimer(0);
5629
5630 CursedWeaponsManager.getInstance().checkPlayer(player);
5631
5632 player.setAllianceWithVarkaKetra(rset.getInt("varka_ketra_ally"));
5633
5634 player.setDeathPenaltyBuffLevel(rset.getInt("death_penalty_level"));
5635
5636 // Set the x,y,z position of the L2PcInstance and make it invisible
5637 player.setXYZInvisible(rset.getInt("x"), rset.getInt("y"), rset.getInt("z"));
5638
5639 // Set Hero status if it applies
5640 if (Hero.getInstance().isActiveHero(objectId))
5641 player.setHero(true);
5642
5643 // Set pledge class rank.
5644 player.setPledgeClass(L2ClanMember.calculatePledgeClass(player));
5645
5646 // Retrieve from the database all secondary data of this L2PcInstance and reward expertise/lucky skills if necessary.
5647 // Note that Clan, Noblesse and Hero skills are given separately and not here.
5648 player.restoreCharData();
5649 player.rewardSkills();
5650
5651 // buff and status icons
5652 if (Config.STORE_SKILL_COOLTIME)
5653 player.restoreEffects();
5654
5655 // Restore current CP, HP and MP values
5656 final double currentHp = rset.getDouble("curHp");
5657
5658 player.setCurrentCp(rset.getDouble("curCp"));
5659 player.setCurrentHp(currentHp);
5660 player.setCurrentMp(rset.getDouble("curMp"));
5661
5662 if (currentHp < 0.5)
5663 {
5664 player.setIsDead(true);
5665 player.stopHpMpRegeneration();
5666 }
5667
5668 // Restore pet if exists in the world
5669 player.setPet(L2World.getInstance().getPet(player.getObjectId()));
5670 if (player.getPet() != null)
5671 player.getPet().setOwner(player);
5672
5673 player.refreshOverloaded();
5674 player.refreshExpertisePenalty();
5675
5676 player.restoreFriendList();
5677
5678 // Retrieve the name and ID of the other characters assigned to this account.
5679 PreparedStatement stmt = con.prepareStatement("SELECT obj_Id, char_name FROM characters WHERE account_name=? AND obj_Id<>?");
5680 stmt.setString(1, player._accountName);
5681 stmt.setInt(2, objectId);
5682 ResultSet chars = stmt.executeQuery();
5683
5684 while (chars.next())
5685 player._chars.put(chars.getInt("obj_Id"), chars.getString("char_name"));
5686
5687 chars.close();
5688 stmt.close();
5689 break;
5690 }
5691
5692 rset.close();
5693 statement.close();
5694 }
5695 catch (Exception e)
5696 {
5697 _log.severe("Could not restore char data: " + e);
5698 }
5699
5700 return player;
5701 }
5702
5703 public Forum getMail()
5704 {
5705 if (_forumMail == null)
5706 {
5707 setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName()));
5708
5709 if (_forumMail == null)
5710 {
5711 ForumsBBSManager.getInstance().createNewForum(getName(), ForumsBBSManager.getInstance().getForumByName("MailRoot"), Forum.MAIL, Forum.OWNERONLY, getObjectId());
5712 setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName()));
5713 }
5714 }
5715
5716 return _forumMail;
5717 }
5718
5719 public void setMail(Forum forum)
5720 {
5721 _forumMail = forum;
5722 }
5723
5724 public Forum getMemo()
5725 {
5726 if (_forumMemo == null)
5727 {
5728 setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName));
5729
5730 if (_forumMemo == null)
5731 {
5732 ForumsBBSManager.getInstance().createNewForum(_accountName, ForumsBBSManager.getInstance().getForumByName("MemoRoot"), Forum.MEMO, Forum.OWNERONLY, getObjectId());
5733 setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName));
5734 }
5735 }
5736
5737 return _forumMemo;
5738 }
5739
5740 public void setMemo(Forum forum)
5741 {
5742 _forumMemo = forum;
5743 }
5744
5745 /**
5746 * Restores sub-class data for the L2PcInstance, used to check the current class index for the character.
5747 * @param player The player to make checks on.
5748 * @return true if successful.
5749 */
5750 private static boolean restoreSubClassData(L2PcInstance player)
5751 {
5752 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5753 {
5754 PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_SUBCLASSES);
5755 statement.setInt(1, player.getObjectId());
5756
5757 ResultSet rset = statement.executeQuery();
5758
5759 while (rset.next())
5760 {
5761 SubClass subClass = new SubClass();
5762 subClass.setClassId(rset.getInt("class_id"));
5763 subClass.setLevel(rset.getByte("level"));
5764 subClass.setExp(rset.getLong("exp"));
5765 subClass.setSp(rset.getInt("sp"));
5766 subClass.setClassIndex(rset.getInt("class_index"));
5767
5768 // Enforce the correct indexing of _subClasses against their class indexes.
5769 player.getSubClasses().put(subClass.getClassIndex(), subClass);
5770 }
5771 rset.close();
5772 statement.close();
5773 }
5774 catch (Exception e)
5775 {
5776 _log.warning("Could not restore classes for " + player.getName() + ": " + e);
5777 e.printStackTrace();
5778 }
5779
5780 return true;
5781 }
5782
5783 /**
5784 * Restores secondary data for the L2PcInstance, based on the current class index.
5785 */
5786 private void restoreCharData()
5787 {
5788 // Retrieve from the database all skills of this L2PcInstance and add them to _skills.
5789 restoreSkills();
5790
5791 // Retrieve from the database all macroses of this L2PcInstance and add them to _macroses.
5792 _macroses.restore();
5793
5794 // Retrieve from the database all shortCuts of this L2PcInstance and add them to _shortCuts.
5795 _shortCuts.restore();
5796
5797 // Retrieve from the database all henna of this L2PcInstance and add them to _henna.
5798 restoreHenna();
5799
5800 // Retrieve from the database all recom data of this L2PcInstance and add to _recomChars.
5801 restoreRecom();
5802
5803 // Retrieve from the database the recipe book of this L2PcInstance.
5804 if (!isSubClassActive())
5805 restoreRecipeBook();
5806 }
5807
5808 /**
5809 * Store recipe book data for this L2PcInstance, if not on an active sub-class.
5810 */
5811 private void storeRecipeBook()
5812 {
5813 // If the player is on a sub-class don't even attempt to store a recipe book.
5814 if (isSubClassActive())
5815 return;
5816
5817 if (getCommonRecipeBook().isEmpty() && getDwarvenRecipeBook().isEmpty())
5818 return;
5819
5820 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5821 {
5822 PreparedStatement statement = con.prepareStatement("DELETE FROM character_recipebook WHERE char_id=?");
5823 statement.setInt(1, getObjectId());
5824 statement.execute();
5825 statement.close();
5826
5827 for (RecipeList recipe : getCommonRecipeBook())
5828 {
5829 statement = con.prepareStatement("INSERT INTO character_recipebook (char_id, id, type) values(?,?,0)");
5830 statement.setInt(1, getObjectId());
5831 statement.setInt(2, recipe.getId());
5832 statement.execute();
5833 statement.close();
5834 }
5835
5836 for (RecipeList recipe : getDwarvenRecipeBook())
5837 {
5838 statement = con.prepareStatement("INSERT INTO character_recipebook (char_id, id, type) values(?,?,1)");
5839 statement.setInt(1, getObjectId());
5840 statement.setInt(2, recipe.getId());
5841 statement.execute();
5842 statement.close();
5843 }
5844 }
5845 catch (Exception e)
5846 {
5847 _log.warning("Could not store recipe book data: " + e);
5848 }
5849 }
5850
5851 /**
5852 * Restore recipe book data for this L2PcInstance.
5853 */
5854 private void restoreRecipeBook()
5855 {
5856 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5857 {
5858 PreparedStatement statement = con.prepareStatement("SELECT id, type FROM character_recipebook WHERE char_id=?");
5859 statement.setInt(1, getObjectId());
5860 ResultSet rset = statement.executeQuery();
5861
5862 while (rset.next())
5863 {
5864 final RecipeList recipe = RecipeTable.getInstance().getRecipeList(rset.getInt("id"));
5865 if (rset.getInt("type") == 1)
5866 registerDwarvenRecipeList(recipe);
5867 else
5868 registerCommonRecipeList(recipe);
5869 }
5870
5871 rset.close();
5872 statement.close();
5873 }
5874 catch (Exception e)
5875 {
5876 _log.warning("Could not restore recipe book data:" + e);
5877 }
5878 }
5879
5880 /**
5881 * Update L2PcInstance stats in the characters table of the database.
5882 * @param storeActiveEffects
5883 */
5884 public synchronized void store(boolean storeActiveEffects)
5885 {
5886 // update client coords, if these look like true
5887 if (isInsideRadius(getClientX(), getClientY(), 1000, true))
5888 setXYZ(getClientX(), getClientY(), getClientZ());
5889
5890 storeCharBase();
5891 storeCharSub();
5892 storeEffect(storeActiveEffects);
5893 storeRecipeBook();
5894
5895 SevenSigns.getInstance().saveSevenSignsData(getObjectId());
5896
5897 _vars.storeMe();
5898 }
5899
5900 public void store()
5901 {
5902 store(true);
5903 }
5904
5905 private void storeCharBase()
5906 {
5907 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5908 {
5909 // Get the exp, level, and sp of base class to store in base table
5910 int currentClassIndex = getClassIndex();
5911 _classIndex = 0;
5912 long exp = getStat().getExp();
5913 int level = getStat().getLevel();
5914 int sp = getStat().getSp();
5915 _classIndex = currentClassIndex;
5916
5917 PreparedStatement statement = con.prepareStatement(UPDATE_CHARACTER);
5918
5919 statement.setInt(1, level);
5920 statement.setInt(2, getMaxHp());
5921 statement.setDouble(3, getCurrentHp());
5922 statement.setInt(4, getMaxCp());
5923 statement.setDouble(5, getCurrentCp());
5924 statement.setInt(6, getMaxMp());
5925 statement.setDouble(7, getCurrentMp());
5926 statement.setInt(8, getAppearance().getFace());
5927 statement.setInt(9, getAppearance().getHairStyle());
5928 statement.setInt(10, getAppearance().getHairColor());
5929 statement.setInt(11, getAppearance().getSex() ? 1 : 0);
5930 statement.setInt(12, getHeading());
5931 statement.setInt(13, _observerMode ? _savedLocation.getX() : getX());
5932 statement.setInt(14, _observerMode ? _savedLocation.getY() : getY());
5933 statement.setInt(15, _observerMode ? _savedLocation.getZ() : getZ());
5934 statement.setLong(16, exp);
5935 statement.setLong(17, getExpBeforeDeath());
5936 statement.setInt(18, sp);
5937 statement.setInt(19, getKarma());
5938 statement.setInt(20, getPvpKills());
5939 statement.setInt(21, getPkKills());
5940 statement.setInt(22, getRecomHave());
5941 statement.setInt(23, getRecomLeft());
5942 statement.setInt(24, getClanId());
5943 statement.setInt(25, getRace().ordinal());
5944 statement.setInt(26, getClassId().getId());
5945 statement.setLong(27, getDeleteTimer());
5946 statement.setString(28, getTitle());
5947 statement.setInt(29, getAccessLevel().getLevel());
5948 statement.setInt(30, isOnlineInt());
5949 statement.setInt(31, isIn7sDungeon() ? 1 : 0);
5950 statement.setInt(32, getClanPrivileges());
5951 statement.setInt(33, wantsPeace() ? 1 : 0);
5952 statement.setInt(34, getBaseClass());
5953
5954 long totalOnlineTime = _onlineTime;
5955 if (_onlineBeginTime > 0)
5956 totalOnlineTime += (System.currentTimeMillis() - _onlineBeginTime) / 1000;
5957
5958 statement.setLong(35, totalOnlineTime);
5959 statement.setInt(36, getPunishLevel().value());
5960 statement.setLong(37, getPunishTimer());
5961 statement.setInt(38, isNoble() ? 1 : 0);
5962 statement.setLong(39, getPowerGrade());
5963 statement.setInt(40, getPledgeType());
5964 statement.setLong(41, getLastRecomUpdate());
5965 statement.setInt(42, getLvlJoinedAcademy());
5966 statement.setLong(43, getApprentice());
5967 statement.setLong(44, getSponsor());
5968 statement.setInt(45, getAllianceWithVarkaKetra());
5969 statement.setLong(46, getClanJoinExpiryTime());
5970 statement.setLong(47, getClanCreateExpiryTime());
5971 statement.setString(48, getName());
5972 statement.setLong(49, getDeathPenaltyBuffLevel());
5973 statement.setInt(50, isffaction() ? 1 : 0);
5974 statement.setInt(51, issfaction() ? 1 : 0);
5975 statement.setInt(52, getObjectId());
5976
5977 statement.execute();
5978 statement.close();
5979 }
5980 catch (Exception e)
5981 {
5982 _log.warning("Could not store char base data: " + e);
5983 }
5984 }
5985
5986 private void storeCharSub()
5987 {
5988 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
5989 {
5990 PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_SUBCLASS);
5991
5992 if (getTotalSubClasses() > 0)
5993 {
5994 for (SubClass subClass : getSubClasses().values())
5995 {
5996 statement.setLong(1, subClass.getExp());
5997 statement.setInt(2, subClass.getSp());
5998 statement.setInt(3, subClass.getLevel());
5999 statement.setInt(4, subClass.getClassId());
6000 statement.setInt(5, getObjectId());
6001 statement.setInt(6, subClass.getClassIndex());
6002
6003 statement.execute();
6004 }
6005 }
6006 statement.close();
6007 }
6008 catch (Exception e)
6009 {
6010 _log.warning("Could not store sub class data for " + getName() + ": " + e);
6011 }
6012 }
6013
6014 private void storeEffect(boolean storeEffects)
6015 {
6016 if (!Config.STORE_SKILL_COOLTIME)
6017 return;
6018
6019 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6020 {
6021 // Delete all current stored effects for char to avoid dupe
6022 PreparedStatement statement = con.prepareStatement(DELETE_SKILL_SAVE);
6023
6024 statement.setInt(1, getObjectId());
6025 statement.setInt(2, getClassIndex());
6026 statement.execute();
6027 statement.close();
6028
6029 int buff_index = 0;
6030
6031 final List<Integer> storedSkills = new ArrayList<>();
6032
6033 // Store all effect data along with calulated remaining reuse delays for matching skills. 'restore_type'= 0.
6034 statement = con.prepareStatement(ADD_SKILL_SAVE);
6035
6036 if (storeEffects)
6037 {
6038 for (L2Effect effect : getAllEffects())
6039 {
6040 if (effect == null)
6041 continue;
6042
6043 switch (effect.getEffectType())
6044 {
6045 case HEAL_OVER_TIME:
6046 case COMBAT_POINT_HEAL_OVER_TIME:
6047 continue;
6048 }
6049
6050 L2Skill skill = effect.getSkill();
6051 if (storedSkills.contains(skill.getReuseHashCode()))
6052 continue;
6053
6054 storedSkills.add(skill.getReuseHashCode());
6055
6056 if (!effect.isHerbEffect() && effect.getInUse() && !skill.isToggle())
6057 {
6058 statement.setInt(1, getObjectId());
6059 statement.setInt(2, skill.getId());
6060 statement.setInt(3, skill.getLevel());
6061 statement.setInt(4, effect.getCount());
6062 statement.setInt(5, effect.getTime());
6063
6064 if (_reuseTimeStamps.containsKey(skill.getReuseHashCode()))
6065 {
6066 TimeStamp t = _reuseTimeStamps.get(skill.getReuseHashCode());
6067 statement.setLong(6, t.hasNotPassed() ? t.getReuse() : 0);
6068 statement.setDouble(7, t.hasNotPassed() ? t.getStamp() : 0);
6069 }
6070 else
6071 {
6072 statement.setLong(6, 0);
6073 statement.setDouble(7, 0);
6074 }
6075
6076 statement.setInt(8, 0);
6077 statement.setInt(9, getClassIndex());
6078 statement.setInt(10, ++buff_index);
6079 statement.execute();
6080 }
6081 }
6082 }
6083
6084 // Store the reuse delays of remaining skills which lost effect but still under reuse delay. 'restore_type' 1.
6085 for (Map.Entry<Integer, TimeStamp> timestampEntry : _reuseTimeStamps.entrySet())
6086 {
6087 final int hash = timestampEntry.getKey();
6088 if (storedSkills.contains(hash))
6089 continue;
6090
6091 TimeStamp t = timestampEntry.getValue();
6092 if (t != null && t.hasNotPassed())
6093 {
6094 storedSkills.add(hash);
6095
6096 statement.setInt(1, getObjectId());
6097 statement.setInt(2, t.getSkillId());
6098 statement.setInt(3, t.getSkillLvl());
6099 statement.setInt(4, -1);
6100 statement.setInt(5, -1);
6101 statement.setLong(6, t.getReuse());
6102 statement.setDouble(7, t.getStamp());
6103 statement.setInt(8, 1);
6104 statement.setInt(9, getClassIndex());
6105 statement.setInt(10, ++buff_index);
6106 statement.execute();
6107 }
6108 }
6109 statement.close();
6110 }
6111 catch (Exception e)
6112 {
6113 _log.log(Level.WARNING, "Could not store char effect data: ", e);
6114 }
6115 }
6116
6117 /**
6118 * @return True if the L2PcInstance is online.
6119 */
6120 public boolean isOnline()
6121 {
6122 return _isOnline;
6123 }
6124
6125 /**
6126 * @return an int interpretation of online status.
6127 */
6128 public int isOnlineInt()
6129 {
6130 if (_isOnline && getClient() != null)
6131 return getClient().isDetached() ? 2 : 1;
6132
6133 return 0;
6134 }
6135
6136 public boolean isIn7sDungeon()
6137 {
6138 return _isIn7sDungeon;
6139 }
6140
6141 /**
6142 * Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance and save update in the character_skills table of the database.
6143 * <ul>
6144 * <li>Replace oldSkill by newSkill or Add the newSkill</li>
6145 * <li>If an old skill has been replaced, remove all its Func objects of L2Character calculator set</li>
6146 * <li>Add Func objects of newSkill to the calculator set of the L2Character</li>
6147 * </ul>
6148 * @param newSkill The L2Skill to add to the L2Character
6149 * @param store
6150 * @return The L2Skill replaced or null if just added a new L2Skill
6151 */
6152 public L2Skill addSkill(L2Skill newSkill, boolean store)
6153 {
6154 // Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance
6155 L2Skill oldSkill = super.addSkill(newSkill);
6156
6157 // Add or update a L2PcInstance skill in the character_skills table of the database
6158 if (store)
6159 storeSkill(newSkill, oldSkill, -1);
6160
6161 return oldSkill;
6162 }
6163
6164 @Override
6165 public L2Skill removeSkill(L2Skill skill, boolean store)
6166 {
6167 if (store)
6168 return removeSkill(skill);
6169
6170 return super.removeSkill(skill, true);
6171 }
6172
6173 public L2Skill removeSkill(L2Skill skill, boolean store, boolean cancelEffect)
6174 {
6175 if (store)
6176 return removeSkill(skill);
6177
6178 return super.removeSkill(skill, cancelEffect);
6179 }
6180
6181 /**
6182 * 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.
6183 * <ul>
6184 * <li>Remove the skill from the L2Character _skills</li>
6185 * <li>Remove all its Func objects from the L2Character calculator set</li>
6186 * </ul>
6187 * @param skill The L2Skill to remove from the L2Character
6188 * @return The L2Skill removed
6189 */
6190 @Override
6191 public L2Skill removeSkill(L2Skill skill)
6192 {
6193 // Remove a skill from the L2Character and its Func objects from calculator set of the L2Character
6194 L2Skill oldSkill = super.removeSkill(skill);
6195
6196 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6197 {
6198 PreparedStatement statement = con.prepareStatement(DELETE_SKILL_FROM_CHAR);
6199
6200 if (oldSkill != null)
6201 {
6202 statement.setInt(1, oldSkill.getId());
6203 statement.setInt(2, getObjectId());
6204 statement.setInt(3, getClassIndex());
6205 statement.execute();
6206 }
6207 statement.close();
6208 }
6209 catch (Exception e)
6210 {
6211 _log.warning("Error could not delete skill: " + e);
6212 }
6213
6214 // Don't busy with shortcuts if skill was a passive skill.
6215 if (skill != null && !skill.isPassive())
6216 {
6217 for (L2ShortCut sc : getAllShortCuts())
6218 {
6219 if (sc != null && sc.getId() == skill.getId() && sc.getType() == L2ShortCut.TYPE_SKILL)
6220 deleteShortCut(sc.getSlot(), sc.getPage());
6221 }
6222 }
6223
6224 return oldSkill;
6225 }
6226
6227 /**
6228 * Add or update a L2PcInstance skill in the character_skills table of the database. <BR>
6229 * <BR>
6230 * If newClassIndex > -1, the skill will be stored with that class index, not the current one.
6231 * @param newSkill
6232 * @param oldSkill
6233 * @param newClassIndex
6234 */
6235 private void storeSkill(L2Skill newSkill, L2Skill oldSkill, int newClassIndex)
6236 {
6237 int classIndex = _classIndex;
6238
6239 if (newClassIndex > -1)
6240 classIndex = newClassIndex;
6241
6242 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6243 {
6244 if (oldSkill != null && newSkill != null)
6245 {
6246 PreparedStatement statement = con.prepareStatement(UPDATE_CHARACTER_SKILL_LEVEL);
6247 statement.setInt(1, newSkill.getLevel());
6248 statement.setInt(2, oldSkill.getId());
6249 statement.setInt(3, getObjectId());
6250 statement.setInt(4, classIndex);
6251 statement.execute();
6252 statement.close();
6253 }
6254 else if (newSkill != null)
6255 {
6256 PreparedStatement statement = con.prepareStatement(ADD_NEW_SKILL);
6257 statement.setInt(1, getObjectId());
6258 statement.setInt(2, newSkill.getId());
6259 statement.setInt(3, newSkill.getLevel());
6260 statement.setInt(4, classIndex);
6261 statement.execute();
6262 statement.close();
6263 }
6264 else
6265 {
6266 _log.warning("storeSkill() couldn't store new skill. It's null type.");
6267 }
6268 }
6269 catch (Exception e)
6270 {
6271 _log.warning("Error could not store char skills: " + e);
6272 }
6273 }
6274
6275 /**
6276 * Retrieve from the database all skills of this L2PcInstance and add them to _skills.
6277 */
6278 private void restoreSkills()
6279 {
6280 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6281 {
6282 PreparedStatement statement = con.prepareStatement(RESTORE_SKILLS_FOR_CHAR);
6283 statement.setInt(1, getObjectId());
6284 statement.setInt(2, getClassIndex());
6285 ResultSet rset = statement.executeQuery();
6286
6287 // Go though the recordset of this SQL query
6288 while (rset.next())
6289 {
6290 int id = rset.getInt("skill_id");
6291 int level = rset.getInt("skill_level");
6292
6293 if (id > 9000)
6294 continue; // fake skills for base stats
6295
6296 // Create a L2Skill object for each record
6297 L2Skill skill = SkillTable.getInstance().getInfo(id, level);
6298
6299 // Add the L2Skill object to the L2Character _skills and its Func objects to the calculator set of the L2Character
6300 super.addSkill(skill);
6301 }
6302
6303 rset.close();
6304 statement.close();
6305 }
6306 catch (Exception e)
6307 {
6308 _log.warning("Could not restore character skills: " + e);
6309 }
6310 }
6311
6312 /**
6313 * Retrieve from the database all skill effects of this L2PcInstance and add them to the player.
6314 */
6315 public void restoreEffects()
6316 {
6317 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6318 {
6319 PreparedStatement statement = con.prepareStatement(RESTORE_SKILL_SAVE);
6320 statement.setInt(1, getObjectId());
6321 statement.setInt(2, getClassIndex());
6322 ResultSet rset = statement.executeQuery();
6323
6324 while (rset.next())
6325 {
6326 int effectCount = rset.getInt("effect_count");
6327 int effectCurTime = rset.getInt("effect_cur_time");
6328 long reuseDelay = rset.getLong("reuse_delay");
6329 long systime = rset.getLong("systime");
6330 int restoreType = rset.getInt("restore_type");
6331
6332 final L2Skill skill = SkillTable.getInstance().getInfo(rset.getInt("skill_id"), rset.getInt("skill_level"));
6333 if (skill == null)
6334 continue;
6335
6336 final long remainingTime = systime - System.currentTimeMillis();
6337 if (remainingTime > 10)
6338 {
6339 disableSkill(skill, remainingTime);
6340 addTimeStamp(skill, reuseDelay, systime);
6341 }
6342
6343 /**
6344 * Restore Type 1 The remaning skills lost effect upon logout but were still under a high reuse delay.
6345 */
6346 if (restoreType > 0)
6347 continue;
6348
6349 /**
6350 * Restore Type 0 These skills were still in effect on the character upon logout. Some of which were self casted and might still have a long reuse delay which also is restored.
6351 */
6352 if (skill.hasEffects())
6353 {
6354 final Env env = new Env();
6355 env.setCharacter(this);
6356 env.setTarget(this);
6357 env.setSkill(skill);
6358
6359 for (EffectTemplate et : skill.getEffectTemplates())
6360 {
6361 final L2Effect ef = et.getEffect(env);
6362 if (ef != null)
6363 {
6364 ef.setCount(effectCount);
6365 ef.setFirstTime(effectCurTime);
6366 ef.scheduleEffect();
6367 }
6368 }
6369 }
6370 }
6371
6372 rset.close();
6373 statement.close();
6374
6375 statement = con.prepareStatement(DELETE_SKILL_SAVE);
6376 statement.setInt(1, getObjectId());
6377 statement.setInt(2, getClassIndex());
6378 statement.executeUpdate();
6379 statement.close();
6380 }
6381 catch (Exception e)
6382 {
6383 _log.log(Level.WARNING, "Could not restore " + this + " active effect data: " + e.getMessage(), e);
6384 }
6385 }
6386
6387 /**
6388 * Retrieve from the database all Henna of this L2PcInstance, add them to _henna and calculate stats of the L2PcInstance.
6389 */
6390 private void restoreHenna()
6391 {
6392 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6393 {
6394 PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_HENNAS);
6395 statement.setInt(1, getObjectId());
6396 statement.setInt(2, getClassIndex());
6397 ResultSet rset = statement.executeQuery();
6398
6399 for (int i = 0; i < 3; i++)
6400 _henna[i] = null;
6401
6402 while (rset.next())
6403 {
6404 int slot = rset.getInt("slot");
6405
6406 if (slot < 1 || slot > 3)
6407 continue;
6408
6409 int symbolId = rset.getInt("symbol_id");
6410 if (symbolId != 0)
6411 {
6412 Henna tpl = HennaTable.getInstance().getTemplate(symbolId);
6413 if (tpl != null)
6414 _henna[slot - 1] = tpl;
6415 }
6416 }
6417
6418 rset.close();
6419 statement.close();
6420 }
6421 catch (Exception e)
6422 {
6423 _log.warning("could not restore henna: " + e);
6424 }
6425
6426 // Calculate Henna modifiers of this L2PcInstance
6427 recalcHennaStats();
6428 }
6429
6430 /**
6431 * Retrieve from the database all Recommendation data of this L2PcInstance, add to _recomChars and calculate stats of the L2PcInstance.
6432 */
6433 private void restoreRecom()
6434 {
6435 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6436 {
6437 PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_RECOMS);
6438 statement.setInt(1, getObjectId());
6439 ResultSet rset = statement.executeQuery();
6440 while (rset.next())
6441 _recomChars.add(rset.getInt("target_id"));
6442
6443 rset.close();
6444 statement.close();
6445 }
6446 catch (Exception e)
6447 {
6448 _log.warning("could not restore recommendations: " + e);
6449 }
6450 }
6451
6452 /**
6453 * @return the number of Henna empty slot of the L2PcInstance.
6454 */
6455 public int getHennaEmptySlots()
6456 {
6457 int totalSlots = 0;
6458 if (getClassId().level() == 1)
6459 totalSlots = 2;
6460 else
6461 totalSlots = 3;
6462
6463 for (int i = 0; i < 3; i++)
6464 {
6465 if (_henna[i] != null)
6466 totalSlots--;
6467 }
6468
6469 if (totalSlots <= 0)
6470 return 0;
6471
6472 return totalSlots;
6473 }
6474
6475 /**
6476 * Remove a Henna of the L2PcInstance, save update in the character_hennas table of the database and send HennaInfo/UserInfo packet to this L2PcInstance.
6477 * @param slot The slot number to make checks on.
6478 * @return true if successful.
6479 */
6480 public boolean removeHenna(int slot)
6481 {
6482 if (slot < 1 || slot > 3)
6483 return false;
6484
6485 slot--;
6486
6487 if (_henna[slot] == null)
6488 return false;
6489
6490 Henna henna = _henna[slot];
6491 _henna[slot] = null;
6492
6493 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6494 {
6495 PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA);
6496
6497 statement.setInt(1, getObjectId());
6498 statement.setInt(2, slot + 1);
6499 statement.setInt(3, getClassIndex());
6500
6501 statement.execute();
6502 statement.close();
6503 }
6504 catch (Exception e)
6505 {
6506 _log.warning("could not remove char henna: " + e);
6507 }
6508
6509 // Calculate Henna modifiers of this L2PcInstance
6510 recalcHennaStats();
6511
6512 // Send HennaInfo packet to this L2PcInstance
6513 sendPacket(new HennaInfo(this));
6514
6515 // Send UserInfo packet to this L2PcInstance
6516 sendPacket(new UserInfo(this));
6517
6518 reduceAdena("Henna", henna.getPrice() / 5, this, false);
6519
6520 // Add the recovered dyes to the player's inventory and notify them.
6521 addItem("Henna", henna.getDyeId(), Henna.getAmountDyeRequire() / 2, this, true);
6522 sendPacket(SystemMessageId.SYMBOL_DELETED);
6523 return true;
6524 }
6525
6526 /**
6527 * Add a Henna to the L2PcInstance, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2PcInstance.
6528 * @param henna The Henna template to add.
6529 */
6530 public void addHenna(Henna henna)
6531 {
6532 for (int i = 0; i < 3; i++)
6533 {
6534 if (_henna[i] == null)
6535 {
6536 _henna[i] = henna;
6537
6538 // Calculate Henna modifiers of this L2PcInstance
6539 recalcHennaStats();
6540
6541 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
6542 {
6543 PreparedStatement statement = con.prepareStatement(ADD_CHAR_HENNA);
6544
6545 statement.setInt(1, getObjectId());
6546 statement.setInt(2, henna.getSymbolId());
6547 statement.setInt(3, i + 1);
6548 statement.setInt(4, getClassIndex());
6549
6550 statement.execute();
6551 statement.close();
6552 }
6553 catch (Exception e)
6554 {
6555 _log.warning("could not save char henna: " + e);
6556 }
6557
6558 sendPacket(new HennaInfo(this));
6559 sendPacket(new UserInfo(this));
6560 sendPacket(SystemMessageId.SYMBOL_ADDED);
6561 return;
6562 }
6563 }
6564 }
6565
6566 /**
6567 * Calculate Henna modifiers of this L2PcInstance.
6568 */
6569 private void recalcHennaStats()
6570 {
6571 _hennaINT = 0;
6572 _hennaSTR = 0;
6573 _hennaCON = 0;
6574 _hennaMEN = 0;
6575 _hennaWIT = 0;
6576 _hennaDEX = 0;
6577
6578 for (int i = 0; i < 3; i++)
6579 {
6580 if (_henna[i] == null)
6581 continue;
6582
6583 _hennaINT += _henna[i].getStatINT();
6584 _hennaSTR += _henna[i].getStatSTR();
6585 _hennaMEN += _henna[i].getStatMEN();
6586 _hennaCON += _henna[i].getStatCON();
6587 _hennaWIT += _henna[i].getStatWIT();
6588 _hennaDEX += _henna[i].getStatDEX();
6589 }
6590
6591 if (_hennaINT > 5)
6592 _hennaINT = 5;
6593
6594 if (_hennaSTR > 5)
6595 _hennaSTR = 5;
6596
6597 if (_hennaMEN > 5)
6598 _hennaMEN = 5;
6599
6600 if (_hennaCON > 5)
6601 _hennaCON = 5;
6602
6603 if (_hennaWIT > 5)
6604 _hennaWIT = 5;
6605
6606 if (_hennaDEX > 5)
6607 _hennaDEX = 5;
6608 }
6609
6610 /**
6611 * @param slot A slot to check.
6612 * @return the Henna of this L2PcInstance corresponding to the selected slot.
6613 */
6614 public Henna getHenna(int slot)
6615 {
6616 if (slot < 1 || slot > 3)
6617 return null;
6618
6619 return _henna[slot - 1];
6620 }
6621
6622 public int getHennaStatINT()
6623 {
6624 return _hennaINT;
6625 }
6626
6627 public int getHennaStatSTR()
6628 {
6629 return _hennaSTR;
6630 }
6631
6632 public int getHennaStatCON()
6633 {
6634 return _hennaCON;
6635 }
6636
6637 public int getHennaStatMEN()
6638 {
6639 return _hennaMEN;
6640 }
6641
6642 public int getHennaStatWIT()
6643 {
6644 return _hennaWIT;
6645 }
6646
6647 public int getHennaStatDEX()
6648 {
6649 return _hennaDEX;
6650 }
6651
6652 /**
6653 * Return True if the L2PcInstance is autoAttackable.
6654 * <ul>
6655 * <li>Check if the attacker isn't the L2PcInstance Pet</li>
6656 * <li>Check if the attacker is L2MonsterInstance</li>
6657 * <li>If the attacker is a L2PcInstance, check if it is not in the same party</li>
6658 * <li>Check if the L2PcInstance has Karma</li>
6659 * <li>If the attacker is a L2PcInstance, check if it is not in the same siege clan (Attacker, Defender)</li>
6660 * </ul>
6661 */
6662 @Override
6663 public boolean isAutoAttackable(L2Character attacker)
6664 {
6665 if ((attacker instanceof L2PcInstance) && ((L2PcInstance) attacker).isffaction())
6666 return true;
6667
6668 if ((attacker instanceof L2PcInstance) && ((L2PcInstance) attacker).issfaction())
6669 return true;
6670
6671 // Check if the attacker isn't the L2PcInstance Pet
6672 if (attacker == this || attacker == getPet())
6673 return false;
6674
6675 // Check if the attacker is a L2MonsterInstance
6676 if (attacker instanceof L2MonsterInstance)
6677 return true;
6678
6679 // Check if the attacker is not in the same party
6680 if (getParty() != null && getParty().getPartyMembers().contains(attacker))
6681 return false;
6682
6683 // Check if the attacker is a L2Playable
6684 if (attacker instanceof L2Playable)
6685 {
6686 if (isInsideZone(ZoneId.PEACE))
6687 return false;
6688
6689 // Get L2PcInstance
6690 final L2PcInstance cha = attacker.getActingPlayer();
6691
6692 // Check if the attacker is in olympiad and olympiad start
6693 if (attacker instanceof L2PcInstance && cha.isInOlympiadMode())
6694 {
6695 if (isInOlympiadMode() && isOlympiadStart() && cha.getOlympiadGameId() == getOlympiadGameId())
6696 return true;
6697
6698 return false;
6699 }
6700
6701 // is AutoAttackable if both players are in the same duel and the duel is still going on
6702 if (getDuelState() == DuelState.DUELLING && getDuelId() == cha.getDuelId())
6703 return true;
6704
6705 if (getClan() != null)
6706 {
6707 final Siege siege = SiegeManager.getSiege(getX(), getY(), getZ());
6708 if (siege != null)
6709 {
6710 // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Defender clan
6711 if (siege.checkIsDefender(cha.getClan()) && siege.checkIsDefender(getClan()))
6712 return false;
6713
6714 // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Attacker clan
6715 if (siege.checkIsAttacker(cha.getClan()) && siege.checkIsAttacker(getClan()))
6716 return false;
6717 }
6718
6719 // Check if clan is at war
6720 if (getClan().isAtWarWith(cha.getClanId()) && !wantsPeace() && !cha.wantsPeace() && !isAcademyMember())
6721 return true;
6722 }
6723
6724 // Check if the L2PcInstance is in an arena.
6725 if (isInArena() && attacker.isInArena())
6726 return true;
6727
6728 // Check if the attacker is not in the same ally.
6729 if (getAllyId() != 0 && getAllyId() == cha.getAllyId())
6730 return false;
6731
6732 // Check if the attacker is not in the same clan.
6733 if (getClan() != null && getClan().isMember(cha.getObjectId()))
6734 return false;
6735
6736 // Now check again if the L2PcInstance is in pvp zone (as arenas check was made before, it ends with sieges).
6737 if (isInsideZone(ZoneId.PVP) && attacker.isInsideZone(ZoneId.PVP))
6738 return true;
6739 }
6740 else if (attacker instanceof L2SiegeGuardInstance)
6741 {
6742 if (getClan() != null)
6743 {
6744 final Siege siege = SiegeManager.getSiege(this);
6745 return (siege != null && siege.checkIsAttacker(getClan()));
6746 }
6747 }
6748
6749 // Check if the L2PcInstance has Karma
6750 if (getKarma() > 0 || getPvpFlag() > 0)
6751 return true;
6752
6753 return false;
6754 }
6755
6756 /**
6757 * Check if the active L2Skill can be casted.
6758 * <ul>
6759 * <li>Check if the skill isn't toggle and is offensive</li>
6760 * <li>Check if the target is in the skill cast range</li>
6761 * <li>Check if the skill is Spoil type and if the target isn't already spoiled</li>
6762 * <li>Check if the caster owns enought consummed Item, enough HP and MP to cast the skill</li>
6763 * <li>Check if the caster isn't sitting</li>
6764 * <li>Check if all skills are enabled and this skill is enabled</li>
6765 * <li>Check if the caster own the weapon needed</li>
6766 * <li>Check if the skill is active</li>
6767 * <li>Check if all casting conditions are completed</li>
6768 * <li>Notify the AI with CAST and target</li>
6769 * </ul>
6770 * @param skill The L2Skill to use
6771 * @param forceUse used to force ATTACK on players
6772 * @param dontMove used to prevent movement, if not in range
6773 */
6774 @Override
6775 public boolean useMagic(L2Skill skill, boolean forceUse, boolean dontMove)
6776 {
6777 // Check if the skill is active
6778 if (skill.isPassive())
6779 {
6780 sendPacket(ActionFailed.STATIC_PACKET);
6781 return false;
6782 }
6783
6784 // Cancels the use of skills when player uses a cursed weapon or is flying.
6785 if ((isCursedWeaponEquipped() && !skill.isDemonicSkill()) // If CW, allow ONLY demonic skills.
6786 || (getMountType() == 1 && !skill.isStriderSkill()) // If mounted, allow ONLY Strider skills.
6787 || (getMountType() == 2 && !skill.isFlyingSkill())) // If flying, allow ONLY Wyvern skills.
6788 {
6789 sendPacket(ActionFailed.STATIC_PACKET);
6790 return false;
6791 }
6792
6793 // Players wearing Formal Wear cannot use skills.
6794 final ItemInstance formal = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
6795 if (formal != null && formal.getItem().getBodyPart() == Item.SLOT_ALLDRESS)
6796 {
6797 sendPacket(SystemMessageId.CANNOT_USE_ITEMS_SKILLS_WITH_FORMALWEAR);
6798 sendPacket(ActionFailed.STATIC_PACKET);
6799 return false;
6800 }
6801
6802 // ************************************* Check Casting in Progress *******************************************
6803
6804 // If a skill is currently being used, queue this one if this is not the same
6805 if (isCastingNow())
6806 {
6807 // Check if new skill different from current skill in progress ; queue it in the player _queuedSkill
6808 if (_currentSkill.getSkill() != null && skill.getId() != _currentSkill.getSkillId())
6809 setQueuedSkill(skill, forceUse, dontMove);
6810
6811 sendPacket(ActionFailed.STATIC_PACKET);
6812 return false;
6813 }
6814
6815 setIsCastingNow(true);
6816
6817 // Set the player _currentSkill.
6818 setCurrentSkill(skill, forceUse, dontMove);
6819
6820 // Wipe queued skill.
6821 if (_queuedSkill.getSkill() != null)
6822 setQueuedSkill(null, false, false);
6823
6824 if (!checkUseMagicConditions(skill, forceUse, dontMove))
6825 {
6826 setIsCastingNow(false);
6827 return false;
6828 }
6829
6830 // Check if the target is correct and Notify the AI with CAST and target
6831 L2Object target = null;
6832
6833 switch (skill.getTargetType())
6834 {
6835 case TARGET_AURA:
6836 case TARGET_FRONT_AURA:
6837 case TARGET_BEHIND_AURA:
6838 case TARGET_GROUND:
6839 case TARGET_SELF:
6840 case TARGET_CORPSE_ALLY:
6841 case TARGET_AURA_UNDEAD:
6842 target = this;
6843 break;
6844
6845 default: // Get the first target of the list
6846 target = skill.getFirstOfTargetList(this);
6847 break;
6848 }
6849
6850 // Notify the AI with CAST and target
6851 getAI().setIntention(CtrlIntention.CAST, skill, target);
6852 return true;
6853 }
6854
6855 private boolean checkUseMagicConditions(L2Skill skill, boolean forceUse, boolean dontMove)
6856 {
6857 // ************************************* Check Player State *******************************************
6858
6859 // Check if the player is dead or out of control.
6860 if (isDead() || isOutOfControl())
6861 {
6862 sendPacket(ActionFailed.STATIC_PACKET);
6863 return false;
6864 }
6865
6866 L2SkillType sklType = skill.getSkillType();
6867
6868 if (isFishing() && (sklType != L2SkillType.PUMPING && sklType != L2SkillType.REELING && sklType != L2SkillType.FISHING))
6869 {
6870 // Only fishing skills are available
6871 sendPacket(SystemMessageId.ONLY_FISHING_SKILLS_NOW);
6872 return false;
6873 }
6874
6875 if (inObserverMode())
6876 {
6877 sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
6878 abortCast();
6879 sendPacket(ActionFailed.STATIC_PACKET);
6880 return false;
6881 }
6882
6883 // Check if the caster is sitted. Toggle skills can be only removed, not activated.
6884 if (isSitting())
6885 {
6886 if (skill.isToggle())
6887 {
6888 // Get effects of the skill
6889 L2Effect effect = getFirstEffect(skill.getId());
6890 if (effect != null)
6891 {
6892 effect.exit();
6893
6894 // Send ActionFailed to the L2PcInstance
6895 sendPacket(ActionFailed.STATIC_PACKET);
6896 return false;
6897 }
6898 }
6899
6900 // Send a System Message to the caster
6901 sendPacket(SystemMessageId.CANT_MOVE_SITTING);
6902
6903 // Send ActionFailed to the L2PcInstance
6904 sendPacket(ActionFailed.STATIC_PACKET);
6905 return false;
6906 }
6907
6908 // Check if the skill type is TOGGLE
6909 if (skill.isToggle())
6910 {
6911 // Get effects of the skill
6912 L2Effect effect = getFirstEffect(skill.getId());
6913
6914 if (effect != null)
6915 {
6916 // If the toggle is different of FakeDeath, you can de-activate it clicking on it.
6917 if (skill.getId() != 60)
6918 effect.exit();
6919
6920 // Send ActionFailed to the L2PcInstance
6921 sendPacket(ActionFailed.STATIC_PACKET);
6922 return false;
6923 }
6924 }
6925
6926 // Check if the player uses "Fake Death" skill
6927 if (isFakeDeath())
6928 {
6929 // Send ActionFailed to the L2PcInstance
6930 sendPacket(ActionFailed.STATIC_PACKET);
6931 return false;
6932 }
6933
6934 // ************************************* Check Target *******************************************
6935 // Create and set a L2Object containing the target of the skill
6936 L2Object target = null;
6937 SkillTargetType sklTargetType = skill.getTargetType();
6938 Location worldPosition = getCurrentSkillWorldPosition();
6939
6940 if (sklTargetType == SkillTargetType.TARGET_GROUND && worldPosition == null)
6941 {
6942 _log.info("WorldPosition is null for skill: " + skill.getName() + ", player: " + getName() + ".");
6943 sendPacket(ActionFailed.STATIC_PACKET);
6944 return false;
6945 }
6946
6947 switch (sklTargetType)
6948 {
6949 // Target the player if skill type is AURA, PARTY, CLAN or SELF
6950 case TARGET_AURA:
6951 case TARGET_FRONT_AURA:
6952 case TARGET_BEHIND_AURA:
6953 case TARGET_AURA_UNDEAD:
6954 case TARGET_PARTY:
6955 case TARGET_ALLY:
6956 case TARGET_CLAN:
6957 case TARGET_GROUND:
6958 case TARGET_SELF:
6959 case TARGET_CORPSE_ALLY:
6960 case TARGET_AREA_SUMMON:
6961 target = this;
6962 break;
6963 case TARGET_PET:
6964 case TARGET_SUMMON:
6965 target = getPet();
6966 break;
6967 default:
6968 target = getTarget();
6969 break;
6970 }
6971
6972 // Check the validity of the target
6973 if (target == null)
6974 {
6975 sendPacket(ActionFailed.STATIC_PACKET);
6976 return false;
6977 }
6978
6979 if (target instanceof L2DoorInstance)
6980 {
6981 if (!((L2DoorInstance) target).isAttackable(this) // Siege doors only hittable during siege
6982 || (((L2DoorInstance) target).isUnlockable() && skill.getSkillType() != L2SkillType.UNLOCK)) // unlockable doors
6983 {
6984 sendPacket(SystemMessageId.INCORRECT_TARGET);
6985 sendPacket(ActionFailed.STATIC_PACKET);
6986 return false;
6987 }
6988 }
6989
6990 // Are the target and the player in the same duel?
6991 if (isInDuel())
6992 {
6993 if (target instanceof L2Playable)
6994 {
6995 // Get L2PcInstance
6996 L2PcInstance cha = target.getActingPlayer();
6997 if (cha.getDuelId() != getDuelId())
6998 {
6999 sendPacket(SystemMessageId.INCORRECT_TARGET);
7000 sendPacket(ActionFailed.STATIC_PACKET);
7001 return false;
7002 }
7003 }
7004 }
7005
7006 // ************************************* Check skill availability *******************************************
7007
7008 // Siege summon checks. Both checks send a message to the player if it return false.
7009 if (skill.isSiegeSummonSkill() && (!SiegeManager.checkIfOkToSummon(this) || !SevenSigns.getInstance().checkSummonConditions(this)))
7010 return false;
7011
7012 // Check if this skill is enabled (ex : reuse time)
7013 if (isSkillDisabled(skill))
7014 {
7015 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_PREPARED_FOR_REUSE).addSkillName(skill));
7016 return false;
7017 }
7018
7019 // ************************************* Check casting conditions *******************************************
7020
7021 // Check if all casting conditions are completed
7022 if (!skill.checkCondition(this, target, false))
7023 {
7024 // Send ActionFailed to the L2PcInstance
7025 sendPacket(ActionFailed.STATIC_PACKET);
7026 return false;
7027 }
7028
7029 // ************************************* Check Skill Type *******************************************
7030
7031 // Check if this is offensive magic skill
7032 if (skill.isOffensive())
7033 {
7034 if (isInsidePeaceZone(this, target))
7035 {
7036 // If L2Character or target is in a peace zone, send a system message TARGET_IN_PEACEZONE ActionFailed
7037 sendPacket(SystemMessageId.TARGET_IN_PEACEZONE);
7038 sendPacket(ActionFailed.STATIC_PACKET);
7039 return false;
7040 }
7041
7042 if (isInOlympiadMode() && !isOlympiadStart())
7043 {
7044 // if L2PcInstance is in Olympia and the match isn't already start, send ActionFailed
7045 sendPacket(ActionFailed.STATIC_PACKET);
7046 return false;
7047 }
7048
7049 // Check if the target is attackable
7050 if (!target.isAttackable() && !getAccessLevel().allowPeaceAttack() && (Config.FACTION_SYSTEM_ENABLE == false))
7051 {
7052 // If target is not attackable, send ActionFailed
7053 sendPacket(ActionFailed.STATIC_PACKET);
7054 return false;
7055 }
7056
7057 // Check if a Forced ATTACK is in progress on non-attackable target
7058 if (!target.isAutoAttackable(this) && !forceUse)
7059 {
7060 switch (sklTargetType)
7061 {
7062 case TARGET_AURA:
7063 case TARGET_FRONT_AURA:
7064 case TARGET_BEHIND_AURA:
7065 case TARGET_AURA_UNDEAD:
7066 case TARGET_CLAN:
7067 case TARGET_ALLY:
7068 case TARGET_PARTY:
7069 case TARGET_SELF:
7070 case TARGET_GROUND:
7071 case TARGET_CORPSE_ALLY:
7072 case TARGET_AREA_SUMMON:
7073 break;
7074 default: // Send ActionFailed to the L2PcInstance
7075 sendPacket(ActionFailed.STATIC_PACKET);
7076 return false;
7077 }
7078 }
7079
7080 // Check if the target is in the skill cast range
7081 if (dontMove)
7082 {
7083 // Calculate the distance between the L2PcInstance and the target
7084 if (sklTargetType == SkillTargetType.TARGET_GROUND)
7085 {
7086 if (!isInsideRadius(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), skill.getCastRange() + getTemplate().getCollisionRadius(), false, false))
7087 {
7088 // Send a System Message to the caster
7089 sendPacket(SystemMessageId.TARGET_TOO_FAR);
7090
7091 // Send ActionFailed to the L2PcInstance
7092 sendPacket(ActionFailed.STATIC_PACKET);
7093 return false;
7094 }
7095 }
7096 else if (skill.getCastRange() > 0 && !isInsideRadius(target, skill.getCastRange() + getTemplate().getCollisionRadius(), false, false))
7097 {
7098 // Send a System Message to the caster
7099 sendPacket(SystemMessageId.TARGET_TOO_FAR);
7100
7101 // Send ActionFailed to the L2PcInstance
7102 sendPacket(ActionFailed.STATIC_PACKET);
7103 return false;
7104 }
7105 }
7106 }
7107
7108 // Check if the skill is defensive
7109 if (!skill.isOffensive() && target instanceof L2MonsterInstance && !forceUse)
7110 {
7111 // check if the target is a monster and if force attack is set.. if not then we don't want to cast.
7112 switch (sklTargetType)
7113 {
7114 case TARGET_PET:
7115 case TARGET_SUMMON:
7116 case TARGET_AURA:
7117 case TARGET_FRONT_AURA:
7118 case TARGET_BEHIND_AURA:
7119 case TARGET_AURA_UNDEAD:
7120 case TARGET_CLAN:
7121 case TARGET_SELF:
7122 case TARGET_CORPSE_ALLY:
7123 case TARGET_PARTY:
7124 case TARGET_ALLY:
7125 case TARGET_CORPSE_MOB:
7126 case TARGET_AREA_CORPSE_MOB:
7127 case TARGET_GROUND:
7128 break;
7129 default:
7130 {
7131 switch (sklType)
7132 {
7133 case BEAST_FEED:
7134 case DELUXE_KEY_UNLOCK:
7135 case UNLOCK:
7136 break;
7137 default:
7138 sendPacket(ActionFailed.STATIC_PACKET);
7139 return false;
7140 }
7141 break;
7142 }
7143 }
7144 }
7145
7146 // Check if the skill is Spoil type and if the target isn't already spoiled
7147 if (sklType == L2SkillType.SPOIL)
7148 {
7149 if (!(target instanceof L2MonsterInstance))
7150 {
7151 // Send a System Message to the L2PcInstance
7152 sendPacket(SystemMessageId.INCORRECT_TARGET);
7153
7154 // Send ActionFailed to the L2PcInstance
7155 sendPacket(ActionFailed.STATIC_PACKET);
7156 return false;
7157 }
7158 }
7159
7160 // Check if the skill is Sweep type and if conditions not apply
7161 if (sklType == L2SkillType.SWEEP && target instanceof L2Attackable)
7162 {
7163 if (((L2Attackable) target).isDead())
7164 {
7165 final int spoilerId = ((L2Attackable) target).getSpoilerId();
7166 if (spoilerId == 0)
7167 {
7168 // Send a System Message to the L2PcInstance
7169 sendPacket(SystemMessageId.SWEEPER_FAILED_TARGET_NOT_SPOILED);
7170
7171 // Send ActionFailed to the L2PcInstance
7172 sendPacket(ActionFailed.STATIC_PACKET);
7173 return false;
7174 }
7175
7176 if (getObjectId() != spoilerId && !isInLooterParty(spoilerId))
7177 {
7178 // Send a System Message to the L2PcInstance
7179 sendPacket(SystemMessageId.SWEEP_NOT_ALLOWED);
7180
7181 // Send ActionFailed to the L2PcInstance
7182 sendPacket(ActionFailed.STATIC_PACKET);
7183 return false;
7184 }
7185 }
7186 }
7187
7188 // Check if the skill is Drain Soul (Soul Crystals) and if the target is a MOB
7189 if (sklType == L2SkillType.DRAIN_SOUL)
7190 {
7191 if (!(target instanceof L2MonsterInstance))
7192 {
7193 // Send a System Message to the L2PcInstance
7194 sendPacket(SystemMessageId.INCORRECT_TARGET);
7195
7196 // Send ActionFailed to the L2PcInstance
7197 sendPacket(ActionFailed.STATIC_PACKET);
7198 return false;
7199 }
7200 }
7201
7202 // Check if this is a Pvp skill and target isn't a non-flagged/non-karma player
7203 switch (sklTargetType)
7204 {
7205 case TARGET_PARTY:
7206 case TARGET_ALLY: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList()
7207 case TARGET_CLAN: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList()
7208 case TARGET_AURA:
7209 case TARGET_FRONT_AURA:
7210 case TARGET_BEHIND_AURA:
7211 case TARGET_AURA_UNDEAD:
7212 case TARGET_GROUND:
7213 case TARGET_SELF:
7214 case TARGET_CORPSE_ALLY:
7215 break;
7216 default:
7217 if (!checkPvpSkill(target, skill) && !getAccessLevel().allowPeaceAttack())
7218 {
7219 // Send a System Message to the L2PcInstance
7220 sendPacket(SystemMessageId.TARGET_IS_INCORRECT);
7221
7222 // Send ActionFailed to the L2PcInstance
7223 sendPacket(ActionFailed.STATIC_PACKET);
7224 return false;
7225 }
7226 }
7227
7228 if ((sklTargetType == SkillTargetType.TARGET_HOLY && !checkIfOkToCastSealOfRule(CastleManager.getInstance().getCastle(this), false, skill, target)) || (sklType == L2SkillType.SIEGEFLAG && !L2SkillSiegeFlag.checkIfOkToPlaceFlag(this, false)) || (sklType == L2SkillType.STRSIEGEASSAULT && !checkIfOkToUseStriderSiegeAssault(skill)) || (sklType == L2SkillType.SUMMON_FRIEND && !(checkSummonerStatus(this) && checkSummonTargetStatus(target, this))))
7229 {
7230 sendPacket(ActionFailed.STATIC_PACKET);
7231 abortCast();
7232 return false;
7233 }
7234
7235 // GeoData Los Check here
7236 if (skill.getCastRange() > 0)
7237 {
7238 if (sklTargetType == SkillTargetType.TARGET_GROUND)
7239 {
7240 if (!PathFinding.getInstance().canSeeTarget(this, worldPosition))
7241 {
7242 sendPacket(SystemMessageId.CANT_SEE_TARGET);
7243 sendPacket(ActionFailed.STATIC_PACKET);
7244 return false;
7245 }
7246 }
7247 else if (!PathFinding.getInstance().canSeeTarget(this, target))
7248 {
7249 sendPacket(SystemMessageId.CANT_SEE_TARGET);
7250 sendPacket(ActionFailed.STATIC_PACKET);
7251 return false;
7252 }
7253 }
7254 // finally, after passing all conditions
7255 return true;
7256 }
7257
7258 public boolean checkIfOkToUseStriderSiegeAssault(L2Skill skill)
7259 {
7260 SystemMessage sm;
7261 Castle castle = CastleManager.getInstance().getCastle(this);
7262
7263 if (!isRiding())
7264 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7265 else if (!(getTarget() instanceof L2DoorInstance))
7266 sm = SystemMessage.getSystemMessage(SystemMessageId.INCORRECT_TARGET);
7267 else if (castle == null || castle.getCastleId() <= 0)
7268 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7269 else if (!castle.getSiege().isInProgress() || castle.getSiege().getAttackerClan(getClan()) == null)
7270 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7271 else
7272 return true;
7273
7274 sendPacket(sm);
7275 return false;
7276 }
7277
7278 public boolean checkIfOkToCastSealOfRule(Castle castle, boolean isCheckOnly, L2Skill skill, L2Object target)
7279 {
7280 SystemMessage sm;
7281
7282 if (castle == null || castle.getCastleId() <= 0)
7283 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7284 else if (!castle.getArtefacts().contains(target))
7285 sm = SystemMessage.getSystemMessage(SystemMessageId.INCORRECT_TARGET);
7286 else if (!castle.getSiege().isInProgress())
7287 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7288 else if (!Util.checkIfInRange(200, this, target, true))
7289 sm = SystemMessage.getSystemMessage(SystemMessageId.DIST_TOO_FAR_CASTING_STOPPED);
7290 else if (!isInsideZone(ZoneId.CAST_ON_ARTIFACT))
7291 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7292 else if (castle.getSiege().getAttackerClan(getClan()) == null)
7293 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED).addSkillName(skill);
7294 else
7295 {
7296 if (!isCheckOnly)
7297 {
7298 sm = SystemMessage.getSystemMessage(SystemMessageId.OPPONENT_STARTED_ENGRAVING);
7299 castle.getSiege().announceToPlayer(sm, false);
7300 }
7301 return true;
7302 }
7303 sendPacket(sm);
7304 return false;
7305 }
7306
7307 public boolean isInLooterParty(int LooterId)
7308 {
7309 L2PcInstance looter = L2World.getInstance().getPlayer(LooterId);
7310
7311 // if L2PcInstance is in a CommandChannel
7312 if (isInParty() && getParty().isInCommandChannel() && looter != null)
7313 return getParty().getCommandChannel().getMembers().contains(looter);
7314
7315 if (isInParty() && looter != null)
7316 return getParty().getPartyMembers().contains(looter);
7317
7318 return false;
7319 }
7320
7321 /**
7322 * Check if the requested casting is a Pc->Pc skill cast and if it's a valid pvp condition
7323 * @param target L2Object instance containing the target
7324 * @param skill L2Skill instance with the skill being casted
7325 * @return {@code false} if the skill is a pvpSkill and target is not a valid pvp target, {@code true} otherwise.
7326 */
7327 public boolean checkPvpSkill(L2Object target, L2Skill skill)
7328 {
7329 if (issfaction() || isffaction())
7330 return true;
7331
7332 if (skill == null || target == null)
7333 return false;
7334
7335 if (!(target instanceof L2Playable))
7336 return true;
7337
7338 if (skill.isDebuff() || skill.isOffensive())
7339 {
7340 final L2PcInstance targetPlayer = target.getActingPlayer();
7341 if (targetPlayer == null || this == target)
7342 return false;
7343
7344 // Peace Zone
7345 if (target.isInsideZone(ZoneId.PEACE))
7346 return false;
7347
7348 // Duel
7349 if (isInDuel() && targetPlayer.isInDuel() && getDuelId() == targetPlayer.getDuelId())
7350 return true;
7351
7352 final boolean isCtrlPressed = getCurrentSkill() != null && getCurrentSkill().isCtrlPressed();
7353
7354 // Party
7355 if (isInParty() && targetPlayer.isInParty())
7356 {
7357 // Same Party
7358 if (getParty().getLeader() == targetPlayer.getParty().getLeader())
7359 {
7360 if (skill.getEffectRange() > 0 && isCtrlPressed && getTarget() == target && skill.isDamage())
7361 return true;
7362
7363 return false;
7364 }
7365 else if (getParty().getCommandChannel() != null && getParty().getCommandChannel().containsPlayer(targetPlayer))
7366 {
7367 if (skill.getEffectRange() > 0 && isCtrlPressed && getTarget() == target && skill.isDamage())
7368 return true;
7369
7370 return false;
7371 }
7372 }
7373
7374 // You can debuff anyone except party members while in an arena...
7375 if (isInsideZone(ZoneId.PVP) && targetPlayer.isInsideZone(ZoneId.PVP))
7376 return true;
7377
7378 // Olympiad
7379 if (isInOlympiadMode() && targetPlayer.isInOlympiadMode() && getOlympiadGameId() == targetPlayer.getOlympiadGameId())
7380 return true;
7381
7382 final L2Clan aClan = getClan();
7383 final L2Clan tClan = targetPlayer.getClan();
7384
7385 if (aClan != null && tClan != null)
7386 {
7387 if (aClan.isAtWarWith(tClan.getClanId()) && tClan.isAtWarWith(aClan.getClanId()))
7388 {
7389 // Check if skill can do dmg
7390 if (skill.getEffectRange() > 0 && isCtrlPressed && getTarget() == target && skill.isAOE())
7391 return true;
7392
7393 return isCtrlPressed;
7394 }
7395 else if (getClanId() == targetPlayer.getClanId() || (getAllyId() > 0 && getAllyId() == targetPlayer.getAllyId()))
7396 {
7397 // Check if skill can do dmg
7398 if (skill.getEffectRange() > 0 && isCtrlPressed && getTarget() == target && skill.isDamage())
7399 return true;
7400
7401 return false;
7402 }
7403 }
7404
7405 // On retail, it is impossible to debuff a "peaceful" player.
7406 if (targetPlayer.getPvpFlag() == 0 && targetPlayer.getKarma() == 0)
7407 {
7408 // Check if skill can do dmg
7409 if (skill.getEffectRange() > 0 && isCtrlPressed && getTarget() == target && skill.isDamage())
7410 return true;
7411
7412 return false;
7413 }
7414
7415 if (targetPlayer.getPvpFlag() > 0 || targetPlayer.getKarma() > 0)
7416 return true;
7417
7418 return false;
7419 }
7420 return true;
7421 }
7422
7423 /**
7424 * @return True if the L2PcInstance is a Mage (based on class templates).
7425 */
7426 public boolean isMageClass()
7427 {
7428 return getClassId().isMage();
7429 }
7430
7431 public boolean isMounted()
7432 {
7433 return _mountType > 0;
7434 }
7435
7436 /**
7437 * This method allows to :
7438 * <ul>
7439 * <li>change isRiding/isFlying flags</li>
7440 * <li>gift player with Wyvern Breath skill if mount is a wyvern</li>
7441 * <li>send the skillList (faded icons update)</li>
7442 * </ul>
7443 * @param npcId the npcId of the mount
7444 * @param npcLevel The level of the mount
7445 * @param mountType 0, 1 or 2 (dismount, strider or wyvern).
7446 * @return always true.
7447 */
7448 public boolean setMount(int npcId, int npcLevel, int mountType)
7449 {
7450 switch (mountType)
7451 {
7452 case 0: // Dismounted
7453 if (isFlying())
7454 removeSkill(FrequentSkill.WYVERN_BREATH.getSkill());
7455 break;
7456
7457 case 2: // Flying Wyvern
7458 addSkill(FrequentSkill.WYVERN_BREATH.getSkill(), false); // not saved to DB
7459 break;
7460 }
7461
7462 _mountNpcId = npcId;
7463 _mountType = mountType;
7464 _mountLevel = npcLevel;
7465
7466 sendSkillList(); // Update faded icons && eventual added skills.
7467 return true;
7468 }
7469
7470 @Override
7471 public boolean isSeated()
7472 {
7473 return _mountObjectID > 0;
7474 }
7475
7476 @Override
7477 public boolean isRiding()
7478 {
7479 return _mountType == 1;
7480 }
7481
7482 @Override
7483 public boolean isFlying()
7484 {
7485 return _mountType == 2;
7486 }
7487
7488 /**
7489 * @return the type of Pet mounted (0 : none, 1 : Strider, 2 : Wyvern).
7490 */
7491 public int getMountType()
7492 {
7493 return _mountType;
7494 }
7495
7496 @Override
7497 public final void stopAllEffects()
7498 {
7499 super.stopAllEffects();
7500 updateAndBroadcastStatus(2);
7501 }
7502
7503 @Override
7504 public final void stopAllEffectsExceptThoseThatLastThroughDeath()
7505 {
7506 super.stopAllEffectsExceptThoseThatLastThroughDeath();
7507 updateAndBroadcastStatus(2);
7508 }
7509
7510 /**
7511 * Stop all toggle-type effects
7512 */
7513 public final void stopAllToggles()
7514 {
7515 _effects.stopAllToggles();
7516 }
7517
7518 public final void stopCubics()
7519 {
7520 if (getCubics() != null)
7521 {
7522 boolean removed = false;
7523 for (L2CubicInstance cubic : getCubics().values())
7524 {
7525 cubic.stopAction();
7526 delCubic(cubic.getId());
7527 removed = true;
7528 }
7529 if (removed)
7530 broadcastUserInfo();
7531 }
7532 }
7533
7534 public final void stopCubicsByOthers()
7535 {
7536 if (getCubics() != null)
7537 {
7538 boolean removed = false;
7539 for (L2CubicInstance cubic : getCubics().values())
7540 {
7541 if (cubic.givenByOther())
7542 {
7543 cubic.stopAction();
7544 delCubic(cubic.getId());
7545 removed = true;
7546 }
7547 }
7548 if (removed)
7549 broadcastUserInfo();
7550 }
7551 }
7552
7553 /**
7554 * Send UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers.<BR>
7555 * <ul>
7556 * <li>Send UserInfo to this L2PcInstance (Public and Private Data)</li>
7557 * <li>Send CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance (Public data only)</li>
7558 * </ul>
7559 * <FONT COLOR=#FF0000><B> <U>Caution</U> : DON'T SEND UserInfo packet to other players instead of CharInfo packet. Indeed, UserInfo packet contains PRIVATE DATA as MaxHP, STR, DEX...</B></FONT><BR>
7560 * <BR>
7561 */
7562 @Override
7563 public void updateAbnormalEffect()
7564 {
7565 broadcastUserInfo();
7566 }
7567
7568 /**
7569 * Disable the Inventory and create a new task to enable it after 1.5s.
7570 */
7571 public void tempInventoryDisable()
7572 {
7573 _inventoryDisable = true;
7574
7575 ThreadPoolManager.getInstance().scheduleGeneral(new InventoryEnable(), 1500);
7576 }
7577
7578 /**
7579 * @return True if the Inventory is disabled.
7580 */
7581 public boolean isInventoryDisabled()
7582 {
7583 return _inventoryDisable;
7584 }
7585
7586 protected class InventoryEnable implements Runnable
7587 {
7588 @Override
7589 public void run()
7590 {
7591 _inventoryDisable = false;
7592 }
7593 }
7594
7595 public Map<Integer, L2CubicInstance> getCubics()
7596 {
7597 return _cubics;
7598 }
7599
7600 /**
7601 * Add a L2CubicInstance to the L2PcInstance _cubics.
7602 * @param id
7603 * @param level
7604 * @param matk
7605 * @param activationtime
7606 * @param activationchance
7607 * @param totalLifetime
7608 * @param givenByOther
7609 */
7610 public void addCubic(int id, int level, double matk, int activationtime, int activationchance, int totalLifetime, boolean givenByOther)
7611 {
7612 _cubics.put(id, new L2CubicInstance(this, id, level, (int) matk, activationtime, activationchance, totalLifetime, givenByOther));
7613 }
7614
7615 /**
7616 * Remove a L2CubicInstance from the L2PcInstance _cubics.
7617 * @param id
7618 */
7619 public void delCubic(int id)
7620 {
7621 _cubics.remove(id);
7622 }
7623
7624 /**
7625 * @param id
7626 * @return the L2CubicInstance corresponding to the Identifier of the L2PcInstance _cubics.
7627 */
7628 public L2CubicInstance getCubic(int id)
7629 {
7630 return _cubics.get(id);
7631 }
7632
7633 @Override
7634 public String toString()
7635 {
7636 return "player " + getName();
7637 }
7638
7639 /**
7640 * @return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127).
7641 */
7642 public int getEnchantEffect()
7643 {
7644 ItemInstance wpn = getActiveWeaponInstance();
7645
7646 if (wpn == null)
7647 return 0;
7648
7649 return Math.min(127, wpn.getEnchantLevel());
7650 }
7651
7652 /**
7653 * Set the _currentFolkNpc of the player.
7654 * @param npc
7655 */
7656 public void setCurrentFolkNPC(L2Npc npc)
7657 {
7658 _currentFolkNpc = npc;
7659 }
7660
7661 /**
7662 * @return the _currentFolkNpc of the player.
7663 */
7664 public L2Npc getCurrentFolkNPC()
7665 {
7666 return _currentFolkNpc;
7667 }
7668
7669 /**
7670 * @return True if L2PcInstance is a participant in the Festival of Darkness.
7671 */
7672 public boolean isFestivalParticipant()
7673 {
7674 return SevenSignsFestival.getInstance().isParticipant(this);
7675 }
7676
7677 public void addAutoSoulShot(int itemId)
7678 {
7679 _activeSoulShots.add(itemId);
7680 }
7681
7682 public boolean removeAutoSoulShot(int itemId)
7683 {
7684 return _activeSoulShots.remove(itemId);
7685 }
7686
7687 public Set<Integer> getAutoSoulShot()
7688 {
7689 return _activeSoulShots;
7690 }
7691
7692 @Override
7693 public boolean isChargedShot(ShotType type)
7694 {
7695 ItemInstance weapon = getActiveWeaponInstance();
7696 return weapon != null && weapon.isChargedShot(type);
7697 }
7698
7699 @Override
7700 public void setChargedShot(ShotType type, boolean charged)
7701 {
7702 ItemInstance weapon = getActiveWeaponInstance();
7703 if (weapon != null)
7704 weapon.setChargedShot(type, charged);
7705 }
7706
7707 @Override
7708 public void rechargeShots(boolean physical, boolean magic)
7709 {
7710 if (_activeSoulShots.isEmpty())
7711 return;
7712
7713 for (int itemId : _activeSoulShots)
7714 {
7715 ItemInstance item = getInventory().getItemByItemId(itemId);
7716 if (item != null)
7717 {
7718 if (magic && item.getItem().getDefaultAction() == ActionType.spiritshot)
7719 {
7720 IItemHandler handler = ItemHandler.getInstance().getItemHandler(item.getEtcItem());
7721 if (handler != null)
7722 handler.useItem(this, item, false);
7723 }
7724
7725 if (physical && item.getItem().getDefaultAction() == ActionType.soulshot)
7726 {
7727 IItemHandler handler = ItemHandler.getInstance().getItemHandler(item.getEtcItem());
7728 if (handler != null)
7729 handler.useItem(this, item, false);
7730 }
7731 }
7732 else
7733 removeAutoSoulShot(itemId);
7734 }
7735 }
7736
7737 /**
7738 * Cancel autoshot use for shot itemId
7739 * @param itemId int id to disable
7740 * @return true if canceled.
7741 */
7742 public boolean disableAutoShot(int itemId)
7743 {
7744 if (_activeSoulShots.contains(itemId))
7745 {
7746 removeAutoSoulShot(itemId);
7747 sendPacket(new ExAutoSoulShot(itemId, 0));
7748 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.AUTO_USE_OF_S1_CANCELLED).addItemName(itemId));
7749 return true;
7750 }
7751
7752 return false;
7753 }
7754
7755 /**
7756 * Cancel all autoshots for player
7757 */
7758 public void disableAutoShotsAll()
7759 {
7760 for (int itemId : _activeSoulShots)
7761 {
7762 sendPacket(new ExAutoSoulShot(itemId, 0));
7763 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.AUTO_USE_OF_S1_CANCELLED).addItemName(itemId));
7764 }
7765 _activeSoulShots.clear();
7766 }
7767
7768 class LookingForFishTask implements Runnable
7769 {
7770 boolean _isNoob, _isUpperGrade;
7771 int _fishType, _fishGutsCheck;
7772 long _endTaskTime;
7773
7774 protected LookingForFishTask(int fishWaitTime, int fishGutsCheck, int fishType, boolean isNoob, boolean isUpperGrade)
7775 {
7776 _fishGutsCheck = fishGutsCheck;
7777 _endTaskTime = System.currentTimeMillis() + fishWaitTime + 10000;
7778 _fishType = fishType;
7779 _isNoob = isNoob;
7780 _isUpperGrade = isUpperGrade;
7781 }
7782
7783 @Override
7784 public void run()
7785 {
7786 if (System.currentTimeMillis() >= _endTaskTime)
7787 {
7788 endFishing(false);
7789 return;
7790 }
7791
7792 if (_fishType == -1)
7793 return;
7794
7795 int check = Rnd.get(1000);
7796 if (_fishGutsCheck > check)
7797 {
7798 stopLookingForFishTask();
7799 startFishCombat(_isNoob, _isUpperGrade);
7800 }
7801 }
7802 }
7803
7804 public int getClanPrivileges()
7805 {
7806 return _clanPrivileges;
7807 }
7808
7809 public void setClanPrivileges(int n)
7810 {
7811 _clanPrivileges = n;
7812 }
7813
7814 // baron etc
7815 public void setPledgeClass(int classId)
7816 {
7817 _pledgeClass = classId;
7818 }
7819
7820 public int getPledgeClass()
7821 {
7822 return _pledgeClass;
7823 }
7824
7825 public void setPledgeType(int typeId)
7826 {
7827 _pledgeType = typeId;
7828 }
7829
7830 public int getPledgeType()
7831 {
7832 return _pledgeType;
7833 }
7834
7835 public int getApprentice()
7836 {
7837 return _apprentice;
7838 }
7839
7840 public void setApprentice(int apprentice_id)
7841 {
7842 _apprentice = apprentice_id;
7843 }
7844
7845 public int getSponsor()
7846 {
7847 return _sponsor;
7848 }
7849
7850 public void setSponsor(int sponsor_id)
7851 {
7852 _sponsor = sponsor_id;
7853 }
7854
7855 @Override
7856 public void sendMessage(String message)
7857 {
7858 sendPacket(SystemMessage.sendString(message));
7859 }
7860
7861 /**
7862 * Unsummon all types of summons : pets, cubics, normal summons and trained beasts.
7863 */
7864 public void dropAllSummons()
7865 {
7866 // Delete summons and pets
7867 if (getPet() != null)
7868 getPet().unSummon(this);
7869
7870 // Delete trained beasts
7871 if (getTrainedBeast() != null)
7872 getTrainedBeast().deleteMe();
7873
7874 // Delete any form of cubics
7875 stopCubics();
7876 }
7877
7878 public void enterObserverMode(int x, int y, int z)
7879 {
7880 _savedLocation.setXYZ(getX(), getY(), getZ());
7881 _observerMode = true;
7882
7883 standUp();
7884
7885 dropAllSummons();
7886 setTarget(null);
7887 setIsParalyzed(true);
7888 startParalyze();
7889 setIsInvul(true);
7890 getAppearance().setInvisible();
7891
7892 sendPacket(new ObservationMode(x, y, z));
7893 getKnownList().removeAllKnownObjects(); // reinit knownlist
7894 setXYZ(x, y, z);
7895
7896 broadcastUserInfo();
7897 }
7898
7899 public void enterOlympiadObserverMode(int id)
7900 {
7901 final OlympiadGameTask task = OlympiadGameManager.getInstance().getOlympiadTask(id);
7902 if (task == null)
7903 return;
7904
7905 dropAllSummons();
7906
7907 if (getParty() != null)
7908 getParty().removePartyMember(this, MessageType.Expelled);
7909
7910 _olympiadGameId = id;
7911
7912 standUp();
7913
7914 if (!_observerMode)
7915 _savedLocation.setXYZ(getX(), getY(), getZ());
7916
7917 _observerMode = true;
7918 setTarget(null);
7919 setIsInvul(true);
7920 getAppearance().setInvisible();
7921 teleToLocation(task.getZone().getSpawns().get(2), 0);
7922 sendPacket(new ExOlympiadMode(3));
7923 broadcastUserInfo();
7924 }
7925
7926 public void leaveObserverMode()
7927 {
7928 setTarget(null);
7929 getKnownList().removeAllKnownObjects(); // reinit knownlist
7930 setXYZ(_savedLocation.getX(), _savedLocation.getY(), _savedLocation.getZ());
7931 setIsParalyzed(false);
7932 stopParalyze(false);
7933 getAppearance().setVisible();
7934 setIsInvul(false);
7935
7936 if (hasAI())
7937 getAI().setIntention(CtrlIntention.IDLE);
7938
7939 // prevent receive falling damage
7940 setFalling();
7941
7942 _observerMode = false;
7943 _savedLocation.setXYZ(getX(), getY(), getZ());
7944 sendPacket(new ObservationReturn(_savedLocation));
7945 broadcastUserInfo();
7946 }
7947
7948 public void leaveOlympiadObserverMode()
7949 {
7950 if (_olympiadGameId == -1)
7951 return;
7952
7953 _olympiadGameId = -1;
7954 _observerMode = false;
7955
7956 setTarget(null);
7957 sendPacket(new ExOlympiadMode(0));
7958 teleToLocation(_savedLocation, 20);
7959 getAppearance().setVisible();
7960 setIsInvul(false);
7961
7962 if (hasAI())
7963 getAI().setIntention(CtrlIntention.IDLE);
7964
7965 _savedLocation.setXYZ(getX(), getY(), getZ());
7966 broadcastUserInfo();
7967 }
7968
7969 public void setOlympiadSide(int i)
7970 {
7971 _olympiadSide = i;
7972 }
7973
7974 public int getOlympiadSide()
7975 {
7976 return _olympiadSide;
7977 }
7978
7979 public void setOlympiadGameId(int id)
7980 {
7981 _olympiadGameId = id;
7982 }
7983
7984 public int getOlympiadGameId()
7985 {
7986 return _olympiadGameId;
7987 }
7988
7989 public Location getSavedLocation()
7990 {
7991 return _savedLocation;
7992 }
7993
7994 public boolean inObserverMode()
7995 {
7996 return _observerMode;
7997 }
7998
7999 public int getTeleMode()
8000 {
8001 return _telemode;
8002 }
8003
8004 public void setTeleMode(int mode)
8005 {
8006 _telemode = mode;
8007 }
8008
8009 public void setLoto(int i, int val)
8010 {
8011 _loto[i] = val;
8012 }
8013
8014 public int getLoto(int i)
8015 {
8016 return _loto[i];
8017 }
8018
8019 public void setRace(int i, int val)
8020 {
8021 _race[i] = val;
8022 }
8023
8024 public int getRace(int i)
8025 {
8026 return _race[i];
8027 }
8028
8029 public boolean isInRefusalMode()
8030 {
8031 return _messageRefusal;
8032 }
8033
8034 public void setInRefusalMode(boolean mode)
8035 {
8036 _messageRefusal = mode;
8037 sendPacket(new EtcStatusUpdate(this));
8038 }
8039
8040 public void setTradeRefusal(boolean mode)
8041 {
8042 _tradeRefusal = mode;
8043 }
8044
8045 public boolean getTradeRefusal()
8046 {
8047 return _tradeRefusal;
8048 }
8049
8050 public void setExchangeRefusal(boolean mode)
8051 {
8052 _exchangeRefusal = mode;
8053 }
8054
8055 public boolean getExchangeRefusal()
8056 {
8057 return _exchangeRefusal;
8058 }
8059
8060 public BlockList getBlockList()
8061 {
8062 return _blockList;
8063 }
8064
8065 public void setHero(boolean hero)
8066 {
8067 if (hero && _baseClass == _activeClass)
8068 {
8069 for (L2Skill s : SkillTable.getHeroSkills())
8070 addSkill(s, false); // Dont Save Hero skills to database
8071 }
8072 else
8073 {
8074 for (L2Skill s : SkillTable.getHeroSkills())
8075 super.removeSkill(s); // Just Remove skills from nonHero characters
8076 }
8077 _hero = hero;
8078
8079 sendSkillList();
8080 }
8081
8082 public void setIsInOlympiadMode(boolean b)
8083 {
8084 _inOlympiadMode = b;
8085 }
8086
8087 public void setIsOlympiadStart(boolean b)
8088 {
8089 _OlympiadStart = b;
8090 }
8091
8092 public boolean isOlympiadStart()
8093 {
8094 return _OlympiadStart;
8095 }
8096
8097 public boolean isHero()
8098 {
8099 return _hero;
8100 }
8101
8102 public boolean isInOlympiadMode()
8103 {
8104 return _inOlympiadMode;
8105 }
8106
8107 public boolean isInDuel()
8108 {
8109 return _isInDuel;
8110 }
8111
8112 public int getDuelId()
8113 {
8114 return _duelId;
8115 }
8116
8117 public void setDuelState(DuelState state)
8118 {
8119 _duelState = state;
8120 }
8121
8122 public DuelState getDuelState()
8123 {
8124 return _duelState;
8125 }
8126
8127 /**
8128 * Sets up the duel state using a non 0 duelId.
8129 * @param duelId 0=not in a duel
8130 */
8131 public void setInDuel(int duelId)
8132 {
8133 if (duelId > 0)
8134 {
8135 _isInDuel = true;
8136 _duelState = DuelState.DUELLING;
8137 _duelId = duelId;
8138 }
8139 else
8140 {
8141 if (_duelState == DuelState.DEAD)
8142 {
8143 enableAllSkills();
8144 getStatus().startHpMpRegeneration();
8145 }
8146 _isInDuel = false;
8147 _duelState = DuelState.NO_DUEL;
8148 _duelId = 0;
8149 }
8150 }
8151
8152 /**
8153 * This returns a SystemMessage stating why the player is not available for duelling.
8154 * @return S1_CANNOT_DUEL... message
8155 */
8156 public SystemMessage getNoDuelReason()
8157 {
8158 SystemMessage sm = SystemMessage.getSystemMessage(_noDuelReason);
8159 sm.addPcName(this);
8160 _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL;
8161 return sm;
8162 }
8163
8164 /**
8165 * Checks if this player might join / start a duel. To get the reason use getNoDuelReason() after calling this function.
8166 * @return true if the player might join/start a duel.
8167 */
8168 public boolean canDuel()
8169 {
8170 if (isInCombat() || getPunishLevel() == PunishLevel.JAIL)
8171 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_CURRENTLY_ENGAGED_IN_BATTLE;
8172 else if (isDead() || isAlikeDead() || (getCurrentHp() < getMaxHp() / 2 || getCurrentMp() < getMaxMp() / 2))
8173 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_HP_OR_MP_IS_BELOW_50_PERCENT;
8174 else if (isInDuel())
8175 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_ALREADY_ENGAGED_IN_A_DUEL;
8176 else if (isInOlympiadMode())
8177 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_PARTICIPATING_IN_THE_OLYMPIAD;
8178 else if (isCursedWeaponEquipped() || getKarma() != 0)
8179 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_IN_A_CHAOTIC_STATE;
8180 else if (isInStoreMode())
8181 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_CURRENTLY_ENGAGED_IN_A_PRIVATE_STORE_OR_MANUFACTURE;
8182 else if (isMounted() || isInBoat())
8183 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_CURRENTLY_RIDING_A_BOAT_WYVERN_OR_STRIDER;
8184 else if (isFishing())
8185 _noDuelReason = SystemMessageId.S1_CANNOT_DUEL_BECAUSE_S1_IS_CURRENTLY_FISHING;
8186 else if (isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.PEACE) || isInsideZone(ZoneId.SIEGE))
8187 _noDuelReason = SystemMessageId.S1_CANNOT_MAKE_A_CHALLANGE_TO_A_DUEL_BECAUSE_S1_IS_CURRENTLY_IN_A_DUEL_PROHIBITED_AREA;
8188 else
8189 return true;
8190
8191 return false;
8192 }
8193
8194 public boolean isNoble()
8195 {
8196 return _noble;
8197 }
8198
8199 /**
8200 * Set Noblesse Status, and reward with nobles' skills.
8201 * @param val Add skills if setted to true, else remove skills.
8202 * @param store Store the status directly in the db if setted to true.
8203 */
8204 public void setNoble(boolean val, boolean store)
8205 {
8206 if (val)
8207 for (L2Skill s : SkillTable.getNobleSkills())
8208 addSkill(s, false); // Dont Save Noble skills to Sql
8209 else
8210 for (L2Skill s : SkillTable.getNobleSkills())
8211 super.removeSkill(s); // Just Remove skills without deleting from Sql
8212
8213 _noble = val;
8214
8215 sendSkillList();
8216
8217 if (store)
8218 {
8219 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
8220 {
8221 PreparedStatement statement = con.prepareStatement(UPDATE_NOBLESS);
8222 statement.setBoolean(1, val);
8223 statement.setInt(2, getObjectId());
8224 statement.executeUpdate();
8225 statement.close();
8226 }
8227 catch (Exception e)
8228 {
8229 _log.log(Level.WARNING, "Could not update " + getName() + " nobless status: " + e.getMessage(), e);
8230 }
8231 }
8232 }
8233
8234 public void setLvlJoinedAcademy(int lvl)
8235 {
8236 _lvlJoinedAcademy = lvl;
8237 }
8238
8239 public int getLvlJoinedAcademy()
8240 {
8241 return _lvlJoinedAcademy;
8242 }
8243
8244 public boolean isAcademyMember()
8245 {
8246 return _lvlJoinedAcademy > 0;
8247 }
8248
8249 public void setTeam(int team)
8250 {
8251 _team = team;
8252 }
8253
8254 public int getTeam()
8255 {
8256 return _team;
8257 }
8258
8259 public void setWantsPeace(boolean wantsPeace)
8260 {
8261 _wantsPeace = wantsPeace;
8262 }
8263
8264 public boolean wantsPeace()
8265 {
8266 return _wantsPeace;
8267 }
8268
8269 public boolean isFishing()
8270 {
8271 return _fishingLoc != null;
8272 }
8273
8274 public void setAllianceWithVarkaKetra(int sideAndLvlOfAlliance)
8275 {
8276 _alliedVarkaKetra = sideAndLvlOfAlliance;
8277 }
8278
8279 /**
8280 * [-5,-1] varka, 0 neutral, [1,5] ketra
8281 * @return the side faction.
8282 */
8283 public int getAllianceWithVarkaKetra()
8284 {
8285 return _alliedVarkaKetra;
8286 }
8287
8288 public boolean isAlliedWithVarka()
8289 {
8290 return (_alliedVarkaKetra < 0);
8291 }
8292
8293 public boolean isAlliedWithKetra()
8294 {
8295 return (_alliedVarkaKetra > 0);
8296 }
8297
8298 public void sendSkillList()
8299 {
8300 final ItemInstance formal = getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
8301 final boolean isWearingFormalWear = formal != null && formal.getItem().getBodyPart() == Item.SLOT_ALLDRESS;
8302
8303 boolean isDisabled = false;
8304 SkillList sl = new SkillList();
8305 for (L2Skill s : getAllSkills())
8306 {
8307 if (s == null)
8308 continue;
8309
8310 if (s.getId() > 9000 && s.getId() < 9007)
8311 continue; // Fake skills to change base stats
8312
8313 if (getClan() != null)
8314 isDisabled = s.isClanSkill() && getClan().getReputationScore() < 0;
8315
8316 if (isCursedWeaponEquipped()) // Only Demonic skills are available
8317 isDisabled = !s.isDemonicSkill();
8318 else if (isMounted()) // else if, because only ONE state is possible
8319 {
8320 if (getMountType() == 1) // Only Strider skills are available
8321 isDisabled = !s.isStriderSkill();
8322 else if (getMountType() == 2) // Only Wyvern skills are available
8323 isDisabled = !s.isFlyingSkill();
8324 }
8325
8326 if (isWearingFormalWear)
8327 isDisabled = true;
8328
8329 sl.addSkill(s.getId(), s.getLevel(), s.isPassive(), isDisabled);
8330 }
8331 sendPacket(sl);
8332 }
8333
8334 public boolean isffaction()
8335 {
8336 return _isffaction;
8337 }
8338
8339 public boolean issfaction()
8340 {
8341 return _issfaction;
8342 }
8343
8344 public void setffaction(boolean value)
8345 {
8346 _isffaction = value;
8347 }
8348
8349 public void setsfaction(boolean value)
8350 {
8351 _issfaction = value;
8352 }
8353
8354 /**
8355 * 1. Add the specified class ID as a subclass (up to the maximum number of <b>three</b>) for this character.<BR>
8356 * 2. This method no longer changes the active _classIndex of the player. This is only done by the calling of setActiveClass() method as that should be the only way to do so.
8357 * @param classId
8358 * @param classIndex
8359 * @return boolean subclassAdded
8360 */
8361 public boolean addSubClass(int classId, int classIndex)
8362 {
8363 if (!_subclassLock.tryLock())
8364 return false;
8365
8366 try
8367 {
8368 if (getTotalSubClasses() == 3 || classIndex == 0)
8369 return false;
8370
8371 if (getSubClasses().containsKey(classIndex))
8372 return false;
8373
8374 // Note: Never change _classIndex in any method other than setActiveClass().
8375
8376 SubClass newClass = new SubClass();
8377 newClass.setClassId(classId);
8378 newClass.setClassIndex(classIndex);
8379
8380 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
8381 {
8382 PreparedStatement statement = con.prepareStatement(ADD_CHAR_SUBCLASS);
8383 statement.setInt(1, getObjectId());
8384 statement.setInt(2, newClass.getClassId());
8385 statement.setLong(3, newClass.getExp());
8386 statement.setInt(4, newClass.getSp());
8387 statement.setInt(5, newClass.getLevel());
8388 statement.setInt(6, newClass.getClassIndex()); // <-- Added
8389
8390 statement.execute();
8391 statement.close();
8392 }
8393 catch (Exception e)
8394 {
8395 _log.warning("WARNING: Could not add character sub class for " + getName() + ": " + e);
8396 return false;
8397 }
8398
8399 // Commit after database INSERT incase exception is thrown.
8400 getSubClasses().put(newClass.getClassIndex(), newClass);
8401
8402 ClassId subTemplate = ClassId.values()[classId];
8403 Collection<L2SkillLearn> skillTree = SkillTreeTable.getInstance().getAllowedSkills(subTemplate);
8404
8405 if (skillTree == null)
8406 return true;
8407
8408 final Map<Integer, L2Skill> prevSkillList = new LinkedHashMap<>();
8409
8410 for (L2SkillLearn skillInfo : skillTree)
8411 {
8412 if (skillInfo.getMinLevel() <= 40)
8413 {
8414 L2Skill prevSkill = prevSkillList.get(skillInfo.getId());
8415 L2Skill newSkill = SkillTable.getInstance().getInfo(skillInfo.getId(), skillInfo.getLevel());
8416
8417 if (prevSkill != null && (prevSkill.getLevel() > newSkill.getLevel()))
8418 continue;
8419
8420 prevSkillList.put(newSkill.getId(), newSkill);
8421 storeSkill(newSkill, prevSkill, classIndex);
8422 }
8423 }
8424
8425 return true;
8426 }
8427 finally
8428 {
8429 _subclassLock.unlock();
8430 }
8431 }
8432
8433 /**
8434 * 1. Completely erase all existance of the subClass linked to the classIndex.<BR>
8435 * 2. Send over the newClassId to addSubClass()to create a new instance on this classIndex.<BR>
8436 * 3. Upon Exception, revert the player to their BaseClass to avoid further problems.<BR>
8437 * @param classIndex
8438 * @param newClassId
8439 * @return boolean subclassAdded
8440 */
8441 public boolean modifySubClass(int classIndex, int newClassId)
8442 {
8443 if (!_subclassLock.tryLock())
8444 return false;
8445
8446 try
8447 {
8448 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
8449 {
8450 // Remove all henna info stored for this sub-class.
8451 PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNAS);
8452 statement.setInt(1, getObjectId());
8453 statement.setInt(2, classIndex);
8454 statement.execute();
8455 statement.close();
8456
8457 // Remove all shortcuts info stored for this sub-class.
8458 statement = con.prepareStatement(DELETE_CHAR_SHORTCUTS);
8459 statement.setInt(1, getObjectId());
8460 statement.setInt(2, classIndex);
8461 statement.execute();
8462 statement.close();
8463
8464 // Remove all effects info stored for this sub-class.
8465 statement = con.prepareStatement(DELETE_SKILL_SAVE);
8466 statement.setInt(1, getObjectId());
8467 statement.setInt(2, classIndex);
8468 statement.execute();
8469 statement.close();
8470
8471 // Remove all skill info stored for this sub-class.
8472 statement = con.prepareStatement(DELETE_CHAR_SKILLS);
8473 statement.setInt(1, getObjectId());
8474 statement.setInt(2, classIndex);
8475 statement.execute();
8476 statement.close();
8477
8478 // Remove all basic info stored about this sub-class.
8479 statement = con.prepareStatement(DELETE_CHAR_SUBCLASS);
8480 statement.setInt(1, getObjectId());
8481 statement.setInt(2, classIndex);
8482 statement.execute();
8483 statement.close();
8484 }
8485 catch (Exception e)
8486 {
8487 _log.warning("Could not modify subclass for " + getName() + " to class index " + classIndex + ": " + e);
8488
8489 // This must be done in order to maintain data consistency.
8490 getSubClasses().remove(classIndex);
8491 return false;
8492 }
8493
8494 getSubClasses().remove(classIndex);
8495 }
8496 finally
8497 {
8498 _subclassLock.unlock();
8499 }
8500
8501 return addSubClass(newClassId, classIndex);
8502 }
8503
8504 public boolean isSubClassActive()
8505 {
8506 return _classIndex > 0;
8507 }
8508
8509 public Map<Integer, SubClass> getSubClasses()
8510 {
8511 return _subClasses;
8512 }
8513
8514 public int getTotalSubClasses()
8515 {
8516 return getSubClasses().size();
8517 }
8518
8519 public int getBaseClass()
8520 {
8521 return _baseClass;
8522 }
8523
8524 public int getActiveClass()
8525 {
8526 return _activeClass;
8527 }
8528
8529 public int getClassIndex()
8530 {
8531 return _classIndex;
8532 }
8533
8534 private void setClassTemplate(int classId)
8535 {
8536 _activeClass = classId;
8537
8538 PcTemplate t = CharTemplateTable.getInstance().getTemplate(classId);
8539
8540 if (t == null)
8541 {
8542 _log.severe("Missing template for classId: " + classId);
8543 throw new Error();
8544 }
8545
8546 // Set the template of the L2PcInstance
8547 setTemplate(t);
8548 }
8549
8550 /**
8551 * Changes the character's class based on the given class index. <BR>
8552 * <BR>
8553 * An index of zero specifies the character's original (base) class, while indexes 1-3 specifies the character's sub-classes respectively.
8554 * @param classIndex
8555 * @return true if successful.
8556 */
8557 public boolean setActiveClass(int classIndex)
8558 {
8559 if (!_subclassLock.tryLock())
8560 return false;
8561
8562 try
8563 {
8564 // Remove active item skills before saving char to database because next time when choosing this class, worn items can be different
8565 for (ItemInstance item : getInventory().getAugmentedItems())
8566 {
8567 if (item != null && item.isEquipped())
8568 item.getAugmentation().removeBonus(this);
8569 }
8570
8571 // abort any kind of cast.
8572 abortCast();
8573
8574 // Stop casting for any player that may be casting a force buff on this l2pcinstance.
8575 for (L2Character character : getKnownList().getKnownType(L2Character.class))
8576 if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
8577 character.abortCast();
8578
8579 store();
8580 _reuseTimeStamps.clear();
8581
8582 // clear charges
8583 _charges.set(0);
8584 stopChargeTask();
8585
8586 if (classIndex == 0)
8587 setClassTemplate(getBaseClass());
8588 else
8589 {
8590 try
8591 {
8592 setClassTemplate(getSubClasses().get(classIndex).getClassId());
8593 }
8594 catch (Exception e)
8595 {
8596 _log.info("Could not switch " + getName() + "'s sub class to class index " + classIndex + ": " + e);
8597 return false;
8598 }
8599 }
8600 _classIndex = classIndex;
8601
8602 if (isInParty())
8603 getParty().recalculatePartyLevel();
8604
8605 if (getPet() instanceof L2SummonInstance)
8606 getPet().unSummon(this);
8607
8608 for (L2Skill oldSkill : getAllSkills())
8609 super.removeSkill(oldSkill);
8610
8611 stopAllEffectsExceptThoseThatLastThroughDeath();
8612 stopCubics();
8613
8614 if (isSubClassActive())
8615 {
8616 _dwarvenRecipeBook.clear();
8617 _commonRecipeBook.clear();
8618 }
8619 else
8620 restoreRecipeBook();
8621
8622 restoreSkills();
8623 rewardSkills();
8624 regiveTemporarySkills();
8625
8626 // Prevents some issues when changing between subclases that shares skills
8627 getDisabledSkills().clear();
8628
8629 restoreEffects();
8630 updateEffectIcons();
8631 sendPacket(new EtcStatusUpdate(this));
8632
8633 // If player has quest "Repent Your Sins", remove it
8634 QuestState st = getQuestState("Q422_RepentYourSins");
8635 if (st != null)
8636 st.exitQuest(true);
8637
8638 for (int i = 0; i < 3; i++)
8639 _henna[i] = null;
8640
8641 restoreHenna();
8642 sendPacket(new HennaInfo(this));
8643
8644 if (getCurrentHp() > getMaxHp())
8645 setCurrentHp(getMaxHp());
8646 if (getCurrentMp() > getMaxMp())
8647 setCurrentMp(getMaxMp());
8648 if (getCurrentCp() > getMaxCp())
8649 setCurrentCp(getMaxCp());
8650
8651 refreshOverloaded();
8652 refreshExpertisePenalty();
8653 broadcastUserInfo();
8654
8655 // Clear resurrect xp calculation
8656 setExpBeforeDeath(0);
8657
8658 _shortCuts.restore();
8659 sendPacket(new ShortCutInit(this));
8660
8661 broadcastPacket(new SocialAction(this, 15));
8662 sendPacket(new SkillCoolTime(this));
8663 return true;
8664 }
8665 finally
8666 {
8667 _subclassLock.unlock();
8668 }
8669 }
8670
8671 public boolean isLocked()
8672 {
8673 return _subclassLock.isLocked();
8674 }
8675
8676 public void stopWaterTask()
8677 {
8678 if (_isInWater)
8679 {
8680 _isInWater = false;
8681 sendPacket(new SetupGauge(2, 0));
8682 WaterTaskManager.getInstance().remove(this);
8683 }
8684 }
8685
8686 public void startWaterTask()
8687 {
8688 if (!isDead() && !_isInWater)
8689 {
8690 _isInWater = true;
8691 final int time = (int) calcStat(Stats.BREATH, 60000 * getRace().getBreathMultiplier(), this, null);
8692
8693 sendPacket(new SetupGauge(2, time));
8694 WaterTaskManager.getInstance().add(this, time);
8695 }
8696 }
8697
8698 public void checkWaterState()
8699 {
8700 if (isInsideZone(ZoneId.WATER))
8701 startWaterTask();
8702 else
8703 stopWaterTask();
8704 }
8705
8706 public void onPlayerEnter()
8707 {
8708 if (isCursedWeaponEquipped())
8709 CursedWeaponsManager.getInstance().getCursedWeapon(getCursedWeaponEquippedId()).cursedOnLogin();
8710
8711 // Add to the GameTimeTask to keep inform about activity time.
8712 GameTimeTaskManager.getInstance().add(this);
8713
8714 // Teleport player if the Seven Signs period isn't the good one, or if the player isn't in a cabal.
8715 if (isIn7sDungeon() && !isGM())
8716 {
8717 if (SevenSigns.getInstance().isSealValidationPeriod() || SevenSigns.getInstance().isCompResultsPeriod())
8718 {
8719 if (SevenSigns.getInstance().getPlayerCabal(getObjectId()) != SevenSigns.getInstance().getCabalHighestScore())
8720 {
8721 teleToLocation(MapRegionTable.TeleportWhereType.Town);
8722 setIsIn7sDungeon(false);
8723 }
8724 }
8725 else if (SevenSigns.getInstance().getPlayerCabal(getObjectId()) == SevenSigns.CABAL_NULL)
8726 {
8727 teleToLocation(MapRegionTable.TeleportWhereType.Town);
8728 setIsIn7sDungeon(false);
8729 }
8730 }
8731
8732 // Jail task
8733 updatePunishState();
8734
8735 if (isGM())
8736 {
8737 if (isInvul())
8738 sendMessage("Entering world in Invulnerable mode.");
8739 if (getAppearance().getInvisible())
8740 sendMessage("Entering world in Invisible mode.");
8741 if (isInRefusalMode())
8742 sendMessage("Entering world in Message Refusal mode.");
8743 }
8744
8745 revalidateZone(true);
8746 notifyFriends(true);
8747 }
8748
8749 public long getLastAccess()
8750 {
8751 return _lastAccess;
8752 }
8753
8754 private void checkRecom(int recsHave, int recsLeft)
8755 {
8756 Calendar check = Calendar.getInstance();
8757 check.setTimeInMillis(_lastRecomUpdate);
8758 check.add(Calendar.DAY_OF_MONTH, 1);
8759
8760 Calendar min = Calendar.getInstance();
8761
8762 _recomHave = recsHave;
8763 _recomLeft = recsLeft;
8764
8765 if (getStat().getLevel() < 10 || check.after(min))
8766 return;
8767
8768 restartRecom();
8769 }
8770
8771 public void restartRecom()
8772 {
8773 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
8774 {
8775 PreparedStatement statement = con.prepareStatement(DELETE_CHAR_RECOMS);
8776 statement.setInt(1, getObjectId());
8777 statement.execute();
8778 statement.close();
8779
8780 _recomChars.clear();
8781 }
8782 catch (Exception e)
8783 {
8784 _log.warning("could not clear char recommendations: " + e);
8785 }
8786
8787 if (getStat().getLevel() < 20)
8788 {
8789 _recomLeft = 3;
8790 _recomHave--;
8791 }
8792 else if (getStat().getLevel() < 40)
8793 {
8794 _recomLeft = 6;
8795 _recomHave -= 2;
8796 }
8797 else
8798 {
8799 _recomLeft = 9;
8800 _recomHave -= 3;
8801 }
8802
8803 if (_recomHave < 0)
8804 _recomHave = 0;
8805
8806 // If we have to update last update time, but it's now before 13, we should set it to yesterday
8807 Calendar update = Calendar.getInstance();
8808 if (update.get(Calendar.HOUR_OF_DAY) < 13)
8809 update.add(Calendar.DAY_OF_MONTH, -1);
8810
8811 update.set(Calendar.HOUR_OF_DAY, 13);
8812 _lastRecomUpdate = update.getTimeInMillis();
8813 }
8814
8815 @Override
8816 public void doRevive()
8817 {
8818 super.doRevive();
8819
8820 stopEffects(L2EffectType.CHARMOFCOURAGE);
8821 sendPacket(new EtcStatusUpdate(this));
8822
8823 _reviveRequested = 0;
8824 _revivePower = 0;
8825
8826 if (isMounted())
8827 startFeed(_mountNpcId);
8828
8829 if (isInParty() && getParty().isInDimensionalRift())
8830 {
8831 if (!DimensionalRiftManager.getInstance().checkIfInPeaceZone(getX(), getY(), getZ()))
8832 getParty().getDimensionalRift().memberRessurected(this);
8833 }
8834
8835 // Schedule a paralyzed task to wait for the animation to finish
8836 ThreadPoolManager.getInstance().scheduleGeneral(new Runnable()
8837 {
8838 @Override
8839 public void run()
8840 {
8841 setIsParalyzed(false);
8842 }
8843 }, getAnimationTimer());
8844 setIsParalyzed(true);
8845 }
8846
8847 @Override
8848 public void doRevive(double revivePower)
8849 {
8850 // Restore the player's lost experience, depending on the % return of the skill used (based on its power).
8851 restoreExp(revivePower);
8852 doRevive();
8853 }
8854
8855 public void reviveRequest(L2PcInstance Reviver, L2Skill skill, boolean Pet)
8856 {
8857 if (_reviveRequested == 1)
8858 {
8859 // Resurrection has already been proposed.
8860 if (_revivePet == Pet)
8861 Reviver.sendPacket(SystemMessageId.RES_HAS_ALREADY_BEEN_PROPOSED);
8862 else
8863 {
8864 if (Pet)
8865 // A pet cannot be resurrected while it's owner is in the process of resurrecting.
8866 Reviver.sendPacket(SystemMessageId.CANNOT_RES_PET2);
8867 else
8868 // While a pet is attempting to resurrect, it cannot help in resurrecting its master.
8869 Reviver.sendPacket(SystemMessageId.MASTER_CANNOT_RES);
8870 }
8871 return;
8872 }
8873
8874 if ((Pet && getPet() != null && getPet().isDead()) || (!Pet && isDead()))
8875 {
8876 _reviveRequested = 1;
8877
8878 if (isPhoenixBlessed())
8879 _revivePower = 100;
8880 else if (isAffected(L2EffectFlag.CHARM_OF_COURAGE))
8881 _revivePower = 0;
8882 else
8883 _revivePower = Formulas.calculateSkillResurrectRestorePercent(skill.getPower(), Reviver);
8884
8885 _revivePet = Pet;
8886
8887 if (isAffected(L2EffectFlag.CHARM_OF_COURAGE))
8888 {
8889 sendPacket(new ConfirmDlg(SystemMessageId.DO_YOU_WANT_TO_BE_RESTORED).addTime(60000));
8890 return;
8891 }
8892
8893 sendPacket(new ConfirmDlg(SystemMessageId.RESSURECTION_REQUEST_BY_S1).addPcName(Reviver));
8894 }
8895 }
8896
8897 public void reviveAnswer(int answer)
8898 {
8899 if (_reviveRequested != 1 || (!isDead() && !_revivePet) || (_revivePet && getPet() != null && !getPet().isDead()))
8900 return;
8901
8902 if (answer == 0 && isPhoenixBlessed())
8903 stopPhoenixBlessing(null);
8904 else if (answer == 1)
8905 {
8906 if (!_revivePet)
8907 {
8908 if (_revivePower != 0)
8909 doRevive(_revivePower);
8910 else
8911 doRevive();
8912 }
8913 else if (getPet() != null)
8914 {
8915 if (_revivePower != 0)
8916 getPet().doRevive(_revivePower);
8917 else
8918 getPet().doRevive();
8919 }
8920 }
8921 _reviveRequested = 0;
8922 _revivePower = 0;
8923 }
8924
8925 public boolean isReviveRequested()
8926 {
8927 return (_reviveRequested == 1);
8928 }
8929
8930 public boolean isRevivingPet()
8931 {
8932 return _revivePet;
8933 }
8934
8935 public void removeReviving()
8936 {
8937 _reviveRequested = 0;
8938 _revivePower = 0;
8939 }
8940
8941 public void onActionRequest()
8942 {
8943 if (isSpawnProtected())
8944 {
8945 sendMessage("As you acted, you are no longer under spawn protection.");
8946 setProtection(false);
8947 }
8948 }
8949
8950 /**
8951 * @param expertiseIndex The expertiseIndex to set.
8952 */
8953 public void setExpertiseIndex(int expertiseIndex)
8954 {
8955 _expertiseIndex = expertiseIndex;
8956 }
8957
8958 /**
8959 * @return Returns the expertiseIndex.
8960 */
8961 public int getExpertiseIndex()
8962 {
8963 return _expertiseIndex;
8964 }
8965
8966 @Override
8967 public final void onTeleported()
8968 {
8969 super.onTeleported();
8970
8971 // Force a revalidation
8972 revalidateZone(true);
8973
8974 if (Config.PLAYER_SPAWN_PROTECTION > 0)
8975 setProtection(true);
8976
8977 // Stop toggles upon teleport.
8978 if (!isGM())
8979 stopAllToggles();
8980
8981 // Modify the position of the tamed beast if necessary
8982 if (getTrainedBeast() != null)
8983 {
8984 getTrainedBeast().getAI().stopFollow();
8985 getTrainedBeast().teleToLocation(getPosition().getX(), getPosition().getY(), getPosition().getZ(), 0);
8986 getTrainedBeast().getAI().startFollow(this);
8987 }
8988
8989 // Modify the position of the pet if necessary
8990 L2Summon pet = getPet();
8991 if (pet != null)
8992 {
8993 pet.setFollowStatus(false);
8994 pet.teleToLocation(getPosition().getX(), getPosition().getY(), getPosition().getZ(), 0);
8995 ((L2SummonAI) pet.getAI()).setStartFollowController(true);
8996 pet.setFollowStatus(true);
8997 }
8998 }
8999
9000 @Override
9001 public void addExpAndSp(long addToExp, int addToSp)
9002 {
9003 getStat().addExpAndSp(addToExp, addToSp);
9004 }
9005
9006 public void removeExpAndSp(long removeExp, int removeSp)
9007 {
9008 getStat().removeExpAndSp(removeExp, removeSp);
9009 }
9010
9011 @Override
9012 public void reduceCurrentHp(double value, L2Character attacker, boolean awake, boolean isDOT, L2Skill skill)
9013 {
9014 if (skill != null)
9015 getStatus().reduceHp(value, attacker, awake, isDOT, skill.isToggle(), skill.getDmgDirectlyToHP());
9016 else
9017 getStatus().reduceHp(value, attacker, awake, isDOT, false, false);
9018
9019 // notify the tamed beast of attacks
9020 if (getTrainedBeast() != null)
9021 getTrainedBeast().onOwnerGotAttacked(attacker);
9022 }
9023
9024 public synchronized void addBypass(String bypass)
9025 {
9026 if (bypass == null)
9027 return;
9028
9029 _validBypass.add(bypass);
9030 }
9031
9032 public synchronized void addBypass2(String bypass)
9033 {
9034 if (bypass == null)
9035 return;
9036
9037 _validBypass2.add(bypass);
9038 }
9039
9040 public synchronized boolean validateBypass(String cmd)
9041 {
9042 for (String bp : _validBypass)
9043 {
9044 if (bp == null)
9045 continue;
9046
9047 if (bp.equals(cmd))
9048 return true;
9049 }
9050
9051 for (String bp : _validBypass2)
9052 {
9053 if (bp == null)
9054 continue;
9055
9056 if (cmd.startsWith(bp))
9057 return true;
9058 }
9059
9060 return false;
9061 }
9062
9063 /**
9064 * Test multiple cases where the item shouldn't be able to manipulate.
9065 * @param objectId : The item objectId.
9066 * @return true if it the item can be manipulated, false ovtherwise.
9067 */
9068 public boolean validateItemManipulation(int objectId)
9069 {
9070 final ItemInstance item = getInventory().getItemByObjectId(objectId);
9071
9072 // You don't own the item, or item is null.
9073 if (item == null || item.getOwnerId() != getObjectId())
9074 return false;
9075
9076 // Pet whom item you try to manipulate is summoned/mounted.
9077 if (getPet() != null && getPet().getControlItemId() == objectId || getMountObjectID() == objectId)
9078 return false;
9079
9080 if (getActiveEnchantItem() != null && getActiveEnchantItem().getObjectId() == objectId)
9081 return false;
9082
9083 // Can't trade a cursed weapon.
9084 if (CursedWeaponsManager.getInstance().isCursed(item.getItemId()))
9085 return false;
9086
9087 return true;
9088 }
9089
9090 public synchronized void clearBypass()
9091 {
9092 _validBypass.clear();
9093 _validBypass2.clear();
9094 }
9095
9096 /**
9097 * @return Returns the inBoat.
9098 */
9099 public boolean isInBoat()
9100 {
9101 return _vehicle != null && _vehicle.isBoat();
9102 }
9103
9104 public L2BoatInstance getBoat()
9105 {
9106 return (L2BoatInstance) _vehicle;
9107 }
9108
9109 public L2Vehicle getVehicle()
9110 {
9111 return _vehicle;
9112 }
9113
9114 public void setVehicle(L2Vehicle v)
9115 {
9116 if (v == null && _vehicle != null)
9117 _vehicle.removePassenger(this);
9118
9119 _vehicle = v;
9120 }
9121
9122 public void setInCrystallize(boolean inCrystallize)
9123 {
9124 _inCrystallize = inCrystallize;
9125 }
9126
9127 public boolean isInCrystallize()
9128 {
9129 return _inCrystallize;
9130 }
9131
9132 public Location getInVehiclePosition()
9133 {
9134 return _inVehiclePosition;
9135 }
9136
9137 public void setInVehiclePosition(Location pt)
9138 {
9139 _inVehiclePosition = pt;
9140 }
9141
9142 /**
9143 * Manage the delete task of a L2PcInstance (Leave Party, Unsummon pet, Save its inventory in the database, Remove it from the world...).
9144 * <ul>
9145 * <li>If the L2PcInstance is in observer mode, set its position to its position before entering in observer mode</li>
9146 * <li>Set the online Flag to True or False and update the characters table of the database with online status and lastAccess</li>
9147 * <li>Stop the HP/MP/CP Regeneration task</li>
9148 * <li>Cancel Crafting, Attak or Cast</li>
9149 * <li>Remove the L2PcInstance from the world</li>
9150 * <li>Stop Party and Unsummon Pet</li>
9151 * <li>Update database with items in its inventory and remove them from the world</li>
9152 * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI</li>
9153 * <li>Close the connection with the client</li>
9154 * </ul>
9155 */
9156 @Override
9157 public void deleteMe()
9158 {
9159 cleanup();
9160 store();
9161 super.deleteMe();
9162 }
9163
9164 private synchronized void cleanup()
9165 {
9166 try
9167 {
9168 // Put the online status to false
9169 setOnlineStatus(false, true);
9170
9171 // abort cast & attack and remove the target. Cancels movement aswell.
9172 abortAttack();
9173 abortCast();
9174 stopMove(null);
9175 setTarget(null);
9176
9177 PartyMatchWaitingList.getInstance().removePlayer(this);
9178 if (_partyroom != 0)
9179 {
9180 PartyMatchRoom room = PartyMatchRoomList.getInstance().getRoom(_partyroom);
9181 if (room != null)
9182 room.deleteMember(this);
9183 }
9184
9185 if (isFlying())
9186 removeSkill(SkillTable.getInstance().getInfo(4289, 1));
9187
9188 // Stop all scheduled tasks
9189 stopAllTimers();
9190
9191 // Cancel the cast of eventual fusion skill users on this target.
9192 for (L2Character character : getKnownList().getKnownType(L2Character.class))
9193 if (character.getFusionSkill() != null && character.getFusionSkill().getTarget() == this)
9194 character.abortCast();
9195
9196 // Stop signets & toggles effects.
9197 for (L2Effect effect : getAllEffects())
9198 {
9199 if (effect.getSkill().isToggle())
9200 {
9201 effect.exit();
9202 continue;
9203 }
9204
9205 switch (effect.getEffectType())
9206 {
9207 case SIGNET_GROUND:
9208 case SIGNET_EFFECT:
9209 effect.exit();
9210 break;
9211 }
9212 }
9213
9214 // Remove the L2PcInstance from the world
9215 decayMe();
9216
9217 // Remove from world regions zones
9218 L2WorldRegion oldRegion = getWorldRegion();
9219 if (oldRegion != null)
9220 oldRegion.removeFromZones(this);
9221
9222 // If a party is in progress, leave it
9223 if (isInParty())
9224 leaveParty();
9225
9226 // If the L2PcInstance has Pet, unsummon it
9227 if (getPet() != null)
9228 getPet().unSummon(this);
9229
9230 // Handle removal from olympiad game
9231 if (OlympiadManager.getInstance().isRegistered(this) || getOlympiadGameId() != -1)
9232 OlympiadManager.getInstance().removeDisconnectedCompetitor(this);
9233
9234 // set the status for pledge member list to OFFLINE
9235 if (getClan() != null)
9236 {
9237 L2ClanMember clanMember = getClan().getClanMember(getObjectId());
9238 if (clanMember != null)
9239 clanMember.setPlayerInstance(null);
9240 }
9241
9242 // deals with sudden exit in the middle of transaction
9243 if (getActiveRequester() != null)
9244 {
9245 setActiveRequester(null);
9246 cancelActiveTrade();
9247 }
9248
9249 // If the L2PcInstance is a GM, remove it from the GM List
9250 if (isGM())
9251 GmListTable.getInstance().deleteGm(this);
9252
9253 // Check if the L2PcInstance is in observer mode to set its position to its position
9254 // before entering in observer mode
9255 if (inObserverMode())
9256 setXYZInvisible(_savedLocation.getX(), _savedLocation.getY(), _savedLocation.getZ());
9257
9258 // Oust player from boat
9259 if (getVehicle() != null)
9260 getVehicle().oustPlayer(this, true);
9261
9262 // Update inventory and remove them from the world
9263 getInventory().deleteMe();
9264
9265 // Update warehouse and remove them from the world
9266 clearWarehouse();
9267
9268 // Update freight and remove them from the world
9269 clearFreight();
9270 clearDepositedFreight();
9271
9272 if (isCursedWeaponEquipped())
9273 CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquippedId).setPlayer(null);
9274
9275 // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI
9276 getKnownList().removeAllKnownObjects();
9277
9278 if (getClanId() > 0)
9279 getClan().broadcastToOtherOnlineMembers(new PledgeShowMemberListUpdate(this), this);
9280
9281 if (isSeated())
9282 {
9283 final L2Object obj = L2World.getInstance().getObject(getMountObjectID());
9284 ((L2StaticObjectInstance) obj).setBusy(false);
9285 }
9286
9287 // Remove L2Object object from _allObjects of L2World
9288 L2World.getInstance().removeObject(this);
9289 L2World.getInstance().removePlayer(this); // force remove in case of crash during teleport
9290
9291 // friends & blocklist update
9292 notifyFriends(false);
9293 getBlockList().playerLogout();
9294 }
9295 catch (Exception e)
9296 {
9297 _log.log(Level.WARNING, "Exception on deleteMe()" + e.getMessage(), e);
9298 }
9299 }
9300
9301 public void startFishing(Location loc)
9302 {
9303 stopMove(null);
9304 setIsImmobilized(true);
9305
9306 _fishingLoc = loc;
9307
9308 // Starts fishing
9309 int group = getRandomGroup();
9310
9311 _fish = FishTable.getFish(getRandomFishLvl(), getRandomFishType(group), group);
9312 if (_fish == null)
9313 {
9314 endFishing(false);
9315 return;
9316 }
9317
9318 sendPacket(SystemMessageId.CAST_LINE_AND_START_FISHING);
9319
9320 broadcastPacket(new ExFishingStart(this, _fish.getType(_lure.isNightLure()), loc, _lure.isNightLure()));
9321 sendPacket(new PlaySound(1, "SF_P_01", 0, 0, 0, 0, 0));
9322 startLookingForFishTask();
9323 }
9324
9325 public void stopLookingForFishTask()
9326 {
9327 if (_taskforfish != null)
9328 {
9329 _taskforfish.cancel(false);
9330 _taskforfish = null;
9331 }
9332 }
9333
9334 public void startLookingForFishTask()
9335 {
9336 if (!isDead() && _taskforfish == null)
9337 {
9338 int checkDelay = 0;
9339 boolean isNoob = false;
9340 boolean isUpperGrade = false;
9341
9342 if (_lure != null)
9343 {
9344 int lureid = _lure.getItemId();
9345 isNoob = _fish.getGroup() == 0;
9346 isUpperGrade = _fish.getGroup() == 2;
9347 if (lureid == 6519 || lureid == 6522 || lureid == 6525 || lureid == 8505 || lureid == 8508 || lureid == 8511) // low grade
9348 checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (1.33)));
9349 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
9350 checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (1.00)));
9351 else if (lureid == 6521 || lureid == 6524 || lureid == 6527 || lureid == 8507 || lureid == 8510 || lureid == 8513) // high grade
9352 checkDelay = Math.round((float) (_fish.getGutsCheckTime() * (0.66)));
9353 }
9354 _taskforfish = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new LookingForFishTask(_fish.getWaitTime(), _fish.getFishGuts(), _fish.getType(_lure.isNightLure()), isNoob, isUpperGrade), 10000, checkDelay);
9355 }
9356 }
9357
9358 private int getRandomGroup()
9359 {
9360 switch (_lure.getItemId())
9361 {
9362 case 7807: // green for beginners
9363 case 7808: // purple for beginners
9364 case 7809: // yellow for beginners
9365 case 8486: // prize-winning for beginners
9366 return 0;
9367
9368 case 8485: // prize-winning luminous
9369 case 8506: // green luminous
9370 case 8509: // purple luminous
9371 case 8512: // yellow luminous
9372 return 2;
9373
9374 default:
9375 return 1;
9376 }
9377 }
9378
9379 private int getRandomFishType(int group)
9380 {
9381 int check = Rnd.get(100);
9382 int type = 1;
9383 switch (group)
9384 {
9385 case 0: // fish for novices
9386 switch (_lure.getItemId())
9387 {
9388 case 7807: // green lure, preferred by fast-moving (nimble) fish (type 5)
9389 if (check <= 54)
9390 type = 5;
9391 else if (check <= 77)
9392 type = 4;
9393 else
9394 type = 6;
9395 break;
9396
9397 case 7808: // purple lure, preferred by fat fish (type 4)
9398 if (check <= 54)
9399 type = 4;
9400 else if (check <= 77)
9401 type = 6;
9402 else
9403 type = 5;
9404 break;
9405
9406 case 7809: // yellow lure, preferred by ugly fish (type 6)
9407 if (check <= 54)
9408 type = 6;
9409 else if (check <= 77)
9410 type = 5;
9411 else
9412 type = 4;
9413 break;
9414
9415 case 8486: // prize-winning fishing lure for beginners
9416 if (check <= 33)
9417 type = 4;
9418 else if (check <= 66)
9419 type = 5;
9420 else
9421 type = 6;
9422 break;
9423 }
9424 break;
9425
9426 case 1: // normal fish
9427 switch (_lure.getItemId())
9428 {
9429 case 7610:
9430 case 7611:
9431 case 7612:
9432 case 7613:
9433 type = 3;
9434 break;
9435
9436 case 6519: // all theese lures (green) are prefered by fast-moving (nimble) fish (type 1)
9437 case 8505:
9438 case 6520:
9439 case 6521:
9440 case 8507:
9441 if (check <= 54)
9442 type = 1;
9443 else if (check <= 74)
9444 type = 0;
9445 else if (check <= 94)
9446 type = 2;
9447 else
9448 type = 3;
9449 break;
9450
9451 case 6522: // all theese lures (purple) are prefered by fat fish (type 0)
9452 case 8508:
9453 case 6523:
9454 case 6524:
9455 case 8510:
9456 if (check <= 54)
9457 type = 0;
9458 else if (check <= 74)
9459 type = 1;
9460 else if (check <= 94)
9461 type = 2;
9462 else
9463 type = 3;
9464 break;
9465
9466 case 6525: // all theese lures (yellow) are prefered by ugly fish (type 2)
9467 case 8511:
9468 case 6526:
9469 case 6527:
9470 case 8513:
9471 if (check <= 55)
9472 type = 2;
9473 else if (check <= 74)
9474 type = 1;
9475 else if (check <= 94)
9476 type = 0;
9477 else
9478 type = 3;
9479 break;
9480 case 8484: // prize-winning fishing lure
9481 if (check <= 33)
9482 type = 0;
9483 else if (check <= 66)
9484 type = 1;
9485 else
9486 type = 2;
9487 break;
9488 }
9489 break;
9490
9491 case 2: // upper grade fish, luminous lure
9492 switch (_lure.getItemId())
9493 {
9494 case 8506: // green lure, preferred by fast-moving (nimble) fish (type 8)
9495 if (check <= 54)
9496 type = 8;
9497 else if (check <= 77)
9498 type = 7;
9499 else
9500 type = 9;
9501 break;
9502
9503 case 8509: // purple lure, preferred by fat fish (type 7)
9504 if (check <= 54)
9505 type = 7;
9506 else if (check <= 77)
9507 type = 9;
9508 else
9509 type = 8;
9510 break;
9511
9512 case 8512: // yellow lure, preferred by ugly fish (type 9)
9513 if (check <= 54)
9514 type = 9;
9515 else if (check <= 77)
9516 type = 8;
9517 else
9518 type = 7;
9519 break;
9520
9521 case 8485: // prize-winning fishing lure
9522 if (check <= 33)
9523 type = 7;
9524 else if (check <= 66)
9525 type = 8;
9526 else
9527 type = 9;
9528 break;
9529 }
9530 }
9531 return type;
9532 }
9533
9534 private int getRandomFishLvl()
9535 {
9536 int skilllvl = getSkillLevel(1315);
9537
9538 final L2Effect e = getFirstEffect(2274);
9539 if (e != null)
9540 skilllvl = (int) e.getSkill().getPower();
9541
9542 if (skilllvl <= 0)
9543 return 1;
9544
9545 int randomlvl;
9546
9547 final int check = Rnd.get(100);
9548 if (check <= 50)
9549 randomlvl = skilllvl;
9550 else if (check <= 85)
9551 {
9552 randomlvl = skilllvl - 1;
9553 if (randomlvl <= 0)
9554 randomlvl = 1;
9555 }
9556 else
9557 {
9558 randomlvl = skilllvl + 1;
9559 if (randomlvl > 27)
9560 randomlvl = 27;
9561 }
9562 return randomlvl;
9563 }
9564
9565 public void startFishCombat(boolean isNoob, boolean isUpperGrade)
9566 {
9567 _fishCombat = new L2Fishing(this, _fish, isNoob, isUpperGrade, _lure.getItemId());
9568 }
9569
9570 public void endFishing(boolean win)
9571 {
9572 if (_fishCombat == null)
9573 sendPacket(SystemMessageId.BAIT_LOST_FISH_GOT_AWAY);
9574 else
9575 _fishCombat = null;
9576
9577 _lure = null;
9578 _fishingLoc = null;
9579
9580 // Ends fishing
9581 broadcastPacket(new ExFishingEnd(win, getObjectId()));
9582 sendPacket(SystemMessageId.REEL_LINE_AND_STOP_FISHING);
9583 setIsImmobilized(false);
9584 stopLookingForFishTask();
9585 }
9586
9587 public L2Fishing getFishCombat()
9588 {
9589 return _fishCombat;
9590 }
9591
9592 public Location getFishingLoc()
9593 {
9594 return _fishingLoc;
9595 }
9596
9597 public void setLure(ItemInstance lure)
9598 {
9599 _lure = lure;
9600 }
9601
9602 public ItemInstance getLure()
9603 {
9604 return _lure;
9605 }
9606
9607 public int getInventoryLimit()
9608 {
9609 return ((getRace() == Race.Dwarf) ? Config.INVENTORY_MAXIMUM_DWARF : Config.INVENTORY_MAXIMUM_NO_DWARF) + (int) getStat().calcStat(Stats.INV_LIM, 0, null, null);
9610 }
9611
9612 public static int getQuestInventoryLimit()
9613 {
9614 return Config.INVENTORY_MAXIMUM_QUEST_ITEMS;
9615 }
9616
9617 public int getWareHouseLimit()
9618 {
9619 return ((getRace() == Race.Dwarf) ? Config.WAREHOUSE_SLOTS_DWARF : Config.WAREHOUSE_SLOTS_NO_DWARF) + (int) getStat().calcStat(Stats.WH_LIM, 0, null, null);
9620 }
9621
9622 public int getPrivateSellStoreLimit()
9623 {
9624 return ((getRace() == Race.Dwarf) ? Config.MAX_PVTSTORE_SLOTS_DWARF : Config.MAX_PVTSTORE_SLOTS_OTHER) + (int) getStat().calcStat(Stats.P_SELL_LIM, 0, null, null);
9625 }
9626
9627 public int getPrivateBuyStoreLimit()
9628 {
9629 return ((getRace() == Race.Dwarf) ? Config.MAX_PVTSTORE_SLOTS_DWARF : Config.MAX_PVTSTORE_SLOTS_OTHER) + (int) getStat().calcStat(Stats.P_BUY_LIM, 0, null, null);
9630 }
9631
9632 public int getFreightLimit()
9633 {
9634 return Config.FREIGHT_SLOTS + (int) getStat().calcStat(Stats.FREIGHT_LIM, 0, null, null);
9635 }
9636
9637 public int getDwarfRecipeLimit()
9638 {
9639 return Config.DWARF_RECIPE_LIMIT + (int) getStat().calcStat(Stats.REC_D_LIM, 0, null, null);
9640 }
9641
9642 public int getCommonRecipeLimit()
9643 {
9644 return Config.COMMON_RECIPE_LIMIT + (int) getStat().calcStat(Stats.REC_C_LIM, 0, null, null);
9645 }
9646
9647 public int getMountNpcId()
9648 {
9649 return _mountNpcId;
9650 }
9651
9652 public int getMountLevel()
9653 {
9654 return _mountLevel;
9655 }
9656
9657 public void setMountObjectID(int newID)
9658 {
9659 _mountObjectID = newID;
9660 }
9661
9662 public int getMountObjectID()
9663 {
9664 return _mountObjectID;
9665 }
9666
9667 /**
9668 * @return the current player skill in use.
9669 */
9670 public SkillUseHolder getCurrentSkill()
9671 {
9672 return _currentSkill;
9673 }
9674
9675 /**
9676 * Update the _currentSkill holder.
9677 * @param skill : The skill to update for (or null)
9678 * @param ctrlPressed : The boolean information regarding ctrl key.
9679 * @param shiftPressed : The boolean information regarding shift key.
9680 */
9681 public void setCurrentSkill(L2Skill skill, boolean ctrlPressed, boolean shiftPressed)
9682 {
9683 _currentSkill.setSkill(skill);
9684 _currentSkill.setCtrlPressed(ctrlPressed);
9685 _currentSkill.setShiftPressed(shiftPressed);
9686 }
9687
9688 /**
9689 * @return the current pet skill in use.
9690 */
9691 public SkillUseHolder getCurrentPetSkill()
9692 {
9693 return _currentPetSkill;
9694 }
9695
9696 /**
9697 * Update the _currentPetSkill holder.
9698 * @param skill : The skill to update for (or null)
9699 * @param ctrlPressed : The boolean information regarding ctrl key.
9700 * @param shiftPressed : The boolean information regarding shift key.
9701 */
9702 public void setCurrentPetSkill(L2Skill skill, boolean ctrlPressed, boolean shiftPressed)
9703 {
9704 _currentPetSkill.setSkill(skill);
9705 _currentPetSkill.setCtrlPressed(ctrlPressed);
9706 _currentPetSkill.setShiftPressed(shiftPressed);
9707 }
9708
9709 /**
9710 * @return the current queued skill in use.
9711 */
9712 public SkillUseHolder getQueuedSkill()
9713 {
9714 return _queuedSkill;
9715 }
9716
9717 /**
9718 * Update the _queuedSkill holder.
9719 * @param skill : The skill to update for (or null)
9720 * @param ctrlPressed : The boolean information regarding ctrl key.
9721 * @param shiftPressed : The boolean information regarding shift key.
9722 */
9723 public void setQueuedSkill(L2Skill skill, boolean ctrlPressed, boolean shiftPressed)
9724 {
9725 _queuedSkill.setSkill(skill);
9726 _queuedSkill.setCtrlPressed(ctrlPressed);
9727 _queuedSkill.setShiftPressed(shiftPressed);
9728 }
9729
9730 /**
9731 * @return the timer to delay animation tasks, based on run speed.
9732 */
9733 public int getAnimationTimer()
9734 {
9735 return Math.max(1000, 5000 - getRunSpeed() * 20);
9736 }
9737
9738 /**
9739 * @return punishment level of player
9740 */
9741 public PunishLevel getPunishLevel()
9742 {
9743 return _punishLevel;
9744 }
9745
9746 /**
9747 * @return True if player is jailed
9748 */
9749 public boolean isInJail()
9750 {
9751 return _punishLevel == PunishLevel.JAIL;
9752 }
9753
9754 /**
9755 * @return True if player is chat banned
9756 */
9757 public boolean isChatBanned()
9758 {
9759 return _punishLevel == PunishLevel.CHAT;
9760 }
9761
9762 public void setPunishLevel(int state)
9763 {
9764 switch (state)
9765 {
9766 case 0:
9767 _punishLevel = PunishLevel.NONE;
9768 break;
9769 case 1:
9770 _punishLevel = PunishLevel.CHAT;
9771 break;
9772 case 2:
9773 _punishLevel = PunishLevel.JAIL;
9774 break;
9775 case 3:
9776 _punishLevel = PunishLevel.CHAR;
9777 break;
9778 case 4:
9779 _punishLevel = PunishLevel.ACC;
9780 break;
9781 }
9782 }
9783
9784 /**
9785 * Sets punish level for player based on delay
9786 * @param state
9787 * @param delayInMinutes -- 0 for infinite
9788 */
9789 public void setPunishLevel(PunishLevel state, int delayInMinutes)
9790 {
9791 long delayInMilliseconds = delayInMinutes * 60000L;
9792 switch (state)
9793 {
9794 case NONE: // Remove Punishments
9795 {
9796 switch (_punishLevel)
9797 {
9798 case CHAT:
9799 {
9800 _punishLevel = state;
9801 stopPunishTask(true);
9802 sendPacket(new EtcStatusUpdate(this));
9803 sendMessage("Chatting is now available.");
9804 sendPacket(new PlaySound("systemmsg_e.345"));
9805 break;
9806 }
9807 case JAIL:
9808 {
9809 _punishLevel = state;
9810
9811 // Open a Html message to inform the player
9812 final NpcHtmlMessage html = new NpcHtmlMessage(0);
9813 html.setFile("data/html/jail_out.htm");
9814 sendPacket(html);
9815
9816 stopPunishTask(true);
9817 teleToLocation(17836, 170178, -3507, 20); // Floran village
9818 break;
9819 }
9820 }
9821 break;
9822 }
9823 case CHAT: // Chat ban
9824 {
9825 // not allow player to escape jail using chat ban
9826 if (_punishLevel == PunishLevel.JAIL)
9827 break;
9828
9829 _punishLevel = state;
9830 _punishTimer = 0;
9831 sendPacket(new EtcStatusUpdate(this));
9832
9833 // Remove the task if any
9834 stopPunishTask(false);
9835
9836 if (delayInMinutes > 0)
9837 {
9838 _punishTimer = delayInMilliseconds;
9839
9840 // start the countdown
9841 _punishTask = ThreadPoolManager.getInstance().scheduleGeneral(new PunishTask(), _punishTimer);
9842 sendMessage("Chatting has been suspended for " + delayInMinutes + " minute(s).");
9843 }
9844 else
9845 sendMessage("Chatting has been suspended.");
9846
9847 // Send same sound packet in both "delay" cases.
9848 sendPacket(new PlaySound("systemmsg_e.346"));
9849 break;
9850
9851 }
9852 case JAIL: // Jail Player
9853 {
9854 _punishLevel = state;
9855 _punishTimer = 0;
9856
9857 // Remove the task if any
9858 stopPunishTask(false);
9859
9860 if (delayInMinutes > 0)
9861 {
9862 _punishTimer = delayInMilliseconds;
9863
9864 // start the countdown
9865 _punishTask = ThreadPoolManager.getInstance().scheduleGeneral(new PunishTask(), _punishTimer);
9866 sendMessage("You are jailed for " + delayInMinutes + " minutes.");
9867 }
9868
9869 if (OlympiadManager.getInstance().isRegisteredInComp(this))
9870 OlympiadManager.getInstance().removeDisconnectedCompetitor(this);
9871
9872 // Open a Html message to inform the player
9873 final NpcHtmlMessage html = new NpcHtmlMessage(0);
9874 html.setFile("data/html/jail_in.htm");
9875 sendPacket(html);
9876
9877 setIsIn7sDungeon(false);
9878 teleToLocation(-114356, -249645, -2984, 0); // Jail
9879 break;
9880 }
9881 case CHAR: // Ban Character
9882 {
9883 setAccessLevel(-100);
9884 logout();
9885 break;
9886 }
9887 case ACC: // Ban Account
9888 {
9889 setAccountAccesslevel(-100);
9890 logout();
9891 break;
9892 }
9893 default:
9894 {
9895 _punishLevel = state;
9896 break;
9897 }
9898 }
9899
9900 // store in database
9901 storeCharBase();
9902 }
9903
9904 public long getPunishTimer()
9905 {
9906 return _punishTimer;
9907 }
9908
9909 public void setPunishTimer(long time)
9910 {
9911 _punishTimer = time;
9912 }
9913
9914 private void updatePunishState()
9915 {
9916 if (getPunishLevel() != PunishLevel.NONE)
9917 {
9918 // If punish timer exists, restart punishtask.
9919 if (_punishTimer > 0)
9920 {
9921 _punishTask = ThreadPoolManager.getInstance().scheduleGeneral(new PunishTask(), _punishTimer);
9922 sendMessage("You are still " + getPunishLevel().string() + " for " + Math.round(_punishTimer / 60000f) + " minutes.");
9923 }
9924 if (getPunishLevel() == PunishLevel.JAIL)
9925 {
9926 // If player escaped, put him back in jail
9927 if (!isInsideZone(ZoneId.JAIL))
9928 teleToLocation(-114356, -249645, -2984, 20);
9929 }
9930 }
9931 }
9932
9933 public void stopPunishTask(boolean save)
9934 {
9935 if (_punishTask != null)
9936 {
9937 if (save)
9938 {
9939 long delay = _punishTask.getDelay(TimeUnit.MILLISECONDS);
9940 if (delay < 0)
9941 delay = 0;
9942 setPunishTimer(delay);
9943 }
9944 _punishTask.cancel(false);
9945 _punishTask = null;
9946 }
9947 }
9948
9949 protected class PunishTask implements Runnable
9950 {
9951 @Override
9952 public void run()
9953 {
9954 setPunishLevel(PunishLevel.NONE, 0);
9955 }
9956 }
9957
9958 public int getPowerGrade()
9959 {
9960 return _powerGrade;
9961 }
9962
9963 public void setPowerGrade(int power)
9964 {
9965 _powerGrade = power;
9966 }
9967
9968 public boolean isCursedWeaponEquipped()
9969 {
9970 return _cursedWeaponEquippedId != 0;
9971 }
9972
9973 public void setCursedWeaponEquippedId(int value)
9974 {
9975 _cursedWeaponEquippedId = value;
9976 }
9977
9978 public int getCursedWeaponEquippedId()
9979 {
9980 return _cursedWeaponEquippedId;
9981 }
9982
9983 public void shortBuffStatusUpdate(int magicId, int level, int time)
9984 {
9985 if (_shortBuffTask != null)
9986 {
9987 _shortBuffTask.cancel(false);
9988 _shortBuffTask = null;
9989 }
9990 _shortBuffTask = ThreadPoolManager.getInstance().scheduleGeneral(new ShortBuffTask(), time * 1000);
9991 setShortBuffTaskSkillId(magicId);
9992
9993 sendPacket(new ShortBuffStatusUpdate(magicId, level, time));
9994 }
9995
9996 public int getShortBuffTaskSkillId()
9997 {
9998 return _shortBuffTaskSkillId;
9999 }
10000
10001 public void setShortBuffTaskSkillId(int id)
10002 {
10003 _shortBuffTaskSkillId = id;
10004 }
10005
10006 public int getDeathPenaltyBuffLevel()
10007 {
10008 return _deathPenaltyBuffLevel;
10009 }
10010
10011 public void setDeathPenaltyBuffLevel(int level)
10012 {
10013 _deathPenaltyBuffLevel = level;
10014 }
10015
10016 public void calculateDeathPenaltyBuffLevel(L2Character killer)
10017 {
10018 if (_deathPenaltyBuffLevel >= 15) // maximum level reached
10019 return;
10020
10021 if ((getKarma() > 0 || Rnd.get(1, 100) <= Config.DEATH_PENALTY_CHANCE) && !(killer instanceof L2PcInstance) && !isGM() && !(getCharmOfLuck() && (killer == null || killer.isRaid())) && !isPhoenixBlessed() && !(isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.SIEGE)))
10022 {
10023 if (_deathPenaltyBuffLevel != 0)
10024 {
10025 final L2Skill skill = SkillTable.getInstance().getInfo(5076, _deathPenaltyBuffLevel);
10026 if (skill != null)
10027 removeSkill(skill, true);
10028 }
10029
10030 _deathPenaltyBuffLevel++;
10031
10032 addSkill(SkillTable.getInstance().getInfo(5076, _deathPenaltyBuffLevel), false);
10033 sendPacket(new EtcStatusUpdate(this));
10034 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DEATH_PENALTY_LEVEL_S1_ADDED).addNumber(_deathPenaltyBuffLevel));
10035 }
10036 }
10037
10038 public void reduceDeathPenaltyBuffLevel()
10039 {
10040 if (_deathPenaltyBuffLevel <= 0)
10041 return;
10042
10043 final L2Skill skill = SkillTable.getInstance().getInfo(5076, _deathPenaltyBuffLevel);
10044 if (skill != null)
10045 removeSkill(skill, true);
10046
10047 _deathPenaltyBuffLevel--;
10048
10049 if (_deathPenaltyBuffLevel > 0)
10050 {
10051 addSkill(SkillTable.getInstance().getInfo(5076, _deathPenaltyBuffLevel), false);
10052 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.DEATH_PENALTY_LEVEL_S1_ADDED).addNumber(_deathPenaltyBuffLevel));
10053 }
10054 else
10055 sendPacket(SystemMessageId.DEATH_PENALTY_LIFTED);
10056
10057 sendPacket(new EtcStatusUpdate(this));
10058 }
10059
10060 public void restoreDeathPenaltyBuffLevel()
10061 {
10062 if (_deathPenaltyBuffLevel > 0)
10063 addSkill(SkillTable.getInstance().getInfo(5076, _deathPenaltyBuffLevel), false);
10064 }
10065
10066 private final Map<Integer, TimeStamp> _reuseTimeStamps = new ConcurrentHashMap<>();
10067
10068 public Collection<TimeStamp> getReuseTimeStamps()
10069 {
10070 return _reuseTimeStamps.values();
10071 }
10072
10073 public Map<Integer, TimeStamp> getReuseTimeStamp()
10074 {
10075 return _reuseTimeStamps;
10076 }
10077
10078 /**
10079 * Simple class containing all neccessary information to maintain valid timestamps and reuse for skills upon relog. Filter this carefully as it becomes redundant to store reuse for small delays.
10080 * @author Yesod
10081 */
10082 public static class TimeStamp
10083 {
10084 private final int _skillId;
10085 private final int _skillLvl;
10086 private final long _reuse;
10087 private final long _stamp;
10088
10089 public TimeStamp(L2Skill skill, long reuse)
10090 {
10091 _skillId = skill.getId();
10092 _skillLvl = skill.getLevel();
10093 _reuse = reuse;
10094 _stamp = System.currentTimeMillis() + reuse;
10095 }
10096
10097 public TimeStamp(L2Skill skill, long reuse, long systime)
10098 {
10099 _skillId = skill.getId();
10100 _skillLvl = skill.getLevel();
10101 _reuse = reuse;
10102 _stamp = systime;
10103 }
10104
10105 public long getStamp()
10106 {
10107 return _stamp;
10108 }
10109
10110 public int getSkillId()
10111 {
10112 return _skillId;
10113 }
10114
10115 public int getSkillLvl()
10116 {
10117 return _skillLvl;
10118 }
10119
10120 public long getReuse()
10121 {
10122 return _reuse;
10123 }
10124
10125 public long getRemaining()
10126 {
10127 return Math.max(_stamp - System.currentTimeMillis(), 0);
10128 }
10129
10130 public boolean hasNotPassed()
10131 {
10132 return System.currentTimeMillis() < _stamp;
10133 }
10134 }
10135
10136 /**
10137 * Index according to skill id the current timestamp of use.
10138 * @param skill
10139 * @param reuse delay
10140 */
10141 @Override
10142 public void addTimeStamp(L2Skill skill, long reuse)
10143 {
10144 _reuseTimeStamps.put(skill.getReuseHashCode(), new TimeStamp(skill, reuse));
10145 }
10146
10147 /**
10148 * Index according to skill this TimeStamp instance for restoration purposes only.
10149 * @param skill
10150 * @param reuse
10151 * @param systime
10152 */
10153 public void addTimeStamp(L2Skill skill, long reuse, long systime)
10154 {
10155 _reuseTimeStamps.put(skill.getReuseHashCode(), new TimeStamp(skill, reuse, systime));
10156 }
10157
10158 @Override
10159 public L2PcInstance getActingPlayer()
10160 {
10161 return this;
10162 }
10163
10164 @Override
10165 public final void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss)
10166 {
10167 // Check if hit is missed
10168 if (miss)
10169 {
10170 sendPacket(SystemMessageId.MISSED_TARGET);
10171 return;
10172 }
10173
10174 // Check if hit is critical
10175 if (pcrit)
10176 sendPacket(SystemMessageId.CRITICAL_HIT);
10177 if (mcrit)
10178 sendPacket(SystemMessageId.CRITICAL_HIT_MAGIC);
10179
10180 if (target.isInvul())
10181 {
10182 if (target.isParalyzed())
10183 sendPacket(SystemMessageId.OPPONENT_PETRIFIED);
10184 else
10185 sendPacket(SystemMessageId.ATTACK_WAS_BLOCKED);
10186 }
10187 else
10188 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_DID_S1_DMG).addNumber(damage));
10189
10190 if (isInOlympiadMode() && target instanceof L2PcInstance && ((L2PcInstance) target).isInOlympiadMode() && ((L2PcInstance) target).getOlympiadGameId() == getOlympiadGameId())
10191 OlympiadGameManager.getInstance().notifyCompetitorDamage(this, damage);
10192 }
10193
10194 public void checkItemRestriction()
10195 {
10196 for (int i = 0; i < Inventory.PAPERDOLL_TOTALSLOTS; i++)
10197 {
10198 ItemInstance equippedItem = getInventory().getPaperdollItem(i);
10199 if (equippedItem != null && !equippedItem.getItem().checkCondition(this, this, false))
10200 {
10201 getInventory().unEquipItemInSlot(i);
10202
10203 InventoryUpdate iu = new InventoryUpdate();
10204 iu.addModifiedItem(equippedItem);
10205 sendPacket(iu);
10206
10207 SystemMessage sm = null;
10208 if (equippedItem.getEnchantLevel() > 0)
10209 {
10210 sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPMENT_S1_S2_REMOVED);
10211 sm.addNumber(equippedItem.getEnchantLevel());
10212 sm.addItemName(equippedItem);
10213 }
10214 else
10215 {
10216 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISARMED);
10217 sm.addItemName(equippedItem);
10218 }
10219 sendPacket(sm);
10220 }
10221 }
10222 }
10223
10224 protected class Dismount implements Runnable
10225 {
10226 @Override
10227 public void run()
10228 {
10229 try
10230 {
10231 dismount();
10232 }
10233 catch (Exception e)
10234 {
10235 _log.log(Level.WARNING, "Exception on dismount(): " + e.getMessage(), e);
10236 }
10237 }
10238 }
10239
10240 public void enteredNoLanding(int delay)
10241 {
10242 _dismountTask = ThreadPoolManager.getInstance().scheduleGeneral(new Dismount(), delay * 1000);
10243 }
10244
10245 public void exitedNoLanding()
10246 {
10247 if (_dismountTask != null)
10248 {
10249 _dismountTask.cancel(true);
10250 _dismountTask = null;
10251 }
10252 }
10253
10254 public void setIsInSiege(boolean b)
10255 {
10256 _isInSiege = b;
10257 }
10258
10259 public boolean isInSiege()
10260 {
10261 return _isInSiege;
10262 }
10263
10264 /**
10265 * Remove player from BossZones (used on char logout/exit)
10266 */
10267 public void removeFromBossZone()
10268 {
10269 try
10270 {
10271 for (L2BossZone _zone : GrandBossManager.getInstance().getZones())
10272 _zone.removePlayer(this);
10273 }
10274 catch (Exception e)
10275 {
10276 _log.log(Level.WARNING, "Exception on removeFromBossZone(): " + e.getMessage(), e);
10277 }
10278 }
10279
10280 /**
10281 * @return the number of charges this L2PcInstance got.
10282 */
10283 public int getCharges()
10284 {
10285 return _charges.get();
10286 }
10287
10288 public void increaseCharges(int count, int max)
10289 {
10290 if (_charges.get() >= max)
10291 {
10292 sendPacket(SystemMessageId.FORCE_MAXLEVEL_REACHED);
10293 return;
10294 }
10295
10296 restartChargeTask();
10297
10298 if (_charges.addAndGet(count) >= max)
10299 {
10300 _charges.set(max);
10301 sendPacket(SystemMessageId.FORCE_MAXLEVEL_REACHED);
10302 }
10303 else
10304 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FORCE_INCREASED_TO_S1).addNumber(_charges.get()));
10305
10306 sendPacket(new EtcStatusUpdate(this));
10307 }
10308
10309 public boolean decreaseCharges(int count)
10310 {
10311 if (_charges.get() < count)
10312 return false;
10313
10314 if (_charges.addAndGet(-count) == 0)
10315 stopChargeTask();
10316 else
10317 restartChargeTask();
10318
10319 sendPacket(new EtcStatusUpdate(this));
10320 return true;
10321 }
10322
10323 public void clearCharges()
10324 {
10325 _charges.set(0);
10326 sendPacket(new EtcStatusUpdate(this));
10327 }
10328
10329 /**
10330 * Starts/Restarts the ChargeTask to Clear Charges after 10 Mins.
10331 */
10332 private void restartChargeTask()
10333 {
10334 if (_chargeTask != null)
10335 {
10336 _chargeTask.cancel(false);
10337 _chargeTask = null;
10338 }
10339 _chargeTask = ThreadPoolManager.getInstance().scheduleGeneral(new ChargeTask(), 600000);
10340 }
10341
10342 /**
10343 * Stops the Charges Clearing Task.
10344 */
10345 public void stopChargeTask()
10346 {
10347 if (_chargeTask != null)
10348 {
10349 _chargeTask.cancel(false);
10350 _chargeTask = null;
10351 }
10352 }
10353
10354 protected class ChargeTask implements Runnable
10355 {
10356 @Override
10357 public void run()
10358 {
10359 clearCharges();
10360 }
10361 }
10362
10363 /**
10364 * Signets check used to valid who is affected when he entered in the aoe effect.
10365 * @param cha The target to make checks on.
10366 * @return true if player can attack the target.
10367 */
10368 public boolean canAttackCharacter(L2Character cha)
10369 {
10370 if (cha instanceof L2Attackable)
10371 return true;
10372
10373 if (cha instanceof L2Playable)
10374 {
10375 if (cha.isInArena())
10376 return true;
10377
10378 final L2PcInstance target = cha.getActingPlayer();
10379
10380 if (isInDuel() && target.isInDuel() && target.getDuelId() == getDuelId())
10381 return true;
10382
10383 if (isInParty() && target.isInParty())
10384 {
10385 if (getParty() == target.getParty())
10386 return false;
10387
10388 if ((getParty().getCommandChannel() != null || target.getParty().getCommandChannel() != null) && (getParty().getCommandChannel() == target.getParty().getCommandChannel()))
10389 return false;
10390 }
10391
10392 if (getClan() != null && target.getClan() != null)
10393 {
10394 if (getClanId() == target.getClanId())
10395 return false;
10396
10397 if ((getAllyId() > 0 || target.getAllyId() > 0) && getAllyId() == target.getAllyId())
10398 return false;
10399
10400 if (getClan().isAtWarWith(target.getClanId()))
10401 return true;
10402 }
10403 else
10404 {
10405 if (target.getPvpFlag() == 0 && target.getKarma() == 0)
10406 return false;
10407 }
10408 }
10409 return true;
10410 }
10411
10412 /**
10413 * Request Teleport
10414 * @param requester The player who requested the teleport.
10415 * @param skill The used skill.
10416 * @return true if successful.
10417 **/
10418 public boolean teleportRequest(L2PcInstance requester, L2Skill skill)
10419 {
10420 if (_summonRequest.getTarget() != null && requester != null)
10421 return false;
10422
10423 _summonRequest.setTarget(requester, skill);
10424 return true;
10425 }
10426
10427 /**
10428 * Action teleport
10429 * @param answer
10430 * @param requesterId
10431 **/
10432 public void teleportAnswer(int answer, int requesterId)
10433 {
10434 if (_summonRequest.getTarget() == null)
10435 return;
10436
10437 if (answer == 1 && _summonRequest.getTarget().getObjectId() == requesterId)
10438 teleToTarget(this, _summonRequest.getTarget(), _summonRequest.getSkill());
10439
10440 _summonRequest.setTarget(null, null);
10441 }
10442
10443 public static void teleToTarget(L2PcInstance targetChar, L2PcInstance summonerChar, L2Skill summonSkill)
10444 {
10445 if (targetChar == null || summonerChar == null || summonSkill == null)
10446 return;
10447
10448 if (!checkSummonerStatus(summonerChar))
10449 return;
10450
10451 if (!checkSummonTargetStatus(targetChar, summonerChar))
10452 return;
10453
10454 final int itemConsumeId = summonSkill.getTargetConsumeId();
10455 final int itemConsumeCount = summonSkill.getTargetConsume();
10456
10457 if (itemConsumeId != 0 && itemConsumeCount != 0)
10458 {
10459 if (targetChar.getInventory().getInventoryItemCount(itemConsumeId, 0) < itemConsumeCount)
10460 {
10461 targetChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_REQUIRED_FOR_SUMMONING).addItemName(summonSkill.getTargetConsumeId()));
10462 return;
10463 }
10464
10465 targetChar.destroyItemByItemId("Consume", itemConsumeId, itemConsumeCount, targetChar, true);
10466 }
10467 targetChar.teleToLocation(summonerChar.getX(), summonerChar.getY(), summonerChar.getZ(), 20);
10468 }
10469
10470 public static boolean checkSummonerStatus(L2PcInstance summonerChar)
10471 {
10472 if (summonerChar == null)
10473 return false;
10474
10475 if (summonerChar.isInOlympiadMode() || summonerChar.inObserverMode() || summonerChar.isInsideZone(ZoneId.NO_SUMMON_FRIEND) || summonerChar.isMounted())
10476 return false;
10477
10478 return true;
10479 }
10480
10481 public static boolean checkSummonTargetStatus(L2Object target, L2PcInstance summonerChar)
10482 {
10483 if (target == null || !(target instanceof L2PcInstance))
10484 return false;
10485
10486 L2PcInstance targetChar = (L2PcInstance) target;
10487
10488 if (targetChar.isAlikeDead())
10489 {
10490 summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_IS_DEAD_AT_THE_MOMENT_AND_CANNOT_BE_SUMMONED).addPcName(targetChar));
10491 return false;
10492 }
10493
10494 if (targetChar.isInStoreMode())
10495 {
10496 summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_CURRENTLY_TRADING_OR_OPERATING_PRIVATE_STORE_AND_CANNOT_BE_SUMMONED).addPcName(targetChar));
10497 return false;
10498 }
10499
10500 if (targetChar.isRooted() || targetChar.isInCombat())
10501 {
10502 summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_IS_ENGAGED_IN_COMBAT_AND_CANNOT_BE_SUMMONED).addPcName(targetChar));
10503 return false;
10504 }
10505
10506 if (targetChar.isInOlympiadMode())
10507 {
10508 summonerChar.sendPacket(SystemMessageId.YOU_CANNOT_SUMMON_PLAYERS_WHO_ARE_IN_OLYMPIAD);
10509 return false;
10510 }
10511
10512 if (targetChar.isFestivalParticipant() || targetChar.isMounted())
10513 {
10514 summonerChar.sendPacket(SystemMessageId.YOUR_TARGET_IS_IN_AN_AREA_WHICH_BLOCKS_SUMMONING);
10515 return false;
10516 }
10517
10518 if (targetChar.inObserverMode() || targetChar.isInsideZone(ZoneId.NO_SUMMON_FRIEND))
10519 {
10520 summonerChar.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.S1_IN_SUMMON_BLOCKING_AREA).addCharName(targetChar));
10521 return false;
10522 }
10523
10524 return true;
10525 }
10526
10527 public final int getClientX()
10528 {
10529 return _clientX;
10530 }
10531
10532 public final int getClientY()
10533 {
10534 return _clientY;
10535 }
10536
10537 public final int getClientZ()
10538 {
10539 return _clientZ;
10540 }
10541
10542 public final int getClientHeading()
10543 {
10544 return _clientHeading;
10545 }
10546
10547 public final void setClientX(int val)
10548 {
10549 _clientX = val;
10550 }
10551
10552 public final void setClientY(int val)
10553 {
10554 _clientY = val;
10555 }
10556
10557 public final void setClientZ(int val)
10558 {
10559 _clientZ = val;
10560 }
10561
10562 public final void setClientHeading(int val)
10563 {
10564 _clientHeading = val;
10565 }
10566
10567 /**
10568 * @return the mailPosition.
10569 */
10570 public int getMailPosition()
10571 {
10572 return _mailPosition;
10573 }
10574
10575 /**
10576 * @param mailPosition The mailPosition to set.
10577 */
10578 public void setMailPosition(int mailPosition)
10579 {
10580 _mailPosition = mailPosition;
10581 }
10582
10583 /**
10584 * @param z
10585 * @return true if character falling now On the start of fall return false for correct coord sync !
10586 */
10587 public final boolean isFalling(int z)
10588 {
10589 if (isDead() || isFlying() || isInsideZone(ZoneId.WATER))
10590 return false;
10591
10592 if (System.currentTimeMillis() < _fallingTimestamp)
10593 return true;
10594
10595 final int deltaZ = getZ() - z;
10596 if (deltaZ <= getBaseTemplate().getFallHeight())
10597 return false;
10598
10599 final int damage = (int) Formulas.calcFallDam(this, deltaZ);
10600 if (damage > 0)
10601 {
10602 reduceCurrentHp(Math.min(damage, getCurrentHp() - 1), null, false, true, null);
10603 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FALL_DAMAGE_S1).addNumber(damage));
10604 }
10605
10606 setFalling();
10607
10608 return false;
10609 }
10610
10611 /**
10612 * Set falling timestamp
10613 */
10614 public final void setFalling()
10615 {
10616 _fallingTimestamp = System.currentTimeMillis() + FALLING_VALIDATION_DELAY;
10617 }
10618
10619 public boolean isAllowedToEnchantSkills()
10620 {
10621 if (isLocked())
10622 return false;
10623
10624 if (AttackStanceTaskManager.getInstance().isInAttackStance(this))
10625 return false;
10626
10627 if (isCastingNow() || isCastingSimultaneouslyNow())
10628 return false;
10629
10630 if (isInBoat())
10631 return false;
10632
10633 return true;
10634 }
10635
10636 /**
10637 * Friendlist / selected Friendlist (for community board)
10638 */
10639 private final List<Integer> _friendList = new ArrayList<>();
10640 private final List<Integer> _selectedFriendList = new ArrayList<>();
10641
10642 public List<Integer> getFriendList()
10643 {
10644 return _friendList;
10645 }
10646
10647 public void selectFriend(Integer friendId)
10648 {
10649 if (!_selectedFriendList.contains(friendId))
10650 _selectedFriendList.add(friendId);
10651 }
10652
10653 public void deselectFriend(Integer friendId)
10654 {
10655 if (_selectedFriendList.contains(friendId))
10656 _selectedFriendList.remove(friendId);
10657 }
10658
10659 public List<Integer> getSelectedFriendList()
10660 {
10661 return _selectedFriendList;
10662 }
10663
10664 private void restoreFriendList()
10665 {
10666 _friendList.clear();
10667
10668 try (Connection con = L2DatabaseFactory.getInstance().getConnection())
10669 {
10670 PreparedStatement statement = con.prepareStatement("SELECT friend_id FROM character_friends WHERE char_id = ? AND relation = 0");
10671 statement.setInt(1, getObjectId());
10672 ResultSet rset = statement.executeQuery();
10673
10674 int friendId;
10675 while (rset.next())
10676 {
10677 friendId = rset.getInt("friend_id");
10678 if (friendId == getObjectId())
10679 continue;
10680
10681 _friendList.add(friendId);
10682 }
10683
10684 rset.close();
10685 statement.close();
10686 }
10687 catch (Exception e)
10688 {
10689 _log.log(Level.WARNING, "Error found in " + getName() + "'s friendlist: " + e.getMessage(), e);
10690 }
10691 }
10692
10693 private void notifyFriends(boolean login)
10694 {
10695 for (int id : _friendList)
10696 {
10697 L2PcInstance friend = L2World.getInstance().getPlayer(id);
10698 if (friend != null)
10699 {
10700 friend.sendPacket(new FriendList(friend));
10701
10702 if (login)
10703 friend.sendPacket(SystemMessage.getSystemMessage(SystemMessageId.FRIEND_S1_HAS_LOGGED_IN).addPcName(this));
10704 }
10705 }
10706 }
10707
10708 private final List<Integer> _selectedBlocksList = new ArrayList<>();
10709
10710 public void selectBlock(Integer friendId)
10711 {
10712 if (!_selectedBlocksList.contains(friendId))
10713 _selectedBlocksList.add(friendId);
10714 }
10715
10716 public void deselectBlock(Integer friendId)
10717 {
10718 if (_selectedBlocksList.contains(friendId))
10719 _selectedBlocksList.remove(friendId);
10720 }
10721
10722 public List<Integer> getSelectedBlocksList()
10723 {
10724 return _selectedBlocksList;
10725 }
10726
10727 @Override
10728 public void broadcastRelationsChanges()
10729 {
10730 for (L2PcInstance player : getKnownList().getKnownType(L2PcInstance.class))
10731 {
10732 player.sendPacket(new RelationChanged(this, getRelation(player), isAutoAttackable(player)));
10733 if (getPet() != null)
10734 player.sendPacket(new RelationChanged(getPet(), getRelation(player), isAutoAttackable(player)));
10735 }
10736 }
10737
10738 @Override
10739 public void sendInfo(L2PcInstance activeChar)
10740 {
10741 if (isInBoat())
10742 getPosition().setWorldPosition(getBoat().getPosition().getWorldPosition());
10743
10744 if (getPoly().isMorphed())
10745 activeChar.sendPacket(new AbstractNpcInfo.PcMorphInfo(this, getPoly().getNpcTemplate()));
10746 else
10747 {
10748 activeChar.sendPacket(new CharInfo(this));
10749
10750 if (isSeated())
10751 {
10752 final L2Object object = L2World.getInstance().getObject(getMountObjectID());
10753 if (object instanceof L2StaticObjectInstance)
10754 activeChar.sendPacket(new ChairSit(getObjectId(), ((L2StaticObjectInstance) object).getStaticObjectId()));
10755 }
10756 }
10757
10758 final int relation1 = getRelation(activeChar);
10759 activeChar.sendPacket(new RelationChanged(this, relation1, isAutoAttackable(activeChar)));
10760 if (getPet() != null)
10761 activeChar.sendPacket(new RelationChanged(getPet(), relation1, isAutoAttackable(activeChar)));
10762
10763 final int relation2 = activeChar.getRelation(this);
10764 sendPacket(new RelationChanged(activeChar, relation2, activeChar.isAutoAttackable(this)));
10765 if (activeChar.getPet() != null)
10766 sendPacket(new RelationChanged(activeChar.getPet(), relation2, activeChar.isAutoAttackable(this)));
10767
10768 if (isInBoat())
10769 activeChar.sendPacket(new GetOnVehicle(getObjectId(), getBoat().getObjectId(), getInVehiclePosition()));
10770
10771 switch (getPrivateStoreType())
10772 {
10773 case SELL:
10774 case PACKAGE_SELL:
10775 activeChar.sendPacket(new PrivateStoreMsgSell(this));
10776 break;
10777
10778 case BUY:
10779 activeChar.sendPacket(new PrivateStoreMsgBuy(this));
10780 break;
10781
10782 case MANUFACTURE:
10783 activeChar.sendPacket(new RecipeShopMsg(this));
10784 break;
10785 }
10786 }
10787}