Pārlūkot izejas kodu

Support for Run Away effect

Set fear status on NPC.
Zoey76 10 gadi atpakaļ
vecāks
revīzija
f79cbe28e0

+ 20 - 15
L2J_Server/java/com/l2jserver/gameserver/ai/AbstractAI.java

@@ -328,11 +328,9 @@ public abstract class AbstractAI implements Ctrl
 	/**
 	/**
 	 * Launch the L2CharacterAI onEvt method corresponding to the Event. <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
 	 * Launch the L2CharacterAI onEvt method corresponding to the Event. <FONT COLOR=#FF0000><B> <U>Caution</U> : The current general intention won't be change (ex : If the character attack and is stunned, he will attack again after the stunned period)</B></FONT>
 	 * @param evt The event whose the AI must be notified
 	 * @param evt The event whose the AI must be notified
-	 * @param arg0 The first parameter of the Event (optional target)
-	 * @param arg1 The second parameter of the Event (optional target)
 	 */
 	 */
 	@Override
 	@Override
-	public final void notifyEvent(CtrlEvent evt, Object arg0, Object arg1)
+	public final void notifyEvent(CtrlEvent evt, Object... args)
 	{
 	{
 		if ((!_actor.isVisible() && !_actor.isTeleporting()) || !_actor.hasAI())
 		if ((!_actor.isVisible() && !_actor.isTeleporting()) || !_actor.hasAI())
 		{
 		{
@@ -345,31 +343,31 @@ public abstract class AbstractAI implements Ctrl
 				onEvtThink();
 				onEvtThink();
 				break;
 				break;
 			case EVT_ATTACKED:
 			case EVT_ATTACKED:
-				onEvtAttacked((L2Character) arg0);
+				onEvtAttacked((L2Character) args[0]);
 				break;
 				break;
 			case EVT_AGGRESSION:
 			case EVT_AGGRESSION:
-				onEvtAggression((L2Character) arg0, ((Number) arg1).intValue());
+				onEvtAggression((L2Character) args[0], ((Number) args[1]).intValue());
 				break;
 				break;
 			case EVT_STUNNED:
 			case EVT_STUNNED:
-				onEvtStunned((L2Character) arg0);
+				onEvtStunned((L2Character) args[0]);
 				break;
 				break;
 			case EVT_PARALYZED:
 			case EVT_PARALYZED:
-				onEvtParalyzed((L2Character) arg0);
+				onEvtParalyzed((L2Character) args[0]);
 				break;
 				break;
 			case EVT_SLEEPING:
 			case EVT_SLEEPING:
-				onEvtSleeping((L2Character) arg0);
+				onEvtSleeping((L2Character) args[0]);
 				break;
 				break;
 			case EVT_ROOTED:
 			case EVT_ROOTED:
-				onEvtRooted((L2Character) arg0);
+				onEvtRooted((L2Character) args[0]);
 				break;
 				break;
 			case EVT_CONFUSED:
 			case EVT_CONFUSED:
-				onEvtConfused((L2Character) arg0);
+				onEvtConfused((L2Character) args[0]);
 				break;
 				break;
 			case EVT_MUTED:
 			case EVT_MUTED:
-				onEvtMuted((L2Character) arg0);
+				onEvtMuted((L2Character) args[0]);
 				break;
 				break;
 			case EVT_EVADED:
 			case EVT_EVADED:
-				onEvtEvaded((L2Character) arg0);
+				onEvtEvaded((L2Character) args[0]);
 				break;
 				break;
 			case EVT_READY_TO_ACT:
 			case EVT_READY_TO_ACT:
 				if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow())
 				if (!_actor.isCastingNow() && !_actor.isCastingSimultaneouslyNow())
@@ -378,7 +376,7 @@ public abstract class AbstractAI implements Ctrl
 				}
 				}
 				break;
 				break;
 			case EVT_USER_CMD:
 			case EVT_USER_CMD:
-				onEvtUserCmd(arg0, arg1);
+				onEvtUserCmd(args[0], args[1]);
 				break;
 				break;
 			case EVT_ARRIVED:
 			case EVT_ARRIVED:
 				// happens e.g. from stopmove but we don't process it if we're casting
 				// happens e.g. from stopmove but we don't process it if we're casting
@@ -395,10 +393,10 @@ public abstract class AbstractAI implements Ctrl
 				}
 				}
 				break;
 				break;
 			case EVT_ARRIVED_BLOCKED:
 			case EVT_ARRIVED_BLOCKED:
