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