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