-				onEvtArrivedBlocked((Location) arg0);
+				onEvtArrivedBlocked((Location) args[0]);
 				break;
 				break;
 			case EVT_FORGET_OBJECT:
 			case EVT_FORGET_OBJECT:
-				onEvtForgetObject((L2Object) arg0);
+				onEvtForgetObject((L2Object) args[0]);
 				break;
 				break;
 			case EVT_CANCEL:
 			case EVT_CANCEL:
 				onEvtCancel();
 				onEvtCancel();
@@ -412,6 +410,11 @@ public abstract class AbstractAI implements Ctrl
 			case EVT_FINISH_CASTING:
 			case EVT_FINISH_CASTING:
 				onEvtFinishCasting();
 				onEvtFinishCasting();
 				break;
 				break;
+			case EVT_AFRAID:
+			{
+				onEvtAfraid((L2Character) args[0], (Boolean) args[1]);
+				break;
+			}
 		}
 		}
 		
 		
 		// Do next action.
 		// Do next action.
@@ -479,6 +482,8 @@ public abstract class AbstractAI implements Ctrl
 	
 	
 	protected abstract void onEvtFinishCasting();
 	protected abstract void onEvtFinishCasting();
 	
 	
+	protected abstract void onEvtAfraid(L2Character effector, boolean start);
+	
 	/**
 	/**
 	 * Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor. <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
 	 * Cancel action client side by sending Server->Client packet ActionFailed to the L2PcInstance actor. <FONT COLOR=#FF0000><B> <U>Caution</U> : Low level function, used by AI subclasses</B></FONT>
 	 */
 	 */

+ 2 - 3
L2J_Server/java/com/l2jserver/gameserver/ai/Ctrl.java

@@ -100,8 +100,7 @@ public interface Ctrl
 	/**
 	/**
 	 * Notify an event.
 	 * Notify an event.
 	 * @param evt the event
 	 * @param evt the event
-	 * @param arg0 the arg0
-	 * @param arg1 the arg1
+	 * @param args the args
 	 */
 	 */
-	void notifyEvent(CtrlEvent evt, Object arg0, Object arg1);
+	void notifyEvent(CtrlEvent evt, Object... args);
 }
 }

+ 85 - 28
L2J_Server/java/com/l2jserver/gameserver/ai/L2AttackableAI.java

@@ -26,6 +26,7 @@ import java.util.Collection;
 import java.util.List;
 import java.util.List;
 import java.util.Set;
 import java.util.Set;
 import java.util.concurrent.Future;
 import java.util.concurrent.Future;
+import java.util.concurrent.TimeUnit;
 
 
 import com.l2jserver.Config;
 import com.l2jserver.Config;
 import com.l2jserver.gameserver.GameTimeController;
 import com.l2jserver.gameserver.GameTimeController;
@@ -59,6 +60,7 @@ import com.l2jserver.gameserver.model.events.EventDispatcher;
 import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableFactionCall;
 import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableFactionCall;
 import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableHate;
 import com.l2jserver.gameserver.model.events.impl.character.npc.attackable.OnAttackableHate;
 import com.l2jserver.gameserver.model.events.returns.TerminateReturn;
 import com.l2jserver.gameserver.model.events.returns.TerminateReturn;
+import com.l2jserver.gameserver.model.skills.AbnormalVisualEffect;
 import com.l2jserver.gameserver.model.skills.Skill;
 import com.l2jserver.gameserver.model.skills.Skill;
 import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
 import com.l2jserver.gameserver.model.skills.targets.L2TargetType;
 import com.l2jserver.gameserver.model.zone.ZoneId;
 import com.l2jserver.gameserver.model.zone.ZoneId;
