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