/* * Copyright (C) 2004-2015 L2J Server * * This file is part of L2J Server. * * L2J Server is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * L2J Server is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see <http://www.gnu.org/licenses/>. */ package com.l2jserver.gameserver.model.actor.instance; import java.sql.Connection; import java.sql.Date; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.Arrays; import java.util.Calendar; import java.util.Collection; import java.util.Collections; import java.util.HashMap; import java.util.HashSet; import java.util.Iterator; import java.util.LinkedHashMap; import java.util.LinkedList; import java.util.List; import java.util.Map; import java.util.Map.Entry; import java.util.Set; import java.util.concurrent.ConcurrentHashMap; import java.util.concurrent.ConcurrentSkipListMap; import java.util.concurrent.CopyOnWriteArrayList; import java.util.concurrent.Future; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.locks.ReentrantLock; import java.util.logging.Level; import com.l2jserver.Config; import com.l2jserver.L2DatabaseFactory; import com.l2jserver.gameserver.GameTimeController; import com.l2jserver.gameserver.GeoData; import com.l2jserver.gameserver.ItemsAutoDestroy; import com.l2jserver.gameserver.LoginServerThread; import com.l2jserver.gameserver.RecipeController; import com.l2jserver.gameserver.ThreadPoolManager; import com.l2jserver.gameserver.ai.CtrlIntention; import com.l2jserver.gameserver.ai.L2CharacterAI; import com.l2jserver.gameserver.ai.L2PlayerAI; import com.l2jserver.gameserver.ai.L2SummonAI; import com.l2jserver.gameserver.cache.WarehouseCacheManager; import com.l2jserver.gameserver.communitybbs.BB.Forum; import com.l2jserver.gameserver.communitybbs.Manager.ForumsBBSManager; import com.l2jserver.gameserver.data.sql.impl.CharNameTable; import com.l2jserver.gameserver.data.sql.impl.CharSummonTable; import com.l2jserver.gameserver.data.sql.impl.ClanTable; import com.l2jserver.gameserver.data.xml.impl.AdminData; import com.l2jserver.gameserver.data.xml.impl.ClassListData; import com.l2jserver.gameserver.data.xml.impl.EnchantSkillGroupsData; import com.l2jserver.gameserver.data.xml.impl.ExperienceData; import com.l2jserver.gameserver.data.xml.impl.FishData; import com.l2jserver.gameserver.data.xml.impl.HennaData; import com.l2jserver.gameserver.data.xml.impl.NpcData; import com.l2jserver.gameserver.data.xml.impl.PetDataTable; import com.l2jserver.gameserver.data.xml.impl.PlayerTemplateData; import com.l2jserver.gameserver.data.xml.impl.PlayerXpPercentLostData; import com.l2jserver.gameserver.data.xml.impl.RecipeData; import com.l2jserver.gameserver.data.xml.impl.SkillTreesData; import com.l2jserver.gameserver.datatables.ItemTable; import com.l2jserver.gameserver.datatables.SkillData; import com.l2jserver.gameserver.enums.CastleSide; import com.l2jserver.gameserver.enums.CategoryType; import com.l2jserver.gameserver.enums.ChatType; import com.l2jserver.gameserver.enums.HtmlActionScope; import com.l2jserver.gameserver.enums.IllegalActionPunishmentType; import com.l2jserver.gameserver.enums.InstanceType; import com.l2jserver.gameserver.enums.MountType; import com.l2jserver.gameserver.enums.PartyDistributionType; import com.l2jserver.gameserver.enums.PartySmallWindowUpdateType; import com.l2jserver.gameserver.enums.PlayerAction; import com.l2jserver.gameserver.enums.PrivateStoreType; import com.l2jserver.gameserver.enums.Race; import com.l2jserver.gameserver.enums.Sex; import com.l2jserver.gameserver.enums.ShortcutType; import com.l2jserver.gameserver.enums.ShotType; import com.l2jserver.gameserver.enums.SubclassInfoType; import com.l2jserver.gameserver.enums.Team; import com.l2jserver.gameserver.enums.UserInfoType; import com.l2jserver.gameserver.handler.IItemHandler; import com.l2jserver.gameserver.handler.ItemHandler; import com.l2jserver.gameserver.idfactory.IdFactory; import com.l2jserver.gameserver.instancemanager.AntiFeedManager; import com.l2jserver.gameserver.instancemanager.CastleManager; import com.l2jserver.gameserver.instancemanager.CoupleManager; import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager; import com.l2jserver.gameserver.instancemanager.DuelManager; import com.l2jserver.gameserver.instancemanager.FortManager; import com.l2jserver.gameserver.instancemanager.FortSiegeManager; import com.l2jserver.gameserver.instancemanager.GrandBossManager; import com.l2jserver.gameserver.instancemanager.HandysBlockCheckerManager; import com.l2jserver.gameserver.instancemanager.InstanceManager; import com.l2jserver.gameserver.instancemanager.ItemsOnGroundManager; import com.l2jserver.gameserver.instancemanager.MentorManager; import com.l2jserver.gameserver.instancemanager.PremiumManager; import com.l2jserver.gameserver.instancemanager.PunishmentManager; import com.l2jserver.gameserver.instancemanager.QuestManager; import com.l2jserver.gameserver.instancemanager.SiegeManager; import com.l2jserver.gameserver.instancemanager.ZoneManager; import com.l2jserver.gameserver.model.ArenaParticipantsHolder; import com.l2jserver.gameserver.model.BlockList; import com.l2jserver.gameserver.model.ClanPrivilege; import com.l2jserver.gameserver.model.L2AccessLevel; import com.l2jserver.gameserver.model.L2Clan; import com.l2jserver.gameserver.model.L2ClanMember; import com.l2jserver.gameserver.model.L2ContactList; import com.l2jserver.gameserver.model.L2EnchantSkillLearn; import com.l2jserver.gameserver.model.L2ManufactureItem; import com.l2jserver.gameserver.model.L2Object; import com.l2jserver.gameserver.model.L2Party; import com.l2jserver.gameserver.model.L2Party.messageType; import com.l2jserver.gameserver.model.L2PetData; import com.l2jserver.gameserver.model.L2PetLevelData; import com.l2jserver.gameserver.model.L2PremiumItem; import com.l2jserver.gameserver.model.L2Radar; import com.l2jserver.gameserver.model.L2RecipeList; import com.l2jserver.gameserver.model.L2Request; import com.l2jserver.gameserver.model.L2SkillLearn; import com.l2jserver.gameserver.model.L2World; import com.l2jserver.gameserver.model.L2WorldRegion; import com.l2jserver.gameserver.model.Location; import com.l2jserver.gameserver.model.Macro; import com.l2jserver.gameserver.model.MacroList; import com.l2jserver.gameserver.model.PartyMatchRoom; import com.l2jserver.gameserver.model.PartyMatchRoomList; import com.l2jserver.gameserver.model.PartyMatchWaitingList; import com.l2jserver.gameserver.model.PcCondOverride; import com.l2jserver.gameserver.model.ShortCuts; import com.l2jserver.gameserver.model.Shortcut; import com.l2jserver.gameserver.model.TeleportBookmark; import com.l2jserver.gameserver.model.TeleportWhereType; import com.l2jserver.gameserver.model.TimeStamp; import com.l2jserver.gameserver.model.TradeList; import com.l2jserver.gameserver.model.UIKeysSettings; import com.l2jserver.gameserver.model.actor.L2Attackable; import com.l2jserver.gameserver.model.actor.L2Character; import com.l2jserver.gameserver.model.actor.L2Decoy; import com.l2jserver.gameserver.model.actor.L2Npc; import com.l2jserver.gameserver.model.actor.L2Playable; import com.l2jserver.gameserver.model.actor.L2Summon; import com.l2jserver.gameserver.model.actor.L2Vehicle; import com.l2jserver.gameserver.model.actor.appearance.PcAppearance; import com.l2jserver.gameserver.model.actor.knownlist.PcKnownList; import com.l2jserver.gameserver.model.actor.request.AbstractRequest; import com.l2jserver.gameserver.model.actor.stat.PcStat; import com.l2jserver.gameserver.model.actor.status.PcStatus; import com.l2jserver.gameserver.model.actor.tasks.player.DismountTask; import com.l2jserver.gameserver.model.actor.tasks.player.FameTask; import com.l2jserver.gameserver.model.actor.tasks.player.GameGuardCheckTask; import com.l2jserver.gameserver.model.actor.tasks.player.InventoryEnableTask; import com.l2jserver.gameserver.model.actor.tasks.player.LookingForFishTask; import com.l2jserver.gameserver.model.actor.tasks.player.PetFeedTask; import com.l2jserver.gameserver.model.actor.tasks.player.PvPFlagTask; import com.l2jserver.gameserver.model.actor.tasks.player.RecoGiveTask; import com.l2jserver.gameserver.model.actor.tasks.player.RentPetTask; import com.l2jserver.gameserver.model.actor.tasks.player.ResetChargesTask; import com.l2jserver.gameserver.model.actor.tasks.player.ResetSoulsTask; import com.l2jserver.gameserver.model.actor.tasks.player.SitDownTask; import com.l2jserver.gameserver.model.actor.tasks.player.StandUpTask; import com.l2jserver.gameserver.model.actor.tasks.player.TeleportWatchdogTask; import com.l2jserver.gameserver.model.actor.tasks.player.WarnUserTakeBreakTask; import com.l2jserver.gameserver.model.actor.tasks.player.WaterTask; import com.l2jserver.gameserver.model.actor.templates.L2PcTemplate; import com.l2jserver.gameserver.model.actor.transform.Transform; import com.l2jserver.gameserver.model.base.ClassId; import com.l2jserver.gameserver.model.base.ClassLevel; import com.l2jserver.gameserver.model.base.PlayerClass; import com.l2jserver.gameserver.model.base.SubClass; import com.l2jserver.gameserver.model.effects.EffectFlag; import com.l2jserver.gameserver.model.effects.L2EffectType; import com.l2jserver.gameserver.model.entity.Castle; import com.l2jserver.gameserver.model.entity.Duel; import com.l2jserver.gameserver.model.entity.Fort; import com.l2jserver.gameserver.model.entity.Friend; import com.l2jserver.gameserver.model.entity.Hero; import com.l2jserver.gameserver.model.entity.Instance; import com.l2jserver.gameserver.model.entity.L2Event; import com.l2jserver.gameserver.model.entity.Siege; import com.l2jserver.gameserver.model.entity.TvTEvent; import com.l2jserver.gameserver.model.events.EventDispatcher; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerEquipItem; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerFameChanged; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerHennaRemove; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerKarmaChanged; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerLogin; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerLogout; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerPKChanged; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerProfessionChange; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerPvPChanged; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerPvPKill; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerSubChange; import com.l2jserver.gameserver.model.events.impl.character.player.OnPlayerTransform; import com.l2jserver.gameserver.model.events.impl.character.player.mentoring.OnPlayerMenteeStatus; import com.l2jserver.gameserver.model.events.impl.character.player.mentoring.OnPlayerMentorStatus; import com.l2jserver.gameserver.model.fishing.L2Fish; import com.l2jserver.gameserver.model.fishing.L2Fishing; import com.l2jserver.gameserver.model.holders.ItemHolder; import com.l2jserver.gameserver.model.holders.PlayerEventHolder; import com.l2jserver.gameserver.model.holders.SkillUseHolder; import com.l2jserver.gameserver.model.interfaces.IEventListener; import com.l2jserver.gameserver.model.interfaces.ILocational; import com.l2jserver.gameserver.model.itemcontainer.Inventory; import com.l2jserver.gameserver.model.itemcontainer.ItemContainer; import com.l2jserver.gameserver.model.itemcontainer.PcAuction; import com.l2jserver.gameserver.model.itemcontainer.PcFreight; import com.l2jserver.gameserver.model.itemcontainer.PcInventory; import com.l2jserver.gameserver.model.itemcontainer.PcRefund; import com.l2jserver.gameserver.model.itemcontainer.PcWarehouse; import com.l2jserver.gameserver.model.itemcontainer.PetInventory; import com.l2jserver.gameserver.model.items.L2Armor; import com.l2jserver.gameserver.model.items.L2EtcItem; import com.l2jserver.gameserver.model.items.L2Henna; import com.l2jserver.gameserver.model.items.L2Item; import com.l2jserver.gameserver.model.items.L2Weapon; import com.l2jserver.gameserver.model.items.instance.L2ItemInstance; import com.l2jserver.gameserver.model.items.type.ActionType; import com.l2jserver.gameserver.model.items.type.ArmorType; import com.l2jserver.gameserver.model.items.type.EtcItemType; import com.l2jserver.gameserver.model.items.type.WeaponType; import com.l2jserver.gameserver.model.multisell.PreparedListContainer; import com.l2jserver.gameserver.model.olympiad.OlympiadGameManager; import com.l2jserver.gameserver.model.olympiad.OlympiadGameTask; import com.l2jserver.gameserver.model.olympiad.OlympiadManager; import com.l2jserver.gameserver.model.punishment.PunishmentAffect; import com.l2jserver.gameserver.model.punishment.PunishmentType; import com.l2jserver.gameserver.model.quest.Quest; import com.l2jserver.gameserver.model.quest.QuestState; import com.l2jserver.gameserver.model.skills.AbnormalType; import com.l2jserver.gameserver.model.skills.BuffInfo; import com.l2jserver.gameserver.model.skills.CommonSkill; import com.l2jserver.gameserver.model.skills.Skill; import com.l2jserver.gameserver.model.skills.targets.L2TargetType; import com.l2jserver.gameserver.model.stats.Formulas; import com.l2jserver.gameserver.model.stats.Stats; import com.l2jserver.gameserver.model.variables.AccountVariables; import com.l2jserver.gameserver.model.variables.PlayerVariables; import com.l2jserver.gameserver.model.zone.L2ZoneType; import com.l2jserver.gameserver.model.zone.ZoneId; import com.l2jserver.gameserver.model.zone.type.L2BattalionZone; import com.l2jserver.gameserver.model.zone.type.L2BossZone; import com.l2jserver.gameserver.network.L2GameClient; import com.l2jserver.gameserver.network.SystemMessageId; import com.l2jserver.gameserver.network.serverpackets.AbstractHtmlPacket; import com.l2jserver.gameserver.network.serverpackets.ActionFailed; import com.l2jserver.gameserver.network.serverpackets.ChangeWaitType; import com.l2jserver.gameserver.network.serverpackets.CharInfo; import com.l2jserver.gameserver.network.serverpackets.ConfirmDlg; import com.l2jserver.gameserver.network.serverpackets.EtcStatusUpdate; import com.l2jserver.gameserver.network.serverpackets.ExAbnormalStatusUpdateFromTarget; import com.l2jserver.gameserver.network.serverpackets.ExAcquireAPSkillList; import com.l2jserver.gameserver.network.serverpackets.ExAdenaInvenCount; import com.l2jserver.gameserver.network.serverpackets.ExAutoSoulShot; import com.l2jserver.gameserver.network.serverpackets.ExDuelUpdateUserInfo; import com.l2jserver.gameserver.network.serverpackets.ExFishingEnd; import com.l2jserver.gameserver.network.serverpackets.ExFishingStart; import com.l2jserver.gameserver.network.serverpackets.ExGetBookMarkInfoPacket; import com.l2jserver.gameserver.network.serverpackets.ExGetOnAirShip; import com.l2jserver.gameserver.network.serverpackets.ExMagicAttackInfo; import com.l2jserver.gameserver.network.serverpackets.ExOlympiadMode; import com.l2jserver.gameserver.network.serverpackets.ExPledgeCount; import com.l2jserver.gameserver.network.serverpackets.ExPrivateStoreSetWholeMsg; import com.l2jserver.gameserver.network.serverpackets.ExSetCompassZoneCode; import com.l2jserver.gameserver.network.serverpackets.ExStartScenePlayer; import com.l2jserver.gameserver.network.serverpackets.ExStorageMaxCount; import com.l2jserver.gameserver.network.serverpackets.ExSubjobInfo; import com.l2jserver.gameserver.network.serverpackets.ExUseSharedGroupItem; import com.l2jserver.gameserver.network.serverpackets.ExUserInfoAbnormalVisualEffect; import com.l2jserver.gameserver.network.serverpackets.ExUserInfoCubic; import com.l2jserver.gameserver.network.serverpackets.ExUserInfoInvenWeight; import com.l2jserver.gameserver.network.serverpackets.FlyToLocation.FlyType; import com.l2jserver.gameserver.network.serverpackets.GameGuardQuery; import com.l2jserver.gameserver.network.serverpackets.GetOnVehicle; import com.l2jserver.gameserver.network.serverpackets.HennaInfo; import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate; import com.l2jserver.gameserver.network.serverpackets.ItemList; import com.l2jserver.gameserver.network.serverpackets.L2GameServerPacket; import com.l2jserver.gameserver.network.serverpackets.LeaveWorld; import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse; import com.l2jserver.gameserver.network.serverpackets.MyTargetSelected; import com.l2jserver.gameserver.network.serverpackets.NicknameChanged; import com.l2jserver.gameserver.network.serverpackets.ObservationMode; import com.l2jserver.gameserver.network.serverpackets.ObservationReturn; import com.l2jserver.gameserver.network.serverpackets.PartySmallWindowUpdate; import com.l2jserver.gameserver.network.serverpackets.PetInventoryUpdate; import com.l2jserver.gameserver.network.serverpackets.PlaySound; import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListDelete; import com.l2jserver.gameserver.network.serverpackets.PledgeShowMemberListUpdate; import com.l2jserver.gameserver.network.serverpackets.PrivateStoreListBuy; import com.l2jserver.gameserver.network.serverpackets.PrivateStoreListSell; import com.l2jserver.gameserver.network.serverpackets.PrivateStoreManageListBuy; import com.l2jserver.gameserver.network.serverpackets.PrivateStoreManageListSell; import com.l2jserver.gameserver.network.serverpackets.PrivateStoreMsgBuy; import com.l2jserver.gameserver.network.serverpackets.PrivateStoreMsgSell; import com.l2jserver.gameserver.network.serverpackets.RecipeShopMsg; import com.l2jserver.gameserver.network.serverpackets.RecipeShopSellList; import com.l2jserver.gameserver.network.serverpackets.RelationChanged; import com.l2jserver.gameserver.network.serverpackets.Ride; import com.l2jserver.gameserver.network.serverpackets.ServerClose; import com.l2jserver.gameserver.network.serverpackets.SetupGauge; import com.l2jserver.gameserver.network.serverpackets.ShortCutInit; import com.l2jserver.gameserver.network.serverpackets.SkillCoolTime; import com.l2jserver.gameserver.network.serverpackets.SkillList; import com.l2jserver.gameserver.network.serverpackets.Snoop; import com.l2jserver.gameserver.network.serverpackets.SocialAction; import com.l2jserver.gameserver.network.serverpackets.StatusUpdate; import com.l2jserver.gameserver.network.serverpackets.StopMove; import com.l2jserver.gameserver.network.serverpackets.SystemMessage; import com.l2jserver.gameserver.network.serverpackets.TargetSelected; import com.l2jserver.gameserver.network.serverpackets.TargetUnselected; import com.l2jserver.gameserver.network.serverpackets.TradeDone; import com.l2jserver.gameserver.network.serverpackets.TradeOtherDone; import com.l2jserver.gameserver.network.serverpackets.TradeStart; import com.l2jserver.gameserver.network.serverpackets.UserInfo; import com.l2jserver.gameserver.network.serverpackets.ValidateLocation; import com.l2jserver.gameserver.network.serverpackets.friend.L2FriendStatus; import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager; import com.l2jserver.gameserver.util.Broadcast; import com.l2jserver.gameserver.util.FloodProtectors; import com.l2jserver.gameserver.util.Util; import com.l2jserver.util.EnumIntBitmask; import com.l2jserver.util.Rnd; /** * This class represents all player characters in the world.<br> * There is always a client-thread connected to this (except if a player-store is activated upon logout). */ public final class L2PcInstance extends L2Playable { // Character Skill SQL String Definitions: private static final String RESTORE_SKILLS_FOR_CHAR = "SELECT skill_id,skill_level FROM character_skills WHERE charId=? AND class_index=?"; private static final String UPDATE_CHARACTER_SKILL_LEVEL = "UPDATE character_skills SET skill_level=? WHERE skill_id=? AND charId=? AND class_index=?"; private static final String ADD_NEW_SKILLS = "REPLACE INTO character_skills (charId,skill_id,skill_level,class_index) VALUES (?,?,?,?)"; private static final String DELETE_SKILL_FROM_CHAR = "DELETE FROM character_skills WHERE skill_id=? AND charId=? AND class_index=?"; private static final String DELETE_CHAR_SKILLS = "DELETE FROM character_skills WHERE charId=? AND class_index=?"; // Character Skill Save SQL String Definitions: private static final String ADD_SKILL_SAVE = "INSERT INTO character_skills_save (charId,skill_id,skill_level,remaining_time,reuse_delay,systime,restore_type,class_index,buff_index) VALUES (?,?,?,?,?,?,?,?,?)"; private static final String RESTORE_SKILL_SAVE = "SELECT skill_id,skill_level,remaining_time, reuse_delay, systime, restore_type FROM character_skills_save WHERE charId=? AND class_index=? ORDER BY buff_index ASC"; private static final String DELETE_SKILL_SAVE = "DELETE FROM character_skills_save WHERE charId=? AND class_index=?"; // Character Item Reuse Time String Definition: private static final String ADD_ITEM_REUSE_SAVE = "INSERT INTO character_item_reuse_save (charId,itemId,itemObjId,reuseDelay,systime) VALUES (?,?,?,?,?)"; private static final String RESTORE_ITEM_REUSE_SAVE = "SELECT charId,itemId,itemObjId,reuseDelay,systime FROM character_item_reuse_save WHERE charId=?"; private static final String DELETE_ITEM_REUSE_SAVE = "DELETE FROM character_item_reuse_save WHERE charId=?"; // Character Character SQL String Definitions: private static final String INSERT_CHARACTER = "INSERT INTO characters (account_name,charId,char_name,level,maxHp,curHp,maxCp,curCp,maxMp,curMp,face,hairStyle,hairColor,sex,exp,sp,karma,fame,pvpkills,pkkills,clanid,race,classid,deletetime,cancraft,title,title_color,accesslevel,online,clan_privs,wantspeace,base_class,nobless,power_grade,createDate) values (?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?,?)"; 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=?,fame=?,pvpkills=?,pkkills=?,clanid=?,race=?,classid=?,deletetime=?,title=?,title_color=?,accesslevel=?,online=?,clan_privs=?,wantspeace=?,base_class=?,onlinetime=?,nobless=?,power_grade=?,subpledge=?,lvl_joined_academy=?,apprentice=?,sponsor=?,clan_join_expiry_time=?,clan_create_expiry_time=?,char_name=?,death_penalty_level=?,bookmarkslot=?,vitality_points=?,pccafe_points=?,language=?,faction=? WHERE charId=?"; private static final String RESTORE_CHARACTER = "SELECT * FROM characters WHERE charId=?"; // Character Premium System String Definitions: private static final String RESTORE_PREMIUMSERVICE = "SELECT premium_service,enddate FROM account_premium WHERE account_name=?"; // Character Teleport Bookmark: private static final String INSERT_TP_BOOKMARK = "INSERT INTO character_tpbookmark (charId,Id,x,y,z,icon,tag,name) values (?,?,?,?,?,?,?,?)"; private static final String UPDATE_TP_BOOKMARK = "UPDATE character_tpbookmark SET icon=?,tag=?,name=? where charId=? AND Id=?"; private static final String RESTORE_TP_BOOKMARK = "SELECT Id,x,y,z,icon,tag,name FROM character_tpbookmark WHERE charId=?"; private static final String DELETE_TP_BOOKMARK = "DELETE FROM character_tpbookmark WHERE charId=? AND Id=?"; // Character Subclass SQL String Definitions: private static final String RESTORE_CHAR_SUBCLASSES = "SELECT class_id,exp,sp,vitality_points,level,class_index,dual_class FROM character_subclasses WHERE charId=? ORDER BY class_index ASC"; private static final String ADD_CHAR_SUBCLASS = "INSERT INTO character_subclasses (charId,class_id,exp,sp,level,class_index,dual_class) VALUES (?,?,?,?,?,?,?)"; private static final String UPDATE_CHAR_SUBCLASS = "UPDATE character_subclasses SET exp=?,sp=?,vitality_points=?,level=?,class_id=?,dual_class=? WHERE charId=? AND class_index =?"; private static final String DELETE_CHAR_SUBCLASS = "DELETE FROM character_subclasses WHERE charId=? AND class_index=?"; // Character Henna SQL String Definitions: private static final String RESTORE_CHAR_HENNAS = "SELECT slot,symbol_id FROM character_hennas WHERE charId=? AND class_index=?"; private static final String ADD_CHAR_HENNA = "INSERT INTO character_hennas (charId,symbol_id,slot,class_index) VALUES (?,?,?,?)"; private static final String DELETE_CHAR_HENNA = "DELETE FROM character_hennas WHERE charId=? AND slot=? AND class_index=?"; private static final String DELETE_CHAR_HENNAS = "DELETE FROM character_hennas WHERE charId=? AND class_index=?"; // Character Shortcut SQL String Definitions: private static final String DELETE_CHAR_SHORTCUTS = "DELETE FROM character_shortcuts WHERE charId=? AND class_index=?"; // Character Recipe List Save private static final String DELETE_CHAR_RECIPE_SHOP = "DELETE FROM character_recipeshoplist WHERE charId=?"; private static final String INSERT_CHAR_RECIPE_SHOP = "REPLACE INTO character_recipeshoplist (`charId`, `recipeId`, `price`, `index`) VALUES (?, ?, ?, ?)"; private static final String RESTORE_CHAR_RECIPE_SHOP = "SELECT * FROM character_recipeshoplist WHERE charId=? ORDER BY `index`"; private static final String COND_OVERRIDE_KEY = "cond_override"; public static final int ID_NONE = -1; public static final int REQUEST_TIMEOUT = 15; private int _pcBangPoints = 0; public static final String WORLD_CHAT_VARIABLE_NAME = "WORLD_CHAT_POINTS"; private final List<IEventListener> _eventListeners = new CopyOnWriteArrayList<>(); private L2GameClient _client; private final String _accountName; private long _deleteTimer; private Calendar _createDate = Calendar.getInstance(); private String _lang = null; private String _htmlPrefix = null; private volatile boolean _isOnline = false; private long _onlineTime; private long _onlineBeginTime; private long _lastAccess; private long _uptime; private final ReentrantLock _subclassLock = new ReentrantLock(); protected int _baseClass; protected int _activeClass; protected int _classIndex = 0; /** data for mounted pets */ private int _controlItemId; private L2PetData _data; private L2PetLevelData _leveldata; private int _curFeed; protected Future<?> _mountFeedTask; private ScheduledFuture<?> _dismountTask; private boolean _petItems = false; /** The list of sub-classes this character has. */ private Map<Integer, SubClass> _subClasses; private final PcAppearance _appearance; /** The Experience of the L2PcInstance before the last Death Penalty */ private long _expBeforeDeath; /** The Karma of the L2PcInstance (if higher than 0, the name of the L2PcInstance appears in red) */ private int _karma; /** The number of player killed during a PvP (the player killed was PvP Flagged) */ private int _pvpKills; /** The PK counter of the L2PcInstance (= Number of non PvP Flagged player killed) */ private int _pkKills; /** The PvP Flag state of the L2PcInstance (0=White, 1=Purple) */ private byte _pvpFlag; /** The Fame of this L2PcInstance */ private int _fame; private ScheduledFuture<?> _fameTask; private volatile ScheduledFuture<?> _teleportWatchdog; /** The Siege state of the L2PcInstance */ private byte _siegeState = 0; /** The id of castle/fort which the L2PcInstance is registered for siege */ private int _siegeSide = 0; private int _curWeightPenalty = 0; private int _lastCompassZone; // the last compass zone update send to the client private final L2ContactList _contactList = new L2ContactList(this); private int _bookmarkslot = 0; // The Teleport Bookmark Slot private final Map<Integer, TeleportBookmark> _tpbookmarks = new HashMap<>(); private boolean _canFeed; private boolean _isInSiege; private boolean _isInHideoutSiege = false; /** Olympiad */ private boolean _inOlympiadMode = false; private boolean _OlympiadStart = false; private int _olympiadGameId = -1; private int _olympiadSide = -1; /** Olympiad buff count. */ private int _olyBuffsCount = 0; /** Duel */ private boolean _isInDuel = false; private int _duelState = Duel.DUELSTATE_NODUEL; private int _duelId = 0; private SystemMessageId _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL; /** Boat and AirShip */ private L2Vehicle _vehicle = null; private Location _inVehiclePosition; public ScheduledFuture<?> _taskforfish; private MountType _mountType = MountType.NONE; private int _mountNpcId; private int _mountLevel; /** Store object used to summon the strider you are mounting **/ private int _mountObjectID = 0; public int _telemode = 0; private boolean _inCrystallize; private boolean _inCraftMode; private long _offlineShopStart = 0; private Transform _transformation; /** The table containing all L2RecipeList of the L2PcInstance */ private final Map<Integer, L2RecipeList> _dwarvenRecipeBook = new HashMap<>(); private final Map<Integer, L2RecipeList> _commonRecipeBook = new HashMap<>(); /** Premium Items */ private final Map<Integer, L2PremiumItem> _premiumItems = new HashMap<>(); /** True if the L2PcInstance is sitting */ private boolean _waitTypeSitting; /** Location before entering Observer Mode */ private final Location _lastLoc = new Location(0, 0, 0); private boolean _observerMode = false; /** Stored from last ValidatePosition **/ private final Location _lastServerPosition = new Location(0, 0, 0); /** The number of recommendation obtained by the L2PcInstance */ private int _recomHave; // how much I was recommended by others /** The number of recommendation that the L2PcInstance can give */ private int _recomLeft; // how many recommendations I can give to others /** Recommendation task **/ private ScheduledFuture<?> _recoGiveTask; /** Recommendation Two Hours bonus **/ protected boolean _recoTwoHoursGiven = false; private int _vitalityPoints = 140000; private final PcInventory _inventory = new PcInventory(this); private final PcAuction _auctionInventory = new PcAuction(this); private final PcFreight _freight = new PcFreight(this); private PcWarehouse _warehouse; private PcRefund _refund; private PrivateStoreType _privateStoreType = PrivateStoreType.NONE; private TradeList _activeTradeList; private ItemContainer _activeWarehouse; private volatile Map<Integer, L2ManufactureItem> _manufactureItems; private String _storeName = ""; private TradeList _sellList; private TradeList _buyList; // Multisell private PreparedListContainer _currentMultiSell = null; private boolean _noble = false; private boolean _hero = false; /** Premium System */ private boolean _premiumStatus = false; /** Faction System */ private boolean _isGood = false; private boolean _isEvil = false; /** The L2FolkInstance corresponding to the last Folk which one the player talked. */ private L2Npc _lastFolkNpc = null; /** Last NPC Id talked on a quest */ private int _questNpcObject = 0; /** The table containing all Quests began by the L2PcInstance */ private final Map<String, QuestState> _quests = new HashMap<>(); /** The list containing all shortCuts of this player. */ private final ShortCuts _shortCuts = new ShortCuts(this); /** The list containing all macros of this player. */ private final MacroList _macros = new MacroList(this); private final List<L2PcInstance> _snoopListener = new ArrayList<>(); private final List<L2PcInstance> _snoopedPlayer = new ArrayList<>(); // hennas private final L2Henna[] _henna = new L2Henna[3]; private int _hennaSTR; private int _hennaINT; private int _hennaDEX; private int _hennaMEN; private int _hennaWIT; private int _hennaCON; private int _hennaLUC; private int _hennaCHA; /** The Pet of the L2PcInstance */ private L2Summon _pet = null; /** Servitors of the L2PcInstance */ private volatile Map<Integer, L2Summon> _servitors = null; /** The L2Decoy of the L2PcInstance */ private L2Decoy _decoy = null; /** The L2Trap of the L2PcInstance */ private L2TrapInstance _trap = null; /** The L2Agathion of the L2PcInstance */ private int _agathionId = 0; // apparently, a L2PcInstance CAN have both a summon AND a tamed beast at the same time!! // after Freya players can control more than one tamed beast private List<L2TamedBeastInstance> _tamedBeast = null; private boolean _minimapAllowed = false; // client radar // TODO: This needs to be better integrated and saved/loaded private final L2Radar _radar; // Party matching // private int _partymatching = 0; private int _partyroom = 0; // private int _partywait = 0; // Clan related attributes /** The Clan Identifier of the L2PcInstance */ private int _clanId; /** The Clan object of the L2PcInstance */ private L2Clan _clan; /** Apprentice and Sponsor IDs */ private int _apprentice = 0; private int _sponsor = 0; private long _clanJoinExpiryTime; private long _clanCreateExpiryTime; private int _powerGrade = 0; private volatile EnumIntBitmask<ClanPrivilege> _clanPrivileges = new EnumIntBitmask<>(ClanPrivilege.class, false); /** L2PcInstance's pledge class (knight, Baron, etc.) */ private int _pledgeClass = 0; private int _pledgeType = 0; /** Level at which the player joined the clan as an academy member */ private int _lvlJoinedAcademy = 0; private int _wantsPeace = 0; // charges private final AtomicInteger _charges = new AtomicInteger(); private ScheduledFuture<?> _chargeTask = null; // Absorbed Souls private int _souls = 0; private ScheduledFuture<?> _soulTask = null; // WorldPosition used by TARGET_SIGNET_GROUND private Location _currentSkillWorldPosition; private L2AccessLevel _accessLevel; private boolean _messageRefusal = false; // message refusal mode private boolean _silenceMode = false; // silence mode private List<Integer> _silenceModeExcluded; // silence mode private boolean _dietMode = false; // ignore weight penalty private boolean _tradeRefusal = false; // Trade refusal private boolean _exchangeRefusal = false; // Exchange refusal private L2Party _party; PartyDistributionType _partyDistributionType; // this is needed to find the inviting player for Party response // there can only be one active party request at once private L2PcInstance _activeRequester; private long _requestExpireTime = 0; private final L2Request _request = new L2Request(this); private L2ItemInstance _arrowItem; private L2ItemInstance _boltItem; // Used for protection after teleport private long _protectEndTime = 0; private L2ItemInstance _lure = null; public boolean isSpawnProtected() { return _protectEndTime > GameTimeController.getInstance().getGameTicks(); } private long _teleportProtectEndTime = 0; public boolean isTeleportProtected() { return _teleportProtectEndTime > GameTimeController.getInstance().getGameTicks(); } // protects a char from aggro mobs when getting up from fake death private long _recentFakeDeathEndTime = 0; private boolean _isFakeDeath; /** The fists L2Weapon of the L2PcInstance (used when no weapon is equipped) */ private L2Weapon _fistsWeaponItem; private final Map<Integer, String> _chars = new HashMap<>(); // private byte _updateKnownCounter = 0; private int _expertiseArmorPenalty = 0; private int _expertiseWeaponPenalty = 0; private int _expertisePenaltyBonus = 0; private volatile Map<Class<? extends AbstractRequest>, AbstractRequest> _requests; private boolean _isEnchanting = false; private int _activeEnchantItemId = ID_NONE; private int _activeEnchantSupportItemId = ID_NONE; private int _activeEnchantAttrItemId = ID_NONE; private long _activeEnchantTimestamp = 0; private int _firstCompoundOID = -1; private int _secondCompoundOID = -1; private boolean _usingPrimeShop = false; protected boolean _inventoryDisable = false; /** Player's cubics. */ private final Map<Integer, L2CubicInstance> _cubics = new ConcurrentSkipListMap<>(); /** Active shots. */ protected Set<Integer> _activeSoulShots = ConcurrentHashMap.newKeySet(); public final ReentrantLock soulShotLock = new ReentrantLock(); /** Event parameters */ private PlayerEventHolder eventStatus = null; private byte _handysBlockCheckerEventArena = -1; /** new loto ticket **/ private final int _loto[] = new int[5]; // public static int _loto_nums[] = {0,1,2,3,4,5,6,7,8,9,}; /** new race ticket **/ private final int _race[] = new int[2]; private final BlockList _blockList = new BlockList(this); private L2Fishing _fishCombat; private boolean _fishing = false; private int _fishx = 0; private int _fishy = 0; private int _fishz = 0; private volatile Set<Integer> _transformAllowedSkills; private ScheduledFuture<?> _taskRentPet; private ScheduledFuture<?> _taskWater; /** Last Html Npcs, 0 = last html was not bound to an npc */ private final int[] _htmlActionOriginObjectIds = new int[HtmlActionScope.values().length]; /** * Origin of the last incoming html action request.<br> * This can be used for htmls continuing the conversation with an npc. */ private int _lastHtmlActionOriginObjId; /** Bypass validations */ @SuppressWarnings("unchecked") private final LinkedList<String>[] _htmlActionCaches = new LinkedList[HtmlActionScope.values().length]; private Forum _forumMail; private Forum _forumMemo; /** Current skill in use. Note that L2Character has _lastSkillCast, but this has the button presses */ private SkillUseHolder _currentSkill; private SkillUseHolder _currentPetSkill; /** Skills queued because a skill is already in progress */ private SkillUseHolder _queuedSkill; private int _cursedWeaponEquippedId = 0; private boolean _combatFlagEquippedId = false; private boolean _canRevive = true; private int _reviveRequested = 0; private double _revivePower = 0; private boolean _revivePet = false; private double _cpUpdateIncCheck = .0; private double _cpUpdateDecCheck = .0; private double _cpUpdateInterval = .0; private double _mpUpdateIncCheck = .0; private double _mpUpdateDecCheck = .0; private double _mpUpdateInterval = .0; private double _originalCp = .0; private double _originalHp = .0; private double _originalMp = .0; /** Char Coords from Client */ private int _clientX; private int _clientY; private int _clientZ; private int _clientHeading; // during fall validations will be disabled for 10 ms. private static final int FALLING_VALIDATION_DELAY = 10000; private volatile long _fallingTimestamp = 0; private int _multiSocialTarget = 0; private int _multiSociaAction = 0; private int _movieId = 0; private String _adminConfirmCmd = null; private volatile long _lastItemAuctionInfoRequest = 0; private Future<?> _PvPRegTask; private long _pvpFlagLasts; private long _notMoveUntil = 0; /** Map containing all custom skills of this player. */ private Map<Integer, Skill> _customSkills = null; private volatile int _actionMask; /** * Creates a player. * @param objectId the object ID * @param template the player template * @param accountName the account name * @param app the player appearance */ private L2PcInstance(int objectId, L2PcTemplate template, String accountName, PcAppearance app) { super(objectId, template); setInstanceType(InstanceType.L2PcInstance); super.initCharStatusUpdateValues(); initPcStatusUpdateValues(); for (int i = 0; i < _htmlActionCaches.length; ++i) { _htmlActionCaches[i] = new LinkedList<>(); } _accountName = accountName; app.setOwner(this); _appearance = app; // Create an AI getAI(); // Create a L2Radar object _radar = new L2Radar(this); } /** * Creates a player. * @param template the player template * @param accountName the account name * @param app the player appearance */ private L2PcInstance(L2PcTemplate template, String accountName, PcAppearance app) { this(IdFactory.getInstance().getNextId(), template, accountName, app); } public void setPvpFlagLasts(long time) { _pvpFlagLasts = time; } public long getPvpFlagLasts() { return _pvpFlagLasts; } public void startPvPFlag() { updatePvPFlag(1); if (_PvPRegTask == null) { _PvPRegTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new PvPFlagTask(this), 1000, 1000); } } public void stopPvpRegTask() { if (_PvPRegTask != null) { _PvPRegTask.cancel(true); _PvPRegTask = null; } } public void stopPvPFlag() { stopPvpRegTask(); updatePvPFlag(0); _PvPRegTask = null; } // Character UI private UIKeysSettings _uiKeySettings; // L2JMOD Wedding private boolean _married = false; private int _partnerId = 0; private int _coupleId = 0; private boolean _engagerequest = false; private int _engageid = 0; private boolean _marryrequest = false; private boolean _marryaccepted = false; // Save responder name for log it private String _lastPetitionGmName = null; private boolean _hasCharmOfCourage = false; private final Set<Integer> _whisperers = ConcurrentHashMap.newKeySet(); private int _jumpTrackId = 0; /** * Create a new L2PcInstance and add it in the characters table of the database.<br> * <B><U> Actions</U> :</B> * <ul> * <li>Create a new L2PcInstance with an account name</li> * <li>Set the name, the Hair Style, the Hair Color and the Face type of the L2PcInstance</li> * <li>Add the player in the characters table of the database</li> * </ul> * @param template The L2PcTemplate to apply to the L2PcInstance * @param accountName The name of the L2PcInstance * @param name The name of the L2PcInstance * @param app the player's appearance * @return The L2PcInstance added to the database or null */ public static L2PcInstance create(L2PcTemplate template, String accountName, String name, PcAppearance app) { // Create a new L2PcInstance with an account name L2PcInstance player = new L2PcInstance(template, accountName, app); // Set the name of the L2PcInstance player.setName(name); // Set Character's create time player.setCreateDate(Calendar.getInstance()); // Set the base class ID to that of the actual class ID. player.setBaseClass(player.getClassId()); // Give 20 recommendations player.setRecomLeft(20); // Add the player in the characters table of the database return player.createDb() ? player : null; } public String getAccountName() { if (getClient() == null) { return getAccountNamePlayer(); } return getClient().getAccountName(); } public String getAccountNamePlayer() { return _accountName; } public Map<Integer, String> getAccountChars() { return _chars; } public int getRelation(L2PcInstance target) { int result = 0; if (getClan() != null) { result |= RelationChanged.RELATION_CLAN_MEMBER; if (getClan() == target.getClan()) { result |= RelationChanged.RELATION_CLAN_MATE; } if (getAllyId() != 0) { result |= RelationChanged.RELATION_ALLY_MEMBER; } } if (isClanLeader()) { result |= RelationChanged.RELATION_LEADER; } if ((getParty() != null) && (getParty() == target.getParty())) { result |= RelationChanged.RELATION_HAS_PARTY; for (int i = 0; i < getParty().getMembers().size(); i++) { if (getParty().getMembers().get(i) != this) { continue; } switch (i) { case 0: result |= RelationChanged.RELATION_PARTYLEADER; // 0x10 break; case 1: result |= RelationChanged.RELATION_PARTY4; // 0x8 break; case 2: result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x7 break; case 3: result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY2; // 0x6 break; case 4: result |= RelationChanged.RELATION_PARTY3 + RelationChanged.RELATION_PARTY1; // 0x5 break; case 5: result |= RelationChanged.RELATION_PARTY3; // 0x4 break; case 6: result |= RelationChanged.RELATION_PARTY2 + RelationChanged.RELATION_PARTY1; // 0x3 break; case 7: result |= RelationChanged.RELATION_PARTY2; // 0x2 break; case 8: result |= RelationChanged.RELATION_PARTY1; // 0x1 break; } } } if (getSiegeState() != 0) { result |= RelationChanged.RELATION_INSIEGE; if (getSiegeState() != target.getSiegeState()) { result |= RelationChanged.RELATION_ENEMY; } else { result |= RelationChanged.RELATION_ALLY; } if (getSiegeState() == 1) { result |= RelationChanged.RELATION_ATTACKER; } } if ((getClan() != null) && (target.getClan() != null)) { if ((target.getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && (getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && target.getClan().isAtWarWith(getClan().getId())) { result |= RelationChanged.RELATION_1SIDED_WAR; if (getClan().isAtWarWith(target.getClan().getId())) { result |= RelationChanged.RELATION_MUTUAL_WAR; } } } if (getBlockCheckerArena() != -1) { result |= RelationChanged.RELATION_INSIEGE; ArenaParticipantsHolder holder = HandysBlockCheckerManager.getInstance().getHolder(getBlockCheckerArena()); if (holder.getPlayerTeam(this) == 0) { result |= RelationChanged.RELATION_ENEMY; } else { result |= RelationChanged.RELATION_ALLY; } result |= RelationChanged.RELATION_ATTACKER; } return result; } /** * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world (call restore method).<br> * <B><U> Actions</U> :</B> * <ul> * <li>Retrieve the L2PcInstance from the characters table of the database</li> * <li>Add the L2PcInstance object in _allObjects</li> * <li>Set the x,y,z position of the L2PcInstance and make it invisible</li> * <li>Update the overloaded status of the L2PcInstance</li> * </ul> * @param objectId Identifier of the object to initialized * @return The L2PcInstance loaded from the database */ public static L2PcInstance load(int objectId) { return restore(objectId); } private void initPcStatusUpdateValues() { _cpUpdateInterval = getMaxCp() / 352.0; _cpUpdateIncCheck = getMaxCp(); _cpUpdateDecCheck = getMaxCp() - _cpUpdateInterval; _mpUpdateInterval = getMaxMp() / 352.0; _mpUpdateIncCheck = getMaxMp(); _mpUpdateDecCheck = getMaxMp() - _mpUpdateInterval; } @Override public final PcKnownList getKnownList() { return (PcKnownList) super.getKnownList(); } @Override public void initKnownList() { setKnownList(new PcKnownList(this)); } @Override public final PcStat getStat() { return (PcStat) super.getStat(); } @Override public void initCharStat() { setStat(new PcStat(this)); } @Override public final PcStatus getStatus() { return (PcStatus) super.getStatus(); } @Override public void initCharStatus() { setStatus(new PcStatus(this)); } public final PcAppearance getAppearance() { return _appearance; } public final boolean isHairAccessoryEnabled() { return getVariables().getBoolean("hairAccessoryEnabled", true); } public final void setHairAccessoryEnabled(boolean enabled) { getVariables().set("hairAccessoryEnabled", enabled); } /** * @return the base L2PcTemplate link to the L2PcInstance. */ public final L2PcTemplate getBaseTemplate() { return PlayerTemplateData.getInstance().getTemplate(_baseClass); } /** * @return the L2PcTemplate link to the L2PcInstance. */ @Override public final L2PcTemplate getTemplate() { return (L2PcTemplate) super.getTemplate(); } /** * @param newclass */ public void setTemplate(ClassId newclass) { super.setTemplate(PlayerTemplateData.getInstance().getTemplate(newclass)); } @Override protected L2CharacterAI initAI() { return new L2PlayerAI(this); } /** Return the Level of the L2PcInstance. */ @Override public final int getLevel() { return getStat().getLevel(); } @Override public double getLevelMod() { if (isTransformed()) { double levelMod = getTransformation().getLevelMod(this); if (levelMod > -1) { return levelMod; } } return super.getLevelMod(); } public void setBaseClass(int baseClass) { _baseClass = baseClass; } public void setBaseClass(ClassId classId) { _baseClass = classId.ordinal(); } public boolean isInStoreMode() { return getPrivateStoreType() != PrivateStoreType.NONE; } public boolean isInCraftMode() { return _inCraftMode; } public void isInCraftMode(boolean b) { _inCraftMode = b; } /** * Manage Logout Task: * <ul> * <li>Remove player from world</li> * <li>Save player data into DB</li> * </ul> */ public void logout() { logout(true); } /** * Manage Logout Task: * <ul> * <li>Remove player from world</li> * <li>Save player data into DB</li> * </ul> * @param closeClient */ public void logout(boolean closeClient) { try { closeNetConnection(closeClient); } catch (Exception e) { _log.log(Level.WARNING, "Exception on logout(): " + e.getMessage(), e); } } /** * @return a table containing all Common L2RecipeList of the L2PcInstance. */ public L2RecipeList[] getCommonRecipeBook() { return _commonRecipeBook.values().toArray(new L2RecipeList[_commonRecipeBook.values().size()]); } /** * @return a table containing all Dwarf L2RecipeList of the L2PcInstance. */ public L2RecipeList[] getDwarvenRecipeBook() { return _dwarvenRecipeBook.values().toArray(new L2RecipeList[_dwarvenRecipeBook.values().size()]); } /** * Add a new L2RecipList to the table _commonrecipebook containing all L2RecipeList of the L2PcInstance * @param recipe The L2RecipeList to add to the _recipebook * @param saveToDb */ public void registerCommonRecipeList(L2RecipeList recipe, boolean saveToDb) { _commonRecipeBook.put(recipe.getId(), recipe); if (saveToDb) { insertNewRecipeData(recipe.getId(), false); } } /** * Add a new L2RecipList to the table _recipebook containing all L2RecipeList of the L2PcInstance * @param recipe The L2RecipeList to add to the _recipebook * @param saveToDb */ public void registerDwarvenRecipeList(L2RecipeList recipe, boolean saveToDb) { _dwarvenRecipeBook.put(recipe.getId(), recipe); if (saveToDb) { insertNewRecipeData(recipe.getId(), true); } } /** * @param recipeId The Identifier of the L2RecipeList to check in the player's recipe books * @return {@code true}if player has the recipe on Common or Dwarven Recipe book else returns {@code false} */ public boolean hasRecipeList(int recipeId) { return _dwarvenRecipeBook.containsKey(recipeId) || _commonRecipeBook.containsKey(recipeId); } /** * Tries to remove a L2RecipList from the table _DwarvenRecipeBook or from table _CommonRecipeBook, those table contain all L2RecipeList of the L2PcInstance * @param recipeId The Identifier of the L2RecipeList to remove from the _recipebook */ public void unregisterRecipeList(int recipeId) { if (_dwarvenRecipeBook.remove(recipeId) != null) { deleteRecipeData(recipeId, true); } else if (_commonRecipeBook.remove(recipeId) != null) { deleteRecipeData(recipeId, false); } else { _log.warning("Attempted to remove unknown RecipeList: " + recipeId); } for (Shortcut sc : getAllShortCuts()) { if ((sc != null) && (sc.getId() == recipeId) && (sc.getType() == ShortcutType.RECIPE)) { deleteShortCut(sc.getSlot(), sc.getPage()); } } } private void insertNewRecipeData(int recipeId, boolean isDwarf) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("INSERT INTO character_recipebook (charId, id, classIndex, type) values(?,?,?,?)")) { statement.setInt(1, getObjectId()); statement.setInt(2, recipeId); statement.setInt(3, isDwarf ? _classIndex : 0); statement.setInt(4, isDwarf ? 1 : 0); statement.execute(); } catch (SQLException e) { _log.log(Level.WARNING, "SQL exception while inserting recipe: " + recipeId + " from character " + getObjectId(), e); } } private void deleteRecipeData(int recipeId, boolean isDwarf) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("DELETE FROM character_recipebook WHERE charId=? AND id=? AND classIndex=?")) { statement.setInt(1, getObjectId()); statement.setInt(2, recipeId); statement.setInt(3, isDwarf ? _classIndex : 0); statement.execute(); } catch (SQLException e) { _log.log(Level.WARNING, "SQL exception while deleting recipe: " + recipeId + " from character " + getObjectId(), e); } } /** * @return the Id for the last talked quest NPC. */ public int getLastQuestNpcObject() { return _questNpcObject; } public void setLastQuestNpcObject(int npcId) { _questNpcObject = npcId; } /** * @param quest The name of the quest * @return the QuestState object corresponding to the quest name. */ public QuestState getQuestState(String quest) { return _quests.get(quest); } /** * Add a QuestState to the table _quest containing all quests began by the L2PcInstance. * @param qs The QuestState to add to _quest */ public void setQuestState(QuestState qs) { _quests.put(qs.getQuestName(), qs); } /** * Verify if the player has the quest state. * @param quest the quest state to check * @return {@code true} if the player has the quest state, {@code false} otherwise */ public boolean hasQuestState(String quest) { return _quests.containsKey(quest); } /** * Remove a QuestState from the table _quest containing all quests began by the L2PcInstance. * @param quest The name of the quest */ public void delQuestState(String quest) { _quests.remove(quest); } /** * @return List of {@link QuestState}s of the current player. */ public List<QuestState> getAllQuestStates() { return new ArrayList<>(_quests.values()); } /** * @return a table containing all Quest in progress from the table _quests. */ public Quest[] getAllActiveQuests() { List<Quest> quests = new ArrayList<>(); for (QuestState qs : _quests.values()) { if ((qs == null) || (qs.getQuest() == null) || (!qs.isStarted() && !Config.DEVELOPER)) { continue; } final int questId = qs.getQuest().getId(); if ((questId > 19999) || (questId < 1)) { continue; } quests.add(qs.getQuest()); } return quests.toArray(new Quest[quests.size()]); } public void processQuestEvent(String questName, String event) { final Quest quest = QuestManager.getInstance().getQuest(questName); if ((quest == null) || (event == null) || event.isEmpty()) { return; } if (getLastQuestNpcObject() > 0) { final L2Object object = L2World.getInstance().findObject(getLastQuestNpcObject()); if (object.isNpc() && isInsideRadius(object, L2Npc.INTERACTION_DISTANCE, false, false)) { final L2Npc npc = (L2Npc) object; quest.notifyEvent(event, npc, this); } } } /** List of all QuestState instance that needs to be notified of this L2PcInstance's or its pet's death */ private volatile List<QuestState> _notifyQuestOfDeathList; /** * Add QuestState instance that is to be notified of L2PcInstance's death. * @param qs The QuestState that subscribe to this event */ public void addNotifyQuestOfDeath(QuestState qs) { if (qs == null) { return; } if (!getNotifyQuestOfDeath().contains(qs)) { getNotifyQuestOfDeath().add(qs); } } /** * Remove QuestState instance that is to be notified of L2PcInstance's death. * @param qs The QuestState that subscribe to this event */ public void removeNotifyQuestOfDeath(QuestState qs) { if ((qs == null) || (_notifyQuestOfDeathList == null)) { return; } _notifyQuestOfDeathList.remove(qs); } /** * @return a list of QuestStates which registered for notify of death of this L2PcInstance. */ public final List<QuestState> getNotifyQuestOfDeath() { if (_notifyQuestOfDeathList == null) { synchronized (this) { if (_notifyQuestOfDeathList == null) { _notifyQuestOfDeathList = new ArrayList<>(); } } } return _notifyQuestOfDeathList; } public final boolean isNotifyQuestOfDeathEmpty() { return (_notifyQuestOfDeathList == null) || _notifyQuestOfDeathList.isEmpty(); } /** * @return a table containing all L2ShortCut of the L2PcInstance. */ public Shortcut[] getAllShortCuts() { return _shortCuts.getAllShortCuts(); } /** * @param slot The slot in which the shortCuts is equipped * @param page The page of shortCuts containing the slot * @return the L2ShortCut of the L2PcInstance corresponding to the position (page-slot). */ public Shortcut getShortCut(int slot, int page) { return _shortCuts.getShortCut(slot, page); } /** * Add a L2shortCut to the L2PcInstance _shortCuts * @param shortcut */ public void registerShortCut(Shortcut shortcut) { _shortCuts.registerShortCut(shortcut); } /** * Updates the shortcut bars with the new skill. * @param skillId the skill Id to search and update. * @param skillLevel the skill level to update. */ public void updateShortCuts(int skillId, int skillLevel) { _shortCuts.updateShortCuts(skillId, skillLevel); } /** * Delete the L2ShortCut corresponding to the position (page-slot) from the L2PcInstance _shortCuts. * @param slot * @param page */ public void deleteShortCut(int slot, int page) { _shortCuts.deleteShortCut(slot, page); } /** * @param macro the macro to add to this L2PcInstance. */ public void registerMacro(Macro macro) { _macros.registerMacro(macro); } /** * @param id the macro Id to delete. */ public void deleteMacro(int id) { _macros.deleteMacro(id); } /** * @return all L2Macro of the L2PcInstance. */ public MacroList getMacros() { return _macros; } /** * Set the siege state of the L2PcInstance. * @param siegeState 1 = attacker, 2 = defender, 0 = not involved */ public void setSiegeState(byte siegeState) { _siegeState = siegeState; } /** * Get the siege state of the L2PcInstance. * @return 1 = attacker, 2 = defender, 0 = not involved */ @Override public byte getSiegeState() { return _siegeState; } /** * Set the siege Side of the L2PcInstance. * @param val */ public void setSiegeSide(int val) { _siegeSide = val; } public boolean isRegisteredOnThisSiegeField(int val) { if ((_siegeSide != val) && ((_siegeSide < 81) || (_siegeSide > 89))) { return false; } return true; } @Override public int getSiegeSide() { return _siegeSide; } /** * Set the PvP Flag of the L2PcInstance. * @param pvpFlag */ public void setPvpFlag(int pvpFlag) { _pvpFlag = (byte) pvpFlag; } @Override public byte getPvpFlag() { return _pvpFlag; } @Override public void updatePvPFlag(int value) { if (getPvpFlag() == value) { return; } setPvpFlag(value); UserInfo ui = new UserInfo(this, false); ui.addComponentType(UserInfoType.SOCIAL); sendPacket(ui); // If this player has a pet update the pets pvp flag as well if (hasSummon()) { final RelationChanged rc = new RelationChanged(); final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, getRelation(this), false); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, getRelation(this), false)); } sendPacket(rc); } final Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values(); for (L2PcInstance player : plrs) { if ((player == null) || !isVisibleFor(player)) { continue; } final int relation = getRelation(player); Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); if ((oldrelation == null) || (oldrelation != relation)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(this, relation, isAutoAttackable(player)); if (hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation, isAutoAttackable(player)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player))); } } player.sendPacket(rc); getKnownList().getKnownRelations().put(player.getObjectId(), relation); } } } @Override public void revalidateZone(boolean force) { // Cannot validate if not in a world region (happens during teleport) if (getWorldRegion() == null) { return; } // This function is called too often from movement code if (force) { _zoneValidateCounter = 4; } else { _zoneValidateCounter--; if (_zoneValidateCounter < 0) { _zoneValidateCounter = 4; } else { return; } } getWorldRegion().revalidateZones(this); if (Config.ALLOW_WATER) { checkWaterState(); } if (isInsideZone(ZoneId.ALTERED)) { if (_lastCompassZone == ExSetCompassZoneCode.ALTEREDZONE) { return; } _lastCompassZone = ExSetCompassZoneCode.ALTEREDZONE; ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.ALTEREDZONE); sendPacket(cz); } else if (isInsideZone(ZoneId.SIEGE)) { if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2) { return; } _lastCompassZone = ExSetCompassZoneCode.SIEGEWARZONE2; ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.SIEGEWARZONE2); sendPacket(cz); } else if (isInsideZone(ZoneId.PVP)) { if (_lastCompassZone == ExSetCompassZoneCode.PVPZONE) { return; } _lastCompassZone = ExSetCompassZoneCode.PVPZONE; ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.PVPZONE); sendPacket(cz); } else if (isInsideZone(ZoneId.PEACE)) { if (_lastCompassZone == ExSetCompassZoneCode.PEACEZONE) { return; } _lastCompassZone = ExSetCompassZoneCode.PEACEZONE; ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.PEACEZONE); sendPacket(cz); } else { if (_lastCompassZone == ExSetCompassZoneCode.GENERALZONE) { return; } if (_lastCompassZone == ExSetCompassZoneCode.SIEGEWARZONE2) { updatePvPStatus(); } _lastCompassZone = ExSetCompassZoneCode.GENERALZONE; ExSetCompassZoneCode cz = new ExSetCompassZoneCode(ExSetCompassZoneCode.GENERALZONE); sendPacket(cz); } } /** * @return True if the L2PcInstance can Craft Dwarven Recipes. */ public boolean hasDwarvenCraft() { return getSkillLevel(CommonSkill.CREATE_DWARVEN.getId()) >= 1; } public int getDwarvenCraft() { return getSkillLevel(CommonSkill.CREATE_DWARVEN.getId()); } /** * @return True if the L2PcInstance can Craft Dwarven Recipes. */ public boolean hasCommonCraft() { return getSkillLevel(CommonSkill.CREATE_COMMON.getId()) >= 1; } public int getCommonCraft() { return getSkillLevel(CommonSkill.CREATE_COMMON.getId()); } /** * @return the PK counter of the L2PcInstance. */ public int getPkKills() { return _pkKills; } /** * Set the PK counter of the L2PcInstance. * @param pkKills */ public void setPkKills(int pkKills) { EventDispatcher.getInstance().notifyEventAsync(new OnPlayerPKChanged(this, _pkKills, pkKills), this); _pkKills = pkKills; } /** * @return the _deleteTimer of the L2PcInstance. */ public long getDeleteTimer() { return _deleteTimer; } /** * Set the _deleteTimer of the L2PcInstance. * @param deleteTimer */ public void setDeleteTimer(long deleteTimer) { _deleteTimer = deleteTimer; } /** * @return the number of recommendation obtained by the L2PcInstance. */ public int getRecomHave() { return _recomHave; } /** * Increment the number of recommendation obtained by the L2PcInstance (Max : 255). */ protected void incRecomHave() { if (_recomHave < 255) { _recomHave++; } } /** * Set the number of recommendation obtained by the L2PcInstance (Max : 255). * @param value */ public void setRecomHave(int value) { _recomHave = Math.min(Math.max(value, 0), 255); } /** * Set the number of recommendation obtained by the L2PcInstance (Max : 255). * @param value */ public void setRecomLeft(int value) { _recomLeft = Math.min(Math.max(value, 0), 255); } /** * @return the number of recommendation that the L2PcInstance can give. */ public int getRecomLeft() { return _recomLeft; } /** * Increment the number of recommendation that the L2PcInstance can give. */ protected void decRecomLeft() { if (_recomLeft > 0) { _recomLeft--; } } public void giveRecom(L2PcInstance target) { target.incRecomHave(); decRecomLeft(); } /** * Set the exp of the L2PcInstance before a death * @param exp */ public void setExpBeforeDeath(long exp) { _expBeforeDeath = exp; } public long getExpBeforeDeath() { return _expBeforeDeath; } /** * Return the Karma of the L2PcInstance. */ @Override public int getKarma() { return _karma; } /** * Set the Karma of the L2PcInstance and send a Server->Client packet StatusUpdate (broadcast). * @param karma */ public void setKarma(int karma) { // Notify to scripts. EventDispatcher.getInstance().notifyEventAsync(new OnPlayerKarmaChanged(this, getKarma(), karma), this); if (karma < 0) { karma = 0; } if ((_karma == 0) && (karma > 0)) { Collection<L2Object> objs = getKnownList().getKnownObjects().values(); for (L2Object object : objs) { if (!(object instanceof L2GuardInstance)) { continue; } if (((L2GuardInstance) object).getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) { ((L2GuardInstance) object).getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE, null); } } } else if ((_karma > 0) && (karma == 0)) { // Send a Server->Client StatusUpdate packet with Karma and PvP Flag to the L2PcInstance and all L2PcInstance to inform (broadcast) setKarmaFlag(0); } _karma = karma; broadcastKarma(); } public int getExpertiseArmorPenalty() { return _expertiseArmorPenalty; } public int getExpertiseWeaponPenalty() { return _expertiseWeaponPenalty; } public int getExpertisePenaltyBonus() { return _expertisePenaltyBonus; } public void setExpertisePenaltyBonus(int bonus) { _expertisePenaltyBonus = bonus; } public int getWeightPenalty() { if (_dietMode) { return 0; } return _curWeightPenalty; } /** * Update the overloaded status of the L2PcInstance. */ public void refreshOverloaded() { int maxLoad = getMaxLoad(); if (maxLoad > 0) { long weightproc = (((getCurrentLoad() - getBonusWeightPenalty()) * 1000) / getMaxLoad()); int newWeightPenalty; if ((weightproc < 500) || _dietMode) { newWeightPenalty = 0; } else if (weightproc < 666) { newWeightPenalty = 1; } else if (weightproc < 800) { newWeightPenalty = 2; } else if (weightproc < 1000) { newWeightPenalty = 3; } else { newWeightPenalty = 4; } if (_curWeightPenalty != newWeightPenalty) { _curWeightPenalty = newWeightPenalty; if ((newWeightPenalty > 0) && !_dietMode) { addSkill(SkillData.getInstance().getSkill(4270, newWeightPenalty)); setIsOverloaded(getCurrentLoad() > maxLoad); } else { removeSkill(getKnownSkill(4270), false, true); setIsOverloaded(false); } sendPacket(new EtcStatusUpdate(this)); broadcastPacket(new CharInfo(this)); } } } public void refreshExpertisePenalty() { if (!Config.EXPERTISE_PENALTY) { return; } final int expertiseLevel = getExpertiseLevel(); int armorPenalty = 0; int weaponPenalty = 0; int crystaltype; for (L2ItemInstance item : getInventory().getItems()) { if ((item != null) && item.isEquipped() && ((item.getItemType() != EtcItemType.ARROW) && (item.getItemType() != EtcItemType.BOLT))) { crystaltype = item.getItem().getCrystalType().getId(); if (crystaltype > expertiseLevel) { if (item.isWeapon() && (crystaltype > weaponPenalty)) { weaponPenalty = crystaltype; } else if (crystaltype > armorPenalty) { armorPenalty = crystaltype; } } } } boolean changed = false; final int bonus = getExpertisePenaltyBonus(); // calc weapon penalty weaponPenalty = weaponPenalty - expertiseLevel - bonus; weaponPenalty = Math.min(Math.max(weaponPenalty, 0), 4); if ((getExpertiseWeaponPenalty() != weaponPenalty) || (getSkillLevel(CommonSkill.WEAPON_GRADE_PENALTY.getId()) != weaponPenalty)) { _expertiseWeaponPenalty = weaponPenalty; if (_expertiseWeaponPenalty > 0) { addSkill(SkillData.getInstance().getSkill(CommonSkill.WEAPON_GRADE_PENALTY.getId(), _expertiseWeaponPenalty)); } else { removeSkill(getKnownSkill(CommonSkill.WEAPON_GRADE_PENALTY.getId()), false, true); } changed = true; } // calc armor penalty armorPenalty = armorPenalty - expertiseLevel - bonus; armorPenalty = Math.min(Math.max(armorPenalty, 0), 4); if ((getExpertiseArmorPenalty() != armorPenalty) || (getSkillLevel(CommonSkill.ARMOR_GRADE_PENALTY.getId()) != armorPenalty)) { _expertiseArmorPenalty = armorPenalty; if (_expertiseArmorPenalty > 0) { addSkill(SkillData.getInstance().getSkill(CommonSkill.ARMOR_GRADE_PENALTY.getId(), _expertiseArmorPenalty)); } else { removeSkill(getKnownSkill(CommonSkill.ARMOR_GRADE_PENALTY.getId()), false, true); } changed = true; } if (changed) { sendPacket(new EtcStatusUpdate(this)); } } public void useEquippableItem(L2ItemInstance item, boolean abortAttack) { // Equip or unEquip L2ItemInstance[] items = null; final boolean isEquiped = item.isEquipped(); final int oldInvLimit = getInventoryLimit(); SystemMessage sm = null; if (isEquiped) { if (item.getEnchantLevel() > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED); sm.addInt(item.getEnchantLevel()); sm.addItemName(item); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED); sm.addItemName(item); } sendPacket(sm); int slot = getInventory().getSlotFromItem(item); // we can't unequip talisman by body slot if ((slot == L2Item.SLOT_DECO) || (slot == L2Item.SLOT_BROOCH_JEWEL)) { items = getInventory().unEquipItemInSlotAndRecord(item.getLocationSlot()); } else { items = getInventory().unEquipItemInBodySlotAndRecord(slot); } } else { items = getInventory().equipItemAndRecord(item); if (item.isEquipped()) { if (item.getEnchantLevel() > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.EQUIPPED_S1_S2); sm.addInt(item.getEnchantLevel()); sm.addItemName(item); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EQUIPPED_YOUR_S1); sm.addItemName(item); } sendPacket(sm); // Consume mana - will start a task if required; returns if item is not a shadow item item.decreaseMana(false); if ((item.getItem().getBodyPart() & L2Item.SLOT_MULTI_ALLWEAPON) != 0) { rechargeShots(true, true); } } else { sendPacket(SystemMessageId.YOU_DO_NOT_MEET_THE_REQUIRED_CONDITION_TO_EQUIP_THAT_ITEM); } } refreshExpertisePenalty(); broadcastUserInfo(); InventoryUpdate iu = new InventoryUpdate(); iu.addItems(Arrays.asList(items)); sendPacket(iu); if (abortAttack) { abortAttack(); } if (getInventoryLimit() != oldInvLimit) { sendPacket(new ExStorageMaxCount(this)); } // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerEquipItem(this, item), this); } /** * @return the the PvP Kills of the L2PcInstance (Number of player killed during a PvP). */ public int getPvpKills() { return _pvpKills; } /** * Set the the PvP Kills of the L2PcInstance (Number of player killed during a PvP). * @param pvpKills */ public void setPvpKills(int pvpKills) { EventDispatcher.getInstance().notifyEventAsync(new OnPlayerPvPChanged(this, _pvpKills, pvpKills), this); _pvpKills = pvpKills; } /** * @return the Fame of this L2PcInstance */ public int getFame() { return _fame; } /** * Set the Fame of this L2PcInstane * @param fame */ public void setFame(int fame) { EventDispatcher.getInstance().notifyEventAsync(new OnPlayerFameChanged(this, _fame, fame), this); _fame = (fame > Config.MAX_PERSONAL_FAME_POINTS) ? Config.MAX_PERSONAL_FAME_POINTS : fame; } /** * @return the ClassId object of the L2PcInstance contained in L2PcTemplate. */ public ClassId getClassId() { return getTemplate().getClassId(); } /** * Set the template of the L2PcInstance. * @param Id The Identifier of the L2PcTemplate to set to the L2PcInstance */ public void setClassId(int Id) { if (!_subclassLock.tryLock()) { return; } try { if ((getLvlJoinedAcademy() != 0) && (_clan != null) && (PlayerClass.values()[Id].getLevel() == ClassLevel.THIRD)) { if (getLvlJoinedAcademy() <= 16) { _clan.addReputationScore(Config.JOIN_ACADEMY_MAX_REP_SCORE, true); } else if (getLvlJoinedAcademy() >= 39) { _clan.addReputationScore(Config.JOIN_ACADEMY_MIN_REP_SCORE, true); } else { _clan.addReputationScore((Config.JOIN_ACADEMY_MAX_REP_SCORE - ((getLvlJoinedAcademy() - 16) * 20)), true); } setLvlJoinedAcademy(0); // oust pledge member from the academy, cuz he has finished his 2nd class transfer SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.CLAN_MEMBER_S1_HAS_BEEN_EXPELLED); msg.addPcName(this); _clan.broadcastToOnlineMembers(msg); _clan.broadcastToOnlineMembers(new PledgeShowMemberListDelete(getName())); _clan.removeClanMember(getObjectId(), 0); sendPacket(SystemMessageId.CONGRATULATIONS_YOU_WILL_NOW_GRADUATE_FROM_THE_CLAN_ACADEMY_AND_LEAVE_YOUR_CURRENT_CLAN_YOU_CAN_NOW_JOIN_A_CLAN_WITHOUT_BEING_SUBJECT_TO_ANY_PENALTIES); // receive graduation gift getInventory().addItem("Gift", 8181, 1, this, null); // give academy circlet } if (isSubClassActive()) { getSubClasses().get(_classIndex).setClassId(Id); } setTarget(this); broadcastPacket(new MagicSkillUse(this, 5103, 1, 1000, 0)); setClassTemplate(Id); if (getClassId().level() == 3) { sendPacket(SystemMessageId.CONGRATULATIONS_YOU_VE_COMPLETED_YOUR_THIRD_CLASS_TRANSFER_QUEST); } else { sendPacket(SystemMessageId.CONGRATULATIONS_YOU_VE_COMPLETED_A_CLASS_TRANSFER); } // Update class icon in party and clan if (isInParty()) { getParty().broadcastPacket(new PartySmallWindowUpdate(this, true)); } if (getClan() != null) { getClan().broadcastToOnlineMembers(new PledgeShowMemberListUpdate(this)); } sendPacket(new ExSubjobInfo(this, SubclassInfoType.CLASS_CHANGED)); // Add AutoGet skills and normal skills and/or learnByFS depending on configurations. rewardSkills(); if (!canOverrideCond(PcCondOverride.SKILL_CONDITIONS) && Config.DECREASE_SKILL_LEVEL) { checkPlayerSkills(); } notifyFriends(L2FriendStatus.MODE_CLASS); } finally { _subclassLock.unlock(); } } /** * Used for AltGameSkillLearn to set a custom skill learning class Id. */ private ClassId _learningClass = getClassId(); /** * @return the custom skill learning class Id. */ public ClassId getLearningClass() { return _learningClass; } /** * @param learningClass the custom skill learning class Id to set. */ public void setLearningClass(ClassId learningClass) { _learningClass = learningClass; } /** * @return the Experience of the L2PcInstance. */ public long getExp() { return getStat().getExp(); } public void setActiveEnchantAttrItemId(int objectId) { _activeEnchantAttrItemId = objectId; } public int getActiveEnchantAttrItemId() { return _activeEnchantAttrItemId; } public void setActiveEnchantItemId(int objectId) { // If we don't have a Enchant Item, we are not enchanting. if (objectId == ID_NONE) { setActiveEnchantSupportItemId(ID_NONE); setActiveEnchantTimestamp(0); setIsEnchanting(false); } _activeEnchantItemId = objectId; } public int getActiveEnchantItemId() { return _activeEnchantItemId; } public void setActiveEnchantSupportItemId(int objectId) { _activeEnchantSupportItemId = objectId; } public int getActiveEnchantSupportItemId() { return _activeEnchantSupportItemId; } public long getActiveEnchantTimestamp() { return _activeEnchantTimestamp; } public void setActiveEnchantTimestamp(long val) { _activeEnchantTimestamp = val; } public void setIsEnchanting(boolean val) { _isEnchanting = val; } public boolean isEnchanting() { return _isEnchanting; } /** * Set the fists weapon of the L2PcInstance (used when no weapon is equiped). * @param weaponItem The fists L2Weapon to set to the L2PcInstance */ public void setFistsWeaponItem(L2Weapon weaponItem) { _fistsWeaponItem = weaponItem; } /** * @return the fists weapon of the L2PcInstance (used when no weapon is equipped). */ public L2Weapon getFistsWeaponItem() { return _fistsWeaponItem; } /** * @param classId * @return the fists weapon of the L2PcInstance Class (used when no weapon is equipped). */ public L2Weapon findFistsWeaponItem(int classId) { L2Weapon weaponItem = null; if ((classId >= 0x00) && (classId <= 0x09)) { // human fighter fists L2Item temp = ItemTable.getInstance().getTemplate(246); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x0a) && (classId <= 0x11)) { // human mage fists L2Item temp = ItemTable.getInstance().getTemplate(251); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x12) && (classId <= 0x18)) { // elven fighter fists L2Item temp = ItemTable.getInstance().getTemplate(244); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x19) && (classId <= 0x1e)) { // elven mage fists L2Item temp = ItemTable.getInstance().getTemplate(249); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x1f) && (classId <= 0x25)) { // dark elven fighter fists L2Item temp = ItemTable.getInstance().getTemplate(245); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x26) && (classId <= 0x2b)) { // dark elven mage fists L2Item temp = ItemTable.getInstance().getTemplate(250); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x2c) && (classId <= 0x30)) { // orc fighter fists L2Item temp = ItemTable.getInstance().getTemplate(248); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x31) && (classId <= 0x34)) { // orc mage fists L2Item temp = ItemTable.getInstance().getTemplate(252); weaponItem = (L2Weapon) temp; } else if ((classId >= 0x35) && (classId <= 0x39)) { // dwarven fists L2Item temp = ItemTable.getInstance().getTemplate(247); weaponItem = (L2Weapon) temp; } return weaponItem; } /** * This method reward all AutoGet skills and Normal skills if Auto-Learn configuration is true. */ public void rewardSkills() { // Give all normal skills if activated Auto-Learn is activated, included AutoGet skills. if (Config.AUTO_LEARN_SKILLS) { giveAvailableSkills(Config.AUTO_LEARN_FS_SKILLS, true); } else { giveAvailableAutoGetSkills(); } checkPlayerSkills(); checkItemRestriction(); sendSkillList(); } /** * Re-give all skills which aren't saved to database, like Noble, Hero, Clan Skills.<br> */ public void regiveTemporarySkills() { // Do not call this on enterworld or char load // Add noble skills if noble if (isNoble()) { setNoble(true); } // Add Hero skills if hero if (isHero()) { setHero(true); } // Add clan skills if (getClan() != null) { L2Clan clan = getClan(); clan.addSkillEffects(this); if ((clan.getLevel() >= SiegeManager.getInstance().getSiegeClanMinLevel()) && isClanLeader()) { SiegeManager.getInstance().addSiegeSkills(this); } if (getClan().getCastleId() > 0) { CastleManager.getInstance().getCastleByOwner(getClan()).giveResidentialSkills(this); } if (getClan().getFortId() > 0) { FortManager.getInstance().getFortByOwner(getClan()).giveResidentialSkills(this); } } // Reload passive skills from armors / jewels / weapons getInventory().reloadEquippedItems(); } /** * Give all available skills to the player. * @param includedByFs if {@code true} forgotten scroll skills present in the skill tree will be added * @param includeAutoGet if {@code true} auto-get skills present in the skill tree will be added * @return the amount of new skills earned */ public int giveAvailableSkills(boolean includedByFs, boolean includeAutoGet) { int skillCounter = 0; // Get available skills Collection<Skill> skills = SkillTreesData.getInstance().getAllAvailableSkills(this, getClassId(), includedByFs, includeAutoGet); List<Skill> skillsForStore = new ArrayList<>(); for (Skill sk : skills) { if (getKnownSkill(sk.getId()) == sk) { continue; } if (getSkillLevel(sk.getId()) == -1) { skillCounter++; } // fix when learning toggle skills if (sk.isToggle() && isAffectedBySkill(sk.getId())) { stopSkillEffects(true, sk.getId()); } addSkill(sk, false); skillsForStore.add(sk); } storeSkills(skillsForStore, -1); if (Config.AUTO_LEARN_SKILLS && (skillCounter > 0)) { sendMessage("You have learned " + skillCounter + " new skills."); } return skillCounter; } /** * Give all available auto-get skills to the player. */ public void giveAvailableAutoGetSkills() { // Get available skills final List<L2SkillLearn> autoGetSkills = SkillTreesData.getInstance().getAvailableAutoGetSkills(this); final SkillData st = SkillData.getInstance(); Skill skill; for (L2SkillLearn s : autoGetSkills) { skill = st.getSkill(s.getSkillId(), s.getSkillLevel()); if (skill != null) { addSkill(skill, true); } else { _log.warning("Skipping null auto-get skill for player: " + toString()); } } } /** * Set the Experience value of the L2PcInstance. * @param exp */ public void setExp(long exp) { if (exp < 0) { exp = 0; } getStat().setExp(exp); } /** * @return the Race object of the L2PcInstance. */ @Override public Race getRace() { if (!isSubClassActive()) { return getTemplate().getRace(); } return PlayerTemplateData.getInstance().getTemplate(_baseClass).getRace(); } public L2Radar getRadar() { return _radar; } /* Return true if Hellbound minimap allowed */ public boolean isMinimapAllowed() { return _minimapAllowed; } /* Enable or disable minimap on Hellbound */ public void setMinimapAllowed(boolean b) { _minimapAllowed = b; } /** * @return the SP amount of the L2PcInstance. */ public long getSp() { return getStat().getSp(); } /** * Set the SP amount of the L2PcInstance. * @param sp */ public void setSp(long sp) { if (sp < 0) { sp = 0; } super.getStat().setSp(sp); } /** * @param castleId * @return true if this L2PcInstance is a clan leader in ownership of the passed castle */ public boolean isCastleLord(int castleId) { L2Clan clan = getClan(); // player has clan and is the clan leader, check the castle info if ((clan != null) && (clan.getLeader().getPlayerInstance() == this)) { // if the clan has a castle and it is actually the queried castle, return true Castle castle = CastleManager.getInstance().getCastleByOwner(clan); if ((castle != null) && (castle == CastleManager.getInstance().getCastleById(castleId))) { return true; } } return false; } /** * @return the Clan Identifier of the L2PcInstance. */ @Override public int getClanId() { return _clanId; } /** * @return the Clan Crest Identifier of the L2PcInstance or 0. */ public int getClanCrestId() { if (_clan != null) { return _clan.getCrestId(); } return 0; } /** * @return The Clan CrestLarge Identifier or 0 */ public int getClanCrestLargeId() { if ((_clan != null) && ((_clan.getCastleId() != 0) || (_clan.getHideoutId() != 0))) { return _clan.getCrestLargeId(); } return 0; } public long getClanJoinExpiryTime() { return _clanJoinExpiryTime; } public void setClanJoinExpiryTime(long time) { _clanJoinExpiryTime = time; } public long getClanCreateExpiryTime() { return _clanCreateExpiryTime; } public void setClanCreateExpiryTime(long time) { _clanCreateExpiryTime = time; } public void setOnlineTime(long time) { _onlineTime = time; _onlineBeginTime = System.currentTimeMillis(); } /** * Return the PcInventory Inventory of the L2PcInstance contained in _inventory. */ @Override public PcInventory getInventory() { return _inventory; } public PcAuction getAuctionInventory() { return _auctionInventory; } /** * Delete a ShortCut of the L2PcInstance _shortCuts. * @param objectId */ public void removeItemFromShortCut(int objectId) { _shortCuts.deleteShortCutByObjectId(objectId); } /** * @return True if the L2PcInstance is sitting. */ public boolean isSitting() { return _waitTypeSitting; } /** * Set _waitTypeSitting to given value * @param state */ public void setIsSitting(boolean state) { _waitTypeSitting = state; } /** * Sit down the L2PcInstance, set the AI Intention to AI_INTENTION_REST and send a Server->Client ChangeWaitType packet (broadcast) */ public void sitDown() { sitDown(true); } public void sitDown(boolean checkCast) { if (checkCast && isCastingNow()) { sendMessage("Cannot sit while casting"); return; } if (!_waitTypeSitting && !isAttackingDisabled() && !isOutOfControl() && !isImmobilized()) { breakAttack(); setIsSitting(true); getAI().setIntention(CtrlIntention.AI_INTENTION_REST); broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_SITTING)); // Schedule a sit down task to wait for the animation to finish ThreadPoolManager.getInstance().scheduleGeneral(new SitDownTask(this), 2500); setIsParalyzed(true); } } /** * Stand up the L2PcInstance, set the AI Intention to AI_INTENTION_IDLE and send a Server->Client ChangeWaitType packet (broadcast) */ public void standUp() { if (L2Event.isParticipant(this) && getEventStatus().isSitForced()) { sendMessage("A dark force beyond your mortal understanding makes your knees to shake when you try to stand up..."); } else if (_waitTypeSitting && !isInStoreMode() && !isAlikeDead()) { if (getEffectList().isAffected(EffectFlag.RELAXING)) { stopEffects(L2EffectType.RELAXING); } broadcastPacket(new ChangeWaitType(this, ChangeWaitType.WT_STANDING)); // Schedule a stand up task to wait for the animation to finish ThreadPoolManager.getInstance().scheduleGeneral(new StandUpTask(this), 2500); } } /** * @return the PcWarehouse object of the L2PcInstance. */ public PcWarehouse getWarehouse() { if (_warehouse == null) { _warehouse = new PcWarehouse(this); _warehouse.restore(); } if (Config.WAREHOUSE_CACHE) { WarehouseCacheManager.getInstance().addCacheTask(this); } return _warehouse; } /** * Free memory used by Warehouse */ public void clearWarehouse() { if (_warehouse != null) { _warehouse.deleteMe(); } _warehouse = null; } /** * @return the PcFreight object of the L2PcInstance. */ public PcFreight getFreight() { return _freight; } /** * @return true if refund list is not empty */ public boolean hasRefund() { return (_refund != null) && (_refund.getSize() > 0) && Config.ALLOW_REFUND; } /** * @return refund object or create new if not exist */ public PcRefund getRefund() { if (_refund == null) { _refund = new PcRefund(this); } return _refund; } /** * Clear refund */ public void clearRefund() { if (_refund != null) { _refund.deleteMe(); } _refund = null; } /** * @return the Adena amount of the L2PcInstance. */ public long getAdena() { return _inventory.getAdena(); } /** * @return the Ancient Adena amount of the L2PcInstance. */ public long getAncientAdena() { return _inventory.getAncientAdena(); } /** * @return the Beauty Tickets of the L2PcInstance. */ public long getBeautyTickets() { return _inventory.getBeautyTickets(); } /** * Add adena to Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param count : int Quantity of adena to be added * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action */ public void addAdena(String process, long count, L2Object reference, boolean sendMessage) { if (sendMessage) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1_ADENA); sm.addLong(count); sendPacket(sm); } if (count > 0) { _inventory.addAdena(process, count, this, reference); // Send update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate iu = new InventoryUpdate(); iu.addItem(_inventory.getAdenaInstance()); sendPacket(iu); } else { sendPacket(new ItemList(this, false)); } } } /** * Reduce adena in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param count : long Quantity of adena to be reduced * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ public boolean reduceAdena(String process, long count, L2Object reference, boolean sendMessage) { if (count > getAdena()) { if (sendMessage) { sendPacket(SystemMessageId.YOU_DO_NOT_HAVE_ENOUGH_ADENA); } return false; } if (count > 0) { L2ItemInstance adenaItem = _inventory.getAdenaInstance(); if (!_inventory.reduceAdena(process, count, this, reference)) { return false; } // Send update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate iu = new InventoryUpdate(); iu.addItem(adenaItem); sendPacket(iu); } else { sendPacket(new ItemList(this, false)); } sendPacket(new ExAdenaInvenCount(this)); if (sendMessage) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_ADENA_DISAPPEARED); sm.addLong(count); sendPacket(sm); } } return true; } /** * Reduce Beauty Tickets in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param count : long Quantity of Beauty Tickets to be reduced * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ public boolean reduceBeautyTickets(String process, long count, L2Object reference, boolean sendMessage) { if (count > getBeautyTickets()) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return false; } if (count > 0) { L2ItemInstance beautyTickets = _inventory.getAdenaInstance(); if (!_inventory.reduceBeautyTickets(process, count, this, reference)) { return false; } // Send update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate iu = new InventoryUpdate(); iu.addItem(beautyTickets); sendPacket(iu); } else { sendPacket(new ItemList(this, false)); } if (sendMessage) { if (count > 1) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED); sm.addItemName(Inventory.BEAUTY_TICKET_ID); sm.addLong(count); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); sm.addItemName(Inventory.BEAUTY_TICKET_ID); sendPacket(sm); } } } return true; } /** * Add ancient adena to Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param count : int Quantity of ancient adena to be added * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action */ public void addAncientAdena(String process, long count, L2Object reference, boolean sendMessage) { if (sendMessage) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); sm.addItemName(Inventory.ANCIENT_ADENA_ID); sm.addLong(count); sendPacket(sm); } if (count > 0) { _inventory.addAncientAdena(process, count, this, reference); if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate iu = new InventoryUpdate(); iu.addItem(_inventory.getAncientAdenaInstance()); sendPacket(iu); } else { sendPacket(new ItemList(this, false)); } } } /** * Reduce ancient adena in Inventory of the L2PcInstance and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param count : long Quantity of ancient adena to be reduced * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ public boolean reduceAncientAdena(String process, long count, L2Object reference, boolean sendMessage) { if (count > getAncientAdena()) { if (sendMessage) { sendPacket(SystemMessageId.YOU_DO_NOT_HAVE_ENOUGH_ADENA); } return false; } if (count > 0) { L2ItemInstance ancientAdenaItem = _inventory.getAncientAdenaInstance(); if (!_inventory.reduceAncientAdena(process, count, this, reference)) { return false; } if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate iu = new InventoryUpdate(); iu.addItem(ancientAdenaItem); sendPacket(iu); } else { sendPacket(new ItemList(this, false)); } if (sendMessage) { if (count > 1) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED); sm.addItemName(Inventory.ANCIENT_ADENA_ID); sm.addLong(count); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); sm.addItemName(Inventory.ANCIENT_ADENA_ID); sendPacket(sm); } } } return true; } /** * Adds item to inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param item : L2ItemInstance to be added * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action */ public void addItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) { if (item.getCount() > 0) { // Sends message to client if requested if (sendMessage) { if (item.getCount() > 1) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S2_S1); sm.addItemName(item); sm.addLong(item.getCount()); sendPacket(sm); } else if (item.getEnchantLevel() > 0) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_A_S1_S2); sm.addInt(item.getEnchantLevel()); sm.addItemName(item); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S1); sm.addItemName(item); sendPacket(sm); } } // Add the item to inventory L2ItemInstance newitem = _inventory.addItem(process, item, this, reference); // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); // If over capacity, drop the item if (!canOverrideCond(PcCondOverride.ITEM_CONDITIONS) && !_inventory.validateCapacity(0, item.isQuestItem()) && newitem.isDropable() && (!newitem.isStackable() || (newitem.getLastChange() != L2ItemInstance.MODIFIED))) { dropItem("InvDrop", newitem, null, true, true); } else if (CursedWeaponsManager.getInstance().isCursed(newitem.getId())) { CursedWeaponsManager.getInstance().activate(this, newitem); } // Combat Flag else if (FortSiegeManager.getInstance().isCombat(item.getId())) { if (FortSiegeManager.getInstance().activateCombatFlag(this, item)) { Fort fort = FortManager.getInstance().getFort(this); fort.getSiege().announceToPlayer(SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_ACQUIRED_THE_FLAG), getName()); } } } } /** * Adds item to Inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param itemId : int Item Identifier of the item to be added * @param count : long Quantity of items to be added * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return */ public L2ItemInstance addItem(String process, int itemId, long count, L2Object reference, boolean sendMessage) { if (count > 0) { final L2Item item = ItemTable.getInstance().getTemplate(itemId); if (item == null) { _log.log(Level.SEVERE, "Item doesn't exist so cannot be added. Item ID: " + itemId); return null; } // Sends message to client if requested if (sendMessage && ((!isCastingNow() && item.hasExImmediateEffect()) || !item.hasExImmediateEffect())) { if (count > 1) { if (process.equalsIgnoreCase("Sweeper") || process.equalsIgnoreCase("Quest")) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); sm.addItemName(itemId); sm.addLong(count); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S2_S1); sm.addItemName(itemId); sm.addLong(count); sendPacket(sm); } } else { if (process.equalsIgnoreCase("Sweeper") || process.equalsIgnoreCase("Quest")) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S1); sm.addItemName(itemId); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_OBTAINED_S1); sm.addItemName(itemId); sendPacket(sm); } } } // Auto-use herbs. if (item.hasExImmediateEffect()) { final IItemHandler handler = ItemHandler.getInstance().getHandler(item instanceof L2EtcItem ? (L2EtcItem) item : null); if (handler == null) { _log.warning("No item handler registered for Herb ID " + item.getId() + "!"); } else { handler.useItem(this, new L2ItemInstance(itemId), false); } } else { // Add the item to inventory L2ItemInstance createdItem = _inventory.addItem(process, itemId, count, this, reference); // If over capacity, drop the item if (!canOverrideCond(PcCondOverride.ITEM_CONDITIONS) && !_inventory.validateCapacity(0, item.isQuestItem()) && createdItem.isDropable() && (!createdItem.isStackable() || (createdItem.getLastChange() != L2ItemInstance.MODIFIED))) { dropItem("InvDrop", createdItem, null, true); } else if (CursedWeaponsManager.getInstance().isCursed(createdItem.getId())) { CursedWeaponsManager.getInstance().activate(this, createdItem); } return createdItem; } } return null; } /** * @param process the process name * @param item the item holder * @param reference the reference object * @param sendMessage if {@code true} a system message will be sent */ public void addItem(String process, ItemHolder item, L2Object reference, boolean sendMessage) { addItem(process, item.getId(), item.getCount(), reference, sendMessage); } /** * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param item : L2ItemInstance to be destroyed * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ public boolean destroyItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) { return destroyItem(process, item, item.getCount(), reference, sendMessage); } /** * Destroy item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param item : L2ItemInstance to be destroyed * @param count * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ public boolean destroyItem(String process, L2ItemInstance item, long count, L2Object reference, boolean sendMessage) { item = _inventory.destroyItem(process, item, count, this, reference); if (item == null) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return false; } // Send inventory update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate playerIU = new InventoryUpdate(); playerIU.addItem(item); sendPacket(playerIU); } else { sendPacket(new ItemList(this, false)); } // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); sendPacket(new ExAdenaInvenCount(this)); // Sends message to client if requested if (sendMessage) { if (count > 1) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED); sm.addItemName(item); sm.addLong(count); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); sm.addItemName(item); sendPacket(sm); } } return true; } /** * Destroys item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param objectId : int Item Instance identifier of the item to be destroyed * @param count : int Quantity of items to be destroyed * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ @Override public boolean destroyItem(String process, int objectId, long count, L2Object reference, boolean sendMessage) { L2ItemInstance item = _inventory.getItemByObjectId(objectId); if (item == null) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return false; } return destroyItem(process, item, count, reference, sendMessage); } /** * Destroys shots from inventory without logging and only occasional saving to database. Sends a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param objectId : int Item Instance identifier of the item to be destroyed * @param count : int Quantity of items to be destroyed * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ public boolean destroyItemWithoutTrace(String process, int objectId, long count, L2Object reference, boolean sendMessage) { L2ItemInstance item = _inventory.getItemByObjectId(objectId); if ((item == null) || (item.getCount() < count)) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return false; } return destroyItem(null, item, count, reference, sendMessage); } /** * Destroy item from inventory by using its <B>itemId</B> and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param itemId : int Item identifier of the item to be destroyed * @param count : int Quantity of items to be destroyed * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @return boolean informing if the action was successful */ @Override public boolean destroyItemByItemId(String process, int itemId, long count, L2Object reference, boolean sendMessage) { if (itemId == Inventory.ADENA_ID) { return reduceAdena(process, count, reference, sendMessage); } L2ItemInstance item = _inventory.getItemByItemId(itemId); if ((item == null) || (item.getCount() < count) || (_inventory.destroyItemByItemId(process, itemId, count, this, reference) == null)) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return false; } // Send inventory update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate playerIU = new InventoryUpdate(); playerIU.addItem(item); sendPacket(playerIU); } else { sendPacket(new ItemList(this, false)); } // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); sendPacket(new ExAdenaInvenCount(this)); // Sends message to client if requested if (sendMessage) { if (count > 1) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S2_S1_S_DISAPPEARED); sm.addItemName(itemId); sm.addLong(count); sendPacket(sm); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); sm.addItemName(itemId); sendPacket(sm); } } return true; } /** * Transfers item to another ItemContainer and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param objectId : int Item Identifier of the item to be transfered * @param count : long Quantity of items to be transfered * @param target * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @return L2ItemInstance corresponding to the new item or the updated item in inventory */ public L2ItemInstance transferItem(String process, int objectId, long count, Inventory target, L2Object reference) { L2ItemInstance oldItem = checkItemManipulation(objectId, count, "transfer"); if (oldItem == null) { return null; } L2ItemInstance newItem = getInventory().transferItem(process, objectId, count, target, this, reference); if (newItem == null) { return null; } // Send inventory update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate playerIU = new InventoryUpdate(); if ((oldItem.getCount() > 0) && (oldItem != newItem)) { playerIU.addModifiedItem(oldItem); } else { playerIU.addRemovedItem(oldItem); } sendPacket(playerIU); } else { sendPacket(new ItemList(this, false)); } // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); sendPacket(new ExAdenaInvenCount(this)); // Send target update packet if (target instanceof PcInventory) { L2PcInstance targetPlayer = ((PcInventory) target).getOwner(); if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate playerIU = new InventoryUpdate(); if (newItem.getCount() > count) { playerIU.addModifiedItem(newItem); } else { playerIU.addNewItem(newItem); } targetPlayer.sendPacket(playerIU); } else { targetPlayer.sendPacket(new ItemList(targetPlayer, false)); } // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); sendPacket(new ExAdenaInvenCount(this)); } else if (target instanceof PetInventory) { PetInventoryUpdate petIU = new PetInventoryUpdate(); if (newItem.getCount() > count) { petIU.addModifiedItem(newItem); } else { petIU.addNewItem(newItem); } ((PetInventory) target).getOwner().sendPacket(petIU); } return newItem; } /** * Use instead of calling {@link #addItem(String, L2ItemInstance, L2Object, boolean)} and {@link #destroyItemByItemId(String, int, long, L2Object, boolean)}<br> * This method validates slots and weight limit, for stackable and non-stackable items. * @param process a generic string representing the process that is exchanging this items * @param reference the (probably NPC) reference, could be null * @param coinId the item Id of the item given on the exchange * @param cost the amount of items given on the exchange * @param rewardId the item received on the exchange * @param count the amount of items received on the exchange * @param sendMessage if {@code true} it will send messages to the acting player * @return {@code true} if the player successfully exchanged the items, {@code false} otherwise */ public boolean exchangeItemsById(String process, L2Object reference, int coinId, long cost, int rewardId, long count, boolean sendMessage) { final PcInventory inv = getInventory(); if (!inv.validateCapacityByItemId(rewardId, count)) { if (sendMessage) { sendPacket(SystemMessageId.YOUR_INVENTORY_IS_FULL); } return false; } if (!inv.validateWeightByItemId(rewardId, count)) { if (sendMessage) { sendPacket(SystemMessageId.YOU_HAVE_EXCEEDED_THE_WEIGHT_LIMIT); } return false; } if (destroyItemByItemId(process, coinId, cost, reference, sendMessage)) { addItem(process, rewardId, count, reference, sendMessage); return true; } return false; } /** * Drop item from inventory and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process String Identifier of process triggering this action * @param item L2ItemInstance to be dropped * @param reference L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage boolean Specifies whether to send message to Client about this action * @param protectItem whether or not dropped item must be protected temporary against other players * @return boolean informing if the action was successful */ public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage, boolean protectItem) { item = _inventory.dropItem(process, item, this, reference); if (item == null) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return false; } item.dropMe(this, (getX() + Rnd.get(50)) - 25, (getY() + Rnd.get(50)) - 25, getZ() + 20); if ((Config.AUTODESTROY_ITEM_AFTER > 0) && Config.DESTROY_DROPPED_PLAYER_ITEM && !Config.LIST_PROTECTED_ITEMS.contains(item.getId())) { if ((item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM) || !item.isEquipable()) { ItemsAutoDestroy.getInstance().addItem(item); } } // protection against auto destroy dropped item if (Config.DESTROY_DROPPED_PLAYER_ITEM) { if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM)) { item.setProtected(false); } else { item.setProtected(true); } } else { item.setProtected(true); } // retail drop protection if (protectItem) { item.getDropProtection().protect(this); } // Send inventory update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate playerIU = new InventoryUpdate(); playerIU.addItem(item); sendPacket(playerIU); } else { sendPacket(new ItemList(this, false)); } // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); sendPacket(new ExAdenaInvenCount(this)); // Sends message to client if requested if (sendMessage) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_DROPPED_S1); sm.addItemName(item); sendPacket(sm); } return true; } public boolean dropItem(String process, L2ItemInstance item, L2Object reference, boolean sendMessage) { return dropItem(process, item, reference, sendMessage, false); } /** * Drop item from inventory by using its <B>objectID</B> and send a Server->Client InventoryUpdate packet to the L2PcInstance. * @param process : String Identifier of process triggering this action * @param objectId : int Item Instance identifier of the item to be dropped * @param count : long Quantity of items to be dropped * @param x : int coordinate for drop X * @param y : int coordinate for drop Y * @param z : int coordinate for drop Z * @param reference : L2Object Object referencing current action like NPC selling item or previous item in transformation * @param sendMessage : boolean Specifies whether to send message to Client about this action * @param protectItem * @return L2ItemInstance corresponding to the new item or the updated item in inventory */ public L2ItemInstance dropItem(String process, int objectId, long count, int x, int y, int z, L2Object reference, boolean sendMessage, boolean protectItem) { L2ItemInstance invitem = _inventory.getItemByObjectId(objectId); L2ItemInstance item = _inventory.dropItem(process, objectId, count, this, reference); if (item == null) { if (sendMessage) { sendPacket(SystemMessageId.INCORRECT_ITEM_COUNT2); } return null; } item.dropMe(this, x, y, z); if ((Config.AUTODESTROY_ITEM_AFTER > 0) && Config.DESTROY_DROPPED_PLAYER_ITEM && !Config.LIST_PROTECTED_ITEMS.contains(item.getId())) { if ((item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM) || !item.isEquipable()) { ItemsAutoDestroy.getInstance().addItem(item); } } if (Config.DESTROY_DROPPED_PLAYER_ITEM) { if (!item.isEquipable() || (item.isEquipable() && Config.DESTROY_EQUIPABLE_PLAYER_ITEM)) { item.setProtected(false); } else { item.setProtected(true); } } else { item.setProtected(true); } // retail drop protection if (protectItem) { item.getDropProtection().protect(this); } // Send inventory update packet if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate playerIU = new InventoryUpdate(); playerIU.addItem(invitem); sendPacket(playerIU); } else { sendPacket(new ItemList(this, false)); } // Update current load as well sendPacket(new ExUserInfoInvenWeight(this)); sendPacket(new ExAdenaInvenCount(this)); // Sends message to client if requested if (sendMessage) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_DROPPED_S1); sm.addItemName(item); sendPacket(sm); } return item; } public L2ItemInstance checkItemManipulation(int objectId, long count, String action) { // TODO: if we remove objects that are not visisble from the L2World, we'll have to remove this check if (L2World.getInstance().findObject(objectId) == null) { _log.finest(getObjectId() + ": player tried to " + action + " item not available in L2World"); return null; } L2ItemInstance item = getInventory().getItemByObjectId(objectId); if ((item == null) || (item.getOwnerId() != getObjectId())) { _log.finest(getObjectId() + ": player tried to " + action + " item he is not owner of"); return null; } if ((count < 0) || ((count > 1) && !item.isStackable())) { _log.finest(getObjectId() + ": player tried to " + action + " item with invalid count: " + count); return null; } if (count > item.getCount()) { _log.finest(getObjectId() + ": player tried to " + action + " more items than he owns"); return null; } // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting final L2Summon pet = getPet(); if (((pet != null) && (pet.getControlObjectId() == objectId)) || (getMountObjectID() == objectId)) { if (Config.DEBUG) { _log.finest(getObjectId() + ": player tried to " + action + " item controling pet"); } return null; } if (getActiveEnchantItemId() == objectId) { if (Config.DEBUG) { _log.finest(getObjectId() + ":player tried to " + action + " an enchant scroll he was using"); } return null; } // We cannot put a Weapon with Augmention in WH while casting (Possible Exploit) if (item.isAugmented() && (isCastingNow() || isCastingSimultaneouslyNow())) { return null; } return item; } /** * Set _protectEndTime according settings. * @param protect */ public void setProtection(boolean protect) { if (Config.DEVELOPER && (protect || (_protectEndTime > 0))) { _log.warning(getName() + ": Protection " + (protect ? "ON " + (GameTimeController.getInstance().getGameTicks() + (Config.PLAYER_SPAWN_PROTECTION * GameTimeController.TICKS_PER_SECOND)) : "OFF") + " (currently " + GameTimeController.getInstance().getGameTicks() + ")"); } _protectEndTime = protect ? GameTimeController.getInstance().getGameTicks() + (Config.PLAYER_SPAWN_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0; } public void setTeleportProtection(boolean protect) { if (Config.DEVELOPER && (protect || (_teleportProtectEndTime > 0))) { _log.warning(getName() + ": Tele Protection " + (protect ? "ON " + (GameTimeController.getInstance().getGameTicks() + (Config.PLAYER_TELEPORT_PROTECTION * GameTimeController.TICKS_PER_SECOND)) : "OFF") + " (currently " + GameTimeController.getInstance().getGameTicks() + ")"); } _teleportProtectEndTime = protect ? GameTimeController.getInstance().getGameTicks() + (Config.PLAYER_TELEPORT_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0; } /** * Set protection from agro mobs when getting up from fake death, according settings. * @param protect */ public void setRecentFakeDeath(boolean protect) { _recentFakeDeathEndTime = protect ? GameTimeController.getInstance().getGameTicks() + (Config.PLAYER_FAKEDEATH_UP_PROTECTION * GameTimeController.TICKS_PER_SECOND) : 0; } public boolean isRecentFakeDeath() { return _recentFakeDeathEndTime > GameTimeController.getInstance().getGameTicks(); } public final boolean isFakeDeath() { return _isFakeDeath; } public final void setIsFakeDeath(boolean value) { _isFakeDeath = value; } @Override public final boolean isAlikeDead() { return super.isAlikeDead() || isFakeDeath(); } /** * @return the client owner of this char. */ public L2GameClient getClient() { return _client; } public void setClient(L2GameClient client) { _client = client; } public String getIPAddress() { String ip = "N/A"; if ((_client != null) && (_client.getConnectionAddress() != null)) { ip = _client.getConnectionAddress().getHostAddress(); } return ip; } /** * Close the active connection with the client. * @param closeClient */ private void closeNetConnection(boolean closeClient) { L2GameClient client = _client; if (client != null) { if (client.isDetached()) { client.cleanMe(true); } else { if (!client.getConnection().isClosed()) { if (closeClient) { client.close(LeaveWorld.STATIC_PACKET); } else { client.close(ServerClose.STATIC_PACKET); } } } } } public Location getCurrentSkillWorldPosition() { return _currentSkillWorldPosition; } public void setCurrentSkillWorldPosition(Location worldPosition) { _currentSkillWorldPosition = worldPosition; } @Override public void enableSkill(Skill skill) { super.enableSkill(skill); removeTimeStamp(skill); } @Override public boolean checkDoCastConditions(Skill skill) { if (!super.checkDoCastConditions(skill)) { return false; } if (inObserverMode()) { return false; } if (isInOlympiadMode() && skill.isBlockedInOlympiad()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_THAT_SKILL_IN_A_OLYMPIAD_MATCH); return false; } // Check if the spell using charges or not in AirShip if (((getCharges() < skill.getChargeConsume())) || (isInAirShip() && !skill.hasEffectType(L2EffectType.REFUEL_AIRSHIP))) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED_DUE_TO_UNSUITABLE_TERMS); sm.addSkillName(skill); sendPacket(sm); return false; } return true; } /** * Returns true if cp update should be done, false if not * @return boolean */ private boolean needCpUpdate() { double currentCp = getCurrentCp(); if ((currentCp <= 1.0) || (getMaxCp() < MAX_HP_BAR_PX)) { return true; } if ((currentCp <= _cpUpdateDecCheck) || (currentCp >= _cpUpdateIncCheck)) { if (currentCp == getMaxCp()) { _cpUpdateIncCheck = currentCp + 1; _cpUpdateDecCheck = currentCp - _cpUpdateInterval; } else { double doubleMulti = currentCp / _cpUpdateInterval; int intMulti = (int) doubleMulti; _cpUpdateDecCheck = _cpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti); _cpUpdateIncCheck = _cpUpdateDecCheck + _cpUpdateInterval; } return true; } return false; } /** * Returns true if mp update should be done, false if not * @return boolean */ private boolean needMpUpdate() { double currentMp = getCurrentMp(); if ((currentMp <= 1.0) || (getMaxMp() < MAX_HP_BAR_PX)) { return true; } if ((currentMp <= _mpUpdateDecCheck) || (currentMp >= _mpUpdateIncCheck)) { if (currentMp == getMaxMp()) { _mpUpdateIncCheck = currentMp + 1; _mpUpdateDecCheck = currentMp - _mpUpdateInterval; } else { double doubleMulti = currentMp / _mpUpdateInterval; int intMulti = (int) doubleMulti; _mpUpdateDecCheck = _mpUpdateInterval * (doubleMulti < intMulti ? intMulti-- : intMulti); _mpUpdateIncCheck = _mpUpdateDecCheck + _mpUpdateInterval; } return true; } return false; } /** * Send packet StatusUpdate with current HP,MP and CP to the L2PcInstance and only current HP, MP and Level to all other L2PcInstance of the Party. <B><U> Actions</U> :</B> <li>Send the Server->Client packet StatusUpdate with current HP, MP and CP to this L2PcInstance</li><BR> * <li>Send the Server->Client packet PartySmallWindowUpdate with current HP, MP and Level to all other L2PcInstance of the Party</li> <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND current HP and MP to all L2PcInstance of the _statusListener</B></FONT> */ @Override public void broadcastStatusUpdate() { // TODO We mustn't send these informations to other players // Send the Server->Client packet StatusUpdate with current HP and MP to all L2PcInstance that must be informed of HP/MP updates of this L2PcInstance // super.broadcastStatusUpdate(); // Send the Server->Client packet StatusUpdate with current HP, MP and CP to this L2PcInstance StatusUpdate su = new StatusUpdate(this); su.addAttribute(StatusUpdate.MAX_HP, getMaxHp()); su.addAttribute(StatusUpdate.CUR_HP, (int) getCurrentHp()); su.addAttribute(StatusUpdate.MAX_MP, getMaxMp()); su.addAttribute(StatusUpdate.CUR_MP, (int) getCurrentMp()); su.addAttribute(StatusUpdate.MAX_CP, getMaxCp()); su.addAttribute(StatusUpdate.CUR_CP, (int) getCurrentCp()); broadcastPacket(su); final boolean needCpUpdate = needCpUpdate(); final boolean needHpUpdate = needHpUpdate(); final boolean needMpUpdate = needMpUpdate(); // Check if a party is in progress and party window update is usefull if (isInParty() && (needCpUpdate || needHpUpdate || needMpUpdate)) { final PartySmallWindowUpdate partyWindow = new PartySmallWindowUpdate(this, false); if (needCpUpdate) { partyWindow.addUpdateType(PartySmallWindowUpdateType.CURRENT_CP); partyWindow.addUpdateType(PartySmallWindowUpdateType.MAX_CP); } if (needHpUpdate) { partyWindow.addUpdateType(PartySmallWindowUpdateType.CURRENT_HP); partyWindow.addUpdateType(PartySmallWindowUpdateType.MAX_HP); } if (needMpUpdate) { partyWindow.addUpdateType(PartySmallWindowUpdateType.CURRENT_MP); partyWindow.addUpdateType(PartySmallWindowUpdateType.MAX_MP); } getParty().broadcastToPartyMembers(this, partyWindow); } if (isInOlympiadMode() && isOlympiadStart() && (needCpUpdate || needHpUpdate)) { final OlympiadGameTask game = OlympiadGameManager.getInstance().getOlympiadTask(getOlympiadGameId()); if ((game != null) && game.isBattleStarted()) { game.getZone().broadcastStatusUpdate(this); } } // In duel MP updated only with CP or HP if (isInDuel() && (needCpUpdate || needHpUpdate)) { DuelManager.getInstance().broadcastToOppositTeam(this, new ExDuelUpdateUserInfo(this)); } } /** * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers. <B><U> Concept</U> :</B> Others L2PcInstance in the detection area of the L2PcInstance are identified in <B>_knownPlayers</B>. In order to inform other players of this * L2PcInstance state modifications, server just need to go through _knownPlayers to send Server->Client Packet <B><U> Actions</U> :</B> <li>Send a Server->Client packet UserInfo to this L2PcInstance (Public and Private Data)</li> <li>Send a Server->Client packet CharInfo to all L2PcInstance in * _KnownPlayers of the L2PcInstance (Public data only)</li> <FONT COLOR=#FF0000><B> <U>Caution</U> : DON'T SEND UserInfo packet to other players instead of CharInfo packet. Indeed, UserInfo packet contains PRIVATE DATA as MaxHP, STR, DEX...</B></FONT> */ public final void broadcastUserInfo() { // Send a Server->Client packet UserInfo to this L2PcInstance sendPacket(new UserInfo(this)); // Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance broadcastPacket(new CharInfo(this)); } public final void broadcastUserInfo(UserInfoType... types) { // Send a Server->Client packet UserInfo to this L2PcInstance UserInfo ui = new UserInfo(this, false); ui.addComponentType(types); sendPacket(ui); // Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance broadcastPacket(new CharInfo(this)); } public final void broadcastTitleInfo() { // Send a Server->Client packet UserInfo to this L2PcInstance UserInfo ui = new UserInfo(this, false); ui.addComponentType(UserInfoType.CLAN); sendPacket(ui); // Send a Server->Client packet TitleUpdate to all L2PcInstance in _KnownPlayers of the L2PcInstance broadcastPacket(new NicknameChanged(this)); } @Override public final void broadcastPacket(L2GameServerPacket mov) { if (!(mov instanceof CharInfo)) { sendPacket(mov); } mov.setInvisible(isInvisible()); final Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values(); for (L2PcInstance player : plrs) { if ((player == null) || !isVisibleFor(player)) { continue; } player.sendPacket(mov); final int relation = getRelation(player); Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); if ((oldrelation == null) || (oldrelation != relation)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(this, relation, isAutoAttackable(player)); if (hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation, isAutoAttackable(player)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player))); } } player.sendPacket(rc); getKnownList().getKnownRelations().put(player.getObjectId(), relation); } } } @Override public void broadcastPacket(L2GameServerPacket mov, int radiusInKnownlist) { if (!(mov instanceof CharInfo)) { sendPacket(mov); } mov.setInvisible(isInvisible()); final Collection<L2PcInstance> plrs = getKnownList().getKnownPlayersInRadius(radiusInKnownlist); for (L2PcInstance player : plrs) { if ((player == null) || !isVisibleFor(player)) { continue; } player.sendPacket(mov); if (mov instanceof CharInfo) { final int relation = getRelation(player); Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); if ((oldrelation == null) || (oldrelation != relation)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(this, relation, isAutoAttackable(player)); if (hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation, isAutoAttackable(player)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player))); } } player.sendPacket(rc); getKnownList().getKnownRelations().put(player.getObjectId(), relation); } } } } /** * @return the Alliance Identifier of the L2PcInstance. */ @Override public int getAllyId() { if (_clan == null) { return 0; } return _clan.getAllyId(); } public int getAllyCrestId() { if (getClanId() == 0) { return 0; } if (getClan().getAllyId() == 0) { return 0; } return getClan().getAllyCrestId(); } public void queryGameGuard() { if (getClient() != null) { getClient().setGameGuardOk(false); sendPacket(GameGuardQuery.STATIC_PACKET); } if (Config.GAMEGUARD_ENFORCE) { ThreadPoolManager.getInstance().scheduleGeneral(new GameGuardCheckTask(this), 30 * 1000); } } /** * Send a Server->Client packet StatusUpdate to the L2PcInstance. */ @Override public void sendPacket(L2GameServerPacket packet) { if (_client != null) { _client.sendPacket(packet); } } /** * Send SystemMessage packet. * @param id SystemMessageId */ @Override public void sendPacket(SystemMessageId id) { sendPacket(SystemMessage.getSystemMessage(id)); } /** * Manage Interact Task with another L2PcInstance. <B><U> Actions</U> :</B> <li>If the private store is a STORE_PRIVATE_SELL, send a Server->Client PrivateBuyListSell packet to the L2PcInstance</li> <li>If the private store is a STORE_PRIVATE_BUY, send a Server->Client PrivateBuyListBuy packet * to the L2PcInstance</li> <li>If the private store is a STORE_PRIVATE_MANUFACTURE, send a Server->Client RecipeShopSellList packet to the L2PcInstance</li> * @param target The L2Character targeted */ public void doInteract(L2Character target) { if (target instanceof L2PcInstance) { L2PcInstance temp = (L2PcInstance) target; sendPacket(ActionFailed.STATIC_PACKET); if ((temp.getPrivateStoreType() == PrivateStoreType.SELL) || (temp.getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL)) { sendPacket(new PrivateStoreListSell(this, temp)); } else if (temp.getPrivateStoreType() == PrivateStoreType.BUY) { sendPacket(new PrivateStoreListBuy(this, temp)); } else if (temp.getPrivateStoreType() == PrivateStoreType.MANUFACTURE) { sendPacket(new RecipeShopSellList(this, temp)); } } else { // _interactTarget=null should never happen but one never knows ^^; if (target != null) { target.onAction(this); } } } /** * Manages AutoLoot Task.<br> * <ul> * <li>Send a system message to the player.</li> * <li>Add the item to the player's inventory.</li> * <li>Send a Server->Client packet InventoryUpdate to this player with NewItem (use a new slot) or ModifiedItem (increase amount).</li> * <li>Send a Server->Client packet StatusUpdate to this player with current weight.</li> * </ul> * <font color=#FF0000><B><U>Caution</U>: If a party is in progress, distribute the items between the party members!</b></font> * @param target the NPC dropping the item * @param itemId the item ID * @param itemCount the item count */ public void doAutoLoot(L2Attackable target, int itemId, long itemCount) { if (isInParty() && !ItemTable.getInstance().getTemplate(itemId).hasExImmediateEffect()) { getParty().distributeItem(this, itemId, itemCount, false, target); } else if (itemId == Inventory.ADENA_ID) { addAdena("Loot", itemCount, target, true); } else { addItem("Loot", itemId, itemCount, target, true); } } /** * Method overload for {@link L2PcInstance#doAutoLoot(L2Attackable, int, long)} * @param target the NPC dropping the item * @param item the item holder */ public void doAutoLoot(L2Attackable target, ItemHolder item) { doAutoLoot(target, item.getId(), item.getCount()); } /** * Manage Pickup Task. <B><U> Actions</U> :</B> <li>Send a Server->Client packet StopMove to this L2PcInstance</li> <li>Remove the L2ItemInstance from the world and send server->client GetItem packets</li> <li>Send a System Message to the L2PcInstance : YOU_PICKED_UP_S1_ADENA or * YOU_PICKED_UP_S1_S2</li> <li>Add the Item to the L2PcInstance inventory</li> <li>Send a Server->Client packet InventoryUpdate to this L2PcInstance with NewItem (use a new slot) or ModifiedItem (increase amount)</li> <li>Send a Server->Client packet StatusUpdate to this L2PcInstance with * current weight</li> <FONT COLOR=#FF0000><B> <U>Caution</U> : If a Party is in progress, distribute Items between party members</B></FONT> * @param object The L2ItemInstance to pick up */ @Override public void doPickupItem(L2Object object) { if (isAlikeDead() || isFakeDeath()) { return; } // Set the AI Intention to AI_INTENTION_IDLE getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); // Check if the L2Object to pick up is a L2ItemInstance if (!(object instanceof L2ItemInstance)) { // dont try to pickup anything that is not an item :) _log.warning(this + " trying to pickup wrong target." + getTarget()); return; } L2ItemInstance target = (L2ItemInstance) object; // Send a Server->Client packet ActionFailed to this L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); // Send a Server->Client packet StopMove to this L2PcInstance StopMove sm = new StopMove(this); sendPacket(sm); SystemMessage smsg = null; synchronized (target) { // Check if the target to pick up is visible if (!target.isVisible()) { // Send a Server->Client packet ActionFailed to this L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return; } if (!target.getDropProtection().tryPickUp(this)) { sendPacket(ActionFailed.STATIC_PACKET); smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1); smsg.addItemName(target); sendPacket(smsg); return; } if (((isInParty() && (getParty().getDistributionType() == PartyDistributionType.FINDERS_KEEPERS)) || !isInParty()) && !_inventory.validateCapacity(target)) { sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.YOUR_INVENTORY_IS_FULL); return; } if (isInvul() && !canOverrideCond(PcCondOverride.ITEM_CONDITIONS)) { sendPacket(ActionFailed.STATIC_PACKET); smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1); smsg.addItemName(target); sendPacket(smsg); return; } if ((target.getOwnerId() != 0) && (target.getOwnerId() != getObjectId()) && !isInLooterParty(target.getOwnerId())) { if (target.getId() == Inventory.ADENA_ID) { smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1_ADENA); smsg.addLong(target.getCount()); } else if (target.getCount() > 1) { smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S2_S1_S); smsg.addItemName(target); smsg.addLong(target.getCount()); } else { smsg = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_FAILED_TO_PICK_UP_S1); smsg.addItemName(target); } sendPacket(ActionFailed.STATIC_PACKET); sendPacket(smsg); return; } // You can pickup only 1 combat flag if (FortSiegeManager.getInstance().isCombat(target.getId())) { if (!FortSiegeManager.getInstance().checkIfCanPickup(this)) { return; } } if ((target.getItemLootShedule() != null) && ((target.getOwnerId() == getObjectId()) || isInLooterParty(target.getOwnerId()))) { target.resetOwnerTimer(); } // Remove the L2ItemInstance from the world and send server->client GetItem packets target.pickupMe(this); if (Config.SAVE_DROPPED_ITEM) { ItemsOnGroundManager.getInstance().removeObject(target); } } // Auto use herbs - pick up if (target.getItem().hasExImmediateEffect()) { IItemHandler handler = ItemHandler.getInstance().getHandler(target.getEtcItem()); if (handler == null) { _log.warning("No item handler registered for item ID: " + target.getId() + "."); } else { handler.useItem(this, target, false); } ItemTable.getInstance().destroyItem("Consume", target, this, null); } // Cursed Weapons are not distributed else if (CursedWeaponsManager.getInstance().isCursed(target.getId())) { addItem("Pickup", target, null, true); } else if (FortSiegeManager.getInstance().isCombat(target.getId())) { addItem("Pickup", target, null, true); } else { // if item is instance of L2ArmorType or L2WeaponType broadcast an "Attention" system message if ((target.getItemType() instanceof ArmorType) || (target.getItemType() instanceof WeaponType)) { if (target.getEnchantLevel() > 0) { smsg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_C1_HAS_PICKED_UP_S2_S3); smsg.addPcName(this); smsg.addInt(target.getEnchantLevel()); smsg.addItemName(target.getId()); broadcastPacket(smsg, 1400); } else { smsg = SystemMessage.getSystemMessage(SystemMessageId.ATTENTION_C1_HAS_PICKED_UP_S2); smsg.addPcName(this); smsg.addItemName(target.getId()); broadcastPacket(smsg, 1400); } } // Check if a Party is in progress if (isInParty()) { getParty().distributeItem(this, target); } else if ((target.getId() == Inventory.ADENA_ID) && (getInventory().getAdenaInstance() != null)) { addAdena("Pickup", target.getCount(), null, true); ItemTable.getInstance().destroyItem("Pickup", target, this, null); } else { addItem("Pickup", target, null, true); // Auto-Equip arrows/bolts if player has a bow/crossbow and player picks up arrows/bolts. final L2ItemInstance weapon = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); if (weapon != null) { final L2EtcItem etcItem = target.getEtcItem(); if (etcItem != null) { final EtcItemType itemType = etcItem.getItemType(); if ((weapon.getItemType() == WeaponType.BOW) && (itemType == EtcItemType.ARROW)) { checkAndEquipArrows(); } else if ((weapon.getItemType() == WeaponType.CROSSBOW) && (itemType == EtcItemType.BOLT)) { checkAndEquipBolts(); } } } } } } @Override public void doAttack(L2Character target) { super.doAttack(target); setRecentFakeDeath(false); } @Override public void doCast(Skill skill) { super.doCast(skill); setRecentFakeDeath(false); } public boolean canOpenPrivateStore() { if ((Config.SHOP_MIN_RANGE_FROM_NPC > 0) || (Config.SHOP_MIN_RANGE_FROM_PLAYER > 0)) { for (L2Character cha : getKnownList().getKnownCharacters()) { if (Util.checkIfInRange(cha.getMinShopDistance(), this, cha, true)) { sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_CANNOT_OPEN_A_PRIVATE_STORE_HERE)); return false; } } } return !isAlikeDead() && !isInOlympiadMode() && !isMounted() && !isInsideZone(ZoneId.NO_STORE) && !isCastingNow(); } @Override public int getMinShopDistance() { return (isSitting()) ? Config.SHOP_MIN_RANGE_FROM_PLAYER : 0; } public void tryOpenPrivateBuyStore() { // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death) if (canOpenPrivateStore()) { if ((getPrivateStoreType() == PrivateStoreType.BUY) || (getPrivateStoreType() == PrivateStoreType.BUY_MANAGE)) { setPrivateStoreType(PrivateStoreType.NONE); } if (getPrivateStoreType() == PrivateStoreType.NONE) { if (isSitting()) { standUp(); } setPrivateStoreType(PrivateStoreType.BUY_MANAGE); sendPacket(new PrivateStoreManageListBuy(this)); } } else { if (isInsideZone(ZoneId.NO_STORE)) { sendPacket(SystemMessageId.YOU_CANNOT_OPEN_A_PRIVATE_STORE_HERE); } sendPacket(ActionFailed.STATIC_PACKET); } } public void tryOpenPrivateSellStore(boolean isPackageSale) { // Player shouldn't be able to set stores if he/she is alike dead (dead or fake death) if (canOpenPrivateStore()) { if ((getPrivateStoreType() == PrivateStoreType.SELL) || (getPrivateStoreType() == PrivateStoreType.SELL_MANAGE) || (getPrivateStoreType() == PrivateStoreType.PACKAGE_SELL)) { setPrivateStoreType(PrivateStoreType.NONE); } if (getPrivateStoreType() == PrivateStoreType.NONE) { if (isSitting()) { standUp(); } setPrivateStoreType(PrivateStoreType.SELL_MANAGE); sendPacket(new PrivateStoreManageListSell(this, isPackageSale)); } } else { if ((getPrivateStoreType() != PrivateStoreType.NONE) && !isAlikeDead()) { setPrivateStoreType(PrivateStoreType.NONE); if (isSitting()) { standUp(); } return; } if (isInsideZone(ZoneId.NO_STORE)) { sendPacket(SystemMessageId.YOU_CANNOT_OPEN_A_PRIVATE_STORE_HERE); } sendPacket(ActionFailed.STATIC_PACKET); } } public final PreparedListContainer getMultiSell() { return _currentMultiSell; } public final void setMultiSell(PreparedListContainer list) { _currentMultiSell = list; } @Override public boolean isTransformed() { return (_transformation != null) && !_transformation.isStance(); } public boolean isInStance() { return (_transformation != null) && _transformation.isStance(); } public void transform(Transform transformation) { if (!Config.ALLOW_MOUNTS_DURING_SIEGE && transformation.isRiding() && isInsideZone(ZoneId.SIEGE)) { return; } if (_transformation != null) { // You already polymorphed and cannot polymorph again. SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOU_ALREADY_POLYMORPHED_AND_CANNOT_POLYMORPH_AGAIN); sendPacket(msg); return; } setQueuedSkill(null, false, false); if (isMounted()) { // Get off the strider or something else if character is mounted dismount(); } _transformation = transformation; getEffectList().stopAllToggles(); transformation.onTransform(this); sendSkillList(); sendPacket(new SkillCoolTime(this)); sendPacket(new ExUserInfoAbnormalVisualEffect(this)); broadcastUserInfo(); // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerTransform(this, transformation.getId()), this); } @Override public void untransform() { if (_transformation != null) { setQueuedSkill(null, false, false); _transformation.onUntransform(this); _transformation = null; getEffectList().stopSkillEffects(false, AbnormalType.TRANSFORM); sendSkillList(); sendPacket(new SkillCoolTime(this)); broadcastUserInfo(); // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerTransform(this, 0), this); } } @Override public Transform getTransformation() { return _transformation; } /** * This returns the transformation Id of the current transformation. For example, if a player is transformed as a Buffalo, and then picks up the Zariche, the transform Id returned will be that of the Zariche, and NOT the Buffalo. * @return Transformation Id */ public int getTransformationId() { return (isTransformed() ? getTransformation().getId() : 0); } public int getTransformationDisplayId() { return (isTransformed() ? getTransformation().getDisplayId() : 0); } /** * Set a target. <B><U> Actions</U> :</B> * <ul> * <li>Remove the L2PcInstance from the _statusListener of the old target if it was a L2Character</li> * <li>Add the L2PcInstance to the _statusListener of the new target if it's a L2Character</li> * <li>Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object)</li> * </ul> * @param newTarget The L2Object to target */ @Override public void setTarget(L2Object newTarget) { if (newTarget != null) { boolean isInParty = (newTarget.isPlayer() && isInParty() && getParty().containsPlayer(newTarget.getActingPlayer())); // Prevents /target exploiting if (!isInParty && (Math.abs(newTarget.getZ() - getZ()) > 1000)) { newTarget = null; } // Check if the new target is visible if ((newTarget != null) && !isInParty && !newTarget.isVisible()) { newTarget = null; } // vehicles cant be targeted if (!isGM() && (newTarget instanceof L2Vehicle)) { newTarget = null; } } // Get the current target L2Object oldTarget = getTarget(); if (oldTarget != null) { if (oldTarget.equals(newTarget)) // no target change? { // Validate location of the target. if ((newTarget != null) && (newTarget.getObjectId() != getObjectId())) { sendPacket(new ValidateLocation(newTarget)); } return; } // Remove the target from the status listener. oldTarget.removeStatusListener(this); } if (newTarget instanceof L2Character) { final L2Character target = (L2Character) newTarget; // Validate location of the new target. if (newTarget.getObjectId() != getObjectId()) { sendPacket(new ValidateLocation(target)); } // Show the client his new target. sendPacket(new MyTargetSelected(this, target)); // Register target to listen for hp changes. target.addStatusListener(this); // Send max/current hp. final StatusUpdate su = new StatusUpdate(target); su.addAttribute(StatusUpdate.MAX_HP, target.getMaxHp()); su.addAttribute(StatusUpdate.CUR_HP, (int) target.getCurrentHp()); sendPacket(su); // To others the new target, and not yourself! Broadcast.toKnownPlayers(this, new TargetSelected(getObjectId(), newTarget.getObjectId(), getX(), getY(), getZ())); // Send buffs sendPacket(new ExAbnormalStatusUpdateFromTarget(target)); } // Target was removed? if ((newTarget == null) && (getTarget() != null)) { broadcastPacket(new TargetUnselected(this)); } // Target the new L2Object (add the target to the L2PcInstance _target, _knownObject and L2PcInstance to _KnownObject of the L2Object) super.setTarget(newTarget); } /** * Return the active weapon instance (always equiped in the right hand). */ @Override public L2ItemInstance getActiveWeaponInstance() { return getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); } /** * Return the active weapon item (always equiped in the right hand). */ @Override public L2Weapon getActiveWeaponItem() { L2ItemInstance weapon = getActiveWeaponInstance(); if (weapon == null) { return getFistsWeaponItem(); } return (L2Weapon) weapon.getItem(); } public L2ItemInstance getChestArmorInstance() { return getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST); } public L2ItemInstance getLegsArmorInstance() { return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LEGS); } public L2Armor getActiveChestArmorItem() { L2ItemInstance armor = getChestArmorInstance(); if (armor == null) { return null; } return (L2Armor) armor.getItem(); } public L2Armor getActiveLegsArmorItem() { L2ItemInstance legs = getLegsArmorInstance(); if (legs == null) { return null; } return (L2Armor) legs.getItem(); } public boolean isWearingHeavyArmor() { L2ItemInstance legs = getLegsArmorInstance(); L2ItemInstance armor = getChestArmorInstance(); if ((armor != null) && (legs != null)) { if ((legs.getItemType() == ArmorType.HEAVY) && (armor.getItemType() == ArmorType.HEAVY)) { return true; } } if (armor != null) { if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && (armor.getItemType() == ArmorType.HEAVY))) { return true; } } return false; } public boolean isWearingLightArmor() { L2ItemInstance legs = getLegsArmorInstance(); L2ItemInstance armor = getChestArmorInstance(); if ((armor != null) && (legs != null)) { if ((legs.getItemType() == ArmorType.LIGHT) && (armor.getItemType() == ArmorType.LIGHT)) { return true; } } if (armor != null) { if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && (armor.getItemType() == ArmorType.LIGHT))) { return true; } } return false; } public boolean isWearingMagicArmor() { L2ItemInstance legs = getLegsArmorInstance(); L2ItemInstance armor = getChestArmorInstance(); if ((armor != null) && (legs != null)) { if ((legs.getItemType() == ArmorType.MAGIC) && (armor.getItemType() == ArmorType.MAGIC)) { return true; } } if (armor != null) { if (((getInventory().getPaperdollItem(Inventory.PAPERDOLL_CHEST).getItem().getBodyPart() == L2Item.SLOT_FULL_ARMOR) && (armor.getItemType() == ArmorType.MAGIC))) { return true; } } return false; } public boolean isMarried() { return _married; } public void setMarried(boolean state) { _married = state; } public boolean isEngageRequest() { return _engagerequest; } public void setEngageRequest(boolean state, int playerid) { _engagerequest = state; _engageid = playerid; } public void setMarryRequest(boolean state) { _marryrequest = state; } public boolean isMarryRequest() { return _marryrequest; } public void setMarryAccepted(boolean state) { _marryaccepted = state; } public boolean isMarryAccepted() { return _marryaccepted; } public int getEngageId() { return _engageid; } public int getPartnerId() { return _partnerId; } public void setPartnerId(int partnerid) { _partnerId = partnerid; } public int getCoupleId() { return _coupleId; } public void setCoupleId(int coupleId) { _coupleId = coupleId; } public void engageAnswer(int answer) { if (!_engagerequest) { return; } else if (_engageid == 0) { return; } else { L2PcInstance ptarget = L2World.getInstance().getPlayer(_engageid); setEngageRequest(false, 0); if (ptarget != null) { if (answer == 1) { CoupleManager.getInstance().createCouple(ptarget, L2PcInstance.this); ptarget.sendMessage("Request to Engage has been >ACCEPTED<"); } else { ptarget.sendMessage("Request to Engage has been >DENIED<!"); } } } } /** * Return the secondary weapon instance (always equiped in the left hand). */ @Override public L2ItemInstance getSecondaryWeaponInstance() { return getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); } /** * Return the secondary L2Item item (always equiped in the left hand).<BR> * Arrows, Shield..<BR> */ @Override public L2Item getSecondaryWeaponItem() { L2ItemInstance item = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); if (item != null) { return item.getItem(); } return null; } /** * Kill the L2Character, Apply Death Penalty, Manage gain/loss Karma and Item Drop. <B><U> Actions</U> :</B> <li>Reduce the Experience of the L2PcInstance in function of the calculated Death Penalty</li> <li>If necessary, unsummon the Pet of the killed L2PcInstance</li> <li>Manage Karma gain for * attacker and Karam loss for the killed L2PcInstance</li> <li>If the killed L2PcInstance has Karma, manage Drop Item</li> <li>Kill the L2PcInstance</li> * @param killer */ @Override public boolean doDie(L2Character killer) { if (killer != null) { final L2PcInstance pk = killer.getActingPlayer(); if (pk != null) { EventDispatcher.getInstance().notifyEventAsync(new OnPlayerPvPKill(pk, this), this); TvTEvent.onKill(killer, this); if (L2Event.isParticipant(pk)) { pk.getEventStatus().getKills().add(this); } // announce pvp/pk if (Config.ANNOUNCE_PK_PVP && !pk.isGM()) { String msg = ""; if (getPvpFlag() == 0) { msg = Config.ANNOUNCE_PK_MSG.replace("$killer", pk.getName()).replace("$target", getName()); if (Config.ANNOUNCE_PK_PVP_NORMAL_MESSAGE) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S13); sm.addString(msg); Broadcast.toAllOnlinePlayers(sm); } else { Broadcast.toAllOnlinePlayers(msg, false); } } else if (getPvpFlag() != 0) { msg = Config.ANNOUNCE_PVP_MSG.replace("$killer", pk.getName()).replace("$target", getName()); if (Config.ANNOUNCE_PK_PVP_NORMAL_MESSAGE) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S13); sm.addString(msg); Broadcast.toAllOnlinePlayers(sm); } else { Broadcast.toAllOnlinePlayers(msg, false); } } } } broadcastStatusUpdate(); // Clear resurrect xp calculation setExpBeforeDeath(0); // Issues drop of Cursed Weapon. if (isCursedWeaponEquipped()) { CursedWeaponsManager.getInstance().drop(_cursedWeaponEquippedId, killer); } else if (isCombatFlagEquipped()) { final Fort fort = FortManager.getInstance().getFort(this); if (fort != null) { FortSiegeManager.getInstance().dropCombatFlag(this, fort.getResidenceId()); } else { final int slot = getInventory().getSlotFromItem(getInventory().getItemByItemId(9819)); getInventory().unEquipItemInBodySlot(slot); destroyItem("CombatFlag", getInventory().getItemByItemId(9819), null, true); } } else { final boolean insidePvpZone = isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.SIEGE); if ((pk == null) || !pk.isCursedWeaponEquipped()) { onDieDropItem(killer); // Check if any item should be dropped if (!insidePvpZone) { if ((pk != null) && (pk.getClan() != null) && (getClan() != null) && !isAcademyMember() && !(pk.isAcademyMember())) { if ((_clan.isAtWarWith(pk.getClanId()) && pk.getClan().isAtWarWith(_clan.getId())) || (isInSiege() && pk.isInSiege())) { if (AntiFeedManager.getInstance().check(killer, this)) { // when your reputation score is 0 or below, the other clan cannot acquire any reputation points if (getClan().getReputationScore() > 0) { pk.getClan().addReputationScore(Config.REPUTATION_SCORE_PER_KILL, false); } // when the opposing sides reputation score is 0 or below, your clans reputation score does not decrease if (pk.getClan().getReputationScore() > 0) { _clan.takeReputationScore(Config.REPUTATION_SCORE_PER_KILL, false); } } } } } // If player is Lucky shouldn't get penalized. if (!isLucky() && !insidePvpZone) { calculateDeathExpPenalty(killer, isAtWarWith(pk)); } } } } // calculate Shilen's Breath debuff level calculateShilensBreathDebuffLevel(killer); // Kill the L2PcInstance if (!super.doDie(killer)) { return false; } if (isMounted()) { stopFeed(); } synchronized (this) { if (isFakeDeath()) { stopFakeDeath(true); } } // Unsummon Cubics if (!_cubics.isEmpty()) { for (L2CubicInstance cubic : _cubics.values()) { cubic.stopAction(); cubic.cancelDisappear(); } _cubics.clear(); } if (isChannelized()) { getSkillChannelized().abortChannelization(); } if (getAgathionId() != 0) { setAgathionId(0); } stopRentPet(); stopWaterTask(); AntiFeedManager.getInstance().setLastDeathTime(getObjectId()); return true; } private void onDieDropItem(L2Character killer) { if (L2Event.isParticipant(this) || (killer == null)) { return; } L2PcInstance pk = killer.getActingPlayer(); if ((getKarma() <= 0) && (pk != null) && (pk.getClan() != null) && (getClan() != null) && (pk.getClan().isAtWarWith(getClanId()) // || getClan().isAtWarWith(((L2PcInstance)killer).getClanId()) )) { return; } if ((!isInsideZone(ZoneId.PVP) || (pk == null)) && (!isGM() || Config.KARMA_DROP_GM)) { boolean isKarmaDrop = false; boolean isKillerNpc = (killer instanceof L2Npc); int pkLimit = Config.KARMA_PK_LIMIT; int dropEquip = 0; int dropEquipWeapon = 0; int dropItem = 0; int dropLimit = 0; int dropPercent = 0; if ((getKarma() > 0) && (getPkKills() >= pkLimit)) { isKarmaDrop = true; dropPercent = Config.KARMA_RATE_DROP; dropEquip = Config.KARMA_RATE_DROP_EQUIP; dropEquipWeapon = Config.KARMA_RATE_DROP_EQUIP_WEAPON; dropItem = Config.KARMA_RATE_DROP_ITEM; dropLimit = Config.KARMA_DROP_LIMIT; } else if (isKillerNpc && (getLevel() > 4)) { dropPercent = Config.PLAYER_RATE_DROP; dropEquip = Config.PLAYER_RATE_DROP_EQUIP; dropEquipWeapon = Config.PLAYER_RATE_DROP_EQUIP_WEAPON; dropItem = Config.PLAYER_RATE_DROP_ITEM; dropLimit = Config.PLAYER_DROP_LIMIT; } if ((dropPercent > 0) && (Rnd.get(100) < dropPercent)) { int dropCount = 0; int itemDropPercent = 0; final L2Summon pet = getPet(); for (L2ItemInstance itemDrop : getInventory().getItems()) { // Don't drop if (itemDrop.isShadowItem() || // Dont drop Shadow Items itemDrop.isTimeLimitedItem() || // Dont drop Time Limited Items !itemDrop.isDropable() || (itemDrop.getId() == Inventory.ADENA_ID) || // Adena (itemDrop.getItem().getType2() == L2Item.TYPE2_QUEST) || // Quest Items ((pet != null) && (pet.getControlObjectId() == itemDrop.getId())) || // Control Item of active pet (Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_ITEMS, itemDrop.getId()) >= 0) || // Item listed in the non droppable item list (Arrays.binarySearch(Config.KARMA_LIST_NONDROPPABLE_PET_ITEMS, itemDrop.getId()) >= 0 // Item listed in the non droppable pet item list )) { continue; } if (itemDrop.isEquipped()) { // Set proper chance according to Item type of equipped Item itemDropPercent = itemDrop.getItem().getType2() == L2Item.TYPE2_WEAPON ? dropEquipWeapon : dropEquip; getInventory().unEquipItemInSlot(itemDrop.getLocationSlot()); } else { itemDropPercent = dropItem; // Item in inventory } // NOTE: Each time an item is dropped, the chance of another item being dropped gets lesser (dropCount * 2) if (Rnd.get(100) < itemDropPercent) { dropItem("DieDrop", itemDrop, killer, true); if (isKarmaDrop) { _log.warning(getName() + " has karma and dropped id = " + itemDrop.getId() + ", count = " + itemDrop.getCount()); } else { _log.warning(getName() + " dropped id = " + itemDrop.getId() + ", count = " + itemDrop.getCount()); } if (++dropCount >= dropLimit) { break; } } } } } } public void onKillUpdatePvPKarma(L2Character target) { if ((target == null) || !target.isPlayable()) { return; } L2PcInstance targetPlayer = target.getActingPlayer(); if ((targetPlayer == null) || (targetPlayer == this)) { return; } if (isCursedWeaponEquipped() && target.isPlayer()) { CursedWeaponsManager.getInstance().increaseKills(_cursedWeaponEquippedId); return; } // If in duel and you kill (only can kill l2summon), do nothing if (isInDuel() && targetPlayer.isInDuel()) { return; } // If in Arena, do nothing if (isInsideZone(ZoneId.PVP) || targetPlayer.isInsideZone(ZoneId.PVP)) { if ((getSiegeState() > 0) && (targetPlayer.getSiegeState() > 0) && (getSiegeState() != targetPlayer.getSiegeState())) { final L2Clan killerClan = getClan(); final L2Clan targetClan = targetPlayer.getClan(); if ((killerClan != null) && (targetClan != null)) { killerClan.addSiegeKill(); targetClan.addSiegeDeath(); } } return; } // Check if it's pvp if ((checkIfPvP(target) && (targetPlayer.getPvpFlag() != 0)) || (isInsideZone(ZoneId.PVP) && targetPlayer.isInsideZone(ZoneId.PVP))) { increasePvpKills(target); } else { if (Config.FACTION_SYSTEM_ENABLED && ((isGood() && targetPlayer.isEvil()) || (isEvil() && targetPlayer.isGood()))) { increasePvpKills(target); return; } if (Config.FACTION_SYSTEM_ENABLED && ((isGood() && targetPlayer.isGood()) || (isEvil() && targetPlayer.isEvil()))) { increasePkKillsAndKarma(target); } // Target player doesn't have pvp flag set // check about wars if ((targetPlayer.getClan() != null) && (getClan() != null) && getClan().isAtWarWith(targetPlayer.getClanId()) && targetPlayer.getClan().isAtWarWith(getClanId()) && (targetPlayer.getPledgeType() != L2Clan.SUBUNIT_ACADEMY) && (getPledgeType() != L2Clan.SUBUNIT_ACADEMY)) { // 'Both way war' -> 'PvP Kill' increasePvpKills(target); return; } // 'No war' or 'One way war' -> 'Normal PK' if (targetPlayer.getKarma() > 0) // Target player has karma { if (Config.KARMA_AWARD_PK_KILL) { increasePvpKills(target); } } else if (targetPlayer.isOnDarkSide()) // Member's of Dark side can be killed without any penalty { increasePvpKills(target); } else if (targetPlayer.getPvpFlag() == 0) // Target player doesn't have karma { increasePkKillsAndKarma(target); checkItemRestriction(); // Unequip adventurer items } } } /** * Increase the pvp kills count and send the info to the player * @param target */ public void increasePvpKills(L2Character target) { if ((target instanceof L2PcInstance) && AntiFeedManager.getInstance().check(this, target)) { setPvpKills(getPvpKills() + 1); L2BattalionZone.givereward(this); // Send a Server->Client UserInfo packet to attacker with its Karma and PK Counter UserInfo ui = new UserInfo(this, false); ui.addComponentType(UserInfoType.SOCIAL); sendPacket(ui); } } /** * Increase pk count, karma and send the info to the player * @param target */ public void increasePkKillsAndKarma(L2Character target) { // Only playables can increase karma/pk if ((target == null) || !target.isPlayable()) { return; } if (!Config.FACTION_SYSTEM_ENABLED) { // Calculate new karma. (calculate karma before incrase pk count!) setKarma(getKarma() + Formulas.calculateKarmaGain(getPkKills(), target.isSummon())); } // PK Points are increased only if you kill a player. if (target.isPlayer()) { setPkKills(getPkKills() + 1); } // Update player's UI. UserInfo ui = new UserInfo(this, false); ui.addComponentType(UserInfoType.SOCIAL); sendPacket(ui); } public void updatePvPStatus() { if (isInsideZone(ZoneId.PVP)) { return; } setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME); if (getPvpFlag() == 0) { startPvPFlag(); } } public void updatePvPStatus(L2Character target) { L2PcInstance player_target = target.getActingPlayer(); if (player_target == null) { return; } if (Config.FACTION_SYSTEM_ENABLED && target.isPlayer() && ((isGood() && player_target.isEvil()) || (isEvil() && player_target.isGood()))) { return; } if ((isInDuel() && (player_target.getDuelId() == getDuelId()))) { return; } if ((!isInsideZone(ZoneId.PVP) || !player_target.isInsideZone(ZoneId.PVP)) && (player_target.getKarma() == 0)) { if (checkIfPvP(player_target)) { setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_PVP_TIME); } else { setPvpFlagLasts(System.currentTimeMillis() + Config.PVP_NORMAL_TIME); } if (getPvpFlag() == 0) { startPvPFlag(); } } } /** * @return {@code true} if player has Lucky effect and is level 9 or less */ public boolean isLucky() { return (getLevel() <= 9) && isAffectedBySkill(CommonSkill.LUCKY.getId()); } /** * Restore the specified % of experience this L2PcInstance has lost and sends a Server->Client StatusUpdate packet. * @param restorePercent */ public void restoreExp(double restorePercent) { if (getExpBeforeDeath() > 0) { // Restore the specified % of lost experience. getStat().addExp(Math.round(((getExpBeforeDeath() - getExp()) * restorePercent) / 100)); setExpBeforeDeath(0); } } /** * Reduce the Experience (and level if necessary) of the L2PcInstance in function of the calculated Death Penalty.<BR> * <B><U> Actions</U> :</B> <li>Calculate the Experience loss</li> <li>Set the value of _expBeforeDeath</li> <li>Set the new Experience value of the L2PcInstance and Decrease its level if necessary</li> <li>Send a Server->Client StatusUpdate packet with its new Experience</li> * @param killer * @param atWar */ public void calculateDeathExpPenalty(L2Character killer, boolean atWar) { final int lvl = getLevel(); double percentLost = PlayerXpPercentLostData.getInstance().getXpPercent(getLevel()); if (killer != null) { if (killer.isRaid()) { percentLost *= calcStat(Stats.REDUCE_EXP_LOST_BY_RAID, 1); } else if (killer.isMonster()) { percentLost *= calcStat(Stats.REDUCE_EXP_LOST_BY_MOB, 1); } else if (killer.isPlayable()) { percentLost *= calcStat(Stats.REDUCE_EXP_LOST_BY_PVP, 1); } } if (getKarma() > 0) { percentLost *= Config.RATE_KARMA_EXP_LOST; } // Calculate the Experience loss long lostExp = 0; if (!L2Event.isParticipant(this)) { if (lvl < ExperienceData.getInstance().getMaxLevel()) { lostExp = Math.round(((getStat().getExpForLevel(lvl + 1) - getStat().getExpForLevel(lvl)) * percentLost) / 100); } else { lostExp = Math.round(((getStat().getExpForLevel(ExperienceData.getInstance().getMaxLevel()) - getStat().getExpForLevel(ExperienceData.getInstance().getMaxLevel() - 1)) * percentLost) / 100); } } if (atWar) { lostExp /= 4.0; } setExpBeforeDeath(getExp()); getStat().addExp(-lostExp); } public boolean isPartyWaiting() { return PartyMatchWaitingList.getInstance().getPlayers().contains(this); } public void setPartyRoom(int id) { _partyroom = id; } public int getPartyRoom() { return _partyroom; } public boolean isInPartyMatchRoom() { return _partyroom > 0; } /** * Stop the HP/MP/CP Regeneration task. <B><U> Actions</U> :</B> <li>Set the RegenActive flag to False</li> <li>Stop the HP/MP/CP Regeneration task</li> */ public void stopAllTimers() { stopHpMpRegeneration(); stopWarnUserTakeBreak(); stopWaterTask(); stopFeed(); clearPetData(); storePetFood(_mountNpcId); stopRentPet(); stopPvpRegTask(); stopSoulTask(); stopChargeTask(); stopFameTask(); stopRecoGiveTask(); } @Override public L2Summon getPet() { return _pet; } @Override public Map<Integer, L2Summon> getServitors() { return _servitors == null ? Collections.emptyMap() : _servitors; } public L2Summon getAnyServitor() { return getServitors().values().stream().findAny().orElse(null); } @Override public L2Summon getServitor(int objectId) { return getServitors().get(objectId); } /** * @return the L2Decoy of the L2PcInstance or null. */ public L2Decoy getDecoy() { return _decoy; } /** * @return the L2Trap of the L2PcInstance or null. */ public L2TrapInstance getTrap() { return _trap; } /** * Set the L2Summon of the L2PcInstance. * @param pet */ public void setPet(L2Summon pet) { _pet = pet; } public void addServitor(L2Summon servitor) { if (_servitors == null) { synchronized (this) { if (_servitors == null) { _servitors = new ConcurrentHashMap<>(1); } } } _servitors.put(servitor.getObjectId(), servitor); } /** * Set the L2Decoy of the L2PcInstance. * @param decoy */ public void setDecoy(L2Decoy decoy) { _decoy = decoy; } /** * Set the L2Trap of this L2PcInstance * @param trap */ public void setTrap(L2TrapInstance trap) { _trap = trap; } /** * @return the L2Summon of the L2PcInstance or null. */ public List<L2TamedBeastInstance> getTrainedBeasts() { return _tamedBeast; } /** * Set the L2Summon of the L2PcInstance. * @param tamedBeast */ public void addTrainedBeast(L2TamedBeastInstance tamedBeast) { if (_tamedBeast == null) { _tamedBeast = new ArrayList<>(); } _tamedBeast.add(tamedBeast); } /** * @return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...). */ public L2Request getRequest() { return _request; } /** * Set the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...). * @param requester */ public void setActiveRequester(L2PcInstance requester) { _activeRequester = requester; } /** * @return the L2PcInstance requester of a transaction (ex : FriendInvite, JoinAlly, JoinParty...). */ public L2PcInstance getActiveRequester() { L2PcInstance requester = _activeRequester; if (requester != null) { if (requester.isRequestExpired() && (_activeTradeList == null)) { _activeRequester = null; } } return _activeRequester; } /** * @return True if a transaction is in progress. */ public boolean isProcessingRequest() { return (getActiveRequester() != null) || (_requestExpireTime > GameTimeController.getInstance().getGameTicks()); } /** * @return True if a transaction is in progress. */ public boolean isProcessingTransaction() { return (getActiveRequester() != null) || (_activeTradeList != null) || (_requestExpireTime > GameTimeController.getInstance().getGameTicks()); } /** * Select the Warehouse to be used in next activity. * @param partner */ public void onTransactionRequest(L2PcInstance partner) { _requestExpireTime = GameTimeController.getInstance().getGameTicks() + (REQUEST_TIMEOUT * GameTimeController.TICKS_PER_SECOND); partner.setActiveRequester(this); } /** * Return true if last request is expired. * @return */ public boolean isRequestExpired() { return !(_requestExpireTime > GameTimeController.getInstance().getGameTicks()); } /** * Select the Warehouse to be used in next activity. */ public void onTransactionResponse() { _requestExpireTime = 0; } /** * Select the Warehouse to be used in next activity. * @param warehouse */ public void setActiveWarehouse(ItemContainer warehouse) { _activeWarehouse = warehouse; } /** * @return active Warehouse. */ public ItemContainer getActiveWarehouse() { return _activeWarehouse; } /** * Select the TradeList to be used in next activity. * @param tradeList */ public void setActiveTradeList(TradeList tradeList) { _activeTradeList = tradeList; } /** * @return active TradeList. */ public TradeList getActiveTradeList() { return _activeTradeList; } public void onTradeStart(L2PcInstance partner) { _activeTradeList = new TradeList(this); _activeTradeList.setPartner(partner); SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.YOU_BEGIN_TRADING_WITH_C1); msg.addPcName(partner); sendPacket(msg); sendPacket(new TradeStart(this)); } public void onTradeConfirm(L2PcInstance partner) { SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_CONFIRMED_THE_TRADE); msg.addPcName(partner); sendPacket(msg); sendPacket(TradeOtherDone.STATIC_PACKET); } public void onTradeCancel(L2PcInstance partner) { if (_activeTradeList == null) { return; } _activeTradeList.lock(); _activeTradeList = null; sendPacket(new TradeDone(0)); SystemMessage msg = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_CANCELLED_THE_TRADE); msg.addPcName(partner); sendPacket(msg); } public void onTradeFinish(boolean successfull) { _activeTradeList = null; sendPacket(new TradeDone(1)); if (successfull) { sendPacket(SystemMessageId.YOUR_TRADE_WAS_SUCCESSFUL); } } public void startTrade(L2PcInstance partner) { onTradeStart(partner); partner.onTradeStart(this); } public void cancelActiveTrade() { if (_activeTradeList == null) { return; } L2PcInstance partner = _activeTradeList.getPartner(); if (partner != null) { partner.onTradeCancel(this); } onTradeCancel(this); } public boolean hasManufactureShop() { return (_manufactureItems != null) && !_manufactureItems.isEmpty(); } /** * Get the manufacture items map of this player. * @return the the manufacture items map */ public Map<Integer, L2ManufactureItem> getManufactureItems() { if (_manufactureItems == null) { synchronized (this) { if (_manufactureItems == null) { _manufactureItems = Collections.synchronizedMap(new LinkedHashMap<Integer, L2ManufactureItem>()); } } } return _manufactureItems; } /** * Get the store name, if any. * @return the store name */ public String getStoreName() { return _storeName; } /** * Set the store name. * @param name the store name to set */ public void setStoreName(String name) { _storeName = name == null ? "" : name; } /** * @return the _buyList object of the L2PcInstance. */ public TradeList getSellList() { if (_sellList == null) { _sellList = new TradeList(this); } return _sellList; } /** * @return the _buyList object of the L2PcInstance. */ public TradeList getBuyList() { if (_buyList == null) { _buyList = new TradeList(this); } return _buyList; } /** * Set the Private Store type of the L2PcInstance. <B><U> Values </U> :</B> <li>0 : STORE_PRIVATE_NONE</li> <li>1 : STORE_PRIVATE_SELL</li> <li>2 : sellmanage</li><BR> * <li>3 : STORE_PRIVATE_BUY</li><BR> * <li>4 : buymanage</li><BR> * <li>5 : STORE_PRIVATE_MANUFACTURE</li><BR> * @param privateStoreType */ public void setPrivateStoreType(PrivateStoreType privateStoreType) { _privateStoreType = privateStoreType; if (Config.OFFLINE_DISCONNECT_FINISHED && (privateStoreType == PrivateStoreType.NONE) && ((getClient() == null) || getClient().isDetached())) { deleteMe(); } } /** * <B><U> Values </U> :</B> <li>0 : STORE_PRIVATE_NONE</li> <li>1 : STORE_PRIVATE_SELL</li> <li>2 : sellmanage</li><BR> * <li>3 : STORE_PRIVATE_BUY</li><BR> * <li>4 : buymanage</li><BR> * <li>5 : STORE_PRIVATE_MANUFACTURE</li><BR> * @return the Private Store type of the L2PcInstance. */ public PrivateStoreType getPrivateStoreType() { return _privateStoreType; } /** * Set the _clan object, _clanId, _clanLeader Flag and title of the L2PcInstance. * @param clan */ public void setClan(L2Clan clan) { _clan = clan; setTitle(""); if (clan == null) { _clanId = 0; _clanPrivileges = new EnumIntBitmask<>(ClanPrivilege.class, false); _pledgeType = 0; _powerGrade = 0; _lvlJoinedAcademy = 0; _apprentice = 0; _sponsor = 0; _activeWarehouse = null; return; } if (!clan.isMember(getObjectId())) { // char has been kicked from clan setClan(null); return; } _clanId = clan.getId(); } /** * @return the _clan object of the L2PcInstance. */ @Override public L2Clan getClan() { return _clan; } /** * @return True if the L2PcInstance is the leader of its clan. */ public boolean isClanLeader() { if (getClan() == null) { return false; } return getObjectId() == getClan().getLeaderId(); } /** * Reduce the number of arrows/bolts owned by the L2PcInstance and send it Server->Client Packet InventoryUpdate or ItemList (to unequip if the last arrow was consummed). */ @Override protected void reduceArrowCount(boolean bolts) { final L2ItemInstance arrows = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); if (arrows == null) { getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND); if (bolts) { _boltItem = null; } else { _arrowItem = null; } sendPacket(new ItemList(this, false)); return; } // Infinite quiver doesn't decreases arrows upon use if (arrows.isEtcItem() && arrows.getEtcItem().isInfinite()) { return; } // Adjust item quantity if (arrows.getCount() > 1) { synchronized (arrows) { arrows.changeCountWithoutTrace(-1, this, null); arrows.setLastChange(L2ItemInstance.MODIFIED); // could do also without saving, but let's save approx 1 of 10 if ((GameTimeController.getInstance().getGameTicks() % 10) == 0) { arrows.updateDatabase(); } _inventory.refreshWeight(); } } else { // Destroy entire item and save to database _inventory.destroyItem("Consume", arrows, this, null); getInventory().unEquipItemInSlot(Inventory.PAPERDOLL_LHAND); if (bolts) { _boltItem = null; } else { _arrowItem = null; } sendPacket(new ItemList(this, false)); return; } if (!Config.FORCE_INVENTORY_UPDATE) { InventoryUpdate iu = new InventoryUpdate(); iu.addModifiedItem(arrows); sendPacket(iu); } else { sendPacket(new ItemList(this, false)); } } /** * Equip arrows needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True. */ @Override protected boolean checkAndEquipArrows() { // Check if nothing is equiped in left hand if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) { // Get the L2ItemInstance of the arrows needed for this bow _arrowItem = getInventory().findArrowForBow(getActiveWeaponItem()); if (_arrowItem != null) { // Equip arrows needed in left hand getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _arrowItem); // Send a Server->Client packet ItemList to this L2PcINstance to update left hand equipement ItemList il = new ItemList(this, false); sendPacket(il); } } else { // Get the L2ItemInstance of arrows equiped in left hand _arrowItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); } return _arrowItem != null; } /** * Equip bolts needed in left hand and send a Server->Client packet ItemList to the L2PcINstance then return True. */ @Override protected boolean checkAndEquipBolts() { // Check if nothing is equiped in left hand if (getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND) == null) { // Get the L2ItemInstance of the arrows needed for this bow _boltItem = getInventory().findBoltForCrossBow(getActiveWeaponItem()); if (_boltItem != null) { // Equip arrows needed in left hand getInventory().setPaperdollItem(Inventory.PAPERDOLL_LHAND, _boltItem); // Send a Server->Client packet ItemList to this L2PcINstance to update left hand equipement ItemList il = new ItemList(this, false); sendPacket(il); } } else { // Get the L2ItemInstance of arrows equiped in left hand _boltItem = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); } return _boltItem != null; } /** * Disarm the player's weapon. * @return {@code true} if the player was disarmed or doesn't have a weapon to disarm, {@code false} otherwise. */ public boolean disarmWeapons() { // If there is no weapon to disarm then return true. final L2ItemInstance wpn = getInventory().getPaperdollItem(Inventory.PAPERDOLL_RHAND); if (wpn == null) { return true; } // Don't allow disarming a cursed weapon if (isCursedWeaponEquipped()) { return false; } // Don't allow disarming a Combat Flag or Territory Ward. if (isCombatFlagEquipped()) { return false; } // Don't allow disarming if the weapon is force equip. if (wpn.getWeaponItem().isForceEquip()) { return false; } final L2ItemInstance[] unequiped = getInventory().unEquipItemInBodySlotAndRecord(wpn.getItem().getBodyPart()); final InventoryUpdate iu = new InventoryUpdate(); for (L2ItemInstance itm : unequiped) { iu.addModifiedItem(itm); } sendPacket(iu); abortAttack(); broadcastUserInfo(); // This can be 0 if the user pressed the right mousebutton twice very fast. if (unequiped.length > 0) { final SystemMessage sm; if (unequiped[0].getEnchantLevel() > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED); sm.addInt(unequiped[0].getEnchantLevel()); sm.addItemName(unequiped[0]); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED); sm.addItemName(unequiped[0]); } sendPacket(sm); } return true; } /** * Disarm the player's shield. * @return {@code true}. */ public boolean disarmShield() { L2ItemInstance sld = getInventory().getPaperdollItem(Inventory.PAPERDOLL_LHAND); if (sld != null) { L2ItemInstance[] unequiped = getInventory().unEquipItemInBodySlotAndRecord(sld.getItem().getBodyPart()); InventoryUpdate iu = new InventoryUpdate(); for (L2ItemInstance itm : unequiped) { iu.addModifiedItem(itm); } sendPacket(iu); abortAttack(); broadcastUserInfo(); // this can be 0 if the user pressed the right mousebutton twice very fast if (unequiped.length > 0) { SystemMessage sm = null; if (unequiped[0].getEnchantLevel() > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED); sm.addInt(unequiped[0].getEnchantLevel()); sm.addItemName(unequiped[0]); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED); sm.addItemName(unequiped[0]); } sendPacket(sm); } } return true; } public boolean mount(L2Summon pet) { if (!Config.ALLOW_MOUNTS_DURING_SIEGE && isInsideZone(ZoneId.SIEGE)) { return false; } if (!disarmWeapons() || !disarmShield() || isTransformed()) { return false; } getEffectList().stopAllToggles(); setMount(pet.getId(), pet.getLevel()); setMountObjectID(pet.getControlObjectId()); clearPetData(); startFeed(pet.getId()); broadcastPacket(new Ride(this)); // Notify self and others about speed change broadcastUserInfo(); pet.unSummon(this); return true; } public boolean mount(int npcId, int controlItemObjId, boolean useFood) { if (!disarmWeapons() || !disarmShield() || isTransformed()) { return false; } getEffectList().stopAllToggles(); setMount(npcId, getLevel()); clearPetData(); setMountObjectID(controlItemObjId); broadcastPacket(new Ride(this)); // Notify self and others about speed change broadcastUserInfo(); if (useFood) { startFeed(npcId); } return true; } public boolean mountPlayer(L2Summon pet) { if ((pet != null) && pet.isMountable() && !isMounted() && !isBetrayed()) { if (isDead()) { // A strider cannot be ridden when dead sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_STRIDER_CANNOT_BE_RIDDEN_WHEN_DEAD); return false; } else if (pet.isDead()) { // A dead strider cannot be ridden. sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_DEAD_STRIDER_CANNOT_BE_RIDDEN); return false; } else if (pet.isInCombat() || pet.isRooted()) { // A strider in battle cannot be ridden sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_STRIDER_IN_BATTLE_CANNOT_BE_RIDDEN); return false; } else if (isInCombat()) { // A strider cannot be ridden while in battle sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_STRIDER_CANNOT_BE_RIDDEN_WHILE_IN_BATTLE); return false; } else if (isSitting()) { // A strider can be ridden only when standing sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_STRIDER_CAN_BE_RIDDEN_ONLY_WHEN_STANDING); return false; } else if (isFishing()) { // You can't mount, dismount, break and drop items while fishing sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.YOU_CANNOT_DO_THAT_WHILE_FISHING2); return false; } else if (isTransformed() || isCursedWeaponEquipped()) { // no message needed, player while transformed doesn't have mount action sendPacket(ActionFailed.STATIC_PACKET); return false; } else if (getInventory().getItemByItemId(9819) != null) { sendPacket(ActionFailed.STATIC_PACKET); // FIXME: Wrong Message sendMessage("You cannot mount a steed while holding a flag."); return false; } else if (pet.isHungry()) { sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_HUNGRY_STRIDER_CANNOT_BE_MOUNTED_OR_DISMOUNTED); return false; } else if (!Util.checkIfInRange(200, this, pet, true)) { sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.YOU_ARE_TOO_FAR_AWAY_FROM_YOUR_MOUNT_TO_RIDE); return false; } else if (!pet.isDead() && !isMounted()) { mount(pet); } } else if (isRentedPet()) { stopRentPet(); } else if (isMounted()) { if ((getMountType() == MountType.WYVERN) && isInsideZone(ZoneId.NO_LANDING)) { sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.YOU_ARE_NOT_ALLOWED_TO_DISMOUNT_IN_THIS_LOCATION); return false; } else if (isHungry()) { sendPacket(ActionFailed.STATIC_PACKET); sendPacket(SystemMessageId.A_HUNGRY_STRIDER_CANNOT_BE_MOUNTED_OR_DISMOUNTED); return false; } else { dismount(); } } return true; } public boolean dismount() { boolean wasFlying = isFlying(); sendPacket(new SetupGauge(3, 0, 0)); int petId = _mountNpcId; setMount(0, 0); stopFeed(); clearPetData(); if (wasFlying) { removeSkill(CommonSkill.WYVERN_BREATH.getSkill()); } broadcastPacket(new Ride(this)); setMountObjectID(0); storePetFood(petId); // Notify self and others about speed change broadcastUserInfo(); return true; } public void setUptime(long time) { _uptime = time; } public long getUptime() { return System.currentTimeMillis() - _uptime; } /** * Return True if the L2PcInstance is invulnerable. */ @Override public boolean isInvul() { return super.isInvul() || (_teleportProtectEndTime > GameTimeController.getInstance().getGameTicks()); } /** * Return True if the L2PcInstance has a Party in progress. */ @Override public boolean isInParty() { return _party != null; } /** * Set the _party object of the L2PcInstance (without joining it). * @param party */ public void setParty(L2Party party) { _party = party; } /** * Set the _party object of the L2PcInstance AND join it. * @param party */ public void joinParty(L2Party party) { if (party != null) { // First set the party otherwise this wouldn't be considered // as in a party into the L2Character.updateEffectIcons() call. _party = party; party.addPartyMember(this); } } /** * Manage the Leave Party task of the L2PcInstance. */ public void leaveParty() { if (isInParty()) { _party.removePartyMember(this, messageType.Disconnected); _party = null; } } /** * Return the _party object of the L2PcInstance. */ @Override public L2Party getParty() { return _party; } public void setPartyDistributionType(PartyDistributionType pdt) { _partyDistributionType = pdt; } public PartyDistributionType getPartyDistributionType() { return _partyDistributionType; } /** * Return True if the L2PcInstance is a GM. */ @Override public boolean isGM() { return getAccessLevel().isGm(); } /** * Set the _accessLevel of the L2PcInstance. * @param level * @param broadcast */ public void setAccessLevel(int level, boolean broadcast) { _accessLevel = AdminData.getInstance().getAccessLevel(level); getAppearance().setNameColor(_accessLevel.getNameColor()); getAppearance().setTitleColor(_accessLevel.getTitleColor()); if (broadcast) { broadcastUserInfo(); } CharNameTable.getInstance().addName(this); if (!AdminData.getInstance().hasAccessLevel(level)) { _log.warning("Tryed to set unregistered access level " + level + " for " + toString() + ". Setting access level without privileges!"); } else if (level > 0) { _log.warning(_accessLevel.getName() + " access level set for character " + getName() + "! Just a warning to be careful ;)"); } } public void setAccountAccesslevel(int level) { LoginServerThread.getInstance().sendAccessLevel(getAccountName(), level); } /** * @return the _accessLevel of the L2PcInstance. */ @Override public L2AccessLevel getAccessLevel() { if (Config.EVERYBODY_HAS_ADMIN_RIGHTS) { return AdminData.getInstance().getMasterAccessLevel(); } else if (_accessLevel == null) { setAccessLevel(0, false); } return _accessLevel; } /** * Update Stats of the L2PcInstance client side by sending Server->Client packet UserInfo/StatusUpdate to this L2PcInstance and CharInfo/StatusUpdate to all L2PcInstance in its _KnownPlayers (broadcast). * @param broadcastType */ public void updateAndBroadcastStatus(int broadcastType) { refreshOverloaded(); refreshExpertisePenalty(); // Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers (broadcast) if (broadcastType == 1) { sendPacket(new UserInfo(this)); } if (broadcastType == 2) { broadcastUserInfo(); } } /** * Send a Server->Client StatusUpdate packet with Karma and PvP Flag to the L2PcInstance and all L2PcInstance to inform (broadcast). * @param flag */ public void setKarmaFlag(int flag) { StatusUpdate su = new StatusUpdate(this); su.addAttribute(StatusUpdate.PVP_FLAG, getKarma()); sendPacket(su); final Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values(); for (L2PcInstance player : plrs) { if ((player == null) || !isVisibleFor(player)) { continue; } final int relation = getRelation(player); Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); if ((oldrelation == null) || (oldrelation != relation)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(this, relation, isAutoAttackable(player)); if (hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation, isAutoAttackable(player)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player))); } } player.sendPacket(rc); getKnownList().getKnownRelations().put(player.getObjectId(), relation); } } } /** * Send a Server->Client StatusUpdate packet with Karma to the L2PcInstance and all L2PcInstance to inform (broadcast). */ public void broadcastKarma() { StatusUpdate su = new StatusUpdate(this); su.addAttribute(StatusUpdate.KARMA, getKarma()); sendPacket(su); final Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values(); for (L2PcInstance player : plrs) { if ((player == null) || !isVisibleFor(player)) { continue; } final int relation = getRelation(player); Integer oldrelation = getKnownList().getKnownRelations().get(player.getObjectId()); if ((oldrelation == null) || (oldrelation != relation)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(this, relation, isAutoAttackable(player)); if (hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation, isAutoAttackable(player)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation, isAutoAttackable(player))); } } player.sendPacket(rc); getKnownList().getKnownRelations().put(player.getObjectId(), relation); } } } /** * 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). * @param isOnline * @param updateInDb */ public void setOnlineStatus(boolean isOnline, boolean updateInDb) { if (_isOnline != isOnline) { _isOnline = isOnline; } // Update the characters table of the database with online status and lastAccess (called when login and logout) if (updateInDb) { updateOnlineStatus(); } } /** * Update the characters table of the database with online status and lastAccess of this L2PcInstance (called when login and logout). */ public void updateOnlineStatus() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("UPDATE characters SET online=?, lastAccess=? WHERE charId=?")) { statement.setInt(1, isOnlineInt()); statement.setLong(2, System.currentTimeMillis()); statement.setInt(3, getObjectId()); statement.execute(); } catch (Exception e) { _log.log(Level.SEVERE, "Failed updating character online status.", e); } } /** * Create a new player in the characters table of the database. * @return */ private boolean createDb() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(INSERT_CHARACTER)) { statement.setString(1, _accountName); statement.setInt(2, getObjectId()); statement.setString(3, getName()); statement.setInt(4, getLevel()); statement.setInt(5, getMaxHp()); statement.setDouble(6, getCurrentHp()); statement.setInt(7, getMaxCp()); statement.setDouble(8, getCurrentCp()); statement.setInt(9, getMaxMp()); statement.setDouble(10, getCurrentMp()); statement.setInt(11, getAppearance().getFace()); statement.setInt(12, getAppearance().getHairStyle()); statement.setInt(13, getAppearance().getHairColor()); statement.setInt(14, getAppearance().getSex() ? 1 : 0); statement.setLong(15, getExp()); statement.setLong(16, getSp()); statement.setInt(17, getKarma()); statement.setInt(18, getFame()); statement.setInt(19, getPvpKills()); statement.setInt(20, getPkKills()); statement.setInt(21, getClanId()); statement.setInt(22, getRace().ordinal()); statement.setInt(23, getClassId().getId()); statement.setLong(24, getDeleteTimer()); statement.setInt(25, hasDwarvenCraft() ? 1 : 0); statement.setString(26, getTitle()); statement.setInt(27, getAppearance().getTitleColor()); statement.setInt(28, getAccessLevel().getLevel()); statement.setInt(29, isOnlineInt()); statement.setInt(30, getClanPrivileges().getBitmask()); statement.setInt(31, getWantsPeace()); statement.setInt(32, getBaseClass()); statement.setInt(33, isNoble() ? 1 : 0); statement.setLong(34, 0); statement.setDate(35, new Date(getCreateDate().getTimeInMillis())); statement.executeUpdate(); } catch (Exception e) { _log.log(Level.SEVERE, "Could not insert char data: " + e.getMessage(), e); return false; } return true; } /** * Retrieve a L2PcInstance from the characters table of the database and add it in _allObjects of the L2world. <B><U> Actions</U> :</B> <li>Retrieve the L2PcInstance from the characters table of the database</li> <li>Add the L2PcInstance object in _allObjects</li> <li>Set the x,y,z position of * the L2PcInstance and make it invisible</li> <li>Update the overloaded status of the L2PcInstance</li> * @param objectId Identifier of the object to initialized * @return The L2PcInstance loaded from the database */ private static L2PcInstance restore(int objectId) { L2PcInstance player = null; double currentCp = 0; double currentHp = 0; double currentMp = 0; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_CHARACTER)) { // Retrieve the L2PcInstance from the characters table of the database statement.setInt(1, objectId); try (ResultSet rset = statement.executeQuery()) { if (rset.next()) { final int activeClassId = rset.getInt("classid"); final boolean female = rset.getInt("sex") != Sex.MALE.ordinal(); final L2PcTemplate template = PlayerTemplateData.getInstance().getTemplate(activeClassId); PcAppearance app = new PcAppearance(rset.getByte("face"), rset.getByte("hairColor"), rset.getByte("hairStyle"), female); player = new L2PcInstance(objectId, template, rset.getString("account_name"), app); player.setName(rset.getString("char_name")); restorePremiumSystemData(player, rset.getString("account_name")); player._lastAccess = rset.getLong("lastAccess"); player.getStat().setExp(rset.getLong("exp")); player.setExpBeforeDeath(rset.getLong("expBeforeDeath")); player.getStat().setLevel(rset.getByte("level")); player.getStat().setSp(rset.getLong("sp")); player.setWantsPeace(rset.getInt("wantspeace")); player.setHeading(rset.getInt("heading")); player.setKarma(rset.getInt("karma")); player.setFame(rset.getInt("fame")); player.setPvpKills(rset.getInt("pvpkills")); player.setPkKills(rset.getInt("pkkills")); player.setOnlineTime(rset.getLong("onlinetime")); player.setNoble(rset.getInt("nobless") == 1); final int factionId = rset.getInt("faction"); if (factionId == 1) { player.setGood(); } if (factionId == 2) { player.setEvil(); } player.setClanJoinExpiryTime(rset.getLong("clan_join_expiry_time")); if (player.getClanJoinExpiryTime() < System.currentTimeMillis()) { player.setClanJoinExpiryTime(0); } player.setClanCreateExpiryTime(rset.getLong("clan_create_expiry_time")); if (player.getClanCreateExpiryTime() < System.currentTimeMillis()) { player.setClanCreateExpiryTime(0); } int clanId = rset.getInt("clanid"); player.setPowerGrade(rset.getInt("power_grade")); player.setPledgeType(rset.getInt("subpledge")); // player.setApprentice(rset.getInt("apprentice")); // Set Hero status if it applies player.setHero(Hero.getInstance().isHero(objectId)); if (clanId > 0) { player.setClan(ClanTable.getInstance().getClan(clanId)); } if (player.getClan() != null) { if (player.getClan().getLeaderId() != player.getObjectId()) { if (player.getPowerGrade() == 0) { player.setPowerGrade(5); } player.setClanPrivileges(player.getClan().getRankPrivs(player.getPowerGrade())); } else { player.getClanPrivileges().setAll(); player.setPowerGrade(1); } player.setPledgeClass(L2ClanMember.calculatePledgeClass(player)); } else { if (player.isNoble()) { player.setPledgeClass(5); } if (player.isHero()) { player.setPledgeClass(8); } player.getClanPrivileges().clear(); } player.setDeleteTimer(rset.getLong("deletetime")); player.setTitle(rset.getString("title")); player.setAccessLevel(rset.getInt("accesslevel"), false); int titleColor = rset.getInt("title_color"); if (titleColor != PcAppearance.DEFAULT_TITLE_COLOR) { player.getAppearance().setTitleColor(titleColor); } player.setFistsWeaponItem(player.findFistsWeaponItem(activeClassId)); player.setUptime(System.currentTimeMillis()); currentHp = rset.getDouble("curHp"); currentCp = rset.getDouble("curCp"); currentMp = rset.getDouble("curMp"); player._classIndex = 0; try { player.setBaseClass(rset.getInt("base_class")); } catch (Exception e) { // TODO: Should this be logged? player.setBaseClass(activeClassId); } // Restore Subclass Data (cannot be done earlier in function) if (restoreSubClassData(player)) { if (activeClassId != player.getBaseClass()) { for (SubClass subClass : player.getSubClasses().values()) { if (subClass.getClassId() == activeClassId) { player._classIndex = subClass.getClassIndex(); } } } } if ((player.getClassIndex() == 0) && (activeClassId != player.getBaseClass())) { // Subclass in use but doesn't exist in DB - // a possible restart-while-modifysubclass cheat has been attempted. // Switching to use base class player.setClassId(player.getBaseClass()); _log.warning("Player " + player.getName() + " reverted to base class. Possibly has tried a relogin exploit while subclassing."); } else { player._activeClass = activeClassId; } player.setApprentice(rset.getInt("apprentice")); player.setSponsor(rset.getInt("sponsor")); player.setLvlJoinedAcademy(rset.getInt("lvl_joined_academy")); CursedWeaponsManager.getInstance().checkPlayer(player); // Set the x,y,z position of the L2PcInstance and make it invisible player.setXYZInvisible(rset.getInt("x"), rset.getInt("y"), rset.getInt("z")); // Set Teleport Bookmark Slot player.setBookMarkSlot(rset.getInt("BookmarkSlot")); player._vitalityPoints = rset.getInt("vitality_points"); player.setPcBangPoints(rset.getInt("pccafe_points")); // character creation Time player.getCreateDate().setTime(rset.getDate("createDate")); // Language player.setLang(rset.getString("language")); // Retrieve the name and ID of the other characters assigned to this account. try (PreparedStatement stmt = con.prepareStatement("SELECT charId, char_name FROM characters WHERE account_name=? AND charId<>?")) { stmt.setString(1, player._accountName); stmt.setInt(2, objectId); try (ResultSet chars = stmt.executeQuery()) { while (chars.next()) { player._chars.put(chars.getInt("charId"), chars.getString("char_name")); } } } } } if (player == null) { return null; } // Retrieve from the database all items of this L2PcInstance and add them to _inventory player.getInventory().restore(); player.getFreight().restore(); if (!Config.WAREHOUSE_CACHE) { player.getWarehouse(); } // Retrieve from the database all secondary data of this L2PcInstance // Note that Clan, Noblesse and Hero skills are given separately and not here. // Retrieve from the database all skills of this L2PcInstance and add them to _skills player.restoreCharData(); // Reward auto-get skills and all available skills if auto-learn skills is true. player.rewardSkills(); player.restoreItemReuse(); // Restore current Cp, HP and MP values player.setCurrentCp(currentCp); player.setCurrentHp(currentHp); player.setCurrentMp(currentMp); player.setOriginalCpHpMp(currentCp, currentHp, currentMp); if (currentHp < 0.5) { player.setIsDead(true); player.stopHpMpRegeneration(); } // Restore pet if exists in the world player.setPet(L2World.getInstance().getPet(player.getObjectId())); final L2Summon pet = player.getPet(); if (pet != null) { pet.setOwner(player); } if (player.hasServitors()) { for (L2Summon summon : player.getServitors().values()) { summon.setOwner(player); } } // Update the overloaded status of the L2PcInstance player.refreshOverloaded(); // Update the expertise status of the L2PcInstance player.refreshExpertisePenalty(); player.restoreFriendList(); if (Config.STORE_UI_SETTINGS) { player.restoreUISettings(); } if (player.isGM()) { final long masks = player.getVariables().getLong(COND_OVERRIDE_KEY, PcCondOverride.getAllExceptionsMask()); player.setOverrideCond(masks); } player.loadRecommendations(); player.startRecoGiveTask(); } catch (Exception e) { _log.log(Level.SEVERE, "Failed loading character.", e); } return player; } /** * @return */ public Forum getMail() { if (_forumMail == null) { setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName())); if (_forumMail == null) { ForumsBBSManager.getInstance().createNewForum(getName(), ForumsBBSManager.getInstance().getForumByName("MailRoot"), Forum.MAIL, Forum.OWNERONLY, getObjectId()); setMail(ForumsBBSManager.getInstance().getForumByName("MailRoot").getChildByName(getName())); } } return _forumMail; } /** * @param forum */ public void setMail(Forum forum) { _forumMail = forum; } /** * @return */ public Forum getMemo() { if (_forumMemo == null) { setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName)); if (_forumMemo == null) { ForumsBBSManager.getInstance().createNewForum(_accountName, ForumsBBSManager.getInstance().getForumByName("MemoRoot"), Forum.MEMO, Forum.OWNERONLY, getObjectId()); setMemo(ForumsBBSManager.getInstance().getForumByName("MemoRoot").getChildByName(_accountName)); } } return _forumMemo; } /** * @param forum */ public void setMemo(Forum forum) { _forumMemo = forum; } /** * Restores sub-class data for the L2PcInstance, used to check the current class index for the character. * @param player * @return */ private static boolean restoreSubClassData(L2PcInstance player) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_SUBCLASSES)) { statement.setInt(1, player.getObjectId()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { SubClass subClass = new SubClass(); subClass.setClassId(rset.getInt("class_id")); subClass.setIsDualClass(rset.getBoolean("dual_class")); subClass.setLevel(rset.getByte("level")); subClass.setExp(rset.getLong("exp")); subClass.setSp(rset.getLong("sp")); subClass.setVitalityPoints(rset.getInt("vitality_points")); subClass.setClassIndex(rset.getInt("class_index")); // Enforce the correct indexing of _subClasses against their class indexes. player.getSubClasses().put(subClass.getClassIndex(), subClass); } } } catch (Exception e) { _log.log(Level.WARNING, "Could not restore classes for " + player.getName() + ": " + e.getMessage(), e); } return true; } /** * Restores: * <ul> * <li>Skills</li> * <li>Macros</li> * <li>Short-cuts</li> * <li>Henna</li> * <li>Teleport Bookmark</li> * <li>Recipe Book</li> * <li>Recipe Shop List (If configuration enabled)</li> * <li>Premium Item List</li> * <li>Pet Inventory Items</li> * </ul> */ private void restoreCharData() { // Retrieve from the database all skills of this L2PcInstance and add them to _skills. restoreSkills(); // Retrieve from the database all macroses of this L2PcInstance and add them to _macros. _macros.restoreMe(); // Retrieve from the database all shortCuts of this L2PcInstance and add them to _shortCuts. _shortCuts.restoreMe(); // Retrieve from the database all henna of this L2PcInstance and add them to _henna. restoreHenna(); // Retrieve from the database all teleport bookmark of this L2PcInstance and add them to _tpbookmark. restoreTeleportBookmark(); // Retrieve from the database the recipe book of this L2PcInstance. restoreRecipeBook(true); // Restore Recipe Shop list. if (Config.STORE_RECIPE_SHOPLIST) { restoreRecipeShopList(); } // Load Premium Item List. loadPremiumItemList(); // Restore items in pet inventory. restorePetInventoryItems(); } /** * Restore recipe book data for this L2PcInstance. * @param loadCommon */ private void restoreRecipeBook(boolean loadCommon) { final String sql = loadCommon ? "SELECT id, type, classIndex FROM character_recipebook WHERE charId=?" : "SELECT id FROM character_recipebook WHERE charId=? AND classIndex=? AND type = 1"; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(sql)) { statement.setInt(1, getObjectId()); if (!loadCommon) { statement.setInt(2, _classIndex); } try (ResultSet rset = statement.executeQuery()) { _dwarvenRecipeBook.clear(); L2RecipeList recipe; RecipeData rd = RecipeData.getInstance(); while (rset.next()) { recipe = rd.getRecipeList(rset.getInt("id")); if (loadCommon) { if (rset.getInt(2) == 1) { if (rset.getInt(3) == _classIndex) { registerDwarvenRecipeList(recipe, false); } } else { registerCommonRecipeList(recipe, false); } } else { registerDwarvenRecipeList(recipe, false); } } } } catch (Exception e) { _log.log(Level.SEVERE, "Could not restore recipe book data:" + e.getMessage(), e); } } public Map<Integer, L2PremiumItem> getPremiumItemList() { return _premiumItems; } private void loadPremiumItemList() { final String sql = "SELECT itemNum, itemId, itemCount, itemSender FROM character_premium_items WHERE charId=?"; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(sql)) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { int itemNum = rset.getInt("itemNum"); int itemId = rset.getInt("itemId"); long itemCount = rset.getLong("itemCount"); String itemSender = rset.getString("itemSender"); _premiumItems.put(itemNum, new L2PremiumItem(itemId, itemCount, itemSender)); } } } catch (Exception e) { _log.log(Level.SEVERE, "Could not restore premium items: " + e.getMessage(), e); } } public void updatePremiumItem(int itemNum, long newcount) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("UPDATE character_premium_items SET itemCount=? WHERE charId=? AND itemNum=? ")) { statement.setLong(1, newcount); statement.setInt(2, getObjectId()); statement.setInt(3, itemNum); statement.execute(); } catch (Exception e) { _log.log(Level.SEVERE, "Could not update premium items: " + e.getMessage(), e); } } public void deletePremiumItem(int itemNum) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("DELETE FROM character_premium_items WHERE charId=? AND itemNum=? ")) { statement.setInt(1, getObjectId()); statement.setInt(2, itemNum); statement.execute(); } catch (Exception e) { _log.log(Level.SEVERE, "Could not delete premium item: " + e); } } /** * Update L2PcInstance stats in the characters table of the database. * @param storeActiveEffects */ public synchronized void store(boolean storeActiveEffects) { storeCharBase(); storeCharSub(); storeEffect(storeActiveEffects); storeItemReuseDelay(); if (Config.STORE_RECIPE_SHOPLIST) { storeRecipeShopList(); } if (Config.STORE_UI_SETTINGS) { storeUISettings(); } getBlockList().updateBlockMemos(); updateMemos(); final PlayerVariables vars = getScript(PlayerVariables.class); if (vars != null) { vars.storeMe(); } final AccountVariables aVars = getScript(AccountVariables.class); if (aVars != null) { aVars.storeMe(); } } @Override public void storeMe() { store(true); } private void storeCharBase() { // Get the exp, level, and sp of base class to store in base table long exp = getStat().getBaseExp(); int level = getStat().getBaseLevel(); long sp = getStat().getBaseSp(); try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(UPDATE_CHARACTER)) { statement.setInt(1, level); statement.setInt(2, getMaxHp()); statement.setDouble(3, getCurrentHp()); statement.setInt(4, getMaxCp()); statement.setDouble(5, getCurrentCp()); statement.setInt(6, getMaxMp()); statement.setDouble(7, getCurrentMp()); statement.setInt(8, getAppearance().getFace()); statement.setInt(9, getAppearance().getHairStyle()); statement.setInt(10, getAppearance().getHairColor()); statement.setInt(11, getAppearance().getSex() ? 1 : 0); statement.setInt(12, getHeading()); statement.setInt(13, _observerMode ? _lastLoc.getX() : getX()); statement.setInt(14, _observerMode ? _lastLoc.getY() : getY()); statement.setInt(15, _observerMode ? _lastLoc.getZ() : getZ()); statement.setLong(16, exp); statement.setLong(17, getExpBeforeDeath()); statement.setLong(18, sp); statement.setInt(19, getKarma()); statement.setInt(20, getFame()); statement.setInt(21, getPvpKills()); statement.setInt(22, getPkKills()); statement.setInt(23, getClanId()); statement.setInt(24, getRace().ordinal()); statement.setInt(25, getClassId().getId()); statement.setLong(26, getDeleteTimer()); statement.setString(27, getTitle()); statement.setInt(28, getAppearance().getTitleColor()); statement.setInt(29, getAccessLevel().getLevel()); statement.setInt(30, isOnlineInt()); statement.setInt(31, getClanPrivileges().getBitmask()); statement.setInt(32, getWantsPeace()); statement.setInt(33, getBaseClass()); long totalOnlineTime = _onlineTime; if (_onlineBeginTime > 0) { totalOnlineTime += (System.currentTimeMillis() - _onlineBeginTime) / 1000; } statement.setLong(34, totalOnlineTime); statement.setInt(35, isNoble() ? 1 : 0); statement.setInt(36, getPowerGrade()); statement.setInt(37, getPledgeType()); statement.setInt(38, getLvlJoinedAcademy()); statement.setLong(39, getApprentice()); statement.setLong(40, getSponsor()); statement.setLong(41, getClanJoinExpiryTime()); statement.setLong(42, getClanCreateExpiryTime()); statement.setString(43, getName()); statement.setLong(44, 0); // unset statement.setInt(45, getBookMarkSlot()); statement.setInt(46, _vitalityPoints); // unset statement.setInt(47, getPcBangPoints()); statement.setString(48, getLang()); int factionId = 0; if (isGood()) { factionId = 1; } if (isEvil()) { factionId = 2; } statement.setInt(49, factionId); statement.setInt(50, getObjectId()); statement.execute(); } catch (Exception e) { _log.log(Level.WARNING, "Could not store char base data: " + this + " - " + e.getMessage(), e); } } private void storeCharSub() { if (getTotalSubClasses() <= 0) { return; } try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(UPDATE_CHAR_SUBCLASS)) { for (SubClass subClass : getSubClasses().values()) { statement.setLong(1, subClass.getExp()); statement.setLong(2, subClass.getSp()); statement.setInt(3, subClass.getVitalityPoints()); statement.setInt(4, subClass.getLevel()); statement.setInt(5, subClass.getClassId()); statement.setBoolean(6, subClass.isDualClass()); statement.setInt(7, getObjectId()); statement.setInt(8, subClass.getClassIndex()); statement.execute(); statement.clearParameters(); } } catch (Exception e) { _log.log(Level.WARNING, "Could not store sub class data for " + getName() + ": " + e.getMessage(), e); } } @Override public void storeEffect(boolean storeEffects) { if (!Config.STORE_SKILL_COOLTIME) { return; } try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement delete = con.prepareStatement(DELETE_SKILL_SAVE); PreparedStatement statement = con.prepareStatement(ADD_SKILL_SAVE);) { // Delete all current stored effects for char to avoid dupe delete.setInt(1, getObjectId()); delete.setInt(2, getClassIndex()); delete.execute(); int buff_index = 0; final List<Integer> storedSkills = new ArrayList<>(); // Store all effect data along with calulated remaining // reuse delays for matching skills. 'restore_type'= 0. if (storeEffects) { for (BuffInfo info : getEffectList().getEffects()) { if (info == null) { continue; } final Skill skill = info.getSkill(); // Do not save heals. if (skill.getAbnormalType() == AbnormalType.LIFE_FORCE_OTHERS) { continue; } if (skill.isToggle() || skill.isMentoring()) { continue; } // Dances and songs are not kept in retail. if (skill.isDance() && !Config.ALT_STORE_DANCES) { continue; } if (storedSkills.contains(skill.getReuseHashCode())) { continue; } storedSkills.add(skill.getReuseHashCode()); statement.setInt(1, getObjectId()); statement.setInt(2, skill.getId()); statement.setInt(3, skill.getLevel()); statement.setInt(4, info.getTime()); final TimeStamp t = getSkillReuseTimeStamp(skill.getReuseHashCode()); statement.setLong(5, (t != null) && t.hasNotPassed() ? t.getReuse() : 0); statement.setDouble(6, (t != null) && t.hasNotPassed() ? t.getStamp() : 0); statement.setInt(7, 0); // Store type 0, active buffs/debuffs. statement.setInt(8, getClassIndex()); statement.setInt(9, ++buff_index); statement.execute(); } } // Skills under reuse. final Map<Integer, TimeStamp> reuseTimeStamps = getSkillReuseTimeStamps(); if (reuseTimeStamps != null) { for (Entry<Integer, TimeStamp> ts : reuseTimeStamps.entrySet()) { final int hash = ts.getKey(); if (storedSkills.contains(hash)) { continue; } final TimeStamp t = ts.getValue(); if ((t != null) && t.hasNotPassed()) { storedSkills.add(hash); statement.setInt(1, getObjectId()); statement.setInt(2, t.getSkillId()); statement.setInt(3, t.getSkillLvl()); statement.setInt(4, -1); statement.setLong(5, t.getReuse()); statement.setDouble(6, t.getStamp()); statement.setInt(7, 1); // Restore type 1, skill reuse. statement.setInt(8, getClassIndex()); statement.setInt(9, ++buff_index); statement.execute(); } } } } catch (Exception e) { _log.log(Level.WARNING, "Could not store char effect data: ", e); } } private void storeItemReuseDelay() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement ps1 = con.prepareStatement(DELETE_ITEM_REUSE_SAVE); PreparedStatement ps2 = con.prepareStatement(ADD_ITEM_REUSE_SAVE)) { ps1.setInt(1, getObjectId()); ps1.execute(); final Map<Integer, TimeStamp> itemReuseTimeStamps = getItemReuseTimeStamps(); if (itemReuseTimeStamps != null) { for (TimeStamp ts : itemReuseTimeStamps.values()) { if ((ts != null) && ts.hasNotPassed()) { ps2.setInt(1, getObjectId()); ps2.setInt(2, ts.getItemId()); ps2.setInt(3, ts.getItemObjectId()); ps2.setLong(4, ts.getReuse()); ps2.setDouble(5, ts.getStamp()); ps2.execute(); } } } } catch (Exception e) { _log.log(Level.WARNING, "Could not store char item reuse data: ", e); } } /** * @return True if the L2PcInstance is on line. */ public boolean isOnline() { return _isOnline; } public int isOnlineInt() { if (_isOnline && (getClient() != null)) { return getClient().isDetached() ? 2 : 1; } return 0; } @Override public Skill addSkill(Skill newSkill) { addCustomSkill(newSkill); return super.addSkill(newSkill); } /** * Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance and save update in the character_skills table of the database. <B><U> Concept</U> :</B> All skills own by a L2PcInstance are identified in <B>_skills</B> <B><U> Actions</U> :</B> <li>Replace * oldSkill by newSkill or Add the newSkill</li> <li>If an old skill has been replaced, remove all its Func objects of L2Character calculator set</li> <li>Add Func objects of newSkill to the calculator set of the L2Character</li> * @param newSkill The L2Skill to add to the L2Character * @param store * @return The L2Skill replaced or null if just added a new L2Skill */ public Skill addSkill(Skill newSkill, boolean store) { // Add a skill to the L2PcInstance _skills and its Func objects to the calculator set of the L2PcInstance final Skill oldSkill = addSkill(newSkill); // Add or update a L2PcInstance skill in the character_skills table of the database if (store) { storeSkill(newSkill, oldSkill, -1); } return oldSkill; } @Override public Skill removeSkill(Skill skill, boolean store) { removeCustomSkill(skill); return store ? removeSkill(skill) : super.removeSkill(skill, true); } public Skill removeSkill(Skill skill, boolean store, boolean cancelEffect) { removeCustomSkill(skill); return store ? removeSkill(skill) : super.removeSkill(skill, cancelEffect); } /** * Remove a skill from the L2Character and its Func objects from calculator set of the L2Character and save update in the character_skills table of the database. <B><U> Concept</U> :</B> All skills own by a L2Character are identified in <B>_skills</B> <B><U> Actions</U> :</B> <li>Remove the * skill from the L2Character _skills</li> <li>Remove all its Func objects from the L2Character calculator set</li> <B><U> Overridden in </U> :</B> <li>L2PcInstance : Save update in the character_skills table of the database</li> * @param skill The L2Skill to remove from the L2Character * @return The L2Skill removed */ public Skill removeSkill(Skill skill) { removeCustomSkill(skill); // Remove a skill from the L2Character and its Func objects from calculator set of the L2Character final Skill oldSkill = super.removeSkill(skill, true); if (oldSkill != null) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(DELETE_SKILL_FROM_CHAR)) { // Remove or update a L2PcInstance skill from the character_skills table of the database statement.setInt(1, oldSkill.getId()); statement.setInt(2, getObjectId()); statement.setInt(3, getClassIndex()); statement.execute(); } catch (Exception e) { _log.log(Level.WARNING, "Error could not delete skill: " + e.getMessage(), e); } } if ((getTransformationId() > 0) || isCursedWeaponEquipped()) { return oldSkill; } if (skill != null) { for (Shortcut sc : getAllShortCuts()) { if ((sc != null) && (sc.getId() == skill.getId()) && (sc.getType() == ShortcutType.SKILL) && !((skill.getId() >= 3080) && (skill.getId() <= 3259))) { deleteShortCut(sc.getSlot(), sc.getPage()); } } } return oldSkill; } /** * Add or update a L2PcInstance skill in the character_skills table of the database.<br> * If newClassIndex > -1, the skill will be stored with that class index, not the current one. * @param newSkill * @param oldSkill * @param newClassIndex */ private void storeSkill(Skill newSkill, Skill oldSkill, int newClassIndex) { final int classIndex = (newClassIndex > -1) ? newClassIndex : _classIndex; try (Connection con = L2DatabaseFactory.getInstance().getConnection()) { if ((oldSkill != null) && (newSkill != null)) { try (PreparedStatement ps = con.prepareStatement(UPDATE_CHARACTER_SKILL_LEVEL)) { ps.setInt(1, newSkill.getLevel()); ps.setInt(2, oldSkill.getId()); ps.setInt(3, getObjectId()); ps.setInt(4, classIndex); ps.execute(); } } else if (newSkill != null) { try (PreparedStatement ps = con.prepareStatement(ADD_NEW_SKILLS)) { ps.setInt(1, getObjectId()); ps.setInt(2, newSkill.getId()); ps.setInt(3, newSkill.getLevel()); ps.setInt(4, classIndex); ps.execute(); } } else { _log.warning("Could not store new skill, it's null!"); } } catch (Exception e) { _log.log(Level.WARNING, "Error could not store char skills: " + e.getMessage(), e); } } /** * Adds or updates player's skills in the database. * @param newSkills the list of skills to store * @param newClassIndex if newClassIndex > -1, the skills will be stored for that class index, not the current one */ private void storeSkills(List<Skill> newSkills, int newClassIndex) { if (newSkills.isEmpty()) { return; } final int classIndex = (newClassIndex > -1) ? newClassIndex : _classIndex; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement ps = con.prepareStatement(ADD_NEW_SKILLS)) { con.setAutoCommit(false); for (final Skill addSkill : newSkills) { ps.setInt(1, getObjectId()); ps.setInt(2, addSkill.getId()); ps.setInt(3, addSkill.getLevel()); ps.setInt(4, classIndex); ps.addBatch(); } ps.executeBatch(); con.commit(); } catch (SQLException e) { _log.log(Level.WARNING, "Error could not store char skills: " + e.getMessage(), e); } } /** * Retrieve from the database all skills of this L2PcInstance and add them to _skills. */ private void restoreSkills() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_SKILLS_FOR_CHAR)) { // Retrieve all skills of this L2PcInstance from the database statement.setInt(1, getObjectId()); statement.setInt(2, getClassIndex()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { final int id = rset.getInt("skill_id"); final int level = rset.getInt("skill_level"); // Create a L2Skill object for each record final Skill skill = SkillData.getInstance().getSkill(id, level); if (skill == null) { _log.warning("Skipped null skill Id: " + id + " Level: " + level + " while restoring player skills for playerObjId: " + getObjectId()); continue; } // Add the L2Skill object to the L2Character _skills and its Func objects to the calculator set of the L2Character addSkill(skill); if (Config.SKILL_CHECK_ENABLE && (!canOverrideCond(PcCondOverride.SKILL_CONDITIONS) || Config.SKILL_CHECK_GM)) { if (!SkillTreesData.getInstance().isSkillAllowed(this, skill)) { Util.handleIllegalPlayerAction(this, "Player " + getName() + " has invalid skill " + skill.getName() + " (" + skill.getId() + "/" + skill.getLevel() + "), class:" + ClassListData.getInstance().getClass(getClassId()).getClassName(), IllegalActionPunishmentType.BROADCAST); if (Config.SKILL_CHECK_REMOVE) { removeSkill(skill); } } } } } } catch (Exception e) { _log.log(Level.WARNING, "Could not restore character " + this + " skills: " + e.getMessage(), e); } } /** * Retrieve from the database all skill effects of this L2PcInstance and add them to the player. */ @Override public void restoreEffects() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_SKILL_SAVE)) { statement.setInt(1, getObjectId()); statement.setInt(2, getClassIndex()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { int remainingTime = rset.getInt("remaining_time"); long reuseDelay = rset.getLong("reuse_delay"); long systime = rset.getLong("systime"); int restoreType = rset.getInt("restore_type"); final Skill skill = SkillData.getInstance().getSkill(rset.getInt("skill_id"), rset.getInt("skill_level")); if (skill == null) { continue; } final long time = systime - System.currentTimeMillis(); if (time > 10) { disableSkill(skill, time); addTimeStamp(skill, reuseDelay, systime); } // Restore Type 1 The remaning skills lost effect upon logout but were still under a high reuse delay. if (restoreType > 0) { continue; } // Restore Type 0 These skill were still in effect on the character upon logout. // Some of which were self casted and might still have had a long reuse delay which also is restored. skill.applyEffects(this, this, false, remainingTime); } } // Remove previously restored skills try (PreparedStatement delete = con.prepareStatement(DELETE_SKILL_SAVE)) { delete.setInt(1, getObjectId()); delete.setInt(2, getClassIndex()); delete.executeUpdate(); } } catch (Exception e) { _log.log(Level.WARNING, "Could not restore " + this + " active effect data: " + e.getMessage(), e); } } /** * Retrieve from the database all Item Reuse Time of this L2PcInstance and add them to the player. */ private void restoreItemReuse() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_ITEM_REUSE_SAVE); PreparedStatement delete = con.prepareStatement(DELETE_ITEM_REUSE_SAVE);) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { int itemId; @SuppressWarnings("unused") int itemObjId; long reuseDelay; long systime; boolean isInInventory; long remainingTime; while (rset.next()) { itemId = rset.getInt("itemId"); itemObjId = rset.getInt("itemObjId"); reuseDelay = rset.getLong("reuseDelay"); systime = rset.getLong("systime"); isInInventory = true; // Using item Id L2ItemInstance item = getInventory().getItemByItemId(itemId); if (item == null) { item = getWarehouse().getItemByItemId(itemId); isInInventory = false; } if ((item != null) && (item.getId() == itemId) && (item.getReuseDelay() > 0)) { remainingTime = systime - System.currentTimeMillis(); // Hardcoded to 10 seconds. if (remainingTime > 10) { addTimeStampItem(item, reuseDelay, systime); if (isInInventory && item.isEtcItem()) { final int group = item.getSharedReuseGroup(); if (group > 0) { sendPacket(new ExUseSharedGroupItem(itemId, group, (int) remainingTime, (int) reuseDelay)); } } } } } } // Delete item reuse. delete.setInt(1, getObjectId()); delete.executeUpdate(); } catch (Exception e) { _log.log(Level.WARNING, "Could not restore " + this + " Item Reuse data: " + e.getMessage(), e); } } /** * Retrieve from the database all Henna of this L2PcInstance, add them to _henna and calculate stats of the L2PcInstance. */ private void restoreHenna() { for (int i = 0; i < 3; i++) { _henna[i] = null; } try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_HENNAS)) { statement.setInt(1, getObjectId()); statement.setInt(2, getClassIndex()); try (ResultSet rset = statement.executeQuery()) { int slot; int symbolId; while (rset.next()) { slot = rset.getInt("slot"); if ((slot < 1) || (slot > 3)) { continue; } symbolId = rset.getInt("symbol_id"); if (symbolId == 0) { continue; } _henna[slot - 1] = HennaData.getInstance().getHenna(symbolId); } } } catch (Exception e) { _log.log(Level.SEVERE, "Failed restoing character " + this + " hennas.", e); } // Calculate henna modifiers of this player. recalcHennaStats(); } /** * @return the number of Henna empty slot of the L2PcInstance. */ public int getHennaEmptySlots() { int totalSlots = 0; if (getClassId().level() == 1) { totalSlots = 2; } else { totalSlots = 3; } for (int i = 0; i < 3; i++) { if (_henna[i] != null) { totalSlots--; } } if (totalSlots <= 0) { return 0; } return totalSlots; } /** * Remove a Henna of the L2PcInstance, save update in the character_hennas table of the database and send Server->Client HennaInfo/UserInfo packet to this L2PcInstance. * @param slot * @return */ public boolean removeHenna(int slot) { if ((slot < 1) || (slot > 3)) { return false; } slot--; L2Henna henna = _henna[slot]; if (henna == null) { return false; } _henna[slot] = null; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(DELETE_CHAR_HENNA)) { statement.setInt(1, getObjectId()); statement.setInt(2, slot + 1); statement.setInt(3, getClassIndex()); statement.execute(); } catch (Exception e) { _log.log(Level.SEVERE, "Failed remocing character henna.", e); } // Calculate Henna modifiers of this L2PcInstance recalcHennaStats(); // Send Server->Client HennaInfo packet to this L2PcInstance sendPacket(new HennaInfo(this)); // Send Server->Client UserInfo packet to this L2PcInstance UserInfo ui = new UserInfo(this, false); ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED); sendPacket(ui); // Add the recovered dyes to the player's inventory and notify them. getInventory().addItem("Henna", henna.getDyeItemId(), henna.getCancelCount(), this, null); reduceAdena("Henna", henna.getCancelFee(), this, false); final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HAVE_EARNED_S2_S1_S); sm.addItemName(henna.getDyeItemId()); sm.addLong(henna.getCancelCount()); sendPacket(sm); sendPacket(SystemMessageId.THE_SYMBOL_HAS_BEEN_DELETED); // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this); return true; } /** * 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. * @param henna the henna to add to the player. * @return {@code true} if the henna is added to the player, {@code false} otherwise. */ public boolean addHenna(L2Henna henna) { for (int i = 0; i < 3; i++) { if (_henna[i] == null) { _henna[i] = henna; // Calculate Henna modifiers of this L2PcInstance recalcHennaStats(); try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(ADD_CHAR_HENNA)) { statement.setInt(1, getObjectId()); statement.setInt(2, henna.getDyeId()); statement.setInt(3, i + 1); statement.setInt(4, getClassIndex()); statement.execute(); } catch (Exception e) { _log.log(Level.SEVERE, "Failed saving character henna.", e); } // Send Server->Client HennaInfo packet to this L2PcInstance sendPacket(new HennaInfo(this)); // Send Server->Client UserInfo packet to this L2PcInstance UserInfo ui = new UserInfo(this, false); ui.addComponentType(UserInfoType.BASE_STATS, UserInfoType.MAX_HPCPMP, UserInfoType.STATS, UserInfoType.SPEED); sendPacket(ui); // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerHennaRemove(this, henna), this); return true; } } return false; } /** * Calculate Henna modifiers of this L2PcInstance. */ private void recalcHennaStats() { _hennaINT = 0; _hennaSTR = 0; _hennaCON = 0; _hennaMEN = 0; _hennaWIT = 0; _hennaDEX = 0; _hennaLUC = 0; _hennaCHA = 0; for (L2Henna h : _henna) { if (h == null) { continue; } _hennaINT += ((_hennaINT + h.getStatINT()) > 5) ? 5 - _hennaINT : h.getStatINT(); _hennaSTR += ((_hennaSTR + h.getStatSTR()) > 5) ? 5 - _hennaSTR : h.getStatSTR(); _hennaMEN += ((_hennaMEN + h.getStatMEN()) > 5) ? 5 - _hennaMEN : h.getStatMEN(); _hennaCON += ((_hennaCON + h.getStatCON()) > 5) ? 5 - _hennaCON : h.getStatCON(); _hennaWIT += ((_hennaWIT + h.getStatWIT()) > 5) ? 5 - _hennaWIT : h.getStatWIT(); _hennaDEX += ((_hennaDEX + h.getStatDEX()) > 5) ? 5 - _hennaDEX : h.getStatDEX(); _hennaLUC += ((_hennaLUC + h.getStatLUC()) > 5) ? 5 - _hennaLUC : h.getStatLUC(); _hennaCHA += ((_hennaCHA + h.getStatCHA()) > 5) ? 5 - _hennaCHA : h.getStatCHA(); } } /** * @param slot the character inventory henna slot. * @return the Henna of this L2PcInstance corresponding to the selected slot. */ public L2Henna getHenna(int slot) { if ((slot < 1) || (slot > 3)) { return null; } return _henna[slot - 1]; } /** * @return {@code true} if player has at least 1 henna symbol, {@code false} otherwise. */ public boolean hasHennas() { for (L2Henna henna : _henna) { if (henna != null) { return true; } } return false; } /** * @return the henna holder for this player. */ public L2Henna[] getHennaList() { return _henna; } /** * @return the INT Henna modifier of this L2PcInstance. */ public int getHennaStatINT() { return _hennaINT; } /** * @return the STR Henna modifier of this L2PcInstance. */ public int getHennaStatSTR() { return _hennaSTR; } /** * @return the CON Henna modifier of this L2PcInstance. */ public int getHennaStatCON() { return _hennaCON; } /** * @return the MEN Henna modifier of this L2PcInstance. */ public int getHennaStatMEN() { return _hennaMEN; } /** * @return the WIT Henna modifier of this L2PcInstance. */ public int getHennaStatWIT() { return _hennaWIT; } /** * @return the DEX Henna modifier of this L2PcInstance. */ public int getHennaStatDEX() { return _hennaDEX; } /** * @return the LUC Henna modifier of this L2PcInstance. */ public int getHennaStatLUC() { return _hennaLUC; } /** * @return the CHA Henna modifier of this L2PcInstance. */ public int getHennaStatCHA() { return _hennaCHA; } /** * Return True if the L2PcInstance is autoAttackable.<br> * <B><U>Actions</U>:</B> * <ul> * <li>Check if the attacker isn't the L2PcInstance Pet</li> * <li>Check if the attacker is L2MonsterInstance</li> * <li>If the attacker is a L2PcInstance, check if it is not in the same party</li> * <li>Check if the L2PcInstance has Karma</li> * <li>If the attacker is a L2PcInstance, check if it is not in the same siege clan (Attacker, Defender)</li> * </ul> */ @Override public boolean isAutoAttackable(L2Character attacker) { if (attacker == null) { return false; } // Check if the attacker isn't the L2PcInstance Pet if ((attacker == this) || (attacker == getPet()) || attacker.hasServitor(attacker.getObjectId())) { return false; } // Friendly mobs doesnt attack players if (attacker instanceof L2FriendlyMobInstance) { return false; } // Check if the attacker is a L2MonsterInstance if (attacker.isMonster()) { return true; } // is AutoAttackable if both players are in the same duel and the duel is still going on if (attacker.isPlayer() && (getDuelState() == Duel.DUELSTATE_DUELLING) && (getDuelId() == ((L2PcInstance) attacker).getDuelId())) { return true; } // Check if the attacker is not in the same party. NOTE: Party checks goes before oly checks in order to prevent patry member autoattack at oly. if (isInParty() && getParty().getMembers().contains(attacker)) { return false; } // Check if the attacker is in olympia and olympia start if (attacker.isPlayer() && attacker.getActingPlayer().isInOlympiadMode()) { if (isInOlympiadMode() && isOlympiadStart() && (((L2PcInstance) attacker).getOlympiadGameId() == getOlympiadGameId())) { return true; } return false; } // Check if the attacker is in TvT and TvT is started if (isOnEvent()) { return true; } // Check if the attacker is a L2Playable if (attacker.isPlayable()) { if (isInsideZone(ZoneId.PEACE)) { return false; } // Get L2PcInstance L2PcInstance attackerPlayer = attacker.getActingPlayer(); if (getClan() != null) { Siege siege = SiegeManager.getInstance().getSiege(getX(), getY(), getZ()); if (siege != null) { // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Defender clan if (siege.checkIsDefender(attackerPlayer.getClan()) && siege.checkIsDefender(getClan())) { return false; } // Check if a siege is in progress and if attacker and the L2PcInstance aren't in the Attacker clan if (siege.checkIsAttacker(attackerPlayer.getClan()) && siege.checkIsAttacker(getClan())) { return false; } } // Check if clan is at war if ((getClan() != null) && (attackerPlayer.getClan() != null) && getClan().isAtWarWith(attackerPlayer.getClanId()) && attackerPlayer.getClan().isAtWarWith(getClanId()) && (getWantsPeace() == 0) && (attackerPlayer.getWantsPeace() == 0) && !isAcademyMember()) { return true; } } // Check if the L2PcInstance is in an arena, but NOT siege zone. NOTE: This check comes before clan/ally checks, but after party checks. // This is done because in arenas, clan/ally members can autoattack if they arent in party. if ((isInsideZone(ZoneId.PVP) && attackerPlayer.isInsideZone(ZoneId.PVP)) && !(isInsideZone(ZoneId.SIEGE) && attackerPlayer.isInsideZone(ZoneId.SIEGE))) { return true; } // Check if the attacker is not in the same clan if ((getClan() != null) && getClan().isMember(attacker.getObjectId())) { return false; } // Check if the attacker is not in the same ally if (attacker.isPlayer() && (getAllyId() != 0) && (getAllyId() == attackerPlayer.getAllyId())) { return false; } // Now check again if the L2PcInstance is in pvp zone, but this time at siege PvP zone, applying clan/ally checks if ((isInsideZone(ZoneId.PVP) && attackerPlayer.isInsideZone(ZoneId.PVP)) && (isInsideZone(ZoneId.SIEGE) && attackerPlayer.isInsideZone(ZoneId.SIEGE))) { return true; } if (Config.FACTION_SYSTEM_ENABLED && ((isGood() && attackerPlayer.isEvil()) || (isEvil() && attackerPlayer.isGood()))) { return true; } } else if (attacker instanceof L2DefenderInstance) { if (getClan() != null) { Siege siege = SiegeManager.getInstance().getSiege(this); return ((siege != null) && siege.checkIsAttacker(getClan())); } } // Check if the L2PcInstance has Karma if ((getKarma() > 0) || (getPvpFlag() > 0)) { return true; } return false; } /** * Check if the active L2Skill can be casted.<br> * <B><U>Actions</U>:</B> * <ul> * <li>Check if the skill isn't toggle and is offensive</li> * <li>Check if the target is in the skill cast range</li> * <li>Check if the skill is Spoil type and if the target isn't already spoiled</li> * <li>Check if the caster owns enought consummed Item, enough HP and MP to cast the skill</li> * <li>Check if the caster isn't sitting</li> * <li>Check if all skills are enabled and this skill is enabled</li> * <li>Check if the caster own the weapon needed</li> * <li>Check if the skill is active</li> * <li>Check if all casting conditions are completed</li> * <li>Notify the AI with AI_INTENTION_CAST and target</li> * </ul> * @param skill The L2Skill to use * @param forceUse used to force ATTACK on players * @param dontMove used to prevent movement, if not in range */ @Override public boolean useMagic(Skill skill, boolean forceUse, boolean dontMove) { // Check if the skill is active if (skill.isPassive()) { // just ignore the passive skill request. why does the client send it anyway ?? // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } // ************************************* Check Casting in Progress ******************************************* // If a skill is currently being used, queue this one if this is not the same if (isCastingNow()) { SkillUseHolder currentSkill = getCurrentSkill(); // Check if new skill different from current skill in progress if ((currentSkill != null) && (skill.getId() == currentSkill.getSkillId())) { sendPacket(ActionFailed.STATIC_PACKET); return false; } else if (isSkillDisabled(skill)) { sendPacket(ActionFailed.STATIC_PACKET); return false; } // Create a new SkillDat object and queue it in the player _queuedSkill setQueuedSkill(skill, forceUse, dontMove); sendPacket(ActionFailed.STATIC_PACKET); return false; } setIsCastingNow(true); // Create a new SkillDat object and set the player _currentSkill // This is used mainly to save & queue the button presses, since L2Character has // _lastSkillCast which could otherwise replace it setCurrentSkill(skill, forceUse, dontMove); if (getQueuedSkill() != null) { setQueuedSkill(null, false, false); } if (!checkUseMagicConditions(skill, forceUse, dontMove)) { setIsCastingNow(false); return false; } // Check if the target is correct and Notify the AI with AI_INTENTION_CAST and target L2Object target = null; switch (skill.getTargetType()) { case AURA: // AURA, SELF should be cast even if no target has been found case FRONT_AURA: case BEHIND_AURA: case GROUND: case SELF: case AURA_CORPSE_MOB: case COMMAND_CHANNEL: target = this; break; default: // Get the first target of the list target = skill.getFirstOfTargetList(this); break; } // Notify the AI with AI_INTENTION_CAST and target getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target); return true; } private boolean checkUseMagicConditions(Skill skill, boolean forceUse, boolean dontMove) { // ************************************* Check Player State ******************************************* // Abnormal effects(ex : Stun, Sleep...) are checked in L2Character useMagic() if (isOutOfControl() || isParalyzed() || isStunned() || isSleeping()) { sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check if the player is dead if (isDead()) { // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } if (isFishing() && !skill.hasEffectType(L2EffectType.FISHING, L2EffectType.FISHING_START)) { // Only fishing skills are available sendPacket(SystemMessageId.ONLY_FISHING_SKILLS_MAY_BE_USED_AT_THIS_TIME); return false; } if (inObserverMode()) { sendPacket(SystemMessageId.OBSERVERS_CANNOT_PARTICIPATE); abortCast(); sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check if the caster is sitting if (isSitting()) { // Send a System Message to the caster sendPacket(SystemMessageId.YOU_CANNOT_MOVE_WHILE_SITTING); // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check if the skill type is toggle. if (skill.isToggle() && isAffectedBySkill(skill.getId())) { stopSkillEffects(true, skill.getId()); sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check if the player uses "Fake Death" skill // Note: do not check this before TOGGLE reset if (isFakeDeath()) { // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } // ************************************* Check Target ******************************************* // Create and set a L2Object containing the target of the skill L2Object target = null; L2TargetType sklTargetType = skill.getTargetType(); Location worldPosition = getCurrentSkillWorldPosition(); if ((sklTargetType == L2TargetType.GROUND) && (worldPosition == null)) { _log.info("WorldPosition is null for skill: " + skill.getName() + ", player: " + getName() + "."); sendPacket(ActionFailed.STATIC_PACKET); return false; } switch (sklTargetType) { // Target the player if skill type is AURA, PARTY, CLAN or SELF case AURA: case FRONT_AURA: case BEHIND_AURA: case PARTY: case CLAN: case PARTY_CLAN: case GROUND: case SELF: case AREA_SUMMON: case AURA_CORPSE_MOB: case COMMAND_CHANNEL: target = this; break; case PET: target = getPet(); break; case SERVITOR: case SUMMON: target = getServitors().values().stream().findFirst().orElse(null); break; default: target = getTarget(); break; } // Check the validity of the target if (target == null) { sendPacket(ActionFailed.STATIC_PACKET); return false; } // Faction System Guards if (Config.FACTION_SYSTEM_ENABLED && Config.FACTION_GUARDS_ENABLED && (target instanceof L2GuardInstance)) { if (skill.isBad() && ((isGood() && ((L2Npc) target).getTemplate().isClan(Config.FACTION_GOOD_TEAM_NAME)) || (isEvil() && ((L2Npc) target).getTemplate().isClan(Config.FACTION_EVIL_TEAM_NAME)))) { sendPacket(ActionFailed.STATIC_PACKET); return false; } } // skills can be used on Walls and Doors only during siege if (target.isDoor()) { final L2DoorInstance door = (L2DoorInstance) target; if ((door.getCastle() != null) && (door.getCastle().getResidenceId() > 0)) { if (!door.getCastle().getSiege().isInProgress()) { sendPacket(SystemMessageId.INVALID_TARGET); return false; } } else if ((door.getFort() != null) && (door.getFort().getResidenceId() > 0)) { if (!door.getFort().getSiege().isInProgress() || !door.getIsShowHp()) { sendPacket(SystemMessageId.INVALID_TARGET); return false; } } } // Are the target and the player in the same duel? if (isInDuel()) { // Get L2PcInstance if (target instanceof L2Playable) { // Get L2PcInstance L2PcInstance cha = target.getActingPlayer(); if (cha.getDuelId() != getDuelId()) { sendMessage("You cannot do this while duelling."); sendPacket(ActionFailed.STATIC_PACKET); return false; } } } // ************************************* Check skill availability ******************************************* // Check if this skill is enabled (ex : reuse time) if (isSkillDisabled(skill)) { final SystemMessage sm; if (hasSkillReuse(skill.getReuseHashCode())) { int remainingTime = (int) (getSkillRemainingReuseTime(skill.getReuseHashCode()) / 1000); int hours = remainingTime / 3600; int minutes = (remainingTime % 3600) / 60; int seconds = (remainingTime % 60); if (hours > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_S2_HOUR_S_S3_MINUTE_S_AND_S4_SECOND_S_REMAINING_IN_S1_S_RE_USE_TIME); sm.addSkillName(skill); sm.addInt(hours); sm.addInt(minutes); } else if (minutes > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_S2_MINUTE_S_S3_SECOND_S_REMAINING_IN_S1_S_RE_USE_TIME); sm.addSkillName(skill); sm.addInt(minutes); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.THERE_ARE_S2_SECOND_S_REMAINING_IN_S1_S_RE_USE_TIME); sm.addSkillName(skill); } sm.addInt(seconds); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.S1_IS_NOT_AVAILABLE_AT_THIS_TIME_BEING_PREPARED_FOR_REUSE); sm.addSkillName(skill); } sendPacket(sm); return false; } // ************************************* Check casting conditions ******************************************* // Check if all casting conditions are completed if (!skill.checkCondition(this, target, false)) { // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } // ************************************* Check Skill Type ******************************************* // Check if this is bad magic skill if (skill.isBad()) { if ((isInsidePeaceZone(this, target)) && !getAccessLevel().allowPeaceAttack()) { // If L2Character or target is in a peace zone, send a system message TARGET_IN_PEACEZONE a Server->Client packet ActionFailed sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_THIS_TARGET_IN_A_PEACEFUL_ZONE); sendPacket(ActionFailed.STATIC_PACKET); return false; } if (isInOlympiadMode() && !isOlympiadStart()) { // if L2PcInstance is in Olympia and the match isn't already start, send a Server->Client packet ActionFailed sendPacket(ActionFailed.STATIC_PACKET); return false; } if ((target.getActingPlayer() != null) && (getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (target.getActingPlayer().getSiegeState() == getSiegeState()) && (target.getActingPlayer() != this) && (target.getActingPlayer().getSiegeSide() == getSiegeSide())) { sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE); sendPacket(ActionFailed.STATIC_PACKET); return false; } if (!target.canBeAttacked() && !getAccessLevel().allowPeaceAttack() && !target.isDoor()) { // If target is not attackable, send a Server->Client packet ActionFailed sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check for Event Mob's if ((target instanceof L2EventMonsterInstance) && ((L2EventMonsterInstance) target).eventSkillAttackBlocked()) { sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check if a Forced ATTACK is in progress on non-attackable target if (!target.isAutoAttackable(this) && !forceUse) { switch (sklTargetType) { case AURA: case FRONT_AURA: case BEHIND_AURA: case AURA_CORPSE_MOB: case CLAN: case PARTY: case SELF: case GROUND: case AREA_SUMMON: case UNLOCKABLE: break; default: // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } } // Check if the target is in the skill cast range if (dontMove) { // Calculate the distance between the L2PcInstance and the target if (sklTargetType == L2TargetType.GROUND) { if (!isInsideRadius(worldPosition.getX(), worldPosition.getY(), worldPosition.getZ(), skill.getCastRange() + getTemplate().getCollisionRadius(), false, false)) { // Send a System Message to the caster sendPacket(SystemMessageId.YOUR_TARGET_IS_OUT_OF_RANGE); // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } } else if ((skill.getCastRange() > 0) && !isInsideRadius(target, skill.getCastRange() + getTemplate().getCollisionRadius(), false, false)) { // Send a System Message to the caster sendPacket(SystemMessageId.YOUR_TARGET_IS_OUT_OF_RANGE); // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } } } if (skill.hasEffectType(L2EffectType.TELEPORT_TO_TARGET)) { // You cannot jump while rooted right ;) if (isMovementDisabled()) { // Sends message that skill cannot be used... SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_CANNOT_BE_USED_DUE_TO_UNSUITABLE_TERMS); sm.addSkillName(skill.getId()); sendPacket(sm); // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } // And this skill cannot be used in peace zone, not even on NPCs! if (isInsideZone(ZoneId.PEACE)) { // Sends a sys msg to client sendPacket(SystemMessageId.YOU_MAY_NOT_ATTACK_THIS_TARGET_IN_A_PEACEFUL_ZONE); // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } } // Check if the skill is a good magic, target is a monster and if force attack is set, if not then we don't want to cast. if ((skill.getEffectPoint() > 0) && target.isMonster() && !forceUse) { sendPacket(ActionFailed.STATIC_PACKET); return false; } // Check if this is a Pvp skill and target isn't a non-flagged/non-karma player switch (sklTargetType) { case PARTY: case CLAN: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList() case PARTY_CLAN: // For such skills, checkPvpSkill() is called from L2Skill.getTargetList() case AURA: case FRONT_AURA: case BEHIND_AURA: case GROUND: case SELF: break; default: if (!checkPvpSkill(target, skill) && !getAccessLevel().allowPeaceAttack() && target.isPlayable()) { // Send a System Message to the L2PcInstance sendPacket(SystemMessageId.THAT_IS_AN_INCORRECT_TARGET); // Send a Server->Client packet ActionFailed to the L2PcInstance sendPacket(ActionFailed.STATIC_PACKET); return false; } } // GeoData Los Check here if (skill.getCastRange() > 0) { if (sklTargetType == L2TargetType.GROUND) { if (!GeoData.getInstance().canSeeTarget(this, worldPosition)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); sendPacket(ActionFailed.STATIC_PACKET); return false; } } else if (!GeoData.getInstance().canSeeTarget(this, target)) { sendPacket(SystemMessageId.CANNOT_SEE_TARGET); sendPacket(ActionFailed.STATIC_PACKET); return false; } } if ((skill.getFlyType() == FlyType.CHARGE) && !GeoData.getInstance().canMove(this, target)) { sendPacket(SystemMessageId.THE_TARGET_IS_LOCATED_WHERE_YOU_CANNOT_CHARGE); return false; } // finally, after passing all conditions return true; } public boolean isInLooterParty(int LooterId) { L2PcInstance looter = L2World.getInstance().getPlayer(LooterId); // if L2PcInstance is in a CommandChannel if (isInParty() && getParty().isInCommandChannel() && (looter != null)) { return getParty().getCommandChannel().getMembers().contains(looter); } if (isInParty() && (looter != null)) { return getParty().getMembers().contains(looter); } return false; } /** * Check if the requested casting is a Pc->Pc skill cast and if it's a valid pvp condition * @param target L2Object instance containing the target * @param skill L2Skill instance with the skill being casted * @return {@code false} if the skill is a pvpSkill and target is not a valid pvp target, {@code true} otherwise. */ public boolean checkPvpSkill(L2Object target, Skill skill) { if ((skill == null) || (target == null)) { return false; } if (!(target instanceof L2Playable)) { return true; } if (skill.isDebuff() || skill.hasEffectType(L2EffectType.STEAL_ABNORMAL) || skill.isBad()) { final L2PcInstance targetPlayer = target.getActingPlayer(); if ((targetPlayer == null) || (this == target)) { return false; } final boolean isCtrlPressed = (getCurrentSkill() != null) && getCurrentSkill().isCtrlPressed(); // Pece Zone if (target.isInsideZone(ZoneId.PEACE)) { return false; } // Siege if ((getSiegeState() != 0) && (targetPlayer.getSiegeState() != 0)) { // Register for same siege if (getSiegeSide() == targetPlayer.getSiegeSide()) { // Same side if (getSiegeState() == targetPlayer.getSiegeState()) { sendPacket(SystemMessageId.FORCE_ATTACK_IS_IMPOSSIBLE_AGAINST_A_TEMPORARY_ALLIED_MEMBER_DURING_A_SIEGE); return false; } } } // Duel if (isInDuel() && targetPlayer.isInDuel()) { if (getDuelId() == targetPlayer.getDuelId()) { return true; } } // Party if (isInParty() && targetPlayer.isInParty()) { // Same Party if (getParty().getLeader() == targetPlayer.getParty().getLeader()) { if ((skill.getEffectRange() > 0) && isCtrlPressed && (getTarget() == target)) { if (skill.isDamage()) { return true; } } return false; } else if ((getParty().getCommandChannel() != null) && getParty().getCommandChannel().containsPlayer(targetPlayer)) { if ((skill.getEffectRange() > 0) && isCtrlPressed && (getTarget() == target)) { if (skill.isDamage()) { return true; } } return false; } } // You can debuff anyone except party members while in an arena... if (isInsideZone(ZoneId.PVP) && targetPlayer.isInsideZone(ZoneId.PVP)) { return true; } // Olympiad if (isInOlympiadMode() && targetPlayer.isInOlympiadMode()) { if (getOlympiadGameId() == targetPlayer.getOlympiadGameId()) { return true; } } final L2Clan aClan = getClan(); final L2Clan tClan = targetPlayer.getClan(); if ((aClan != null) && (tClan != null)) { if (aClan.isAtWarWith(tClan.getId()) && tClan.isAtWarWith(aClan.getId())) { // Check if skill can do dmg if ((skill.isAOE() && (skill.getEffectRange() > 0)) && isCtrlPressed && (getTarget() == target)) { return true; } return isCtrlPressed; } else if ((getClanId() == targetPlayer.getClanId()) || ((getAllyId() > 0) && (getAllyId() == targetPlayer.getAllyId()))) { // Check if skill can do dmg if ((skill.getEffectRange() > 0) && isCtrlPressed && (getTarget() == target)) { if (skill.isDamage()) { return true; } } return false; } } // Faction System if (Config.FACTION_SYSTEM_ENABLED) { if ((isGood() && target.getActingPlayer().isGood()) || (isEvil() && target.getActingPlayer().isEvil())) { return false; } return true; } // On retail, it is impossible to debuff a "peaceful" player. if ((targetPlayer.getPvpFlag() == 0) && (targetPlayer.getKarma() == 0)) { // Check if skill can do dmg if ((skill.getEffectRange() > 0) && isCtrlPressed && (getTarget() == target)) { if (skill.isDamage()) { return true; } } return false; } if ((targetPlayer.getPvpFlag() > 0) || (targetPlayer.getKarma() > 0)) { return true; } return false; } return true; } /** * @return True if the L2PcInstance is a Mage. */ public boolean isMageClass() { return getClassId().isMage(); } public boolean isMounted() { return _mountType != MountType.NONE; } public boolean checkLandingState() { // Check if char is in a no landing zone if (isInsideZone(ZoneId.NO_LANDING)) { return true; } else // if this is a castle that is currently being sieged, and the rider is NOT a castle owner // he cannot land. // castle owner is the leader of the clan that owns the castle where the pc is if (isInsideZone(ZoneId.SIEGE) && !((getClan() != null) && (CastleManager.getInstance().getCastle(this) == CastleManager.getInstance().getCastleByOwner(getClan())) && (this == getClan().getLeader().getPlayerInstance()))) { return true; } return false; } // returns false if the change of mount type fails. public void setMount(int npcId, int npcLevel) { final MountType type = MountType.findByNpcId(npcId); switch (type) { case NONE: // None { setIsFlying(false); break; } case STRIDER: // Strider { if (isNoble()) { addSkill(CommonSkill.STRIDER_SIEGE_ASSAULT.getSkill(), false); } break; } case WYVERN: // Wyvern { setIsFlying(true); break; } } _mountType = type; _mountNpcId = npcId; _mountLevel = npcLevel; } /** * @return the type of Pet mounted (0 : none, 1 : Strider, 2 : Wyvern, 3: Wolf). */ public MountType getMountType() { return _mountType; } @Override public final void stopAllEffects() { super.stopAllEffects(); updateAndBroadcastStatus(2); } @Override public final void stopAllEffectsExceptThoseThatLastThroughDeath() { super.stopAllEffectsExceptThoseThatLastThroughDeath(); updateAndBroadcastStatus(2); } public final void stopAllEffectsNotStayOnSubclassChange() { getEffectList().stopAllEffectsNotStayOnSubclassChange(); updateAndBroadcastStatus(2); } public final void stopCubics() { if (!_cubics.isEmpty()) { for (L2CubicInstance cubic : _cubics.values()) { cubic.stopAction(); cubic.cancelDisappear(); } _cubics.clear(); sendPacket(new ExUserInfoCubic(this)); broadcastPacket(new CharInfo(this)); } } public final void stopCubicsByOthers() { if (!_cubics.isEmpty()) { boolean broadcast = false; for (L2CubicInstance cubic : _cubics.values()) { if (cubic.givenByOther()) { cubic.stopAction(); cubic.cancelDisappear(); _cubics.remove(cubic.getId()); broadcast = true; } } if (broadcast) { sendPacket(new ExUserInfoCubic(this)); broadcastPacket(new CharInfo(this)); } } } /** * Send a Server->Client packet UserInfo to this L2PcInstance and CharInfo to all L2PcInstance in its _KnownPlayers.<br> * <B><U>Concept</U>:</B><br> * Others L2PcInstance in the detection area of the L2PcInstance are identified in <B>_knownPlayers</B>.<br> * In order to inform other players of this L2PcInstance state modifications, server just need to go through _knownPlayers to send Server->Client Packet<br> * <B><U>Actions</U>:</B> * <ul> * <li>Send a Server->Client packet UserInfo to this L2PcInstance (Public and Private Data)</li> * <li>Send a Server->Client packet CharInfo to all L2PcInstance in _KnownPlayers of the L2PcInstance (Public data only)</li> * </ul> * <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> */ @Override public void updateAbnormalVisualEffects() { sendPacket(new ExUserInfoAbnormalVisualEffect(this)); broadcastPacket(new CharInfo(this)); } /** * Disable the Inventory and create a new task to enable it after 1.5s. * @param val */ public void setInventoryBlockingStatus(boolean val) { _inventoryDisable = val; if (val) { ThreadPoolManager.getInstance().scheduleGeneral(new InventoryEnableTask(this), 1500); } } /** * @return True if the Inventory is disabled. */ public boolean isInventoryDisabled() { return _inventoryDisable; } /** * Add a cubic to this player. * @param cubicId the cubic ID * @param level * @param cubicPower * @param cubicDelay * @param cubicSkillChance * @param cubicMaxCount * @param cubicDuration * @param givenByOther * @return the old cubic for this cubic ID if any, otherwise {@code null} */ public L2CubicInstance addCubic(int cubicId, int level, double cubicPower, int cubicDelay, int cubicSkillChance, int cubicMaxCount, int cubicDuration, boolean givenByOther) { return _cubics.put(cubicId, new L2CubicInstance(this, cubicId, level, (int) cubicPower, cubicDelay, cubicSkillChance, cubicMaxCount, cubicDuration, givenByOther)); } /** * Get the player's cubics. * @return the cubics */ public Map<Integer, L2CubicInstance> getCubics() { return _cubics; } /** * Get the player cubic by cubic ID, if any. * @param cubicId the cubic ID * @return the cubic with the given cubic ID, {@code null} otherwise */ public L2CubicInstance getCubicById(int cubicId) { return _cubics.get(cubicId); } /** * @return the modifier corresponding to the Enchant Effect of the Active Weapon (Min : 127). */ public int getEnchantEffect() { L2ItemInstance wpn = getActiveWeaponInstance(); if (wpn == null) { return 0; } return Math.min(127, wpn.getEnchantLevel()); } /** * Set the _lastFolkNpc of the L2PcInstance corresponding to the last Folk wich one the player talked. * @param folkNpc */ public void setLastFolkNPC(L2Npc folkNpc) { _lastFolkNpc = folkNpc; } /** * @return the _lastFolkNpc of the L2PcInstance corresponding to the last Folk wich one the player talked. */ public L2Npc getLastFolkNPC() { return _lastFolkNpc; } public void addAutoSoulShot(int itemId) { _activeSoulShots.add(itemId); } public boolean removeAutoSoulShot(int itemId) { return _activeSoulShots.remove(itemId); } public Set<Integer> getAutoSoulShot() { return _activeSoulShots; } @Override public void rechargeShots(boolean physical, boolean magic) { for (int itemId : _activeSoulShots) { final L2ItemInstance item = getInventory().getItemByItemId(itemId); if (item == null) { removeAutoSoulShot(itemId); continue; } final IItemHandler handler = ItemHandler.getInstance().getHandler(item.getEtcItem()); if (handler == null) { continue; } if ((magic && (item.getItem().getDefaultAction() == ActionType.SPIRITSHOT)) // || (physical && (item.getItem().getDefaultAction() == ActionType.SOULSHOT))) { handler.useItem(this, item, false); } } } /** * Cancel autoshot for all shots matching crystaltype {@link L2Item#getCrystalType()}. * @param crystalType int type to disable */ public void disableAutoShotByCrystalType(int crystalType) { for (int itemId : _activeSoulShots) { if (ItemTable.getInstance().getTemplate(itemId).getCrystalType().getId() == crystalType) { disableAutoShot(itemId); } } } /** * Cancel autoshot use for shot itemId * @param itemId int id to disable * @return true if canceled. */ public boolean disableAutoShot(int itemId) { if (_activeSoulShots.contains(itemId)) { removeAutoSoulShot(itemId); sendPacket(new ExAutoSoulShot(itemId, 0)); SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_AUTOMATIC_USE_OF_S1_HAS_BEEN_DEACTIVATED); sm.addItemName(itemId); sendPacket(sm); return true; } return false; } /** * Cancel all autoshots for player */ public void disableAutoShotsAll() { for (int itemId : _activeSoulShots) { sendPacket(new ExAutoSoulShot(itemId, 0)); SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.THE_AUTOMATIC_USE_OF_S1_HAS_BEEN_DEACTIVATED); sm.addItemName(itemId); sendPacket(sm); } _activeSoulShots.clear(); } private ScheduledFuture<?> _taskWarnUserTakeBreak; public EnumIntBitmask<ClanPrivilege> getClanPrivileges() { return _clanPrivileges; } public void setClanPrivileges(EnumIntBitmask<ClanPrivilege> clanPrivileges) { _clanPrivileges = clanPrivileges.clone(); } public boolean hasClanPrivilege(ClanPrivilege privilege) { return _clanPrivileges.has(privilege); } // baron etc public void setPledgeClass(int classId) { _pledgeClass = classId; checkItemRestriction(); } public int getPledgeClass() { return _pledgeClass; } public void setPledgeType(int typeId) { _pledgeType = typeId; } @Override public int getPledgeType() { return _pledgeType; } public int getApprentice() { return _apprentice; } public void setApprentice(int apprentice_id) { _apprentice = apprentice_id; } public int getSponsor() { return _sponsor; } public void setSponsor(int sponsor_id) { _sponsor = sponsor_id; } public int getBookMarkSlot() { return _bookmarkslot; } public void setBookMarkSlot(int slot) { _bookmarkslot = slot; sendPacket(new ExGetBookMarkInfoPacket(this)); } @Override public void sendMessage(String message) { sendPacket(SystemMessage.sendString(message)); } public void enterObserverMode(Location loc) { setLastLocation(); // Remove Hide. getEffectList().stopSkillEffects(true, AbnormalType.HIDE); _observerMode = true; setTarget(null); setIsParalyzed(true); startParalyze(); setIsInvul(true); setInvisible(true); sendPacket(new ObservationMode(loc)); teleToLocation(loc, false); broadcastUserInfo(); } public void setLastLocation() { _lastLoc.setXYZ(getX(), getY(), getZ()); } public void unsetLastLocation() { _lastLoc.setXYZ(0, 0, 0); } public void enterOlympiadObserverMode(Location loc, int id) { final L2Summon pet = getPet(); if (pet != null) { pet.unSummon(this); } if (hasServitors()) { getServitors().values().forEach(s -> s.unSummon(this)); } // Remove Hide. getEffectList().stopSkillEffects(true, AbnormalType.HIDE); if (!_cubics.isEmpty()) { for (L2CubicInstance cubic : _cubics.values()) { cubic.stopAction(); cubic.cancelDisappear(); } _cubics.clear(); sendPacket(new ExUserInfoCubic(this)); } if (getParty() != null) { getParty().removePartyMember(this, messageType.Expelled); } _olympiadGameId = id; if (isSitting()) { standUp(); } if (!_observerMode) { setLastLocation(); } _observerMode = true; setTarget(null); setIsInvul(true); setInvisible(true); teleToLocation(loc, false); sendPacket(new ExOlympiadMode(3)); broadcastUserInfo(); } public void leaveObserverMode() { setTarget(null); teleToLocation(_lastLoc, false); unsetLastLocation(); sendPacket(new ObservationReturn(getLocation())); setIsParalyzed(false); if (!isGM()) { setInvisible(false); setIsInvul(false); } if (hasAI()) { getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); } setFalling(); // prevent receive falling damage _observerMode = false; broadcastUserInfo(); } public void leaveOlympiadObserverMode() { if (_olympiadGameId == -1) { return; } _olympiadGameId = -1; _observerMode = false; setTarget(null); sendPacket(new ExOlympiadMode(0)); setInstanceId(0); teleToLocation(_lastLoc, true); if (!isGM()) { setInvisible(false); setIsInvul(false); } if (hasAI()) { getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE); } unsetLastLocation(); broadcastUserInfo(); } public void setOlympiadSide(int i) { _olympiadSide = i; } public int getOlympiadSide() { return _olympiadSide; } public void setOlympiadGameId(int id) { _olympiadGameId = id; } public int getOlympiadGameId() { return _olympiadGameId; } /** * Gets the player's olympiad buff count. * @return the olympiad's buff count */ public int getOlympiadBuffCount() { return _olyBuffsCount; } /** * Sets the player's olympiad buff count. * @param buffs the olympiad's buff count */ public void setOlympiadBuffCount(int buffs) { _olyBuffsCount = buffs; } public Location getLastLocation() { return _lastLoc; } public boolean inObserverMode() { return _observerMode; } public int getTeleMode() { return _telemode; } public void setTeleMode(int mode) { _telemode = mode; } public void setLoto(int i, int val) { _loto[i] = val; } public int getLoto(int i) { return _loto[i]; } public void setRace(int i, int val) { _race[i] = val; } public int getRace(int i) { return _race[i]; } public boolean getMessageRefusal() { return _messageRefusal; } public void setMessageRefusal(boolean mode) { _messageRefusal = mode; sendPacket(new EtcStatusUpdate(this)); } public void setDietMode(boolean mode) { _dietMode = mode; } public boolean getDietMode() { return _dietMode; } public void setTradeRefusal(boolean mode) { _tradeRefusal = mode; } public boolean getTradeRefusal() { return _tradeRefusal; } public void setExchangeRefusal(boolean mode) { _exchangeRefusal = mode; } public boolean getExchangeRefusal() { return _exchangeRefusal; } public BlockList getBlockList() { return _blockList; } /** * @param player * @return returns {@code true} if player is current player cannot accepting messages from the target player, {@code false} otherwise */ public boolean isBlocking(L2PcInstance player) { return _blockList.isBlockAll() || _blockList.isInBlockList(player); } /** * @param player * @return returns {@code true} if player is current player can accepting messages from the target player, {@code false} otherwise */ public boolean isNotBlocking(L2PcInstance player) { return !_blockList.isBlockAll() && !_blockList.isInBlockList(player); } /** * @param player * @return returns {@code true} if player is target player cannot accepting messages from the current player, {@code false} otherwise */ public boolean isBlocked(L2PcInstance player) { return player.getBlockList().isBlockAll() || player.getBlockList().isInBlockList(this); } /** * @param player * @return returns {@code true} if player is target player can accepting messages from the current player, {@code false} otherwise */ public boolean isNotBlocked(L2PcInstance player) { return !player.getBlockList().isBlockAll() && !player.getBlockList().isInBlockList(this); } public void setHero(boolean hero) { if (hero && (_baseClass == _activeClass)) { for (Skill skill : SkillTreesData.getInstance().getHeroSkillTree().values()) { addSkill(skill, false); // Don't persist hero skills into database } } else { for (Skill skill : SkillTreesData.getInstance().getHeroSkillTree().values()) { removeSkill(skill, false, true); // Just remove skills from non-hero players } } _hero = hero; sendSkillList(); } public void setIsInOlympiadMode(boolean b) { _inOlympiadMode = b; } public void setIsOlympiadStart(boolean b) { _OlympiadStart = b; } public boolean isOlympiadStart() { return _OlympiadStart; } public boolean isHero() { return _hero; } public boolean isInOlympiadMode() { return _inOlympiadMode; } @Override public boolean isInDuel() { return _isInDuel; } @Override public int getDuelId() { return _duelId; } public void setDuelState(int mode) { _duelState = mode; } public int getDuelState() { return _duelState; } /** * Sets up the duel state using a non 0 duelId. * @param duelId 0=not in a duel */ public void setIsInDuel(int duelId) { if (duelId > 0) { _isInDuel = true; _duelState = Duel.DUELSTATE_DUELLING; _duelId = duelId; } else { if (_duelState == Duel.DUELSTATE_DEAD) { enableAllSkills(); getStatus().startHpMpRegeneration(); } _isInDuel = false; _duelState = Duel.DUELSTATE_NODUEL; _duelId = 0; } } /** * This returns a SystemMessage stating why the player is not available for duelling. * @return S1_CANNOT_DUEL... message */ public SystemMessage getNoDuelReason() { SystemMessage sm = SystemMessage.getSystemMessage(_noDuelReason); sm.addPcName(this); _noDuelReason = SystemMessageId.THERE_IS_NO_OPPONENT_TO_RECEIVE_YOUR_CHALLENGE_FOR_A_DUEL; return sm; } /** * Checks if this player might join / start a duel.<br> * To get the reason use getNoDuelReason() after calling this function. * @return true if the player might join/start a duel. */ public boolean canDuel() { if (isInCombat() || isJailed()) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_BATTLE; return false; } if (isDead() || isAlikeDead() || ((getCurrentHp() < (getMaxHp() / 2)) || (getCurrentMp() < (getMaxMp() / 2)))) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_S_HP_OR_MP_IS_BELOW_50; return false; } if (isInDuel()) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_ALREADY_ENGAGED_IN_A_DUEL; return false; } if (isInOlympiadMode()) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_PARTICIPATING_IN_THE_OLYMPIAD_OR_THE_CEREMONY_OF_CHAOS; return false; } if (isCursedWeaponEquipped()) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_IN_A_CHAOTIC_OR_PURPLE_STATE; return false; } if (getPrivateStoreType() != PrivateStoreType.NONE) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_ENGAGED_IN_A_PRIVATE_STORE_OR_MANUFACTURE; return false; } if (isMounted() || isInBoat()) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_RIDING_A_BOAT_FENRIR_OR_STRIDER; return false; } if (isFishing()) { _noDuelReason = SystemMessageId.C1_CANNOT_DUEL_BECAUSE_C1_IS_CURRENTLY_FISHING; return false; } if (isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.PEACE) || isInsideZone(ZoneId.SIEGE)) { _noDuelReason = SystemMessageId.C1_CANNOT_MAKE_A_CHALLENGE_TO_A_DUEL_BECAUSE_C1_IS_CURRENTLY_IN_A_DUEL_PROHIBITED_AREA_PEACEFUL_ZONE_BATTLE_ZONE_NEAR_WATER_RESTART_PROHIBITED_AREA; return false; } return true; } public boolean isNoble() { return _noble; } public void setNoble(boolean val) { final Collection<Skill> nobleSkillTree = SkillTreesData.getInstance().getNobleSkillTree().values(); if (val) { for (Skill skill : nobleSkillTree) { addSkill(skill, false); } } else { for (Skill skill : nobleSkillTree) { removeSkill(skill, false, true); } } _noble = val; sendSkillList(); if (val && (getLevel() == ExperienceData.getInstance().getMaxLevel())) { sendPacket(new ExAcquireAPSkillList(this)); } } public void setLvlJoinedAcademy(int lvl) { _lvlJoinedAcademy = lvl; } public int getLvlJoinedAcademy() { return _lvlJoinedAcademy; } @Override public boolean isAcademyMember() { return _lvlJoinedAcademy > 0; } @Override public void setTeam(Team team) { super.setTeam(team); broadcastUserInfo(); final L2Summon pet = getPet(); if (pet != null) { pet.broadcastStatusUpdate(); } if (hasServitors()) { getServitors().values().forEach(L2Summon::broadcastStatusUpdate); } } public void setWantsPeace(int wantsPeace) { _wantsPeace = wantsPeace; } public int getWantsPeace() { return _wantsPeace; } public boolean isFishing() { return _fishing; } public void setFishing(boolean fishing) { _fishing = fishing; } public void sendSkillList() { sendSkillList(0); } public void sendSkillList(int lastLearnedSkillId) { boolean isDisabled = false; SkillList sl = new SkillList(); for (Skill s : getAllSkills()) { if (s == null) { continue; } if ((_transformation != null) && (!hasTransformSkill(s.getId()) && !s.allowOnTransform())) { continue; } if (getClan() != null) { isDisabled = s.isClanSkill() && (getClan().getReputationScore() < 0); } boolean isEnchantable = SkillData.getInstance().isEnchantable(s.getId()); if (isEnchantable) { L2EnchantSkillLearn esl = EnchantSkillGroupsData.getInstance().getSkillEnchantmentBySkillId(s.getId()); if (esl != null) { // if player dont have min level to enchant if (s.getLevel() < esl.getBaseLevel()) { isEnchantable = false; } } // if no enchant data else { isEnchantable = false; } } sl.addSkill(s.getDisplayId(), s.getDisplayLevel(), s.isPassive(), isDisabled, isEnchantable); } sendPacket(sl); } /** * 1. Add the specified class ID as a subclass (up to the maximum number of <b>three</b>) for this character.<BR> * 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. * @param classId * @param classIndex * @param isDualClass * @return boolean subclassAdded */ public boolean addSubClass(int classId, int classIndex, boolean isDualClass) { if (!_subclassLock.tryLock()) { return false; } try { if ((getTotalSubClasses() == Config.MAX_SUBCLASS) || (classIndex == 0)) { return false; } if (getSubClasses().containsKey(classIndex)) { return false; } // Note: Never change _classIndex in any method other than setActiveClass(). final SubClass newClass = new SubClass(); newClass.setClassId(classId); newClass.setClassIndex(classIndex); if (isDualClass) { newClass.setIsDualClass(true); newClass.setExp(ExperienceData.getInstance().getExpForLevel(Config.BASE_DUALCLASS_LEVEL)); newClass.setLevel(Config.BASE_DUALCLASS_LEVEL); } try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(ADD_CHAR_SUBCLASS)) { // Store the basic info about this new sub-class. statement.setInt(1, getObjectId()); statement.setInt(2, newClass.getClassId()); statement.setLong(3, newClass.getExp()); statement.setLong(4, newClass.getSp()); statement.setInt(5, newClass.getLevel()); statement.setInt(6, newClass.getClassIndex()); statement.setBoolean(7, newClass.isDualClass()); statement.execute(); } catch (Exception e) { _log.log(Level.WARNING, "WARNING: Could not add character sub class for " + getName() + ": " + e.getMessage(), e); return false; } // Commit after database INSERT incase exception is thrown. getSubClasses().put(newClass.getClassIndex(), newClass); final ClassId subTemplate = ClassId.getClassId(classId); final Map<Integer, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(subTemplate); final Map<Integer, Skill> prevSkillList = new HashMap<>(); for (L2SkillLearn skillInfo : skillTree.values()) { if (skillInfo.getGetLevel() <= newClass.getLevel()) { final Skill prevSkill = prevSkillList.get(skillInfo.getSkillId()); final Skill newSkill = SkillData.getInstance().getSkill(skillInfo.getSkillId(), skillInfo.getSkillLevel()); if (((prevSkill != null) && (prevSkill.getLevel() > newSkill.getLevel())) || SkillTreesData.getInstance().isRemoveSkill(subTemplate, skillInfo.getSkillId())) { continue; } prevSkillList.put(newSkill.getId(), newSkill); storeSkill(newSkill, prevSkill, classIndex); } } return true; } finally { _subclassLock.unlock(); } } /** * 1. Completely erase all existance of the subClass linked to the classIndex.<br> * 2. Send over the newClassId to addSubClass() to create a new instance on this classIndex.<br> * 3. Upon Exception, revert the player to their BaseClass to avoid further problems. * @param classIndex the class index to delete * @param newClassId the new class Id * @param isDualClass is subclass dualclass * @return {@code true} if the sub-class was modified, {@code false} otherwise */ public boolean modifySubClass(int classIndex, int newClassId, boolean isDualClass) { if (!_subclassLock.tryLock()) { return false; } try { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement deleteHennas = con.prepareStatement(DELETE_CHAR_HENNAS); PreparedStatement deleteShortcuts = con.prepareStatement(DELETE_CHAR_SHORTCUTS); PreparedStatement deleteSkillReuse = con.prepareStatement(DELETE_SKILL_SAVE); PreparedStatement deleteSkills = con.prepareStatement(DELETE_CHAR_SKILLS); PreparedStatement deleteSubclass = con.prepareStatement(DELETE_CHAR_SUBCLASS)) { // Remove all henna info stored for this sub-class. deleteHennas.setInt(1, getObjectId()); deleteHennas.setInt(2, classIndex); deleteHennas.execute(); // Remove all shortcuts info stored for this sub-class. deleteShortcuts.setInt(1, getObjectId()); deleteShortcuts.setInt(2, classIndex); deleteShortcuts.execute(); // Remove all effects info stored for this sub-class. deleteSkillReuse.setInt(1, getObjectId()); deleteSkillReuse.setInt(2, classIndex); deleteSkillReuse.execute(); // Remove all skill info stored for this sub-class. deleteSkills.setInt(1, getObjectId()); deleteSkills.setInt(2, classIndex); deleteSkills.execute(); // Remove all basic info stored about this sub-class. deleteSubclass.setInt(1, getObjectId()); deleteSubclass.setInt(2, classIndex); deleteSubclass.execute(); } catch (Exception e) { _log.log(Level.WARNING, "Could not modify sub class for " + getName() + " to class index " + classIndex + ": " + e.getMessage(), e); // This must be done in order to maintain data consistency. getSubClasses().remove(classIndex); return false; } getSubClasses().remove(classIndex); } finally { _subclassLock.unlock(); } return addSubClass(newClassId, classIndex, isDualClass); } public boolean isSubClassActive() { return _classIndex > 0; } public void setDualClass(int classIndex) { if (isSubClassActive()) { getSubClasses().get(_classIndex).setIsDualClass(true); } } public boolean isDualClassActive() { return isSubClassActive() && getSubClasses().get(_classIndex).isDualClass(); } public boolean hasDualClass() { return getSubClasses().values().stream().anyMatch(SubClass::isDualClass); } public Map<Integer, SubClass> getSubClasses() { if (_subClasses == null) { _subClasses = new HashMap<>(); } return _subClasses; } public int getTotalSubClasses() { return getSubClasses().size(); } public int getBaseClass() { return _baseClass; } public int getActiveClass() { return _activeClass; } public int getClassIndex() { return _classIndex; } private void setClassTemplate(int classId) { _activeClass = classId; final L2PcTemplate pcTemplate = PlayerTemplateData.getInstance().getTemplate(classId); if (pcTemplate == null) { _log.severe("Missing template for classId: " + classId); throw new Error(); } // Set the template of the L2PcInstance setTemplate(pcTemplate); // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerProfessionChange(this, pcTemplate, isSubClassActive()), this); } /** * Changes the character's class based on the given class index.<br> * An index of zero specifies the character's original (base) class, while indexes 1-3 specifies the character's sub-classes respectively.<br> * <font color="00FF00"/>WARNING: Use only on subclase change</font> * @param classIndex * @return */ public boolean setActiveClass(int classIndex) { if (!_subclassLock.tryLock()) { return false; } try { // Cannot switch or change subclasses while transformed if (_transformation != null) { return false; } // Remove active item skills before saving char to database // because next time when choosing this class, weared items can // be different for (L2ItemInstance item : getInventory().getAugmentedItems()) { if ((item != null) && item.isEquipped()) { item.getAugmentation().removeBonus(this); } } // abort any kind of cast. abortCast(); if (isChannelized()) { getSkillChannelized().abortChannelization(); } // 1. Call store() before modifying _classIndex to avoid skill effects rollover. // 2. Register the correct _classId against applied 'classIndex'. store(Config.SUBCLASS_STORE_SKILL_COOLTIME); resetTimeStamps(); // clear charges _charges.set(0); stopChargeTask(); if (hasServitors()) { getServitors().values().forEach(s -> s.unSummon(this)); } if (classIndex == 0) { setClassTemplate(getBaseClass()); } else { try { setClassTemplate(getSubClasses().get(classIndex).getClassId()); } catch (Exception e) { _log.log(Level.WARNING, "Could not switch " + getName() + "'s sub class to class index " + classIndex + ": " + e.getMessage(), e); return false; } } _classIndex = classIndex; setLearningClass(getClassId()); if (isInParty()) { getParty().recalculatePartyLevel(); } // Update the character's change in class status. // 1. Remove any active cubics from the player. // 2. Renovate the characters table in the database with the new class info, storing also buff/effect data. // 3. Remove all existing skills. // 4. Restore all the learned skills for the current class from the database. // 5. Restore effect/buff data for the new class. // 6. Restore henna data for the class, applying the new stat modifiers while removing existing ones. // 7. Reset HP/MP/CP stats and send Server->Client character status packet to reflect changes. // 8. Restore shortcut data related to this class. // 9. Resend a class change animation effect to broadcast to all nearby players. for (Skill oldSkill : getAllSkills()) { removeSkill(oldSkill, false, true); } stopAllEffectsExceptThoseThatLastThroughDeath(); stopAllEffectsNotStayOnSubclassChange(); stopCubics(); restoreRecipeBook(false); restoreSkills(); rewardSkills(); regiveTemporarySkills(); // Prevents some issues when changing between subclases that shares skills resetDisabledSkills(); restoreEffects(); sendPacket(new EtcStatusUpdate(this)); for (int i = 0; i < 3; i++) { _henna[i] = null; } restoreHenna(); sendPacket(new HennaInfo(this)); if (getCurrentHp() > getMaxHp()) { setCurrentHp(getMaxHp()); } if (getCurrentMp() > getMaxMp()) { setCurrentMp(getMaxMp()); } if (getCurrentCp() > getMaxCp()) { setCurrentCp(getMaxCp()); } refreshOverloaded(); refreshExpertisePenalty(); broadcastUserInfo(); // Clear resurrect xp calculation setExpBeforeDeath(0); _shortCuts.restoreMe(); sendPacket(new ShortCutInit(this)); broadcastPacket(new SocialAction(getObjectId(), SocialAction.LEVEL_UP)); sendPacket(new SkillCoolTime(this)); sendPacket(new ExStorageMaxCount(this)); EventDispatcher.getInstance().notifyEventAsync(new OnPlayerSubChange(this), this); return true; } finally { _subclassLock.unlock(); } } public boolean isLocked() { return _subclassLock.isLocked(); } public void stopWarnUserTakeBreak() { if (_taskWarnUserTakeBreak != null) { _taskWarnUserTakeBreak.cancel(true); _taskWarnUserTakeBreak = null; } } public void startWarnUserTakeBreak() { if (_taskWarnUserTakeBreak == null) { _taskWarnUserTakeBreak = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new WarnUserTakeBreakTask(this), 3600000, 3600000); } } public void stopRentPet() { if (_taskRentPet != null) { // if the rent of a wyvern expires while over a flying zone, tp to down before unmounting if (checkLandingState() && (getMountType() == MountType.WYVERN)) { teleToLocation(TeleportWhereType.TOWN); } if (dismount()) // this should always be true now, since we teleported already { _taskRentPet.cancel(true); _taskRentPet = null; } } } public void startRentPet(int seconds) { if (_taskRentPet == null) { _taskRentPet = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new RentPetTask(this), seconds * 1000L, seconds * 1000L); } } public boolean isRentedPet() { if (_taskRentPet != null) { return true; } return false; } public void stopWaterTask() { if (_taskWater != null) { _taskWater.cancel(false); _taskWater = null; sendPacket(new SetupGauge(2, 0)); } } public void startWaterTask() { if (!isDead() && (_taskWater == null)) { int timeinwater = (int) calcStat(Stats.BREATH, 60000, this, null); sendPacket(new SetupGauge(2, timeinwater)); _taskWater = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new WaterTask(this), timeinwater, 1000); } } public boolean isInWater() { if (_taskWater != null) { return true; } return false; } public void checkWaterState() { if (isInsideZone(ZoneId.WATER)) { startWaterTask(); } else { stopWaterTask(); } } public void onPlayerEnter() { startWarnUserTakeBreak(); if (isGM()) { if (isInvul()) { sendMessage("Entering world in Invulnerable mode."); } if (isInvisible()) { sendMessage("Entering world in Invisible mode."); } if (isSilenceMode()) { sendMessage("Entering world in Silence mode."); } } // Buff and status icons if (Config.STORE_SKILL_COOLTIME) { restoreEffects(); } // TODO : Need to fix that hack! if (!isDead()) { setCurrentCp(_originalCp); setCurrentHp(_originalHp); setCurrentMp(_originalMp); } revalidateZone(true); notifyFriends(L2FriendStatus.MODE_ONLINE); if (!canOverrideCond(PcCondOverride.SKILL_CONDITIONS) && Config.DECREASE_SKILL_LEVEL) { checkPlayerSkills(); } try { for (L2ZoneType zone : ZoneManager.getInstance().getZones(this)) { zone.onPlayerLoginInside(this); } } catch (Exception e) { _log.log(Level.SEVERE, "", e); } EventDispatcher.getInstance().notifyEventAsync(new OnPlayerLogin(this), this); if (isMentee()) { // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMenteeStatus(this, true), this); } else if (isMentor()) { // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMentorStatus(this, true), this); } } public long getLastAccess() { return _lastAccess; } @Override public void doRevive() { super.doRevive(); updateEffectIcons(); sendPacket(new EtcStatusUpdate(this)); _revivePet = false; _reviveRequested = 0; _revivePower = 0; if (isMounted()) { startFeed(_mountNpcId); } if (getInstanceId() > 0) { final Instance instance = InstanceManager.getInstance().getInstance(getInstanceId()); if (instance != null) { instance.cancelEjectDeadPlayer(this); } } } @Override public void setName(String value) { super.setName(value); if (Config.CACHE_CHAR_NAMES) { CharNameTable.getInstance().addName(this); } } @Override public void doRevive(double revivePower) { // Restore the player's lost experience, // depending on the % return of the skill used (based on its power). restoreExp(revivePower); doRevive(); } public void reviveRequest(L2PcInstance reviver, Skill skill, boolean Pet, int power) { if (isResurrectionBlocked()) { return; } if (_reviveRequested == 1) { if (_revivePet == Pet) { reviver.sendPacket(SystemMessageId.RESURRECTION_HAS_ALREADY_BEEN_PROPOSED); // Resurrection is already been proposed. } else { if (Pet) { reviver.sendPacket(SystemMessageId.A_PET_CANNOT_BE_RESURRECTED_WHILE_IT_S_OWNER_IS_IN_THE_PROCESS_OF_RESURRECTING); // A pet cannot be resurrected while it's owner is in the process of resurrecting. } else { reviver.sendPacket(SystemMessageId.WHILE_A_PET_IS_BEING_RESURRECTED_IT_CANNOT_HELP_IN_RESURRECTING_ITS_MASTER); // While a pet is attempting to resurrect, it cannot help in resurrecting its master. } } return; } final L2Summon pet = getPet(); if ((Pet && (pet != null) && pet.isDead()) || (!Pet && isDead())) { _reviveRequested = 1; _revivePower = Formulas.calculateSkillResurrectRestorePercent(power, reviver); _revivePet = Pet; if (hasCharmOfCourage()) { final ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.YOUR_CHARM_OF_COURAGE_IS_TRYING_TO_RESURRECT_YOU_WOULD_YOU_LIKE_TO_RESURRECT_NOW.getId()); dlg.addTime(60000); sendPacket(dlg); return; } final long restoreExp = Math.round(((getExpBeforeDeath() - getExp()) * _revivePower) / 100); ConfirmDlg dlg = new ConfirmDlg(SystemMessageId.C1_IS_ATTEMPTING_TO_DO_A_RESURRECTION_THAT_RESTORES_S2_S3_XP_ACCEPT.getId()); dlg.addPcName(reviver); dlg.addLong(restoreExp); dlg.addInt(power); sendPacket(dlg); } } public void reviveAnswer(int answer) { final L2Summon pet = getPet(); if ((_reviveRequested != 1) || (!isDead() && !_revivePet) || (_revivePet && (pet != null) && !pet.isDead())) { return; } if (answer == 1) { if (!_revivePet) { if (_revivePower != 0) { doRevive(_revivePower); } else { doRevive(); } } else if (pet != null) { if (_revivePower != 0) { pet.doRevive(_revivePower); } else { pet.doRevive(); } } } _reviveRequested = 0; _revivePower = 0; } public boolean isReviveRequested() { return (_reviveRequested == 1); } public boolean isRevivingPet() { return _revivePet; } public void removeReviving() { _reviveRequested = 0; _revivePower = 0; } public void onActionRequest() { if (isSpawnProtected()) { sendPacket(SystemMessageId.YOU_ARE_NO_LONGER_PROTECTED_FROM_AGGRESSIVE_MONSTERS); if (Config.RESTORE_SERVITOR_ON_RECONNECT && !hasSummon() && CharSummonTable.getInstance().getServitors().containsKey(getObjectId())) { CharSummonTable.getInstance().restoreServitor(this); } if (Config.RESTORE_PET_ON_RECONNECT && !hasSummon() && CharSummonTable.getInstance().getPets().containsKey(getObjectId())) { CharSummonTable.getInstance().restorePet(this); } } if (isTeleportProtected()) { sendMessage("Teleport spawn protection ended."); } setProtection(false); setTeleportProtection(false); } /** * Expertise of the L2PcInstance (None=0, D=1, C=2, B=3, A=4, S=5, S80=6, S84=7) * @return int Expertise skill level. */ public int getExpertiseLevel() { int level = getSkillLevel(239); if (level < 0) { level = 0; } return level; } @Override public void teleToLocation(ILocational loc, boolean allowRandomOffset) { if ((getVehicle() != null) && !getVehicle().isTeleporting()) { setVehicle(null); } if (isFlyingMounted() && (loc.getZ() < -1005)) { super.teleToLocation(loc.getX(), loc.getY(), -1005, loc.getHeading(), loc.getInstanceId()); } super.teleToLocation(loc, allowRandomOffset); } @Override public final void onTeleported() { super.onTeleported(); if (isInAirShip()) { getAirShip().sendInfo(this); } // Force a revalidation revalidateZone(true); checkItemRestriction(); if ((Config.PLAYER_TELEPORT_PROTECTION > 0) && !isInOlympiadMode()) { setTeleportProtection(true); } // Trained beast is lost after teleport if (getTrainedBeasts() != null) { for (L2TamedBeastInstance tamedBeast : getTrainedBeasts()) { tamedBeast.deleteMe(); } getTrainedBeasts().clear(); } // Modify the position of the pet if necessary final L2Summon pet = getPet(); if (pet != null) { pet.setFollowStatus(false); pet.teleToLocation(getLocation(), false); ((L2SummonAI) pet.getAI()).setStartFollowController(true); pet.setFollowStatus(true); pet.updateAndBroadcastStatus(0); } getServitors().values().forEach(s -> { s.setFollowStatus(false); s.teleToLocation(getLocation(), false); ((L2SummonAI) s.getAI()).setStartFollowController(true); s.setFollowStatus(true); s.updateAndBroadcastStatus(0); }); TvTEvent.onTeleported(this); } @Override public void setIsTeleporting(boolean teleport) { setIsTeleporting(teleport, true); } public void setIsTeleporting(boolean teleport, boolean useWatchDog) { super.setIsTeleporting(teleport); if (!useWatchDog) { return; } if (teleport) { if ((_teleportWatchdog == null) && (Config.TELEPORT_WATCHDOG_TIMEOUT > 0)) { synchronized (this) { if (_teleportWatchdog == null) { _teleportWatchdog = ThreadPoolManager.getInstance().scheduleGeneral(new TeleportWatchdogTask(this), Config.TELEPORT_WATCHDOG_TIMEOUT * 1000); } } } } else { if (_teleportWatchdog != null) { _teleportWatchdog.cancel(false); _teleportWatchdog = null; } } } public void setLastServerPosition(int x, int y, int z) { _lastServerPosition.setXYZ(x, y, z); } public Location getLastServerPosition() { return _lastServerPosition; } public int getLastServerDistance(int x, int y, int z) { return (int) Util.calculateDistance(x, y, z, _lastServerPosition.getX(), _lastServerPosition.getY(), _lastServerPosition.getZ(), true, false); } @Override public void addExpAndSp(long addToExp, long addToSp) { getStat().addExpAndSp(addToExp, addToSp, false); } public void addExpAndSp(long addToExp, long addToSp, boolean useVitality) { getStat().addExpAndSp(addToExp, addToSp, useVitality); } public void removeExpAndSp(long removeExp, long removeSp) { getStat().removeExpAndSp(removeExp, removeSp, true); } public void removeExpAndSp(long removeExp, long removeSp, boolean sendMessage) { getStat().removeExpAndSp(removeExp, removeSp, sendMessage); } @Override public void reduceCurrentHp(double value, L2Character attacker, boolean awake, boolean isDOT, Skill skill) { if (skill != null) { getStatus().reduceHp(value, attacker, awake, isDOT, skill.isToggle(), skill.getDmgDirectlyToHP()); } else { getStatus().reduceHp(value, attacker, awake, isDOT, false, false); } // notify the tamed beast of attacks if (getTrainedBeasts() != null) { for (L2TamedBeastInstance tamedBeast : getTrainedBeasts()) { tamedBeast.onOwnerGotAttacked(attacker); } } } public void broadcastSnoop(ChatType type, String name, String _text) { if (!_snoopListener.isEmpty()) { final Snoop sn = new Snoop(getObjectId(), getName(), type, name, _text); for (L2PcInstance pci : _snoopListener) { if (pci != null) { pci.sendPacket(sn); } } } } public void addSnooper(L2PcInstance pci) { if (!_snoopListener.contains(pci)) { _snoopListener.add(pci); } } public void removeSnooper(L2PcInstance pci) { _snoopListener.remove(pci); } public void addSnooped(L2PcInstance pci) { if (!_snoopedPlayer.contains(pci)) { _snoopedPlayer.add(pci); } } public void removeSnooped(L2PcInstance pci) { _snoopedPlayer.remove(pci); } public void addHtmlAction(HtmlActionScope scope, String action) { _htmlActionCaches[scope.ordinal()].add(action); } public void clearHtmlActions(HtmlActionScope scope) { _htmlActionCaches[scope.ordinal()].clear(); } public void setHtmlActionOriginObjectId(HtmlActionScope scope, int npcObjId) { if (npcObjId < 0) { throw new IllegalArgumentException(); } _htmlActionOriginObjectIds[scope.ordinal()] = npcObjId; } public int getLastHtmlActionOriginId() { return _lastHtmlActionOriginObjId; } private boolean validateHtmlAction(Iterable<String> actionIter, String action) { for (String cachedAction : actionIter) { if (cachedAction.charAt(cachedAction.length() - 1) == AbstractHtmlPacket.VAR_PARAM_START_CHAR) { if (action.startsWith(cachedAction.substring(0, cachedAction.length() - 1).trim())) { return true; } } else if (cachedAction.equals(action)) { return true; } } return false; } /** * Check if the HTML action was sent in a HTML packet.<br> * If the HTML action was not sent for whatever reason, -1 is returned.<br> * Otherwise, the NPC object ID or 0 is returned.<br> * 0 means the HTML action was not bound to an NPC<br> * and no range checks need to be made. * @param action the HTML action to check * @return NPC object ID, 0 or -1 */ public int validateHtmlAction(String action) { for (int i = 0; i < _htmlActionCaches.length; ++i) { if (validateHtmlAction(_htmlActionCaches[i], action)) { _lastHtmlActionOriginObjId = _htmlActionOriginObjectIds[i]; return _lastHtmlActionOriginObjId; } } return -1; } /** * Performs following tests: * <ul> * <li>Inventory contains item</li> * <li>Item owner id == owner id</li> * <li>It isnt pet control item while mounting pet or pet summoned</li> * <li>It isnt active enchant item</li> * <li>It isnt cursed weapon/item</li> * <li>It isnt wear item</li> * </ul> * @param objectId item object id * @param action just for login porpouse * @return */ public boolean validateItemManipulation(int objectId, String action) { L2ItemInstance item = getInventory().getItemByObjectId(objectId); if ((item == null) || (item.getOwnerId() != getObjectId())) { _log.finest(getObjectId() + ": player tried to " + action + " item he is not owner of"); return false; } // Pet is summoned and not the item that summoned the pet AND not the buggle from strider you're mounting final L2Summon pet = getPet(); if (((pet != null) && (pet.getControlObjectId() == objectId)) || (getMountObjectID() == objectId)) { if (Config.DEBUG) { _log.finest(getObjectId() + ": player tried to " + action + " item controling pet"); } return false; } if (getActiveEnchantItemId() == objectId) { if (Config.DEBUG) { _log.finest(getObjectId() + ":player tried to " + action + " an enchant scroll he was using"); } return false; } if (CursedWeaponsManager.getInstance().isCursed(item.getId())) { // can not trade a cursed weapon return false; } return true; } /** * @return Returns the inBoat. */ public boolean isInBoat() { return (_vehicle != null) && _vehicle.isBoat(); } /** * @return */ public L2BoatInstance getBoat() { return (L2BoatInstance) _vehicle; } /** * @return Returns the inAirShip. */ public boolean isInAirShip() { return (_vehicle != null) && _vehicle.isAirShip(); } /** * @return */ public L2AirShipInstance getAirShip() { return (L2AirShipInstance) _vehicle; } public boolean isInShuttle() { return _vehicle instanceof L2ShuttleInstance; } public L2ShuttleInstance getShuttle() { return (L2ShuttleInstance) _vehicle; } public L2Vehicle getVehicle() { return _vehicle; } public void setVehicle(L2Vehicle v) { if ((v == null) && (_vehicle != null)) { _vehicle.removePassenger(this); } _vehicle = v; } public boolean isInVehicle() { return _vehicle != null; } public void setInCrystallize(boolean inCrystallize) { _inCrystallize = inCrystallize; } public boolean isInCrystallize() { return _inCrystallize; } /** * @return */ public Location getInVehiclePosition() { return _inVehiclePosition; } public void setInVehiclePosition(Location pt) { _inVehiclePosition = pt; } /** * Manage the delete task of a L2PcInstance (Leave Party, Unsummon pet, Save its inventory in the database, Remove it from the world...).<br> * <B><U>Actions</U>:</B> * <ul> * <li>If the L2PcInstance is in observer mode, set its position to its position before entering in observer mode</li> * <li>Set the online Flag to True or False and update the characters table of the database with online status and lastAccess</li> * <li>Stop the HP/MP/CP Regeneration task</li> * <li>Cancel Crafting, Attak or Cast</li> * <li>Remove the L2PcInstance from the world</li> * <li>Stop Party and Unsummon Pet</li> * <li>Update database with items in its inventory and remove them from the world</li> * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI</li> * <li>Close the connection with the client</li> * </ul> */ @Override public boolean deleteMe() { cleanup(); storeMe(); return super.deleteMe(); } private synchronized void cleanup() { EventDispatcher.getInstance().notifyEventAsync(new OnPlayerLogout(this), this); for (Friend friend : _friendList.values()) { L2PcInstance player = friend.getFriend(); if (player == null) { continue; } player.putFriendDetailInfo(this); } try { for (L2ZoneType zone : ZoneManager.getInstance().getZones(this)) { zone.onPlayerLogoutInside(this); } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // 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) try { if (!isOnline()) { _log.log(Level.SEVERE, "deleteMe() called on offline character " + this, new RuntimeException()); } setOnlineStatus(false, true); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } try { if (Config.ENABLE_BLOCK_CHECKER_EVENT && (getBlockCheckerArena() != -1)) { HandysBlockCheckerManager.getInstance().onDisconnect(this); } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } try { _isOnline = false; abortAttack(); abortCast(); stopMove(null); setDebug(null); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // remove combat flag try { if (getInventory().getItemByItemId(9819) != null) { Fort fort = FortManager.getInstance().getFort(this); if (fort != null) { FortSiegeManager.getInstance().dropCombatFlag(this, fort.getResidenceId()); } else { int slot = getInventory().getSlotFromItem(getInventory().getItemByItemId(9819)); getInventory().unEquipItemInBodySlot(slot); destroyItem("CombatFlag", getInventory().getItemByItemId(9819), null, true); } } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } try { PartyMatchWaitingList.getInstance().removePlayer(this); if (_partyroom != 0) { PartyMatchRoom room = PartyMatchRoomList.getInstance().getRoom(_partyroom); if (room != null) { room.deleteMember(this); } } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } try { if (isFlying()) { removeSkill(SkillData.getInstance().getSkill(4289, 1)); } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // Recommendations must be saved before task (timer) is canceled try { storeRecommendations(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // Stop the HP/MP/CP Regeneration task (scheduled tasks) try { stopAllTimers(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } try { setIsTeleporting(false); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // Stop crafting, if in progress try { RecipeController.getInstance().requestMakeItemAbort(this); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // Cancel Attak or Cast try { setTarget(null); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } if (isChannelized()) { getSkillChannelized().abortChannelization(); } // Stop all toggles. getEffectList().stopAllToggles(); // Remove from world regions zones final L2WorldRegion oldRegion = getWorldRegion(); if (oldRegion != null) { oldRegion.removeFromZones(this); } // Remove the L2PcInstance from the world try { decayMe(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // If a Party is in progress, leave it (and festival party) if (isInParty()) { try { leaveParty(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } } if (OlympiadManager.getInstance().isRegistered(this) || (getOlympiadGameId() != -1)) { OlympiadManager.getInstance().removeDisconnectedCompetitor(this); } // If the L2PcInstance has Pet, unsummon it if (hasSummon()) { try { L2Summon pet = getPet(); if (pet != null) { pet.setRestoreSummon(true); pet.unSummon(this); // Dead pet wasn't unsummoned, broadcast npcinfo changes (pet will be without owner name - means owner offline) pet = getPet(); if (pet != null) { pet.broadcastNpcInfo(0); } } getServitors().values().forEach(s -> { s.setRestoreSummon(true); s.unSummon(this); }); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); }// returns pet to control item } if (getClan() != null) { // set the status for pledge member list to OFFLINE try { L2ClanMember clanMember = getClan().getClanMember(getObjectId()); if (clanMember != null) { clanMember.setPlayerInstance(null); } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } } if (getActiveRequester() != null) { // deals with sudden exit in the middle of transaction setActiveRequester(null); cancelActiveTrade(); } // If the L2PcInstance is a GM, remove it from the GM List if (isGM()) { try { AdminData.getInstance().deleteGm(this); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } } try { // Check if the L2PcInstance is in observer mode to set its position to its position // before entering in observer mode if (inObserverMode()) { setLocationInvisible(_lastLoc); } if (getVehicle() != null) { getVehicle().oustPlayer(this); } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // remove player from instance and set spawn location if any try { final int instanceId = getInstanceId(); if ((instanceId != 0) && !Config.RESTORE_PLAYER_INSTANCE) { final Instance inst = InstanceManager.getInstance().getInstance(instanceId); if (inst != null) { inst.removePlayer(getObjectId()); final Location loc = inst.getSpawnLoc(); if (loc != null) { final int x = loc.getX() + Rnd.get(-30, 30); final int y = loc.getY() + Rnd.get(-30, 30); setXYZInvisible(x, y, loc.getZ()); final L2Summon pet = getPet(); if (pet != null) // dead pet { pet.teleToLocation(loc, true); pet.setInstanceId(0); } } } } } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // TvT Event removal try { TvTEvent.onLogout(this); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // Update database with items in its inventory and remove them from the world try { getInventory().deleteMe(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } // Update database with items in its warehouse and remove them from the world try { clearWarehouse(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } if (Config.WAREHOUSE_CACHE) { WarehouseCacheManager.getInstance().remCacheTask(this); } try { getFreight().deleteMe(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } try { clearRefund(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } if (isCursedWeaponEquipped()) { try { CursedWeaponsManager.getInstance().getCursedWeapon(_cursedWeaponEquippedId).setPlayer(null); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } } // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attak or Cast and notify AI try { getKnownList().removeAllKnownObjects(); } catch (Exception e) { _log.log(Level.SEVERE, "deleteMe()", e); } if (getClanId() > 0) { getClan().broadcastToOtherOnlineMembers(new PledgeShowMemberListUpdate(this), this); getClan().broadcastToOnlineMembers(new ExPledgeCount(getClan())); // ClanTable.getInstance().getClan(getClanId()).broadcastToOnlineMembers(new PledgeShowMemberListAdd(this)); } for (L2PcInstance player : _snoopedPlayer) { player.removeSnooper(this); } for (L2PcInstance player : _snoopListener) { player.removeSnooped(this); } if (isMentee()) { // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMenteeStatus(this, false), this); } else if (isMentor()) { // Notify to scripts EventDispatcher.getInstance().notifyEventAsync(new OnPlayerMentorStatus(this, false), this); } // Remove L2Object object from _allObjects of L2World L2World.getInstance().removeObject(this); L2World.getInstance().removeFromAllPlayers(this); // force remove in case of crash during teleport try { notifyFriends(L2FriendStatus.MODE_OFFLINE); getBlockList().playerLogout(); } catch (Exception e) { _log.log(Level.WARNING, "Exception on deleteMe() notifyFriends: " + e.getMessage(), e); } } private L2Fish _fish; // startFishing() was stripped of any pre-fishing related checks, namely the fishing zone check. // Also worthy of note is the fact the code to find the hook landing position was also striped. // The stripped code was moved into fishing.java. // In my opinion it makes more sense for it to be there since all other skill related checks were also there. // Last but not least, moving the zone check there, fixed a bug where baits would always be consumed no matter if fishing actualy took place. // startFishing() now takes up 3 arguments, wich are acurately described as being the hook landing coordinates. public void startFishing(int _x, int _y, int _z) { stopMove(null); setIsImmobilized(true); _fishing = true; _fishx = _x; _fishy = _y; _fishz = _z; // broadcastUserInfo(); // Starts fishing int lvl = getRandomFishLvl(); int grade = getRandomFishGrade(); int group = getRandomFishGroup(grade); List<L2Fish> fish = FishData.getInstance().getFish(lvl, group, grade); if ((fish == null) || fish.isEmpty()) { sendMessage("Error - Fish are not defined"); endFishing(false); return; } // Use a copy constructor else the fish data may be over-written below _fish = fish.get(Rnd.get(fish.size())).clone(); fish.clear(); sendPacket(SystemMessageId.YOU_CAST_YOUR_LINE_AND_START_TO_FISH); if (!GameTimeController.getInstance().isNight() && _lure.isNightLure()) { _fish.setFishGroup(-1); } // sendMessage("Hook x,y: " + _x + "," + _y + " - Water Z, Player Z:" + _z + ", " + getZ()); //debug line, uncoment to show coordinates used in fishing. broadcastPacket(new ExFishingStart(this, _fish.getFishGroup(), _x, _y, _z, _lure.isNightLure())); sendPacket(new PlaySound(1, "SF_P_01", 0, 0, 0, 0, 0)); startLookingForFishTask(); } public void stopLookingForFishTask() { if (_taskforfish != null) { _taskforfish.cancel(false); _taskforfish = null; } } public void startLookingForFishTask() { if (!isDead() && (_taskforfish == null)) { int checkDelay = 0; boolean isNoob = false; boolean isUpperGrade = false; if (_lure != null) { int lureid = _lure.getId(); isNoob = _fish.getFishGrade() == 0; isUpperGrade = _fish.getFishGrade() == 2; if ((lureid == 6519) || (lureid == 6522) || (lureid == 6525) || (lureid == 8505) || (lureid == 8508) || (lureid == 8511)) { checkDelay = _fish.getGutsCheckTime() * 133; } else if ((lureid == 6520) || (lureid == 6523) || (lureid == 6526) || ((lureid >= 8505) && (lureid <= 8513)) || ((lureid >= 7610) && (lureid <= 7613)) || ((lureid >= 7807) && (lureid <= 7809)) || ((lureid >= 8484) && (lureid <= 8486))) { checkDelay = _fish.getGutsCheckTime() * 100; } else if ((lureid == 6521) || (lureid == 6524) || (lureid == 6527) || (lureid == 8507) || (lureid == 8510) || (lureid == 8513)) { checkDelay = _fish.getGutsCheckTime() * 66; } } _taskforfish = ThreadPoolManager.getInstance().scheduleEffectAtFixedRate(new LookingForFishTask(this, _fish.getStartCombatTime(), _fish.getFishGuts(), _fish.getFishGroup(), isNoob, isUpperGrade), 10000, checkDelay); } } private int getRandomFishGrade() { switch (_lure.getId()) { case 7807: // green for beginners case 7808: // purple for beginners case 7809: // yellow for beginners case 8486: // prize-winning for beginners return 0; case 8485: // prize-winning luminous case 8506: // green luminous case 8509: // purple luminous case 8512: // yellow luminous return 2; default: return 1; } } private int getRandomFishGroup(int group) { int check = Rnd.get(100); int type = 1; switch (group) { case 0: // fish for novices switch (_lure.getId()) { case 7807: // green lure, preferred by fast-moving (nimble) fish (type 5) if (check <= 54) { type = 5; } else if (check <= 77) { type = 4; } else { type = 6; } break; case 7808: // purple lure, preferred by fat fish (type 4) if (check <= 54) { type = 4; } else if (check <= 77) { type = 6; } else { type = 5; } break; case 7809: // yellow lure, preferred by ugly fish (type 6) if (check <= 54) { type = 6; } else if (check <= 77) { type = 5; } else { type = 4; } break; case 8486: // prize-winning fishing lure for beginners if (check <= 33) { type = 4; } else if (check <= 66) { type = 5; } else { type = 6; } break; } break; case 1: // normal fish switch (_lure.getId()) { case 7610: case 7611: case 7612: case 7613: type = 3; break; case 6519: // all theese lures (green) are prefered by fast-moving (nimble) fish (type 1) case 8505: case 6520: case 6521: case 8507: if (check <= 54) { type = 1; } else if (check <= 74) { type = 0; } else if (check <= 94) { type = 2; } else { type = 3; } break; case 6522: // all theese lures (purple) are prefered by fat fish (type 0) case 8508: case 6523: case 6524: case 8510: if (check <= 54) { type = 0; } else if (check <= 74) { type = 1; } else if (check <= 94) { type = 2; } else { type = 3; } break; case 6525: // all theese lures (yellow) are prefered by ugly fish (type 2) case 8511: case 6526: case 6527: case 8513: if (check <= 55) { type = 2; } else if (check <= 74) { type = 1; } else if (check <= 94) { type = 0; } else { type = 3; } break; case 8484: // prize-winning fishing lure if (check <= 33) { type = 0; } else if (check <= 66) { type = 1; } else { type = 2; } break; } break; case 2: // upper grade fish, luminous lure switch (_lure.getId()) { case 8506: // green lure, preferred by fast-moving (nimble) fish (type 8) if (check <= 54) { type = 8; } else if (check <= 77) { type = 7; } else { type = 9; } break; case 8509: // purple lure, preferred by fat fish (type 7) if (check <= 54) { type = 7; } else if (check <= 77) { type = 9; } else { type = 8; } break; case 8512: // yellow lure, preferred by ugly fish (type 9) if (check <= 54) { type = 9; } else if (check <= 77) { type = 8; } else { type = 7; } break; case 8485: // prize-winning fishing lure if (check <= 33) { type = 7; } else if (check <= 66) { type = 8; } else { type = 9; } break; } } return type; } private int getRandomFishLvl() { int skilllvl = getSkillLevel(1315); final BuffInfo info = getEffectList().getBuffInfoBySkillId(2274); if (info != null) { skilllvl = (int) info.getSkill().getPower(); } if (skilllvl <= 0) { return 1; } int randomlvl; int check = Rnd.get(100); if (check <= 50) { randomlvl = skilllvl; } else if (check <= 85) { randomlvl = skilllvl - 1; if (randomlvl <= 0) { randomlvl = 1; } } else { randomlvl = skilllvl + 1; if (randomlvl > 27) { randomlvl = 27; } } return randomlvl; } public void startFishCombat(boolean isNoob, boolean isUpperGrade) { _fishCombat = new L2Fishing(this, _fish, isNoob, isUpperGrade, _lure.getId()); } public void endFishing(boolean win) { _fishing = false; _fishx = 0; _fishy = 0; _fishz = 0; // broadcastUserInfo(); if (_fishCombat == null) { sendPacket(SystemMessageId.THE_BAIT_HAS_BEEN_LOST_BECAUSE_THE_FISH_GOT_AWAY); } _fishCombat = null; _lure = null; // Ends fishing broadcastPacket(new ExFishingEnd(win, this)); sendPacket(SystemMessageId.YOU_REEL_YOUR_LINE_IN_AND_STOP_FISHING); setIsImmobilized(false); stopLookingForFishTask(); } public L2Fishing getFishCombat() { return _fishCombat; } public int getFishx() { return _fishx; } public int getFishy() { return _fishy; } public int getFishz() { return _fishz; } public void setLure(L2ItemInstance lure) { _lure = lure; } public L2ItemInstance getLure() { return _lure; } public int getInventoryLimit() { int ivlim; if (isGM()) { ivlim = Config.INVENTORY_MAXIMUM_GM; } else if (getRace() == Race.DWARF) { ivlim = Config.INVENTORY_MAXIMUM_DWARF; } else { ivlim = Config.INVENTORY_MAXIMUM_NO_DWARF; } ivlim += (int) getStat().calcStat(Stats.INV_LIM, 0, null, null); return ivlim; } public int getWareHouseLimit() { int whlim; if (getRace() == Race.DWARF) { whlim = Config.WAREHOUSE_SLOTS_DWARF; } else { whlim = Config.WAREHOUSE_SLOTS_NO_DWARF; } whlim += (int) getStat().calcStat(Stats.WH_LIM, 0, null, null); return whlim; } public int getPrivateSellStoreLimit() { int pslim; if (getRace() == Race.DWARF) { pslim = Config.MAX_PVTSTORESELL_SLOTS_DWARF; } else { pslim = Config.MAX_PVTSTORESELL_SLOTS_OTHER; } pslim += (int) getStat().calcStat(Stats.P_SELL_LIM, 0, null, null); return pslim; } public int getPrivateBuyStoreLimit() { int pblim; if (getRace() == Race.DWARF) { pblim = Config.MAX_PVTSTOREBUY_SLOTS_DWARF; } else { pblim = Config.MAX_PVTSTOREBUY_SLOTS_OTHER; } pblim += (int) getStat().calcStat(Stats.P_BUY_LIM, 0, null, null); return pblim; } public int getDwarfRecipeLimit() { int recdlim = Config.DWARF_RECIPE_LIMIT; recdlim += (int) getStat().calcStat(Stats.REC_D_LIM, 0, null, null); return recdlim; } public int getCommonRecipeLimit() { int recclim = Config.COMMON_RECIPE_LIMIT; recclim += (int) getStat().calcStat(Stats.REC_C_LIM, 0, null, null); return recclim; } /** * @return Returns the mountNpcId. */ public int getMountNpcId() { return _mountNpcId; } /** * @return Returns the mountLevel. */ public int getMountLevel() { return _mountLevel; } public void setMountObjectID(int newID) { _mountObjectID = newID; } public int getMountObjectID() { return _mountObjectID; } /** * @return the current skill in use or return null. */ public SkillUseHolder getCurrentSkill() { return _currentSkill; } /** * Create a new SkillDat object and set the player _currentSkill. * @param currentSkill * @param ctrlPressed * @param shiftPressed */ public void setCurrentSkill(Skill currentSkill, boolean ctrlPressed, boolean shiftPressed) { if (currentSkill == null) { _currentSkill = null; return; } _currentSkill = new SkillUseHolder(currentSkill, ctrlPressed, shiftPressed); } /** * @return the current pet skill in use or return null. */ public SkillUseHolder getCurrentPetSkill() { return _currentPetSkill; } /** * Create a new SkillDat object and set the player _currentPetSkill. * @param currentSkill * @param ctrlPressed * @param shiftPressed */ public void setCurrentPetSkill(Skill currentSkill, boolean ctrlPressed, boolean shiftPressed) { if (currentSkill == null) { _currentPetSkill = null; return; } _currentPetSkill = new SkillUseHolder(currentSkill, ctrlPressed, shiftPressed); } public SkillUseHolder getQueuedSkill() { return _queuedSkill; } /** * Create a new SkillDat object and queue it in the player _queuedSkill. * @param queuedSkill * @param ctrlPressed * @param shiftPressed */ public void setQueuedSkill(Skill queuedSkill, boolean ctrlPressed, boolean shiftPressed) { if (queuedSkill == null) { _queuedSkill = null; return; } _queuedSkill = new SkillUseHolder(queuedSkill, ctrlPressed, shiftPressed); } /** * @return {@code true} if player is jailed, {@code false} otherwise. */ public boolean isJailed() { return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.JAIL) || PunishmentManager.getInstance().hasPunishment(getAccountName(), PunishmentAffect.ACCOUNT, PunishmentType.JAIL) || PunishmentManager.getInstance().hasPunishment(getIPAddress(), PunishmentAffect.IP, PunishmentType.JAIL); } /** * @return {@code true} if player is chat banned, {@code false} otherwise. */ public boolean isChatBanned() { return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.CHAT_BAN) || PunishmentManager.getInstance().hasPunishment(getAccountName(), PunishmentAffect.ACCOUNT, PunishmentType.CHAT_BAN) || PunishmentManager.getInstance().hasPunishment(getIPAddress(), PunishmentAffect.IP, PunishmentType.CHAT_BAN); } public void startFameTask(long delay, int fameFixRate) { if ((getLevel() < 40) || (getClassId().level() < 2)) { return; } if (_fameTask == null) { _fameTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new FameTask(this, fameFixRate), delay, delay); } } public void stopFameTask() { if (_fameTask != null) { _fameTask.cancel(false); _fameTask = null; } } public int getPowerGrade() { return _powerGrade; } public void setPowerGrade(int power) { _powerGrade = power; } public boolean isCursedWeaponEquipped() { return _cursedWeaponEquippedId != 0; } public void setCursedWeaponEquippedId(int value) { _cursedWeaponEquippedId = value; } public int getCursedWeaponEquippedId() { return _cursedWeaponEquippedId; } public boolean isCombatFlagEquipped() { return _combatFlagEquippedId; } public void setCombatFlagEquipped(boolean value) { _combatFlagEquippedId = value; } /** * Returns the Number of Souls this L2PcInstance got. * @return */ public int getChargedSouls() { return _souls; } /** * Increase Souls * @param count */ public void increaseSouls(int count) { _souls += count; SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOUR_SOUL_COUNT_HAS_INCREASED_BY_S1_IT_IS_NOW_AT_S2); sm.addInt(count); sm.addInt(_souls); sendPacket(sm); restartSoulTask(); sendPacket(new EtcStatusUpdate(this)); } /** * Decreases existing Souls. * @param count * @param skill * @return */ public boolean decreaseSouls(int count, Skill skill) { _souls -= count; if (getChargedSouls() < 0) { _souls = 0; } if (getChargedSouls() == 0) { stopSoulTask(); } else { restartSoulTask(); } sendPacket(new EtcStatusUpdate(this)); return true; } /** * Clear out all Souls from this L2PcInstance */ public void clearSouls() { _souls = 0; stopSoulTask(); sendPacket(new EtcStatusUpdate(this)); } /** * Starts/Restarts the SoulTask to Clear Souls after 10 Mins. */ private void restartSoulTask() { if (_soulTask != null) { _soulTask.cancel(false); _soulTask = null; } _soulTask = ThreadPoolManager.getInstance().scheduleGeneral(new ResetSoulsTask(this), 600000); } /** * Stops the Clearing Task. */ public void stopSoulTask() { if (_soulTask != null) { _soulTask.cancel(false); _soulTask = null; } } public int getShilensBreathDebuffLevel() { final BuffInfo buff = getEffectList().getBuffInfoBySkillId(CommonSkill.SHILENS_BREATH.getId()); return buff == null ? 0 : buff.getSkill().getLevel(); } public void calculateShilensBreathDebuffLevel(L2Character killer) { if (killer == null) { _log.warning(this + " called calculateShilensBreathDebuffLevel with killer null!"); return; } if (isResurrectSpecialAffected() || isLucky() || isBlockedFromDeathPenalty() || isInsideZone(ZoneId.PVP) || isInsideZone(ZoneId.SIEGE) || canOverrideCond(PcCondOverride.DEATH_PENALTY)) { return; } double percent = 1.0; if (killer.isRaid()) { percent *= calcStat(Stats.REDUCE_DEATH_PENALTY_BY_RAID, 1); } else if (killer.isMonster()) { percent *= calcStat(Stats.REDUCE_DEATH_PENALTY_BY_MOB, 1); } else if (killer.isPlayable()) { percent *= calcStat(Stats.REDUCE_DEATH_PENALTY_BY_PVP, 1); } if (killer.isInCategory(CategoryType.SHILENS_FOLLOWERS) || (Rnd.get(1, 100) <= ((Config.DEATH_PENALTY_CHANCE) * percent))) { if (!killer.isPlayable() || (getKarma() > 0)) { increaseShilensBreathDebuff(); } } } public void increaseShilensBreathDebuff() { int nextLv = getShilensBreathDebuffLevel() + 1; if (nextLv > 5) { nextLv = 5; } final Skill skill = SkillData.getInstance().getSkill(CommonSkill.SHILENS_BREATH.getId(), nextLv); if (skill != null) { skill.applyEffects(this, this); sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_VE_BEEN_AFFLICTED_BY_SHILEN_S_BREATH_LEVEL_S1).addInt(nextLv)); } } public void decreaseShilensBreathDebuff() { final int nextLv = getShilensBreathDebuffLevel() - 1; if (nextLv > 0) { final Skill skill = SkillData.getInstance().getSkill(CommonSkill.SHILENS_BREATH.getId(), nextLv); skill.applyEffects(this, this); sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_VE_BEEN_AFFLICTED_BY_SHILEN_S_BREATH_LEVEL_S1).addInt(nextLv)); } else { sendPacket(SystemMessageId.SHILEN_S_BREATH_HAS_BEEN_PURIFIED); } } public void setShilensBreathDebuffLevel(int level) { if (level > 0) { final Skill skill = SkillData.getInstance().getSkill(CommonSkill.SHILENS_BREATH.getId(), level); skill.applyEffects(this, this); sendPacket(SystemMessage.getSystemMessage(SystemMessageId.YOU_VE_BEEN_AFFLICTED_BY_SHILEN_S_BREATH_LEVEL_S1).addInt(level)); } } @Override public L2PcInstance getActingPlayer() { return this; } @Override public final void sendDamageMessage(L2Character target, int damage, boolean mcrit, boolean pcrit, boolean miss) { // Check if hit is missed if (miss) { if (target.isPlayer()) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_EVADED_C2_S_ATTACK); sm.addPcName(target.getActingPlayer()); sm.addCharName(this); target.sendPacket(sm); } SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_S_ATTACK_WENT_ASTRAY); sm.addPcName(this); sendPacket(sm); sendPacket(new ExMagicAttackInfo(getObjectId(), target.getObjectId(), ExMagicAttackInfo.EVADED)); return; } // Check if hit is critical if (pcrit) { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_LANDED_A_CRITICAL_HIT); sm.addPcName(this); sendPacket(sm); sendPacket(new ExMagicAttackInfo(getObjectId(), target.getObjectId(), ExMagicAttackInfo.CRITICAL)); } if (mcrit) { sendPacket(SystemMessageId.M_CRITICAL); sendPacket(new ExMagicAttackInfo(getObjectId(), target.getObjectId(), ExMagicAttackInfo.CRITICAL)); } if (isInOlympiadMode() && target.isPlayer() && target.getActingPlayer().isInOlympiadMode() && (target.getActingPlayer().getOlympiadGameId() == getOlympiadGameId())) { OlympiadGameManager.getInstance().notifyCompetitorDamage(this, damage); } final SystemMessage sm; if (target.isInvul() && !target.isNpc()) { sm = SystemMessage.getSystemMessage(SystemMessageId.THE_ATTACK_HAS_BEEN_BLOCKED); } else if (target.isDoor() || (target instanceof L2ControlTowerInstance)) { sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_HIT_FOR_S1_DAMAGE); sm.addInt(damage); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.C1_HAS_INFLICTED_S3_DAMAGE_ON_C2_S4); sm.addPcName(this); sm.addCharName(target); sm.addInt(damage); sm.addPopup(target.getObjectId(), getObjectId(), (damage * -1)); } sendPacket(sm); } /** * @param npcId */ public void setAgathionId(int npcId) { _agathionId = npcId; } /** * @return */ public int getAgathionId() { return _agathionId; } public void setVitalityPoints(int points, boolean quiet) { getStat().setVitalityPoints(points, quiet); } public void updateVitalityPoints(int points, boolean useRates, boolean quiet) { getStat().updateVitalityPoints(points, useRates, quiet); } public void checkItemRestriction() { for (int i = 0; i < Inventory.PAPERDOLL_TOTALSLOTS; i++) { L2ItemInstance equippedItem = getInventory().getPaperdollItem(i); if ((equippedItem != null) && !equippedItem.getItem().checkCondition(this, this, false)) { getInventory().unEquipItemInSlot(i); InventoryUpdate iu = new InventoryUpdate(); iu.addModifiedItem(equippedItem); sendPacket(iu); SystemMessage sm = null; if (equippedItem.getItem().getBodyPart() == L2Item.SLOT_BACK) { sendPacket(SystemMessageId.YOUR_CLOAK_HAS_BEEN_UNEQUIPPED_BECAUSE_YOUR_ARMOR_SET_IS_NO_LONGER_COMPLETE); return; } if (equippedItem.getEnchantLevel() > 0) { sm = SystemMessage.getSystemMessage(SystemMessageId.THE_EQUIPMENT_S1_S2_HAS_BEEN_REMOVED); sm.addInt(equippedItem.getEnchantLevel()); sm.addItemName(equippedItem); } else { sm = SystemMessage.getSystemMessage(SystemMessageId.S1_HAS_BEEN_UNEQUIPPED); sm.addItemName(equippedItem); } sendPacket(sm); } } } public void addTransformSkill(int id) { if (_transformAllowedSkills == null) { synchronized (this) { if (_transformAllowedSkills == null) { _transformAllowedSkills = new HashSet<>(); } } } _transformAllowedSkills.add(id); } public boolean hasTransformSkill(int id) { return (_transformAllowedSkills != null) && _transformAllowedSkills.contains(id); } public synchronized void removeAllTransformSkills() { _transformAllowedSkills = null; } protected void startFeed(int npcId) { _canFeed = npcId > 0; if (!isMounted()) { return; } if (hasPet()) { final L2Summon pet = getPet(); setCurrentFeed(((L2PetInstance) pet).getCurrentFed()); _controlItemId = pet.getControlObjectId(); sendPacket(new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume())); if (!isDead()) { _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new PetFeedTask(this), 10000, 10000); } } else if (_canFeed) { setCurrentFeed(getMaxFeed()); SetupGauge sg = new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume()); sendPacket(sg); if (!isDead()) { _mountFeedTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new PetFeedTask(this), 10000, 10000); } } } public void stopFeed() { if (_mountFeedTask != null) { _mountFeedTask.cancel(false); _mountFeedTask = null; } } private final void clearPetData() { _data = null; } public final L2PetData getPetData(int npcId) { if (_data == null) { _data = PetDataTable.getInstance().getPetData(npcId); } return _data; } private final L2PetLevelData getPetLevelData(int npcId) { if (_leveldata == null) { _leveldata = PetDataTable.getInstance().getPetData(npcId).getPetLevelData(getMountLevel()); } return _leveldata; } public int getCurrentFeed() { return _curFeed; } public int getFeedConsume() { // if pet is attacking if (isAttackingNow()) { return getPetLevelData(_mountNpcId).getPetFeedBattle(); } return getPetLevelData(_mountNpcId).getPetFeedNormal(); } public void setCurrentFeed(int num) { boolean lastHungryState = isHungry(); _curFeed = num > getMaxFeed() ? getMaxFeed() : num; SetupGauge sg = new SetupGauge(3, (getCurrentFeed() * 10000) / getFeedConsume(), (getMaxFeed() * 10000) / getFeedConsume()); sendPacket(sg); // broadcast move speed change when strider becomes hungry / full if (lastHungryState != isHungry()) { broadcastUserInfo(); } } private int getMaxFeed() { return getPetLevelData(_mountNpcId).getPetMaxFeed(); } public boolean isHungry() { return _canFeed ? (getCurrentFeed() < ((getPetData(getMountNpcId()).getHungryLimit() / 100f) * getPetLevelData(getMountNpcId()).getPetMaxFeed())) : false; } public void enteredNoLanding(int delay) { _dismountTask = ThreadPoolManager.getInstance().scheduleGeneral(new DismountTask(this), delay * 1000); } public void exitedNoLanding() { if (_dismountTask != null) { _dismountTask.cancel(true); _dismountTask = null; } } public void storePetFood(int petId) { if ((_controlItemId != 0) && (petId != 0)) { String req; req = "UPDATE pets SET fed=? WHERE item_obj_id = ?"; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(req)) { statement.setInt(1, getCurrentFeed()); statement.setInt(2, _controlItemId); statement.executeUpdate(); _controlItemId = 0; } catch (Exception e) { _log.log(Level.SEVERE, "Failed to store Pet [NpcId: " + petId + "] data", e); } } } public void setIsInSiege(boolean b) { _isInSiege = b; } public boolean isInSiege() { return _isInSiege; } /** * @param isInHideoutSiege sets the value of {@link #_isInHideoutSiege}. */ public void setIsInHideoutSiege(boolean isInHideoutSiege) { _isInHideoutSiege = isInHideoutSiege; } /** * @return the value of {@link #_isInHideoutSiege}, {@code true} if the player is participing on a Hideout Siege, otherwise {@code false}. */ public boolean isInHideoutSiege() { return _isInHideoutSiege; } public FloodProtectors getFloodProtectors() { return getClient().getFloodProtectors(); } public boolean isFlyingMounted() { return (isTransformed() && (getTransformation().isFlying())); } /** * Returns the Number of Charges this L2PcInstance got. * @return */ public int getCharges() { return _charges.get(); } public void increaseCharges(int count, int max) { if (_charges.get() >= max) { sendPacket(SystemMessageId.YOUR_FORCE_HAS_REACHED_MAXIMUM_CAPACITY); return; } // Charge clear task should be reset every time a charge is increased. restartChargeTask(); if (_charges.addAndGet(count) >= max) { _charges.set(max); sendPacket(SystemMessageId.YOUR_FORCE_HAS_REACHED_MAXIMUM_CAPACITY); } else { SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOUR_FORCE_HAS_INCREASED_TO_LEVEL_S1); sm.addInt(_charges.get()); sendPacket(sm); } sendPacket(new EtcStatusUpdate(this)); } public boolean decreaseCharges(int count) { if (_charges.get() < count) { return false; } // Charge clear task should be reset every time a charge is decreased and stopped when charges become 0. if (_charges.addAndGet(-count) == 0) { stopChargeTask(); } else { restartChargeTask(); } sendPacket(new EtcStatusUpdate(this)); return true; } public void clearCharges() { _charges.set(0); sendPacket(new EtcStatusUpdate(this)); } /** * Starts/Restarts the ChargeTask to Clear Charges after 10 Mins. */ private void restartChargeTask() { if (_chargeTask != null) { _chargeTask.cancel(false); _chargeTask = null; } _chargeTask = ThreadPoolManager.getInstance().scheduleGeneral(new ResetChargesTask(this), 600000); } /** * Stops the Charges Clearing Task. */ public void stopChargeTask() { if (_chargeTask != null) { _chargeTask.cancel(false); _chargeTask = null; } } public void teleportBookmarkModify(int id, int icon, String tag, String name) { final TeleportBookmark bookmark = _tpbookmarks.get(id); if (bookmark != null) { bookmark.setIcon(icon); bookmark.setTag(tag); bookmark.setName(name); try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(UPDATE_TP_BOOKMARK)) { statement.setInt(1, icon); statement.setString(2, tag); statement.setString(3, name); statement.setInt(4, getObjectId()); statement.setInt(5, id); statement.execute(); } catch (Exception e) { _log.log(Level.WARNING, "Could not update character teleport bookmark data: " + e.getMessage(), e); } } sendPacket(new ExGetBookMarkInfoPacket(this)); } public void teleportBookmarkDelete(int id) { if (_tpbookmarks.remove(id) != null) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(DELETE_TP_BOOKMARK)) { statement.setInt(1, getObjectId()); statement.setInt(2, id); statement.execute(); } catch (Exception e) { _log.log(Level.WARNING, "Could not delete character teleport bookmark data: " + e.getMessage(), e); } sendPacket(new ExGetBookMarkInfoPacket(this)); } } public void teleportBookmarkGo(int id) { if (!teleportBookmarkCondition(0)) { return; } if (getInventory().getInventoryItemCount(13016, 0) == 0) { sendPacket(SystemMessageId.YOU_CANNOT_TELEPORT_BECAUSE_YOU_DO_NOT_HAVE_A_TELEPORT_ITEM); return; } SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); sm.addItemName(13016); sendPacket(sm); final TeleportBookmark bookmark = _tpbookmarks.get(id); if (bookmark != null) { destroyItem("Consume", getInventory().getItemByItemId(13016).getObjectId(), 1, null, false); teleToLocation(bookmark, false); } sendPacket(new ExGetBookMarkInfoPacket(this)); } public boolean teleportBookmarkCondition(int type) { if (isInCombat()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_DURING_A_BATTLE); return false; } else if (isInSiege() || (getSiegeState() != 0)) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_PARTICIPATING_A_LARGE_SCALE_BATTLE_SUCH_AS_A_CASTLE_SIEGE_FORTRESS_SIEGE_OR_CLAN_HALL_SIEGE); return false; } else if (isInDuel()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_DURING_A_DUEL); return false; } else if (isFlying()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_FLYING); return false; } else if (isInOlympiadMode()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_PARTICIPATING_IN_AN_OLYMPIAD_MATCH); return false; } else if (isParalyzed()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_YOU_ARE_IN_A_PETRIFIED_OR_PARALYZED_STATE); return false; } else if (isDead()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_WHILE_YOU_ARE_DEAD); return false; } else if (isInWater()) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_UNDERWATER); return false; } else if ((type == 1) && (isInsideZone(ZoneId.SIEGE) || isInsideZone(ZoneId.CLAN_HALL) || isInsideZone(ZoneId.JAIL) || isInsideZone(ZoneId.CASTLE) || isInsideZone(ZoneId.NO_SUMMON_FRIEND) || isInsideZone(ZoneId.FORT))) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA); return false; } else if (isInsideZone(ZoneId.NO_BOOKMARK) || isInBoat() || isInAirShip()) { if (type == 0) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_IN_THIS_AREA); } else if (type == 1) { sendPacket(SystemMessageId.YOU_CANNOT_USE_MY_TELEPORTS_TO_REACH_THIS_AREA); } return false; } /* * TODO: Instant Zone still not implemented else if (isInsideZone(ZoneId.INSTANT)) { sendPacket(SystemMessage.getSystemMessage(2357)); return; } */ else { return true; } } public void teleportBookmarkAdd(int x, int y, int z, int icon, String tag, String name) { if (!teleportBookmarkCondition(1)) { return; } if (_tpbookmarks.size() >= _bookmarkslot) { sendPacket(SystemMessageId.YOU_HAVE_NO_SPACE_TO_SAVE_THE_TELEPORT_LOCATION); return; } if (getInventory().getInventoryItemCount(20033, 0) == 0) { sendPacket(SystemMessageId.YOU_CANNOT_BOOKMARK_THIS_LOCATION_BECAUSE_YOU_DO_NOT_HAVE_A_MY_TELEPORT_FLAG); return; } int id; for (id = 1; id <= _bookmarkslot; ++id) { if (!_tpbookmarks.containsKey(id)) { break; } } _tpbookmarks.put(id, new TeleportBookmark(id, x, y, z, icon, tag, name)); destroyItem("Consume", getInventory().getItemByItemId(20033).getObjectId(), 1, null, false); SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.S1_DISAPPEARED); sm.addItemName(20033); sendPacket(sm); try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(INSERT_TP_BOOKMARK)) { statement.setInt(1, getObjectId()); statement.setInt(2, id); statement.setInt(3, x); statement.setInt(4, y); statement.setInt(5, z); statement.setInt(6, icon); statement.setString(7, tag); statement.setString(8, name); statement.execute(); } catch (Exception e) { _log.log(Level.WARNING, "Could not insert character teleport bookmark data: " + e.getMessage(), e); } sendPacket(new ExGetBookMarkInfoPacket(this)); } public void restoreTeleportBookmark() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_TP_BOOKMARK)) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { _tpbookmarks.put(rset.getInt("Id"), new TeleportBookmark(rset.getInt("Id"), rset.getInt("x"), rset.getInt("y"), rset.getInt("z"), rset.getInt("icon"), rset.getString("tag"), rset.getString("name"))); } } } catch (Exception e) { _log.log(Level.SEVERE, "Failed restoing character teleport bookmark.", e); } } @Override public void sendInfo(L2PcInstance activeChar) { if (isInBoat()) { setXYZ(getBoat().getLocation()); activeChar.sendPacket(new CharInfo(this)); activeChar.sendPacket(new GetOnVehicle(getObjectId(), getBoat().getObjectId(), getInVehiclePosition())); } else if (isInAirShip()) { setXYZ(getAirShip().getLocation()); activeChar.sendPacket(new CharInfo(this)); activeChar.sendPacket(new ExGetOnAirShip(this, getAirShip())); } else { activeChar.sendPacket(new CharInfo(this)); } int relation1 = getRelation(activeChar); int relation2 = activeChar.getRelation(this); Integer oldrelation = getKnownList().getKnownRelations().get(activeChar.getObjectId()); if ((oldrelation != null) && (oldrelation != relation1)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(this, relation1, isAutoAttackable(activeChar)); if (hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation1, isAutoAttackable(activeChar)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation1, isAutoAttackable(activeChar))); } } activeChar.sendPacket(rc); } oldrelation = activeChar.getKnownList().getKnownRelations().get(getObjectId()); if ((oldrelation != null) && (oldrelation != relation2)) { final RelationChanged rc = new RelationChanged(); rc.addRelation(activeChar, relation2, activeChar.isAutoAttackable(this)); if (activeChar.hasSummon()) { final L2Summon pet = getPet(); if (pet != null) { rc.addRelation(pet, relation2, activeChar.isAutoAttackable(this)); } if (hasServitors()) { getServitors().values().forEach(s -> rc.addRelation(s, relation2, activeChar.isAutoAttackable(this))); } } sendPacket(rc); } switch (getPrivateStoreType()) { case SELL: activeChar.sendPacket(new PrivateStoreMsgSell(this)); break; case PACKAGE_SELL: activeChar.sendPacket(new ExPrivateStoreSetWholeMsg(this)); break; case BUY: activeChar.sendPacket(new PrivateStoreMsgBuy(this)); break; case MANUFACTURE: activeChar.sendPacket(new RecipeShopMsg(this)); break; } if (isMounted()) { // Required double send for fix Mounted H5+ sendPacket(new CharInfo(activeChar)); } } public void showQuestMovie(int id) { if (_movieId > 0) { return; } abortAttack(); abortCast(); stopMove(null); _movieId = id; sendPacket(new ExStartScenePlayer(id)); } public boolean isAllowedToEnchantSkills() { if (isLocked()) { return false; } if (isTransformed() || isInStance()) { return false; } if (AttackStanceTaskManager.getInstance().hasAttackStanceTask(this)) { return false; } if (isCastingNow() || isCastingSimultaneouslyNow()) { return false; } if (isInBoat() || isInAirShip()) { return false; } return true; } /** * Set the _createDate of the L2PcInstance. * @param createDate */ public void setCreateDate(Calendar createDate) { _createDate = createDate; } /** * @return the _createDate of the L2PcInstance. */ public Calendar getCreateDate() { return _createDate; } /** * @return number of days to char birthday. */ public int checkBirthDay() { Calendar now = Calendar.getInstance(); // "Characters with a February 29 creation date will receive a gift on February 28." if ((_createDate.get(Calendar.DAY_OF_MONTH) == 29) && (_createDate.get(Calendar.MONTH) == 1)) { _createDate.add(Calendar.HOUR_OF_DAY, -24); } if ((now.get(Calendar.MONTH) == _createDate.get(Calendar.MONTH)) && (now.get(Calendar.DAY_OF_MONTH) == _createDate.get(Calendar.DAY_OF_MONTH)) && (now.get(Calendar.YEAR) != _createDate.get(Calendar.YEAR))) { return 0; } int i; for (i = 1; i < 6; i++) { now.add(Calendar.HOUR_OF_DAY, 24); if ((now.get(Calendar.MONTH) == _createDate.get(Calendar.MONTH)) && (now.get(Calendar.DAY_OF_MONTH) == _createDate.get(Calendar.DAY_OF_MONTH)) && (now.get(Calendar.YEAR) != _createDate.get(Calendar.YEAR))) { return i; } } return -1; } public int getBirthdays() { long time = (System.currentTimeMillis() - getCreateDate().getTimeInMillis()) / 1000; time /= TimeUnit.DAYS.toMillis(365); return (int) time; } /** * list of character friends */ private final HashMap<Integer, Friend> _friendList = new HashMap<>(); private final ArrayList<Integer> _updateMemos = new ArrayList<>(); public HashMap<Integer, Friend> getFriendList() { return _friendList; } public Friend getFriend(int friendOID) { if (getFriendList().containsKey(friendOID)) { return getFriendList().get(friendOID); } return null; } public void updateMemo(int friendOID) { if (!_updateMemos.contains(friendOID)) { _updateMemos.add(friendOID); } } public void updateMemos() { final String sqlQuery = "UPDATE character_friends SET memo=? WHERE charId=? AND friendId=? AND relation=0"; try (Connection con = L2DatabaseFactory.getInstance().getConnection()) { for (int target : _updateMemos) { try (PreparedStatement statement = con.prepareStatement(sqlQuery)) { Friend friend = _friendList.get(target); statement.setString(1, friend.getMemo()); statement.setInt(2, getObjectId()); statement.setInt(3, target); statement.executeUpdate(); } } } catch (Exception e) { _log.log(Level.WARNING, "Error found in " + getName() + "'s FriendList: " + e.getMessage(), e); } _updateMemos.clear(); } public void addFriend(L2PcInstance player) { _friendList.put(player.getObjectId(), new Friend(0, player.getObjectId(), "")); putFriendDetailInfo(player); } public void restoreFriendList() { _friendList.clear(); final String sqlQuery = "SELECT friendId, memo FROM character_friends WHERE charId=? AND relation=0"; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(sqlQuery)) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { int friendId = rset.getInt("friendId"); if (friendId == getObjectId()) { continue; } String memo = rset.getString("memo"); _friendList.put(friendId, new Friend(0, friendId, memo)); putFriendDetailInfo(friendId); } } } catch (Exception e) { _log.log(Level.WARNING, "Error found in " + getName() + "'s FriendList: " + e.getMessage(), e); } } public void putFriendDetailInfo(L2PcInstance player) { Friend friend = _friendList.get(player.getObjectId()); friend.setLevel(player.getLevel()); friend.setClassId(player.getClassId().getId()); if (player.getClan() != null) { friend.setClanId(player.getClan().getId()); friend.setClanName(player.getClan().getName()); friend.setClanCrestId(player.getClan().getCrestId()); friend.setAllyId(player.getClan().getAllyId()); friend.setAllyName(player.getClan().getAllyName()); friend.setAllyCrestId(player.getClan().getAllyCrestId()); } friend.setLastLogin(System.currentTimeMillis()); friend.setCreateDate(player.getCreateDate().getTimeInMillis()); } public void putFriendDetailInfo(int friendId) { Friend friend = _friendList.get(friendId); int bClassId = 0; try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT * FROM characters WHERE charId=?");) { statement.setInt(1, friendId); ResultSet rset = statement.executeQuery(); while (rset.next()) { friend.setLevel(rset.getByte("level")); friend.setClassId(rset.getInt("classid")); bClassId = (rset.getInt("base_class")); friend.setClanId(rset.getInt("clanid")); friend.setLastLogin(rset.getLong("lastAccess")); friend.setCreateDate(rset.getLong("createDate")); } statement.execute(); rset.close(); } catch (Exception e) { System.out.println("Failed loading character. " + e); e.printStackTrace(); } if (friend.getClassId() != bClassId) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT level FROM character_subclasses WHERE charId=? AND class_id=?");) { statement.setInt(1, friendId); statement.setInt(2, friend.getClassId()); ResultSet rset = statement.executeQuery(); while (rset.next()) { friend.setLevel(rset.getByte("level")); } statement.execute(); rset.close(); } catch (Exception e) { System.out.println("Failed loading character_subclasses. " + e); e.printStackTrace(); } } if (friend.getClanId() != 0) { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT * FROM clan_data WHERE clan_id=?");) { statement.setInt(1, friend.getClanId()); ResultSet rset = statement.executeQuery(); while (rset.next()) { friend.setClanName(rset.getString("clan_name")); friend.setClanCrestId(rset.getInt("crest_id")); friend.setAllyId(rset.getInt("ally_id")); friend.setAllyName(rset.getString("ally_name")); friend.setAllyCrestId(rset.getInt("ally_crest_id")); } statement.execute(); rset.close(); } catch (Exception e) { System.out.println("Failed loading clan_data. " + e); e.printStackTrace(); } } } public void notifyFriends(int type) { L2FriendStatus pkt = new L2FriendStatus(this, type); for (int id : _friendList.keySet()) { L2PcInstance friend = L2World.getInstance().getPlayer(id); if (friend != null) { friend.sendPacket(pkt); } } } /** * Verify if this player is in silence mode. * @return the {@code true} if this player is in silence mode, {@code false} otherwise */ public boolean isSilenceMode() { return _silenceMode; } /** * While at silenceMode, checks if this player blocks PMs for this user * @param playerObjId the player object Id * @return {@code true} if the given Id is not excluded and this player is in silence mode, {@code false} otherwise */ public boolean isSilenceMode(int playerObjId) { if (Config.SILENCE_MODE_EXCLUDE && _silenceMode && (_silenceModeExcluded != null)) { return !_silenceModeExcluded.contains(playerObjId); } return _silenceMode; } /** * Set the silence mode. * @param mode the value */ public void setSilenceMode(boolean mode) { _silenceMode = mode; if (_silenceModeExcluded != null) { _silenceModeExcluded.clear(); // Clear the excluded list on each setSilenceMode } sendPacket(new EtcStatusUpdate(this)); } /** * Add a player to the "excluded silence mode" list. * @param playerObjId the player's object Id */ public void addSilenceModeExcluded(int playerObjId) { if (_silenceModeExcluded == null) { _silenceModeExcluded = new ArrayList<>(1); } _silenceModeExcluded.add(playerObjId); } private void storeRecipeShopList() { if (hasManufactureShop()) { try (Connection con = L2DatabaseFactory.getInstance().getConnection()) { try (PreparedStatement st = con.prepareStatement(DELETE_CHAR_RECIPE_SHOP)) { st.setInt(1, getObjectId()); st.execute(); } try (PreparedStatement st = con.prepareStatement(INSERT_CHAR_RECIPE_SHOP)) { AtomicInteger slot = new AtomicInteger(1); con.setAutoCommit(false); for (L2ManufactureItem item : _manufactureItems.values()) { st.setInt(1, getObjectId()); st.setInt(2, item.getRecipeId()); st.setLong(3, item.getCost()); st.setInt(4, slot.getAndIncrement()); st.addBatch(); } st.executeBatch(); con.commit(); } } catch (Exception e) { _log.log(Level.SEVERE, "Could not store recipe shop for playerId " + getObjectId() + ": ", e); } } } private void restoreRecipeShopList() { if (_manufactureItems != null) { _manufactureItems.clear(); } try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement(RESTORE_CHAR_RECIPE_SHOP)) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { while (rset.next()) { getManufactureItems().put(rset.getInt("recipeId"), new L2ManufactureItem(rset.getInt("recipeId"), rset.getLong("price"))); } } } catch (Exception e) { _log.log(Level.SEVERE, "Could not restore recipe shop list data for playerId: " + getObjectId(), e); } } public double getCollisionRadius() { if (isMounted() && (getMountNpcId() > 0)) { return NpcData.getInstance().getTemplate(getMountNpcId()).getfCollisionRadius(); } else if (isTransformed()) { return getTransformation().getCollisionRadius(this); } return getAppearance().getSex() ? getBaseTemplate().getFCollisionRadiusFemale() : getBaseTemplate().getfCollisionRadius(); } public double getCollisionHeight() { if (isMounted() && (getMountNpcId() > 0)) { return NpcData.getInstance().getTemplate(getMountNpcId()).getfCollisionHeight(); } else if (isTransformed()) { return getTransformation().getCollisionHeight(this); } return getAppearance().getSex() ? getBaseTemplate().getFCollisionHeightFemale() : getBaseTemplate().getfCollisionHeight(); } public final int getClientX() { return _clientX; } public final int getClientY() { return _clientY; } public final int getClientZ() { return _clientZ; } public final int getClientHeading() { return _clientHeading; } public final void setClientX(int val) { _clientX = val; } public final void setClientY(int val) { _clientY = val; } public final void setClientZ(int val) { _clientZ = val; } public final void setClientHeading(int val) { _clientHeading = val; } /** * @param z * @return true if character falling now on the start of fall return false for correct coord sync! */ public final boolean isFalling(int z) { if (isDead() || isFlying() || isFlyingMounted() || isInsideZone(ZoneId.WATER)) { return false; } if (System.currentTimeMillis() < _fallingTimestamp) { return true; } final int deltaZ = getZ() - z; if (deltaZ <= getBaseTemplate().getSafeFallHeight()) { return false; } // If there is no geodata loaded for the place we are client Z correction might cause falling damage. if (!GeoData.getInstance().hasGeo(getX(), getY())) { return false; } final int damage = (int) Formulas.calcFallDam(this, deltaZ); if (damage > 0) { reduceCurrentHp(Math.min(damage, getCurrentHp() - 1), null, false, true, null); SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.YOU_RECEIVED_S1_FALLING_DAMAGE); sm.addInt(damage); sendPacket(sm); } // Prevent falling in game graphics. sendPacket(new ValidateLocation(this)); setFalling(); return false; } /** * Set falling timestamp */ public final void setFalling() { _fallingTimestamp = System.currentTimeMillis() + FALLING_VALIDATION_DELAY; } /** * @return the _movieId */ public int getMovieId() { return _movieId; } public void setMovieId(int id) { _movieId = id; } /** * Update last item auction request timestamp to current */ public void updateLastItemAuctionRequest() { _lastItemAuctionInfoRequest = System.currentTimeMillis(); } /** * @return true if receiving item auction requests<br> * (last request was in 2 seconds before) */ public boolean isItemAuctionPolling() { return (System.currentTimeMillis() - _lastItemAuctionInfoRequest) < 2000; } @Override public boolean isMovementDisabled() { return super.isMovementDisabled() || (_movieId > 0); } private void restoreUISettings() { _uiKeySettings = new UIKeysSettings(getObjectId()); } private void storeUISettings() { if (_uiKeySettings == null) { return; } if (!_uiKeySettings.isSaved()) { _uiKeySettings.saveInDB(); } } public UIKeysSettings getUISettings() { return _uiKeySettings; } public String getHtmlPrefix() { if (!Config.L2JMOD_MULTILANG_ENABLE) { return null; } return _htmlPrefix; } public String getLang() { return _lang; } public boolean setLang(String lang) { boolean result = false; if (Config.L2JMOD_MULTILANG_ENABLE) { if (Config.L2JMOD_MULTILANG_ALLOWED.contains(lang)) { _lang = lang; result = true; } else { _lang = Config.L2JMOD_MULTILANG_DEFAULT; } _htmlPrefix = "data/lang/" + _lang + "/"; } else { _lang = null; _htmlPrefix = null; } return result; } public long getOfflineStartTime() { return _offlineShopStart; } public void setOfflineStartTime(long time) { _offlineShopStart = time; } public int getPcBangPoints() { return _pcBangPoints; } public void setPcBangPoints(final int count) { if (count < 200000) { _pcBangPoints = count; } else { _pcBangPoints = 200000; } } /** * Remove player from BossZones (used on char logout/exit) */ public void removeFromBossZone() { try { for (L2BossZone _zone : GrandBossManager.getInstance().getZones()) { _zone.removePlayer(this); } } catch (Exception e) { _log.log(Level.WARNING, "Exception on removeFromBossZone(): " + e.getMessage(), e); } } /** * Check all player skills for skill level. If player level is lower than skill learn level - 9, skill level is decreased to next possible level. */ public void checkPlayerSkills() { L2SkillLearn learn; for (Entry<Integer, Skill> e : getSkills().entrySet()) { learn = SkillTreesData.getInstance().getClassSkill(e.getKey(), e.getValue().getLevel() % 100, getClassId()); if (learn != null) { int lvlDiff = e.getKey() == CommonSkill.EXPERTISE.getId() ? 0 : 9; if (getLevel() < (learn.getGetLevel() - lvlDiff)) { deacreaseSkillLevel(e.getValue(), lvlDiff); } } } } private void deacreaseSkillLevel(Skill skill, int lvlDiff) { int nextLevel = -1; final Map<Integer, L2SkillLearn> skillTree = SkillTreesData.getInstance().getCompleteClassSkillTree(getClassId()); for (L2SkillLearn sl : skillTree.values()) { if ((sl.getSkillId() == skill.getId()) && (nextLevel < sl.getSkillLevel()) && (getLevel() >= (sl.getGetLevel() - lvlDiff))) { nextLevel = sl.getSkillLevel(); // next possible skill level } } if (nextLevel == -1) { _log.info("Removing skill " + skill + " from player " + toString()); removeSkill(skill, true); // there is no lower skill } else { _log.info("Decreasing skill " + skill + " to " + nextLevel + " for player " + toString()); addSkill(SkillData.getInstance().getSkill(skill.getId(), nextLevel), true); // replace with lower one } } public boolean canMakeSocialAction() { return ((getPrivateStoreType() == PrivateStoreType.NONE) && (getActiveRequester() == null) && !isAlikeDead() && !isAllSkillsDisabled() && !isCastingNow() && !isCastingSimultaneouslyNow() && (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)); } public void setMultiSocialAction(int id, int targetId) { _multiSociaAction = id; _multiSocialTarget = targetId; } public int getMultiSociaAction() { return _multiSociaAction; } public int getMultiSocialTarget() { return _multiSocialTarget; } public Collection<TeleportBookmark> getTeleportBookmarks() { return _tpbookmarks.values(); } public int getBookmarkslot() { return _bookmarkslot; } /** * @return */ public int getQuestInventoryLimit() { return Config.INVENTORY_MAXIMUM_QUEST_ITEMS; } public boolean canAttackCharacter(L2Character cha) { if (cha instanceof L2Attackable) { return true; } else if (cha instanceof L2Playable) { if (cha.isInsideZone(ZoneId.PVP) && !cha.isInsideZone(ZoneId.SIEGE)) { return true; } L2PcInstance target; if (cha instanceof L2Summon) { target = ((L2Summon) cha).getOwner(); } else { target = (L2PcInstance) cha; } if (isInDuel() && target.isInDuel() && (target.getDuelId() == getDuelId())) { return true; } else if (isInParty() && target.isInParty()) { if (getParty() == target.getParty()) { return false; } if (((getParty().getCommandChannel() != null) || (target.getParty().getCommandChannel() != null)) && (getParty().getCommandChannel() == target.getParty().getCommandChannel())) { return false; } } else if ((getClan() != null) && (target.getClan() != null)) { if (getClanId() == target.getClanId()) { return false; } if (((getAllyId() > 0) || (target.getAllyId() > 0)) && (getAllyId() == target.getAllyId())) { return false; } if (getClan().isAtWarWith(target.getClan().getId()) && target.getClan().isAtWarWith(getClan().getId())) { return true; } } else if ((getClan() == null) || (target.getClan() == null)) { if ((target.getPvpFlag() == 0) && (target.getKarma() == 0)) { return false; } } } return true; } /** * Test if player inventory is under 90% capaity * @param includeQuestInv check also quest inventory * @return */ public boolean isInventoryUnder90(boolean includeQuestInv) { return (getInventory().getSize(includeQuestInv) <= (getInventoryLimit() * 0.9)); } public boolean havePetInvItems() { return _petItems; } public void setPetInvItems(boolean haveit) { _petItems = haveit; } /** * Restore Pet's inventory items from database. */ private void restorePetInventoryItems() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT object_id FROM `items` WHERE `owner_id`=? AND (`loc`='PET' OR `loc`='PET_EQUIP') LIMIT 1;")) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { setPetInvItems(rset.next() && (rset.getInt("object_id") > 0)); } } catch (Exception e) { _log.log(Level.SEVERE, "Could not check Items in Pet Inventory for playerId: " + getObjectId(), e); } } public String getAdminConfirmCmd() { return _adminConfirmCmd; } public void setAdminConfirmCmd(String adminConfirmCmd) { _adminConfirmCmd = adminConfirmCmd; } public void setBlockCheckerArena(byte arena) { _handysBlockCheckerEventArena = arena; } public int getBlockCheckerArena() { return _handysBlockCheckerEventArena; } /** * Load L2PcInstance Recommendations data. */ private void loadRecommendations() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("SELECT rec_have,rec_left,time_left FROM character_reco_bonus WHERE charId=? LIMIT 1")) { statement.setInt(1, getObjectId()); try (ResultSet rset = statement.executeQuery()) { if (rset.next()) { setRecomHave(rset.getInt("rec_have")); setRecomLeft(rset.getInt("rec_left")); } } } catch (Exception e) { _log.log(Level.SEVERE, "Could not restore Recommendations for player: " + getObjectId(), e); } } /** * Update L2PcInstance Recommendations data. */ public void storeRecommendations() { try (Connection con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement = con.prepareStatement("INSERT INTO character_reco_bonus (charId,rec_have,rec_left,time_left) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE rec_have=?, rec_left=?, time_left=?")) { statement.setInt(1, getObjectId()); statement.setInt(2, getRecomHave()); statement.setInt(3, getRecomLeft()); statement.setLong(4, 0); // Update part statement.setInt(5, getRecomHave()); statement.setInt(6, getRecomLeft()); statement.setLong(7, 0); statement.execute(); } catch (Exception e) { _log.log(Level.SEVERE, "Could not update Recommendations for player: " + getObjectId(), e); } } public void startRecoGiveTask() { // Create task to give new recommendations _recoGiveTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(new RecoGiveTask(this), 7200000, 3600000); // Store new data storeRecommendations(); } public void stopRecoGiveTask() { if (_recoGiveTask != null) { _recoGiveTask.cancel(false); _recoGiveTask = null; } } public boolean isRecoTwoHoursGiven() { return _recoTwoHoursGiven; } public void setRecoTwoHoursGiven(boolean val) { _recoTwoHoursGiven = val; } public void setPremiumStatus(boolean premiumStatus) { _premiumStatus = premiumStatus; } public boolean hasPremiumStatus() { if (!Config.PREMIUM_SYSTEM_ENABLED) { return false; } return _premiumStatus; } private static void restorePremiumSystemData(L2PcInstance player, String account) { boolean success = false; try (Connection con = L2DatabaseFactory.getInstance().getConnection()) { PreparedStatement statement = con.prepareStatement(RESTORE_PREMIUMSERVICE); statement.setString(1, account); ResultSet rset = statement.executeQuery(); while (rset.next()) { success = true; if (Config.PREMIUM_SYSTEM_ENABLED) { if (rset.getLong("enddate") <= System.currentTimeMillis()) { PremiumManager.getInstance().removePremiumStatus(account); player.setPremiumStatus(false); } else { player.setPremiumStatus(rset.getBoolean("premium_service")); } } else { player.setPremiumStatus(false); } } statement.close(); } catch (Exception e) { _log.warning("Premium System: Could not restore premium system data for " + account + "." + e); e.printStackTrace(); } if (success == false) { PremiumManager.getInstance().removePremiumStatus(player.getAccountName()); player.setPremiumStatus(false); } } public void setLastPetitionGmName(String gmName) { _lastPetitionGmName = gmName; } public String getLastPetitionGmName() { return _lastPetitionGmName; } public L2ContactList getContactList() { return _contactList; } public void setEventStatus() { eventStatus = new PlayerEventHolder(this); } public void setEventStatus(PlayerEventHolder pes) { eventStatus = pes; } public PlayerEventHolder getEventStatus() { return eventStatus; } public long getNotMoveUntil() { return _notMoveUntil; } public void updateNotMoveUntil() { _notMoveUntil = System.currentTimeMillis() + Config.PLAYER_MOVEMENT_BLOCK_TIME; } @Override public boolean isPlayer() { return true; } @Override public boolean isChargedShot(ShotType type) { final L2ItemInstance weapon = getActiveWeaponInstance(); return (weapon != null) && weapon.isChargedShot(type); } @Override public void setChargedShot(ShotType type, boolean charged) { final L2ItemInstance weapon = getActiveWeaponInstance(); if (weapon != null) { weapon.setChargedShot(type, charged); } } /** * @param skillId the display skill Id * @return the custom skill */ public final Skill getCustomSkill(int skillId) { return (_customSkills != null) ? _customSkills.get(skillId) : null; } /** * Add a skill level to the custom skills map. * @param skill the skill to add */ private final void addCustomSkill(Skill skill) { if ((skill != null) && (skill.getDisplayId() != skill.getId())) { if (_customSkills == null) { _customSkills = new ConcurrentHashMap<>(); } _customSkills.put(skill.getDisplayId(), skill); } } /** * Remove a skill level from the custom skill map. * @param skill the skill to remove */ private final void removeCustomSkill(Skill skill) { if ((skill != null) && (_customSkills != null) && (skill.getDisplayId() != skill.getId())) { _customSkills.remove(skill.getDisplayId()); } } /** * @return {@code true} if current player can revive and shows 'To Village' button upon death, {@code false} otherwise. */ @Override public boolean canRevive() { for (IEventListener listener : _eventListeners) { if (listener.isOnEvent() && !listener.canRevive()) { return false; } } return _canRevive; } /** * This method can prevent from displaying 'To Village' button upon death. * @param val */ @Override public void setCanRevive(boolean val) { _canRevive = val; } /** * @return {@code true} if player is on event, {@code false} otherwise. */ @Override public boolean isOnEvent() { for (IEventListener listener : _eventListeners) { if (listener.isOnEvent()) { return true; } } return super.isOnEvent(); } public boolean isBlockedFromExit() { for (IEventListener listener : _eventListeners) { if (listener.isOnEvent() && listener.isBlockingExit()) { return true; } } return false; } public boolean isBlockedFromDeathPenalty() { for (IEventListener listener : _eventListeners) { if (listener.isOnEvent() && listener.isBlockingDeathPenalty()) { return true; } } return false; } public void setOriginalCpHpMp(double cp, double hp, double mp) { _originalCp = cp; _originalHp = hp; _originalMp = mp; } @Override public void addOverrideCond(PcCondOverride... excs) { super.addOverrideCond(excs); getVariables().set(COND_OVERRIDE_KEY, Long.toString(_exceptions)); } @Override public void removeOverridedCond(PcCondOverride... excs) { super.removeOverridedCond(excs); getVariables().set(COND_OVERRIDE_KEY, Long.toString(_exceptions)); } /** * @return {@code true} if {@link PlayerVariables} instance is attached to current player's scripts, {@code false} otherwise. */ public boolean hasVariables() { return getScript(PlayerVariables.class) != null; } /** * @return {@link PlayerVariables} instance containing parameters regarding player. */ public PlayerVariables getVariables() { final PlayerVariables vars = getScript(PlayerVariables.class); return vars != null ? vars : addScript(new PlayerVariables(getObjectId())); } /** * @return {@code true} if {@link AccountVariables} instance is attached to current player's scripts, {@code false} otherwise. */ public boolean hasAccountVariables() { return getScript(AccountVariables.class) != null; } /** * @return {@link AccountVariables} instance containing parameters regarding player. */ public AccountVariables getAccountVariables() { final AccountVariables vars = getScript(AccountVariables.class); return vars != null ? vars : addScript(new AccountVariables(getAccountName())); } /** * Adds a event listener. * @param listener */ public void addEventListener(IEventListener listener) { _eventListeners.add(listener); } /** * Removes event listener * @param listener */ public void removeEventListener(IEventListener listener) { _eventListeners.remove(listener); } public void removeEventListener(Class<? extends IEventListener> clazz) { final Iterator<IEventListener> it = _eventListeners.iterator(); IEventListener event; while (it.hasNext()) { event = it.next(); if (event.getClass() == clazz) { it.remove(); } } } public Collection<IEventListener> getEventListeners() { return _eventListeners; } @Override public int getId() { return getClassId().getId(); } public boolean isPartyBanned() { return PunishmentManager.getInstance().hasPunishment(getObjectId(), PunishmentAffect.CHARACTER, PunishmentType.PARTY_BAN); } /** * @param act * @return {@code true} if action was added successfully, {@code false} otherwise. */ public boolean addAction(PlayerAction act) { if (!hasAction(act)) { _actionMask |= act.getMask(); return true; } return false; } /** * @param act * @return {@code true} if action was removed successfully, {@code false} otherwise. */ public boolean removeAction(PlayerAction act) { if (hasAction(act)) { _actionMask &= ~act.getMask(); return true; } return false; } /** * @param act * @return {@code true} if action is present, {@code false} otherwise. */ public boolean hasAction(PlayerAction act) { return (_actionMask & act.getMask()) == act.getMask(); } /** * Set true/false if character got Charm of Courage * @param val true/false */ public void setCharmOfCourage(boolean val) { _hasCharmOfCourage = val; } /** * @return {@code true} if effect is present, {@code false} otherwise. */ public boolean hasCharmOfCourage() { return _hasCharmOfCourage; } public boolean isAwaken() { if (((getActiveClass() >= 139) && (getActiveClass() <= 181)) || (getActiveClass() >= 188)) { return true; } return false; } public int getJumpTrackId() { return _jumpTrackId; } public void setJumpTrackId(int jumpTrackId) { _jumpTrackId = jumpTrackId; } public boolean isGood() { return _isGood; } public boolean isEvil() { return _isEvil; } public void setGood() { _isGood = true; _isEvil = false; } public void setEvil() { _isGood = false; _isEvil = true; } /** * @param target the target * @return {@code true} if this player got war with the target, {@code false} otherwise. */ public boolean isAtWarWith(L2Character target) { if (target == null) { return false; } if ((_clan != null) && !isAcademyMember()) { if ((target.getClan() != null) && !target.isAcademyMember()) { return _clan.isAtWarWith(target.getClan()); } } return false; } /** * @param target the target * @return {@code true} if this player in same party with the target, {@code false} otherwise. */ public boolean isInPartyWith(L2Character target) { if (!isInParty() || !target.isInParty()) { return false; } return getParty().getLeaderObjectId() == target.getParty().getLeaderObjectId(); } /** * @param target the target * @return {@code true} if this player in same command channel with the target, {@code false} otherwise. */ public boolean isInCommandChannelWith(L2Character target) { if (!isInParty() || !target.isInParty()) { return false; } if (!getParty().isInCommandChannel() || !target.getParty().isInCommandChannel()) { return false; } return getParty().getCommandChannel().getLeaderObjectId() == target.getParty().getCommandChannel().getLeaderObjectId(); } /** * @param target the target * @return {@code true} if this player in same clan with the target, {@code false} otherwise. */ public boolean isInClanWith(L2Character target) { if ((getClanId() == 0) || (target.getClanId() == 0)) { return false; } return getClanId() == target.getClanId(); } /** * @param target the target * @return {@code true} if this player in same ally with the target, {@code false} otherwise. */ public boolean isInAllyWith(L2Character target) { if ((getAllyId() == 0) || (target.getAllyId() == 0)) { return false; } return getAllyId() == target.getAllyId(); } /** * @param target the target * @return {@code true} if this player at duel with the target, {@code false} otherwise. */ public boolean isInDuelWith(L2Character target) { if (!isInDuel() || !target.isInDuel()) { return false; } return getDuelId() == target.getDuelId(); } /** * @param target the target * @return {@code true} if this player is on same siege side with the target, {@code false} otherwise. */ public boolean isOnSameSiegeSideWith(L2Character target) { return (getSiegeState() > 0) && isInsideZone(ZoneId.SIEGE) && (getSiegeState() == target.getSiegeState()) && (getSiegeSide() == target.getSiegeSide()); } /** * Sets the beauty shop hair * @param hairId */ public void setVisualHair(int hairId) { getVariables().set("visualHairId", hairId); } /** * Sets the beauty shop hair color * @param colorId */ public void setVisualHairColor(int colorId) { getVariables().set("visualHairColorId", colorId); } /** * Sets the beauty shop modified face * @param faceId */ public void setVisualFace(int faceId) { getVariables().set("visualFaceId", faceId); } /** * @return the beauty shop hair, or his normal if not changed. */ public int getVisualHair() { return getVariables().getInt("visualHairId", getAppearance().getHairStyle()); } /** * @return the beauty shop hair color, or his normal if not changed. */ public int getVisualHairColor() { return getVariables().getInt("visualHairColorId", getAppearance().getHairColor()); } /** * @return the beauty shop modified face, or his normal if not changed. */ public int getVisualFace() { return getVariables().getInt("visualFaceId", getAppearance().getFace()); } /** * @return {@code true} if player has mentees, {@code false} otherwise */ public boolean isMentor() { return MentorManager.getInstance().isMentor(getObjectId()); } /** * @return {@code true} if player has mentor, {@code false} otherwise */ public boolean isMentee() { return MentorManager.getInstance().isMentee(getObjectId()); } /** * @return the amount of ability points player can spend on learning skills. */ public int getAbilityPoints() { return getVariables().getInt("ABILITY_POINTS", 0); } /** * Sets the amount of ability points player can spend on learning skills. * @param points */ public void setAbilityPoints(int points) { getVariables().set("ABILITY_POINTS", points); } /** * @return how much ability points player has spend on learning skills. */ public int getAbilityPointsUsed() { return getVariables().getInt("ABILITY_POINTS_USED", 0); } /** * Sets how much ability points player has spend on learning skills. * @param points */ public void setAbilityPointsUsed(int points) { getVariables().set("ABILITY_POINTS_USED", points); } /** * @return The amount of times player can use world chat */ public int getWorldChatPoints() { return getVariables().getInt(WORLD_CHAT_VARIABLE_NAME, 1); } /** * Sets the amount of times player can use world chat * @param points */ public void setWorldChatPoints(int points) { getVariables().set(WORLD_CHAT_VARIABLE_NAME, points); } /** * @return Side of the player. */ public CastleSide getPlayerSide() { if ((getClan() == null) || (getClan().getCastleId() == 0)) { return CastleSide.NEUTRAL; } return CastleManager.getInstance().getCastleById(getClan().getCastleId()).getSide(); } /** * @return {@code true} if player is on Dark side, {@code false} otherwise. */ public boolean isOnDarkSide() { return getPlayerSide() == CastleSide.DARK; } /** * @return {@code true} if player is on Light side, {@code false} otherwise. */ public boolean isOnLightSide() { return getPlayerSide() == CastleSide.LIGHT; } public int getMaxSummonPoints() { return (int) getStat().calcStat(Stats.MAX_SUMMON_POINTS, 0, null, null); } public int getSummonPoints() { return getServitors().values().stream().mapToInt(L2Summon::getSummonPoints).sum(); } /** * @param request * @return {@code true} if the request was registered successfully, {@code false} otherwise. */ public boolean addRequest(AbstractRequest request) { if (_requests == null) { synchronized (this) { if (_requests == null) { _requests = new ConcurrentHashMap<>(); } } } return canRequest(request) && (_requests.putIfAbsent(request.getClass(), request) == null); } public boolean canRequest(AbstractRequest request) { return (_requests != null) && _requests.values().stream().allMatch(request::canWorkWith); } /** * @param clazz * @return {@code true} if request was successfully removed, {@code false} in case processing set is not created or not containing the request. */ public boolean removeRequest(Class<? extends AbstractRequest> clazz) { return (_requests != null) && (_requests.remove(clazz) != null); } /** * @param <T> * @param requestClass * @return object that is instance of {@code requestClass} param, {@code null} if not instance or not set. */ public <T extends AbstractRequest> T getRequest(Class<T> requestClass) { return _requests != null ? requestClass.cast(_requests.get(requestClass)) : null; } /** * @return {@code true} if player has any processing request set, {@code false} otherwise. */ public boolean hasRequests() { return (_requests != null) && !_requests.isEmpty(); } public boolean hasItemRequest() { return (_requests != null) && _requests.values().stream().anyMatch(AbstractRequest::isItemRequest); } /** * @param requestClass * @param classes * @return {@code true} if player has the provided request and processing it, {@code false} otherwise. */ @SafeVarargs public final boolean hasRequest(Class<? extends AbstractRequest> requestClass, Class<? extends AbstractRequest>... classes) { if (_requests != null) { for (Class<? extends AbstractRequest> clazz : classes) { if (_requests.containsKey(clazz)) { return true; } } return _requests.containsKey(requestClass); } return false; } /** * @param objectId * @return {@code true} if item object id is currently in use by some request, {@code false} otherwise. */ public boolean isProcessingItem(int objectId) { return (_requests != null) && _requests.values().stream().anyMatch(req -> req.isUsing(objectId)); } /** * Removing all requests associated with the item object id provided. * @param objectId */ public void removeRequestsThatProcessesItem(int objectId) { if (_requests != null) { _requests.values().removeIf(req -> req.isUsing(objectId)); } } /* * Return current vitality points in integer format */ public int getVitalityPoints() { if (getClassId().getId() == getBaseClass()) { return _vitalityPoints; } if (getSubClasses().containsKey(getClassIndex())) { return getSubClasses().get(getClassIndex()).getVitalityPoints(); } return _vitalityPoints; } public void setVitalityPoints(int points) { if (getClassId().getId() == getBaseClass()) { _vitalityPoints = points; return; } if (getSubClasses().containsKey(getClassIndex())) { getSubClasses().get(getClassIndex()).setVitalityPoints(points); } _vitalityPoints = points; } public int getFirstCompoundOID() { return _firstCompoundOID; } public void setFirstCompoundOID(int firstCompoundOID) { _firstCompoundOID = firstCompoundOID; } public int getSecondCompoundOID() { return _secondCompoundOID; } public void setSecondCompoundOID(int secondCompoundOID) { _secondCompoundOID = secondCompoundOID; } public void setUsingPrimeShop(boolean isUsing) { _usingPrimeShop = isUsing; } public boolean isUsingPrimeShop() { return _usingPrimeShop; } /** * @return the prime shop points of the player. */ public int getPrimePoints() { return getAccountVariables().getInt("PRIME_POINTS", 0); } /** * Sets prime shop for current player. * @param points */ public void setPrimePoints(int points) { // Immediate store upon change final AccountVariables vars = getAccountVariables(); vars.set("PRIME_POINTS", points); vars.storeMe(); } /** * Gets the whisperers. * @return the whisperers */ public Set<Integer> getWhisperers() { return _whisperers; } }