From f9afb225c77c5a11c339531f128cb24c8ab12b3a Mon Sep 17 00:00:00 2001 From: MobiusDev <8391001+MobiusDevelopment@users.noreply.github.com> Date: Sat, 11 Nov 2017 21:31:02 +0000 Subject: [PATCH] Partial move siege guard AI to datapack. --- .../data/scripts/ai/others/SiegeGuards.java | 147 +++ .../gameserver/ai/L2AttackableAI.java | 5 +- .../gameserver/ai/L2FortSiegeGuardAI.java | 931 ------------------ .../gameserver/ai/L2SiegeGuardAI.java | 884 ----------------- .../gameserver/ai/L2SpecialSiegeGuardAI.java | 51 - .../gameserver/model/actor/L2Attackable.java | 11 - .../actor/instance/L2DefenderInstance.java | 46 +- .../data/scripts/ai/others/SiegeGuards.java | 147 +++ .../gameserver/ai/L2AttackableAI.java | 5 +- .../gameserver/ai/L2FortSiegeGuardAI.java | 931 ------------------ .../gameserver/ai/L2SiegeGuardAI.java | 884 ----------------- .../gameserver/ai/L2SpecialSiegeGuardAI.java | 51 - .../gameserver/model/actor/L2Attackable.java | 11 - .../actor/instance/L2DefenderInstance.java | 46 +- .../data/scripts/ai/others/SiegeGuards.java | 147 +++ .../gameserver/ai/L2AttackableAI.java | 5 +- .../gameserver/ai/L2FortSiegeGuardAI.java | 931 ------------------ .../gameserver/ai/L2SiegeGuardAI.java | 884 ----------------- .../gameserver/ai/L2SpecialSiegeGuardAI.java | 51 - .../gameserver/model/actor/L2Attackable.java | 11 - .../actor/instance/L2DefenderInstance.java | 46 +- .../data/scripts/ai/others/SiegeGuards.java | 147 +++ .../gameserver/ai/L2AttackableAI.java | 5 +- .../gameserver/ai/L2FortSiegeGuardAI.java | 931 ------------------ .../gameserver/ai/L2SiegeGuardAI.java | 884 ----------------- .../gameserver/ai/L2SpecialSiegeGuardAI.java | 51 - .../gameserver/model/actor/L2Attackable.java | 11 - .../actor/instance/L2DefenderInstance.java | 46 +- .../data/scripts/ai/others/SiegeGuards.java | 147 +++ .../gameserver/ai/L2AttackableAI.java | 5 +- .../gameserver/ai/L2FortSiegeGuardAI.java | 931 ------------------ .../gameserver/ai/L2SiegeGuardAI.java | 884 ----------------- .../gameserver/ai/L2SpecialSiegeGuardAI.java | 51 - .../gameserver/model/actor/L2Attackable.java | 11 - .../actor/instance/L2DefenderInstance.java | 46 +- 35 files changed, 915 insertions(+), 9460 deletions(-) create mode 100644 L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/others/SiegeGuards.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java delete mode 100644 L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java create mode 100644 L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/others/SiegeGuards.java delete mode 100644 L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java delete mode 100644 L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java delete mode 100644 L2J_Mobius_2.5_Underground/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java create mode 100644 L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/others/SiegeGuards.java delete mode 100644 L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java delete mode 100644 L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java delete mode 100644 L2J_Mobius_3.0_Helios/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java create mode 100644 L2J_Mobius_4.0_GrandCrusade/dist/game/data/scripts/ai/others/SiegeGuards.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java delete mode 100644 L2J_Mobius_4.0_GrandCrusade/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java create mode 100644 L2J_Mobius_Classic_2.0_Saviors/dist/game/data/scripts/ai/others/SiegeGuards.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2FortSiegeGuardAI.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SiegeGuardAI.java delete mode 100644 L2J_Mobius_Classic_2.0_Saviors/java/com/l2jmobius/gameserver/ai/L2SpecialSiegeGuardAI.java 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 : - * - * Actor is a L2SiegeGuardInstance : - * - * Actor is a L2FriendlyMobInstance : - * - * Actor is a L2MonsterInstance : - * - * @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 : - * - */ - 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 : - * - * 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) {