L2Attackable.java 47 KB

12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703
  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. * Adds damage and hate to the attacker aggression list for this character.
  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. // Get the AggroInfo of the attacker L2Character from the _aggroList of the L2Attackable
  605. final AggroInfo ai = getAggroList().computeIfAbsent(attacker, AggroInfo::new);
  606. ai.addDamage(damage);
  607. // Traps does not cause aggro
  608. // making this hack because not possible to determine if damage made by trap
  609. // so just check for triggered trap here
  610. final L2PcInstance targetPlayer = attacker.getActingPlayer();
  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. }