2
0

L2Attackable.java 79 KB

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