Addition of AI related task managers.
This commit is contained in:
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,11 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.WorldObject;
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
@@ -42,6 +37,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToLocation;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -50,26 +46,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
public abstract class AbstractAI implements Ctrl
|
public abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
private static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
private NextAction _nextAction;
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @return the _nextAction
|
|
||||||
*/
|
|
||||||
public NextAction getNextAction()
|
|
||||||
{
|
|
||||||
return _nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/**
|
|
||||||
* @param nextAction the next action to set.
|
|
||||||
*/
|
|
||||||
public void setNextAction(NextAction nextAction)
|
|
||||||
{
|
|
||||||
_nextAction = nextAction;
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -97,9 +73,23 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
/** Different internal state flags */
|
/** Different internal state flags */
|
||||||
protected int _moveToPawnTimeout;
|
protected int _moveToPawnTimeout;
|
||||||
|
|
||||||
private Future<?> _followTask = null;
|
private NextAction _nextAction;
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
/**
|
||||||
|
* @return the _nextAction
|
||||||
|
*/
|
||||||
|
public NextAction getNextAction()
|
||||||
|
{
|
||||||
|
return _nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @param nextAction the next action to set.
|
||||||
|
*/
|
||||||
|
public void setNextAction(NextAction nextAction)
|
||||||
|
{
|
||||||
|
_nextAction = nextAction;
|
||||||
|
}
|
||||||
|
|
||||||
protected AbstractAI(Creature creature)
|
protected AbstractAI(Creature creature)
|
||||||
{
|
{
|
||||||
@@ -435,7 +425,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
* @param pawn
|
* @param pawn
|
||||||
* @param offset
|
* @param offset
|
||||||
*/
|
*/
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
if (!_actor.isMovementDisabled() && !_actor.isAttackingNow() && !_actor.isCastingNow())
|
||||||
@@ -720,57 +710,16 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
setTarget(target);
|
setTarget(target);
|
||||||
|
if (range == -1)
|
||||||
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(() ->
|
|
||||||
{
|
{
|
||||||
try
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
}
|
||||||
|
else
|
||||||
final WorldObject followTarget = getTarget(); // copy to prevent NPE
|
|
||||||
if (followTarget == null)
|
|
||||||
{
|
{
|
||||||
if (_actor.isSummon())
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
}
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, followRange))
|
|
||||||
{
|
|
||||||
if (!_actor.isInsideRadius3D(followTarget, 3000))
|
|
||||||
{
|
|
||||||
// if the target is too far (maybe also teleported)
|
|
||||||
if (_actor.isSummon())
|
|
||||||
{
|
|
||||||
((Summon) _actor).setFollowStatus(false);
|
|
||||||
}
|
|
||||||
|
|
||||||
setIntention(AI_INTENTION_IDLE);
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
moveToPawn(followTarget, followRange);
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Exception e)
|
|
||||||
{
|
|
||||||
LOGGER.warning("Error: " + e.getMessage());
|
|
||||||
}
|
|
||||||
}, 5, range == -1 ? FOLLOW_INTERVAL : ATTACK_FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -778,12 +727,7 @@ public abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public void setTarget(WorldObject target)
|
public void setTarget(WorldObject target)
|
||||||
|
|||||||
@@ -23,12 +23,10 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
|||||||
import java.util.Comparator;
|
import java.util.Comparator;
|
||||||
import java.util.List;
|
import java.util.List;
|
||||||
import java.util.Set;
|
import java.util.Set;
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
import java.util.logging.Logger;
|
||||||
import java.util.stream.Stream;
|
import java.util.stream.Stream;
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.enums.AISkillScope;
|
import org.l2jmobius.gameserver.enums.AISkillScope;
|
||||||
@@ -59,6 +57,7 @@ import org.l2jmobius.gameserver.model.items.instance.ItemInstance;
|
|||||||
import org.l2jmobius.gameserver.model.skills.Skill;
|
import org.l2jmobius.gameserver.model.skills.Skill;
|
||||||
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
import org.l2jmobius.gameserver.model.skills.SkillCaster;
|
||||||
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
import org.l2jmobius.gameserver.model.zone.ZoneId;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
import org.l2jmobius.gameserver.util.Util;
|
import org.l2jmobius.gameserver.util.Util;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -69,12 +68,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
private static final Logger LOGGER = Logger.getLogger(AttackableAI.class.getName());
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
private static final int MAX_ATTACK_TIMEOUT = 1200; // int ticks, i.e. 2min
|
||||||
/**
|
|
||||||
* The Attackable AI task executed every 1s (call onEvtThink method).
|
|
||||||
*/
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
/**
|
/**
|
||||||
* The delay after which the attacked is stopped.
|
* The delay after which the attacked is stopped.
|
||||||
*/
|
*/
|
||||||
@@ -192,21 +186,13 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public void startAITask()
|
public void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
public void stopAITask()
|
public void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
super.stopAITask();
|
super.stopAITask();
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1191,7 +1177,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
if (_thinking || getActiveChar().isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking)
|
if (_isThinking)
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -531,7 +531,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -83,7 +83,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -119,7 +119,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
@@ -203,7 +203,7 @@ public class DoppelgangerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
// Check if actor can move
|
// Check if actor can move
|
||||||
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
if (!_actor.isMovementDisabled() && (_actor.getMoveSpeed() > 0))
|
||||||
|
|||||||
@@ -339,7 +339,7 @@ public class PlayerAI extends PlayableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
if (_thinking && (getIntention() != AI_INTENTION_CAST))
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -165,7 +165,7 @@ public class SummonAI extends PlayableAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isCastingNow() || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -112,7 +112,7 @@ public abstract class VehicleAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void moveToPawn(WorldObject pawn, int offset)
|
public void moveToPawn(WorldObject pawn, int offset)
|
||||||
{
|
{
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, followRange))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius3D(followTarget, 3000))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,10 +20,6 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_FOLLOW;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
import java.util.logging.Logger;
|
|
||||||
|
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.model.Location;
|
import org.l2jmobius.gameserver.model.Location;
|
||||||
import org.l2jmobius.gameserver.model.Skill;
|
import org.l2jmobius.gameserver.model.Skill;
|
||||||
@@ -44,6 +40,7 @@ import org.l2jmobius.gameserver.network.serverpackets.MoveToPawn;
|
|||||||
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
import org.l2jmobius.gameserver.network.serverpackets.StopMove;
|
||||||
import org.l2jmobius.gameserver.network.serverpackets.StopRotation;
|
import org.l2jmobius.gameserver.network.serverpackets.StopRotation;
|
||||||
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.CreatureFollowTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Mother class of all objects AI in the world.<br>
|
* Mother class of all objects AI in the world.<br>
|
||||||
@@ -53,56 +50,6 @@ import org.l2jmobius.gameserver.taskmanager.AttackStanceTaskManager;
|
|||||||
*/
|
*/
|
||||||
abstract class AbstractAI implements Ctrl
|
abstract class AbstractAI implements Ctrl
|
||||||
{
|
{
|
||||||
protected static final Logger LOGGER = Logger.getLogger(AbstractAI.class.getName());
|
|
||||||
|
|
||||||
class FollowTask implements Runnable
|
|
||||||
{
|
|
||||||
protected int _range = 60;
|
|
||||||
protected boolean newtask = true;
|
|
||||||
|
|
||||||
public FollowTask()
|
|
||||||
{
|
|
||||||
// null
|
|
||||||
}
|
|
||||||
|
|
||||||
public FollowTask(int range)
|
|
||||||
{
|
|
||||||
_range = range;
|
|
||||||
}
|
|
||||||
|
|
||||||
@Override
|
|
||||||
public void run()
|
|
||||||
{
|
|
||||||
try
|
|
||||||
{
|
|
||||||
if (_followTask == null)
|
|
||||||
{
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
final Creature follow = getFollowTarget();
|
|
||||||
if (follow == null)
|
|
||||||
{
|
|
||||||
stopFollow();
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
if (!_actor.isInsideRadius(follow, _range, true, false))
|
|
||||||
{
|
|
||||||
moveToPawn(follow, _range);
|
|
||||||
}
|
|
||||||
else if (newtask)
|
|
||||||
{
|
|
||||||
newtask = false;
|
|
||||||
_actor.broadcastPacket(new MoveToPawn(_actor, follow, _range));
|
|
||||||
}
|
|
||||||
}
|
|
||||||
catch (Throwable t)
|
|
||||||
{
|
|
||||||
LOGGER.warning(t.getMessage());
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
/** The creature that this AI manages */
|
/** The creature that this AI manages */
|
||||||
protected final Creature _actor;
|
protected final Creature _actor;
|
||||||
|
|
||||||
@@ -132,10 +79,6 @@ abstract class AbstractAI implements Ctrl
|
|||||||
/** Diferent internal state flags */
|
/** Diferent internal state flags */
|
||||||
private int _moveToPawnTimeout;
|
private int _moveToPawnTimeout;
|
||||||
|
|
||||||
protected Future<?> _followTask = null;
|
|
||||||
private static final int FOLLOW_INTERVAL = 1000;
|
|
||||||
private static final int ATTACK_FOLLOW_INTERVAL = 500;
|
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Constructor of AbstractAI.
|
* Constructor of AbstractAI.
|
||||||
* @param accessor The AI accessor of the Creature
|
* @param accessor The AI accessor of the Creature
|
||||||
@@ -770,15 +713,7 @@ abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target)
|
public synchronized void startFollow(Creature target)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
startFollow(target, -1);
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
// Create and Launch an AI Follow Task to execute every 1s
|
|
||||||
_followTarget = target;
|
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(), 5, FOLLOW_INTERVAL);
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -788,14 +723,16 @@ abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void startFollow(Creature target, int range)
|
public synchronized void startFollow(Creature target, int range)
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
stopFollow();
|
||||||
{
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_followTarget = target;
|
_followTarget = target;
|
||||||
_followTask = ThreadPool.scheduleAtFixedRate(new FollowTask(range), 5, ATTACK_FOLLOW_INTERVAL);
|
if (range == -1)
|
||||||
|
{
|
||||||
|
CreatureFollowTaskManager.getInstance().addNormalFollow(_actor, range);
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
CreatureFollowTaskManager.getInstance().addAttackFollow(_actor, range);
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -803,12 +740,7 @@ abstract class AbstractAI implements Ctrl
|
|||||||
*/
|
*/
|
||||||
public synchronized void stopFollow()
|
public synchronized void stopFollow()
|
||||||
{
|
{
|
||||||
if (_followTask != null)
|
CreatureFollowTaskManager.getInstance().remove(_actor);
|
||||||
{
|
|
||||||
// Stop the Follow Task
|
|
||||||
_followTask.cancel(false);
|
|
||||||
_followTask = null;
|
|
||||||
}
|
|
||||||
_followTarget = null;
|
_followTarget = null;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -20,10 +20,7 @@ import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
|
|||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
|
||||||
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
import java.util.concurrent.Future;
|
|
||||||
|
|
||||||
import org.l2jmobius.Config;
|
import org.l2jmobius.Config;
|
||||||
import org.l2jmobius.commons.concurrent.ThreadPool;
|
|
||||||
import org.l2jmobius.commons.util.Rnd;
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
import org.l2jmobius.gameserver.GameTimeController;
|
import org.l2jmobius.gameserver.GameTimeController;
|
||||||
import org.l2jmobius.gameserver.datatables.sql.TerritoryTable;
|
import org.l2jmobius.gameserver.datatables.sql.TerritoryTable;
|
||||||
@@ -56,6 +53,7 @@ import org.l2jmobius.gameserver.model.items.type.WeaponType;
|
|||||||
import org.l2jmobius.gameserver.model.quest.EventType;
|
import org.l2jmobius.gameserver.model.quest.EventType;
|
||||||
import org.l2jmobius.gameserver.model.quest.Quest;
|
import org.l2jmobius.gameserver.model.quest.Quest;
|
||||||
import org.l2jmobius.gameserver.model.spawn.Spawn;
|
import org.l2jmobius.gameserver.model.spawn.Spawn;
|
||||||
|
import org.l2jmobius.gameserver.taskmanager.AttackableThinkTaskManager;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* This class manages AI of Attackable.
|
* This class manages AI of Attackable.
|
||||||
@@ -65,12 +63,8 @@ public class AttackableAI extends CreatureAI
|
|||||||
// protected static final Logger LOGGER = Logger.getLogger(AttackableAI.class);
|
// protected static final Logger LOGGER = Logger.getLogger(AttackableAI.class);
|
||||||
|
|
||||||
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
private static final int RANDOM_WALK_RATE = 30; // confirmed
|
||||||
// private static final int MAX_DRIFT_RANGE = 300;
|
|
||||||
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
|
private static final int MAX_ATTACK_TIMEOUT = 300; // int ticks, i.e. 30 seconds
|
||||||
|
|
||||||
/** The Attackable AI task executed every 1s (call onEvtThink method) */
|
|
||||||
private Future<?> _aiTask;
|
|
||||||
|
|
||||||
/** The delay after wich the attacked is stopped */
|
/** The delay after wich the attacked is stopped */
|
||||||
private int _attackTimeout;
|
private int _attackTimeout;
|
||||||
|
|
||||||
@@ -129,7 +123,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
final Attackable me = (Attackable) _actor;
|
final Attackable me = getActiveChar();
|
||||||
|
|
||||||
// Check if the target isn't invulnerable
|
// Check if the target isn't invulnerable
|
||||||
if (target.isInvul())
|
if (target.isInvul())
|
||||||
@@ -293,20 +287,12 @@ public class AttackableAI extends CreatureAI
|
|||||||
|
|
||||||
public synchronized void startAITask()
|
public synchronized void startAITask()
|
||||||
{
|
{
|
||||||
// If not idle - create an AI task (schedule onEvtThink repeatedly)
|
AttackableThinkTaskManager.getInstance().add(getActiveChar());
|
||||||
if (_aiTask == null)
|
|
||||||
{
|
|
||||||
_aiTask = ThreadPool.scheduleAtFixedRate(this::onEvtThink, 1000, 1000);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
public synchronized void stopAITask()
|
public synchronized void stopAITask()
|
||||||
{
|
{
|
||||||
if (_aiTask != null)
|
AttackableThinkTaskManager.getInstance().remove(getActiveChar());
|
||||||
{
|
|
||||||
_aiTask.cancel(false);
|
|
||||||
_aiTask = null;
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
@@ -331,7 +317,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
// Check if actor is not dead
|
// Check if actor is not dead
|
||||||
if (!_actor.isAlikeDead())
|
if (!_actor.isAlikeDead())
|
||||||
{
|
{
|
||||||
final Attackable npc = (Attackable) _actor;
|
final Attackable npc = getActiveChar();
|
||||||
|
|
||||||
// If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
|
// If its _knownPlayer isn't empty set the Intention to AI_INTENTION_ACTIVE
|
||||||
if (npc.getKnownList().getKnownPlayers().size() > 0)
|
if (npc.getKnownList().getKnownPlayers().size() > 0)
|
||||||
@@ -387,7 +373,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
*/
|
*/
|
||||||
private void thinkActive()
|
private void thinkActive()
|
||||||
{
|
{
|
||||||
final Attackable npc = (Attackable) _actor;
|
final Attackable npc = getActiveChar();
|
||||||
|
|
||||||
// Update every 1s the _globalAggro counter to come close to 0
|
// Update every 1s the _globalAggro counter to come close to 0
|
||||||
if (_globalAggro != 0)
|
if (_globalAggro != 0)
|
||||||
@@ -687,7 +673,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
// Stop hating this target after the attack timeout or if target is dead
|
// Stop hating this target after the attack timeout or if target is dead
|
||||||
if (originalAttackTarget != null)
|
if (originalAttackTarget != null)
|
||||||
{
|
{
|
||||||
((Attackable) _actor).stopHating(originalAttackTarget);
|
(getActiveChar()).stopHating(originalAttackTarget);
|
||||||
}
|
}
|
||||||
|
|
||||||
// Set the AI Intention to AI_INTENTION_ACTIVE
|
// Set the AI Intention to AI_INTENTION_ACTIVE
|
||||||
@@ -860,7 +846,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
else
|
else
|
||||||
{
|
{
|
||||||
hated = ((Attackable) _actor).getMostHated();
|
hated = (getActiveChar()).getMostHated();
|
||||||
}
|
}
|
||||||
|
|
||||||
if (hated == null)
|
if (hated == null)
|
||||||
@@ -1010,7 +996,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isAllSkillsDisabled())
|
||||||
@@ -1062,7 +1048,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
// Add the attacker to the _aggroList of the actor
|
// Add the attacker to the _aggroList of the actor
|
||||||
((Attackable) _actor).addDamageHate(attacker, 0, 1);
|
(getActiveChar()).addDamageHate(attacker, 0, 1);
|
||||||
|
|
||||||
// Set the Creature movement type to run and send Server->Client packet ChangeMoveType to all others PlayerInstance
|
// Set the Creature movement type to run and send Server->Client packet ChangeMoveType to all others PlayerInstance
|
||||||
if (!_actor.isRunning())
|
if (!_actor.isRunning())
|
||||||
@@ -1077,7 +1063,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
{
|
{
|
||||||
setIntention(AI_INTENTION_ATTACK, attacker);
|
setIntention(AI_INTENTION_ATTACK, attacker);
|
||||||
}
|
}
|
||||||
else if (((Attackable) _actor).getMostHated() != getAttackTarget())
|
else if ((getActiveChar()).getMostHated() != getAttackTarget())
|
||||||
{
|
{
|
||||||
setIntention(AI_INTENTION_ATTACK, attacker);
|
setIntention(AI_INTENTION_ATTACK, attacker);
|
||||||
}
|
}
|
||||||
@@ -1098,7 +1084,7 @@ public class AttackableAI extends CreatureAI
|
|||||||
@Override
|
@Override
|
||||||
protected void onEvtAggression(Creature target, int aggro)
|
protected void onEvtAggression(Creature target, int aggro)
|
||||||
{
|
{
|
||||||
final Attackable me = (Attackable) _actor;
|
final Attackable me = getActiveChar();
|
||||||
|
|
||||||
// To avoid lag issue
|
// To avoid lag issue
|
||||||
if (me.isDead())
|
if (me.isDead())
|
||||||
@@ -1140,4 +1126,9 @@ public class AttackableAI extends CreatureAI
|
|||||||
{
|
{
|
||||||
_globalAggro = value;
|
_globalAggro = value;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public Attackable getActiveChar()
|
||||||
|
{
|
||||||
|
return (Attackable) _actor;
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -71,7 +71,7 @@ public class ControllableMobAI extends AttackableAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_isThinking || _actor.isAllSkillsDisabled())
|
if (_isThinking || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -537,7 +537,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
* Do nothing.
|
* Do nothing.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// do nothing
|
// do nothing
|
||||||
}
|
}
|
||||||
@@ -934,7 +934,7 @@ public class CreatureAI extends AbstractAI
|
|||||||
// Get the distance between the current position of the Creature and the target (x,y)
|
// Get the distance between the current position of the Creature and the target (x,y)
|
||||||
if (target == null)
|
if (target == null)
|
||||||
{
|
{
|
||||||
LOGGER.warning("maybeMoveToPawn: target == NULL!");
|
// LOGGER.warning("maybeMoveToPawn: target == NULL!");
|
||||||
return false;
|
return false;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -91,7 +91,7 @@ public class DoorAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// null;
|
// null;
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -819,7 +819,7 @@ public class FortSiegeGuardAI extends CreatureAI implements Runnable
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
|
// if(getIntention() != AI_INTENTION_IDLE && (!_actor.isVisible() || !_actor.hasAI() || !_actor.isKnownPlayers()))
|
||||||
// setIntention(AI_INTENTION_IDLE);
|
// setIntention(AI_INTENTION_IDLE);
|
||||||
|
|||||||
@@ -78,7 +78,7 @@ public class NpcWalkerAI extends CreatureAI implements Runnable
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (!Config.ALLOW_NPC_WALKERS)
|
if (!Config.ALLOW_NPC_WALKERS)
|
||||||
{
|
{
|
||||||
@@ -104,7 +104,7 @@ public class NpcWalkerAI extends CreatureAI implements Runnable
|
|||||||
@Override
|
@Override
|
||||||
protected void onEvtArrivedBlocked(Location location)
|
protected void onEvtArrivedBlocked(Location location)
|
||||||
{
|
{
|
||||||
LOGGER.warning("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + location.getX() + ", " + location.getY() + ", " + location.getZ() + ". Teleporting to next point");
|
// LOGGER.warning("NpcWalker ID: " + getActor().getNpcId() + ": Blocked at rote position [" + _currentPos + "], coords: " + location.getX() + ", " + location.getY() + ", " + location.getZ() + ". Teleporting to next point");
|
||||||
|
|
||||||
if (_route.size() <= _currentPos)
|
if (_route.size() <= _currentPos)
|
||||||
{
|
{
|
||||||
@@ -141,7 +141,7 @@ public class NpcWalkerAI extends CreatureAI implements Runnable
|
|||||||
}
|
}
|
||||||
catch (ArrayIndexOutOfBoundsException e)
|
catch (ArrayIndexOutOfBoundsException e)
|
||||||
{
|
{
|
||||||
LOGGER.info("NpcWalkerInstance: Error, " + e);
|
// LOGGER.info("NpcWalkerInstance: Error, " + e);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -292,7 +292,7 @@ public class PlayerAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -629,7 +629,7 @@ public class SiegeGuardAI extends CreatureAI implements Runnable
|
|||||||
* Manage AI thinking actions of a Attackable.
|
* Manage AI thinking actions of a Attackable.
|
||||||
*/
|
*/
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
// Check if the actor can't use skills and if a thinking action isn't already in progress
|
||||||
if (_thinking || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isAllSkillsDisabled())
|
||||||
|
|||||||
@@ -151,7 +151,7 @@ public class SummonAI extends CreatureAI
|
|||||||
}
|
}
|
||||||
|
|
||||||
@Override
|
@Override
|
||||||
protected void onEvtThink()
|
public void onEvtThink()
|
||||||
{
|
{
|
||||||
if (_thinking || _actor.isAllSkillsDisabled())
|
if (_thinking || _actor.isAllSkillsDisabled())
|
||||||
{
|
{
|
||||||
|
|||||||
@@ -0,0 +1,91 @@
|
|||||||
|
/*
|
||||||
|
* 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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import java.util.Set;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Attackable;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class AttackableThinkTaskManager
|
||||||
|
{
|
||||||
|
private static final Set<Attackable> ATTACKABLES = ConcurrentHashMap.newKeySet();
|
||||||
|
private static boolean _working = false;
|
||||||
|
|
||||||
|
public AttackableThinkTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_working)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_working = true;
|
||||||
|
|
||||||
|
CreatureAI ai;
|
||||||
|
for (Attackable attackable : ATTACKABLES)
|
||||||
|
{
|
||||||
|
if (attackable.hasAI())
|
||||||
|
{
|
||||||
|
ai = attackable.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
ai.onEvtThink();
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
_working = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void add(Attackable attackable)
|
||||||
|
{
|
||||||
|
if (!ATTACKABLES.contains(attackable))
|
||||||
|
{
|
||||||
|
ATTACKABLES.add(attackable);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Attackable attackable)
|
||||||
|
{
|
||||||
|
ATTACKABLES.remove(attackable);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static AttackableThinkTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final AttackableThinkTaskManager INSTANCE = new AttackableThinkTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -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 <http://www.gnu.org/licenses/>.
|
||||||
|
*/
|
||||||
|
package org.l2jmobius.gameserver.taskmanager;
|
||||||
|
|
||||||
|
import static org.l2jmobius.gameserver.ai.CtrlIntention.AI_INTENTION_IDLE;
|
||||||
|
|
||||||
|
import java.util.Map;
|
||||||
|
import java.util.Map.Entry;
|
||||||
|
import java.util.concurrent.ConcurrentHashMap;
|
||||||
|
|
||||||
|
import org.l2jmobius.commons.concurrent.ThreadPool;
|
||||||
|
import org.l2jmobius.commons.util.Rnd;
|
||||||
|
import org.l2jmobius.gameserver.ai.CreatureAI;
|
||||||
|
import org.l2jmobius.gameserver.model.WorldObject;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Creature;
|
||||||
|
import org.l2jmobius.gameserver.model.actor.Summon;
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @author Mobius
|
||||||
|
*/
|
||||||
|
public class CreatureFollowTaskManager
|
||||||
|
{
|
||||||
|
private static final Map<Creature, Integer> NORMAL_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static final Map<Creature, Integer> ATTACK_FOLLOW_CREATURES = new ConcurrentHashMap<>();
|
||||||
|
private static boolean _workingNormal = false;
|
||||||
|
private static boolean _workingAttack = false;
|
||||||
|
|
||||||
|
public CreatureFollowTaskManager()
|
||||||
|
{
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingNormal)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingNormal = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : NORMAL_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingNormal = false;
|
||||||
|
}, 1000, 1000);
|
||||||
|
|
||||||
|
ThreadPool.scheduleAtFixedRate(() ->
|
||||||
|
{
|
||||||
|
if (_workingAttack)
|
||||||
|
{
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
_workingAttack = true;
|
||||||
|
|
||||||
|
for (Entry<Creature, Integer> entry : ATTACK_FOLLOW_CREATURES.entrySet())
|
||||||
|
{
|
||||||
|
follow(entry.getKey(), entry.getValue());
|
||||||
|
}
|
||||||
|
|
||||||
|
_workingAttack = false;
|
||||||
|
}, 500, 500);
|
||||||
|
}
|
||||||
|
|
||||||
|
private void follow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
if (creature.hasAI())
|
||||||
|
{
|
||||||
|
final CreatureAI ai = creature.getAI();
|
||||||
|
if (ai != null)
|
||||||
|
{
|
||||||
|
final WorldObject followTarget = ai.getFollowTarget();
|
||||||
|
if (followTarget == null)
|
||||||
|
{
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
final int followRange = range == -1 ? Rnd.get(50, 100) : range;
|
||||||
|
if (!creature.isInsideRadius(followTarget, followRange, true, false))
|
||||||
|
{
|
||||||
|
if (!creature.isInsideRadius(followTarget, 3000, true, false))
|
||||||
|
{
|
||||||
|
// If the target is too far (maybe also teleported).
|
||||||
|
if (creature.isSummon())
|
||||||
|
{
|
||||||
|
((Summon) creature).setFollowStatus(false);
|
||||||
|
}
|
||||||
|
ai.setIntention(AI_INTENTION_IDLE);
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
ai.moveToPawn(followTarget, followRange);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
else
|
||||||
|
{
|
||||||
|
remove(creature);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addNormalFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void addAttackFollow(Creature creature, int range)
|
||||||
|
{
|
||||||
|
ATTACK_FOLLOW_CREATURES.putIfAbsent(creature, range);
|
||||||
|
}
|
||||||
|
|
||||||
|
public void remove(Creature creature)
|
||||||
|
{
|
||||||
|
NORMAL_FOLLOW_CREATURES.remove(creature);
|
||||||
|
ATTACK_FOLLOW_CREATURES.remove(creature);
|
||||||
|
}
|
||||||
|
|
||||||
|
public static CreatureFollowTaskManager getInstance()
|
||||||
|
{
|
||||||
|
return SingletonHolder.INSTANCE;
|
||||||
|
}
|
||||||
|
|
||||||
|
private static class SingletonHolder
|
||||||
|
{
|
||||||
|
protected static final CreatureFollowTaskManager INSTANCE = new CreatureFollowTaskManager();
|
||||||
|
}
|
||||||
|
}
|
||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user