L2Npc.java 54 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054
  1. /*
  2. * Copyright (C) 2004-2015 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver.model.actor;
  20. import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
  21. import java.util.ArrayList;
  22. import java.util.Collection;
  23. import java.util.Collections;
  24. import java.util.List;
  25. import java.util.Map;
  26. import java.util.concurrent.ConcurrentHashMap;
  27. import java.util.logging.Level;
  28. import com.l2jserver.Config;
  29. import com.l2jserver.gameserver.ItemsAutoDestroy;
  30. import com.l2jserver.gameserver.SevenSigns;
  31. import com.l2jserver.gameserver.SevenSignsFestival;
  32. import com.l2jserver.gameserver.ThreadPoolManager;
  33. import com.l2jserver.gameserver.cache.HtmCache;
  34. import com.l2jserver.gameserver.datatables.ItemTable;
  35. import com.l2jserver.gameserver.datatables.NpcPersonalAIData;
  36. import com.l2jserver.gameserver.enums.AISkillScope;
  37. import com.l2jserver.gameserver.enums.AIType;
  38. import com.l2jserver.gameserver.enums.InstanceType;
  39. import com.l2jserver.gameserver.enums.PrivateStoreType;
  40. import com.l2jserver.gameserver.enums.Race;
  41. import com.l2jserver.gameserver.enums.ShotType;
  42. import com.l2jserver.gameserver.enums.Team;
  43. import com.l2jserver.gameserver.handler.BypassHandler;
  44. import com.l2jserver.gameserver.handler.IBypassHandler;
  45. import com.l2jserver.gameserver.instancemanager.CHSiegeManager;
  46. import com.l2jserver.gameserver.instancemanager.CastleManager;
  47. import com.l2jserver.gameserver.instancemanager.FortManager;
  48. import com.l2jserver.gameserver.instancemanager.TownManager;
  49. import com.l2jserver.gameserver.instancemanager.WalkingManager;
  50. import com.l2jserver.gameserver.model.L2Object;
  51. import com.l2jserver.gameserver.model.L2Spawn;
  52. import com.l2jserver.gameserver.model.L2World;
  53. import com.l2jserver.gameserver.model.L2WorldRegion;
  54. import com.l2jserver.gameserver.model.Location;
  55. import com.l2jserver.gameserver.model.actor.instance.L2ClanHallManagerInstance;
  56. import com.l2jserver.gameserver.model.actor.instance.L2DoormenInstance;
  57. import com.l2jserver.gameserver.model.actor.instance.L2FishermanInstance;
  58. import com.l2jserver.gameserver.model.actor.instance.L2MerchantInstance;
  59. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  60. import com.l2jserver.gameserver.model.actor.instance.L2TeleporterInstance;
  61. import com.l2jserver.gameserver.model.actor.instance.L2TrainerInstance;
  62. import com.l2jserver.gameserver.model.actor.instance.L2WarehouseInstance;
  63. import com.l2jserver.gameserver.model.actor.knownlist.NpcKnownList;
  64. import com.l2jserver.gameserver.model.actor.stat.NpcStat;
  65. import com.l2jserver.gameserver.model.actor.status.NpcStatus;
  66. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  67. import com.l2jserver.gameserver.model.entity.Castle;
  68. import com.l2jserver.gameserver.model.entity.Fort;
  69. import com.l2jserver.gameserver.model.entity.clanhall.SiegableHall;
  70. import com.l2jserver.gameserver.model.events.EventDispatcher;
  71. import com.l2jserver.gameserver.model.events.EventType;
  72. import com.l2jserver.gameserver.model.events.impl.character.npc.OnNpcCanBeSeen;
  73. import com.l2jserver.gameserver.model.events.impl.character.npc.OnNpcEventReceived;
  74. import com.l2jserver.gameserver.model.events.impl.character.npc.OnNpcSkillFinished;
  75. import com.l2jserver.gameserver.model.events.impl.character.npc.OnNpcSpawn;
  76. import com.l2jserver.gameserver.model.events.impl.character.npc.OnNpcTeleport;
  77. import com.l2jserver.gameserver.model.events.returns.TerminateReturn;
  78. import com.l2jserver.gameserver.model.holders.ItemHolder;
  79. import com.l2jserver.gameserver.model.items.L2Item;
  80. import com.l2jserver.gameserver.model.items.L2Weapon;
  81. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  82. import com.l2jserver.gameserver.model.olympiad.Olympiad;
  83. import com.l2jserver.gameserver.model.skills.Skill;
  84. import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
  85. import com.l2jserver.gameserver.model.variables.NpcVariables;
  86. import com.l2jserver.gameserver.model.zone.type.L2TownZone;
  87. import com.l2jserver.gameserver.network.SystemMessageId;
  88. import com.l2jserver.gameserver.network.serverpackets.AbstractNpcInfo;
  89. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  90. import com.l2jserver.gameserver.network.serverpackets.ExChangeNpcState;
  91. import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
  92. import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
  93. import com.l2jserver.gameserver.network.serverpackets.ServerObjectInfo;
  94. import com.l2jserver.gameserver.network.serverpackets.SocialAction;
  95. import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
  96. import com.l2jserver.gameserver.util.Broadcast;
  97. import com.l2jserver.util.Rnd;
  98. /**
  99. * This class represents a Non-Player-Character in the world.<br>
  100. * It can be a monster or a friendly character.<br>
  101. * It uses a template to fetch some static values.
  102. */
  103. public class L2Npc extends L2Character
  104. {
  105. /** The interaction distance of the L2NpcInstance(is used as offset in MovetoLocation method) */
  106. public static final int INTERACTION_DISTANCE = 150;
  107. /** Maximum distance where the drop may appear given this NPC position. */
  108. public static final int RANDOM_ITEM_DROP_LIMIT = 70;
  109. /** The L2Spawn object that manage this L2NpcInstance */
  110. private L2Spawn _spawn;
  111. /** The flag to specify if this L2NpcInstance is busy */
  112. private boolean _isBusy = false;
  113. /** The busy message for this L2NpcInstance */
  114. private String _busyMessage = "";
  115. /** True if endDecayTask has already been called */
  116. private volatile boolean _isDecayed = false;
  117. /** The castle index in the array of L2Castle this L2NpcInstance belongs to */
  118. private int _castleIndex = -2;
  119. /** The fortress index in the array of L2Fort this L2NpcInstance belongs to */
  120. private int _fortIndex = -2;
  121. private boolean _eventMob = false;
  122. private boolean _isInTown = false;
  123. /** True if this L2Npc is autoattackable **/
  124. private boolean _isAutoAttackable = false;
  125. /** Time of last social packet broadcast */
  126. private long _lastSocialBroadcast = 0;
  127. /** Minimum interval between social packets */
  128. private static final int MINIMUM_SOCIAL_INTERVAL = 6000;
  129. /** Support for random animation switching */
  130. private boolean _isRandomAnimationEnabled = true;
  131. private boolean _isTalking = true;
  132. protected RandomAnimationTask _rAniTask = null;
  133. private int _currentLHandId; // normally this shouldn't change from the template, but there exist exceptions
  134. private int _currentRHandId; // normally this shouldn't change from the template, but there exist exceptions
  135. private int _currentEnchant; // normally this shouldn't change from the template, but there exist exceptions
  136. private double _currentCollisionHeight; // used for npc grow effect skills
  137. private double _currentCollisionRadius; // used for npc grow effect skills
  138. private int _soulshotamount = 0;
  139. private int _spiritshotamount = 0;
  140. private int _displayEffect = 0;
  141. private int _shotsMask = 0;
  142. private int _killingBlowWeaponId;
  143. /** Map of summoned NPCs by this NPC. */
  144. private volatile Map<Integer, L2Npc> _summonedNpcs = null;
  145. /**
  146. * Creates a NPC.
  147. * @param template the NPC template
  148. */
  149. public L2Npc(L2NpcTemplate template)
  150. {
  151. // Call the L2Character constructor to set the _template of the L2Character, copy skills from template to object
  152. // and link _calculators to NPC_STD_CALCULATOR
  153. super(template);
  154. setInstanceType(InstanceType.L2Npc);
  155. initCharStatusUpdateValues();
  156. // initialize the "current" equipment
  157. _currentLHandId = getTemplate().getLHandId();
  158. _currentRHandId = getTemplate().getRHandId();
  159. _currentEnchant = Config.ENABLE_RANDOM_ENCHANT_EFFECT ? Rnd.get(4, 21) : getTemplate().getWeaponEnchant();
  160. // initialize the "current" collisions
  161. _currentCollisionHeight = getTemplate().getfCollisionHeight();
  162. _currentCollisionRadius = getTemplate().getfCollisionRadius();
  163. setIsFlying(template.isFlying());
  164. }
  165. public int getSoulShotChance()
  166. {
  167. return getTemplate().getSoulShotChance();
  168. }
  169. public int getSpiritShotChance()
  170. {
  171. return getTemplate().getSpiritShotChance();
  172. }
  173. /**
  174. * @return the primary attack skill Id
  175. */
  176. public int getPrimarySkillId()
  177. {
  178. return getTemplate().getPrimarySkillId();
  179. }
  180. public int getMinSkillChance()
  181. {
  182. return getTemplate().getMinSkillChance();
  183. }
  184. public int getMaxSkillChance()
  185. {
  186. return getTemplate().getMaxSkillChance();
  187. }
  188. public boolean canMove()
  189. {
  190. return getTemplate().canMove();
  191. }
  192. public boolean isChaos()
  193. {
  194. return getTemplate().isChaos();
  195. }
  196. public int getDodge()
  197. {
  198. return getTemplate().getDodge();
  199. }
  200. public int getSSkillChance()
  201. {
  202. return getTemplate().getShortRangeSkillChance();
  203. }
  204. public int getLSkillChance()
  205. {
  206. return getTemplate().getLongRangeSkillChance();
  207. }
  208. public boolean hasLSkill()
  209. {
  210. return getTemplate().getLongRangeSkillId() > 0;
  211. }
  212. public boolean hasSSkill()
  213. {
  214. return getTemplate().getShortRangeSkillId() > 0;
  215. }
  216. public List<Skill> getLongRangeSkill()
  217. {
  218. final List<Skill> skilldata = new ArrayList<>();
  219. if (getTemplate().getLongRangeSkillId() == 0)
  220. {
  221. return skilldata;
  222. }
  223. switch (getTemplate().getLongRangeSkillId())
  224. {
  225. case -1:
  226. {
  227. final Collection<Skill> skills = getAllSkills();
  228. if (skills != null)
  229. {
  230. for (Skill sk : skills)
  231. {
  232. if ((sk == null) || sk.isPassive() || (sk.getTargetType() == L2TargetType.SELF))
  233. {
  234. continue;
  235. }
  236. if (sk.getCastRange() >= 200)
  237. {
  238. skilldata.add(sk);
  239. }
  240. }
  241. }
  242. break;
  243. }
  244. case 1:
  245. {
  246. for (Skill sk : getTemplate().getAISkills(AISkillScope.UNIVERSAL))
  247. {
  248. if (sk.getCastRange() >= 200)
  249. {
  250. skilldata.add(sk);
  251. }
  252. }
  253. break;
  254. }
  255. default:
  256. {
  257. for (Skill sk : getAllSkills())
  258. {
  259. if (sk.getId() == getTemplate().getLongRangeSkillId())
  260. {
  261. skilldata.add(sk);
  262. }
  263. }
  264. }
  265. }
  266. return skilldata;
  267. }
  268. public List<Skill> getShortRangeSkill()
  269. {
  270. final List<Skill> skilldata = new ArrayList<>();
  271. if (getTemplate().getShortRangeSkillId() == 0)
  272. {
  273. return skilldata;
  274. }
  275. switch (getTemplate().getShortRangeSkillId())
  276. {
  277. case -1:
  278. {
  279. Collection<Skill> skills = getAllSkills();
  280. if (skills != null)
  281. {
  282. for (Skill sk : skills)
  283. {
  284. if ((sk == null) || sk.isPassive() || (sk.getTargetType() == L2TargetType.SELF))
  285. {
  286. continue;
  287. }
  288. if (sk.getCastRange() <= 200)
  289. {
  290. skilldata.add(sk);
  291. }
  292. }
  293. }
  294. break;
  295. }
  296. case 1:
  297. {
  298. for (Skill sk : getTemplate().getAISkills(AISkillScope.UNIVERSAL))
  299. {
  300. if (sk.getCastRange() <= 200)
  301. {
  302. skilldata.add(sk);
  303. }
  304. }
  305. break;
  306. }
  307. default:
  308. {
  309. for (Skill sk : getAllSkills())
  310. {
  311. if (sk.getId() == getTemplate().getShortRangeSkillId())
  312. {
  313. skilldata.add(sk);
  314. }
  315. }
  316. }
  317. }
  318. return skilldata;
  319. }
  320. /** Task launching the function onRandomAnimation() */
  321. protected class RandomAnimationTask implements Runnable
  322. {
  323. @Override
  324. public void run()
  325. {
  326. try
  327. {
  328. if (isMob())
  329. {
  330. // Cancel further animation timers until intention is changed to ACTIVE again.
  331. if (getAI().getIntention() != AI_INTENTION_ACTIVE)
  332. {
  333. return;
  334. }
  335. }
  336. else
  337. {
  338. if (!isInActiveRegion())
  339. {
  340. return;
  341. }
  342. }
  343. if (!(isDead() || isStunned() || isSleeping() || isParalyzed()))
  344. {
  345. onRandomAnimation(Rnd.get(2, 3));
  346. }
  347. startRandomAnimationTimer();
  348. }
  349. catch (Exception e)
  350. {
  351. _log.log(Level.SEVERE, "", e);
  352. }
  353. }
  354. }
  355. /**
  356. * Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance and create a new RandomAnimation Task.
  357. * @param animationId
  358. */
  359. public void onRandomAnimation(int animationId)
  360. {
  361. // Send a packet SocialAction to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
  362. long now = System.currentTimeMillis();
  363. if ((now - _lastSocialBroadcast) > MINIMUM_SOCIAL_INTERVAL)
  364. {
  365. _lastSocialBroadcast = now;
  366. broadcastPacket(new SocialAction(getObjectId(), animationId));
  367. }
  368. }
  369. /**
  370. * Create a RandomAnimation Task that will be launched after the calculated delay.
  371. */
  372. public void startRandomAnimationTimer()
  373. {
  374. if (!hasRandomAnimation())
  375. {
  376. return;
  377. }
  378. int minWait = isMob() ? Config.MIN_MONSTER_ANIMATION : Config.MIN_NPC_ANIMATION;
  379. int maxWait = isMob() ? Config.MAX_MONSTER_ANIMATION : Config.MAX_NPC_ANIMATION;
  380. // Calculate the delay before the next animation
  381. int interval = Rnd.get(minWait, maxWait) * 1000;
  382. // Create a RandomAnimation Task that will be launched after the calculated delay
  383. _rAniTask = new RandomAnimationTask();
  384. ThreadPoolManager.getInstance().scheduleGeneral(_rAniTask, interval);
  385. }
  386. /**
  387. * @return true if the server allows Random Animation.
  388. */
  389. public boolean hasRandomAnimation()
  390. {
  391. return ((Config.MAX_NPC_ANIMATION > 0) && _isRandomAnimationEnabled && !getAiType().equals(AIType.CORPSE));
  392. }
  393. /**
  394. * Switches random Animation state into val.
  395. * @param val needed state of random animation
  396. */
  397. public void setRandomAnimationEnabled(boolean val)
  398. {
  399. _isRandomAnimationEnabled = val;
  400. }
  401. /**
  402. * @return {@code true}, if random animation is enabled, {@code false} otherwise.
  403. */
  404. public boolean isRandomAnimationEnabled()
  405. {
  406. return _isRandomAnimationEnabled;
  407. }
  408. @Override
  409. public NpcKnownList getKnownList()
  410. {
  411. return (NpcKnownList) super.getKnownList();
  412. }
  413. @Override
  414. public void initKnownList()
  415. {
  416. setKnownList(new NpcKnownList(this));
  417. }
  418. @Override
  419. public NpcStat getStat()
  420. {
  421. return (NpcStat) super.getStat();
  422. }
  423. @Override
  424. public void initCharStat()
  425. {
  426. setStat(new NpcStat(this));
  427. }
  428. @Override
  429. public NpcStatus getStatus()
  430. {
  431. return (NpcStatus) super.getStatus();
  432. }
  433. @Override
  434. public void initCharStatus()
  435. {
  436. setStatus(new NpcStatus(this));
  437. }
  438. /** Return the L2NpcTemplate of the L2NpcInstance. */
  439. @Override
  440. public final L2NpcTemplate getTemplate()
  441. {
  442. return (L2NpcTemplate) super.getTemplate();
  443. }
  444. /**
  445. * Gets the NPC ID.
  446. * @return the NPC ID
  447. */
  448. @Override
  449. public int getId()
  450. {
  451. return getTemplate().getId();
  452. }
  453. @Override
  454. public boolean canBeAttacked()
  455. {
  456. return Config.ALT_ATTACKABLE_NPCS;
  457. }
  458. /**
  459. * Return the Level of this L2NpcInstance contained in the L2NpcTemplate.
  460. */
  461. @Override
  462. public final int getLevel()
  463. {
  464. return getTemplate().getLevel();
  465. }
  466. /**
  467. * @return True if the L2NpcInstance is aggressive (ex : L2MonsterInstance in function of aggroRange).
  468. */
  469. public boolean isAggressive()
  470. {
  471. return false;
  472. }
  473. /**
  474. * @return the Aggro Range of this L2NpcInstance either contained in the L2NpcTemplate, or overriden by spawnlist AI value.
  475. */
  476. public int getAggroRange()
  477. {
  478. return hasAIValue("aggroRange") ? getAIValue("aggroRange") : getTemplate().getAggroRange();
  479. }
  480. public boolean isInMyClan(L2Npc npc)
  481. {
  482. return getTemplate().isClan(npc.getTemplate().getClans());
  483. }
  484. /**
  485. * Return True if this L2NpcInstance is undead in function of the L2NpcTemplate.
  486. */
  487. @Override
  488. public boolean isUndead()
  489. {
  490. return getTemplate().getRace() == Race.UNDEAD;
  491. }
  492. /**
  493. * Send a packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance.
  494. */
  495. @Override
  496. public void updateAbnormalEffect()
  497. {
  498. // Send a Server->Client packet NpcInfo with state of abnormal effect to all L2PcInstance in the _KnownPlayers of the L2NpcInstance
  499. Collection<L2PcInstance> plrs = getKnownList().getKnownPlayers().values();
  500. for (L2PcInstance player : plrs)
  501. {
  502. if ((player == null) || !isVisibleFor(player))
  503. {
  504. continue;
  505. }
  506. if (getRunSpeed() == 0)
  507. {
  508. player.sendPacket(new ServerObjectInfo(this, player));
  509. }
  510. else
  511. {
  512. player.sendPacket(new AbstractNpcInfo.NpcInfo(this, player));
  513. }
  514. }
  515. }
  516. public boolean isEventMob()
  517. {
  518. return _eventMob;
  519. }
  520. public void setEventMob(boolean val)
  521. {
  522. _eventMob = val;
  523. }
  524. @Override
  525. public boolean isAutoAttackable(L2Character attacker)
  526. {
  527. return _isAutoAttackable;
  528. }
  529. public void setAutoAttackable(boolean flag)
  530. {
  531. _isAutoAttackable = flag;
  532. }
  533. /**
  534. * @return the Identifier of the item in the left hand of this L2NpcInstance contained in the L2NpcTemplate.
  535. */
  536. public int getLeftHandItem()
  537. {
  538. return _currentLHandId;
  539. }
  540. /**
  541. * @return the Identifier of the item in the right hand of this L2NpcInstance contained in the L2NpcTemplate.
  542. */
  543. public int getRightHandItem()
  544. {
  545. return _currentRHandId;
  546. }
  547. public int getEnchantEffect()
  548. {
  549. return _currentEnchant;
  550. }
  551. /**
  552. * @return the busy status of this L2NpcInstance.
  553. */
  554. public final boolean isBusy()
  555. {
  556. return _isBusy;
  557. }
  558. /**
  559. * @param isBusy the busy status of this L2Npc
  560. */
  561. public void setBusy(boolean isBusy)
  562. {
  563. _isBusy = isBusy;
  564. }
  565. /**
  566. * @return the busy message of this L2NpcInstance.
  567. */
  568. public final String getBusyMessage()
  569. {
  570. return _busyMessage;
  571. }
  572. /**
  573. * @param message the busy message of this L2Npc.
  574. */
  575. public void setBusyMessage(String message)
  576. {
  577. _busyMessage = message;
  578. }
  579. /**
  580. * @return true if this L2Npc instance can be warehouse manager.
  581. */
  582. public boolean isWarehouse()
  583. {
  584. return false;
  585. }
  586. public boolean canTarget(L2PcInstance player)
  587. {
  588. if (player.isOutOfControl())
  589. {
  590. player.sendPacket(ActionFailed.STATIC_PACKET);
  591. return false;
  592. }
  593. if (player.isLockedTarget() && (player.getLockedTarget() != this))
  594. {
  595. player.sendPacket(SystemMessageId.FAILED_CHANGE_TARGET);
  596. player.sendPacket(ActionFailed.STATIC_PACKET);
  597. return false;
  598. }
  599. // TODO: More checks...
  600. return true;
  601. }
  602. public boolean canInteract(L2PcInstance player)
  603. {
  604. if (player.isCastingNow() || player.isCastingSimultaneouslyNow())
  605. {
  606. return false;
  607. }
  608. else if (player.isDead() || player.isFakeDeath())
  609. {
  610. return false;
  611. }
  612. else if (player.isSitting())
  613. {
  614. return false;
  615. }
  616. else if (player.getPrivateStoreType() != PrivateStoreType.NONE)
  617. {
  618. return false;
  619. }
  620. else if (!isInsideRadius(player, INTERACTION_DISTANCE, true, false))
  621. {
  622. return false;
  623. }
  624. else if ((player.getInstanceId() != getInstanceId()) && (player.getInstanceId() != -1))
  625. {
  626. return false;
  627. }
  628. else if (isBusy())
  629. {
  630. return false;
  631. }
  632. return true;
  633. }
  634. /**
  635. * @return the L2Castle this L2NpcInstance belongs to.
  636. */
  637. public final Castle getCastle()
  638. {
  639. // Get castle this NPC belongs to (excluding L2Attackable)
  640. if (_castleIndex < 0)
  641. {
  642. L2TownZone town = TownManager.getTown(getX(), getY(), getZ());
  643. if (town != null)
  644. {
  645. _castleIndex = CastleManager.getInstance().getCastleIndex(town.getTaxById());
  646. }
  647. if (_castleIndex < 0)
  648. {
  649. _castleIndex = CastleManager.getInstance().findNearestCastleIndex(this);
  650. }
  651. else
  652. {
  653. _isInTown = true; // Npc was spawned in town
  654. }
  655. }
  656. if (_castleIndex < 0)
  657. {
  658. return null;
  659. }
  660. return CastleManager.getInstance().getCastles().get(_castleIndex);
  661. }
  662. /**
  663. * Verify if the given player is this NPC's lord.
  664. * @param player the player to check
  665. * @return {@code true} if the player is clan leader and owner of a castle of fort that this NPC belongs to, {@code false} otherwise
  666. */
  667. public boolean isMyLord(L2PcInstance player)
  668. {
  669. if (player.isClanLeader())
  670. {
  671. final int castleId = getCastle() != null ? getCastle().getResidenceId() : -1;
  672. final int fortId = getFort() != null ? getFort().getResidenceId() : -1;
  673. return (player.getClan().getCastleId() == castleId) || (player.getClan().getFortId() == fortId);
  674. }
  675. return false;
  676. }
  677. public final SiegableHall getConquerableHall()
  678. {
  679. return CHSiegeManager.getInstance().getNearbyClanHall(getX(), getY(), 10000);
  680. }
  681. /**
  682. * Return closest castle in defined distance
  683. * @param maxDistance long
  684. * @return Castle
  685. */
  686. public final Castle getCastle(long maxDistance)
  687. {
  688. int index = CastleManager.getInstance().findNearestCastleIndex(this, maxDistance);
  689. if (index < 0)
  690. {
  691. return null;
  692. }
  693. return CastleManager.getInstance().getCastles().get(index);
  694. }
  695. /**
  696. * @return the L2Fort this L2NpcInstance belongs to.
  697. */
  698. public final Fort getFort()
  699. {
  700. // Get Fort this NPC belongs to (excluding L2Attackable)
  701. if (_fortIndex < 0)
  702. {
  703. Fort fort = FortManager.getInstance().getFort(getX(), getY(), getZ());
  704. if (fort != null)
  705. {
  706. _fortIndex = FortManager.getInstance().getFortIndex(fort.getResidenceId());
  707. }
  708. if (_fortIndex < 0)
  709. {
  710. _fortIndex = FortManager.getInstance().findNearestFortIndex(this);
  711. }
  712. }
  713. if (_fortIndex < 0)
  714. {
  715. return null;
  716. }
  717. return FortManager.getInstance().getForts().get(_fortIndex);
  718. }
  719. /**
  720. * Return closest Fort in defined distance
  721. * @param maxDistance long
  722. * @return Fort
  723. */
  724. public final Fort getFort(long maxDistance)
  725. {
  726. int index = FortManager.getInstance().findNearestFortIndex(this, maxDistance);
  727. if (index < 0)
  728. {
  729. return null;
  730. }
  731. return FortManager.getInstance().getForts().get(index);
  732. }
  733. public final boolean getIsInTown()
  734. {
  735. if (_castleIndex < 0)
  736. {
  737. getCastle();
  738. }
  739. return _isInTown;
  740. }
  741. /**
  742. * Open a quest or chat window on client with the text of the L2NpcInstance in function of the command.<br>
  743. * <B><U> Example of use </U> :</B>
  744. * <ul>
  745. * <li>Client packet : RequestBypassToServer</li>
  746. * </ul>
  747. * @param player
  748. * @param command The command string received from client
  749. */
  750. public void onBypassFeedback(L2PcInstance player, String command)
  751. {
  752. // if (canInteract(player))
  753. {
  754. if (isBusy() && (getBusyMessage().length() > 0))
  755. {
  756. player.sendPacket(ActionFailed.STATIC_PACKET);
  757. final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
  758. html.setFile(player.getHtmlPrefix(), "data/html/npcbusy.htm");
  759. html.replace("%busymessage%", getBusyMessage());
  760. html.replace("%npcname%", getName());
  761. html.replace("%playername%", player.getName());
  762. player.sendPacket(html);
  763. }
  764. else
  765. {
  766. IBypassHandler handler = BypassHandler.getInstance().getHandler(command);
  767. if (handler != null)
  768. {
  769. handler.useBypass(command, player, this);
  770. }
  771. else
  772. {
  773. _log.info(getClass().getSimpleName() + ": Unknown NPC bypass: \"" + command + "\" NpcId: " + getId());
  774. }
  775. }
  776. }
  777. }
  778. /**
  779. * Return null (regular NPCs don't have weapons instances).
  780. */
  781. @Override
  782. public L2ItemInstance getActiveWeaponInstance()
  783. {
  784. return null;
  785. }
  786. /**
  787. * Return the weapon item equipped in the right hand of the L2NpcInstance or null.
  788. */
  789. @Override
  790. public L2Weapon getActiveWeaponItem()
  791. {
  792. // Get the weapon identifier equipped in the right hand of the L2NpcInstance
  793. int weaponId = getTemplate().getRHandId();
  794. if (weaponId < 1)
  795. {
  796. return null;
  797. }
  798. // Get the weapon item equipped in the right hand of the L2NpcInstance
  799. L2Item item = ItemTable.getInstance().getTemplate(getTemplate().getRHandId());
  800. if (!(item instanceof L2Weapon))
  801. {
  802. return null;
  803. }
  804. return (L2Weapon) item;
  805. }
  806. /**
  807. * Return null (regular NPCs don't have weapons instances).
  808. */
  809. @Override
  810. public L2ItemInstance getSecondaryWeaponInstance()
  811. {
  812. return null;
  813. }
  814. /**
  815. * Return the weapon item equipped in the left hand of the L2NpcInstance or null.
  816. */
  817. @Override
  818. public L2Weapon getSecondaryWeaponItem()
  819. {
  820. // Get the weapon identifier equipped in the right hand of the L2NpcInstance
  821. int weaponId = getTemplate().getLHandId();
  822. if (weaponId < 1)
  823. {
  824. return null;
  825. }
  826. // Get the weapon item equipped in the right hand of the L2NpcInstance
  827. L2Item item = ItemTable.getInstance().getTemplate(getTemplate().getLHandId());
  828. if (!(item instanceof L2Weapon))
  829. {
  830. return null;
  831. }
  832. return (L2Weapon) item;
  833. }
  834. /**
  835. * Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance.
  836. * @param player The L2PcInstance who talks with the L2NpcInstance
  837. * @param content The text of the L2NpcMessage
  838. */
  839. public void insertObjectIdAndShowChatWindow(L2PcInstance player, String content)
  840. {
  841. // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
  842. content = content.replaceAll("%objectId%", String.valueOf(getObjectId()));
  843. player.sendPacket(new NpcHtmlMessage(getObjectId(), content));
  844. }
  845. /**
  846. * <B><U Format of the pathfile</U>:</B>
  847. * <ul>
  848. * <li>if the file exists on the server (page number = 0) : <B>data/html/default/12006.htm</B> (npcId-page number)</li>
  849. * <li>if the file exists on the server (page number > 0) : <B>data/html/default/12006-1.htm</B> (npcId-page number)</li>
  850. * <li>if the file doesn't exist on the server : <B>data/html/npcdefault.htm</B> (message : "I have nothing to say to you")</li>
  851. * </ul>
  852. * @param npcId The Identifier of the L2NpcInstance whose text must be display
  853. * @param val The number of the page to display
  854. * @return the pathfile of the selected HTML file in function of the npcId and of the page number.
  855. */
  856. public String getHtmlPath(int npcId, int val)
  857. {
  858. String pom = "";
  859. if (val == 0)
  860. {
  861. pom = "" + npcId;
  862. }
  863. else
  864. {
  865. pom = npcId + "-" + val;
  866. }
  867. String temp = "data/html/default/" + pom + ".htm";
  868. if (!Config.LAZY_CACHE)
  869. {
  870. // If not running lazy cache the file must be in the cache or it doesnt exist
  871. if (HtmCache.getInstance().contains(temp))
  872. {
  873. return temp;
  874. }
  875. }
  876. else
  877. {
  878. if (HtmCache.getInstance().isLoadable(temp))
  879. {
  880. return temp;
  881. }
  882. }
  883. // If the file is not found, the standard message "I have nothing to say to you" is returned
  884. return "data/html/npcdefault.htm";
  885. }
  886. public void showChatWindow(L2PcInstance player)
  887. {
  888. showChatWindow(player, 0);
  889. }
  890. /**
  891. * Returns true if html exists
  892. * @param player
  893. * @param type
  894. * @return boolean
  895. */
  896. private boolean showPkDenyChatWindow(L2PcInstance player, String type)
  897. {
  898. final String html = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/" + type + "/" + getId() + "-pk.htm");
  899. if (html != null)
  900. {
  901. insertObjectIdAndShowChatWindow(player, html);
  902. player.sendPacket(ActionFailed.STATIC_PACKET);
  903. return true;
  904. }
  905. return false;
  906. }
  907. /**
  908. * Open a chat window on client with the text of the L2NpcInstance.<br>
  909. * <B><U>Actions</U>:</B>
  910. * <ul>
  911. * <li>Get the text of the selected HTML file in function of the npcId and of the page number</li>
  912. * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li>
  913. * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet</li>
  914. * </ul>
  915. * @param player The L2PcInstance that talk with the L2NpcInstance
  916. * @param val The number of the page of the L2NpcInstance to display
  917. */
  918. public void showChatWindow(L2PcInstance player, int val)
  919. {
  920. if (!isTalking())
  921. {
  922. player.sendPacket(ActionFailed.STATIC_PACKET);
  923. return;
  924. }
  925. if (player.isCursedWeaponEquipped() && (!(player.getTarget() instanceof L2ClanHallManagerInstance) || !(player.getTarget() instanceof L2DoormenInstance)))
  926. {
  927. player.setTarget(player);
  928. return;
  929. }
  930. if (player.getKarma() > 0)
  931. {
  932. if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && (this instanceof L2MerchantInstance))
  933. {
  934. if (showPkDenyChatWindow(player, "merchant"))
  935. {
  936. return;
  937. }
  938. }
  939. else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_GK && (this instanceof L2TeleporterInstance))
  940. {
  941. if (showPkDenyChatWindow(player, "teleporter"))
  942. {
  943. return;
  944. }
  945. }
  946. else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_USE_WAREHOUSE && (this instanceof L2WarehouseInstance))
  947. {
  948. if (showPkDenyChatWindow(player, "warehouse"))
  949. {
  950. return;
  951. }
  952. }
  953. else if (!Config.ALT_GAME_KARMA_PLAYER_CAN_SHOP && (this instanceof L2FishermanInstance))
  954. {
  955. if (showPkDenyChatWindow(player, "fisherman"))
  956. {
  957. return;
  958. }
  959. }
  960. }
  961. if (getTemplate().isType("L2Auctioneer") && (val == 0))
  962. {
  963. return;
  964. }
  965. int npcId = getTemplate().getId();
  966. /* For use with Seven Signs implementation */
  967. String filename = SevenSigns.SEVEN_SIGNS_HTML_PATH;
  968. int sealAvariceOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_AVARICE);
  969. int sealGnosisOwner = SevenSigns.getInstance().getSealOwner(SevenSigns.SEAL_GNOSIS);
  970. int playerCabal = SevenSigns.getInstance().getPlayerCabal(player.getObjectId());
  971. int compWinner = SevenSigns.getInstance().getCabalHighestScore();
  972. switch (npcId)
  973. {
  974. case 31127: //
  975. case 31128: //
  976. case 31129: // Dawn Festival Guides
  977. case 31130: //
  978. case 31131: //
  979. filename += "festival/dawn_guide.htm";
  980. break;
  981. case 31137: //
  982. case 31138: //
  983. case 31139: // Dusk Festival Guides
  984. case 31140: //
  985. case 31141: //
  986. filename += "festival/dusk_guide.htm";
  987. break;
  988. case 31092: // Black Marketeer of Mammon
  989. filename += "blkmrkt_1.htm";
  990. break;
  991. case 31113: // Merchant of Mammon
  992. if (Config.ALT_STRICT_SEVENSIGNS)
  993. {
  994. switch (compWinner)
  995. {
  996. case SevenSigns.CABAL_DAWN:
  997. if ((playerCabal != compWinner) || (playerCabal != sealAvariceOwner))
  998. {
  999. player.sendPacket(SystemMessageId.CAN_BE_USED_BY_DAWN);
  1000. player.sendPacket(ActionFailed.STATIC_PACKET);
  1001. return;
  1002. }
  1003. break;
  1004. case SevenSigns.CABAL_DUSK:
  1005. if ((playerCabal != compWinner) || (playerCabal != sealAvariceOwner))
  1006. {
  1007. player.sendPacket(SystemMessageId.CAN_BE_USED_BY_DUSK);
  1008. player.sendPacket(ActionFailed.STATIC_PACKET);
  1009. return;
  1010. }
  1011. break;
  1012. default:
  1013. player.sendPacket(SystemMessageId.SSQ_COMPETITION_UNDERWAY);
  1014. return;
  1015. }
  1016. }
  1017. filename += "mammmerch_1.htm";
  1018. break;
  1019. case 31126: // Blacksmith of Mammon
  1020. if (Config.ALT_STRICT_SEVENSIGNS)
  1021. {
  1022. switch (compWinner)
  1023. {
  1024. case SevenSigns.CABAL_DAWN:
  1025. if ((playerCabal != compWinner) || (playerCabal != sealGnosisOwner))
  1026. {
  1027. player.sendPacket(SystemMessageId.CAN_BE_USED_BY_DAWN);
  1028. player.sendPacket(ActionFailed.STATIC_PACKET);
  1029. return;
  1030. }
  1031. break;
  1032. case SevenSigns.CABAL_DUSK:
  1033. if ((playerCabal != compWinner) || (playerCabal != sealGnosisOwner))
  1034. {
  1035. player.sendPacket(SystemMessageId.CAN_BE_USED_BY_DUSK);
  1036. player.sendPacket(ActionFailed.STATIC_PACKET);
  1037. return;
  1038. }
  1039. break;
  1040. default:
  1041. player.sendPacket(SystemMessageId.SSQ_COMPETITION_UNDERWAY);
  1042. return;
  1043. }
  1044. }
  1045. filename += "mammblack_1.htm";
  1046. break;
  1047. case 31132:
  1048. case 31133:
  1049. case 31134:
  1050. case 31135:
  1051. case 31136: // Festival Witches
  1052. case 31142:
  1053. case 31143:
  1054. case 31144:
  1055. case 31145:
  1056. case 31146:
  1057. filename += "festival/festival_witch.htm";
  1058. break;
  1059. case 31688:
  1060. if (player.isNoble())
  1061. {
  1062. filename = Olympiad.OLYMPIAD_HTML_PATH + "noble_main.htm";
  1063. }
  1064. else
  1065. {
  1066. filename = (getHtmlPath(npcId, val));
  1067. }
  1068. break;
  1069. case 31690:
  1070. case 31769:
  1071. case 31770:
  1072. case 31771:
  1073. case 31772:
  1074. if (player.isHero() || player.isNoble())
  1075. {
  1076. filename = Olympiad.OLYMPIAD_HTML_PATH + "hero_main.htm";
  1077. }
  1078. else
  1079. {
  1080. filename = (getHtmlPath(npcId, val));
  1081. }
  1082. break;
  1083. case 36402:
  1084. if (player.getOlympiadBuffCount() > 0)
  1085. {
  1086. filename = (player.getOlympiadBuffCount() == Config.ALT_OLY_MAX_BUFFS ? Olympiad.OLYMPIAD_HTML_PATH + "olympiad_buffs.htm" : Olympiad.OLYMPIAD_HTML_PATH + "olympiad_5buffs.htm");
  1087. }
  1088. else
  1089. {
  1090. filename = Olympiad.OLYMPIAD_HTML_PATH + "olympiad_nobuffs.htm";
  1091. }
  1092. break;
  1093. case 30298: // Blacksmith Pinter
  1094. if (player.isAcademyMember())
  1095. {
  1096. filename = (getHtmlPath(npcId, 1));
  1097. }
  1098. else
  1099. {
  1100. filename = (getHtmlPath(npcId, val));
  1101. }
  1102. break;
  1103. default:
  1104. if ((npcId >= 31865) && (npcId <= 31918))
  1105. {
  1106. if (val == 0)
  1107. {
  1108. filename += "rift/GuardianOfBorder.htm";
  1109. }
  1110. else
  1111. {
  1112. filename += "rift/GuardianOfBorder-" + val + ".htm";
  1113. }
  1114. break;
  1115. }
  1116. if (((npcId >= 31093) && (npcId <= 31094)) || ((npcId >= 31172) && (npcId <= 31201)) || ((npcId >= 31239) && (npcId <= 31254)))
  1117. {
  1118. return;
  1119. }
  1120. // Get the text of the selected HTML file in function of the npcId and of the page number
  1121. filename = (getHtmlPath(npcId, val));
  1122. break;
  1123. }
  1124. // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
  1125. final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
  1126. html.setFile(player.getHtmlPrefix(), filename);
  1127. if (this instanceof L2MerchantInstance)
  1128. {
  1129. if (Config.LIST_PET_RENT_NPC.contains(npcId))
  1130. {
  1131. html.replace("_Quest", "_RentPet\">Rent Pet</a><br><a action=\"bypass -h npc_%objectId%_Quest");
  1132. }
  1133. }
  1134. html.replace("%objectId%", String.valueOf(getObjectId()));
  1135. html.replace("%festivalMins%", SevenSignsFestival.getInstance().getTimeToNextFestivalStr());
  1136. player.sendPacket(html);
  1137. // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
  1138. player.sendPacket(ActionFailed.STATIC_PACKET);
  1139. }
  1140. /**
  1141. * Open a chat window on client with the text specified by the given file name and path, relative to the datapack root.
  1142. * @param player The L2PcInstance that talk with the L2NpcInstance
  1143. * @param filename The filename that contains the text to send
  1144. */
  1145. public void showChatWindow(L2PcInstance player, String filename)
  1146. {
  1147. // Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance
  1148. final NpcHtmlMessage html = new NpcHtmlMessage(getObjectId());
  1149. html.setFile(player.getHtmlPrefix(), filename);
  1150. html.replace("%objectId%", String.valueOf(getObjectId()));
  1151. player.sendPacket(html);
  1152. // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
  1153. player.sendPacket(ActionFailed.STATIC_PACKET);
  1154. }
  1155. /**
  1156. * @return the Exp Reward of this L2Npc (modified by RATE_XP).
  1157. */
  1158. public long getExpReward()
  1159. {
  1160. return (long) (getLevel() * getLevel() * getTemplate().getExpRate() * Config.RATE_XP);
  1161. }
  1162. /**
  1163. * @return the SP Reward of this L2Npc (modified by RATE_SP).
  1164. */
  1165. public int getSpReward()
  1166. {
  1167. return (int) (getTemplate().getSP() * Config.RATE_SP);
  1168. }
  1169. /**
  1170. * Kill the L2NpcInstance (the corpse disappeared after 7 seconds).<br>
  1171. * <B><U>Actions</U>:</B>
  1172. * <ul>
  1173. * <li>Create a DecayTask to remove the corpse of the L2NpcInstance after 7 seconds</li>
  1174. * <li>Set target to null and cancel Attack or Cast</li>
  1175. * <li>Stop movement</li>
  1176. * <li>Stop HP/MP/CP Regeneration task</li>
  1177. * <li>Stop all active skills effects in progress on the L2Character</li>
  1178. * <li>Send the Server->Client packet StatusUpdate with current HP and MP to all other L2PcInstance to inform</li>
  1179. * <li>Notify L2Character AI</li>
  1180. * </ul>
  1181. * @param killer The L2Character who killed it
  1182. */
  1183. @Override
  1184. public boolean doDie(L2Character killer)
  1185. {
  1186. if (!super.doDie(killer))
  1187. {
  1188. return false;
  1189. }
  1190. // normally this wouldn't really be needed, but for those few exceptions,
  1191. // we do need to reset the weapons back to the initial template weapon.
  1192. _currentLHandId = getTemplate().getLHandId();
  1193. _currentRHandId = getTemplate().getRHandId();
  1194. _currentCollisionHeight = getTemplate().getfCollisionHeight();
  1195. _currentCollisionRadius = getTemplate().getfCollisionRadius();
  1196. final L2Weapon weapon = (killer != null) ? killer.getActiveWeaponItem() : null;
  1197. _killingBlowWeaponId = (weapon != null) ? weapon.getId() : 0;
  1198. DecayTaskManager.getInstance().add(this);
  1199. return true;
  1200. }
  1201. /**
  1202. * Set the spawn of the L2NpcInstance.
  1203. * @param spawn The L2Spawn that manage the L2NpcInstance
  1204. */
  1205. public void setSpawn(L2Spawn spawn)
  1206. {
  1207. _spawn = spawn;
  1208. }
  1209. @Override
  1210. public void onSpawn()
  1211. {
  1212. super.onSpawn();
  1213. // Recharge shots
  1214. _soulshotamount = getTemplate().getSoulShot();
  1215. _spiritshotamount = getTemplate().getSpiritShot();
  1216. _killingBlowWeaponId = 0;
  1217. if (isTeleporting())
  1218. {
  1219. EventDispatcher.getInstance().notifyEventAsync(new OnNpcTeleport(this), this);
  1220. }
  1221. else
  1222. {
  1223. EventDispatcher.getInstance().notifyEventAsync(new OnNpcSpawn(this), this);
  1224. }
  1225. if (!isTeleporting())
  1226. {
  1227. WalkingManager.getInstance().onSpawn(this);
  1228. }
  1229. }
  1230. /**
  1231. * Remove the L2NpcInstance from the world and update its spawn object (for a complete removal use the deleteMe method).<br>
  1232. * <B><U>Actions</U>:</B>
  1233. * <ul>
  1234. * <li>Remove the L2NpcInstance from the world when the decay task is launched</li>
  1235. * <li>Decrease its spawn counter</li>
  1236. * <li>Manage Siege task (killFlag, killCT)</li>
  1237. * </ul>
  1238. * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T REMOVE the object from _allObjects of L2World </B></FONT><BR>
  1239. * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T SEND Server->Client packets to players</B></FONT>
  1240. */
  1241. @Override
  1242. public void onDecay()
  1243. {
  1244. if (isDecayed())
  1245. {
  1246. return;
  1247. }
  1248. setDecayed(true);
  1249. // Remove the L2NpcInstance from the world when the decay task is launched
  1250. super.onDecay();
  1251. // Decrease its spawn counter
  1252. if (_spawn != null)
  1253. {
  1254. _spawn.decreaseCount(this);
  1255. }
  1256. // Notify Walking Manager
  1257. WalkingManager.getInstance().onDeath(this);
  1258. // Removes itself from the summoned list.
  1259. final L2Character summoner = getSummoner();
  1260. if ((summoner != null) && summoner.isNpc())
  1261. {
  1262. ((L2Npc) summoner).removeSummonedNpc(getObjectId());
  1263. }
  1264. }
  1265. /**
  1266. * Remove PROPERLY the L2NpcInstance from the world.<br>
  1267. * <B><U>Actions</U>:</B>
  1268. * <ul>
  1269. * <li>Remove the L2NpcInstance from the world and update its spawn object</li>
  1270. * <li>Remove all L2Object from _knownObjects and _knownPlayer of the L2NpcInstance then cancel Attack or Cast and notify AI</li>
  1271. * <li>Remove L2Object object from _allObjects of L2World</li>
  1272. * </ul>
  1273. * <FONT COLOR=#FF0000><B><U>Caution</U>: This method DOESN'T SEND Server->Client packets to players</B></FONT><br>
  1274. * UnAfraid: TODO: Add Listener here
  1275. */
  1276. @Override
  1277. public boolean deleteMe()
  1278. {
  1279. try
  1280. {
  1281. onDecay();
  1282. }
  1283. catch (Exception e)
  1284. {
  1285. _log.log(Level.SEVERE, "Failed decayMe().", e);
  1286. }
  1287. if (isChannelized())
  1288. {
  1289. getSkillChannelized().abortChannelization();
  1290. }
  1291. final L2WorldRegion oldRegion = getWorldRegion();
  1292. if (oldRegion != null)
  1293. {
  1294. oldRegion.removeFromZones(this);
  1295. }
  1296. // Remove all L2Object from _knownObjects and _knownPlayer of the L2Character then cancel Attack or Cast and notify AI
  1297. try
  1298. {
  1299. getKnownList().removeAllKnownObjects();
  1300. }
  1301. catch (Exception e)
  1302. {
  1303. _log.log(Level.SEVERE, "Failed removing cleaning knownlist.", e);
  1304. }
  1305. // Remove L2Object object from _allObjects of L2World
  1306. L2World.getInstance().removeObject(this);
  1307. return super.deleteMe();
  1308. }
  1309. /**
  1310. * @return the L2Spawn object that manage this L2NpcInstance.
  1311. */
  1312. public L2Spawn getSpawn()
  1313. {
  1314. return _spawn;
  1315. }
  1316. @Override
  1317. public String toString()
  1318. {
  1319. return getClass().getSimpleName() + ":" + getName() + "(" + getId() + ")" + "[" + getObjectId() + "]";
  1320. }
  1321. public boolean isDecayed()
  1322. {
  1323. return _isDecayed;
  1324. }
  1325. public void setDecayed(boolean decayed)
  1326. {
  1327. _isDecayed = decayed;
  1328. }
  1329. public void endDecayTask()
  1330. {
  1331. if (!isDecayed())
  1332. {
  1333. DecayTaskManager.getInstance().cancel(this);
  1334. onDecay();
  1335. }
  1336. }
  1337. public boolean isMob() // rather delete this check
  1338. {
  1339. return false; // This means we use MAX_NPC_ANIMATION instead of MAX_MONSTER_ANIMATION
  1340. }
  1341. // Two functions to change the appearance of the equipped weapons on the NPC
  1342. // This is only useful for a few NPCs and is most likely going to be called from AI
  1343. public void setLHandId(int newWeaponId)
  1344. {
  1345. _currentLHandId = newWeaponId;
  1346. updateAbnormalEffect();
  1347. }
  1348. public void setRHandId(int newWeaponId)
  1349. {
  1350. _currentRHandId = newWeaponId;
  1351. updateAbnormalEffect();
  1352. }
  1353. public void setLRHandId(int newLWeaponId, int newRWeaponId)
  1354. {
  1355. _currentRHandId = newRWeaponId;
  1356. _currentLHandId = newLWeaponId;
  1357. updateAbnormalEffect();
  1358. }
  1359. public void setEnchant(int newEnchantValue)
  1360. {
  1361. _currentEnchant = newEnchantValue;
  1362. updateAbnormalEffect();
  1363. }
  1364. public boolean isShowName()
  1365. {
  1366. return getTemplate().isShowName();
  1367. }
  1368. @Override
  1369. public boolean isTargetable()
  1370. {
  1371. return getTemplate().isTargetable();
  1372. }
  1373. public void setCollisionHeight(double height)
  1374. {
  1375. _currentCollisionHeight = height;
  1376. }
  1377. public void setCollisionRadius(double radius)
  1378. {
  1379. _currentCollisionRadius = radius;
  1380. }
  1381. public double getCollisionHeight()
  1382. {
  1383. return _currentCollisionHeight;
  1384. }
  1385. public double getCollisionRadius()
  1386. {
  1387. return _currentCollisionRadius;
  1388. }
  1389. @Override
  1390. public void sendInfo(L2PcInstance activeChar)
  1391. {
  1392. if (isVisibleFor(activeChar))
  1393. {
  1394. if (Config.CHECK_KNOWN && activeChar.isGM())
  1395. {
  1396. activeChar.sendMessage("Added NPC: " + getName());
  1397. }
  1398. if (getRunSpeed() == 0)
  1399. {
  1400. activeChar.sendPacket(new ServerObjectInfo(this, activeChar));
  1401. }
  1402. else
  1403. {
  1404. activeChar.sendPacket(new AbstractNpcInfo.NpcInfo(this, activeChar));
  1405. }
  1406. }
  1407. }
  1408. public void showNoTeachHtml(L2PcInstance player)
  1409. {
  1410. int npcId = getId();
  1411. String html = "";
  1412. if (this instanceof L2WarehouseInstance)
  1413. {
  1414. html = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/warehouse/" + npcId + "-noteach.htm");
  1415. }
  1416. else if (this instanceof L2TrainerInstance)
  1417. {
  1418. html = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/trainer/" + npcId + "-noteach.htm");
  1419. // Trainer Healer?
  1420. if (html == null)
  1421. {
  1422. html = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/scripts/ai/npc/Trainers/HealerTrainer/" + npcId + "-noteach.html");
  1423. }
  1424. }
  1425. final NpcHtmlMessage noTeachMsg = new NpcHtmlMessage(getObjectId());
  1426. if (html == null)
  1427. {
  1428. _log.warning("Npc " + npcId + " missing noTeach html!");
  1429. noTeachMsg.setHtml("<html><body>I cannot teach you any skills.<br>You must find your current class teachers.</body></html>");
  1430. }
  1431. else
  1432. {
  1433. noTeachMsg.setHtml(html);
  1434. noTeachMsg.replace("%objectId%", String.valueOf(getObjectId()));
  1435. }
  1436. player.sendPacket(noTeachMsg);
  1437. }
  1438. public L2Npc scheduleDespawn(long delay)
  1439. {
  1440. ThreadPoolManager.getInstance().scheduleGeneral(() ->
  1441. {
  1442. if (!isDecayed())
  1443. {
  1444. deleteMe();
  1445. }
  1446. }, delay);
  1447. return this;
  1448. }
  1449. @Override
  1450. protected final void notifyQuestEventSkillFinished(Skill skill, L2Object target)
  1451. {
  1452. if ((target != null) && target.isPlayable())
  1453. {
  1454. EventDispatcher.getInstance().notifyEventAsync(new OnNpcSkillFinished(this, target.getActingPlayer(), skill), this);
  1455. }
  1456. }
  1457. @Override
  1458. public boolean isMovementDisabled()
  1459. {
  1460. return super.isMovementDisabled() || !canMove() || getAiType().equals(AIType.CORPSE);
  1461. }
  1462. public AIType getAiType()
  1463. {
  1464. return getTemplate().getAIType();
  1465. }
  1466. public void setDisplayEffect(int val)
  1467. {
  1468. if (val != _displayEffect)
  1469. {
  1470. _displayEffect = val;
  1471. broadcastPacket(new ExChangeNpcState(getObjectId(), val));
  1472. }
  1473. }
  1474. public int getDisplayEffect()
  1475. {
  1476. return _displayEffect;
  1477. }
  1478. public int getColorEffect()
  1479. {
  1480. return 0;
  1481. }
  1482. @Override
  1483. public boolean isNpc()
  1484. {
  1485. return true;
  1486. }
  1487. @Override
  1488. public void setTeam(Team team)
  1489. {
  1490. super.setTeam(team);
  1491. broadcastInfo();
  1492. }
  1493. /**
  1494. * @return {@code true} if this L2Npc is registered in WalkingManager
  1495. */
  1496. @Override
  1497. public boolean isWalker()
  1498. {
  1499. return WalkingManager.getInstance().isRegistered(this);
  1500. }
  1501. @Override
  1502. public boolean isChargedShot(ShotType type)
  1503. {
  1504. return (_shotsMask & type.getMask()) == type.getMask();
  1505. }
  1506. @Override
  1507. public void setChargedShot(ShotType type, boolean charged)
  1508. {
  1509. if (charged)
  1510. {
  1511. _shotsMask |= type.getMask();
  1512. }
  1513. else
  1514. {
  1515. _shotsMask &= ~type.getMask();
  1516. }
  1517. }
  1518. @Override
  1519. public void rechargeShots(boolean physical, boolean magic)
  1520. {
  1521. if ((_soulshotamount > 0) || (_spiritshotamount > 0))
  1522. {
  1523. if (physical)
  1524. {
  1525. if (_soulshotamount == 0)
  1526. {
  1527. return;
  1528. }
  1529. else if (Rnd.get(100) > getSoulShotChance())
  1530. {
  1531. return;
  1532. }
  1533. _soulshotamount--;
  1534. Broadcast.toSelfAndKnownPlayersInRadius(this, new MagicSkillUse(this, this, 2154, 1, 0, 0), 600);
  1535. setChargedShot(ShotType.SOULSHOTS, true);
  1536. }
  1537. if (magic)
  1538. {
  1539. if (_spiritshotamount == 0)
  1540. {
  1541. return;
  1542. }
  1543. else if (Rnd.get(100) > getSpiritShotChance())
  1544. {
  1545. return;
  1546. }
  1547. _spiritshotamount--;
  1548. Broadcast.toSelfAndKnownPlayersInRadius(this, new MagicSkillUse(this, this, 2061, 1, 0, 0), 600);
  1549. setChargedShot(ShotType.SPIRITSHOTS, true);
  1550. }
  1551. }
  1552. }
  1553. /**
  1554. * Short wrapper for backward compatibility
  1555. * @return stored script value
  1556. */
  1557. public int getScriptValue()
  1558. {
  1559. return getVariables().getInt("SCRIPT_VAL");
  1560. }
  1561. /**
  1562. * Short wrapper for backward compatibility. Stores script value
  1563. * @param val value to store
  1564. */
  1565. public void setScriptValue(int val)
  1566. {
  1567. getVariables().set("SCRIPT_VAL", val);
  1568. }
  1569. /**
  1570. * Short wrapper for backward compatibility.
  1571. * @param val value to store
  1572. * @return {@code true} if stored script value equals given value, {@code false} otherwise
  1573. */
  1574. public boolean isScriptValue(int val)
  1575. {
  1576. return getVariables().getInt("SCRIPT_VAL") == val;
  1577. }
  1578. /**
  1579. * @param paramName the parameter name to check
  1580. * @return given AI parameter value
  1581. */
  1582. public int getAIValue(final String paramName)
  1583. {
  1584. return hasAIValue(paramName) ? NpcPersonalAIData.getInstance().getAIValue(getSpawn().getName(), paramName) : -1;
  1585. }
  1586. /**
  1587. * @param paramName the parameter name to check
  1588. * @return {@code true} if given parameter is set for NPC, {@code false} otherwise
  1589. */
  1590. public boolean hasAIValue(final String paramName)
  1591. {
  1592. return (getSpawn() != null) && (getSpawn().getName() != null) && NpcPersonalAIData.getInstance().hasAIValue(getSpawn().getName(), paramName);
  1593. }
  1594. /**
  1595. * @param npc NPC to check
  1596. * @return {@code true} if both given NPC and this NPC is in the same spawn group, {@code false} otherwise
  1597. */
  1598. public boolean isInMySpawnGroup(L2Npc npc)
  1599. {
  1600. return ((getSpawn() != null) && (npc.getSpawn() != null) && (getSpawn().getName() != null) && (getSpawn().getName().equals(npc.getSpawn().getName())));
  1601. }
  1602. /**
  1603. * @return {@code true} if NPC currently located in own spawn point, {@code false} otherwise
  1604. */
  1605. public boolean staysInSpawnLoc()
  1606. {
  1607. return ((getSpawn() != null) && (getSpawn().getX(this) == getX()) && (getSpawn().getY(this) == getY()));
  1608. }
  1609. /**
  1610. * @return {@code true} if {@link NpcVariables} instance is attached to current player's scripts, {@code false} otherwise.
  1611. */
  1612. public boolean hasVariables()
  1613. {
  1614. return getScript(NpcVariables.class) != null;
  1615. }
  1616. /**
  1617. * @return {@link NpcVariables} instance containing parameters regarding NPC.
  1618. */
  1619. public NpcVariables getVariables()
  1620. {
  1621. final NpcVariables vars = getScript(NpcVariables.class);
  1622. return vars != null ? vars : addScript(new NpcVariables());
  1623. }
  1624. /**
  1625. * Send an "event" to all NPC's within given radius
  1626. * @param eventName - name of event
  1627. * @param radius - radius to send event
  1628. * @param reference - L2Object to pass, if needed
  1629. */
  1630. public void broadcastEvent(String eventName, int radius, L2Object reference)
  1631. {
  1632. for (L2Object obj : L2World.getInstance().getVisibleObjects(this, radius))
  1633. {
  1634. if (obj.isNpc() && obj.hasListener(EventType.ON_NPC_EVENT_RECEIVED))
  1635. {
  1636. EventDispatcher.getInstance().notifyEventAsync(new OnNpcEventReceived(eventName, this, (L2Npc) obj, reference), obj);
  1637. }
  1638. }
  1639. }
  1640. /**
  1641. * Sends an event to a given object.
  1642. * @param eventName the event name
  1643. * @param receiver the receiver
  1644. * @param reference the reference
  1645. */
  1646. public void sendScriptEvent(String eventName, L2Object receiver, L2Object reference)
  1647. {
  1648. EventDispatcher.getInstance().notifyEventAsync(new OnNpcEventReceived(eventName, this, (L2Npc) receiver, reference), receiver);
  1649. }
  1650. /**
  1651. * Gets point in range between radiusMin and radiusMax from this NPC
  1652. * @param radiusMin miminal range from NPC (not closer than)
  1653. * @param radiusMax maximal range from NPC (not further than)
  1654. * @return Location in given range from this NPC
  1655. */
  1656. public Location getPointInRange(int radiusMin, int radiusMax)
  1657. {
  1658. if ((radiusMax == 0) || (radiusMax < radiusMin))
  1659. {
  1660. return new Location(getX(), getY(), getZ());
  1661. }
  1662. final int radius = Rnd.get(radiusMin, radiusMax);
  1663. final double angle = Rnd.nextDouble() * 2 * Math.PI;
  1664. return new Location((int) (getX() + (radius * Math.cos(angle))), (int) (getY() + (radius * Math.sin(angle))), getZ());
  1665. }
  1666. /**
  1667. * Drops an item.
  1668. * @param player the last attacker or main damage dealer
  1669. * @param itemId the item ID
  1670. * @param itemCount the item count
  1671. * @return the dropped item
  1672. */
  1673. public L2ItemInstance dropItem(L2PcInstance player, int itemId, long itemCount)
  1674. {
  1675. L2ItemInstance item = null;
  1676. for (int i = 0; i < itemCount; i++)
  1677. {
  1678. // Randomize drop position.
  1679. final int newX = (getX() + Rnd.get((RANDOM_ITEM_DROP_LIMIT * 2) + 1)) - RANDOM_ITEM_DROP_LIMIT;
  1680. final int newY = (getY() + Rnd.get((RANDOM_ITEM_DROP_LIMIT * 2) + 1)) - RANDOM_ITEM_DROP_LIMIT;
  1681. final int newZ = getZ() + 20;
  1682. if (ItemTable.getInstance().getTemplate(itemId) == null)
  1683. {
  1684. _log.log(Level.SEVERE, "Item doesn't exist so cannot be dropped. Item ID: " + itemId + " Quest: " + getName());
  1685. return null;
  1686. }
  1687. item = ItemTable.getInstance().createItem("Loot", itemId, itemCount, player, this);
  1688. if (item == null)
  1689. {
  1690. return null;
  1691. }
  1692. if (player != null)
  1693. {
  1694. item.getDropProtection().protect(player);
  1695. }
  1696. item.dropMe(this, newX, newY, newZ);
  1697. // Add drop to auto destroy item task.
  1698. if (!Config.LIST_PROTECTED_ITEMS.contains(itemId))
  1699. {
  1700. if (((Config.AUTODESTROY_ITEM_AFTER > 0) && !item.getItem().hasExImmediateEffect()) || ((Config.HERB_AUTO_DESTROY_TIME > 0) && item.getItem().hasExImmediateEffect()))
  1701. {
  1702. ItemsAutoDestroy.getInstance().addItem(item);
  1703. }
  1704. }
  1705. item.setProtected(false);
  1706. // If stackable, end loop as entire count is included in 1 instance of item.
  1707. if (item.isStackable() || !Config.MULTIPLE_ITEM_DROP)
  1708. {
  1709. break;
  1710. }
  1711. }
  1712. return item;
  1713. }
  1714. /**
  1715. * Method overload for {@link L2Attackable#dropItem(L2PcInstance, int, long)}
  1716. * @param player the last attacker or main damage dealer
  1717. * @param item the item holder
  1718. * @return the dropped item
  1719. */
  1720. public L2ItemInstance dropItem(L2PcInstance player, ItemHolder item)
  1721. {
  1722. return dropItem(player, item.getId(), item.getCount());
  1723. }
  1724. @Override
  1725. public final String getName()
  1726. {
  1727. return getTemplate().getName();
  1728. }
  1729. @Override
  1730. public boolean isVisibleFor(L2PcInstance player)
  1731. {
  1732. if (hasListener(EventType.ON_NPC_CAN_BE_SEEN))
  1733. {
  1734. final TerminateReturn term = EventDispatcher.getInstance().notifyEvent(new OnNpcCanBeSeen(this, player), this, TerminateReturn.class);
  1735. if (term != null)
  1736. {
  1737. return term.terminate();
  1738. }
  1739. }
  1740. return super.isVisibleFor(player);
  1741. }
  1742. /**
  1743. * Sets if the players can talk with this npc or not
  1744. * @param val {@code true} if the players can talk, {@code false} otherwise
  1745. */
  1746. public void setTalking(boolean val)
  1747. {
  1748. _isTalking = val;
  1749. }
  1750. /**
  1751. * Checks if the players can talk to this npc.
  1752. * @return {@code true} if the players can talk, {@code false} otherwise.
  1753. */
  1754. public boolean isTalking()
  1755. {
  1756. return _isTalking;
  1757. }
  1758. /**
  1759. * Sets the weapon id with which this npc was killed.
  1760. * @param weaponId
  1761. */
  1762. public void setKillingBlowWeapon(int weaponId)
  1763. {
  1764. _killingBlowWeaponId = weaponId;
  1765. }
  1766. /**
  1767. * @return the id of the weapon with which player killed this npc.
  1768. */
  1769. public int getKillingBlowWeapon()
  1770. {
  1771. return _killingBlowWeaponId;
  1772. }
  1773. /**
  1774. * Adds a summoned NPC.
  1775. * @param npc the summoned NPC
  1776. */
  1777. public final void addSummonedNpc(L2Npc npc)
  1778. {
  1779. if (_summonedNpcs == null)
  1780. {
  1781. synchronized (this)
  1782. {
  1783. if (_summonedNpcs == null)
  1784. {
  1785. _summonedNpcs = new ConcurrentHashMap<>();
  1786. }
  1787. }
  1788. }
  1789. _summonedNpcs.put(npc.getObjectId(), npc);
  1790. npc.setSummoner(this);
  1791. }
  1792. /**
  1793. * Removes a summoned NPC by object ID.
  1794. * @param objectId the summoned NPC object ID
  1795. */
  1796. public final void removeSummonedNpc(int objectId)
  1797. {
  1798. if (_summonedNpcs != null)
  1799. {
  1800. _summonedNpcs.remove(objectId);
  1801. }
  1802. }
  1803. /**
  1804. * Gets the summoned NPCs.
  1805. * @return the summoned NPCs
  1806. */
  1807. public final Collection<L2Npc> getSummonedNpcs()
  1808. {
  1809. return _summonedNpcs != null ? _summonedNpcs.values() : Collections.<L2Npc> emptyList();
  1810. }
  1811. /**
  1812. * Gets the summoned NPC by object ID.
  1813. * @param objectId the summoned NPC object ID
  1814. * @return the summoned NPC
  1815. */
  1816. public final L2Npc getSummonedNpc(int objectId)
  1817. {
  1818. if (_summonedNpcs != null)
  1819. {
  1820. return _summonedNpcs.get(objectId);
  1821. }
  1822. return null;
  1823. }
  1824. /**
  1825. * Gets the summoned NPC count.
  1826. * @return the summoned NPC count
  1827. */
  1828. public final int getSummonedNpcCount()
  1829. {
  1830. return _summonedNpcs != null ? _summonedNpcs.size() : 0;
  1831. }
  1832. /**
  1833. * Resets the summoned NPCs list.
  1834. */
  1835. public final void resetSummonedNpcs()
  1836. {
  1837. if (_summonedNpcs != null)
  1838. {
  1839. _summonedNpcs.clear();
  1840. }
  1841. }
  1842. }