MinionList.java 11 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.gameserver.util;
  16. import java.util.Iterator;
  17. import java.util.List;
  18. import java.util.Set;
  19. import java.util.logging.Logger;
  20. import javolution.util.FastList;
  21. import javolution.util.FastSet;
  22. import com.l2jserver.Config;
  23. import com.l2jserver.gameserver.ThreadPoolManager;
  24. import com.l2jserver.gameserver.datatables.NpcTable;
  25. import com.l2jserver.gameserver.idfactory.IdFactory;
  26. import com.l2jserver.gameserver.model.L2MinionData;
  27. import com.l2jserver.gameserver.model.actor.L2Character;
  28. import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
  29. import com.l2jserver.gameserver.templates.chars.L2NpcTemplate;
  30. import com.l2jserver.util.Rnd;
  31. /**
  32. *
  33. * @author luisantonioa, DS
  34. *
  35. */
  36. public class MinionList
  37. {
  38. private static Logger _log = Logger.getLogger(MinionList.class.getName());
  39. private final L2MonsterInstance _master;
  40. /** List containing the current spawned minions */
  41. private final List<L2MonsterInstance> _minionReferences;
  42. /** List containing the cached deleted minions for reuse */
  43. private List<L2MonsterInstance> _reusedMinionReferences = null;
  44. public MinionList(L2MonsterInstance pMaster)
  45. {
  46. if (pMaster == null)
  47. throw new NullPointerException("MinionList: master is null");
  48. _master = pMaster;
  49. _minionReferences = new FastList<L2MonsterInstance>().shared();
  50. }
  51. /**
  52. * Returns list of the spawned (alive) minions.
  53. */
  54. public List<L2MonsterInstance> getSpawnedMinions()
  55. {
  56. return _minionReferences;
  57. }
  58. /**
  59. * Manage the spawn of Minions.<BR><BR>
  60. *
  61. * <B><U> Actions</U> :</B><BR><BR>
  62. * <li>Get the Minion data of all Minions that must be spawn </li>
  63. * <li>For each Minion type, spawn the amount of Minion needed </li><BR><BR>
  64. */
  65. public final void spawnMinions()
  66. {
  67. if (_master.isAlikeDead())
  68. return;
  69. List<L2MinionData> minions = _master.getTemplate().getMinionData();
  70. if (minions == null)
  71. return;
  72. int minionCount, minionId, minionsToSpawn;
  73. for (L2MinionData minion : minions)
  74. {
  75. minionCount = minion.getAmount();
  76. minionId = minion.getMinionId();
  77. minionsToSpawn = minionCount - countSpawnedMinionsById(minionId);
  78. if (minionsToSpawn > 0)
  79. {
  80. for (int i = 0; i < minionsToSpawn; i++)
  81. spawnMinion(minionId);
  82. }
  83. }
  84. // remove non-needed minions
  85. deleteReusedMinions();
  86. }
  87. /**
  88. * Delete all spawned minions and try to reuse them.
  89. */
  90. public void deleteSpawnedMinions()
  91. {
  92. if (!_minionReferences.isEmpty())
  93. {
  94. for (L2MonsterInstance minion : _minionReferences)
  95. {
  96. if (minion != null)
  97. {
  98. minion.setLeader(null);
  99. minion.deleteMe();
  100. if (_reusedMinionReferences != null)
  101. _reusedMinionReferences.add(minion);
  102. }
  103. }
  104. _minionReferences.clear();
  105. }
  106. }
  107. /**
  108. * Delete all reused minions to prevent memory leaks.
  109. */
  110. public void deleteReusedMinions()
  111. {
  112. if (_reusedMinionReferences != null)
  113. _reusedMinionReferences.clear();
  114. }
  115. // hooks
  116. /**
  117. * Called on the master spawn
  118. * Old minions (from previous spawn) are deleted.
  119. * If master can respawn - enabled reuse of the killed minions.
  120. */
  121. public void onMasterSpawn()
  122. {
  123. deleteSpawnedMinions();
  124. // if master has spawn and can respawn - try to reuse minions
  125. if (_reusedMinionReferences == null
  126. && _master.getTemplate().getMinionData() != null
  127. && _master.getSpawn() != null
  128. && _master.getSpawn().isRespawnEnabled())
  129. _reusedMinionReferences = new FastList<L2MonsterInstance>().shared();
  130. }
  131. /**
  132. * Called on the minion spawn
  133. * and added them in the list of the spawned minions.
  134. */
  135. public void onMinionSpawn(L2MonsterInstance minion)
  136. {
  137. _minionReferences.add(minion);
  138. }
  139. /**
  140. * Called on the master death/delete.
  141. * @param force if true - force delete of the spawned minions
  142. * By default minions deleted only for raidbosses
  143. */
  144. public void onMasterDie(boolean force)
  145. {
  146. if (_master.isRaid() || force)
  147. deleteSpawnedMinions();
  148. }
  149. /**
  150. * Called on the minion death/delete.
  151. * Removed minion from the list of the spawned minions and reuse if possible.
  152. * @param respawnTime (ms) enable respawning of this minion while master is alive.
  153. * -1 - use default value: 0 (disable) for mobs and config value for raids.
  154. */
  155. public void onMinionDie(L2MonsterInstance minion, int respawnTime)
  156. {
  157. minion.setLeader(null); // prevent memory leaks
  158. _minionReferences.remove(minion);
  159. if (_reusedMinionReferences != null)
  160. _reusedMinionReferences.add(minion);
  161. final int time = respawnTime < 0 ? _master.isRaid() ? (int)Config.RAID_MINION_RESPAWN_TIMER : 0 : respawnTime;
  162. if (time > 0 && !_master.isAlikeDead())
  163. ThreadPoolManager.getInstance().scheduleGeneral(new MinionRespawnTask(minion), time);
  164. }
  165. /**
  166. * Called if master/minion was attacked.
  167. * Master and all free minions receive aggro against attacker.
  168. */
  169. public void onAssist(L2Character caller, L2Character attacker)
  170. {
  171. if (attacker == null)
  172. return;
  173. if (!_master.isAlikeDead() && !_master.isInCombat())
  174. _master.addDamageHate(attacker, 0, 1);
  175. final boolean callerIsMaster = caller == _master;
  176. int aggro = callerIsMaster ? 10 : 1;
  177. if (_master.isRaid())
  178. aggro *= 10;
  179. for (L2MonsterInstance minion : _minionReferences)
  180. {
  181. if (minion != null && !minion.isDead() && (callerIsMaster || !minion.isInCombat()))
  182. minion.addDamageHate(attacker, 0, aggro);
  183. }
  184. }
  185. /**
  186. * Called from onTeleported() of the master
  187. * Alive and able to move minions teleported to master.
  188. */
  189. public void onMasterTeleported()
  190. {
  191. final int offset = 200;
  192. final int minRadius = (int)_master.getCollisionRadius() + 30;
  193. for (L2MonsterInstance minion : _minionReferences)
  194. {
  195. if (minion != null && !minion.isDead() && !minion.isMovementDisabled())
  196. {
  197. int newX = Rnd.get(minRadius * 2, offset * 2); // x
  198. int newY = Rnd.get(newX, offset * 2); // distance
  199. newY = (int)Math.sqrt(newY*newY - newX*newX); // y
  200. if (newX > offset + minRadius)
  201. newX = _master.getX() + newX - offset;
  202. else
  203. newX = _master.getX() - newX + minRadius;
  204. if (newY > offset + minRadius)
  205. newY = _master.getY() + newY - offset;
  206. else
  207. newY = _master.getY() - newY + minRadius;
  208. minion.teleToLocation(newX, newY, _master.getZ());
  209. }
  210. }
  211. }
  212. private final void spawnMinion(int minionId)
  213. {
  214. if (minionId == 0)
  215. return;
  216. // searching in reused minions
  217. if (_reusedMinionReferences != null && !_reusedMinionReferences.isEmpty())
  218. {
  219. L2MonsterInstance minion;
  220. Iterator<L2MonsterInstance> iter = _reusedMinionReferences.iterator();
  221. while (iter.hasNext())
  222. {
  223. minion = iter.next();
  224. if (minion != null && minion.getNpcId() == minionId)
  225. {
  226. iter.remove();
  227. minion.refreshID();
  228. initializeNpcInstance(_master, minion);
  229. return;
  230. }
  231. }
  232. }
  233. // not found in cache
  234. spawnMinion(_master, minionId);
  235. }
  236. private final class MinionRespawnTask implements Runnable
  237. {
  238. private final L2MonsterInstance _minion;
  239. public MinionRespawnTask(L2MonsterInstance minion)
  240. {
  241. _minion = minion;
  242. }
  243. public void run()
  244. {
  245. if (!_master.isAlikeDead() && _master.isVisible())
  246. {
  247. // minion can be already spawned or deleted
  248. if (!_minion.isVisible())
  249. {
  250. if (_reusedMinionReferences != null)
  251. _reusedMinionReferences.remove(_minion);
  252. _minion.refreshID();
  253. initializeNpcInstance(_master, _minion);
  254. }
  255. }
  256. }
  257. }
  258. /**
  259. * Init a Minion and add it in the world as a visible object.<BR><BR>
  260. *
  261. * <B><U> Actions</U> :</B><BR><BR>
  262. * <li>Get the template of the Minion to spawn </li>
  263. * <li>Create and Init the Minion and generate its Identifier </li>
  264. * <li>Set the Minion HP, MP and Heading </li>
  265. * <li>Set the Minion leader to this RaidBoss </li>
  266. * <li>Init the position of the Minion and add it in the world as a visible object </li><BR><BR>
  267. *
  268. * @param master L2MonsterInstance used as master for this minion
  269. * @param minionid The L2NpcTemplate Identifier of the Minion to spawn
  270. *
  271. */
  272. public static final L2MonsterInstance spawnMinion(L2MonsterInstance master, int minionId)
  273. {
  274. // Get the template of the Minion to spawn
  275. L2NpcTemplate minionTemplate = NpcTable.getInstance().getTemplate(minionId);
  276. if (minionTemplate == null)
  277. return null;
  278. // Create and Init the Minion and generate its Identifier
  279. L2MonsterInstance minion = new L2MonsterInstance(IdFactory.getInstance().getNextId(), minionTemplate);
  280. return initializeNpcInstance(master, minion);
  281. }
  282. private static final L2MonsterInstance initializeNpcInstance(L2MonsterInstance master, L2MonsterInstance minion)
  283. {
  284. minion.stopAllEffects();
  285. minion.setIsDead(false);
  286. minion.setDecayed(false);
  287. // Set the Minion HP, MP and Heading
  288. minion.setCurrentHpMp(minion.getMaxHp(), minion.getMaxMp());
  289. minion.setHeading(master.getHeading());
  290. // Set the Minion leader to this RaidBoss
  291. minion.setLeader(master);
  292. //move monster to masters instance
  293. minion.setInstanceId(master.getInstanceId());
  294. // Init the position of the Minion and add it in the world as a visible object
  295. final int offset = 200;
  296. final int minRadius = (int)master.getCollisionRadius() + 30;
  297. int newX = Rnd.get(minRadius * 2, offset * 2); // x
  298. int newY = Rnd.get(newX, offset * 2); // distance
  299. newY = (int)Math.sqrt(newY*newY - newX*newX); // y
  300. if (newX > offset + minRadius)
  301. newX = master.getX() + newX - offset;
  302. else
  303. newX = master.getX() - newX + minRadius;
  304. if (newY > offset + minRadius)
  305. newY = master.getY() + newY - offset;
  306. else
  307. newY = master.getY() - newY + minRadius;
  308. minion.spawnMe(newX, newY, master.getZ());
  309. if (Config.DEBUG)
  310. _log.fine("Spawned minion template " + minion.getNpcId() + " with objid: " + minion.getObjectId() + " to boss " + master.getObjectId() + " ,at: " + minion.getX() + " x, " + minion.getY() + " y, " + minion.getZ() + " z");
  311. return minion;
  312. }
  313. // Statistics part
  314. private final int countSpawnedMinionsById(int minionId)
  315. {
  316. int count = 0;
  317. for (L2MonsterInstance minion : _minionReferences)
  318. {
  319. if (minion != null && minion.getNpcId() == minionId)
  320. count++;
  321. }
  322. return count;
  323. }
  324. public final int countSpawnedMinions()
  325. {
  326. return _minionReferences.size();
  327. }
  328. public final int lazyCountSpawnedMinionsGroups()
  329. {
  330. Set<Integer> seenGroups = new FastSet<Integer>();
  331. for (L2MonsterInstance minion : _minionReferences)
  332. {
  333. if (minion == null)
  334. continue;
  335. seenGroups.add(minion.getNpcId());
  336. }
  337. return seenGroups.size();
  338. }
  339. }