L2Attackable.java 89 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703170417051706170717081709171017111712171317141715171617171718171917201721172217231724172517261727172817291730173117321733173417351736173717381739174017411742174317441745174617471748174917501751175217531754175517561757175817591760176117621763176417651766176717681769177017711772177317741775177617771778177917801781178217831784178517861787178817891790179117921793179417951796179717981799180018011802180318041805180618071808180918101811181218131814181518161817181818191820182118221823182418251826182718281829183018311832183318341835183618371838183918401841184218431844184518461847184818491850185118521853185418551856185718581859186018611862186318641865186618671868186918701871187218731874187518761877187818791880188118821883188418851886188718881889189018911892189318941895189618971898189919001901190219031904190519061907190819091910191119121913191419151916191719181919192019211922192319241925192619271928192919301931193219331934193519361937193819391940194119421943194419451946194719481949195019511952195319541955195619571958195919601961196219631964196519661967196819691970197119721973197419751976197719781979198019811982198319841985198619871988198919901991199219931994199519961997199819992000200120022003200420052006200720082009201020112012201320142015201620172018201920202021202220232024202520262027202820292030203120322033203420352036203720382039204020412042204320442045204620472048204920502051205220532054205520562057205820592060206120622063206420652066206720682069207020712072207320742075207620772078207920802081208220832084208520862087208820892090209120922093209420952096209720982099210021012102210321042105210621072108210921102111211221132114211521162117211821192120212121222123212421252126212721282129213021312132213321342135213621372138213921402141214221432144214521462147214821492150215121522153215421552156215721582159216021612162216321642165216621672168216921702171217221732174217521762177217821792180218121822183218421852186218721882189219021912192219321942195219621972198219922002201220222032204220522062207220822092210221122122213221422152216221722182219222022212222222322242225222622272228222922302231223222332234223522362237223822392240224122422243224422452246224722482249225022512252225322542255225622572258225922602261226222632264226522662267226822692270227122722273227422752276227722782279228022812282228322842285228622872288228922902291229222932294229522962297229822992300230123022303
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package net.sf.l2j.gameserver.model;
  16. import java.util.List;
  17. import java.util.Map;
  18. import java.util.logging.Level;
  19. import javolution.util.FastList;
  20. import javolution.util.FastMap;
  21. import net.sf.l2j.Config;
  22. import net.sf.l2j.gameserver.ItemsAutoDestroy;
  23. import net.sf.l2j.gameserver.ThreadPoolManager;
  24. import net.sf.l2j.gameserver.ai.CtrlEvent;
  25. import net.sf.l2j.gameserver.ai.CtrlIntention;
  26. import net.sf.l2j.gameserver.ai.L2AttackableAI;
  27. import net.sf.l2j.gameserver.ai.L2CharacterAI;
  28. import net.sf.l2j.gameserver.ai.L2SiegeGuardAI;
  29. import net.sf.l2j.gameserver.clientpackets.Say2;
  30. import net.sf.l2j.gameserver.datatables.EventDroplist;
  31. import net.sf.l2j.gameserver.datatables.ItemTable;
  32. import net.sf.l2j.gameserver.datatables.SkillTable;
  33. import net.sf.l2j.gameserver.datatables.EventDroplist.DateDrop;
  34. import net.sf.l2j.gameserver.instancemanager.CursedWeaponsManager;
  35. import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
  36. import net.sf.l2j.gameserver.model.actor.instance.L2FolkInstance;
  37. import net.sf.l2j.gameserver.model.actor.instance.L2GrandBossInstance;
  38. import net.sf.l2j.gameserver.model.actor.instance.L2MinionInstance;
  39. import net.sf.l2j.gameserver.model.actor.instance.L2MonsterInstance;
  40. import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
  41. import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
  42. import net.sf.l2j.gameserver.model.actor.instance.L2PetInstance;
  43. import net.sf.l2j.gameserver.model.actor.instance.L2PlayableInstance;
  44. import net.sf.l2j.gameserver.model.actor.instance.L2RaidBossInstance;
  45. import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance;
  46. import net.sf.l2j.gameserver.model.actor.instance.L2SummonInstance;
  47. import net.sf.l2j.gameserver.model.actor.knownlist.AttackableKnownList;
  48. import net.sf.l2j.gameserver.model.base.SoulCrystal;
  49. import net.sf.l2j.gameserver.model.quest.Quest;
  50. import net.sf.l2j.gameserver.network.SystemMessageId;
  51. import net.sf.l2j.gameserver.serverpackets.CreatureSay;
  52. import net.sf.l2j.gameserver.serverpackets.InventoryUpdate;
  53. import net.sf.l2j.gameserver.serverpackets.SystemMessage;
  54. import net.sf.l2j.gameserver.skills.Stats;
  55. import net.sf.l2j.gameserver.templates.L2EtcItemType;
  56. import net.sf.l2j.gameserver.templates.L2NpcTemplate;
  57. import net.sf.l2j.gameserver.util.Util;
  58. import net.sf.l2j.util.Rnd;
  59. /**
  60. * This class manages all NPC that can be attacked.<BR><BR>
  61. *
  62. * L2Attackable :<BR><BR>
  63. * <li>L2ArtefactInstance</li>
  64. * <li>L2FriendlyMobInstance</li>
  65. * <li>L2MonsterInstance</li>
  66. * <li>L2SiegeGuardInstance </li>
  67. *
  68. * @version $Revision: 1.24.2.3.2.16 $ $Date: 2005/04/11 19:11:21 $
  69. */
  70. public class L2Attackable extends L2NpcInstance
  71. {
  72. //protected static Logger _log = Logger.getLogger(L2Attackable.class.getName());
  73. /**
  74. * This class contains all AggroInfo of the L2Attackable against the attacker L2Character.<BR><BR>
  75. *
  76. * <B><U> Data</U> :</B><BR><BR>
  77. * <li>attacker : The attaker L2Character concerned by this AggroInfo of this L2Attackable </li>
  78. * <li>hate : Hate level of this L2Attackable against the attaker L2Character (hate = damage) </li>
  79. * <li>damage : Number of damages that the attaker L2Character gave to this L2Attackable </li><BR><BR>
  80. *
  81. */
  82. public final class AggroInfo
  83. {
  84. /** The attaker L2Character concerned by this AggroInfo of this L2Attackable */
  85. protected L2Character _attacker;
  86. /** Hate level of this L2Attackable against the attaker L2Character (hate = damage) */
  87. protected int _hate;
  88. /** Number of damages that the attaker L2Character gave to this L2Attackable */
  89. protected int _damage;
  90. /**
  91. * Constructor of AggroInfo.<BR><BR>
  92. */
  93. AggroInfo(L2Character pAttacker)
  94. {
  95. _attacker = pAttacker;
  96. }
  97. /**
  98. * Verify is object is equal to this AggroInfo.<BR><BR>
  99. */
  100. @Override
  101. public boolean equals(Object obj)
  102. {
  103. if (this == obj) return true;
  104. if (obj instanceof AggroInfo) return (((AggroInfo)obj)._attacker == _attacker);
  105. return false;
  106. }
  107. /**
  108. * Return the Identifier of the attaker L2Character.<BR><BR>
  109. */
  110. @Override
  111. public int hashCode()
  112. {
  113. return _attacker.getObjectId();
  114. }
  115. }
  116. /**
  117. * This class contains all RewardInfo of the L2Attackable against the any attacker L2Character, based on amount of damage done.<BR><BR>
  118. *
  119. * <B><U> Data</U> :</B><BR><BR>
  120. * <li>attacker : The attaker L2Character concerned by this RewardInfo of this L2Attackable </li>
  121. * <li>dmg : Total amount of damage done by the attacker to this L2Attackable (summon + own) </li>
  122. *
  123. */
  124. protected final class RewardInfo
  125. {
  126. protected L2Character _attacker;
  127. protected int _dmg = 0;
  128. public RewardInfo(L2Character pAttacker, int pDmg)
  129. {
  130. _attacker = pAttacker;
  131. _dmg = pDmg;
  132. }
  133. public void addDamage(int pDmg)
  134. {
  135. _dmg += pDmg;
  136. }
  137. @Override
  138. public boolean equals(Object obj)
  139. {
  140. if (this == obj) return true;
  141. if (obj instanceof RewardInfo) return (((RewardInfo)obj)._attacker == _attacker);
  142. return false;
  143. }
  144. @Override
  145. public int hashCode()
  146. {
  147. return _attacker.getObjectId();
  148. }
  149. }
  150. /**
  151. * This class contains all AbsorberInfo of the L2Attackable against the absorber L2Character.<BR><BR>
  152. *
  153. * <B><U> Data</U> :</B><BR><BR>
  154. * <li>absorber : The attaker L2Character concerned by this AbsorberInfo of this L2Attackable </li>
  155. *
  156. */
  157. public final class AbsorberInfo
  158. {
  159. /** The attaker L2Character concerned by this AbsorberInfo of this L2Attackable */
  160. protected L2PcInstance _absorber;
  161. protected int _crystalId;
  162. protected double _absorbedHP;
  163. /**
  164. * Constructor of AbsorberInfo.<BR><BR>
  165. */
  166. AbsorberInfo(L2PcInstance attacker, int pCrystalId, double pAbsorbedHP)
  167. {
  168. _absorber = attacker;
  169. _crystalId = pCrystalId;
  170. _absorbedHP = pAbsorbedHP;
  171. }
  172. /**
  173. * Verify is object is equal to this AbsorberInfo.<BR><BR>
  174. */
  175. @Override
  176. public boolean equals(Object obj)
  177. {
  178. if (this == obj) return true;
  179. if (obj instanceof AbsorberInfo) return (((AbsorberInfo)obj)._absorber == _absorber);
  180. return false;
  181. }
  182. /**
  183. * Return the Identifier of the absorber L2Character.<BR><BR>
  184. */
  185. @Override
  186. public int hashCode()
  187. {
  188. return _absorber.getObjectId();
  189. }
  190. }
  191. /**
  192. * This class is used to create item reward lists instead of creating item instances.<BR><BR>
  193. */
  194. public final class RewardItem
  195. {
  196. protected int _itemId;
  197. protected int _count;
  198. public RewardItem(int itemId, int count)
  199. {
  200. _itemId = itemId;
  201. _count = count;
  202. }
  203. public int getItemId() { return _itemId;}
  204. public int getCount() { return _count;}
  205. }
  206. /** The table containing all autoAttackable L2Character in its Aggro Range and L2Character that attacked the L2Attackable
  207. * This Map is Thread Safe, but Removing Object While Interating Over It Will Result NPE
  208. * */
  209. private FastMap<L2Character, AggroInfo> _aggroList = new FastMap<L2Character, AggroInfo>().setShared(true);
  210. /** Use this to Read or Put Object to this Map */
  211. public final FastMap<L2Character, AggroInfo> getAggroListRP()
  212. {
  213. return _aggroList;
  214. }
  215. /** Use this to Remove Object from this Map
  216. * This Should be Synchronized While Interating over This Map - ie u cant interating and removing object at once*/
  217. public final FastMap<L2Character, AggroInfo> getAggroList()
  218. {
  219. return _aggroList;
  220. }
  221. private boolean _isReturningToSpawnPoint = false;
  222. public final boolean isReturningToSpawnPoint() { return _isReturningToSpawnPoint; }
  223. public final void setisReturningToSpawnPoint(boolean value) { _isReturningToSpawnPoint = value; }
  224. private boolean _canReturnToSpawnPoint = true;
  225. public final boolean canReturnToSpawnPoint() { return _canReturnToSpawnPoint; }
  226. public final void setCanReturnToSpawnPoint(boolean value) { _canReturnToSpawnPoint = value; }
  227. /** Table containing all Items that a Dwarf can Sweep on this L2Attackable */
  228. private RewardItem[] _sweepItems;
  229. /** crops */
  230. private RewardItem[] _harvestItems;
  231. private boolean _seeded;
  232. private int _seedType = 0;
  233. private L2PcInstance _seeder = null;
  234. /** True if an over-hit enabled skill has successfully landed on the L2Attackable */
  235. private boolean _overhit;
  236. /** Stores the extra (over-hit) damage done to the L2Attackable when the attacker uses an over-hit enabled skill */
  237. private double _overhitDamage;
  238. /** Stores the attacker who used the over-hit enabled skill on the L2Attackable */
  239. private L2Character _overhitAttacker;
  240. /** First CommandChannel who attacked the L2Attackable and meet the requirements **/
  241. private L2CommandChannel _firstCommandChannelAttacked = null;
  242. private CommandChannelTimer _commandChannelTimer = null;
  243. /** True if a Soul Crystal was successfuly used on the L2Attackable */
  244. private boolean _absorbed;
  245. /** The table containing all L2PcInstance that successfuly absorbed the soul of this L2Attackable */
  246. private FastMap<L2PcInstance, AbsorberInfo> _absorbersList = new FastMap<L2PcInstance, AbsorberInfo>().setShared(true);
  247. /** Have this L2Attackable to reward Exp and SP on Die? **/
  248. private boolean _mustGiveExpSp;
  249. /**
  250. * Constructor of L2Attackable (use L2Character and L2NpcInstance constructor).<BR><BR>
  251. *
  252. * <B><U> Actions</U> :</B><BR><BR>
  253. * <li>Call the L2Character constructor to set the _template of the L2Attackable (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR) </li>
  254. * <li>Set the name of the L2Attackable</li>
  255. * <li>Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it </li><BR><BR>
  256. *
  257. * @param objectId Identifier of the object to initialized
  258. * @param L2NpcTemplate Template to apply to the NPC
  259. */
  260. public L2Attackable(int objectId, L2NpcTemplate template)
  261. {
  262. super(objectId, template);
  263. getKnownList(); // init knownlist
  264. _mustGiveExpSp = true;
  265. }
  266. @Override
  267. public AttackableKnownList getKnownList()
  268. {
  269. if(super.getKnownList() == null || !(super.getKnownList() instanceof AttackableKnownList))
  270. setKnownList(new AttackableKnownList(this));
  271. return (AttackableKnownList)super.getKnownList();
  272. }
  273. /**
  274. * Return the L2Character AI of the L2Attackable and if its null create a new one.<BR><BR>
  275. */
  276. @Override
  277. public L2CharacterAI getAI()
  278. {
  279. if (_ai == null)
  280. {
  281. synchronized(this)
  282. {
  283. if (_ai == null)
  284. _ai = new L2AttackableAI(new AIAccessor());
  285. }
  286. }
  287. return _ai;
  288. }
  289. // get condition to hate, actually isAggressive() is checked
  290. // by monster and karma by guards in motheds that overwrite this one.
  291. /**
  292. * Not used.<BR><BR>
  293. *
  294. * @deprecated
  295. *
  296. */
  297. @Deprecated
  298. public boolean getCondition2(L2Character target)
  299. {
  300. if (target instanceof L2FolkInstance || target instanceof L2DoorInstance)
  301. return false;
  302. if (target.isAlikeDead()
  303. || !isInsideRadius(target, getAggroRange(), false, false)
  304. || Math.abs(getZ()-target.getZ()) > 100
  305. )
  306. return false;
  307. return !target.isInvul();
  308. }
  309. /**
  310. * Reduce the current HP of the L2Attackable.<BR><BR>
  311. *
  312. * @param damage The HP decrease value
  313. * @param attacker The L2Character who attacks
  314. *
  315. */
  316. @Override
  317. public void reduceCurrentHp(double damage, L2Character attacker)
  318. {
  319. reduceCurrentHp(damage, attacker, true);
  320. }
  321. /**
  322. * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.<BR><BR>
  323. *
  324. * @param i The HP decrease value
  325. * @param attacker The L2Character who attacks
  326. * @param awake The awake state (If True : stop sleeping)
  327. *
  328. */
  329. @Override
  330. public void reduceCurrentHp(double damage, L2Character attacker, boolean awake)
  331. {
  332. /*
  333. if ((this instanceof L2SiegeGuardInstance) && (attacker instanceof L2SiegeGuardInstance))
  334. //if((this.getEffect(L2Effect.EffectType.CONFUSION)!=null) && (attacker.getEffect(L2Effect.EffectType.CONFUSION)!=null))
  335. return;
  336. if ((this instanceof L2MonsterInstance)&&(attacker instanceof L2MonsterInstance))
  337. if((this.getEffect(L2Effect.EffectType.CONFUSION)!=null) && (attacker.getEffect(L2Effect.EffectType.CONFUSION)!=null))
  338. return;
  339. */
  340. // CommandChannel
  341. if (_commandChannelTimer == null && this.isRaid())
  342. {
  343. if (attacker.isInParty() && attacker.getParty().isInCommandChannel()
  344. && attacker.getParty().getCommandChannel().meetRaidWarCondition(this))
  345. {
  346. _firstCommandChannelAttacked = attacker.getParty().getCommandChannel();
  347. _commandChannelTimer = new CommandChannelTimer(this, attacker.getParty().getCommandChannel());
  348. ThreadPoolManager.getInstance().scheduleGeneral(_commandChannelTimer, 300000); // 5 min
  349. _firstCommandChannelAttacked.broadcastToChannelMembers(new CreatureSay(0, Say2.PARTYROOM_ALL, "", "You have looting rights!"));
  350. }
  351. }
  352. if (isEventMob) return;
  353. // Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList
  354. if (attacker != null) addDamage(attacker, (int)damage);
  355. // If this L2Attackable is a L2MonsterInstance and it has spawned minions, call its minions to battle
  356. if (this instanceof L2MonsterInstance)
  357. {
  358. L2MonsterInstance master = (L2MonsterInstance) this;
  359. if (this instanceof L2MinionInstance)
  360. {
  361. master = ((L2MinionInstance)this).getLeader();
  362. if (!master.isInCombat()&&!master.isDead()) master.addDamage(attacker, 1);
  363. }
  364. if (master.hasMinions())
  365. master.callMinionsToAssist(attacker);
  366. }
  367. // Reduce the current HP of the L2Attackable and launch the doDie Task if necessary
  368. super.reduceCurrentHp(damage, attacker, awake);
  369. }
  370. public synchronized void setMustRewardExpSp(boolean value) {
  371. _mustGiveExpSp = value;
  372. }
  373. public synchronized boolean getMustRewardExpSP() { return _mustGiveExpSp; }
  374. /**
  375. * Kill the L2Attackable (the corpse disappeared after 7 seconds), distribute rewards (EXP, SP, Drops...) and notify Quest Engine.<BR><BR>
  376. *
  377. * <B><U> Actions</U> :</B><BR><BR>
  378. * <li>Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members </li>
  379. * <li>Notify the Quest Engine of the L2Attackable death if necessary</li>
  380. * <li>Kill the L2NpcInstance (the corpse disappeared after 7 seconds) </li><BR><BR>
  381. *
  382. * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR><BR>
  383. *
  384. * @param killer The L2Character that has killed the L2Attackable
  385. *
  386. */
  387. @Override
  388. public boolean doDie(L2Character killer)
  389. {
  390. // Kill the L2NpcInstance (the corpse disappeared after 7 seconds)
  391. if (!super.doDie(killer))
  392. return false;
  393. // Enhance soul crystals of the attacker if this L2Attackable had its soul absorbed
  394. try {
  395. if (killer instanceof L2PcInstance)
  396. {
  397. levelSoulCrystals(killer);
  398. }
  399. }
  400. catch (Exception e) { _log.log(Level.SEVERE, "", e); }
  401. // Notify the Quest Engine of the L2Attackable death if necessary
  402. try {
  403. if (killer instanceof L2PcInstance || killer instanceof L2Summon || killer instanceof L2Trap)
  404. {
  405. L2PcInstance player = null;
  406. if (killer instanceof L2PcInstance)
  407. player = (L2PcInstance)killer;
  408. else if (killer instanceof L2Summon)
  409. player = ((L2Summon)killer).getOwner();
  410. else if (killer instanceof L2Trap)
  411. player = ((L2Trap)killer).getOwner();
  412. if (getTemplate().getEventQuests(Quest.QuestEventType.MOBKILLED) != null)
  413. for (Quest quest: getTemplate().getEventQuests(Quest.QuestEventType.MOBKILLED))
  414. quest.notifyKill(this, player, killer instanceof L2Summon);
  415. }
  416. }
  417. catch (Exception e) { _log.log(Level.SEVERE, "", e); }
  418. setChampion(false);
  419. if (Config.L2JMOD_CHAMPION_ENABLE)
  420. {
  421. //Set champion on next spawn
  422. if (!(this instanceof L2GrandBossInstance) && this instanceof L2MonsterInstance && Config.L2JMOD_CHAMPION_FREQUENCY > 0 && getLevel()>=Config.L2JMOD_CHAMP_MIN_LVL && getLevel()<=Config.L2JMOD_CHAMP_MAX_LVL)
  423. {
  424. int random = Rnd.get(100);
  425. if (random < Config.L2JMOD_CHAMPION_FREQUENCY)
  426. setChampion(true);
  427. }
  428. }
  429. return true;
  430. }
  431. /**
  432. * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members.<BR><BR>
  433. *
  434. * <B><U> Actions</U> :</B><BR><BR>
  435. * <li>Get the L2PcInstance owner of the L2SummonInstance (if necessary) and L2Party in progress </li>
  436. * <li>Calculate the Experience and SP rewards in function of the level difference</li>
  437. * <li>Add Exp and SP rewards to L2PcInstance (including Summon penalty) and to Party members in the known area of the last attacker </li><BR><BR>
  438. *
  439. * <FONT COLOR=#FF0000><B> <U>Caution</U> : This method DOESN'T GIVE rewards to L2PetInstance</B></FONT><BR><BR>
  440. *
  441. * @param lastAttacker The L2Character that has killed the L2Attackable
  442. *
  443. */
  444. @Override
  445. protected void calculateRewards(L2Character lastAttacker)
  446. {
  447. // Creates an empty list of rewards
  448. FastMap<L2Character, RewardInfo> rewards = new FastMap<L2Character, RewardInfo>().setShared(true);
  449. try
  450. {
  451. if (getAggroListRP().isEmpty()) return;
  452. // Manage Base, Quests and Sweep drops of the L2Attackable
  453. doItemDrop(lastAttacker);
  454. // Manage drop of Special Events created by GM for a defined period
  455. doEventDrop(lastAttacker);
  456. if (!getMustRewardExpSP()) return;
  457. int rewardCount = 0;
  458. int damage;
  459. L2Character attacker, ddealer;
  460. RewardInfo reward;
  461. // While Interating over This Map Removing Object is Not Allowed
  462. synchronized (getAggroList())
  463. {
  464. // Go through the _aggroList of the L2Attackable
  465. for (AggroInfo info : getAggroListRP().values())
  466. {
  467. if (info == null) continue;
  468. // Get the L2Character corresponding to this attacker
  469. attacker = info._attacker;
  470. // Get damages done by this attacker
  471. damage = info._damage;
  472. // Prevent unwanted behavior
  473. if (damage > 1)
  474. {
  475. if ( (attacker instanceof L2SummonInstance) ||
  476. ((attacker instanceof L2PetInstance) &&
  477. ((L2PetInstance)attacker).getPetData().getOwnerExpTaken() > 0) )
  478. ddealer = ((L2Summon)attacker).getOwner();
  479. else
  480. ddealer = info._attacker;
  481. // Check if ddealer isn't too far from this (killed monster)
  482. if (!Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, ddealer, true)) continue;
  483. // Calculate real damages (Summoners should get own damage plus summon's damage)
  484. reward = rewards.get(ddealer);
  485. if (reward == null)
  486. {
  487. reward = new RewardInfo(ddealer, damage);
  488. rewardCount++;
  489. }
  490. else
  491. {
  492. reward.addDamage(damage);
  493. }
  494. rewards.put(ddealer, reward);
  495. }
  496. }
  497. }
  498. if (!rewards.isEmpty())
  499. {
  500. L2Party attackerParty;
  501. long exp;
  502. int levelDiff, partyDmg, partyLvl, sp;
  503. float partyMul, penalty;
  504. RewardInfo reward2;
  505. int[] tmp;
  506. for (FastMap.Entry<L2Character, RewardInfo> entry = rewards.head(), end = rewards.tail(); (entry = entry.getNext()) != end;)
  507. {
  508. if (entry == null) continue;
  509. reward = entry.getValue();
  510. if(reward == null) continue;
  511. // Penalty applied to the attacker's XP
  512. penalty = 0;
  513. // Attacker to be rewarded
  514. attacker = reward._attacker;
  515. // Total amount of damage done
  516. damage = reward._dmg;
  517. // If the attacker is a Pet, get the party of the owner
  518. if (attacker instanceof L2PetInstance)
  519. attackerParty = ((L2PetInstance)attacker).getParty();
  520. else if (attacker instanceof L2PcInstance)
  521. attackerParty = ((L2PcInstance)attacker).getParty();
  522. else
  523. return;
  524. // If this attacker is a L2PcInstance with a summoned L2SummonInstance, get Exp Penalty applied for the current summoned L2SummonInstance
  525. if (attacker instanceof L2PcInstance && ((L2PcInstance)attacker).getPet() instanceof L2SummonInstance)
  526. {
  527. penalty = ((L2SummonInstance)((L2PcInstance)attacker).getPet()).getExpPenalty();
  528. }
  529. // We must avoid "over damage", if any
  530. if (damage > getMaxHp()) damage = getMaxHp();
  531. // If there's NO party in progress
  532. if (attackerParty == null)
  533. {
  534. // Calculate Exp and SP rewards
  535. if (attacker.getKnownList().knowsObject(this))
  536. {
  537. // Calculate the difference of level between this attacker (L2PcInstance or L2SummonInstance owner) and the L2Attackable
  538. // mob = 24, atk = 10, diff = -14 (full xp)
  539. // mob = 24, atk = 28, diff = 4 (some xp)
  540. // mob = 24, atk = 50, diff = 26 (no xp)
  541. levelDiff = attacker.getLevel() - getLevel();
  542. tmp = calculateExpAndSp(levelDiff, damage);
  543. exp = tmp[0];
  544. exp *= 1 - penalty;
  545. sp = tmp[1];
  546. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  547. {
  548. exp *= Config.L2JMOD_CHAMPION_REWARDS;
  549. sp *= Config.L2JMOD_CHAMPION_REWARDS;
  550. }
  551. // Check for an over-hit enabled strike
  552. if (attacker instanceof L2PcInstance)
  553. {
  554. L2PcInstance player = (L2PcInstance)attacker;
  555. if (isOverhit() && attacker == getOverhitAttacker())
  556. {
  557. player.sendPacket(new SystemMessage(SystemMessageId.OVER_HIT));
  558. exp += calculateOverhitExp(exp);
  559. }
  560. }
  561. // Distribute the Exp and SP between the L2PcInstance and its L2Summon
  562. if (!attacker.isDead())
  563. {
  564. long addexp = Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null));
  565. int addsp = (int)attacker.calcStat(Stats.EXPSP_RATE, sp, null, null);
  566. if (attacker instanceof L2PcInstance)
  567. {
  568. if (((L2PcInstance)attacker).getSkillLevel(467) > 0)
  569. {
  570. L2Skill skill = SkillTable.getInstance().getInfo(467,((L2PcInstance)attacker).getSkillLevel(467));
  571. if (skill.getExpNeeded() <= addexp)
  572. {
  573. ((L2PcInstance)attacker).absorbSoul(skill,this);
  574. }
  575. }
  576. }
  577. attacker.addExpAndSp(addexp,addsp);
  578. }
  579. }
  580. }
  581. else
  582. {
  583. //share with party members
  584. partyDmg = 0;
  585. partyMul = 1.f;
  586. partyLvl = 0;
  587. // Get all L2Character that can be rewarded in the party
  588. List<L2PlayableInstance> rewardedMembers = new FastList<L2PlayableInstance>();
  589. // Go through all L2PcInstance in the party
  590. List<L2PcInstance> groupMembers;
  591. if (attackerParty.isInCommandChannel())
  592. groupMembers = attackerParty.getCommandChannel().getMembers();
  593. else
  594. groupMembers = attackerParty.getPartyMembers();
  595. for (L2PcInstance pl : groupMembers)
  596. {
  597. if (pl == null || pl.isDead()) continue;
  598. // Get the RewardInfo of this L2PcInstance from L2Attackable rewards
  599. reward2 = rewards.get(pl);
  600. // If the L2PcInstance is in the L2Attackable rewards add its damages to party damages
  601. if (reward2 != null)
  602. {
  603. if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, pl, true))
  604. {
  605. partyDmg += reward2._dmg; // Add L2PcInstance damages to party damages
  606. rewardedMembers.add(pl);
  607. if (pl.getLevel() > partyLvl)
  608. {
  609. if(attackerParty.isInCommandChannel())
  610. partyLvl = attackerParty.getCommandChannel().getLevel();
  611. else
  612. partyLvl = pl.getLevel();
  613. }
  614. }
  615. rewards.remove(pl); // Remove the L2PcInstance from the L2Attackable rewards
  616. }
  617. else
  618. {
  619. // Add L2PcInstance of the party (that have attacked or not) to members that can be rewarded
  620. // and in range of the monster.
  621. if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, pl, true))
  622. {
  623. rewardedMembers.add(pl);
  624. if (pl.getLevel() > partyLvl)
  625. {
  626. if(attackerParty.isInCommandChannel())
  627. partyLvl = attackerParty.getCommandChannel().getLevel();
  628. else
  629. partyLvl = pl.getLevel();
  630. }
  631. }
  632. }
  633. L2PlayableInstance summon = pl.getPet();
  634. if (summon != null && summon instanceof L2PetInstance)
  635. {
  636. reward2 = rewards.get(summon);
  637. if (reward2 != null) // Pets are only added if they have done damage
  638. {
  639. if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, summon, true))
  640. {
  641. partyDmg += reward2._dmg; // Add summon damages to party damages
  642. rewardedMembers.add(summon);
  643. if (summon.getLevel() > partyLvl)
  644. partyLvl = summon.getLevel();
  645. }
  646. rewards.remove(summon); // Remove the summon from the L2Attackable rewards
  647. }
  648. }
  649. }
  650. // If the party didn't killed this L2Attackable alone
  651. if (partyDmg < getMaxHp()) partyMul = ((float)partyDmg / (float)getMaxHp());
  652. // Avoid "over damage"
  653. if (partyDmg > getMaxHp()) partyDmg = getMaxHp();
  654. // Calculate the level difference between Party and L2Attackable
  655. levelDiff = partyLvl - getLevel();
  656. // Calculate Exp and SP rewards
  657. tmp = calculateExpAndSp(levelDiff, partyDmg);
  658. exp = tmp[0];
  659. sp = tmp[1];
  660. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  661. {
  662. exp *= Config.L2JMOD_CHAMPION_REWARDS;
  663. sp *= Config.L2JMOD_CHAMPION_REWARDS;
  664. }
  665. exp *= partyMul;
  666. sp *= partyMul;
  667. // Check for an over-hit enabled strike
  668. // (When in party, the over-hit exp bonus is given to the whole party and splitted proportionally through the party members)
  669. if (attacker instanceof L2PcInstance)
  670. {
  671. L2PcInstance player = (L2PcInstance)attacker;
  672. if (isOverhit() && attacker == getOverhitAttacker())
  673. {
  674. player.sendPacket(new SystemMessage(SystemMessageId.OVER_HIT));
  675. exp += calculateOverhitExp(exp);
  676. }
  677. }
  678. // Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker
  679. if (partyDmg > 0) attackerParty.distributeXpAndSp(exp, sp, rewardedMembers, partyLvl, this);
  680. }
  681. }
  682. }
  683. rewards = null;
  684. }
  685. catch (Exception e)
  686. {
  687. _log.log(Level.SEVERE, "", e);
  688. }
  689. }
  690. /**
  691. * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.<BR><BR>
  692. *
  693. * @param attacker The L2Character that gave damages to this L2Attackable
  694. * @param damage The number of damages given by the attacker L2Character
  695. *
  696. */
  697. public void addDamage(L2Character attacker, int damage)
  698. {
  699. addDamageHate(attacker, damage, damage);
  700. }
  701. /**
  702. * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.<BR><BR>
  703. *
  704. * @param attacker The L2Character that gave damages to this L2Attackable
  705. * @param damage The number of damages given by the attacker L2Character
  706. * @param aggro The hate (=damage) given by the attacker L2Character
  707. *
  708. */
  709. public void addDamageHate(L2Character attacker, int damage, int aggro)
  710. {
  711. if (attacker == null /*|| _aggroList == null*/) return;
  712. // Get the AggroInfo of the attacker L2Character from the _aggroList of the L2Attackable
  713. AggroInfo ai = getAggroListRP().get(attacker);
  714. if (ai == null)
  715. {
  716. ai = new AggroInfo(attacker);
  717. ai._damage = 0;
  718. ai._hate = 0;
  719. getAggroListRP().put(attacker, ai);
  720. }
  721. // If aggro is negative, its comming from SEE_SPELL, buffs use constant 150
  722. if (aggro < 0) {
  723. ai._hate -= (aggro*150)/(getLevel()+7);
  724. aggro = -aggro;
  725. }
  726. // if damage == 0 -> this is case of adding only to aggro list, dont apply formula on it
  727. else if (damage == 0) ai._hate += aggro;
  728. // else its damage that must be added using constant 100
  729. else ai._hate += (aggro*100)/(getLevel()+7);
  730. // Add new damage and aggro (=damage) to the AggroInfo object
  731. ai._damage += damage;
  732. // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
  733. if (aggro > 0 && getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE) getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  734. // Notify the L2Attackable AI with EVT_ATTACKED
  735. if (damage > 0)
  736. {
  737. getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, attacker);
  738. try {
  739. if (attacker instanceof L2PcInstance || attacker instanceof L2Summon)
  740. {
  741. L2PcInstance player = attacker instanceof L2PcInstance ? (L2PcInstance)attacker : ((L2Summon)attacker).getOwner();
  742. if (getTemplate().getEventQuests(Quest.QuestEventType.MOBGOTATTACKED) !=null)
  743. for (Quest quest: getTemplate().getEventQuests(Quest.QuestEventType.MOBGOTATTACKED))
  744. quest.notifyAttack(this, player, damage, attacker instanceof L2Summon);
  745. }
  746. }
  747. catch (Exception e) { _log.log(Level.SEVERE, "", e); }
  748. }
  749. }
  750. public void reduceHate(L2Character target, int amount)
  751. {
  752. if (getAI() instanceof L2SiegeGuardAI)
  753. {
  754. // TODO: this just prevents error until siege guards are handled properly
  755. stopHating(target);
  756. setTarget(null);
  757. getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE, null, null);
  758. return;
  759. }
  760. if (target == null) // whole aggrolist
  761. {
  762. L2Character mostHated = getMostHated();
  763. if (mostHated == null) // makes target passive for a moment more
  764. {
  765. ((L2AttackableAI)getAI()).setGlobalAggro(-25);
  766. return;
  767. }
  768. else
  769. {
  770. for(L2Character aggroed : getAggroListRP().keySet())
  771. {
  772. AggroInfo ai = getAggroListRP().get(aggroed);
  773. if (ai == null) return;
  774. ai._hate -= amount;
  775. }
  776. }
  777. amount = getHating(mostHated);
  778. if (amount <= 0)
  779. {
  780. ((L2AttackableAI)getAI()).setGlobalAggro(-25);
  781. clearAggroList();
  782. getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  783. setWalking();
  784. }
  785. return;
  786. }
  787. AggroInfo ai = getAggroListRP().get(target);
  788. if (ai == null) return;
  789. ai._hate -= amount;
  790. if (ai._hate <= 0)
  791. {
  792. if (getMostHated() == null)
  793. {
  794. ((L2AttackableAI)getAI()).setGlobalAggro(-25);
  795. clearAggroList();
  796. getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  797. setWalking();
  798. }
  799. }
  800. }
  801. /**
  802. * Clears _aggroList hate of the L2Character without removing from the list.<BR><BR>
  803. */
  804. public void stopHating(L2Character target)
  805. {
  806. if (target == null) return;
  807. AggroInfo ai = getAggroListRP().get(target);
  808. if (ai == null) return;
  809. ai._hate = 0;
  810. }
  811. /**
  812. * Return the most hated L2Character of the L2Attackable _aggroList.<BR><BR>
  813. */
  814. public L2Character getMostHated()
  815. {
  816. if (getAggroListRP().isEmpty() || isAlikeDead()) return null;
  817. L2Character mostHated = null;
  818. int maxHate = 0;
  819. // While Interating over This Map Removing Object is Not Allowed
  820. synchronized (getAggroList())
  821. {
  822. // Go through the aggroList of the L2Attackable
  823. for (AggroInfo ai : getAggroListRP().values())
  824. {
  825. if (ai == null) continue;
  826. if (ai._attacker.isAlikeDead() || !getKnownList().knowsObject(ai._attacker) ||!ai._attacker.isVisible())
  827. ai._hate = 0;
  828. if (ai._hate > maxHate)
  829. {
  830. mostHated = ai._attacker;
  831. maxHate = ai._hate;
  832. }
  833. }
  834. }
  835. return mostHated;
  836. }
  837. /**
  838. * Return the hate level of the L2Attackable against this L2Character contained in _aggroList.<BR><BR>
  839. *
  840. * @param target The L2Character whose hate level must be returned
  841. *
  842. */
  843. public int getHating(L2Character target)
  844. {
  845. if (getAggroListRP().isEmpty()) return 0;
  846. AggroInfo ai = getAggroListRP().get(target);
  847. if (ai == null) return 0;
  848. if (ai._attacker instanceof L2PcInstance && (((L2PcInstance)ai._attacker).getAppearance().getInvisible() || ai._attacker.isInvul()))
  849. {
  850. //Remove Object Should Use This Method and Can be Blocked While Interating
  851. getAggroList().remove(target);
  852. return 0;
  853. }
  854. if (!ai._attacker.isVisible())
  855. {
  856. getAggroList().remove(target);
  857. return 0;
  858. }
  859. if (ai._attacker.isAlikeDead())
  860. {
  861. ai._hate = 0;
  862. return 0;
  863. }
  864. return ai._hate;
  865. }
  866. /**
  867. * Calculates quantity of items for specific drop acording to current situation <br>
  868. *
  869. * @param drop The L2DropData count is being calculated for
  870. * @param lastAttacker The L2PcInstance that has killed the L2Attackable
  871. * @param deepBlueDrop Factor to divide the drop chance
  872. * @param levelModifier level modifier in %'s (will be subtracted from drop chance)
  873. */
  874. private RewardItem calculateRewardItem(L2PcInstance lastAttacker, L2DropData drop, int levelModifier, boolean isSweep)
  875. {
  876. // Get default drop chance
  877. float dropChance = drop.getChance();
  878. int deepBlueDrop = 1;
  879. if (Config.DEEPBLUE_DROP_RULES)
  880. {
  881. if (levelModifier > 0)
  882. {
  883. // We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
  884. // NOTE: This is valid only for adena drops! Others drops will still obey server's rate
  885. deepBlueDrop = 3;
  886. if (drop.getItemId() == 57) deepBlueDrop *= (int)Config.RATE_DROP_ITEMS;
  887. }
  888. }
  889. if(deepBlueDrop == 0) //avoid div by 0
  890. deepBlueDrop = 1;
  891. // Check if we should apply our maths so deep blue mobs will not drop that easy
  892. if (Config.DEEPBLUE_DROP_RULES) dropChance = ((drop.getChance() - ((drop.getChance() * levelModifier)/100)) / deepBlueDrop);
  893. // Applies Drop rates
  894. if (drop.getItemId() == 57) dropChance *= Config.RATE_DROP_ADENA;
  895. else if (isSweep) dropChance *= Config.RATE_DROP_SPOIL;
  896. else dropChance *= Config.RATE_DROP_ITEMS;
  897. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  898. dropChance *= Config.L2JMOD_CHAMPION_REWARDS;
  899. // Round drop chance
  900. dropChance = Math.round(dropChance);
  901. // Set our limits for chance of drop
  902. if (dropChance < 1) dropChance = 1;
  903. // if (drop.getItemId() == 57 && dropChance > L2DropData.MAX_CHANCE) dropChance = L2DropData.MAX_CHANCE; // If item is adena, dont drop multiple time
  904. // Get min and max Item quantity that can be dropped in one time
  905. int minCount = drop.getMinDrop();
  906. int maxCount = drop.getMaxDrop();
  907. int itemCount = 0;
  908. // Count and chance adjustment for high rate servers
  909. if (dropChance > L2DropData.MAX_CHANCE && !Config.PRECISE_DROP_CALCULATION)
  910. {
  911. int multiplier = (int)dropChance / L2DropData.MAX_CHANCE;
  912. if (minCount < maxCount) itemCount += Rnd.get(minCount * multiplier, maxCount * multiplier);
  913. else if (minCount == maxCount) itemCount += minCount * multiplier;
  914. else itemCount += multiplier;
  915. dropChance = dropChance % L2DropData.MAX_CHANCE;
  916. }
  917. // Check if the Item must be dropped
  918. int random = Rnd.get(L2DropData.MAX_CHANCE);
  919. while (random < dropChance)
  920. {
  921. // Get the item quantity dropped
  922. if (minCount < maxCount) itemCount += Rnd.get(minCount, maxCount);
  923. else if (minCount == maxCount) itemCount += minCount;
  924. else itemCount++;
  925. // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
  926. dropChance -= L2DropData.MAX_CHANCE;
  927. }
  928. if (Config.L2JMOD_CHAMPION_ENABLE)
  929. if ((drop.getItemId() == 57 || (drop.getItemId() >= 6360 && drop.getItemId() <= 6362)) && isChampion())
  930. itemCount *= Config.L2JMOD_CHAMPION_ADENAS_REWARDS;
  931. if (itemCount > 0) return new RewardItem(drop.getItemId(), itemCount);
  932. else if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop...");
  933. return null;
  934. }
  935. /**
  936. * Calculates quantity of items for specific drop CATEGORY according to current situation <br>
  937. * Only a max of ONE item from a category is allowed to be dropped.
  938. *
  939. * @param drop The L2DropData count is being calculated for
  940. * @param lastAttacker The L2PcInstance that has killed the L2Attackable
  941. * @param deepBlueDrop Factor to divide the drop chance
  942. * @param levelModifier level modifier in %'s (will be subtracted from drop chance)
  943. */
  944. private RewardItem calculateCategorizedRewardItem(L2PcInstance lastAttacker, L2DropCategory categoryDrops, int levelModifier)
  945. {
  946. if (categoryDrops == null)
  947. return null;
  948. // Get default drop chance for the category (that's the sum of chances for all items in the category)
  949. // keep track of the base category chance as it'll be used later, if an item is drop from the category.
  950. // for everything else, use the total "categoryDropChance"
  951. int basecategoryDropChance = categoryDrops.getCategoryChance() ;
  952. int categoryDropChance = basecategoryDropChance;
  953. int deepBlueDrop = 1;
  954. if (Config.DEEPBLUE_DROP_RULES)
  955. {
  956. if (levelModifier > 0)
  957. {
  958. // We should multiply by the server's drop rate, so we always get a low chance of drop for deep blue mobs.
  959. // NOTE: This is valid only for adena drops! Others drops will still obey server's rate
  960. deepBlueDrop = 3;
  961. }
  962. }
  963. if(deepBlueDrop == 0) //avoid div by 0
  964. deepBlueDrop = 1;
  965. // Check if we should apply our maths so deep blue mobs will not drop that easy
  966. if (Config.DEEPBLUE_DROP_RULES) categoryDropChance = ((categoryDropChance - ((categoryDropChance * levelModifier)/100)) / deepBlueDrop);
  967. // Applies Drop rates
  968. categoryDropChance *= Config.RATE_DROP_ITEMS;
  969. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  970. categoryDropChance *= Config.L2JMOD_CHAMPION_REWARDS;
  971. // Round drop chance
  972. categoryDropChance = Math.round(categoryDropChance);
  973. // Set our limits for chance of drop
  974. if (categoryDropChance < 1) categoryDropChance = 1;
  975. // Check if an Item from this category must be dropped
  976. if (Rnd.get(L2DropData.MAX_CHANCE) < categoryDropChance)
  977. {
  978. L2DropData drop = categoryDrops.dropOne();
  979. if (drop == null)
  980. return null;
  981. // Now decide the quantity to drop based on the rates and penalties. To get this value
  982. // simply divide the modified categoryDropChance by the base category chance. This
  983. // results in a chance that will dictate the drops amounts: for each amount over 100
  984. // that it is, it will give another chance to add to the min/max quantities.
  985. //
  986. // For example, If the final chance is 120%, then the item should drop between
  987. // its min and max one time, and then have 20% chance to drop again. If the final
  988. // chance is 330%, it will similarly give 3 times the min and max, and have a 30%
  989. // chance to give a 4th time.
  990. // At least 1 item will be dropped for sure. So the chance will be adjusted to 100%
  991. // if smaller.
  992. int dropChance = drop.getChance();
  993. if (drop.getItemId() == 57) dropChance *= Config.RATE_DROP_ADENA;
  994. else dropChance *= Config.RATE_DROP_ITEMS;
  995. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  996. dropChance *= Config.L2JMOD_CHAMPION_REWARDS;
  997. dropChance = Math.round(dropChance);
  998. if (dropChance < L2DropData.MAX_CHANCE)
  999. dropChance = L2DropData.MAX_CHANCE;
  1000. // Get min and max Item quantity that can be dropped in one time
  1001. int min = drop.getMinDrop();
  1002. int max = drop.getMaxDrop();
  1003. // Get the item quantity dropped
  1004. int itemCount = 0;
  1005. // Count and chance adjustment for high rate servers
  1006. if (dropChance > L2DropData.MAX_CHANCE && !Config.PRECISE_DROP_CALCULATION)
  1007. {
  1008. int multiplier = dropChance / L2DropData.MAX_CHANCE;
  1009. if (min < max) itemCount += Rnd.get(min * multiplier, max * multiplier);
  1010. else if (min == max) itemCount += min * multiplier;
  1011. else itemCount += multiplier;
  1012. dropChance = dropChance % L2DropData.MAX_CHANCE;
  1013. }
  1014. // Check if the Item must be dropped
  1015. int random = Rnd.get(L2DropData.MAX_CHANCE);
  1016. while (random < dropChance)
  1017. {
  1018. // Get the item quantity dropped
  1019. if (min < max) itemCount += Rnd.get(min, max);
  1020. else if (min == max) itemCount += min;
  1021. else itemCount++;
  1022. // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
  1023. dropChance -= L2DropData.MAX_CHANCE;
  1024. }
  1025. if (Config.L2JMOD_CHAMPION_ENABLE)
  1026. if ((drop.getItemId() == 57 || (drop.getItemId() >= 6360 && drop.getItemId() <= 6362)) && isChampion())
  1027. itemCount *= Config.L2JMOD_CHAMPION_ADENAS_REWARDS;
  1028. if (itemCount > 0)
  1029. return new RewardItem(drop.getItemId(), itemCount);
  1030. else if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop...");
  1031. }
  1032. return null;
  1033. /*
  1034. // Applies Drop rates
  1035. if (drop.getItemId() == 57) dropChance *= Config.RATE_DROP_ADENA;
  1036. else if (isSweep) dropChance *= Config.RATE_DROP_SPOIL;
  1037. else dropChance *= Config.RATE_DROP_ITEMS;
  1038. // Round drop chance
  1039. dropChance = Math.round(dropChance);
  1040. // Set our limits for chance of drop
  1041. if (dropChance < 1) dropChance = 1;
  1042. // if (drop.getItemId() == 57 && dropChance > L2DropData.MAX_CHANCE) dropChance = L2DropData.MAX_CHANCE; // If item is adena, dont drop multiple time
  1043. // Get min and max Item quantity that can be dropped in one time
  1044. int minCount = drop.getMinDrop();
  1045. int maxCount = drop.getMaxDrop();
  1046. int itemCount = 0;
  1047. if (itemCount > 0) return new RewardItem(drop.getItemId(), itemCount);
  1048. else if (itemCount == 0 && Config.DEBUG) _log.fine("Roll produced 0 items to drop...");
  1049. return null;
  1050. */
  1051. }
  1052. /**
  1053. * Calculates the level modifier for drop<br>
  1054. *
  1055. * @param lastAttacker The L2PcInstance that has killed the L2Attackable
  1056. */
  1057. private int calculateLevelModifierForDrop(L2PcInstance lastAttacker)
  1058. {
  1059. if (Config.DEEPBLUE_DROP_RULES)
  1060. {
  1061. int highestLevel = lastAttacker.getLevel();
  1062. // Check to prevent very high level player to nearly kill mob and let low level player do the last hit.
  1063. if (getAttackByList() != null && !getAttackByList().isEmpty())
  1064. {
  1065. for (L2Character atkChar: getAttackByList())
  1066. if (atkChar != null && atkChar.getLevel() > highestLevel) highestLevel = atkChar.getLevel();
  1067. }
  1068. // According to official data (Prima), deep blue mobs are 9 or more levels below players
  1069. if (highestLevel - 9 >= getLevel()) return ((highestLevel - (getLevel() + 8)) * 9);
  1070. }
  1071. return 0;
  1072. }
  1073. public void doItemDrop(L2Character lastAttacker)
  1074. {
  1075. doItemDrop(getTemplate(),lastAttacker);
  1076. }
  1077. /**
  1078. * Manage Base, Quests and Special Events drops of L2Attackable (called by calculateRewards).<BR><BR>
  1079. *
  1080. * <B><U> Concept</U> :</B><BR><BR>
  1081. * During a Special Event all L2Attackable can drop extra Items.
  1082. * Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist.
  1083. * Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR><BR>
  1084. *
  1085. * <B><U> Actions</U> : </B><BR><BR>
  1086. * <li>Manage drop of Special Events created by GM for a defined period </li>
  1087. * <li>Get all possible drops of this L2Attackable from L2NpcTemplate and add it Quest drops</li>
  1088. * <li>For each possible drops (base + quests), calculate which one must be dropped (random) </li>
  1089. * <li>Get each Item quantity dropped (random) </li>
  1090. * <li>Create this or these L2ItemInstance corresponding to each Item Identifier dropped</li>
  1091. * <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li>
  1092. * <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR><BR>
  1093. *
  1094. * @param lastAttacker The L2Character that has killed the L2Attackable
  1095. */
  1096. public void doItemDrop(L2NpcTemplate npcTemplate, L2Character lastAttacker)
  1097. {
  1098. L2PcInstance player = null;
  1099. if (lastAttacker instanceof L2PcInstance) player = (L2PcInstance)lastAttacker;
  1100. else if (lastAttacker instanceof L2Summon) player = ((L2Summon)lastAttacker).getOwner();
  1101. else if (lastAttacker instanceof L2Trap) player = ((L2Trap)lastAttacker).getOwner();
  1102. if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
  1103. int levelModifier = calculateLevelModifierForDrop(player); // level modifier in %'s (will be subtracted from drop chance)
  1104. CursedWeaponsManager.getInstance().checkDrop(this, player);
  1105. // now throw all categorized drops and handle spoil.
  1106. if (npcTemplate.getDropData()!=null)
  1107. for(L2DropCategory cat:npcTemplate.getDropData())
  1108. {
  1109. RewardItem item = null;
  1110. if (cat.isSweep())
  1111. {
  1112. // according to sh1ny, seeded mobs CAN be spoiled and swept.
  1113. if ( isSpoil()/* && !isSeeded() */)
  1114. {
  1115. FastList<RewardItem> sweepList = new FastList<RewardItem>();
  1116. for(L2DropData drop: cat.getAllDrops() )
  1117. {
  1118. item = calculateRewardItem(player, drop, levelModifier, true);
  1119. if (item == null) continue;
  1120. if (Config.DEBUG) _log.fine("Item id to spoil: " + item.getItemId() + " amount: " + item.getCount());
  1121. sweepList.add(item);
  1122. }
  1123. // Set the table _sweepItems of this L2Attackable
  1124. if (!sweepList.isEmpty())
  1125. _sweepItems = sweepList.toArray(new RewardItem[sweepList.size()]);
  1126. }
  1127. }
  1128. else
  1129. {
  1130. if (isSeeded())
  1131. {
  1132. L2DropData drop = cat.dropSeedAllowedDropsOnly();
  1133. if(drop == null)
  1134. continue;
  1135. item = calculateRewardItem(player, drop, levelModifier, false);
  1136. }
  1137. else
  1138. {
  1139. item = calculateCategorizedRewardItem(player, cat, levelModifier);
  1140. }
  1141. if (item != null)
  1142. {
  1143. if (Config.DEBUG) _log.fine("Item id to drop: " + item.getItemId() + " amount: " + item.getCount());
  1144. // Check if the autoLoot mode is active
  1145. if (Config.AUTO_LOOT) player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
  1146. else DropItem(player, item); // drop the item on the ground
  1147. // Broadcast message if RaidBoss was defeated
  1148. if(this instanceof L2RaidBossInstance)
  1149. {
  1150. SystemMessage sm;
  1151. sm = new SystemMessage(SystemMessageId.S1_DIED_DROPPED_S3_S2);
  1152. sm.addString(getName());
  1153. sm.addItemName(item.getItemId());
  1154. sm.addNumber(item.getCount());
  1155. broadcastPacket(sm);
  1156. }
  1157. }
  1158. }
  1159. }
  1160. // Apply Special Item drop with rnd qty for champions
  1161. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion() && (player.getLevel() <= getLevel()) && Config.L2JMOD_CHAMPION_REWARD > 0 && (Rnd.get(100) < Config.L2JMOD_CHAMPION_REWARD))
  1162. {
  1163. int champqty = Rnd.get(Config.L2JMOD_CHAMPION_REWARD_QTY);
  1164. champqty++; //quantity should actually vary between 1 and whatever admin specified as max, inclusive.
  1165. RewardItem item = new RewardItem(Config.L2JMOD_CHAMPION_REWARD_ID,champqty);
  1166. if (Config.AUTO_LOOT) player.addItem("ChampionLoot", item.getItemId(), item.getCount(), this, true); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
  1167. else DropItem(player, item);
  1168. }
  1169. //Instant Item Drop :>
  1170. double rateHp = getStat().calcStat(Stats.MAX_HP , 1, this, null);
  1171. if (rateHp <= 1 && String.valueOf(npcTemplate.type).contentEquals("L2Monster")) //only L2Monster with <= 1x HP can drop herbs
  1172. {
  1173. boolean _hp = false;
  1174. boolean _mp = false;
  1175. boolean _spec = false;
  1176. //ptk - patk type enhance
  1177. int random = Rnd.get(1000); // note *10
  1178. if ((random < Config.RATE_DROP_SPECIAL_HERBS) && !_spec) // && !_spec useless yet
  1179. {
  1180. RewardItem item = new RewardItem(8612, 1); // Herb of Warrior
  1181. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1182. else DropItem(player, item);
  1183. _spec = true;
  1184. }
  1185. else for (int i = 0; i < 3; i++)
  1186. {
  1187. random = Rnd.get(100);
  1188. if (random < Config.RATE_DROP_COMMON_HERBS)
  1189. {
  1190. RewardItem item = null;
  1191. if (i == 0) item = new RewardItem(8606, 1); // Herb of Power
  1192. if (i == 1) item = new RewardItem(8608, 1); // Herb of Atk. Spd.
  1193. if (i == 2) item = new RewardItem(8610, 1); // Herb of Critical Attack
  1194. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1195. else DropItem(player, item);
  1196. break;
  1197. }
  1198. }
  1199. //mtk - matk type enhance
  1200. random = Rnd.get(1000); // note *10
  1201. if ((random < Config.RATE_DROP_SPECIAL_HERBS) && !_spec)
  1202. {
  1203. RewardItem item = new RewardItem(8613, 1); // Herb of Mystic
  1204. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1205. else DropItem(player, item);
  1206. _spec = true;
  1207. }
  1208. else for (int i = 0; i < 2; i++)
  1209. {
  1210. random = Rnd.get(100);
  1211. if (random < Config.RATE_DROP_COMMON_HERBS)
  1212. {
  1213. RewardItem item = null;
  1214. if (i == 0) item = new RewardItem(8607, 1); // Herb of Magic
  1215. if (i == 1) item = new RewardItem(8609, 1); // Herb of Casting Speed
  1216. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1217. else DropItem(player, item);
  1218. break;
  1219. }
  1220. }
  1221. //hp+mp type
  1222. random = Rnd.get(1000); // note *10
  1223. if ((random < Config.RATE_DROP_SPECIAL_HERBS) && !_spec)
  1224. {
  1225. RewardItem item = new RewardItem(8614, 1); // Herb of Recovery
  1226. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1227. else DropItem(player, item);
  1228. _mp = true;
  1229. _hp = true;
  1230. _spec = true;
  1231. }
  1232. //hp - restore hp type
  1233. if (!_hp)
  1234. {
  1235. random = Rnd.get(100);
  1236. if (random < Config.RATE_DROP_MP_HP_HERBS)
  1237. {
  1238. RewardItem item = new RewardItem(8600, 1); // Herb of Life
  1239. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1240. else DropItem(player, item);
  1241. _hp = true;
  1242. }
  1243. }
  1244. if (!_hp)
  1245. {
  1246. random = Rnd.get(100);
  1247. if (random < Config.RATE_DROP_GREATER_HERBS)
  1248. {
  1249. RewardItem item = new RewardItem(8601, 1); // Greater Herb of Life
  1250. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1251. else DropItem(player, item);
  1252. _hp = true;
  1253. }
  1254. }
  1255. if (!_hp)
  1256. {
  1257. random = Rnd.get(1000); // note *10
  1258. if (random < Config.RATE_DROP_SUPERIOR_HERBS)
  1259. {
  1260. RewardItem item = new RewardItem(8602, 1); // Superior Herb of Life
  1261. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1262. else DropItem(player, item);
  1263. }
  1264. }
  1265. //mp - restore mp type
  1266. if (!_mp)
  1267. {
  1268. random = Rnd.get(100);
  1269. if (random < Config.RATE_DROP_MP_HP_HERBS)
  1270. {
  1271. RewardItem item = new RewardItem(8603, 1); // Herb of Manna
  1272. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1273. else DropItem(player, item);
  1274. _mp = true;
  1275. }
  1276. }
  1277. if (!_mp)
  1278. {
  1279. random = Rnd.get(100);
  1280. if (random < Config.RATE_DROP_GREATER_HERBS)
  1281. {
  1282. RewardItem item = new RewardItem(8604, 1); // Greater Herb of Mana
  1283. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1284. else DropItem(player, item);
  1285. _mp = true;
  1286. }
  1287. }
  1288. if (!_mp)
  1289. {
  1290. random = Rnd.get(1000); // note *10
  1291. if (random < Config.RATE_DROP_SUPERIOR_HERBS)
  1292. {
  1293. RewardItem item = new RewardItem(8605, 1); // Superior Herb of Mana
  1294. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1295. else DropItem(player, item);
  1296. }
  1297. }
  1298. // speed enhance type
  1299. random = Rnd.get(100);
  1300. if (random < Config.RATE_DROP_COMMON_HERBS)
  1301. {
  1302. RewardItem item = new RewardItem(8611, 1); // Herb of Speed
  1303. if (Config.AUTO_LOOT && Config.AUTO_LOOT_HERBS) player.addItem("Loot", item.getItemId(), item.getCount(), this, true);
  1304. else DropItem(player, item);
  1305. }
  1306. }
  1307. }
  1308. /**
  1309. * Manage Special Events drops created by GM for a defined period.<BR><BR>
  1310. *
  1311. * <B><U> Concept</U> :</B><BR><BR>
  1312. * During a Special Event all L2Attackable can drop extra Items.
  1313. * Those extra Items are defined in the table <B>allNpcDateDrops</B> of the EventDroplist.
  1314. * Each Special Event has a start and end date to stop to drop extra Items automaticaly. <BR><BR>
  1315. *
  1316. * <B><U> Actions</U> : <I>If an extra drop must be generated</I></B><BR><BR>
  1317. * <li>Get an Item Identifier (random) from the DateDrop Item table of this Event </li>
  1318. * <li>Get the Item quantity dropped (random) </li>
  1319. * <li>Create this or these L2ItemInstance corresponding to this Item Identifier</li>
  1320. * <li>If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, give this or these Item(s) to the L2PcInstance that has killed the L2Attackable</li>
  1321. * <li>If the autoLoot mode isn't actif or if the L2Character that has killed the L2Attackable is not a L2PcInstance, add this or these Item(s) in the world as a visible object at the position where mob was last</li><BR><BR>
  1322. *
  1323. * @param lastAttacker The L2Character that has killed the L2Attackable
  1324. */
  1325. public void doEventDrop(L2Character lastAttacker)
  1326. {
  1327. L2PcInstance player = null;
  1328. if (lastAttacker instanceof L2PcInstance)
  1329. player = (L2PcInstance)lastAttacker;
  1330. else if (lastAttacker instanceof L2Summon)
  1331. player = ((L2Summon)lastAttacker).getOwner();
  1332. else if (lastAttacker instanceof L2Trap)
  1333. player = ((L2Trap)lastAttacker).getOwner();
  1334. if (player == null) return; // Don't drop anything if the last attacker or ownere isn't L2PcInstance
  1335. if (player.getLevel() - getLevel() > 9) return;
  1336. // Go through DateDrop of EventDroplist allNpcDateDrops within the date range
  1337. for (DateDrop drop : EventDroplist.getInstance().getAllDrops())
  1338. {
  1339. if (Rnd.get(L2DropData.MAX_CHANCE) < drop.chance)
  1340. {
  1341. RewardItem item = new RewardItem(drop.items[Rnd.get(drop.items.length)], Rnd.get(drop.min, drop.max));
  1342. if (Config.AUTO_LOOT) player.doAutoLoot(this, item); // Give this or these Item(s) to the L2PcInstance that has killed the L2Attackable
  1343. else DropItem(player, item); // drop the item on the ground
  1344. }
  1345. }
  1346. }
  1347. /**
  1348. * Drop reward item.<BR><BR>
  1349. */
  1350. public L2ItemInstance DropItem(L2PcInstance lastAttacker, RewardItem item)
  1351. {
  1352. int randDropLim = 70;
  1353. L2ItemInstance ditem = null;
  1354. for (int i = 0; i < item.getCount(); i++)
  1355. {
  1356. // Randomize drop position
  1357. int newX = getX() + Rnd.get(randDropLim * 2 + 1) - randDropLim;
  1358. int newY = getY() + Rnd.get(randDropLim * 2 + 1) - randDropLim;
  1359. int newZ = Math.max(getZ(), lastAttacker.getZ()) + 20; // TODO: temp hack, do somethign nicer when we have geodatas
  1360. // Init the dropped L2ItemInstance and add it in the world as a visible object at the position where mob was last
  1361. ditem = ItemTable.getInstance().createItem("Loot", item.getItemId(), item.getCount(), lastAttacker, this);
  1362. ditem.dropMe(this, newX, newY, newZ);
  1363. // Add drop to auto destroy item task
  1364. if (!Config.LIST_PROTECTED_ITEMS.contains(item.getItemId())){
  1365. if ((Config.AUTODESTROY_ITEM_AFTER > 0 && ditem.getItemType() != L2EtcItemType.HERB)
  1366. || (Config.HERB_AUTO_DESTROY_TIME > 0 && ditem.getItemType() == L2EtcItemType.HERB))
  1367. ItemsAutoDestroy.getInstance().addItem(ditem);
  1368. }
  1369. ditem.setProtected(false);
  1370. // If stackable, end loop as entire count is included in 1 instance of item
  1371. if (ditem.isStackable() || !Config.MULTIPLE_ITEM_DROP) break;
  1372. }
  1373. return ditem;
  1374. }
  1375. public L2ItemInstance DropItem(L2PcInstance lastAttacker, int itemId, int itemCount)
  1376. {
  1377. return DropItem(lastAttacker, new RewardItem(itemId, itemCount));
  1378. }
  1379. /**
  1380. * Return the active weapon of this L2Attackable (= null).<BR><BR>
  1381. */
  1382. public L2ItemInstance getActiveWeapon()
  1383. {
  1384. return null;
  1385. }
  1386. /**
  1387. * Return True if the _aggroList of this L2Attackable is Empty.<BR><BR>
  1388. */
  1389. public boolean noTarget()
  1390. {
  1391. return getAggroListRP().isEmpty();
  1392. }
  1393. /**
  1394. * Return True if the _aggroList of this L2Attackable contains the L2Character.<BR><BR>
  1395. *
  1396. * @param player The L2Character searched in the _aggroList of the L2Attackable
  1397. *
  1398. */
  1399. public boolean containsTarget(L2Character player)
  1400. {
  1401. return getAggroListRP().containsKey(player);
  1402. }
  1403. /**
  1404. * Clear the _aggroList of the L2Attackable.<BR><BR>
  1405. */
  1406. public void clearAggroList()
  1407. {
  1408. getAggroList().clear();
  1409. }
  1410. /**
  1411. * Return True if a Dwarf use Sweep on the L2Attackable and if item can be spoiled.<BR><BR>
  1412. */
  1413. public boolean isSweepActive()
  1414. {
  1415. return _sweepItems != null;
  1416. }
  1417. /**
  1418. * Return table containing all L2ItemInstance that can be spoiled.<BR><BR>
  1419. */
  1420. public synchronized RewardItem[] takeSweep()
  1421. {
  1422. RewardItem[] sweep = _sweepItems;
  1423. _sweepItems = null;
  1424. return sweep;
  1425. }
  1426. /**
  1427. * Return table containing all L2ItemInstance that can be harvested.<BR><BR>
  1428. */
  1429. public synchronized RewardItem[] takeHarvest()
  1430. {
  1431. RewardItem[] harvest = _harvestItems;
  1432. _harvestItems = null;
  1433. return harvest;
  1434. }
  1435. /**
  1436. * Set the over-hit flag on the L2Attackable.<BR><BR>
  1437. *
  1438. * @param status The status of the over-hit flag
  1439. *
  1440. */
  1441. public void overhitEnabled(boolean status)
  1442. {
  1443. _overhit = status;
  1444. }
  1445. /**
  1446. * Set the over-hit values like the attacker who did the strike and the ammount of damage done by the skill.<BR><BR>
  1447. *
  1448. * @param attacker The L2Character who hit on the L2Attackable using the over-hit enabled skill
  1449. * @param damage The ammount of damage done by the over-hit enabled skill on the L2Attackable
  1450. *
  1451. */
  1452. public void setOverhitValues(L2Character attacker, double damage)
  1453. {
  1454. // Calculate the over-hit damage
  1455. // Ex: mob had 10 HP left, over-hit skill did 50 damage total, over-hit damage is 40
  1456. double overhitDmg = ((getCurrentHp() - damage) * (-1));
  1457. if (overhitDmg < 0)
  1458. {
  1459. // we didn't killed the mob with the over-hit strike. (it wasn't really an over-hit strike)
  1460. // let's just clear all the over-hit related values
  1461. overhitEnabled(false);
  1462. _overhitDamage = 0;
  1463. _overhitAttacker = null;
  1464. return;
  1465. }
  1466. overhitEnabled(true);
  1467. _overhitDamage = overhitDmg;
  1468. _overhitAttacker = attacker;
  1469. }
  1470. /**
  1471. * Return the L2Character who hit on the L2Attackable using an over-hit enabled skill.<BR><BR>
  1472. *
  1473. * @return L2Character attacker
  1474. */
  1475. public L2Character getOverhitAttacker()
  1476. {
  1477. return _overhitAttacker;
  1478. }
  1479. /**
  1480. * Return the ammount of damage done on the L2Attackable using an over-hit enabled skill.<BR><BR>
  1481. *
  1482. * @return double damage
  1483. */
  1484. public double getOverhitDamage()
  1485. {
  1486. return _overhitDamage;
  1487. }
  1488. /**
  1489. * Return True if the L2Attackable was hit by an over-hit enabled skill.<BR><BR>
  1490. */
  1491. public boolean isOverhit()
  1492. {
  1493. return _overhit;
  1494. }
  1495. /**
  1496. * Activate the absorbed soul condition on the L2Attackable.<BR><BR>
  1497. */
  1498. public void absorbSoul()
  1499. {
  1500. _absorbed = true;
  1501. }
  1502. /**
  1503. * Return True if the L2Attackable had his soul absorbed.<BR><BR>
  1504. */
  1505. public boolean isAbsorbed()
  1506. {
  1507. return _absorbed;
  1508. }
  1509. /**
  1510. * Adds an attacker that successfully absorbed the soul of this L2Attackable into the _absorbersList.<BR><BR>
  1511. *
  1512. * params: attacker - a valid L2PcInstance
  1513. * condition - an integer indicating the event when mob dies. This should be:
  1514. * = 0 - "the crystal scatters";
  1515. * = 1 - "the crystal failed to absorb. nothing happens";
  1516. * = 2 - "the crystal resonates because you got more than 1 crystal on you";
  1517. * = 3 - "the crystal cannot absorb the soul because the mob level is too low";
  1518. * = 4 - "the crystal successfuly absorbed the soul";
  1519. */
  1520. public void addAbsorber(L2PcInstance attacker, int crystalId)
  1521. {
  1522. // This just works for targets like L2MonsterInstance
  1523. if (!(this instanceof L2MonsterInstance))
  1524. return;
  1525. // The attacker must not be null
  1526. if (attacker == null)
  1527. return;
  1528. // This L2Attackable must be of one type in the _absorbingMOBS_levelXX tables.
  1529. // OBS: This is done so to avoid triggering the absorbed conditions for mobs that can't be absorbed.
  1530. if (getAbsorbLevel() == 0)
  1531. return;
  1532. // If we have no _absorbersList initiated, do it
  1533. AbsorberInfo ai = _absorbersList.get(attacker);
  1534. // If the L2Character attacker isn't already in the _absorbersList of this L2Attackable, add it
  1535. if (ai == null)
  1536. {
  1537. ai = new AbsorberInfo(attacker, crystalId, getCurrentHp());
  1538. _absorbersList.put(attacker, ai);
  1539. }
  1540. else
  1541. {
  1542. ai._absorber = attacker;
  1543. ai._crystalId = crystalId;
  1544. ai._absorbedHP = getCurrentHp();
  1545. }
  1546. // Set this L2Attackable as absorbed
  1547. absorbSoul();
  1548. }
  1549. /**
  1550. * Calculate the leveling chance of Soul Crystals based on the attacker that killed this L2Attackable
  1551. *
  1552. * @param attacker The player that last killed this L2Attackable
  1553. * $ Rewrite 06.12.06 - Yesod
  1554. */
  1555. private void levelSoulCrystals(L2Character attacker)
  1556. {
  1557. // Only L2PcInstance can absorb a soul
  1558. if (!(attacker instanceof L2PcInstance) && !(attacker instanceof L2Summon))
  1559. {
  1560. resetAbsorbList(); return;
  1561. }
  1562. int maxAbsorbLevel = getAbsorbLevel();
  1563. int minAbsorbLevel = 0;
  1564. // If this is not a valid L2Attackable, clears the _absorbersList and just return
  1565. if (maxAbsorbLevel == 0)
  1566. {
  1567. resetAbsorbList(); return;
  1568. }
  1569. // All boss mobs with maxAbsorbLevel 13 have minAbsorbLevel of 12 else 10
  1570. if (maxAbsorbLevel > 10)
  1571. minAbsorbLevel = maxAbsorbLevel > 12 ? 12 : 10;
  1572. //Init some useful vars
  1573. boolean isSuccess = true;
  1574. boolean doLevelup = true;
  1575. boolean isBossMob = maxAbsorbLevel > 10 ? true : false;
  1576. L2NpcTemplate.AbsorbCrystalType absorbType = getTemplate().absorbType;
  1577. L2PcInstance killer = (attacker instanceof L2Summon)? ((L2Summon)attacker).getOwner() : (L2PcInstance)attacker;
  1578. // If this mob is a boss, then skip some checkings
  1579. if (!isBossMob)
  1580. {
  1581. // Fail if this L2Attackable isn't absorbed or there's no one in its _absorbersList
  1582. if (!isAbsorbed() /*|| _absorbersList == null*/)
  1583. {
  1584. resetAbsorbList();
  1585. return;
  1586. }
  1587. // Fail if the killer isn't in the _absorbersList of this L2Attackable and mob is not boss
  1588. AbsorberInfo ai = _absorbersList.get(killer);
  1589. if (ai == null || ai._absorber.getObjectId() != killer.getObjectId())
  1590. isSuccess = false;
  1591. // Check if the soul crystal was used when HP of this L2Attackable wasn't higher than half of it
  1592. if (ai != null && ai._absorbedHP > (getMaxHp()/2.0))
  1593. isSuccess = false;
  1594. if (!isSuccess) {
  1595. resetAbsorbList();
  1596. return;
  1597. }
  1598. }
  1599. // ********
  1600. String[] crystalNFO = null;
  1601. String crystalNME = "";
  1602. int dice = Rnd.get(100);
  1603. int crystalQTY = 0;
  1604. int crystalLVL = 0;
  1605. int crystalOLD = 0;
  1606. int crystalNEW = 0;
  1607. // ********
  1608. // Now we have four choices:
  1609. // 1- The Monster level is too low for the crystal. Nothing happens.
  1610. // 2- Everything is correct, but it failed. Nothing happens. (57.5%)
  1611. // 3- Everything is correct, but it failed. The crystal scatters. A sound event is played. (10%)
  1612. // 4- Everything is correct, the crystal level up. A sound event is played. (32.5%)
  1613. List<L2PcInstance> players = new FastList<L2PcInstance>();
  1614. if (absorbType == L2NpcTemplate.AbsorbCrystalType.FULL_PARTY && killer.isInParty())
  1615. players = killer.getParty().getPartyMembers();
  1616. else if (absorbType == L2NpcTemplate.AbsorbCrystalType.PARTY_ONE_RANDOM && killer.isInParty())
  1617. {
  1618. // This is a naive method for selecting a random member. It gets any random party member and
  1619. // then checks if the member has a valid crystal. It does not select the random party member
  1620. // among those who have crystals, only. However, this might actually be correct (same as retail).
  1621. players.add(killer.getParty().getPartyMembers().get(Rnd.get(killer.getParty().getMemberCount())));
  1622. }
  1623. else
  1624. players.add(killer);
  1625. for (L2PcInstance player : players)
  1626. {
  1627. if (player == null) continue;
  1628. crystalQTY = 0;
  1629. L2ItemInstance[] inv = player.getInventory().getItems();
  1630. for (L2ItemInstance item : inv)
  1631. {
  1632. int itemId = item.getItemId();
  1633. for (int id : SoulCrystal.SoulCrystalTable)
  1634. {
  1635. // Find any of the 39 possible crystals.
  1636. if (id == itemId)
  1637. {
  1638. crystalQTY++;
  1639. // Keep count but make sure the player has no more than 1 crystal
  1640. if (crystalQTY > 1)
  1641. {
  1642. isSuccess = false; break;
  1643. }
  1644. // Validate if the crystal has already leveled
  1645. if(id != SoulCrystal.RED_NEW_CRYSTAL
  1646. && id != SoulCrystal.GRN_NEW_CYRSTAL
  1647. && id != SoulCrystal.BLU_NEW_CRYSTAL)
  1648. {
  1649. try
  1650. {
  1651. if (item.getItem().getName().contains("Grade"))
  1652. {
  1653. // Split the name of the crystal into 'name' & 'level'
  1654. crystalNFO = item.getItem().getName().trim().replace(" Grade ", "-").split("-");
  1655. // Set Level to 13
  1656. crystalLVL = 13;
  1657. // Get Name
  1658. crystalNME = crystalNFO[0].toLowerCase();
  1659. } else
  1660. {
  1661. // Split the name of the crystal into 'name' & 'level'
  1662. crystalNFO = item.getItem().getName().trim().replace(" Stage ", "").split("-");
  1663. // Get Level
  1664. crystalLVL = Integer.parseInt(crystalNFO[1].trim());
  1665. // Get Name
  1666. crystalNME = crystalNFO[0].toLowerCase();
  1667. }
  1668. // Allocate current and levelup ids' for higher level crystals
  1669. if(crystalLVL > 9)
  1670. {
  1671. for(int i = 0; i < SoulCrystal.HighSoulConvert.length; i++)
  1672. // Get the next stage above 10 using array.
  1673. if(id == SoulCrystal.HighSoulConvert[i][0])
  1674. {
  1675. crystalNEW = SoulCrystal.HighSoulConvert[i][1]; break;
  1676. }
  1677. }
  1678. else
  1679. crystalNEW = id+1;
  1680. }
  1681. catch (NumberFormatException nfe)
  1682. {
  1683. _log.log(Level.WARNING, "An attempt to identify a soul crystal failed, " +
  1684. "verify the names have not changed in etcitem " +
  1685. "table.", nfe);
  1686. player.sendMessage("There has been an error handling your soul crystal." +
  1687. " Please notify your server admin.");
  1688. isSuccess = false;
  1689. break;
  1690. }
  1691. catch (Exception e)
  1692. {
  1693. e.printStackTrace();
  1694. isSuccess = false;
  1695. break;
  1696. }
  1697. }
  1698. else
  1699. {
  1700. crystalNME = item.getItem().getName().toLowerCase().trim();
  1701. crystalNEW = id+1;
  1702. }
  1703. // Done
  1704. crystalOLD = id;
  1705. break;
  1706. }
  1707. }
  1708. if (!isSuccess) break;
  1709. }
  1710. // If the crystal level is way too high for this mob, say that we can't increase it
  1711. if ((crystalLVL < minAbsorbLevel) || (crystalLVL >= maxAbsorbLevel))
  1712. doLevelup = false;
  1713. // The player doesn't have any crystals with him get to the next player.
  1714. if (crystalQTY < 1 || crystalQTY > 1 || !isSuccess || !doLevelup)
  1715. {
  1716. // Too many crystals in inventory.
  1717. if (crystalQTY > 1)
  1718. {
  1719. player.sendPacket(new SystemMessage(SystemMessageId.SOUL_CRYSTAL_ABSORBING_FAILED_RESONATION));
  1720. }
  1721. // The soul crystal stage of the player is way too high
  1722. else if (!doLevelup)
  1723. player.sendPacket(new SystemMessage(SystemMessageId.SOUL_CRYSTAL_ABSORBING_REFUSED));
  1724. crystalQTY = 0;
  1725. continue;
  1726. }
  1727. /* TODO: Confirm boss chance for crystal level up and for crystal breaking.
  1728. * It is known that bosses with FULL_PARTY crystal level ups have 100% success rate, but this is not
  1729. * the case for the other bosses (one-random or last-hit).
  1730. * While not confirmed, it is most reasonable that crystals leveled up at bosses will never break.
  1731. * Also, the chance to level up is guessed as around 70% if not higher.
  1732. */
  1733. int chanceLevelUp = isBossMob? 70:SoulCrystal.LEVEL_CHANCE;
  1734. // If succeeds or it is a full party absorb, level up the crystal.
  1735. if (((absorbType == L2NpcTemplate.AbsorbCrystalType.FULL_PARTY) && doLevelup) || (dice <= chanceLevelUp))
  1736. {
  1737. // Give staged crystal
  1738. exchangeCrystal(player, crystalOLD, crystalNEW, false);
  1739. }
  1740. // If true and not a last-hit mob, break the crystal.
  1741. else if ((!isBossMob) && dice >= (100.0 - SoulCrystal.BREAK_CHANCE))
  1742. {
  1743. // Remove current crystal an give a broken open.
  1744. if (crystalNME.startsWith("red"))
  1745. exchangeCrystal(player, crystalOLD, SoulCrystal.RED_BROKEN_CRYSTAL, true);
  1746. else if (crystalNME.startsWith("gre"))
  1747. exchangeCrystal(player, crystalOLD, SoulCrystal.GRN_BROKEN_CYRSTAL, true);
  1748. else if (crystalNME.startsWith("blu"))
  1749. exchangeCrystal(player, crystalOLD, SoulCrystal.BLU_BROKEN_CRYSTAL, true);
  1750. resetAbsorbList();
  1751. }
  1752. else
  1753. player.sendPacket(new SystemMessage(SystemMessageId.SOUL_CRYSTAL_ABSORBING_FAILED));
  1754. }
  1755. }
  1756. private void exchangeCrystal(L2PcInstance player, int takeid, int giveid, boolean broke)
  1757. {
  1758. L2ItemInstance Item = player.getInventory().destroyItemByItemId("SoulCrystal", takeid, 1, player, this);
  1759. if (Item != null)
  1760. {
  1761. // Prepare inventory update packet
  1762. InventoryUpdate playerIU = new InventoryUpdate();
  1763. playerIU.addRemovedItem(Item);
  1764. // Add new crystal to the killer's inventory
  1765. Item = player.getInventory().addItem("SoulCrystal", giveid, 1, player, this);
  1766. playerIU.addItem(Item);
  1767. // Send a sound event and text message to the player
  1768. if(broke)
  1769. {
  1770. player.sendPacket(new SystemMessage(SystemMessageId.SOUL_CRYSTAL_BROKE));
  1771. }
  1772. else
  1773. player.sendPacket(new SystemMessage(SystemMessageId.SOUL_CRYSTAL_ABSORBING_SUCCEEDED));
  1774. // Send system message
  1775. SystemMessage sms = new SystemMessage(SystemMessageId.EARNED_ITEM);
  1776. sms.addItemName(giveid);
  1777. player.sendPacket(sms);
  1778. // Send inventory update packet
  1779. player.sendPacket(playerIU);
  1780. }
  1781. }
  1782. private void resetAbsorbList()
  1783. {
  1784. _absorbed = false;
  1785. _absorbersList.clear();
  1786. }
  1787. /**
  1788. * Calculate the Experience and SP to distribute to attacker (L2PcInstance, L2SummonInstance or L2Party) of the L2Attackable.<BR><BR>
  1789. *
  1790. * @param diff The difference of level between attacker (L2PcInstance, L2SummonInstance or L2Party) and the L2Attackable
  1791. * @param damage The damages given by the attacker (L2PcInstance, L2SummonInstance or L2Party)
  1792. *
  1793. */
  1794. private int[] calculateExpAndSp(int diff, int damage)
  1795. {
  1796. double xp;
  1797. double sp;
  1798. if(diff < -5) diff = -5; // makes possible to use ALT_GAME_EXPONENT configuration
  1799. xp = (double)getExpReward() * damage / getMaxHp();
  1800. if (Config.ALT_GAME_EXPONENT_XP != 0) xp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_XP);
  1801. sp = (double)getSpReward() * damage / getMaxHp();
  1802. if (Config.ALT_GAME_EXPONENT_SP != 0) sp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_SP);
  1803. if (Config.ALT_GAME_EXPONENT_XP == 0 && Config.ALT_GAME_EXPONENT_SP == 0)
  1804. {
  1805. if(diff > 5) // formula revised May 07
  1806. {
  1807. double pow = Math.pow((double)5/6, diff-5);
  1808. xp = xp*pow;
  1809. sp = sp*pow;
  1810. }
  1811. if (xp <= 0)
  1812. {
  1813. xp = 0;
  1814. sp = 0;
  1815. }
  1816. else if (sp <= 0)
  1817. {
  1818. sp = 0;
  1819. }
  1820. }
  1821. int[] tmp = { (int)xp, (int)sp };
  1822. return tmp;
  1823. }
  1824. public long calculateOverhitExp(long normalExp)
  1825. {
  1826. // Get the percentage based on the total of extra (over-hit) damage done relative to the total (maximum) ammount of HP on the L2Attackable
  1827. double overhitPercentage = ((getOverhitDamage() * 100) / getMaxHp());
  1828. // Over-hit damage percentages are limited to 25% max
  1829. if (overhitPercentage > 25)
  1830. overhitPercentage = 25;
  1831. // Get the overhit exp bonus according to the above over-hit damage percentage
  1832. // (1/1 basis - 13% of over-hit damage, 13% of extra exp is given, and so on...)
  1833. double overhitExp = ((overhitPercentage / 100) * normalExp);
  1834. // Return the rounded ammount of exp points to be added to the player's normal exp reward
  1835. long bonusOverhit = Math.round(overhitExp);
  1836. return bonusOverhit;
  1837. }
  1838. /**
  1839. * Return True.<BR><BR>
  1840. */
  1841. @Override
  1842. public boolean isAttackable()
  1843. {
  1844. return true;
  1845. }
  1846. @Override
  1847. public void onSpawn()
  1848. {
  1849. super.onSpawn();
  1850. // Clear mob spoil,seed
  1851. setSpoil(false);
  1852. // Clear all aggro char from list
  1853. clearAggroList();
  1854. // Clear Harvester Rewrard List
  1855. _harvestItems = null;
  1856. // Clear mod Seeded stat
  1857. setSeeded(false);
  1858. _sweepItems = null;
  1859. resetAbsorbList();
  1860. setWalking();
  1861. // check the region where this mob is, do not activate the AI if region is inactive.
  1862. if (!isInActiveRegion())
  1863. if (this instanceof L2SiegeGuardInstance)
  1864. ((L2SiegeGuardAI) getAI()).stopAITask();
  1865. else
  1866. ((L2AttackableAI) getAI()).stopAITask();
  1867. }
  1868. /**
  1869. * Sets state of the mob to seeded. Paramets needed to be set before.
  1870. */
  1871. public void setSeeded()
  1872. {
  1873. if (_seedType != 0 && _seeder != null)
  1874. setSeeded(_seedType, _seeder.getLevel());
  1875. }
  1876. /**
  1877. * Sets the seed parametrs, but not the seed state
  1878. * @param id - id of the seed
  1879. * @param seeder - player who is sowind the seed
  1880. */
  1881. public void setSeeded(int id, L2PcInstance seeder)
  1882. {
  1883. if (!_seeded) {
  1884. _seedType = id;
  1885. _seeder = seeder;
  1886. }
  1887. }
  1888. public void setSeeded(int id, int seederLvl)
  1889. {
  1890. _seeded = true;
  1891. _seedType = id;
  1892. int count = 1;
  1893. Map<Integer, L2Skill> skills = getTemplate().getSkills();
  1894. if (skills != null)
  1895. {
  1896. for (int skillId : skills.keySet())
  1897. {
  1898. switch (skillId) {
  1899. case 4303: //Strong type x2
  1900. count *= 2;
  1901. break;
  1902. case 4304: //Strong type x3
  1903. count *= 3;
  1904. break;
  1905. case 4305: //Strong type x4
  1906. count *= 4;
  1907. break;
  1908. case 4306: //Strong type x5
  1909. count *= 5;
  1910. break;
  1911. case 4307: //Strong type x6
  1912. count *= 6;
  1913. break;
  1914. case 4308: //Strong type x7
  1915. count *= 7;
  1916. break;
  1917. case 4309: //Strong type x8
  1918. count *= 8;
  1919. break;
  1920. case 4310: //Strong type x9
  1921. count *= 9;
  1922. break;
  1923. }
  1924. }
  1925. }
  1926. int diff = (getLevel() - (L2Manor.getInstance().getSeedLevel(_seedType) - 5));
  1927. // hi-lvl mobs bonus
  1928. if (diff > 0)
  1929. {
  1930. count += diff;
  1931. }
  1932. FastList<RewardItem> harvested = new FastList<RewardItem>();
  1933. harvested.add(new RewardItem(L2Manor.getInstance().getCropType(_seedType), count* Config.RATE_DROP_MANOR));
  1934. _harvestItems = harvested.toArray(new RewardItem[harvested.size()]);
  1935. }
  1936. public void setSeeded(boolean seeded)
  1937. {
  1938. _seeded = seeded;
  1939. }
  1940. public L2PcInstance getSeeder()
  1941. {
  1942. return _seeder;
  1943. }
  1944. public int getSeedType()
  1945. {
  1946. return _seedType;
  1947. }
  1948. public boolean isSeeded()
  1949. {
  1950. return _seeded;
  1951. }
  1952. private int getAbsorbLevel()
  1953. {
  1954. return getTemplate().absorbLevel;
  1955. }
  1956. /**
  1957. * Check if the server allows Random Animation.<BR><BR>
  1958. */
  1959. // This is located here because L2Monster and L2FriendlyMob both extend this class. The other non-pc instances extend either L2NpcInstance or L2MonsterInstance.
  1960. @Override
  1961. public boolean hasRandomAnimation()
  1962. {
  1963. return ((Config.MAX_MONSTER_ANIMATION > 0) && !(this instanceof L2GrandBossInstance));
  1964. }
  1965. @Override
  1966. public boolean isMob()
  1967. {
  1968. return true; // This means we use MAX_MONSTER_ANIMATION instead of MAX_NPC_ANIMATION
  1969. }
  1970. protected void setCommandChannelTimer(CommandChannelTimer commandChannelTimer)
  1971. {
  1972. _commandChannelTimer = commandChannelTimer;
  1973. }
  1974. public CommandChannelTimer getCommandChannelTimer()
  1975. {
  1976. return _commandChannelTimer;
  1977. }
  1978. public L2CommandChannel getFirstCommandChannelAttacked()
  1979. {
  1980. return _firstCommandChannelAttacked;
  1981. }
  1982. public void setFirstCommandChannelAttacked(
  1983. L2CommandChannel firstCommandChannelAttacked)
  1984. {
  1985. _firstCommandChannelAttacked = firstCommandChannelAttacked;
  1986. }
  1987. private class CommandChannelTimer implements Runnable
  1988. {
  1989. private L2Attackable _monster;
  1990. private L2CommandChannel _channel;
  1991. public CommandChannelTimer(L2Attackable monster, L2CommandChannel channel)
  1992. {
  1993. _monster = monster;
  1994. _channel = channel;
  1995. }
  1996. /**
  1997. * @see java.lang.Runnable#run()
  1998. */
  1999. public void run()
  2000. {
  2001. _monster.setCommandChannelTimer(null);
  2002. _monster.setFirstCommandChannelAttacked(null);
  2003. for (L2Character player : _monster.getAggroListRP().keySet())
  2004. {
  2005. if (player.isInParty() && player.getParty().isInCommandChannel())
  2006. {
  2007. if (player.getParty().getCommandChannel().equals(_channel))
  2008. {
  2009. // if a player which is in first attacked CommandChannel, restart the timer ;)
  2010. _monster.setCommandChannelTimer(this);
  2011. _monster.setFirstCommandChannelAttacked(_channel);
  2012. ThreadPoolManager.getInstance().scheduleGeneral(this, 300000); // 5 min
  2013. break;
  2014. }
  2015. }
  2016. }
  2017. }
  2018. }
  2019. }