12345678910111213141516171819202122232425262728293031323334353637383940414243444546474849505152535455565758596061626364656667686970717273747576777879808182838485868788899091929394959697989910010110210310410510610710810911011111211311411511611711811912012112212312412512612712812913013113213313413513613713813914014114214314414514614714814915015115215315415515615715815916016116216316416516616716816917017117217317417517617717817918018118218318418518618718818919019119219319419519619719819920020120220320420520620720820921021121221321421521621721821922022122222322422522622722822923023123223323423523623723823924024124224324424524624724824925025125225325425525625725825926026126226326426526626726826927027127227327427527627727827928028128228328428528628728828929029129229329429529629729829930030130230330430530630730830931031131231331431531631731831932032132232332432532632732832933033133233333433533633733833934034134234334434534634734834935035135235335435535635735835936036136236336436536636736836937037137237337437537637737837938038138238338438538638738838939039139239339439539639739839940040140240340440540640740840941041141241341441541641741841942042142242342442542642742842943043143243343443543643743843944044144244344444544644744844945045145245345445545645745845946046146246346446546646746846947047147247347447547647747847948048148248348448548648748848949049149249349449549649749849950050150250350450550650750850951051151251351451551651751851952052152252352452552652752852953053153253353453553653753853954054154254354454554654754854955055155255355455555655755855956056156256356456556656756856957057157257357457557657757857958058158258358458558658758858959059159259359459559659759859960060160260360460560660760860961061161261361461561661761861962062162262362462562662762862963063163263363463563663763863964064164264364464564664764864965065165265365465565665765865966066166266366466566666766866967067167267367467567667767867968068168268368468568668768868969069169269369469569669769869970070170270370470570670770870971071171271371471571671771871972072172272372472572672772872973073173273373473573673773873974074174274374474574674774874975075175275375475575675775875976076176276376476576676776876977077177277377477577677777877978078178278378478578678778878979079179279379479579679779879980080180280380480580680780880981081181281381481581681781881982082182282382482582682782882983083183283383483583683783883984084184284384484584684784884985085185285385485585685785885986086186286386486586686786886987087187287387487587687787887988088188288388488588688788888989089189289389489589689789889990090190290390490590690790890991091191291391491591691791891992092192292392492592692792892993093193293393493593693793893994094194294394494594694794894995095195295395495595695795895996096196296396496596696796896997097197297397497597697797897998098198298398498598698798898999099199299399499599699799899910001001100210031004100510061007100810091010101110121013101410151016101710181019102010211022102310241025102610271028102910301031103210331034103510361037103810391040104110421043104410451046104710481049105010511052105310541055105610571058105910601061106210631064106510661067106810691070107110721073107410751076107710781079108010811082108310841085108610871088108910901091109210931094109510961097109810991100110111021103110411051106110711081109111011111112111311141115111611171118111911201121112211231124112511261127112811291130113111321133113411351136113711381139114011411142114311441145114611471148114911501151115211531154115511561157115811591160116111621163116411651166116711681169117011711172117311741175117611771178117911801181118211831184118511861187118811891190119111921193119411951196119711981199120012011202120312041205120612071208120912101211121212131214121512161217121812191220122112221223122412251226122712281229123012311232123312341235123612371238123912401241124212431244124512461247124812491250125112521253125412551256125712581259126012611262126312641265126612671268126912701271127212731274127512761277127812791280128112821283128412851286128712881289129012911292129312941295129612971298129913001301130213031304130513061307130813091310131113121313131413151316131713181319132013211322132313241325132613271328132913301331133213331334133513361337133813391340134113421343134413451346134713481349135013511352135313541355135613571358135913601361136213631364136513661367136813691370137113721373137413751376137713781379138013811382138313841385138613871388138913901391139213931394139513961397139813991400140114021403140414051406140714081409141014111412141314141415141614171418141914201421142214231424142514261427142814291430143114321433143414351436143714381439144014411442144314441445144614471448144914501451145214531454145514561457145814591460146114621463146414651466146714681469147014711472147314741475147614771478147914801481148214831484148514861487148814891490149114921493149414951496149714981499150015011502150315041505150615071508150915101511151215131514151515161517151815191520152115221523152415251526152715281529153015311532153315341535153615371538153915401541154215431544154515461547154815491550155115521553155415551556155715581559156015611562156315641565156615671568156915701571157215731574157515761577157815791580158115821583158415851586158715881589159015911592159315941595159615971598159916001601160216031604160516061607160816091610161116121613161416151616161716181619162016211622162316241625162616271628162916301631163216331634163516361637163816391640164116421643164416451646164716481649165016511652165316541655165616571658165916601661166216631664166516661667166816691670167116721673167416751676167716781679168016811682168316841685168616871688168916901691169216931694169516961697169816991700170117021703 |
- /*
- * Copyright (C) 2004-2015 L2J Server
- *
- * This file is part of L2J Server.
- *
- * L2J Server is free software: you can redistribute it and/or modify
- * it under the terms of the GNU General Public License as published by
- * the Free Software Foundation, either version 3 of the License, or
- * (at your option) any later version.
- *
- * L2J Server is distributed in the hope that it will be useful,
- * but WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
- * General Public License for more details.
- *
- * You should have received a copy of the GNU General Public License
- * along with this program. If not, see <http://www.gnu.org/licenses/>.
- */
- package com.l2jserver.gameserver.model.actor;
- import java.util.ArrayList;
- import java.util.Collection;
- import java.util.LinkedList;
- import java.util.List;
- import java.util.Map;
- import java.util.concurrent.ConcurrentHashMap;
- import java.util.concurrent.atomic.AtomicReference;
- import java.util.logging.Level;
- import com.l2jserver.Config;
- import com.l2jserver.gameserver.ThreadPoolManager;
- import com.l2jserver.gameserver.ai.CtrlEvent;
- import com.l2jserver.gameserver.ai.CtrlIntention;
- import com.l2jserver.gameserver.ai.L2AttackableAI;
- import com.l2jserver.gameserver.ai.L2CharacterAI;
- import com.l2jserver.gameserver.ai.L2FortSiegeGuardAI;
- import com.l2jserver.gameserver.ai.L2SiegeGuardAI;
- import com.l2jserver.gameserver.datatables.EventDroplist;
- import com.l2jserver.gameserver.datatables.EventDroplist.DateDrop;
- import com.l2jserver.gameserver.datatables.ItemTable;
- import com.l2jserver.gameserver.enums.InstanceType;
- import com.l2jserver.gameserver.instancemanager.CursedWeaponsManager;
- import com.l2jserver.gameserver.instancemanager.WalkingManager;
- import com.l2jserver.gameserver.model.AbsorberInfo;
- import com.l2jserver.gameserver.model.AggroInfo;
- import com.l2jserver.gameserver.model.DamageDoneInfo;
- import com.l2jserver.gameserver.model.L2CommandChannel;
- import com.l2jserver.gameserver.model.L2Object;
- import com.l2jserver.gameserver.model.L2Party;
- import com.l2jserver.gameserver.model.L2Seed;
- import com.l2jserver.gameserver.model.actor.instance.L2GrandBossInstance;
- import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
- import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
- import com.l2jserver.gameserver.model.actor.instance.L2ServitorInstance;
- import com.l2jserver.gameserver.model.actor.knownlist.AttackableKnownList;
- import com.l2jserver.gameserver.model.actor.status.AttackableStatus;
- import com.l2jserver.gameserver.model.actor.tasks.attackable.CommandChannelTimer;
- import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
- import com.l2jserver.gameserver.model.drops.DropListScope;
- import com.l2jserver.gameserver.model.events.EventDispatcher;
- import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableAggroRangeEnter;
- import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableAttack;
- import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableKill;
- import com.l2jserver.gameserver.model.holders.ItemHolder;
- import com.l2jserver.gameserver.model.items.L2Item;
- import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
- import com.l2jserver.gameserver.model.skills.Skill;
- import com.l2jserver.gameserver.model.stats.Stats;
- import com.l2jserver.gameserver.network.SystemMessageId;
- import com.l2jserver.gameserver.network.clientpackets.Say2;
- import com.l2jserver.gameserver.network.serverpackets.CreatureSay;
- import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
- import com.l2jserver.gameserver.taskmanager.DecayTaskManager;
- import com.l2jserver.gameserver.util.Util;
- import com.l2jserver.util.Rnd;
- public class L2Attackable extends L2Npc
- {
- // Raid
- private boolean _isRaid = false;
- private boolean _isRaidMinion = false;
- //
- private boolean _champion = false;
- private final Map<L2Character, AggroInfo> _aggroList = new ConcurrentHashMap<>();
- private boolean _isReturningToSpawnPoint = false;
- private boolean _canReturnToSpawnPoint = true;
- private boolean _seeThroughSilentMove = false;
- // Manor
- private boolean _seeded = false;
- private L2Seed _seed = null;
- private int _seederObjId = 0;
- private final AtomicReference<ItemHolder> _harvestItem = new AtomicReference<>();
- // Spoil
- private int _spoilerObjectId;
- private final AtomicReference<Collection<ItemHolder>> _sweepItems = new AtomicReference<>();
- // Over-hit
- private boolean _overhit;
- private double _overhitDamage;
- private L2Character _overhitAttacker;
- // Command channel
- private volatile L2CommandChannel _firstCommandChannelAttacked = null;
- private CommandChannelTimer _commandChannelTimer = null;
- private long _commandChannelLastAttack = 0;
- // Soul crystal
- private boolean _absorbed;
- private final Map<Integer, AbsorberInfo> _absorbersList = new ConcurrentHashMap<>();
- // Misc
- private boolean _mustGiveExpSp;
- protected int _onKillDelay = 5000;
-
- /**
- * Creates an attackable NPC.
- * @param template the attackable NPC template
- */
- public L2Attackable(L2NpcTemplate template)
- {
- super(template);
- setInstanceType(InstanceType.L2Attackable);
- setIsInvul(false);
- _mustGiveExpSp = true;
- }
-
- @Override
- public AttackableKnownList getKnownList()
- {
- return (AttackableKnownList) super.getKnownList();
- }
-
- @Override
- public void initKnownList()
- {
- setKnownList(new AttackableKnownList(this));
- }
-
- @Override
- public AttackableStatus getStatus()
- {
- return (AttackableStatus) super.getStatus();
- }
-
- @Override
- public void initCharStatus()
- {
- setStatus(new AttackableStatus(this));
- }
-
- @Override
- protected L2CharacterAI initAI()
- {
- return new L2AttackableAI(this);
- }
-
- public final Map<L2Character, AggroInfo> getAggroList()
- {
- return _aggroList;
- }
-
- public final boolean isReturningToSpawnPoint()
- {
- return _isReturningToSpawnPoint;
- }
-
- public final void setisReturningToSpawnPoint(boolean value)
- {
- _isReturningToSpawnPoint = value;
- }
-
- public final boolean canReturnToSpawnPoint()
- {
- return _canReturnToSpawnPoint;
- }
-
- public final void setCanReturnToSpawnPoint(boolean value)
- {
- _canReturnToSpawnPoint = value;
- }
-
- public boolean canSeeThroughSilentMove()
- {
- return _seeThroughSilentMove;
- }
-
- public void setSeeThroughSilentMove(boolean val)
- {
- _seeThroughSilentMove = val;
- }
-
- /**
- * Use the skill if minimum checks are pass.
- * @param skill the skill
- */
- public void useMagic(Skill skill)
- {
- if ((skill == null) || isAlikeDead() || skill.isPassive() || isCastingNow() || isSkillDisabled(skill))
- {
- return;
- }
-
- if ((getCurrentMp() < (getStat().getMpConsume(skill) + getStat().getMpInitialConsume(skill))) || (getCurrentHp() <= skill.getHpConsume()))
- {
- return;
- }
-
- if (!skill.isStatic())
- {
- if (skill.isMagic())
- {
- if (isMuted())
- {
- return;
- }
- }
- else
- {
- if (isPhysicalMuted())
- {
- return;
- }
- }
- }
-
- final L2Object target = skill.getFirstOfTargetList(this);
- if (target != null)
- {
- getAI().setIntention(CtrlIntention.AI_INTENTION_CAST, skill, target);
- }
- }
-
- /**
- * Reduce the current HP of the L2Attackable.
- * @param damage The HP decrease value
- * @param attacker The L2Character who attacks
- */
- @Override
- public void reduceCurrentHp(double damage, L2Character attacker, Skill skill)
- {
- reduceCurrentHp(damage, attacker, true, false, skill);
- }
-
- /**
- * Reduce the current HP of the L2Attackable, update its _aggroList and launch the doDie Task if necessary.
- * @param damage The HP decrease value
- * @param attacker The L2Character who attacks
- * @param awake The awake state (If True : stop sleeping)
- * @param isDOT
- * @param skill
- */
- @Override
- public void reduceCurrentHp(double damage, L2Character attacker, boolean awake, boolean isDOT, Skill skill)
- {
- if (isRaid() && !isMinion() && (attacker != null) && (attacker.getParty() != null) && attacker.getParty().isInCommandChannel() && attacker.getParty().getCommandChannel().meetRaidWarCondition(this))
- {
- if (_firstCommandChannelAttacked == null) // looting right isn't set
- {
- synchronized (this)
- {
- if (_firstCommandChannelAttacked == null)
- {
- _firstCommandChannelAttacked = attacker.getParty().getCommandChannel();
- if (_firstCommandChannelAttacked != null)
- {
- _commandChannelTimer = new CommandChannelTimer(this);
- _commandChannelLastAttack = System.currentTimeMillis();
- ThreadPoolManager.getInstance().scheduleGeneral(_commandChannelTimer, 10000); // check for last attack
- _firstCommandChannelAttacked.broadcastPacket(new CreatureSay(0, Say2.PARTYROOM_ALL, "", "You have looting rights!")); // TODO: retail msg
- }
- }
- }
- }
- else if (attacker.getParty().getCommandChannel().equals(_firstCommandChannelAttacked)) // is in same channel
- {
- _commandChannelLastAttack = System.currentTimeMillis(); // update last attack time
- }
- }
-
- if (isEventMob())
- {
- return;
- }
-
- // Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList
- if (attacker != null)
- {
- addDamage(attacker, (int) damage, skill);
- }
-
- // If this L2Attackable is a L2MonsterInstance and it has spawned minions, call its minions to battle
- if (this instanceof L2MonsterInstance)
- {
- L2MonsterInstance master = (L2MonsterInstance) this;
-
- if (master.hasMinions())
- {
- master.getMinionList().onAssist(this, attacker);
- }
-
- master = master.getLeader();
- if ((master != null) && master.hasMinions())
- {
- master.getMinionList().onAssist(this, attacker);
- }
- }
- // Reduce the current HP of the L2Attackable and launch the doDie Task if necessary
- super.reduceCurrentHp(damage, attacker, awake, isDOT, skill);
- }
-
- public synchronized void setMustRewardExpSp(boolean value)
- {
- _mustGiveExpSp = value;
- }
-
- public synchronized boolean getMustRewardExpSP()
- {
- return _mustGiveExpSp;
- }
-
- /**
- * Kill the L2Attackable (the corpse disappeared after 7 seconds), distribute rewards (EXP, SP, Drops...) and notify Quest Engine.<br>
- * Actions:<br>
- * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members<br>
- * Notify the Quest Engine of the L2Attackable death if necessary.<br>
- * Kill the L2NpcInstance (the corpse disappeared after 7 seconds)<br>
- * Caution: This method DOESN'T GIVE rewards to L2PetInstance.
- * @param killer The L2Character that has killed the L2Attackable
- */
- @Override
- public boolean doDie(L2Character killer)
- {
- // Kill the L2NpcInstance (the corpse disappeared after 7 seconds)
- if (!super.doDie(killer))
- {
- return false;
- }
-
- if ((killer != null) && killer.isPlayable())
- {
- // Delayed notification
- EventDispatcher.getInstance().notifyEventAsyncDelayed(new OnAttackableKill(killer.getActingPlayer(), this, killer.isSummon()), this, _onKillDelay);
- }
-
- // Notify to minions if there are.
- if (isMonster())
- {
- final L2MonsterInstance mob = (L2MonsterInstance) this;
- if ((mob.getLeader() != null) && mob.getLeader().hasMinions())
- {
- final int respawnTime = Config.MINIONS_RESPAWN_TIME.containsKey(getId()) ? Config.MINIONS_RESPAWN_TIME.get(getId()) * 1000 : -1;
- mob.getLeader().getMinionList().onMinionDie(mob, respawnTime);
- }
-
- if (mob.hasMinions())
- {
- mob.getMinionList().onMasterDie(false);
- }
- }
- return true;
- }
-
- /**
- * Distribute Exp and SP rewards to L2PcInstance (including Summon owner) that hit the L2Attackable and to their Party members.<br>
- * Actions:<br>
- * Get the L2PcInstance owner of the L2ServitorInstance (if necessary) and L2Party in progress.<br>
- * Calculate the Experience and SP rewards in function of the level difference.<br>
- * Add Exp and SP rewards to L2PcInstance (including Summon penalty) and to Party members in the known area of the last attacker.<br>
- * Caution : This method DOESN'T GIVE rewards to L2PetInstance.
- * @param lastAttacker The L2Character that has killed the L2Attackable
- */
- @Override
- protected void calculateRewards(L2Character lastAttacker)
- {
- try
- {
- if (getAggroList().isEmpty())
- {
- return;
- }
-
- // NOTE: Concurrent-safe map is used because while iterating to verify all conditions sometimes an entry must be removed.
- final Map<L2PcInstance, DamageDoneInfo> rewards = new ConcurrentHashMap<>();
-
- L2PcInstance maxDealer = null;
- int maxDamage = 0;
- long totalDamage = 0;
- // While Iterating over This Map Removing Object is Not Allowed
- // Go through the _aggroList of the L2Attackable
- for (AggroInfo info : getAggroList().values())
- {
- if (info == null)
- {
- continue;
- }
-
- // Get the L2Character corresponding to this attacker
- final L2PcInstance attacker = info.getAttacker().getActingPlayer();
- if (attacker != null)
- {
- // Get damages done by this attacker
- final int damage = info.getDamage();
-
- // Prevent unwanted behavior
- if (damage > 1)
- {
- // Check if damage dealer isn't too far from this (killed monster)
- if (!Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, attacker, true))
- {
- continue;
- }
-
- totalDamage += damage;
-
- // Calculate real damages (Summoners should get own damage plus summon's damage)
- final DamageDoneInfo reward = rewards.computeIfAbsent(attacker, DamageDoneInfo::new);
- reward.addDamage(damage);
-
- if (reward.getDamage() > maxDamage)
- {
- maxDealer = attacker;
- maxDamage = reward.getDamage();
- }
- }
- }
- }
-
- // Manage Base, Quests and Sweep drops of the L2Attackable
- doItemDrop((maxDealer != null) && maxDealer.isOnline() ? maxDealer : lastAttacker);
-
- // Manage drop of Special Events created by GM for a defined period
- doEventDrop(lastAttacker);
-
- if (!getMustRewardExpSP())
- {
- return;
- }
-
- if (!rewards.isEmpty())
- {
- for (DamageDoneInfo reward : rewards.values())
- {
- if (reward == null)
- {
- continue;
- }
-
- // Attacker to be rewarded
- final L2PcInstance attacker = reward.getAttacker();
-
- // Total amount of damage done
- final int damage = reward.getDamage();
-
- // Get party
- final L2Party attackerParty = attacker.getParty();
-
- // Penalty applied to the attacker's XP
- // If this attacker have servitor, get Exp Penalty applied for the servitor.
- final float penalty = attacker.hasServitor() ? ((L2ServitorInstance) attacker.getSummon()).getExpMultiplier() : 1;
-
- // If there's NO party in progress
- if (attackerParty == null)
- {
- // Calculate Exp and SP rewards
- if (attacker.getKnownList().knowsObject(this))
- {
- // Calculate the difference of level between this attacker (player or servitor owner) and the L2Attackable
- // mob = 24, atk = 10, diff = -14 (full xp)
- // mob = 24, atk = 28, diff = 4 (some xp)
- // mob = 24, atk = 50, diff = 26 (no xp)
- final int levelDiff = attacker.getLevel() - getLevel();
-
- final int[] expSp = calculateExpAndSp(levelDiff, damage, totalDamage);
- long exp = expSp[0];
- int sp = expSp[1];
-
- if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
- {
- exp *= Config.L2JMOD_CHAMPION_REWARDS;
- sp *= Config.L2JMOD_CHAMPION_REWARDS;
- }
-
- exp *= penalty;
-
- // Check for an over-hit enabled strike
- L2Character overhitAttacker = getOverhitAttacker();
- if (isOverhit() && (overhitAttacker != null) && (overhitAttacker.getActingPlayer() != null) && (attacker == overhitAttacker.getActingPlayer()))
- {
- attacker.sendPacket(SystemMessageId.OVER_HIT);
- exp += calculateOverhitExp(exp);
- }
-
- // Distribute the Exp and SP between the L2PcInstance and its L2Summon
- if (!attacker.isDead())
- {
- final long addexp = Math.round(attacker.calcStat(Stats.EXPSP_RATE, exp, null, null));
- final int addsp = (int) attacker.calcStat(Stats.EXPSP_RATE, sp, null, null);
-
- attacker.addExpAndSp(addexp, addsp, useVitalityRate());
- if (addexp > 0)
- {
- attacker.updateVitalityPoints(getVitalityPoints(damage), true, false);
- }
- }
- }
- }
- else
- {
- // share with party members
- int partyDmg = 0;
- float partyMul = 1;
- int partyLvl = 0;
-
- // Get all L2Character that can be rewarded in the party
- final List<L2PcInstance> rewardedMembers = new ArrayList<>();
- // Go through all L2PcInstance in the party
- final List<L2PcInstance> groupMembers = attackerParty.isInCommandChannel() ? attackerParty.getCommandChannel().getMembers() : attackerParty.getMembers();
- for (L2PcInstance partyPlayer : groupMembers)
- {
- if ((partyPlayer == null) || partyPlayer.isDead())
- {
- continue;
- }
-
- // Get the RewardInfo of this L2PcInstance from L2Attackable rewards
- final DamageDoneInfo reward2 = rewards.get(partyPlayer);
-
- // If the L2PcInstance is in the L2Attackable rewards add its damages to party damages
- if (reward2 != null)
- {
- if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, partyPlayer, true))
- {
- partyDmg += reward2.getDamage(); // Add L2PcInstance damages to party damages
- rewardedMembers.add(partyPlayer);
-
- if (partyPlayer.getLevel() > partyLvl)
- {
- if (attackerParty.isInCommandChannel())
- {
- partyLvl = attackerParty.getCommandChannel().getLevel();
- }
- else
- {
- partyLvl = partyPlayer.getLevel();
- }
- }
- }
- rewards.remove(partyPlayer); // Remove the L2PcInstance from the L2Attackable rewards
- }
- else
- {
- // Add L2PcInstance of the party (that have attacked or not) to members that can be rewarded
- // and in range of the monster.
- if (Util.checkIfInRange(Config.ALT_PARTY_RANGE, this, partyPlayer, true))
- {
- rewardedMembers.add(partyPlayer);
- if (partyPlayer.getLevel() > partyLvl)
- {
- if (attackerParty.isInCommandChannel())
- {
- partyLvl = attackerParty.getCommandChannel().getLevel();
- }
- else
- {
- partyLvl = partyPlayer.getLevel();
- }
- }
- }
- }
- }
-
- // If the party didn't killed this L2Attackable alone
- if (partyDmg < totalDamage)
- {
- partyMul = ((float) partyDmg / totalDamage);
- }
-
- // Calculate the level difference between Party and L2Attackable
- final int levelDiff = partyLvl - getLevel();
-
- // Calculate Exp and SP rewards
- final int[] expSp = calculateExpAndSp(levelDiff, partyDmg, totalDamage);
- long exp = expSp[0];
- int sp = expSp[1];
-
- if (Config.L2JMOD_CHAMPION_ENABLE && isChampion())
- {
- exp *= Config.L2JMOD_CHAMPION_REWARDS;
- sp *= Config.L2JMOD_CHAMPION_REWARDS;
- }
-
- exp *= partyMul;
- sp *= partyMul;
-
- // Check for an over-hit enabled strike
- // (When in party, the over-hit exp bonus is given to the whole party and splitted proportionally through the party members)
- L2Character overhitAttacker = getOverhitAttacker();
- if (isOverhit() && (overhitAttacker != null) && (overhitAttacker.getActingPlayer() != null) && (attacker == overhitAttacker.getActingPlayer()))
- {
- attacker.sendPacket(SystemMessageId.OVER_HIT);
- exp += calculateOverhitExp(exp);
- }
-
- // Distribute Experience and SP rewards to L2PcInstance Party members in the known area of the last attacker
- if (partyDmg > 0)
- {
- attackerParty.distributeXpAndSp(exp, sp, rewardedMembers, partyLvl, partyDmg, this);
- }
- }
- }
- }
- }
- catch (Exception e)
- {
- _log.log(Level.SEVERE, "", e);
- }
- }
-
- @Override
- public void addAttackerToAttackByList(L2Character player)
- {
- if ((player == null) || (player == this) || getAttackByList().contains(player))
- {
- return;
- }
- getAttackByList().add(player);
- }
-
- /**
- * Add damage and hate to the attacker AggroInfo of the L2Attackable _aggroList.
- * @param attacker The L2Character that gave damages to this L2Attackable
- * @param damage The number of damages given by the attacker L2Character
- * @param skill
- */
- public void addDamage(L2Character attacker, int damage, Skill skill)
- {
- if (attacker == null)
- {
- return;
- }
-
- // Notify the L2Attackable AI with EVT_ATTACKED
- if (!isDead())
- {
- try
- {
- // If monster is on walk - stop it
- if (isWalker() && !isCoreAIDisabled() && WalkingManager.getInstance().isOnWalk(this))
- {
- WalkingManager.getInstance().stopMoving(this, false, true);
- }
-
- getAI().notifyEvent(CtrlEvent.EVT_ATTACKED, attacker);
- addDamageHate(attacker, damage, (damage * 100) / (getLevel() + 7));
-
- final L2PcInstance player = attacker.getActingPlayer();
- if (player != null)
- {
- EventDispatcher.getInstance().notifyEventAsync(new OnAttackableAttack(player, this, damage, skill, attacker.isSummon()), this);
- }
- }
- catch (Exception e)
- {
- _log.log(Level.SEVERE, "", e);
- }
- }
- }
-
- /**
- * Adds damage and hate to the attacker aggression list for this character.
- * @param attacker The L2Character that gave damages to this L2Attackable
- * @param damage The number of damages given by the attacker L2Character
- * @param aggro The hate (=damage) given by the attacker L2Character
- */
- public void addDamageHate(L2Character attacker, int damage, int aggro)
- {
- if (attacker == null)
- {
- return;
- }
-
- // Get the AggroInfo of the attacker L2Character from the _aggroList of the L2Attackable
- final AggroInfo ai = getAggroList().computeIfAbsent(attacker, AggroInfo::new);
- ai.addDamage(damage);
-
- // Traps does not cause aggro
- // making this hack because not possible to determine if damage made by trap
- // so just check for triggered trap here
- final L2PcInstance targetPlayer = attacker.getActingPlayer();
- if ((targetPlayer == null) || (targetPlayer.getTrap() == null) || !targetPlayer.getTrap().isTriggered())
- {
- ai.addHate(aggro);
- }
-
- if ((targetPlayer != null) && (aggro == 0))
- {
- addDamageHate(attacker, 0, 1);
-
- // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
- if (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE)
- {
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
- }
-
- // Notify to scripts
- EventDispatcher.getInstance().notifyEventAsync(new OnAttackableAggroRangeEnter(this, targetPlayer, attacker.isSummon()), this);
- }
- else if ((targetPlayer == null) && (aggro == 0))
- {
- aggro = 1;
- ai.addHate(1);
- }
-
- // Set the intention to the L2Attackable to AI_INTENTION_ACTIVE
- if ((aggro != 0) && (getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
- {
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
- }
- }
-
- public void reduceHate(L2Character target, int amount)
- {
- if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
- {
- // TODO: this just prevents error until siege guards are handled properly
- stopHating(target);
- setTarget(null);
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
- return;
- }
-
- if (target == null) // whole aggrolist
- {
- L2Character mostHated = getMostHated();
- if (mostHated == null) // makes target passive for a moment more
- {
- ((L2AttackableAI) getAI()).setGlobalAggro(-25);
- return;
- }
-
- for (AggroInfo ai : getAggroList().values())
- {
- if (ai == null)
- {
- return;
- }
- ai.addHate(amount);
- }
-
- amount = getHating(mostHated);
- if (amount >= 0)
- {
- ((L2AttackableAI) getAI()).setGlobalAggro(-25);
- clearAggroList();
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
- setWalking();
- }
- return;
- }
-
- AggroInfo ai = getAggroList().get(target);
- if (ai == null)
- {
- _log.info("target " + target + " not present in aggro list of " + this);
- return;
- }
-
- ai.addHate(amount);
- if ((ai.getHate() >= 0) && (getMostHated() == null))
- {
- ((L2AttackableAI) getAI()).setGlobalAggro(-25);
- clearAggroList();
- getAI().setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
- setWalking();
- }
- }
-
- /**
- * Clears _aggroList hate of the L2Character without removing from the list.
- * @param target
- */
- public void stopHating(L2Character target)
- {
- if (target == null)
- {
- return;
- }
- AggroInfo ai = getAggroList().get(target);
- if (ai != null)
- {
- ai.stopHate();
- }
- }
-
- /**
- * @return the most hated L2Character of the L2Attackable _aggroList.
- */
- public L2Character getMostHated()
- {
- if (getAggroList().isEmpty() || isAlikeDead())
- {
- return null;
- }
-
- L2Character mostHated = null;
- int maxHate = 0;
-
- // While Interacting over This Map Removing Object is Not Allowed
- // Go through the aggroList of the L2Attackable
- for (AggroInfo ai : getAggroList().values())
- {
- if (ai == null)
- {
- continue;
- }
-
- if (ai.checkHate(this) > maxHate)
- {
- mostHated = ai.getAttacker();
- maxHate = ai.getHate();
- }
- }
-
- return mostHated;
- }
-
- /**
- * @return the 2 most hated L2Character of the L2Attackable _aggroList.
- */
- public List<L2Character> get2MostHated()
- {
- if (getAggroList().isEmpty() || isAlikeDead())
- {
- return null;
- }
-
- L2Character mostHated = null;
- L2Character secondMostHated = null;
- int maxHate = 0;
- List<L2Character> result = new ArrayList<>();
-
- // While iterating over this map removing objects is not allowed
- // Go through the aggroList of the L2Attackable
- for (AggroInfo ai : getAggroList().values())
- {
- if (ai == null)
- {
- continue;
- }
-
- if (ai.checkHate(this) > maxHate)
- {
- secondMostHated = mostHated;
- mostHated = ai.getAttacker();
- maxHate = ai.getHate();
- }
- }
-
- result.add(mostHated);
-
- if (getAttackByList().contains(secondMostHated))
- {
- result.add(secondMostHated);
- }
- else
- {
- result.add(null);
- }
- return result;
- }
-
- public List<L2Character> getHateList()
- {
- if (getAggroList().isEmpty() || isAlikeDead())
- {
- return null;
- }
-
- List<L2Character> result = new ArrayList<>();
- for (AggroInfo ai : getAggroList().values())
- {
- if (ai == null)
- {
- continue;
- }
- ai.checkHate(this);
-
- result.add(ai.getAttacker());
- }
- return result;
- }
-
- /**
- * @param target The L2Character whose hate level must be returned
- * @return the hate level of the L2Attackable against this L2Character contained in _aggroList.
- */
- public int getHating(final L2Character target)
- {
- if (getAggroList().isEmpty() || (target == null))
- {
- return 0;
- }
-
- final AggroInfo ai = getAggroList().get(target);
- if (ai == null)
- {
- return 0;
- }
-
- if (ai.getAttacker() instanceof L2PcInstance)
- {
- L2PcInstance act = (L2PcInstance) ai.getAttacker();
- if (act.isInvisible() || ai.getAttacker().isInvul() || act.isSpawnProtected())
- {
- // Remove Object Should Use This Method and Can be Blocked While Interacting
- getAggroList().remove(target);
- return 0;
- }
- }
-
- if (!ai.getAttacker().isVisible() || ai.getAttacker().isInvisible())
- {
- getAggroList().remove(target);
- return 0;
- }
-
- if (ai.getAttacker().isAlikeDead())
- {
- ai.stopHate();
- return 0;
- }
- return ai.getHate();
- }
-
- public void doItemDrop(L2Character mainDamageDealer)
- {
- doItemDrop(getTemplate(), mainDamageDealer);
- }
-
- /**
- * Manage Base, Quests and Special Events drops of L2Attackable (called by calculateRewards).<br>
- * Concept:<br>
- * During a Special Event all L2Attackable can drop extra Items.<br>
- * Those extra Items are defined in the table allNpcDateDrops of the EventDroplist.<br>
- * Each Special Event has a start and end date to stop to drop extra Items automatically.<br>
- * Actions:<br>
- * Manage drop of Special Events created by GM for a defined period.<br>
- * Get all possible drops of this L2Attackable from L2NpcTemplate and add it Quest drops.<br>
- * For each possible drops (base + quests), calculate which one must be dropped (random).<br>
- * Get each Item quantity dropped (random).<br>
- * Create this or these L2ItemInstance corresponding to each Item Identifier dropped.<br>
- * 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>
- * 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.
- * @param npcTemplate
- * @param mainDamageDealer
- */
- public void doItemDrop(L2NpcTemplate npcTemplate, L2Character mainDamageDealer)
- {
- if (mainDamageDealer == null)
- {
- return;
- }
-
- L2PcInstance player = mainDamageDealer.getActingPlayer();
-
- // Don't drop anything if the last attacker or owner isn't L2PcInstance
- if (player == null)
- {
- return;
- }
-
- CursedWeaponsManager.getInstance().checkDrop(this, player);
-
- if (isSpoiled())
- {
- _sweepItems.set(npcTemplate.calculateDrops(DropListScope.CORPSE, this, player));
- }
-
- Collection<ItemHolder> deathItems = npcTemplate.calculateDrops(DropListScope.DEATH, this, player);
- if (deathItems != null)
- {
- for (ItemHolder drop : deathItems)
- {
- L2Item item = ItemTable.getInstance().getTemplate(drop.getId());
- // Check if the autoLoot mode is active
- if (isFlying() || (!item.hasExImmediateEffect() && ((!isRaid() && Config.AUTO_LOOT) || (isRaid() && Config.AUTO_LOOT_RAIDS))) || (item.hasExImmediateEffect() && Config.AUTO_LOOT_HERBS))
- {
- player.doAutoLoot(this, drop); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
- }
- else
- {
- dropItem(player, drop); // drop the item on the ground
- }
-
- // Broadcast message if RaidBoss was defeated
- if (isRaid() && !isRaidMinion() && drop.getCount() > 0)
- {
- final SystemMessage sm = SystemMessage.getSystemMessage(SystemMessageId.C1_DIED_DROPPED_S3_S2);
- sm.addCharName(this);
- sm.addItemName(item);
- sm.addLong(drop.getCount());
- broadcastPacket(sm);
- }
- }
- }
-
- // Apply Special Item drop with random(rnd) quantity(qty) for champions.
- if (Config.L2JMOD_CHAMPION_ENABLE && isChampion() && ((Config.L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE > 0) || (Config.L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE > 0)))
- {
- int champqty = Rnd.get(Config.L2JMOD_CHAMPION_REWARD_QTY);
- ItemHolder item = new ItemHolder(Config.L2JMOD_CHAMPION_REWARD_ID, ++champqty);
-
- if ((player.getLevel() <= getLevel()) && (Rnd.get(100) < Config.L2JMOD_CHAMPION_REWARD_LOWER_LVL_ITEM_CHANCE))
- {
- if (Config.AUTO_LOOT || isFlying())
- {
- player.addItem("ChampionLoot", item.getId(), item.getCount(), this, true); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
- }
- else
- {
- dropItem(player, item);
- }
- }
- else if ((player.getLevel() > getLevel()) && (Rnd.get(100) < Config.L2JMOD_CHAMPION_REWARD_HIGHER_LVL_ITEM_CHANCE))
- {
- if (Config.AUTO_LOOT || isFlying())
- {
- player.addItem("ChampionLoot", item.getId(), item.getCount(), this, true); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
- }
- else
- {
- dropItem(player, item);
- }
- }
- }
- }
-
- /**
- * Manage Special Events drops created by GM for a defined period.<br>
- * Concept:<br>
- * During a Special Event all L2Attackable can drop extra Items.<br>
- * Those extra Items are defined in the table allNpcDateDrops of the EventDroplist.<br>
- * Each Special Event has a start and end date to stop to drop extra Items automatically.<br>
- * Actions: <I>If an extra drop must be generated</I><br>
- * Get an Item Identifier (random) from the DateDrop Item table of this Event.<br>
- * Get the Item quantity dropped (random).<br>
- * Create this or these L2ItemInstance corresponding to this Item Identifier.<br>
- * 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>
- * 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
- * @param lastAttacker The L2Character that has killed the L2Attackable
- */
- public void doEventDrop(L2Character lastAttacker)
- {
- if (lastAttacker == null)
- {
- return;
- }
-
- L2PcInstance player = lastAttacker.getActingPlayer();
-
- // Don't drop anything if the last attacker or owner isn't L2PcInstance
- if (player == null)
- {
- return;
- }
-
- if ((player.getLevel() - getLevel()) > 9)
- {
- return;
- }
-
- // Go through DateDrop of EventDroplist allNpcDateDrops within the date range
- for (DateDrop drop : EventDroplist.getInstance().getAllDrops())
- {
- if (Rnd.get(1000000) < drop.getEventDrop().getDropChance())
- {
- final int itemId = drop.getEventDrop().getItemIdList()[Rnd.get(drop.getEventDrop().getItemIdList().length)];
- final long itemCount = Rnd.get(drop.getEventDrop().getMinCount(), drop.getEventDrop().getMaxCount());
- if (Config.AUTO_LOOT || isFlying())
- {
- player.doAutoLoot(this, itemId, itemCount); // Give the item(s) to the L2PcInstance that has killed the L2Attackable
- }
- else
- {
- dropItem(player, itemId, itemCount); // drop the item on the ground
- }
- }
- }
- }
-
- /**
- * @return the active weapon of this L2Attackable (= null).
- */
- public L2ItemInstance getActiveWeapon()
- {
- return null;
- }
-
- /**
- * @param player The L2Character searched in the _aggroList of the L2Attackable
- * @return True if the _aggroList of this L2Attackable contains the L2Character.
- */
- public boolean containsTarget(L2Character player)
- {
- return getAggroList().containsKey(player);
- }
-
- /**
- * Clear the _aggroList of the L2Attackable.
- */
- public void clearAggroList()
- {
- getAggroList().clear();
-
- // clear overhit values
- _overhit = false;
- _overhitDamage = 0;
- _overhitAttacker = null;
- }
-
- /**
- * @return {@code true} if there is a loot to sweep, {@code false} otherwise.
- */
- @Override
- public boolean isSweepActive()
- {
- return _sweepItems.get() != null;
- }
-
- /**
- * @return a copy of dummy items for the spoil loot.
- */
- public List<L2Item> getSpoilLootItems()
- {
- final Collection<ItemHolder> sweepItems = _sweepItems.get();
- final List<L2Item> lootItems = new LinkedList<>();
- if (sweepItems != null)
- {
- for (ItemHolder item : sweepItems)
- {
- lootItems.add(ItemTable.getInstance().getTemplate(item.getId()));
- }
- }
- return lootItems;
- }
-
- /**
- * @return table containing all L2ItemInstance that can be spoiled.
- */
- public Collection<ItemHolder> takeSweep()
- {
- return _sweepItems.getAndSet(null);
- }
-
- /**
- * @return table containing all L2ItemInstance that can be harvested.
- */
- public ItemHolder takeHarvest()
- {
- return _harvestItem.getAndSet(null);
- }
-
- /**
- * Checks if the corpse is too old.
- * @param attacker the player to validate
- * @param remainingTime the time to check
- * @param sendMessage if {@code true} will send a message of corpse too old
- * @return {@code true} if the corpse is too old
- */
- public boolean isOldCorpse(L2PcInstance attacker, int remainingTime, boolean sendMessage)
- {
- if (isDead() && (DecayTaskManager.getInstance().getRemainingTime(this) < remainingTime))
- {
- if (sendMessage && (attacker != null))
- {
- attacker.sendPacket(SystemMessageId.CORPSE_TOO_OLD_SKILL_NOT_USED);
- }
- return true;
- }
- return false;
- }
-
- /**
- * @param sweeper the player to validate.
- * @param sendMessage sendMessage if {@code true} will send a message of sweep not allowed.
- * @return {@code true} if is the spoiler or is in the spoiler party.
- */
- public boolean checkSpoilOwner(L2PcInstance sweeper, boolean sendMessage)
- {
- if ((sweeper.getObjectId() != getSpoilerObjectId()) && !sweeper.isInLooterParty(getSpoilerObjectId()))
- {
- if (sendMessage)
- {
- sweeper.sendPacket(SystemMessageId.SWEEP_NOT_ALLOWED);
- }
- return false;
- }
- return true;
- }
-
- /**
- * Set the over-hit flag on the L2Attackable.
- * @param status The status of the over-hit flag
- */
- public void overhitEnabled(boolean status)
- {
- _overhit = status;
- }
-
- /**
- * Set the over-hit values like the attacker who did the strike and the amount of damage done by the skill.
- * @param attacker The L2Character who hit on the L2Attackable using the over-hit enabled skill
- * @param damage The amount of damage done by the over-hit enabled skill on the L2Attackable
- */
- public void setOverhitValues(L2Character attacker, double damage)
- {
- // Calculate the over-hit damage
- // Ex: mob had 10 HP left, over-hit skill did 50 damage total, over-hit damage is 40
- double overhitDmg = -(getCurrentHp() - damage);
- if (overhitDmg < 0)
- {
- // we didn't killed the mob with the over-hit strike. (it wasn't really an over-hit strike)
- // let's just clear all the over-hit related values
- overhitEnabled(false);
- _overhitDamage = 0;
- _overhitAttacker = null;
- return;
- }
- overhitEnabled(true);
- _overhitDamage = overhitDmg;
- _overhitAttacker = attacker;
- }
-
- /**
- * Return the L2Character who hit on the L2Attackable using an over-hit enabled skill.
- * @return L2Character attacker
- */
- public L2Character getOverhitAttacker()
- {
- return _overhitAttacker;
- }
-
- /**
- * Return the amount of damage done on the L2Attackable using an over-hit enabled skill.
- * @return double damage
- */
- public double getOverhitDamage()
- {
- return _overhitDamage;
- }
-
- /**
- * @return True if the L2Attackable was hit by an over-hit enabled skill.
- */
- public boolean isOverhit()
- {
- return _overhit;
- }
-
- /**
- * Activate the absorbed soul condition on the L2Attackable.
- */
- public void absorbSoul()
- {
- _absorbed = true;
- }
-
- /**
- * @return True if the L2Attackable had his soul absorbed.
- */
- public boolean isAbsorbed()
- {
- return _absorbed;
- }
-
- /**
- * Adds an attacker that successfully absorbed the soul of this L2Attackable into the _absorbersList.
- * @param attacker
- */
- public void addAbsorber(L2PcInstance attacker)
- {
- // If we have no _absorbersList initiated, do it
- final AbsorberInfo ai = _absorbersList.get(attacker.getObjectId());
-
- // If the L2Character attacker isn't already in the _absorbersList of this L2Attackable, add it
- if (ai == null)
- {
- _absorbersList.put(attacker.getObjectId(), new AbsorberInfo(attacker.getObjectId(), getCurrentHp()));
- }
- else
- {
- ai.setAbsorbedHp(getCurrentHp());
- }
-
- // Set this L2Attackable as absorbed
- absorbSoul();
- }
-
- public void resetAbsorbList()
- {
- _absorbed = false;
- _absorbersList.clear();
- }
-
- public Map<Integer, AbsorberInfo> getAbsorbersList()
- {
- return _absorbersList;
- }
-
- /**
- * Calculate the Experience and SP to distribute to attacker (L2PcInstance, L2ServitorInstance or L2Party) of the L2Attackable.
- * @param diff The difference of level between attacker (L2PcInstance, L2ServitorInstance or L2Party) and the L2Attackable
- * @param damage The damages given by the attacker (L2PcInstance, L2ServitorInstance or L2Party)
- * @param totalDamage The total damage done
- * @return
- */
- private int[] calculateExpAndSp(int diff, int damage, long totalDamage)
- {
- double xp;
- double sp;
-
- if (diff < -5)
- {
- diff = -5; // makes possible to use ALT_GAME_EXPONENT configuration
- }
-
- xp = ((double) getExpReward() * damage) / totalDamage;
- if (Config.ALT_GAME_EXPONENT_XP != 0)
- {
- xp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_XP);
- }
-
- sp = ((double) getSpReward() * damage) / totalDamage;
- if (Config.ALT_GAME_EXPONENT_SP != 0)
- {
- sp *= Math.pow(2., -diff / Config.ALT_GAME_EXPONENT_SP);
- }
-
- if ((Config.ALT_GAME_EXPONENT_XP == 0) && (Config.ALT_GAME_EXPONENT_SP == 0))
- {
- if (diff > 5) // formula revised May 07
- {
- double pow = Math.pow((double) 5 / 6, diff - 5);
- xp = xp * pow;
- sp = sp * pow;
- }
-
- if (xp <= 0)
- {
- xp = 0;
- sp = 0;
- }
- else if (sp <= 0)
- {
- sp = 0;
- }
- }
- int[] tmp =
- {
- (int) xp,
- (int) sp
- };
- return tmp;
- }
-
- public long calculateOverhitExp(long normalExp)
- {
- // Get the percentage based on the total of extra (over-hit) damage done relative to the total (maximum) ammount of HP on the L2Attackable
- double overhitPercentage = ((getOverhitDamage() * 100) / getMaxHp());
-
- // Over-hit damage percentages are limited to 25% max
- if (overhitPercentage > 25)
- {
- overhitPercentage = 25;
- }
-
- // Get the overhit exp bonus according to the above over-hit damage percentage
- // (1/1 basis - 13% of over-hit damage, 13% of extra exp is given, and so on...)
- double overhitExp = ((overhitPercentage / 100) * normalExp);
-
- // Return the rounded ammount of exp points to be added to the player's normal exp reward
- long bonusOverhit = Math.round(overhitExp);
- return bonusOverhit;
- }
-
- /**
- * Return True.
- */
- @Override
- public boolean canBeAttacked()
- {
- return true;
- }
-
- @Override
- public void onSpawn()
- {
- super.onSpawn();
- // Clear mob spoil, seed
- setSpoilerObjectId(0);
- // Clear all aggro char from list
- clearAggroList();
- // Clear Harvester reward
- _harvestItem.set(null);
- // Clear mod Seeded stat
- _seeded = false;
- _seed = null;
- _seederObjId = 0;
- // Clear overhit value
- overhitEnabled(false);
-
- _sweepItems.set(null);
- resetAbsorbList();
-
- setWalking();
-
- // check the region where this mob is, do not activate the AI if region is inactive.
- if (!isInActiveRegion())
- {
- if (hasAI())
- {
- getAI().stopAITask();
- }
- }
- }
-
- /**
- * Checks if its spoiled.
- * @return {@code true} if its spoiled, {@code false} otherwise
- */
- public final boolean isSpoiled()
- {
- return _spoilerObjectId != 0;
- }
-
- /**
- * Gets the spoiler object id.
- * @return the spoiler object id if its spoiled, 0 otherwise
- */
- public final int getSpoilerObjectId()
- {
- return _spoilerObjectId;
- }
-
- /**
- * Sets the spoiler object id.
- * @param spoilerObjectId the spoiler object id
- */
- public final void setSpoilerObjectId(int spoilerObjectId)
- {
- _spoilerObjectId = spoilerObjectId;
- }
-
- /**
- * Sets state of the mob to seeded. Parameters needed to be set before.
- * @param seeder
- */
- public final void setSeeded(L2PcInstance seeder)
- {
- if ((_seed != null) && (_seederObjId == seeder.getObjectId()))
- {
- _seeded = true;
-
- int count = 1;
- for (int skillId : getTemplate().getSkills().keySet())
- {
- switch (skillId)
- {
- case 4303: // Strong type x2
- count *= 2;
- break;
- case 4304: // Strong type x3
- count *= 3;
- break;
- case 4305: // Strong type x4
- count *= 4;
- break;
- case 4306: // Strong type x5
- count *= 5;
- break;
- case 4307: // Strong type x6
- count *= 6;
- break;
- case 4308: // Strong type x7
- count *= 7;
- break;
- case 4309: // Strong type x8
- count *= 8;
- break;
- case 4310: // Strong type x9
- count *= 9;
- break;
- }
- }
-
- // hi-lvl mobs bonus
- final int diff = getLevel() - _seed.getLevel() - 5;
- if (diff > 0)
- {
- count += diff;
- }
- _harvestItem.set(new ItemHolder(_seed.getCropId(), count * Config.RATE_DROP_MANOR));
- }
- }
-
- /**
- * Sets the seed parameters, but not the seed state
- * @param seed - instance {@link L2Seed} of used seed
- * @param seeder - player who sows the seed
- */
- public final void setSeeded(L2Seed seed, L2PcInstance seeder)
- {
- if (!_seeded)
- {
- _seed = seed;
- _seederObjId = seeder.getObjectId();
- }
- }
-
- public final int getSeederId()
- {
- return _seederObjId;
- }
-
- public final L2Seed getSeed()
- {
- return _seed;
- }
-
- public final boolean isSeeded()
- {
- return _seeded;
- }
-
- /**
- * Set delay for onKill() call, in ms Default: 5000 ms
- * @param delay
- */
- public final void setOnKillDelay(int delay)
- {
- _onKillDelay = delay;
- }
-
- public final int getOnKillDelay()
- {
- return _onKillDelay;
- }
-
- /**
- * Check if the server allows Random Animation.
- */
- // This is located here because L2Monster and L2FriendlyMob both extend this class. The other non-pc instances extend either L2NpcInstance or L2MonsterInstance.
- @Override
- public boolean hasRandomAnimation()
- {
- return ((Config.MAX_MONSTER_ANIMATION > 0) && isRandomAnimationEnabled() && !(this instanceof L2GrandBossInstance));
- }
-
- @Override
- public boolean isMob()
- {
- return true; // This means we use MAX_MONSTER_ANIMATION instead of MAX_NPC_ANIMATION
- }
-
- public void setCommandChannelTimer(CommandChannelTimer commandChannelTimer)
- {
- _commandChannelTimer = commandChannelTimer;
- }
-
- public CommandChannelTimer getCommandChannelTimer()
- {
- return _commandChannelTimer;
- }
-
- public L2CommandChannel getFirstCommandChannelAttacked()
- {
- return _firstCommandChannelAttacked;
- }
-
- public void setFirstCommandChannelAttacked(L2CommandChannel firstCommandChannelAttacked)
- {
- _firstCommandChannelAttacked = firstCommandChannelAttacked;
- }
-
- /**
- * @return the _commandChannelLastAttack
- */
- public long getCommandChannelLastAttack()
- {
- return _commandChannelLastAttack;
- }
-
- /**
- * @param channelLastAttack the _commandChannelLastAttack to set
- */
- public void setCommandChannelLastAttack(long channelLastAttack)
- {
- _commandChannelLastAttack = channelLastAttack;
- }
-
- public void returnHome()
- {
- clearAggroList();
-
- if (hasAI() && (getSpawn() != null))
- {
- getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, getSpawn().getLocation(this));
- }
- }
-
- /*
- * Return vitality points decrease (if positive) or increase (if negative) based on damage. Maximum for damage = maxHp.
- */
- public float getVitalityPoints(int damage)
- {
- // sanity check
- if (damage <= 0)
- {
- return 0;
- }
-
- final float divider = (getLevel() > 0) && (getExpReward() > 0) ? (getTemplate().getBaseHpMax() * 9 * getLevel() * getLevel()) / (100 * getExpReward()) : 0;
- if (divider == 0)
- {
- return 0;
- }
-
- // negative value - vitality will be consumed
- return -Math.min(damage, getMaxHp()) / divider;
- }
-
- /*
- * True if vitality rate for exp and sp should be applied
- */
- public boolean useVitalityRate()
- {
- if (isChampion() && !Config.L2JMOD_CHAMPION_ENABLE_VITALITY)
- {
- return false;
- }
-
- return true;
- }
-
- /** Return True if the L2Character is RaidBoss or his minion. */
- @Override
- public boolean isRaid()
- {
- return _isRaid;
- }
-
- /**
- * Set this Npc as a Raid instance.
- * @param isRaid
- */
- public void setIsRaid(boolean isRaid)
- {
- _isRaid = isRaid;
- }
-
- /**
- * Set this Npc as a Minion instance.
- * @param val
- */
- public void setIsRaidMinion(boolean val)
- {
- _isRaid = val;
- _isRaidMinion = val;
- }
-
- @Override
- public boolean isRaidMinion()
- {
- return _isRaidMinion;
- }
-
- @Override
- public boolean isMinion()
- {
- return getLeader() != null;
- }
-
- /**
- * @return leader of this minion or null.
- */
- public L2Attackable getLeader()
- {
- return null;
- }
-
- public void setChampion(boolean champ)
- {
- _champion = champ;
- }
-
- @Override
- public boolean isChampion()
- {
- return _champion;
- }
-
- @Override
- public boolean isAttackable()
- {
- return true;
- }
- }
|