@@ -70,30 +72,53 @@ import com.l2jserver.util.Rnd;
  */
  */
 public class L2AttackableAI extends L2CharacterAI implements Runnable
 public class L2AttackableAI extends L2CharacterAI implements Runnable
 {
 {
+	/**
+	 * Fear task.
+	 * @author Zoey76
+	 */
+	public static class FearTask implements Runnable
+	{
+		private final L2Character _effected;
+		private final L2Character _effector;
+		private boolean _start;
+		
+		public FearTask(L2Character effected, L2Character effector, boolean start)
+		{
+			_effected = effected;
+			_effector = effector;
+			_start = start;
+		}
+		
+		@Override
+		public void run()
+		{
+			final int fearTimeLeft = ((L2AttackableAI) _effected.getAI()).getFearTime() - FEAR_TICKS;
+			((L2AttackableAI) _effected.getAI()).setFearTime(fearTimeLeft);
+			_effected.getAI().onEvtAfraid(_effector, _start);
+			_start = false;
+		}
+	}
+	
+	protected static final int FEAR_TICKS = 5;
 	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_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 L2Attackable AI task executed every 1s (call onEvtThink method).
-	 */
+	/** The L2Attackable AI task executed every 1s (call onEvtThink method). */
 	private Future<?> _aiTask;
 	private Future<?> _aiTask;
-	/**
-	 * The delay after which the attacked is stopped.
-	 */
+	/** The delay after which the attacked is stopped. */
 	private int _attackTimeout;
 	private int _attackTimeout;
-	/**
-	 * The L2Attackable aggro counter.
-	 */
+	/** The L2Attackable aggro counter. */
 	private int _globalAggro;
 	private int _globalAggro;
-	/**
-	 * The flag used to indicate that a thinking action is in progress, to prevent recursive thinking.
-	 */
+	/** The flag used to indicate that a thinking action is in progress, to prevent recursive thinking. */
 	private boolean _thinking;
 	private boolean _thinking;
 	
 	
-	private int timepass = 0;
-	private int chaostime = 0;
+	private int _timePass = 0;
+	private int _chaosTime = 0;
 	private final L2NpcTemplate _skillrender;
 	private final L2NpcTemplate _skillrender;
-	int lastBuffTick;
+	private int _lastBuffTick;
+	// Fear parameters
+	private int _fearTime;
+	private Future<?> _fearTask = null;
 	
 	
 	/**
 	/**
 	 * Constructor of L2AttackableAI.
 	 * Constructor of L2AttackableAI.
@@ -386,7 +411,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
 		_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
 		_attackTimeout = MAX_ATTACK_TIMEOUT + GameTimeController.getInstance().getGameTicks();
 		
 		
 		// self and buffs
 		// self and buffs
-		if ((lastBuffTick + 30) < GameTimeController.getInstance().getGameTicks())
+		if ((_lastBuffTick + 30) < GameTimeController.getInstance().getGameTicks())
 		{
 		{
 			for (Skill sk : _skillrender.getAISkills(AISkillScope.BUFF))
 			for (Skill sk : _skillrender.getAISkills(AISkillScope.BUFF))
 			{
 			{
@@ -395,13 +420,35 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
 					break;
 					break;
 				}
 				}
 			}
 			}
-			lastBuffTick = GameTimeController.getInstance().getGameTicks();
+			_lastBuffTick = GameTimeController.getInstance().getGameTicks();
 		}
 		}
 		
 		
 		// Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
 		// Manage the Attack Intention : Stop current Attack (if necessary), Start a new Attack and Launch Think Event
 		super.onIntentionAttack(target);
 		super.onIntentionAttack(target);
 	}
 	}
 	
 	
+	@Override
+	protected void onEvtAfraid(L2Character effector, boolean start)
+	{
+		if ((_fearTime > 0) && (_fearTask == null))
+		{
+			_fearTask = ThreadPoolManager.getInstance().scheduleAiAtFixedRate(new FearTask(_actor, effector, start), 0, FEAR_TICKS, TimeUnit.SECONDS);
+			_actor.startAbnormalVisualEffect(true, AbnormalVisualEffect.TURN_FLEE);
+		}
+		else
+		{
+			super.onEvtAfraid(effector, start);
+			
+			if ((_fearTime <= 0) && (_fearTask != null))
+			{
+				_fearTask.cancel(true);
+				_fearTask = null;
+				_actor.stopAbnormalVisualEffect(true, AbnormalVisualEffect.TURN_FLEE);
+				setIntention(CtrlIntention.AI_INTENTION_IDLE);
+			}
+		}
+	}
+	
 	protected void thinkCast()
 	protected void thinkCast()
 	{
 	{
 		if (checkTargetLost(getCastTarget()))
 		if (checkTargetLost(getCastTarget()))
@@ -923,29 +970,29 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
 		// BOSS/Raid Minion Target Reconsider
 		// BOSS/Raid Minion Target Reconsider
 		if (npc.isRaid() || npc.isRaidMinion())
 		if (npc.isRaid() || npc.isRaidMinion())
 		{
 		{
-			chaostime++;
+			_chaosTime++;
 			if (npc instanceof L2RaidBossInstance)
 			if (npc instanceof L2RaidBossInstance)
 			{
 			{
 				if (!((L2MonsterInstance) npc).hasMinions())
 				if (!((L2MonsterInstance) npc).hasMinions())
 				{
 				{
-					if (chaostime > Config.RAID_CHAOS_TIME)
+					if (_chaosTime > Config.RAID_CHAOS_TIME)
 					{
 					{
 						if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 100) / npc.getMaxHp())))
 						if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 100) / npc.getMaxHp())))
 						{
 						{
 							aggroReconsider();
 							aggroReconsider();
-							chaostime = 0;
+							_chaosTime = 0;
 							return;
 							return;
 						}
 						}
 					}
 					}
 				}
 				}
 				else
 				else
 				{
 				{
-					if (chaostime > Config.RAID_CHAOS_TIME)
+					if (_chaosTime > Config.RAID_CHAOS_TIME)
 					{
 					{
 						if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 200) / npc.getMaxHp())))
 						if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 200) / npc.getMaxHp())))
 						{
 						{
 							aggroReconsider();
 							aggroReconsider();
-							chaostime = 0;
+							_chaosTime = 0;
 							return;
 							return;
 						}
 						}
 					}
 					}
@@ -953,25 +1000,25 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
 			}
 			}
 			else if (npc instanceof L2GrandBossInstance)
 			else if (npc instanceof L2GrandBossInstance)
 			{
 			{
-				if (chaostime > Config.GRAND_CHAOS_TIME)
+				if (_chaosTime > Config.GRAND_CHAOS_TIME)
 				{
 				{
 					double chaosRate = 100 - ((npc.getCurrentHp() * 300) / npc.getMaxHp());
 					double chaosRate = 100 - ((npc.getCurrentHp() * 300) / npc.getMaxHp());
 					if (((chaosRate <= 10) && (Rnd.get(100) <= 10)) || ((chaosRate > 10) && (Rnd.get(100) <= chaosRate)))
 					if (((chaosRate <= 10) && (Rnd.get(100) <= 10)) || ((chaosRate > 10) && (Rnd.get(100) <= chaosRate)))
 					{
 					{
 						aggroReconsider();
 						aggroReconsider();
-						chaostime = 0;
+						_chaosTime = 0;
 						return;
 						return;
 					}
 					}
 				}
 				}
 			}
 			}
 			else
 			else
 			{
 			{
-				if (chaostime > Config.MINION_CHAOS_TIME)
+				if (_chaosTime > Config.MINION_CHAOS_TIME)
 				{
 				{
 					if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 200) / npc.getMaxHp())))
 					if (Rnd.get(100) <= (100 - ((npc.getCurrentHp() * 200) / npc.getMaxHp())))
 					{
 					{
 						aggroReconsider();
 						aggroReconsider();
-						chaostime = 0;
+						_chaosTime = 0;
 						return;
 						return;
 					}
 					}
 				}
 				}
@@ -2717,7 +2764,7 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
 	 */
 	 */
 	public void setTimepass(int TP)
 	public void setTimepass(int TP)
 	{
 	{
-		timepass = TP;
+		_timePass = TP;
 	}
 	}
 	
 	
 	/**
 	/**
@@ -2725,11 +2772,21 @@ public class L2AttackableAI extends L2CharacterAI implements Runnable
 	 */
 	 */
 	public int getTimepass()
 	public int getTimepass()
 	{
 	{
-		return timepass;
+		return _timePass;
 	}
 	}
 	
 	
 	public L2Attackable getActiveChar()
 	public L2Attackable getActiveChar()
 	{
 	{
 		return (L2Attackable) _actor;
 		return (L2Attackable) _actor;
 	}
 	}
+	
+	public int getFearTime()
+	{
+		return _fearTime;
+	}
+	
+	public void setFearTime(int fearTime)
+	{
+		_fearTime = fearTime;
+	}
 }
 }

