diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/SiegeGuards.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/SiegeGuards.java
new file mode 100644
index 0000000000..fb9007f8c8
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/SiegeGuards.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * 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 .
+ */
+package ai.others;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.actor.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.L2Summon;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.entity.Castle;
+import com.l2jmobius.gameserver.model.entity.Fort;
+
+import ai.AbstractNpcAI;
+
+/**
+ * @author Mobius
+ */
+public class SiegeGuards extends AbstractNpcAI
+{
+ //@formatter:off
+ // NPCs
+ private static final int[] CASTLE_GUARDS =
+ {
+ 35064, 35065, 35066, 35067, 35068, 35069, 35071, 35072, 35079, 35080, 35081, 35082, 35083, 35084, 35085, // Gludio
+ 35106, 35107, 35108, 35109, 35110, 35111, 35113, 35114, 35121, 35122, 35123,35124, 35125, 35126, 35127, // Dion
+ 35150, 35151, 35152, 35153, 35155, 35156, 35163, 35164, 35165, 35166, 35167, 35168, 35169, // Giran
+ 35192, 35193, 35194, 35195, 35197, 35198, 35205, 35206, 35207, 35208, 35209, 35210, 35211, // Oren
+ 35234, 35239, 35240, 35248, 35249, 35250, 35251, 35252, 35253, 35254, // Aden
+ 35280, 35281, 35282, 35283, 35284, 35285, 35287, 35288, 35295, 35296, 35297, 35298, 35299, 35300, 35301, // Innadril
+ 35324, 35325, 35326, 35327, 35328, 35330, 35339, 35340, 35341, 35343, 35350, 35351, // Goddard
+ 35475, 35477, 35480, 35484, 35486, 35487, 35488, 35489, 35490, // Rune
+ 35516, 35517, 35518, 35519, 35520, 35522, 35531, 35532, 35533, 35535, 35542, 35543, // Schuttgart
+ };
+ private static final int[] FORT_GUARDS =
+ {
+ 35670, 35671, 35672, 35673, 35674, 35678, 35679, 35681, 35682, 35684, 35685, // Shanty
+ 35702, 35703, 35704, 35705, 35706, 35707, 35708, 35709, 35710, 35711, 35712, 35714, 35715, 35717, 35718, 35720, 35722, 35723, // Southern
+ 35739, 35740, 35741, 35742, 35743, 35747, 35748, 35750, 35751, 35753, 35754, // Hive
+ 35758, 35767, 35771, 35772, 35773, 35774, 35775, 35776, 35777, 35778, 35779, 35780, 35781, 35783, 35784, 35786, 35787, 35789, 35791, 35792, // Valley
+ 35808, 35809, 35810, 35811, 35812, 35816, 35817, 35819, 35820, 35822, 35823, // Ivory
+ 35839, 35840, 35841, 35842, 35843, 35847, 35848, 35850, 35851, 35853, 35854, // Narsell
+ 35868, 35869, 35871, 35872, 35873, 35874, 35875, 35876, 35877, 35878, 35879, 35880, 35881, 35883, 35884, 35886, 35887, 35889, 35891, 35892, // Bayou
+ 35908, 35909, 35910, 35911, 35912, 35916, 35917, 35919, 35920, 35922, 35923, // White Sands
+ 35940, 35941, 35942, 35943, 35944, 35945, 35946, 35947, 35948, 35949, 35950, 35952, 35953, 35955, 35956, 35958, 35960, 35961, // Borderland
+ 35978, 35979, 35980, 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35988, 35990, 35991, 35993, 35994, 35996, 35998, 35999, // Swamp
+ 36015, 36016, 36017, 36018, 36019, 36023, 36024, 36026, 36027, 36029, 36030, // Archaic
+ 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, 36056, 36057, 36059, 36060, 36062, 36063, 36065, 36067, 36068, 36468, // Floran
+ 36079, 36085, 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, 36097, 36098, 36100, 36101, 36103, 36105, 36106, // Removed on Helios (113)
+ 36122, 36123, 36124, 36125, 36126, 36130, 36131, 36133, 36134, 36136, 36137, // Tanor
+ 36153, 36154, 36155, 36156, 36157, 36161, 36162, 36164, 36165, 36167, 36168, // Dragonspine
+ 36185, 36186, 36187, 36188, 36189, 36190, 36191, 36192, 36193, 36194, 36195, 36197, 36198, 36200, 36201, 36203, 36205, 36206, // Antharas
+ 36223, 36224, 36225, 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36235, 36236, 36238, 36239, 36241, 36243, 36244, // Western
+ 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36273, 36274, 36276, 36277, 36279, 36281, 36282, // Hunters
+ 36298, 36299, 36300, 36301, 36302, 36306, 36307, 36309, 36310, 36312, 36313, // Aaru
+ 36330, 36331, 36332, 36333, 36334, 36342, 36343, 36345, 36346, 36348, 36351, // Demon
+ 36368, 36369, 36370, 36371, 36372, 36380, 36381, 36383, 36384, 36386, 36389 // Monastic
+ };
+ private static final int[] MERCENARIES =
+ {
+ 35015, 35016, 35017, 35018, 35019, 35025, 35026, 35027, 35028, 35029, 35035, 35036, 35037, 35038, 35039, 35045, 35046, 35047, 35048, 35049, 35055, 35056, 35057, 35058, 35059, 35060, 35061
+ };
+ private static final int[] STATIONARY_MERCENARIES =
+ {
+ 35010, 35011, 35012, 35013, 35014, 35020, 35021, 35022, 35023, 35024, 35030, 35031, 35032, 35033, 35034, 35040, 35041, 35042, 35043, 35044, 35050, 35051, 35052, 35053, 35054, 35092, 35093, 35094,
+ 35134, 35135, 35136, 35176, 35177, 35178, 35218, 35219, 35220, 35261, 35262, 35263, 35264, 35265, 35308, 35309, 35310, 35352, 35353, 35354, 35497, 35498, 35499, 35500, 35501, 35544, 35545, 35546
+ };
+ //@formatter:on
+
+ public SiegeGuards()
+ {
+ addAttackId(CASTLE_GUARDS);
+ addAttackId(FORT_GUARDS);
+ addAttackId(MERCENARIES);
+ addAttackId(STATIONARY_MERCENARIES);
+ addSeeCreatureId(CASTLE_GUARDS);
+ addSeeCreatureId(FORT_GUARDS);
+ addSeeCreatureId(MERCENARIES);
+ addSeeCreatureId(STATIONARY_MERCENARIES);
+ addSpawnId(CASTLE_GUARDS);
+ addSpawnId(FORT_GUARDS);
+ addSpawnId(MERCENARIES);
+ addSpawnId(STATIONARY_MERCENARIES);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((attacker.getSiegeState() == 2) && !attacker.isRegisteredOnThisSiegeField(activeSiegeId)) || (attacker.getSiegeState() == 0)) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
+ {
+ ((L2Attackable) npc).stopHating(attacker);
+ return null;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
+ {
+ if (creature.isPlayable() && (npc.calculateDirectionTo(creature) < npc.getAggroRange()) && GeoEngine.getInstance().canSeeTarget(npc, creature))
+ {
+ final L2Summon summon = isSummon ? creature.getServitors().values().stream().findFirst().orElse(creature.getPet()) : null;
+ final L2PcInstance cha = summon == null ? (L2PcInstance) creature : summon.getOwner();
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((cha.getSiegeState() != 2) || cha.isRegisteredOnThisSiegeField(activeSiegeId)) && (cha.getSiegeState() != 0)) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_IDLE))
+ {
+ if (!cha.isInvisible()) // skip invisible players
+ {
+ addAttackPlayerDesire(npc, cha);
+ }
+ }
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(false);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new SiegeGuards();
+ }
+}
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
index 28f68acde3..6253fa9329 100644
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
+++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
@@ -42,6 +42,7 @@ import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Playable;
+import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
@@ -444,8 +445,8 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
return;
}
- // Check if the actor is a L2GuardInstance
- if ((npc instanceof L2GuardInstance) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
+ // Check if the actor is a guard
+ if (((npc instanceof L2GuardInstance) || (npc instanceof L2DefenderInstance)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
{
// Order to the L2GuardInstance to return to its home location because there's no target to attack
npc.returnHome();
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
deleted file mode 100644
index 426228ee50..0000000000
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2FortCommanderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2FortSiegeGuardAI(L2Character accessor)
- {
- super(accessor);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- private boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead() || (target instanceof L2FortCommanderInstance) || target.isPlayable())
- {
- L2PcInstance player = null;
- if (target instanceof L2PcInstance)
- {
- player = ((L2PcInstance) target);
- }
- else if (target instanceof L2Summon)
- {
- player = ((L2Summon) target).getOwner();
- }
- if ((player == null) || ((player.getClan() != null) && (player.getClan().getFortId() == ((L2Npc) _actor).getFort().getResidenceId())))
- {
- return false;
- }
- }
-
- // Check if the target isn't invulnerable
- if ((target != null) && target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target instanceof L2Summon)
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target instanceof L2Playable)
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
-
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
- if (_actor.getWalkSpeed() >= 0)
- {
- if (_actor instanceof L2DefenderInstance)
- {
- ((L2DefenderInstance) _actor).returnHome();
- }
- else
- {
- ((L2FortCommanderInstance) _actor).returnHome();
- }
- }
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (cha == null)
- {
- continue;
- }
-
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && ((L2Npc) _actor).getFort().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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- L2DefenderInstance sGuard;
- if (_actor instanceof L2FortCommanderInstance)
- {
- sGuard = (L2FortCommanderInstance) _actor;
- }
- else
- {
- sGuard = (L2DefenderInstance) _actor;
- }
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- // LOGGER.warning("AttackableAI: Attack target is NULL.");
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // never attack defenders
- if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
- {
- // Cancel the target
- sGuard.stopHating(attackTarget);
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- 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
- final 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 (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- _actor.doAttack(attackTarget);
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the actor can't use skills and if a thinking action isn't already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = _actor instanceof L2FortCommanderInstance ? (L2FortCommanderInstance) _actor : (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
deleted file mode 100644
index 7cd4bb5dc1..0000000000
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
- // private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2SiegeGuardAI(L2Character creature)
- {
- super(creature);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- protected boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead())
- {
- return false;
- }
-
- // Check if the target isn't invulnerable
- if (target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target.isSummon())
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target.isPlayable())
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2DefenderInstance to return to its home location because there's no target to attack
- ((L2DefenderInstance) _actor).returnHome();
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && (((L2Npc) _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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isHealer)
- {
- return;
- }
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer)
- {
- // Temporary hack for preventing guards jumping off towers,
- // before replacing this with effective geodata checks and AI modification
- final 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- if (!_selfAnalysis.isHealer)
- {
- _actor.doAttack(attackTarget);
- }
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the thinking action is already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
deleted file mode 100644
index 7d8c8dfcaa..0000000000
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.l2jmobius.gameserver.model.actor.L2Character;
-
-/**
- * @author BiggBoss
- */
-public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI
-{
- private final List _allied = new ArrayList<>();
-
- public L2SpecialSiegeGuardAI(L2Character creature)
- {
- super(creature);
- }
-
- public List getAlly()
- {
- return _allied;
- }
-
- @Override
- protected boolean autoAttackCondition(L2Character target)
- {
- if (_allied.contains(target.getObjectId()))
- {
- return false;
- }
-
- return super.autoAttackCondition(target);
- }
-}
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
index 05dfc40cb5..d977fa1826 100644
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
+++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
@@ -38,8 +38,6 @@ import com.l2jmobius.gameserver.ai.CtrlEvent;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.ai.L2AttackableAI;
import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2FortSiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
import com.l2jmobius.gameserver.data.xml.impl.ExtendDropData;
import com.l2jmobius.gameserver.datatables.EventDroplist;
import com.l2jmobius.gameserver.datatables.EventDroplist.DateDrop;
@@ -753,15 +751,6 @@ public class L2Attackable extends L2Npc
public void reduceHate(L2Character target, int amount)
{
- if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
- {
- // TODO: this just prevents error until siege guards are handled properly
- stopHating(target);
- setTarget(null);
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
- return;
- }
-
if (target == null) // whole aggrolist
{
final L2Character mostHated = getMostHated();
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
index 8a45fb20e5..955f062388 100644
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
+++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
@@ -18,9 +18,6 @@ package com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.ai.CtrlIntention;
-import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SpecialSiegeGuardAI;
import com.l2jmobius.gameserver.enums.InstanceType;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
@@ -45,16 +42,6 @@ public class L2DefenderInstance extends L2Attackable
setInstanceType(InstanceType.L2DefenderInstance);
}
- @Override
- protected L2CharacterAI initAI()
- {
- if (getCastle(10000) != null)
- {
- return new L2SiegeGuardAI(this);
- }
- return new L2SpecialSiegeGuardAI(this);
- }
-
@Override
public void addDamage(L2Character attacker, int damage, Skill skill)
{
@@ -189,6 +176,39 @@ public class L2DefenderInstance extends L2Attackable
player.sendPacket(ActionFailed.STATIC_PACKET);
}
+ @Override
+ public void useMagic(Skill skill)
+ {
+ if (!skill.isBad())
+ {
+ L2Character target = this;
+ for (L2Character nearby : L2World.getInstance().getVisibleObjects(this, L2Character.class, skill.getCastRange()))
+ {
+ if (nearby == this)
+ {
+ continue;
+ }
+ if (nearby instanceof L2DefenderInstance)
+ {
+ target = nearby;
+ }
+ if (nearby.isPlayer())
+ {
+ final L2PcInstance player = (L2PcInstance) nearby;
+ final Castle castle = getCastle();
+ final Fort fortress = getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId))
+ {
+ target = nearby;
+ }
+ }
+ }
+ setTarget(target);
+ }
+ super.useMagic(skill);
+ }
+
@Override
public void addDamageHate(L2Character attacker, int damage, int aggro)
{
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/SiegeGuards.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/SiegeGuards.java
new file mode 100644
index 0000000000..fb9007f8c8
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/SiegeGuards.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * 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 .
+ */
+package ai.others;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.actor.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.L2Summon;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.entity.Castle;
+import com.l2jmobius.gameserver.model.entity.Fort;
+
+import ai.AbstractNpcAI;
+
+/**
+ * @author Mobius
+ */
+public class SiegeGuards extends AbstractNpcAI
+{
+ //@formatter:off
+ // NPCs
+ private static final int[] CASTLE_GUARDS =
+ {
+ 35064, 35065, 35066, 35067, 35068, 35069, 35071, 35072, 35079, 35080, 35081, 35082, 35083, 35084, 35085, // Gludio
+ 35106, 35107, 35108, 35109, 35110, 35111, 35113, 35114, 35121, 35122, 35123,35124, 35125, 35126, 35127, // Dion
+ 35150, 35151, 35152, 35153, 35155, 35156, 35163, 35164, 35165, 35166, 35167, 35168, 35169, // Giran
+ 35192, 35193, 35194, 35195, 35197, 35198, 35205, 35206, 35207, 35208, 35209, 35210, 35211, // Oren
+ 35234, 35239, 35240, 35248, 35249, 35250, 35251, 35252, 35253, 35254, // Aden
+ 35280, 35281, 35282, 35283, 35284, 35285, 35287, 35288, 35295, 35296, 35297, 35298, 35299, 35300, 35301, // Innadril
+ 35324, 35325, 35326, 35327, 35328, 35330, 35339, 35340, 35341, 35343, 35350, 35351, // Goddard
+ 35475, 35477, 35480, 35484, 35486, 35487, 35488, 35489, 35490, // Rune
+ 35516, 35517, 35518, 35519, 35520, 35522, 35531, 35532, 35533, 35535, 35542, 35543, // Schuttgart
+ };
+ private static final int[] FORT_GUARDS =
+ {
+ 35670, 35671, 35672, 35673, 35674, 35678, 35679, 35681, 35682, 35684, 35685, // Shanty
+ 35702, 35703, 35704, 35705, 35706, 35707, 35708, 35709, 35710, 35711, 35712, 35714, 35715, 35717, 35718, 35720, 35722, 35723, // Southern
+ 35739, 35740, 35741, 35742, 35743, 35747, 35748, 35750, 35751, 35753, 35754, // Hive
+ 35758, 35767, 35771, 35772, 35773, 35774, 35775, 35776, 35777, 35778, 35779, 35780, 35781, 35783, 35784, 35786, 35787, 35789, 35791, 35792, // Valley
+ 35808, 35809, 35810, 35811, 35812, 35816, 35817, 35819, 35820, 35822, 35823, // Ivory
+ 35839, 35840, 35841, 35842, 35843, 35847, 35848, 35850, 35851, 35853, 35854, // Narsell
+ 35868, 35869, 35871, 35872, 35873, 35874, 35875, 35876, 35877, 35878, 35879, 35880, 35881, 35883, 35884, 35886, 35887, 35889, 35891, 35892, // Bayou
+ 35908, 35909, 35910, 35911, 35912, 35916, 35917, 35919, 35920, 35922, 35923, // White Sands
+ 35940, 35941, 35942, 35943, 35944, 35945, 35946, 35947, 35948, 35949, 35950, 35952, 35953, 35955, 35956, 35958, 35960, 35961, // Borderland
+ 35978, 35979, 35980, 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35988, 35990, 35991, 35993, 35994, 35996, 35998, 35999, // Swamp
+ 36015, 36016, 36017, 36018, 36019, 36023, 36024, 36026, 36027, 36029, 36030, // Archaic
+ 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, 36056, 36057, 36059, 36060, 36062, 36063, 36065, 36067, 36068, 36468, // Floran
+ 36079, 36085, 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, 36097, 36098, 36100, 36101, 36103, 36105, 36106, // Removed on Helios (113)
+ 36122, 36123, 36124, 36125, 36126, 36130, 36131, 36133, 36134, 36136, 36137, // Tanor
+ 36153, 36154, 36155, 36156, 36157, 36161, 36162, 36164, 36165, 36167, 36168, // Dragonspine
+ 36185, 36186, 36187, 36188, 36189, 36190, 36191, 36192, 36193, 36194, 36195, 36197, 36198, 36200, 36201, 36203, 36205, 36206, // Antharas
+ 36223, 36224, 36225, 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36235, 36236, 36238, 36239, 36241, 36243, 36244, // Western
+ 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36273, 36274, 36276, 36277, 36279, 36281, 36282, // Hunters
+ 36298, 36299, 36300, 36301, 36302, 36306, 36307, 36309, 36310, 36312, 36313, // Aaru
+ 36330, 36331, 36332, 36333, 36334, 36342, 36343, 36345, 36346, 36348, 36351, // Demon
+ 36368, 36369, 36370, 36371, 36372, 36380, 36381, 36383, 36384, 36386, 36389 // Monastic
+ };
+ private static final int[] MERCENARIES =
+ {
+ 35015, 35016, 35017, 35018, 35019, 35025, 35026, 35027, 35028, 35029, 35035, 35036, 35037, 35038, 35039, 35045, 35046, 35047, 35048, 35049, 35055, 35056, 35057, 35058, 35059, 35060, 35061
+ };
+ private static final int[] STATIONARY_MERCENARIES =
+ {
+ 35010, 35011, 35012, 35013, 35014, 35020, 35021, 35022, 35023, 35024, 35030, 35031, 35032, 35033, 35034, 35040, 35041, 35042, 35043, 35044, 35050, 35051, 35052, 35053, 35054, 35092, 35093, 35094,
+ 35134, 35135, 35136, 35176, 35177, 35178, 35218, 35219, 35220, 35261, 35262, 35263, 35264, 35265, 35308, 35309, 35310, 35352, 35353, 35354, 35497, 35498, 35499, 35500, 35501, 35544, 35545, 35546
+ };
+ //@formatter:on
+
+ public SiegeGuards()
+ {
+ addAttackId(CASTLE_GUARDS);
+ addAttackId(FORT_GUARDS);
+ addAttackId(MERCENARIES);
+ addAttackId(STATIONARY_MERCENARIES);
+ addSeeCreatureId(CASTLE_GUARDS);
+ addSeeCreatureId(FORT_GUARDS);
+ addSeeCreatureId(MERCENARIES);
+ addSeeCreatureId(STATIONARY_MERCENARIES);
+ addSpawnId(CASTLE_GUARDS);
+ addSpawnId(FORT_GUARDS);
+ addSpawnId(MERCENARIES);
+ addSpawnId(STATIONARY_MERCENARIES);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((attacker.getSiegeState() == 2) && !attacker.isRegisteredOnThisSiegeField(activeSiegeId)) || (attacker.getSiegeState() == 0)) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
+ {
+ ((L2Attackable) npc).stopHating(attacker);
+ return null;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
+ {
+ if (creature.isPlayable() && (npc.calculateDirectionTo(creature) < npc.getAggroRange()) && GeoEngine.getInstance().canSeeTarget(npc, creature))
+ {
+ final L2Summon summon = isSummon ? creature.getServitors().values().stream().findFirst().orElse(creature.getPet()) : null;
+ final L2PcInstance cha = summon == null ? (L2PcInstance) creature : summon.getOwner();
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((cha.getSiegeState() != 2) || cha.isRegisteredOnThisSiegeField(activeSiegeId)) && (cha.getSiegeState() != 0)) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_IDLE))
+ {
+ if (!cha.isInvisible()) // skip invisible players
+ {
+ addAttackPlayerDesire(npc, cha);
+ }
+ }
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(false);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new SiegeGuards();
+ }
+}
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
index 28f68acde3..6253fa9329 100644
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
+++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
@@ -42,6 +42,7 @@ import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Playable;
+import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
@@ -444,8 +445,8 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
return;
}
- // Check if the actor is a L2GuardInstance
- if ((npc instanceof L2GuardInstance) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
+ // Check if the actor is a guard
+ if (((npc instanceof L2GuardInstance) || (npc instanceof L2DefenderInstance)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
{
// Order to the L2GuardInstance to return to its home location because there's no target to attack
npc.returnHome();
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
deleted file mode 100644
index 426228ee50..0000000000
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2FortCommanderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2FortSiegeGuardAI(L2Character accessor)
- {
- super(accessor);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- private boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead() || (target instanceof L2FortCommanderInstance) || target.isPlayable())
- {
- L2PcInstance player = null;
- if (target instanceof L2PcInstance)
- {
- player = ((L2PcInstance) target);
- }
- else if (target instanceof L2Summon)
- {
- player = ((L2Summon) target).getOwner();
- }
- if ((player == null) || ((player.getClan() != null) && (player.getClan().getFortId() == ((L2Npc) _actor).getFort().getResidenceId())))
- {
- return false;
- }
- }
-
- // Check if the target isn't invulnerable
- if ((target != null) && target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target instanceof L2Summon)
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target instanceof L2Playable)
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
-
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
- if (_actor.getWalkSpeed() >= 0)
- {
- if (_actor instanceof L2DefenderInstance)
- {
- ((L2DefenderInstance) _actor).returnHome();
- }
- else
- {
- ((L2FortCommanderInstance) _actor).returnHome();
- }
- }
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (cha == null)
- {
- continue;
- }
-
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && ((L2Npc) _actor).getFort().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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- L2DefenderInstance sGuard;
- if (_actor instanceof L2FortCommanderInstance)
- {
- sGuard = (L2FortCommanderInstance) _actor;
- }
- else
- {
- sGuard = (L2DefenderInstance) _actor;
- }
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- // LOGGER.warning("AttackableAI: Attack target is NULL.");
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // never attack defenders
- if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
- {
- // Cancel the target
- sGuard.stopHating(attackTarget);
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- 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
- final 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 (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- _actor.doAttack(attackTarget);
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the actor can't use skills and if a thinking action isn't already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = _actor instanceof L2FortCommanderInstance ? (L2FortCommanderInstance) _actor : (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
deleted file mode 100644
index 7cd4bb5dc1..0000000000
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
- // private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2SiegeGuardAI(L2Character creature)
- {
- super(creature);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- protected boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead())
- {
- return false;
- }
-
- // Check if the target isn't invulnerable
- if (target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target.isSummon())
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target.isPlayable())
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2DefenderInstance to return to its home location because there's no target to attack
- ((L2DefenderInstance) _actor).returnHome();
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && (((L2Npc) _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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isHealer)
- {
- return;
- }
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer)
- {
- // Temporary hack for preventing guards jumping off towers,
- // before replacing this with effective geodata checks and AI modification
- final 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- if (!_selfAnalysis.isHealer)
- {
- _actor.doAttack(attackTarget);
- }
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the thinking action is already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
deleted file mode 100644
index 7d8c8dfcaa..0000000000
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.l2jmobius.gameserver.model.actor.L2Character;
-
-/**
- * @author BiggBoss
- */
-public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI
-{
- private final List _allied = new ArrayList<>();
-
- public L2SpecialSiegeGuardAI(L2Character creature)
- {
- super(creature);
- }
-
- public List getAlly()
- {
- return _allied;
- }
-
- @Override
- protected boolean autoAttackCondition(L2Character target)
- {
- if (_allied.contains(target.getObjectId()))
- {
- return false;
- }
-
- return super.autoAttackCondition(target);
- }
-}
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
index 9d12eccfd5..920a3e1b39 100644
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
+++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
@@ -38,8 +38,6 @@ import com.l2jmobius.gameserver.ai.CtrlEvent;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.ai.L2AttackableAI;
import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2FortSiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
import com.l2jmobius.gameserver.data.xml.impl.ExtendDropData;
import com.l2jmobius.gameserver.datatables.EventDroplist;
import com.l2jmobius.gameserver.datatables.EventDroplist.DateDrop;
@@ -749,15 +747,6 @@ public class L2Attackable extends L2Npc
public void reduceHate(L2Character target, int amount)
{
- if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
- {
- // TODO: this just prevents error until siege guards are handled properly
- stopHating(target);
- setTarget(null);
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
- return;
- }
-
if (target == null) // whole aggrolist
{
final L2Character mostHated = getMostHated();
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
index 8a45fb20e5..955f062388 100644
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
+++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
@@ -18,9 +18,6 @@ package com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.ai.CtrlIntention;
-import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SpecialSiegeGuardAI;
import com.l2jmobius.gameserver.enums.InstanceType;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
@@ -45,16 +42,6 @@ public class L2DefenderInstance extends L2Attackable
setInstanceType(InstanceType.L2DefenderInstance);
}
- @Override
- protected L2CharacterAI initAI()
- {
- if (getCastle(10000) != null)
- {
- return new L2SiegeGuardAI(this);
- }
- return new L2SpecialSiegeGuardAI(this);
- }
-
@Override
public void addDamage(L2Character attacker, int damage, Skill skill)
{
@@ -189,6 +176,39 @@ public class L2DefenderInstance extends L2Attackable
player.sendPacket(ActionFailed.STATIC_PACKET);
}
+ @Override
+ public void useMagic(Skill skill)
+ {
+ if (!skill.isBad())
+ {
+ L2Character target = this;
+ for (L2Character nearby : L2World.getInstance().getVisibleObjects(this, L2Character.class, skill.getCastRange()))
+ {
+ if (nearby == this)
+ {
+ continue;
+ }
+ if (nearby instanceof L2DefenderInstance)
+ {
+ target = nearby;
+ }
+ if (nearby.isPlayer())
+ {
+ final L2PcInstance player = (L2PcInstance) nearby;
+ final Castle castle = getCastle();
+ final Fort fortress = getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId))
+ {
+ target = nearby;
+ }
+ }
+ }
+ setTarget(target);
+ }
+ super.useMagic(skill);
+ }
+
@Override
public void addDamageHate(L2Character attacker, int damage, int aggro)
{
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/SiegeGuards.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/SiegeGuards.java
new file mode 100644
index 0000000000..fb9007f8c8
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/SiegeGuards.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * 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 .
+ */
+package ai.others;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.actor.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.L2Summon;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.entity.Castle;
+import com.l2jmobius.gameserver.model.entity.Fort;
+
+import ai.AbstractNpcAI;
+
+/**
+ * @author Mobius
+ */
+public class SiegeGuards extends AbstractNpcAI
+{
+ //@formatter:off
+ // NPCs
+ private static final int[] CASTLE_GUARDS =
+ {
+ 35064, 35065, 35066, 35067, 35068, 35069, 35071, 35072, 35079, 35080, 35081, 35082, 35083, 35084, 35085, // Gludio
+ 35106, 35107, 35108, 35109, 35110, 35111, 35113, 35114, 35121, 35122, 35123,35124, 35125, 35126, 35127, // Dion
+ 35150, 35151, 35152, 35153, 35155, 35156, 35163, 35164, 35165, 35166, 35167, 35168, 35169, // Giran
+ 35192, 35193, 35194, 35195, 35197, 35198, 35205, 35206, 35207, 35208, 35209, 35210, 35211, // Oren
+ 35234, 35239, 35240, 35248, 35249, 35250, 35251, 35252, 35253, 35254, // Aden
+ 35280, 35281, 35282, 35283, 35284, 35285, 35287, 35288, 35295, 35296, 35297, 35298, 35299, 35300, 35301, // Innadril
+ 35324, 35325, 35326, 35327, 35328, 35330, 35339, 35340, 35341, 35343, 35350, 35351, // Goddard
+ 35475, 35477, 35480, 35484, 35486, 35487, 35488, 35489, 35490, // Rune
+ 35516, 35517, 35518, 35519, 35520, 35522, 35531, 35532, 35533, 35535, 35542, 35543, // Schuttgart
+ };
+ private static final int[] FORT_GUARDS =
+ {
+ 35670, 35671, 35672, 35673, 35674, 35678, 35679, 35681, 35682, 35684, 35685, // Shanty
+ 35702, 35703, 35704, 35705, 35706, 35707, 35708, 35709, 35710, 35711, 35712, 35714, 35715, 35717, 35718, 35720, 35722, 35723, // Southern
+ 35739, 35740, 35741, 35742, 35743, 35747, 35748, 35750, 35751, 35753, 35754, // Hive
+ 35758, 35767, 35771, 35772, 35773, 35774, 35775, 35776, 35777, 35778, 35779, 35780, 35781, 35783, 35784, 35786, 35787, 35789, 35791, 35792, // Valley
+ 35808, 35809, 35810, 35811, 35812, 35816, 35817, 35819, 35820, 35822, 35823, // Ivory
+ 35839, 35840, 35841, 35842, 35843, 35847, 35848, 35850, 35851, 35853, 35854, // Narsell
+ 35868, 35869, 35871, 35872, 35873, 35874, 35875, 35876, 35877, 35878, 35879, 35880, 35881, 35883, 35884, 35886, 35887, 35889, 35891, 35892, // Bayou
+ 35908, 35909, 35910, 35911, 35912, 35916, 35917, 35919, 35920, 35922, 35923, // White Sands
+ 35940, 35941, 35942, 35943, 35944, 35945, 35946, 35947, 35948, 35949, 35950, 35952, 35953, 35955, 35956, 35958, 35960, 35961, // Borderland
+ 35978, 35979, 35980, 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35988, 35990, 35991, 35993, 35994, 35996, 35998, 35999, // Swamp
+ 36015, 36016, 36017, 36018, 36019, 36023, 36024, 36026, 36027, 36029, 36030, // Archaic
+ 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, 36056, 36057, 36059, 36060, 36062, 36063, 36065, 36067, 36068, 36468, // Floran
+ 36079, 36085, 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, 36097, 36098, 36100, 36101, 36103, 36105, 36106, // Removed on Helios (113)
+ 36122, 36123, 36124, 36125, 36126, 36130, 36131, 36133, 36134, 36136, 36137, // Tanor
+ 36153, 36154, 36155, 36156, 36157, 36161, 36162, 36164, 36165, 36167, 36168, // Dragonspine
+ 36185, 36186, 36187, 36188, 36189, 36190, 36191, 36192, 36193, 36194, 36195, 36197, 36198, 36200, 36201, 36203, 36205, 36206, // Antharas
+ 36223, 36224, 36225, 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36235, 36236, 36238, 36239, 36241, 36243, 36244, // Western
+ 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36273, 36274, 36276, 36277, 36279, 36281, 36282, // Hunters
+ 36298, 36299, 36300, 36301, 36302, 36306, 36307, 36309, 36310, 36312, 36313, // Aaru
+ 36330, 36331, 36332, 36333, 36334, 36342, 36343, 36345, 36346, 36348, 36351, // Demon
+ 36368, 36369, 36370, 36371, 36372, 36380, 36381, 36383, 36384, 36386, 36389 // Monastic
+ };
+ private static final int[] MERCENARIES =
+ {
+ 35015, 35016, 35017, 35018, 35019, 35025, 35026, 35027, 35028, 35029, 35035, 35036, 35037, 35038, 35039, 35045, 35046, 35047, 35048, 35049, 35055, 35056, 35057, 35058, 35059, 35060, 35061
+ };
+ private static final int[] STATIONARY_MERCENARIES =
+ {
+ 35010, 35011, 35012, 35013, 35014, 35020, 35021, 35022, 35023, 35024, 35030, 35031, 35032, 35033, 35034, 35040, 35041, 35042, 35043, 35044, 35050, 35051, 35052, 35053, 35054, 35092, 35093, 35094,
+ 35134, 35135, 35136, 35176, 35177, 35178, 35218, 35219, 35220, 35261, 35262, 35263, 35264, 35265, 35308, 35309, 35310, 35352, 35353, 35354, 35497, 35498, 35499, 35500, 35501, 35544, 35545, 35546
+ };
+ //@formatter:on
+
+ public SiegeGuards()
+ {
+ addAttackId(CASTLE_GUARDS);
+ addAttackId(FORT_GUARDS);
+ addAttackId(MERCENARIES);
+ addAttackId(STATIONARY_MERCENARIES);
+ addSeeCreatureId(CASTLE_GUARDS);
+ addSeeCreatureId(FORT_GUARDS);
+ addSeeCreatureId(MERCENARIES);
+ addSeeCreatureId(STATIONARY_MERCENARIES);
+ addSpawnId(CASTLE_GUARDS);
+ addSpawnId(FORT_GUARDS);
+ addSpawnId(MERCENARIES);
+ addSpawnId(STATIONARY_MERCENARIES);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((attacker.getSiegeState() == 2) && !attacker.isRegisteredOnThisSiegeField(activeSiegeId)) || (attacker.getSiegeState() == 0)) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
+ {
+ ((L2Attackable) npc).stopHating(attacker);
+ return null;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
+ {
+ if (creature.isPlayable() && (npc.calculateDirectionTo(creature) < npc.getAggroRange()) && GeoEngine.getInstance().canSeeTarget(npc, creature))
+ {
+ final L2Summon summon = isSummon ? creature.getServitors().values().stream().findFirst().orElse(creature.getPet()) : null;
+ final L2PcInstance cha = summon == null ? (L2PcInstance) creature : summon.getOwner();
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((cha.getSiegeState() != 2) || cha.isRegisteredOnThisSiegeField(activeSiegeId)) && (cha.getSiegeState() != 0)) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_IDLE))
+ {
+ if (!cha.isInvisible()) // skip invisible players
+ {
+ addAttackPlayerDesire(npc, cha);
+ }
+ }
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(false);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new SiegeGuards();
+ }
+}
diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
index 28f68acde3..6253fa9329 100644
--- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
+++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
@@ -42,6 +42,7 @@ import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Playable;
+import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
@@ -444,8 +445,8 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
return;
}
- // Check if the actor is a L2GuardInstance
- if ((npc instanceof L2GuardInstance) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
+ // Check if the actor is a guard
+ if (((npc instanceof L2GuardInstance) || (npc instanceof L2DefenderInstance)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
{
// Order to the L2GuardInstance to return to its home location because there's no target to attack
npc.returnHome();
diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
deleted file mode 100644
index 426228ee50..0000000000
--- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2FortCommanderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2FortSiegeGuardAI(L2Character accessor)
- {
- super(accessor);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- private boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead() || (target instanceof L2FortCommanderInstance) || target.isPlayable())
- {
- L2PcInstance player = null;
- if (target instanceof L2PcInstance)
- {
- player = ((L2PcInstance) target);
- }
- else if (target instanceof L2Summon)
- {
- player = ((L2Summon) target).getOwner();
- }
- if ((player == null) || ((player.getClan() != null) && (player.getClan().getFortId() == ((L2Npc) _actor).getFort().getResidenceId())))
- {
- return false;
- }
- }
-
- // Check if the target isn't invulnerable
- if ((target != null) && target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target instanceof L2Summon)
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target instanceof L2Playable)
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
-
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
- if (_actor.getWalkSpeed() >= 0)
- {
- if (_actor instanceof L2DefenderInstance)
- {
- ((L2DefenderInstance) _actor).returnHome();
- }
- else
- {
- ((L2FortCommanderInstance) _actor).returnHome();
- }
- }
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (cha == null)
- {
- continue;
- }
-
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && ((L2Npc) _actor).getFort().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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- L2DefenderInstance sGuard;
- if (_actor instanceof L2FortCommanderInstance)
- {
- sGuard = (L2FortCommanderInstance) _actor;
- }
- else
- {
- sGuard = (L2DefenderInstance) _actor;
- }
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- // LOGGER.warning("AttackableAI: Attack target is NULL.");
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // never attack defenders
- if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
- {
- // Cancel the target
- sGuard.stopHating(attackTarget);
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- 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
- final 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 (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- _actor.doAttack(attackTarget);
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the actor can't use skills and if a thinking action isn't already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = _actor instanceof L2FortCommanderInstance ? (L2FortCommanderInstance) _actor : (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
deleted file mode 100644
index 7cd4bb5dc1..0000000000
--- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
- // private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2SiegeGuardAI(L2Character creature)
- {
- super(creature);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- protected boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead())
- {
- return false;
- }
-
- // Check if the target isn't invulnerable
- if (target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target.isSummon())
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target.isPlayable())
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2DefenderInstance to return to its home location because there's no target to attack
- ((L2DefenderInstance) _actor).returnHome();
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && (((L2Npc) _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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isHealer)
- {
- return;
- }
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer)
- {
- // Temporary hack for preventing guards jumping off towers,
- // before replacing this with effective geodata checks and AI modification
- final 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- if (!_selfAnalysis.isHealer)
- {
- _actor.doAttack(attackTarget);
- }
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the thinking action is already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
deleted file mode 100644
index 7d8c8dfcaa..0000000000
--- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.l2jmobius.gameserver.model.actor.L2Character;
-
-/**
- * @author BiggBoss
- */
-public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI
-{
- private final List _allied = new ArrayList<>();
-
- public L2SpecialSiegeGuardAI(L2Character creature)
- {
- super(creature);
- }
-
- public List getAlly()
- {
- return _allied;
- }
-
- @Override
- protected boolean autoAttackCondition(L2Character target)
- {
- if (_allied.contains(target.getObjectId()))
- {
- return false;
- }
-
- return super.autoAttackCondition(target);
- }
-}
diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
index 9d12eccfd5..920a3e1b39 100644
--- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
+++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
@@ -38,8 +38,6 @@ import com.l2jmobius.gameserver.ai.CtrlEvent;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.ai.L2AttackableAI;
import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2FortSiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
import com.l2jmobius.gameserver.data.xml.impl.ExtendDropData;
import com.l2jmobius.gameserver.datatables.EventDroplist;
import com.l2jmobius.gameserver.datatables.EventDroplist.DateDrop;
@@ -749,15 +747,6 @@ public class L2Attackable extends L2Npc
public void reduceHate(L2Character target, int amount)
{
- if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
- {
- // TODO: this just prevents error until siege guards are handled properly
- stopHating(target);
- setTarget(null);
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
- return;
- }
-
if (target == null) // whole aggrolist
{
final L2Character mostHated = getMostHated();
diff --git a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
index 8a45fb20e5..955f062388 100644
--- a/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
+++ b/L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
@@ -18,9 +18,6 @@ package com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.ai.CtrlIntention;
-import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SpecialSiegeGuardAI;
import com.l2jmobius.gameserver.enums.InstanceType;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
@@ -45,16 +42,6 @@ public class L2DefenderInstance extends L2Attackable
setInstanceType(InstanceType.L2DefenderInstance);
}
- @Override
- protected L2CharacterAI initAI()
- {
- if (getCastle(10000) != null)
- {
- return new L2SiegeGuardAI(this);
- }
- return new L2SpecialSiegeGuardAI(this);
- }
-
@Override
public void addDamage(L2Character attacker, int damage, Skill skill)
{
@@ -189,6 +176,39 @@ public class L2DefenderInstance extends L2Attackable
player.sendPacket(ActionFailed.STATIC_PACKET);
}
+ @Override
+ public void useMagic(Skill skill)
+ {
+ if (!skill.isBad())
+ {
+ L2Character target = this;
+ for (L2Character nearby : L2World.getInstance().getVisibleObjects(this, L2Character.class, skill.getCastRange()))
+ {
+ if (nearby == this)
+ {
+ continue;
+ }
+ if (nearby instanceof L2DefenderInstance)
+ {
+ target = nearby;
+ }
+ if (nearby.isPlayer())
+ {
+ final L2PcInstance player = (L2PcInstance) nearby;
+ final Castle castle = getCastle();
+ final Fort fortress = getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId))
+ {
+ target = nearby;
+ }
+ }
+ }
+ setTarget(target);
+ }
+ super.useMagic(skill);
+ }
+
@Override
public void addDamageHate(L2Character attacker, int damage, int aggro)
{
diff --git a/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/SiegeGuards.java b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/SiegeGuards.java
new file mode 100644
index 0000000000..fb9007f8c8
--- /dev/null
+++ b/L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/SiegeGuards.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * 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 .
+ */
+package ai.others;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.actor.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.L2Summon;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.entity.Castle;
+import com.l2jmobius.gameserver.model.entity.Fort;
+
+import ai.AbstractNpcAI;
+
+/**
+ * @author Mobius
+ */
+public class SiegeGuards extends AbstractNpcAI
+{
+ //@formatter:off
+ // NPCs
+ private static final int[] CASTLE_GUARDS =
+ {
+ 35064, 35065, 35066, 35067, 35068, 35069, 35071, 35072, 35079, 35080, 35081, 35082, 35083, 35084, 35085, // Gludio
+ 35106, 35107, 35108, 35109, 35110, 35111, 35113, 35114, 35121, 35122, 35123,35124, 35125, 35126, 35127, // Dion
+ 35150, 35151, 35152, 35153, 35155, 35156, 35163, 35164, 35165, 35166, 35167, 35168, 35169, // Giran
+ 35192, 35193, 35194, 35195, 35197, 35198, 35205, 35206, 35207, 35208, 35209, 35210, 35211, // Oren
+ 35234, 35239, 35240, 35248, 35249, 35250, 35251, 35252, 35253, 35254, // Aden
+ 35280, 35281, 35282, 35283, 35284, 35285, 35287, 35288, 35295, 35296, 35297, 35298, 35299, 35300, 35301, // Innadril
+ 35324, 35325, 35326, 35327, 35328, 35330, 35339, 35340, 35341, 35343, 35350, 35351, // Goddard
+ 35475, 35477, 35480, 35484, 35486, 35487, 35488, 35489, 35490, // Rune
+ 35516, 35517, 35518, 35519, 35520, 35522, 35531, 35532, 35533, 35535, 35542, 35543, // Schuttgart
+ };
+ private static final int[] FORT_GUARDS =
+ {
+ 35670, 35671, 35672, 35673, 35674, 35678, 35679, 35681, 35682, 35684, 35685, // Shanty
+ 35702, 35703, 35704, 35705, 35706, 35707, 35708, 35709, 35710, 35711, 35712, 35714, 35715, 35717, 35718, 35720, 35722, 35723, // Southern
+ 35739, 35740, 35741, 35742, 35743, 35747, 35748, 35750, 35751, 35753, 35754, // Hive
+ 35758, 35767, 35771, 35772, 35773, 35774, 35775, 35776, 35777, 35778, 35779, 35780, 35781, 35783, 35784, 35786, 35787, 35789, 35791, 35792, // Valley
+ 35808, 35809, 35810, 35811, 35812, 35816, 35817, 35819, 35820, 35822, 35823, // Ivory
+ 35839, 35840, 35841, 35842, 35843, 35847, 35848, 35850, 35851, 35853, 35854, // Narsell
+ 35868, 35869, 35871, 35872, 35873, 35874, 35875, 35876, 35877, 35878, 35879, 35880, 35881, 35883, 35884, 35886, 35887, 35889, 35891, 35892, // Bayou
+ 35908, 35909, 35910, 35911, 35912, 35916, 35917, 35919, 35920, 35922, 35923, // White Sands
+ 35940, 35941, 35942, 35943, 35944, 35945, 35946, 35947, 35948, 35949, 35950, 35952, 35953, 35955, 35956, 35958, 35960, 35961, // Borderland
+ 35978, 35979, 35980, 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35988, 35990, 35991, 35993, 35994, 35996, 35998, 35999, // Swamp
+ 36015, 36016, 36017, 36018, 36019, 36023, 36024, 36026, 36027, 36029, 36030, // Archaic
+ 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, 36056, 36057, 36059, 36060, 36062, 36063, 36065, 36067, 36068, 36468, // Floran
+ 36079, 36085, 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, 36097, 36098, 36100, 36101, 36103, 36105, 36106, // Removed on Helios (113)
+ 36122, 36123, 36124, 36125, 36126, 36130, 36131, 36133, 36134, 36136, 36137, // Tanor
+ 36153, 36154, 36155, 36156, 36157, 36161, 36162, 36164, 36165, 36167, 36168, // Dragonspine
+ 36185, 36186, 36187, 36188, 36189, 36190, 36191, 36192, 36193, 36194, 36195, 36197, 36198, 36200, 36201, 36203, 36205, 36206, // Antharas
+ 36223, 36224, 36225, 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36235, 36236, 36238, 36239, 36241, 36243, 36244, // Western
+ 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36273, 36274, 36276, 36277, 36279, 36281, 36282, // Hunters
+ 36298, 36299, 36300, 36301, 36302, 36306, 36307, 36309, 36310, 36312, 36313, // Aaru
+ 36330, 36331, 36332, 36333, 36334, 36342, 36343, 36345, 36346, 36348, 36351, // Demon
+ 36368, 36369, 36370, 36371, 36372, 36380, 36381, 36383, 36384, 36386, 36389 // Monastic
+ };
+ private static final int[] MERCENARIES =
+ {
+ 35015, 35016, 35017, 35018, 35019, 35025, 35026, 35027, 35028, 35029, 35035, 35036, 35037, 35038, 35039, 35045, 35046, 35047, 35048, 35049, 35055, 35056, 35057, 35058, 35059, 35060, 35061
+ };
+ private static final int[] STATIONARY_MERCENARIES =
+ {
+ 35010, 35011, 35012, 35013, 35014, 35020, 35021, 35022, 35023, 35024, 35030, 35031, 35032, 35033, 35034, 35040, 35041, 35042, 35043, 35044, 35050, 35051, 35052, 35053, 35054, 35092, 35093, 35094,
+ 35134, 35135, 35136, 35176, 35177, 35178, 35218, 35219, 35220, 35261, 35262, 35263, 35264, 35265, 35308, 35309, 35310, 35352, 35353, 35354, 35497, 35498, 35499, 35500, 35501, 35544, 35545, 35546
+ };
+ //@formatter:on
+
+ public SiegeGuards()
+ {
+ addAttackId(CASTLE_GUARDS);
+ addAttackId(FORT_GUARDS);
+ addAttackId(MERCENARIES);
+ addAttackId(STATIONARY_MERCENARIES);
+ addSeeCreatureId(CASTLE_GUARDS);
+ addSeeCreatureId(FORT_GUARDS);
+ addSeeCreatureId(MERCENARIES);
+ addSeeCreatureId(STATIONARY_MERCENARIES);
+ addSpawnId(CASTLE_GUARDS);
+ addSpawnId(FORT_GUARDS);
+ addSpawnId(MERCENARIES);
+ addSpawnId(STATIONARY_MERCENARIES);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((attacker.getSiegeState() == 2) && !attacker.isRegisteredOnThisSiegeField(activeSiegeId)) || (attacker.getSiegeState() == 0)) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
+ {
+ ((L2Attackable) npc).stopHating(attacker);
+ return null;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
+ {
+ if (creature.isPlayable() && (npc.calculateDirectionTo(creature) < npc.getAggroRange()) && GeoEngine.getInstance().canSeeTarget(npc, creature))
+ {
+ final L2Summon summon = isSummon ? creature.getServitors().values().stream().findFirst().orElse(creature.getPet()) : null;
+ final L2PcInstance cha = summon == null ? (L2PcInstance) creature : summon.getOwner();
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((cha.getSiegeState() != 2) || cha.isRegisteredOnThisSiegeField(activeSiegeId)) && (cha.getSiegeState() != 0)) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_IDLE))
+ {
+ if (!cha.isInvisible()) // skip invisible players
+ {
+ addAttackPlayerDesire(npc, cha);
+ }
+ }
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(false);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new SiegeGuards();
+ }
+}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
index 28f68acde3..6253fa9329 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
@@ -42,6 +42,7 @@ import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Playable;
+import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
@@ -444,8 +445,8 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
return;
}
- // Check if the actor is a L2GuardInstance
- if ((npc instanceof L2GuardInstance) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
+ // Check if the actor is a guard
+ if (((npc instanceof L2GuardInstance) || (npc instanceof L2DefenderInstance)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
{
// Order to the L2GuardInstance to return to its home location because there's no target to attack
npc.returnHome();
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
deleted file mode 100644
index 426228ee50..0000000000
--- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2FortCommanderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2FortSiegeGuardAI(L2Character accessor)
- {
- super(accessor);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- private boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead() || (target instanceof L2FortCommanderInstance) || target.isPlayable())
- {
- L2PcInstance player = null;
- if (target instanceof L2PcInstance)
- {
- player = ((L2PcInstance) target);
- }
- else if (target instanceof L2Summon)
- {
- player = ((L2Summon) target).getOwner();
- }
- if ((player == null) || ((player.getClan() != null) && (player.getClan().getFortId() == ((L2Npc) _actor).getFort().getResidenceId())))
- {
- return false;
- }
- }
-
- // Check if the target isn't invulnerable
- if ((target != null) && target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target instanceof L2Summon)
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target instanceof L2Playable)
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
-
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
- if (_actor.getWalkSpeed() >= 0)
- {
- if (_actor instanceof L2DefenderInstance)
- {
- ((L2DefenderInstance) _actor).returnHome();
- }
- else
- {
- ((L2FortCommanderInstance) _actor).returnHome();
- }
- }
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (cha == null)
- {
- continue;
- }
-
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && ((L2Npc) _actor).getFort().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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- L2DefenderInstance sGuard;
- if (_actor instanceof L2FortCommanderInstance)
- {
- sGuard = (L2FortCommanderInstance) _actor;
- }
- else
- {
- sGuard = (L2DefenderInstance) _actor;
- }
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- // LOGGER.warning("AttackableAI: Attack target is NULL.");
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // never attack defenders
- if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
- {
- // Cancel the target
- sGuard.stopHating(attackTarget);
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- 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
- final 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 (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- _actor.doAttack(attackTarget);
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the actor can't use skills and if a thinking action isn't already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = _actor instanceof L2FortCommanderInstance ? (L2FortCommanderInstance) _actor : (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
\ No newline at end of file
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
deleted file mode 100644
index 7cd4bb5dc1..0000000000
--- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
- // private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2SiegeGuardAI(L2Character creature)
- {
- super(creature);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- protected boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead())
- {
- return false;
- }
-
- // Check if the target isn't invulnerable
- if (target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target.isSummon())
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target.isPlayable())
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2DefenderInstance to return to its home location because there's no target to attack
- ((L2DefenderInstance) _actor).returnHome();
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && (((L2Npc) _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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isHealer)
- {
- return;
- }
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer)
- {
- // Temporary hack for preventing guards jumping off towers,
- // before replacing this with effective geodata checks and AI modification
- final 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- if (!_selfAnalysis.isHealer)
- {
- _actor.doAttack(attackTarget);
- }
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the thinking action is already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
deleted file mode 100644
index 7d8c8dfcaa..0000000000
--- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.l2jmobius.gameserver.model.actor.L2Character;
-
-/**
- * @author BiggBoss
- */
-public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI
-{
- private final List _allied = new ArrayList<>();
-
- public L2SpecialSiegeGuardAI(L2Character creature)
- {
- super(creature);
- }
-
- public List getAlly()
- {
- return _allied;
- }
-
- @Override
- protected boolean autoAttackCondition(L2Character target)
- {
- if (_allied.contains(target.getObjectId()))
- {
- return false;
- }
-
- return super.autoAttackCondition(target);
- }
-}
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
index 9d12eccfd5..920a3e1b39 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
@@ -38,8 +38,6 @@ import com.l2jmobius.gameserver.ai.CtrlEvent;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.ai.L2AttackableAI;
import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2FortSiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
import com.l2jmobius.gameserver.data.xml.impl.ExtendDropData;
import com.l2jmobius.gameserver.datatables.EventDroplist;
import com.l2jmobius.gameserver.datatables.EventDroplist.DateDrop;
@@ -749,15 +747,6 @@ public class L2Attackable extends L2Npc
public void reduceHate(L2Character target, int amount)
{
- if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
- {
- // TODO: this just prevents error until siege guards are handled properly
- stopHating(target);
- setTarget(null);
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
- return;
- }
-
if (target == null) // whole aggrolist
{
final L2Character mostHated = getMostHated();
diff --git a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
index 8a45fb20e5..955f062388 100644
--- a/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
+++ b/L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
@@ -18,9 +18,6 @@ package com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.ai.CtrlIntention;
-import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SpecialSiegeGuardAI;
import com.l2jmobius.gameserver.enums.InstanceType;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
@@ -45,16 +42,6 @@ public class L2DefenderInstance extends L2Attackable
setInstanceType(InstanceType.L2DefenderInstance);
}
- @Override
- protected L2CharacterAI initAI()
- {
- if (getCastle(10000) != null)
- {
- return new L2SiegeGuardAI(this);
- }
- return new L2SpecialSiegeGuardAI(this);
- }
-
@Override
public void addDamage(L2Character attacker, int damage, Skill skill)
{
@@ -189,6 +176,39 @@ public class L2DefenderInstance extends L2Attackable
player.sendPacket(ActionFailed.STATIC_PACKET);
}
+ @Override
+ public void useMagic(Skill skill)
+ {
+ if (!skill.isBad())
+ {
+ L2Character target = this;
+ for (L2Character nearby : L2World.getInstance().getVisibleObjects(this, L2Character.class, skill.getCastRange()))
+ {
+ if (nearby == this)
+ {
+ continue;
+ }
+ if (nearby instanceof L2DefenderInstance)
+ {
+ target = nearby;
+ }
+ if (nearby.isPlayer())
+ {
+ final L2PcInstance player = (L2PcInstance) nearby;
+ final Castle castle = getCastle();
+ final Fort fortress = getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId))
+ {
+ target = nearby;
+ }
+ }
+ }
+ setTarget(target);
+ }
+ super.useMagic(skill);
+ }
+
@Override
public void addDamageHate(L2Character attacker, int damage, int aggro)
{
diff --git a/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/SiegeGuards.java b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/SiegeGuards.java
new file mode 100644
index 0000000000..fb9007f8c8
--- /dev/null
+++ b/L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/SiegeGuards.java
@@ -0,0 +1,147 @@
+/*
+ * This file is part of the L2J Mobius project.
+ *
+ * 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 .
+ */
+package ai.others;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.actor.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.L2Summon;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.entity.Castle;
+import com.l2jmobius.gameserver.model.entity.Fort;
+
+import ai.AbstractNpcAI;
+
+/**
+ * @author Mobius
+ */
+public class SiegeGuards extends AbstractNpcAI
+{
+ //@formatter:off
+ // NPCs
+ private static final int[] CASTLE_GUARDS =
+ {
+ 35064, 35065, 35066, 35067, 35068, 35069, 35071, 35072, 35079, 35080, 35081, 35082, 35083, 35084, 35085, // Gludio
+ 35106, 35107, 35108, 35109, 35110, 35111, 35113, 35114, 35121, 35122, 35123,35124, 35125, 35126, 35127, // Dion
+ 35150, 35151, 35152, 35153, 35155, 35156, 35163, 35164, 35165, 35166, 35167, 35168, 35169, // Giran
+ 35192, 35193, 35194, 35195, 35197, 35198, 35205, 35206, 35207, 35208, 35209, 35210, 35211, // Oren
+ 35234, 35239, 35240, 35248, 35249, 35250, 35251, 35252, 35253, 35254, // Aden
+ 35280, 35281, 35282, 35283, 35284, 35285, 35287, 35288, 35295, 35296, 35297, 35298, 35299, 35300, 35301, // Innadril
+ 35324, 35325, 35326, 35327, 35328, 35330, 35339, 35340, 35341, 35343, 35350, 35351, // Goddard
+ 35475, 35477, 35480, 35484, 35486, 35487, 35488, 35489, 35490, // Rune
+ 35516, 35517, 35518, 35519, 35520, 35522, 35531, 35532, 35533, 35535, 35542, 35543, // Schuttgart
+ };
+ private static final int[] FORT_GUARDS =
+ {
+ 35670, 35671, 35672, 35673, 35674, 35678, 35679, 35681, 35682, 35684, 35685, // Shanty
+ 35702, 35703, 35704, 35705, 35706, 35707, 35708, 35709, 35710, 35711, 35712, 35714, 35715, 35717, 35718, 35720, 35722, 35723, // Southern
+ 35739, 35740, 35741, 35742, 35743, 35747, 35748, 35750, 35751, 35753, 35754, // Hive
+ 35758, 35767, 35771, 35772, 35773, 35774, 35775, 35776, 35777, 35778, 35779, 35780, 35781, 35783, 35784, 35786, 35787, 35789, 35791, 35792, // Valley
+ 35808, 35809, 35810, 35811, 35812, 35816, 35817, 35819, 35820, 35822, 35823, // Ivory
+ 35839, 35840, 35841, 35842, 35843, 35847, 35848, 35850, 35851, 35853, 35854, // Narsell
+ 35868, 35869, 35871, 35872, 35873, 35874, 35875, 35876, 35877, 35878, 35879, 35880, 35881, 35883, 35884, 35886, 35887, 35889, 35891, 35892, // Bayou
+ 35908, 35909, 35910, 35911, 35912, 35916, 35917, 35919, 35920, 35922, 35923, // White Sands
+ 35940, 35941, 35942, 35943, 35944, 35945, 35946, 35947, 35948, 35949, 35950, 35952, 35953, 35955, 35956, 35958, 35960, 35961, // Borderland
+ 35978, 35979, 35980, 35981, 35982, 35983, 35984, 35985, 35986, 35987, 35988, 35990, 35991, 35993, 35994, 35996, 35998, 35999, // Swamp
+ 36015, 36016, 36017, 36018, 36019, 36023, 36024, 36026, 36027, 36029, 36030, // Archaic
+ 36047, 36048, 36049, 36050, 36051, 36052, 36053, 36054, 36055, 36056, 36057, 36059, 36060, 36062, 36063, 36065, 36067, 36068, 36468, // Floran
+ 36079, 36085, 36086, 36087, 36088, 36089, 36090, 36091, 36092, 36093, 36094, 36095, 36097, 36098, 36100, 36101, 36103, 36105, 36106, // Removed on Helios (113)
+ 36122, 36123, 36124, 36125, 36126, 36130, 36131, 36133, 36134, 36136, 36137, // Tanor
+ 36153, 36154, 36155, 36156, 36157, 36161, 36162, 36164, 36165, 36167, 36168, // Dragonspine
+ 36185, 36186, 36187, 36188, 36189, 36190, 36191, 36192, 36193, 36194, 36195, 36197, 36198, 36200, 36201, 36203, 36205, 36206, // Antharas
+ 36223, 36224, 36225, 36226, 36227, 36228, 36229, 36230, 36231, 36232, 36233, 36235, 36236, 36238, 36239, 36241, 36243, 36244, // Western
+ 36261, 36262, 36263, 36264, 36265, 36266, 36267, 36268, 36269, 36270, 36271, 36273, 36274, 36276, 36277, 36279, 36281, 36282, // Hunters
+ 36298, 36299, 36300, 36301, 36302, 36306, 36307, 36309, 36310, 36312, 36313, // Aaru
+ 36330, 36331, 36332, 36333, 36334, 36342, 36343, 36345, 36346, 36348, 36351, // Demon
+ 36368, 36369, 36370, 36371, 36372, 36380, 36381, 36383, 36384, 36386, 36389 // Monastic
+ };
+ private static final int[] MERCENARIES =
+ {
+ 35015, 35016, 35017, 35018, 35019, 35025, 35026, 35027, 35028, 35029, 35035, 35036, 35037, 35038, 35039, 35045, 35046, 35047, 35048, 35049, 35055, 35056, 35057, 35058, 35059, 35060, 35061
+ };
+ private static final int[] STATIONARY_MERCENARIES =
+ {
+ 35010, 35011, 35012, 35013, 35014, 35020, 35021, 35022, 35023, 35024, 35030, 35031, 35032, 35033, 35034, 35040, 35041, 35042, 35043, 35044, 35050, 35051, 35052, 35053, 35054, 35092, 35093, 35094,
+ 35134, 35135, 35136, 35176, 35177, 35178, 35218, 35219, 35220, 35261, 35262, 35263, 35264, 35265, 35308, 35309, 35310, 35352, 35353, 35354, 35497, 35498, 35499, 35500, 35501, 35544, 35545, 35546
+ };
+ //@formatter:on
+
+ public SiegeGuards()
+ {
+ addAttackId(CASTLE_GUARDS);
+ addAttackId(FORT_GUARDS);
+ addAttackId(MERCENARIES);
+ addAttackId(STATIONARY_MERCENARIES);
+ addSeeCreatureId(CASTLE_GUARDS);
+ addSeeCreatureId(FORT_GUARDS);
+ addSeeCreatureId(MERCENARIES);
+ addSeeCreatureId(STATIONARY_MERCENARIES);
+ addSpawnId(CASTLE_GUARDS);
+ addSpawnId(FORT_GUARDS);
+ addSpawnId(MERCENARIES);
+ addSpawnId(STATIONARY_MERCENARIES);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((attacker.getSiegeState() == 2) && !attacker.isRegisteredOnThisSiegeField(activeSiegeId)) || (attacker.getSiegeState() == 0)) && (npc.getAI().getIntention() == CtrlIntention.AI_INTENTION_IDLE))
+ {
+ ((L2Attackable) npc).stopHating(attacker);
+ return null;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
+ {
+ if (creature.isPlayable() && (npc.calculateDirectionTo(creature) < npc.getAggroRange()) && GeoEngine.getInstance().canSeeTarget(npc, creature))
+ {
+ final L2Summon summon = isSummon ? creature.getServitors().values().stream().findFirst().orElse(creature.getPet()) : null;
+ final L2PcInstance cha = summon == null ? (L2PcInstance) creature : summon.getOwner();
+ final Castle castle = npc.getCastle();
+ final Fort fortress = npc.getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((((cha.getSiegeState() != 2) || cha.isRegisteredOnThisSiegeField(activeSiegeId)) && (cha.getSiegeState() != 0)) || (npc.getAI().getIntention() != CtrlIntention.AI_INTENTION_IDLE))
+ {
+ if (!cha.isInvisible()) // skip invisible players
+ {
+ addAttackPlayerDesire(npc, cha);
+ }
+ }
+ }
+ return super.onSeeCreature(npc, creature, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(false);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new SiegeGuards();
+ }
+}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
index 28f68acde3..6253fa9329 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2AttackableAI.java
@@ -42,6 +42,7 @@ import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.L2Character;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.gameserver.model.actor.L2Playable;
+import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2MonsterInstance;
@@ -444,8 +445,8 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
return;
}
- // Check if the actor is a L2GuardInstance
- if ((npc instanceof L2GuardInstance) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
+ // Check if the actor is a guard
+ if (((npc instanceof L2GuardInstance) || (npc instanceof L2DefenderInstance)) && !npc.isWalker() && !npc.isRandomWalkingEnabled())
{
// Order to the L2GuardInstance to return to its home location because there's no target to attack
npc.returnHome();
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
deleted file mode 100644
index 426228ee50..0000000000
--- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java
+++ /dev/null
@@ -1,931 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2FortCommanderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2FortSiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2FortSiegeGuardAI(L2Character accessor)
- {
- super(accessor);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- private boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead() || (target instanceof L2FortCommanderInstance) || target.isPlayable())
- {
- L2PcInstance player = null;
- if (target instanceof L2PcInstance)
- {
- player = ((L2PcInstance) target);
- }
- else if (target instanceof L2Summon)
- {
- player = ((L2Summon) target).getOwner();
- }
- if ((player == null) || ((player.getClan() != null) && (player.getClan().getFortId() == ((L2Npc) _actor).getFort().getResidenceId())))
- {
- return false;
- }
- }
-
- // Check if the target isn't invulnerable
- if ((target != null) && target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target instanceof L2Summon)
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target instanceof L2Playable)
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
-
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2SiegeGuardInstance to return to its home location because there's no target to attack
- if (_actor.getWalkSpeed() >= 0)
- {
- if (_actor instanceof L2DefenderInstance)
- {
- ((L2DefenderInstance) _actor).returnHome();
- }
- else
- {
- ((L2FortCommanderInstance) _actor).returnHome();
- }
- }
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (cha == null)
- {
- continue;
- }
-
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && ((L2Npc) _actor).getFort().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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- L2DefenderInstance sGuard;
- if (_actor instanceof L2FortCommanderInstance)
- {
- sGuard = (L2FortCommanderInstance) _actor;
- }
- else
- {
- sGuard = (L2DefenderInstance) _actor;
- }
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- // LOGGER.warning("AttackableAI: Attack target is NULL.");
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // never attack defenders
- if ((attackTarget instanceof L2PcInstance) && sGuard.getFort().getSiege().checkIsDefender(((L2PcInstance) attackTarget).getClan()))
- {
- // Cancel the target
- sGuard.stopHating(attackTarget);
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- 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
- final 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 (_actor.getWalkSpeed() <= 0)
- {
- return;
- }
- 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- _actor.doAttack(attackTarget);
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the actor can't use skills and if a thinking action isn't already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = _actor instanceof L2FortCommanderInstance ? (L2FortCommanderInstance) _actor : (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
\ No newline at end of file
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
deleted file mode 100644
index 7cd4bb5dc1..0000000000
--- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java
+++ /dev/null
@@ -1,884 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
-import static com.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
-
-import java.util.Collection;
-import java.util.concurrent.Future;
-
-import com.l2jmobius.commons.util.Rnd;
-import com.l2jmobius.gameserver.GameTimeController;
-import com.l2jmobius.gameserver.ThreadPoolManager;
-import com.l2jmobius.gameserver.geoengine.GeoEngine;
-import com.l2jmobius.gameserver.model.L2Object;
-import com.l2jmobius.gameserver.model.L2World;
-import com.l2jmobius.gameserver.model.actor.L2Attackable;
-import com.l2jmobius.gameserver.model.actor.L2Character;
-import com.l2jmobius.gameserver.model.actor.L2Npc;
-import com.l2jmobius.gameserver.model.actor.L2Playable;
-import com.l2jmobius.gameserver.model.actor.L2Summon;
-import com.l2jmobius.gameserver.model.actor.instance.L2DefenderInstance;
-import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
-import com.l2jmobius.gameserver.model.effects.L2EffectType;
-import com.l2jmobius.gameserver.model.skills.Skill;
-import com.l2jmobius.gameserver.util.Util;
-
-/**
- * This class manages AI of L2Attackable.
- */
-public class L2SiegeGuardAI extends L2CharacterAI implements Runnable
-{
- private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
-
- /** The L2Attackable AI task executed every 1s (call onEvtThink method) */
- private Future> _aiTask;
-
- /** For attack AI, analysis of mob and its targets */
- private final SelfAnalysis _selfAnalysis = new SelfAnalysis();
- // private TargetAnalysis _mostHatedAnalysis = new TargetAnalysis();
-
- /** The delay after which the attacked is stopped */
- private int _attackTimeout;
-
- /** The L2Attackable aggro counter */
- private int _globalAggro;
-
- /** The flag used to indicate that a thinking action is in progress */
- private boolean _thinking; // to prevent recursive thinking
-
- private final int _attackRange;
-
- public L2SiegeGuardAI(L2Character creature)
- {
- super(creature);
- _selfAnalysis.init();
- _attackTimeout = Integer.MAX_VALUE;
- _globalAggro = -10; // 10 seconds timeout of ATTACK after respawn
- _attackRange = _actor.getPhysicalAttackRange();
- }
-
- @Override
- public void run()
- {
- // Launch actions corresponding to the Event Think
- onEvtThink();
- }
-
- /**
- * Actor is a L2GuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- * - The L2MonsterInstance target is aggressive
- *
- * Actor is a L2SiegeGuardInstance :
- *
- * - The target isn't a Folk or a Door
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - A siege is in progress
- * - The L2PcInstance target isn't a Defender
- *
- * Actor is a L2FriendlyMobInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The L2PcInstance target has karma (=PK)
- *
- * Actor is a L2MonsterInstance :
- *
- * - The target isn't a Folk, a Door or another L2NpcInstance
- * - The target isn't dead, isn't invulnerable, isn't in silent moving mode AND too far (>100)
- * - The target is in the actor Aggro range and is at the same height
- * - The actor is Aggressive
- *
- * @param target The targeted L2Object
- * @return True if the target is autoattackable (depends on the actor type).
- */
- protected boolean autoAttackCondition(L2Character target)
- {
- // Check if the target isn't another guard, folk or a door
- if ((target == null) || (target instanceof L2DefenderInstance) || target.isNpc() || target.isDoor() || target.isAlikeDead())
- {
- return false;
- }
-
- // Check if the target isn't invulnerable
- if (target.isInvul())
- {
- return false;
- }
-
- // Get the owner if the target is a summon
- if (target.isSummon())
- {
- final L2PcInstance owner = ((L2Summon) target).getOwner();
- if (_actor.isInsideRadius(owner, 1000, true, false))
- {
- target = owner;
- }
- }
-
- // Check if the target is a L2PcInstance
- if (target.isPlayable())
- {
- // Check if the target isn't in silent move mode AND too far (>100)
- if (((L2Playable) target).isSilentMovingAffected() && !_actor.isInsideRadius(target, 250, false, false))
- {
- return false;
- }
- }
- // Los Check Here
- return (_actor.isAutoAttackable(target) && GeoEngine.getInstance().canSeeTarget(_actor, target));
- }
-
- /**
- * Set the Intention of this L2CharacterAI and create an AI Task executed every 1s (call onEvtThink method) for this L2Attackable.
- * Caution : If actor _knowPlayer isn't EMPTY, AI_INTENTION_IDLE will be change in AI_INTENTION_ACTIVE
- * @param intention The new Intention to set to the AI
- * @param args The first parameter of the Intention
- */
- @Override
- synchronized void changeIntention(CtrlIntention intention, Object... args)
- {
- if (intention == AI_INTENTION_IDLE /* || intention == AI_INTENTION_ACTIVE */) // active becomes idle if only a summon is present
- {
- // Check if actor is not dead
- if (!_actor.isAlikeDead())
- {
- final L2Attackable npc = (L2Attackable) _actor;
-
- // If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
- if (!L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class).isEmpty())
- {
- intention = AI_INTENTION_ACTIVE;
- }
- else
- {
- intention = AI_INTENTION_IDLE;
- }
- }
-
- if (intention == AI_INTENTION_IDLE)
- {
- // Set the Intention of this L2AttackableAI to AI_INTENTION_IDLE
- super.changeIntention(AI_INTENTION_IDLE);
-
- // Stop AI task and detach AI from NPC
- if (_aiTask != null)
- {
- _aiTask.cancel(true);
- _aiTask = null;
- }
-
- // Cancel the AI
- _actor.detachAI();
-
- return;
- }
- }
-
- // Set the Intention of this L2AttackableAI to intention
- super.changeIntention(intention, args);
-
- // If not idle - create an AI task (schedule onEvtThink repeatedly)
- if (_aiTask == null)
- {
- _aiTask = ThreadPoolManager.scheduleAtFixedRate(this, 1000, 1000);
- }
- }
-
- /**
- * Manage the Attack Intention : Stop current Attack (if necessary), Calculate attack timeout, Start a new Attack and Launch Think Event.
- * @param target The L2Character to attack
- */
- @Override
- protected void onIntentionAttack(L2Character target)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
- // if (_actor.getTarget() != null)
- super.onIntentionAttack(target);
- }
-
- /**
- * Manage AI standard thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update every 1s the _globalAggro counter to come close to 0
- * - If the actor is Aggressive and can attack, add all autoAttackable L2Character in its Aggro Range to its _aggroList, chose a target and order to attack it
- * - If the actor can't attack, order to it to return to its home location
- *
- */
- private void thinkActive()
- {
- final L2Attackable npc = (L2Attackable) _actor;
- final L2Object target = getTarget();
- // Update every 1s the _globalAggro counter to come close to 0
- if (_globalAggro != 0)
- {
- if (_globalAggro < 0)
- {
- _globalAggro++;
- }
- else
- {
- _globalAggro--;
- }
- }
-
- // Add all autoAttackable L2Character in L2Attackable Aggro Range to its _aggroList with 0 damage and 1 hate
- // A L2Attackable isn't aggressive during 10s after its spawn because _globalAggro is set to -10
- if (_globalAggro >= 0)
- {
- L2World.getInstance().forEachVisibleObjectInRange(npc, L2Character.class, _attackRange, t ->
- {
- if (autoAttackCondition(t)) // check aggression
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int hating = npc.getHating(t);
-
- // Add the attacker to the L2Attackable _aggroList with 0 damage and 1 hate
- if (hating == 0)
- {
- npc.addDamageHate(t, 0, 1);
- }
- }
- });
-
- // Chose a target from its aggroList
- L2Character hated;
- if (_actor.isConfused() && (target != null) && target.isCharacter())
- {
- hated = (L2Character) target; // Force mobs to attack anybody if confused
- }
- else
- {
- hated = npc.getMostHated();
- // _mostHatedAnalysis.Update(hated);
- }
-
- // Order to the L2Attackable to attack the target
- if (hated != null)
- {
- // Get the hate level of the L2Attackable against this L2Character target contained in _aggroList
- final int aggro = npc.getHating(hated);
-
- if ((aggro + _globalAggro) > 0)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the AI Intention to AI_INTENTION_ATTACK
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated, null);
- }
-
- return;
- }
- }
- // Order to the L2DefenderInstance to return to its home location because there's no target to attack
- ((L2DefenderInstance) _actor).returnHome();
- }
-
- /**
- * Manage AI attack thinks of a L2Attackable (called by onEvtThink).
- * Actions :
- *
- * - Update the attack timeout if actor is running
- * - If target is dead or timeout is expired, stop this attack and set the Intention to AI_INTENTION_ACTIVE
- * - Call all L2Object of its Faction inside the Faction Range
- * - Chose a target and order to attack it with magic skill or physical attack
- *
- * TODO: Manage casting rules to healer mobs (like Ant Nurses)
- */
- private void thinkAttack()
- {
- if (_attackTimeout < GameTimeController.getInstance().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.getInstance().getGameTicks();
- }
- }
-
- final L2Object target = getTarget();
- final L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- // Check if target is dead or if timeout is expired to stop this attack
- if ((attackTarget == null) || attackTarget.isAlikeDead() || (_attackTimeout < GameTimeController.getInstance().getGameTicks()))
- {
- // Stop hating this target after the attack timeout or if target is dead
- if (attackTarget != null)
- {
- final L2Attackable npc = (L2Attackable) _actor;
- npc.stopHating(attackTarget);
- }
-
- // Cancel target and timeout
- _attackTimeout = Integer.MAX_VALUE;
- setTarget(null);
-
- // Set the AI Intention to AI_INTENTION_ACTIVE
- setIntention(AI_INTENTION_ACTIVE, null, null);
-
- _actor.setWalking();
- return;
- }
-
- factionNotifyAndSupport();
- attackPrepare();
- }
-
- private final void factionNotifyAndSupport()
- {
- final L2Object target = getTarget();
- // Call all L2Object of its Faction inside the Faction Range
- if ((((L2Npc) _actor).getTemplate().getClans() == null) || (target == null))
- {
- return;
- }
-
- if (target.isInvul())
- {
- return; // speeding it up for siege guards
- }
-
- // Go through all L2Character that belong to its faction
- // for (L2Character cha : _actor.getKnownList().getKnownCharactersInRadius(((L2NpcInstance) _actor).getFactionRange()+_actor.getTemplate().collisionRadius))
- for (L2Character cha : L2World.getInstance().getVisibleObjects(_actor, L2Character.class, 1000))
- {
- if (!cha.isNpc())
- {
- if (_selfAnalysis.hasHealOrResurrect && cha.isPlayer() && (((L2Npc) _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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, cha, true))
- {
- continue;
- }
-
- final int chance = 5;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, cha))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(cha);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- continue;
- }
-
- final L2Npc npc = (L2Npc) cha;
-
- if (!npc.isInMyClan((L2Npc) _actor))
- {
- continue;
- }
-
- if (npc.getAI() != null) // TODO: possibly check not needed
- {
- if (!npc.isDead() && (Math.abs(target.getZ() - npc.getZ()) < 600)
- // && _actor.getAttackByList().contains(getTarget())
- && ((npc.getAI()._intention == CtrlIntention.AI_INTENTION_IDLE) || (npc.getAI()._intention == CtrlIntention.AI_INTENTION_ACTIVE))
- // limiting aggro for siege guards
- && npc.isInsideRadius(target, 1500, true, false) && GeoEngine.getInstance().canSeeTarget(npc, target))
- {
- // Notify the L2Object AI with EVT_AGGRESSION
- npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, target, 1);
- return;
- }
- // 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 (Skill sk : _selfAnalysis.healSkills)
- {
- if (_actor.getCurrentMp() < sk.getMpConsume())
- {
- continue;
- }
- if (_actor.isSkillDisabled(sk))
- {
- continue;
- }
- if (!Util.checkIfInRange(sk.getCastRange(), _actor, npc, true))
- {
- continue;
- }
-
- final int chance = 4;
- if (chance >= Rnd.get(100))
- {
- continue;
- }
- if (!GeoEngine.getInstance().canSeeTarget(_actor, npc))
- {
- break;
- }
-
- final L2Object OldTarget = getTarget();
- setTarget(npc);
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- }
- }
-
- private void attackPrepare()
- {
- final L2Object target = getTarget();
- L2Character attackTarget = (target != null) && target.isCharacter() ? (L2Character) target : null;
- if (attackTarget == null)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- // Get all information needed to choose between physical or magical attack
- Collection skills = null;
- double dist_2 = 0;
- int range = 0;
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
-
- try
- {
- setTarget(attackTarget);
- skills = _actor.getAllSkills();
- dist_2 = _actor.calculateDistance(attackTarget, false, true);
- range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + attackTarget.getTemplate().getCollisionRadius();
- if (attackTarget.isMoving())
- {
- range += 50;
- }
- }
- catch (NullPointerException e)
- {
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- return;
- }
-
- if (!GeoEngine.getInstance().canSeeTarget(_actor, attackTarget))
- {
- // Siege guards differ from normal mobs currently:
- // If target cannot seen, don't attack any more
- sGuard.stopHating(attackTarget);
- 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 * range)))
- {
- // check for long ranged skills and heal/buff skills
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if ((dist_2 <= (castRange * castRange)) && (castRange > 70) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !sk.isPassive())
- {
-
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
-
- // Check if the L2SiegeGuardInstance is attacking, knows the target and can't run
- if (!(_actor.isAttackingNow()) && (_actor.getRunSpeed() == 0) && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- else
- {
- final double dx = _actor.getX() - attackTarget.getX();
- final double dy = _actor.getY() - attackTarget.getY();
- final double dz = _actor.getZ() - attackTarget.getZ();
- final double homeX = attackTarget.getX() - sGuard.getSpawn().getX();
- final double homeY = attackTarget.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance isn't too far from it's home location
- if ((((dx * dx) + (dy * dy)) > 10000) && (((homeX * homeX) + (homeY * homeY)) > 3240000) // 1800 * 1800
- && (_actor.isInSurroundingRegion(attackTarget)))
- {
- // Cancel the target
- setTarget(null);
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- 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
- {
- if (_selfAnalysis.isHealer)
- {
- return;
- }
- if (_selfAnalysis.isMage)
- {
- range = _selfAnalysis.maxCastRange - 50;
- }
- if (attackTarget.isMoving())
- {
- moveToPawn(attackTarget, range - 70);
- }
- else
- {
- moveToPawn(attackTarget, range);
- }
- }
- }
- }
-
- return;
-
- }
- // Else, if the actor is muted and far from target, just "move to pawn"
- else if (_actor.isMuted() && (dist_2 > (range * range)) && !_selfAnalysis.isHealer)
- {
- // Temporary hack for preventing guards jumping off towers,
- // before replacing this with effective geodata checks and AI modification
- final 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 * range))
- {
- // Force mobs to attack anybody if confused
- L2Character hated = null;
- if (_actor.isConfused())
- {
- hated = attackTarget;
- }
- else
- {
- hated = ((L2Attackable) _actor).getMostHated();
- }
-
- if (hated == null)
- {
- setIntention(AI_INTENTION_ACTIVE, null, null);
- return;
- }
- if (hated != attackTarget)
- {
- attackTarget = hated;
- }
-
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // check for close combat skills && heal/buff skills
- if (!_actor.isMuted() && (Rnd.nextInt(100) <= 5))
- {
- for (Skill sk : skills)
- {
- final int castRange = sk.getCastRange();
-
- if (((castRange * castRange) >= dist_2) && !sk.isPassive() && (_actor.getCurrentMp() >= _actor.getStat().getMpConsume(sk)) && !_actor.isSkillDisabled(sk))
- {
- final L2Object OldTarget = getTarget();
- if ((sk.isContinuous() && !sk.isDebuff()) || (sk.hasEffectType(L2EffectType.HEAL)))
- {
- boolean useSkillSelf = true;
- if ((sk.hasEffectType(L2EffectType.HEAL)) && (_actor.getCurrentHp() > (int) (_actor.getMaxHp() / 1.5)))
- {
- useSkillSelf = false;
- break;
- }
-
- if ((sk.isContinuous() && !sk.isDebuff()) && _actor.isAffectedBySkill(sk.getId()))
- {
- useSkillSelf = false;
- }
-
- if (useSkillSelf)
- {
- setTarget(_actor);
- }
- }
-
- _actor.doCast(sk);
- setTarget(OldTarget);
- return;
- }
- }
- }
- // Finally, do the physical attack itself
- if (!_selfAnalysis.isHealer)
- {
- _actor.doAttack(attackTarget);
- }
- }
- }
-
- /**
- * Manage AI thinking actions of a L2Attackable.
- */
- @Override
- protected void onEvtThink()
- {
- // if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
- // setIntention(AI_INTENTION_IDLE);
-
- // Check if the thinking action is already in progress
- if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
- {
- return;
- }
-
- // Start thinking action
- _thinking = true;
-
- try
- {
- // Manage AI thinks of a L2Attackable
- if (getIntention() == AI_INTENTION_ACTIVE)
- {
- thinkActive();
- }
- else if (getIntention() == AI_INTENTION_ATTACK)
- {
- thinkAttack();
- }
- }
- finally
- {
- // Stop thinking action
- _thinking = false;
- }
- }
-
- /**
- * Launch actions corresponding to the Event Attacked.
- * Actions :
- *
- * - Init the attack : Calculate the attack timeout, Set the _globalAggro to 0, Add the attacker to the actor _aggroList
- * - Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- * - Set the Intention to AI_INTENTION_ATTACK
- *
- * @param attacker The L2Character that attacks the actor
- */
- @Override
- protected void onEvtAttacked(L2Character attacker)
- {
- // Calculate the attack timeout
- _attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
-
- // Set the _globalAggro to 0 to permit attack even just after spawn
- if (_globalAggro < 0)
- {
- _globalAggro = 0;
- }
-
- // Add the attacker to the _aggroList of the actor
- ((L2Attackable) _actor).addDamageHate(attacker, 0, 1);
-
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- // Set the Intention to AI_INTENTION_ATTACK
- if (getIntention() != AI_INTENTION_ATTACK)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, attacker, null);
- }
-
- super.onEvtAttacked(attacker);
- }
-
- /**
- * Launch actions corresponding to the Event Aggression.
- * Actions :
- *
- * - Add the target to the actor _aggroList or update hate if already present
- * - Set the actor Intention to AI_INTENTION_ATTACK (if actor is L2GuardInstance check if it isn't too far from its home location)
- *
- * @param aggro The value of hate to add to the actor against the target
- */
- @Override
- protected void onEvtAggression(L2Character target, int aggro)
- {
- if (_actor == null)
- {
- return;
- }
- final L2Attackable me = (L2Attackable) _actor;
-
- if (target != null)
- {
- // Add the target to the actor _aggroList or update hate if already present
- me.addDamageHate(target, 0, aggro);
-
- // Get the hate of the actor against the target
- aggro = me.getHating(target);
-
- if (aggro <= 0)
- {
- if (me.getMostHated() == null)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- return;
- }
-
- // Set the actor AI Intention to AI_INTENTION_ATTACK
- if (getIntention() != CtrlIntention.AI_INTENTION_ATTACK)
- {
- // Set the L2Character movement type to run and send Server->Client packet ChangeMoveType to all others L2PcInstance
- if (!_actor.isRunning())
- {
- _actor.setRunning();
- }
-
- final L2DefenderInstance sGuard = (L2DefenderInstance) _actor;
- final double homeX = target.getX() - sGuard.getSpawn().getX();
- final double homeY = target.getY() - sGuard.getSpawn().getY();
-
- // Check if the L2SiegeGuardInstance is not too far from its home location
- if (((homeX * homeX) + (homeY * homeY)) < 3240000)
- {
- setIntention(CtrlIntention.AI_INTENTION_ATTACK, target, null);
- }
- }
- }
- else
- {
- // currently only for setting lower general aggro
- if (aggro >= 0)
- {
- return;
- }
-
- final L2Character mostHated = me.getMostHated();
- if (mostHated == null)
- {
- _globalAggro = -25;
- return;
- }
-
- for (L2Character aggroed : me.getAggroList().keySet())
- {
- me.addDamageHate(aggroed, 0, aggro);
- }
-
- aggro = me.getHating(mostHated);
- if (aggro <= 0)
- {
- _globalAggro = -25;
- me.clearAggroList();
- setIntention(AI_INTENTION_IDLE, null, null);
- }
- }
- }
-
- @Override
- public void stopAITask()
- {
- if (_aiTask != null)
- {
- _aiTask.cancel(false);
- _aiTask = null;
- }
- _actor.detachAI();
- super.stopAITask();
- }
-}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
deleted file mode 100644
index 7d8c8dfcaa..0000000000
--- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java
+++ /dev/null
@@ -1,51 +0,0 @@
-/*
- * This file is part of the L2J Mobius project.
- *
- * 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 .
- */
-package com.l2jmobius.gameserver.ai;
-
-import java.util.ArrayList;
-import java.util.List;
-
-import com.l2jmobius.gameserver.model.actor.L2Character;
-
-/**
- * @author BiggBoss
- */
-public final class L2SpecialSiegeGuardAI extends L2SiegeGuardAI
-{
- private final List _allied = new ArrayList<>();
-
- public L2SpecialSiegeGuardAI(L2Character creature)
- {
- super(creature);
- }
-
- public List getAlly()
- {
- return _allied;
- }
-
- @Override
- protected boolean autoAttackCondition(L2Character target)
- {
- if (_allied.contains(target.getObjectId()))
- {
- return false;
- }
-
- return super.autoAttackCondition(target);
- }
-}
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
index 1f14847338..ca1001110c 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/L2Attackable.java
@@ -38,8 +38,6 @@ import com.l2jmobius.gameserver.ai.CtrlEvent;
import com.l2jmobius.gameserver.ai.CtrlIntention;
import com.l2jmobius.gameserver.ai.L2AttackableAI;
import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2FortSiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
import com.l2jmobius.gameserver.data.xml.impl.ExtendDropData;
import com.l2jmobius.gameserver.datatables.EventDroplist;
import com.l2jmobius.gameserver.datatables.EventDroplist.DateDrop;
@@ -749,15 +747,6 @@ public class L2Attackable extends L2Npc
public void reduceHate(L2Character target, int amount)
{
- if ((getAI() instanceof L2SiegeGuardAI) || (getAI() instanceof L2FortSiegeGuardAI))
- {
- // TODO: this just prevents error until siege guards are handled properly
- stopHating(target);
- setTarget(null);
- getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
- return;
- }
-
if (target == null) // whole aggrolist
{
final L2Character mostHated = getMostHated();
diff --git a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
index 8a45fb20e5..955f062388 100644
--- a/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
+++ b/L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/model/actor/instance/L2DefenderInstance.java
@@ -18,9 +18,6 @@ package com.l2jmobius.gameserver.model.actor.instance;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.ai.CtrlIntention;
-import com.l2jmobius.gameserver.ai.L2CharacterAI;
-import com.l2jmobius.gameserver.ai.L2SiegeGuardAI;
-import com.l2jmobius.gameserver.ai.L2SpecialSiegeGuardAI;
import com.l2jmobius.gameserver.enums.InstanceType;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.FortManager;
@@ -45,16 +42,6 @@ public class L2DefenderInstance extends L2Attackable
setInstanceType(InstanceType.L2DefenderInstance);
}
- @Override
- protected L2CharacterAI initAI()
- {
- if (getCastle(10000) != null)
- {
- return new L2SiegeGuardAI(this);
- }
- return new L2SpecialSiegeGuardAI(this);
- }
-
@Override
public void addDamage(L2Character attacker, int damage, Skill skill)
{
@@ -189,6 +176,39 @@ public class L2DefenderInstance extends L2Attackable
player.sendPacket(ActionFailed.STATIC_PACKET);
}
+ @Override
+ public void useMagic(Skill skill)
+ {
+ if (!skill.isBad())
+ {
+ L2Character target = this;
+ for (L2Character nearby : L2World.getInstance().getVisibleObjects(this, L2Character.class, skill.getCastRange()))
+ {
+ if (nearby == this)
+ {
+ continue;
+ }
+ if (nearby instanceof L2DefenderInstance)
+ {
+ target = nearby;
+ }
+ if (nearby.isPlayer())
+ {
+ final L2PcInstance player = (L2PcInstance) nearby;
+ final Castle castle = getCastle();
+ final Fort fortress = getFort();
+ final int activeSiegeId = (fortress != null ? fortress.getResidenceId() : (castle != null ? castle.getResidenceId() : 0));
+ if ((player.getSiegeState() == 2) && !player.isRegisteredOnThisSiegeField(activeSiegeId))
+ {
+ target = nearby;
+ }
+ }
+ }
+ setTarget(target);
+ }
+ super.useMagic(skill);
+ }
+
@Override
public void addDamageHate(L2Character attacker, int damage, int aggro)
{