L2Attackable.java 47 KB

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