Advertisement
Not a member of Pastebin yet?
Sign Up,
it unlocks many cool features!
- Index: java/net/sf/l2j/gameserver/skills/l2skills/L2SkillElemental.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/skills/l2skills/L2SkillElemental.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/skills/l2skills/L2SkillElemental.java (revision 1771)
- @@ -20,6 +20,7 @@
- import net.sf.l2j.gameserver.model.L2Object;
- import net.sf.l2j.gameserver.model.L2Skill;
- import net.sf.l2j.gameserver.model.L2Summon;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- import net.sf.l2j.gameserver.network.SystemMessageId;
- import net.sf.l2j.gameserver.serverpackets.SystemMessage;
- @@ -95,6 +96,11 @@
- activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- for (int index = 0; index < targets.length; index++)
- {
- Index: java/net/sf/l2j/gameserver/skills/l2skills/L2SkillDrain.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/skills/l2skills/L2SkillDrain.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/skills/l2skills/L2SkillDrain.java (revision 1771)
- @@ -89,6 +89,12 @@
- activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- +
- boolean mcrit = Formulas.getInstance().calcMCrit(activeChar.getMCriticalHit(target, this));
- int damage = (int)Formulas.getInstance().calcMagicDam(
- Index: java/net/sf/l2j/gameserver/handler/skillhandlers/Heal.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/skillhandlers/Heal.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/handler/skillhandlers/Heal.java (revision 1771)
- @@ -23,6 +23,7 @@
- import net.sf.l2j.gameserver.model.L2Summon;
- import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- import net.sf.l2j.gameserver.network.SystemMessageId;
- import net.sf.l2j.gameserver.serverpackets.StatusUpdate;
- @@ -125,6 +126,10 @@
- activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + if(((L2NpcInstance)activeChar).isUsingShot(false)) hp *= 1.5;
- + }
- }
- //int cLev = activeChar.getLevel();
- Index: java/net/sf/l2j/gameserver/handler/skillhandlers/Manadam.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/skillhandlers/Manadam.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/handler/skillhandlers/Manadam.java (revision 1771)
- @@ -1,128 +1,149 @@
- -/*
- - * This program 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.
- - *
- - * This program 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 net.sf.l2j.gameserver.handler.skillhandlers;
- -
- -import net.sf.l2j.gameserver.handler.ISkillHandler;
- -import net.sf.l2j.gameserver.model.L2Character;
- -import net.sf.l2j.gameserver.model.L2ItemInstance;
- -import net.sf.l2j.gameserver.model.L2Object;
- -import net.sf.l2j.gameserver.model.L2Skill;
- -import net.sf.l2j.gameserver.model.L2Summon;
- -import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- -import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- -import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- -import net.sf.l2j.gameserver.network.SystemMessageId;
- -import net.sf.l2j.gameserver.serverpackets.StatusUpdate;
- -import net.sf.l2j.gameserver.serverpackets.SystemMessage;
- -import net.sf.l2j.gameserver.skills.Formulas;
- -
- -/**
- - * Class handling the Mana damage skill
- - *
- - * @author slyce
- - */
- -public class Manadam implements ISkillHandler
- -{
- -private static final SkillType[] SKILL_IDS = { SkillType.MANADAM };
- -
- -public void useSkill(@SuppressWarnings("unused")
- -L2Character activeChar, L2Skill skill, L2Object[] targets)
- -{
- - L2Character target = null;
- -
- - if (activeChar.isAlikeDead())
- - return;
- -
- - boolean ss = false;
- - boolean bss = false;
- -
- - L2ItemInstance weaponInst = activeChar.getActiveWeaponInstance();
- -
- - if (weaponInst != null)
- - {
- - if (weaponInst.getChargedSpiritshot() == L2ItemInstance.CHARGED_BLESSED_SPIRITSHOT)
- - {
- - bss = true;
- - weaponInst.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
- - } else if (weaponInst.getChargedSpiritshot() == L2ItemInstance.CHARGED_SPIRITSHOT)
- - {
- - ss = true;
- - weaponInst.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
- - }
- - }
- - for (int index = 0; index < targets.length; index++)
- - {
- - target = (L2Character) targets[index];
- -
- - if (target.reflectSkill(skill))
- - target = activeChar;
- -
- - boolean acted = Formulas.getInstance().calcMagicAffected(activeChar, target, skill);
- - if (target.isInvul() || !acted)
- - {
- - activeChar.sendPacket(new SystemMessage(SystemMessageId.MISSED_TARGET));
- - } else
- - {
- - double damage = Formulas.getInstance().calcManaDam(activeChar, target, skill, ss, bss);
- -
- - double mp = (damage > target.getCurrentMp() ? target.getCurrentMp() : damage);
- - target.reduceCurrentMp(mp);
- - if (damage > 0)
- - {
- - if (target.isSleeping())
- - {
- - target.stopSleeping(null);
- - }
- - else if (target.isImmobileUntilAttacked())
- - {
- - target.stopImmobileUntilAttacked(null);
- - }
- - }
- -
- - StatusUpdate sump = new StatusUpdate(target.getObjectId());
- - sump.addAttribute(StatusUpdate.CUR_MP, (int) target.getCurrentMp());
- - // [L2J_JP EDIT START - TSL]
- - target.sendPacket(sump);
- - SystemMessage sm = new SystemMessage(SystemMessageId.S2_MP_HAS_BEEN_DRAINED_BY_S1);
- - if (activeChar instanceof L2NpcInstance)
- - {
- - int mobId = ((L2NpcInstance) activeChar).getNpcId();
- - sm.addNpcName(mobId);
- - } else if (activeChar instanceof L2Summon)
- - {
- - int mobId = ((L2Summon) activeChar).getNpcId();
- - sm.addNpcName(mobId);
- - } else
- - {
- - sm.addString(activeChar.getName());
- - }
- - sm.addNumber((int) mp);
- - target.sendPacket(sm);
- - if (activeChar instanceof L2PcInstance)
- - {
- - SystemMessage sm2 = new SystemMessage(SystemMessageId.YOUR_OPPONENTS_MP_WAS_REDUCED_BY_S1);
- - sm2.addNumber((int) mp);
- - activeChar.sendPacket(sm2);
- - }
- - // [L2J_JP EDIT END - TSL]
- - }
- - }
- -}
- -
- -public SkillType[] getSkillIds()
- -{
- - return SKILL_IDS;
- -}
- -}
- +/*
- + * This program 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.
- + *
- + * This program 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 net.sf.l2j.gameserver.handler.skillhandlers;
- +
- +import net.sf.l2j.gameserver.handler.ISkillHandler;
- +import net.sf.l2j.gameserver.model.L2Character;
- +import net.sf.l2j.gameserver.model.L2ItemInstance;
- +import net.sf.l2j.gameserver.model.L2Object;
- +import net.sf.l2j.gameserver.model.L2Skill;
- +import net.sf.l2j.gameserver.model.L2Summon;
- +import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- +import net.sf.l2j.gameserver.network.SystemMessageId;
- +import net.sf.l2j.gameserver.serverpackets.StatusUpdate;
- +import net.sf.l2j.gameserver.serverpackets.SystemMessage;
- +import net.sf.l2j.gameserver.skills.Formulas;
- +
- +/**
- + * Class handling the Mana damage skill
- + *
- + * @author slyce
- + */
- +public class Manadam implements ISkillHandler
- +{
- +private static final SkillType[] SKILL_IDS = { SkillType.MANADAM };
- +
- +public void useSkill(@SuppressWarnings("unused")
- +L2Character activeChar, L2Skill skill, L2Object[] targets)
- +{
- + L2Character target = null;
- +
- + if (activeChar.isAlikeDead())
- + return;
- +
- + boolean ss = false;
- + boolean bss = false;
- +
- + L2ItemInstance weaponInst = activeChar.getActiveWeaponInstance();
- +
- + if (weaponInst != null)
- + {
- + if (weaponInst.getChargedSpiritshot() == L2ItemInstance.CHARGED_BLESSED_SPIRITSHOT)
- + {
- + bss = true;
- + weaponInst.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
- + } else if (weaponInst.getChargedSpiritshot() == L2ItemInstance.CHARGED_SPIRITSHOT)
- + {
- + ss = true;
- + weaponInst.setChargedSpiritshot(L2ItemInstance.CHARGED_NONE);
- + }
- + }
- + // If there is no weapon equipped, check for an active summon.
- + else if (activeChar instanceof L2Summon)
- + {
- + L2Summon activeSummon = (L2Summon) activeChar;
- +
- + if (activeSummon.getChargedSpiritShot() == L2ItemInstance.CHARGED_BLESSED_SPIRITSHOT)
- + {
- + bss = true;
- + activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- + }
- + else if (activeSummon.getChargedSpiritShot() == L2ItemInstance.CHARGED_SPIRITSHOT)
- + {
- + ss = true;
- + activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- + }
- + }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- + for (int index = 0; index < targets.length; index++)
- + {
- + target = (L2Character) targets[index];
- +
- + if (target.reflectSkill(skill))
- + target = activeChar;
- +
- + boolean acted = Formulas.getInstance().calcMagicAffected(activeChar, target, skill);
- + if (target.isInvul() || !acted)
- + {
- + activeChar.sendPacket(new SystemMessage(SystemMessageId.MISSED_TARGET));
- + } else
- + {
- + double damage = Formulas.getInstance().calcManaDam(activeChar, target, skill, ss, bss);
- +
- + double mp = (damage > target.getCurrentMp() ? target.getCurrentMp() : damage);
- + target.reduceCurrentMp(mp);
- + if (damage > 0)
- + {
- + if (target.isSleeping())
- + {
- + target.stopSleeping(null);
- + }
- + else if (target.isImmobileUntilAttacked())
- + {
- + target.stopImmobileUntilAttacked(null);
- + }
- + }
- +
- + StatusUpdate sump = new StatusUpdate(target.getObjectId());
- + sump.addAttribute(StatusUpdate.CUR_MP, (int) target.getCurrentMp());
- + // [L2J_JP EDIT START - TSL]
- + target.sendPacket(sump);
- + SystemMessage sm = new SystemMessage(SystemMessageId.S2_MP_HAS_BEEN_DRAINED_BY_S1);
- + if (activeChar instanceof L2NpcInstance)
- + {
- + int mobId = ((L2NpcInstance) activeChar).getNpcId();
- + sm.addNpcName(mobId);
- + } else if (activeChar instanceof L2Summon)
- + {
- + int mobId = ((L2Summon) activeChar).getNpcId();
- + sm.addNpcName(mobId);
- + } else
- + {
- + sm.addString(activeChar.getName());
- + }
- + sm.addNumber((int) mp);
- + target.sendPacket(sm);
- + if (activeChar instanceof L2PcInstance)
- + {
- + SystemMessage sm2 = new SystemMessage(SystemMessageId.YOUR_OPPONENTS_MP_WAS_REDUCED_BY_S1);
- + sm2.addNumber((int) mp);
- + activeChar.sendPacket(sm2);
- + }
- + // [L2J_JP EDIT END - TSL]
- + }
- + }
- +}
- +
- +public SkillType[] getSkillIds()
- +{
- + return SKILL_IDS;
- +}
- +}
- Index: java/config/altsettings.properties
- ===================================================================
- --- java/config/altsettings.properties (revision 1770)
- +++ java/config/altsettings.properties (revision 1771)
- @@ -44,9 +44,6 @@
- # Alternative Rate Value for Perfect Shield Block Rate.
- AltPerfectShieldBlockRate = 5
- -# Alternative AltGameMobAttackAI, like C1
- -AltGameMobAttackAI = False
- -
- # Alternative mob behavior in peace zones
- # Default = True; Set to False to prevent mobs from auto-agro against players in peace zones
- AltMobAgroInPeaceZone = True
- Index: java/net/sf/l2j/Config.java
- ===================================================================
- --- java/net/sf/l2j/Config.java (revision 1770)
- +++ java/net/sf/l2j/Config.java (revision 1771)
- @@ -1683,7 +1683,6 @@
- ALT_PERFECT_SHLD_BLOCK = Integer.parseInt(altSettings.getProperty("AltPerfectShieldBlockRate", "10"));
- ALT_GAME_DELEVEL = Boolean.parseBoolean(altSettings.getProperty("Delevel", "true"));
- ALT_GAME_MAGICFAILURES = Boolean.parseBoolean(altSettings.getProperty("MagicFailures", "false"));
- - ALT_GAME_MOB_ATTACK_AI = Boolean.parseBoolean(altSettings.getProperty("AltGameMobAttackAI", "false"));
- ALT_MOB_AGRO_IN_PEACEZONE = Boolean.parseBoolean(altSettings.getProperty("AltMobAgroInPeaceZone", "true"));
- ALT_GAME_EXPONENT_XP = Float.parseFloat(altSettings.getProperty("AltGameExponentXp", "0."));
- ALT_GAME_EXPONENT_SP = Float.parseFloat(altSettings.getProperty("AltGameExponentSp", "0."));
- @@ -2376,7 +2375,6 @@
- else if (pName.equalsIgnoreCase("AltPerfectShieldBlockRate")) ALT_PERFECT_SHLD_BLOCK = Integer.parseInt(pValue);
- else if (pName.equalsIgnoreCase("Delevel")) ALT_GAME_DELEVEL = Boolean.valueOf(pValue);
- else if (pName.equalsIgnoreCase("MagicFailures")) ALT_GAME_MAGICFAILURES = Boolean.valueOf(pValue);
- - else if (pName.equalsIgnoreCase("AltGameMobAttackAI")) ALT_GAME_MOB_ATTACK_AI = Boolean.valueOf(pValue);
- else if (pName.equalsIgnoreCase("AltMobAgroInPeaceZone")) ALT_MOB_AGRO_IN_PEACEZONE = Boolean.valueOf(pValue);
- else if (pName.equalsIgnoreCase("AltGameExponentXp")) ALT_GAME_EXPONENT_XP = Float.parseFloat(pValue);
- Index: java/net/sf/l2j/gameserver/model/L2Skill.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2Skill.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/model/L2Skill.java (revision 1771)
- @@ -2228,6 +2228,28 @@
- }
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + // for buff purposes, returns one unbuffed friendly mob nearby or mob itself?
- + L2NpcInstance npc = (L2NpcInstance) activeChar;
- + for (L2Object newTarget : activeChar.getKnownList().getKnownObjects().values())
- + {
- + if (newTarget instanceof L2NpcInstance && ((L2NpcInstance)newTarget).getFactionId() == npc.getFactionId())
- + {
- + if (!Util.checkIfInRange(getCastRange(), activeChar, newTarget, true))
- + continue;
- + if (((L2NpcInstance)newTarget).getFirstEffect(this) != null)
- + {
- + targetList.add((L2NpcInstance)newTarget);
- + break;
- + }
- + }
- + }
- + if (targetList.isEmpty())
- + {
- + targetList.add(activeChar);
- + }
- + }
- return targetList.toArray(new L2Character[targetList.size()]);
- }
- Index: java/net/sf/l2j/gameserver/model/L2ItemInstance.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2ItemInstance.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/model/L2ItemInstance.java (revision 1771)
- @@ -63,7 +63,8 @@
- PET,
- PET_EQUIP,
- LEASE,
- - FREIGHT
- + FREIGHT,
- + NPC
- }
- /** ID of the owner */
- @@ -939,7 +940,7 @@
- {
- return;
- }
- - if (_loc == ItemLocation.VOID || _ownerId == 0)
- + if (_loc == ItemLocation.VOID || _loc == ItemLocation.NPC || _ownerId == 0)
- {
- return;
- }
- Index: java/net/sf/l2j/gameserver/model/actor/instance/L2NpcInstance.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/actor/instance/L2NpcInstance.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/model/actor/instance/L2NpcInstance.java (revision 1771)
- @@ -53,6 +53,7 @@
- import net.sf.l2j.gameserver.model.L2World;
- import net.sf.l2j.gameserver.model.L2WorldRegion;
- import net.sf.l2j.gameserver.model.MobGroupTable;
- +import net.sf.l2j.gameserver.model.NpcInventory;
- import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- import net.sf.l2j.gameserver.model.actor.knownlist.NpcKnownList;
- import net.sf.l2j.gameserver.model.actor.stat.NpcStat;
- @@ -69,6 +70,7 @@
- import net.sf.l2j.gameserver.serverpackets.ExShowVariationCancelWindow;
- import net.sf.l2j.gameserver.serverpackets.ExShowVariationMakeWindow;
- import net.sf.l2j.gameserver.serverpackets.InventoryUpdate;
- +import net.sf.l2j.gameserver.serverpackets.MagicSkillUse;
- import net.sf.l2j.gameserver.serverpackets.MyTargetSelected;
- import net.sf.l2j.gameserver.serverpackets.NpcHtmlMessage;
- import net.sf.l2j.gameserver.serverpackets.NpcInfo;
- @@ -104,6 +106,8 @@
- /** The L2Spawn object that manage this L2NpcInstance */
- private L2Spawn _spawn;
- +
- + private NpcInventory _inventory = null;
- /** The flag to specify if this L2NpcInstance is busy */
- private boolean _isBusy = false;
- @@ -276,6 +280,8 @@
- // Set the name of the L2Character
- setName(template.name);
- + if ((template.ss > 0 || template.bss > 0) && template.ssRate > 0)
- + _inventory = new NpcInventory(this);
- }
- @@ -2170,7 +2176,9 @@
- @Override
- public void onSpawn()
- {
- - super.onSpawn();
- + if (_inventory != null) _inventory.Reset();
- +
- + super.onSpawn();
- if (getTemplate().getEventQuests(Quest.QuestEventType.NPC_SPAWNED) != null)
- for (Quest quest: getTemplate().getEventQuests(Quest.QuestEventType.NPC_SPAWNED))
- @@ -2297,4 +2305,61 @@
- {
- return _currentCollisionRadius;
- }
- +
- + public boolean rechargeAutoSoulShot(boolean physical, boolean magic)
- + {
- + if (this.getTemplate().ssRate == 0) return false;
- +
- + L2Weapon weaponItem = getActiveWeaponItem();
- + if (weaponItem == null)
- + {
- + //_log.warning("NpcId "+getNpcId()+" missing weaponItem definition in DP - or wrong use of shots.");
- + return false;
- + }
- + if (magic)
- + {
- + if (this.getTemplate().ssRate < Rnd.get(100))
- + {
- + _inventory.bshotInUse = false;
- + return false;
- + }
- +
- + if (null != _inventory.destroyItemByItemId("Consume", 3947, weaponItem.getSpiritShotCount(), null, null))
- + {
- + _inventory.bshotInUse = true;
- + broadcastPacket(new MagicSkillUse(this, this, 2061, 1, 0, 0), 360000); // no grade
- + return true;
- + }
- + else
- + _inventory.bshotInUse = false;
- +
- + }
- + if (physical)
- + {
- + if (this.getTemplate().ssRate < Rnd.get(100))
- + {
- + _inventory.sshotInUse = false;
- + return false;
- + }
- +
- + if (null != _inventory.destroyItemByItemId("Consume", 1835, weaponItem.getSoulShotCount(), null, null))
- + {
- + _inventory.sshotInUse = true;
- + broadcastPacket(new MagicSkillUse(this, this, 2039, 1, 0, 0), 360000); // no grade
- + return true;
- + }
- + else
- + _inventory.sshotInUse = false;
- + }
- + return false;
- + }
- +
- + public boolean isUsingShot(boolean physical)
- + {
- + if (_inventory == null) return false;
- + if (physical && _inventory.sshotInUse) return true;
- + if (!physical && _inventory.bshotInUse) return true;
- + return false;
- + }
- +
- }
- Index: java/net/sf/l2j/gameserver/model/L2Character.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2Character.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/model/L2Character.java (revision 1771)
- @@ -789,7 +789,9 @@
- // Verify if soulshots are charged.
- boolean wasSSCharged;
- - if (this instanceof L2Summon && !(this instanceof L2PetInstance))
- + if (this instanceof L2NpcInstance)
- + wasSSCharged = ((L2NpcInstance)this).rechargeAutoSoulShot(true, false);
- + else if (this instanceof L2Summon && !(this instanceof L2PetInstance))
- wasSSCharged = (((L2Summon)this).getChargedSoulShot() != L2ItemInstance.CHARGED_NONE);
- else
- wasSSCharged = (weaponInst != null && weaponInst.getChargedSoulshot() != L2ItemInstance.CHARGED_NONE);
- @@ -1351,7 +1353,9 @@
- //Recharge AutoSoulShot
- if (skill.useSoulShot())
- {
- - if (this instanceof L2PcInstance)
- + if (this instanceof L2NpcInstance)
- + ((L2NpcInstance)this).rechargeAutoSoulShot(true, false);
- + else if (this instanceof L2PcInstance)
- ((L2PcInstance)this).rechargeAutoSoulShot(true, false, false);
- else if (this instanceof L2Summon)
- ((L2Summon)this).getOwner().rechargeAutoSoulShot(true, false, true);
- @@ -1358,7 +1362,7 @@
- }
- else if (skill.useSpiritShot())
- {
- - if (this instanceof L2PcInstance)
- + if (this instanceof L2PcInstance)
- ((L2PcInstance)this).rechargeAutoSoulShot(false, true, false);
- else if (this instanceof L2Summon)
- ((L2Summon)this).getOwner().rechargeAutoSoulShot(false, true, true);
- @@ -1485,6 +1489,14 @@
- }
- }
- }
- + else if (this instanceof L2NpcInstance && skill.useSpiritShot() && !forceBuff)
- + {
- + if(((L2NpcInstance)this).rechargeAutoSoulShot(false, true))
- + {
- + hitTime = (int)(0.70 * hitTime);
- + coolTime = (int)(0.70 * coolTime);
- + }
- + }
- // Set the _castEndTime and _castInterruptTim. +10 ticks for lag situations, will be reseted in onMagicFinalizer
- _castEndTime = 10 + GameTimeController.getGameTicks() + (coolTime + hitTime) / GameTimeController.MILLIS_IN_TICK;
- Index: java/net/sf/l2j/gameserver/model/L2Attackable.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/L2Attackable.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/model/L2Attackable.java (revision 1771)
- @@ -948,7 +948,43 @@
- }
- return mostHated;
- }
- +
- + /**
- + * Return the 2 most hated L2Character of the L2Attackable _aggroList.<BR><BR>
- + */
- + public List<L2Character> get2MostHated()
- + {
- + if (getAggroListRP().isEmpty() || isAlikeDead()) return null;
- + L2Character mostHated = null;
- + L2Character secondMostHated = null;
- + int maxHate = 0;
- + List<L2Character> result = new FastList<L2Character>();
- +
- + // While Interating over This Map Removing Object is Not Allowed
- + synchronized (getAggroList())
- + {
- + // Go through the aggroList of the L2Attackable
- + for (AggroInfo ai : getAggroListRP().values())
- + {
- + if (ai == null) continue;
- + if (ai._attacker.isAlikeDead() || !getKnownList().knowsObject(ai._attacker) ||!ai._attacker.isVisible())
- + ai._hate = 0;
- + if (ai._hate > maxHate)
- + {
- + secondMostHated = mostHated;
- + mostHated = ai._attacker;
- + maxHate = ai._hate;
- + }
- + }
- + }
- + result.add(mostHated);
- + if (getAttackByList().contains(secondMostHated))
- + result.add(secondMostHated);
- + else result.add(null);
- + return result;
- + }
- +
- /**
- * Return the hate level of the L2Attackable against this L2Character contained in _aggroList.<BR><BR>
- *
- Index: java/net/sf/l2j/gameserver/model/NpcInventory.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/model/NpcInventory.java (revision 0)
- +++ java/net/sf/l2j/gameserver/model/NpcInventory.java (revision 1771)
- @@ -0,0 +1,89 @@
- +/* This program 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 2, or (at your option)
- + * any later version.
- + *
- + * This program 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, write to the Free Software
- + * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA
- + * 02111-1307, USA.
- + *
- + * http://www.gnu.org/copyleft/gpl.html
- + */
- +package net.sf.l2j.gameserver.model;
- +
- +import java.util.List;
- +
- +import javolution.util.FastList;
- +import net.sf.l2j.gameserver.model.L2ItemInstance.ItemLocation;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- +
- +public class NpcInventory extends Inventory
- +{
- + public static final int ADENA_ID = 57;
- + public static final int ANCIENT_ADENA_ID = 5575;
- +
- + private final L2NpcInstance _owner;
- +
- + public boolean sshotInUse = false;
- + public boolean bshotInUse = false;
- +
- + public NpcInventory(L2NpcInstance owner)
- + {
- + _owner = owner;
- + }
- +
- + public void Reset()
- + {
- + this.destroyAllItems("Reset", null, null);
- + if (_owner.getTemplate().ss > 0) this.addItem("Reset", 1835, _owner.getTemplate().ss, null, null);
- + if (_owner.getTemplate().bss > 0) this.addItem("Reset", 3947, _owner.getTemplate().bss, null, null);
- + }
- +
- + @Override
- + public L2NpcInstance getOwner() { return _owner; }
- + @Override
- + protected ItemLocation getBaseLocation() { return ItemLocation.NPC; }
- + @Override
- + protected ItemLocation getEquipLocation() { return ItemLocation.NPC; }
- +
- + /**
- + * Returns the list of all items in inventory that have a given item id.
- + * @return L2ItemInstance[] : matching items from inventory
- + */
- + public L2ItemInstance[] getAllItemsByItemId(int itemId)
- + {
- + List<L2ItemInstance> list = new FastList<L2ItemInstance>();
- + for (L2ItemInstance item : _items)
- + {
- + if (item.getItemId() == itemId)
- + list.add(item);
- + }
- +
- + return list.toArray(new L2ItemInstance[list.size()]);
- + }
- +
- + /**
- + * Refresh the weight of equipment loaded
- + */
- + @Override
- + public void refreshWeight()
- + {
- + // not needed
- + }
- +
- + /**
- + * Get back items in inventory from database
- + */
- + @Override
- + public void restore()
- + {
- + // not needed
- + }
- +
- +}
- Index: java/net/sf/l2j/gameserver/handler/skillhandlers/Continuous.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/skillhandlers/Continuous.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/handler/skillhandlers/Continuous.java (revision 1771)
- @@ -28,6 +28,7 @@
- import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- import net.sf.l2j.gameserver.model.actor.instance.L2ClanHallManagerInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PlayableInstance;
- import net.sf.l2j.gameserver.network.SystemMessageId;
- @@ -162,6 +163,11 @@
- activeSummon.setChargedSoulShot(L2ItemInstance.CHARGED_NONE);
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- acted = Formulas.getInstance().calcSkillSuccess(activeChar, target, skill, ss, sps, bss);
- }
- Index: java/net/sf/l2j/gameserver/handler/skillhandlers/Mdam.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/skillhandlers/Mdam.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/handler/skillhandlers/Mdam.java (revision 1771)
- @@ -97,6 +97,11 @@
- activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- for (int index = 0; index < targets.length; index++)
- {
- Index: java/net/sf/l2j/gameserver/handler/skillhandlers/Disablers.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/skillhandlers/Disablers.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/handler/skillhandlers/Disablers.java (revision 1771)
- @@ -31,6 +31,7 @@
- import net.sf.l2j.gameserver.model.L2Skill;
- import net.sf.l2j.gameserver.model.L2Summon;
- import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2SiegeSummonInstance;
- import net.sf.l2j.gameserver.model.base.Experience;
- @@ -152,6 +153,11 @@
- }
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- for (int index = 0; index < targets.length; index++)
- {
- Index: java/net/sf/l2j/gameserver/handler/skillhandlers/CpDam.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/handler/skillhandlers/CpDam.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/handler/skillhandlers/CpDam.java (revision 1771)
- @@ -21,6 +21,7 @@
- import net.sf.l2j.gameserver.model.L2Skill;
- import net.sf.l2j.gameserver.model.L2Summon;
- import net.sf.l2j.gameserver.model.L2Skill.SkillType;
- +import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- import net.sf.l2j.gameserver.skills.Formulas;
- @@ -85,6 +86,11 @@
- activeSummon.setChargedSpiritShot(L2ItemInstance.CHARGED_NONE);
- }
- }
- + else if (activeChar instanceof L2NpcInstance)
- + {
- + bss = ((L2NpcInstance)activeChar).isUsingShot(false);
- + ss = ((L2NpcInstance)activeChar).isUsingShot(true);
- + }
- for (int index = 0; index < targets.length; index++)
- {
- Index: java/net/sf/l2j/gameserver/ai/L2AttackableAI.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/ai/L2AttackableAI.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/ai/L2AttackableAI.java (revision 1771)
- @@ -18,8 +18,11 @@
- import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
- import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
- +import java.util.List;
- import java.util.concurrent.Future;
- +import javolution.util.FastList;
- +
- import net.sf.l2j.Config;
- import net.sf.l2j.gameserver.GameTimeController;
- import net.sf.l2j.gameserver.GeoData;
- @@ -29,7 +32,6 @@
- import net.sf.l2j.gameserver.model.L2Attackable;
- import net.sf.l2j.gameserver.model.L2CharPosition;
- import net.sf.l2j.gameserver.model.L2Character;
- -import net.sf.l2j.gameserver.model.L2Effect;
- import net.sf.l2j.gameserver.model.L2Object;
- import net.sf.l2j.gameserver.model.L2Skill;
- import net.sf.l2j.gameserver.model.L2Summon;
- @@ -37,17 +39,17 @@
- import net.sf.l2j.gameserver.model.actor.instance.L2FestivalMonsterInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2FolkInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2FriendlyMobInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2GrandBossInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2GuardInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2MinionInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2MonsterInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- -import net.sf.l2j.gameserver.model.actor.instance.L2PenaltyMonsterInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2RaidBossInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2RiftInvaderInstance;
- -import net.sf.l2j.gameserver.templates.L2Weapon;
- -import net.sf.l2j.gameserver.templates.L2WeaponType;
- +import net.sf.l2j.gameserver.taskmanager.DecayTaskManager;
- import net.sf.l2j.util.Rnd;
- +import net.sf.l2j.gameserver.util.Util;
- /**
- * This class manages AI of L2Attackable.<BR><BR>
- @@ -73,7 +75,12 @@
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
- -
- +
- + /** For attack AI, analysis of mob and its targets */
- + private SelfAnalysis _selfAnalysis = new SelfAnalysis();
- + private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
- + private TargetAnalysis _secondMostHatedAnalysis = new TargetAnalysis();
- +
- /**
- * Constructor of L2AttackableAI.<BR><BR>
- *
- @@ -83,7 +90,7 @@
- public L2AttackableAI(L2Character.AIAccessor accessor)
- {
- super(accessor);
- -
- + _selfAnalysis.Init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- }
- @@ -161,8 +168,7 @@
- if (!(me instanceof L2RaidBossInstance) && ((L2PcInstance)target).isSilentMoving())
- return false;
- - // Check if player is an ally //TODO! [Nemesiss] it should be rather boolean or smth like that
- - // Comparing String isnt good idea!
- + // Check if player is an ally (comparing mem addr)
- if (me.getFactionId() == "varka" && ((L2PcInstance)target).isAlliedWithVarka())
- return false;
- if (me.getFactionId() == "ketra" && ((L2PcInstance)target).isAlliedWithKetra())
- @@ -180,6 +186,9 @@
- && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(me.getX(), me.getY(), me.getZ()))
- return false;
- }
- +
- + if (_selfAnalysis.cannotMoveOnLand && !target.isInsideZone(L2Character.ZONE_WATER))
- + return false;
- }
- // Check if the actor is a L2GuardInstance
- @@ -318,6 +327,30 @@
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + // self and buffs
- + if (_selfAnalysis.lastBuffTick+100 < GameTimeController.getGameTicks())
- + {
- + for (L2Skill sk : _selfAnalysis.buffSkills)
- + {
- + if (_actor.getFirstEffect(sk.getId()) == null)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + // no clan buffs here?
- + if (sk.getTargetType() == L2Skill.SkillTargetType.TARGET_CLAN)
- + continue;
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(_actor);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + // forcing long reuse delay so if cast get interrupted or there would be several buffs, doesn't cast again
- + _selfAnalysis.lastBuffTick = GameTimeController.getGameTicks();
- + _actor.setTarget(OldTarget);
- + }
- + }
- + }
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- super.onIntentionAttack(target);
- }
- @@ -380,7 +413,7 @@
- // Chose a target from its aggroList
- L2Character hated;
- - if (_actor.isConfused()) hated = getAttackTarget(); // Force mobs to attak anybody if confused
- + if (_actor.isConfused()) hated = getAttackTarget(); // effect handles selection
- else hated = npc.getMostHated();
- // Order to the L2Attackable to attack the target
- @@ -437,18 +470,54 @@
- moveTo(x1, y1, z1);
- return;
- }
- + else if (Rnd.nextInt(RANDOM_WALK_RATE) == 0)
- + {
- + // self and clan buffs
- + for (L2Skill sk : _selfAnalysis.buffSkills)
- + {
- + if (_actor.getFirstEffect(sk.getId()) == null)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(_actor);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- + }
- + }
- + }
- }
- // Order to the L2MonsterInstance to random walk (1/100)
- else if (npc.getSpawn() != null && Rnd.nextInt(RANDOM_WALK_RATE) == 0)
- {
- int x1, y1, z1;
- -
- + int range = Config.MAX_DRIFT_RANGE;
- +
- + // self and clan buffs
- + for (L2Skill sk : _selfAnalysis.buffSkills)
- + {
- + if (_actor.getFirstEffect(sk.getId()) == null)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(_actor);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- + }
- + }
- +
- // If NPC with random coord in territory
- if (npc.getSpawn().getLocx() == 0 && npc.getSpawn().getLocy() == 0)
- {
- - // If NPC with random fixed coord, don't move
- - if (Territory.getInstance().getProcMax(npc.getSpawn().getLocation()) > 0) return;
- -
- // Calculate a destination point in the spawn area
- int p[] = Territory.getInstance().getRandomPoint(npc.getSpawn().getLocation());
- x1 = p[0];
- @@ -458,21 +527,23 @@
- // Calculate the distance between the current position of the L2Character and the target (x,y)
- double distance2 = _actor.getPlanDistanceSq(x1, y1);
- - if (distance2 > Config.MAX_DRIFT_RANGE * Config.MAX_DRIFT_RANGE)
- + if (distance2 > range * range)
- {
- npc.setisReturningToSpawnPoint(true);
- - float delay = (float) Math.sqrt(distance2) / Config.MAX_DRIFT_RANGE;
- + float delay = (float) Math.sqrt(distance2) / range;
- x1 = _actor.getX() + (int) ((x1 - _actor.getX()) / delay);
- y1 = _actor.getY() + (int) ((y1 - _actor.getY()) / delay);
- }
- +
- + // If NPC with random fixed coord, don't move (unless needs to return to spawnpoint)
- + if (Territory.getInstance().getProcMax(npc.getSpawn().getLocation()) > 0 && !npc.isReturningToSpawnPoint())
- + return;
- }
- else
- {
- // If NPC with fixed coord
- - x1 = npc.getSpawn().getLocx() + Rnd.nextInt(Config.MAX_DRIFT_RANGE * 2)
- - - Config.MAX_DRIFT_RANGE;
- - y1 = npc.getSpawn().getLocy() + Rnd.nextInt(Config.MAX_DRIFT_RANGE * 2)
- - - Config.MAX_DRIFT_RANGE;
- + x1 = npc.getSpawn().getLocx() + Rnd.nextInt(range * 2) - range;
- + y1 = npc.getSpawn().getLocy() + Rnd.nextInt(range * 2) - range;
- z1 = npc.getZ();
- }
- @@ -480,9 +551,6 @@
- // Move the actor to Location (x,y,z) server side AND client side by sending Server->Client packet CharMoveToLocation (broadcast)
- moveTo(x1, y1, z1);
- }
- -
- - return;
- -
- }
- /**
- @@ -527,234 +595,726 @@
- setIntention(AI_INTENTION_ACTIVE);
- _actor.setWalking();
- + return;
- }
- - else
- +
- + // Handle all L2Object of its Faction inside the Faction Range
- + if (((L2NpcInstance) _actor).getFactionId() != null)
- {
- - // Call all L2Object of its Faction inside the Faction Range
- - if (((L2NpcInstance) _actor).getFactionId() != null)
- - {
- - String faction_id = ((L2NpcInstance) _actor).getFactionId();
- + String faction_id = ((L2NpcInstance) _actor).getFactionId();
- - // Go through all L2Object that belong to its faction
- - for (L2Object obj : _actor.getKnownList().getKnownObjects().values())
- - {
- - if (obj instanceof L2NpcInstance)
- - {
- - L2NpcInstance npc = (L2NpcInstance) obj;
- + // Go through all L2Object that belong to its faction
- + for (L2Object obj : _actor.getKnownList().getKnownObjects().values())
- + {
- + if (obj instanceof L2NpcInstance)
- + {
- + L2NpcInstance npc = (L2NpcInstance) obj;
- - if (npc == null || getAttackTarget() == null || faction_id != npc.getFactionId())
- - continue;
- + if (npc == null || getAttackTarget() == null || faction_id != npc.getFactionId())
- + continue;
- - // Check if the L2Object is inside the Faction Range of the actor
- - if (_actor.isInsideRadius(npc, npc.getFactionRange(), true, false)
- - && GeoData.getInstance().canSeeTarget(_actor, npc)
- - && Math.abs(getAttackTarget().getZ() - npc.getZ()) < 600
- - && npc.getAI() != null
- - && _actor.getAttackByList().contains(getAttackTarget())
- - && (npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE
- - || npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- - {
- - if (getAttackTarget() instanceof L2PcInstance
- - && getAttackTarget().isInParty()
- - && getAttackTarget().getParty().isInDimensionalRift())
- - {
- - byte riftType = getAttackTarget().getParty().getDimensionalRift().getType();
- - byte riftRoom = getAttackTarget().getParty().getDimensionalRift().getCurrentRoom();
- + // Check if the L2Object is inside the Faction Range of the actor
- + if (_actor.isInsideRadius(npc, npc.getFactionRange()+npc.getTemplate().collisionRadius, true, false)
- + && npc.getAI() != null
- + )
- + {
- + if (Math.abs(getAttackTarget().getZ() - npc.getZ()) < 600
- + && _actor.getAttackByList().contains(getAttackTarget())
- + && (npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE
- + || npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE)
- + && GeoData.getInstance().canSeeTarget(_actor, npc))
- + {
- + if (getAttackTarget() instanceof L2PcInstance
- + && getAttackTarget().isInParty()
- + && getAttackTarget().getParty().isInDimensionalRift())
- + {
- + byte riftType = getAttackTarget().getParty().getDimensionalRift().getType();
- + byte riftRoom = getAttackTarget().getParty().getDimensionalRift().getCurrentRoom();
- - if (_actor instanceof L2RiftInvaderInstance
- - && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(npc.getX(), npc.getY(), npc.getZ()))
- - continue;
- - }
- + if (_actor instanceof L2RiftInvaderInstance
- + && !DimensionalRiftManager.getInstance().getRoom(riftType, riftRoom).checkIfInZone(npc.getX(), npc.getY(), npc.getZ()))
- + continue;
- + }
- - // Notify the L2Object AI with EVT_AGGRESSION
- - npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
- - }
- - }
- - }
- + // Notify the L2Object AI with EVT_AGGRESSION
- + npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
- + }
- + // heal or resurrect friends
- + if (_selfAnalysis.hasHealOrResurrect && !_actor.isAttackingDisabled()
- + && npc.getCurrentHp() < npc.getMaxHp()*0.6
- + && _actor.getCurrentHp() > _actor.getMaxHp()/2
- + && _actor.getCurrentMp() > _actor.getMaxMp()/2
- +
- + )
- + {
- + if (npc.isDead() && _actor instanceof L2MinionInstance)
- + {
- + if (((L2MinionInstance)_actor).getLeader() == npc)
- + {
- + for (L2Skill sk : _selfAnalysis.resurrectSkills)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- + continue;
- +
- + if (10 >= Rnd.get(100)) // chance
- + continue;
- + if (!GeoData.getInstance().canSeeTarget(_actor, npc))
- + break;
- +
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(npc);
- + // would this ever be fast enough for the decay not to run?
- + // giving some extra seconds
- + DecayTaskManager.getInstance().cancelDecayTask(npc);
- + DecayTaskManager.getInstance().addDecayTask(npc);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- + }
- + }
- + }
- + else if (npc.isInCombat())
- + {
- + for (L2Skill sk : _selfAnalysis.healSkills)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- + continue;
- +
- + int chance = 4;
- + if (_actor instanceof L2MinionInstance)
- + {
- + // minions support boss
- + if (((L2MinionInstance)_actor).getLeader() == npc)
- + chance = 6;
- + else chance = 3;
- + }
- + if (npc instanceof L2GrandBossInstance)
- + chance = 6;
- + if (chance >= Rnd.get(100)) // chance
- + continue;
- + if (!GeoData.getInstance().canSeeTarget(_actor, npc))
- + break;
- +
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(npc);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- + }
- + }
- + }
- + }
- + }
- }
- + }
- - if(_actor.isAttackingDisabled()) return;
- + if(_actor.isAttackingDisabled()) return;
- - // Get all information needed to chose between physical or magical attack
- - L2Skill[] skills = null;
- - double dist2 = 0;
- - int range = 0;
- + // Get 2 most hated chars
- + List<L2Character> hated = ((L2Attackable) _actor).get2MostHated();
- + if (_actor.isConfused())
- + {
- + if (hated != null)
- + hated.set(0, getAttackTarget()); // effect handles selection
- + else
- + {
- + hated = new FastList<L2Character>();
- + hated.add(getAttackTarget());
- + hated.add(null);
- + }
- + }
- - try
- + if (hated == null || hated.get(0) == null)
- + {
- + setIntention(AI_INTENTION_ACTIVE);
- + return;
- + }
- + // Reconsider target if _actor hasn't got hits in for last 14 sec
- + if (!_actor.isMuted()
- + && _attackTimeout - 160 < GameTimeController.getGameTicks()
- + && hated.get(1) != null)
- + {
- + if (Util.checkIfInRange(900, _actor, hated.get(1), true))
- + {
- + // take off 2* the amount the aggro is larger than second most
- + ((L2Attackable) _actor).reduceHate(hated.get(0), 2 * (((L2Attackable) _actor).getHating(hated.get(0)) - ((L2Attackable) _actor).getHating(hated.get(1))));
- + // Calculate a new attack timeout
- + _attackTimeout = MAX_ATTACK_TIMEOUT
- + + GameTimeController.getGameTicks();
- + }
- + }
- + if (hated.get(0) != getAttackTarget())
- + {
- + setAttackTarget(hated.get(0));
- + }
- + _mostHatedAnalysis.Update(hated.get(0));
- + _secondMostHatedAnalysis.Update(hated.get(1));
- +
- + // Get all information needed to choose between physical or magical attack
- + _actor.setTarget(_mostHatedAnalysis.character);
- + double dist2 = _actor.getPlanDistanceSq(_mostHatedAnalysis.character.getX(), _mostHatedAnalysis.character.getY());
- + int combinedCollision = _actor.getTemplate().collisionRadius + _mostHatedAnalysis.character.getTemplate().collisionRadius;
- + int range = _actor.getPhysicalAttackRange() + combinedCollision;
- +
- + // Considering, if bigger range will be attempted
- + if ( (dist2 < 10000 + combinedCollision*combinedCollision)
- + && !_selfAnalysis.isFighter && !_selfAnalysis.isBalanced
- + && (_selfAnalysis.hasLongRangeSkills || _selfAnalysis.isArcher)
- + && (_mostHatedAnalysis.isBalanced || _mostHatedAnalysis.isFighter)
- + && (_mostHatedAnalysis.character.isRooted() || _mostHatedAnalysis.isSlower)
- + && (Config.GEODATA == 2 ? 20 : 12) >= Rnd.get(100) // chance
- + )
- + {
- + int posX = _actor.getX();
- + int posY = _actor.getY();
- + int posZ = _actor.getZ();
- + double distance = Math.sqrt(dist2); // This way, we only do the sqrt if we need it
- +
- + int signx=-1;
- + int signy=-1;
- + if (_actor.getX()>_mostHatedAnalysis.character.getX())
- + signx=1;
- + if (_actor.getY()>_mostHatedAnalysis.character.getY())
- + signy=1;
- + posX += Math.round((float)((signx * ((range / 2) + (Rnd.get(range)))) - distance));
- + posY += Math.round((float)((signy * ((range / 2) + (Rnd.get(range)))) - distance));
- + setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(posX, posY, posZ, 0));
- + return;
- + }
- +
- + // Cannot see target, needs to go closer, currently just goes to range 300 if mage
- + if ((dist2 > 310*310 + combinedCollision*combinedCollision)
- + && this._selfAnalysis.hasLongRangeSkills
- + && !GeoData.getInstance().canSeeTarget(_actor, _mostHatedAnalysis.character)
- + )
- + {
- + if (!(_selfAnalysis.isMage && _actor.isMuted()))
- + {
- + moveToPawn(_mostHatedAnalysis.character, 300);
- + return;
- + }
- + }
- +
- + if (_mostHatedAnalysis.character.isMoving()) range += 50;
- + // Check if the actor is far from target
- + if (dist2 > range*range)
- + {
- + if (!_actor.isMuted() && (_selfAnalysis.hasLongRangeSkills || !_selfAnalysis.healSkills.isEmpty()))
- + {
- + // check for long ranged skills and heal/buff skills
- + if (!_mostHatedAnalysis.isCanceled)
- + {
- + for (L2Skill sk : _selfAnalysis.cancelSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= 8)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _mostHatedAnalysis.isCanceled = true;
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- + if (this._selfAnalysis.lastDebuffTick+60 < GameTimeController.getGameTicks())
- + {
- + for (L2Skill sk : _selfAnalysis.debuffSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- + int chance = 8;
- + if (_selfAnalysis.isFighter && _mostHatedAnalysis.isMage)
- + chance = 3;
- + if (_selfAnalysis.isFighter && _mostHatedAnalysis.isArcher)
- + chance = 12;
- + if (_selfAnalysis.isMage && !_mostHatedAnalysis.isMage)
- + chance = 10;
- + if (_mostHatedAnalysis.isMagicResistant) chance /= 2;
- +
- + if (Rnd.nextInt(100) <= chance)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _selfAnalysis.lastDebuffTick = GameTimeController.getGameTicks();
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- + if (!_mostHatedAnalysis.character.isMuted())
- + {
- + int chance = 8;
- + if (!(_mostHatedAnalysis.isMage || _mostHatedAnalysis.isBalanced))
- + chance = 3;
- + for (L2Skill sk : _selfAnalysis.muteSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= chance)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- + if (_secondMostHatedAnalysis.character != null && !_secondMostHatedAnalysis.character.isMuted() && (_secondMostHatedAnalysis.isMage || _secondMostHatedAnalysis.isBalanced))
- + {
- + double secondHatedDist2 = _actor.getPlanDistanceSq(_secondMostHatedAnalysis.character.getX(), _secondMostHatedAnalysis.character.getY());
- + for (L2Skill sk : _selfAnalysis.muteSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (secondHatedDist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= 2)
- + {
- + _actor.setTarget(_secondMostHatedAnalysis.character);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(_mostHatedAnalysis.character);
- + return;
- + }
- + }
- + }
- + if (!_mostHatedAnalysis.character.isSleeping())
- + {
- + for (L2Skill sk : _selfAnalysis.sleepSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= 1)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- + if (_secondMostHatedAnalysis.character != null && !_secondMostHatedAnalysis.character.isSleeping())
- + {
- + double secondHatedDist2 = _actor.getPlanDistanceSq(_secondMostHatedAnalysis.character.getX(), _secondMostHatedAnalysis.character.getY());
- + for (L2Skill sk : _selfAnalysis.sleepSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (secondHatedDist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= 3)
- + {
- + _actor.setTarget(_secondMostHatedAnalysis.character);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(_mostHatedAnalysis.character);
- + return;
- + }
- + }
- + }
- + if (!_mostHatedAnalysis.character.isRooted())
- + {
- + for (L2Skill sk : _selfAnalysis.rootSkills)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= (_mostHatedAnalysis.isSlower ? 3 : 8));
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- + if (!_mostHatedAnalysis.character.isAttackingDisabled())
- + {
- + for (L2Skill sk : _selfAnalysis.generalDisablers)
- + {
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- + if (Rnd.nextInt(100) <= ((_selfAnalysis.isFighter && _actor.isRooted()) ? 15 : 7))
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- + if (_actor.getCurrentHp() < _actor.getMaxHp()*0.4)
- + {
- + for (L2Skill sk : _selfAnalysis.healSkills)
- + {
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + )
- + continue;
- + int chance = 7;
- + if (_mostHatedAnalysis.character.isAttackingDisabled()) chance += 10;
- + if (_secondMostHatedAnalysis.character == null || _secondMostHatedAnalysis.character.isAttackingDisabled()) chance += 10;
- + if (Rnd.nextInt(100) <= chance)
- + {
- + _actor.setTarget(_actor);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(_mostHatedAnalysis.character);
- + return;
- + }
- + }
- + }
- +
- + // chance decision for launching long range skills
- + int castingChance = 5;
- + if (_selfAnalysis.isMage) castingChance = 50; // mages
- + if (_selfAnalysis.isBalanced)
- + {
- + if (!_mostHatedAnalysis.isFighter) // advance to mages
- + castingChance = 15;
- + else
- + castingChance = 25; // stay away from fighters
- + }
- + if (_selfAnalysis.isFighter)
- + {
- + if(_mostHatedAnalysis.isMage)
- + castingChance = 3;
- + else
- + castingChance = 7;
- + if (_actor.isRooted())
- + castingChance = 20; // doesn't matter if no success first round
- + }
- + for (L2Skill sk : _selfAnalysis.generalSkills)
- + {
- +
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange)
- + )
- + continue;
- +
- + if (Rnd.nextInt(100) <= castingChance)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + return;
- + }
- + }
- + }
- +
- + // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)
- + if (_selfAnalysis.isMage)
- + {
- + if (_actor.isMuted()) return;
- + range = _selfAnalysis.maxCastRange;
- + }
- + if (_mostHatedAnalysis.character.isMoving()) range -= 100; if (range < 5) range = 5;
- + moveToPawn(getAttackTarget(), range);
- + return;
- + }
- + // **************************************************
- + // Else, if this is close enough for physical attacks
- + else
- + {
- + // In case many mobs are trying to hit from same place, move a bit,
- + // circling around the target
- + if (Rnd.nextInt(100) <= 33) // check it once per 3 seconds
- + {
- + for (L2Object nearby : _actor.getKnownList().getKnownCharactersInRadius(20))
- + {
- + if (nearby instanceof L2Attackable
- + && nearby != getAttackTarget())
- + {
- + int diffx = Rnd.get(combinedCollision, combinedCollision+40);
- + if (Rnd.get(10)<5) diffx = -diffx;
- + int diffy = Rnd.get(combinedCollision, combinedCollision+40);
- + if (Rnd.get(10)<5) diffy = -diffy;
- + moveTo(getAttackTarget().getX() + diffx, getAttackTarget().getY()
- + + diffy, getAttackTarget().getZ());
- + return;
- + }
- + }
- + }
- +
- + // Calculate a new attack timeout.
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- +
- + // check for close combat skills && heal/buff skills
- +
- + if (!_mostHatedAnalysis.isCanceled)
- {
- - _actor.setTarget(getAttackTarget());
- - skills = _actor.getAllSkills();
- - dist2 = _actor.getPlanDistanceSq(getAttackTarget().getX(), getAttackTarget().getY());
- - range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + getAttackTarget().getTemplate().collisionRadius;
- + for (L2Skill sk : _selfAnalysis.cancelSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange))
- + continue;
- + if (Rnd.nextInt(100) <= 8)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _mostHatedAnalysis.isCanceled = true;
- + return;
- + }
- + }
- + }
- + if (this._selfAnalysis.lastDebuffTick+60 < GameTimeController.getGameTicks())
- + {
- + for (L2Skill sk : _selfAnalysis.debuffSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange))
- + continue;
- + int chance = 5;
- + if (_selfAnalysis.isFighter && _mostHatedAnalysis.isMage)
- + chance = 3;
- + if (_selfAnalysis.isFighter && _mostHatedAnalysis.isArcher)
- + chance = 3;
- + if (_selfAnalysis.isMage && !_mostHatedAnalysis.isMage)
- + chance = 4;
- + if (_mostHatedAnalysis.isMagicResistant) chance /= 2;
- + if (sk.getCastRange() < 200) chance += 3;
- + if (Rnd.nextInt(100) <= chance)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _selfAnalysis.lastDebuffTick = GameTimeController.getGameTicks();
- + return;
- + }
- + }
- }
- - catch (NullPointerException e)
- + if (!_mostHatedAnalysis.character.isMuted() && (_mostHatedAnalysis.isMage || _mostHatedAnalysis.isBalanced))
- {
- - //_log.warning("AttackableAI: Attack target is NULL.");
- - setIntention(AI_INTENTION_ACTIVE);
- - return;
- + for (L2Skill sk : _selfAnalysis.muteSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange))
- + continue;
- + if (Rnd.nextInt(100) <= 7)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + return;
- + }
- + }
- + }
- + if (_secondMostHatedAnalysis.character != null && !_secondMostHatedAnalysis.character.isMuted() && (_secondMostHatedAnalysis.isMage || _secondMostHatedAnalysis.isBalanced))
- + {
- + double secondHatedDist2 = _actor.getPlanDistanceSq(_secondMostHatedAnalysis.character.getX(), _secondMostHatedAnalysis.character.getY());
- + for (L2Skill sk : _selfAnalysis.muteSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (secondHatedDist2 > castRange * castRange))
- + continue;
- + if (Rnd.nextInt(100) <= 3)
- + {
- + _actor.setTarget(_secondMostHatedAnalysis.character);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(_mostHatedAnalysis.character);
- + return;
- + }
- + }
- }
- -
- - L2Weapon weapon = _actor.getActiveWeaponItem();
- - if (weapon != null && weapon.getItemType() == L2WeaponType.BOW)
- + if (_secondMostHatedAnalysis.character != null && !_secondMostHatedAnalysis.character.isSleeping())
- {
- - // Micht: kepping this one otherwise we should do 2 sqrt
- - double distance2 = _actor.getPlanDistanceSq(getAttackTarget().getX(), getAttackTarget().getY());
- - if (distance2 <= 10000)
- - {
- - int chance = 5;
- - if (chance >= Rnd.get(100))
- - {
- - int posX = _actor.getX();
- - int posY = _actor.getY();
- - int posZ = _actor.getZ();
- - double distance = Math.sqrt(distance2); // This way, we only do the sqrt if we need it
- -
- - int signx=-1;
- - int signy=-1;
- - if (_actor.getX()>getAttackTarget().getX())
- - signx=1;
- - if (_actor.getY()>getAttackTarget().getY())
- - signy=1;
- - posX += Math.round((float)((signx * ((range / 2) + (Rnd.get(range)))) - distance));
- - posY += Math.round((float)((signy * ((range / 2) + (Rnd.get(range)))) - distance));
- - setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, new L2CharPosition(posX, posY, posZ, 0));
- - return;
- - }
- - }
- + double secondHatedDist2 = _actor.getPlanDistanceSq(_secondMostHatedAnalysis.character.getX(), _secondMostHatedAnalysis.character.getY());
- + for (L2Skill sk : _selfAnalysis.sleepSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (secondHatedDist2 > castRange * castRange))
- + continue;
- + if (Rnd.nextInt(100) <= 4)
- + {
- + _actor.setTarget(_secondMostHatedAnalysis.character);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(_mostHatedAnalysis.character);
- + return;
- + }
- + }
- }
- -
- - // Force mobs to attack anybody if confused
- - L2Character hated;
- - if (_actor.isConfused()) hated = getAttackTarget();
- - else hated = ((L2Attackable) _actor).getMostHated();
- -
- - if (hated == null)
- + if (!_mostHatedAnalysis.character.isRooted() && _mostHatedAnalysis.isFighter && !_selfAnalysis.isFighter)
- {
- - setIntention(AI_INTENTION_ACTIVE);
- - return;
- + for (L2Skill sk : _selfAnalysis.rootSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange))
- + continue;
- + if (Rnd.nextInt(100) <= 4)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + return;
- + }
- + }
- }
- - if (hated != getAttackTarget())
- + if (!_mostHatedAnalysis.character.isAttackingDisabled())
- {
- - setAttackTarget(hated);
- + for (L2Skill sk : _selfAnalysis.generalDisablers)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange))
- + continue;
- + if (Rnd.nextInt(100) <= ((sk.getCastRange() < 200) ? 10 : 7))
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + return;
- + }
- + }
- }
- - // We should calculate new distance cuz mob can have changed the target
- - dist2 = _actor.getPlanDistanceSq(hated.getX(), hated.getY());
- -
- - if (hated.isMoving()) range += 50;
- - // Check if the actor isn't far from target
- - if (dist2 > range*range)
- + if (_actor.getCurrentHp() < _actor.getMaxHp()*0.4)
- {
- - // check for long ranged skills and heal/buff skills
- - if (!_actor.isMuted() &&
- - (!Config.ALT_GAME_MOB_ATTACK_AI || (_actor instanceof L2MonsterInstance && Rnd.nextInt(100) <= 5))
- - )
- - for (L2Skill sk : skills)
- - {
- - int castRange = sk.getCastRange();
- -
- - if (((sk.getSkillType() == L2Skill.SkillType.BUFF || sk.getSkillType() == L2Skill.SkillType.HEAL) || (dist2 >= castRange * castRange / 9.0)
- - && (dist2 <= castRange * castRange) && (castRange > 70))
- - && !_actor.isSkillDisabled(sk.getId())
- - && _actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)
- - && !sk.isPassive()
- - && Rnd.nextInt(100) <= 5)
- - {
- - L2Object OldTarget = _actor.getTarget();
- - if (sk.getSkillType() == L2Skill.SkillType.BUFF
- - || sk.getSkillType() == L2Skill.SkillType.HEAL)
- - {
- - boolean useSkillSelf = true;
- - if (sk.getSkillType() == L2Skill.SkillType.HEAL
- - && _actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))
- - {
- - useSkillSelf = false;
- - break;
- - }
- - if (sk.getSkillType() == L2Skill.SkillType.BUFF)
- - {
- - L2Effect[] effects = _actor.getAllEffects();
- - for (int i = 0; effects != null && i < effects.length; i++)
- - {
- - L2Effect effect = effects[i];
- - if (effect.getSkill() == sk)
- - {
- - useSkillSelf = false;
- - break;
- - }
- - }
- - }
- - if (useSkillSelf) _actor.setTarget(_actor);
- - }
- -
- - clientStopMoving(null);
- - _accessor.doCast(sk);
- - _actor.setTarget(OldTarget);
- - return;
- - }
- - }
- -
- - // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)
- - if (hated.isMoving()) range -= 100; if (range < 5) range = 5;
- - moveToPawn(getAttackTarget(), range);
- - return;
- + for (L2Skill sk : _selfAnalysis.healSkills)
- + {
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk))
- + continue;
- + int chance = 7;
- + if (_mostHatedAnalysis.character.isAttackingDisabled()) chance += 10;
- + if (_secondMostHatedAnalysis.character == null || _secondMostHatedAnalysis.character.isAttackingDisabled()) chance += 10;
- + if (Rnd.nextInt(100) <= chance)
- + {
- + _actor.setTarget(_actor);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(_mostHatedAnalysis.character);
- + return;
- + }
- + }
- }
- - // Else, if this is close enough to attack
- - else
- + for (L2Skill sk : _selfAnalysis.generalSkills)
- {
- - _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- -
- - // check for close combat skills && heal/buff skills
- - if (!_actor.isMuted() /*&& _rnd.nextInt(100) <= 5*/)
- + if ((_actor.isMuted() && sk.isMagic())
- + || (_actor.isPsychicalMuted() && !sk.isMagic()))
- + continue;
- + int castRange = sk.getCastRange() + combinedCollision;
- + if (_actor.isSkillDisabled(sk.getId())
- + || _actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)
- + || (dist2 > castRange * castRange))
- + continue;
- +
- + // chance decision for launching general skills in melee fight
- + // close range skills should be higher, long range lower
- + int castingChance = 5;
- + if (_selfAnalysis.isMage)
- {
- - boolean useSkillSelf = true;;
- - for (L2Skill sk : skills)
- - {
- - if (/*sk.getCastRange() >= dist && sk.getCastRange() <= 70 && */!sk.isPassive()
- - && _actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)
- - && !_actor.isSkillDisabled(sk.getId()) && (Rnd.nextInt(100) <= 8
- - || (_actor instanceof L2PenaltyMonsterInstance && Rnd.nextInt(100) <= 20)))
- - {
- - L2Object OldTarget = _actor.getTarget();
- - if (sk.getSkillType() == L2Skill.SkillType.BUFF
- - || sk.getSkillType() == L2Skill.SkillType.HEAL)
- - {
- - useSkillSelf = true;
- - if (sk.getSkillType() == L2Skill.SkillType.HEAL
- - && _actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))
- - {
- - useSkillSelf = false;
- - break;
- - }
- - if (sk.getSkillType() == L2Skill.SkillType.BUFF)
- - {
- - L2Effect[] effects = _actor.getAllEffects();
- - for (int i = 0; effects != null && i < effects.length; i++)
- - {
- - L2Effect effect = effects[i];
- - if (effect.getSkill() == sk)
- - {
- - useSkillSelf = false;
- - break;
- - }
- - }
- - }
- - if (useSkillSelf) _actor.setTarget(_actor);
- - }
- - // GeoData Los Check here
- - if (!useSkillSelf && !GeoData.getInstance().canSeeTarget(_actor, _actor.getTarget()))
- - return;
- - clientStopMoving(null);
- - _accessor.doCast(sk);
- - _actor.setTarget(OldTarget);
- - return;
- - }
- - }
- + if (sk.getCastRange() < 200) castingChance = 35;
- + else castingChance = 25; // mages
- }
- -
- - // Finally, physical attacks
- - clientStopMoving(null);
- - _accessor.doAttack(hated);
- + if (_selfAnalysis.isBalanced)
- + {
- + if (sk.getCastRange() < 200) castingChance = 12;
- + else
- + {
- + if (_mostHatedAnalysis.isMage) // hit mages
- + castingChance = 2;
- + else
- + castingChance = 5;
- + }
- +
- + }
- + if (_selfAnalysis.isFighter)
- + {
- + if (sk.getCastRange() < 200) castingChance = 12;
- + else
- + {
- + if(_mostHatedAnalysis.isMage)
- + castingChance = 1;
- + else
- + castingChance = 3;
- + }
- + }
- + if (Rnd.nextInt(100) <= castingChance)
- + {
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + return;
- + }
- }
- +
- + // Finally, physical attacks
- + clientStopMoving(null);
- + _accessor.doAttack(getAttackTarget());
- }
- }
- @@ -873,4 +1433,5 @@
- {
- _globalAggro = value;
- }
- +
- }
- Index: java/net/sf/l2j/gameserver/ai/L2CharacterAI.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/ai/L2CharacterAI.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/ai/L2CharacterAI.java (revision 1771)
- @@ -14,6 +14,8 @@
- */
- package net.sf.l2j.gameserver.ai;
- +import java.util.List;
- +import javolution.util.FastList;
- import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
- import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
- import static net.sf.l2j.gameserver.ai.CtrlIntention.AI_INTENTION_CAST;
- @@ -32,10 +34,17 @@
- import net.sf.l2j.gameserver.model.L2Skill;
- import net.sf.l2j.gameserver.model.actor.instance.L2BoatInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2GrandBossInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2RaidBossInstance;
- +import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance;
- import net.sf.l2j.gameserver.serverpackets.AutoAttackStop;
- import net.sf.l2j.gameserver.taskmanager.AttackStanceTaskManager;
- +import net.sf.l2j.gameserver.templates.L2NpcTemplate;
- +import net.sf.l2j.gameserver.templates.L2Weapon;
- +import net.sf.l2j.gameserver.templates.L2WeaponType;
- +import net.sf.l2j.util.Rnd;
- /**
- * This class manages AI of L2Character.<BR><BR>
- @@ -1030,4 +1039,202 @@
- }
- return false;
- }
- +
- + protected class SelfAnalysis
- + {
- + public boolean isMage = false;
- + public boolean isBalanced;
- + public boolean isArcher = false;
- + public boolean isFighter = false;
- + public boolean cannotMoveOnLand = false;
- + public List<L2Skill> generalSkills = new FastList<L2Skill>();
- + public List<L2Skill> buffSkills = new FastList<L2Skill>();
- + public int lastBuffTick = 0;
- + public List<L2Skill> debuffSkills = new FastList<L2Skill>();
- + public int lastDebuffTick = 0;
- + public List<L2Skill> cancelSkills = new FastList<L2Skill>();
- + public List<L2Skill> healSkills = new FastList<L2Skill>();
- + //public List<L2Skill> trickSkills = new FastList<L2Skill>();
- + public List<L2Skill> generalDisablers = new FastList<L2Skill>();
- + public List<L2Skill> sleepSkills = new FastList<L2Skill>();
- + public List<L2Skill> rootSkills = new FastList<L2Skill>();
- + public List<L2Skill> muteSkills = new FastList<L2Skill>();
- + public List<L2Skill> resurrectSkills = new FastList<L2Skill>();
- + public boolean hasHealOrResurrect = false;
- + public boolean hasLongRangeSkills = false;
- + public boolean hasLongRangeDamageSkills = false;
- + public int maxCastRange = 0;
- +
- + public SelfAnalysis()
- + {
- + }
- +
- + public void Init()
- + {
- + switch (((L2NpcTemplate)_actor.getTemplate()).AI)
- + {
- + case FIGHTER:
- + isFighter = true;
- + break;
- + case MAGE:
- + isMage = true;
- + break;
- + case BALANCED:
- + isBalanced = true;
- + break;
- + case ARCHER:
- + isArcher = true;
- + break;
- + default:
- + isFighter = true;
- + break;
- + }
- + // water movement analysis
- + if (_actor instanceof L2NpcInstance)
- + {
- + // well, lol..
- + if (((L2NpcInstance)_actor).getNpcId() == 20314) // great white shark
- + cannotMoveOnLand = true;
- + }
- + // skill analysis
- + for (L2Skill sk : _actor.getAllSkills())
- + {
- + int castRange = sk.getCastRange();
- + boolean isLongRangeDamageSkill = false;
- + switch(sk.getSkillType())
- + {
- + case HEAL:
- + case HEAL_PERCENT:
- + case HEAL_STATIC:
- + case BALANCE_LIFE:
- + case HOT:
- + healSkills.add(sk);
- + hasHealOrResurrect = true;
- + continue; // won't be considered something for fighting
- + case BUFF:
- + buffSkills.add(sk);
- + continue; // won't be considered something for fighting
- + case PARALYZE:
- + case STUN:
- + generalDisablers.add(sk);
- + break;
- + case MUTE:
- + muteSkills.add(sk);
- + break;
- + case SLEEP:
- + sleepSkills.add(sk);
- + break;
- + case ROOT:
- + rootSkills.add(sk);
- + break;
- + case FEAR: // could be used as an alternative for healing?
- + case CONFUSION:
- + // trickSkills.add(sk);
- + case DEBUFF:
- + debuffSkills.add(sk);
- + break;
- + case CANCEL:
- + case MAGE_BANE:
- + case WARRIOR_BANE:
- + case NEGATE:
- + cancelSkills.add(sk);
- + break;
- + case RESURRECT:
- + resurrectSkills.add(sk);
- + hasHealOrResurrect = true;
- + break;
- + case NOTDONE:
- + continue; // won't be considered something for fighting
- + default:
- + if (!sk.isPassive()) {
- + generalSkills.add(sk);
- + isLongRangeDamageSkill = true;
- + }
- + break;
- + }
- + if (castRange > 70) {
- + hasLongRangeSkills = true;
- + if (isLongRangeDamageSkill)
- + hasLongRangeDamageSkills = true;
- + }
- + if (castRange > maxCastRange) maxCastRange = castRange;
- +
- + }
- + // Because of missing skills, some mages/balanced cannot play like mages
- + if (!hasLongRangeDamageSkills && isMage)
- + {
- + isBalanced = true;
- + isMage = false;
- + isFighter = false;
- + }
- + if (!hasLongRangeSkills && (isMage || isBalanced))
- + {
- + isBalanced = false;
- + isMage = false;
- + isFighter = true;
- + }
- + if (generalSkills.isEmpty() && isMage)
- + {
- + isBalanced = true;
- + isMage = false;
- + }
- + }
- + }
- +
- + protected class TargetAnalysis
- + {
- + public L2Character character;
- + public boolean isMage;
- + public boolean isBalanced;
- + public boolean isArcher;
- + public boolean isFighter;
- + public boolean isCanceled;
- + public boolean isSlower;
- + public boolean isMagicResistant;
- +
- + public TargetAnalysis()
- + {
- + }
- +
- + public void Update(L2Character target)
- + {
- + // update status once in 4 seconds
- + if (target == character && Rnd.nextInt(100) > 25)
- + return;
- + character = target;
- + if (target == null)
- + return;
- + isMage = false;
- + isBalanced = false;
- + isArcher = false;
- + isFighter = false;
- + isCanceled = false;
- +
- + if (target.getMAtk(null, null) > 1.5*target.getPAtk(null))
- + isMage = true;
- + else if (target.getPAtk(null)*0.8 < target.getMAtk(null, null)
- + || target.getMAtk(null, null)*0.8 > target.getPAtk(null))
- + {
- + isBalanced = true;
- + }
- + else
- + {
- + L2Weapon weapon = target.getActiveWeaponItem();
- + if (weapon != null && (weapon.getItemType() == L2WeaponType.BOW || weapon.getItemType() == L2WeaponType.CROSSBOW))
- + isArcher = true;
- + else
- + isFighter = true;
- + }
- + if (target.getRunSpeed() < _actor.getRunSpeed()-3)
- + isSlower = true;
- + else
- + isSlower = false;
- + if (target.getMDef(null, null)*1.2 > _actor.getMAtk(null, null))
- + isMagicResistant = true;
- + else
- + isMagicResistant = false;
- + if (target.getBuffCount() < 4)
- + isCanceled = true;
- + }
- + }
- }
- Index: java/net/sf/l2j/gameserver/ai/L2SiegeGuardAI.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/ai/L2SiegeGuardAI.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/ai/L2SiegeGuardAI.java (revision 1771)
- @@ -32,10 +32,10 @@
- import net.sf.l2j.gameserver.model.L2Summon;
- import net.sf.l2j.gameserver.model.actor.instance.L2DoorInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2FolkInstance;
- -import net.sf.l2j.gameserver.model.actor.instance.L2MonsterInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2NpcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
- import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance;
- +import net.sf.l2j.gameserver.util.Util;
- import net.sf.l2j.util.Rnd;
- /**
- @@ -52,7 +52,11 @@
- /** The L2Attackable AI task executed every 1s (call onEvtThink method)*/
- private Future<?> _aiTask;
- - /** The delay after wich the attacked is stopped */
- + /** For attack AI, analysis of mob and its targets */
- + private SelfAnalysis _selfAnalysis = new SelfAnalysis();
- + //private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
- +
- + /** The delay after which the attacked is stopped */
- private int _attackTimeout;
- /** The L2Attackable aggro counter */
- @@ -72,10 +76,9 @@
- public L2SiegeGuardAI(L2Character.AIAccessor accessor)
- {
- super(accessor);
- -
- + _selfAnalysis.Init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- -
- _attackRange = ((L2Attackable) _actor).getPhysicalAttackRange();
- }
- @@ -125,9 +128,18 @@
- target instanceof L2SiegeGuardInstance ||
- target instanceof L2FolkInstance ||
- target instanceof L2DoorInstance ||
- - target.isAlikeDead() ||
- - target.isInvul()) return false;
- + target.isAlikeDead()) return false;
- + // Check if the target isn't invulnerable
- + if (target.isInvul())
- + {
- + // However EffectInvincible requires to check GMs specially
- + if (target instanceof L2PcInstance && ((L2PcInstance)target).isGM())
- + return false;
- + if (target instanceof L2Summon && ((L2Summon)target).getOwner().isGM())
- + return false;
- + }
- +
- // Get the owner if the target is a summon
- if (target instanceof L2Summon)
- {
- @@ -143,7 +155,7 @@
- if (((L2PcInstance) target).isSilentMoving()
- && !_actor.isInsideRadius(target, 250, false, false)) return false;
- }
- - // Los Check Here
- + // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoData.getInstance().canSeeTarget(_actor, target));
- }
- @@ -263,7 +275,8 @@
- L2Character hated;
- if (_actor.isConfused()) hated = _attackTarget; // Force mobs to attak anybody if confused
- else hated = npc.getMostHated();
- -
- + //_mostHatedAnalysis.Update(hated);
- +
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- @@ -286,12 +299,174 @@
- // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
- ((L2SiegeGuardInstance) _actor).returnHome();
- return;
- + }
- + /**
- + * Manage AI attack thinks of a L2Attackable (called by onEvtThink).<BR><BR>
- + *
- + * <B><U> Actions</U> :</B><BR><BR>
- + * <li>Update the attack timeout if actor is running</li>
- + * <li>If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE</li>
- + * <li>Call all L2Object of its Faction inside the Faction Range</li>
- + * <li>Chose a target and order to attack it with magic skill or physical attack</li><BR><BR>
- + *
- + * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- + *
- + */
- + private void thinkAttack()
- + {
- + if (Config.DEBUG)
- + _log.info("L2SiegeGuardAI.thinkAttack(); timeout="
- + + (_attackTimeout - GameTimeController.getGameTicks()));
- +
- + if (_attackTimeout < GameTimeController.getGameTicks())
- + {
- + // Check if the actor is running
- + if (_actor.isRunning())
- + {
- + // Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance
- + _actor.setWalking();
- +
- + // Calculate a new attack timeout
- + _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- + }
- + }
- +
- + // Check if target is dead or if timeout is expired to stop this attack
- + if (_attackTarget == null || _attackTarget.isAlikeDead()
- + || _attackTimeout < GameTimeController.getGameTicks())
- + {
- + // Stop hating this target after the attack timeout or if target is dead
- + if (_attackTarget != null)
- + {
- + L2Attackable npc = (L2Attackable) _actor;
- + npc.stopHating(_attackTarget);
- + }
- +
- + // Cancel target and timeout
- + _attackTimeout = Integer.MAX_VALUE;
- + _attackTarget = null;
- +
- + // Set the AI Intention to AI_INTENTION_ACTIVE
- + setIntention(AI_INTENTION_ACTIVE, null, null);
- +
- + _actor.setWalking();
- + return;
- + }
- +
- + factionNotifyAndSupport();
- + attackPrepare();
- }
- + private final void factionNotifyAndSupport()
- + {
- + L2Character target = getAttackTarget();
- + // Call all L2Object of its Faction inside the Faction Range
- + if (((L2NpcInstance) _actor).getFactionId() == null || target == null || _actor == null)
- + return;
- +
- + if (target.isInvul()) return; // speeding it up for siege guards
- +
- + String faction_id = ((L2NpcInstance) _actor).getFactionId();
- +
- + // Go through all L2Character that belong to its faction
- + //for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- + for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(1000))
- + {
- + if (cha == null) continue;
- +
- + if (!(cha instanceof L2NpcInstance))
- + {
- + if(_selfAnalysis.hasHealOrResurrect && cha instanceof L2PcInstance && ((L2NpcInstance) _actor).getCastle().getSiege().checkIsDefender(((L2PcInstance)cha).getClan()))
- + {
- + // heal friends
- + if (!_actor.isAttackingDisabled()
- + && cha.getCurrentHp() < cha.getMaxHp()*0.6
- + && _actor.getCurrentHp() > _actor.getMaxHp()/2
- + && _actor.getCurrentMp() > _actor.getMaxMp()/2
- + && cha.isInCombat())
- + {
- + for (L2Skill sk : _selfAnalysis.healSkills)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- + continue;
- +
- + int chance = 5;
- + if (chance >= Rnd.get(100)) // chance
- + continue;
- + if (!GeoData.getInstance().canSeeTarget(_actor, cha))
- + break;
- +
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(cha);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- + }
- + }
- + }
- + continue;
- + }
- +
- + L2NpcInstance npc = (L2NpcInstance) cha;
- +
- + if (faction_id != npc.getFactionId()) continue;
- +
- + if (npc.getAI() != null) // TODO: possibly check not needed
- + {
- + if (Math.abs(target.getZ() - npc.getZ()) < 600
- + //&& _actor.getAttackByList().contains(getAttackTarget())
- + && (npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE
- + || npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE)
- + //limiting aggro for siege guards
- + && target.isInsideRadius(npc, 1500, true, false)
- + && GeoData.getInstance().canSeeTarget(npc, target))
- + {
- + // Notify the L2Object AI with EVT_AGGRESSION
- + npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
- + }
- + // heal friends
- + if (_selfAnalysis.hasHealOrResurrect && !_actor.isAttackingDisabled()
- + && npc.getCurrentHp() < npc.getMaxHp()*0.6
- + && _actor.getCurrentHp() > _actor.getMaxHp()/2
- + && _actor.getCurrentMp() > _actor.getMaxMp()/2
- + && npc.isInCombat())
- + {
- + for (L2Skill sk : _selfAnalysis.healSkills)
- + {
- + if (_actor.getCurrentMp() < sk.getMpConsume())
- + continue;
- + if (_actor.isSkillDisabled(sk.getId()))
- + continue;
- + if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- + continue;
- +
- + int chance = 4;
- + if (chance >= Rnd.get(100)) // chance
- + continue;
- + if (!GeoData.getInstance().canSeeTarget(_actor, npc))
- + break;
- +
- + L2Object OldTarget = _actor.getTarget();
- + _actor.setTarget(npc);
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- + }
- + }
- + }
- + }
- + }
- +
- private void attackPrepare()
- {
- - // Get all information needed to chose between physical or magical attack
- + // Get all information needed to choose between physical or magical attack
- L2Skill[] skills = null;
- double dist_2 = 0;
- int range = 0;
- @@ -303,89 +478,90 @@
- skills = _actor.getAllSkills();
- dist_2 = _actor.getPlanDistanceSq(_attackTarget.getX(), _attackTarget.getY());
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().collisionRadius + _attackTarget.getTemplate().collisionRadius;
- + if (_attackTarget.isMoving()) range += 50;
- }
- catch (NullPointerException e)
- {
- - //_log.warning("AttackableAI: Attack target is NULL.");
- - _actor.setTarget(null);
- - setIntention(AI_INTENTION_IDLE, null, null);
- - return;
- + //_log.warning("AttackableAI: Attack target is NULL.");
- + _actor.setTarget(null);
- + setIntention(AI_INTENTION_IDLE, null, null);
- + return;
- }
- -
- +
- // never attack defenders
- if(_attackTarget instanceof L2PcInstance && sGuard.getCastle().getSiege().checkIsDefender(((L2PcInstance)_attackTarget).getClan()))
- {
- - // Cancel the target
- - sGuard.stopHating(_attackTarget);
- - _actor.setTarget(null);
- - setIntention(AI_INTENTION_IDLE, null, null);
- - return;
- + // Cancel the target
- + sGuard.stopHating(_attackTarget);
- + _actor.setTarget(null);
- + setIntention(AI_INTENTION_IDLE, null, null);
- + return;
- }
- if (!GeoData.getInstance().canSeeTarget(_actor, _attackTarget))
- - {
- - // Siege guards differ from normal mobs currently:
- - // If target cannot seen, don't attack any more
- - sGuard.stopHating(_attackTarget);
- - _actor.setTarget(null);
- - setIntention(AI_INTENTION_IDLE, null, null);
- - return;
- - }
- + {
- + // Siege guards differ from normal mobs currently:
- + // If target cannot seen, don't attack any more
- + sGuard.stopHating(_attackTarget);
- + _actor.setTarget(null);
- + setIntention(AI_INTENTION_IDLE, null, null);
- + return;
- + }
- // Check if the actor isn't muted and if it is far from target
- - if (!_actor.isMuted() && dist_2 > (range + 20) * (range + 20))
- + if (!_actor.isMuted() && dist_2 > range * range)
- {
- // check for long ranged skills and heal/buff skills
- - if (!Config.ALT_GAME_MOB_ATTACK_AI
- - || (_actor instanceof L2MonsterInstance && Rnd.nextInt(100) <= 5))
- - for (L2Skill sk : skills)
- + for (L2Skill sk : skills)
- + {
- + int castRange = sk.getCastRange();
- +
- + if ((dist_2 <= castRange * castRange)
- + && castRange > 70
- + && !_actor.isSkillDisabled(sk.getId())
- + && _actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)
- + && !sk.isPassive())
- {
- - int castRange = sk.getCastRange();
- -
- - if (((sk.getSkillType() == L2Skill.SkillType.BUFF || sk.getSkillType() == L2Skill.SkillType.HEAL) || (dist_2 >= castRange * castRange / 9)
- - && (dist_2 <= castRange * castRange) && (castRange > 70))
- - && !_actor.isSkillDisabled(sk.getId())
- - && _actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk) && !sk.isPassive())
- +
- + L2Object OldTarget = _actor.getTarget();
- + if (sk.getSkillType() == L2Skill.SkillType.BUFF
- + || sk.getSkillType() == L2Skill.SkillType.HEAL)
- {
- - L2Object OldTarget = _actor.getTarget();
- - if (sk.getSkillType() == L2Skill.SkillType.BUFF
- - || sk.getSkillType() == L2Skill.SkillType.HEAL)
- + boolean useSkillSelf = true;
- + if (sk.getSkillType() == L2Skill.SkillType.HEAL
- + && _actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))
- {
- - boolean useSkillSelf = true;
- - if (sk.getSkillType() == L2Skill.SkillType.HEAL
- - && _actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5))
- + useSkillSelf = false;
- + break;
- + }
- + if (sk.getSkillType() == L2Skill.SkillType.BUFF)
- + {
- + L2Effect[] effects = _actor.getAllEffects();
- + for (int i = 0; effects != null && i < effects.length; i++)
- {
- - useSkillSelf = false;
- - break;
- - }
- - if (sk.getSkillType() == L2Skill.SkillType.BUFF)
- - {
- - L2Effect[] effects = _actor.getAllEffects();
- - for (int i = 0; effects != null && i < effects.length; i++)
- + L2Effect effect = effects[i];
- + if (effect.getSkill() == sk)
- {
- - L2Effect effect = effects[i];
- - if (effect.getSkill() == sk)
- - {
- - useSkillSelf = false;
- - break;
- - }
- + useSkillSelf = false;
- + break;
- }
- }
- - if (useSkillSelf) _actor.setTarget(_actor);
- }
- + if (useSkillSelf) _actor.setTarget(_actor);
- + }
- - clientStopMoving(null);
- - _accessor.doCast(sk);
- - _actor.setTarget(OldTarget);
- - return;
- - }
- + clientStopMoving(null);
- + _accessor.doCast(sk);
- + _actor.setTarget(OldTarget);
- + return;
- }
- + }
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0)
- && (_actor.getKnownList().knowsObject(_attackTarget)))
- {
- - // Cancel the target
- + // Cancel the target
- _actor.getKnownList().removeKnownObject(_attackTarget);
- _actor.setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- @@ -409,10 +585,16 @@
- }
- else // Move the actor to Pawn server side AND client side by sending Server->Client packet MoveToPawn (broadcast)
- {
- - // Temporary hack for preventing guards jumping off towers,
- - // before replacing this with effective geodata checks and AI modification
- - if(dz*dz < 170*170) // normally 130 if guard z coordinates correct
- - moveToPawn(_attackTarget, range);
- + // Temporary hack for preventing guards jumping off towers,
- + // before replacing this with effective geodata checks and AI modification
- + if(dz*dz < 170*170) // normally 130 if guard z coordinates correct
- + {
- + if (_selfAnalysis.isMage) range = _selfAnalysis.maxCastRange-50;
- + if (_attackTarget.isMoving())
- + moveToPawn(_attackTarget, range-70);
- + else
- + moveToPawn(_attackTarget, range);
- + }
- }
- }
- @@ -420,19 +602,25 @@
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- - else if (_actor.isMuted() && dist_2 > (range + 20) * (range + 20))
- + else if (_actor.isMuted() && dist_2 > range * range)
- {
- - // Temporary hack for preventing guards jumping off towers,
- - // before replacing this with effective geodata checks and AI modification
- - double dz = _actor.getZ() - _attackTarget.getZ();
- - if(dz*dz < 170*170) // normally 130 if guard z coordinates correct
- - moveToPawn(_attackTarget, range);
- + // Temporary hack for preventing guards jumping off towers,
- + // before replacing this with effective geodata checks and AI modification
- + double dz = _actor.getZ() - _attackTarget.getZ();
- + if(dz*dz < 170*170) // normally 130 if guard z coordinates correct
- + {
- + if (_selfAnalysis.isMage) range = _selfAnalysis.maxCastRange-50;
- + if (_attackTarget.isMoving())
- + moveToPawn(_attackTarget, range-70);
- + else
- + moveToPawn(_attackTarget, range);
- + }
- return;
- }
- // Else, if this is close enough to attack
- - else if (dist_2 <= (range + 20) * (range + 20))
- + else if (dist_2 <= range * range)
- {
- - // Force mobs to attak anybody if confused
- + // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused()) hated = _attackTarget;
- else hated = ((L2Attackable) _actor).getMostHated();
- @@ -453,7 +641,7 @@
- {
- int castRange = sk.getCastRange();
- - if (castRange * castRange >= dist_2 && castRange <= 70 && !sk.isPassive()
- + if (castRange * castRange >= dist_2 && !sk.isPassive()
- && _actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)
- && !_actor.isSkillDisabled(sk.getId()))
- {
- @@ -495,109 +683,8 @@
- _accessor.doAttack(_attackTarget);
- }
- }
- -
- +
- /**
- - * Manage AI attack thinks of a L2Attackable (called by onEvtThink).<BR><BR>
- - *
- - * <B><U> Actions</U> :</B><BR><BR>
- - * <li>Update the attack timeout if actor is running</li>
- - * <li>If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE</li>
- - * <li>Call all L2Object of its Faction inside the Faction Range</li>
- - * <li>Chose a target and order to attack it with magic skill or physical attack</li><BR><BR>
- - *
- - * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- - *
- - */
- - private void thinkAttack()
- - {
- - if (Config.DEBUG)
- - _log.info("L2SiegeGuardAI.thinkAttack(); timeout="
- - + (_attackTimeout - GameTimeController.getGameTicks()));
- -
- - if (_attackTimeout < GameTimeController.getGameTicks())
- - {
- - // Check if the actor is running
- - if (_actor.isRunning())
- - {
- - // Set the actor movement type to walk and send Server->Client packet ChangeMoveType to all others L2PcInstance
- - _actor.setWalking();
- -
- - // Calculate a new attack timeout
- - _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getGameTicks();
- - }
- - }
- -
- - // Check if target is dead or if timeout is expired to stop this attack
- - if (_attackTarget == null || _attackTarget.isAlikeDead()
- - || _attackTimeout < GameTimeController.getGameTicks())
- - {
- - // Stop hating this target after the attack timeout or if target is dead
- - if (_attackTarget != null)
- - {
- - L2Attackable npc = (L2Attackable) _actor;
- - npc.stopHating(_attackTarget);
- - }
- -
- - // Cancel target and timeout
- - _attackTimeout = Integer.MAX_VALUE;
- - _attackTarget = null;
- -
- - // Set the AI Intention to AI_INTENTION_ACTIVE
- - setIntention(AI_INTENTION_ACTIVE, null, null);
- -
- - _actor.setWalking();
- - return;
- - }
- -
- - attackPrepare();
- - factionNotify();
- - }
- -
- - private final void factionNotify()
- - {
- - // Call all L2Object of its Faction inside the Faction Range
- - if (((L2NpcInstance) _actor).getFactionId() == null || _attackTarget == null || _actor == null)
- - return;
- -
- - if (_attackTarget.isInvul()) return;
- -
- - String faction_id = ((L2NpcInstance) _actor).getFactionId();
- -
- - // Go through all L2Object that belong to its faction
- - for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(1000))
- - {
- - if (cha == null) continue;
- -
- - if (!(cha instanceof L2NpcInstance)) continue;
- -
- - L2NpcInstance npc = (L2NpcInstance) cha;
- -
- - if (faction_id != npc.getFactionId()) continue;
- -
- - // Check if the L2Object is inside the Faction Range of the actor
- - if ((npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE || npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_ACTIVE)
- - && _actor.isInsideRadius(npc, npc.getFactionRange(), false, true)
- - && npc.getTarget() == null
- - && _attackTarget.isInsideRadius(npc, npc.getFactionRange(), false, true)
- - )
- - {
- - if (Config.GEODATA > 0)
- - {
- - if (GeoData.getInstance().canSeeTarget(npc, _attackTarget))
- - // Notify the L2Object AI with EVT_AGGRESSION
- - npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attackTarget, 1);
- - }
- - else
- - {
- - if (Math.abs(_attackTarget.getZ() - npc.getZ()) < 600)
- - // Notify the L2Object AI with EVT_AGGRESSION
- - npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, _attackTarget, 1);
- - }
- - }
- - }
- - }
- -
- - /**
- * Manage AI thinking actions of a L2Attackable.<BR><BR>
- */
- @Override
- Index: java/net/sf/l2j/gameserver/templates/L2NpcTemplate.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/templates/L2NpcTemplate.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/templates/L2NpcTemplate.java (revision 1771)
- @@ -68,8 +68,12 @@
- public final int factionRange;
- public final int absorbLevel;
- public final AbsorbCrystalType absorbType;
- + public final short ss;
- + public final short bss;
- + public final short ssRate;
- public Race race;
- - public final String jClass;
- + public final String jClass;
- + public final AIType AI;
- public static enum AbsorbCrystalType
- {
- @@ -78,6 +82,14 @@
- PARTY_ONE_RANDOM
- }
- + public static enum AIType
- + {
- + FIGHTER,
- + ARCHER,
- + BALANCED,
- + MAGE
- + }
- +
- public static enum Race
- {
- UNDEAD,
- @@ -125,7 +137,7 @@
- /**
- * Constructor of L2Character.<BR><BR>
- *
- - * @param set The StatsSet object to transfert data to the method
- + * @param set The StatsSet object to transfer data to the method
- *
- */
- public L2NpcTemplate(StatsSet set)
- @@ -154,10 +166,18 @@
- factionRange = set.getInteger("factionRange");
- absorbLevel = set.getInteger("absorb_level", 0);
- absorbType = AbsorbCrystalType.valueOf(set.getString("absorb_type"));
- + ss = (short)set.getInteger("ss", 0);
- + bss = (short)set.getInteger("bss", 0);
- + ssRate = (short)set.getInteger("ssRate", 0);
- race = null;
- //_npcStatsSet = set;
- _teachInfo = null;
- jClass = set.getString("jClass");
- + String ai = set.getString("AI", "fighter");
- + if (ai.equalsIgnoreCase("archer")) AI = AIType.ARCHER;
- + else if (ai.equalsIgnoreCase("balanced")) AI = AIType.BALANCED;
- + else if (ai.equalsIgnoreCase("mage")) AI = AIType.MAGE;
- + else AI = AIType.FIGHTER;
- }
- public void addTeachInfo(ClassId classId)
- Index: java/net/sf/l2j/gameserver/datatables/NpcTable.java
- ===================================================================
- --- java/net/sf/l2j/gameserver/datatables/NpcTable.java (revision 1770)
- +++ java/net/sf/l2j/gameserver/datatables/NpcTable.java (revision 1771)
- @@ -73,7 +73,7 @@
- {
- con = L2DatabaseFactory.getInstance().getConnection();
- PreparedStatement statement;
- - statement = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[] {"id", "idTemplate", "name", "serverSideName", "title", "serverSideTitle", "class", "collision_radius", "collision_height", "level", "sex", "type", "attackrange", "hp", "mp", "hpreg", "mpreg", "str", "con", "dex", "int", "wit", "men", "exp", "sp", "patk", "pdef", "matk", "mdef", "atkspd", "aggro", "matkspd", "rhand", "lhand", "armor", "walkspd", "runspd", "faction_id", "faction_range", "isUndead", "absorb_level", "absorb_type"}) + " FROM npc");
- + statement = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[] {"id", "idTemplate", "name", "serverSideName", "title", "serverSideTitle", "class", "collision_radius", "collision_height", "level", "sex", "type", "attackrange", "hp", "mp", "hpreg", "mpreg", "str", "con", "dex", "int", "wit", "men", "exp", "sp", "patk", "pdef", "matk", "mdef", "atkspd", "aggro", "matkspd", "rhand", "lhand", "armor", "walkspd", "runspd", "faction_id", "faction_range", "isUndead", "absorb_level", "absorb_type", "ss", "bss", "ss_rate", "AI"}) + " FROM npc");
- ResultSet npcdata = statement.executeQuery();
- fillNpcTable(npcdata);
- @@ -288,6 +288,12 @@
- npcDat.set("absorb_level", NpcData.getString("absorb_level"));
- npcDat.set("absorb_type", NpcData.getString("absorb_type"));
- +
- + npcDat.set("ss", NpcData.getInt("ss"));
- + npcDat.set("bss", NpcData.getInt("bss"));
- + npcDat.set("ssRate", NpcData.getInt("ss_rate"));
- +
- + npcDat.set("AI", NpcData.getString("AI"));
- L2NpcTemplate template = new L2NpcTemplate(npcDat);
- template.addVulnerability(Stats.BOW_WPN_VULN,1);
- @@ -331,7 +337,7 @@
- // reload the NPC base data
- con = L2DatabaseFactory.getInstance().getConnection();
- - PreparedStatement st = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[] {"id", "idTemplate", "name", "serverSideName", "title", "serverSideTitle", "class", "collision_radius", "collision_height", "level", "sex", "type", "attackrange", "hp", "mp", "hpreg", "mpreg", "str", "con", "dex", "int", "wit", "men", "exp", "sp", "patk", "pdef", "matk", "mdef", "atkspd", "aggro", "matkspd", "rhand", "lhand", "armor", "walkspd", "runspd", "faction_id", "faction_range", "isUndead", "absorb_level", "absorb_type"}) + " FROM npc WHERE id=?");
- + PreparedStatement st = con.prepareStatement("SELECT " + L2DatabaseFactory.getInstance().safetyString(new String[] {"id", "idTemplate", "name", "serverSideName", "title", "serverSideTitle", "class", "collision_radius", "collision_height", "level", "sex", "type", "attackrange", "hp", "mp", "hpreg", "mpreg", "str", "con", "dex", "int", "wit", "men", "exp", "sp", "patk", "pdef", "matk", "mdef", "atkspd", "aggro", "matkspd", "rhand", "lhand", "armor", "walkspd", "runspd", "faction_id", "faction_range", "isUndead", "absorb_level", "absorb_type", "ss", "bss", "ss_rate", "AI"}) + " FROM npc WHERE id=?");
- st.setInt(1, id);
- ResultSet rs = st.executeQuery();
- fillNpcTable(rs);
Advertisement
Add Comment
Please, Sign In to add comment
Advertisement