L2Attackable.java 92 KB

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