L2Npc.java 55 KB

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