L2Attackable.java 47 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174117511761177117811791180118111821183118411851186118711881189119011911192119311941195119611971198119912001201120212031204120512061207120812091210121112121213121412151216121712181219122012211222122312241225122612271228122912301231123212331234123512361237123812391240124112421243124412451246124712481249125012511252125312541255125612571258125912601261126212631264126512661267126812691270127112721273127412751276127712781279128012811282128312841285128612871288128912901291129212931294129512961297129812991300130113021303130413051306130713081309131013111312131313141315131613171318131913201321132213231324132513261327132813291330133113321333133413351336133713381339134013411342134313441345134613471348134913501351135213531354135513561357135813591360136113621363136413651366136713681369137013711372137313741375137613771378137913801381138213831384138513861387138813891390139113921393139413951396139713981399140014011402140314041405140614071408140914101411141214131414141514161417141814191420142114221423142414251426142714281429143014311432143314341435143614371438143914401441144214431444144514461447144814491450145114521453145414551456145714581459146014611462146314641465146614671468146914701471147214731474147514761477147814791480148114821483148414851486148714881489149014911492149314941495149614971498149915001501150215031504150515061507150815091510151115121513151415151516151715181519152015211522152315241525152615271528152915301531153215331534153515361537153815391540154115421543154415451546154715481549155015511552155315541555155615571558155915601561156215631564156515661567156815691570157115721573157415751576157715781579158015811582158315841585158615871588158915901591159215931594159515961597159815991600160116021603160416051606160716081609161016111612161316141615161616171618161916201621162216231624162516261627162816291630163116321633163416351636163716381639164016411642164316441645164616471648164916501651165216531654165516561657165816591660166116621663166416651666166716681669167016711672167316741675167616771678167916801681168216831684168516861687168816891690169116921693169416951696169716981699170017011702170317041705170617071708170917101711171217131714171517161717171817191720
  1. /*
  2. * Copyright (C) 2004-2015 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver.model.actor;
  20. import java.util.ArrayList;
  21. import java.util.Collection;
  22. import java.util.LinkedList;
  23. import java.util.List;
  24. import java.util.Map;
  25. import java.util.concurrent.ConcurrentHashMap;
  26. import java.util.concurrent.atomic.AtomicReference;
  27. import java.util.logging.Level;
  28. import com.l2jserver.Config;
  29. import com.l2jserver.gameserver.ThreadPoolManager;
  30. import com.l2jserver.gameserver.ai.CtrlEvent;
  31. import com.l2jserver.gameserver.ai.CtrlIntention;
  32. import com.l2jserver.gameserver.ai.L2AttackableAI;
  33. import com.l2jserver.gameserver.ai.L2CharacterAI;
  34. import com.l2jserver.gameserver.ai.L2FortSiegeGuardAI;
  35. import com.l2jserver.gameserver.ai.L2SiegeGuardAI;
  36. import com.l2jserver.gameserver.datatables.EventDroplist;
  37. import com.l2jserver.gameserver.datatables.EventDroplist.DateDrop;
  38. import com.l2jserver.gameserver.datatables.ItemTable;
  39. import com.l2jserver.gameserver.enums.InstanceType;
  40. import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
  41. import com.l2jserver.gameserver.instancemanager.WalkingManager;
  42. import com.l2jserver.gameserver.model.AbsorberInfo;
  43. import com.l2jserver.gameserver.model.AggroInfo;
  44. import com.l2jserver.gameserver.model.DamageDoneInfo;
  45. import com.l2jserver.gameserver.model.L2CommandChannel;
  46. import com.l2jserver.gameserver.model.L2Object;
  47. import com.l2jserver.gameserver.model.L2Party;
  48. import com.l2jserver.gameserver.model.L2Seed;
  49. import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance;
  50. import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
  51. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  52. import com.l2jserver.gameserver.model.actor.instance.L2ServitorInstance;
  53. import com.l2jserver.gameserver.model.actor.knownlist.AttackableKnownList;
  54. import com.l2jserver.gameserver.model.actor.status.AttackableStatus;
  55. import com.l2jserver.gameserver.model.actor.tasks.attackable.CommandChannelTimer;
  56. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  57. import com.l2jserver.gameserver.model.drops.DropListScope;
  58. import com.l2jserver.gameserver.model.events.EventDispatcher;
  59. import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableAggroRangeEnter;
  60. import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableAttack;
  61. import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableKill;
  62. import com.l2jserver.gameserver.model.holders.ItemHolder;
  63. import com.l2jserver.gameserver.model.items.L2Item;
  64. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  65. import com.l2jserver.gameserver.model.skills.Skill;
  66. import com.l2jserver.gameserver.model.stats.Stats;
  67. import com.l2jserver.gameserver.network.SystemMessageId;
  68. import com.l2jserver.gameserver.network.clientpackets.Say2;
  69. import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
  70. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  71. import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
  72. import com.l2jserver.gameserver.util.Util;
  73. import com.l2jserver.util.Rnd;
  74. public class L2Attackable extends L2Npc
  75. {
  76. // Raid
  77. private boolean _isRaid = false;
  78. private boolean _isRaidMinion = false;
  79. //
  80. private boolean _champion = false;
  81. private final Map<L2Character, AggroInfo> _aggroList = new ConcurrentHashMap<>();
  82. private boolean _isReturningToSpawnPoint = false;
  83. private boolean _canReturnToSpawnPoint = true;
  84. private boolean _seeThroughSilentMove = false;
  85. // Manor
  86. private boolean _seeded = false;
  87. private L2Seed _seed = null;
  88. private int _seederObjId = 0;
  89. private final AtomicReference<ItemHolder> _harvestItem = new AtomicReference<>();
  90. // Spoil
  91. private int _spoilerObjectId;
  92. private final AtomicReference<Collection<ItemHolder>> _sweepItems = new AtomicReference<>();
  93. // Over-hit
  94. private boolean _overhit;
  95. private double _overhitDamage;
  96. private L2Character _overhitAttacker;
  97. // Command channel
  98. private volatile L2CommandChannel _firstCommandChannelAttacked = null;
  99. private CommandChannelTimer _commandChannelTimer = null;
  100. private long _commandChannelLastAttack = 0;
  101. // Soul crystal
  102. private boolean _absorbed;
  103. private final Map<Integer, AbsorberInfo> _absorbersList = new ConcurrentHashMap<>();
  104. // Misc
  105. private boolean _mustGiveExpSp;
  106. protected int _onKillDelay = 5000;
  107. /**
  108. * Constructor of L2Attackable (use L2Character and L2NpcInstance constructor).<br>
  109. * Actions:<br>
  110. * Call the L2Character constructor to set the _template of the L2Attackable (copy skills from template to object and link _calculators to NPC_STD_CALCULATOR)<br>
  111. * Set the name of the L2Attackable<br>
  112. * Create a RandomAnimation Task that will be launched after the calculated delay if the server allow it.
  113. * @param objectId identifier of the object initialized.
  114. * @param template the template to apply to the NPC.
  115. */
  116. public L2Attackable(int objectId, L2NpcTemplate template)
  117. {
  118. super(objectId, template);
  119. setInstanceType(InstanceType.L2Attackable);
  120. setIsInvul(false);
  121. _mustGiveExpSp = true;
  122. }
  123. @Override
  124. public AttackableKnownList getKnownList()
  125. {
  126. return (AttackableKnownList) super.getKnownList();
  127. }
  128. @Override
  129. public void initKnownList()
  130. {
  131. setKnownList(new AttackableKnownList(this));
  132. }
  133. @Override
  134. public AttackableStatus getStatus()
  135. {
  136. return (AttackableStatus) super.getStatus();
  137. }
  138. @Override
  139. public void initCharStatus()
  140. {
  141. setStatus(new AttackableStatus(this));
  142. }
  143. @Override
  144. protected L2CharacterAI initAI()
  145. {
  146. return new L2AttackableAI(new AIAccessor());
  147. }
  148. public final Map<L2Character, AggroInfo> getAggroList()
  149. {
  150. return _aggroList;
  151. }
  152. public final boolean isReturningToSpawnPoint()
  153. {
  154. return _isReturningToSpawnPoint;
  155. }
  156. public final void setisReturningToSpawnPoint(boolean value)
  157. {
  158. _isReturningToSpawnPoint = value;
  159. }
  160. public final boolean canReturnToSpawnPoint()
  161. {
  162. return _canReturnToSpawnPoint;
  163. }
  164. public final void setCanReturnToSpawnPoint(boolean value)
  165. {
  166. _canReturnToSpawnPoint = value;
  167. }
  168. public boolean canSeeThroughSilentMove()
  169. {
  170. return _seeThroughSilentMove;
  171. }
  172. public void setSeeThroughSilentMove(boolean val)
  173. {
  174. _seeThroughSilentMove = val;
  175. }
  176. /**
  177. * Use the skill if minimum checks are pass.
  178. * @param skill the skill
  179. */
  180. public void useMagic(Skill skill)
  181. {
  182. if ((skill == null) || isAlikeDead() || skill.isPassive() || isCastingNow() || isSkillDisabled(skill))
  183. {
  184. return;
  185. }
  186. if ((getCurrentMp() < (getStat().getMpConsume(skill) + getStat().getMpInitialConsume(skill))) || (getCurrentHp() <= skill.getHpConsume()))
  187. {
  188. return;
  189. }
  190. if (!skill.isStatic())
  191. {
  192. if (skill.isMagic())
  193. {
  194. if (isMuted())
  195. {
  196. return;
  197. }
  198. }
  199. else
  200. {
  201. if (isPhysicalMuted())
  202. {
  203. return;
  204. }
  205. }
  206. }
  207. final L2Object target = skill.getFirstOfTargetList(this);
  208. if (target != null)
  209. {
  210. getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target);
  211. }
  212. }
  213. /**
  214. * Reduce the current HP of the L2Attackable.
  215. * @param damage The HP decrease value
  216. * @param attacker The L2Character who attacks
  217. */
  218. @Override
  219. public void reduceCurrentHp(double damage, L2Character attacker, Skill skill)
  220. {
  221. reduceCurrentHp(damage, attacker, true, false, skill);
  222. }
  223. /**
  224. * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.
  225. * @param damage The HP decrease value
  226. * @param attacker The L2Character who attacks
  227. * @param awake The awake state (If True : stop sleeping)
  228. * @param isDOT
  229. * @param skill
  230. */
  231. @Override
  232. public void reduceCurrentHp(double damage, L2Character attacker, boolean awake, boolean isDOT, Skill skill)
  233. {
  234. if (isRaid() && !isMinion() && (attacker != null) && (attacker.getParty() != null) && attacker.getParty().isInCommandChannel() && attacker.getParty().getCommandChannel().meetRaidWarCondition(this))
  235. {
  236. if (_firstCommandChannelAttacked == null) // looting right isn't set
  237. {
  238. synchronized (this)
  239. {
  240. if (_firstCommandChannelAttacked == null)
  241. {
  242. _firstCommandChannelAttacked = attacker.getParty().getCommandChannel();
  243. if (_firstCommandChannelAttacked != null)
  244. {
  245. _commandChannelTimer = new CommandChannelTimer(this);
  246. _commandChannelLastAttack = System.currentTimeMillis();
  247. ThreadPoolManager.getInstance().scheduleGeneral(_commandChannelTimer, 10000); // check for last attack
  248. _firstCommandChannelAttacked.broadcastPacket(new CreatureSay(0, Say2.PARTYROOM_ALL, "", "You have looting rights!")); // TODO: retail msg
  249. }
  250. }
  251. }
  252. }
  253. else if (attacker.getParty().getCommandChannel().equals(_firstCommandChannelAttacked)) // is in same channel
  254. {
  255. _commandChannelLastAttack = System.currentTimeMillis(); // update last attack time
  256. }
  257. }
  258. if (isEventMob())
  259. {
  260. return;
  261. }
  262. // Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList
  263. if (attacker != null)
  264. {
  265. addDamage(attacker, (int) damage, skill);
  266. }
  267. // If this L2Attackable is a L2MonsterInstance and it has spawned minions, call its minions to battle
  268. if (this instanceof L2MonsterInstance)
  269. {
  270. L2MonsterInstance master = (L2MonsterInstance) this;
  271. if (master.hasMinions())
  272. {
  273. master.getMinionList().onAssist(this, attacker);
  274. }
  275. master = master.getLeader();
  276. if ((master != null) && master.hasMinions())
  277. {
  278. master.getMinionList().onAssist(this, attacker);
  279. }
  280. }
  281. // Reduce the current HP of the L2Attackable and launch the doDie Task if necessary
  282. super.reduceCurrentHp(damage, attacker, awake, isDOT, skill);
  283. }
  284. public synchronized void setMustRewardExpSp(boolean value)
  285. {
  286. _mustGiveExpSp = value;
  287. }
  288. public synchronized boolean getMustRewardExpSP()
  289. {
  290. return _mustGiveExpSp;
  291. }
  292. /**
  293. * Kill the L2Attackable (the corpse disappeared after 7 seconds), distribute rewards (EXP, SP, Drops...) and notify Quest Engine.<br>
  294. * Actions:<br>
  295. * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members<br>
  296. * Notify the Quest Engine of the L2Attackable death if necessary.<br>
  297. * Kill the L2NpcInstance (the corpse disappeared after 7 seconds)<br>
  298. * Caution: This method DOESN'T GIVE rewards to L2PetInstance.
  299. * @param killer The L2Character that has killed the L2Attackable
  300. */
  301. @Override
  302. public boolean doDie(L2Character killer)
  303. {
  304. // Kill the L2NpcInstance (the corpse disappeared after 7 seconds)
  305. if (!super.doDie(killer))
  306. {
  307. return false;
  308. }
  309. if ((killer != null) && killer.isPlayable())
  310. {
  311. // Delayed notification
  312. EventDispatcher.getInstance().notifyEventAsyncDelayed(new OnAttackableKill(killer.getActingPlayer(), this, killer.isSummon()), this, _onKillDelay);
  313. }
  314. // Notify to minions if there are.
  315. if (isMonster())
  316. {
  317. final L2MonsterInstance mob = (L2MonsterInstance) this;
  318. if ((mob.getLeader() != null) && mob.getLeader().hasMinions())
  319. {
  320. final int respawnTime = Config.MINIONS_RESPAWN_TIME.containsKey(getId()) ? Config.MINIONS_RESPAWN_TIME.get(getId()) * 1000 : -1;
  321. mob.getLeader().getMinionList().onMinionDie(mob, respawnTime);
  322. }
  323. if (mob.hasMinions())
  324. {
  325. mob.getMinionList().onMasterDie(false);
  326. }
  327. }
  328. return true;
  329. }
  330. /**
  331. * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members.<br>
  332. * Actions:<br>
  333. * Get the L2PcInstance owner of the L2ServitorInstance (if necessary) and L2Party in progress.<br>
  334. * Calculate the Experience and SP rewards in function of the level difference.<br>
  335. * Add Exp and SP rewards to L2PcInstance (including Summon penalty) and to Party members in the known area of the last attacker.<br>
  336. * Caution : This method DOESN'T GIVE rewards to L2PetInstance.
  337. * @param lastAttacker The L2Character that has killed the L2Attackable
  338. */
  339. @Override
  340. protected void calculateRewards(L2Character lastAttacker)
  341. {
  342. try
  343. {
  344. if (getAggroList().isEmpty())
  345. {
  346. return;
  347. }
  348. // NOTE: Concurrent-safe map is used because while iterating to verify all conditions sometimes an entry must be removed.
  349. final Map<L2PcInstance, DamageDoneInfo> rewards = new ConcurrentHashMap<>();
  350. L2PcInstance maxDealer = null;
  351. int maxDamage = 0;
  352. long totalDamage = 0;
  353. // While Iterating over This Map Removing Object is Not Allowed
  354. // Go through the _aggroList of the L2Attackable
  355. for (AggroInfo info : getAggroList().values())
  356. {
  357. if (info == null)
  358. {
  359. continue;
  360. }
  361. // Get the L2Character corresponding to this attacker
  362. final L2PcInstance attacker = info.getAttacker().getActingPlayer();
  363. if (attacker != null)
  364. {
  365. // Get damages done by this attacker
  366. final int damage = info.getDamage();
  367. // Prevent unwanted behavior
  368. if (damage > 1)
  369. {
  370. // Check if damage dealer isn't too far from this (killed monster)
  371. if (!Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, attacker, true))
  372. {
  373. continue;
  374. }
  375. totalDamage += damage;
  376. // Calculate real damages (Summoners should get own damage plus summon's damage)
  377. DamageDoneInfo reward = rewards.get(attacker);
  378. if (reward == null)
  379. {
  380. reward = new DamageDoneInfo(attacker, damage);
  381. rewards.put(attacker, reward);
  382. }
  383. else
  384. {
  385. reward.addDamage(damage);
  386. }
  387. if (reward.getDamage() > maxDamage)
  388. {
  389. maxDealer = attacker;
  390. maxDamage = reward.getDamage();
  391. }
  392. }
  393. }
  394. }
  395. // Manage Base, Quests and Sweep drops of the L2Attackable
  396. doItemDrop((maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker);
  397. // Manage drop of Special Events created by GM for a defined period
  398. doEventDrop(lastAttacker);
  399. if (!getMustRewardExpSP())
  400. {
  401. return;
  402. }
  403. if (!rewards.isEmpty())
  404. {
  405. for (DamageDoneInfo reward : rewards.values())
  406. {
  407. if (reward == null)
  408. {
  409. continue;
  410. }
  411. // Attacker to be rewarded
  412. final L2PcInstance attacker = reward.getAttacker();
  413. // Total amount of damage done
  414. final int damage = reward.getDamage();
  415. // Get party
  416. final L2Party attackerParty = attacker.getParty();
  417. // Penalty applied to the attacker's XP
  418. // If this attacker have servitor, get Exp Penalty applied for the servitor.
  419. final float penalty = attacker.hasServitor() ? ((L2ServitorInstance) attacker.getSummon()).getExpMultiplier() : 1;
  420. // If there's NO party in progress
  421. if (attackerParty == null)
  422. {
  423. // Calculate Exp and SP rewards
  424. if (attacker.getKnownList().knowsObject(this))
  425. {
  426. // Calculate the difference of level between this attacker (player or servitor owner) and the L2Attackable
  427. // mob = 24, atk = 10, diff = -14 (full xp)
  428. // mob = 24, atk = 28, diff = 4 (some xp)
  429. // mob = 24, atk = 50, diff = 26 (no xp)
  430. final int levelDiff = attacker.getLevel() - getLevel();
  431. final int[] expSp = calculateExpAndSp(levelDiff, damage, totalDamage);
  432. long exp = expSp[0];
  433. int sp = expSp[1];
  434. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  435. {
  436. exp *= Config.L2JMOD_CHAMPION_REWARDS;
  437. sp *= Config.L2JMOD_CHAMPION_REWARDS;
  438. }
  439. exp *= penalty;
  440. // Check for an over-hit enabled strike
  441. L2Character overhitAttacker = getOverhitAttacker();
  442. if (isOverhit() && (overhitAttacker != null) && (overhitAttacker.getActingPlayer() != null) && (attacker == overhitAttacker.getActingPlayer()))
  443. {
  444. attacker.sendPacket(SystemMessageId.OVER_HIT);
  445. exp += calculateOverhitExp(exp);
  446. }
  447. // Distribute the Exp and SP between the L2PcInstance and its L2Summon
  448. if (!attacker.isDead())
  449. {
  450. final long addexp = Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null));
  451. final int addsp = (int) attacker.calcStat(Stats.EXPSP_RATE, sp, null, null);
  452. attacker.addExpAndSp(addexp, addsp, useVitalityRate());
  453. if (addexp > 0)
  454. {
  455. attacker.updateVitalityPoints(getVitalityPoints(damage), true, false);
  456. }
  457. }
  458. }
  459. }
  460. else
  461. {
  462. // share with party members
  463. int partyDmg = 0;
  464. float partyMul = 1;
  465. int partyLvl = 0;
  466. // Get all L2Character that can be rewarded in the party
  467. final List<L2PcInstance> rewardedMembers = new ArrayList<>();
  468. // Go through all L2PcInstance in the party
  469. final List<L2PcInstance> groupMembers = attackerParty.isInCommandChannel() ? attackerParty.getCommandChannel().getMembers() : attackerParty.getMembers();
  470. for (L2PcInstance partyPlayer : groupMembers)
  471. {
  472. if ((partyPlayer == null) || partyPlayer.isDead())
  473. {
  474. continue;
  475. }
  476. // Get the RewardInfo of this L2PcInstance from L2Attackable rewards
  477. final DamageDoneInfo reward2 = rewards.get(partyPlayer);
  478. // If the L2PcInstance is in the L2Attackable rewards add its damages to party damages
  479. if (reward2 != null)
  480. {
  481. if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, partyPlayer, true))
  482. {
  483. partyDmg += reward2.getDamage(); // Add L2PcInstance damages to party damages
  484. rewardedMembers.add(partyPlayer);
  485. if (partyPlayer.getLevel() > partyLvl)
  486. {
  487. if (attackerParty.isInCommandChannel())
  488. {
  489. partyLvl = attackerParty.getCommandChannel().getLevel();
  490. }
  491. else
  492. {
  493. partyLvl = partyPlayer.getLevel();
  494. }
  495. }
  496. }
  497. rewards.remove(partyPlayer); // Remove the L2PcInstance from the L2Attackable rewards
  498. }
  499. else
  500. {
  501. // Add L2PcInstance of the party (that have attacked or not) to members that can be rewarded
  502. // and in range of the monster.
  503. if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, partyPlayer, true))
  504. {
  505. rewardedMembers.add(partyPlayer);
  506. if (partyPlayer.getLevel() > partyLvl)
  507. {
  508. if (attackerParty.isInCommandChannel())
  509. {
  510. partyLvl = attackerParty.getCommandChannel().getLevel();
  511. }
  512. else
  513. {
  514. partyLvl = partyPlayer.getLevel();
  515. }
  516. }
  517. }
  518. }
  519. }
  520. // If the party didn't killed this L2Attackable alone
  521. if (partyDmg < totalDamage)
  522. {
  523. partyMul = ((float) partyDmg / totalDamage);
  524. }
  525. // Calculate the level difference between Party and L2Attackable
  526. final int levelDiff = partyLvl - getLevel();
  527. // Calculate Exp and SP rewards
  528. final int[] expSp = calculateExpAndSp(levelDiff, partyDmg, totalDamage);
  529. long exp = expSp[0];
  530. int sp = expSp[1];
  531. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
  532. {
  533. exp *= Config.L2JMOD_CHAMPION_REWARDS;
  534. sp *= Config.L2JMOD_CHAMPION_REWARDS;
  535. }
  536. exp *= partyMul;
  537. sp *= partyMul;
  538. // Check for an over-hit enabled strike
  539. // (When in party, the over-hit exp bonus is given to the whole party and splitted proportionally through the party members)
  540. L2Character overhitAttacker = getOverhitAttacker();
  541. if (isOverhit() && (overhitAttacker != null) && (overhitAttacker.getActingPlayer() != null) && (attacker == overhitAttacker.getActingPlayer()))
  542. {
  543. attacker.sendPacket(SystemMessageId.OVER_HIT);
  544. exp += calculateOverhitExp(exp);
  545. }
  546. // Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker
  547. if (partyDmg > 0)
  548. {
  549. attackerParty.distributeXpAndSp(exp, sp, rewardedMembers, partyLvl, partyDmg, this);
  550. }
  551. }
  552. }
  553. }
  554. }
  555. catch (Exception e)
  556. {
  557. _log.log(Level.SEVERE, "", e);
  558. }
  559. }
  560. @Override
  561. public void addAttackerToAttackByList(L2Character player)
  562. {
  563. if ((player == null) || (player == this) || getAttackByList().contains(player))
  564. {
  565. return;
  566. }
  567. getAttackByList().add(player);
  568. }
  569. /**
  570. * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.
  571. * @param attacker The L2Character that gave damages to this L2Attackable
  572. * @param damage The number of damages given by the attacker L2Character
  573. * @param skill
  574. */
  575. public void addDamage(L2Character attacker, int damage, Skill skill)
  576. {
  577. if (attacker == null)
  578. {
  579. return;
  580. }
  581. // Notify the L2Attackable AI with EVT_ATTACKED
  582. if (!isDead())
  583. {
  584. try
  585. {
  586. // If monster is on walk - stop it
  587. if (isWalker() && !isCoreAIDisabled() && WalkingManager.getInstance().isOnWalk(this))
  588. {
  589. WalkingManager.getInstance().stopMoving(this, false, true);
  590. }
  591. getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, attacker);
  592. addDamageHate(attacker, damage, (damage * 100) / (getLevel() + 7));
  593. final L2PcInstance player = attacker.getActingPlayer();
  594. if (player != null)
  595. {
  596. EventDispatcher.getInstance().notifyEventAsync(new OnAttackableAttack(player, this, damage, skill, attacker.isSummon()), this);
  597. }
  598. }
  599. catch (Exception e)
  600. {
  601. _log.log(Level.SEVERE, "", e);
  602. }
  603. }
  604. }
  605. /**
  606. * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.
  607. * @param attacker The L2Character that gave damages to this L2Attackable
  608. * @param damage The number of damages given by the attacker L2Character
  609. * @param aggro The hate (=damage) given by the attacker L2Character
  610. */
  611. public void addDamageHate(L2Character attacker, int damage, int aggro)
  612. {
  613. if (attacker == null)
  614. {
  615. return;
  616. }
  617. final L2PcInstance targetPlayer = attacker.getActingPlayer();
  618. // Get the AggroInfo of the attacker L2Character from the _aggroList of the L2Attackable
  619. AggroInfo ai = getAggroList().get(attacker);
  620. if (ai == null)
  621. {
  622. ai = new AggroInfo(attacker);
  623. getAggroList().put(attacker, ai);
  624. }
  625. ai.addDamage(damage);
  626. // traps does not cause aggro
  627. // making this hack because not possible to determine if damage made by trap
  628. // so just check for triggered trap here
  629. if ((targetPlayer == null) || (targetPlayer.getTrap() == null) || !targetPlayer.getTrap().isTriggered())
  630. {
  631. ai.addHate(aggro);
  632. }
  633. if ((targetPlayer != null) && (aggro == 0))
  634. {
  635. addDamageHate(attacker, 0, 1);
  636. // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
  637. if (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
  638. {
  639. getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  640. }
  641. // Notify to scripts
  642. EventDispatcher.getInstance().notifyEventAsync(new OnAttackableAggroRangeEnter(this, targetPlayer, attacker.isSummon()), this);
  643. }
  644. else if ((targetPlayer == null) && (aggro == 0))
  645. {
  646. aggro = 1;
  647. ai.addHate(1);
  648. }
  649. // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
  650. if ((aggro != 0) && (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
  651. {
  652. getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  653. }
  654. }
  655. public void reduceHate(L2Character target, int amount)
  656. {
  657. if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
  658. {
  659. // TODO: this just prevents error until siege guards are handled properly
  660. stopHating(target);
  661. setTarget(null);
  662. getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
  663. return;
  664. }
  665. if (target == null) // whole aggrolist
  666. {
  667. L2Character mostHated = getMostHated();
  668. if (mostHated == null) // makes target passive for a moment more
  669. {
  670. ((L2AttackableAI) getAI()).setGlobalAggro(-25);
  671. return;
  672. }
  673. for (AggroInfo ai : getAggroList().values())
  674. {
  675. if (ai == null)
  676. {
  677. return;
  678. }
  679. ai.addHate(amount);
  680. }
  681. amount = getHating(mostHated);
  682. if (amount >= 0)
  683. {
  684. ((L2AttackableAI) getAI()).setGlobalAggro(-25);
  685. clearAggroList();
  686. getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  687. setWalking();
  688. }
  689. return;
  690. }
  691. AggroInfo ai = getAggroList().get(target);
  692. if (ai == null)
  693. {
  694. _log.info("target " + target + " not present in aggro list of " + this);
  695. return;
  696. }
  697. ai.addHate(amount);
  698. if ((ai.getHate() >= 0) && (getMostHated() == null))
  699. {
  700. ((L2AttackableAI) getAI()).setGlobalAggro(-25);
  701. clearAggroList();
  702. getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  703. setWalking();
  704. }
  705. }
  706. /**
  707. * Clears _aggroList hate of the L2Character without removing from the list.
  708. * @param target
  709. */
  710. public void stopHating(L2Character target)
  711. {
  712. if (target == null)
  713. {
  714. return;
  715. }
  716. AggroInfo ai = getAggroList().get(target);
  717. if (ai != null)
  718. {
  719. ai.stopHate();
  720. }
  721. }
  722. /**
  723. * @return the most hated L2Character of the L2Attackable _aggroList.
  724. */
  725. public L2Character getMostHated()
  726. {
  727. if (getAggroList().isEmpty() || isAlikeDead())
  728. {
  729. return null;
  730. }
  731. L2Character mostHated = null;
  732. int maxHate = 0;
  733. // While Interacting over This Map Removing Object is Not Allowed
  734. // Go through the aggroList of the L2Attackable
  735. for (AggroInfo ai : getAggroList().values())
  736. {
  737. if (ai == null)
  738. {
  739. continue;
  740. }
  741. if (ai.checkHate(this) > maxHate)
  742. {
  743. mostHated = ai.getAttacker();
  744. maxHate = ai.getHate();
  745. }
  746. }
  747. return mostHated;
  748. }
  749. /**
  750. * @return the 2 most hated L2Character of the L2Attackable _aggroList.
  751. */
  752. public List<L2Character> get2MostHated()
  753. {
  754. if (getAggroList().isEmpty() || isAlikeDead())
  755. {
  756. return null;
  757. }
  758. L2Character mostHated = null;
  759. L2Character secondMostHated = null;
  760. int maxHate = 0;
  761. List<L2Character> result = new ArrayList<>();
  762. // While iterating over this map removing objects is not allowed
  763. // Go through the aggroList of the L2Attackable
  764. for (AggroInfo ai : getAggroList().values())
  765. {
  766. if (ai == null)
  767. {
  768. continue;
  769. }
  770. if (ai.checkHate(this) > maxHate)
  771. {
  772. secondMostHated = mostHated;
  773. mostHated = ai.getAttacker();
  774. maxHate = ai.getHate();
  775. }
  776. }
  777. result.add(mostHated);
  778. if (getAttackByList().contains(secondMostHated))
  779. {
  780. result.add(secondMostHated);
  781. }
  782. else
  783. {
  784. result.add(null);
  785. }
  786. return result;
  787. }
  788. public List<L2Character> getHateList()
  789. {
  790. if (getAggroList().isEmpty() || isAlikeDead())
  791. {
  792. return null;
  793. }
  794. List<L2Character> result = new ArrayList<>();
  795. for (AggroInfo ai : getAggroList().values())
  796. {
  797. if (ai == null)
  798. {
  799. continue;
  800. }
  801. ai.checkHate(this);
  802. result.add(ai.getAttacker());
  803. }
  804. return result;
  805. }
  806. /**
  807. * @param target The L2Character whose hate level must be returned
  808. * @return the hate level of the L2Attackable against this L2Character contained in _aggroList.
  809. */
  810. public int getHating(final L2Character target)
  811. {
  812. if (getAggroList().isEmpty() || (target == null))
  813. {
  814. return 0;
  815. }
  816. final AggroInfo ai = getAggroList().get(target);
  817. if (ai == null)
  818. {
  819. return 0;
  820. }
  821. if (ai.getAttacker() instanceof L2PcInstance)
  822. {
  823. L2PcInstance act = (L2PcInstance) ai.getAttacker();
  824. if (act.isInvisible() || ai.getAttacker().isInvul() || act.isSpawnProtected())
  825. {
  826. // Remove Object Should Use This Method and Can be Blocked While Interacting
  827. getAggroList().remove(target);
  828. return 0;
  829. }
  830. }
  831. if (!ai.getAttacker().isVisible() || ai.getAttacker().isInvisible())
  832. {
  833. getAggroList().remove(target);
  834. return 0;
  835. }
  836. if (ai.getAttacker().isAlikeDead())
  837. {
  838. ai.stopHate();
  839. return 0;
  840. }
  841. return ai.getHate();
  842. }
  843. public void doItemDrop(L2Character mainDamageDealer)
  844. {
  845. doItemDrop(getTemplate(), mainDamageDealer);
  846. }
  847. /**
  848. * Manage Base, Quests and Special Events drops of L2Attackable (called by calculateRewards).<br>
  849. * Concept:<br>
  850. * During a Special Event all L2Attackable can drop extra Items.<br>
  851. * Those extra Items are defined in the table allNpcDateDrops of the EventDroplist.<br>
  852. * Each Special Event has a start and end date to stop to drop extra Items automatically.<br>
  853. * Actions:<br>
  854. * Manage drop of Special Events created by GM for a defined period.<br>
  855. * Get all possible drops of this L2Attackable from L2NpcTemplate and add it Quest drops.<br>
  856. * For each possible drops (base + quests), calculate which one must be dropped (random).<br>
  857. * Get each Item quantity dropped (random).<br>
  858. * Create this or these L2ItemInstance corresponding to each Item Identifier dropped.<br>
  859. * If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, Give the item(s) to the L2PcInstance that has killed the L2Attackable.<br>
  860. * 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.
  861. * @param npcTemplate
  862. * @param mainDamageDealer
  863. */
  864. public void doItemDrop(L2NpcTemplate npcTemplate, L2Character mainDamageDealer)
  865. {
  866. if (mainDamageDealer == null)
  867. {
  868. return;
  869. }
  870. L2PcInstance player = mainDamageDealer.getActingPlayer();
  871. // Don't drop anything if the last attacker or owner isn't L2PcInstance
  872. if (player == null)
  873. {
  874. return;
  875. }
  876. CursedWeaponsManager.getInstance().checkDrop(this, player);
  877. if (isSpoiled())
  878. {
  879. _sweepItems.set(npcTemplate.calculateDrops(DropListScope.CORPSE, this, player));
  880. }
  881. Collection<ItemHolder> deathItems = npcTemplate.calculateDrops(DropListScope.DEATH, this, player);
  882. if (deathItems != null)
  883. {
  884. for (ItemHolder drop : deathItems)
  885. {
  886. L2Item item = ItemTable.getInstance().getTemplate(drop.getId());
  887. // Check if the autoLoot mode is active
  888. if (isFlying() || (!item.hasExImmediateEffect() && ((!isRaid() && Config.AUTO_LOOT) || (isRaid() && Config.AUTO_LOOT_RAIDS))) || (item.hasExImmediateEffect() && Config.AUTO_LOOT_HERBS))
  889. {
  890. player.doAutoLoot(this, drop); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
  891. }
  892. else
  893. {
  894. dropItem(player, drop); // drop the item on the ground
  895. }
  896. // Broadcast message if RaidBoss was defeated
  897. if (isRaid() && !isRaidMinion())
  898. {
  899. final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_DIED_DROPPED_S3_S2);
  900. sm.addCharName(this);
  901. sm.addItemName(item);
  902. sm.addLong(drop.getCount());
  903. broadcastPacket(sm);
  904. }
  905. }
  906. }
  907. // Apply Special Item drop with random(rnd) quantity(qty) for champions.
  908. if (Config.L2JMOD_CHAMPION_ENABLE && isChampion() && ((Config.L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE > 0) || (Config.L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE > 0)))
  909. {
  910. int champqty = Rnd.get(Config.L2JMOD_CHAMPION_REWARD_QTY);
  911. ItemHolder item = new ItemHolder(Config.L2JMOD_CHAMPION_REWARD_ID, ++champqty);
  912. if ((player.getLevel() <= getLevel()) && (Rnd.get(100) < Config.L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE))
  913. {
  914. if (Config.AUTO_LOOT || isFlying())
  915. {
  916. player.addItem("ChampionLoot", item.getId(), item.getCount(), this, true); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
  917. }
  918. else
  919. {
  920. dropItem(player, item);
  921. }
  922. }
  923. else if ((player.getLevel() > getLevel()) && (Rnd.get(100) < Config.L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE))
  924. {
  925. if (Config.AUTO_LOOT || isFlying())
  926. {
  927. player.addItem("ChampionLoot", item.getId(), item.getCount(), this, true); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
  928. }
  929. else
  930. {
  931. dropItem(player, item);
  932. }
  933. }
  934. }
  935. }
  936. /**
  937. * Manage Special Events drops created by GM for a defined period.<br>
  938. * Concept:<br>
  939. * During a Special Event all L2Attackable can drop extra Items.<br>
  940. * Those extra Items are defined in the table allNpcDateDrops of the EventDroplist.<br>
  941. * Each Special Event has a start and end date to stop to drop extra Items automatically.<br>
  942. * Actions: <I>If an extra drop must be generated</I><br>
  943. * Get an Item Identifier (random) from the DateDrop Item table of this Event.<br>
  944. * Get the Item quantity dropped (random).<br>
  945. * Create this or these L2ItemInstance corresponding to this Item Identifier.<br>
  946. * If the autoLoot mode is actif and if the L2Character that has killed the L2Attackable is a L2PcInstance, Give the item(s) to the L2PcInstance that has killed the L2Attackable<br>
  947. * 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
  948. * @param lastAttacker The L2Character that has killed the L2Attackable
  949. */
  950. public void doEventDrop(L2Character lastAttacker)
  951. {
  952. if (lastAttacker == null)
  953. {
  954. return;
  955. }
  956. L2PcInstance player = lastAttacker.getActingPlayer();
  957. // Don't drop anything if the last attacker or owner isn't L2PcInstance
  958. if (player == null)
  959. {
  960. return;
  961. }
  962. if ((player.getLevel() - getLevel()) > 9)
  963. {
  964. return;
  965. }
  966. // Go through DateDrop of EventDroplist allNpcDateDrops within the date range
  967. for (DateDrop drop : EventDroplist.getInstance().getAllDrops())
  968. {
  969. if (Rnd.get(1000000) < drop.getEventDrop().getDropChance())
  970. {
  971. final int itemId = drop.getEventDrop().getItemIdList()[Rnd.get(drop.getEventDrop().getItemIdList().length)];
  972. final long itemCount = Rnd.get(drop.getEventDrop().getMinCount(), drop.getEventDrop().getMaxCount());
  973. if (Config.AUTO_LOOT || isFlying())
  974. {
  975. player.doAutoLoot(this, itemId, itemCount); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
  976. }
  977. else
  978. {
  979. dropItem(player, itemId, itemCount); // drop the item on the ground
  980. }
  981. }
  982. }
  983. }
  984. /**
  985. * @return the active weapon of this L2Attackable (= null).
  986. */
  987. public L2ItemInstance getActiveWeapon()
  988. {
  989. return null;
  990. }
  991. /**
  992. * @param player The L2Character searched in the _aggroList of the L2Attackable
  993. * @return True if the _aggroList of this L2Attackable contains the L2Character.
  994. */
  995. public boolean containsTarget(L2Character player)
  996. {
  997. return getAggroList().containsKey(player);
  998. }
  999. /**
  1000. * Clear the _aggroList of the L2Attackable.
  1001. */
  1002. public void clearAggroList()
  1003. {
  1004. getAggroList().clear();
  1005. // clear overhit values
  1006. _overhit = false;
  1007. _overhitDamage = 0;
  1008. _overhitAttacker = null;
  1009. }
  1010. /**
  1011. * @return {@code true} if there is a loot to sweep, {@code false} otherwise.
  1012. */
  1013. @Override
  1014. public boolean isSweepActive()
  1015. {
  1016. return _sweepItems.get() != null;
  1017. }
  1018. /**
  1019. * @return a copy of dummy items for the spoil loot.
  1020. */
  1021. public List<L2Item> getSpoilLootItems()
  1022. {
  1023. final Collection<ItemHolder> sweepItems = _sweepItems.get();
  1024. final List<L2Item> lootItems = new LinkedList<>();
  1025. if (sweepItems != null)
  1026. {
  1027. for (ItemHolder item : sweepItems)
  1028. {
  1029. lootItems.add(ItemTable.getInstance().getTemplate(item.getId()));
  1030. }
  1031. }
  1032. return lootItems;
  1033. }
  1034. /**
  1035. * @return table containing all L2ItemInstance that can be spoiled.
  1036. */
  1037. public Collection<ItemHolder> takeSweep()
  1038. {
  1039. return _sweepItems.getAndSet(null);
  1040. }
  1041. /**
  1042. * @return table containing all L2ItemInstance that can be harvested.
  1043. */
  1044. public ItemHolder takeHarvest()
  1045. {
  1046. return _harvestItem.getAndSet(null);
  1047. }
  1048. /**
  1049. * Checks if the corpse is too old.
  1050. * @param attacker the player to validate
  1051. * @param remainingTime the time to check
  1052. * @param sendMessage if {@code true} will send a message of corpse too old
  1053. * @return {@code true} if the corpse is too old
  1054. */
  1055. public boolean isOldCorpse(L2PcInstance attacker, int remainingTime, boolean sendMessage)
  1056. {
  1057. if (isDead() && (DecayTaskManager.getInstance().getRemainingTime(this) < remainingTime))
  1058. {
  1059. if (sendMessage && (attacker != null))
  1060. {
  1061. attacker.sendPacket(SystemMessageId.CORPSE_TOO_OLD_SKILL_NOT_USED);
  1062. }
  1063. return true;
  1064. }
  1065. return false;
  1066. }
  1067. /**
  1068. * @param sweeper the player to validate.
  1069. * @param sendMessage sendMessage if {@code true} will send a message of sweep not allowed.
  1070. * @return {@code true} if is the spoiler or is in the spoiler party.
  1071. */
  1072. public boolean checkSpoilOwner(L2PcInstance sweeper, boolean sendMessage)
  1073. {
  1074. if ((sweeper.getObjectId() != getSpoilerObjectId()) && !sweeper.isInLooterParty(getSpoilerObjectId()))
  1075. {
  1076. if (sendMessage)
  1077. {
  1078. sweeper.sendPacket(SystemMessageId.SWEEP_NOT_ALLOWED);
  1079. }
  1080. return false;
  1081. }
  1082. return true;
  1083. }
  1084. /**
  1085. * Set the over-hit flag on the L2Attackable.
  1086. * @param status The status of the over-hit flag
  1087. */
  1088. public void overhitEnabled(boolean status)
  1089. {
  1090. _overhit = status;
  1091. }
  1092. /**
  1093. * Set the over-hit values like the attacker who did the strike and the amount of damage done by the skill.
  1094. * @param attacker The L2Character who hit on the L2Attackable using the over-hit enabled skill
  1095. * @param damage The amount of damage done by the over-hit enabled skill on the L2Attackable
  1096. */
  1097. public void setOverhitValues(L2Character attacker, double damage)
  1098. {
  1099. // Calculate the over-hit damage
  1100. // Ex: mob had 10 HP left, over-hit skill did 50 damage total, over-hit damage is 40
  1101. double overhitDmg = -(getCurrentHp() - damage);
  1102. if (overhitDmg < 0)
  1103. {
  1104. // we didn't killed the mob with the over-hit strike. (it wasn't really an over-hit strike)
  1105. // let's just clear all the over-hit related values
  1106. overhitEnabled(false);
  1107. _overhitDamage = 0;
  1108. _overhitAttacker = null;
  1109. return;
  1110. }
  1111. overhitEnabled(true);
  1112. _overhitDamage = overhitDmg;
  1113. _overhitAttacker = attacker;
  1114. }
  1115. /**
  1116. * Return the L2Character who hit on the L2Attackable using an over-hit enabled skill.
  1117. * @return L2Character attacker
  1118. */
  1119. public L2Character getOverhitAttacker()
  1120. {
  1121. return _overhitAttacker;
  1122. }
  1123. /**
  1124. * Return the amount of damage done on the L2Attackable using an over-hit enabled skill.
  1125. * @return double damage
  1126. */
  1127. public double getOverhitDamage()
  1128. {
  1129. return _overhitDamage;
  1130. }
  1131. /**
  1132. * @return True if the L2Attackable was hit by an over-hit enabled skill.
  1133. */
  1134. public boolean isOverhit()
  1135. {
  1136. return _overhit;
  1137. }
  1138. /**
  1139. * Activate the absorbed soul condition on the L2Attackable.
  1140. */
  1141. public void absorbSoul()
  1142. {
  1143. _absorbed = true;
  1144. }
  1145. /**
  1146. * @return True if the L2Attackable had his soul absorbed.
  1147. */
  1148. public boolean isAbsorbed()
  1149. {
  1150. return _absorbed;
  1151. }
  1152. /**
  1153. * Adds an attacker that successfully absorbed the soul of this L2Attackable into the _absorbersList.
  1154. * @param attacker
  1155. */
  1156. public void addAbsorber(L2PcInstance attacker)
  1157. {
  1158. // If we have no _absorbersList initiated, do it
  1159. final AbsorberInfo ai = _absorbersList.get(attacker.getObjectId());
  1160. // If the L2Character attacker isn't already in the _absorbersList of this L2Attackable, add it
  1161. if (ai == null)
  1162. {
  1163. _absorbersList.put(attacker.getObjectId(), new AbsorberInfo(attacker.getObjectId(), getCurrentHp()));
  1164. }
  1165. else
  1166. {
  1167. ai.setAbsorbedHp(getCurrentHp());
  1168. }
  1169. // Set this L2Attackable as absorbed
  1170. absorbSoul();
  1171. }
  1172. public void resetAbsorbList()
  1173. {
  1174. _absorbed = false;
  1175. _absorbersList.clear();
  1176. }
  1177. public Map<Integer, AbsorberInfo> getAbsorbersList()
  1178. {
  1179. return _absorbersList;
  1180. }
  1181. /**
  1182. * Calculate the Experience and SP to distribute to attacker (L2PcInstance, L2ServitorInstance or L2Party) of the L2Attackable.
  1183. * @param diff The difference of level between attacker (L2PcInstance, L2ServitorInstance or L2Party) and the L2Attackable
  1184. * @param damage The damages given by the attacker (L2PcInstance, L2ServitorInstance or L2Party)
  1185. * @param totalDamage The total damage done
  1186. * @return
  1187. */
  1188. private int[] calculateExpAndSp(int diff, int damage, long totalDamage)
  1189. {
  1190. double xp;
  1191. double sp;
  1192. if (diff < -5)
  1193. {
  1194. diff = -5; // makes possible to use ALT_GAME_EXPONENT configuration
  1195. }
  1196. xp = ((double) getExpReward() * damage) / totalDamage;
  1197. if (Config.ALT_GAME_EXPONENT_XP != 0)
  1198. {
  1199. xp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_XP);
  1200. }
  1201. sp = ((double) getSpReward() * damage) / totalDamage;
  1202. if (Config.ALT_GAME_EXPONENT_SP != 0)
  1203. {
  1204. sp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_SP);
  1205. }
  1206. if ((Config.ALT_GAME_EXPONENT_XP == 0) && (Config.ALT_GAME_EXPONENT_SP == 0))
  1207. {
  1208. if (diff > 5) // formula revised May 07
  1209. {
  1210. double pow = Math.pow((double) 5 / 6, diff - 5);
  1211. xp = xp * pow;
  1212. sp = sp * pow;
  1213. }
  1214. if (xp <= 0)
  1215. {
  1216. xp = 0;
  1217. sp = 0;
  1218. }
  1219. else if (sp <= 0)
  1220. {
  1221. sp = 0;
  1222. }
  1223. }
  1224. int[] tmp =
  1225. {
  1226. (int) xp,
  1227. (int) sp
  1228. };
  1229. return tmp;
  1230. }
  1231. public long calculateOverhitExp(long normalExp)
  1232. {
  1233. // Get the percentage based on the total of extra (over-hit) damage done relative to the total (maximum) ammount of HP on the L2Attackable
  1234. double overhitPercentage = ((getOverhitDamage() * 100) / getMaxHp());
  1235. // Over-hit damage percentages are limited to 25% max
  1236. if (overhitPercentage > 25)
  1237. {
  1238. overhitPercentage = 25;
  1239. }
  1240. // Get the overhit exp bonus according to the above over-hit damage percentage
  1241. // (1/1 basis - 13% of over-hit damage, 13% of extra exp is given, and so on...)
  1242. double overhitExp = ((overhitPercentage / 100) * normalExp);
  1243. // Return the rounded ammount of exp points to be added to the player's normal exp reward
  1244. long bonusOverhit = Math.round(overhitExp);
  1245. return bonusOverhit;
  1246. }
  1247. /**
  1248. * Return True.
  1249. */
  1250. @Override
  1251. public boolean canBeAttacked()
  1252. {
  1253. return true;
  1254. }
  1255. @Override
  1256. public void onSpawn()
  1257. {
  1258. super.onSpawn();
  1259. // Clear mob spoil, seed
  1260. setSpoilerObjectId(0);
  1261. // Clear all aggro char from list
  1262. clearAggroList();
  1263. // Clear Harvester reward
  1264. _harvestItem.set(null);
  1265. // Clear mod Seeded stat
  1266. _seeded = false;
  1267. _seed = null;
  1268. _seederObjId = 0;
  1269. // Clear overhit value
  1270. overhitEnabled(false);
  1271. _sweepItems.set(null);
  1272. resetAbsorbList();
  1273. setWalking();
  1274. // check the region where this mob is, do not activate the AI if region is inactive.
  1275. if (!isInActiveRegion())
  1276. {
  1277. if (hasAI())
  1278. {
  1279. getAI().stopAITask();
  1280. }
  1281. }
  1282. }
  1283. /**
  1284. * Checks if its spoiled.
  1285. * @return {@code true} if its spoiled, {@code false} otherwise
  1286. */
  1287. public final boolean isSpoiled()
  1288. {
  1289. return _spoilerObjectId != 0;
  1290. }
  1291. /**
  1292. * Gets the spoiler object id.
  1293. * @return the spoiler object id if its spoiled, 0 otherwise
  1294. */
  1295. public final int getSpoilerObjectId()
  1296. {
  1297. return _spoilerObjectId;
  1298. }
  1299. /**
  1300. * Sets the spoiler object id.
  1301. * @param spoilerObjectId the spoiler object id
  1302. */
  1303. public final void setSpoilerObjectId(int spoilerObjectId)
  1304. {
  1305. _spoilerObjectId = spoilerObjectId;
  1306. }
  1307. /**
  1308. * Sets state of the mob to seeded. Parameters needed to be set before.
  1309. * @param seeder
  1310. */
  1311. public final void setSeeded(L2PcInstance seeder)
  1312. {
  1313. if ((_seed != null) && (_seederObjId == seeder.getObjectId()))
  1314. {
  1315. _seeded = true;
  1316. int count = 1;
  1317. for (int skillId : getTemplate().getSkills().keySet())
  1318. {
  1319. switch (skillId)
  1320. {
  1321. case 4303: // Strong type x2
  1322. count *= 2;
  1323. break;
  1324. case 4304: // Strong type x3
  1325. count *= 3;
  1326. break;
  1327. case 4305: // Strong type x4
  1328. count *= 4;
  1329. break;
  1330. case 4306: // Strong type x5
  1331. count *= 5;
  1332. break;
  1333. case 4307: // Strong type x6
  1334. count *= 6;
  1335. break;
  1336. case 4308: // Strong type x7
  1337. count *= 7;
  1338. break;
  1339. case 4309: // Strong type x8
  1340. count *= 8;
  1341. break;
  1342. case 4310: // Strong type x9
  1343. count *= 9;
  1344. break;
  1345. }
  1346. }
  1347. // hi-lvl mobs bonus
  1348. final int diff = getLevel() - _seed.getLevel() - 5;
  1349. if (diff > 0)
  1350. {
  1351. count += diff;
  1352. }
  1353. _harvestItem.set(new ItemHolder(_seed.getCropId(), count * Config.RATE_DROP_MANOR));
  1354. }
  1355. }
  1356. /**
  1357. * Sets the seed parameters, but not the seed state
  1358. * @param seed - instance {@link L2Seed} of used seed
  1359. * @param seeder - player who sows the seed
  1360. */
  1361. public final void setSeeded(L2Seed seed, L2PcInstance seeder)
  1362. {
  1363. if (!_seeded)
  1364. {
  1365. _seed = seed;
  1366. _seederObjId = seeder.getObjectId();
  1367. }
  1368. }
  1369. public final int getSeederId()
  1370. {
  1371. return _seederObjId;
  1372. }
  1373. public final L2Seed getSeed()
  1374. {
  1375. return _seed;
  1376. }
  1377. public final boolean isSeeded()
  1378. {
  1379. return _seeded;
  1380. }
  1381. /**
  1382. * Set delay for onKill() call, in ms Default: 5000 ms
  1383. * @param delay
  1384. */
  1385. public final void setOnKillDelay(int delay)
  1386. {
  1387. _onKillDelay = delay;
  1388. }
  1389. public final int getOnKillDelay()
  1390. {
  1391. return _onKillDelay;
  1392. }
  1393. /**
  1394. * Check if the server allows Random Animation.
  1395. */
  1396. // This is located here because L2Monster and L2FriendlyMob both extend this class. The other non-pc instances extend either L2NpcInstance or L2MonsterInstance.
  1397. @Override
  1398. public boolean hasRandomAnimation()
  1399. {
  1400. return ((Config.MAX_MONSTER_ANIMATION > 0) && isRandomAnimationEnabled() && !(this instanceof L2GrandBossInstance));
  1401. }
  1402. @Override
  1403. public boolean isMob()
  1404. {
  1405. return true; // This means we use MAX_MONSTER_ANIMATION instead of MAX_NPC_ANIMATION
  1406. }
  1407. public void setCommandChannelTimer(CommandChannelTimer commandChannelTimer)
  1408. {
  1409. _commandChannelTimer = commandChannelTimer;
  1410. }
  1411. public CommandChannelTimer getCommandChannelTimer()
  1412. {
  1413. return _commandChannelTimer;
  1414. }
  1415. public L2CommandChannel getFirstCommandChannelAttacked()
  1416. {
  1417. return _firstCommandChannelAttacked;
  1418. }
  1419. public void setFirstCommandChannelAttacked(L2CommandChannel firstCommandChannelAttacked)
  1420. {
  1421. _firstCommandChannelAttacked = firstCommandChannelAttacked;
  1422. }
  1423. /**
  1424. * @return the _commandChannelLastAttack
  1425. */
  1426. public long getCommandChannelLastAttack()
  1427. {
  1428. return _commandChannelLastAttack;
  1429. }
  1430. /**
  1431. * @param channelLastAttack the _commandChannelLastAttack to set
  1432. */
  1433. public void setCommandChannelLastAttack(long channelLastAttack)
  1434. {
  1435. _commandChannelLastAttack = channelLastAttack;
  1436. }
  1437. public void returnHome()
  1438. {
  1439. clearAggroList();
  1440. if (hasAI() && (getSpawn() != null))
  1441. {
  1442. getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation(this));
  1443. }
  1444. }
  1445. /*
  1446. * Return vitality points decrease (if positive) or increase (if negative) based on damage. Maximum for damage = maxHp.
  1447. */
  1448. public float getVitalityPoints(int damage)
  1449. {
  1450. // sanity check
  1451. if (damage <= 0)
  1452. {
  1453. return 0;
  1454. }
  1455. final float divider = (getLevel() > 0) && (getExpReward() > 0) ? (getTemplate().getBaseHpMax() * 9 * getLevel() * getLevel()) / (100 * getExpReward()) : 0;
  1456. if (divider == 0)
  1457. {
  1458. return 0;
  1459. }
  1460. // negative value - vitality will be consumed
  1461. return -Math.min(damage, getMaxHp()) / divider;
  1462. }
  1463. /*
  1464. * True if vitality rate for exp and sp should be applied
  1465. */
  1466. public boolean useVitalityRate()
  1467. {
  1468. if (isChampion() && !Config.L2JMOD_CHAMPION_ENABLE_VITALITY)
  1469. {
  1470. return false;
  1471. }
  1472. return true;
  1473. }
  1474. /** Return True if the L2Character is RaidBoss or his minion. */
  1475. @Override
  1476. public boolean isRaid()
  1477. {
  1478. return _isRaid;
  1479. }
  1480. /**
  1481. * Set this Npc as a Raid instance.
  1482. * @param isRaid
  1483. */
  1484. public void setIsRaid(boolean isRaid)
  1485. {
  1486. _isRaid = isRaid;
  1487. }
  1488. /**
  1489. * Set this Npc as a Minion instance.
  1490. * @param val
  1491. */
  1492. public void setIsRaidMinion(boolean val)
  1493. {
  1494. _isRaid = val;
  1495. _isRaidMinion = val;
  1496. }
  1497. @Override
  1498. public boolean isRaidMinion()
  1499. {
  1500. return _isRaidMinion;
  1501. }
  1502. @Override
  1503. public boolean isMinion()
  1504. {
  1505. return getLeader() != null;
  1506. }
  1507. /**
  1508. * @return leader of this minion or null.
  1509. */
  1510. public L2Attackable getLeader()
  1511. {
  1512. return null;
  1513. }
  1514. public void setChampion(boolean champ)
  1515. {
  1516. _champion = champ;
  1517. }
  1518. @Override
  1519. public boolean isChampion()
  1520. {
  1521. return _champion;
  1522. }
  1523. @Override
  1524. public boolean isAttackable()
  1525. {
  1526. return true;
  1527. }
  1528. }