· 8 years ago · Apr 25, 2018, 03:26 AM
1/*
2 * This file is part of the L2J Mobius project.
3 *
4 * This program is free software: you can redistribute it and/or modify
5 * it under the terms of the GNU General Public License as published by
6 * the Free Software Foundation, either version 3 of the License, or
7 * (at your option) any later version.
8 *
9 * This program is distributed in the hope that it will be useful,
10 * but WITHOUT ANY WARRANTY; without even the implied warranty of
11 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
12 * General Public License for more details.
13 *
14 * You should have received a copy of the GNU General Public License
15 * along with this program. If not, see <http://www.gnu.org/licenses/>.
16 */
17package com.l2jmobius.gameserver.model.actor.instance;
18
19import java.sql.Connection;
20import java.sql.Date;
21import java.sql.PreparedStatement;
22import java.sql.ResultSet;
23import java.sql.SQLException;
24import java.util.ArrayList;
25import java.util.Arrays;
26import java.util.Calendar;
27import java.util.Collection;
28import java.util.Collections;
29import java.util.HashMap;
30import java.util.LinkedList;
31import java.util.List;
32import java.util.Map;
33import java.util.Map.Entry;
34import java.util.Objects;
35import java.util.Set;
36import java.util.concurrent.ConcurrentHashMap;
37import java.util.concurrent.ConcurrentSkipListMap;
38import java.util.concurrent.Future;
39import java.util.concurrent.ScheduledFuture;
40import java.util.concurrent.TimeUnit;
41import java.util.concurrent.atomic.AtomicInteger;
42import java.util.concurrent.locks.ReentrantLock;
43import java.util.logging.Level;
44import java.util.stream.Collectors;
45
46import com.l2jmobius.Config;
47import com.l2jmobius.commons.concurrent.ThreadPool;
48import com.l2jmobius.commons.database.DatabaseFactory;
49import com.l2jmobius.commons.util.CommonUtil;
50import com.l2jmobius.commons.util.Rnd;
51import com.l2jmobius.gameserver.GameTimeController;
52import com.l2jmobius.gameserver.ItemsAutoDestroy;
53import com.l2jmobius.gameserver.LoginServerThread;
54import com.l2jmobius.gameserver.ai.CtrlIntention;
55import com.l2jmobius.gameserver.ai.L2CharacterAI;
56import com.l2jmobius.gameserver.ai.L2PlayerAI;
57import com.l2jmobius.gameserver.ai.L2SummonAI;
58import com.l2jmobius.gameserver.cache.WarehouseCacheManager;
59import com.l2jmobius.gameserver.communitybbs.BB.Forum;
60import com.l2jmobius.gameserver.communitybbs.Manager.ForumsBBSManager;
61import com.l2jmobius.gameserver.data.sql.impl.CharNameTable;
62import com.l2jmobius.gameserver.data.sql.impl.CharSummonTable;
63import com.l2jmobius.gameserver.data.sql.impl.ClanTable;
64import com.l2jmobius.gameserver.data.xml.impl.AdminData;
65import com.l2jmobius.gameserver.data.xml.impl.AttendanceRewardData;
66import com.l2jmobius.gameserver.data.xml.impl.CategoryData;
67import com.l2jmobius.gameserver.data.xml.impl.ClassListData;
68import com.l2jmobius.gameserver.data.xml.impl.ExperienceData;
69import com.l2jmobius.gameserver.data.xml.impl.HennaData;
70import com.l2jmobius.gameserver.data.xml.impl.MonsterBookData;
71import com.l2jmobius.gameserver.data.xml.impl.NpcData;
72import com.l2jmobius.gameserver.data.xml.impl.PetDataTable;
73import com.l2jmobius.gameserver.data.xml.impl.PlayerTemplateData;
74import com.l2jmobius.gameserver.data.xml.impl.PlayerXpPercentLostData;
75import com.l2jmobius.gameserver.data.xml.impl.RecipeData;
76import com.l2jmobius.gameserver.data.xml.impl.SkillData;
77import com.l2jmobius.gameserver.data.xml.impl.SkillTreesData;
78import com.l2jmobius.gameserver.datatables.ItemTable;
79import com.l2jmobius.gameserver.enums.AdminTeleportType;
80import com.l2jmobius.gameserver.enums.BroochJewel;
81import com.l2jmobius.gameserver.enums.CastleSide;
82import com.l2jmobius.gameserver.enums.CategoryType;
83import com.l2jmobius.gameserver.enums.ChatType;
84import com.l2jmobius.gameserver.enums.Faction;
85import com.l2jmobius.gameserver.enums.GroupType;
86import com.l2jmobius.gameserver.enums.HtmlActionScope;
87import com.l2jmobius.gameserver.enums.IllegalActionPunishmentType;
88import com.l2jmobius.gameserver.enums.InstanceType;
89import com.l2jmobius.gameserver.enums.ItemGrade;
90import com.l2jmobius.gameserver.enums.MountType;
91import com.l2jmobius.gameserver.enums.NextActionType;
92import com.l2jmobius.gameserver.enums.PartyDistributionType;
93import com.l2jmobius.gameserver.enums.PartySmallWindowUpdateType;
94import com.l2jmobius.gameserver.enums.PlayerAction;
95import com.l2jmobius.gameserver.enums.PrivateStoreType;
96import com.l2jmobius.gameserver.enums.Race;
97import com.l2jmobius.gameserver.enums.Sex;
98import com.l2jmobius.gameserver.enums.ShortcutType;
99import com.l2jmobius.gameserver.enums.StatusUpdateType;
100import com.l2jmobius.gameserver.enums.SubclassInfoType;
101import com.l2jmobius.gameserver.enums.Team;
102import com.l2jmobius.gameserver.enums.UserInfoType;
103import com.l2jmobius.gameserver.geoengine.GeoEngine;
104import com.l2jmobius.gameserver.handler.AdminCommandHandler;
105import com.l2jmobius.gameserver.handler.IAdminCommandHandler;
106import com.l2jmobius.gameserver.handler.IItemHandler;
107import com.l2jmobius.gameserver.handler.ItemHandler;
108import com.l2jmobius.gameserver.idfactory.IdFactory;
109import com.l2jmobius.gameserver.instancemanager.AntiFeedManager;
110import com.l2jmobius.gameserver.instancemanager.CastleManager;
111import com.l2jmobius.gameserver.instancemanager.CursedWeaponsManager;
112import com.l2jmobius.gameserver.instancemanager.DuelManager;
113import com.l2jmobius.gameserver.instancemanager.FortManager;
114import com.l2jmobius.gameserver.instancemanager.FortSiegeManager;
115import com.l2jmobius.gameserver.instancemanager.GlobalVariablesManager;
116import com.l2jmobius.gameserver.instancemanager.HandysBlockCheckerManager;
117import com.l2jmobius.gameserver.instancemanager.ItemsOnGroundManager;
118import com.l2jmobius.gameserver.instancemanager.MatchingRoomManager;
119import com.l2jmobius.gameserver.instancemanager.MentorManager;
120import com.l2jmobius.gameserver.instancemanager.PunishmentManager;
121import com.l2jmobius.gameserver.instancemanager.QuestManager;
122import com.l2jmobius.gameserver.instancemanager.SellBuffsManager;
123import com.l2jmobius.gameserver.instancemanager.SiegeManager;
124import com.l2jmobius.gameserver.instancemanager.ZoneManager;
125import com.l2jmobius.gameserver.model.ArenaParticipantsHolder;
126import com.l2jmobius.gameserver.model.BlockList;
127import com.l2jmobius.gameserver.model.ClanPrivilege;
128import com.l2jmobius.gameserver.model.ClanWar;
129import com.l2jmobius.gameserver.model.Fishing;
130import com.l2jmobius.gameserver.model.L2AccessLevel;
131import com.l2jmobius.gameserver.model.L2Clan;
132import com.l2jmobius.gameserver.model.L2ClanMember;
133import com.l2jmobius.gameserver.model.L2CommandChannel;
134import com.l2jmobius.gameserver.model.L2ContactList;
135import com.l2jmobius.gameserver.model.L2Object;
136import com.l2jmobius.gameserver.model.L2Party;
137import com.l2jmobius.gameserver.model.L2Party.MessageType;
138import com.l2jmobius.gameserver.model.L2PetData;
139import com.l2jmobius.gameserver.model.L2PetLevelData;
140import com.l2jmobius.gameserver.model.L2PremiumItem;
141import com.l2jmobius.gameserver.model.L2Radar;
142import com.l2jmobius.gameserver.model.L2Request;
143import com.l2jmobius.gameserver.model.L2SkillLearn;
144import com.l2jmobius.gameserver.model.L2World;
145import com.l2jmobius.gameserver.model.Location;
146import com.l2jmobius.gameserver.model.Macro;
147import com.l2jmobius.gameserver.model.MacroList;
148import com.l2jmobius.gameserver.model.PcCondOverride;
149import com.l2jmobius.gameserver.model.ShortCuts;
150import com.l2jmobius.gameserver.model.Shortcut;
151import com.l2jmobius.gameserver.model.TeleportBookmark;
152import com.l2jmobius.gameserver.model.TeleportWhereType;
153import com.l2jmobius.gameserver.model.TimeStamp;
154import com.l2jmobius.gameserver.model.TradeList;
155import com.l2jmobius.gameserver.model.actor.L2Attackable;
156import com.l2jmobius.gameserver.model.actor.L2Character;
157import com.l2jmobius.gameserver.model.actor.L2Npc;
158import com.l2jmobius.gameserver.model.actor.L2Playable;
159import com.l2jmobius.gameserver.model.actor.L2Summon;
160import com.l2jmobius.gameserver.model.actor.L2Vehicle;
161import com.l2jmobius.gameserver.model.actor.appearance.PcAppearance;
162import com.l2jmobius.gameserver.model.actor.request.AbstractRequest;
163import com.l2jmobius.gameserver.model.actor.request.SayuneRequest;
164import com.l2jmobius.gameserver.model.actor.stat.PcStat;
165import com.l2jmobius.gameserver.model.actor.status.PcStatus;
166import com.l2jmobius.gameserver.model.actor.tasks.player.DismountTask;
167import com.l2jmobius.gameserver.model.actor.tasks.player.FameTask;
168import com.l2jmobius.gameserver.model.actor.tasks.player.HennaDurationTask;
169import com.l2jmobius.gameserver.model.actor.tasks.player.InventoryEnableTask;
170import com.l2jmobius.gameserver.model.actor.tasks.player.PetFeedTask;
171import com.l2jmobius.gameserver.model.actor.tasks.player.PvPFlagTask;
172import com.l2jmobius.gameserver.model.actor.tasks.player.RecoGiveTask;
173import com.l2jmobius.gameserver.model.actor.tasks.player.RentPetTask;
174import com.l2jmobius.gameserver.model.actor.tasks.player.ResetChargesTask;
175import com.l2jmobius.gameserver.model.actor.tasks.player.ResetSoulsTask;
176import com.l2jmobius.gameserver.model.actor.tasks.player.SitDownTask;
177import com.l2jmobius.gameserver.model.actor.tasks.player.StandUpTask;
178import com.l2jmobius.gameserver.model.actor.tasks.player.TeleportWatchdogTask;
179import com.l2jmobius.gameserver.model.actor.tasks.player.WarnUserTakeBreakTask;
180import com.l2jmobius.gameserver.model.actor.tasks.player.WaterTask;
181import com.l2jmobius.gameserver.model.actor.templates.L2PcTemplate;
182import com.l2jmobius.gameserver.model.actor.transform.Transform;
183import com.l2jmobius.gameserver.model.base.ClassId;
184import com.l2jmobius.gameserver.model.base.SubClass;
185import com.l2jmobius.gameserver.model.ceremonyofchaos.CeremonyOfChaosEvent;
186import com.l2jmobius.gameserver.model.cubic.CubicInstance;
187import com.l2jmobius.gameserver.model.effects.EffectFlag;
188import com.l2jmobius.gameserver.model.effects.L2EffectType;
189import com.l2jmobius.gameserver.model.entity.Castle;
190import com.l2jmobius.gameserver.model.entity.Duel;
191import com.l2jmobius.gameserver.model.entity.Fort;
192import com.l2jmobius.gameserver.model.entity.Hero;
193import com.l2jmobius.gameserver.model.entity.L2Event;
194import com.l2jmobius.gameserver.model.entity.Siege;
195import com.l2jmobius.gameserver.model.eventengine.AbstractEvent;
196import com.l2jmobius.gameserver.model.events.EventDispatcher;
197import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerAbilityPointsChanged;
198import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerEquipItem;
199import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerFameChanged;
200import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerHennaAdd;
201import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerHennaRemove;
202import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogin;
203import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerLogout;
204import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerMenteeStatus;
205import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerMentorStatus;
206import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPKChanged;
207import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionCancel;
208import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerProfessionChange;
209import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPvPChanged;
210import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerPvPKill;
211import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerReputationChanged;
212import com.l2jmobius.gameserver.model.events.impl.character.player.OnPlayerSubChange;
213import com.l2jmobius.gameserver.model.holders.AttendanceInfoHolder;
214import com.l2jmobius.gameserver.model.holders.ItemHolder;
215import com.l2jmobius.gameserver.model.holders.MonsterBookCardHolder;
216import com.l2jmobius.gameserver.model.holders.MonsterBookRewardHolder;
217import com.l2jmobius.gameserver.model.holders.MovieHolder;
218import com.l2jmobius.gameserver.model.holders.PlayerEventHolder;
219import com.l2jmobius.gameserver.model.holders.PreparedMultisellListHolder;
220import com.l2jmobius.gameserver.model.holders.RecipeHolder;
221import com.l2jmobius.gameserver.model.holders.SellBuffHolder;
222import com.l2jmobius.gameserver.model.holders.SkillUseHolder;
223import com.l2jmobius.gameserver.model.holders.TrainingHolder;
224import com.l2jmobius.gameserver.model.instancezone.Instance;
225import com.l2jmobius.gameserver.model.interfaces.ILocational;
226import com.l2jmobius.gameserver.model.itemcontainer.Inventory;
227import com.l2jmobius.gameserver.model.itemcontainer.ItemContainer;
228import com.l2jmobius.gameserver.model.itemcontainer.PcFreight;
229import com.l2jmobius.gameserver.model.itemcontainer.PcInventory;
230import com.l2jmobius.gameserver.model.itemcontainer.PcRefund;
231import com.l2jmobius.gameserver.model.itemcontainer.PcWarehouse;
232import com.l2jmobius.gameserver.model.itemcontainer.PetInventory;
233import com.l2jmobius.gameserver.model.items.L2Armor;
234import com.l2jmobius.gameserver.model.items.L2EtcItem;
235import com.l2jmobius.gameserver.model.items.L2Henna;
236import com.l2jmobius.gameserver.model.items.L2Item;
237import com.l2jmobius.gameserver.model.items.L2Weapon;
238import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
239import com.l2jmobius.gameserver.model.items.type.ActionType;
240import com.l2jmobius.gameserver.model.items.type.ArmorType;
241import com.l2jmobius.gameserver.model.items.type.CrystalType;
242import com.l2jmobius.gameserver.model.items.type.EtcItemType;
243import com.l2jmobius.gameserver.model.items.type.WeaponType;
244import com.l2jmobius.gameserver.model.matching.MatchingRoom;
245import com.l2jmobius.gameserver.model.olympiad.OlympiadGameManager;
246import com.l2jmobius.gameserver.model.olympiad.OlympiadGameTask;
247import com.l2jmobius.gameserver.model.olympiad.OlympiadManager;
248import com.l2jmobius.gameserver.model.punishment.PunishmentAffect;
249import com.l2jmobius.gameserver.model.punishment.PunishmentTask;
250import com.l2jmobius.gameserver.model.punishment.PunishmentType;
251import com.l2jmobius.gameserver.model.quest.Quest;
252import com.l2jmobius.gameserver.model.quest.QuestState;
253import com.l2jmobius.gameserver.model.skills.AbnormalType;
254import com.l2jmobius.gameserver.model.skills.BuffInfo;
255import com.l2jmobius.gameserver.model.skills.CommonSkill;
256import com.l2jmobius.gameserver.model.skills.Skill;
257import com.l2jmobius.gameserver.model.skills.SkillCaster;
258import com.l2jmobius.gameserver.model.skills.SkillCastingType;
259import com.l2jmobius.gameserver.model.skills.targets.TargetType;
260import com.l2jmobius.gameserver.model.stats.BaseStats;
261import com.l2jmobius.gameserver.model.stats.Formulas;
262import com.l2jmobius.gameserver.model.stats.MoveType;
263import com.l2jmobius.gameserver.model.stats.Stats;
264import com.l2jmobius.gameserver.model.variables.AccountVariables;
265import com.l2jmobius.gameserver.model.variables.PlayerVariables;
266import com.l2jmobius.gameserver.model.zone.L2ZoneType;
267import com.l2jmobius.gameserver.model.zone.ZoneId;
268import com.l2jmobius.gameserver.network.Disconnection;
269import com.l2jmobius.gameserver.network.L2GameClient;
270import com.l2jmobius.gameserver.network.SystemMessageId;
271import com.l2jmobius.gameserver.network.serverpackets.AbstractHtmlPacket;
272import com.l2jmobius.gameserver.network.serverpackets.AcquireSkillList;
273import com.l2jmobius.gameserver.network.serverpackets.ActionFailed;
274import com.l2jmobius.gameserver.network.serverpackets.ChangeWaitType;
275import com.l2jmobius.gameserver.network.serverpackets.CharInfo;
276import com.l2jmobius.gameserver.network.serverpackets.ConfirmDlg;
277import com.l2jmobius.gameserver.network.serverpackets.EtcStatusUpdate;
278import com.l2jmobius.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget;
279import com.l2jmobius.gameserver.network.serverpackets.ExAdenaInvenCount;
280import com.l2jmobius.gameserver.network.serverpackets.ExAlterSkillRequest;
281import com.l2jmobius.gameserver.network.serverpackets.ExAutoSoulShot;
282import com.l2jmobius.gameserver.network.serverpackets.ExBrPremiumState;
283import com.l2jmobius.gameserver.network.serverpackets.ExDuelUpdateUserInfo;
284import com.l2jmobius.gameserver.network.serverpackets.ExGetBookMarkInfoPacket;
285import com.l2jmobius.gameserver.network.serverpackets.ExGetOnAirShip;
286import com.l2jmobius.gameserver.network.serverpackets.ExMagicAttackInfo;
287import com.l2jmobius.gameserver.network.serverpackets.ExOlympiadMode;
288import com.l2jmobius.gameserver.network.serverpackets.ExPledgeCount;
289import com.l2jmobius.gameserver.network.serverpackets.ExPrivateStoreSetWholeMsg;
290import com.l2jmobius.gameserver.network.serverpackets.ExQuestItemList;
291import com.l2jmobius.gameserver.network.serverpackets.ExSetCompassZoneCode;
292import com.l2jmobius.gameserver.network.serverpackets.ExStartScenePlayer;
293import com.l2jmobius.gameserver.network.serverpackets.ExStopScenePlayer;
294import com.l2jmobius.gameserver.network.serverpackets.ExStorageMaxCount;
295import com.l2jmobius.gameserver.network.serverpackets.ExSubjobInfo;
296import com.l2jmobius.gameserver.network.serverpackets.ExUseSharedGroupItem;
297import com.l2jmobius.gameserver.network.serverpackets.ExUserInfoAbnormalVisualEffect;
298import com.l2jmobius.gameserver.network.serverpackets.ExUserInfoCubic;
299import com.l2jmobius.gameserver.network.serverpackets.ExUserInfoInvenWeight;
300import com.l2jmobius.gameserver.network.serverpackets.GetOnVehicle;
301import com.l2jmobius.gameserver.network.serverpackets.HennaInfo;
302import com.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
303import com.l2jmobius.gameserver.network.serverpackets.InventoryUpdate;
304import com.l2jmobius.gameserver.network.serverpackets.ItemList;
305import com.l2jmobius.gameserver.network.serverpackets.MagicSkillUse;
306import com.l2jmobius.gameserver.network.serverpackets.MyTargetSelected;
307import com.l2jmobius.gameserver.network.serverpackets.NicknameChanged;
308import com.l2jmobius.gameserver.network.serverpackets.ObservationMode;
309import com.l2jmobius.gameserver.network.serverpackets.ObservationReturn;
310import com.l2jmobius.gameserver.network.serverpackets.PartySmallWindowUpdate;
311import com.l2jmobius.gameserver.network.serverpackets.PetInventoryUpdate;
312import com.l2jmobius.gameserver.network.serverpackets.PledgeShowMemberListDelete;
313import com.l2jmobius.gameserver.network.serverpackets.PledgeShowMemberListUpdate;
314import com.l2jmobius.gameserver.network.serverpackets.PrivateStoreListBuy;
315import com.l2jmobius.gameserver.network.serverpackets.PrivateStoreListSell;
316import com.l2jmobius.gameserver.network.serverpackets.PrivateStoreManageListBuy;
317import com.l2jmobius.gameserver.network.serverpackets.PrivateStoreMsgBuy;
318import com.l2jmobius.gameserver.network.serverpackets.PrivateStoreMsgSell;
319import com.l2jmobius.gameserver.network.serverpackets.RecipeShopMsg;
320import com.l2jmobius.gameserver.network.serverpackets.RecipeShopSellList;
321import com.l2jmobius.gameserver.network.serverpackets.RelationChanged;
322import com.l2jmobius.gameserver.network.serverpackets.Ride;
323import com.l2jmobius.gameserver.network.serverpackets.SetupGauge;
324import com.l2jmobius.gameserver.network.serverpackets.ShortCutInit;
325import com.l2jmobius.gameserver.network.serverpackets.SkillCoolTime;
326import com.l2jmobius.gameserver.network.serverpackets.SkillList;
327import com.l2jmobius.gameserver.network.serverpackets.Snoop;
328import com.l2jmobius.gameserver.network.serverpackets.SocialAction;
329import com.l2jmobius.gameserver.network.serverpackets.StatusUpdate;
330import com.l2jmobius.gameserver.network.serverpackets.StopMove;
331import com.l2jmobius.gameserver.network.serverpackets.SystemMessage;
332import com.l2jmobius.gameserver.network.serverpackets.TargetSelected;
333import com.l2jmobius.gameserver.network.serverpackets.TargetUnselected;
334import com.l2jmobius.gameserver.network.serverpackets.TradeDone;
335import com.l2jmobius.gameserver.network.serverpackets.TradeOtherDone;
336import com.l2jmobius.gameserver.network.serverpackets.TradeStart;
337import com.l2jmobius.gameserver.network.serverpackets.UserInfo;
338import com.l2jmobius.gameserver.network.serverpackets.ValidateLocation;
339import com.l2jmobius.gameserver.network.serverpackets.commission.ExResponseCommissionInfo;
340import com.l2jmobius.gameserver.network.serverpackets.friend.L2FriendStatus;
341import com.l2jmobius.gameserver.network.serverpackets.monsterbook.ExMonsterBook;
342import com.l2jmobius.gameserver.network.serverpackets.monsterbook.ExMonsterBookCloseForce;
343import com.l2jmobius.gameserver.network.serverpackets.monsterbook.ExMonsterBookRewardIcon;
344import com.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
345import com.l2jmobius.gameserver.util.Broadcast;
346import com.l2jmobius.gameserver.util.EnumIntBitmask;
347import com.l2jmobius.gameserver.util.FloodProtectors;
348import com.l2jmobius.gameserver.util.GMAudit;
349import com.l2jmobius.gameserver.util.Util;
350
351/**
352 * This class represents all player characters in the world.<br>
353 * There is always a client-thread connected to this (except if a player-store is activated upon logout).
354 */
355public final class L2PcInstance extends L2Playable
356{
357 // Character Skill SQL String Definitions:
358 private static final String RESTORE_SKILLS_FOR_CHAR = "SELECT skill_id,skill_level,skill_sub_level FROM character_skills WHERE charId=? AND class_index=?";
359 private static final String UPDATE_CHARACTER_SKILL_LEVEL = "UPDATE character_skills SET skill_level=?, skill_sub_level=? WHERE skill_id=? AND charId=? AND class_index=?";
360 private static final String ADD_NEW_SKILLS = "REPLACE INTO character_skills (charId,skill_id,skill_level,skill_sub_level,class_index) VALUES (?,?,?,?,?)";
361 private static final String DELETE_SKILL_FROM_CHAR = "DELETE FROM character_skills WHERE skill_id=? AND charId=? AND class_index=?";
362 private static final String DELETE_CHAR_SKILLS = "DELETE FROM character_skills WHERE charId=? AND class_index=?";
363
364 // Character Skill Save SQL String Definitions:
365 private static final String ADD_SKILL_SAVE = "INSERT INTO character_skills_save (charId,skill_id,skill_level,skill_sub_level,remaining_time,reuse_delay,systime,restore_type,class_index,buff_index) VALUES (?,?,?,?,?,?,?,?,?,?)";
366 private static final String RESTORE_SKILL_SAVE = "SELECT skill_id,skill_level,skill_sub_level,remaining_time, reuse_delay, systime, restore_type FROM character_skills_save WHERE charId=? AND class_index=? ORDER BY buff_index ASC";
367 private static final String DELETE_SKILL_SAVE = "DELETE FROM character_skills_save WHERE charId=? AND class_index=?";
368
369 // Character Item Reuse Time String Definition:
370 private static final String ADD_ITEM_REUSE_SAVE = "INSERT INTO character_item_reuse_save (charId,itemId,itemObjId,reuseDelay,systime) VALUES (?,?,?,?,?)";
371 private static final String RESTORE_ITEM_REUSE_SAVE = "SELECT charId,itemId,itemObjId,reuseDelay,systime FROM character_item_reuse_save WHERE charId=?";
372 private static final String DELETE_ITEM_REUSE_SAVE = "DELETE FROM character_item_reuse_save WHERE charId=?";
373
374 // Character Character SQL String Definitions:
375 private static final String INSERT_CHARACTER = "INSERT INTO characters (account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,face,hairStyle,hairColor,sex,exp,sp,reputation,fame,raidbossPoints,pvpkills,pkkills,clanid,race,classid,deletetime,cancraft,title,title_color,online,clan_privs,wantspeace,base_class,nobless,power_grade,vitality_points,createDate) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)";
376 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=?,reputation=?,fame=?,raidbossPoints=?,pvpkills=?,pkkills=?,clanid=?,race=?,classid=?,deletetime=?,title=?,title_color=?,online=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,nobless=?,power_grade=?,subpledge=?,lvl_joined_academy=?,apprentice=?,sponsor=?,clan_join_expiry_time=?,clan_create_expiry_time=?,char_name=?,bookmarkslot=?,vitality_points=?,language=?,faction=?,pccafe_points=? WHERE charId=?";
377 private static final String UPDATE_CHARACTER_ACCESS = "UPDATE characters SET accesslevel = ? WHERE charId = ?";
378 private static final String RESTORE_CHARACTER = "SELECT * FROM characters WHERE charId=?";
379
380 // Character Teleport Bookmark:
381 private static final String INSERT_TP_BOOKMARK = "INSERT INTO character_tpbookmark (charId,Id,x,y,z,icon,tag,name) values (?,?,?,?,?,?,?,?)";
382 private static final String UPDATE_TP_BOOKMARK = "UPDATE character_tpbookmark SET icon=?,tag=?,name=? where charId=? AND Id=?";
383 private static final String RESTORE_TP_BOOKMARK = "SELECT Id,x,y,z,icon,tag,name FROM character_tpbookmark WHERE charId=?";
384 private static final String DELETE_TP_BOOKMARK = "DELETE FROM character_tpbookmark WHERE charId=? AND Id=?";
385
386 // Character Subclass SQL String Definitions:
387 private static final String RESTORE_CHAR_SUBCLASSES = "SELECT class_id,exp,sp,level,vitality_points,class_index,dual_class FROM character_subclasses WHERE charId=? ORDER BY class_index ASC";
388 private static final String ADD_CHAR_SUBCLASS = "INSERT INTO character_subclasses (charId,class_id,exp,sp,level,vitality_points,class_index,dual_class) VALUES (?,?,?,?,?,?,?,?)";
389 private static final String UPDATE_CHAR_SUBCLASS = "UPDATE character_subclasses SET exp=?,sp=?,level=?,vitality_points=?,class_id=?,dual_class=? WHERE charId=? AND class_index =?";
390 private static final String DELETE_CHAR_SUBCLASS = "DELETE FROM character_subclasses WHERE charId=? AND class_index=?";
391
392 // Character Henna SQL String Definitions:
393 private static final String RESTORE_CHAR_HENNAS = "SELECT slot,symbol_id FROM character_hennas WHERE charId=? AND class_index=?";
394 private static final String ADD_CHAR_HENNA = "REPLACE INTO character_hennas (charId,symbol_id,slot,class_index) VALUES (?,?,?,?)";
395 private static final String DELETE_CHAR_HENNA = "DELETE FROM character_hennas WHERE charId=? AND slot=? AND class_index=?";
396 private static final String DELETE_CHAR_HENNAS = "DELETE FROM character_hennas WHERE charId=? AND class_index=?";
397
398 // Character Shortcut SQL String Definitions:
399 private static final String DELETE_CHAR_SHORTCUTS = "DELETE FROM character_shortcuts WHERE charId=? AND class_index=?";
400
401 // Character Recipe List Save
402 private static final String DELETE_CHAR_RECIPE_SHOP = "DELETE FROM character_recipeshoplist WHERE charId=?";
403 private static final String INSERT_CHAR_RECIPE_SHOP = "REPLACE INTO character_recipeshoplist (`charId`, `recipeId`, `price`, `index`) VALUES (?, ?, ?, ?)";
404 private static final String RESTORE_CHAR_RECIPE_SHOP = "SELECT * FROM character_recipeshoplist WHERE charId=? ORDER BY `index`";
405
406 private static final String COND_OVERRIDE_KEY = "cond_override";
407
408 public static final String NEWBIE_KEY = "NEWBIE";
409
410 public static final int ID_NONE = -1;
411
412 public static final int REQUEST_TIMEOUT = 15;
413
414 private int _pcCafePoints = 0;
415
416 private L2GameClient _client;
417
418 private final String _accountName;
419 private long _deleteTimer;
420 private Calendar _createDate = Calendar.getInstance();
421
422 private String _lang = null;
423 private String _htmlPrefix = null;
424
425 private volatile boolean _isOnline = false;
426 private long _onlineTime;
427 private long _onlineBeginTime;
428 private long _lastAccess;
429 private long _uptime;
430
431 private final ReentrantLock _subclassLock = new ReentrantLock();
432 protected int _baseClass;
433 protected int _activeClass;
434 protected int _classIndex = 0;
435
436 /** data for mounted pets */
437 private int _controlItemId;
438 private L2PetData _data;
439 private L2PetLevelData _leveldata;
440 private int _curFeed;
441 protected Future<?> _mountFeedTask;
442 private ScheduledFuture<?> _dismountTask;
443 private boolean _petItems = false;
444
445 /** The list of sub-classes this character has. */
446 private volatile Map<Integer, SubClass> _subClasses;
447
448 private static final String ORIGINAL_CLASS_VAR = "OriginalClass";
449
450 private final PcAppearance _appearance;
451
452 /** The Experience of the L2PcInstance before the last Death Penalty */
453 private long _expBeforeDeath;
454
455 /** The number of player killed during a PvP (the player killed was PvP Flagged) */
456 private int _pvpKills;
457
458 /** The PK counter of the L2PcInstance (= Number of non PvP Flagged player killed) */
459 private int _pkKills;
460
461 /** The PvP Flag state of the L2PcInstance (0=White, 1=Purple) */
462 private byte _pvpFlag;
463
464 /** The Fame of this L2PcInstance */
465 private int _fame;
466 private ScheduledFuture<?> _fameTask;
467
468 /** The Raidboss points of this PlayerInstance */
469 private int _raidbossPoints;
470
471 private volatile ScheduledFuture<?> _teleportWatchdog;
472
473 /** The Siege state of the L2PcInstance */
474 private byte _siegeState = 0;
475
476 /** The id of castle/fort which the L2PcInstance is registered for siege */
477 private int _siegeSide = 0;
478
479 private int _curWeightPenalty = 0;
480
481 private int _lastCompassZone; // the last compass zone update send to the client
482
483 private final L2ContactList _contactList = new L2ContactList(this);
484
485 private int _bookmarkslot = 0; // The Teleport Bookmark Slot
486
487 private final Map<Integer, TeleportBookmark> _tpbookmarks = new ConcurrentSkipListMap<>();
488
489 private boolean _canFeed;
490 private boolean _isInSiege;
491 private boolean _isInHideoutSiege = false;
492
493 /** Olympiad */
494 private boolean _inOlympiadMode = false;
495 private boolean _OlympiadStart = false;
496 private int _olympiadGameId = -1;
497 private int _olympiadSide = -1;
498
499 /** Duel */
500 private boolean _isInDuel = false;
501 private int _duelState = Duel.DUELSTATE_NODUEL;
502 private int _duelId = 0;
503 private SystemMessageId _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL;
504
505 /** Faceoff */
506 private int _attackerObjId = 0;
507
508 /** Boat and AirShip */
509 private L2Vehicle _vehicle = null;
510 private Location _inVehiclePosition;
511
512 private MountType _mountType = MountType.NONE;
513 private int _mountNpcId;
514 private int _mountLevel;
515 /** Store object used to summon the strider you are mounting **/
516 private int _mountObjectID = 0;
517
518 private AdminTeleportType _teleportType = AdminTeleportType.NORMAL;
519
520 private boolean _inCrystallize;
521 private volatile boolean _isCrafting;
522
523 private long _offlineShopStart = 0;
524
525 /** The table containing all L2RecipeList of the L2PcInstance */
526 private final Map<Integer, RecipeHolder> _dwarvenRecipeBook = new ConcurrentSkipListMap<>();
527 private final Map<Integer, RecipeHolder> _commonRecipeBook = new ConcurrentSkipListMap<>();
528
529 /** Premium Items */
530 private final Map<Integer, L2PremiumItem> _premiumItems = new ConcurrentSkipListMap<>();
531
532 /** True if the L2PcInstance is sitting */
533 private boolean _waitTypeSitting = false;
534
535 /** Location before entering Observer Mode */
536 private Location _lastLoc;
537 private boolean _observerMode = false;
538
539 /** Stored from last ValidatePosition **/
540 private final Location _lastServerPosition = new Location(0, 0, 0);
541
542 /** The number of recommendation obtained by the L2PcInstance */
543 private int _recomHave; // how much I was recommended by others
544 /** The number of recommendation that the L2PcInstance can give */
545 private int _recomLeft; // how many recommendations I can give to others
546 /** Recommendation task **/
547 private ScheduledFuture<?> _recoGiveTask;
548 /** Recommendation Two Hours bonus **/
549 protected boolean _recoTwoHoursGiven = false;
550
551 private ScheduledFuture<?> _onlineTimeUpdateTask;
552
553 private final PcInventory _inventory = new PcInventory(this);
554 private final PcFreight _freight = new PcFreight(this);
555 private PcWarehouse _warehouse;
556 private PcRefund _refund;
557
558 private PrivateStoreType _privateStoreType = PrivateStoreType.NONE;
559
560 private TradeList _activeTradeList;
561 private ItemContainer _activeWarehouse;
562 private volatile Map<Integer, Long> _manufactureItems;
563 private String _storeName = "";
564 private TradeList _sellList;
565 private TradeList _buyList;
566
567 // Multisell
568 private PreparedMultisellListHolder _currentMultiSell = null;
569
570 private int _nobleLevel = 0;
571 private boolean _hero = false;
572 private boolean _trueHero = false;
573
574 /** Premium System */
575 private boolean _premiumStatus = false;
576
577 /** Faction System */
578 private boolean _isGood = false;
579 private boolean _isEvil = false;
580
581 /** The L2FolkInstance corresponding to the last Folk which one the player talked. */
582 private L2Npc _lastFolkNpc = null;
583
584 /** Last NPC Id talked on a quest */
585 private int _questNpcObject = 0;
586
587 /** Used for simulating Quest onTalk */
588 private boolean _simulatedTalking = false;
589
590 /** The table containing all Quests began by the L2PcInstance */
591 private final Map<String, QuestState> _quests = new ConcurrentHashMap<>();
592
593 /** The list containing all shortCuts of this player. */
594 private final ShortCuts _shortCuts = new ShortCuts(this);
595
596 /** The list containing all macros of this player. */
597 private final MacroList _macros = new MacroList(this);
598
599 private final Set<L2PcInstance> _snoopListener = ConcurrentHashMap.newKeySet();
600 private final Set<L2PcInstance> _snoopedPlayer = ConcurrentHashMap.newKeySet();
601
602 /** Hennas */
603 private final L2Henna[] _henna = new L2Henna[4];
604 private final Map<BaseStats, Integer> _hennaBaseStats = new ConcurrentHashMap<>();
605 private final Map<Integer, ScheduledFuture<?>> _hennaRemoveSchedules = new ConcurrentHashMap<>(4);
606
607 /** The Pet of the L2PcInstance */
608 private L2PetInstance _pet = null;
609 /** Servitors of the L2PcInstance */
610 private volatile Map<Integer, L2Summon> _servitors = null;
611 /** The L2Agathion of the L2PcInstance */
612 private int _agathionId = 0;
613 // apparently, a L2PcInstance CAN have both a summon AND a tamed beast at the same time!!
614 // after Freya players can control more than one tamed beast
615 private volatile Set<L2TamedBeastInstance> _tamedBeast = null;
616
617 // client radar
618 // TODO: This needs to be better integrated and saved/loaded
619 private final L2Radar _radar;
620
621 private MatchingRoom _matchingRoom;
622
623 // Clan related attributes
624 /** The Clan Identifier of the L2PcInstance */
625 private int _clanId;
626
627 /** The Clan object of the L2PcInstance */
628 private L2Clan _clan;
629
630 /** Apprentice and Sponsor IDs */
631 private int _apprentice = 0;
632 private int _sponsor = 0;
633
634 private long _clanJoinExpiryTime;
635 private long _clanCreateExpiryTime;
636
637 private int _powerGrade = 0;
638 private volatile EnumIntBitmask<ClanPrivilege> _clanPrivileges = new EnumIntBitmask<>(ClanPrivilege.class, false);
639
640 /** L2PcInstance's pledge class (knight, Baron, etc.) */
641 private int _pledgeClass = 0;
642 private int _pledgeType = 0;
643
644 /** Level at which the player joined the clan as an academy member */
645 private int _lvlJoinedAcademy = 0;
646
647 private int _wantsPeace = 0;
648
649 // charges
650 private final AtomicInteger _charges = new AtomicInteger();
651 private ScheduledFuture<?> _chargeTask = null;
652
653 // Absorbed Souls
654 private int _souls = 0;
655 private ScheduledFuture<?> _soulTask = null;
656
657 // WorldPosition used by TARGET_SIGNET_GROUND
658 private Location _currentSkillWorldPosition;
659
660 private L2AccessLevel _accessLevel;
661
662 private boolean _messageRefusal = false; // message refusal mode
663
664 private boolean _silenceMode = false; // silence mode
665 private List<Integer> _silenceModeExcluded; // silence mode
666 private boolean _dietMode = false; // ignore weight penalty
667 private boolean _tradeRefusal = false; // Trade refusal
668 private boolean _exchangeRefusal = false; // Exchange refusal
669
670 private L2Party _party;
671
672 // this is needed to find the inviting player for Party response
673 // there can only be one active party request at once
674 private L2PcInstance _activeRequester;
675 private long _requestExpireTime = 0;
676 private final L2Request _request = new L2Request(this);
677
678 // Used for protection after teleport
679 private long _spawnProtectEndTime = 0;
680 private long _teleportProtectEndTime = 0;
681
682 private volatile Map<Integer, ExResponseCommissionInfo> _lastCommissionInfos;
683
684 @SuppressWarnings("rawtypes")
685 private volatile Map<Class<? extends AbstractEvent>, AbstractEvent<?>> _events;
686 private boolean _isOnCustomEvent = false;
687
688 // protects a char from aggro mobs when getting up from fake death
689 private long _recentFakeDeathEndTime = 0;
690
691 /** The fists L2Weapon of the L2PcInstance (used when no weapon is equipped) */
692 private L2Weapon _fistsWeaponItem;
693
694 private final Map<Integer, String> _chars = new ConcurrentSkipListMap<>();
695
696 // private byte _updateKnownCounter = 0;
697
698 private int _createItemLevel;
699 private int _createCommonItemLevel;
700 private ItemGrade _crystallizeGrade = ItemGrade.NONE;
701 private CrystalType _expertiseLevel = CrystalType.NONE;
702 private int _expertiseArmorPenalty = 0;
703 private int _expertiseWeaponPenalty = 0;
704 private int _expertisePenaltyBonus = 0;
705
706 private volatile Map<Class<? extends AbstractRequest>, AbstractRequest> _requests;
707
708 protected boolean _inventoryDisable = false;
709 /** Player's cubics. */
710 private final Map<Integer, CubicInstance> _cubics = new ConcurrentSkipListMap<>();
711 /** Active shots. */
712 protected Set<Integer> _activeSoulShots = ConcurrentHashMap.newKeySet();
713 /** Active Brooch Jewels **/
714 private BroochJewel _activeRubyJewel = null;
715 private BroochJewel _activeShappireJewel = null;
716
717 public final ReentrantLock soulShotLock = new ReentrantLock();
718
719 /** Event parameters */
720 private PlayerEventHolder eventStatus = null;
721
722 private byte _handysBlockCheckerEventArena = -1;
723
724 /** new race ticket **/
725 private final int _race[] = new int[2];
726
727 private final BlockList _blockList = new BlockList(this);
728
729 private volatile Map<Integer, Skill> _transformSkills;
730 private ScheduledFuture<?> _taskRentPet;
731 private ScheduledFuture<?> _taskWater;
732
733 private ScheduledFuture<?> _skillListRefreshTask;
734
735 /** Last Html Npcs, 0 = last html was not bound to an npc */
736 private final int[] _htmlActionOriginObjectIds = new int[HtmlActionScope.values().length];
737 /**
738 * Origin of the last incoming html action request.<br>
739 * This can be used for htmls continuing the conversation with an npc.
740 */
741 private int _lastHtmlActionOriginObjId;
742
743 /** Bypass validations */
744 @SuppressWarnings("unchecked")
745 private final LinkedList<String>[] _htmlActionCaches = new LinkedList[HtmlActionScope.values().length];
746
747 private Forum _forumMail;
748 private Forum _forumMemo;
749
750 /** Skills queued because a skill is already in progress */
751 private SkillUseHolder _queuedSkill;
752 private boolean _alterSkillActive = false;
753
754 private int _cursedWeaponEquippedId = 0;
755 private boolean _combatFlagEquippedId = false;
756
757 private boolean _canRevive = true;
758 private int _reviveRequested = 0;
759 private double _revivePower = 0;
760 private boolean _revivePet = false;
761
762 private double _cpUpdateIncCheck = .0;
763 private double _cpUpdateDecCheck = .0;
764 private double _cpUpdateInterval = .0;
765 private double _mpUpdateIncCheck = .0;
766 private double _mpUpdateDecCheck = .0;
767 private double _mpUpdateInterval = .0;
768
769 private double _originalCp = .0;
770 private double _originalHp = .0;
771 private double _originalMp = .0;
772
773 /** Char Coords from Client */
774 private int _clientX;
775 private int _clientY;
776 private int _clientZ;
777 private int _clientHeading;
778
779 // during fall validations will be disabled for 10 ms.
780 private static final int FALLING_VALIDATION_DELAY = 10000;
781 private volatile long _fallingTimestamp = 0;
782
783 private int _multiSocialTarget = 0;
784 private int _multiSociaAction = 0;
785
786 private MovieHolder _movieHolder = null;
787
788 private String _adminConfirmCmd = null;
789
790 private volatile long _lastItemAuctionInfoRequest = 0;
791
792 private Future<?> _PvPRegTask;
793
794 private long _pvpFlagLasts;
795
796 private long _notMoveUntil = 0;
797
798 /** Map containing all custom skills of this player. */
799 private Map<Integer, Skill> _customSkills = null;
800
801 private volatile int _actionMask;
802
803 private int _questZoneId = -1;
804
805 private final Fishing _fishing = new Fishing(this);
806
807 private Future<?> _autoSaveTask = null;
808
809 public void setPvpFlagLasts(long time)
810 {
811 _pvpFlagLasts = time;
812 }
813
814 public long getPvpFlagLasts()
815 {
816 return _pvpFlagLasts;
817 }
818
819 public void startPvPFlag()
820 {
821 updatePvPFlag(1);
822
823 if (_PvPRegTask == null)
824 {
825 _PvPRegTask = ThreadPool.scheduleAtFixedRate(new PvPFlagTask(this), 1000, 1000);
826 }
827 }
828
829 public void stopPvpRegTask()
830 {
831 if (_PvPRegTask != null)
832 {
833 _PvPRegTask.cancel(true);
834 _PvPRegTask = null;
835 }
836 }
837
838 public void stopPvPFlag()
839 {
840 stopPvpRegTask();
841
842 updatePvPFlag(0);
843
844 _PvPRegTask = null;
845 }
846
847 // Monster Book variables
848 private final static String MONSTER_BOOK_KILLS_VAR = "MONSTER_BOOK_KILLS_";
849 private final static String MONSTER_BOOK_LEVEL_VAR = "MONSTER_BOOK_LEVEL_";
850
851 // Training Camp
852 private final static String TRAINING_CAMP_VAR = "TRAINING_CAMP";
853 private final static String TRAINING_CAMP_DURATION = "TRAINING_CAMP_DURATION";
854
855 // Attendance Reward system
856 private final static String ATTENDANCE_DATE_VAR = "ATTENDANCE_DATE";
857 private final static String ATTENDANCE_INDEX_VAR = "ATTENDANCE_INDEX";
858
859 // Save responder name for log it
860 private String _lastPetitionGmName = null;
861
862 private boolean _hasCharmOfCourage = false;
863
864 private final Set<Integer> _whisperers = ConcurrentHashMap.newKeySet();
865
866 // Selling buffs system
867 private boolean _isSellingBuffs = false;
868 private List<SellBuffHolder> _sellingBuffs = null;
869
870 public boolean isSellingBuffs()
871 {
872 return _isSellingBuffs;
873 }
874
875 public void setIsSellingBuffs(boolean val)
876 {
877 _isSellingBuffs = val;
878 }
879
880 public List<SellBuffHolder> getSellingBuffs()
881 {
882 if (_sellingBuffs == null)
883 {
884 _sellingBuffs = new ArrayList<>();
885 }
886 return _sellingBuffs;
887 }
888
889 /**
890 * Create a new L2PcInstance and add it in the characters table of the database.<br>
891 * <B><U> Actions</U> :</B>
892 * <ul>
893 * <li>Create a new L2PcInstance with an account name</li>
894 * <li>Set the name, the Hair Style, the Hair Color and the Face type of the L2PcInstance</li>
895 * <li>Add the player in the characters table of the database</li>
896 * </ul>
897 * @param template The L2PcTemplate to apply to the L2PcInstance
898 * @param accountName The name of the L2PcInstance
899 * @param name The name of the L2PcInstance
900 * @param app the player's appearance
901 * @return The L2PcInstance added to the database or null
902 */
903 public static L2PcInstance create(L2PcTemplate template, String accountName, String name, PcAppearance app)
904 {
905 // Create a new L2PcInstance with an account name
906 final L2PcInstance player = new L2PcInstance(template, accountName, app);
907 // Set the name of the L2PcInstance
908 player.setName(name);
909 // Set access level
910 player.setAccessLevel(0, false, false);
911 // Set Character's create time
912 player.setCreateDate(Calendar.getInstance());
913 // Set the base class ID to that of the actual class ID.
914 player.setBaseClass(player.getClassId());
915 // Give 20 recommendations
916 player.setRecomLeft(20);
917 // Add the player in the characters table of the database
918 if (player.createDb())
919 {
920 if (Config.CACHE_CHAR_NAMES)
921 {
922 CharNameTable.getInstance().addName(player);
923 }
924 return player;
925 }
926 return null;
927 }
928
929 public String getAccountName()
930 {
931 if (getClient() == null)
932 {
933 return getAccountNamePlayer();
934 }
935 return getClient().getAccountName();
936 }
937
938 public String getAccountNamePlayer()
939 {
940 return _accountName;
941 }
942
943 public Map<Integer, String> getAccountChars()
944 {
945 return _chars;
946 }
947
948 public int getRelation(L2PcInstance target)
949 {
950 int result = 0;
951
952 if (getClan() != null)
953 {
954 result |= RelationChanged.RELATION_CLAN_MEMBER;
955 if (getClan() == target.getClan())
956 {
957 result |= RelationChanged.RELATION_CLAN_MATE;
958 }
959 if (getAllyId() != 0)
960 {
961 result |= RelationChanged.RELATION_ALLY_MEMBER;
962 }
963 }
964 if (isClanLeader())
965 {
966 result |= RelationChanged.RELATION_LEADER;
967 }
968 if ((getParty() != null) && (getParty() == target.getParty()))
969 {
970 result |= RelationChanged.RELATION_HAS_PARTY;
971 for (int i = 0; i < getParty().getMembers().size(); i++)
972 {
973 if (getParty().getMembers().get(i) != this)
974 {
975 continue;
976 }
977 switch (i)
978 {
979 case 0:
980 {
981 result |= RelationChanged.RELATION_PARTYLEADER; // 0x10
982 break;
983 }
984 case 1:
985 {
986 result |= RelationChanged.RELATION_PARTY4; // 0x8
987 break;
988 }
989 case 2:
990 {
991 result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x7
992 break;
993 }
994 case 3:
995 {
996 result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2; // 0x6
997 break;
998 }
999 case 4:
1000 {
1001 result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY1; // 0x5
1002 break;
1003 }
1004 case 5:
1005 {
1006 result |= RelationChanged.RELATION_PARTY3; // 0x4
1007 break;
1008 }
1009 case 6:
1010 {
1011 result |= RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x3
1012 break;
1013 }
1014 case 7:
1015 {
1016 result |= RelationChanged.RELATION_PARTY2; // 0x2
1017 break;
1018 }
1019 case 8:
1020 {
1021 result |= RelationChanged.RELATION_PARTY1; // 0x1
1022 break;
1023 }
1024 }
1025 }
1026 }
1027 if (getSiegeState() != 0)
1028 {
1029 result |= RelationChanged.RELATION_INSIEGE;
1030 if (getSiegeState() != target.getSiegeState())
1031 {
1032 result |= RelationChanged.RELATION_ENEMY;
1033 }
1034 else
1035 {
1036 result |= RelationChanged.RELATION_ALLY;
1037 }
1038 if (getSiegeState() == 1)
1039 {
1040 result |= RelationChanged.RELATION_ATTACKER;
1041 }
1042 }
1043 if ((getClan() != null) && (target.getClan() != null))
1044 {
1045 if ((target.getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && (getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && target.getClan().isAtWarWith(getClan().getId()))
1046 {
1047 result |= RelationChanged.RELATION_1SIDED_WAR;
1048 if (getClan().isAtWarWith(target.getClan().getId()))
1049 {
1050 result |= RelationChanged.RELATION_MUTUAL_WAR;
1051 }
1052 }
1053 }
1054 if (getBlockCheckerArena() != -1)
1055 {
1056 result |= RelationChanged.RELATION_INSIEGE;
1057 final ArenaParticipantsHolder holder = HandysBlockCheckerManager.getInstance().getHolder(getBlockCheckerArena());
1058 if (holder.getPlayerTeam(this) == 0)
1059 {
1060 result |= RelationChanged.RELATION_ENEMY;
1061 }
1062 else
1063 {
1064 result |= RelationChanged.RELATION_ALLY;
1065 }
1066 result |= RelationChanged.RELATION_ATTACKER;
1067 }
1068 return result;
1069 }
1070
1071 /**
1072 * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world (call restore method).<br>
1073 * <B><U> Actions</U> :</B>
1074 * <ul>
1075 * <li>Retrieve the L2PcInstance from the characters table of the database</li>
1076 * <li>Add the L2PcInstance object in _allObjects</li>
1077 * <li>Set the x,y,z position of the L2PcInstance and make it invisible</li>
1078 * <li>Update the overloaded status of the L2PcInstance</li>
1079 * </ul>
1080 * @param objectId Identifier of the object to initialized
1081 * @return The L2PcInstance loaded from the database
1082 */
1083 public static L2PcInstance load(int objectId)
1084 {
1085 return restore(objectId);
1086 }
1087
1088 private void initPcStatusUpdateValues()
1089 {
1090 _cpUpdateInterval = getMaxCp() / 352.0;
1091 _cpUpdateIncCheck = getMaxCp();
1092 _cpUpdateDecCheck = getMaxCp() - _cpUpdateInterval;
1093 _mpUpdateInterval = getMaxMp() / 352.0;
1094 _mpUpdateIncCheck = getMaxMp();
1095 _mpUpdateDecCheck = getMaxMp() - _mpUpdateInterval;
1096 }
1097
1098 /**
1099 * Constructor of L2PcInstance (use L2Character constructor).<br>
1100 * <B><U> Actions</U> :</B>
1101 * <ul>
1102 * <li>Call the L2Character constructor to create an empty _skills slot and copy basic Calculator set to this L2PcInstance</li>
1103 * <li>Set the name of the L2PcInstance</li>
1104 * </ul>
1105 * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method SET the level of the L2PcInstance to 1</B></FONT>
1106 * @param objectId Identifier of the object to initialized
1107 * @param template The L2PcTemplate to apply to the L2PcInstance
1108 * @param accountName The name of the account including this L2PcInstance
1109 * @param app
1110 */
1111 private L2PcInstance(int objectId, L2PcTemplate template, String accountName, PcAppearance app)
1112 {
1113 super(objectId, template);
1114 setInstanceType(InstanceType.L2PcInstance);
1115 super.initCharStatusUpdateValues();
1116 initPcStatusUpdateValues();
1117
1118 for (int i = 0; i < _htmlActionCaches.length; ++i)
1119 {
1120 _htmlActionCaches[i] = new LinkedList<>();
1121 }
1122
1123 _accountName = accountName;
1124 app.setOwner(this);
1125 _appearance = app;
1126
1127 // Create an AI
1128 getAI();
1129
1130 // Create a L2Radar object
1131 _radar = new L2Radar(this);
1132 }
1133
1134 /**
1135 * Creates a player.
1136 * @param template the player template
1137 * @param accountName the account name
1138 * @param app the player appearance
1139 */
1140 private L2PcInstance(L2PcTemplate template, String accountName, PcAppearance app)
1141 {
1142 this(IdFactory.getInstance().getNextId(), template, accountName, app);
1143 }
1144
1145 @Override
1146 public final PcStat getStat()
1147 {
1148 return (PcStat) super.getStat();
1149 }
1150
1151 @Override
1152 public void initCharStat()
1153 {
1154 setStat(new PcStat(this));
1155 }
1156
1157 @Override
1158 public final PcStatus getStatus()
1159 {
1160 return (PcStatus) super.getStatus();
1161 }
1162
1163 @Override
1164 public void initCharStatus()
1165 {
1166 setStatus(new PcStatus(this));
1167 }
1168
1169 public final PcAppearance getAppearance()
1170 {
1171 return _appearance;
1172 }
1173
1174 public final boolean isHairAccessoryEnabled()
1175 {
1176 return getVariables().getBoolean(PlayerVariables.HAIR_ACCESSORY_VARIABLE_NAME, true);
1177 }
1178
1179 public final void setHairAccessoryEnabled(boolean enabled)
1180 {
1181 getVariables().set(PlayerVariables.HAIR_ACCESSORY_VARIABLE_NAME, enabled);
1182 }
1183
1184 /**
1185 * @return the base L2PcTemplate link to the L2PcInstance.
1186 */
1187 public final L2PcTemplate getBaseTemplate()
1188 {
1189 final ClassId originalClass = getOriginalClass();
1190 if (originalClass != null)
1191 {
1192 return PlayerTemplateData.getInstance().getTemplate(originalClass.getId());
1193 }
1194 return PlayerTemplateData.getInstance().getTemplate(_baseClass);
1195 }
1196
1197 public ClassId getOriginalClass()
1198 {
1199 return getVariables().getEnum(ORIGINAL_CLASS_VAR, ClassId.class, null);
1200 }
1201
1202 public void setOriginalClass(ClassId newClass)
1203 {
1204 getVariables().set(ORIGINAL_CLASS_VAR, newClass);
1205 }
1206
1207 public void resetOriginalClass()
1208 {
1209 getVariables().remove(ORIGINAL_CLASS_VAR);
1210 }
1211
1212 /**
1213 * @return the L2PcTemplate link to the L2PcInstance.
1214 */
1215 @Override
1216 public final L2PcTemplate getTemplate()
1217 {
1218 return (L2PcTemplate) super.getTemplate();
1219 }
1220
1221 /**
1222 * @param newclass
1223 */
1224 public void setTemplate(ClassId newclass)
1225 {
1226 super.setTemplate(PlayerTemplateData.getInstance().getTemplate(newclass));
1227 }
1228
1229 @Override
1230 protected L2CharacterAI initAI()
1231 {
1232 return new L2PlayerAI(this);
1233 }
1234
1235 /** Return the Level of the L2PcInstance. */
1236 @Override
1237 public final int getLevel()
1238 {
1239 return getStat().getLevel();
1240 }
1241
1242 public void setBaseClass(int baseClass)
1243 {
1244 _baseClass = baseClass;
1245 }
1246
1247 public void setBaseClass(ClassId classId)
1248 {
1249 _baseClass = classId.ordinal();
1250 }
1251
1252 public boolean isInStoreMode()
1253 {
1254 return getPrivateStoreType() != PrivateStoreType.NONE;
1255 }
1256
1257 public boolean isCrafting()
1258 {
1259 return _isCrafting;
1260 }
1261
1262 public void setIsCrafting(boolean isCrafting)
1263 {
1264 _isCrafting = isCrafting;
1265 }
1266
1267 /**
1268 * @return a table containing all Common L2RecipeList of the L2PcInstance.
1269 */
1270 public Collection<RecipeHolder> getCommonRecipeBook()
1271 {
1272 return _commonRecipeBook.values();
1273 }
1274
1275 /**
1276 * @return a table containing all Dwarf L2RecipeList of the L2PcInstance.
1277 */
1278 public Collection<RecipeHolder> getDwarvenRecipeBook()
1279 {
1280 return _dwarvenRecipeBook.values();
1281 }
1282
1283 /**
1284 * Add a new L2RecipList to the table _commonrecipebook containing all L2RecipeList of the L2PcInstance
1285 * @param recipe The L2RecipeList to add to the _recipebook
1286 * @param saveToDb
1287 */
1288 public void registerCommonRecipeList(RecipeHolder recipe, boolean saveToDb)
1289 {
1290 _commonRecipeBook.put(recipe.getId(), recipe);
1291
1292 if (saveToDb)
1293 {
1294 insertNewRecipeData(recipe.getId(), false);
1295 }
1296 }
1297
1298 /**
1299 * Add a new L2RecipList to the table _recipebook containing all L2RecipeList of the L2PcInstance
1300 * @param recipe The L2RecipeList to add to the _recipebook
1301 * @param saveToDb
1302 */
1303 public void registerDwarvenRecipeList(RecipeHolder recipe, boolean saveToDb)
1304 {
1305 _dwarvenRecipeBook.put(recipe.getId(), recipe);
1306
1307 if (saveToDb)
1308 {
1309 insertNewRecipeData(recipe.getId(), true);
1310 }
1311 }
1312
1313 /**
1314 * @param recipeId The Identifier of the L2RecipeList to check in the player's recipe books
1315 * @return {@code true}if player has the recipe on Common or Dwarven Recipe book else returns {@code false}
1316 */
1317 public boolean hasRecipeList(int recipeId)
1318 {
1319 return _dwarvenRecipeBook.containsKey(recipeId) || _commonRecipeBook.containsKey(recipeId);
1320 }
1321
1322 /**
1323 * Tries to remove a L2RecipList from the table _DwarvenRecipeBook or from table _CommonRecipeBook, those table contain all L2RecipeList of the L2PcInstance
1324 * @param recipeId The Identifier of the L2RecipeList to remove from the _recipebook
1325 */
1326 public void unregisterRecipeList(int recipeId)
1327 {
1328 if (_dwarvenRecipeBook.remove(recipeId) != null)
1329 {
1330 deleteRecipeData(recipeId, true);
1331 }
1332 else if (_commonRecipeBook.remove(recipeId) != null)
1333 {
1334 deleteRecipeData(recipeId, false);
1335 }
1336 else
1337 {
1338 LOGGER.warning("Attempted to remove unknown RecipeList: " + recipeId);
1339 }
1340
1341 for (Shortcut sc : getAllShortCuts())
1342 {
1343 if ((sc != null) && (sc.getId() == recipeId) && (sc.getType() == ShortcutType.RECIPE))
1344 {
1345 deleteShortCut(sc.getSlot(), sc.getPage());
1346 }
1347 }
1348 }
1349
1350 private void insertNewRecipeData(int recipeId, boolean isDwarf)
1351 {
1352 try (Connection con = DatabaseFactory.getInstance().getConnection();
1353 PreparedStatement statement = con.prepareStatement("INSERT INTO character_recipebook (charId, id, classIndex, type) values(?,?,?,?)"))
1354 {
1355 statement.setInt(1, getObjectId());
1356 statement.setInt(2, recipeId);
1357 statement.setInt(3, isDwarf ? _classIndex : 0);
1358 statement.setInt(4, isDwarf ? 1 : 0);
1359 statement.execute();
1360 }
1361 catch (SQLException e)
1362 {
1363 LOGGER.log(Level.WARNING, "SQL exception while inserting recipe: " + recipeId + " from character " + getObjectId(), e);
1364 }
1365 }
1366
1367 private void deleteRecipeData(int recipeId, boolean isDwarf)
1368 {
1369 try (Connection con = DatabaseFactory.getInstance().getConnection();
1370 PreparedStatement statement = con.prepareStatement("DELETE FROM character_recipebook WHERE charId=? AND id=? AND classIndex=?"))
1371 {
1372 statement.setInt(1, getObjectId());
1373 statement.setInt(2, recipeId);
1374 statement.setInt(3, isDwarf ? _classIndex : 0);
1375 statement.execute();
1376 }
1377 catch (SQLException e)
1378 {
1379 LOGGER.log(Level.WARNING, "SQL exception while deleting recipe: " + recipeId + " from character " + getObjectId(), e);
1380 }
1381 }
1382
1383 /**
1384 * @return the Id for the last talked quest NPC.
1385 */
1386 public int getLastQuestNpcObject()
1387 {
1388 return _questNpcObject;
1389 }
1390
1391 public void setLastQuestNpcObject(int npcId)
1392 {
1393 _questNpcObject = npcId;
1394 }
1395
1396 public boolean isSimulatingTalking()
1397 {
1398 return _simulatedTalking;
1399 }
1400
1401 public void setSimulatedTalking(boolean value)
1402 {
1403 _simulatedTalking = value;
1404 }
1405
1406 /**
1407 * @param quest The name of the quest
1408 * @return the QuestState object corresponding to the quest name.
1409 */
1410 public QuestState getQuestState(String quest)
1411 {
1412 return _quests.get(quest);
1413 }
1414
1415 /**
1416 * Add a QuestState to the table _quest containing all quests began by the L2PcInstance.
1417 * @param qs The QuestState to add to _quest
1418 */
1419 public void setQuestState(QuestState qs)
1420 {
1421 _quests.put(qs.getQuestName(), qs);
1422 }
1423
1424 /**
1425 * Verify if the player has the quest state.
1426 * @param quest the quest state to check
1427 * @return {@code true} if the player has the quest state, {@code false} otherwise
1428 */
1429 public boolean hasQuestState(String quest)
1430 {
1431 return _quests.containsKey(quest);
1432 }
1433
1434 /**
1435 * Remove a QuestState from the table _quest containing all quests began by the L2PcInstance.
1436 * @param quest The name of the quest
1437 */
1438 public void delQuestState(String quest)
1439 {
1440 _quests.remove(quest);
1441 }
1442
1443 /**
1444 * @return List of {@link QuestState}s of the current player.
1445 */
1446 public List<QuestState> getAllQuestStates()
1447 {
1448 return new ArrayList<>(_quests.values());
1449 }
1450
1451 /**
1452 * @return a table containing all Quest in progress from the table _quests.
1453 */
1454 public List<Quest> getAllActiveQuests()
1455 {
1456 //@formatter:off
1457 return _quests.values().stream()
1458 .filter(QuestState::isStarted)
1459 .map(QuestState::getQuest)
1460 .filter(Objects::nonNull)
1461 .filter(q -> q.getId() > 1)
1462 .collect(Collectors.toList());
1463 //@formatter:on
1464 }
1465
1466 public void processQuestEvent(String questName, String event)
1467 {
1468 final Quest quest = QuestManager.getInstance().getQuest(questName);
1469 if ((quest == null) || (event == null) || event.isEmpty())
1470 {
1471 return;
1472 }
1473
1474 final L2Npc target = getLastFolkNPC();
1475
1476 if ((target != null) && isInsideRadius(target, L2Npc.INTERACTION_DISTANCE, false, false))
1477 {
1478 quest.notifyEvent(event, target, this);
1479 }
1480 else if (getLastQuestNpcObject() > 0)
1481 {
1482 final L2Object object = L2World.getInstance().findObject(getLastQuestNpcObject());
1483
1484 if (object.isNpc() && isInsideRadius(object, L2Npc.INTERACTION_DISTANCE, false, false))
1485 {
1486 final L2Npc npc = (L2Npc) object;
1487 quest.notifyEvent(event, npc, this);
1488 }
1489 }
1490 }
1491
1492 /** List of all QuestState instance that needs to be notified of this L2PcInstance's or its pet's death */
1493 private volatile Set<QuestState> _notifyQuestOfDeathList;
1494
1495 /**
1496 * Add QuestState instance that is to be notified of L2PcInstance's death.
1497 * @param qs The QuestState that subscribe to this event
1498 */
1499 public void addNotifyQuestOfDeath(QuestState qs)
1500 {
1501 if (qs == null)
1502 {
1503 return;
1504 }
1505
1506 if (!getNotifyQuestOfDeath().contains(qs))
1507 {
1508 getNotifyQuestOfDeath().add(qs);
1509 }
1510 }
1511
1512 /**
1513 * Remove QuestState instance that is to be notified of L2PcInstance's death.
1514 * @param qs The QuestState that subscribe to this event
1515 */
1516 public void removeNotifyQuestOfDeath(QuestState qs)
1517 {
1518 if ((qs == null) || (_notifyQuestOfDeathList == null))
1519 {
1520 return;
1521 }
1522
1523 _notifyQuestOfDeathList.remove(qs);
1524 }
1525
1526 /**
1527 * @return a list of QuestStates which registered for notify of death of this L2PcInstance.
1528 */
1529 public final Set<QuestState> getNotifyQuestOfDeath()
1530 {
1531 if (_notifyQuestOfDeathList == null)
1532 {
1533 synchronized (this)
1534 {
1535 if (_notifyQuestOfDeathList == null)
1536 {
1537 _notifyQuestOfDeathList = ConcurrentHashMap.newKeySet();
1538 }
1539 }
1540 }
1541
1542 return _notifyQuestOfDeathList;
1543 }
1544
1545 public final boolean isNotifyQuestOfDeathEmpty()
1546 {
1547 return (_notifyQuestOfDeathList == null) || _notifyQuestOfDeathList.isEmpty();
1548 }
1549
1550 /**
1551 * @return a table containing all L2ShortCut of the L2PcInstance.
1552 */
1553 public Shortcut[] getAllShortCuts()
1554 {
1555 return _shortCuts.getAllShortCuts();
1556 }
1557
1558 /**
1559 * @param slot The slot in which the shortCuts is equipped
1560 * @param page The page of shortCuts containing the slot
1561 * @return the L2ShortCut of the L2PcInstance corresponding to the position (page-slot).
1562 */
1563 public Shortcut getShortCut(int slot, int page)
1564 {
1565 return _shortCuts.getShortCut(slot, page);
1566 }
1567
1568 /**
1569 * Add a L2shortCut to the L2PcInstance _shortCuts
1570 * @param shortcut
1571 */
1572 public void registerShortCut(Shortcut shortcut)
1573 {
1574 _shortCuts.registerShortCut(shortcut);
1575 }
1576
1577 /**
1578 * Updates the shortcut bars with the new skill.
1579 * @param skillId the skill Id to search and update.
1580 * @param skillLevel the skill level to update.
1581 * @param skillSubLevel the skill sub level to update.
1582 */
1583 public void updateShortCuts(int skillId, int skillLevel, int skillSubLevel)
1584 {
1585 _shortCuts.updateShortCuts(skillId, skillLevel, skillSubLevel);
1586 }
1587
1588 /**
1589 * Delete the L2ShortCut corresponding to the position (page-slot) from the L2PcInstance _shortCuts.
1590 * @param slot
1591 * @param page
1592 */
1593 public void deleteShortCut(int slot, int page)
1594 {
1595 _shortCuts.deleteShortCut(slot, page);
1596 }
1597
1598 /**
1599 * @param macro the macro to add to this L2PcInstance.
1600 */
1601 public void registerMacro(Macro macro)
1602 {
1603 _macros.registerMacro(macro);
1604 }
1605
1606 /**
1607 * @param id the macro Id to delete.
1608 */
1609 public void deleteMacro(int id)
1610 {
1611 _macros.deleteMacro(id);
1612 }
1613
1614 /**
1615 * @return all L2Macro of the L2PcInstance.
1616 */
1617 public MacroList getMacros()
1618 {
1619 return _macros;
1620 }
1621
1622 /**
1623 * Set the siege state of the L2PcInstance.
1624 * @param siegeState 1 = attacker, 2 = defender, 0 = not involved
1625 */
1626 public void setSiegeState(byte siegeState)
1627 {
1628 _siegeState = siegeState;
1629 }
1630
1631 /**
1632 * Get the siege state of the L2PcInstance.
1633 * @return 1 = attacker, 2 = defender, 0 = not involved
1634 */
1635 public byte getSiegeState()
1636 {
1637 return _siegeState;
1638 }
1639
1640 /**
1641 * Set the siege Side of the L2PcInstance.
1642 * @param val
1643 */
1644 public void setSiegeSide(int val)
1645 {
1646 _siegeSide = val;
1647 }
1648
1649 public boolean isRegisteredOnThisSiegeField(int val)
1650 {
1651 if ((_siegeSide != val) && ((_siegeSide < 81) || (_siegeSide > 89)))
1652 {
1653 return false;
1654 }
1655 return true;
1656 }
1657
1658 public int getSiegeSide()
1659 {
1660 return _siegeSide;
1661 }
1662
1663 /**
1664 * Set the PvP Flag of the L2PcInstance.
1665 * @param pvpFlag
1666 */
1667 public void setPvpFlag(int pvpFlag)
1668 {
1669 _pvpFlag = (byte) pvpFlag;
1670 }
1671
1672 @Override
1673 public byte getPvpFlag()
1674 {
1675 return _pvpFlag;
1676 }
1677
1678 @Override
1679 public void updatePvPFlag(int value)
1680 {
1681 if (getPvpFlag() == value)
1682 {
1683 return;
1684 }
1685 setPvpFlag(value);
1686
1687 final StatusUpdate su = new StatusUpdate(this);
1688 computeStatusUpdate(su, StatusUpdateType.PVP_FLAG);
1689 if (su.hasUpdates())
1690 {
1691 broadcastPacket(su);
1692 sendPacket(su);
1693 }
1694
1695 // If this player has a pet update the pets pvp flag as well
1696 if (hasSummon())
1697 {
1698 final RelationChanged rc = new RelationChanged();
1699 final L2Summon pet = getPet();
1700 if (pet != null)
1701 {
1702 rc.addRelation(pet, getRelation(this), false);
1703 }
1704 if (hasServitors())
1705 {
1706 getServitors().values().forEach(s -> rc.addRelation(s, getRelation(this), false));
1707 }
1708 sendPacket(rc);
1709 }
1710
1711 L2World.getInstance().forEachVisibleObject(this, L2PcInstance.class, player ->
1712 {
1713 if (!isVisibleFor(player))
1714 {
1715 return;
1716 }
1717
1718 final int relation = getRelation(player);
1719 final Integer oldrelation = getKnownRelations().get(player.getObjectId());
1720 if ((oldrelation == null) || (oldrelation != relation))
1721 {
1722 final RelationChanged rc = new RelationChanged();
1723 rc.addRelation(this, relation, isAutoAttackable(player));
1724 if (hasSummon())
1725 {
1726 final L2Summon pet = getPet();
1727 if (pet != null)
1728 {
1729 rc.addRelation(pet, relation, isAutoAttackable(player));
1730 }
1731 if (hasServitors())
1732 {
1733 getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player)));
1734 }
1735 }
1736 player.sendPacket(rc);
1737 getKnownRelations().put(player.getObjectId(), relation);
1738 }
1739 });
1740 }
1741
1742 @Override
1743 public void revalidateZone(boolean force)
1744 {
1745 // Cannot validate if not in a world region (happens during teleport)
1746 if (getWorldRegion() == null)
1747 {
1748 return;
1749 }
1750
1751 // This function is called too often from movement code
1752 if (force)
1753 {
1754 _zoneValidateCounter = 4;
1755 }
1756 else
1757 {
1758 _zoneValidateCounter--;
1759 if (_zoneValidateCounter < 0)
1760 {
1761 _zoneValidateCounter = 4;
1762 }
1763 else
1764 {
1765 return;
1766 }
1767 }
1768
1769 ZoneManager.getInstance().getRegion(this).revalidateZones(this);
1770
1771 if (Config.ALLOW_WATER)
1772 {
1773 checkWaterState();
1774 }
1775
1776 if (isInsideZone(ZoneId.ALTERED))
1777 {
1778 if (_lastCompassZone == ExSetCompassZoneCode.ALTEREDZONE)
1779 {
1780 return;
1781 }
1782 _lastCompassZone = ExSetCompassZoneCode.ALTEREDZONE;
1783 final ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.ALTEREDZONE);
1784 sendPacket(cz);
1785 }
1786 else if (isInsideZone(ZoneId.SIEGE))
1787 {
1788 if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2)
1789 {
1790 return;
1791 }
1792 _lastCompassZone = ExSetCompassZoneCode.SIEGEWARZONE2;
1793 final ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.SIEGEWARZONE2);
1794 sendPacket(cz);
1795 }
1796 else if (isInsideZone(ZoneId.PVP))
1797 {
1798 if (_lastCompassZone == ExSetCompassZoneCode.PVPZONE)
1799 {
1800 return;
1801 }
1802 _lastCompassZone = ExSetCompassZoneCode.PVPZONE;
1803 final ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.PVPZONE);
1804 sendPacket(cz);
1805 }
1806 else if (isInsideZone(ZoneId.PEACE))
1807 {
1808 if (_lastCompassZone == ExSetCompassZoneCode.PEACEZONE)
1809 {
1810 return;
1811 }
1812 _lastCompassZone = ExSetCompassZoneCode.PEACEZONE;
1813 final ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.PEACEZONE);
1814 sendPacket(cz);
1815 }
1816 else
1817 {
1818 if (_lastCompassZone == ExSetCompassZoneCode.GENERALZONE)
1819 {
1820 return;
1821 }
1822 if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2)
1823 {
1824 updatePvPStatus();
1825 }
1826 _lastCompassZone = ExSetCompassZoneCode.GENERALZONE;
1827 final ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.GENERALZONE);
1828 sendPacket(cz);
1829 }
1830 }
1831
1832 /**
1833 * @return the maximum dwarven recipe level this character can craft.
1834 */
1835 public int getCreateItemLevel()
1836 {
1837 return _createItemLevel;
1838 }
1839
1840 public void setCreateItemLevel(int createItemLevel)
1841 {
1842 _createItemLevel = createItemLevel;
1843 }
1844
1845 /**
1846 * @return the maximum common recipe level this character can craft.
1847 */
1848 public int getCreateCommonItemLevel()
1849 {
1850 return _createCommonItemLevel;
1851 }
1852
1853 public void setCreateCommonItemLevel(int createCommonItemLevel)
1854 {
1855 _createCommonItemLevel = createCommonItemLevel;
1856 }
1857
1858 public ItemGrade getCrystallizeGrade()
1859 {
1860 return _crystallizeGrade;
1861 }
1862
1863 public void setCrystallizeGrade(ItemGrade crystallizeGrade)
1864 {
1865 _crystallizeGrade = crystallizeGrade != null ? crystallizeGrade : ItemGrade.NONE;
1866 }
1867
1868 /**
1869 * @return the PK counter of the L2PcInstance.
1870 */
1871 public int getPkKills()
1872 {
1873 return _pkKills;
1874 }
1875
1876 /**
1877 * Set the PK counter of the L2PcInstance.
1878 * @param pkKills
1879 */
1880 public void setPkKills(int pkKills)
1881 {
1882 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerPKChanged(this, _pkKills, pkKills), this);
1883 _pkKills = pkKills;
1884 }
1885
1886 /**
1887 * @return the _deleteTimer of the L2PcInstance.
1888 */
1889 public long getDeleteTimer()
1890 {
1891 return _deleteTimer;
1892 }
1893
1894 /**
1895 * Set the _deleteTimer of the L2PcInstance.
1896 * @param deleteTimer
1897 */
1898 public void setDeleteTimer(long deleteTimer)
1899 {
1900 _deleteTimer = deleteTimer;
1901 }
1902
1903 /**
1904 * @return the number of recommendation obtained by the L2PcInstance.
1905 */
1906 public int getRecomHave()
1907 {
1908 return _recomHave;
1909 }
1910
1911 /**
1912 * Increment the number of recommendation obtained by the L2PcInstance (Max : 255).
1913 */
1914 protected void incRecomHave()
1915 {
1916 if (_recomHave < 255)
1917 {
1918 _recomHave++;
1919 }
1920 }
1921
1922 /**
1923 * Set the number of recommendation obtained by the L2PcInstance (Max : 255).
1924 * @param value
1925 */
1926 public void setRecomHave(int value)
1927 {
1928 _recomHave = Math.min(Math.max(value, 0), 255);
1929 }
1930
1931 /**
1932 * Set the number of recommendation obtained by the L2PcInstance (Max : 255).
1933 * @param value
1934 */
1935 public void setRecomLeft(int value)
1936 {
1937 _recomLeft = Math.min(Math.max(value, 0), 255);
1938 }
1939
1940 /**
1941 * @return the number of recommendation that the L2PcInstance can give.
1942 */
1943 public int getRecomLeft()
1944 {
1945 return _recomLeft;
1946 }
1947
1948 /**
1949 * Increment the number of recommendation that the L2PcInstance can give.
1950 */
1951 protected void decRecomLeft()
1952 {
1953 if (_recomLeft > 0)
1954 {
1955 _recomLeft--;
1956 }
1957 }
1958
1959 public void giveRecom(L2PcInstance target)
1960 {
1961 target.incRecomHave();
1962 decRecomLeft();
1963 }
1964
1965 /**
1966 * Set the exp of the L2PcInstance before a death
1967 * @param exp
1968 */
1969 public void setExpBeforeDeath(long exp)
1970 {
1971 _expBeforeDeath = exp;
1972 }
1973
1974 public long getExpBeforeDeath()
1975 {
1976 return _expBeforeDeath;
1977 }
1978
1979 public void setInitialReputation(int reputation)
1980 {
1981 super.setReputation(reputation);
1982 }
1983
1984 /**
1985 * Set the reputation of the PlayerInstance and send a Server->Client packet StatusUpdate (broadcast).
1986 * @param reputation
1987 */
1988 @Override
1989 public void setReputation(int reputation)
1990 {
1991 // Notify to scripts.
1992 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerReputationChanged(this, getReputation(), reputation), this);
1993
1994 if (reputation > Config.MAX_REPUTATION) // Max count of positive reputation
1995 {
1996 reputation = Config.MAX_REPUTATION;
1997 }
1998
1999 if (getReputation() == reputation)
2000 {
2001 return;
2002 }
2003
2004 if ((getReputation() >= 0) && (reputation < 0))
2005 {
2006 L2World.getInstance().forEachVisibleObject(this, L2GuardInstance.class, object ->
2007 {
2008 if (object.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
2009 {
2010 object.getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
2011 }
2012 });
2013 }
2014
2015 super.setReputation(reputation);
2016
2017 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOUR_REPUTATION_HAS_BEEN_CHANGED_TO_S1).addInt(getReputation()));
2018 broadcastReputation();
2019 }
2020
2021 public int getExpertiseArmorPenalty()
2022 {
2023 return _expertiseArmorPenalty;
2024 }
2025
2026 public int getExpertiseWeaponPenalty()
2027 {
2028 return _expertiseWeaponPenalty;
2029 }
2030
2031 public int getExpertisePenaltyBonus()
2032 {
2033 return _expertisePenaltyBonus;
2034 }
2035
2036 public void setExpertisePenaltyBonus(int bonus)
2037 {
2038 _expertisePenaltyBonus = bonus;
2039 }
2040
2041 public int getWeightPenalty()
2042 {
2043 return _dietMode ? 0 : _curWeightPenalty;
2044 }
2045
2046 /**
2047 * Update the overloaded status of the L2PcInstance.
2048 * @param broadcast TODO
2049 */
2050 public void refreshOverloaded(boolean broadcast)
2051 {
2052 final int maxLoad = getMaxLoad();
2053 if (maxLoad > 0)
2054 {
2055 final long weightproc = (((getCurrentLoad() - getBonusWeightPenalty()) * 1000) / getMaxLoad());
2056 int newWeightPenalty;
2057 if ((weightproc < 500) || _dietMode)
2058 {
2059 newWeightPenalty = 0;
2060 }
2061 else if (weightproc < 666)
2062 {
2063 newWeightPenalty = 1;
2064 }
2065 else if (weightproc < 800)
2066 {
2067 newWeightPenalty = 2;
2068 }
2069 else if (weightproc < 1000)
2070 {
2071 newWeightPenalty = 3;
2072 }
2073 else
2074 {
2075 newWeightPenalty = 4;
2076 }
2077
2078 if (_curWeightPenalty != newWeightPenalty)
2079 {
2080 _curWeightPenalty = newWeightPenalty;
2081 if ((newWeightPenalty > 0) && !_dietMode)
2082 {
2083 addSkill(SkillData.getInstance().getSkill(CommonSkill.WEIGHT_PENALTY.getId(), newWeightPenalty));
2084 setIsOverloaded(getCurrentLoad() > maxLoad);
2085 }
2086 else
2087 {
2088 removeSkill(getKnownSkill(4270), false, true);
2089 setIsOverloaded(false);
2090 }
2091 if (broadcast)
2092 {
2093 sendPacket(new EtcStatusUpdate(this));
2094 broadcastUserInfo();
2095 }
2096 }
2097 }
2098 }
2099
2100 public void refreshExpertisePenalty()
2101 {
2102 if (!Config.EXPERTISE_PENALTY)
2103 {
2104 return;
2105 }
2106
2107 final CrystalType expertiseLevel = getExpertiseLevel().plusLevel(getExpertisePenaltyBonus());
2108
2109 int armorPenalty = 0;
2110 int weaponPenalty = 0;
2111
2112 for (L2ItemInstance item : getInventory().getPaperdollItems(item -> (item != null) && ((item.getItemType() != EtcItemType.ARROW) && (item.getItemType() != EtcItemType.BOLT)) && item.getItem().getCrystalType().isGreater(expertiseLevel)))
2113 {
2114 if (item.isArmor())
2115 {
2116 // Armor penalty level increases depending on amount of penalty armors equipped, not grade level difference.
2117 armorPenalty = CommonUtil.constrain(armorPenalty + 1, 0, 4);
2118 }
2119 else
2120 {
2121 // Weapon penalty level increases based on grade difference.
2122 weaponPenalty = CommonUtil.constrain(item.getItem().getCrystalType().getLevel() - expertiseLevel.getLevel(), 0, 4);
2123 }
2124 }
2125
2126 boolean changed = false;
2127
2128 if ((getExpertiseWeaponPenalty() != weaponPenalty) || (getSkillLevel(CommonSkill.WEAPON_GRADE_PENALTY.getId()) != weaponPenalty))
2129 {
2130 _expertiseWeaponPenalty = weaponPenalty;
2131 if (_expertiseWeaponPenalty > 0)
2132 {
2133 addSkill(SkillData.getInstance().getSkill(CommonSkill.WEAPON_GRADE_PENALTY.getId(), _expertiseWeaponPenalty));
2134 }
2135 else
2136 {
2137 removeSkill(CommonSkill.WEAPON_GRADE_PENALTY.getId(), true);
2138 }
2139 changed = true;
2140 }
2141
2142 if ((getExpertiseArmorPenalty() != armorPenalty) || (getSkillLevel(CommonSkill.ARMOR_GRADE_PENALTY.getId()) != armorPenalty))
2143 {
2144 _expertiseArmorPenalty = armorPenalty;
2145 if (_expertiseArmorPenalty > 0)
2146 {
2147 addSkill(SkillData.getInstance().getSkill(CommonSkill.ARMOR_GRADE_PENALTY.getId(), _expertiseArmorPenalty));
2148 }
2149 else
2150 {
2151 removeSkill(CommonSkill.ARMOR_GRADE_PENALTY.getId(), true);
2152 }
2153 changed = true;
2154 }
2155
2156 if (changed)
2157 {
2158 sendSkillList(); // Update expertise penalty icon in skill list.
2159 sendPacket(new EtcStatusUpdate(this));
2160 }
2161 }
2162
2163 public void useEquippableItem(L2ItemInstance item, boolean abortAttack)
2164 {
2165 // Equip or unEquip
2166 L2ItemInstance[] items = null;
2167 final boolean isEquiped = item.isEquipped();
2168 final int oldInvLimit = getInventoryLimit();
2169 SystemMessage sm = null;
2170
2171 if (isEquiped)
2172 {
2173 if (item.getEnchantLevel() > 0)
2174 {
2175 sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED);
2176 sm.addInt(item.getEnchantLevel());
2177 sm.addItemName(item);
2178 }
2179 else
2180 {
2181 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED);
2182 sm.addItemName(item);
2183 }
2184 sendPacket(sm);
2185
2186 final int slot = getInventory().getSlotFromItem(item);
2187 // we can't unequip talisman by body slot
2188 if ((slot == L2Item.SLOT_DECO) || (slot == L2Item.SLOT_BROOCH_JEWEL))
2189 {
2190 items = getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot());
2191 }
2192 else
2193 {
2194 items = getInventory().unEquipItemInBodySlotAndRecord(slot);
2195 }
2196 }
2197 else
2198 {
2199 items = getInventory().equipItemAndRecord(item);
2200
2201 if (item.isEquipped())
2202 {
2203 if (item.getEnchantLevel() > 0)
2204 {
2205 sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPPED_S1_S2);
2206 sm.addInt(item.getEnchantLevel());
2207 sm.addItemName(item);
2208 }
2209 else
2210 {
2211 sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EQUIPPED_YOUR_S1);
2212 sm.addItemName(item);
2213 }
2214 sendPacket(sm);
2215
2216 // Consume mana - will start a task if required; returns if item is not a shadow item
2217 item.decreaseMana(false);
2218
2219 if ((item.getItem().getBodyPart() & L2Item.SLOT_MULTI_ALLWEAPON) != 0)
2220 {
2221 rechargeShots(true, true, false);
2222 }
2223 }
2224 else
2225 {
2226 sendPacket(SystemMessageId.YOU_DO_NOT_MEET_THE_REQUIRED_CONDITION_TO_EQUIP_THAT_ITEM);
2227 }
2228 }
2229
2230 refreshExpertisePenalty();
2231
2232 broadcastUserInfo();
2233
2234 final InventoryUpdate iu = new InventoryUpdate();
2235 iu.addItems(Arrays.asList(items));
2236 sendInventoryUpdate(iu);
2237
2238 if (abortAttack)
2239 {
2240 abortAttack();
2241 }
2242
2243 if (getInventoryLimit() != oldInvLimit)
2244 {
2245 sendPacket(new ExStorageMaxCount(this));
2246 }
2247
2248 // Notify to scripts
2249 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerEquipItem(this, item), this);
2250 }
2251
2252 /**
2253 * @return the the PvP Kills of the L2PcInstance (Number of player killed during a PvP).
2254 */
2255 public int getPvpKills()
2256 {
2257 return _pvpKills;
2258 }
2259
2260 /**
2261 * Set the the PvP Kills of the L2PcInstance (Number of player killed during a PvP).
2262 * @param pvpKills
2263 */
2264 public void setPvpKills(int pvpKills)
2265 {
2266 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerPvPChanged(this, _pvpKills, pvpKills), this);
2267 _pvpKills = pvpKills;
2268 }
2269
2270 /**
2271 * @return the Fame of this L2PcInstance
2272 */
2273 public int getFame()
2274 {
2275 return _fame;
2276 }
2277
2278 /**
2279 * Set the Fame of this L2PcInstane
2280 * @param fame
2281 */
2282 public void setFame(int fame)
2283 {
2284 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerFameChanged(this, _fame, fame), this);
2285 _fame = (fame > Config.MAX_PERSONAL_FAME_POINTS) ? Config.MAX_PERSONAL_FAME_POINTS : fame;
2286 }
2287
2288 /**
2289 * @return the Raidboss points of this PlayerInstance
2290 */
2291 public int getRaidbossPoints()
2292 {
2293 return _raidbossPoints;
2294 }
2295
2296 /**
2297 * Set the Raidboss points of this PlayerInstance
2298 * @param points
2299 */
2300 public void setRaidbossPoints(int points)
2301 {
2302 _raidbossPoints = points;
2303 }
2304
2305 /**
2306 * Increase the Raidboss points of this PlayerInstance
2307 * @param increasePoints
2308 */
2309 public void increaseRaidbossPoints(int increasePoints)
2310 {
2311 setRaidbossPoints(getRaidbossPoints() + increasePoints);
2312 }
2313
2314 /**
2315 * @return the ClassId object of the L2PcInstance contained in L2PcTemplate.
2316 */
2317 public ClassId getClassId()
2318 {
2319 return getTemplate().getClassId();
2320 }
2321
2322 /**
2323 * Set the template of the L2PcInstance.
2324 * @param Id The Identifier of the L2PcTemplate to set to the L2PcInstance
2325 */
2326 public void setClassId(int Id)
2327 {
2328 if (!_subclassLock.tryLock())
2329 {
2330 return;
2331 }
2332
2333 try
2334 {
2335 if ((getLvlJoinedAcademy() != 0) && (_clan != null) && CategoryData.getInstance().isInCategory(CategoryType.THIRD_CLASS_GROUP, Id))
2336 {
2337 if (getLvlJoinedAcademy() <= 16)
2338 {
2339 _clan.addReputationScore(Config.JOIN_ACADEMY_MAX_REP_SCORE, true);
2340 }
2341 else if (getLvlJoinedAcademy() >= 39)
2342 {
2343 _clan.addReputationScore(Config.JOIN_ACADEMY_MIN_REP_SCORE, true);
2344 }
2345 else
2346 {
2347 _clan.addReputationScore((Config.JOIN_ACADEMY_MAX_REP_SCORE - ((getLvlJoinedAcademy() - 16) * 20)), true);
2348 }
2349 setLvlJoinedAcademy(0);
2350 // oust pledge member from the academy, cuz he has finished his 2nd class transfer
2351 final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.CLAN_MEMBER_S1_HAS_BEEN_EXPELLED);
2352 msg.addPcName(this);
2353 _clan.broadcastToOnlineMembers(msg);
2354 _clan.broadcastToOnlineMembers(new PledgeShowMemberListDelete(getName()));
2355 _clan.removeClanMember(getObjectId(), 0);
2356 sendPacket(SystemMessageId.CONGRATULATIONS_YOU_WILL_NOW_GRADUATE_FROM_THE_CLAN_ACADEMY_AND_LEAVE_YOUR_CURRENT_CLAN_YOU_CAN_NOW_JOIN_A_CLAN_WITHOUT_BEING_SUBJECT_TO_ANY_PENALTIES);
2357
2358 // receive graduation gift
2359 getInventory().addItem("Gift", 8181, 1, this, null); // give academy circlet
2360 }
2361 if (isSubClassActive())
2362 {
2363 getSubClasses().get(_classIndex).setClassId(Id);
2364 }
2365 setTarget(this);
2366 broadcastPacket(new MagicSkillUse(this, 5103, 1, 1000, 0));
2367 setClassTemplate(Id);
2368 if (isInCategory(CategoryType.FOURTH_CLASS_GROUP))
2369 {
2370 sendPacket(SystemMessageId.CONGRATULATIONS_YOU_VE_COMPLETED_YOUR_THIRD_CLASS_TRANSFER_QUEST);
2371 }
2372 else
2373 {
2374 sendPacket(SystemMessageId.CONGRATULATIONS_YOU_VE_COMPLETED_A_CLASS_TRANSFER);
2375 }
2376
2377 // Update class icon in party and clan
2378 if (isInParty())
2379 {
2380 getParty().broadcastPacket(new PartySmallWindowUpdate(this, true));
2381 }
2382
2383 if (getClan() != null)
2384 {
2385 getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this));
2386 }
2387
2388 sendPacket(new ExSubjobInfo(this, SubclassInfoType.CLASS_CHANGED));
2389
2390 // Add AutoGet skills and normal skills and/or learnByFS depending on configurations.
2391 rewardSkills();
2392
2393 if (!canOverrideCond(PcCondOverride.SKILL_CONDITIONS) && Config.DECREASE_SKILL_LEVEL)
2394 {
2395 checkPlayerSkills();
2396 }
2397
2398 notifyFriends(L2FriendStatus.MODE_CLASS);
2399 }
2400 finally
2401 {
2402 _subclassLock.unlock();
2403 }
2404 }
2405
2406 /**
2407 * @return the Experience of the L2PcInstance.
2408 */
2409 public long getExp()
2410 {
2411 return getStat().getExp();
2412 }
2413
2414 /**
2415 * Set the fists weapon of the L2PcInstance (used when no weapon is equiped).
2416 * @param weaponItem The fists L2Weapon to set to the L2PcInstance
2417 */
2418 public void setFistsWeaponItem(L2Weapon weaponItem)
2419 {
2420 _fistsWeaponItem = weaponItem;
2421 }
2422
2423 /**
2424 * @return the fists weapon of the L2PcInstance (used when no weapon is equipped).
2425 */
2426 public L2Weapon getFistsWeaponItem()
2427 {
2428 return _fistsWeaponItem;
2429 }
2430
2431 /**
2432 * @param classId
2433 * @return the fists weapon of the L2PcInstance Class (used when no weapon is equipped).
2434 */
2435 public L2Weapon findFistsWeaponItem(int classId)
2436 {
2437 L2Weapon weaponItem = null;
2438 if ((classId >= 0x00) && (classId <= 0x09))
2439 {
2440 // human fighter fists
2441 final L2Item temp = ItemTable.getInstance().getTemplate(246);
2442 weaponItem = (L2Weapon) temp;
2443 }
2444 else if ((classId >= 0x0a) && (classId <= 0x11))
2445 {
2446 // human mage fists
2447 final L2Item temp = ItemTable.getInstance().getTemplate(251);
2448 weaponItem = (L2Weapon) temp;
2449 }
2450 else if ((classId >= 0x12) && (classId <= 0x18))
2451 {
2452 // elven fighter fists
2453 final L2Item temp = ItemTable.getInstance().getTemplate(244);
2454 weaponItem = (L2Weapon) temp;
2455 }
2456 else if ((classId >= 0x19) && (classId <= 0x1e))
2457 {
2458 // elven mage fists
2459 final L2Item temp = ItemTable.getInstance().getTemplate(249);
2460 weaponItem = (L2Weapon) temp;
2461 }
2462 else if ((classId >= 0x1f) && (classId <= 0x25))
2463 {
2464 // dark elven fighter fists
2465 final L2Item temp = ItemTable.getInstance().getTemplate(245);
2466 weaponItem = (L2Weapon) temp;
2467 }
2468 else if ((classId >= 0x26) && (classId <= 0x2b))
2469 {
2470 // dark elven mage fists
2471 final L2Item temp = ItemTable.getInstance().getTemplate(250);
2472 weaponItem = (L2Weapon) temp;
2473 }
2474 else if ((classId >= 0x2c) && (classId <= 0x30))
2475 {
2476 // orc fighter fists
2477 final L2Item temp = ItemTable.getInstance().getTemplate(248);
2478 weaponItem = (L2Weapon) temp;
2479 }
2480 else if ((classId >= 0x31) && (classId <= 0x34))
2481 {
2482 // orc mage fists
2483 final L2Item temp = ItemTable.getInstance().getTemplate(252);
2484 weaponItem = (L2Weapon) temp;
2485 }
2486 else if ((classId >= 0x35) && (classId <= 0x39))
2487 {
2488 // dwarven fists
2489 final L2Item temp = ItemTable.getInstance().getTemplate(247);
2490 weaponItem = (L2Weapon) temp;
2491 }
2492
2493 return weaponItem;
2494 }
2495
2496 /**
2497 * This method reward all AutoGet skills and Normal skills if Auto-Learn configuration is true.
2498 */
2499 public void rewardSkills()
2500 {
2501 // Give all normal skills if activated Auto-Learn is activated, included AutoGet skills.
2502 if (Config.AUTO_LEARN_SKILLS)
2503 {
2504 giveAvailableSkills(Config.AUTO_LEARN_FS_SKILLS, true);
2505 }
2506 else
2507 {
2508 giveAvailableAutoGetSkills();
2509 }
2510
2511 if (Config.DECREASE_SKILL_LEVEL && !canOverrideCond(PcCondOverride.SKILL_CONDITIONS))
2512 {
2513 checkPlayerSkills();
2514 }
2515
2516 checkItemRestriction();
2517 sendSkillList();
2518 }
2519
2520 /**
2521 * Re-give all skills which aren't saved to database, like Noble, Hero, Clan Skills.<br>
2522 */
2523 public void regiveTemporarySkills()
2524 {
2525 // Do not call this on enterworld or char load
2526
2527 // Add noble skills if noble
2528 if (_nobleLevel > 0)
2529 {
2530 setNobleLevel(_nobleLevel);
2531 }
2532
2533 // Add Hero skills if hero
2534 if (isHero())
2535 {
2536 setHero(true);
2537 }
2538
2539 // Add clan skills
2540 if (getClan() != null)
2541 {
2542 final L2Clan clan = getClan();
2543 clan.addSkillEffects(this);
2544
2545 if ((clan.getLevel() >= SiegeManager.getInstance().getSiegeClanMinLevel()) && isClanLeader())
2546 {
2547 SiegeManager.getInstance().addSiegeSkills(this);
2548 }
2549 if (getClan().getCastleId() > 0)
2550 {
2551 CastleManager.getInstance().getCastleByOwner(getClan()).giveResidentialSkills(this);
2552 }
2553 if (getClan().getFortId() > 0)
2554 {
2555 FortManager.getInstance().getFortByOwner(getClan()).giveResidentialSkills(this);
2556 }
2557 }
2558
2559 // Reload passive skills from armors / jewels / weapons
2560 getInventory().reloadEquippedItems();
2561 }
2562
2563 /**
2564 * Give all available skills to the player.
2565 * @param includedByFs if {@code true} forgotten scroll skills present in the skill tree will be added
2566 * @param includeAutoGet if {@code true} auto-get skills present in the skill tree will be added
2567 * @return the amount of new skills earned
2568 */
2569 public int giveAvailableSkills(boolean includedByFs, boolean includeAutoGet)
2570 {
2571 int skillCounter = 0;
2572 // Get available skills
2573 final Collection<Skill> skills = SkillTreesData.getInstance().getAllAvailableSkills(this, getTemplate().getClassId(), includedByFs, includeAutoGet);
2574 final List<Skill> skillsForStore = new ArrayList<>();
2575
2576 for (Skill skill : skills)
2577 {
2578 if (getKnownSkill(skill.getId()) == skill)
2579 {
2580 continue;
2581 }
2582
2583 if (getSkillLevel(skill.getId()) == 0)
2584 {
2585 skillCounter++;
2586 }
2587
2588 // fix when learning toggle skills
2589 if (skill.isToggle() && !skill.isNecessaryToggle() && isAffectedBySkill(skill.getId()))
2590 {
2591 stopSkillEffects(true, skill.getId());
2592 }
2593
2594 addSkill(skill, false);
2595 skillsForStore.add(skill);
2596 }
2597 storeSkills(skillsForStore, -1);
2598 if (Config.AUTO_LEARN_SKILLS && (skillCounter > 0))
2599 {
2600 sendMessage("You have learned " + skillCounter + " new skills.");
2601 }
2602 return skillCounter;
2603 }
2604
2605 /**
2606 * Give all available auto-get skills to the player.
2607 */
2608 public void giveAvailableAutoGetSkills()
2609 {
2610 // Get available skills
2611 final List<L2SkillLearn> autoGetSkills = SkillTreesData.getInstance().getAvailableAutoGetSkills(this);
2612 final SkillData st = SkillData.getInstance();
2613 Skill skill;
2614 for (L2SkillLearn s : autoGetSkills)
2615 {
2616 skill = st.getSkill(s.getSkillId(), s.getSkillLevel());
2617 if (skill != null)
2618 {
2619 addSkill(skill, true);
2620 }
2621 else
2622 {
2623 LOGGER.warning("Skipping null auto-get skill for player: " + toString());
2624 }
2625 }
2626 }
2627
2628 /**
2629 * Set the Experience value of the L2PcInstance.
2630 * @param exp
2631 */
2632 public void setExp(long exp)
2633 {
2634 if (exp < 0)
2635 {
2636 exp = 0;
2637 }
2638
2639 getStat().setExp(exp);
2640 }
2641
2642 /**
2643 * @return the Race object of the L2PcInstance.
2644 */
2645 @Override
2646 public Race getRace()
2647 {
2648 final ClassId originalClass = getOriginalClass();
2649 if (originalClass != null)
2650 {
2651 return originalClass.getRace();
2652 }
2653
2654 if (!isSubClassActive())
2655 {
2656 return getTemplate().getRace();
2657 }
2658 return PlayerTemplateData.getInstance().getTemplate(_baseClass).getRace();
2659 }
2660
2661 public L2Radar getRadar()
2662 {
2663 return _radar;
2664 }
2665
2666 /**
2667 * @return the SP amount of the L2PcInstance.
2668 */
2669 public long getSp()
2670 {
2671 return getStat().getSp();
2672 }
2673
2674 /**
2675 * Set the SP amount of the L2PcInstance.
2676 * @param sp
2677 */
2678 public void setSp(long sp)
2679 {
2680 if (sp < 0)
2681 {
2682 sp = 0;
2683 }
2684
2685 super.getStat().setSp(sp);
2686 }
2687
2688 /**
2689 * @param castleId
2690 * @return true if this L2PcInstance is a clan leader in ownership of the passed castle
2691 */
2692 public boolean isCastleLord(int castleId)
2693 {
2694 final L2Clan clan = getClan();
2695
2696 // player has clan and is the clan leader, check the castle info
2697 if ((clan != null) && (clan.getLeader().getPlayerInstance() == this))
2698 {
2699 // if the clan has a castle and it is actually the queried castle, return true
2700 final Castle castle = CastleManager.getInstance().getCastleByOwner(clan);
2701 if ((castle != null) && (castle == CastleManager.getInstance().getCastleById(castleId)))
2702 {
2703 return true;
2704 }
2705 }
2706
2707 return false;
2708 }
2709
2710 /**
2711 * @return the Clan Identifier of the L2PcInstance.
2712 */
2713 @Override
2714 public int getClanId()
2715 {
2716 return _clanId;
2717 }
2718
2719 /**
2720 * @return the Clan Crest Identifier of the L2PcInstance or 0.
2721 */
2722 public int getClanCrestId()
2723 {
2724 if (_clan != null)
2725 {
2726 return _clan.getCrestId();
2727 }
2728
2729 return 0;
2730 }
2731
2732 /**
2733 * @return The Clan CrestLarge Identifier or 0
2734 */
2735 public int getClanCrestLargeId()
2736 {
2737 if ((_clan != null) && ((_clan.getCastleId() != 0) || (_clan.getHideoutId() != 0)))
2738 {
2739 return _clan.getCrestLargeId();
2740 }
2741 return 0;
2742 }
2743
2744 public long getClanJoinExpiryTime()
2745 {
2746 return _clanJoinExpiryTime;
2747 }
2748
2749 public void setClanJoinExpiryTime(long time)
2750 {
2751 _clanJoinExpiryTime = time;
2752 }
2753
2754 public long getClanCreateExpiryTime()
2755 {
2756 return _clanCreateExpiryTime;
2757 }
2758
2759 public void setClanCreateExpiryTime(long time)
2760 {
2761 _clanCreateExpiryTime = time;
2762 }
2763
2764 public void setOnlineTime(long time)
2765 {
2766 _onlineTime = time;
2767 _onlineBeginTime = System.currentTimeMillis();
2768 }
2769
2770 /**
2771 * Return the PcInventory Inventory of the L2PcInstance contained in _inventory.
2772 */
2773 @Override
2774 public PcInventory getInventory()
2775 {
2776 return _inventory;
2777 }
2778
2779 /**
2780 * Delete a ShortCut of the L2PcInstance _shortCuts.
2781 * @param objectId
2782 */
2783 public void removeItemFromShortCut(int objectId)
2784 {
2785 _shortCuts.deleteShortCutByObjectId(objectId);
2786 }
2787
2788 /**
2789 * @return True if the L2PcInstance is sitting.
2790 */
2791 public boolean isSitting()
2792 {
2793 return _waitTypeSitting;
2794 }
2795
2796 /**
2797 * Set _waitTypeSitting to given value
2798 * @param state
2799 */
2800 public void setIsSitting(boolean state)
2801 {
2802 _waitTypeSitting = state;
2803 }
2804
2805 /**
2806 * Sit down the L2PcInstance, set the AI Intention to AI_INTENTION_REST and send a Server->Client ChangeWaitType packet (broadcast)
2807 */
2808 public void sitDown()
2809 {
2810 sitDown(true);
2811 }
2812
2813 public void sitDown(boolean checkCast)
2814 {
2815 if (checkCast && isCastingNow())
2816 {
2817 sendMessage("Cannot sit while casting");
2818 return;
2819 }
2820
2821 if (!_waitTypeSitting && !isAttackingDisabled() && !isControlBlocked() && !isImmobilized() && !isFishing())
2822 {
2823 breakAttack();
2824 setIsSitting(true);
2825 getAI().setIntention(CtrlIntention.AI_INTENTION_REST);
2826 broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING));
2827 // Schedule a sit down task to wait for the animation to finish
2828 ThreadPool.schedule(new SitDownTask(this), 2500);
2829 setBlockActions(true);
2830 }
2831 }
2832
2833 /**
2834 * Stand up the L2PcInstance, set the AI Intention to AI_INTENTION_IDLE and send a Server->Client ChangeWaitType packet (broadcast)
2835 */
2836 public void standUp()
2837 {
2838 if (L2Event.isParticipant(this) && getEventStatus().isSitForced())
2839 {
2840 sendMessage("A dark force beyond your mortal understanding makes your knees to shake when you try to stand up...");
2841 }
2842 else if (_waitTypeSitting && !isInStoreMode() && !isAlikeDead())
2843 {
2844 if (getEffectList().isAffected(EffectFlag.RELAXING))
2845 {
2846 stopEffects(EffectFlag.RELAXING);
2847 }
2848
2849 broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING));
2850 // Schedule a stand up task to wait for the animation to finish
2851 ThreadPool.schedule(new StandUpTask(this), 2500);
2852 }
2853 }
2854
2855 /**
2856 * @return the PcWarehouse object of the L2PcInstance.
2857 */
2858 public PcWarehouse getWarehouse()
2859 {
2860 if (_warehouse == null)
2861 {
2862 _warehouse = new PcWarehouse(this);
2863 _warehouse.restore();
2864 }
2865 if (Config.WAREHOUSE_CACHE)
2866 {
2867 WarehouseCacheManager.getInstance().addCacheTask(this);
2868 }
2869 return _warehouse;
2870 }
2871
2872 /**
2873 * Free memory used by Warehouse
2874 */
2875 public void clearWarehouse()
2876 {
2877 if (_warehouse != null)
2878 {
2879 _warehouse.deleteMe();
2880 }
2881 _warehouse = null;
2882 }
2883
2884 /**
2885 * @return the PcFreight object of the L2PcInstance.
2886 */
2887 public PcFreight getFreight()
2888 {
2889 return _freight;
2890 }
2891
2892 /**
2893 * @return true if refund list is not empty
2894 */
2895 public boolean hasRefund()
2896 {
2897 return (_refund != null) && (_refund.getSize() > 0) && Config.ALLOW_REFUND;
2898 }
2899
2900 /**
2901 * @return refund object or create new if not exist
2902 */
2903 public PcRefund getRefund()
2904 {
2905 if (_refund == null)
2906 {
2907 _refund = new PcRefund(this);
2908 }
2909 return _refund;
2910 }
2911
2912 /**
2913 * Clear refund
2914 */
2915 public void clearRefund()
2916 {
2917 if (_refund != null)
2918 {
2919 _refund.deleteMe();
2920 }
2921 _refund = null;
2922 }
2923
2924 /**
2925 * @return the Adena amount of the L2PcInstance.
2926 */
2927 public long getAdena()
2928 {
2929 return _inventory.getAdena();
2930 }
2931
2932 /**
2933 * @return the Ancient Adena amount of the L2PcInstance.
2934 */
2935 public long getAncientAdena()
2936 {
2937 return _inventory.getAncientAdena();
2938 }
2939
2940 /**
2941 * @return the Beauty Tickets of the L2PcInstance.
2942 */
2943 public long getBeautyTickets()
2944 {
2945 return _inventory.getBeautyTickets();
2946 }
2947
2948 /**
2949 * Add adena to Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance.
2950 * @param process : String Identifier of process triggering this action
2951 * @param count : int Quantity of adena to be added
2952 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
2953 * @param sendMessage : boolean Specifies whether to send message to Client about this action
2954 */
2955 public void addAdena(String process, long count, L2Object reference, boolean sendMessage)
2956 {
2957 if (sendMessage)
2958 {
2959 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_ADENA);
2960 sm.addLong(count);
2961 sendPacket(sm);
2962 }
2963
2964 if (count > 0)
2965 {
2966 _inventory.addAdena(process, count, this, reference);
2967
2968 // Send update packet
2969 if (!Config.FORCE_INVENTORY_UPDATE)
2970 {
2971 final InventoryUpdate iu = new InventoryUpdate();
2972 iu.addItem(_inventory.getAdenaInstance());
2973 sendInventoryUpdate(iu);
2974 }
2975 else
2976 {
2977 sendItemList(false);
2978 }
2979 }
2980 }
2981
2982 /**
2983 * Reduce adena in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance.
2984 * @param process : String Identifier of process triggering this action
2985 * @param count : long Quantity of adena to be reduced
2986 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
2987 * @param sendMessage : boolean Specifies whether to send message to Client about this action
2988 * @return boolean informing if the action was successful
2989 */
2990 public boolean reduceAdena(String process, long count, L2Object reference, boolean sendMessage)
2991 {
2992 if (count > getAdena())
2993 {
2994 if (sendMessage)
2995 {
2996 sendPacket(SystemMessageId.YOU_DO_NOT_HAVE_ENOUGH_ADENA);
2997 }
2998 return false;
2999 }
3000
3001 if (count > 0)
3002 {
3003 final L2ItemInstance adenaItem = _inventory.getAdenaInstance();
3004 if (!_inventory.reduceAdena(process, count, this, reference))
3005 {
3006 return false;
3007 }
3008
3009 // Send update packet
3010 if (!Config.FORCE_INVENTORY_UPDATE)
3011 {
3012 final InventoryUpdate iu = new InventoryUpdate();
3013 iu.addItem(adenaItem);
3014 sendInventoryUpdate(iu);
3015 }
3016 else
3017 {
3018 sendItemList(false);
3019 }
3020
3021 if (sendMessage)
3022 {
3023 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_ADENA_DISAPPEARED);
3024 sm.addLong(count);
3025 sendPacket(sm);
3026 }
3027 }
3028
3029 return true;
3030 }
3031
3032 /**
3033 * Reduce Beauty Tickets in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3034 * @param process : String Identifier of process triggering this action
3035 * @param count : long Quantity of Beauty Tickets to be reduced
3036 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3037 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3038 * @return boolean informing if the action was successful
3039 */
3040 public boolean reduceBeautyTickets(String process, long count, L2Object reference, boolean sendMessage)
3041 {
3042 if (count > getBeautyTickets())
3043 {
3044 if (sendMessage)
3045 {
3046 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3047 }
3048 return false;
3049 }
3050
3051 if (count > 0)
3052 {
3053 final L2ItemInstance beautyTickets = _inventory.getBeautyTicketsInstance();
3054 if (!_inventory.reduceBeautyTickets(process, count, this, reference))
3055 {
3056 return false;
3057 }
3058
3059 // Send update packet
3060 if (!Config.FORCE_INVENTORY_UPDATE)
3061 {
3062 final InventoryUpdate iu = new InventoryUpdate();
3063 iu.addItem(beautyTickets);
3064 sendInventoryUpdate(iu);
3065 }
3066 else
3067 {
3068 sendItemList(false);
3069 }
3070
3071 if (sendMessage)
3072 {
3073 if (count > 1)
3074 {
3075 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED);
3076 sm.addItemName(Inventory.BEAUTY_TICKET_ID);
3077 sm.addLong(count);
3078 sendPacket(sm);
3079 }
3080 else
3081 {
3082 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
3083 sm.addItemName(Inventory.BEAUTY_TICKET_ID);
3084 sendPacket(sm);
3085 }
3086 }
3087 }
3088
3089 return true;
3090 }
3091
3092 /**
3093 * Add ancient adena to Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3094 * @param process : String Identifier of process triggering this action
3095 * @param count : int Quantity of ancient adena to be added
3096 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3097 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3098 */
3099 public void addAncientAdena(String process, long count, L2Object reference, boolean sendMessage)
3100 {
3101 if (sendMessage)
3102 {
3103 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
3104 sm.addItemName(Inventory.ANCIENT_ADENA_ID);
3105 sm.addLong(count);
3106 sendPacket(sm);
3107 }
3108
3109 if (count > 0)
3110 {
3111 _inventory.addAncientAdena(process, count, this, reference);
3112
3113 if (!Config.FORCE_INVENTORY_UPDATE)
3114 {
3115 final InventoryUpdate iu = new InventoryUpdate();
3116 iu.addItem(_inventory.getAncientAdenaInstance());
3117 sendInventoryUpdate(iu);
3118 }
3119 else
3120 {
3121 sendItemList(false);
3122 }
3123 }
3124 }
3125
3126 /**
3127 * Reduce ancient adena in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3128 * @param process : String Identifier of process triggering this action
3129 * @param count : long Quantity of ancient adena to be reduced
3130 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3131 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3132 * @return boolean informing if the action was successful
3133 */
3134 public boolean reduceAncientAdena(String process, long count, L2Object reference, boolean sendMessage)
3135 {
3136 if (count > getAncientAdena())
3137 {
3138 if (sendMessage)
3139 {
3140 sendPacket(SystemMessageId.YOU_DO_NOT_HAVE_ENOUGH_ADENA);
3141 }
3142
3143 return false;
3144 }
3145
3146 if (count > 0)
3147 {
3148 final L2ItemInstance ancientAdenaItem = _inventory.getAncientAdenaInstance();
3149 if (!_inventory.reduceAncientAdena(process, count, this, reference))
3150 {
3151 return false;
3152 }
3153
3154 if (!Config.FORCE_INVENTORY_UPDATE)
3155 {
3156 final InventoryUpdate iu = new InventoryUpdate();
3157 iu.addItem(ancientAdenaItem);
3158 sendInventoryUpdate(iu);
3159 }
3160 else
3161 {
3162 sendItemList(false);
3163 }
3164
3165 if (sendMessage)
3166 {
3167 if (count > 1)
3168 {
3169 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED);
3170 sm.addItemName(Inventory.ANCIENT_ADENA_ID);
3171 sm.addLong(count);
3172 sendPacket(sm);
3173 }
3174 else
3175 {
3176 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
3177 sm.addItemName(Inventory.ANCIENT_ADENA_ID);
3178 sendPacket(sm);
3179 }
3180 }
3181 }
3182
3183 return true;
3184 }
3185
3186 /**
3187 * Adds item to inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3188 * @param process : String Identifier of process triggering this action
3189 * @param item : L2ItemInstance to be added
3190 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3191 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3192 */
3193 public void addItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage)
3194 {
3195 if (item.getCount() > 0)
3196 {
3197 // Sends message to client if requested
3198 if (sendMessage)
3199 {
3200 if (item.getCount() > 1)
3201 {
3202 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S2_S1);
3203 sm.addItemName(item);
3204 sm.addLong(item.getCount());
3205 sendPacket(sm);
3206 }
3207 else if (item.getEnchantLevel() > 0)
3208 {
3209 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_A_S1_S2);
3210 sm.addInt(item.getEnchantLevel());
3211 sm.addItemName(item);
3212 sendPacket(sm);
3213 }
3214 else
3215 {
3216 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S1);
3217 sm.addItemName(item);
3218 sendPacket(sm);
3219 }
3220 }
3221
3222 // Add the item to inventory
3223 final L2ItemInstance newitem = _inventory.addItem(process, item, this, reference);
3224
3225 // If over capacity, drop the item
3226 if (!canOverrideCond(PcCondOverride.ITEM_CONDITIONS) && !_inventory.validateCapacity(0, item.isQuestItem()) && newitem.isDropable() && (!newitem.isStackable() || (newitem.getLastChange() != L2ItemInstance.MODIFIED)))
3227 {
3228 dropItem("InvDrop", newitem, null, true, true);
3229 }
3230 else if (CursedWeaponsManager.getInstance().isCursed(newitem.getId()))
3231 {
3232 CursedWeaponsManager.getInstance().activate(this, newitem);
3233 }
3234
3235 // Combat Flag
3236 else if (FortSiegeManager.getInstance().isCombat(item.getId()))
3237 {
3238 if (FortSiegeManager.getInstance().activateCombatFlag(this, item))
3239 {
3240 final Fort fort = FortManager.getInstance().getFort(this);
3241 fort.getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_ACQUIRED_THE_FLAG), getName());
3242 }
3243 }
3244 }
3245 }
3246
3247 /**
3248 * Adds item to Inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3249 * @param process : String Identifier of process triggering this action
3250 * @param itemId : int Item Identifier of the item to be added
3251 * @param count : long Quantity of items to be added
3252 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3253 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3254 * @return
3255 */
3256 public L2ItemInstance addItem(String process, int itemId, long count, L2Object reference, boolean sendMessage)
3257 {
3258 if (count > 0)
3259 {
3260 final L2Item item = ItemTable.getInstance().getTemplate(itemId);
3261 if (item == null)
3262 {
3263 LOGGER.severe("Item doesn't exist so cannot be added. Item ID: " + itemId);
3264 return null;
3265 }
3266 // Sends message to client if requested
3267 if (sendMessage && ((!isCastingNow() && item.hasExImmediateEffect()) || !item.hasExImmediateEffect()))
3268 {
3269 if (count > 1)
3270 {
3271 if (process.equalsIgnoreCase("Sweeper") || process.equalsIgnoreCase("Quest"))
3272 {
3273 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
3274 sm.addItemName(itemId);
3275 sm.addLong(count);
3276 sendPacket(sm);
3277 }
3278 else
3279 {
3280 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S2_S1);
3281 sm.addItemName(itemId);
3282 sm.addLong(count);
3283 sendPacket(sm);
3284 }
3285 }
3286 else if (process.equalsIgnoreCase("Sweeper") || process.equalsIgnoreCase("Quest"))
3287 {
3288 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1);
3289 sm.addItemName(itemId);
3290 sendPacket(sm);
3291 }
3292 else
3293 {
3294 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S1);
3295 sm.addItemName(itemId);
3296 sendPacket(sm);
3297 }
3298 }
3299
3300 // Auto-use herbs.
3301 if (item.hasExImmediateEffect())
3302 {
3303 final IItemHandler handler = ItemHandler.getInstance().getHandler(item instanceof L2EtcItem ? (L2EtcItem) item : null);
3304 if (handler == null)
3305 {
3306 LOGGER.warning("No item handler registered for Herb ID " + item.getId() + "!");
3307 }
3308 else
3309 {
3310 handler.useItem(this, new L2ItemInstance(itemId), false);
3311 }
3312 }
3313 else
3314 {
3315 // Add the item to inventory
3316 final L2ItemInstance createdItem = _inventory.addItem(process, itemId, count, this, reference);
3317
3318 // If over capacity, drop the item
3319 if (!canOverrideCond(PcCondOverride.ITEM_CONDITIONS) && !_inventory.validateCapacity(0, item.isQuestItem()) && createdItem.isDropable() && (!createdItem.isStackable() || (createdItem.getLastChange() != L2ItemInstance.MODIFIED)))
3320 {
3321 dropItem("InvDrop", createdItem, null, true);
3322 }
3323 else if (CursedWeaponsManager.getInstance().isCursed(createdItem.getId()))
3324 {
3325 CursedWeaponsManager.getInstance().activate(this, createdItem);
3326 }
3327 return createdItem;
3328 }
3329 }
3330 return null;
3331 }
3332
3333 /**
3334 * @param process the process name
3335 * @param item the item holder
3336 * @param reference the reference object
3337 * @param sendMessage if {@code true} a system message will be sent
3338 */
3339 public void addItem(String process, ItemHolder item, L2Object reference, boolean sendMessage)
3340 {
3341 addItem(process, item.getId(), item.getCount(), reference, sendMessage);
3342 }
3343
3344 /**
3345 * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3346 * @param process : String Identifier of process triggering this action
3347 * @param item : L2ItemInstance to be destroyed
3348 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3349 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3350 * @return boolean informing if the action was successful
3351 */
3352 public boolean destroyItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage)
3353 {
3354 return destroyItem(process, item, item.getCount(), reference, sendMessage);
3355 }
3356
3357 /**
3358 * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3359 * @param process : String Identifier of process triggering this action
3360 * @param item : L2ItemInstance to be destroyed
3361 * @param count
3362 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3363 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3364 * @return boolean informing if the action was successful
3365 */
3366 public boolean destroyItem(String process, L2ItemInstance item, long count, L2Object reference, boolean sendMessage)
3367 {
3368 item = _inventory.destroyItem(process, item, count, this, reference);
3369
3370 if (item == null)
3371 {
3372 if (sendMessage)
3373 {
3374 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3375 }
3376 return false;
3377 }
3378
3379 // Send inventory update packet
3380 if (!Config.FORCE_INVENTORY_UPDATE)
3381 {
3382 final InventoryUpdate playerIU = new InventoryUpdate();
3383 playerIU.addItem(item);
3384 sendInventoryUpdate(playerIU);
3385 }
3386 else
3387 {
3388 sendItemList(false);
3389 }
3390
3391 // Sends message to client if requested
3392 if (sendMessage)
3393 {
3394 if (count > 1)
3395 {
3396 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED);
3397 sm.addItemName(item);
3398 sm.addLong(count);
3399 sendPacket(sm);
3400 }
3401 else
3402 {
3403 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
3404 sm.addItemName(item);
3405 sendPacket(sm);
3406 }
3407 }
3408
3409 return true;
3410 }
3411
3412 /**
3413 * Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3414 * @param process : String Identifier of process triggering this action
3415 * @param objectId : int Item Instance identifier of the item to be destroyed
3416 * @param count : int Quantity of items to be destroyed
3417 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3418 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3419 * @return boolean informing if the action was successful
3420 */
3421 @Override
3422 public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage)
3423 {
3424 final L2ItemInstance item = _inventory.getItemByObjectId(objectId);
3425
3426 if (item == null)
3427 {
3428 if (sendMessage)
3429 {
3430 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3431 }
3432
3433 return false;
3434 }
3435 return destroyItem(process, item, count, reference, sendMessage);
3436 }
3437
3438 /**
3439 * Destroys shots from inventory without logging and only occasional saving to database. Sends a Server->Client InventoryUpdate packet to the L2PcInstance.
3440 * @param process : String Identifier of process triggering this action
3441 * @param objectId : int Item Instance identifier of the item to be destroyed
3442 * @param count : int Quantity of items to be destroyed
3443 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3444 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3445 * @return boolean informing if the action was successful
3446 */
3447 public boolean destroyItemWithoutTrace(String process, int objectId, long count, L2Object reference, boolean sendMessage)
3448 {
3449 final L2ItemInstance item = _inventory.getItemByObjectId(objectId);
3450
3451 if ((item == null) || (item.getCount() < count))
3452 {
3453 if (sendMessage)
3454 {
3455 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3456 }
3457
3458 return false;
3459 }
3460
3461 return destroyItem(null, item, count, reference, sendMessage);
3462 }
3463
3464 /**
3465 * Destroy item from inventory by using its <B>itemId</B> and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3466 * @param process : String Identifier of process triggering this action
3467 * @param itemId : int Item identifier of the item to be destroyed
3468 * @param count : int Quantity of items to be destroyed
3469 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3470 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3471 * @return boolean informing if the action was successful
3472 */
3473 @Override
3474 public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference, boolean sendMessage)
3475 {
3476 if (itemId == Inventory.ADENA_ID)
3477 {
3478 return reduceAdena(process, count, reference, sendMessage);
3479 }
3480
3481 final L2ItemInstance item = _inventory.getItemByItemId(itemId);
3482
3483 if ((item == null) || (item.getCount() < count) || (_inventory.destroyItemByItemId(process, itemId, count, this, reference) == null))
3484 {
3485 if (sendMessage)
3486 {
3487 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3488 }
3489
3490 return false;
3491 }
3492
3493 // Send inventory update packet
3494 if (!Config.FORCE_INVENTORY_UPDATE)
3495 {
3496 final InventoryUpdate playerIU = new InventoryUpdate();
3497 playerIU.addItem(item);
3498 sendInventoryUpdate(playerIU);
3499 }
3500 else
3501 {
3502 sendItemList(false);
3503 }
3504
3505 // Sends message to client if requested
3506 if (sendMessage)
3507 {
3508 if (count > 1)
3509 {
3510 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED);
3511 sm.addItemName(itemId);
3512 sm.addLong(count);
3513 sendPacket(sm);
3514 }
3515 else
3516 {
3517 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
3518 sm.addItemName(itemId);
3519 sendPacket(sm);
3520 }
3521 }
3522
3523 return true;
3524 }
3525
3526 /**
3527 * Transfers item to another ItemContainer and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3528 * @param process : String Identifier of process triggering this action
3529 * @param objectId : int Item Identifier of the item to be transfered
3530 * @param count : long Quantity of items to be transfered
3531 * @param target
3532 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3533 * @return L2ItemInstance corresponding to the new item or the updated item in inventory
3534 */
3535 public L2ItemInstance transferItem(String process, int objectId, long count, Inventory target, L2Object reference)
3536 {
3537 final L2ItemInstance oldItem = checkItemManipulation(objectId, count, "transfer");
3538 if (oldItem == null)
3539 {
3540 return null;
3541 }
3542 final L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, this, reference);
3543 if (newItem == null)
3544 {
3545 return null;
3546 }
3547
3548 // Send inventory update packet
3549 if (!Config.FORCE_INVENTORY_UPDATE)
3550 {
3551 final InventoryUpdate playerIU = new InventoryUpdate();
3552
3553 if ((oldItem.getCount() > 0) && (oldItem != newItem))
3554 {
3555 playerIU.addModifiedItem(oldItem);
3556 }
3557 else
3558 {
3559 playerIU.addRemovedItem(oldItem);
3560 }
3561
3562 sendInventoryUpdate(playerIU);
3563 }
3564 else
3565 {
3566 sendItemList(false);
3567 }
3568
3569 // Send target update packet
3570 if (target instanceof PcInventory)
3571 {
3572 final L2PcInstance targetPlayer = ((PcInventory) target).getOwner();
3573
3574 if (!Config.FORCE_INVENTORY_UPDATE)
3575 {
3576 final InventoryUpdate playerIU = new InventoryUpdate();
3577
3578 if (newItem.getCount() > count)
3579 {
3580 playerIU.addModifiedItem(newItem);
3581 }
3582 else
3583 {
3584 playerIU.addNewItem(newItem);
3585 }
3586
3587 targetPlayer.sendPacket(playerIU);
3588 }
3589 else
3590 {
3591 targetPlayer.sendItemList(false);
3592 }
3593 }
3594 else if (target instanceof PetInventory)
3595 {
3596 final PetInventoryUpdate petIU = new PetInventoryUpdate();
3597
3598 if (newItem.getCount() > count)
3599 {
3600 petIU.addModifiedItem(newItem);
3601 }
3602 else
3603 {
3604 petIU.addNewItem(newItem);
3605 }
3606
3607 ((PetInventory) target).getOwner().sendPacket(petIU);
3608 }
3609 return newItem;
3610 }
3611
3612 /**
3613 * Use instead of calling {@link #addItem(String, L2ItemInstance, L2Object, boolean)} and {@link #destroyItemByItemId(String, int, long, L2Object, boolean)}<br>
3614 * This method validates slots and weight limit, for stackable and non-stackable items.
3615 * @param process a generic string representing the process that is exchanging this items
3616 * @param reference the (probably NPC) reference, could be null
3617 * @param coinId the item Id of the item given on the exchange
3618 * @param cost the amount of items given on the exchange
3619 * @param rewardId the item received on the exchange
3620 * @param count the amount of items received on the exchange
3621 * @param sendMessage if {@code true} it will send messages to the acting player
3622 * @return {@code true} if the player successfully exchanged the items, {@code false} otherwise
3623 */
3624 public boolean exchangeItemsById(String process, L2Object reference, int coinId, long cost, int rewardId, long count, boolean sendMessage)
3625 {
3626 final PcInventory inv = getInventory();
3627 if (!inv.validateCapacityByItemId(rewardId, count))
3628 {
3629 if (sendMessage)
3630 {
3631 sendPacket(SystemMessageId.YOUR_INVENTORY_IS_FULL);
3632 }
3633 return false;
3634 }
3635
3636 if (!inv.validateWeightByItemId(rewardId, count))
3637 {
3638 if (sendMessage)
3639 {
3640 sendPacket(SystemMessageId.YOU_HAVE_EXCEEDED_THE_WEIGHT_LIMIT);
3641 }
3642 return false;
3643 }
3644
3645 if (destroyItemByItemId(process, coinId, cost, reference, sendMessage))
3646 {
3647 addItem(process, rewardId, count, reference, sendMessage);
3648 return true;
3649 }
3650 return false;
3651 }
3652
3653 /**
3654 * Drop item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3655 * @param process String Identifier of process triggering this action
3656 * @param item L2ItemInstance to be dropped
3657 * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation
3658 * @param sendMessage boolean Specifies whether to send message to Client about this action
3659 * @param protectItem whether or not dropped item must be protected temporary against other players
3660 * @return boolean informing if the action was successful
3661 */
3662 public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage, boolean protectItem)
3663 {
3664 item = _inventory.dropItem(process, item, this, reference);
3665
3666 if (item == null)
3667 {
3668 if (sendMessage)
3669 {
3670 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3671 }
3672
3673 return false;
3674 }
3675
3676 item.dropMe(this, (getX() + Rnd.get(50)) - 25, (getY() + Rnd.get(50)) - 25, getZ() + 20);
3677
3678 if ((Config.AUTODESTROY_ITEM_AFTER > 0) && Config.DESTROY_DROPPED_PLAYER_ITEM && !Config.LIST_PROTECTED_ITEMS.contains(item.getId()))
3679 {
3680 if ((item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM) || !item.isEquipable())
3681 {
3682 ItemsAutoDestroy.getInstance().addItem(item);
3683 }
3684 }
3685
3686 // protection against auto destroy dropped item
3687 if (Config.DESTROY_DROPPED_PLAYER_ITEM)
3688 {
3689 if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM))
3690 {
3691 item.setProtected(false);
3692 }
3693 else
3694 {
3695 item.setProtected(true);
3696 }
3697 }
3698 else
3699 {
3700 item.setProtected(true);
3701 }
3702
3703 // retail drop protection
3704 if (protectItem)
3705 {
3706 item.getDropProtection().protect(this);
3707 }
3708
3709 // Send inventory update packet
3710 if (!Config.FORCE_INVENTORY_UPDATE)
3711 {
3712 final InventoryUpdate playerIU = new InventoryUpdate();
3713 playerIU.addItem(item);
3714 sendInventoryUpdate(playerIU);
3715 }
3716 else
3717 {
3718 sendItemList(false);
3719 }
3720
3721 // Sends message to client if requested
3722 if (sendMessage)
3723 {
3724 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_DROPPED_S1);
3725 sm.addItemName(item);
3726 sendPacket(sm);
3727 }
3728
3729 return true;
3730 }
3731
3732 public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage)
3733 {
3734 return dropItem(process, item, reference, sendMessage, false);
3735 }
3736
3737 /**
3738 * Drop item from inventory by using its <B>objectID</B> and send a Server->Client InventoryUpdate packet to the L2PcInstance.
3739 * @param process : String Identifier of process triggering this action
3740 * @param objectId : int Item Instance identifier of the item to be dropped
3741 * @param count : long Quantity of items to be dropped
3742 * @param x : int coordinate for drop X
3743 * @param y : int coordinate for drop Y
3744 * @param z : int coordinate for drop Z
3745 * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation
3746 * @param sendMessage : boolean Specifies whether to send message to Client about this action
3747 * @param protectItem
3748 * @return L2ItemInstance corresponding to the new item or the updated item in inventory
3749 */
3750 public L2ItemInstance dropItem(String process, int objectId, long count, int x, int y, int z, L2Object reference, boolean sendMessage, boolean protectItem)
3751 {
3752 final L2ItemInstance invitem = _inventory.getItemByObjectId(objectId);
3753 final L2ItemInstance item = _inventory.dropItem(process, objectId, count, this, reference);
3754
3755 if (item == null)
3756 {
3757 if (sendMessage)
3758 {
3759 sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT_2);
3760 }
3761
3762 return null;
3763 }
3764
3765 item.dropMe(this, x, y, z);
3766
3767 if ((Config.AUTODESTROY_ITEM_AFTER > 0) && Config.DESTROY_DROPPED_PLAYER_ITEM && !Config.LIST_PROTECTED_ITEMS.contains(item.getId()))
3768 {
3769 if ((item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM) || !item.isEquipable())
3770 {
3771 ItemsAutoDestroy.getInstance().addItem(item);
3772 }
3773 }
3774 if (Config.DESTROY_DROPPED_PLAYER_ITEM)
3775 {
3776 if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM))
3777 {
3778 item.setProtected(false);
3779 }
3780 else
3781 {
3782 item.setProtected(true);
3783 }
3784 }
3785 else
3786 {
3787 item.setProtected(true);
3788 }
3789
3790 // retail drop protection
3791 if (protectItem)
3792 {
3793 item.getDropProtection().protect(this);
3794 }
3795
3796 // Send inventory update packet
3797 if (!Config.FORCE_INVENTORY_UPDATE)
3798 {
3799 final InventoryUpdate playerIU = new InventoryUpdate();
3800 playerIU.addItem(invitem);
3801 sendInventoryUpdate(playerIU);
3802 }
3803 else
3804 {
3805 sendItemList(false);
3806 }
3807
3808 // Sends message to client if requested
3809 if (sendMessage)
3810 {
3811 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_DROPPED_S1);
3812 sm.addItemName(item);
3813 sendPacket(sm);
3814 }
3815
3816 return item;
3817 }
3818
3819 public L2ItemInstance checkItemManipulation(int objectId, long count, String action)
3820 {
3821 // TODO: if we remove objects that are not visisble from the L2World, we'll have to remove this check
3822 if (L2World.getInstance().findObject(objectId) == null)
3823 {
3824 LOGGER.finest(getObjectId() + ": player tried to " + action + " item not available in L2World");
3825 return null;
3826 }
3827
3828 final L2ItemInstance item = getInventory().getItemByObjectId(objectId);
3829
3830 if ((item == null) || (item.getOwnerId() != getObjectId()))
3831 {
3832 LOGGER.finest(getObjectId() + ": player tried to " + action + " item he is not owner of");
3833 return null;
3834 }
3835
3836 if ((count < 0) || ((count > 1) && !item.isStackable()))
3837 {
3838 LOGGER.finest(getObjectId() + ": player tried to " + action + " item with invalid count: " + count);
3839 return null;
3840 }
3841
3842 if (count > item.getCount())
3843 {
3844 LOGGER.finest(getObjectId() + ": player tried to " + action + " more items than he owns");
3845 return null;
3846 }
3847
3848 // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting
3849 final L2Summon pet = getPet();
3850 if (((pet != null) && (pet.getControlObjectId() == objectId)) || (getMountObjectID() == objectId))
3851 {
3852 return null;
3853 }
3854
3855 if (isProcessingItem(objectId))
3856 {
3857 return null;
3858 }
3859
3860 // We cannot put a Weapon with Augmention in WH while casting (Possible Exploit)
3861 if (item.isAugmented() && isCastingNow())
3862 {
3863 return null;
3864 }
3865
3866 return item;
3867 }
3868
3869 public boolean isSpawnProtected()
3870 {
3871 return _spawnProtectEndTime > System.currentTimeMillis();
3872 }
3873
3874 public boolean isTeleportProtected()
3875 {
3876 return _teleportProtectEndTime > System.currentTimeMillis();
3877 }
3878
3879 public void setSpawnProtection(boolean protect)
3880 {
3881 _spawnProtectEndTime = protect ? System.currentTimeMillis() + (Config.PLAYER_SPAWN_PROTECTION * 1000) : 0;
3882 }
3883
3884 public void setTeleportProtection(boolean protect)
3885 {
3886 _teleportProtectEndTime = protect ? System.currentTimeMillis() + (Config.PLAYER_TELEPORT_PROTECTION * 1000) : 0;
3887 }
3888
3889 /**
3890 * Set protection from aggro mobs when getting up from fake death, according settings.
3891 * @param protect
3892 */
3893 public void setRecentFakeDeath(boolean protect)
3894 {
3895 _recentFakeDeathEndTime = protect ? GameTimeController.getInstance().getGameTicks() + (Config.PLAYER_FAKEDEATH_UP_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0;
3896 }
3897
3898 public boolean isRecentFakeDeath()
3899 {
3900 return _recentFakeDeathEndTime > GameTimeController.getInstance().getGameTicks();
3901 }
3902
3903 public final boolean isFakeDeath()
3904 {
3905 return isAffected(EffectFlag.FAKE_DEATH);
3906 }
3907
3908 @Override
3909 public final boolean isAlikeDead()
3910 {
3911 return super.isAlikeDead() || isFakeDeath();
3912 }
3913
3914 /**
3915 * @return the client owner of this char.
3916 */
3917 public L2GameClient getClient()
3918 {
3919 return _client;
3920 }
3921
3922 public void setClient(L2GameClient client)
3923 {
3924 _client = client;
3925 }
3926
3927 public String getIPAddress()
3928 {
3929 String ip = "N/A";
3930 if ((_client != null) && (_client.getConnectionAddress() != null))
3931 {
3932 ip = _client.getConnectionAddress().getHostAddress();
3933 }
3934 return ip;
3935 }
3936
3937 public Location getCurrentSkillWorldPosition()
3938 {
3939 return _currentSkillWorldPosition;
3940 }
3941
3942 public void setCurrentSkillWorldPosition(Location worldPosition)
3943 {
3944 _currentSkillWorldPosition = worldPosition;
3945 }
3946
3947 @Override
3948 public void enableSkill(Skill skill)
3949 {
3950 super.enableSkill(skill);
3951 removeTimeStamp(skill);
3952 }
3953
3954 /**
3955 * Returns true if cp update should be done, false if not
3956 * @return boolean
3957 */
3958 private boolean needCpUpdate()
3959 {
3960 final double currentCp = getCurrentCp();
3961
3962 if ((currentCp <= 1.0) || (getMaxCp() < MAX_HP_BAR_PX))
3963 {
3964 return true;
3965 }
3966
3967 if ((currentCp <= _cpUpdateDecCheck) || (currentCp >= _cpUpdateIncCheck))
3968 {
3969 if (currentCp == getMaxCp())
3970 {
3971 _cpUpdateIncCheck = currentCp + 1;
3972 _cpUpdateDecCheck = currentCp - _cpUpdateInterval;
3973 }
3974 else
3975 {
3976 final double doubleMulti = currentCp / _cpUpdateInterval;
3977 int intMulti = (int) doubleMulti;
3978
3979 _cpUpdateDecCheck = _cpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
3980 _cpUpdateIncCheck = _cpUpdateDecCheck + _cpUpdateInterval;
3981 }
3982
3983 return true;
3984 }
3985
3986 return false;
3987 }
3988
3989 /**
3990 * Returns true if mp update should be done, false if not
3991 * @return boolean
3992 */
3993 private boolean needMpUpdate()
3994 {
3995 final double currentMp = getCurrentMp();
3996
3997 if ((currentMp <= 1.0) || (getMaxMp() < MAX_HP_BAR_PX))
3998 {
3999 return true;
4000 }
4001
4002 if ((currentMp <= _mpUpdateDecCheck) || (currentMp >= _mpUpdateIncCheck))
4003 {
4004 if (currentMp == getMaxMp())
4005 {
4006 _mpUpdateIncCheck = currentMp + 1;
4007 _mpUpdateDecCheck = currentMp - _mpUpdateInterval;
4008 }
4009 else
4010 {
4011 final double doubleMulti = currentMp / _mpUpdateInterval;
4012 int intMulti = (int) doubleMulti;
4013
4014 _mpUpdateDecCheck = _mpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti);
4015 _mpUpdateIncCheck = _mpUpdateDecCheck + _mpUpdateInterval;
4016 }
4017
4018 return true;
4019 }
4020
4021 return false;
4022 }
4023
4024 /**
4025 * Send packet StatusUpdate with current HP,MP and CP to the L2PcInstance and only current HP, MP and Level to all other L2PcInstance of the Party. <B><U> Actions</U> :</B>
4026 * <li>Send the Server->Client packet StatusUpdate with current HP, MP and CP to this L2PcInstance</li><BR>
4027 * <li>Send the Server->Client packet PartySmallWindowUpdate with current HP, MP and Level to all other L2PcInstance of the Party</li> <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND current HP and MP to all L2PcInstance of the _statusListener</B></FONT>
4028 */
4029 @Override
4030 public void broadcastStatusUpdate(L2Character caster)
4031 {
4032 final StatusUpdate su = new StatusUpdate(this);
4033 if (caster != null)
4034 {
4035 su.addCaster(caster);
4036 }
4037
4038 computeStatusUpdate(su, StatusUpdateType.LEVEL);
4039 computeStatusUpdate(su, StatusUpdateType.MAX_HP);
4040 computeStatusUpdate(su, StatusUpdateType.CUR_HP);
4041 computeStatusUpdate(su, StatusUpdateType.MAX_MP);
4042 computeStatusUpdate(su, StatusUpdateType.CUR_MP);
4043 computeStatusUpdate(su, StatusUpdateType.MAX_CP);
4044 computeStatusUpdate(su, StatusUpdateType.CUR_CP);
4045 if (su.hasUpdates())
4046 {
4047 broadcastPacket(su);
4048 }
4049
4050 final boolean needCpUpdate = needCpUpdate();
4051 final boolean needHpUpdate = needHpUpdate();
4052 final boolean needMpUpdate = needMpUpdate();
4053
4054 final L2Party party = getParty();
4055
4056 // Check if a party is in progress and party window update is usefull
4057 if ((party != null) && (needCpUpdate || needHpUpdate || needMpUpdate))
4058 {
4059 final PartySmallWindowUpdate partyWindow = new PartySmallWindowUpdate(this, false);
4060 if (needCpUpdate)
4061 {
4062 partyWindow.addComponentType(PartySmallWindowUpdateType.CURRENT_CP);
4063 partyWindow.addComponentType(PartySmallWindowUpdateType.MAX_CP);
4064 }
4065 if (needHpUpdate)
4066 {
4067 partyWindow.addComponentType(PartySmallWindowUpdateType.CURRENT_HP);
4068 partyWindow.addComponentType(PartySmallWindowUpdateType.MAX_HP);
4069 }
4070 if (needMpUpdate)
4071 {
4072 partyWindow.addComponentType(PartySmallWindowUpdateType.CURRENT_MP);
4073 partyWindow.addComponentType(PartySmallWindowUpdateType.MAX_MP);
4074 }
4075 party.broadcastToPartyMembers(this, partyWindow);
4076 }
4077
4078 if (isInOlympiadMode() && isOlympiadStart() && (needCpUpdate || needHpUpdate))
4079 {
4080 final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(getOlympiadGameId());
4081 if ((game != null) && game.isBattleStarted())
4082 {
4083 game.getStadium().broadcastStatusUpdate(this);
4084 }
4085 }
4086
4087 // In duel MP updated only with CP or HP
4088 if (isInDuel() && (needCpUpdate || needHpUpdate))
4089 {
4090 DuelManager.getInstance().broadcastToOppositTeam(this, new ExDuelUpdateUserInfo(this));
4091 }
4092 }
4093
4094 /**
4095 * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers. <B><U> Concept</U> :</B> Others L2PcInstance in the detection area of the L2PcInstance are identified in <B>_knownPlayers</B>. In order to inform other players of this
4096 * L2PcInstance state modifications, server just need to go through _knownPlayers to send Server->Client Packet <B><U> Actions</U> :</B>
4097 * <li>Send a Server->Client packet UserInfo to this L2PcInstance (Public and Private Data)</li>
4098 * <li>Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance (Public data only)</li> <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,
4099 * STR, DEX...</B></FONT>
4100 */
4101 public final void broadcastUserInfo()
4102 {
4103 // Send user info to the current player
4104 sendPacket(new UserInfo(this));
4105
4106 // Broadcast char info to known players
4107 broadcastCharInfo();
4108 }
4109
4110 public final void broadcastUserInfo(UserInfoType... types)
4111 {
4112 // Send user info to the current player
4113 final UserInfo ui = new UserInfo(this, false);
4114 ui.addComponentType(types);
4115 sendPacket(ui);
4116
4117 // Broadcast char info to all known players
4118 broadcastCharInfo();
4119 }
4120
4121 public final void broadcastCharInfo()
4122 {
4123 final CharInfo charInfo = new CharInfo(this, false);
4124 L2World.getInstance().forEachVisibleObject(this, L2PcInstance.class, player ->
4125 {
4126 if (isVisibleFor(player))
4127 {
4128 if (isInvisible() && player.canOverrideCond(PcCondOverride.SEE_ALL_PLAYERS))
4129 {
4130 player.sendPacket(new CharInfo(this, true));
4131 }
4132 else
4133 {
4134 player.sendPacket(charInfo);
4135 }
4136 }
4137 });
4138 }
4139
4140 public final void broadcastTitleInfo()
4141 {
4142 // Send a Server->Client packet UserInfo to this L2PcInstance
4143 final UserInfo ui = new UserInfo(this, false);
4144 ui.addComponentType(UserInfoType.CLAN);
4145 sendPacket(ui);
4146
4147 // Send a Server->Client packet TitleUpdate to all L2PcInstance in _KnownPlayers of the L2PcInstance
4148 broadcastPacket(new NicknameChanged(this));
4149 }
4150
4151 @Override
4152 public final void broadcastPacket(IClientOutgoingPacket mov)
4153 {
4154 if (!(mov instanceof CharInfo))
4155 {
4156 sendPacket(mov);
4157 }
4158
4159 L2World.getInstance().forEachVisibleObject(this, L2PcInstance.class, player ->
4160 {
4161 if (!isVisibleFor(player))
4162 {
4163 return;
4164 }
4165
4166 player.sendPacket(mov);
4167 final int relation = getRelation(player);
4168 final Integer oldrelation = getKnownRelations().get(player.getObjectId());
4169 if ((oldrelation == null) || (oldrelation != relation))
4170 {
4171 final RelationChanged rc = new RelationChanged();
4172 rc.addRelation(this, relation, isAutoAttackable(player));
4173 if (hasSummon())
4174 {
4175 final L2Summon pet = getPet();
4176 if (pet != null)
4177 {
4178 rc.addRelation(pet, relation, isAutoAttackable(player));
4179 }
4180 if (hasServitors())
4181 {
4182 getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player)));
4183 }
4184 }
4185 player.sendPacket(rc);
4186 getKnownRelations().put(player.getObjectId(), relation);
4187 }
4188 });
4189 }
4190
4191 @Override
4192 public void broadcastPacket(IClientOutgoingPacket mov, int radiusInKnownlist)
4193 {
4194 if (!(mov instanceof CharInfo))
4195 {
4196 sendPacket(mov);
4197 }
4198
4199 L2World.getInstance().forEachVisibleObject(this, L2PcInstance.class, player ->
4200 {
4201 if (!isVisibleFor(player) || (calculateDistance(player, true, false) >= radiusInKnownlist))
4202 {
4203 return;
4204 }
4205 player.sendPacket(mov);
4206 if (mov instanceof CharInfo)
4207 {
4208 final int relation = getRelation(player);
4209 final Integer oldrelation = getKnownRelations().get(player.getObjectId());
4210 if ((oldrelation == null) || (oldrelation != relation))
4211 {
4212 final RelationChanged rc = new RelationChanged();
4213 rc.addRelation(this, relation, isAutoAttackable(player));
4214 if (hasSummon())
4215 {
4216 final L2Summon pet = getPet();
4217 if (pet != null)
4218 {
4219 rc.addRelation(pet, relation, isAutoAttackable(player));
4220 }
4221 if (hasServitors())
4222 {
4223 getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player)));
4224 }
4225 }
4226 player.sendPacket(rc);
4227 getKnownRelations().put(player.getObjectId(), relation);
4228 }
4229 }
4230 });
4231 }
4232
4233 /**
4234 * @return the Alliance Identifier of the L2PcInstance.
4235 */
4236 @Override
4237 public int getAllyId()
4238 {
4239 if (_clan == null)
4240 {
4241 return 0;
4242 }
4243 return _clan.getAllyId();
4244 }
4245
4246 public int getAllyCrestId()
4247 {
4248 if (getClanId() == 0)
4249 {
4250 return 0;
4251 }
4252 if (getClan().getAllyId() == 0)
4253 {
4254 return 0;
4255 }
4256 return getClan().getAllyCrestId();
4257 }
4258
4259 //@formatter:off
4260 /*
4261 public void queryGameGuard()
4262 {
4263 if (getClient() != null)
4264 {
4265 getClient().setGameGuardOk(false);
4266 sendPacket(GameGuardQuery.STATIC_PACKET);
4267 }
4268 if (Config.GAMEGUARD_ENFORCE)
4269 {
4270 ThreadPoolManager.scheduleGeneral(new GameGuardCheckTask(this), 30 * 1000);
4271 }
4272 }*/
4273 //@formatter:on
4274
4275 /**
4276 * Send a Server->Client packet StatusUpdate to the L2PcInstance.
4277 */
4278 @Override
4279 public void sendPacket(IClientOutgoingPacket... packets)
4280 {
4281 if (_client != null)
4282 {
4283 for (IClientOutgoingPacket packet : packets)
4284 {
4285 _client.sendPacket(packet);
4286 }
4287 }
4288 }
4289
4290 /**
4291 * Send SystemMessage packet.
4292 * @param id SystemMessageId
4293 */
4294 @Override
4295 public void sendPacket(SystemMessageId id)
4296 {
4297 sendPacket(SystemMessage.getSystemMessage(id));
4298 }
4299
4300 /**
4301 * Manage Interact Task with another L2PcInstance. <B><U> Actions</U> :</B>
4302 * <li>If the private store is a STORE_PRIVATE_SELL, send a Server->Client PrivateBuyListSell packet to the L2PcInstance</li>
4303 * <li>If the private store is a STORE_PRIVATE_BUY, send a Server->Client PrivateBuyListBuy packet to the L2PcInstance</li>
4304 * <li>If the private store is a STORE_PRIVATE_MANUFACTURE, send a Server->Client RecipeShopSellList packet to the L2PcInstance</li>
4305 * @param target The L2Character targeted
4306 */
4307 public void doInteract(L2Character target)
4308 {
4309 if (target instanceof L2PcInstance)
4310 {
4311 final L2PcInstance targetPlayer = (L2PcInstance) target;
4312 sendPacket(ActionFailed.STATIC_PACKET);
4313
4314 if ((targetPlayer.getPrivateStoreType() == PrivateStoreType.SELL) || (targetPlayer.getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL))
4315 {
4316 if (isSellingBuffs())
4317 {
4318 SellBuffsManager.getInstance().sendBuffMenu(this, targetPlayer, 0);
4319 }
4320 else
4321 {
4322 sendPacket(new PrivateStoreListSell(this, targetPlayer));
4323 }
4324 }
4325 else if (targetPlayer.getPrivateStoreType() == PrivateStoreType.BUY)
4326 {
4327 sendPacket(new PrivateStoreListBuy(this, targetPlayer));
4328 }
4329 else if (targetPlayer.getPrivateStoreType() == PrivateStoreType.MANUFACTURE)
4330 {
4331 sendPacket(new RecipeShopSellList(this, targetPlayer));
4332 }
4333 }
4334 else if (target != null) // _interactTarget=null should never happen but one never knows ^^;
4335 {
4336 target.onAction(this);
4337 }
4338 }
4339
4340 /**
4341 * Manages AutoLoot Task.<br>
4342 * <ul>
4343 * <li>Send a system message to the player.</li>
4344 * <li>Add the item to the player's inventory.</li>
4345 * <li>Send a Server->Client packet InventoryUpdate to this player with NewItem (use a new slot) or ModifiedItem (increase amount).</li>
4346 * <li>Send a Server->Client packet StatusUpdate to this player with current weight.</li>
4347 * </ul>
4348 * <font color=#FF0000><B><U>Caution</U>: If a party is in progress, distribute the items between the party members!</b></font>
4349 * @param target the NPC dropping the item
4350 * @param itemId the item ID
4351 * @param itemCount the item count
4352 */
4353 public void doAutoLoot(L2Attackable target, int itemId, long itemCount)
4354 {
4355 if (isInParty() && !ItemTable.getInstance().getTemplate(itemId).hasExImmediateEffect())
4356 {
4357 getParty().distributeItem(this, itemId, itemCount, false, target);
4358 }
4359 else if (itemId == Inventory.ADENA_ID)
4360 {
4361 addAdena("Loot", itemCount, target, true);
4362 }
4363 else
4364 {
4365 addItem("Loot", itemId, itemCount, target, true);
4366 }
4367 }
4368
4369 /**
4370 * Method overload for {@link L2PcInstance#doAutoLoot(L2Attackable, int, long)}
4371 * @param target the NPC dropping the item
4372 * @param item the item holder
4373 */
4374 public void doAutoLoot(L2Attackable target, ItemHolder item)
4375 {
4376 doAutoLoot(target, item.getId(), item.getCount());
4377 }
4378
4379 /**
4380 * Manage Pickup Task. <B><U> Actions</U> :</B>
4381 * <li>Send a Server->Client packet StopMove to this L2PcInstance</li>
4382 * <li>Remove the L2ItemInstance from the world and send server->client GetItem packets</li>
4383 * <li>Send a System Message to the L2PcInstance : YOU_PICKED_UP_S1_ADENA or YOU_PICKED_UP_S1_S2</li>
4384 * <li>Add the Item to the L2PcInstance inventory</li>
4385 * <li>Send a Server->Client packet InventoryUpdate to this L2PcInstance with NewItem (use a new slot) or ModifiedItem (increase amount)</li>
4386 * <li>Send a Server->Client packet StatusUpdate to this L2PcInstance with current weight</li> <FONT COLOR=#FF0000><B> <U>Caution</U> : If a Party is in progress, distribute Items between party members</B></FONT>
4387 * @param object The L2ItemInstance to pick up
4388 */
4389 @Override
4390 public void doPickupItem(L2Object object)
4391 {
4392 if (isAlikeDead() || isFakeDeath())
4393 {
4394 return;
4395 }
4396
4397 // Set the AI Intention to AI_INTENTION_IDLE
4398 getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
4399
4400 // Check if the L2Object to pick up is a L2ItemInstance
4401 if (!object.isItem())
4402 {
4403 // dont try to pickup anything that is not an item :)
4404 LOGGER.warning(this + " trying to pickup wrong target." + getTarget());
4405 return;
4406 }
4407
4408 final L2ItemInstance target = (L2ItemInstance) object;
4409
4410 // Send a Server->Client packet ActionFailed to this L2PcInstance
4411 sendPacket(ActionFailed.STATIC_PACKET);
4412
4413 // Send a Server->Client packet StopMove to this L2PcInstance
4414 final StopMove sm = new StopMove(this);
4415 sendPacket(sm);
4416
4417 SystemMessage smsg = null;
4418 synchronized (target)
4419 {
4420 // Check if the target to pick up is visible
4421 if (!target.isSpawned())
4422 {
4423 // Send a Server->Client packet ActionFailed to this L2PcInstance
4424 sendPacket(ActionFailed.STATIC_PACKET);
4425 return;
4426 }
4427
4428 if (!target.getDropProtection().tryPickUp(this))
4429 {
4430 sendPacket(ActionFailed.STATIC_PACKET);
4431 smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1);
4432 smsg.addItemName(target);
4433 sendPacket(smsg);
4434 return;
4435 }
4436
4437 if (((isInParty() && (getParty().getDistributionType() == PartyDistributionType.FINDERS_KEEPERS)) || !isInParty()) && !_inventory.validateCapacity(target))
4438 {
4439 sendPacket(ActionFailed.STATIC_PACKET);
4440 sendPacket(SystemMessageId.YOUR_INVENTORY_IS_FULL);
4441 return;
4442 }
4443
4444 if (isInvul() && !canOverrideCond(PcCondOverride.ITEM_CONDITIONS))
4445 {
4446 sendPacket(ActionFailed.STATIC_PACKET);
4447 smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1);
4448 smsg.addItemName(target);
4449 sendPacket(smsg);
4450 return;
4451 }
4452
4453 if ((target.getOwnerId() != 0) && (target.getOwnerId() != getObjectId()) && !isInLooterParty(target.getOwnerId()))
4454 {
4455 if (target.getId() == Inventory.ADENA_ID)
4456 {
4457 smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1_ADENA);
4458 smsg.addLong(target.getCount());
4459 }
4460 else if (target.getCount() > 1)
4461 {
4462 smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S2_S1_S);
4463 smsg.addItemName(target);
4464 smsg.addLong(target.getCount());
4465 }
4466 else
4467 {
4468 smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1);
4469 smsg.addItemName(target);
4470 }
4471 sendPacket(ActionFailed.STATIC_PACKET);
4472 sendPacket(smsg);
4473 return;
4474 }
4475
4476 // You can pickup only 1 combat flag
4477 if (FortSiegeManager.getInstance().isCombat(target.getId()))
4478 {
4479 if (!FortSiegeManager.getInstance().checkIfCanPickup(this))
4480 {
4481 return;
4482 }
4483 }
4484
4485 if ((target.getItemLootShedule() != null) && ((target.getOwnerId() == getObjectId()) || isInLooterParty(target.getOwnerId())))
4486 {
4487 target.resetOwnerTimer();
4488 }
4489
4490 // Remove the L2ItemInstance from the world and send server->client GetItem packets
4491 target.pickupMe(this);
4492 if (Config.SAVE_DROPPED_ITEM)
4493 {
4494 ItemsOnGroundManager.getInstance().removeObject(target);
4495 }
4496 }
4497
4498 // Auto use herbs - pick up
4499 if (target.getItem().hasExImmediateEffect())
4500 {
4501 final IItemHandler handler = ItemHandler.getInstance().getHandler(target.getEtcItem());
4502 if (handler == null)
4503 {
4504 LOGGER.warning("No item handler registered for item ID: " + target.getId() + ".");
4505 }
4506 else
4507 {
4508 handler.useItem(this, target, false);
4509 }
4510 ItemTable.getInstance().destroyItem("Consume", target, this, null);
4511 }
4512 // Cursed Weapons are not distributed
4513 else if (CursedWeaponsManager.getInstance().isCursed(target.getId()))
4514 {
4515 addItem("Pickup", target, null, true);
4516 }
4517 else if (FortSiegeManager.getInstance().isCombat(target.getId()))
4518 {
4519 addItem("Pickup", target, null, true);
4520 }
4521 else
4522 {
4523 // if item is instance of L2ArmorType or L2WeaponType broadcast an "Attention" system message
4524 if ((target.getItemType() instanceof ArmorType) || (target.getItemType() instanceof WeaponType))
4525 {
4526 if (target.getEnchantLevel() > 0)
4527 {
4528 smsg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_C1_HAS_PICKED_UP_S2_S3);
4529 smsg.addPcName(this);
4530 smsg.addInt(target.getEnchantLevel());
4531 smsg.addItemName(target.getId());
4532 broadcastPacket(smsg, 1400);
4533 }
4534 else
4535 {
4536 smsg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_C1_HAS_PICKED_UP_S2);
4537 smsg.addPcName(this);
4538 smsg.addItemName(target.getId());
4539 broadcastPacket(smsg, 1400);
4540 }
4541 }
4542
4543 // Check if a Party is in progress
4544 if (isInParty())
4545 {
4546 getParty().distributeItem(this, target);
4547 }
4548 else if ((target.getId() == Inventory.ADENA_ID) && (getInventory().getAdenaInstance() != null))
4549 {
4550 addAdena("Pickup", target.getCount(), null, true);
4551 ItemTable.getInstance().destroyItem("Pickup", target, this, null);
4552 }
4553 else
4554 {
4555 addItem("Pickup", target, null, true);
4556 // Auto-Equip arrows/bolts if player has a bow/crossbow and player picks up arrows/bolts.
4557 final L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
4558 if (weapon != null)
4559 {
4560 final L2EtcItem etcItem = target.getEtcItem();
4561 if (etcItem != null)
4562 {
4563 final EtcItemType itemType = etcItem.getItemType();
4564 if (((weapon.getItemType() == WeaponType.BOW) && (itemType == EtcItemType.ARROW)) || (((weapon.getItemType() == WeaponType.CROSSBOW) || (weapon.getItemType() == WeaponType.TWOHANDCROSSBOW)) && (itemType == EtcItemType.BOLT)))
4565 {
4566 checkAndEquipAmmunition(itemType);
4567 }
4568 }
4569 }
4570 }
4571 }
4572 }
4573
4574 @Override
4575 public void doAutoAttack(L2Character target)
4576 {
4577 super.doAutoAttack(target);
4578 setRecentFakeDeath(false);
4579 if (target.isFakePlayer())
4580 {
4581 updatePvPStatus();
4582 }
4583 }
4584
4585 @Override
4586 public void doCast(Skill skill)
4587 {
4588 super.doCast(skill);
4589 setRecentFakeDeath(false);
4590 }
4591
4592 public boolean canOpenPrivateStore()
4593 {
4594 if ((Config.SHOP_MIN_RANGE_FROM_NPC > 0) || (Config.SHOP_MIN_RANGE_FROM_PLAYER > 0))
4595 {
4596 for (L2Character cha : L2World.getInstance().getVisibleObjects(this, L2Character.class, 1000))
4597 {
4598 if (Util.checkIfInRange(cha.getMinShopDistance(), this, cha, true))
4599 {
4600 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_OPEN_A_PRIVATE_STORE_HERE));
4601 return false;
4602 }
4603 }
4604 }
4605
4606 return !isSellingBuffs() && !isAlikeDead() && !isInOlympiadMode() && !isMounted() && !isInsideZone(ZoneId.NO_STORE) && !isCastingNow();
4607 }
4608
4609 @Override
4610 public int getMinShopDistance()
4611 {
4612 return isSitting() ? Config.SHOP_MIN_RANGE_FROM_PLAYER : 0;
4613 }
4614
4615 public void tryOpenPrivateBuyStore()
4616 {
4617 // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death)
4618 if (canOpenPrivateStore())
4619 {
4620 if ((getPrivateStoreType() == PrivateStoreType.BUY) || (getPrivateStoreType() == PrivateStoreType.BUY_MANAGE))
4621 {
4622 setPrivateStoreType(PrivateStoreType.NONE);
4623 }
4624 if (getPrivateStoreType() == PrivateStoreType.NONE)
4625 {
4626 if (isSitting())
4627 {
4628 standUp();
4629 }
4630 setPrivateStoreType(PrivateStoreType.BUY_MANAGE);
4631 sendPacket(new PrivateStoreManageListBuy(this));
4632 }
4633 }
4634 else
4635 {
4636 if (isInsideZone(ZoneId.NO_STORE))
4637 {
4638 sendPacket(SystemMessageId.YOU_CANNOT_OPEN_A_PRIVATE_STORE_HERE);
4639 }
4640 sendPacket(ActionFailed.STATIC_PACKET);
4641 }
4642 }
4643
4644 public final PreparedMultisellListHolder getMultiSell()
4645 {
4646 return _currentMultiSell;
4647 }
4648
4649 public final void setMultiSell(PreparedMultisellListHolder list)
4650 {
4651 _currentMultiSell = list;
4652 }
4653
4654 /**
4655 * Set a target. <B><U> Actions</U> :</B>
4656 * <ul>
4657 * <li>Remove the L2PcInstance from the _statusListener of the old target if it was a L2Character</li>
4658 * <li>Add the L2PcInstance to the _statusListener of the new target if it's a L2Character</li>
4659 * <li>Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object)</li>
4660 * </ul>
4661 * @param newTarget The L2Object to target
4662 */
4663 @Override
4664 public void setTarget(L2Object newTarget)
4665 {
4666 if (newTarget != null)
4667 {
4668 final boolean isInParty = (newTarget.isPlayer() && isInParty() && getParty().containsPlayer(newTarget.getActingPlayer()));
4669
4670 // Prevents /target exploiting
4671 if (!isInParty && (Math.abs(newTarget.getZ() - getZ()) > 1000))
4672 {
4673 newTarget = null;
4674 }
4675
4676 // Check if the new target is visible
4677 if ((newTarget != null) && !isInParty && !newTarget.isSpawned())
4678 {
4679 newTarget = null;
4680 }
4681
4682 // vehicles cant be targeted
4683 if (!isGM() && (newTarget instanceof L2Vehicle))
4684 {
4685 newTarget = null;
4686 }
4687 }
4688
4689 // Get the current target
4690 final L2Object oldTarget = getTarget();
4691
4692 if (oldTarget != null)
4693 {
4694 if (oldTarget.equals(newTarget)) // no target change?
4695 {
4696 // Validate location of the target.
4697 if ((newTarget != null) && (newTarget.getObjectId() != getObjectId()))
4698 {
4699 sendPacket(new ValidateLocation(newTarget));
4700 }
4701 return;
4702 }
4703
4704 // Remove the target from the status listener.
4705 oldTarget.removeStatusListener(this);
4706 }
4707
4708 if (newTarget instanceof L2Character)
4709 {
4710 final L2Character target = (L2Character) newTarget;
4711
4712 // Validate location of the new target.
4713 if (newTarget.getObjectId() != getObjectId())
4714 {
4715 sendPacket(new ValidateLocation(target));
4716 }
4717
4718 // Show the client his new target.
4719 sendPacket(new MyTargetSelected(this, target));
4720
4721 // Register target to listen for hp changes.
4722 target.addStatusListener(this);
4723
4724 // Send max/current hp.
4725 final StatusUpdate su = new StatusUpdate(target);
4726 su.addUpdate(StatusUpdateType.MAX_HP, target.getMaxHp());
4727 su.addUpdate(StatusUpdateType.CUR_HP, (int) target.getCurrentHp());
4728 sendPacket(su);
4729
4730 // To others the new target, and not yourself!
4731 Broadcast.toKnownPlayers(this, new TargetSelected(getObjectId(), newTarget.getObjectId(), getX(), getY(), getZ()));
4732
4733 // Send buffs
4734 sendPacket(new ExAbnormalStatusUpdateFromTarget(target));
4735 }
4736
4737 // Target was removed?
4738 if ((newTarget == null) && (getTarget() != null))
4739 {
4740 broadcastPacket(new TargetUnselected(this));
4741 }
4742
4743 // Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object)
4744 super.setTarget(newTarget);
4745 }
4746
4747 /**
4748 * Return the active weapon instance (always equiped in the right hand).
4749 */
4750 @Override
4751 public L2ItemInstance getActiveWeaponInstance()
4752 {
4753 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
4754 }
4755
4756 /**
4757 * Return the active weapon item (always equiped in the right hand).
4758 */
4759 @Override
4760 public L2Weapon getActiveWeaponItem()
4761 {
4762 final L2ItemInstance weapon = getActiveWeaponInstance();
4763 if (weapon == null)
4764 {
4765 return getFistsWeaponItem();
4766 }
4767
4768 return (L2Weapon) weapon.getItem();
4769 }
4770
4771 public L2ItemInstance getChestArmorInstance()
4772 {
4773 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST);
4774 }
4775
4776 public L2ItemInstance getLegsArmorInstance()
4777 {
4778 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS);
4779 }
4780
4781 public L2Armor getActiveChestArmorItem()
4782 {
4783 final L2ItemInstance armor = getChestArmorInstance();
4784
4785 if (armor == null)
4786 {
4787 return null;
4788 }
4789
4790 return (L2Armor) armor.getItem();
4791 }
4792
4793 public L2Armor getActiveLegsArmorItem()
4794 {
4795 final L2ItemInstance legs = getLegsArmorInstance();
4796
4797 if (legs == null)
4798 {
4799 return null;
4800 }
4801
4802 return (L2Armor) legs.getItem();
4803 }
4804
4805 public boolean isWearingHeavyArmor()
4806 {
4807 final L2ItemInstance legs = getLegsArmorInstance();
4808 final L2ItemInstance armor = getChestArmorInstance();
4809
4810 if ((armor != null) && (legs != null))
4811 {
4812 if ((legs.getItemType() == ArmorType.HEAVY) && (armor.getItemType() == ArmorType.HEAVY))
4813 {
4814 return true;
4815 }
4816 }
4817 if (armor != null)
4818 {
4819 if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && (armor.getItemType() == ArmorType.HEAVY)))
4820 {
4821 return true;
4822 }
4823 }
4824 return false;
4825 }
4826
4827 public boolean isWearingLightArmor()
4828 {
4829 final L2ItemInstance legs = getLegsArmorInstance();
4830 final L2ItemInstance armor = getChestArmorInstance();
4831
4832 if ((armor != null) && (legs != null))
4833 {
4834 if ((legs.getItemType() == ArmorType.LIGHT) && (armor.getItemType() == ArmorType.LIGHT))
4835 {
4836 return true;
4837 }
4838 }
4839 if (armor != null)
4840 {
4841 if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && (armor.getItemType() == ArmorType.LIGHT)))
4842 {
4843 return true;
4844 }
4845 }
4846 return false;
4847 }
4848
4849 public boolean isWearingMagicArmor()
4850 {
4851 final L2ItemInstance legs = getLegsArmorInstance();
4852 final L2ItemInstance armor = getChestArmorInstance();
4853
4854 if ((armor != null) && (legs != null))
4855 {
4856 if ((legs.getItemType() == ArmorType.MAGIC) && (armor.getItemType() == ArmorType.MAGIC))
4857 {
4858 return true;
4859 }
4860 }
4861 if (armor != null)
4862 {
4863 if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && (armor.getItemType() == ArmorType.MAGIC)))
4864 {
4865 return true;
4866 }
4867 }
4868 return false;
4869 }
4870
4871 /**
4872 * Return the secondary weapon instance (always equiped in the left hand).
4873 */
4874 @Override
4875 public L2ItemInstance getSecondaryWeaponInstance()
4876 {
4877 return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
4878 }
4879
4880 /**
4881 * Return the secondary L2Item item (always equiped in the left hand).<BR>
4882 * Arrows, Shield..<BR>
4883 */
4884 @Override
4885 public L2Item getSecondaryWeaponItem()
4886 {
4887 final L2ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
4888 if (item != null)
4889 {
4890 return item.getItem();
4891 }
4892 return null;
4893 }
4894
4895 /**
4896 * Kill the L2Character, Apply Death Penalty, Manage gain/loss Karma and Item Drop. <B><U> Actions</U> :</B>
4897 * <li>Reduce the Experience of the L2PcInstance in function of the calculated Death Penalty</li>
4898 * <li>If necessary, unsummon the Pet of the killed L2PcInstance</li>
4899 * <li>Manage Karma gain for attacker and Karam loss for the killed L2PcInstance</li>
4900 * <li>If the killed L2PcInstance has Karma, manage Drop Item</li>
4901 * <li>Kill the L2PcInstance</li>
4902 * @param killer
4903 */
4904 @Override
4905 public boolean doDie(L2Character killer)
4906 {
4907 if (killer != null)
4908 {
4909 final L2PcInstance pk = killer.getActingPlayer();
4910 final boolean fpcKill = killer.isFakePlayer();
4911 if ((pk != null) || fpcKill)
4912 {
4913 if (pk != null)
4914 {
4915 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerPvPKill(pk, this), this);
4916
4917 if (L2Event.isParticipant(pk))
4918 {
4919 pk.getEventStatus().addKill(this);
4920 }
4921
4922 // pvp/pk item rewards
4923 if (!(Config.DISABLE_REWARDS_IN_INSTANCES && (getInstanceId() != 0)) && //
4924 !(Config.DISABLE_REWARDS_IN_PVP_ZONES && isInsideZone(ZoneId.PVP)))
4925 {
4926 // pvp
4927 if (Config.REWARD_PVP_ITEM && (getPvpFlag() != 0))
4928 {
4929 pk.addItem("PvP Item Reward", Config.REWARD_PVP_ITEM_ID, Config.REWARD_PVP_ITEM_AMOUNT, this, Config.REWARD_PVP_ITEM_MESSAGE);
4930 }
4931 // pk
4932 if (Config.REWARD_PK_ITEM && (getPvpFlag() == 0))
4933 {
4934 pk.addItem("PK Item Reward", Config.REWARD_PK_ITEM_ID, Config.REWARD_PK_ITEM_AMOUNT, this, Config.REWARD_PK_ITEM_MESSAGE);
4935 }
4936 }
4937 }
4938
4939 // announce pvp/pk
4940 if (Config.ANNOUNCE_PK_PVP && (((pk != null) && !pk.isGM()) || fpcKill))
4941 {
4942 String msg = "";
4943 if (getPvpFlag() == 0)
4944 {
4945 msg = Config.ANNOUNCE_PK_MSG.replace("$killer", killer.getName()).replace("$target", getName());
4946 if (Config.ANNOUNCE_PK_PVP_NORMAL_MESSAGE)
4947 {
4948 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_3);
4949 sm.addString(msg);
4950 Broadcast.toAllOnlinePlayers(sm);
4951 }
4952 else
4953 {
4954 Broadcast.toAllOnlinePlayers(msg, false);
4955 }
4956 }
4957 else if (getPvpFlag() != 0)
4958 {
4959 msg = Config.ANNOUNCE_PVP_MSG.replace("$killer", killer.getName()).replace("$target", getName());
4960 if (Config.ANNOUNCE_PK_PVP_NORMAL_MESSAGE)
4961 {
4962 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_3);
4963 sm.addString(msg);
4964 Broadcast.toAllOnlinePlayers(sm);
4965 }
4966 else
4967 {
4968 Broadcast.toAllOnlinePlayers(msg, false);
4969 }
4970 }
4971 }
4972
4973 if (fpcKill && Config.FAKE_PLAYER_KILL_KARMA && (getPvpFlag() == 0) && (getReputation() >= 0))
4974 {
4975 killer.setReputation(killer.getReputation() - 150);
4976 }
4977 }
4978
4979 broadcastStatusUpdate();
4980 // Clear resurrect xp calculation
4981 setExpBeforeDeath(0);
4982
4983 // Calculate Shilen's Breath debuff level. It must happen right before death, because buffs aren't applied on dead characters.
4984 calculateShilensBreathDebuffLevel(killer);
4985
4986 // Kill the L2PcInstance
4987 if (!super.doDie(killer))
4988 {
4989 return false;
4990 }
4991
4992 // Issues drop of Cursed Weapon.
4993 if (isCursedWeaponEquipped())
4994 {
4995 CursedWeaponsManager.getInstance().drop(_cursedWeaponEquippedId, killer);
4996 }
4997 else if (isCombatFlagEquipped())
4998 {
4999 final Fort fort = FortManager.getInstance().getFort(this);
5000 if (fort != null)
5001 {
5002 FortSiegeManager.getInstance().dropCombatFlag(this, fort.getResidenceId());
5003 }
5004 else
5005 {
5006 final int slot = getInventory().getSlotFromItem(getInventory().getItemByItemId(9819));
5007 getInventory().unEquipItemInBodySlot(slot);
5008 destroyItem("CombatFlag", getInventory().getItemByItemId(9819), null, true);
5009 }
5010 }
5011 else
5012 {
5013 final boolean insidePvpZone = isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.SIEGE);
5014 if ((pk == null) || !pk.isCursedWeaponEquipped())
5015 {
5016 onDieDropItem(killer); // Check if any item should be dropped
5017
5018 if (!insidePvpZone && (pk != null))
5019 {
5020 final L2Clan pkClan = pk.getClan();
5021 if ((pkClan != null) && (getClan() != null) && !isAcademyMember() && !(pk.isAcademyMember()))
5022 {
5023 final ClanWar clanWar = _clan.getWarWith(pkClan.getId());
5024 if ((clanWar != null) && AntiFeedManager.getInstance().check(killer, this))
5025 {
5026 clanWar.onKill(pk, this);
5027 }
5028 }
5029 }
5030 // If player is Lucky shouldn't get penalized.
5031 if (!isLucky() && !insidePvpZone)
5032 {
5033 calculateDeathExpPenalty(killer);
5034 }
5035 }
5036 }
5037 }
5038
5039 if (isMounted())
5040 {
5041 stopFeed();
5042 }
5043 synchronized (this)
5044 {
5045 if (isFakeDeath())
5046 {
5047 stopFakeDeath(true);
5048 }
5049 }
5050
5051 // Unsummon Cubics
5052 if (!_cubics.isEmpty())
5053 {
5054 _cubics.values().forEach(CubicInstance::deactivate);
5055 _cubics.clear();
5056 }
5057
5058 if (isChannelized())
5059 {
5060 getSkillChannelized().abortChannelization();
5061 }
5062
5063 if (getAgathionId() != 0)
5064 {
5065 setAgathionId(0);
5066 }
5067
5068 stopRentPet();
5069 stopWaterTask();
5070
5071 AntiFeedManager.getInstance().setLastDeathTime(getObjectId());
5072
5073 // FIXME: Karma reduction tempfix.
5074 if (getReputation() < 0)
5075 {
5076 final int newRep = getReputation() - (getReputation() / 4);
5077 setReputation(newRep < -20 ? newRep : 0);
5078 }
5079
5080 return true;
5081 }
5082
5083 private void onDieDropItem(L2Character killer)
5084 {
5085 if (L2Event.isParticipant(this) || (killer == null))
5086 {
5087 return;
5088 }
5089
5090 final L2PcInstance pk = killer.getActingPlayer();
5091 if ((getReputation() >= 0) && (pk != null) && (pk.getClan() != null) && (getClan() != null) && (pk.getClan().isAtWarWith(getClanId())
5092 // || getClan().isAtWarWith(((L2PcInstance)killer).getClanId())
5093 ))
5094 {
5095 return;
5096 }
5097
5098 if ((!isInsideZone(ZoneId.PVP) || (pk == null)) && (!isGM() || Config.KARMA_DROP_GM))
5099 {
5100 boolean isKarmaDrop = false;
5101 final int pkLimit = Config.KARMA_PK_LIMIT;
5102
5103 int dropEquip = 0;
5104 int dropEquipWeapon = 0;
5105 int dropItem = 0;
5106 int dropLimit = 0;
5107 int dropPercent = 0;
5108
5109 if ((getReputation() < 0) && (getPkKills() >= pkLimit))
5110 {
5111 isKarmaDrop = true;
5112 dropPercent = Config.KARMA_RATE_DROP;
5113 dropEquip = Config.KARMA_RATE_DROP_EQUIP;
5114 dropEquipWeapon = Config.KARMA_RATE_DROP_EQUIP_WEAPON;
5115 dropItem = Config.KARMA_RATE_DROP_ITEM;
5116 dropLimit = Config.KARMA_DROP_LIMIT;
5117 }
5118 else if (killer.isNpc() && (getLevel() > 4))
5119 {
5120 dropPercent = Config.PLAYER_RATE_DROP;
5121 dropEquip = Config.PLAYER_RATE_DROP_EQUIP;
5122 dropEquipWeapon = Config.PLAYER_RATE_DROP_EQUIP_WEAPON;
5123 dropItem = Config.PLAYER_RATE_DROP_ITEM;
5124 dropLimit = Config.PLAYER_DROP_LIMIT;
5125 }
5126
5127 if ((dropPercent > 0) && (Rnd.get(100) < dropPercent))
5128 {
5129 int dropCount = 0;
5130 int itemDropPercent = 0;
5131 final L2Summon pet = getPet();
5132
5133 for (L2ItemInstance itemDrop : getInventory().getItems())
5134 {
5135 // Don't drop
5136 if (itemDrop.isShadowItem() || // Dont drop Shadow Items
5137 itemDrop.isTimeLimitedItem() || // Dont drop Time Limited Items
5138 !itemDrop.isDropable() || (itemDrop.getId() == Inventory.ADENA_ID) || // Adena
5139 (itemDrop.getItem().getType2() == L2Item.TYPE2_QUEST) || // Quest Items
5140 ((pet != null) && (pet.getControlObjectId() == itemDrop.getId())) || // Control Item of active pet
5141 (Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_ITEMS, itemDrop.getId()) >= 0) || // Item listed in the non droppable item list
5142 (Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_PET_ITEMS, itemDrop.getId()) >= 0 // Item listed in the non droppable pet item list
5143 ))
5144 {
5145 continue;
5146 }
5147
5148 if (itemDrop.isEquipped())
5149 {
5150 // Set proper chance according to Item type of equipped Item
5151 itemDropPercent = itemDrop.getItem().getType2() == L2Item.TYPE2_WEAPON ? dropEquipWeapon : dropEquip;
5152 getInventory().unEquipItemInSlot(itemDrop.getLocationSlot());
5153 }
5154 else
5155 {
5156 itemDropPercent = dropItem; // Item in inventory
5157 }
5158
5159 // NOTE: Each time an item is dropped, the chance of another item being dropped gets lesser (dropCount * 2)
5160 if (Rnd.get(100) < itemDropPercent)
5161 {
5162 dropItem("DieDrop", itemDrop, killer, true);
5163
5164 if (isKarmaDrop)
5165 {
5166 LOGGER.warning(getName() + " has karma and dropped id = " + itemDrop.getId() + ", count = " + itemDrop.getCount());
5167 }
5168 else
5169 {
5170 LOGGER.warning(getName() + " dropped id = " + itemDrop.getId() + ", count = " + itemDrop.getCount());
5171 }
5172
5173 if (++dropCount >= dropLimit)
5174 {
5175 break;
5176 }
5177 }
5178 }
5179 }
5180 }
5181 }
5182
5183 public void onPlayerKill(L2Playable killedPlayable)
5184 {
5185 final L2PcInstance player = getActingPlayer();
5186 final L2PcInstance killedPlayer = killedPlayable.getActingPlayer();
5187
5188 // Avoid nulls && check if player != killedPlayer
5189 if ((player == null) || (killedPlayer == null) || (player == killedPlayer))
5190 {
5191 return;
5192 }
5193
5194 // Cursed weapons progress
5195 if (player.isCursedWeaponEquipped() && killedPlayer.isPlayer())
5196 {
5197 CursedWeaponsManager.getInstance().increaseKills(getCursedWeaponEquippedId());
5198 return;
5199 }
5200
5201 // Duel support
5202 if (player.isInDuel() && killedPlayer.getActingPlayer().isInDuel())
5203 {
5204 return;
5205 }
5206
5207 // Do nothing if both players are in PVP zone
5208 if (player.isInsideZone(ZoneId.PVP) && killedPlayer.isInsideZone(ZoneId.PVP))
5209 {
5210 return;
5211 }
5212
5213 // If both players are in SIEGE zone just increase siege kills/deaths
5214 if (player.isInsideZone(ZoneId.SIEGE) && killedPlayer.isInsideZone(ZoneId.SIEGE))
5215 {
5216 if ((player.getSiegeState() > 0) && (killedPlayer.getSiegeState() > 0) && (player.getSiegeState() != killedPlayer.getSiegeState()))
5217 {
5218 final L2Clan killerClan = player.getClan();
5219 final L2Clan targetClan = killedPlayer.getClan();
5220 if ((killerClan != null) && (targetClan != null))
5221 {
5222 killerClan.addSiegeKill();
5223 targetClan.addSiegeDeath();
5224 }
5225 }
5226 return;
5227 }
5228
5229 if (player.checkIfPvP(killedPlayer))
5230 {
5231 // Check if player should get + rep
5232 if (killedPlayer.getReputation() < 0)
5233 {
5234 final int levelDiff = killedPlayer.getLevel() - player.getLevel();
5235 if ((player.getReputation() >= 0) && (levelDiff < 11) && (levelDiff > -11)) // TODO: Time check, same player can't be killed again in 8 hours
5236 {
5237 player.setReputation(player.getReputation() + Config.REPUTATION_INCREASE);
5238 }
5239 }
5240
5241 player.setPvpKills(player.getPvpKills() + 1);
5242 }
5243 else if ((getReputation() > 0) && (getPkKills() == 0))
5244 {
5245 player.setReputation(0);
5246 player.setPkKills(player.getPkKills() + 1);
5247 }
5248 else
5249 {
5250 // Calculate new karma and increase pk count
5251 player.setReputation(player.getReputation() - Formulas.calculateKarmaGain(player.getPkKills(), killedPlayable.isSummon()));
5252 player.setPkKills(player.getPkKills() + 1);
5253 }
5254
5255 final UserInfo ui = new UserInfo(this, false);
5256 ui.addComponentType(UserInfoType.SOCIAL);
5257 player.sendPacket(ui);
5258 player.checkItemRestriction();
5259 }
5260
5261 public void updatePvPStatus()
5262 {
5263 if (isInsideZone(ZoneId.PVP))
5264 {
5265 return;
5266 }
5267 setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME);
5268
5269 if (getPvpFlag() == 0)
5270 {
5271 startPvPFlag();
5272 }
5273 }
5274
5275 public void updatePvPStatus(L2Character target)
5276 {
5277 final L2PcInstance player_target = target.getActingPlayer();
5278 if (player_target == null)
5279 {
5280 return;
5281 }
5282
5283 if (this == player_target)
5284 {
5285 return;
5286 }
5287
5288 if (Config.FACTION_SYSTEM_ENABLED && target.isPlayer() && ((isGood() && player_target.isEvil()) || (isEvil() && player_target.isGood())))
5289 {
5290 return;
5291 }
5292
5293 if (isInDuel() && (player_target.getDuelId() == getDuelId()))
5294 {
5295 return;
5296 }
5297 if ((!isInsideZone(ZoneId.PVP) || !player_target.isInsideZone(ZoneId.PVP)) && (player_target.getReputation() >= 0))
5298 {
5299 if (checkIfPvP(player_target))
5300 {
5301 setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_PVP_TIME);
5302 }
5303 else
5304 {
5305 setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME);
5306 }
5307 if (getPvpFlag() == 0)
5308 {
5309 startPvPFlag();
5310 }
5311 }
5312 }
5313
5314 /**
5315 * @return {@code true} if player has Lucky effect and is level 9 or less
5316 */
5317 public boolean isLucky()
5318 {
5319 return (getLevel() <= 9) && isAffectedBySkill(CommonSkill.LUCKY.getId());
5320 }
5321
5322 /**
5323 * Restore the specified % of experience this L2PcInstance has lost and sends a Server->Client StatusUpdate packet.
5324 * @param restorePercent
5325 */
5326 public void restoreExp(double restorePercent)
5327 {
5328 if (getExpBeforeDeath() > 0)
5329 {
5330 // Restore the specified % of lost experience.
5331 getStat().addExp(Math.round(((getExpBeforeDeath() - getExp()) * restorePercent) / 100));
5332 setExpBeforeDeath(0);
5333 }
5334 }
5335
5336 /**
5337 * Reduce the Experience (and level if necessary) of the L2PcInstance in function of the calculated Death Penalty.<BR>
5338 * <B><U> Actions</U> :</B>
5339 * <li>Calculate the Experience loss</li>
5340 * <li>Set the value of _expBeforeDeath</li>
5341 * <li>Set the new Experience value of the L2PcInstance and Decrease its level if necessary</li>
5342 * <li>Send a Server->Client StatusUpdate packet with its new Experience</li>
5343 * @param killer
5344 */
5345 public void calculateDeathExpPenalty(L2Character killer)
5346 {
5347 final int lvl = getLevel();
5348 double percentLost = PlayerXpPercentLostData.getInstance().getXpPercent(getLevel());
5349
5350 if (killer != null)
5351 {
5352 if (killer.isRaid())
5353 {
5354 percentLost *= getStat().getValue(Stats.REDUCE_EXP_LOST_BY_RAID, 1);
5355 }
5356 else if (killer.isMonster())
5357 {
5358 percentLost *= getStat().getValue(Stats.REDUCE_EXP_LOST_BY_MOB, 1);
5359 }
5360 else if (killer.isPlayable())
5361 {
5362 percentLost *= getStat().getValue(Stats.REDUCE_EXP_LOST_BY_PVP, 1);
5363 }
5364 }
5365
5366 if (getReputation() < 0)
5367 {
5368 percentLost *= Config.RATE_KARMA_EXP_LOST;
5369 }
5370
5371 // Calculate the Experience loss
5372 long lostExp = 0;
5373 if (!L2Event.isParticipant(this))
5374 {
5375 if (lvl < ExperienceData.getInstance().getMaxLevel())
5376 {
5377 lostExp = Math.round(((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost) / 100);
5378 }
5379 else
5380 {
5381 lostExp = Math.round(((getStat().getExpForLevel(ExperienceData.getInstance().getMaxLevel()) - getStat().getExpForLevel(ExperienceData.getInstance().getMaxLevel() - 1)) * percentLost) / 100);
5382 }
5383 }
5384
5385 if ((killer != null) && killer.isPlayable() && atWarWith(killer.getActingPlayer()))
5386 {
5387 lostExp /= 4.0;
5388 }
5389
5390 setExpBeforeDeath(getExp());
5391 getStat().removeExp(lostExp);
5392 }
5393
5394 /**
5395 * Stop the HP/MP/CP Regeneration task. <B><U> Actions</U> :</B>
5396 * <li>Set the RegenActive flag to False</li>
5397 * <li>Stop the HP/MP/CP Regeneration task</li>
5398 */
5399 public void stopAllTimers()
5400 {
5401 stopHpMpRegeneration();
5402 stopWarnUserTakeBreak();
5403 stopWaterTask();
5404 stopFeed();
5405 clearPetData();
5406 storePetFood(_mountNpcId);
5407 stopRentPet();
5408 stopPvpRegTask();
5409 stopSoulTask();
5410 stopChargeTask();
5411 stopFameTask();
5412 stopRecoGiveTask();
5413 stopOnlineTimeUpdateTask();
5414 }
5415
5416 @Override
5417 public L2PetInstance getPet()
5418 {
5419 return _pet;
5420 }
5421
5422 @Override
5423 public Map<Integer, L2Summon> getServitors()
5424 {
5425 return _servitors == null ? Collections.emptyMap() : _servitors;
5426 }
5427
5428 public L2Summon getAnyServitor()
5429 {
5430 return getServitors().values().stream().findAny().orElse(null);
5431 }
5432
5433 public L2Summon getFirstServitor()
5434 {
5435 return getServitors().values().stream().findFirst().orElse(null);
5436 }
5437
5438 @Override
5439 public L2Summon getServitor(int objectId)
5440 {
5441 return getServitors().get(objectId);
5442 }
5443
5444 public List<L2Summon> getServitorsAndPets()
5445 {
5446 final List<L2Summon> summons = new ArrayList<>();
5447 summons.addAll(getServitors().values());
5448
5449 final L2PetInstance pet = getPet();
5450 if (pet != null)
5451 {
5452 summons.add(pet);
5453 }
5454
5455 return summons;
5456 }
5457
5458 /**
5459 * @return any summoned trap by this player or null.
5460 */
5461 public L2TrapInstance getTrap()
5462 {
5463 return getSummonedNpcs().stream().filter(L2Npc::isTrap).map(L2TrapInstance.class::cast).findAny().orElse(null);
5464 }
5465
5466 /**
5467 * Set the summoned Pet of the L2PcInstance.
5468 * @param pet
5469 */
5470 public void setPet(L2PetInstance pet)
5471 {
5472 _pet = pet;
5473 }
5474
5475 public void addServitor(L2Summon servitor)
5476 {
5477 if (_servitors == null)
5478 {
5479 synchronized (this)
5480 {
5481 if (_servitors == null)
5482 {
5483 _servitors = new ConcurrentHashMap<>(1);
5484 }
5485 }
5486 }
5487 _servitors.put(servitor.getObjectId(), servitor);
5488 }
5489
5490 /**
5491 * @return the L2Summon of the L2PcInstance or null.
5492 */
5493 public Set<L2TamedBeastInstance> getTrainedBeasts()
5494 {
5495 return _tamedBeast;
5496 }
5497
5498 /**
5499 * Set the L2Summon of the L2PcInstance.
5500 * @param tamedBeast
5501 */
5502 public void addTrainedBeast(L2TamedBeastInstance tamedBeast)
5503 {
5504 if (_tamedBeast == null)
5505 {
5506 synchronized (this)
5507 {
5508 if (_tamedBeast == null)
5509 {
5510 _tamedBeast = ConcurrentHashMap.newKeySet();
5511 }
5512 }
5513 }
5514 _tamedBeast.add(tamedBeast);
5515 }
5516
5517 /**
5518 * @return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
5519 */
5520 public L2Request getRequest()
5521 {
5522 return _request;
5523 }
5524
5525 /**
5526 * Set the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
5527 * @param requester
5528 */
5529 public void setActiveRequester(L2PcInstance requester)
5530 {
5531 _activeRequester = requester;
5532 }
5533
5534 /**
5535 * @return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...).
5536 */
5537 public L2PcInstance getActiveRequester()
5538 {
5539 final L2PcInstance requester = _activeRequester;
5540 if (requester != null)
5541 {
5542 if (requester.isRequestExpired() && (_activeTradeList == null))
5543 {
5544 _activeRequester = null;
5545 }
5546 }
5547 return _activeRequester;
5548 }
5549
5550 /**
5551 * @return True if a transaction is in progress.
5552 */
5553 public boolean isProcessingRequest()
5554 {
5555 return (getActiveRequester() != null) || (_requestExpireTime > GameTimeController.getInstance().getGameTicks());
5556 }
5557
5558 /**
5559 * @return True if a transaction is in progress.
5560 */
5561 public boolean isProcessingTransaction()
5562 {
5563 return (getActiveRequester() != null) || (_activeTradeList != null) || (_requestExpireTime > GameTimeController.getInstance().getGameTicks());
5564 }
5565
5566 /**
5567 * Used by fake players to emulate proper behavior.
5568 */
5569 public void blockRequest()
5570 {
5571 _requestExpireTime = GameTimeController.getInstance().getGameTicks() + (REQUEST_TIMEOUT * GameTimeController.TICKS_PER_SECOND);
5572 }
5573
5574 /**
5575 * Select the Warehouse to be used in next activity.
5576 * @param partner
5577 */
5578 public void onTransactionRequest(L2PcInstance partner)
5579 {
5580 _requestExpireTime = GameTimeController.getInstance().getGameTicks() + (REQUEST_TIMEOUT * GameTimeController.TICKS_PER_SECOND);
5581 partner.setActiveRequester(this);
5582 }
5583
5584 /**
5585 * Return true if last request is expired.
5586 * @return
5587 */
5588 public boolean isRequestExpired()
5589 {
5590 return !(_requestExpireTime > GameTimeController.getInstance().getGameTicks());
5591 }
5592
5593 /**
5594 * Select the Warehouse to be used in next activity.
5595 */
5596 public void onTransactionResponse()
5597 {
5598 _requestExpireTime = 0;
5599 }
5600
5601 /**
5602 * Select the Warehouse to be used in next activity.
5603 * @param warehouse
5604 */
5605 public void setActiveWarehouse(ItemContainer warehouse)
5606 {
5607 _activeWarehouse = warehouse;
5608 }
5609
5610 /**
5611 * @return active Warehouse.
5612 */
5613 public ItemContainer getActiveWarehouse()
5614 {
5615 return _activeWarehouse;
5616 }
5617
5618 /**
5619 * Select the TradeList to be used in next activity.
5620 * @param tradeList
5621 */
5622 public void setActiveTradeList(TradeList tradeList)
5623 {
5624 _activeTradeList = tradeList;
5625 }
5626
5627 /**
5628 * @return active TradeList.
5629 */
5630 public TradeList getActiveTradeList()
5631 {
5632 return _activeTradeList;
5633 }
5634
5635 public void onTradeStart(L2PcInstance partner)
5636 {
5637 _activeTradeList = new TradeList(this);
5638 _activeTradeList.setPartner(partner);
5639
5640 final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOU_BEGIN_TRADING_WITH_C1);
5641 msg.addPcName(partner);
5642 sendPacket(msg);
5643 sendPacket(new TradeStart(this));
5644 }
5645
5646 public void onTradeConfirm(L2PcInstance partner)
5647 {
5648 final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_CONFIRMED_THE_TRADE);
5649 msg.addPcName(partner);
5650 sendPacket(msg);
5651 sendPacket(TradeOtherDone.STATIC_PACKET);
5652 }
5653
5654 public void onTradeCancel(L2PcInstance partner)
5655 {
5656 if (_activeTradeList == null)
5657 {
5658 return;
5659 }
5660
5661 _activeTradeList.lock();
5662 _activeTradeList = null;
5663
5664 sendPacket(new TradeDone(0));
5665 final SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_CANCELLED_THE_TRADE);
5666 msg.addPcName(partner);
5667 sendPacket(msg);
5668 }
5669
5670 public void onTradeFinish(boolean successfull)
5671 {
5672 _activeTradeList = null;
5673 sendPacket(new TradeDone(1));
5674 if (successfull)
5675 {
5676 sendPacket(SystemMessageId.YOUR_TRADE_WAS_SUCCESSFUL);
5677 }
5678 }
5679
5680 public void startTrade(L2PcInstance partner)
5681 {
5682 onTradeStart(partner);
5683 partner.onTradeStart(this);
5684 }
5685
5686 public void cancelActiveTrade()
5687 {
5688 if (_activeTradeList == null)
5689 {
5690 return;
5691 }
5692
5693 final L2PcInstance partner = _activeTradeList.getPartner();
5694 if (partner != null)
5695 {
5696 partner.onTradeCancel(this);
5697 }
5698 onTradeCancel(this);
5699 }
5700
5701 public boolean hasManufactureShop()
5702 {
5703 return (_manufactureItems != null) && !_manufactureItems.isEmpty();
5704 }
5705
5706 /**
5707 * Get the manufacture items map of this player.
5708 * @return the the manufacture items map
5709 */
5710 public Map<Integer, Long> getManufactureItems()
5711 {
5712 if (_manufactureItems == null)
5713 {
5714 return Collections.emptyMap();
5715 }
5716
5717 return _manufactureItems;
5718 }
5719
5720 public void setManufactureItems(Map<Integer, Long> manufactureItems)
5721 {
5722 _manufactureItems = manufactureItems;
5723 }
5724
5725 /**
5726 * Get the store name, if any.
5727 * @return the store name
5728 */
5729 public String getStoreName()
5730 {
5731 return _storeName;
5732 }
5733
5734 /**
5735 * Set the store name.
5736 * @param name the store name to set
5737 */
5738 public void setStoreName(String name)
5739 {
5740 _storeName = name == null ? "" : name;
5741 }
5742
5743 /**
5744 * @return the _buyList object of the L2PcInstance.
5745 */
5746 public TradeList getSellList()
5747 {
5748 if (_sellList == null)
5749 {
5750 _sellList = new TradeList(this);
5751 }
5752 return _sellList;
5753 }
5754
5755 /**
5756 * @return the _buyList object of the L2PcInstance.
5757 */
5758 public TradeList getBuyList()
5759 {
5760 if (_buyList == null)
5761 {
5762 _buyList = new TradeList(this);
5763 }
5764 return _buyList;
5765 }
5766
5767 /**
5768 * Set the Private Store type of the L2PcInstance. <B><U> Values </U> :</B>
5769 * <li>0 : STORE_PRIVATE_NONE</li>
5770 * <li>1 : STORE_PRIVATE_SELL</li>
5771 * <li>2 : sellmanage</li><BR>
5772 * <li>3 : STORE_PRIVATE_BUY</li><BR>
5773 * <li>4 : buymanage</li><BR>
5774 * <li>5 : STORE_PRIVATE_MANUFACTURE</li><BR>
5775 * @param privateStoreType
5776 */
5777 public void setPrivateStoreType(PrivateStoreType privateStoreType)
5778 {
5779 _privateStoreType = privateStoreType;
5780
5781 if (Config.OFFLINE_DISCONNECT_FINISHED && (privateStoreType == PrivateStoreType.NONE) && ((getClient() == null) || getClient().isDetached()))
5782 {
5783 deleteMe();
5784 }
5785 }
5786
5787 /**
5788 * <B><U> Values </U> :</B>
5789 * <li>0 : STORE_PRIVATE_NONE</li>
5790 * <li>1 : STORE_PRIVATE_SELL</li>
5791 * <li>2 : sellmanage</li><BR>
5792 * <li>3 : STORE_PRIVATE_BUY</li><BR>
5793 * <li>4 : buymanage</li><BR>
5794 * <li>5 : STORE_PRIVATE_MANUFACTURE</li><BR>
5795 * @return the Private Store type of the L2PcInstance.
5796 */
5797 public PrivateStoreType getPrivateStoreType()
5798 {
5799 return _privateStoreType;
5800 }
5801
5802 /**
5803 * Set the _clan object, _clanId, _clanLeader Flag and title of the L2PcInstance.
5804 * @param clan
5805 */
5806 public void setClan(L2Clan clan)
5807 {
5808 _clan = clan;
5809
5810 if (clan == null)
5811 {
5812 setTitle("");
5813 _clanId = 0;
5814 _clanPrivileges = new EnumIntBitmask<>(ClanPrivilege.class, false);
5815 _pledgeType = 0;
5816 _powerGrade = 0;
5817 _lvlJoinedAcademy = 0;
5818 _apprentice = 0;
5819 _sponsor = 0;
5820 _activeWarehouse = null;
5821 return;
5822 }
5823
5824 if (!clan.isMember(getObjectId()))
5825 {
5826 // char has been kicked from clan
5827 setClan(null);
5828 return;
5829 }
5830
5831 _clanId = clan.getId();
5832 }
5833
5834 /**
5835 * @return the _clan object of the L2PcInstance.
5836 */
5837 @Override
5838 public L2Clan getClan()
5839 {
5840 return _clan;
5841 }
5842
5843 /**
5844 * @return True if the L2PcInstance is the leader of its clan.
5845 */
5846 public boolean isClanLeader()
5847 {
5848 if (getClan() == null)
5849 {
5850 return false;
5851 }
5852 return getObjectId() == getClan().getLeaderId();
5853 }
5854
5855 /**
5856 * Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True.
5857 * @param type
5858 */
5859 @Override
5860 protected boolean checkAndEquipAmmunition(EtcItemType type)
5861 {
5862 L2ItemInstance arrows = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
5863 if (arrows == null)
5864 {
5865 final L2Weapon weapon = getActiveWeaponItem();
5866 if (type == EtcItemType.ARROW)
5867 {
5868 arrows = getInventory().findArrowForBow(weapon);
5869 }
5870 else if (type == EtcItemType.BOLT)
5871 {
5872 arrows = getInventory().findBoltForCrossBow(weapon);
5873 }
5874 if (arrows != null)
5875 {
5876 // Equip arrows needed in left hand
5877 getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, arrows);
5878 sendItemList(false);
5879 return true;
5880 }
5881 }
5882 else
5883 {
5884 return true;
5885 }
5886 return false;
5887 }
5888
5889 /**
5890 * Disarm the player's weapon.
5891 * @return {@code true} if the player was disarmed or doesn't have a weapon to disarm, {@code false} otherwise.
5892 */
5893 public boolean disarmWeapons()
5894 {
5895 // If there is no weapon to disarm then return true.
5896 final L2ItemInstance wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND);
5897 if (wpn == null)
5898 {
5899 return true;
5900 }
5901
5902 // Don't allow disarming a cursed weapon
5903 if (isCursedWeaponEquipped())
5904 {
5905 return false;
5906 }
5907
5908 // Don't allow disarming a Combat Flag or Territory Ward.
5909 if (isCombatFlagEquipped())
5910 {
5911 return false;
5912 }
5913
5914 // Don't allow disarming if the weapon is force equip.
5915 if (wpn.getWeaponItem().isForceEquip())
5916 {
5917 return false;
5918 }
5919
5920 final L2ItemInstance[] unequiped = getInventory().unEquipItemInBodySlotAndRecord(wpn.getItem().getBodyPart());
5921 final InventoryUpdate iu = new InventoryUpdate();
5922 for (L2ItemInstance itm : unequiped)
5923 {
5924 iu.addModifiedItem(itm);
5925 }
5926
5927 sendInventoryUpdate(iu);
5928 abortAttack();
5929 broadcastUserInfo();
5930
5931 // This can be 0 if the user pressed the right mousebutton twice very fast.
5932 if (unequiped.length > 0)
5933 {
5934 final SystemMessage sm;
5935 if (unequiped[0].getEnchantLevel() > 0)
5936 {
5937 sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED);
5938 sm.addInt(unequiped[0].getEnchantLevel());
5939 sm.addItemName(unequiped[0]);
5940 }
5941 else
5942 {
5943 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED);
5944 sm.addItemName(unequiped[0]);
5945 }
5946 sendPacket(sm);
5947 }
5948 return true;
5949 }
5950
5951 /**
5952 * Disarm the player's shield.
5953 * @return {@code true}.
5954 */
5955 public boolean disarmShield()
5956 {
5957 final L2ItemInstance sld = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND);
5958 if (sld != null)
5959 {
5960 final L2ItemInstance[] unequiped = getInventory().unEquipItemInBodySlotAndRecord(sld.getItem().getBodyPart());
5961 final InventoryUpdate iu = new InventoryUpdate();
5962 for (L2ItemInstance itm : unequiped)
5963 {
5964 iu.addModifiedItem(itm);
5965 }
5966 sendInventoryUpdate(iu);
5967
5968 abortAttack();
5969 broadcastUserInfo();
5970
5971 // this can be 0 if the user pressed the right mousebutton twice very fast
5972 if (unequiped.length > 0)
5973 {
5974 SystemMessage sm = null;
5975 if (unequiped[0].getEnchantLevel() > 0)
5976 {
5977 sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED);
5978 sm.addInt(unequiped[0].getEnchantLevel());
5979 sm.addItemName(unequiped[0]);
5980 }
5981 else
5982 {
5983 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED);
5984 sm.addItemName(unequiped[0]);
5985 }
5986 sendPacket(sm);
5987 }
5988 }
5989 return true;
5990 }
5991
5992 public boolean mount(L2Summon pet)
5993 {
5994 if (!disarmWeapons() || !disarmShield() || isTransformed())
5995 {
5996 return false;
5997 }
5998
5999 getEffectList().stopAllToggles();
6000 setMount(pet.getId(), pet.getLevel());
6001 setMountObjectID(pet.getControlObjectId());
6002 clearPetData();
6003 startFeed(pet.getId());
6004 broadcastPacket(new Ride(this));
6005
6006 // Notify self and others about speed change
6007 broadcastUserInfo();
6008
6009 pet.unSummon(this);
6010 return true;
6011 }
6012
6013 public boolean mount(int npcId, int controlItemObjId, boolean useFood)
6014 {
6015 if (!disarmWeapons() || !disarmShield() || isTransformed())
6016 {
6017 return false;
6018 }
6019
6020 getEffectList().stopAllToggles();
6021 setMount(npcId, getLevel());
6022 clearPetData();
6023 setMountObjectID(controlItemObjId);
6024 broadcastPacket(new Ride(this));
6025
6026 // Notify self and others about speed change
6027 broadcastUserInfo();
6028 if (useFood)
6029 {
6030 startFeed(npcId);
6031 }
6032 return true;
6033 }
6034
6035 public boolean mountPlayer(L2Summon pet)
6036 {
6037 if ((pet != null) && pet.isMountable() && !isMounted() && !isBetrayed())
6038 {
6039 if (isDead())
6040 {
6041 // A strider cannot be ridden when dead
6042 sendPacket(ActionFailed.STATIC_PACKET);
6043 sendPacket(SystemMessageId.A_STRIDER_CANNOT_BE_RIDDEN_WHEN_DEAD);
6044 return false;
6045 }
6046 else if (pet.isDead())
6047 {
6048 // A dead strider cannot be ridden.
6049 sendPacket(ActionFailed.STATIC_PACKET);
6050 sendPacket(SystemMessageId.A_DEAD_STRIDER_CANNOT_BE_RIDDEN);
6051 return false;
6052 }
6053 else if (pet.isInCombat() || pet.isRooted())
6054 {
6055 // A strider in battle cannot be ridden
6056 sendPacket(ActionFailed.STATIC_PACKET);
6057 sendPacket(SystemMessageId.A_STRIDER_IN_BATTLE_CANNOT_BE_RIDDEN);
6058 return false;
6059
6060 }
6061 else if (isInCombat())
6062 {
6063 // A strider cannot be ridden while in battle
6064 sendPacket(ActionFailed.STATIC_PACKET);
6065 sendPacket(SystemMessageId.A_STRIDER_CANNOT_BE_RIDDEN_WHILE_IN_BATTLE);
6066 return false;
6067 }
6068 else if (isSitting())
6069 {
6070 // A strider can be ridden only when standing
6071 sendPacket(ActionFailed.STATIC_PACKET);
6072 sendPacket(SystemMessageId.A_STRIDER_CAN_BE_RIDDEN_ONLY_WHEN_STANDING);
6073 return false;
6074 }
6075 else if (isFishing())
6076 {
6077 // You can't mount, dismount, break and drop items while fishing
6078 sendPacket(ActionFailed.STATIC_PACKET);
6079 sendPacket(SystemMessageId.YOU_CANNOT_DO_THAT_WHILE_FISHING_2);
6080 return false;
6081 }
6082 else if (isTransformed() || isCursedWeaponEquipped())
6083 {
6084 // no message needed, player while transformed doesn't have mount action
6085 sendPacket(ActionFailed.STATIC_PACKET);
6086 return false;
6087 }
6088 else if (getInventory().getItemByItemId(9819) != null)
6089 {
6090 sendPacket(ActionFailed.STATIC_PACKET);
6091 // FIXME: Wrong Message
6092 sendMessage("You cannot mount a steed while holding a flag.");
6093 return false;
6094 }
6095 else if (pet.isHungry())
6096 {
6097 sendPacket(ActionFailed.STATIC_PACKET);
6098 sendPacket(SystemMessageId.A_HUNGRY_STRIDER_CANNOT_BE_MOUNTED_OR_DISMOUNTED);
6099 return false;
6100 }
6101 else if (!Util.checkIfInRange(200, this, pet, true))
6102 {
6103 sendPacket(ActionFailed.STATIC_PACKET);
6104 sendPacket(SystemMessageId.YOU_ARE_TOO_FAR_AWAY_FROM_YOUR_MOUNT_TO_RIDE);
6105 return false;
6106 }
6107 else if (!pet.isDead() && !isMounted())
6108 {
6109 mount(pet);
6110 }
6111 }
6112 else if (isRentedPet())
6113 {
6114 stopRentPet();
6115 }
6116 else if (isMounted())
6117 {
6118 if ((getMountType() == MountType.WYVERN) && isInsideZone(ZoneId.NO_LANDING))
6119 {
6120 sendPacket(ActionFailed.STATIC_PACKET);
6121 sendPacket(SystemMessageId.YOU_ARE_NOT_ALLOWED_TO_DISMOUNT_IN_THIS_LOCATION);
6122 return false;
6123 }
6124 else if (isHungry())
6125 {
6126 sendPacket(ActionFailed.STATIC_PACKET);
6127 sendPacket(SystemMessageId.A_HUNGRY_STRIDER_CANNOT_BE_MOUNTED_OR_DISMOUNTED);
6128 return false;
6129 }
6130 else
6131 {
6132 dismount();
6133 }
6134 }
6135 return true;
6136 }
6137
6138 public boolean dismount()
6139 {
6140 final boolean wasFlying = isFlying();
6141
6142 sendPacket(new SetupGauge(3, 0, 0));
6143 final int petId = _mountNpcId;
6144 setMount(0, 0);
6145 stopFeed();
6146 clearPetData();
6147 if (wasFlying)
6148 {
6149 removeSkill(CommonSkill.WYVERN_BREATH.getSkill());
6150 }
6151 broadcastPacket(new Ride(this));
6152 setMountObjectID(0);
6153 storePetFood(petId);
6154 // Notify self and others about speed change
6155 broadcastUserInfo();
6156 return true;
6157 }
6158
6159 public void setUptime(long time)
6160 {
6161 _uptime = time;
6162 }
6163
6164 public long getUptime()
6165 {
6166 return System.currentTimeMillis() - _uptime;
6167 }
6168
6169 /**
6170 * Return True if the L2PcInstance is invulnerable.
6171 */
6172 @Override
6173 public boolean isInvul()
6174 {
6175 return super.isInvul() || isTeleportProtected();
6176 }
6177
6178 /**
6179 * Return True if the L2PcInstance has a Party in progress.
6180 */
6181 @Override
6182 public boolean isInParty()
6183 {
6184 return _party != null;
6185 }
6186
6187 /**
6188 * Set the _party object of the L2PcInstance (without joining it).
6189 * @param party
6190 */
6191 public void setParty(L2Party party)
6192 {
6193 _party = party;
6194 }
6195
6196 /**
6197 * Set the _party object of the L2PcInstance AND join it.
6198 * @param party
6199 */
6200 public void joinParty(L2Party party)
6201 {
6202 if (party != null)
6203 {
6204 // First set the party otherwise this wouldn't be considered
6205 // as in a party into the L2Character.updateEffectIcons() call.
6206 _party = party;
6207 party.addPartyMember(this);
6208 }
6209 }
6210
6211 /**
6212 * Manage the Leave Party task of the L2PcInstance.
6213 */
6214 public void leaveParty()
6215 {
6216 if (isInParty())
6217 {
6218 _party.removePartyMember(this, MessageType.DISCONNECTED);
6219 _party = null;
6220 }
6221 }
6222
6223 /**
6224 * Return the _party object of the L2PcInstance.
6225 */
6226 @Override
6227 public L2Party getParty()
6228 {
6229 return _party;
6230 }
6231
6232 public boolean isInCommandChannel()
6233 {
6234 return isInParty() && getParty().isInCommandChannel();
6235 }
6236
6237 public L2CommandChannel getCommandChannel()
6238 {
6239 return (isInCommandChannel()) ? getParty().getCommandChannel() : null;
6240 }
6241
6242 /**
6243 * Return True if the L2PcInstance is a GM.
6244 */
6245 @Override
6246 public boolean isGM()
6247 {
6248 return getAccessLevel().isGm();
6249 }
6250
6251 /**
6252 * Set the _accessLevel of the L2PcInstance.
6253 * @param level
6254 * @param broadcast
6255 * @param updateInDb
6256 */
6257 public void setAccessLevel(int level, boolean broadcast, boolean updateInDb)
6258 {
6259 L2AccessLevel accessLevel = AdminData.getInstance().getAccessLevel(level);
6260 if (accessLevel == null)
6261 {
6262 LOGGER.warning("Can't find access level " + level + " for character " + toString());
6263 accessLevel = AdminData.getInstance().getAccessLevel(0);
6264 }
6265
6266 if ((accessLevel.getLevel() == 0) && (Config.DEFAULT_ACCESS_LEVEL > 0))
6267 {
6268 accessLevel = AdminData.getInstance().getAccessLevel(Config.DEFAULT_ACCESS_LEVEL);
6269 if (accessLevel == null)
6270 {
6271 LOGGER.warning("Config's default access level (" + Config.DEFAULT_ACCESS_LEVEL + ") is not defined, defaulting to 0!");
6272 accessLevel = AdminData.getInstance().getAccessLevel(0);
6273 Config.DEFAULT_ACCESS_LEVEL = 0;
6274 }
6275 }
6276
6277 _accessLevel = accessLevel;
6278
6279 getAppearance().setNameColor(_accessLevel.getNameColor());
6280 getAppearance().setTitleColor(_accessLevel.getTitleColor());
6281 if (broadcast)
6282 {
6283 broadcastUserInfo();
6284 }
6285
6286 if (updateInDb)
6287 {
6288 try (Connection con = DatabaseFactory.getInstance().getConnection();
6289 PreparedStatement ps = con.prepareStatement(UPDATE_CHARACTER_ACCESS))
6290 {
6291 ps.setInt(1, accessLevel.getLevel());
6292 ps.setInt(2, getObjectId());
6293 ps.executeUpdate();
6294 }
6295 catch (SQLException e)
6296 {
6297 LOGGER.log(Level.WARNING, "Failed to update character's accesslevel in db: " + toString(), e);
6298 }
6299 }
6300
6301 CharNameTable.getInstance().addName(this);
6302
6303 if (accessLevel == null)
6304 {
6305 LOGGER.warning("Tryed to set unregistered access level " + level + " for " + toString() + ". Setting access level without privileges!");
6306 }
6307 else if (level > 0)
6308 {
6309 LOGGER.warning(_accessLevel.getName() + " access level set for character " + getName() + "! Just a warning to be careful ;)");
6310 }
6311 }
6312
6313 public void setAccountAccesslevel(int level)
6314 {
6315 LoginServerThread.getInstance().sendAccessLevel(getAccountName(), level);
6316 }
6317
6318 /**
6319 * @return the _accessLevel of the L2PcInstance.
6320 */
6321 @Override
6322 public L2AccessLevel getAccessLevel()
6323 {
6324 return _accessLevel;
6325 }
6326
6327 /**
6328 * Update Stats of the L2PcInstance client side by sending Server->Client packet UserInfo/StatusUpdate to this L2PcInstance and CharInfo/StatusUpdate to all L2PcInstance in its _KnownPlayers (broadcast).
6329 * @param broadcastType
6330 */
6331 public void updateAndBroadcastStatus(int broadcastType)
6332 {
6333 refreshOverloaded(true);
6334 refreshExpertisePenalty();
6335 // Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers (broadcast)
6336 if (broadcastType == 1)
6337 {
6338 sendPacket(new UserInfo(this));
6339 }
6340 if (broadcastType == 2)
6341 {
6342 broadcastUserInfo();
6343 }
6344 }
6345
6346 /**
6347 * Send a Server->Client StatusUpdate packet with Karma to the L2PcInstance and all L2PcInstance to inform (broadcast).
6348 */
6349 public void broadcastReputation()
6350 {
6351 broadcastUserInfo(UserInfoType.SOCIAL);
6352
6353 L2World.getInstance().forEachVisibleObject(this, L2PcInstance.class, player ->
6354 {
6355 if (!isVisibleFor(player))
6356 {
6357 return;
6358 }
6359
6360 final int relation = getRelation(player);
6361 final Integer oldrelation = getKnownRelations().get(player.getObjectId());
6362 if ((oldrelation == null) || (oldrelation != relation))
6363 {
6364 final RelationChanged rc = new RelationChanged();
6365 rc.addRelation(this, relation, isAutoAttackable(player));
6366 if (hasSummon())
6367 {
6368 final L2Summon pet = getPet();
6369 if (pet != null)
6370 {
6371 rc.addRelation(pet, relation, isAutoAttackable(player));
6372 }
6373 if (hasServitors())
6374 {
6375 getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player)));
6376 }
6377 }
6378 player.sendPacket(rc);
6379 getKnownRelations().put(player.getObjectId(), relation);
6380 }
6381 });
6382 }
6383
6384 /**
6385 * 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).
6386 * @param isOnline
6387 * @param updateInDb
6388 */
6389 public void setOnlineStatus(boolean isOnline, boolean updateInDb)
6390 {
6391 if (_isOnline != isOnline)
6392 {
6393 _isOnline = isOnline;
6394 }
6395
6396 // Update the characters table of the database with online status and lastAccess (called when login and logout)
6397 if (updateInDb)
6398 {
6399 updateOnlineStatus();
6400 }
6401 }
6402
6403 /**
6404 * Update the characters table of the database with online status and lastAccess of this L2PcInstance (called when login and logout).
6405 */
6406 public void updateOnlineStatus()
6407 {
6408 try (Connection con = DatabaseFactory.getInstance().getConnection();
6409 PreparedStatement statement = con.prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE charId=?"))
6410 {
6411 statement.setInt(1, isOnlineInt());
6412 statement.setLong(2, System.currentTimeMillis());
6413 statement.setInt(3, getObjectId());
6414 statement.execute();
6415 }
6416 catch (Exception e)
6417 {
6418 LOGGER.log(Level.SEVERE, "Failed updating character online status.", e);
6419 }
6420 }
6421
6422 /**
6423 * Create a new player in the characters table of the database.
6424 * @return
6425 */
6426 private boolean createDb()
6427 {
6428 try (Connection con = DatabaseFactory.getInstance().getConnection();
6429 PreparedStatement statement = con.prepareStatement(INSERT_CHARACTER))
6430 {
6431 statement.setString(1, _accountName);
6432 statement.setInt(2, getObjectId());
6433 statement.setString(3, getName());
6434 statement.setInt(4, getLevel());
6435 statement.setInt(5, getMaxHp());
6436 statement.setDouble(6, getCurrentHp());
6437 statement.setInt(7, getMaxCp());
6438 statement.setDouble(8, getCurrentCp());
6439 statement.setInt(9, getMaxMp());
6440 statement.setDouble(10, getCurrentMp());
6441 statement.setInt(11, getAppearance().getFace());
6442 statement.setInt(12, getAppearance().getHairStyle());
6443 statement.setInt(13, getAppearance().getHairColor());
6444 statement.setInt(14, getAppearance().getSex() ? 1 : 0);
6445 statement.setLong(15, getExp());
6446 statement.setLong(16, getSp());
6447 statement.setInt(17, getReputation());
6448 statement.setInt(18, getFame());
6449 statement.setInt(19, getRaidbossPoints());
6450 statement.setInt(20, getPvpKills());
6451 statement.setInt(21, getPkKills());
6452 statement.setInt(22, getClanId());
6453 statement.setInt(23, getRace().ordinal());
6454 statement.setInt(24, getClassId().getId());
6455 statement.setLong(25, getDeleteTimer());
6456 statement.setInt(26, getCreateItemLevel() > 0 ? 1 : 0);
6457 statement.setString(27, getTitle());
6458 statement.setInt(28, getAppearance().getTitleColor());
6459 statement.setInt(29, isOnlineInt());
6460 statement.setInt(30, getClanPrivileges().getBitmask());
6461 statement.setInt(31, getWantsPeace());
6462 statement.setInt(32, getBaseClass());
6463 statement.setInt(33, _nobleLevel);
6464 statement.setLong(34, 0);
6465 statement.setInt(35, PcStat.MIN_VITALITY_POINTS);
6466 statement.setDate(36, new Date(getCreateDate().getTimeInMillis()));
6467 statement.executeUpdate();
6468 }
6469 catch (Exception e)
6470 {
6471 LOGGER.log(Level.SEVERE, "Could not insert char data: " + e.getMessage(), e);
6472 return false;
6473 }
6474 return true;
6475 }
6476
6477 /**
6478 * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world. <B><U> Actions</U> :</B>
6479 * <li>Retrieve the L2PcInstance from the characters table of the database</li>
6480 * <li>Add the L2PcInstance object in _allObjects</li>
6481 * <li>Set the x,y,z position of the L2PcInstance and make it invisible</li>
6482 * <li>Update the overloaded status of the L2PcInstance</li>
6483 * @param objectId Identifier of the object to initialized
6484 * @return The L2PcInstance loaded from the database
6485 */
6486 private static L2PcInstance restore(int objectId)
6487 {
6488 L2PcInstance player = null;
6489 double currentCp = 0;
6490 double currentHp = 0;
6491 double currentMp = 0;
6492 try (Connection con = DatabaseFactory.getInstance().getConnection();
6493 PreparedStatement statement = con.prepareStatement(RESTORE_CHARACTER))
6494 {
6495 // Retrieve the L2PcInstance from the characters table of the database
6496 statement.setInt(1, objectId);
6497 try (ResultSet rset = statement.executeQuery())
6498 {
6499 if (rset.next())
6500 {
6501 final int activeClassId = rset.getInt("classid");
6502 final boolean female = rset.getInt("sex") != Sex.MALE.ordinal();
6503 final L2PcTemplate template = PlayerTemplateData.getInstance().getTemplate(activeClassId);
6504 final PcAppearance app = new PcAppearance(rset.getByte("face"), rset.getByte("hairColor"), rset.getByte("hairStyle"), female);
6505
6506 player = new L2PcInstance(objectId, template, rset.getString("account_name"), app);
6507 player.setName(rset.getString("char_name"));
6508 player.setLastAccess(rset.getLong("lastAccess"));
6509
6510 player.getStat().setExp(rset.getLong("exp"));
6511 player.setExpBeforeDeath(rset.getLong("expBeforeDeath"));
6512 player.getStat().setLevel(rset.getByte("level"));
6513 player.getStat().setSp(rset.getLong("sp"));
6514
6515 player.setWantsPeace(rset.getInt("wantspeace"));
6516
6517 player.setHeading(rset.getInt("heading"));
6518
6519 player.setInitialReputation(rset.getInt("reputation"));
6520 player.setFame(rset.getInt("fame"));
6521 player.setRaidbossPoints(rset.getInt("raidbossPoints"));
6522 player.setPvpKills(rset.getInt("pvpkills"));
6523 player.setPkKills(rset.getInt("pkkills"));
6524 player.setOnlineTime(rset.getLong("onlinetime"));
6525 final int nobleLevel = rset.getInt("nobless");
6526 player.setNobleLevel(nobleLevel);
6527
6528 final int factionId = rset.getInt("faction");
6529 if (factionId == 1)
6530 {
6531 player.setGood();
6532 }
6533 if (factionId == 2)
6534 {
6535 player.setEvil();
6536 }
6537
6538 player.setClanJoinExpiryTime(rset.getLong("clan_join_expiry_time"));
6539 if (player.getClanJoinExpiryTime() < System.currentTimeMillis())
6540 {
6541 player.setClanJoinExpiryTime(0);
6542 }
6543 player.setClanCreateExpiryTime(rset.getLong("clan_create_expiry_time"));
6544 if (player.getClanCreateExpiryTime() < System.currentTimeMillis())
6545 {
6546 player.setClanCreateExpiryTime(0);
6547 }
6548
6549 player.setPcCafePoints(rset.getInt("pccafe_points"));
6550
6551 final int clanId = rset.getInt("clanid");
6552 player.setPowerGrade(rset.getInt("power_grade"));
6553 player.getStat().setVitalityPoints(rset.getInt("vitality_points"));
6554 player.setPledgeType(rset.getInt("subpledge"));
6555 // player.setApprentice(rset.getInt("apprentice"));
6556
6557 // Set Hero status if it applies.
6558 player.setHero(Hero.getInstance().isHero(objectId));
6559
6560 if (clanId > 0)
6561 {
6562 player.setClan(ClanTable.getInstance().getClan(clanId));
6563 }
6564
6565 if (player.getClan() != null)
6566 {
6567 if (player.getClan().getLeaderId() != player.getObjectId())
6568 {
6569 if (player.getPowerGrade() == 0)
6570 {
6571 player.setPowerGrade(5);
6572 }
6573 player.setClanPrivileges(player.getClan().getRankPrivs(player.getPowerGrade()));
6574 }
6575 else
6576 {
6577 player.getClanPrivileges().setAll();
6578 player.setPowerGrade(1);
6579 }
6580 player.setPledgeClass(L2ClanMember.calculatePledgeClass(player));
6581 }
6582 else
6583 {
6584 if (nobleLevel > 0)
6585 {
6586 player.setPledgeClass(5);
6587 }
6588
6589 if (player.isHero())
6590 {
6591 player.setPledgeClass(8);
6592 }
6593
6594 player.getClanPrivileges().clear();
6595 }
6596
6597 player.setDeleteTimer(rset.getLong("deletetime"));
6598 player.setTitle(rset.getString("title"));
6599 player.setAccessLevel(rset.getInt("accesslevel"), false, false);
6600 final int titleColor = rset.getInt("title_color");
6601 if (titleColor != PcAppearance.DEFAULT_TITLE_COLOR)
6602 {
6603 player.getAppearance().setTitleColor(titleColor);
6604 }
6605 player.setFistsWeaponItem(player.findFistsWeaponItem(activeClassId));
6606 player.setUptime(System.currentTimeMillis());
6607
6608 currentHp = rset.getDouble("curHp");
6609 currentCp = rset.getDouble("curCp");
6610 currentMp = rset.getDouble("curMp");
6611
6612 player.setClassIndex(0);
6613 try
6614 {
6615 player.setBaseClass(rset.getInt("base_class"));
6616 }
6617 catch (Exception e)
6618 {
6619 player.setBaseClass(activeClassId);
6620 LOGGER.log(Level.WARNING, "Exception during player.setBaseClass for player: " + player + " base class: " + rset.getInt("base_class"), e);
6621 }
6622
6623 // Restore Subclass Data (cannot be done earlier in function)
6624 if (restoreSubClassData(player))
6625 {
6626 if (activeClassId != player.getBaseClass())
6627 {
6628 for (SubClass subClass : player.getSubClasses().values())
6629 {
6630 if (subClass.getClassId() == activeClassId)
6631 {
6632 player.setClassIndex(subClass.getClassIndex());
6633 }
6634 }
6635 }
6636 }
6637 if ((player.getClassIndex() == 0) && (activeClassId != player.getBaseClass()))
6638 {
6639 // Subclass in use but doesn't exist in DB -
6640 // a possible restart-while-modifysubclass cheat has been attempted.
6641 // Switching to use base class
6642 player.setClassId(player.getBaseClass());
6643 LOGGER.warning("Player " + player.getName() + " reverted to base class. Possibly has tried a relogin exploit while subclassing.");
6644 }
6645 else
6646 {
6647 player._activeClass = activeClassId;
6648 }
6649
6650 player.setApprentice(rset.getInt("apprentice"));
6651 player.setSponsor(rset.getInt("sponsor"));
6652 player.setLvlJoinedAcademy(rset.getInt("lvl_joined_academy"));
6653
6654 CursedWeaponsManager.getInstance().checkPlayer(player);
6655
6656 // Set the x,y,z position of the L2PcInstance and make it invisible
6657 player.setXYZInvisible(rset.getInt("x"), rset.getInt("y"), rset.getInt("z"));
6658
6659 // Set Teleport Bookmark Slot
6660 player.setBookMarkSlot(rset.getInt("BookmarkSlot"));
6661
6662 // character creation Time
6663 player.getCreateDate().setTime(rset.getDate("createDate"));
6664
6665 // Language
6666 player.setLang(rset.getString("language"));
6667
6668 // Retrieve the name and ID of the other characters assigned to this account.
6669 try (PreparedStatement stmt = con.prepareStatement("SELECT charId, char_name FROM characters WHERE account_name=? AND charId<>?"))
6670 {
6671 stmt.setString(1, player._accountName);
6672 stmt.setInt(2, objectId);
6673 try (ResultSet chars = stmt.executeQuery())
6674 {
6675 while (chars.next())
6676 {
6677 player._chars.put(chars.getInt("charId"), chars.getString("char_name"));
6678 }
6679 }
6680 }
6681 }
6682 }
6683
6684 if (player == null)
6685 {
6686 return null;
6687 }
6688
6689 if (player.isGM())
6690 {
6691 final long masks = player.getVariables().getLong(COND_OVERRIDE_KEY, PcCondOverride.getAllExceptionsMask());
6692 player.setOverrideCond(masks);
6693 }
6694
6695 // Retrieve from the database all secondary data of this L2PcInstance
6696 // Note that Clan, Noblesse and Hero skills are given separately and not here.
6697 // Retrieve from the database all skills of this L2PcInstance and add them to _skills
6698 player.restoreCharData();
6699
6700 // Reward auto-get skills and all available skills if auto-learn skills is true.
6701 player.rewardSkills();
6702
6703 // Retrieve from the database all items of this L2PcInstance and add them to _inventory
6704 player.getInventory().restore();
6705 player.getFreight().restore();
6706 if (!Config.WAREHOUSE_CACHE)
6707 {
6708 player.getWarehouse();
6709 }
6710
6711 player.restoreItemReuse();
6712
6713 // Restore player shortcuts
6714 player.restoreShortCuts();
6715
6716 // Initialize status update cache
6717 player.initStatusUpdateCache();
6718
6719 // Restore current Cp, HP and MP values
6720 player.setCurrentCp(currentCp);
6721 player.setCurrentHp(currentHp);
6722 player.setCurrentMp(currentMp);
6723
6724 player.setOriginalCpHpMp(currentCp, currentHp, currentMp);
6725
6726 if (currentHp < 0.5)
6727 {
6728 player.setIsDead(true);
6729 player.stopHpMpRegeneration();
6730 }
6731
6732 // Restore pet if exists in the world
6733 player.setPet(L2World.getInstance().getPet(player.getObjectId()));
6734 final L2Summon pet = player.getPet();
6735 if (pet != null)
6736 {
6737 pet.setOwner(player);
6738 }
6739
6740 if (player.hasServitors())
6741 {
6742 for (L2Summon summon : player.getServitors().values())
6743 {
6744 summon.setOwner(player);
6745 }
6746 }
6747
6748 // CoC Monthly winner. (True Hero)
6749 final int trueHeroId = GlobalVariablesManager.getInstance().getInt(GlobalVariablesManager.COC_TRUE_HERO, 0);
6750 if (trueHeroId == player.getObjectId())
6751 {
6752 if (!GlobalVariablesManager.getInstance().getBoolean(GlobalVariablesManager.COC_TRUE_HERO_REWARDED, true))
6753 {
6754 GlobalVariablesManager.getInstance().set(GlobalVariablesManager.COC_TRUE_HERO_REWARDED, true);
6755 player.addItem("CoC-Hero", 35565, 1, player, true); // Mysterious Belt
6756 player.addItem("CoC-Hero", 35564, 1, player, true); // Ruler's Authority
6757 player.setFame(player.getFame() + 5000);
6758 player.sendMessage("You have been rewarded with 5.000 fame points.");
6759 }
6760 player.setTrueHero(true);
6761 }
6762
6763 // Recalculate all stats
6764 player.getStat().recalculateStats(false);
6765
6766 // Update the overloaded status of the L2PcInstance
6767 player.refreshOverloaded(false);
6768
6769 // Update the expertise status of the L2PcInstance
6770 player.refreshExpertisePenalty();
6771
6772 player.restoreFriendList();
6773
6774 player.loadRecommendations();
6775 player.startRecoGiveTask();
6776 player.startOnlineTimeUpdateTask();
6777
6778 player.setOnlineStatus(true, false);
6779
6780 player.startAutoSaveTask();
6781 }
6782 catch (Exception e)
6783 {
6784 LOGGER.log(Level.SEVERE, "Failed loading character.", e);
6785 }
6786 return player;
6787 }
6788
6789 /**
6790 * @return
6791 */
6792 public Forum getMail()
6793 {
6794 if (_forumMail == null)
6795 {
6796 setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName()));
6797
6798 if (_forumMail == null)
6799 {
6800 ForumsBBSManager.getInstance().createNewForum(getName(), ForumsBBSManager.getInstance().getForumByName("MailRoot"), Forum.MAIL, Forum.OWNERONLY, getObjectId());
6801 setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName()));
6802 }
6803 }
6804
6805 return _forumMail;
6806 }
6807
6808 /**
6809 * @param forum
6810 */
6811 public void setMail(Forum forum)
6812 {
6813 _forumMail = forum;
6814 }
6815
6816 /**
6817 * @return
6818 */
6819 public Forum getMemo()
6820 {
6821 if (_forumMemo == null)
6822 {
6823 setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName));
6824
6825 if (_forumMemo == null)
6826 {
6827 ForumsBBSManager.getInstance().createNewForum(_accountName, ForumsBBSManager.getInstance().getForumByName("MemoRoot"), Forum.MEMO, Forum.OWNERONLY, getObjectId());
6828 setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName));
6829 }
6830 }
6831
6832 return _forumMemo;
6833 }
6834
6835 /**
6836 * @param forum
6837 */
6838 public void setMemo(Forum forum)
6839 {
6840 _forumMemo = forum;
6841 }
6842
6843 /**
6844 * Restores sub-class data for the L2PcInstance, used to check the current class index for the character.
6845 * @param player
6846 * @return
6847 */
6848 private static boolean restoreSubClassData(L2PcInstance player)
6849 {
6850 try (Connection con = DatabaseFactory.getInstance().getConnection();
6851 PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_SUBCLASSES))
6852 {
6853 statement.setInt(1, player.getObjectId());
6854 try (ResultSet rset = statement.executeQuery())
6855 {
6856 while (rset.next())
6857 {
6858 final SubClass subClass = new SubClass();
6859 subClass.setClassId(rset.getInt("class_id"));
6860 subClass.setIsDualClass(rset.getBoolean("dual_class"));
6861 subClass.setVitalityPoints(rset.getInt("vitality_points"));
6862 subClass.setLevel(rset.getByte("level"));
6863 subClass.setExp(rset.getLong("exp"));
6864 subClass.setSp(rset.getLong("sp"));
6865 subClass.setClassIndex(rset.getInt("class_index"));
6866
6867 // Enforce the correct indexing of _subClasses against their class indexes.
6868 player.getSubClasses().put(subClass.getClassIndex(), subClass);
6869 }
6870 }
6871 }
6872 catch (Exception e)
6873 {
6874 LOGGER.log(Level.WARNING, "Could not restore classes for " + player.getName() + ": " + e.getMessage(), e);
6875 }
6876 return true;
6877 }
6878
6879 /**
6880 * Restores:
6881 * <ul>
6882 * <li>Skills</li>
6883 * <li>Macros</li>
6884 * <li>Henna</li>
6885 * <li>Teleport Bookmark</li>
6886 * <li>Recipe Book</li>
6887 * <li>Recipe Shop List (If configuration enabled)</li>
6888 * <li>Premium Item List</li>
6889 * <li>Pet Inventory Items</li>
6890 * </ul>
6891 */
6892 private void restoreCharData()
6893 {
6894 // Retrieve from the database all skills of this L2PcInstance and add them to _skills.
6895 restoreSkills();
6896
6897 // Retrieve from the database all macroses of this L2PcInstance and add them to _macros.
6898 _macros.restoreMe();
6899
6900 // Retrieve from the database all henna of this L2PcInstance and add them to _henna.
6901 restoreHenna();
6902
6903 // Retrieve from the database all teleport bookmark of this L2PcInstance and add them to _tpbookmark.
6904 restoreTeleportBookmark();
6905
6906 // Retrieve from the database the recipe book of this L2PcInstance.
6907 restoreRecipeBook(true);
6908
6909 // Restore Recipe Shop list.
6910 if (Config.STORE_RECIPE_SHOPLIST)
6911 {
6912 restoreRecipeShopList();
6913 }
6914
6915 // Load Premium Item List.
6916 loadPremiumItemList();
6917
6918 // Restore items in pet inventory.
6919 restorePetInventoryItems();
6920 }
6921
6922 /**
6923 * Restores:
6924 * <ul>
6925 * <li>Short-cuts</li>
6926 * </ul>
6927 */
6928 private void restoreShortCuts()
6929 {
6930 // Retrieve from the database all shortCuts of this L2PcInstance and add them to _shortCuts.
6931 _shortCuts.restoreMe();
6932 }
6933
6934 /**
6935 * Restore recipe book data for this L2PcInstance.
6936 * @param loadCommon
6937 */
6938 private void restoreRecipeBook(boolean loadCommon)
6939 {
6940 final String sql = loadCommon ? "SELECT id, type, classIndex FROM character_recipebook WHERE charId=?" : "SELECT id FROM character_recipebook WHERE charId=? AND classIndex=? AND type = 1";
6941 try (Connection con = DatabaseFactory.getInstance().getConnection();
6942 PreparedStatement statement = con.prepareStatement(sql))
6943 {
6944 statement.setInt(1, getObjectId());
6945 if (!loadCommon)
6946 {
6947 statement.setInt(2, _classIndex);
6948 }
6949
6950 try (ResultSet rset = statement.executeQuery())
6951 {
6952 _dwarvenRecipeBook.clear();
6953
6954 RecipeHolder recipe;
6955 RecipeData rd = RecipeData.getInstance();
6956 while (rset.next())
6957 {
6958 recipe = rd.getRecipe(rset.getInt("id"));
6959 if (loadCommon)
6960 {
6961 if (rset.getInt(2) == 1)
6962 {
6963 if (rset.getInt(3) == _classIndex)
6964 {
6965 registerDwarvenRecipeList(recipe, false);
6966 }
6967 }
6968 else
6969 {
6970 registerCommonRecipeList(recipe, false);
6971 }
6972 }
6973 else
6974 {
6975 registerDwarvenRecipeList(recipe, false);
6976 }
6977 }
6978 }
6979 }
6980 catch (Exception e)
6981 {
6982 LOGGER.log(Level.SEVERE, "Could not restore recipe book data:" + e.getMessage(), e);
6983 }
6984 }
6985
6986 public Map<Integer, L2PremiumItem> getPremiumItemList()
6987 {
6988 return _premiumItems;
6989 }
6990
6991 private void loadPremiumItemList()
6992 {
6993 final String sql = "SELECT itemNum, itemId, itemCount, itemSender FROM character_premium_items WHERE charId=?";
6994 try (Connection con = DatabaseFactory.getInstance().getConnection();
6995 PreparedStatement statement = con.prepareStatement(sql))
6996 {
6997 statement.setInt(1, getObjectId());
6998 try (ResultSet rset = statement.executeQuery())
6999 {
7000 while (rset.next())
7001 {
7002 final int itemNum = rset.getInt("itemNum");
7003 final int itemId = rset.getInt("itemId");
7004 final long itemCount = rset.getLong("itemCount");
7005 final String itemSender = rset.getString("itemSender");
7006 _premiumItems.put(itemNum, new L2PremiumItem(itemId, itemCount, itemSender));
7007 }
7008 }
7009 }
7010 catch (Exception e)
7011 {
7012 LOGGER.log(Level.SEVERE, "Could not restore premium items: " + e.getMessage(), e);
7013 }
7014 }
7015
7016 public void updatePremiumItem(int itemNum, long newcount)
7017 {
7018 try (Connection con = DatabaseFactory.getInstance().getConnection();
7019 PreparedStatement statement = con.prepareStatement("UPDATE character_premium_items SET itemCount=? WHERE charId=? AND itemNum=? "))
7020 {
7021 statement.setLong(1, newcount);
7022 statement.setInt(2, getObjectId());
7023 statement.setInt(3, itemNum);
7024 statement.execute();
7025 }
7026 catch (Exception e)
7027 {
7028 LOGGER.log(Level.SEVERE, "Could not update premium items: " + e.getMessage(), e);
7029 }
7030 }
7031
7032 public void deletePremiumItem(int itemNum)
7033 {
7034 try (Connection con = DatabaseFactory.getInstance().getConnection();
7035 PreparedStatement statement = con.prepareStatement("DELETE FROM character_premium_items WHERE charId=? AND itemNum=? "))
7036 {
7037 statement.setInt(1, getObjectId());
7038 statement.setInt(2, itemNum);
7039 statement.execute();
7040 }
7041 catch (Exception e)
7042 {
7043 LOGGER.severe("Could not delete premium item: " + e);
7044 }
7045 }
7046
7047 /**
7048 * Update L2PcInstance stats in the characters table of the database.
7049 * @param storeActiveEffects
7050 */
7051 public synchronized void store(boolean storeActiveEffects)
7052 {
7053 storeCharBase();
7054 storeCharSub();
7055 storeEffect(storeActiveEffects);
7056 storeItemReuseDelay();
7057 if (Config.STORE_RECIPE_SHOPLIST)
7058 {
7059 storeRecipeShopList();
7060 }
7061
7062 final PlayerVariables vars = getScript(PlayerVariables.class);
7063 if (vars != null)
7064 {
7065 vars.storeMe();
7066 }
7067
7068 final AccountVariables aVars = getScript(AccountVariables.class);
7069 if (aVars != null)
7070 {
7071 aVars.storeMe();
7072 }
7073 }
7074
7075 @Override
7076 public void storeMe()
7077 {
7078 store(true);
7079 }
7080
7081 private void storeCharBase()
7082 {
7083 // Get the exp, level, and sp of base class to store in base table
7084 final long exp = getStat().getBaseExp();
7085 final int level = getStat().getBaseLevel();
7086 final long sp = getStat().getBaseSp();
7087 try (Connection con = DatabaseFactory.getInstance().getConnection();
7088 PreparedStatement statement = con.prepareStatement(UPDATE_CHARACTER))
7089 {
7090 statement.setInt(1, level);
7091 statement.setInt(2, getMaxHp());
7092 statement.setDouble(3, getCurrentHp());
7093 statement.setInt(4, getMaxCp());
7094 statement.setDouble(5, getCurrentCp());
7095 statement.setInt(6, getMaxMp());
7096 statement.setDouble(7, getCurrentMp());
7097 statement.setInt(8, getAppearance().getFace());
7098 statement.setInt(9, getAppearance().getHairStyle());
7099 statement.setInt(10, getAppearance().getHairColor());
7100 statement.setInt(11, getAppearance().getSex() ? 1 : 0);
7101 statement.setInt(12, getHeading());
7102 statement.setInt(13, _lastLoc != null ? _lastLoc.getX() : getX());
7103 statement.setInt(14, _lastLoc != null ? _lastLoc.getY() : getY());
7104 statement.setInt(15, _lastLoc != null ? _lastLoc.getZ() : getZ());
7105 statement.setLong(16, exp);
7106 statement.setLong(17, getExpBeforeDeath());
7107 statement.setLong(18, sp);
7108 statement.setInt(19, getReputation());
7109 statement.setInt(20, getFame());
7110 statement.setInt(21, getRaidbossPoints());
7111 statement.setInt(22, getPvpKills());
7112 statement.setInt(23, getPkKills());
7113 statement.setInt(24, getClanId());
7114 statement.setInt(25, getRace().ordinal());
7115 statement.setInt(26, getClassId().getId());
7116 statement.setLong(27, getDeleteTimer());
7117 statement.setString(28, getTitle());
7118 statement.setInt(29, getAppearance().getTitleColor());
7119 statement.setInt(30, isOnlineInt());
7120 statement.setInt(31, getClanPrivileges().getBitmask());
7121 statement.setInt(32, getWantsPeace());
7122 statement.setInt(33, getBaseClass());
7123
7124 long totalOnlineTime = _onlineTime;
7125 if (_onlineBeginTime > 0)
7126 {
7127 totalOnlineTime += (System.currentTimeMillis() - _onlineBeginTime) / 1000;
7128 }
7129
7130 statement.setLong(34, totalOnlineTime);
7131 statement.setInt(35, _nobleLevel);
7132 statement.setInt(36, getPowerGrade());
7133 statement.setInt(37, getPledgeType());
7134 statement.setInt(38, getLvlJoinedAcademy());
7135 statement.setLong(39, getApprentice());
7136 statement.setLong(40, getSponsor());
7137 statement.setLong(41, getClanJoinExpiryTime());
7138 statement.setLong(42, getClanCreateExpiryTime());
7139 statement.setString(43, getName());
7140 statement.setInt(44, getBookMarkSlot());
7141 statement.setInt(45, getStat().getBaseVitalityPoints());
7142 statement.setString(46, getLang());
7143
7144 int factionId = 0;
7145 if (isGood())
7146 {
7147 factionId = 1;
7148 }
7149 if (isEvil())
7150 {
7151 factionId = 2;
7152 }
7153 statement.setInt(47, factionId);
7154 statement.setInt(48, getPcCafePoints());
7155 statement.setInt(49, getObjectId());
7156
7157 statement.execute();
7158 }
7159 catch (Exception e)
7160 {
7161 LOGGER.log(Level.WARNING, "Could not store char base data: " + this + " - " + e.getMessage(), e);
7162 }
7163 }
7164
7165 private void storeCharSub()
7166 {
7167 if (getTotalSubClasses() <= 0)
7168 {
7169 return;
7170 }
7171
7172 try (Connection con = DatabaseFactory.getInstance().getConnection();
7173 PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_SUBCLASS))
7174 {
7175 for (SubClass subClass : getSubClasses().values())
7176 {
7177 statement.setLong(1, subClass.getExp());
7178 statement.setLong(2, subClass.getSp());
7179 statement.setInt(3, subClass.getLevel());
7180 statement.setInt(4, subClass.getVitalityPoints());
7181 statement.setInt(5, subClass.getClassId());
7182 statement.setBoolean(6, subClass.isDualClass());
7183 statement.setInt(7, getObjectId());
7184 statement.setInt(8, subClass.getClassIndex());
7185 statement.execute();
7186 statement.clearParameters();
7187 }
7188 }
7189 catch (Exception e)
7190 {
7191 LOGGER.log(Level.WARNING, "Could not store sub class data for " + getName() + ": " + e.getMessage(), e);
7192 }
7193 }
7194
7195 @Override
7196 public void storeEffect(boolean storeEffects)
7197 {
7198 if (!Config.STORE_SKILL_COOLTIME)
7199 {
7200 return;
7201 }
7202
7203 try (Connection con = DatabaseFactory.getInstance().getConnection();
7204 PreparedStatement delete = con.prepareStatement(DELETE_SKILL_SAVE);
7205 PreparedStatement statement = con.prepareStatement(ADD_SKILL_SAVE))
7206 {
7207 // Delete all current stored effects for char to avoid dupe
7208 delete.setInt(1, getObjectId());
7209 delete.setInt(2, getClassIndex());
7210 delete.execute();
7211
7212 int buff_index = 0;
7213 final List<Long> storedSkills = new ArrayList<>();
7214
7215 // Store all effect data along with calulated remaining
7216 // reuse delays for matching skills. 'restore_type'= 0.
7217 if (storeEffects)
7218 {
7219 for (BuffInfo info : getEffectList().getEffects())
7220 {
7221 if (info == null)
7222 {
7223 continue;
7224 }
7225
7226 final Skill skill = info.getSkill();
7227
7228 // Do not store those effects.
7229 if (skill.isDeleteAbnormalOnLeave())
7230 {
7231 continue;
7232 }
7233
7234 // Do not save heals.
7235 if (skill.getAbnormalType() == AbnormalType.LIFE_FORCE_OTHERS)
7236 {
7237 continue;
7238 }
7239
7240 // Toggles are skipped, unless they are necessary to be always on.
7241 if ((skill.isToggle() && !skill.isNecessaryToggle()))
7242 {
7243 continue;
7244 }
7245
7246 if (skill.isMentoring())
7247 {
7248 continue;
7249 }
7250
7251 // Dances and songs are not kept in retail.
7252 if (skill.isDance() && !Config.ALT_STORE_DANCES)
7253 {
7254 continue;
7255 }
7256
7257 if (storedSkills.contains(skill.getReuseHashCode()))
7258 {
7259 continue;
7260 }
7261
7262 storedSkills.add(skill.getReuseHashCode());
7263
7264 statement.setInt(1, getObjectId());
7265 statement.setInt(2, skill.getId());
7266 statement.setInt(3, skill.getLevel());
7267 statement.setInt(4, skill.getSubLevel());
7268 statement.setInt(5, info.getTime());
7269
7270 final TimeStamp t = getSkillReuseTimeStamp(skill.getReuseHashCode());
7271 statement.setLong(6, (t != null) && t.hasNotPassed() ? t.getReuse() : 0);
7272 statement.setDouble(7, (t != null) && t.hasNotPassed() ? t.getStamp() : 0);
7273
7274 statement.setInt(8, 0); // Store type 0, active buffs/debuffs.
7275 statement.setInt(9, getClassIndex());
7276 statement.setInt(10, ++buff_index);
7277 statement.execute();
7278 }
7279 }
7280
7281 // Skills under reuse.
7282 final Map<Long, TimeStamp> reuseTimeStamps = getSkillReuseTimeStamps();
7283 if (reuseTimeStamps != null)
7284 {
7285 for (Entry<Long, TimeStamp> ts : reuseTimeStamps.entrySet())
7286 {
7287 final long hash = ts.getKey();
7288 if (storedSkills.contains(hash))
7289 {
7290 continue;
7291 }
7292
7293 final TimeStamp t = ts.getValue();
7294 if ((t != null) && t.hasNotPassed())
7295 {
7296 storedSkills.add(hash);
7297
7298 statement.setInt(1, getObjectId());
7299 statement.setInt(2, t.getSkillId());
7300 statement.setInt(3, t.getSkillLvl());
7301 statement.setInt(4, t.getSkillSubLvl());
7302 statement.setInt(5, -1);
7303 statement.setLong(6, t.getReuse());
7304 statement.setDouble(7, t.getStamp());
7305 statement.setInt(8, 1); // Restore type 1, skill reuse.
7306 statement.setInt(9, getClassIndex());
7307 statement.setInt(10, ++buff_index);
7308 statement.execute();
7309 }
7310 }
7311 }
7312 }
7313 catch (Exception e)
7314 {
7315 LOGGER.log(Level.WARNING, "Could not store char effect data: ", e);
7316 }
7317 }
7318
7319 private void storeItemReuseDelay()
7320 {
7321 try (Connection con = DatabaseFactory.getInstance().getConnection();
7322 PreparedStatement ps1 = con.prepareStatement(DELETE_ITEM_REUSE_SAVE);
7323 PreparedStatement ps2 = con.prepareStatement(ADD_ITEM_REUSE_SAVE))
7324 {
7325 ps1.setInt(1, getObjectId());
7326 ps1.execute();
7327
7328 final Map<Integer, TimeStamp> itemReuseTimeStamps = getItemReuseTimeStamps();
7329 if (itemReuseTimeStamps != null)
7330 {
7331 for (TimeStamp ts : itemReuseTimeStamps.values())
7332 {
7333 if ((ts != null) && ts.hasNotPassed())
7334 {
7335 ps2.setInt(1, getObjectId());
7336 ps2.setInt(2, ts.getItemId());
7337 ps2.setInt(3, ts.getItemObjectId());
7338 ps2.setLong(4, ts.getReuse());
7339 ps2.setDouble(5, ts.getStamp());
7340 ps2.execute();
7341 }
7342 }
7343 }
7344 }
7345 catch (Exception e)
7346 {
7347 LOGGER.log(Level.WARNING, "Could not store char item reuse data: ", e);
7348 }
7349 }
7350
7351 /**
7352 * @return True if the L2PcInstance is on line.
7353 */
7354 public boolean isOnline()
7355 {
7356 return _isOnline;
7357 }
7358
7359 public int isOnlineInt()
7360 {
7361 if (_isOnline && (getClient() != null))
7362 {
7363 return getClient().isDetached() ? 2 : 1;
7364 }
7365 return 0;
7366 }
7367
7368 @Override
7369 public Skill addSkill(Skill newSkill)
7370 {
7371 addCustomSkill(newSkill);
7372 return super.addSkill(newSkill);
7373 }
7374
7375 /**
7376 * Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance and save update in the character_skills table of the database. <B><U> Concept</U> :</B> All skills own by a L2PcInstance are identified in <B>_skills</B> <B><U> Actions</U> :</B>
7377 * <li>Replace oldSkill by newSkill or Add the newSkill</li>
7378 * <li>If an old skill has been replaced, remove all its Func objects of L2Character calculator set</li>
7379 * <li>Add Func objects of newSkill to the calculator set of the L2Character</li>
7380 * @param newSkill The L2Skill to add to the L2Character
7381 * @param store
7382 * @return The L2Skill replaced or null if just added a new L2Skill
7383 */
7384 public Skill addSkill(Skill newSkill, boolean store)
7385 {
7386 // Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance
7387 final Skill oldSkill = addSkill(newSkill);
7388 // Add or update a L2PcInstance skill in the character_skills table of the database
7389 if (store)
7390 {
7391 storeSkill(newSkill, oldSkill, -1);
7392 }
7393 return oldSkill;
7394 }
7395
7396 @Override
7397 public Skill removeSkill(Skill skill, boolean store)
7398 {
7399 removeCustomSkill(skill);
7400 return store ? removeSkill(skill) : super.removeSkill(skill, true);
7401 }
7402
7403 public Skill removeSkill(Skill skill, boolean store, boolean cancelEffect)
7404 {
7405 removeCustomSkill(skill);
7406 return store ? removeSkill(skill) : super.removeSkill(skill, cancelEffect);
7407 }
7408
7409 /**
7410 * Remove a skill from the L2Character and its Func objects from calculator set of the L2Character and save update in the character_skills table of the database. <B><U> Concept</U> :</B> All skills own by a L2Character are identified in <B>_skills</B> <B><U> Actions</U> :</B>
7411 * <li>Remove the skill from the L2Character _skills</li>
7412 * <li>Remove all its Func objects from the L2Character calculator set</li> <B><U> Overridden in </U> :</B>
7413 * <li>L2PcInstance : Save update in the character_skills table of the database</li>
7414 * @param skill The L2Skill to remove from the L2Character
7415 * @return The L2Skill removed
7416 */
7417 public Skill removeSkill(Skill skill)
7418 {
7419 removeCustomSkill(skill);
7420
7421 // Remove a skill from the Creature and its stats
7422 final Skill oldSkill = super.removeSkill(skill, true);
7423 if (oldSkill != null)
7424 {
7425 try (Connection con = DatabaseFactory.getInstance().getConnection();
7426 PreparedStatement statement = con.prepareStatement(DELETE_SKILL_FROM_CHAR))
7427 {
7428 // Remove or update a L2PcInstance skill from the character_skills table of the database
7429 statement.setInt(1, oldSkill.getId());
7430 statement.setInt(2, getObjectId());
7431 statement.setInt(3, getClassIndex());
7432 statement.execute();
7433 }
7434 catch (Exception e)
7435 {
7436 LOGGER.log(Level.WARNING, "Error could not delete skill: " + e.getMessage(), e);
7437 }
7438 }
7439
7440 if ((getTransformationId() > 0) || isCursedWeaponEquipped())
7441 {
7442 return oldSkill;
7443 }
7444
7445 if (skill != null)
7446 {
7447 for (Shortcut sc : getAllShortCuts())
7448 {
7449 if ((sc != null) && (sc.getId() == skill.getId()) && (sc.getType() == ShortcutType.SKILL) && !((skill.getId() >= 3080) && (skill.getId() <= 3259)))
7450 {
7451 deleteShortCut(sc.getSlot(), sc.getPage());
7452 }
7453 }
7454 }
7455 return oldSkill;
7456 }
7457
7458 /**
7459 * Add or update a L2PcInstance skill in the character_skills table of the database.<br>
7460 * If newClassIndex > -1, the skill will be stored with that class index, not the current one.
7461 * @param newSkill
7462 * @param oldSkill
7463 * @param newClassIndex
7464 */
7465 private void storeSkill(Skill newSkill, Skill oldSkill, int newClassIndex)
7466 {
7467 final int classIndex = (newClassIndex > -1) ? newClassIndex : _classIndex;
7468 try (Connection con = DatabaseFactory.getInstance().getConnection())
7469 {
7470 if ((oldSkill != null) && (newSkill != null))
7471 {
7472 try (PreparedStatement ps = con.prepareStatement(UPDATE_CHARACTER_SKILL_LEVEL))
7473 {
7474 ps.setInt(1, newSkill.getLevel());
7475 ps.setInt(2, newSkill.getSubLevel());
7476 ps.setInt(3, oldSkill.getId());
7477 ps.setInt(4, getObjectId());
7478 ps.setInt(5, classIndex);
7479 ps.execute();
7480 }
7481 }
7482 else if (newSkill != null)
7483 {
7484 try (PreparedStatement ps = con.prepareStatement(ADD_NEW_SKILLS))
7485 {
7486 ps.setInt(1, getObjectId());
7487 ps.setInt(2, newSkill.getId());
7488 ps.setInt(3, newSkill.getLevel());
7489 ps.setInt(4, newSkill.getSubLevel());
7490 ps.setInt(5, classIndex);
7491 ps.execute();
7492 }
7493 }
7494 // else
7495 // {
7496 // LOGGER.warning("Could not store new skill, it's null!");
7497 // }
7498 }
7499 catch (Exception e)
7500 {
7501 LOGGER.log(Level.WARNING, "Error could not store char skills: " + e.getMessage(), e);
7502 }
7503 }
7504
7505 /**
7506 * Adds or updates player's skills in the database.
7507 * @param newSkills the list of skills to store
7508 * @param newClassIndex if newClassIndex > -1, the skills will be stored for that class index, not the current one
7509 */
7510 private void storeSkills(List<Skill> newSkills, int newClassIndex)
7511 {
7512 if (newSkills.isEmpty())
7513 {
7514 return;
7515 }
7516
7517 final int classIndex = (newClassIndex > -1) ? newClassIndex : _classIndex;
7518 try (Connection con = DatabaseFactory.getInstance().getConnection();
7519 PreparedStatement ps = con.prepareStatement(ADD_NEW_SKILLS))
7520 {
7521 con.setAutoCommit(false);
7522 for (Skill addSkill : newSkills)
7523 {
7524 ps.setInt(1, getObjectId());
7525 ps.setInt(2, addSkill.getId());
7526 ps.setInt(3, addSkill.getLevel());
7527 ps.setInt(4, addSkill.getSubLevel());
7528 ps.setInt(5, classIndex);
7529 ps.addBatch();
7530 }
7531 ps.executeBatch();
7532 con.commit();
7533 }
7534 catch (SQLException e)
7535 {
7536 LOGGER.log(Level.WARNING, "Error could not store char skills: " + e.getMessage(), e);
7537 }
7538 }
7539
7540 /**
7541 * Retrieve from the database all skills of this L2PcInstance and add them to _skills.
7542 */
7543 private void restoreSkills()
7544 {
7545 try (Connection con = DatabaseFactory.getInstance().getConnection();
7546 PreparedStatement statement = con.prepareStatement(RESTORE_SKILLS_FOR_CHAR))
7547 {
7548 // Retrieve all skills of this L2PcInstance from the database
7549 statement.setInt(1, getObjectId());
7550 statement.setInt(2, getClassIndex());
7551 try (ResultSet rset = statement.executeQuery())
7552 {
7553 while (rset.next())
7554 {
7555 final int id = rset.getInt("skill_id");
7556 final int level = rset.getInt("skill_level");
7557 final int subLevel = rset.getInt("skill_sub_level");
7558
7559 // Create a L2Skill object for each record
7560 final Skill skill = SkillData.getInstance().getSkill(id, level, subLevel);
7561
7562 if (skill == null)
7563 {
7564 LOGGER.warning("Skipped null skill Id: " + id + " Level: " + level + " while restoring player skills for playerObjId: " + getObjectId());
7565 continue;
7566 }
7567
7568 // Add the L2Skill object to the L2Character _skills and its Func objects to the calculator set of the L2Character
7569 addSkill(skill);
7570
7571 if (Config.SKILL_CHECK_ENABLE && (!canOverrideCond(PcCondOverride.SKILL_CONDITIONS) || Config.SKILL_CHECK_GM))
7572 {
7573 if (!SkillTreesData.getInstance().isSkillAllowed(this, skill))
7574 {
7575 Util.handleIllegalPlayerAction(this, "Player " + getName() + " has invalid skill " + skill.getName() + " (" + skill.getId() + "/" + skill.getLevel() + "), class:" + ClassListData.getInstance().getClass(getClassId()).getClassName(), IllegalActionPunishmentType.BROADCAST);
7576 if (Config.SKILL_CHECK_REMOVE)
7577 {
7578 removeSkill(skill);
7579 }
7580 }
7581 }
7582 }
7583 }
7584 }
7585 catch (Exception e)
7586 {
7587 LOGGER.log(Level.WARNING, "Could not restore character " + this + " skills: " + e.getMessage(), e);
7588 }
7589 }
7590
7591 /**
7592 * Retrieve from the database all skill effects of this L2PcInstance and add them to the player.
7593 */
7594 @Override
7595 public void restoreEffects()
7596 {
7597 try (Connection con = DatabaseFactory.getInstance().getConnection();
7598 PreparedStatement statement = con.prepareStatement(RESTORE_SKILL_SAVE))
7599 {
7600 statement.setInt(1, getObjectId());
7601 statement.setInt(2, getClassIndex());
7602 try (ResultSet rset = statement.executeQuery())
7603 {
7604 while (rset.next())
7605 {
7606 final int remainingTime = rset.getInt("remaining_time");
7607 final long reuseDelay = rset.getLong("reuse_delay");
7608 final long systime = rset.getLong("systime");
7609 final int restoreType = rset.getInt("restore_type");
7610
7611 final Skill skill = SkillData.getInstance().getSkill(rset.getInt("skill_id"), rset.getInt("skill_level"), rset.getInt("skill_sub_level"));
7612 if (skill == null)
7613 {
7614 continue;
7615 }
7616
7617 final long time = systime - System.currentTimeMillis();
7618 if (time > 10)
7619 {
7620 disableSkill(skill, time);
7621 addTimeStamp(skill, reuseDelay, systime);
7622 }
7623
7624 // Restore Type 1 The remaning skills lost effect upon logout but were still under a high reuse delay.
7625 if (restoreType > 0)
7626 {
7627 continue;
7628 }
7629
7630 // Restore Type 0 These skill were still in effect on the character upon logout.
7631 // Some of which were self casted and might still have had a long reuse delay which also is restored.
7632 skill.applyEffects(this, this, false, remainingTime);
7633 }
7634 }
7635 // Remove previously restored skills
7636 try (PreparedStatement delete = con.prepareStatement(DELETE_SKILL_SAVE))
7637 {
7638 delete.setInt(1, getObjectId());
7639 delete.setInt(2, getClassIndex());
7640 delete.executeUpdate();
7641 }
7642 }
7643 catch (Exception e)
7644 {
7645 LOGGER.log(Level.WARNING, "Could not restore " + this + " active effect data: " + e.getMessage(), e);
7646 }
7647 }
7648
7649 /**
7650 * Retrieve from the database all Item Reuse Time of this L2PcInstance and add them to the player.
7651 */
7652 private void restoreItemReuse()
7653 {
7654 try (Connection con = DatabaseFactory.getInstance().getConnection();
7655 PreparedStatement statement = con.prepareStatement(RESTORE_ITEM_REUSE_SAVE);
7656 PreparedStatement delete = con.prepareStatement(DELETE_ITEM_REUSE_SAVE))
7657 {
7658 statement.setInt(1, getObjectId());
7659 try (ResultSet rset = statement.executeQuery())
7660 {
7661 int itemId;
7662 @SuppressWarnings("unused")
7663 int itemObjId;
7664 long reuseDelay;
7665 long systime;
7666 boolean isInInventory;
7667 long remainingTime;
7668 while (rset.next())
7669 {
7670 itemId = rset.getInt("itemId");
7671 itemObjId = rset.getInt("itemObjId");
7672 reuseDelay = rset.getLong("reuseDelay");
7673 systime = rset.getLong("systime");
7674 isInInventory = true;
7675
7676 // Using item Id
7677 L2ItemInstance item = getInventory().getItemByItemId(itemId);
7678 if (item == null)
7679 {
7680 item = getWarehouse().getItemByItemId(itemId);
7681 isInInventory = false;
7682 }
7683
7684 if ((item != null) && (item.getId() == itemId) && (item.getReuseDelay() > 0))
7685 {
7686 remainingTime = systime - System.currentTimeMillis();
7687 // Hardcoded to 10 seconds.
7688 if (remainingTime > 10)
7689 {
7690 addTimeStampItem(item, reuseDelay, systime);
7691
7692 if (isInInventory && item.isEtcItem())
7693 {
7694 final int group = item.getSharedReuseGroup();
7695 if (group > 0)
7696 {
7697 sendPacket(new ExUseSharedGroupItem(itemId, group, (int) remainingTime, (int) reuseDelay));
7698 }
7699 }
7700 }
7701 }
7702 }
7703 }
7704
7705 // Delete item reuse.
7706 delete.setInt(1, getObjectId());
7707 delete.executeUpdate();
7708 }
7709 catch (Exception e)
7710 {
7711 LOGGER.log(Level.WARNING, "Could not restore " + this + " Item Reuse data: " + e.getMessage(), e);
7712 }
7713 }
7714
7715 /**
7716 * Retrieve from the database all Henna of this L2PcInstance, add them to _henna and calculate stats of the L2PcInstance.
7717 */
7718 private void restoreHenna()
7719 {
7720 for (int i = 1; i < 5; i++)
7721 {
7722 _henna[i - 1] = null;
7723 }
7724
7725 try (Connection con = DatabaseFactory.getInstance().getConnection();
7726 PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_HENNAS))
7727 {
7728 statement.setInt(1, getObjectId());
7729 statement.setInt(2, getClassIndex());
7730 try (ResultSet rset = statement.executeQuery())
7731 {
7732 int slot;
7733 int symbolId;
7734 while (rset.next())
7735 {
7736 slot = rset.getInt("slot");
7737 if ((slot < 1) || (slot > 4))
7738 {
7739 continue;
7740 }
7741
7742 symbolId = rset.getInt("symbol_id");
7743 if (symbolId == 0)
7744 {
7745 continue;
7746 }
7747
7748 final L2Henna henna = HennaData.getInstance().getHenna(symbolId);
7749
7750 // Task for henna duration
7751 if (henna.getDuration() > 0)
7752 {
7753 final long currentTime = System.currentTimeMillis();
7754 final long remainingTime = currentTime - getVariables().getLong("HennaDuration" + slot, currentTime);
7755 if (remainingTime < 0)
7756 {
7757 removeHenna(slot);
7758 continue;
7759 }
7760 _hennaRemoveSchedules.put(slot, ThreadPool.schedule(new HennaDurationTask(this, slot), System.currentTimeMillis() + remainingTime));
7761 }
7762
7763 _henna[slot - 1] = henna;
7764
7765 // Reward henna skills
7766 for (Skill skill : henna.getSkills())
7767 {
7768 addSkill(skill, false);
7769 }
7770 }
7771 }
7772 }
7773 catch (Exception e)
7774 {
7775 LOGGER.log(Level.SEVERE, "Failed restoing character " + this + " hennas.", e);
7776 }
7777
7778 // Calculate henna modifiers of this player.
7779 recalcHennaStats();
7780 }
7781
7782 /**
7783 * @return the number of Henna empty slot of the L2PcInstance.
7784 */
7785 public int getHennaEmptySlots()
7786 {
7787 int totalSlots = 0;
7788 if (getClassId().level() == 1)
7789 {
7790 totalSlots = 2;
7791 }
7792 else if (getClassId().level() > 1)
7793 {
7794 totalSlots = 3;
7795 }
7796
7797 for (int i = 0; i < 3; i++)
7798 {
7799 if (_henna[i] != null)
7800 {
7801 totalSlots--;
7802 }
7803 }
7804
7805 if (totalSlots <= 0)
7806 {
7807 return 0;
7808 }
7809
7810 return totalSlots;
7811 }
7812
7813 /**
7814 * Remove a Henna of the L2PcInstance, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2PcInstance.
7815 * @param slot
7816 * @return
7817 */
7818 public boolean removeHenna(int slot)
7819 {
7820 if ((slot < 1) || (slot > 4))
7821 {
7822 return false;
7823 }
7824
7825 final L2Henna henna = _henna[slot - 1];
7826 if (henna == null)
7827 {
7828 return false;
7829 }
7830
7831 _henna[slot - 1] = null;
7832
7833 try (Connection con = DatabaseFactory.getInstance().getConnection();
7834 PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA))
7835 {
7836 statement.setInt(1, getObjectId());
7837 statement.setInt(2, slot);
7838 statement.setInt(3, getClassIndex());
7839 statement.execute();
7840 }
7841 catch (Exception e)
7842 {
7843 LOGGER.log(Level.SEVERE, "Failed removing character henna.", e);
7844 }
7845
7846 // Calculate Henna modifiers of this L2PcInstance
7847 recalcHennaStats();
7848
7849 // Send Server->Client HennaInfo packet to this L2PcInstance
7850 sendPacket(new HennaInfo(this));
7851
7852 // Send Server->Client UserInfo packet to this L2PcInstance
7853 final UserInfo ui = new UserInfo(this, false);
7854 ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED);
7855 sendPacket(ui);
7856
7857 final long currentTime = System.currentTimeMillis();
7858 final long timeLeft = getVariables().getLong("HennaDuration" + slot, currentTime) - currentTime;
7859 if ((henna.getDuration() < 0) || (timeLeft > 0))
7860 {
7861 // Add the recovered dyes to the player's inventory and notify them.
7862 if ((henna.getCancelFee() > 0) && (hasPremiumStatus() || (slot != 4)))
7863 {
7864 reduceAdena("Henna", henna.getCancelFee(), this, false);
7865 }
7866 if (henna.getCancelCount() > 0)
7867 {
7868 getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null);
7869 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S);
7870 sm.addItemName(henna.getDyeItemId());
7871 sm.addLong(henna.getCancelCount());
7872 sendPacket(sm);
7873 }
7874 }
7875 sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED);
7876
7877 // Remove henna duration task
7878 if (henna.getDuration() > 0)
7879 {
7880 getVariables().remove("HennaDuration" + slot);
7881 if (_hennaRemoveSchedules.get(slot) != null)
7882 {
7883 _hennaRemoveSchedules.get(slot).cancel(false);
7884 _hennaRemoveSchedules.remove(slot);
7885 }
7886 }
7887
7888 // Remove henna skills
7889 for (Skill skill : henna.getSkills())
7890 {
7891 removeSkill(skill, false);
7892 }
7893
7894 // Notify to scripts
7895 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this);
7896 return true;
7897 }
7898
7899 /**
7900 * Add a Henna to the L2PcInstance, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2PcInstance.
7901 * @param henna the henna to add to the player.
7902 * @return {@code true} if the henna is added to the player, {@code false} otherwise.
7903 */
7904 public boolean addHenna(L2Henna henna)
7905 {
7906 for (int i = 1; i < 5; i++)
7907 {
7908 // Check for retail premium dyes slot
7909 if (!Config.PREMIUM_HENNA_SLOT_ALL_DYES)
7910 {
7911 if (i == 4)
7912 {
7913 if ((_henna[3] != null) || !henna.isPremium())
7914 {
7915 return false;
7916 }
7917 }
7918 else if (henna.isPremium())
7919 {
7920 continue;
7921 }
7922 }
7923
7924 if (_henna[i - 1] == null)
7925 {
7926 _henna[i - 1] = henna;
7927
7928 // Calculate Henna modifiers of this L2PcInstance
7929 recalcHennaStats();
7930
7931 try (Connection con = DatabaseFactory.getInstance().getConnection();
7932 PreparedStatement statement = con.prepareStatement(ADD_CHAR_HENNA))
7933 {
7934 statement.setInt(1, getObjectId());
7935 statement.setInt(2, henna.getDyeId());
7936 statement.setInt(3, i);
7937 statement.setInt(4, getClassIndex());
7938 statement.execute();
7939 }
7940 catch (Exception e)
7941 {
7942 LOGGER.log(Level.SEVERE, "Failed saving character henna.", e);
7943 }
7944
7945 // Task for henna duration
7946 if (henna.getDuration() > 0)
7947 {
7948 final long currentTime = System.currentTimeMillis();
7949 final long durationInMillis = henna.getDuration() * 60000;
7950 getVariables().set("HennaDuration" + i, currentTime + durationInMillis);
7951 _hennaRemoveSchedules.put(i, ThreadPool.schedule(new HennaDurationTask(this, i), currentTime + durationInMillis));
7952 }
7953
7954 // Reward henna skills
7955 for (Skill skill : henna.getSkills())
7956 {
7957 addSkill(skill, false);
7958 }
7959
7960 // Send Server->Client HennaInfo packet to this L2PcInstance
7961 sendPacket(new HennaInfo(this));
7962
7963 // Send Server->Client UserInfo packet to this L2PcInstance
7964 final UserInfo ui = new UserInfo(this, false);
7965 ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED);
7966 sendPacket(ui);
7967
7968 // Notify to scripts
7969 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaAdd(this, henna), this);
7970 return true;
7971 }
7972 }
7973 return false;
7974 }
7975
7976 /**
7977 * Calculate Henna modifiers of this L2PcInstance.
7978 */
7979 private void recalcHennaStats()
7980 {
7981 _hennaBaseStats.clear();
7982 for (L2Henna henna : _henna)
7983 {
7984 if (henna == null)
7985 {
7986 continue;
7987 }
7988
7989 for (Entry<BaseStats, Integer> entry : henna.getBaseStats().entrySet())
7990 {
7991 _hennaBaseStats.merge(entry.getKey(), entry.getValue(), Integer::sum);
7992 }
7993 }
7994 }
7995
7996 /**
7997 * @param slot the character inventory henna slot.
7998 * @return the Henna of this L2PcInstance corresponding to the selected slot.
7999 */
8000 public L2Henna getHenna(int slot)
8001 {
8002 if ((slot < 1) || (slot > 4))
8003 {
8004 return null;
8005 }
8006 return _henna[slot - 1];
8007 }
8008
8009 /**
8010 * @return {@code true} if player has at least 1 henna symbol, {@code false} otherwise.
8011 */
8012 public boolean hasHennas()
8013 {
8014 for (L2Henna henna : _henna)
8015 {
8016 if (henna != null)
8017 {
8018 return true;
8019 }
8020 }
8021 return false;
8022 }
8023
8024 /**
8025 * @return the henna holder for this player.
8026 */
8027 public L2Henna[] getHennaList()
8028 {
8029 return _henna;
8030 }
8031
8032 /**
8033 * @param stat
8034 * @return the henna bonus of specified base stat
8035 */
8036 public int getHennaValue(BaseStats stat)
8037 {
8038 return _hennaBaseStats.getOrDefault(stat, 0);
8039 }
8040
8041 /**
8042 * @return map of all henna base stats bonus
8043 */
8044 public Map<BaseStats, Integer> getHennaBaseStats()
8045 {
8046 return _hennaBaseStats;
8047 }
8048
8049 /**
8050 * Checks if the player has basic property resist towards mesmerizing debuffs.
8051 * @return {@code true} if the player has resist towards mesmerizing debuffs, {@code false} otherwise
8052 */
8053 @Override
8054 public boolean hasBasicPropertyResist()
8055 {
8056 return isInCategory(CategoryType.SIXTH_CLASS_GROUP);
8057 }
8058
8059 private void startAutoSaveTask()
8060 {
8061 if ((Config.CHAR_DATA_STORE_INTERVAL > 0) && (_autoSaveTask == null))
8062 {
8063 _autoSaveTask = ThreadPool.scheduleAtFixedRate(this::autoSave, 300_000L, TimeUnit.MINUTES.toMillis(Config.CHAR_DATA_STORE_INTERVAL));
8064 }
8065 }
8066
8067 private void stopAutoSaveTask()
8068 {
8069 if (_autoSaveTask != null)
8070 {
8071 _autoSaveTask.cancel(false);
8072 _autoSaveTask = null;
8073 }
8074 }
8075
8076 protected void autoSave()
8077 {
8078 storeMe();
8079 storeRecommendations();
8080
8081 if (Config.UPDATE_ITEMS_ON_CHAR_STORE)
8082 {
8083 getInventory().updateDatabase();
8084 getWarehouse().updateDatabase();
8085 }
8086 }
8087
8088 public boolean canLogout()
8089 {
8090 if (hasItemRequest())
8091 {
8092 return false;
8093 }
8094
8095 if (isLocked())
8096 {
8097 LOGGER.warning("Player " + getName() + " tried to restart/logout during class change.");
8098 return false;
8099 }
8100
8101 if (AttackStanceTaskManager.getInstance().hasAttackStanceTask(this) && !(isGM() && Config.GM_RESTART_FIGHTING))
8102 {
8103 return false;
8104 }
8105
8106 if (isBlockedFromExit())
8107 {
8108 return false;
8109 }
8110
8111 return true;
8112 }
8113
8114 /**
8115 * Return True if the L2PcInstance is autoAttackable.<br>
8116 * <B><U>Actions</U>:</B>
8117 * <ul>
8118 * <li>Check if the attacker isn't the L2PcInstance Pet</li>
8119 * <li>Check if the attacker is L2MonsterInstance</li>
8120 * <li>If the attacker is a L2PcInstance, check if it is not in the same party</li>
8121 * <li>Check if the L2PcInstance has Karma</li>
8122 * <li>If the attacker is a L2PcInstance, check if it is not in the same siege clan (Attacker, Defender)</li>
8123 * </ul>
8124 */
8125 @Override
8126 public boolean isAutoAttackable(L2Character attacker)
8127 {
8128 if (attacker == null)
8129 {
8130 return false;
8131 }
8132
8133 // Check if the attacker isn't the L2PcInstance Pet
8134 if ((attacker == this) || (attacker == getPet()) || attacker.hasServitor(attacker.getObjectId()))
8135 {
8136 return false;
8137 }
8138
8139 // Friendly mobs doesnt attack players
8140 if (attacker instanceof L2FriendlyMobInstance)
8141 {
8142 return false;
8143 }
8144
8145 // Check if the attacker is a L2MonsterInstance
8146 if (attacker.isMonster())
8147 {
8148 return true;
8149 }
8150
8151 // is AutoAttackable if both players are in the same duel and the duel is still going on
8152 if (attacker.isPlayable() && (getDuelState() == Duel.DUELSTATE_DUELLING) && (getDuelId() == attacker.getActingPlayer().getDuelId()))
8153 {
8154 return true;
8155 }
8156
8157 // Check if the attacker is not in the same party. NOTE: Party checks goes before oly checks in order to prevent patry member autoattack at oly.
8158 if (isInParty() && getParty().getMembers().contains(attacker))
8159 {
8160 return false;
8161 }
8162
8163 // Check if the attacker is in olympia and olympia start
8164 if (attacker.isPlayer() && attacker.getActingPlayer().isInOlympiadMode())
8165 {
8166 if (isInOlympiadMode() && isOlympiadStart() && (((L2PcInstance) attacker).getOlympiadGameId() == getOlympiadGameId()))
8167 {
8168 return true;
8169 }
8170 return false;
8171 }
8172
8173 if (isOnCustomEvent() && (getTeam() == attacker.getTeam()))
8174 {
8175 return false;
8176 }
8177
8178 // CoC needs this check?
8179 if (isOnEvent())
8180 {
8181 return true;
8182 }
8183
8184 // Check if the attacker is a L2Playable
8185 if (attacker.isPlayable())
8186 {
8187 if (isInsideZone(ZoneId.PEACE))
8188 {
8189 return false;
8190 }
8191
8192 // Get L2PcInstance
8193 final L2PcInstance attackerPlayer = attacker.getActingPlayer();
8194
8195 if (getClan() != null)
8196 {
8197 final Siege siege = SiegeManager.getInstance().getSiege(getX(), getY(), getZ());
8198 if (siege != null)
8199 {
8200 // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Defender clan
8201 if (siege.checkIsDefender(attackerPlayer.getClan()) && siege.checkIsDefender(getClan()))
8202 {
8203 return false;
8204 }
8205
8206 // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Attacker clan
8207 if (siege.checkIsAttacker(attackerPlayer.getClan()) && siege.checkIsAttacker(getClan()))
8208 {
8209 return false;
8210 }
8211 }
8212
8213 // Check if clan is at war
8214 if ((getClan() != null) && (attackerPlayer.getClan() != null) && getClan().isAtWarWith(attackerPlayer.getClanId()) && attackerPlayer.getClan().isAtWarWith(getClanId()) && (getWantsPeace() == 0) && (attackerPlayer.getWantsPeace() == 0) && !isAcademyMember())
8215 {
8216 return true;
8217 }
8218 }
8219
8220 // Check if the L2PcInstance is in an arena, but NOT siege zone. NOTE: This check comes before clan/ally checks, but after party checks.
8221 // This is done because in arenas, clan/ally members can autoattack if they arent in party.
8222 if ((isInsideZone(ZoneId.PVP) && attackerPlayer.isInsideZone(ZoneId.PVP)) && !(isInsideZone(ZoneId.SIEGE) && attackerPlayer.isInsideZone(ZoneId.SIEGE)))
8223 {
8224 return true;
8225 }
8226
8227 // Check if the attacker is not in the same clan
8228 if ((getClan() != null) && getClan().isMember(attacker.getObjectId()))
8229 {
8230 return false;
8231 }
8232
8233 // Check if the attacker is not in the same ally
8234 if (attacker.isPlayer() && (getAllyId() != 0) && (getAllyId() == attackerPlayer.getAllyId()))
8235 {
8236 return false;
8237 }
8238
8239 // Now check again if the L2PcInstance is in pvp zone, but this time at siege PvP zone, applying clan/ally checks
8240 if (isInsideZone(ZoneId.PVP) && attackerPlayer.isInsideZone(ZoneId.PVP) && isInsideZone(ZoneId.SIEGE) && attackerPlayer.isInsideZone(ZoneId.SIEGE))
8241 {
8242 return true;
8243 }
8244
8245 if (Config.FACTION_SYSTEM_ENABLED && ((isGood() && attackerPlayer.isEvil()) || (isEvil() && attackerPlayer.isGood())))
8246 {
8247 return true;
8248 }
8249 }
8250
8251 if (attacker instanceof L2DefenderInstance)
8252 {
8253 if (getClan() != null)
8254 {
8255 final Siege siege = SiegeManager.getInstance().getSiege(this);
8256 return ((siege != null) && siege.checkIsAttacker(getClan()));
8257 }
8258 }
8259
8260 if (attacker instanceof L2GuardInstance)
8261 {
8262 return (getReputation() < 0); // Guards attack only PK players.
8263 }
8264
8265 // Check if the L2PcInstance has Karma
8266 if ((getReputation() < 0) || (getPvpFlag() > 0))
8267 {
8268 return true;
8269 }
8270
8271 return false;
8272 }
8273
8274 /**
8275 * Check if the active L2Skill can be casted.<br>
8276 * <B><U>Actions</U>:</B>
8277 * <ul>
8278 * <li>Check if the skill isn't toggle and is offensive</li>
8279 * <li>Check if the target is in the skill cast range</li>
8280 * <li>Check if the skill is Spoil type and if the target isn't already spoiled</li>
8281 * <li>Check if the caster owns enought consummed Item, enough HP and MP to cast the skill</li>
8282 * <li>Check if the caster isn't sitting</li>
8283 * <li>Check if all skills are enabled and this skill is enabled</li>
8284 * <li>Check if the caster own the weapon needed</li>
8285 * <li>Check if the skill is active</li>
8286 * <li>Check if all casting conditions are completed</li>
8287 * <li>Notify the AI with AI_INTENTION_CAST and target</li>
8288 * </ul>
8289 * @param skill The L2Skill to use
8290 * @param forceUse used to force ATTACK on players
8291 * @param dontMove used to prevent movement, if not in range
8292 */
8293 @Override
8294 public boolean useMagic(Skill skill, L2ItemInstance item, boolean forceUse, boolean dontMove)
8295 {
8296 // Passive skills cannot be used.
8297 if (skill.isPassive())
8298 {
8299 sendPacket(ActionFailed.STATIC_PACKET);
8300 return false;
8301 }
8302
8303 if (isTransformed() && !hasTransformSkill(skill))
8304 {
8305 sendPacket(ActionFailed.STATIC_PACKET);
8306 return false;
8307 }
8308
8309 // If Alternate rule Karma punishment is set to true, forbid skill Return to player with Karma
8310 if (!Config.ALT_GAME_KARMA_PLAYER_CAN_TELEPORT && (getReputation() < 0) && skill.hasEffectType(L2EffectType.TELEPORT))
8311 {
8312 sendPacket(ActionFailed.STATIC_PACKET);
8313 return false;
8314 }
8315
8316 // players mounted on pets cannot use any toggle skills
8317 if (skill.isToggle() && isMounted())
8318 {
8319 sendPacket(ActionFailed.STATIC_PACKET);
8320 return false;
8321 }
8322
8323 // Support for wizard skills with stances (Fire, Water, Wind, Earth)
8324 final Skill attachedSkill = skill.getAttachedSkill(this);
8325 if (attachedSkill != null)
8326 {
8327 skill = attachedSkill;
8328 }
8329
8330 // Alter skills
8331 if (_alterSkillActive)
8332 {
8333 sendPacket(new ExAlterSkillRequest(null, -1, -1, -1));
8334 _alterSkillActive = false;
8335 }
8336
8337 // ************************************* Check Player State *******************************************
8338
8339 // Abnormal effects(ex : Stun, Sleep...) are checked in L2Character useMagic()
8340 if (!skill.canCastWhileDisabled() && (isControlBlocked() || hasBlockActions()))
8341 {
8342 sendPacket(ActionFailed.STATIC_PACKET);
8343 return false;
8344 }
8345
8346 // Check if the player is dead
8347 if (isDead())
8348 {
8349 sendPacket(ActionFailed.STATIC_PACKET);
8350 return false;
8351 }
8352
8353 // Check if fishing and trying to use non-fishing skills.
8354 if (isFishing() && !skill.hasEffectType(L2EffectType.FISHING, L2EffectType.FISHING_START))
8355 {
8356 sendPacket(SystemMessageId.ONLY_FISHING_SKILLS_MAY_BE_USED_AT_THIS_TIME);
8357 return false;
8358 }
8359
8360 if (inObserverMode())
8361 {
8362 sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE);
8363 sendPacket(ActionFailed.STATIC_PACKET);
8364 return false;
8365 }
8366
8367 if (isSkillDisabled(skill))
8368 {
8369 final SystemMessage sm;
8370 if (hasSkillReuse(skill.getReuseHashCode()))
8371 {
8372 final int remainingTime = (int) (getSkillRemainingReuseTime(skill.getReuseHashCode()) / 1000);
8373 final int hours = remainingTime / 3600;
8374 final int minutes = (remainingTime % 3600) / 60;
8375 final int seconds = (remainingTime % 60);
8376 if (hours > 0)
8377 {
8378 sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_S2_HOUR_S_S3_MINUTE_S_AND_S4_SECOND_S_REMAINING_IN_S1_S_RE_USE_TIME);
8379 sm.addSkillName(skill);
8380 sm.addInt(hours);
8381 sm.addInt(minutes);
8382 }
8383 else if (minutes > 0)
8384 {
8385 sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_S2_MINUTE_S_S3_SECOND_S_REMAINING_IN_S1_S_RE_USE_TIME);
8386 sm.addSkillName(skill);
8387 sm.addInt(minutes);
8388 }
8389 else
8390 {
8391 sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_S2_SECOND_S_REMAINING_IN_S1_S_RE_USE_TIME);
8392 sm.addSkillName(skill);
8393 }
8394
8395 sm.addInt(seconds);
8396 }
8397 else
8398 {
8399 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_IS_NOT_AVAILABLE_AT_THIS_TIME_BEING_PREPARED_FOR_REUSE);
8400 sm.addSkillName(skill);
8401 }
8402
8403 sendPacket(sm);
8404 return false;
8405 }
8406
8407 // Check if the caster is sitting
8408 if (isSitting())
8409 {
8410 sendPacket(SystemMessageId.YOU_CANNOT_MOVE_WHILE_SITTING);
8411 sendPacket(ActionFailed.STATIC_PACKET);
8412 return false;
8413 }
8414
8415 // Check if the skill type is toggle and disable it, unless the toggle is necessary to be on.
8416 if (skill.isToggle())
8417 {
8418 if (isAffectedBySkill(skill.getId()))
8419 {
8420 if (!skill.isNecessaryToggle())
8421 {
8422 stopSkillEffects(true, skill.getId());
8423 }
8424 sendPacket(ActionFailed.STATIC_PACKET);
8425 return false;
8426 }
8427 else if (skill.getToggleGroupId() > 0)
8428 {
8429 getEffectList().stopAllTogglesOfGroup(skill.getToggleGroupId());
8430 }
8431 }
8432
8433 // Check if the player uses "Fake Death" skill
8434 // Note: do not check this before TOGGLE reset
8435 if (isFakeDeath())
8436 {
8437 sendPacket(ActionFailed.STATIC_PACKET);
8438 return false;
8439 }
8440
8441 // ************************************* Check Target *******************************************
8442 // Create and set a L2Object containing the target of the skill
8443 final L2Object target = skill.getTarget(this, forceUse, dontMove, true);
8444 final Location worldPosition = getCurrentSkillWorldPosition();
8445
8446 if ((skill.getTargetType() == TargetType.GROUND) && (worldPosition == null))
8447 {
8448 LOGGER.info("WorldPosition is null for skill: " + skill.getName() + ", player: " + getName() + ".");
8449 sendPacket(ActionFailed.STATIC_PACKET);
8450 return false;
8451 }
8452
8453 // Check the validity of the target
8454 if (target == null)
8455 {
8456 sendPacket(ActionFailed.STATIC_PACKET);
8457 return false;
8458 }
8459
8460 // Check if all casting conditions are completed
8461 if (!skill.checkCondition(this, target))
8462 {
8463 sendPacket(ActionFailed.STATIC_PACKET);
8464
8465 // Upon failed conditions, next action is called.
8466 if ((skill.getNextAction() != NextActionType.NONE) && (target != this) && target.isAutoAttackable(this))
8467 {
8468 if ((getAI().getNextIntention() == null) || (getAI().getNextIntention().getCtrlIntention() != CtrlIntention.AI_INTENTION_MOVE_TO))
8469 {
8470 if (skill.getNextAction() == NextActionType.ATTACK)
8471 {
8472 getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, target);
8473 }
8474 else if (skill.getNextAction() == NextActionType.CAST)
8475 {
8476 getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target, item, false, false);
8477 }
8478 }
8479 }
8480
8481 return false;
8482 }
8483
8484 final boolean doubleCast = isAffected(EffectFlag.DOUBLE_CAST) && skill.canDoubleCast();
8485
8486 // If a skill is currently being used, queue this one if this is not the same
8487 // In case of double casting, check if both slots are occupied, then queue skill.
8488 if ((!doubleCast && isCastingNow(SkillCaster::isAnyNormalType)) || (isCastingNow(s -> s.getCastingType() == SkillCastingType.NORMAL) && isCastingNow(s -> s.getCastingType() == SkillCastingType.NORMAL_SECOND)))
8489 {
8490 // Do not queue skill if called by an item.
8491 if (item == null)
8492 {
8493 // Create a new SkillDat object and queue it in the player _queuedSkill
8494 setQueuedSkill(skill, item, forceUse, dontMove);
8495 }
8496 sendPacket(ActionFailed.STATIC_PACKET);
8497 return false;
8498 }
8499
8500 if (getQueuedSkill() != null)
8501 {
8502 setQueuedSkill(null, null, false, false);
8503 }
8504
8505 // Notify the AI with AI_INTENTION_CAST and target
8506 getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target, item, forceUse, dontMove);
8507 return true;
8508 }
8509
8510 public boolean isInLooterParty(int LooterId)
8511 {
8512 final L2PcInstance looter = L2World.getInstance().getPlayer(LooterId);
8513
8514 // if L2PcInstance is in a CommandChannel
8515 if (isInParty() && getParty().isInCommandChannel() && (looter != null))
8516 {
8517 return getParty().getCommandChannel().getMembers().contains(looter);
8518 }
8519
8520 if (isInParty() && (looter != null))
8521 {
8522 return getParty().getMembers().contains(looter);
8523 }
8524
8525 return false;
8526 }
8527
8528 /**
8529 * @return True if the L2PcInstance is a Mage.
8530 */
8531 public boolean isMageClass()
8532 {
8533 return getClassId().isMage();
8534 }
8535
8536 public boolean isMounted()
8537 {
8538 return _mountType != MountType.NONE;
8539 }
8540
8541 public boolean checkLandingState()
8542 {
8543 // Check if char is in a no landing zone
8544 if (isInsideZone(ZoneId.NO_LANDING))
8545 {
8546 return true;
8547 }
8548 else
8549 // if this is a castle that is currently being sieged, and the rider is NOT a castle owner
8550 // he cannot land.
8551 // castle owner is the leader of the clan that owns the castle where the pc is
8552 if (isInsideZone(ZoneId.SIEGE) && !((getClan() != null) && (CastleManager.getInstance().getCastle(this) == CastleManager.getInstance().getCastleByOwner(getClan())) && (this == getClan().getLeader().getPlayerInstance())))
8553 {
8554 return true;
8555 }
8556
8557 return false;
8558 }
8559
8560 // returns false if the change of mount type fails.
8561 public void setMount(int npcId, int npcLevel)
8562 {
8563 final MountType type = MountType.findByNpcId(npcId);
8564 switch (type)
8565 {
8566 case NONE: // None
8567 {
8568 setIsFlying(false);
8569 break;
8570 }
8571 case STRIDER: // Strider
8572 {
8573 if (_nobleLevel > 0)
8574 {
8575 addSkill(CommonSkill.STRIDER_SIEGE_ASSAULT.getSkill(), false);
8576 }
8577 break;
8578 }
8579 case WYVERN: // Wyvern
8580 {
8581 setIsFlying(true);
8582 break;
8583 }
8584 }
8585
8586 _mountType = type;
8587 _mountNpcId = npcId;
8588 _mountLevel = npcLevel;
8589 }
8590
8591 /**
8592 * @return the type of Pet mounted (0 : none, 1 : Strider, 2 : Wyvern, 3: Wolf).
8593 */
8594 public MountType getMountType()
8595 {
8596 return _mountType;
8597 }
8598
8599 @Override
8600 public final void stopAllEffects()
8601 {
8602 super.stopAllEffects();
8603 updateAndBroadcastStatus(2);
8604 }
8605
8606 @Override
8607 public final void stopAllEffectsExceptThoseThatLastThroughDeath()
8608 {
8609 super.stopAllEffectsExceptThoseThatLastThroughDeath();
8610 updateAndBroadcastStatus(2);
8611 }
8612
8613 public final void stopCubics()
8614 {
8615 if (!_cubics.isEmpty())
8616 {
8617 _cubics.values().forEach(CubicInstance::deactivate);
8618 _cubics.clear();
8619 }
8620 }
8621
8622 public final void stopCubicsByOthers()
8623 {
8624 if (!_cubics.isEmpty())
8625 {
8626 boolean broadcast = false;
8627 for (CubicInstance cubic : _cubics.values())
8628 {
8629 if (cubic.isGivenByOther())
8630 {
8631 cubic.deactivate();
8632 _cubics.remove(cubic.getTemplate().getId());
8633 broadcast = true;
8634 }
8635 }
8636 if (broadcast)
8637 {
8638 sendPacket(new ExUserInfoCubic(this));
8639 broadcastUserInfo();
8640 }
8641 }
8642 }
8643
8644 /**
8645 * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers.<br>
8646 * <B><U>Concept</U>:</B><br>
8647 * Others L2PcInstance in the detection area of the L2PcInstance are identified in <B>_knownPlayers</B>.<br>
8648 * In order to inform other players of this L2PcInstance state modifications, server just need to go through _knownPlayers to send Server->Client Packet<br>
8649 * <B><U>Actions</U>:</B>
8650 * <ul>
8651 * <li>Send a Server->Client packet UserInfo to this L2PcInstance (Public and Private Data)</li>
8652 * <li>Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance (Public data only)</li>
8653 * </ul>
8654 * <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>
8655 */
8656 @Override
8657 public void updateAbnormalVisualEffects()
8658 {
8659 sendPacket(new ExUserInfoAbnormalVisualEffect(this));
8660 broadcastCharInfo();
8661 }
8662
8663 /**
8664 * Disable the Inventory and create a new task to enable it after 1.5s.
8665 * @param val
8666 */
8667 public void setInventoryBlockingStatus(boolean val)
8668 {
8669 _inventoryDisable = val;
8670 if (val)
8671 {
8672 ThreadPool.schedule(new InventoryEnableTask(this), 1500);
8673 }
8674 }
8675
8676 /**
8677 * @return True if the Inventory is disabled.
8678 */
8679 public boolean isInventoryDisabled()
8680 {
8681 return _inventoryDisable;
8682 }
8683
8684 /**
8685 * Add a cubic to this player.
8686 * @param cubic
8687 * @return the old cubic for this cubic ID if any, otherwise {@code null}
8688 */
8689 public CubicInstance addCubic(CubicInstance cubic)
8690 {
8691 return _cubics.put(cubic.getTemplate().getId(), cubic);
8692 }
8693
8694 /**
8695 * Get the player's cubics.
8696 * @return the cubics
8697 */
8698 public Map<Integer, CubicInstance> getCubics()
8699 {
8700 return _cubics;
8701 }
8702
8703 /**
8704 * Get the player cubic by cubic ID, if any.
8705 * @param cubicId the cubic ID
8706 * @return the cubic with the given cubic ID, {@code null} otherwise
8707 */
8708 public CubicInstance getCubicById(int cubicId)
8709 {
8710 return _cubics.get(cubicId);
8711 }
8712
8713 /**
8714 * @return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127).
8715 */
8716 public int getEnchantEffect()
8717 {
8718 final L2ItemInstance wpn = getActiveWeaponInstance();
8719
8720 if (wpn == null)
8721 {
8722 return 0;
8723 }
8724
8725 return Math.min(127, wpn.getEnchantLevel());
8726 }
8727
8728 /**
8729 * Set the _lastFolkNpc of the L2PcInstance corresponding to the last Folk wich one the player talked.
8730 * @param folkNpc
8731 */
8732 public void setLastFolkNPC(L2Npc folkNpc)
8733 {
8734 _lastFolkNpc = folkNpc;
8735 }
8736
8737 /**
8738 * @return the _lastFolkNpc of the L2PcInstance corresponding to the last Folk wich one the player talked.
8739 */
8740 public L2Npc getLastFolkNPC()
8741 {
8742 return _lastFolkNpc;
8743 }
8744
8745 public void addAutoSoulShot(int itemId)
8746 {
8747 _activeSoulShots.add(itemId);
8748 }
8749
8750 public boolean removeAutoSoulShot(int itemId)
8751 {
8752 return _activeSoulShots.remove(itemId);
8753 }
8754
8755 public Set<Integer> getAutoSoulShot()
8756 {
8757 return _activeSoulShots;
8758 }
8759
8760 @Override
8761 public void rechargeShots(boolean physical, boolean magic, boolean fish)
8762 {
8763 for (int itemId : _activeSoulShots)
8764 {
8765 final L2ItemInstance item = getInventory().getItemByItemId(itemId);
8766 if (item == null)
8767 {
8768 removeAutoSoulShot(itemId);
8769 continue;
8770 }
8771
8772 final IItemHandler handler = ItemHandler.getInstance().getHandler(item.getEtcItem());
8773 if (handler == null)
8774 {
8775 continue;
8776 }
8777
8778 final ActionType defaultAction = item.getItem().getDefaultAction();
8779 if ((magic && (defaultAction == ActionType.SPIRITSHOT)) || (physical && (defaultAction == ActionType.SOULSHOT)) || (fish && (defaultAction == ActionType.FISHINGSHOT)))
8780 {
8781 handler.useItem(this, item, false);
8782 }
8783 }
8784 }
8785
8786 /**
8787 * Cancel autoshot for all shots matching crystaltype {@link L2Item#getCrystalType()}.
8788 * @param crystalType int type to disable
8789 */
8790 public void disableAutoShotByCrystalType(int crystalType)
8791 {
8792 for (int itemId : _activeSoulShots)
8793 {
8794 if (ItemTable.getInstance().getTemplate(itemId).getCrystalType().getLevel() == crystalType)
8795 {
8796 disableAutoShot(itemId);
8797 }
8798 }
8799 }
8800
8801 /**
8802 * Cancel autoshot use for shot itemId
8803 * @param itemId int id to disable
8804 * @return true if canceled.
8805 */
8806 public boolean disableAutoShot(int itemId)
8807 {
8808 if (_activeSoulShots.contains(itemId))
8809 {
8810 removeAutoSoulShot(itemId);
8811 sendPacket(new ExAutoSoulShot(itemId, false, 0));
8812
8813 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_AUTOMATIC_USE_OF_S1_HAS_BEEN_DEACTIVATED);
8814 sm.addItemName(itemId);
8815 sendPacket(sm);
8816 return true;
8817 }
8818 return false;
8819 }
8820
8821 /**
8822 * Cancel all autoshots for player
8823 */
8824 public void disableAutoShotsAll()
8825 {
8826 for (int itemId : _activeSoulShots)
8827 {
8828 sendPacket(new ExAutoSoulShot(itemId, false, 0));
8829 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_AUTOMATIC_USE_OF_S1_HAS_BEEN_DEACTIVATED);
8830 sm.addItemName(itemId);
8831 sendPacket(sm);
8832 }
8833 _activeSoulShots.clear();
8834 }
8835
8836 public BroochJewel getActiveRubyJewel()
8837 {
8838 return _activeRubyJewel;
8839 }
8840
8841 public void setActiveRubyJewel(BroochJewel jewel)
8842 {
8843 _activeRubyJewel = jewel;
8844 }
8845
8846 public BroochJewel getActiveShappireJewel()
8847 {
8848 return _activeShappireJewel;
8849 }
8850
8851 public void setActiveShappireJewel(BroochJewel jewel)
8852 {
8853 _activeShappireJewel = jewel;
8854 }
8855
8856 public void updateActiveBroochJewel()
8857 {
8858 // Update active Ruby jewel.
8859 if ((getInventory().getItemByItemId(BroochJewel.GREATER_RUBY.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.GREATER_RUBY.getItemId()).isEquipped()))
8860 {
8861 setActiveRubyJewel(BroochJewel.GREATER_RUBY);
8862 }
8863 else if ((getInventory().getItemByItemId(BroochJewel.RUBY_LV5.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.RUBY_LV5.getItemId()).isEquipped()))
8864 {
8865 setActiveRubyJewel(BroochJewel.RUBY_LV5);
8866 }
8867 else if ((getInventory().getItemByItemId(BroochJewel.RUBY_LV4.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.RUBY_LV4.getItemId()).isEquipped()))
8868 {
8869 setActiveRubyJewel(BroochJewel.RUBY_LV4);
8870 }
8871 else if ((getInventory().getItemByItemId(BroochJewel.RUBY_LV3.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.RUBY_LV3.getItemId()).isEquipped()))
8872 {
8873 setActiveRubyJewel(BroochJewel.RUBY_LV3);
8874 }
8875 else if ((getInventory().getItemByItemId(BroochJewel.RUBY_LV2.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.RUBY_LV2.getItemId()).isEquipped()))
8876 {
8877 setActiveRubyJewel(BroochJewel.RUBY_LV2);
8878 }
8879 else if ((getInventory().getItemByItemId(BroochJewel.RUBY_LV1.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.RUBY_LV1.getItemId()).isEquipped()))
8880 {
8881 setActiveRubyJewel(BroochJewel.RUBY_LV1);
8882 }
8883 else
8884 {
8885 setActiveRubyJewel(null);
8886 }
8887 // Update active Sapphire jewel.
8888 if ((getInventory().getItemByItemId(BroochJewel.GREATER_SHAPPHIRE.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.GREATER_SHAPPHIRE.getItemId()).isEquipped()))
8889 {
8890 setActiveShappireJewel(BroochJewel.GREATER_SHAPPHIRE);
8891 }
8892 else if ((getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV5.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV5.getItemId()).isEquipped()))
8893 {
8894 setActiveShappireJewel(BroochJewel.SHAPPHIRE_LV5);
8895 }
8896 else if ((getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV4.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV4.getItemId()).isEquipped()))
8897 {
8898 setActiveShappireJewel(BroochJewel.SHAPPHIRE_LV4);
8899 }
8900 else if ((getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV3.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV3.getItemId()).isEquipped()))
8901 {
8902 setActiveShappireJewel(BroochJewel.SHAPPHIRE_LV3);
8903 }
8904 else if ((getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV2.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV2.getItemId()).isEquipped()))
8905 {
8906 setActiveShappireJewel(BroochJewel.SHAPPHIRE_LV2);
8907 }
8908 else if ((getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV1.getItemId()) != null) && (getInventory().getItemByItemId(BroochJewel.SHAPPHIRE_LV1.getItemId()).isEquipped()))
8909 {
8910 setActiveShappireJewel(BroochJewel.SHAPPHIRE_LV1);
8911 }
8912 else
8913 {
8914 setActiveShappireJewel(null);
8915 }
8916 }
8917
8918 private ScheduledFuture<?> _taskWarnUserTakeBreak;
8919
8920 public EnumIntBitmask<ClanPrivilege> getClanPrivileges()
8921 {
8922 return _clanPrivileges;
8923 }
8924
8925 public void setClanPrivileges(EnumIntBitmask<ClanPrivilege> clanPrivileges)
8926 {
8927 _clanPrivileges = clanPrivileges.clone();
8928 }
8929
8930 public boolean hasClanPrivilege(ClanPrivilege privilege)
8931 {
8932 return _clanPrivileges.has(privilege);
8933 }
8934
8935 // baron etc
8936 public void setPledgeClass(int classId)
8937 {
8938 _pledgeClass = classId;
8939 checkItemRestriction();
8940 }
8941
8942 public int getPledgeClass()
8943 {
8944 return _pledgeClass;
8945 }
8946
8947 public void setPledgeType(int typeId)
8948 {
8949 _pledgeType = typeId;
8950 }
8951
8952 @Override
8953 public int getPledgeType()
8954 {
8955 return _pledgeType;
8956 }
8957
8958 public int getApprentice()
8959 {
8960 return _apprentice;
8961 }
8962
8963 public void setApprentice(int apprentice_id)
8964 {
8965 _apprentice = apprentice_id;
8966 }
8967
8968 public int getSponsor()
8969 {
8970 return _sponsor;
8971 }
8972
8973 public void setSponsor(int sponsor_id)
8974 {
8975 _sponsor = sponsor_id;
8976 }
8977
8978 public int getBookMarkSlot()
8979 {
8980 return _bookmarkslot;
8981 }
8982
8983 public void setBookMarkSlot(int slot)
8984 {
8985 _bookmarkslot = slot;
8986 sendPacket(new ExGetBookMarkInfoPacket(this));
8987 }
8988
8989 @Override
8990 public void sendMessage(String message)
8991 {
8992 sendPacket(SystemMessage.sendString(message));
8993 }
8994
8995 public void setObserving(boolean state)
8996 {
8997 _observerMode = state;
8998 setTarget(null);
8999 setBlockActions(state);
9000 setIsInvul(state);
9001 setInvisible(state);
9002 if (hasAI() && !state)
9003 {
9004 getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
9005 }
9006 }
9007
9008 public void enterObserverMode(Location loc)
9009 {
9010 setLastLocation();
9011
9012 // Remove Hide.
9013 getEffectList().stopEffects(AbnormalType.HIDE);
9014
9015 setObserving(true);
9016 sendPacket(new ObservationMode(loc));
9017
9018 teleToLocation(loc, false);
9019
9020 broadcastUserInfo();
9021 }
9022
9023 public void setLastLocation()
9024 {
9025 _lastLoc = new Location(getX(), getY(), getZ());
9026 }
9027
9028 public void unsetLastLocation()
9029 {
9030 _lastLoc = null;
9031 }
9032
9033 public void enterOlympiadObserverMode(Location loc, int id)
9034 {
9035 final L2Summon pet = getPet();
9036 if (pet != null)
9037 {
9038 pet.unSummon(this);
9039 }
9040
9041 if (hasServitors())
9042 {
9043 getServitors().values().forEach(s -> s.unSummon(this));
9044 }
9045
9046 // Remove Hide.
9047 getEffectList().stopEffects(AbnormalType.HIDE);
9048
9049 if (!_cubics.isEmpty())
9050 {
9051 _cubics.values().forEach(CubicInstance::deactivate);
9052 _cubics.clear();
9053 sendPacket(new ExUserInfoCubic(this));
9054 }
9055
9056 if (getParty() != null)
9057 {
9058 getParty().removePartyMember(this, MessageType.EXPELLED);
9059 }
9060
9061 _olympiadGameId = id;
9062 if (isSitting())
9063 {
9064 standUp();
9065 }
9066 if (!_observerMode)
9067 {
9068 setLastLocation();
9069 }
9070
9071 _observerMode = true;
9072 setTarget(null);
9073 setIsInvul(true);
9074 setInvisible(true);
9075 setInstance(OlympiadGameManager.getInstance().getOlympiadTask(id).getStadium().getInstance());
9076 teleToLocation(loc, false);
9077 sendPacket(new ExOlympiadMode(3));
9078
9079 broadcastUserInfo();
9080 }
9081
9082 public void leaveObserverMode()
9083 {
9084 setTarget(null);
9085 setInstance(null);
9086 teleToLocation(_lastLoc, false);
9087 unsetLastLocation();
9088 sendPacket(new ObservationReturn(getLocation()));
9089
9090 setBlockActions(false);
9091 if (!isGM())
9092 {
9093 setInvisible(false);
9094 setIsInvul(false);
9095 }
9096 if (hasAI())
9097 {
9098 getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
9099 }
9100
9101 setFalling(); // prevent receive falling damage
9102 _observerMode = false;
9103
9104 broadcastUserInfo();
9105 }
9106
9107 public void leaveOlympiadObserverMode()
9108 {
9109 if (_olympiadGameId == -1)
9110 {
9111 return;
9112 }
9113 _olympiadGameId = -1;
9114 _observerMode = false;
9115 setTarget(null);
9116 sendPacket(new ExOlympiadMode(0));
9117 setInstance(null);
9118 teleToLocation(_lastLoc, true);
9119 if (!isGM())
9120 {
9121 setInvisible(false);
9122 setIsInvul(false);
9123 }
9124 if (hasAI())
9125 {
9126 getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
9127 }
9128 unsetLastLocation();
9129 broadcastUserInfo();
9130 }
9131
9132 public void setOlympiadSide(int i)
9133 {
9134 _olympiadSide = i;
9135 }
9136
9137 public int getOlympiadSide()
9138 {
9139 return _olympiadSide;
9140 }
9141
9142 public void setOlympiadGameId(int id)
9143 {
9144 _olympiadGameId = id;
9145 }
9146
9147 public int getOlympiadGameId()
9148 {
9149 return _olympiadGameId;
9150 }
9151
9152 public Location getLastLocation()
9153 {
9154 return _lastLoc;
9155 }
9156
9157 public boolean inObserverMode()
9158 {
9159 return _observerMode;
9160 }
9161
9162 public AdminTeleportType getTeleMode()
9163 {
9164 return _teleportType;
9165 }
9166
9167 public void setTeleMode(AdminTeleportType type)
9168 {
9169 _teleportType = type;
9170 }
9171
9172 public void setRace(int i, int val)
9173 {
9174 _race[i] = val;
9175 }
9176
9177 public int getRace(int i)
9178 {
9179 return _race[i];
9180 }
9181
9182 public boolean getMessageRefusal()
9183 {
9184 return _messageRefusal;
9185 }
9186
9187 public void setMessageRefusal(boolean mode)
9188 {
9189 _messageRefusal = mode;
9190 sendPacket(new EtcStatusUpdate(this));
9191 }
9192
9193 public void setDietMode(boolean mode)
9194 {
9195 _dietMode = mode;
9196 }
9197
9198 public boolean getDietMode()
9199 {
9200 return _dietMode;
9201 }
9202
9203 public void setTradeRefusal(boolean mode)
9204 {
9205 _tradeRefusal = mode;
9206 }
9207
9208 public boolean getTradeRefusal()
9209 {
9210 return _tradeRefusal;
9211 }
9212
9213 public void setExchangeRefusal(boolean mode)
9214 {
9215 _exchangeRefusal = mode;
9216 }
9217
9218 public boolean getExchangeRefusal()
9219 {
9220 return _exchangeRefusal;
9221 }
9222
9223 public BlockList getBlockList()
9224 {
9225 return _blockList;
9226 }
9227
9228 /**
9229 * @param player
9230 * @return returns {@code true} if player is current player cannot accepting messages from the target player, {@code false} otherwise
9231 */
9232 public boolean isBlocking(L2PcInstance player)
9233 {
9234 return _blockList.isBlockAll() || _blockList.isInBlockList(player);
9235 }
9236
9237 /**
9238 * @param player
9239 * @return returns {@code true} if player is current player can accepting messages from the target player, {@code false} otherwise
9240 */
9241 public boolean isNotBlocking(L2PcInstance player)
9242 {
9243 return !_blockList.isBlockAll() && !_blockList.isInBlockList(player);
9244 }
9245
9246 /**
9247 * @param player
9248 * @return returns {@code true} if player is target player cannot accepting messages from the current player, {@code false} otherwise
9249 */
9250 public boolean isBlocked(L2PcInstance player)
9251 {
9252 return player.getBlockList().isBlockAll() || player.getBlockList().isInBlockList(this);
9253 }
9254
9255 /**
9256 * @param player
9257 * @return returns {@code true} if player is target player can accepting messages from the current player, {@code false} otherwise
9258 */
9259 public boolean isNotBlocked(L2PcInstance player)
9260 {
9261 return !player.getBlockList().isBlockAll() && !player.getBlockList().isInBlockList(this);
9262 }
9263
9264 public void setHero(boolean hero)
9265 {
9266 if (hero && (_baseClass == _activeClass))
9267 {
9268 for (Skill skill : SkillTreesData.getInstance().getHeroSkillTree())
9269 {
9270 addSkill(skill, false); // Don't persist hero skills into database
9271 }
9272 }
9273 else
9274 {
9275 for (Skill skill : SkillTreesData.getInstance().getHeroSkillTree())
9276 {
9277 removeSkill(skill, false, true); // Just remove skills from non-hero players
9278 }
9279 }
9280 _hero = hero;
9281
9282 sendSkillList();
9283 }
9284
9285 public void setIsInOlympiadMode(boolean b)
9286 {
9287 _inOlympiadMode = b;
9288 }
9289
9290 public void setIsOlympiadStart(boolean b)
9291 {
9292 _OlympiadStart = b;
9293 }
9294
9295 public boolean isOlympiadStart()
9296 {
9297 return _OlympiadStart;
9298 }
9299
9300 public boolean isHero()
9301 {
9302 return _hero;
9303 }
9304
9305 public boolean isInOlympiadMode()
9306 {
9307 return _inOlympiadMode;
9308 }
9309
9310 public boolean isInDuel()
9311 {
9312 return _isInDuel;
9313 }
9314
9315 public int getDuelId()
9316 {
9317 return _duelId;
9318 }
9319
9320 public void setDuelState(int mode)
9321 {
9322 _duelState = mode;
9323 }
9324
9325 public int getDuelState()
9326 {
9327 return _duelState;
9328 }
9329
9330 /**
9331 * Sets up the duel state using a non 0 duelId.
9332 * @param duelId 0=not in a duel
9333 */
9334 public void setIsInDuel(int duelId)
9335 {
9336 if (duelId > 0)
9337 {
9338 _isInDuel = true;
9339 _duelState = Duel.DUELSTATE_DUELLING;
9340 _duelId = duelId;
9341 }
9342 else
9343 {
9344 if (_duelState == Duel.DUELSTATE_DEAD)
9345 {
9346 enableAllSkills();
9347 getStatus().startHpMpRegeneration();
9348 }
9349 _isInDuel = false;
9350 _duelState = Duel.DUELSTATE_NODUEL;
9351 _duelId = 0;
9352 }
9353 }
9354
9355 /**
9356 * This returns a SystemMessage stating why the player is not available for duelling.
9357 * @return S1_CANNOT_DUEL... message
9358 */
9359 public SystemMessage getNoDuelReason()
9360 {
9361 final SystemMessage sm = SystemMessage.getSystemMessage(_noDuelReason);
9362 sm.addPcName(this);
9363 _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL;
9364 return sm;
9365 }
9366
9367 /**
9368 * Checks if this player might join / start a duel.<br>
9369 * To get the reason use getNoDuelReason() after calling this function.
9370 * @return true if the player might join/start a duel.
9371 */
9372 public boolean canDuel()
9373 {
9374 if (isInCombat() || isJailed())
9375 {
9376 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_BATTLE;
9377 return false;
9378 }
9379 if (isDead() || isAlikeDead() || ((getCurrentHp() < (getMaxHp() / 2)) || (getCurrentMp() < (getMaxMp() / 2))))
9380 {
9381 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_S_HP_OR_MP_IS_BELOW_50;
9382 return false;
9383 }
9384 if (isInDuel())
9385 {
9386 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_ALREADY_ENGAGED_IN_A_DUEL;
9387 return false;
9388 }
9389 if (isInOlympiadMode() || isOnEvent(CeremonyOfChaosEvent.class))
9390 {
9391 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_PARTICIPATING_IN_THE_OLYMPIAD_OR_THE_CEREMONY_OF_CHAOS;
9392 return false;
9393 }
9394 if (isOnEvent()) // custom event message
9395 {
9396 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_BATTLE;
9397 return false;
9398 }
9399 if (isCursedWeaponEquipped())
9400 {
9401 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_IN_A_CHAOTIC_OR_PURPLE_STATE;
9402 return false;
9403 }
9404 if (getPrivateStoreType() != PrivateStoreType.NONE)
9405 {
9406 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_A_PRIVATE_STORE_OR_MANUFACTURE;
9407 return false;
9408 }
9409 if (isMounted() || isInBoat())
9410 {
9411 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_RIDING_A_BOAT_FENRIR_OR_STRIDER;
9412 return false;
9413 }
9414 if (isFishing())
9415 {
9416 _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_FISHING;
9417 return false;
9418 }
9419 if (isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.PEACE) || isInsideZone(ZoneId.SIEGE))
9420 {
9421 _noDuelReason = SystemMessageId.C1_IS_IN_AN_AREA_WHERE_DUEL_IS_NOT_ALLOWED_AND_YOU_CANNOT_APPLY_FOR_A_DUEL;
9422 return false;
9423 }
9424 return true;
9425 }
9426
9427 public int getNobleLevel()
9428 {
9429 return _nobleLevel;
9430 }
9431
9432 public void setNobleLevel(int level)
9433 {
9434 if (level != 0)
9435 {
9436 SkillTreesData.getInstance().getNobleSkillAutoGetTree().forEach(skill -> addSkill(skill, false));
9437 }
9438 else
9439 {
9440 SkillTreesData.getInstance().getNobleSkillTree().forEach(skill -> removeSkill(skill, false, true));
9441 }
9442 _nobleLevel = level;
9443 sendSkillList();
9444 }
9445
9446 public void setLvlJoinedAcademy(int lvl)
9447 {
9448 _lvlJoinedAcademy = lvl;
9449 }
9450
9451 public int getLvlJoinedAcademy()
9452 {
9453 return _lvlJoinedAcademy;
9454 }
9455
9456 @Override
9457 public boolean isAcademyMember()
9458 {
9459 return _lvlJoinedAcademy > 0;
9460 }
9461
9462 @Override
9463 public void setTeam(Team team)
9464 {
9465 super.setTeam(team);
9466 broadcastUserInfo();
9467 final L2Summon pet = getPet();
9468 if (pet != null)
9469 {
9470 pet.broadcastStatusUpdate();
9471 }
9472 if (hasServitors())
9473 {
9474 getServitors().values().forEach(L2Summon::broadcastStatusUpdate);
9475 }
9476 }
9477
9478 public void setWantsPeace(int wantsPeace)
9479 {
9480 _wantsPeace = wantsPeace;
9481 }
9482
9483 public int getWantsPeace()
9484 {
9485 return _wantsPeace;
9486 }
9487
9488 public void sendSkillList()
9489 {
9490 if (_skillListRefreshTask == null)
9491 {
9492 _skillListRefreshTask = ThreadPool.schedule(() ->
9493 {
9494 sendSkillList(0);
9495 _skillListRefreshTask = null;
9496 }, 1000);
9497 }
9498 }
9499
9500 public void sendSkillList(int lastLearnedSkillId)
9501 {
9502 boolean isDisabled = false;
9503 final SkillList sl = new SkillList();
9504
9505 for (Skill s : getSkillList())
9506 {
9507 if (getClan() != null)
9508 {
9509 isDisabled = s.isClanSkill() && (getClan().getReputationScore() < 0);
9510 }
9511
9512 sl.addSkill(s.getDisplayId(), s.getReuseDelayGroup(), s.getDisplayLevel(), s.getSubLevel(), s.isPassive(), isDisabled, s.isEnchantable());
9513 }
9514 if (lastLearnedSkillId > 0)
9515 {
9516 sl.setLastLearnedSkillId(lastLearnedSkillId);
9517 }
9518 sendPacket(sl);
9519
9520 sendPacket(new AcquireSkillList(this));
9521 }
9522
9523 /**
9524 * 1. Add the specified class ID as a subclass (up to the maximum number of <b>three</b>) for this character.<BR>
9525 * 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.
9526 * @param classId
9527 * @param classIndex
9528 * @param isDualClass
9529 * @return boolean subclassAdded
9530 */
9531 public boolean addSubClass(int classId, int classIndex, boolean isDualClass)
9532 {
9533 if (!_subclassLock.tryLock())
9534 {
9535 return false;
9536 }
9537
9538 try
9539 {
9540 if ((getTotalSubClasses() == Config.MAX_SUBCLASS) || (classIndex == 0))
9541 {
9542 return false;
9543 }
9544
9545 if (getSubClasses().containsKey(classIndex))
9546 {
9547 return false;
9548 }
9549
9550 // Note: Never change _classIndex in any method other than setActiveClass().
9551
9552 final SubClass newClass = new SubClass();
9553 newClass.setClassId(classId);
9554 newClass.setClassIndex(classIndex);
9555 newClass.setVitalityPoints(PcStat.MAX_VITALITY_POINTS);
9556 if (isDualClass)
9557 {
9558 newClass.setIsDualClass(true);
9559 newClass.setExp(ExperienceData.getInstance().getExpForLevel(Config.BASE_DUALCLASS_LEVEL));
9560 newClass.setLevel(Config.BASE_DUALCLASS_LEVEL);
9561 }
9562
9563 try (Connection con = DatabaseFactory.getInstance().getConnection();
9564 PreparedStatement statement = con.prepareStatement(ADD_CHAR_SUBCLASS))
9565 {
9566 // Store the basic info about this new sub-class.
9567 statement.setInt(1, getObjectId());
9568 statement.setInt(2, newClass.getClassId());
9569 statement.setLong(3, newClass.getExp());
9570 statement.setLong(4, newClass.getSp());
9571 statement.setInt(5, newClass.getLevel());
9572 statement.setInt(6, newClass.getVitalityPoints());
9573 statement.setInt(7, newClass.getClassIndex());
9574 statement.setBoolean(8, newClass.isDualClass());
9575 statement.execute();
9576 }
9577 catch (Exception e)
9578 {
9579 LOGGER.log(Level.WARNING, "WARNING: Could not add character sub class for " + getName() + ": " + e.getMessage(), e);
9580 return false;
9581 }
9582
9583 // Commit after database INSERT incase exception is thrown.
9584 getSubClasses().put(newClass.getClassIndex(), newClass);
9585
9586 final ClassId subTemplate = ClassId.getClassId(classId);
9587 final Map<Long, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(subTemplate);
9588 final Map<Integer, Skill> prevSkillList = new HashMap<>();
9589 for (L2SkillLearn skillInfo : skillTree.values())
9590 {
9591 if (skillInfo.getGetLevel() <= newClass.getLevel())
9592 {
9593 final Skill prevSkill = prevSkillList.get(skillInfo.getSkillId());
9594 final Skill newSkill = SkillData.getInstance().getSkill(skillInfo.getSkillId(), skillInfo.getSkillLevel());
9595
9596 if (((prevSkill != null) && (prevSkill.getLevel() > newSkill.getLevel())) || SkillTreesData.getInstance().isRemoveSkill(subTemplate, skillInfo.getSkillId()))
9597 {
9598 continue;
9599 }
9600
9601 prevSkillList.put(newSkill.getId(), newSkill);
9602 storeSkill(newSkill, prevSkill, classIndex);
9603 }
9604 }
9605 return true;
9606 }
9607 finally
9608 {
9609 _subclassLock.unlock();
9610 }
9611 }
9612
9613 /**
9614 * 1. Completely erase all existance of the subClass linked to the classIndex.<br>
9615 * 2. Send over the newClassId to addSubClass() to create a new instance on this classIndex.<br>
9616 * 3. Upon Exception, revert the player to their BaseClass to avoid further problems.
9617 * @param classIndex the class index to delete
9618 * @param newClassId the new class Id
9619 * @param isDualClass is subclass dualclass
9620 * @return {@code true} if the sub-class was modified, {@code false} otherwise
9621 */
9622 public boolean modifySubClass(int classIndex, int newClassId, boolean isDualClass)
9623 {
9624 if (!_subclassLock.tryLock())
9625 {
9626 return false;
9627 }
9628
9629 try
9630 {
9631 // Notify to scripts before class is removed.
9632 if (!getSubClasses().isEmpty()) // also null check
9633 {
9634 final int classId = getSubClasses().get(classIndex).getClassId();
9635 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerProfessionCancel(this, classId), this);
9636 }
9637
9638 final SubClass subClass = getSubClasses().remove(classIndex);
9639 if (subClass == null)
9640 {
9641 return false;
9642 }
9643
9644 if (subClass.isDualClass())
9645 {
9646 getVariables().remove(PlayerVariables.ABILITY_POINTS_DUAL_CLASS);
9647 getVariables().remove(PlayerVariables.ABILITY_POINTS_USED_DUAL_CLASS);
9648 int revelationSkill = getVariables().getInt(PlayerVariables.REVELATION_SKILL_1_DUAL_CLASS, 0);
9649 if (revelationSkill != 0)
9650 {
9651 removeSkill(revelationSkill);
9652 }
9653 revelationSkill = getVariables().getInt(PlayerVariables.REVELATION_SKILL_2_DUAL_CLASS, 0);
9654 if (revelationSkill != 0)
9655 {
9656 removeSkill(revelationSkill);
9657 }
9658 }
9659
9660 try (Connection con = DatabaseFactory.getInstance().getConnection();
9661 PreparedStatement deleteHennas = con.prepareStatement(DELETE_CHAR_HENNAS);
9662 PreparedStatement deleteShortcuts = con.prepareStatement(DELETE_CHAR_SHORTCUTS);
9663 PreparedStatement deleteSkillReuse = con.prepareStatement(DELETE_SKILL_SAVE);
9664 PreparedStatement deleteSkills = con.prepareStatement(DELETE_CHAR_SKILLS);
9665 PreparedStatement deleteSubclass = con.prepareStatement(DELETE_CHAR_SUBCLASS))
9666 {
9667 // Remove all henna info stored for this sub-class.
9668 deleteHennas.setInt(1, getObjectId());
9669 deleteHennas.setInt(2, classIndex);
9670 deleteHennas.execute();
9671
9672 // Remove all shortcuts info stored for this sub-class.
9673 deleteShortcuts.setInt(1, getObjectId());
9674 deleteShortcuts.setInt(2, classIndex);
9675 deleteShortcuts.execute();
9676
9677 // Remove all effects info stored for this sub-class.
9678 deleteSkillReuse.setInt(1, getObjectId());
9679 deleteSkillReuse.setInt(2, classIndex);
9680 deleteSkillReuse.execute();
9681
9682 // Remove all skill info stored for this sub-class.
9683 deleteSkills.setInt(1, getObjectId());
9684 deleteSkills.setInt(2, classIndex);
9685 deleteSkills.execute();
9686
9687 // Remove all basic info stored about this sub-class.
9688 deleteSubclass.setInt(1, getObjectId());
9689 deleteSubclass.setInt(2, classIndex);
9690 deleteSubclass.execute();
9691 }
9692 catch (Exception e)
9693 {
9694 LOGGER.log(Level.WARNING, "Could not modify sub class for " + getName() + " to class index " + classIndex + ": " + e.getMessage(), e);
9695 return false;
9696 }
9697 }
9698 finally
9699 {
9700 _subclassLock.unlock();
9701 }
9702
9703 return addSubClass(newClassId, classIndex, isDualClass);
9704 }
9705
9706 public boolean isSubClassActive()
9707 {
9708 return _classIndex > 0;
9709 }
9710
9711 public void setDualClass(int classIndex)
9712 {
9713 if (isSubClassActive())
9714 {
9715 getSubClasses().get(_classIndex).setIsDualClass(true);
9716 }
9717 }
9718
9719 public boolean isDualClassActive()
9720 {
9721 return isSubClassActive() && getSubClasses().get(_classIndex).isDualClass();
9722 }
9723
9724 public boolean hasDualClass()
9725 {
9726 return getSubClasses().values().stream().anyMatch(SubClass::isDualClass);
9727 }
9728
9729 public SubClass getDualClass()
9730 {
9731 return getSubClasses().values().stream().filter(SubClass::isDualClass).findFirst().orElse(null);
9732 }
9733
9734 public Map<Integer, SubClass> getSubClasses()
9735 {
9736 if (_subClasses == null)
9737 {
9738 synchronized (this)
9739 {
9740 if (_subClasses == null)
9741 {
9742 _subClasses = new ConcurrentHashMap<>();
9743 }
9744 }
9745 }
9746
9747 return _subClasses;
9748 }
9749
9750 public int getTotalSubClasses()
9751 {
9752 return getSubClasses().size();
9753 }
9754
9755 public int getBaseClass()
9756 {
9757 return _baseClass;
9758 }
9759
9760 public int getActiveClass()
9761 {
9762 return _activeClass;
9763 }
9764
9765 public int getClassIndex()
9766 {
9767 return _classIndex;
9768 }
9769
9770 protected void setClassIndex(int classIndex)
9771 {
9772 _classIndex = classIndex;
9773 }
9774
9775 private void setClassTemplate(int classId)
9776 {
9777 _activeClass = classId;
9778
9779 final L2PcTemplate pcTemplate = PlayerTemplateData.getInstance().getTemplate(classId);
9780 if (pcTemplate == null)
9781 {
9782 LOGGER.severe("Missing template for classId: " + classId);
9783 throw new Error();
9784 }
9785 // Set the template of the L2PcInstance
9786 setTemplate(pcTemplate);
9787
9788 // Notify to scripts
9789 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerProfessionChange(this, pcTemplate, isSubClassActive()), this);
9790 }
9791
9792 /**
9793 * Changes the character's class based on the given class index.<br>
9794 * An index of zero specifies the character's original (base) class, while indexes 1-3 specifies the character's sub-classes respectively.<br>
9795 * <font color="00FF00"/>WARNING: Use only on subclase change</font>
9796 * @param classIndex
9797 * @return
9798 */
9799 public boolean setActiveClass(int classIndex)
9800 {
9801 if (!_subclassLock.tryLock())
9802 {
9803 return false;
9804 }
9805
9806 try
9807 {
9808 // Cannot switch or change subclasses while transformed
9809 if (isTransformed())
9810 {
9811 return false;
9812 }
9813
9814 // Remove active item skills before saving char to database
9815 // because next time when choosing this class, weared items can
9816 // be different
9817 for (L2ItemInstance item : getInventory().getPaperdollItems(L2ItemInstance::isAugmented))
9818 {
9819 if ((item != null) && item.isEquipped())
9820 {
9821 item.getAugmentation().removeBonus(this);
9822 }
9823 }
9824
9825 // abort any kind of cast.
9826 abortCast();
9827
9828 if (isChannelized())
9829 {
9830 getSkillChannelized().abortChannelization();
9831 }
9832
9833 // 1. Call store() before modifying _classIndex to avoid skill effects rollover.
9834 // 2. Register the correct _classId against applied 'classIndex'.
9835 store(Config.SUBCLASS_STORE_SKILL_COOLTIME);
9836
9837 if (_sellingBuffs != null)
9838 {
9839 _sellingBuffs.clear();
9840 }
9841
9842 resetTimeStamps();
9843
9844 // clear charges
9845 _charges.set(0);
9846 stopChargeTask();
9847
9848 if (hasServitors())
9849 {
9850 getServitors().values().forEach(s -> s.unSummon(this));
9851 }
9852
9853 if (classIndex == 0)
9854 {
9855 setClassTemplate(getBaseClass());
9856 }
9857 else
9858 {
9859 try
9860 {
9861 setClassTemplate(getSubClasses().get(classIndex).getClassId());
9862 }
9863 catch (Exception e)
9864 {
9865 LOGGER.log(Level.WARNING, "Could not switch " + getName() + "'s sub class to class index " + classIndex + ": " + e.getMessage(), e);
9866 return false;
9867 }
9868 }
9869 _classIndex = classIndex;
9870
9871 if (isInParty())
9872 {
9873 getParty().recalculatePartyLevel();
9874 }
9875
9876 // Update the character's change in class status.
9877 // 1. Remove any active cubics from the player.
9878 // 2. Renovate the characters table in the database with the new class info, storing also buff/effect data.
9879 // 3. Remove all existing skills.
9880 // 4. Restore all the learned skills for the current class from the database.
9881 // 5. Restore effect/buff data for the new class.
9882 // 6. Restore henna data for the class, applying the new stat modifiers while removing existing ones.
9883 // 7. Reset HP/MP/CP stats and send Server->Client character status packet to reflect changes.
9884 // 8. Restore shortcut data related to this class.
9885 // 9. Resend a class change animation effect to broadcast to all nearby players.
9886 for (Skill oldSkill : getAllSkills())
9887 {
9888 removeSkill(oldSkill, false, true);
9889 }
9890
9891 stopAllEffectsExceptThoseThatLastThroughDeath();
9892 stopAllEffects();
9893 stopCubics();
9894
9895 restoreRecipeBook(false);
9896
9897 restoreSkills();
9898 rewardSkills();
9899 regiveTemporarySkills();
9900
9901 // Prevents some issues when changing between subclases that shares skills
9902 resetDisabledSkills();
9903
9904 restoreEffects();
9905
9906 sendPacket(new EtcStatusUpdate(this));
9907
9908 for (int i = 0; i < 4; i++)
9909 {
9910 _henna[i] = null;
9911 }
9912
9913 restoreHenna();
9914 sendPacket(new HennaInfo(this));
9915
9916 if (getCurrentHp() > getMaxHp())
9917 {
9918 setCurrentHp(getMaxHp());
9919 }
9920 if (getCurrentMp() > getMaxMp())
9921 {
9922 setCurrentMp(getMaxMp());
9923 }
9924 if (getCurrentCp() > getMaxCp())
9925 {
9926 setCurrentCp(getMaxCp());
9927 }
9928
9929 refreshOverloaded(true);
9930 refreshExpertisePenalty();
9931 broadcastUserInfo();
9932
9933 // Clear resurrect xp calculation
9934 setExpBeforeDeath(0);
9935
9936 _shortCuts.restoreMe();
9937 sendPacket(new ShortCutInit(this));
9938
9939 broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP));
9940 sendPacket(new SkillCoolTime(this));
9941 sendPacket(new ExStorageMaxCount(this));
9942
9943 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerSubChange(this), this);
9944 return true;
9945 }
9946 finally
9947 {
9948 _subclassLock.unlock();
9949 }
9950 }
9951
9952 public boolean isLocked()
9953 {
9954 return _subclassLock.isLocked();
9955 }
9956
9957 public void stopWarnUserTakeBreak()
9958 {
9959 if (_taskWarnUserTakeBreak != null)
9960 {
9961 _taskWarnUserTakeBreak.cancel(true);
9962 _taskWarnUserTakeBreak = null;
9963 }
9964 }
9965
9966 public void startWarnUserTakeBreak()
9967 {
9968 if (_taskWarnUserTakeBreak == null)
9969 {
9970 _taskWarnUserTakeBreak = ThreadPool.scheduleAtFixedRate(new WarnUserTakeBreakTask(this), 3600000, 3600000);
9971 }
9972 }
9973
9974 public void stopRentPet()
9975 {
9976 if (_taskRentPet != null)
9977 {
9978 // if the rent of a wyvern expires while over a flying zone, tp to down before unmounting
9979 if (checkLandingState() && (getMountType() == MountType.WYVERN))
9980 {
9981 teleToLocation(TeleportWhereType.TOWN);
9982 }
9983
9984 if (dismount()) // this should always be true now, since we teleported already
9985 {
9986 _taskRentPet.cancel(true);
9987 _taskRentPet = null;
9988 }
9989 }
9990 }
9991
9992 public void startRentPet(int seconds)
9993 {
9994 if (_taskRentPet == null)
9995 {
9996 _taskRentPet = ThreadPool.scheduleAtFixedRate(new RentPetTask(this), seconds * 1000L, seconds * 1000L);
9997 }
9998 }
9999
10000 public boolean isRentedPet()
10001 {
10002 if (_taskRentPet != null)
10003 {
10004 return true;
10005 }
10006
10007 return false;
10008 }
10009
10010 public void stopWaterTask()
10011 {
10012 if (_taskWater != null)
10013 {
10014 _taskWater.cancel(false);
10015 _taskWater = null;
10016 sendPacket(new SetupGauge(getObjectId(), 2, 0));
10017 }
10018 }
10019
10020 public void startWaterTask()
10021 {
10022 if (!isDead() && (_taskWater == null))
10023 {
10024 final int timeinwater = (int) getStat().getValue(Stats.BREATH, 60000);
10025
10026 sendPacket(new SetupGauge(getObjectId(), 2, timeinwater));
10027 _taskWater = ThreadPool.scheduleAtFixedRate(new WaterTask(this), timeinwater, 1000);
10028 }
10029 }
10030
10031 public boolean isInWater()
10032 {
10033 if (_taskWater != null)
10034 {
10035 return true;
10036 }
10037
10038 return false;
10039 }
10040
10041 public void checkWaterState()
10042 {
10043 if (isInsideZone(ZoneId.WATER))
10044 {
10045 startWaterTask();
10046 }
10047 else
10048 {
10049 stopWaterTask();
10050 }
10051 }
10052
10053 public void onPlayerEnter()
10054 {
10055 startWarnUserTakeBreak();
10056
10057 if (isGM())
10058 {
10059 if (isInvul())
10060 {
10061 sendMessage("Entering world in Invulnerable mode.");
10062 }
10063 if (isInvisible())
10064 {
10065 sendMessage("Entering world in Invisible mode.");
10066 }
10067 if (isSilenceMode())
10068 {
10069 sendMessage("Entering world in Silence mode.");
10070 }
10071 }
10072
10073 // Buff and status icons
10074 if (Config.STORE_SKILL_COOLTIME)
10075 {
10076 restoreEffects();
10077 }
10078
10079 // TODO : Need to fix that hack!
10080 if (!isDead())
10081 {
10082 setCurrentCp(_originalCp);
10083 setCurrentHp(_originalHp);
10084 setCurrentMp(_originalMp);
10085 }
10086
10087 revalidateZone(true);
10088
10089 notifyFriends(L2FriendStatus.MODE_ONLINE);
10090 if (!canOverrideCond(PcCondOverride.SKILL_CONDITIONS) && Config.DECREASE_SKILL_LEVEL)
10091 {
10092 checkPlayerSkills();
10093 }
10094
10095 try
10096 {
10097 final SayuneRequest sayune = getRequest(SayuneRequest.class);
10098 if (sayune != null)
10099 {
10100 sayune.onLogout();
10101 }
10102 }
10103 catch (Exception e)
10104 {
10105 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10106 }
10107
10108 try
10109 {
10110 for (L2ZoneType zone : ZoneManager.getInstance().getZones(this))
10111 {
10112 zone.onPlayerLoginInside(this);
10113 }
10114 }
10115 catch (Exception e)
10116 {
10117 LOGGER.log(Level.SEVERE, "", e);
10118 }
10119
10120 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerLogin(this), this);
10121
10122 if (isMentee())
10123 {
10124 // Notify to scripts
10125 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMenteeStatus(this, true), this);
10126 }
10127 else if (isMentor())
10128 {
10129 // Notify to scripts
10130 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMentorStatus(this, true), this);
10131 }
10132 }
10133
10134 public long getLastAccess()
10135 {
10136 return _lastAccess;
10137 }
10138
10139 protected void setLastAccess(long lastAccess)
10140 {
10141 _lastAccess = lastAccess;
10142 }
10143
10144 @Override
10145 public void doRevive()
10146 {
10147 super.doRevive();
10148 sendPacket(new EtcStatusUpdate(this));
10149 _revivePet = false;
10150 _reviveRequested = 0;
10151 _revivePower = 0;
10152
10153 if (isMounted())
10154 {
10155 startFeed(_mountNpcId);
10156 }
10157
10158 // Notify instance
10159 final Instance instance = getInstanceWorld();
10160 if (instance != null)
10161 {
10162 instance.doRevive(this);
10163 }
10164 }
10165
10166 @Override
10167 public void doRevive(double revivePower)
10168 {
10169 doRevive();
10170 restoreExp(revivePower);
10171 }
10172
10173 public void reviveRequest(L2PcInstance reviver, Skill skill, boolean Pet, int power)
10174 {
10175 if (isResurrectionBlocked())
10176 {
10177 return;
10178 }
10179
10180 if (_reviveRequested == 1)
10181 {
10182 if (_revivePet == Pet)
10183 {
10184 reviver.sendPacket(SystemMessageId.RESURRECTION_HAS_ALREADY_BEEN_PROPOSED); // Resurrection is already been proposed.
10185 }
10186 else if (Pet)
10187 {
10188 reviver.sendPacket(SystemMessageId.A_PET_CANNOT_BE_RESURRECTED_WHILE_IT_S_OWNER_IS_IN_THE_PROCESS_OF_RESURRECTING); // A pet cannot be resurrected while it's owner is in the process of resurrecting.
10189 }
10190 else
10191 {
10192 reviver.sendPacket(SystemMessageId.WHILE_A_PET_IS_BEING_RESURRECTED_IT_CANNOT_HELP_IN_RESURRECTING_ITS_MASTER); // While a pet is attempting to resurrect, it cannot help in resurrecting its master.
10193 }
10194 return;
10195 }
10196 final L2Summon pet = getPet();
10197 if ((Pet && (pet != null) && pet.isDead()) || (!Pet && isDead()))
10198 {
10199 _reviveRequested = 1;
10200 _revivePower = Formulas.calculateSkillResurrectRestorePercent(power, reviver);
10201 _revivePet = Pet;
10202
10203 if (hasCharmOfCourage())
10204 {
10205 final ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.YOUR_CHARM_OF_COURAGE_IS_TRYING_TO_RESURRECT_YOU_WOULD_YOU_LIKE_TO_RESURRECT_NOW.getId());
10206 dlg.addTime(60000);
10207 sendPacket(dlg);
10208 return;
10209 }
10210
10211 final long restoreExp = Math.round(((getExpBeforeDeath() - getExp()) * _revivePower) / 100);
10212 final ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.C1_IS_ATTEMPTING_TO_DO_A_RESURRECTION_THAT_RESTORES_S2_S3_XP_ACCEPT.getId());
10213 dlg.addPcName(reviver);
10214 dlg.addLong(restoreExp);
10215 dlg.addInt(power);
10216 sendPacket(dlg);
10217 }
10218 }
10219
10220 public void reviveAnswer(int answer)
10221 {
10222 final L2Summon pet = getPet();
10223 if ((_reviveRequested != 1) || (!isDead() && !_revivePet) || (_revivePet && (pet != null) && !pet.isDead()))
10224 {
10225 return;
10226 }
10227
10228 if (answer == 1)
10229 {
10230 if (!_revivePet)
10231 {
10232 if (_revivePower != 0)
10233 {
10234 doRevive(_revivePower);
10235 }
10236 else
10237 {
10238 doRevive();
10239 }
10240 }
10241 else if (pet != null)
10242 {
10243 if (_revivePower != 0)
10244 {
10245 pet.doRevive(_revivePower);
10246 }
10247 else
10248 {
10249 pet.doRevive();
10250 }
10251 }
10252 }
10253 _reviveRequested = 0;
10254 _revivePower = 0;
10255 }
10256
10257 public boolean isReviveRequested()
10258 {
10259 return (_reviveRequested == 1);
10260 }
10261
10262 public boolean isRevivingPet()
10263 {
10264 return _revivePet;
10265 }
10266
10267 public void removeReviving()
10268 {
10269 _reviveRequested = 0;
10270 _revivePower = 0;
10271 }
10272
10273 public void onActionRequest()
10274 {
10275 if (isSpawnProtected())
10276 {
10277 setSpawnProtection(false);
10278 if (!isInsideZone(ZoneId.PEACE))
10279 {
10280 sendPacket(SystemMessageId.YOU_ARE_NO_LONGER_PROTECTED_FROM_AGGRESSIVE_MONSTERS);
10281 }
10282 if (Config.RESTORE_SERVITOR_ON_RECONNECT && !hasSummon() && CharSummonTable.getInstance().getServitors().containsKey(getObjectId()))
10283 {
10284 CharSummonTable.getInstance().restoreServitor(this);
10285 }
10286 if (Config.RESTORE_PET_ON_RECONNECT && !hasSummon() && CharSummonTable.getInstance().getPets().containsKey(getObjectId()))
10287 {
10288 CharSummonTable.getInstance().restorePet(this);
10289 }
10290 }
10291 if (isTeleportProtected())
10292 {
10293 setTeleportProtection(false);
10294 if (!isInsideZone(ZoneId.PEACE))
10295 {
10296 sendMessage("Teleport spawn protection ended.");
10297 }
10298 }
10299 }
10300
10301 /**
10302 * Expertise of the L2PcInstance (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7, R=8, R95=9, R99=10)
10303 * @return CrystalTyperepresenting expertise level..
10304 */
10305 public CrystalType getExpertiseLevel()
10306 {
10307 return _expertiseLevel;
10308 }
10309
10310 public void setExpertiseLevel(CrystalType crystalType)
10311 {
10312 _expertiseLevel = crystalType != null ? crystalType : CrystalType.NONE;
10313 }
10314
10315 @Override
10316 public void teleToLocation(ILocational loc, boolean allowRandomOffset)
10317 {
10318 if ((getVehicle() != null) && !getVehicle().isTeleporting())
10319 {
10320 setVehicle(null);
10321 }
10322
10323 if (isFlyingMounted() && (loc.getZ() < -1005))
10324 {
10325 super.teleToLocation(loc.getX(), loc.getY(), -1005, loc.getHeading());
10326 }
10327 super.teleToLocation(loc, allowRandomOffset);
10328 }
10329
10330 @Override
10331 public final void onTeleported()
10332 {
10333 super.onTeleported();
10334
10335 if (isInAirShip())
10336 {
10337 getAirShip().sendInfo(this);
10338 }
10339 else // Update last player position upon teleport.
10340 {
10341 setLastServerPosition(getX(), getY(), getZ());
10342 }
10343
10344 // Force a revalidation
10345 revalidateZone(true);
10346
10347 checkItemRestriction();
10348
10349 if ((Config.PLAYER_TELEPORT_PROTECTION > 0) && !isInOlympiadMode())
10350 {
10351 setTeleportProtection(true);
10352 }
10353
10354 // Trained beast is lost after teleport
10355 if (getTrainedBeasts() != null)
10356 {
10357 for (L2TamedBeastInstance tamedBeast : getTrainedBeasts())
10358 {
10359 tamedBeast.deleteMe();
10360 }
10361 getTrainedBeasts().clear();
10362 }
10363
10364 // Modify the position of the pet if necessary
10365 final L2Summon pet = getPet();
10366 if (pet != null)
10367 {
10368 pet.setFollowStatus(false);
10369 pet.teleToLocation(getLocation(), false);
10370 ((L2SummonAI) pet.getAI()).setStartFollowController(true);
10371 pet.setFollowStatus(true);
10372 pet.setInstance(getInstanceWorld());
10373 pet.updateAndBroadcastStatus(0);
10374 }
10375
10376 getServitors().values().forEach(s ->
10377 {
10378 s.setFollowStatus(false);
10379 s.teleToLocation(getLocation(), false);
10380 ((L2SummonAI) s.getAI()).setStartFollowController(true);
10381 s.setFollowStatus(true);
10382 s.setInstance(getInstanceWorld());
10383 s.updateAndBroadcastStatus(0);
10384 });
10385
10386 // show movie if available
10387 if (_movieHolder != null)
10388 {
10389 sendPacket(new ExStartScenePlayer(_movieHolder.getMovie()));
10390 }
10391 }
10392
10393 @Override
10394 public void setIsTeleporting(boolean teleport)
10395 {
10396 setIsTeleporting(teleport, true);
10397 }
10398
10399 public void setIsTeleporting(boolean teleport, boolean useWatchDog)
10400 {
10401 super.setIsTeleporting(teleport);
10402 if (!useWatchDog)
10403 {
10404 return;
10405 }
10406 if (teleport)
10407 {
10408 if ((_teleportWatchdog == null) && (Config.TELEPORT_WATCHDOG_TIMEOUT > 0))
10409 {
10410 synchronized (this)
10411 {
10412 if (_teleportWatchdog == null)
10413 {
10414 _teleportWatchdog = ThreadPool.schedule(new TeleportWatchdogTask(this), Config.TELEPORT_WATCHDOG_TIMEOUT * 1000);
10415 }
10416 }
10417 }
10418 }
10419 else if (_teleportWatchdog != null)
10420 {
10421 _teleportWatchdog.cancel(false);
10422 _teleportWatchdog = null;
10423 }
10424 }
10425
10426 public void setLastServerPosition(int x, int y, int z)
10427 {
10428 _lastServerPosition.setXYZ(x, y, z);
10429 }
10430
10431 public Location getLastServerPosition()
10432 {
10433 return _lastServerPosition;
10434 }
10435
10436 @Override
10437 public void addExpAndSp(double addToExp, double addToSp)
10438 {
10439 getStat().addExpAndSp(addToExp, addToSp, false);
10440 }
10441
10442 public void addExpAndSp(double addToExp, double addToSp, boolean useVitality)
10443 {
10444 getStat().addExpAndSp(addToExp, addToSp, useVitality);
10445 }
10446
10447 public void removeExpAndSp(long removeExp, long removeSp)
10448 {
10449 getStat().removeExpAndSp(removeExp, removeSp, true);
10450 }
10451
10452 public void removeExpAndSp(long removeExp, long removeSp, boolean sendMessage)
10453 {
10454 getStat().removeExpAndSp(removeExp, removeSp, sendMessage);
10455 }
10456
10457 @Override
10458 public void reduceCurrentHp(double value, L2Character attacker, Skill skill, boolean isDOT, boolean directlyToHp, boolean critical, boolean reflect)
10459 {
10460 super.reduceCurrentHp(value, attacker, skill, isDOT, directlyToHp, critical, reflect);
10461
10462 // notify the tamed beast of attacks
10463 if (getTrainedBeasts() != null)
10464 {
10465 for (L2TamedBeastInstance tamedBeast : getTrainedBeasts())
10466 {
10467 tamedBeast.onOwnerGotAttacked(attacker);
10468 }
10469 }
10470 }
10471
10472 public void broadcastSnoop(ChatType type, String name, String _text)
10473 {
10474 if (!_snoopListener.isEmpty())
10475 {
10476 final Snoop sn = new Snoop(getObjectId(), getName(), type, name, _text);
10477
10478 for (L2PcInstance pci : _snoopListener)
10479 {
10480 if (pci != null)
10481 {
10482 pci.sendPacket(sn);
10483 }
10484 }
10485 }
10486 }
10487
10488 public void addSnooper(L2PcInstance pci)
10489 {
10490 if (!_snoopListener.contains(pci))
10491 {
10492 _snoopListener.add(pci);
10493 }
10494 }
10495
10496 public void removeSnooper(L2PcInstance pci)
10497 {
10498 _snoopListener.remove(pci);
10499 }
10500
10501 public void addSnooped(L2PcInstance pci)
10502 {
10503 if (!_snoopedPlayer.contains(pci))
10504 {
10505 _snoopedPlayer.add(pci);
10506 }
10507 }
10508
10509 public void removeSnooped(L2PcInstance pci)
10510 {
10511 _snoopedPlayer.remove(pci);
10512 }
10513
10514 public void addHtmlAction(HtmlActionScope scope, String action)
10515 {
10516 _htmlActionCaches[scope.ordinal()].add(action);
10517 }
10518
10519 public void clearHtmlActions(HtmlActionScope scope)
10520 {
10521 _htmlActionCaches[scope.ordinal()].clear();
10522 }
10523
10524 public void setHtmlActionOriginObjectId(HtmlActionScope scope, int npcObjId)
10525 {
10526 if (npcObjId < 0)
10527 {
10528 throw new IllegalArgumentException();
10529 }
10530
10531 _htmlActionOriginObjectIds[scope.ordinal()] = npcObjId;
10532 }
10533
10534 public int getLastHtmlActionOriginId()
10535 {
10536 return _lastHtmlActionOriginObjId;
10537 }
10538
10539 private boolean validateHtmlAction(Iterable<String> actionIter, String action)
10540 {
10541 for (String cachedAction : actionIter)
10542 {
10543 if (cachedAction.charAt(cachedAction.length() - 1) == AbstractHtmlPacket.VAR_PARAM_START_CHAR)
10544 {
10545 if (action.startsWith(cachedAction.substring(0, cachedAction.length() - 1).trim()))
10546 {
10547 return true;
10548 }
10549 }
10550 else if (cachedAction.equals(action))
10551 {
10552 return true;
10553 }
10554 }
10555
10556 return false;
10557 }
10558
10559 /**
10560 * Check if the HTML action was sent in a HTML packet.<br>
10561 * If the HTML action was not sent for whatever reason, -1 is returned.<br>
10562 * Otherwise, the NPC object ID or 0 is returned.<br>
10563 * 0 means the HTML action was not bound to an NPC<br>
10564 * and no range checks need to be made.
10565 * @param action the HTML action to check
10566 * @return NPC object ID, 0 or -1
10567 */
10568 public int validateHtmlAction(String action)
10569 {
10570 for (int i = 0; i < _htmlActionCaches.length; ++i)
10571 {
10572 if (validateHtmlAction(_htmlActionCaches[i], action))
10573 {
10574 _lastHtmlActionOriginObjId = _htmlActionOriginObjectIds[i];
10575 return _lastHtmlActionOriginObjId;
10576 }
10577 }
10578
10579 return -1;
10580 }
10581
10582 /**
10583 * Performs following tests:
10584 * <ul>
10585 * <li>Inventory contains item</li>
10586 * <li>Item owner id == owner id</li>
10587 * <li>It isnt pet control item while mounting pet or pet summoned</li>
10588 * <li>It isnt active enchant item</li>
10589 * <li>It isnt cursed weapon/item</li>
10590 * <li>It isnt wear item</li>
10591 * </ul>
10592 * @param objectId item object id
10593 * @param action just for login porpouse
10594 * @return
10595 */
10596 public boolean validateItemManipulation(int objectId, String action)
10597 {
10598 final L2ItemInstance item = getInventory().getItemByObjectId(objectId);
10599
10600 if ((item == null) || (item.getOwnerId() != getObjectId()))
10601 {
10602 LOGGER.finest(getObjectId() + ": player tried to " + action + " item he is not owner of");
10603 return false;
10604 }
10605
10606 // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting
10607 final L2Summon pet = getPet();
10608 if (((pet != null) && (pet.getControlObjectId() == objectId)) || (getMountObjectID() == objectId))
10609 {
10610 return false;
10611 }
10612
10613 if (isProcessingItem(objectId))
10614 {
10615 return false;
10616 }
10617
10618 if (CursedWeaponsManager.getInstance().isCursed(item.getId()))
10619 {
10620 // can not trade a cursed weapon
10621 return false;
10622 }
10623
10624 return true;
10625 }
10626
10627 /**
10628 * @return Returns the inBoat.
10629 */
10630 public boolean isInBoat()
10631 {
10632 return (_vehicle != null) && _vehicle.isBoat();
10633 }
10634
10635 /**
10636 * @return
10637 */
10638 public L2BoatInstance getBoat()
10639 {
10640 return (L2BoatInstance) _vehicle;
10641 }
10642
10643 /**
10644 * @return Returns the inAirShip.
10645 */
10646 public boolean isInAirShip()
10647 {
10648 return (_vehicle != null) && _vehicle.isAirShip();
10649 }
10650
10651 /**
10652 * @return
10653 */
10654 public L2AirShipInstance getAirShip()
10655 {
10656 return (L2AirShipInstance) _vehicle;
10657 }
10658
10659 public boolean isInShuttle()
10660 {
10661 return _vehicle instanceof L2ShuttleInstance;
10662 }
10663
10664 public L2ShuttleInstance getShuttle()
10665 {
10666 return (L2ShuttleInstance) _vehicle;
10667 }
10668
10669 public L2Vehicle getVehicle()
10670 {
10671 return _vehicle;
10672 }
10673
10674 public void setVehicle(L2Vehicle v)
10675 {
10676 if ((v == null) && (_vehicle != null))
10677 {
10678 _vehicle.removePassenger(this);
10679 }
10680
10681 _vehicle = v;
10682 }
10683
10684 public boolean isInVehicle()
10685 {
10686 return _vehicle != null;
10687 }
10688
10689 public void setInCrystallize(boolean inCrystallize)
10690 {
10691 _inCrystallize = inCrystallize;
10692 }
10693
10694 public boolean isInCrystallize()
10695 {
10696 return _inCrystallize;
10697 }
10698
10699 /**
10700 * @return
10701 */
10702 public Location getInVehiclePosition()
10703 {
10704 return _inVehiclePosition;
10705 }
10706
10707 public void setInVehiclePosition(Location pt)
10708 {
10709 _inVehiclePosition = pt;
10710 }
10711
10712 /**
10713 * Manage the delete task of a L2PcInstance (Leave Party, Unsummon pet, Save its inventory in the database, Remove it from the world...).<br>
10714 * <B><U>Actions</U>:</B>
10715 * <ul>
10716 * <li>If the L2PcInstance is in observer mode, set its position to its position before entering in observer mode</li>
10717 * <li>Set the online Flag to True or False and update the characters table of the database with online status and lastAccess</li>
10718 * <li>Stop the HP/MP/CP Regeneration task</li>
10719 * <li>Cancel Crafting, Attack or Cast</li>
10720 * <li>Remove the L2PcInstance from the world</li>
10721 * <li>Stop Party and Unsummon Pet</li>
10722 * <li>Update database with items in its inventory and remove them from the world</li>
10723 * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI</li>
10724 * <li>Close the connection with the client</li>
10725 * </ul>
10726 * <br>
10727 * Remember this method is not to be used to half-ass disconnect players! This method is dedicated only to erase the player from the world.<br>
10728 * If you intend to disconnect a player please use {@link Disconnection}
10729 */
10730 @Override
10731 public boolean deleteMe()
10732 {
10733 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerLogout(this), this);
10734
10735 try
10736 {
10737 for (L2ZoneType zone : ZoneManager.getInstance().getZones(this))
10738 {
10739 zone.onPlayerLogoutInside(this);
10740 }
10741 }
10742 catch (Exception e)
10743 {
10744 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10745 }
10746
10747 // 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)
10748 try
10749 {
10750 if (!isOnline())
10751 {
10752 LOGGER.log(Level.SEVERE, "deleteMe() called on offline character " + this, new RuntimeException());
10753 }
10754 setOnlineStatus(false, true);
10755 }
10756 catch (Exception e)
10757 {
10758 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10759 }
10760
10761 try
10762 {
10763 if (Config.ENABLE_BLOCK_CHECKER_EVENT && (getBlockCheckerArena() != -1))
10764 {
10765 HandysBlockCheckerManager.getInstance().onDisconnect(this);
10766 }
10767 }
10768 catch (Exception e)
10769 {
10770 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10771 }
10772
10773 try
10774 {
10775 _isOnline = false;
10776 abortAttack();
10777 abortCast();
10778 stopMove(null);
10779 }
10780 catch (Exception e)
10781 {
10782 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10783 }
10784
10785 // remove combat flag
10786 try
10787 {
10788 if (getInventory().getItemByItemId(9819) != null)
10789 {
10790 final Fort fort = FortManager.getInstance().getFort(this);
10791 if (fort != null)
10792 {
10793 FortSiegeManager.getInstance().dropCombatFlag(this, fort.getResidenceId());
10794 }
10795 else
10796 {
10797 final int slot = getInventory().getSlotFromItem(getInventory().getItemByItemId(9819));
10798 getInventory().unEquipItemInBodySlot(slot);
10799 destroyItem("CombatFlag", getInventory().getItemByItemId(9819), null, true);
10800 }
10801 }
10802 }
10803 catch (Exception e)
10804 {
10805 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10806 }
10807
10808 try
10809 {
10810 if (_matchingRoom != null)
10811 {
10812 _matchingRoom.deleteMember(this, false);
10813 }
10814 MatchingRoomManager.getInstance().removeFromWaitingList(this);
10815 }
10816 catch (Exception e)
10817 {
10818 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10819 }
10820
10821 try
10822 {
10823 if (isFlying())
10824 {
10825 removeSkill(SkillData.getInstance().getSkill(CommonSkill.WYVERN_BREATH.getId(), 1));
10826 }
10827 }
10828 catch (Exception e)
10829 {
10830 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10831 }
10832
10833 // Recommendations must be saved before task (timer) is canceled
10834 try
10835 {
10836 storeRecommendations();
10837 }
10838 catch (Exception e)
10839 {
10840 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10841 }
10842 // Stop the HP/MP/CP Regeneration task (scheduled tasks)
10843 try
10844 {
10845 stopAllTimers();
10846 }
10847 catch (Exception e)
10848 {
10849 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10850 }
10851
10852 try
10853 {
10854 setIsTeleporting(false);
10855 }
10856 catch (Exception e)
10857 {
10858 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10859 }
10860
10861 // Cancel Attak or Cast
10862 try
10863 {
10864 setTarget(null);
10865 }
10866 catch (Exception e)
10867 {
10868 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10869 }
10870
10871 if (isChannelized())
10872 {
10873 getSkillChannelized().abortChannelization();
10874 }
10875
10876 // Stop all toggles.
10877 getEffectList().stopAllToggles();
10878
10879 // Remove from world regions zones
10880 ZoneManager.getInstance().getRegion(this).removeFromZones(this);
10881
10882 // Remove the L2PcInstance from the world
10883 try
10884 {
10885 decayMe();
10886 }
10887 catch (Exception e)
10888 {
10889 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10890 }
10891
10892 // If a Party is in progress, leave it (and festival party)
10893 if (isInParty())
10894 {
10895 try
10896 {
10897 leaveParty();
10898 }
10899 catch (Exception e)
10900 {
10901 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10902 }
10903 }
10904
10905 if (OlympiadManager.getInstance().isRegistered(this) || (getOlympiadGameId() != -1))
10906 {
10907 OlympiadManager.getInstance().removeDisconnectedCompetitor(this);
10908 }
10909
10910 // If the L2PcInstance has Pet, unsummon it
10911 if (hasSummon())
10912 {
10913 try
10914 {
10915 L2Summon pet = getPet();
10916 if (pet != null)
10917 {
10918 pet.setRestoreSummon(true);
10919 pet.unSummon(this);
10920 // Dead pet wasn't unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline)
10921 pet = getPet();
10922 if (pet != null)
10923 {
10924 pet.broadcastNpcInfo(0);
10925 }
10926 }
10927
10928 getServitors().values().forEach(s ->
10929 {
10930 s.setRestoreSummon(true);
10931 s.unSummon(this);
10932 });
10933 }
10934 catch (Exception e)
10935 {
10936 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10937 } // returns pet to control item
10938 }
10939
10940 if (getClan() != null)
10941 {
10942 // set the status for pledge member list to OFFLINE
10943 try
10944 {
10945 final L2ClanMember clanMember = getClan().getClanMember(getObjectId());
10946 if (clanMember != null)
10947 {
10948 clanMember.setPlayerInstance(null);
10949 }
10950
10951 }
10952 catch (Exception e)
10953 {
10954 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10955 }
10956 }
10957
10958 if (getActiveRequester() != null)
10959 {
10960 // deals with sudden exit in the middle of transaction
10961 setActiveRequester(null);
10962 cancelActiveTrade();
10963 }
10964
10965 // If the L2PcInstance is a GM, remove it from the GM List
10966 if (isGM())
10967 {
10968 try
10969 {
10970 AdminData.getInstance().deleteGm(this);
10971 }
10972 catch (Exception e)
10973 {
10974 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10975 }
10976 }
10977
10978 try
10979 {
10980 // Check if the L2PcInstance is in observer mode to set its position to its position
10981 // before entering in observer mode
10982 if (inObserverMode())
10983 {
10984 setLocationInvisible(_lastLoc);
10985 }
10986
10987 if (getVehicle() != null)
10988 {
10989 getVehicle().oustPlayer(this);
10990 }
10991 }
10992 catch (Exception e)
10993 {
10994 LOGGER.log(Level.SEVERE, "deleteMe()", e);
10995 }
10996
10997 // remove player from instance
10998 final Instance inst = getInstanceWorld();
10999 if (inst != null)
11000 {
11001 try
11002 {
11003 inst.onPlayerLogout(this);
11004 }
11005 catch (Exception e)
11006 {
11007 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11008 }
11009 }
11010
11011 try
11012 {
11013 stopCubics();
11014 }
11015 catch (Exception e)
11016 {
11017 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11018 }
11019
11020 // Update database with items in its inventory and remove them from the world
11021 try
11022 {
11023 getInventory().deleteMe();
11024 }
11025 catch (Exception e)
11026 {
11027 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11028 }
11029
11030 // Update database with items in its warehouse and remove them from the world
11031 try
11032 {
11033 clearWarehouse();
11034 }
11035 catch (Exception e)
11036 {
11037 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11038 }
11039 if (Config.WAREHOUSE_CACHE)
11040 {
11041 WarehouseCacheManager.getInstance().remCacheTask(this);
11042 }
11043
11044 try
11045 {
11046 getFreight().deleteMe();
11047 }
11048 catch (Exception e)
11049 {
11050 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11051 }
11052
11053 try
11054 {
11055 clearRefund();
11056 }
11057 catch (Exception e)
11058 {
11059 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11060 }
11061
11062 if (isCursedWeaponEquipped())
11063 {
11064 try
11065 {
11066 CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquippedId).setPlayer(null);
11067 }
11068 catch (Exception e)
11069 {
11070 LOGGER.log(Level.SEVERE, "deleteMe()", e);
11071 }
11072 }
11073
11074 if (getClanId() > 0)
11075 {
11076 getClan().broadcastToOtherOnlineMembers(new PledgeShowMemberListUpdate(this), this);
11077 getClan().broadcastToOnlineMembers(new ExPledgeCount(getClan()));
11078 // ClanTable.getInstance().getClan(getClanId()).broadcastToOnlineMembers(new PledgeShowMemberListAdd(this));
11079 }
11080
11081 for (L2PcInstance player : _snoopedPlayer)
11082 {
11083 player.removeSnooper(this);
11084 }
11085
11086 for (L2PcInstance player : _snoopListener)
11087 {
11088 player.removeSnooped(this);
11089 }
11090
11091 if (isMentee())
11092 {
11093 // Notify to scripts
11094 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMenteeStatus(this, false), this);
11095 }
11096 else if (isMentor())
11097 {
11098 // Notify to scripts
11099 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMentorStatus(this, false), this);
11100 }
11101
11102 // we store all data from players who are disconnected while in an event in order to restore it in the next login
11103 if (L2Event.isParticipant(this))
11104 {
11105 L2Event.savePlayerEventStatus(this);
11106 }
11107
11108 try
11109 {
11110 notifyFriends(L2FriendStatus.MODE_OFFLINE);
11111 getBlockList().playerLogout();
11112 }
11113 catch (Exception e)
11114 {
11115 LOGGER.log(Level.WARNING, "Exception on deleteMe() notifyFriends: " + e.getMessage(), e);
11116 }
11117
11118 // Stop all passives and augment options
11119 getEffectList().stopAllPassives(false, false);
11120 getEffectList().stopAllOptions(false, false);
11121
11122 stopAutoSaveTask();
11123
11124 return super.deleteMe();
11125 }
11126
11127 public int getInventoryLimit()
11128 {
11129 int ivlim;
11130 if (isGM())
11131 {
11132 ivlim = Config.INVENTORY_MAXIMUM_GM;
11133 }
11134 else if (getRace() == Race.DWARF)
11135 {
11136 ivlim = Config.INVENTORY_MAXIMUM_DWARF;
11137 }
11138 else
11139 {
11140 ivlim = Config.INVENTORY_MAXIMUM_NO_DWARF;
11141 }
11142 ivlim += (int) getStat().getValue(Stats.INVENTORY_NORMAL, 0);
11143
11144 return ivlim;
11145 }
11146
11147 public int getWareHouseLimit()
11148 {
11149 int whlim;
11150 if (getRace() == Race.DWARF)
11151 {
11152 whlim = Config.WAREHOUSE_SLOTS_DWARF;
11153 }
11154 else
11155 {
11156 whlim = Config.WAREHOUSE_SLOTS_NO_DWARF;
11157 }
11158
11159 whlim += (int) getStat().getValue(Stats.STORAGE_PRIVATE, 0);
11160
11161 return whlim;
11162 }
11163
11164 public int getPrivateSellStoreLimit()
11165 {
11166 int pslim;
11167
11168 if (getRace() == Race.DWARF)
11169 {
11170 pslim = Config.MAX_PVTSTORESELL_SLOTS_DWARF;
11171 }
11172 else
11173 {
11174 pslim = Config.MAX_PVTSTORESELL_SLOTS_OTHER;
11175 }
11176
11177 pslim += (int) getStat().getValue(Stats.TRADE_SELL, 0);
11178
11179 return pslim;
11180 }
11181
11182 public int getPrivateBuyStoreLimit()
11183 {
11184 int pblim;
11185
11186 if (getRace() == Race.DWARF)
11187 {
11188 pblim = Config.MAX_PVTSTOREBUY_SLOTS_DWARF;
11189 }
11190 else
11191 {
11192 pblim = Config.MAX_PVTSTOREBUY_SLOTS_OTHER;
11193 }
11194 pblim += (int) getStat().getValue(Stats.TRADE_BUY, 0);
11195
11196 return pblim;
11197 }
11198
11199 public int getDwarfRecipeLimit()
11200 {
11201 int recdlim = Config.DWARF_RECIPE_LIMIT;
11202 recdlim += (int) getStat().getValue(Stats.RECIPE_DWARVEN, 0);
11203 return recdlim;
11204 }
11205
11206 public int getCommonRecipeLimit()
11207 {
11208 int recclim = Config.COMMON_RECIPE_LIMIT;
11209 recclim += (int) getStat().getValue(Stats.RECIPE_COMMON, 0);
11210 return recclim;
11211 }
11212
11213 /**
11214 * @return Returns the mountNpcId.
11215 */
11216 public int getMountNpcId()
11217 {
11218 return _mountNpcId;
11219 }
11220
11221 /**
11222 * @return Returns the mountLevel.
11223 */
11224 public int getMountLevel()
11225 {
11226 return _mountLevel;
11227 }
11228
11229 public void setMountObjectID(int newID)
11230 {
11231 _mountObjectID = newID;
11232 }
11233
11234 public int getMountObjectID()
11235 {
11236 return _mountObjectID;
11237 }
11238
11239 public SkillUseHolder getQueuedSkill()
11240 {
11241 return _queuedSkill;
11242 }
11243
11244 /**
11245 * Create a new SkillDat object and queue it in the player _queuedSkill.
11246 * @param queuedSkill
11247 * @param item
11248 * @param ctrlPressed
11249 * @param shiftPressed
11250 */
11251 public void setQueuedSkill(Skill queuedSkill, L2ItemInstance item, boolean ctrlPressed, boolean shiftPressed)
11252 {
11253 if (queuedSkill == null)
11254 {
11255 _queuedSkill = null;
11256 return;
11257 }
11258 _queuedSkill = new SkillUseHolder(queuedSkill, item, ctrlPressed, shiftPressed);
11259 }
11260
11261 public boolean isAlterSkillActive()
11262 {
11263 return _alterSkillActive;
11264 }
11265
11266 public void setAlterSkillActive(boolean alterSkillActive)
11267 {
11268 _alterSkillActive = alterSkillActive;
11269 }
11270
11271 /**
11272 * @return {@code true} if player is jailed, {@code false} otherwise.
11273 */
11274 public boolean isJailed()
11275 {
11276 return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.JAIL) || PunishmentManager.getInstance().hasPunishment(getAccountName(), PunishmentAffect.ACCOUNT, PunishmentType.JAIL) || PunishmentManager.getInstance().hasPunishment(getIPAddress(), PunishmentAffect.IP, PunishmentType.JAIL);
11277 }
11278
11279 /**
11280 * @return {@code true} if player is chat banned, {@code false} otherwise.
11281 */
11282 public boolean isChatBanned()
11283 {
11284 return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.CHAT_BAN) || PunishmentManager.getInstance().hasPunishment(getAccountName(), PunishmentAffect.ACCOUNT, PunishmentType.CHAT_BAN) || PunishmentManager.getInstance().hasPunishment(getIPAddress(), PunishmentAffect.IP, PunishmentType.CHAT_BAN);
11285 }
11286
11287 public void startFameTask(long delay, int fameFixRate)
11288 {
11289 if ((getLevel() < 40) || (getClassId().level() < 2))
11290 {
11291 return;
11292 }
11293 if (_fameTask == null)
11294 {
11295 _fameTask = ThreadPool.scheduleAtFixedRate(new FameTask(this, fameFixRate), delay, delay);
11296 }
11297 }
11298
11299 public void stopFameTask()
11300 {
11301 if (_fameTask != null)
11302 {
11303 _fameTask.cancel(false);
11304 _fameTask = null;
11305 }
11306 }
11307
11308 public int getPowerGrade()
11309 {
11310 return _powerGrade;
11311 }
11312
11313 public void setPowerGrade(int power)
11314 {
11315 _powerGrade = power;
11316 }
11317
11318 public boolean isCursedWeaponEquipped()
11319 {
11320 return _cursedWeaponEquippedId != 0;
11321 }
11322
11323 public void setCursedWeaponEquippedId(int value)
11324 {
11325 _cursedWeaponEquippedId = value;
11326 }
11327
11328 public int getCursedWeaponEquippedId()
11329 {
11330 return _cursedWeaponEquippedId;
11331 }
11332
11333 public boolean isCombatFlagEquipped()
11334 {
11335 return _combatFlagEquippedId;
11336 }
11337
11338 public void setCombatFlagEquipped(boolean value)
11339 {
11340 _combatFlagEquippedId = value;
11341 }
11342
11343 /**
11344 * Returns the Number of Souls this L2PcInstance got.
11345 * @return
11346 */
11347 public int getChargedSouls()
11348 {
11349 return _souls;
11350 }
11351
11352 /**
11353 * Increase Souls
11354 * @param count
11355 */
11356 public void increaseSouls(int count)
11357 {
11358 _souls += count;
11359 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOUR_SOUL_COUNT_HAS_INCREASED_BY_S1_IT_IS_NOW_AT_S2);
11360 sm.addInt(count);
11361 sm.addInt(_souls);
11362 sendPacket(sm);
11363 restartSoulTask();
11364 sendPacket(new EtcStatusUpdate(this));
11365 }
11366
11367 /**
11368 * Decreases existing Souls.
11369 * @param count
11370 * @param skill
11371 * @return
11372 */
11373 public boolean decreaseSouls(int count, Skill skill)
11374 {
11375 _souls -= count;
11376
11377 if (getChargedSouls() < 0)
11378 {
11379 _souls = 0;
11380 }
11381
11382 if (getChargedSouls() == 0)
11383 {
11384 stopSoulTask();
11385 }
11386 else
11387 {
11388 restartSoulTask();
11389 }
11390
11391 sendPacket(new EtcStatusUpdate(this));
11392 return true;
11393 }
11394
11395 /**
11396 * Clear out all Souls from this L2PcInstance
11397 */
11398 public void clearSouls()
11399 {
11400 _souls = 0;
11401 stopSoulTask();
11402 sendPacket(new EtcStatusUpdate(this));
11403 }
11404
11405 /**
11406 * Starts/Restarts the SoulTask to Clear Souls after 10 Mins.
11407 */
11408 private void restartSoulTask()
11409 {
11410 if (_soulTask != null)
11411 {
11412 _soulTask.cancel(false);
11413 _soulTask = null;
11414 }
11415 _soulTask = ThreadPool.schedule(new ResetSoulsTask(this), 600000);
11416
11417 }
11418
11419 /**
11420 * Stops the Clearing Task.
11421 */
11422 public void stopSoulTask()
11423 {
11424 if (_soulTask != null)
11425 {
11426 _soulTask.cancel(false);
11427 _soulTask = null;
11428 }
11429 }
11430
11431 public int getShilensBreathDebuffLevel()
11432 {
11433 final BuffInfo buff = getEffectList().getBuffInfoBySkillId(CommonSkill.SHILENS_BREATH.getId());
11434 return buff == null ? 0 : buff.getSkill().getLevel();
11435 }
11436
11437 public void calculateShilensBreathDebuffLevel(L2Character killer)
11438 {
11439 if (killer == null)
11440 {
11441 LOGGER.warning(this + " called calculateShilensBreathDebuffLevel with killer null!");
11442 return;
11443 }
11444
11445 if (isResurrectSpecialAffected() || isLucky() || isBlockedFromDeathPenalty() || isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.SIEGE) || canOverrideCond(PcCondOverride.DEATH_PENALTY))
11446 {
11447 return;
11448 }
11449 double percent = 1.0;
11450
11451 if (killer.isRaid())
11452 {
11453 percent *= getStat().getValue(Stats.REDUCE_DEATH_PENALTY_BY_RAID, 1);
11454 }
11455 else if (killer.isMonster())
11456 {
11457 percent *= getStat().getValue(Stats.REDUCE_DEATH_PENALTY_BY_MOB, 1);
11458 }
11459 else if (killer.isPlayable())
11460 {
11461 percent *= getStat().getValue(Stats.REDUCE_DEATH_PENALTY_BY_PVP, 1);
11462 }
11463
11464 if ((killer.isNpc() && ((L2Npc) killer).getTemplate().isDeathPenalty()) || (Rnd.get(1, 100) <= ((Config.DEATH_PENALTY_CHANCE) * percent)))
11465 {
11466 if (!killer.isPlayable() || (getReputation() < 0))
11467 {
11468 increaseShilensBreathDebuff();
11469 }
11470 }
11471 }
11472
11473 public void increaseShilensBreathDebuff()
11474 {
11475 int nextLv = getShilensBreathDebuffLevel() + 1;
11476 if (nextLv > 5)
11477 {
11478 nextLv = 5;
11479 }
11480
11481 final Skill skill = SkillData.getInstance().getSkill(CommonSkill.SHILENS_BREATH.getId(), nextLv);
11482 if (skill != null)
11483 {
11484 skill.applyEffects(this, this);
11485 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_VE_BEEN_AFFLICTED_BY_SHILEN_S_BREATH_LEVEL_S1).addInt(nextLv));
11486 }
11487 }
11488
11489 public void decreaseShilensBreathDebuff()
11490 {
11491 final int nextLv = getShilensBreathDebuffLevel() - 1;
11492 if (nextLv > 0)
11493 {
11494 final Skill skill = SkillData.getInstance().getSkill(CommonSkill.SHILENS_BREATH.getId(), nextLv);
11495 skill.applyEffects(this, this);
11496 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_VE_BEEN_AFFLICTED_BY_SHILEN_S_BREATH_LEVEL_S1).addInt(nextLv));
11497 }
11498 else
11499 {
11500 sendPacket(SystemMessageId.SHILEN_S_BREATH_HAS_BEEN_PURIFIED);
11501 }
11502 }
11503
11504 public void setShilensBreathDebuffLevel(int level)
11505 {
11506 if (level > 0)
11507 {
11508 final Skill skill = SkillData.getInstance().getSkill(CommonSkill.SHILENS_BREATH.getId(), level);
11509 skill.applyEffects(this, this);
11510 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_VE_BEEN_AFFLICTED_BY_SHILEN_S_BREATH_LEVEL_S1).addInt(level));
11511 }
11512 }
11513
11514 @Override
11515 public L2PcInstance getActingPlayer()
11516 {
11517 return this;
11518 }
11519
11520 @Override
11521 public void sendDamageMessage(L2Character target, Skill skill, int damage, boolean crit, boolean miss)
11522 {
11523 // Check if hit is missed
11524 if (miss)
11525 {
11526 if (skill == null)
11527 {
11528 if (target.isPlayer())
11529 {
11530 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_EVADED_C2_S_ATTACK);
11531 sm.addPcName(target.getActingPlayer());
11532 sm.addString(getName());
11533 target.sendPacket(sm);
11534 }
11535 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_S_ATTACK_WENT_ASTRAY);
11536 sm.addPcName(this);
11537 sendPacket(sm);
11538 }
11539 else
11540 {
11541 sendPacket(new ExMagicAttackInfo(getObjectId(), target.getObjectId(), ExMagicAttackInfo.EVADED));
11542 }
11543 return;
11544 }
11545
11546 // Check if hit is critical
11547 if (crit)
11548 {
11549 if ((skill == null) || !skill.isMagic())
11550 {
11551 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_LANDED_A_CRITICAL_HIT);
11552 sm.addPcName(this);
11553 sendPacket(sm);
11554 }
11555 else
11556 {
11557 sendPacket(SystemMessageId.M_CRITICAL);
11558 }
11559
11560 if (skill != null)
11561 {
11562 sendPacket(new ExMagicAttackInfo(getObjectId(), target.getObjectId(), ExMagicAttackInfo.CRITICAL));
11563 }
11564 }
11565
11566 if (isInOlympiadMode() && target.isPlayer() && target.getActingPlayer().isInOlympiadMode() && (target.getActingPlayer().getOlympiadGameId() == getOlympiadGameId()))
11567 {
11568 OlympiadGameManager.getInstance().notifyCompetitorDamage(this, damage);
11569 }
11570
11571 final SystemMessage sm;
11572
11573 if ((target.isHpBlocked() && !target.isNpc()) || (target.isPlayer() && target.isAffected(EffectFlag.FACEOFF) && (target.getActingPlayer().getAttackerObjId() != getObjectId())))
11574 {
11575 sm = SystemMessage.getSystemMessage(SystemMessageId.THE_ATTACK_HAS_BEEN_BLOCKED);
11576 }
11577 else if (target.isDoor() || (target instanceof L2ControlTowerInstance))
11578 {
11579 sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HIT_FOR_S1_DAMAGE);
11580 sm.addInt(damage);
11581 }
11582 else
11583 {
11584 sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_INFLICTED_S3_DAMAGE_ON_C2);
11585 sm.addPcName(this);
11586 sm.addString(target.getName());
11587 sm.addInt(damage);
11588 sm.addPopup(target.getObjectId(), getObjectId(), -damage);
11589 }
11590 sendPacket(sm);
11591 }
11592
11593 /**
11594 * @param npcId
11595 */
11596 public void setAgathionId(int npcId)
11597 {
11598 _agathionId = npcId;
11599 }
11600
11601 /**
11602 * @return
11603 */
11604 public int getAgathionId()
11605 {
11606 return _agathionId;
11607 }
11608
11609 public int getVitalityPoints()
11610 {
11611 return getStat().getVitalityPoints();
11612 }
11613
11614 public void setVitalityPoints(int points, boolean quiet)
11615 {
11616 getStat().setVitalityPoints(points, quiet);
11617 }
11618
11619 public void updateVitalityPoints(int points, boolean useRates, boolean quiet)
11620 {
11621 getStat().updateVitalityPoints(points, useRates, quiet);
11622 }
11623
11624 public void checkItemRestriction()
11625 {
11626 for (int i = 0; i < Inventory.PAPERDOLL_TOTALSLOTS; i++)
11627 {
11628 final L2ItemInstance equippedItem = getInventory().getPaperdollItem(i);
11629 if ((equippedItem != null) && !equippedItem.getItem().checkCondition(this, this, false))
11630 {
11631 getInventory().unEquipItemInSlot(i);
11632
11633 final InventoryUpdate iu = new InventoryUpdate();
11634 iu.addModifiedItem(equippedItem);
11635 sendInventoryUpdate(iu);
11636
11637 SystemMessage sm = null;
11638 if (equippedItem.getItem().getBodyPart() == L2Item.SLOT_BACK)
11639 {
11640 sendPacket(SystemMessageId.YOUR_CLOAK_HAS_BEEN_UNEQUIPPED_BECAUSE_YOUR_ARMOR_SET_IS_NO_LONGER_COMPLETE);
11641 return;
11642 }
11643
11644 if (equippedItem.getEnchantLevel() > 0)
11645 {
11646 sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED);
11647 sm.addInt(equippedItem.getEnchantLevel());
11648 sm.addItemName(equippedItem);
11649 }
11650 else
11651 {
11652 sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED);
11653 sm.addItemName(equippedItem);
11654 }
11655 sendPacket(sm);
11656 }
11657 }
11658 }
11659
11660 public void addTransformSkill(Skill skill)
11661 {
11662 if (_transformSkills == null)
11663 {
11664 synchronized (this)
11665 {
11666 if (_transformSkills == null)
11667 {
11668 _transformSkills = new HashMap<>();
11669 }
11670 }
11671 }
11672 _transformSkills.put(skill.getId(), skill);
11673 }
11674
11675 public boolean hasTransformSkill(Skill skill)
11676 {
11677 if (checkTransformed(Transform::allowAllSkills))
11678 {
11679 return true;
11680 }
11681
11682 return (_transformSkills != null) && (_transformSkills.get(skill.getId()) == skill);
11683 }
11684
11685 public boolean hasTransformSkills()
11686 {
11687 return (_transformSkills != null);
11688 }
11689
11690 public Collection<Skill> getAllTransformSkills()
11691 {
11692 final Map<Integer, Skill> transformSkills = _transformSkills;
11693 return transformSkills != null ? transformSkills.values() : Collections.emptyList();
11694 }
11695
11696 public synchronized void removeAllTransformSkills()
11697 {
11698 _transformSkills = null;
11699 }
11700
11701 /**
11702 * @param skillId the id of the skill that this player might have.
11703 * @return {@code skill} object refered to this skill id that this player has, {@code null} otherwise.
11704 */
11705 @Override
11706 public final Skill getKnownSkill(int skillId)
11707 {
11708 final Map<Integer, Skill> transformSkills = _transformSkills;
11709 return transformSkills != null ? transformSkills.getOrDefault(skillId, super.getKnownSkill(skillId)) : super.getKnownSkill(skillId);
11710 }
11711
11712 /**
11713 * @return all visible skills that appear on Alt+K for this player.
11714 */
11715 public Collection<Skill> getSkillList()
11716 {
11717 Collection<Skill> currentSkills = getAllSkills();
11718
11719 if (isTransformed())
11720 {
11721 final Map<Integer, Skill> transformSkills = _transformSkills;
11722 if (transformSkills != null)
11723 {
11724 if (!checkTransformed(Transform::allowAllSkills))
11725 {
11726 // Include transformation skills and those skills that are allowed during transformation.
11727 currentSkills = currentSkills.stream().filter(Skill::allowOnTransform).collect(Collectors.toList());
11728
11729 // Revelation skills.
11730 if (isDualClassActive())
11731 {
11732 int revelationSkill = getVariables().getInt(PlayerVariables.REVELATION_SKILL_1_DUAL_CLASS, 0);
11733 if (revelationSkill != 0)
11734 {
11735 addSkill(SkillData.getInstance().getSkill(revelationSkill, 1), false);
11736 }
11737 revelationSkill = getVariables().getInt(PlayerVariables.REVELATION_SKILL_2_DUAL_CLASS, 0);
11738 if (revelationSkill != 0)
11739 {
11740 addSkill(SkillData.getInstance().getSkill(revelationSkill, 1), false);
11741 }
11742 }
11743 else if (!isSubClassActive())
11744 {
11745 int revelationSkill = getVariables().getInt(PlayerVariables.REVELATION_SKILL_1_MAIN_CLASS, 0);
11746 if (revelationSkill != 0)
11747 {
11748 addSkill(SkillData.getInstance().getSkill(revelationSkill, 1), false);
11749 }
11750 revelationSkill = getVariables().getInt(PlayerVariables.REVELATION_SKILL_2_MAIN_CLASS, 0);
11751 if (revelationSkill != 0)
11752 {
11753 addSkill(SkillData.getInstance().getSkill(revelationSkill, 1), false);
11754 }
11755 }
11756 }
11757 // Include transformation skills.
11758 currentSkills.addAll(transformSkills.values());
11759 }
11760 }
11761
11762 //@formatter:off
11763 return currentSkills.stream()
11764 .filter(Objects::nonNull)
11765 .filter(s -> !s.isBlockActionUseSkill()) // Skills that are blocked from player use are not shown in skill list.
11766 .filter(s -> !SkillTreesData.getInstance().isAlchemySkill(s.getId(), s.getLevel()))
11767 .filter(s -> s.isDisplayInList())
11768 .collect(Collectors.toList());
11769 //@formatter:on
11770 }
11771
11772 protected void startFeed(int npcId)
11773 {
11774 _canFeed = npcId > 0;
11775 if (!isMounted())
11776 {
11777 return;
11778 }
11779 if (hasPet())
11780 {
11781 final L2Summon pet = getPet();
11782 setCurrentFeed(((L2PetInstance) pet).getCurrentFed());
11783 _controlItemId = pet.getControlObjectId();
11784 sendPacket(new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume()));
11785 if (!isDead())
11786 {
11787 _mountFeedTask = ThreadPool.scheduleAtFixedRate(new PetFeedTask(this), 10000, 10000);
11788 }
11789 }
11790 else if (_canFeed)
11791 {
11792 setCurrentFeed(getMaxFeed());
11793 final SetupGauge sg = new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume());
11794 sendPacket(sg);
11795 if (!isDead())
11796 {
11797 _mountFeedTask = ThreadPool.scheduleAtFixedRate(new PetFeedTask(this), 10000, 10000);
11798 }
11799 }
11800 }
11801
11802 public void stopFeed()
11803 {
11804 if (_mountFeedTask != null)
11805 {
11806 _mountFeedTask.cancel(false);
11807 _mountFeedTask = null;
11808 }
11809 }
11810
11811 private void clearPetData()
11812 {
11813 _data = null;
11814 }
11815
11816 public final L2PetData getPetData(int npcId)
11817 {
11818 if (_data == null)
11819 {
11820 _data = PetDataTable.getInstance().getPetData(npcId);
11821 }
11822 return _data;
11823 }
11824
11825 private L2PetLevelData getPetLevelData(int npcId)
11826 {
11827 if (_leveldata == null)
11828 {
11829 _leveldata = PetDataTable.getInstance().getPetData(npcId).getPetLevelData(getMountLevel());
11830 }
11831 return _leveldata;
11832 }
11833
11834 public int getCurrentFeed()
11835 {
11836 return _curFeed;
11837 }
11838
11839 public int getFeedConsume()
11840 {
11841 // if pet is attacking
11842 if (isAttackingNow())
11843 {
11844 return getPetLevelData(_mountNpcId).getPetFeedBattle();
11845 }
11846 return getPetLevelData(_mountNpcId).getPetFeedNormal();
11847 }
11848
11849 public void setCurrentFeed(int num)
11850 {
11851 final boolean lastHungryState = isHungry();
11852 _curFeed = num > getMaxFeed() ? getMaxFeed() : num;
11853 final SetupGauge sg = new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume());
11854 sendPacket(sg);
11855 // broadcast move speed change when strider becomes hungry / full
11856 if (lastHungryState != isHungry())
11857 {
11858 broadcastUserInfo();
11859 }
11860 }
11861
11862 private int getMaxFeed()
11863 {
11864 return getPetLevelData(_mountNpcId).getPetMaxFeed();
11865 }
11866
11867 public boolean isHungry()
11868 {
11869 return hasPet() && _canFeed && (getCurrentFeed() < ((getPetData(getMountNpcId()).getHungryLimit() / 100f) * getPetLevelData(getMountNpcId()).getPetMaxFeed()));
11870 }
11871
11872 public void enteredNoLanding(int delay)
11873 {
11874 _dismountTask = ThreadPool.schedule(new DismountTask(this), delay * 1000);
11875 }
11876
11877 public void exitedNoLanding()
11878 {
11879 if (_dismountTask != null)
11880 {
11881 _dismountTask.cancel(true);
11882 _dismountTask = null;
11883 }
11884 }
11885
11886 public void storePetFood(int petId)
11887 {
11888 if ((_controlItemId != 0) && (petId != 0))
11889 {
11890 String req;
11891 req = "UPDATE pets SET fed=? WHERE item_obj_id = ?";
11892 try (Connection con = DatabaseFactory.getInstance().getConnection();
11893 PreparedStatement statement = con.prepareStatement(req))
11894 {
11895 statement.setInt(1, getCurrentFeed());
11896 statement.setInt(2, _controlItemId);
11897 statement.executeUpdate();
11898 _controlItemId = 0;
11899 }
11900 catch (Exception e)
11901 {
11902 LOGGER.log(Level.SEVERE, "Failed to store Pet [NpcId: " + petId + "] data", e);
11903 }
11904 }
11905 }
11906
11907 public void setIsInSiege(boolean b)
11908 {
11909 _isInSiege = b;
11910 }
11911
11912 public boolean isInSiege()
11913 {
11914 return _isInSiege;
11915 }
11916
11917 /**
11918 * @param isInHideoutSiege sets the value of {@link #_isInHideoutSiege}.
11919 */
11920 public void setIsInHideoutSiege(boolean isInHideoutSiege)
11921 {
11922 _isInHideoutSiege = isInHideoutSiege;
11923 }
11924
11925 /**
11926 * @return the value of {@link #_isInHideoutSiege}, {@code true} if the player is participing on a Hideout Siege, otherwise {@code false}.
11927 */
11928 public boolean isInHideoutSiege()
11929 {
11930 return _isInHideoutSiege;
11931 }
11932
11933 public FloodProtectors getFloodProtectors()
11934 {
11935 return getClient().getFloodProtectors();
11936 }
11937
11938 public boolean isFlyingMounted()
11939 {
11940 return checkTransformed(Transform::isFlying);
11941 }
11942
11943 /**
11944 * Returns the Number of Charges this L2PcInstance got.
11945 * @return
11946 */
11947 public int getCharges()
11948 {
11949 return _charges.get();
11950 }
11951
11952 public void setCharges(int count)
11953 {
11954 restartChargeTask();
11955 _charges.set(count);
11956 }
11957
11958 public boolean decreaseCharges(int count)
11959 {
11960 if (_charges.get() < count)
11961 {
11962 return false;
11963 }
11964
11965 // Charge clear task should be reset every time a charge is decreased and stopped when charges become 0.
11966 if (_charges.addAndGet(-count) == 0)
11967 {
11968 stopChargeTask();
11969 }
11970 else
11971 {
11972 restartChargeTask();
11973 }
11974
11975 sendPacket(new EtcStatusUpdate(this));
11976 return true;
11977 }
11978
11979 public void clearCharges()
11980 {
11981 _charges.set(0);
11982 sendPacket(new EtcStatusUpdate(this));
11983 }
11984
11985 /**
11986 * Starts/Restarts the ChargeTask to Clear Charges after 10 Mins.
11987 */
11988 private void restartChargeTask()
11989 {
11990 if (_chargeTask != null)
11991 {
11992 _chargeTask.cancel(false);
11993 _chargeTask = null;
11994 }
11995 _chargeTask = ThreadPool.schedule(new ResetChargesTask(this), 600000);
11996 }
11997
11998 /**
11999 * Stops the Charges Clearing Task.
12000 */
12001 public void stopChargeTask()
12002 {
12003 if (_chargeTask != null)
12004 {
12005 _chargeTask.cancel(false);
12006 _chargeTask = null;
12007 }
12008 }
12009
12010 public void teleportBookmarkModify(int id, int icon, String tag, String name)
12011 {
12012 final TeleportBookmark bookmark = _tpbookmarks.get(id);
12013 if (bookmark != null)
12014 {
12015 bookmark.setIcon(icon);
12016 bookmark.setTag(tag);
12017 bookmark.setName(name);
12018
12019 try (Connection con = DatabaseFactory.getInstance().getConnection();
12020 PreparedStatement statement = con.prepareStatement(UPDATE_TP_BOOKMARK))
12021 {
12022 statement.setInt(1, icon);
12023 statement.setString(2, tag);
12024 statement.setString(3, name);
12025 statement.setInt(4, getObjectId());
12026 statement.setInt(5, id);
12027 statement.execute();
12028 }
12029 catch (Exception e)
12030 {
12031 LOGGER.log(Level.WARNING, "Could not update character teleport bookmark data: " + e.getMessage(), e);
12032 }
12033 }
12034
12035 sendPacket(new ExGetBookMarkInfoPacket(this));
12036 }
12037
12038 public void teleportBookmarkDelete(int id)
12039 {
12040 if (_tpbookmarks.remove(id) != null)
12041 {
12042 try (Connection con = DatabaseFactory.getInstance().getConnection();
12043 PreparedStatement statement = con.prepareStatement(DELETE_TP_BOOKMARK))
12044 {
12045 statement.setInt(1, getObjectId());
12046 statement.setInt(2, id);
12047 statement.execute();
12048 }
12049 catch (Exception e)
12050 {
12051 LOGGER.log(Level.WARNING, "Could not delete character teleport bookmark data: " + e.getMessage(), e);
12052 }
12053
12054 sendPacket(new ExGetBookMarkInfoPacket(this));
12055 }
12056 }
12057
12058 public void teleportBookmarkGo(int id)
12059 {
12060 if (!teleportBookmarkCondition(0))
12061 {
12062 return;
12063 }
12064 if (getInventory().getInventoryItemCount(13016, 0) == 0)
12065 {
12066 sendPacket(SystemMessageId.YOU_CANNOT_TELEPORT_BECAUSE_YOU_DO_NOT_HAVE_A_TELEPORT_ITEM);
12067 return;
12068 }
12069 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
12070 sm.addItemName(13016);
12071 sendPacket(sm);
12072
12073 final TeleportBookmark bookmark = _tpbookmarks.get(id);
12074 if (bookmark != null)
12075 {
12076 destroyItem("Consume", getInventory().getItemByItemId(13016).getObjectId(), 1, null, false);
12077 teleToLocation(bookmark, false);
12078 }
12079 sendPacket(new ExGetBookMarkInfoPacket(this));
12080 }
12081
12082 public boolean teleportBookmarkCondition(int type)
12083 {
12084 if (isInCombat())
12085 {
12086 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_DURING_A_BATTLE);
12087 return false;
12088 }
12089 else if (isInSiege() || (getSiegeState() != 0))
12090 {
12091 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_PARTICIPATING_A_LARGE_SCALE_BATTLE_SUCH_AS_A_CASTLE_SIEGE_FORTRESS_SIEGE_OR_CLAN_HALL_SIEGE);
12092 return false;
12093 }
12094 else if (isInDuel())
12095 {
12096 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_DURING_A_DUEL);
12097 return false;
12098 }
12099 else if (isFlying())
12100 {
12101 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_FLYING);
12102 return false;
12103 }
12104 else if (isInOlympiadMode())
12105 {
12106 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_PARTICIPATING_IN_AN_OLYMPIAD_MATCH);
12107 return false;
12108 }
12109 else if (hasBlockActions() && hasAbnormalType(AbnormalType.PARALYZE))
12110 {
12111 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_YOU_ARE_IN_A_PETRIFIED_OR_PARALYZED_STATE);
12112 return false;
12113 }
12114 else if (isDead())
12115 {
12116 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_YOU_ARE_DEAD);
12117 return false;
12118 }
12119 else if (isInWater())
12120 {
12121 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_UNDERWATER);
12122 return false;
12123 }
12124 else if ((type == 1) && (isInsideZone(ZoneId.SIEGE) || isInsideZone(ZoneId.CLAN_HALL) || isInsideZone(ZoneId.JAIL) || isInsideZone(ZoneId.CASTLE) || isInsideZone(ZoneId.NO_SUMMON_FRIEND) || isInsideZone(ZoneId.FORT)))
12125 {
12126 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA);
12127 return false;
12128 }
12129 else if (isInsideZone(ZoneId.NO_BOOKMARK) || isInBoat() || isInAirShip())
12130 {
12131 if (type == 0)
12132 {
12133 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_IN_THIS_AREA);
12134 }
12135 else if (type == 1)
12136 {
12137 sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA);
12138 }
12139 return false;
12140 }
12141 /*
12142 * TODO: Instant Zone still not implemented else if (isInsideZone(ZoneId.INSTANT)) { sendPacket(SystemMessage.getSystemMessage(2357)); return; }
12143 */
12144 else
12145 {
12146 return true;
12147 }
12148 }
12149
12150 public void teleportBookmarkAdd(int x, int y, int z, int icon, String tag, String name)
12151 {
12152 if (!teleportBookmarkCondition(1))
12153 {
12154 return;
12155 }
12156
12157 if (_tpbookmarks.size() >= _bookmarkslot)
12158 {
12159 sendPacket(SystemMessageId.YOU_HAVE_NO_SPACE_TO_SAVE_THE_TELEPORT_LOCATION);
12160 return;
12161 }
12162
12163 if (getInventory().getInventoryItemCount(20033, 0) == 0)
12164 {
12165 sendPacket(SystemMessageId.YOU_CANNOT_BOOKMARK_THIS_LOCATION_BECAUSE_YOU_DO_NOT_HAVE_A_MY_TELEPORT_FLAG);
12166 return;
12167 }
12168
12169 int id;
12170 for (id = 1; id <= _bookmarkslot; ++id)
12171 {
12172 if (!_tpbookmarks.containsKey(id))
12173 {
12174 break;
12175 }
12176 }
12177 _tpbookmarks.put(id, new TeleportBookmark(id, x, y, z, icon, tag, name));
12178
12179 destroyItem("Consume", getInventory().getItemByItemId(20033).getObjectId(), 1, null, false);
12180
12181 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED);
12182 sm.addItemName(20033);
12183 sendPacket(sm);
12184
12185 try (Connection con = DatabaseFactory.getInstance().getConnection();
12186 PreparedStatement statement = con.prepareStatement(INSERT_TP_BOOKMARK))
12187 {
12188 statement.setInt(1, getObjectId());
12189 statement.setInt(2, id);
12190 statement.setInt(3, x);
12191 statement.setInt(4, y);
12192 statement.setInt(5, z);
12193 statement.setInt(6, icon);
12194 statement.setString(7, tag);
12195 statement.setString(8, name);
12196 statement.execute();
12197 }
12198 catch (Exception e)
12199 {
12200 LOGGER.log(Level.WARNING, "Could not insert character teleport bookmark data: " + e.getMessage(), e);
12201 }
12202 sendPacket(new ExGetBookMarkInfoPacket(this));
12203 }
12204
12205 public void restoreTeleportBookmark()
12206 {
12207 try (Connection con = DatabaseFactory.getInstance().getConnection();
12208 PreparedStatement statement = con.prepareStatement(RESTORE_TP_BOOKMARK))
12209 {
12210 statement.setInt(1, getObjectId());
12211 try (ResultSet rset = statement.executeQuery())
12212 {
12213 while (rset.next())
12214 {
12215 _tpbookmarks.put(rset.getInt("Id"), new TeleportBookmark(rset.getInt("Id"), rset.getInt("x"), rset.getInt("y"), rset.getInt("z"), rset.getInt("icon"), rset.getString("tag"), rset.getString("name")));
12216 }
12217 }
12218 }
12219 catch (Exception e)
12220 {
12221 LOGGER.log(Level.SEVERE, "Failed restoing character teleport bookmark.", e);
12222 }
12223 }
12224
12225 @Override
12226 public void sendInfo(L2PcInstance activeChar)
12227 {
12228 if (isInBoat())
12229 {
12230 setXYZ(getBoat().getLocation());
12231
12232 activeChar.sendPacket(new CharInfo(this, isInvisible() && activeChar.canOverrideCond(PcCondOverride.SEE_ALL_PLAYERS)));
12233 activeChar.sendPacket(new GetOnVehicle(getObjectId(), getBoat().getObjectId(), getInVehiclePosition()));
12234 }
12235 else if (isInAirShip())
12236 {
12237 setXYZ(getAirShip().getLocation());
12238 activeChar.sendPacket(new CharInfo(this, isInvisible() && activeChar.canOverrideCond(PcCondOverride.SEE_ALL_PLAYERS)));
12239 activeChar.sendPacket(new ExGetOnAirShip(this, getAirShip()));
12240 }
12241 else
12242 {
12243 activeChar.sendPacket(new CharInfo(this, isInvisible() && activeChar.canOverrideCond(PcCondOverride.SEE_ALL_PLAYERS)));
12244 }
12245
12246 final int relation1 = getRelation(activeChar);
12247 final RelationChanged rc1 = new RelationChanged();
12248 rc1.addRelation(this, relation1, isAutoAttackable(activeChar));
12249 if (hasSummon())
12250 {
12251 final L2Summon pet = getPet();
12252 if (pet != null)
12253 {
12254 rc1.addRelation(pet, relation1, isAutoAttackable(activeChar));
12255 }
12256 if (hasServitors())
12257 {
12258 getServitors().values().forEach(s -> rc1.addRelation(s, relation1, isAutoAttackable(activeChar)));
12259 }
12260 }
12261 activeChar.sendPacket(rc1);
12262
12263 final int relation2 = activeChar.getRelation(this);
12264 final RelationChanged rc2 = new RelationChanged();
12265 rc2.addRelation(activeChar, relation2, activeChar.isAutoAttackable(this));
12266 if (activeChar.hasSummon())
12267 {
12268 final L2Summon pet = getPet();
12269 if (pet != null)
12270 {
12271 rc2.addRelation(pet, relation2, activeChar.isAutoAttackable(this));
12272 }
12273 if (hasServitors())
12274 {
12275 getServitors().values().forEach(s -> rc2.addRelation(s, relation2, activeChar.isAutoAttackable(this)));
12276 }
12277 }
12278 sendPacket(rc2);
12279
12280 switch (getPrivateStoreType())
12281 {
12282 case SELL:
12283 {
12284 activeChar.sendPacket(new PrivateStoreMsgSell(this));
12285 break;
12286 }
12287 case PACKAGE_SELL:
12288 {
12289 activeChar.sendPacket(new ExPrivateStoreSetWholeMsg(this));
12290 break;
12291 }
12292 case BUY:
12293 {
12294 activeChar.sendPacket(new PrivateStoreMsgBuy(this));
12295 break;
12296 }
12297 case MANUFACTURE:
12298 {
12299 activeChar.sendPacket(new RecipeShopMsg(this));
12300 break;
12301 }
12302 }
12303 }
12304
12305 public void playMovie(MovieHolder holder)
12306 {
12307 if (getMovieHolder() != null)
12308 {
12309 return;
12310 }
12311 abortAttack();
12312 // abortCast(); Confirmed in retail, playing a movie does not abort cast.
12313 stopMove(null);
12314 setMovieHolder(holder);
12315 if (!isTeleporting())
12316 {
12317 sendPacket(new ExStartScenePlayer(holder.getMovie()));
12318 }
12319 }
12320
12321 public void stopMovie()
12322 {
12323 sendPacket(new ExStopScenePlayer(getMovieHolder().getMovie()));
12324 setMovieHolder(null);
12325 }
12326
12327 public boolean isAllowedToEnchantSkills()
12328 {
12329 if (isLocked())
12330 {
12331 return false;
12332 }
12333 if (isTransformed())
12334 {
12335 return false;
12336 }
12337 if (AttackStanceTaskManager.getInstance().hasAttackStanceTask(this))
12338 {
12339 return false;
12340 }
12341 if (isCastingNow())
12342 {
12343 return false;
12344 }
12345 if (isInBoat() || isInAirShip())
12346 {
12347 return false;
12348 }
12349 return true;
12350 }
12351
12352 /**
12353 * Set the _createDate of the L2PcInstance.
12354 * @param createDate
12355 */
12356 public void setCreateDate(Calendar createDate)
12357 {
12358 _createDate = createDate;
12359 }
12360
12361 /**
12362 * @return the _createDate of the L2PcInstance.
12363 */
12364 public Calendar getCreateDate()
12365 {
12366 return _createDate;
12367 }
12368
12369 /**
12370 * @return number of days to char birthday.
12371 */
12372 public int checkBirthDay()
12373 {
12374 final Calendar now = Calendar.getInstance();
12375
12376 // "Characters with a February 29 creation date will receive a gift on February 28."
12377 if ((_createDate.get(Calendar.DAY_OF_MONTH) == 29) && (_createDate.get(Calendar.MONTH) == 1))
12378 {
12379 _createDate.add(Calendar.HOUR_OF_DAY, -24);
12380 }
12381
12382 if ((now.get(Calendar.MONTH) == _createDate.get(Calendar.MONTH)) && (now.get(Calendar.DAY_OF_MONTH) == _createDate.get(Calendar.DAY_OF_MONTH)) && (now.get(Calendar.YEAR) != _createDate.get(Calendar.YEAR)))
12383 {
12384 return 0;
12385 }
12386
12387 int i;
12388 for (i = 1; i < 6; i++)
12389 {
12390 now.add(Calendar.HOUR_OF_DAY, 24);
12391 if ((now.get(Calendar.MONTH) == _createDate.get(Calendar.MONTH)) && (now.get(Calendar.DAY_OF_MONTH) == _createDate.get(Calendar.DAY_OF_MONTH)) && (now.get(Calendar.YEAR) != _createDate.get(Calendar.YEAR)))
12392 {
12393 return i;
12394 }
12395 }
12396 return -1;
12397 }
12398
12399 public int getBirthdays()
12400 {
12401 long time = (System.currentTimeMillis() - getCreateDate().getTimeInMillis()) / 1000;
12402 time /= TimeUnit.DAYS.toMillis(365);
12403 return (int) time;
12404 }
12405
12406 /**
12407 * list of character friends
12408 */
12409 private final Set<Integer> _friendList = ConcurrentHashMap.newKeySet();
12410
12411 public Set<Integer> getFriendList()
12412 {
12413 return _friendList;
12414 }
12415
12416 public void restoreFriendList()
12417 {
12418 _friendList.clear();
12419
12420 final String sqlQuery = "SELECT friendId FROM character_friends WHERE charId=? AND relation=0";
12421 try (Connection con = DatabaseFactory.getInstance().getConnection();
12422 PreparedStatement statement = con.prepareStatement(sqlQuery))
12423 {
12424 statement.setInt(1, getObjectId());
12425 try (ResultSet rset = statement.executeQuery())
12426 {
12427 while (rset.next())
12428 {
12429 final int friendId = rset.getInt("friendId");
12430 if (friendId == getObjectId())
12431 {
12432 continue;
12433 }
12434 _friendList.add(friendId);
12435 }
12436 }
12437 }
12438 catch (Exception e)
12439 {
12440 LOGGER.log(Level.WARNING, "Error found in " + getName() + "'s FriendList: " + e.getMessage(), e);
12441 }
12442 }
12443
12444 public void notifyFriends(int type)
12445 {
12446 final L2FriendStatus pkt = new L2FriendStatus(this, type);
12447 for (int id : _friendList)
12448 {
12449 final L2PcInstance friend = L2World.getInstance().getPlayer(id);
12450 if (friend != null)
12451 {
12452 friend.sendPacket(pkt);
12453 }
12454 }
12455 }
12456
12457 /**
12458 * Verify if this player is in silence mode.
12459 * @return the {@code true} if this player is in silence mode, {@code false} otherwise
12460 */
12461 public boolean isSilenceMode()
12462 {
12463 return _silenceMode;
12464 }
12465
12466 /**
12467 * While at silenceMode, checks if this player blocks PMs for this user
12468 * @param playerObjId the player object Id
12469 * @return {@code true} if the given Id is not excluded and this player is in silence mode, {@code false} otherwise
12470 */
12471 public boolean isSilenceMode(int playerObjId)
12472 {
12473 if (Config.SILENCE_MODE_EXCLUDE && _silenceMode && (_silenceModeExcluded != null))
12474 {
12475 return !_silenceModeExcluded.contains(playerObjId);
12476 }
12477 return _silenceMode;
12478 }
12479
12480 /**
12481 * Set the silence mode.
12482 * @param mode the value
12483 */
12484 public void setSilenceMode(boolean mode)
12485 {
12486 _silenceMode = mode;
12487 if (_silenceModeExcluded != null)
12488 {
12489 _silenceModeExcluded.clear(); // Clear the excluded list on each setSilenceMode
12490 }
12491 sendPacket(new EtcStatusUpdate(this));
12492 }
12493
12494 /**
12495 * Add a player to the "excluded silence mode" list.
12496 * @param playerObjId the player's object Id
12497 */
12498 public void addSilenceModeExcluded(int playerObjId)
12499 {
12500 if (_silenceModeExcluded == null)
12501 {
12502 _silenceModeExcluded = new ArrayList<>(1);
12503 }
12504 _silenceModeExcluded.add(playerObjId);
12505 }
12506
12507 private void storeRecipeShopList()
12508 {
12509 if (hasManufactureShop())
12510 {
12511 try (Connection con = DatabaseFactory.getInstance().getConnection())
12512 {
12513 try (PreparedStatement st = con.prepareStatement(DELETE_CHAR_RECIPE_SHOP))
12514 {
12515 st.setInt(1, getObjectId());
12516 st.execute();
12517 }
12518
12519 try (PreparedStatement st = con.prepareStatement(INSERT_CHAR_RECIPE_SHOP))
12520 {
12521 final AtomicInteger slot = new AtomicInteger(1);
12522 con.setAutoCommit(false);
12523 for (Entry<Integer, Long> entry : _manufactureItems.entrySet())
12524 {
12525 st.setInt(1, getObjectId());
12526 st.setInt(2, entry.getKey());
12527 st.setLong(3, entry.getValue());
12528 st.setInt(4, slot.getAndIncrement());
12529 st.addBatch();
12530 }
12531 st.executeBatch();
12532 con.commit();
12533 }
12534 }
12535 catch (Exception e)
12536 {
12537 LOGGER.log(Level.SEVERE, "Could not store recipe shop for playerId " + getObjectId() + ": ", e);
12538 }
12539 }
12540 }
12541
12542 private void restoreRecipeShopList()
12543 {
12544 final Map<Integer, Long> manufactureItems = new HashMap<>();
12545
12546 try (Connection con = DatabaseFactory.getInstance().getConnection();
12547 PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_RECIPE_SHOP))
12548 {
12549 statement.setInt(1, getObjectId());
12550 try (ResultSet rset = statement.executeQuery())
12551 {
12552 while (rset.next())
12553 {
12554 manufactureItems.put(rset.getInt("recipeId"), rset.getLong("price"));
12555 }
12556 }
12557 }
12558 catch (Exception e)
12559 {
12560 LOGGER.log(Level.SEVERE, "Could not restore recipe shop list data for playerId: " + getObjectId(), e);
12561 }
12562
12563 _manufactureItems = manufactureItems;
12564 }
12565
12566 @Override
12567 public double getCollisionRadius()
12568 {
12569 if (isMounted() && (getMountNpcId() > 0))
12570 {
12571 return NpcData.getInstance().getTemplate(getMountNpcId()).getfCollisionRadius();
12572 }
12573
12574 final double defaultCollisionRadius = getAppearance().getSex() ? getBaseTemplate().getFCollisionRadiusFemale() : getBaseTemplate().getfCollisionRadius();
12575 return getTransformation().map(transform -> transform.getCollisionRadius(this, defaultCollisionRadius)).orElse(defaultCollisionRadius);
12576 }
12577
12578 @Override
12579 public double getCollisionHeight()
12580 {
12581 if (isMounted() && (getMountNpcId() > 0))
12582 {
12583 return NpcData.getInstance().getTemplate(getMountNpcId()).getfCollisionHeight();
12584 }
12585
12586 final double defaultCollisionHeight = getAppearance().getSex() ? getBaseTemplate().getFCollisionHeightFemale() : getBaseTemplate().getfCollisionHeight();
12587 return getTransformation().map(transform -> transform.getCollisionHeight(this, defaultCollisionHeight)).orElse(defaultCollisionHeight);
12588 }
12589
12590 public final int getClientX()
12591 {
12592 return _clientX;
12593 }
12594
12595 public final int getClientY()
12596 {
12597 return _clientY;
12598 }
12599
12600 public final int getClientZ()
12601 {
12602 return _clientZ;
12603 }
12604
12605 public final int getClientHeading()
12606 {
12607 return _clientHeading;
12608 }
12609
12610 public final void setClientX(int val)
12611 {
12612 _clientX = val;
12613 }
12614
12615 public final void setClientY(int val)
12616 {
12617 _clientY = val;
12618 }
12619
12620 public final void setClientZ(int val)
12621 {
12622 _clientZ = val;
12623 }
12624
12625 public final void setClientHeading(int val)
12626 {
12627 _clientHeading = val;
12628 }
12629
12630 /**
12631 * @param z
12632 * @return true if character falling now on the start of fall return false for correct coord sync!
12633 */
12634 public final boolean isFalling(int z)
12635 {
12636 if (isDead() || isFlying() || isFlyingMounted() || isInsideZone(ZoneId.WATER))
12637 {
12638 return false;
12639 }
12640
12641 if (System.currentTimeMillis() < _fallingTimestamp)
12642 {
12643 return true;
12644 }
12645
12646 final int deltaZ = getZ() - z;
12647 if (deltaZ <= getBaseTemplate().getSafeFallHeight())
12648 {
12649 return false;
12650 }
12651
12652 // If there is no geodata loaded for the place we are client Z correction might cause falling damage.
12653 if (!GeoEngine.getInstance().hasGeo(getX(), getY()))
12654 {
12655 return false;
12656 }
12657
12658 final int damage = (int) Formulas.calcFallDam(this, deltaZ);
12659 if (damage > 0)
12660 {
12661 reduceCurrentHp(Math.min(damage, getCurrentHp() - 1), this, null, false, true, false, false);
12662 final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_RECEIVED_S1_FALLING_DAMAGE);
12663 sm.addInt(damage);
12664 sendPacket(sm);
12665 }
12666
12667 setFalling();
12668
12669 return false;
12670 }
12671
12672 /**
12673 * Set falling timestamp
12674 */
12675 public final void setFalling()
12676 {
12677 _fallingTimestamp = System.currentTimeMillis() + FALLING_VALIDATION_DELAY;
12678 }
12679
12680 /**
12681 * @return the _movie
12682 */
12683 public MovieHolder getMovieHolder()
12684 {
12685 return _movieHolder;
12686 }
12687
12688 public void setMovieHolder(MovieHolder movie)
12689 {
12690 _movieHolder = movie;
12691 }
12692
12693 /**
12694 * Update last item auction request timestamp to current
12695 */
12696 public void updateLastItemAuctionRequest()
12697 {
12698 _lastItemAuctionInfoRequest = System.currentTimeMillis();
12699 }
12700
12701 /**
12702 * @return true if receiving item auction requests<br>
12703 * (last request was in 2 seconds before)
12704 */
12705 public boolean isItemAuctionPolling()
12706 {
12707 return (System.currentTimeMillis() - _lastItemAuctionInfoRequest) < 2000;
12708 }
12709
12710 @Override
12711 public boolean isMovementDisabled()
12712 {
12713 return super.isMovementDisabled() || (getMovieHolder() != null) || _fishing.isFishing();
12714 }
12715
12716 public String getHtmlPrefix()
12717 {
12718 if (!Config.MULTILANG_ENABLE)
12719 {
12720 return null;
12721 }
12722
12723 return _htmlPrefix;
12724 }
12725
12726 public String getLang()
12727 {
12728 return _lang;
12729 }
12730
12731 public boolean setLang(String lang)
12732 {
12733 boolean result = false;
12734 if (Config.MULTILANG_ENABLE)
12735 {
12736 if (Config.MULTILANG_ALLOWED.contains(lang))
12737 {
12738 _lang = lang;
12739 result = true;
12740 }
12741 else
12742 {
12743 _lang = Config.MULTILANG_DEFAULT;
12744 }
12745
12746 _htmlPrefix = "data/lang/" + _lang + "/";
12747 }
12748 else
12749 {
12750 _lang = null;
12751 _htmlPrefix = null;
12752 }
12753
12754 return result;
12755 }
12756
12757 public long getOfflineStartTime()
12758 {
12759 return _offlineShopStart;
12760 }
12761
12762 public void setOfflineStartTime(long time)
12763 {
12764 _offlineShopStart = time;
12765 }
12766
12767 public int getPcCafePoints()
12768 {
12769 return _pcCafePoints;
12770 }
12771
12772 public void setPcCafePoints(int count)
12773 {
12774 _pcCafePoints = count < 200000 ? count : 200000;
12775 }
12776
12777 /**
12778 * Check all player skills for skill level. If player level is lower than skill learn level - 9, skill level is decreased to next possible level.
12779 */
12780 public void checkPlayerSkills()
12781 {
12782 L2SkillLearn learn;
12783 for (Entry<Integer, Skill> e : getSkills().entrySet())
12784 {
12785 learn = SkillTreesData.getInstance().getClassSkill(e.getKey(), e.getValue().getLevel() % 100, getClassId());
12786 if (learn != null)
12787 {
12788 final int lvlDiff = e.getKey() == CommonSkill.EXPERTISE.getId() ? 0 : 9;
12789 if (getLevel() < (learn.getGetLevel() - lvlDiff))
12790 {
12791 deacreaseSkillLevel(e.getValue(), lvlDiff);
12792 }
12793 }
12794 }
12795 }
12796
12797 private void deacreaseSkillLevel(Skill skill, int lvlDiff)
12798 {
12799 int nextLevel = -1;
12800 final Map<Long, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(getClassId());
12801 for (L2SkillLearn sl : skillTree.values())
12802 {
12803 if ((sl.getSkillId() == skill.getId()) && (nextLevel < sl.getSkillLevel()) && (getLevel() >= (sl.getGetLevel() - lvlDiff)))
12804 {
12805 nextLevel = sl.getSkillLevel(); // next possible skill level
12806 }
12807 }
12808
12809 if (nextLevel == -1)
12810 {
12811 LOGGER.info("Removing skill " + skill + " from player " + toString());
12812 removeSkill(skill, true); // there is no lower skill
12813 }
12814 else
12815 {
12816 LOGGER.info("Decreasing skill " + skill + " to " + nextLevel + " for player " + toString());
12817 addSkill(SkillData.getInstance().getSkill(skill.getId(), nextLevel), true); // replace with lower one
12818 }
12819 }
12820
12821 public boolean canMakeSocialAction()
12822 {
12823 return ((getPrivateStoreType() == PrivateStoreType.NONE) && (getActiveRequester() == null) && !isAlikeDead() && !isAllSkillsDisabled() && !isCastingNow() && (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE));
12824 }
12825
12826 public void setMultiSocialAction(int id, int targetId)
12827 {
12828 _multiSociaAction = id;
12829 _multiSocialTarget = targetId;
12830 }
12831
12832 public int getMultiSociaAction()
12833 {
12834 return _multiSociaAction;
12835 }
12836
12837 public int getMultiSocialTarget()
12838 {
12839 return _multiSocialTarget;
12840 }
12841
12842 public Collection<TeleportBookmark> getTeleportBookmarks()
12843 {
12844 return _tpbookmarks.values();
12845 }
12846
12847 public int getBookmarkslot()
12848 {
12849 return _bookmarkslot;
12850 }
12851
12852 /**
12853 * @return
12854 */
12855 public int getQuestInventoryLimit()
12856 {
12857 return Config.INVENTORY_MAXIMUM_QUEST_ITEMS;
12858 }
12859
12860 public boolean canAttackCharacter(L2Character cha)
12861 {
12862 if (cha.isAttackable())
12863 {
12864 return true;
12865 }
12866 else if (cha.isPlayable())
12867 {
12868 if (cha.isInsideZone(ZoneId.PVP) && !cha.isInsideZone(ZoneId.SIEGE))
12869 {
12870 return true;
12871 }
12872
12873 final L2PcInstance target = cha.isSummon() ? ((L2Summon) cha).getOwner() : (L2PcInstance) cha;
12874
12875 if (isInDuel() && target.isInDuel() && (target.getDuelId() == getDuelId()))
12876 {
12877 return true;
12878 }
12879 else if (isInParty() && target.isInParty())
12880 {
12881 if (getParty() == target.getParty())
12882 {
12883 return false;
12884 }
12885 if (((getParty().getCommandChannel() != null) || (target.getParty().getCommandChannel() != null)) && (getParty().getCommandChannel() == target.getParty().getCommandChannel()))
12886 {
12887 return false;
12888 }
12889 }
12890 else if ((getClan() != null) && (target.getClan() != null))
12891 {
12892 if (getClanId() == target.getClanId())
12893 {
12894 return false;
12895 }
12896 if (((getAllyId() > 0) || (target.getAllyId() > 0)) && (getAllyId() == target.getAllyId()))
12897 {
12898 return false;
12899 }
12900 if (getClan().isAtWarWith(target.getClan().getId()) && target.getClan().isAtWarWith(getClan().getId()))
12901 {
12902 return true;
12903 }
12904 }
12905 else if ((getClan() == null) || (target.getClan() == null))
12906 {
12907 if ((target.getPvpFlag() == 0) && (target.getReputation() >= 0))
12908 {
12909 return false;
12910 }
12911 }
12912 }
12913 return true;
12914 }
12915
12916 /**
12917 * Test if player inventory is under 90% capacity
12918 * @param includeQuestInv check also quest inventory
12919 * @return
12920 */
12921 public boolean isInventoryUnder90(boolean includeQuestInv)
12922 {
12923 return (getInventory().getSize(item -> !item.isQuestItem() || includeQuestInv) <= (getInventoryLimit() * 0.9));
12924 }
12925
12926 /**
12927 * Test if player inventory is under 80% capacity
12928 * @param includeQuestInv check also quest inventory
12929 * @return
12930 */
12931 public boolean isInventoryUnder80(boolean includeQuestInv)
12932 {
12933 return (getInventory().getSize(item -> !item.isQuestItem() || includeQuestInv) <= (getInventoryLimit() * 0.8));
12934 }
12935
12936 public boolean havePetInvItems()
12937 {
12938 return _petItems;
12939 }
12940
12941 public void setPetInvItems(boolean haveit)
12942 {
12943 _petItems = haveit;
12944 }
12945
12946 /**
12947 * Restore Pet's inventory items from database.
12948 */
12949 private void restorePetInventoryItems()
12950 {
12951 try (Connection con = DatabaseFactory.getInstance().getConnection();
12952 PreparedStatement statement = con.prepareStatement("SELECT object_id FROM `items` WHERE `owner_id`=? AND (`loc`='PET' OR `loc`='PET_EQUIP') LIMIT 1;"))
12953 {
12954 statement.setInt(1, getObjectId());
12955 try (ResultSet rset = statement.executeQuery())
12956 {
12957 setPetInvItems(rset.next() && (rset.getInt("object_id") > 0));
12958 }
12959 }
12960 catch (Exception e)
12961 {
12962 LOGGER.log(Level.SEVERE, "Could not check Items in Pet Inventory for playerId: " + getObjectId(), e);
12963 }
12964 }
12965
12966 public String getAdminConfirmCmd()
12967 {
12968 return _adminConfirmCmd;
12969 }
12970
12971 public void setAdminConfirmCmd(String adminConfirmCmd)
12972 {
12973 _adminConfirmCmd = adminConfirmCmd;
12974 }
12975
12976 public void setBlockCheckerArena(byte arena)
12977 {
12978 _handysBlockCheckerEventArena = arena;
12979 }
12980
12981 public int getBlockCheckerArena()
12982 {
12983 return _handysBlockCheckerEventArena;
12984 }
12985
12986 /**
12987 * Load L2PcInstance Recommendations data.
12988 */
12989 private void loadRecommendations()
12990 {
12991 try (Connection con = DatabaseFactory.getInstance().getConnection();
12992 PreparedStatement statement = con.prepareStatement("SELECT rec_have, rec_left FROM character_reco_bonus WHERE charId = ?"))
12993 {
12994 statement.setInt(1, getObjectId());
12995 try (ResultSet rset = statement.executeQuery())
12996 {
12997 if (rset.next())
12998 {
12999 setRecomHave(rset.getInt("rec_have"));
13000 setRecomLeft(rset.getInt("rec_left"));
13001 }
13002 }
13003 }
13004 catch (Exception e)
13005 {
13006 LOGGER.log(Level.SEVERE, "Could not restore Recommendations for player: " + getObjectId(), e);
13007 }
13008 }
13009
13010 /**
13011 * Update L2PcInstance Recommendations data.
13012 */
13013 public void storeRecommendations()
13014 {
13015 try (Connection con = DatabaseFactory.getInstance().getConnection();
13016 PreparedStatement ps = con.prepareStatement("REPLACE INTO character_reco_bonus (charId,rec_have,rec_left,time_left) VALUES (?,?,?,?)"))
13017 {
13018 ps.setInt(1, getObjectId());
13019 ps.setInt(2, getRecomHave());
13020 ps.setInt(3, getRecomLeft());
13021 ps.setLong(4, 0);
13022 ps.execute();
13023 }
13024 catch (Exception e)
13025 {
13026 LOGGER.log(Level.SEVERE, "Could not update Recommendations for player: " + getObjectId(), e);
13027 }
13028 }
13029
13030 public void startRecoGiveTask()
13031 {
13032 // Create task to give new recommendations
13033 _recoGiveTask = ThreadPool.scheduleAtFixedRate(new RecoGiveTask(this), 7200000, 3600000);
13034
13035 // Store new data
13036 storeRecommendations();
13037 }
13038
13039 public void stopRecoGiveTask()
13040 {
13041 if (_recoGiveTask != null)
13042 {
13043 _recoGiveTask.cancel(false);
13044 _recoGiveTask = null;
13045 }
13046 }
13047
13048 public boolean isRecoTwoHoursGiven()
13049 {
13050 return _recoTwoHoursGiven;
13051 }
13052
13053 public void setRecoTwoHoursGiven(boolean val)
13054 {
13055 _recoTwoHoursGiven = val;
13056 }
13057
13058 public void setPremiumStatus(boolean premiumStatus)
13059 {
13060 _premiumStatus = premiumStatus;
13061 sendPacket(new ExBrPremiumState(this));
13062 }
13063
13064 public boolean hasPremiumStatus()
13065 {
13066 return Config.PREMIUM_SYSTEM_ENABLED && _premiumStatus;
13067 }
13068
13069 public void setLastPetitionGmName(String gmName)
13070 {
13071 _lastPetitionGmName = gmName;
13072 }
13073
13074 public String getLastPetitionGmName()
13075 {
13076 return _lastPetitionGmName;
13077 }
13078
13079 public L2ContactList getContactList()
13080 {
13081 return _contactList;
13082 }
13083
13084 public void setEventStatus()
13085 {
13086 eventStatus = new PlayerEventHolder(this);
13087 }
13088
13089 public void setEventStatus(PlayerEventHolder pes)
13090 {
13091 eventStatus = pes;
13092 }
13093
13094 public PlayerEventHolder getEventStatus()
13095 {
13096 return eventStatus;
13097 }
13098
13099 public long getNotMoveUntil()
13100 {
13101 return _notMoveUntil;
13102 }
13103
13104 public void updateNotMoveUntil()
13105 {
13106 _notMoveUntil = System.currentTimeMillis() + Config.PLAYER_MOVEMENT_BLOCK_TIME;
13107 }
13108
13109 @Override
13110 public boolean isPlayer()
13111 {
13112 return true;
13113 }
13114
13115 /**
13116 * @param skillId the display skill Id
13117 * @return the custom skill
13118 */
13119 public final Skill getCustomSkill(int skillId)
13120 {
13121 return (_customSkills != null) ? _customSkills.get(skillId) : null;
13122 }
13123
13124 /**
13125 * Add a skill level to the custom skills map.
13126 * @param skill the skill to add
13127 */
13128 private void addCustomSkill(Skill skill)
13129 {
13130 if ((skill != null) && (skill.getDisplayId() != skill.getId()))
13131 {
13132 if (_customSkills == null)
13133 {
13134 _customSkills = new ConcurrentSkipListMap<>();
13135 }
13136 _customSkills.put(skill.getDisplayId(), skill);
13137 }
13138 }
13139
13140 /**
13141 * Remove a skill level from the custom skill map.
13142 * @param skill the skill to remove
13143 */
13144 private void removeCustomSkill(Skill skill)
13145 {
13146 if ((skill != null) && (_customSkills != null) && (skill.getDisplayId() != skill.getId()))
13147 {
13148 _customSkills.remove(skill.getDisplayId());
13149 }
13150 }
13151
13152 /**
13153 * @return {@code true} if current player can revive and shows 'To Village' button upon death, {@code false} otherwise.
13154 */
13155 @Override
13156 public boolean canRevive()
13157 {
13158 if (_events != null)
13159 {
13160 for (AbstractEvent<?> listener : _events.values())
13161 {
13162 if (listener.isOnEvent(this) && !listener.canRevive(this))
13163 {
13164 return false;
13165 }
13166 }
13167 }
13168 return _canRevive;
13169 }
13170
13171 /**
13172 * This method can prevent from displaying 'To Village' button upon death.
13173 * @param val
13174 */
13175 @Override
13176 public void setCanRevive(boolean val)
13177 {
13178 _canRevive = val;
13179 }
13180
13181 public boolean isOnCustomEvent()
13182 {
13183 return _isOnCustomEvent;
13184 }
13185
13186 public void setOnCustomEvent(boolean value)
13187 {
13188 _isOnCustomEvent = value;
13189 }
13190
13191 /**
13192 * @return {@code true} if player is on event, {@code false} otherwise.
13193 */
13194 @Override
13195 public boolean isOnEvent()
13196 {
13197 if (_isOnCustomEvent)
13198 {
13199 return true;
13200 }
13201 if (_events != null)
13202 {
13203 for (AbstractEvent<?> listener : _events.values())
13204 {
13205 if (listener.isOnEvent(this))
13206 {
13207 return true;
13208 }
13209 }
13210 }
13211 return super.isOnEvent();
13212 }
13213
13214 public boolean isBlockedFromExit()
13215 {
13216 if (_isOnCustomEvent)
13217 {
13218 return true;
13219 }
13220 if (_events != null)
13221 {
13222 for (AbstractEvent<?> listener : _events.values())
13223 {
13224 if (listener.isOnEvent(this) && listener.isBlockingExit(this))
13225 {
13226 return true;
13227 }
13228 }
13229 }
13230 return false;
13231 }
13232
13233 public boolean isBlockedFromDeathPenalty()
13234 {
13235 if (_isOnCustomEvent)
13236 {
13237 return true;
13238 }
13239 if (_events != null)
13240 {
13241 for (AbstractEvent<?> listener : _events.values())
13242 {
13243 if (listener.isOnEvent(this) && listener.isBlockingDeathPenalty(this))
13244 {
13245 return true;
13246 }
13247 }
13248 }
13249 return isAffected(EffectFlag.PROTECT_DEATH_PENALTY);
13250 }
13251
13252 public void setOriginalCpHpMp(double cp, double hp, double mp)
13253 {
13254 _originalCp = cp;
13255 _originalHp = hp;
13256 _originalMp = mp;
13257 }
13258
13259 @Override
13260 public void addOverrideCond(PcCondOverride... excs)
13261 {
13262 super.addOverrideCond(excs);
13263 getVariables().set(COND_OVERRIDE_KEY, Long.toString(_exceptions));
13264 }
13265
13266 @Override
13267 public void removeOverridedCond(PcCondOverride... excs)
13268 {
13269 super.removeOverridedCond(excs);
13270 getVariables().set(COND_OVERRIDE_KEY, Long.toString(_exceptions));
13271 }
13272
13273 /**
13274 * @return {@code true} if {@link PlayerVariables} instance is attached to current player's scripts, {@code false} otherwise.
13275 */
13276 public boolean hasVariables()
13277 {
13278 return getScript(PlayerVariables.class) != null;
13279 }
13280
13281 /**
13282 * @return {@link PlayerVariables} instance containing parameters regarding player.
13283 */
13284 public PlayerVariables getVariables()
13285 {
13286 final PlayerVariables vars = getScript(PlayerVariables.class);
13287 return vars != null ? vars : addScript(new PlayerVariables(getObjectId()));
13288 }
13289
13290 /**
13291 * @return {@code true} if {@link AccountVariables} instance is attached to current player's scripts, {@code false} otherwise.
13292 */
13293 public boolean hasAccountVariables()
13294 {
13295 return getScript(AccountVariables.class) != null;
13296 }
13297
13298 /**
13299 * @return {@link AccountVariables} instance containing parameters regarding player.
13300 */
13301 public AccountVariables getAccountVariables()
13302 {
13303 final AccountVariables vars = getScript(AccountVariables.class);
13304 return vars != null ? vars : addScript(new AccountVariables(getAccountName()));
13305 }
13306
13307 @Override
13308 public int getId()
13309 {
13310 return getClassId().getId();
13311 }
13312
13313 public boolean isPartyBanned()
13314 {
13315 return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.PARTY_BAN);
13316 }
13317
13318 /**
13319 * @param act
13320 * @return {@code true} if action was added successfully, {@code false} otherwise.
13321 */
13322 public boolean addAction(PlayerAction act)
13323 {
13324 if (!hasAction(act))
13325 {
13326 _actionMask |= act.getMask();
13327 return true;
13328 }
13329 return false;
13330 }
13331
13332 /**
13333 * @param act
13334 * @return {@code true} if action was removed successfully, {@code false} otherwise.
13335 */
13336 public boolean removeAction(PlayerAction act)
13337 {
13338 if (hasAction(act))
13339 {
13340 _actionMask &= ~act.getMask();
13341 return true;
13342 }
13343 return false;
13344 }
13345
13346 /**
13347 * @param act
13348 * @return {@code true} if action is present, {@code false} otherwise.
13349 */
13350 public boolean hasAction(PlayerAction act)
13351 {
13352 return (_actionMask & act.getMask()) == act.getMask();
13353 }
13354
13355 /**
13356 * Set true/false if character got Charm of Courage
13357 * @param val true/false
13358 */
13359 public void setCharmOfCourage(boolean val)
13360 {
13361 _hasCharmOfCourage = val;
13362 }
13363
13364 /**
13365 * @return {@code true} if effect is present, {@code false} otherwise.
13366 */
13367 public boolean hasCharmOfCourage()
13368 {
13369 return _hasCharmOfCourage;
13370
13371 }
13372
13373 public boolean isGood()
13374 {
13375 return _isGood;
13376 }
13377
13378 public boolean isEvil()
13379 {
13380 return _isEvil;
13381 }
13382
13383 public void setGood()
13384 {
13385 _isGood = true;
13386 _isEvil = false;
13387 }
13388
13389 public void setEvil()
13390 {
13391 _isGood = false;
13392 _isEvil = true;
13393 }
13394
13395 /**
13396 * @param target the target
13397 * @return {@code true} if this player got war with the target, {@code false} otherwise.
13398 */
13399 public boolean atWarWith(L2Playable target)
13400 {
13401 if (target == null)
13402 {
13403 return false;
13404 }
13405 if ((_clan != null) && !isAcademyMember()) // Current player
13406 {
13407 if ((target.getClan() != null) && !target.isAcademyMember()) // Target player
13408 {
13409 return _clan.isAtWarWith(target.getClan());
13410 }
13411 }
13412 return false;
13413 }
13414
13415 /**
13416 * Sets the beauty shop hair
13417 * @param hairId
13418 */
13419 public void setVisualHair(int hairId)
13420 {
13421 getVariables().set("visualHairId", hairId);
13422 }
13423
13424 /**
13425 * Sets the beauty shop hair color
13426 * @param colorId
13427 */
13428 public void setVisualHairColor(int colorId)
13429 {
13430 getVariables().set("visualHairColorId", colorId);
13431 }
13432
13433 /**
13434 * Sets the beauty shop modified face
13435 * @param faceId
13436 */
13437 public void setVisualFace(int faceId)
13438 {
13439 getVariables().set("visualFaceId", faceId);
13440 }
13441
13442 /**
13443 * @return the beauty shop hair, or his normal if not changed.
13444 */
13445 public int getVisualHair()
13446 {
13447 return getVariables().getInt("visualHairId", getAppearance().getHairStyle());
13448 }
13449
13450 /**
13451 * @return the beauty shop hair color, or his normal if not changed.
13452 */
13453 public int getVisualHairColor()
13454 {
13455 return getVariables().getInt("visualHairColorId", getAppearance().getHairColor());
13456 }
13457
13458 /**
13459 * @return the beauty shop modified face, or his normal if not changed.
13460 */
13461 public int getVisualFace()
13462 {
13463 return getVariables().getInt("visualFaceId", getAppearance().getFace());
13464 }
13465
13466 /**
13467 * @return {@code true} if player has mentees, {@code false} otherwise
13468 */
13469 public boolean isMentor()
13470 {
13471 return MentorManager.getInstance().isMentor(getObjectId());
13472 }
13473
13474 /**
13475 * @return {@code true} if player has mentor, {@code false} otherwise
13476 */
13477 public boolean isMentee()
13478 {
13479 return MentorManager.getInstance().isMentee(getObjectId());
13480 }
13481
13482 /**
13483 * @return the amount of ability points player can spend on learning skills.
13484 */
13485 public int getAbilityPoints()
13486 {
13487 // Grand Crusade: 1 point per level after 84
13488 return Math.max(0, getLevel() - 84);
13489 }
13490
13491 /**
13492 * @return how much ability points player has spend on learning skills.
13493 */
13494 public int getAbilityPointsUsed()
13495 {
13496 return getVariables().getInt(isDualClassActive() ? PlayerVariables.ABILITY_POINTS_USED_DUAL_CLASS : PlayerVariables.ABILITY_POINTS_USED_MAIN_CLASS, 0);
13497 }
13498
13499 /**
13500 * Sets how much ability points player has spend on learning skills.
13501 * @param points
13502 */
13503 public void setAbilityPointsUsed(int points)
13504 {
13505 EventDispatcher.getInstance().notifyEventAsync(new OnPlayerAbilityPointsChanged(this, getAbilityPointsUsed(), points), this);
13506 getVariables().set(isDualClassActive() ? PlayerVariables.ABILITY_POINTS_USED_DUAL_CLASS : PlayerVariables.ABILITY_POINTS_USED_MAIN_CLASS, points);
13507 }
13508
13509 /**
13510 * @return The amount of times player can use world chat
13511 */
13512 public int getWorldChatPoints()
13513 {
13514 return (int) getStat().getValue(Stats.WORLD_CHAT_POINTS, Config.WORLD_CHAT_POINTS_PER_DAY);
13515 }
13516
13517 /**
13518 * @return The amount of times player has used world chat
13519 */
13520 public int getWorldChatUsed()
13521 {
13522 return getVariables().getInt(PlayerVariables.WORLD_CHAT_VARIABLE_NAME, 0);
13523 }
13524
13525 /**
13526 * Sets the amount of times player can use world chat
13527 * @param timesUsed how many times world chat has been used up until now.
13528 */
13529 public void setWorldChatUsed(int timesUsed)
13530 {
13531 getVariables().set(PlayerVariables.WORLD_CHAT_VARIABLE_NAME, timesUsed);
13532 }
13533
13534 public void prohibiteCeremonyOfChaos()
13535 {
13536 if (!PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.COC_BAN))
13537 {
13538 PunishmentManager.getInstance().startPunishment(new PunishmentTask(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.COC_BAN, 0, "", getClass().getSimpleName()));
13539 final int penalties = getVariables().getInt(PlayerVariables.CEREMONY_OF_CHAOS_PROHIBITED_PENALTIES, 0);
13540 getVariables().set(PlayerVariables.CEREMONY_OF_CHAOS_PROHIBITED_PENALTIES, penalties + 1);
13541 }
13542 }
13543
13544 public boolean isCeremonyOfChaosProhibited()
13545 {
13546 return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.COC_BAN) || (getVariables().getInt(PlayerVariables.CEREMONY_OF_CHAOS_PROHIBITED_PENALTIES, 0) >= 30);
13547 }
13548
13549 /**
13550 * @return Side of the player.
13551 */
13552 public CastleSide getPlayerSide()
13553 {
13554 if ((getClan() == null) || (getClan().getCastleId() == 0))
13555 {
13556 return CastleSide.NEUTRAL;
13557 }
13558 return CastleManager.getInstance().getCastleById(getClan().getCastleId()).getSide();
13559 }
13560
13561 /**
13562 * @return {@code true} if player is on Dark side, {@code false} otherwise.
13563 */
13564 public boolean isOnDarkSide()
13565 {
13566 return getPlayerSide() == CastleSide.DARK;
13567 }
13568
13569 /**
13570 * @return {@code true} if player is on Light side, {@code false} otherwise.
13571 */
13572 public boolean isOnLightSide()
13573 {
13574 return getPlayerSide() == CastleSide.LIGHT;
13575 }
13576
13577 /**
13578 * @return the maximum amount of points that player can use
13579 */
13580 public int getMaxSummonPoints()
13581 {
13582 return (int) getStat().getValue(Stats.MAX_SUMMON_POINTS, 0);
13583 }
13584
13585 /**
13586 * @return the amount of points that player used
13587 */
13588 public int getSummonPoints()
13589 {
13590 return getServitors().values().stream().mapToInt(L2Summon::getSummonPoints).sum();
13591 }
13592
13593 /**
13594 * @param request
13595 * @return {@code true} if the request was registered successfully, {@code false} otherwise.
13596 */
13597 public boolean addRequest(AbstractRequest request)
13598 {
13599 if (_requests == null)
13600 {
13601 synchronized (this)
13602 {
13603 if (_requests == null)
13604 {
13605 _requests = new ConcurrentHashMap<>();
13606 }
13607 }
13608 }
13609 return canRequest(request) && (_requests.putIfAbsent(request.getClass(), request) == null);
13610 }
13611
13612 public boolean canRequest(AbstractRequest request)
13613 {
13614 return (_requests != null) && _requests.values().stream().allMatch(request::canWorkWith);
13615 }
13616
13617 /**
13618 * @param clazz
13619 * @return {@code true} if request was successfully removed, {@code false} in case processing set is not created or not containing the request.
13620 */
13621 public boolean removeRequest(Class<? extends AbstractRequest> clazz)
13622 {
13623 return (_requests != null) && (_requests.remove(clazz) != null);
13624 }
13625
13626 /**
13627 * @param <T>
13628 * @param requestClass
13629 * @return object that is instance of {@code requestClass} param, {@code null} if not instance or not set.
13630 */
13631 public <T extends AbstractRequest> T getRequest(Class<T> requestClass)
13632 {
13633 return _requests != null ? requestClass.cast(_requests.get(requestClass)) : null;
13634 }
13635
13636 /**
13637 * @return {@code true} if player has any processing request set, {@code false} otherwise.
13638 */
13639 public boolean hasRequests()
13640 {
13641 return (_requests != null) && !_requests.isEmpty();
13642 }
13643
13644 public boolean hasItemRequest()
13645 {
13646 return (_requests != null) && _requests.values().stream().anyMatch(AbstractRequest::isItemRequest);
13647 }
13648
13649 /**
13650 * @param requestClass
13651 * @param classes
13652 * @return {@code true} if player has the provided request and processing it, {@code false} otherwise.
13653 */
13654 @SafeVarargs
13655 public final boolean hasRequest(Class<? extends AbstractRequest> requestClass, Class<? extends AbstractRequest>... classes)
13656 {
13657 if (_requests != null)
13658 {
13659 for (Class<? extends AbstractRequest> clazz : classes)
13660 {
13661 if (_requests.containsKey(clazz))
13662 {
13663 return true;
13664 }
13665 }
13666 return _requests.containsKey(requestClass);
13667 }
13668 return false;
13669 }
13670
13671 /**
13672 * @param objectId
13673 * @return {@code true} if item object id is currently in use by some request, {@code false} otherwise.
13674 */
13675 public boolean isProcessingItem(int objectId)
13676 {
13677 return (_requests != null) && _requests.values().stream().anyMatch(req -> req.isUsing(objectId));
13678 }
13679
13680 /**
13681 * Removing all requests associated with the item object id provided.
13682 * @param objectId
13683 */
13684 public void removeRequestsThatProcessesItem(int objectId)
13685 {
13686 if (_requests != null)
13687 {
13688 _requests.values().removeIf(req -> req.isUsing(objectId));
13689 }
13690 }
13691
13692 /**
13693 * @return the prime shop points of the player.
13694 */
13695 public int getPrimePoints()
13696 {
13697 return getAccountVariables().getInt("PRIME_POINTS", 0);
13698 }
13699
13700 /**
13701 * Sets prime shop for current player.
13702 * @param points
13703 */
13704 public void setPrimePoints(int points)
13705 {
13706 // Immediate store upon change
13707 final AccountVariables vars = getAccountVariables();
13708 vars.set("PRIME_POINTS", Math.max(points, 0));
13709 vars.storeMe();
13710 }
13711
13712 /**
13713 * Gets the last commission infos.
13714 * @return the last commission infos
13715 */
13716 public Map<Integer, ExResponseCommissionInfo> getLastCommissionInfos()
13717 {
13718 if (_lastCommissionInfos == null)
13719 {
13720 synchronized (this)
13721 {
13722 if (_lastCommissionInfos == null)
13723 {
13724 _lastCommissionInfos = new ConcurrentHashMap<>();
13725 }
13726 }
13727 }
13728 return _lastCommissionInfos;
13729 }
13730
13731 /**
13732 * Gets the whisperers.
13733 * @return the whisperers
13734 */
13735 public Set<Integer> getWhisperers()
13736 {
13737 return _whisperers;
13738 }
13739
13740 public MatchingRoom getMatchingRoom()
13741 {
13742 return _matchingRoom;
13743 }
13744
13745 public void setMatchingRoom(MatchingRoom matchingRoom)
13746 {
13747 _matchingRoom = matchingRoom;
13748 }
13749
13750 public boolean isInMatchingRoom()
13751 {
13752 return _matchingRoom != null;
13753 }
13754
13755 public int getVitalityItemsUsed()
13756 {
13757 return getVariables().getInt(PlayerVariables.VITALITY_ITEMS_USED_VARIABLE_NAME, 0);
13758 }
13759
13760 public void setVitalityItemsUsed(int used)
13761 {
13762 final PlayerVariables vars = getVariables();
13763 vars.set(PlayerVariables.VITALITY_ITEMS_USED_VARIABLE_NAME, used);
13764 vars.storeMe();
13765 }
13766
13767 @Override
13768 public boolean isVisibleFor(L2PcInstance player)
13769 {
13770 return (super.isVisibleFor(player) || ((player.getParty() != null) && (player.getParty() == getParty())));
13771 }
13772
13773 /**
13774 * @param fullCommand
13775 */
13776 public void useAdminCommand(String fullCommand)
13777 {
13778 final String command = fullCommand.split(" ")[0];
13779
13780 final IAdminCommandHandler ach = AdminCommandHandler.getInstance().getHandler(command);
13781 if (ach == null)
13782 {
13783 if (isGM())
13784 {
13785 sendMessage("The command " + command.substring(6) + " does not exist!");
13786 }
13787 LOGGER.warning("No handler registered for admin command '" + command + "'");
13788 return;
13789 }
13790
13791 if (!AdminData.getInstance().hasAccess(command, getAccessLevel()))
13792 {
13793 sendMessage("You don't have the access rights to use this command!");
13794 LOGGER.warning("Character " + getName() + " tried to use admin command " + command + ", without proper access level!");
13795 return;
13796 }
13797
13798 if (AdminData.getInstance().requireConfirm(command))
13799 {
13800 setAdminConfirmCmd(fullCommand);
13801 final ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.S1_3);
13802 dlg.addString("Are you sure you want execute command " + fullCommand.substring(6) + " ?");
13803 addAction(PlayerAction.ADMIN_COMMAND);
13804 sendPacket(dlg);
13805 }
13806 else
13807 {
13808 if (Config.GMAUDIT)
13809 {
13810 GMAudit.auditGMAction(getName() + " [" + getObjectId() + "]", fullCommand, (getTarget() != null ? getTarget().getName() : "no-target"));
13811 }
13812
13813 ach.useAdminCommand(fullCommand, this);
13814 }
13815 }
13816
13817 /**
13818 * Set the Quest zone ID.
13819 * @param id the quest zone ID
13820 */
13821 public void setQuestZoneId(int id)
13822 {
13823 _questZoneId = id;
13824 }
13825
13826 /**
13827 * Gets the Quest zone ID.
13828 * @return int the quest zone ID
13829 */
13830 public int getQuestZoneId()
13831 {
13832 return _questZoneId;
13833 }
13834
13835 /**
13836 * @param iu
13837 */
13838 public void sendInventoryUpdate(InventoryUpdate iu)
13839 {
13840 sendPacket(iu);
13841 sendPacket(new ExAdenaInvenCount(this));
13842 sendPacket(new ExUserInfoInvenWeight(this));
13843 }
13844
13845 /**
13846 * @param open
13847 */
13848 public void sendItemList(boolean open)
13849 {
13850 sendPacket(new ItemList(this, open));
13851 sendPacket(new ExQuestItemList(this));
13852 sendPacket(new ExAdenaInvenCount(this));
13853 sendPacket(new ExUserInfoInvenWeight(this));
13854 }
13855
13856 /**
13857 * @param event
13858 * @return {@code true} if event is successfuly registered, {@code false} in case events map is not initialized yet or event is not registered
13859 */
13860 public boolean registerOnEvent(AbstractEvent<?> event)
13861 {
13862 if (_events == null)
13863 {
13864 synchronized (this)
13865 {
13866 if (_events == null)
13867 {
13868 _events = new ConcurrentHashMap<>();
13869 }
13870 }
13871 }
13872 return _events.putIfAbsent(event.getClass(), event) == null;
13873 }
13874
13875 /**
13876 * @param event
13877 * @return {@code true} if event is successfuly removed, {@code false} in case events map is not initialized yet or event is not registered
13878 */
13879 public boolean removeFromEvent(AbstractEvent<?> event)
13880 {
13881 if (_events == null)
13882 {
13883 return false;
13884 }
13885 return _events.remove(event.getClass()) != null;
13886 }
13887
13888 /**
13889 * @param <T>
13890 * @param clazz
13891 * @return the event instance or null in case events map is not initialized yet or event is not registered
13892 */
13893 public <T extends AbstractEvent<?>> T getEvent(Class<T> clazz)
13894 {
13895 if (_events == null)
13896 {
13897 return null;
13898 }
13899
13900 return _events.values().stream().filter(event -> clazz.isAssignableFrom(event.getClass())).map(clazz::cast).findFirst().orElse(null);
13901 }
13902
13903 /**
13904 * @return the first event that player participates on or null if he doesn't
13905 */
13906 public AbstractEvent<?> getEvent()
13907 {
13908 if (_events == null)
13909 {
13910 return null;
13911 }
13912
13913 return _events.values().stream().findFirst().orElse(null);
13914 }
13915
13916 /**
13917 * @param clazz
13918 * @return {@code true} if player is registered on specified event, {@code false} in case events map is not initialized yet or event is not registered
13919 */
13920 public boolean isOnEvent(Class<? extends AbstractEvent<?>> clazz)
13921 {
13922 if (_events == null)
13923 {
13924 return false;
13925 }
13926
13927 return _events.containsKey(clazz);
13928 }
13929
13930 public void setAttackerObjId(int attackerObjId)
13931 {
13932 _attackerObjId = attackerObjId;
13933 }
13934
13935 public int getAttackerObjId()
13936 {
13937 return _attackerObjId;
13938 }
13939
13940 public Fishing getFishing()
13941 {
13942 return _fishing;
13943 }
13944
13945 public boolean isFishing()
13946 {
13947 return _fishing.isFishing();
13948 }
13949
13950 @Override
13951 public MoveType getMoveType()
13952 {
13953 if (isSitting())
13954 {
13955 return MoveType.SITTING;
13956 }
13957 return super.getMoveType();
13958 }
13959
13960 private void startOnlineTimeUpdateTask()
13961 {
13962 if (_onlineTimeUpdateTask != null)
13963 {
13964 stopOnlineTimeUpdateTask();
13965 }
13966
13967 _onlineTimeUpdateTask = ThreadPool.scheduleAtFixedRate(this::updateOnlineTime, 60 * 1000L, 60 * 1000L);
13968 }
13969
13970 private void updateOnlineTime()
13971 {
13972 final L2Clan clan = getClan();
13973 if (clan != null)
13974 {
13975 clan.addMemberOnlineTime(this);
13976 }
13977 }
13978
13979 private void stopOnlineTimeUpdateTask()
13980 {
13981 if (_onlineTimeUpdateTask != null)
13982 {
13983 _onlineTimeUpdateTask.cancel(true);
13984 _onlineTimeUpdateTask = null;
13985 }
13986 }
13987
13988 public GroupType getGroupType()
13989 {
13990 return isInParty() ? (getParty().isInCommandChannel() ? GroupType.COMMAND_CHANNEL : GroupType.PARTY) : GroupType.NONE;
13991 }
13992
13993 public boolean isTrueHero()
13994 {
13995 return _trueHero;
13996 }
13997
13998 public void setTrueHero(boolean val)
13999 {
14000 _trueHero = val;
14001 }
14002
14003 public int getFactionPoints(Faction faction)
14004 {
14005 return getVariables().getInt(faction.toString(), 0);
14006 }
14007
14008 public int getFactionLevel(Faction faction)
14009 {
14010 final int currentPoints = getFactionPoints(faction);
14011 for (int i = 0; i < faction.getLevelCount(); i++)
14012 {
14013 if (currentPoints <= faction.getPointsOfLevel(i))
14014 {
14015 return i;
14016 }
14017 }
14018 return 0;
14019 }
14020
14021 public float getFactionProgress(Faction faction)
14022 {
14023 final int currentLevel = getFactionLevel(faction);
14024 final int currentLevelPoints = getFactionPoints(faction);
14025 final int previousLevelPoints = faction.getPointsOfLevel(currentLevel - 1);
14026 final int nextLevelPoints = faction.getPointsOfLevel(currentLevel + 1);
14027 return (float) (currentLevelPoints - previousLevelPoints) / (nextLevelPoints - previousLevelPoints);
14028 }
14029
14030 public void addFactionPoints(Faction faction, int count)
14031 {
14032 final int currentPoints = getFactionPoints(faction);
14033 final int oldLevel = getFactionLevel(faction);
14034 if ((currentPoints + count) < faction.getPointsOfLevel(faction.getLevelCount() - 1))
14035 {
14036 getVariables().set(faction.toString(), currentPoints + count);
14037 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_OBTAINED_S1_FACTION_POINTS_FOR_S2).addInt(count).addFactionName(faction.getId()));
14038 }
14039 else
14040 {
14041 getVariables().set(faction.toString(), faction.getPointsOfLevel(faction.getLevelCount() - 1));
14042
14043 }
14044 if (oldLevel < getFactionLevel(faction))
14045 {
14046 sendPacket(SystemMessage.getSystemMessage(SystemMessageId.THE_FACTION_LEVEL_OF_S1_HAS_INCREASED_OPEN_THE_FACTIONS_WINDOW_TO_CHECK).addFactionName(faction.getId()));
14047 }
14048 }
14049
14050 public int getMonsterBookKillCount(int cardId)
14051 {
14052 return getVariables().getInt(MONSTER_BOOK_KILLS_VAR + cardId, 0);
14053 }
14054
14055 public int getMonsterBookRewardLevel(int cardId)
14056 {
14057 return getVariables().getInt(MONSTER_BOOK_LEVEL_VAR + cardId, 0);
14058 }
14059
14060 public void updateMonsterBook(MonsterBookCardHolder card)
14061 {
14062 final int killCount = getMonsterBookKillCount(card.getId());
14063 if (killCount < card.getReward(3).getKills()) // no point adding kills when player has reached max
14064 {
14065 getVariables().set(MONSTER_BOOK_KILLS_VAR + card.getId(), killCount + 1);
14066 sendPacket(new ExMonsterBookCloseForce()); // in case it is open
14067 final int rewardLevel = getMonsterBookRewardLevel(card.getId());
14068 if ((getMonsterBookKillCount(card.getId()) >= card.getReward(rewardLevel).getKills()) && (rewardLevel < 4)) // make sure player can be rewarded
14069 {
14070 sendPacket(new ExMonsterBookRewardIcon());
14071 }
14072 }
14073 }
14074
14075 public void rewardMonsterBook(int cardId)
14076 {
14077 final int rewardLevel = getMonsterBookRewardLevel(cardId);
14078 final MonsterBookCardHolder card = MonsterBookData.getInstance().getMonsterBookCardById(cardId);
14079 final MonsterBookRewardHolder reward = card.getReward(rewardLevel);
14080 if ((getMonsterBookKillCount(cardId) >= reward.getKills()) && (rewardLevel < 4)) // make sure player can be rewarded
14081 {
14082 getVariables().set(MONSTER_BOOK_LEVEL_VAR + cardId, rewardLevel + 1);
14083 addExpAndSp(reward.getExp(), reward.getSp());
14084 addFactionPoints(card.getFaction(), reward.getPoints());
14085 sendPacket(new ExMonsterBook(this));
14086 }
14087 }
14088
14089 @Override
14090 protected void initStatusUpdateCache()
14091 {
14092 super.initStatusUpdateCache();
14093 addStatusUpdateValue(StatusUpdateType.LEVEL);
14094 addStatusUpdateValue(StatusUpdateType.MAX_CP);
14095 addStatusUpdateValue(StatusUpdateType.CUR_CP);
14096 }
14097
14098 public boolean tryLuck()
14099 {
14100 if (((Rnd.nextDouble() * 100) < BaseStats.LUC.getValue(getLUC())) && !hasSkillReuse(CommonSkill.LUCKY_CLOVER.getSkill().getReuseHashCode()))
14101 {
14102 SkillCaster.triggerCast(this, this, CommonSkill.LUCKY_CLOVER.getSkill());
14103 sendPacket(SystemMessageId.LADY_LUCK_SMILES_UPON_YOU);
14104 return true;
14105 }
14106 return false;
14107 }
14108
14109 public TrainingHolder getTraingCampInfo()
14110 {
14111 final String info = getAccountVariables().getString(TRAINING_CAMP_VAR, null);
14112 if (info == null)
14113 {
14114 return null;
14115 }
14116 return new TrainingHolder(Integer.parseInt(info.split(";")[0]), Integer.parseInt(info.split(";")[1]), Integer.parseInt(info.split(";")[2]), Long.parseLong(info.split(";")[3]), Long.parseLong(info.split(";")[4]));
14117 }
14118
14119 public void setTraingCampInfo(TrainingHolder holder)
14120 {
14121 getAccountVariables().set(TRAINING_CAMP_VAR, holder.getObjectId() + ";" + holder.getClassIndex() + ";" + holder.getLevel() + ";" + holder.getStartTime() + ";" + holder.getEndTime());
14122 }
14123
14124 public void removeTraingCampInfo()
14125 {
14126 getAccountVariables().remove(TRAINING_CAMP_VAR);
14127 }
14128
14129 public long getTraingCampDuration()
14130 {
14131 return getAccountVariables().getLong(TRAINING_CAMP_DURATION, 0L);
14132 }
14133
14134 public void setTraingCampDuration(long duration)
14135 {
14136 getAccountVariables().set(TRAINING_CAMP_DURATION, duration);
14137 }
14138
14139 public void resetTraingCampDuration()
14140 {
14141 getAccountVariables().remove(TRAINING_CAMP_DURATION);
14142 }
14143
14144 public boolean isInTraingCamp()
14145 {
14146 final TrainingHolder trainingHolder = getTraingCampInfo();
14147 return (trainingHolder != null) && (trainingHolder.getEndTime() > 0);
14148 }
14149
14150 public AttendanceInfoHolder getAttendanceInfo()
14151 {
14152 // Get reset time.
14153 final Calendar calendar = Calendar.getInstance();
14154 if ((calendar.get(Calendar.HOUR_OF_DAY) < 6) && (calendar.get(Calendar.MINUTE) < 30))
14155 {
14156 calendar.add(Calendar.DAY_OF_MONTH, -1);
14157 }
14158 calendar.set(Calendar.HOUR_OF_DAY, 6);
14159 calendar.set(Calendar.MINUTE, 30);
14160 calendar.set(Calendar.SECOND, 0);
14161 calendar.set(Calendar.MILLISECOND, 0);
14162
14163 // Get last player reward time.
14164 final long receiveDate;
14165 int rewardIndex;
14166 if (Config.ATTENDANCE_REWARDS_SHARE_ACCOUNT)
14167 {
14168 receiveDate = getAccountVariables().getLong(ATTENDANCE_DATE_VAR, 0);
14169 rewardIndex = getAccountVariables().getInt(ATTENDANCE_INDEX_VAR, 0);
14170 }
14171 else
14172 {
14173 receiveDate = getVariables().getLong(ATTENDANCE_DATE_VAR, 0);
14174 rewardIndex = getVariables().getInt(ATTENDANCE_INDEX_VAR, 0);
14175 }
14176
14177 // Check if player can receive reward today.
14178 boolean canBeRewarded = false;
14179 if (calendar.getTimeInMillis() > receiveDate)
14180 {
14181 canBeRewarded = true;
14182 // Reset index if max is reached.
14183 if (rewardIndex >= AttendanceRewardData.getInstance().getRewardsCount())
14184 {
14185 rewardIndex = 0;
14186 }
14187 }
14188
14189 return new AttendanceInfoHolder(rewardIndex, canBeRewarded);
14190 }
14191
14192 public void setAttendanceInfo(int rewardIndex)
14193 {
14194 if (Config.ATTENDANCE_REWARDS_SHARE_ACCOUNT)
14195 {
14196 getAccountVariables().set(ATTENDANCE_DATE_VAR, System.currentTimeMillis());
14197 getAccountVariables().set(ATTENDANCE_INDEX_VAR, rewardIndex);
14198 }
14199 else
14200 {
14201 getVariables().set(ATTENDANCE_DATE_VAR, System.currentTimeMillis());
14202 getVariables().set(ATTENDANCE_INDEX_VAR, rewardIndex);
14203 }
14204 }
14205}