L2ControllableMobAI.java 14 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583
  1. /*
  2. * Copyright (C) 2004-2014 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver.ai;
  20. import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ACTIVE;
  21. import static com.l2jserver.gameserver.ai.CtrlIntention.AI_INTENTION_ATTACK;
  22. import java.util.Collection;
  23. import java.util.List;
  24. import javolution.util.FastList;
  25. import com.l2jserver.gameserver.model.L2Object;
  26. import com.l2jserver.gameserver.model.MobGroup;
  27. import com.l2jserver.gameserver.model.MobGroupTable;
  28. import com.l2jserver.gameserver.model.actor.L2Attackable;
  29. import com.l2jserver.gameserver.model.actor.L2Character;
  30. import com.l2jserver.gameserver.model.actor.L2Character.AIAccessor;
  31. import com.l2jserver.gameserver.model.actor.L2Npc;
  32. import com.l2jserver.gameserver.model.actor.L2Playable;
  33. import com.l2jserver.gameserver.model.actor.instance.L2ControllableMobInstance;
  34. import com.l2jserver.gameserver.model.actor.instance.L2DoorInstance;
  35. import com.l2jserver.gameserver.model.actor.instance.L2NpcInstance;
  36. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  37. import com.l2jserver.gameserver.model.skills.Skill;
  38. import com.l2jserver.gameserver.util.Util;
  39. import com.l2jserver.util.Rnd;
  40. /**
  41. * AI for controllable mobs
  42. * @author littlecrow
  43. */
  44. public final class L2ControllableMobAI extends L2AttackableAI
  45. {
  46. public static final int AI_IDLE = 1;
  47. public static final int AI_NORMAL = 2;
  48. public static final int AI_FORCEATTACK = 3;
  49. public static final int AI_FOLLOW = 4;
  50. public static final int AI_CAST = 5;
  51. public static final int AI_ATTACK_GROUP = 6;
  52. private int _alternateAI;
  53. private boolean _isThinking; // to prevent thinking recursively
  54. private boolean _isNotMoving;
  55. private L2Character _forcedTarget;
  56. private MobGroup _targetGroup;
  57. protected void thinkFollow()
  58. {
  59. L2Attackable me = (L2Attackable) _actor;
  60. if (!Util.checkIfInRange(MobGroupTable.FOLLOW_RANGE, me, getForcedTarget(), true))
  61. {
  62. int signX = (Rnd.nextInt(2) == 0) ? -1 : 1;
  63. int signY = (Rnd.nextInt(2) == 0) ? -1 : 1;
  64. int randX = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE);
  65. int randY = Rnd.nextInt(MobGroupTable.FOLLOW_RANGE);
  66. moveTo(getForcedTarget().getX() + (signX * randX), getForcedTarget().getY() + (signY * randY), getForcedTarget().getZ());
  67. }
  68. }
  69. @Override
  70. protected void onEvtThink()
  71. {
  72. if (isThinking())
  73. {
  74. return;
  75. }
  76. setThinking(true);
  77. try
  78. {
  79. switch (getAlternateAI())
  80. {
  81. case AI_IDLE:
  82. if (getIntention() != CtrlIntention.AI_INTENTION_ACTIVE)
  83. {
  84. setIntention(CtrlIntention.AI_INTENTION_ACTIVE);
  85. }
  86. break;
  87. case AI_FOLLOW:
  88. thinkFollow();
  89. break;
  90. case AI_CAST:
  91. thinkCast();
  92. break;
  93. case AI_FORCEATTACK:
  94. thinkForceAttack();
  95. break;
  96. case AI_ATTACK_GROUP:
  97. thinkAttackGroup();
  98. break;
  99. default:
  100. if (getIntention() == AI_INTENTION_ACTIVE)
  101. {
  102. thinkActive();
  103. }
  104. else if (getIntention() == AI_INTENTION_ATTACK)
  105. {
  106. thinkAttack();
  107. }
  108. break;
  109. }
  110. }
  111. finally
  112. {
  113. setThinking(false);
  114. }
  115. }
  116. @Override
  117. protected void thinkCast()
  118. {
  119. L2Attackable npc = (L2Attackable) _actor;
  120. if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead())
  121. {
  122. setAttackTarget(findNextRndTarget());
  123. clientStopMoving(null);
  124. }
  125. if (getAttackTarget() == null)
  126. {
  127. return;
  128. }
  129. npc.setTarget(getAttackTarget());
  130. if (!_actor.isMuted())
  131. {
  132. int max_range = 0;
  133. // check distant skills
  134. for (Skill sk : _actor.getAllSkills())
  135. {
  136. if (Util.checkIfInRange(sk.getCastRange(), _actor, getAttackTarget(), true) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
  137. {
  138. _accessor.doCast(sk);
  139. return;
  140. }
  141. max_range = Math.max(max_range, sk.getCastRange());
  142. }
  143. if (!isNotMoving())
  144. {
  145. moveToPawn(getAttackTarget(), max_range);
  146. }
  147. return;
  148. }
  149. }
  150. protected void thinkAttackGroup()
  151. {
  152. L2Character target = getForcedTarget();
  153. if ((target == null) || target.isAlikeDead())
  154. {
  155. // try to get next group target
  156. setForcedTarget(findNextGroupTarget());
  157. clientStopMoving(null);
  158. }
  159. if (target == null)
  160. {
  161. return;
  162. }
  163. _actor.setTarget(target);
  164. // as a response, we put the target in a forcedattack mode
  165. L2ControllableMobInstance theTarget = (L2ControllableMobInstance) target;
  166. L2ControllableMobAI ctrlAi = (L2ControllableMobAI) theTarget.getAI();
  167. ctrlAi.forceAttack(_actor);
  168. double dist2 = _actor.calculateDistance(target, false, true);
  169. int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + target.getTemplate().getCollisionRadius();
  170. int max_range = range;
  171. if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
  172. {
  173. // check distant skills
  174. for (Skill sk : _actor.getAllSkills())
  175. {
  176. int castRange = sk.getCastRange();
  177. if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
  178. {
  179. _accessor.doCast(sk);
  180. return;
  181. }
  182. max_range = Math.max(max_range, castRange);
  183. }
  184. if (!isNotMoving())
  185. {
  186. moveToPawn(target, range);
  187. }
  188. return;
  189. }
  190. _accessor.doAttack(target);
  191. }
  192. protected void thinkForceAttack()
  193. {
  194. if ((getForcedTarget() == null) || getForcedTarget().isAlikeDead())
  195. {
  196. clientStopMoving(null);
  197. setIntention(AI_INTENTION_ACTIVE);
  198. setAlternateAI(AI_IDLE);
  199. }
  200. _actor.setTarget(getForcedTarget());
  201. double dist2 = _actor.calculateDistance(getForcedTarget(), false, true);
  202. int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + getForcedTarget().getTemplate().getCollisionRadius();
  203. int max_range = range;
  204. if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
  205. {
  206. // check distant skills
  207. for (Skill sk : _actor.getAllSkills())
  208. {
  209. int castRange = sk.getCastRange();
  210. if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
  211. {
  212. _accessor.doCast(sk);
  213. return;
  214. }
  215. max_range = Math.max(max_range, castRange);
  216. }
  217. if (!isNotMoving())
  218. {
  219. moveToPawn(getForcedTarget(), _actor.getPhysicalAttackRange()/* range */);
  220. }
  221. return;
  222. }
  223. _accessor.doAttack(getForcedTarget());
  224. }
  225. @Override
  226. protected void thinkAttack()
  227. {
  228. if ((getAttackTarget() == null) || getAttackTarget().isAlikeDead())
  229. {
  230. if (getAttackTarget() != null)
  231. {
  232. // stop hating
  233. L2Attackable npc = (L2Attackable) _actor;
  234. npc.stopHating(getAttackTarget());
  235. }
  236. setIntention(AI_INTENTION_ACTIVE);
  237. }
  238. else
  239. {
  240. // notify aggression
  241. if (((L2Npc) _actor).getTemplate().getClans() != null)
  242. {
  243. Collection<L2Object> objs = _actor.getKnownList().getKnownObjects().values();
  244. for (L2Object obj : objs)
  245. {
  246. if (!(obj instanceof L2Npc))
  247. {
  248. continue;
  249. }
  250. L2Npc npc = (L2Npc) obj;
  251. if (!npc.isInMyClan((L2Npc) _actor))
  252. {
  253. continue;
  254. }
  255. if (_actor.isInsideRadius(npc, npc.getTemplate().getClanHelpRange(), false, true) && (Math.abs(getAttackTarget().getZ() - npc.getZ()) < 200))
  256. {
  257. npc.getAI().notifyEvent(CtrlEvent.EVT_AGGRESSION, getAttackTarget(), 1);
  258. }
  259. }
  260. }
  261. _actor.setTarget(getAttackTarget());
  262. double dist2 = _actor.calculateDistance(getAttackTarget(), false, true);
  263. int range = _actor.getPhysicalAttackRange() + _actor.getTemplate().getCollisionRadius() + getAttackTarget().getTemplate().getCollisionRadius();
  264. int max_range = range;
  265. if (!_actor.isMuted() && (dist2 > ((range + 20) * (range + 20))))
  266. {
  267. // check distant skills
  268. for (Skill sk : _actor.getAllSkills())
  269. {
  270. int castRange = sk.getCastRange();
  271. if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() > _actor.getStat().getMpConsume(sk)))
  272. {
  273. _accessor.doCast(sk);
  274. return;
  275. }
  276. max_range = Math.max(max_range, castRange);
  277. }
  278. moveToPawn(getAttackTarget(), range);
  279. return;
  280. }
  281. // Force mobs to attack anybody if confused.
  282. L2Character hated;
  283. if (_actor.isConfused())
  284. {
  285. hated = findNextRndTarget();
  286. }
  287. else
  288. {
  289. hated = getAttackTarget();
  290. }
  291. if (hated == null)
  292. {
  293. setIntention(AI_INTENTION_ACTIVE);
  294. return;
  295. }
  296. if (hated != getAttackTarget())
  297. {
  298. setAttackTarget(hated);
  299. }
  300. if (!_actor.isMuted() && (Rnd.nextInt(5) == 3))
  301. {
  302. for (Skill sk : _actor.getAllSkills())
  303. {
  304. int castRange = sk.getCastRange();
  305. if (((castRange * castRange) >= dist2) && !_actor.isSkillDisabled(sk) && (_actor.getCurrentMp() < _actor.getStat().getMpConsume(sk)))
  306. {
  307. _accessor.doCast(sk);
  308. return;
  309. }
  310. }
  311. }
  312. _accessor.doAttack(getAttackTarget());
  313. }
  314. }
  315. @Override
  316. protected void thinkActive()
  317. {
  318. setAttackTarget(findNextRndTarget());
  319. L2Character hated;
  320. if (_actor.isConfused())
  321. {
  322. hated = findNextRndTarget();
  323. }
  324. else
  325. {
  326. hated = getAttackTarget();
  327. }
  328. if (hated != null)
  329. {
  330. _actor.setRunning();
  331. setIntention(CtrlIntention.AI_INTENTION_ATTACK, hated);
  332. }
  333. }
  334. private boolean checkAutoAttackCondition(L2Character target)
  335. {
  336. if ((target == null) || !(_actor instanceof L2Attackable))
  337. {
  338. return false;
  339. }
  340. L2Attackable me = (L2Attackable) _actor;
  341. if ((target instanceof L2NpcInstance) || (target instanceof L2DoorInstance))
  342. {
  343. return false;
  344. }
  345. if (target.isAlikeDead() || !me.isInsideRadius(target, me.getAggroRange(), false, false) || (Math.abs(_actor.getZ() - target.getZ()) > 100))
  346. {
  347. return false;
  348. }
  349. // Check if the target isn't invulnerable
  350. if (target.isInvul())
  351. {
  352. return false;
  353. }
  354. // Spawn protection (only against mobs)
  355. if ((target instanceof L2PcInstance) && ((L2PcInstance) target).isSpawnProtected())
  356. {
  357. return false;
  358. }
  359. // Check if the target is a L2Playable
  360. if (target.isPlayable())
  361. {
  362. // Check if the target isn't in silent move mode
  363. if (((L2Playable) target).isSilentMovingAffected())
  364. {
  365. return false;
  366. }
  367. }
  368. if (target instanceof L2Npc)
  369. {
  370. return false;
  371. }
  372. return me.isAggressive();
  373. }
  374. private L2Character findNextRndTarget()
  375. {
  376. int aggroRange = ((L2Attackable) _actor).getAggroRange();
  377. L2Attackable npc = (L2Attackable) _actor;
  378. int npcX, npcY, targetX, targetY;
  379. double dy, dx;
  380. double dblAggroRange = aggroRange * aggroRange;
  381. List<L2Character> potentialTarget = new FastList<>();
  382. Collection<L2Object> objs = npc.getKnownList().getKnownObjects().values();
  383. for (L2Object obj : objs)
  384. {
  385. if (!(obj instanceof L2Character))
  386. {
  387. continue;
  388. }
  389. npcX = npc.getX();
  390. npcY = npc.getY();
  391. targetX = obj.getX();
  392. targetY = obj.getY();
  393. dx = npcX - targetX;
  394. dy = npcY - targetY;
  395. if (((dx * dx) + (dy * dy)) > dblAggroRange)
  396. {
  397. continue;
  398. }
  399. L2Character target = (L2Character) obj;
  400. if (checkAutoAttackCondition(target))
  401. {
  402. potentialTarget.add(target);
  403. }
  404. }
  405. if (potentialTarget.isEmpty())
  406. {
  407. return null;
  408. }
  409. // we choose a random target
  410. int choice = Rnd.nextInt(potentialTarget.size());
  411. L2Character target = potentialTarget.get(choice);
  412. return target;
  413. }
  414. private L2ControllableMobInstance findNextGroupTarget()
  415. {
  416. return getGroupTarget().getRandomMob();
  417. }
  418. public L2ControllableMobAI(AIAccessor accessor)
  419. {
  420. super(accessor);
  421. setAlternateAI(AI_IDLE);
  422. }
  423. public int getAlternateAI()
  424. {
  425. return _alternateAI;
  426. }
  427. public void setAlternateAI(int _alternateai)
  428. {
  429. _alternateAI = _alternateai;
  430. }
  431. public void forceAttack(L2Character target)
  432. {
  433. setAlternateAI(AI_FORCEATTACK);
  434. setForcedTarget(target);
  435. }
  436. public void forceAttackGroup(MobGroup group)
  437. {
  438. setForcedTarget(null);
  439. setGroupTarget(group);
  440. setAlternateAI(AI_ATTACK_GROUP);
  441. }
  442. public void stop()
  443. {
  444. setAlternateAI(AI_IDLE);
  445. clientStopMoving(null);
  446. }
  447. public void move(int x, int y, int z)
  448. {
  449. moveTo(x, y, z);
  450. }
  451. public void follow(L2Character target)
  452. {
  453. setAlternateAI(AI_FOLLOW);
  454. setForcedTarget(target);
  455. }
  456. public boolean isThinking()
  457. {
  458. return _isThinking;
  459. }
  460. public boolean isNotMoving()
  461. {
  462. return _isNotMoving;
  463. }
  464. public void setNotMoving(boolean isNotMoving)
  465. {
  466. _isNotMoving = isNotMoving;
  467. }
  468. public void setThinking(boolean isThinking)
  469. {
  470. _isThinking = isThinking;
  471. }
  472. private L2Character getForcedTarget()
  473. {
  474. return _forcedTarget;
  475. }
  476. private MobGroup getGroupTarget()
  477. {
  478. return _targetGroup;
  479. }
  480. private void setForcedTarget(L2Character forcedTarget)
  481. {
  482. _forcedTarget = forcedTarget;
  483. }
  484. private void setGroupTarget(MobGroup targetGroup)
  485. {
  486. _targetGroup = targetGroup;
  487. }
  488. }