+ 35 - 3
L2J_Server/java/com/l2jserver/gameserver/ai/L2CharacterAI.java

@@ -31,6 +31,7 @@ import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_REST;
 import java.util.ArrayList;
 import java.util.ArrayList;
 import java.util.List;
 import java.util.List;
 
 
+import com.l2jserver.Config;
 import com.l2jserver.gameserver.GameTimeController;
 import com.l2jserver.gameserver.GameTimeController;
 import com.l2jserver.gameserver.GeoData;
 import com.l2jserver.gameserver.GeoData;
 import com.l2jserver.gameserver.ThreadPoolManager;
 import com.l2jserver.gameserver.ThreadPoolManager;
@@ -58,6 +59,7 @@ import com.l2jserver.gameserver.network.SystemMessageId;
 import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
 import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
 import com.l2jserver.gameserver.network.serverpackets.AutoAttackStop;
 import com.l2jserver.gameserver.network.serverpackets.AutoAttackStop;
 import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
 import com.l2jserver.gameserver.taskmanager.AttackStanceTaskManager;
+import com.l2jserver.gameserver.util.Util;
 import com.l2jserver.util.Rnd;
 import com.l2jserver.util.Rnd;
 
 
 /**
 /**
@@ -90,6 +92,8 @@ public class L2CharacterAI extends AbstractAI
 		}
 		}
 	}
 	}
 	
 	
+	protected static final int FEAR_RANGE = 500;
+	
 	/**
 	/**
 	 * Cast Task
 	 * Cast Task
 	 * @author Zoey76
 	 * @author Zoey76
@@ -276,7 +280,7 @@ public class L2CharacterAI extends AbstractAI
 				stopFollow();
 				stopFollow();
 				
 				
 				// Launch the Think Event
 				// Launch the Think Event
-				notifyEvent(CtrlEvent.EVT_THINK, null);
+				notifyEvent(CtrlEvent.EVT_THINK);
 				
 				
 			}
 			}
 			else
 			else
@@ -295,7 +299,7 @@ public class L2CharacterAI extends AbstractAI
 			stopFollow();
 			stopFollow();
 			
 			
 			// Launch the Think Event
 			// Launch the Think Event
-			notifyEvent(CtrlEvent.EVT_THINK, null);
+			notifyEvent(CtrlEvent.EVT_THINK);
 		}
 		}
 	}
 	}
 	
 	
@@ -341,7 +345,7 @@ public class L2CharacterAI extends AbstractAI
 		changeIntention(AI_INTENTION_CAST, skill, target);
 		changeIntention(AI_INTENTION_CAST, skill, target);
 		
 		
 		// Launch the Think Event
 		// Launch the Think Event
-		notifyEvent(CtrlEvent.EVT_THINK, null);
+		notifyEvent(CtrlEvent.EVT_THINK);
 	}
 	}
 	
 	
 	/**
 	/**
@@ -955,6 +959,34 @@ public class L2CharacterAI extends AbstractAI
 		// do nothing
 		// do nothing
 	}
 	}
 	
 	
+	@Override
+	protected void onEvtAfraid(L2Character effector, boolean start)
+	{
+		double radians = Math.toRadians(start ? Util.calculateAngleFrom(effector, _actor) : Util.convertHeadingToDegree(_actor.getHeading()));
+		
+		int posX = (int) (_actor.getX() + (FEAR_RANGE * Math.cos(radians)));
+		int posY = (int) (_actor.getY() + (FEAR_RANGE * Math.sin(radians)));
+		int posZ = _actor.getZ();
+		
+		if (!_actor.isPet())
+		{
+			_actor.setRunning();
+		}
+		
+		// If pathfinding enabled the creature will go to the defined destination (retail like).
+		// Otherwise it will go to the nearest obstacle.
+		final Location destination;
+		if (Config.PATHFINDING > 0)
+		{
+			destination = new Location(posX, posY, posZ, _actor.getInstanceId());
+		}
+		else
+		{
+			destination = GeoData.getInstance().moveCheck(_actor.getX(), _actor.getY(), _actor.getZ(), posX, posY, posZ, _actor.getInstanceId());
+		}
+		setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, destination);
+	}
+	
 	protected boolean maybeMoveToPosition(ILocational worldPosition, int offset)
 	protected boolean maybeMoveToPosition(ILocational worldPosition, int offset)
 	{
 	{
 		if (worldPosition == null)
 		if (worldPosition == null)

+ 1 - 1
L2J_Server/java/com/l2jserver/gameserver/model/actor/tasks/character/NotifyAITask.java

@@ -41,7 +41,7 @@ public final class NotifyAITask implements Runnable
 	{
 	{
 		if (_character != null)
 		if (_character != null)
 		{
 		{
-			_character.getAI().notifyEvent(_event, null);
+			_character.getAI().notifyEvent(_event);
 		}
 		}
 	}
 	}
 }
 }