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