MercTicketManager.java 20 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404
  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.instancemanager;
  16. import java.sql.Connection;
  17. import java.sql.PreparedStatement;
  18. import java.sql.ResultSet;
  19. import java.util.Iterator;
  20. import java.util.List;
  21. import java.util.logging.Level;
  22. import java.util.logging.Logger;
  23. import javolution.util.FastList;
  24. import com.l2jserver.L2DatabaseFactory;
  25. import com.l2jserver.gameserver.datatables.NpcTable;
  26. import com.l2jserver.gameserver.idfactory.IdFactory;
  27. import com.l2jserver.gameserver.model.AutoChatHandler;
  28. import com.l2jserver.gameserver.model.L2World;
  29. import com.l2jserver.gameserver.model.actor.instance.L2DefenderInstance;
  30. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  31. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  32. import com.l2jserver.gameserver.model.entity.Castle;
  33. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  34. /**
  35. * This class is similar to the SiegeGuardManager, except it handles the loading of the mercenary tickets that are dropped on castle floors by the castle lords.<br>
  36. * These tickets (a.k.a. badges) need to be read after each server reboot except when the server crashed in the middle of an ongoing siege.<br>
  37. * In addition, this class keeps track of the added tickets, in order to properly limit the number of mercenaries in each castle and the number of mercenaries from each mercenary type.<br>
  38. * Finally, we provide auxiliary functions to identify the castle in which each item (and its corresponding NPC) belong to, in order to help avoid mixing them up.
  39. * @author yellowperil, Fulminus
  40. */
  41. public class MercTicketManager
  42. {
  43. private static final Logger _log = Logger.getLogger(MercTicketManager.class.getName());
  44. private static final FastList<L2ItemInstance> _droppedTickets = new FastList<>();
  45. //TODO move all these values into siege.properties
  46. // max tickets per merc type = 10 + (castleid * 2)?
  47. // max ticker per castle = 40 + (castleid * 20)?
  48. private static final int[] MAX_MERC_PER_TYPE = {
  49. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Gludio
  50. 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // Dion
  51. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Giran
  52. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Oren
  53. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Aden
  54. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Innadril
  55. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Goddard
  56. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Rune
  57. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 // Schuttgart
  58. };
  59. //TODO: not retail like: clan lvl 5 - 30 ticks max, lvl 7+ - 50 max
  60. protected static final int[] MERCS_MAX_PER_CASTLE = {
  61. 100, // Gludio
  62. 150, // Dion
  63. 200, // Giran
  64. 300, // Oren
  65. 400, // Aden
  66. 400, // Innadril
  67. 400, // Goddard
  68. 400, // Rune
  69. 400 // Schuttgart
  70. };
  71. private static final int[] ITEM_IDS = {
  72. 3960, 3961, 3962, 3963, 3964, 3965, 3966, 3967, 3968, 3969, 6115, 6116, 6117, 6118, 6119, 6120, 6121, 6122, 6123, 6124, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, 6175, 6176, 6177, 6178, 6179, 6180, 6181, 6182, 6183, 6184, 6235, 6236, 6237, 6238, 6239, 6240, 6241, 6242, 6243, 6244, 6295, 6296, // Gludio
  73. 3973, 3974, 3975, 3976, 3977, 3978, 3979, 3980, 3981, 3982, 6125, 6126, 6127, 6128, 6129, 6130, 6131, 6132, 6133, 6134, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, 6185, 6186, 6187, 6188, 6189, 6190, 6191, 6192, 6193, 6194, 6245, 6246, 6247, 6248, 6249, 6250, 6251, 6252, 6253, 6254, 6297, 6298, // Dion
  74. 3986, 3987, 3988, 3989, 3990, 3991, 3992, 3993, 3994, 3995, 6135, 6136, 6137, 6138, 6139, 6140, 6141, 6142, 6143, 6144, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, 6195, 6196, 6197, 6198, 6199, 6200, 6201, 6202, 6203, 6204, 6255, 6256, 6257, 6258, 6259, 6260, 6261, 6262, 6263, 6264, 6299, 6300, // Giran
  75. 3999, 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 6145, 6146, 6147, 6148, 6149, 6150, 6151, 6152, 6153, 6154, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, 6205, 6206, 6207, 6208, 6209, 6210, 6211, 6212, 6213, 6214, 6265, 6266, 6267, 6268, 6269, 6270, 6271, 6272, 6273, 6274, 6301, 6302, // Oren
  76. 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 6155, 6156, 6157, 6158, 6159, 6160, 6161, 6162, 6163, 6164, 6090, 6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, 6215, 6216, 6217, 6218, 6219, 6220, 6221, 6222, 6223, 6224, 6275, 6276, 6277, 6278, 6279, 6280, 6281, 6282, 6283, 6284, 6303, 6304, // Aden
  77. 5205, 5206, 5207, 5208, 5209, 5210, 5211, 5212, 5213, 5214, 6165, 6166, 6167, 6168, 6169, 6170, 6171, 6172, 6173, 6174, 6105, 6106, 6107, 6108, 6109, 6110, 6111, 6112, 6113, 6114, 6225, 6226, 6227, 6228, 6229, 6230, 6231, 6232, 6233, 6234, 6285, 6286, 6287, 6288, 6289, 6290, 6291, 6292, 6293, 6294, 6305, 6306, // Innadril
  78. 6779, 6780, 6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6802, 6803, 6804, 6805, 6806, 6807, 6808, 6809, 6810, 6811, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 6800, 6801, 6812, 6813, 6814, 6815, 6816, 6817, 6818, 6819, 6820, 6821, 6822, 6823, 6824, 6825, 6826, 6827, 6828, 6829, 6830, 6831, 6832, 6833, // Goddard
  79. 7973, 7974, 7975, 7976, 7977, 7978, 7979, 7980, 7981, 7982, 7998, 7999, 8000, 8001, 8002, 8003, 8004, 8005, 8006, 8007, 7988, 7989, 7990, 7991, 7992, 7993, 7994, 7995, 7996, 7997, 8008, 8009, 8010, 8011, 8012, 8013, 8014, 8015, 8016, 8017, 8018, 8019, 8020, 8021, 8022, 8023, 8024, 8025, 8026, 8027, 8028, 8029, // Rune
  80. 7918, 7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7941, 7942, 7943, 7944, 7945, 7946, 7947, 7948, 7949, 7950, 7931, 7932, 7933, 7934, 7935, 7936, 7937, 7938, 7939, 7940, 7951, 7952, 7953, 7954, 7955, 7956, 7957, 7958, 7959, 7960, 7961, 7962, 7963, 7964, 7965, 7966, 7967, 7968, 7969, 7970, 7971, 7972 // Schuttgart
  81. };
  82. private static final int[] NPC_IDS = {35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Gludio
  83. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Dion
  84. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Giran
  85. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Oren
  86. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Aden
  87. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Innadril
  88. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Goddard
  89. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Rune
  90. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35020, 35021, 35022, 35023, 35024, 35025, 35026, 35027, 35028, 35029, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, 35040, 35041, 35042, 35043, 35044, 35045, 35046, 35047, 35048, 35049, 35050, 35051, 35052, 35053, 35054, 35055, 35056, 35057, 35058, 35059, 35060, 35061, // Schuttgart
  91. };
  92. private static final int GUARDIAN_TYPES_COUNT = 52;
  93. protected MercTicketManager()
  94. {
  95. _droppedTickets.shared();
  96. load();
  97. }
  98. // returns the castleId for the passed ticket item id
  99. public int getTicketCastleId(int itemId)
  100. {
  101. for (int i = 0; i < 9; i++) //CastleID`s from 1 to 9 minus;
  102. {
  103. for (int i2 = 0; i2 < 50; i2 += 10)
  104. //Simplified if statement;
  105. if ((itemId >= ITEM_IDS[i2 + i * GUARDIAN_TYPES_COUNT] && itemId <= ITEM_IDS[i2 + 9 + i * GUARDIAN_TYPES_COUNT]))
  106. return i + 1;
  107. if (itemId >= ITEM_IDS[50] && itemId <= ITEM_IDS[51])
  108. return i + 1;
  109. }
  110. return -1;
  111. }
  112. public void reload()
  113. {
  114. _droppedTickets.clear();
  115. load();
  116. }
  117. /**
  118. * Load merc tickets into the world.
  119. */
  120. private final void load()
  121. {
  122. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  123. {
  124. PreparedStatement statement = con.prepareStatement("SELECT * FROM castle_siege_guards Where isHired = 1");
  125. ResultSet rs = statement.executeQuery();
  126. int npcId;
  127. int itemId;
  128. int x, y, z;
  129. int mercPlaced[] = new int[20];
  130. // start index to begin the search for the itemId corresponding to this NPC
  131. // this will help with:
  132. // a) skip unnecessary iterations in the search loop
  133. // b) avoid finding the wrong itemId whenever tickets of different spawn the same npc!
  134. int startindex = 0;
  135. while (rs.next())
  136. {
  137. npcId = rs.getInt("npcId");
  138. x = rs.getInt("x");
  139. y = rs.getInt("y");
  140. z = rs.getInt("z");
  141. Castle castle = CastleManager.getInstance().getCastle(x, y, z);
  142. if (castle != null)
  143. {
  144. if (mercPlaced[castle.getCastleId() - 1] >= MERCS_MAX_PER_CASTLE[castle.getCastleId() - 1])
  145. continue;
  146. startindex = GUARDIAN_TYPES_COUNT * (castle.getCastleId() - 1);
  147. mercPlaced[castle.getCastleId() - 1] += 1;
  148. }
  149. // find the FIRST ticket itemId with spawns the saved NPC in the saved location
  150. for (int i = startindex; i < startindex + GUARDIAN_TYPES_COUNT; i++)
  151. if (NPC_IDS[i] == npcId) // Find the index of the item used
  152. {
  153. // only handle tickets if a siege is not ongoing in this npc's castle
  154. if ((castle != null) && !(castle.getSiege().getIsInProgress()))
  155. {
  156. itemId = ITEM_IDS[i];
  157. // create the ticket in the gameworld
  158. L2ItemInstance dropticket = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
  159. dropticket.setLocation(L2ItemInstance.ItemLocation.VOID);
  160. dropticket.dropMe(null, x, y, z);
  161. dropticket.setDropTime(0); //avoids it from being removed by the auto item destroyer
  162. L2World.getInstance().storeObject(dropticket);
  163. _droppedTickets.add(dropticket);
  164. }
  165. break;
  166. }
  167. }
  168. rs.close();
  169. statement.close();
  170. _log.info("Loaded: " + _droppedTickets.size() + " Mercenary Tickets");
  171. }
  172. catch (Exception e)
  173. {
  174. _log.log(Level.WARNING, "Exception: loadMercenaryData(): " + e.getMessage(), e);
  175. }
  176. }
  177. /**
  178. * Checks if the passed item has reached the limit of number of dropped
  179. * tickets that this SPECIFIC item may have in its castle
  180. * @param itemId
  181. * @return
  182. */
  183. public boolean isAtTypeLimit(int itemId)
  184. {
  185. int limit = -1;
  186. // find the max value for this item
  187. for (int i = 0; i < ITEM_IDS.length; i++)
  188. if (ITEM_IDS[i] == itemId) // Find the index of the item used
  189. {
  190. limit = MAX_MERC_PER_TYPE[i];
  191. break;
  192. }
  193. if (limit <= 0)
  194. return true;
  195. int count = 0;
  196. for (L2ItemInstance ticket : _droppedTickets)
  197. {
  198. if (ticket != null && ticket.getItemId() == itemId)
  199. count++;
  200. }
  201. if (count >= limit)
  202. return true;
  203. return false;
  204. }
  205. /**
  206. * Checks if the passed item belongs to a castle which has reached its limit
  207. * of number of dropped tickets.
  208. * @param itemId
  209. * @return
  210. */
  211. public boolean isAtCasleLimit(int itemId)
  212. {
  213. int castleId = getTicketCastleId(itemId);
  214. if (castleId <= 0)
  215. return true;
  216. int limit = MERCS_MAX_PER_CASTLE[castleId - 1];
  217. if (limit <= 0)
  218. return true;
  219. int count = 0;
  220. for (L2ItemInstance ticket : _droppedTickets)
  221. {
  222. if ((ticket != null) && (getTicketCastleId(ticket.getItemId()) == castleId))
  223. count++;
  224. }
  225. if (count >= limit)
  226. return true;
  227. return false;
  228. }
  229. public int getMaxAllowedMerc(int castleId)
  230. {
  231. return MERCS_MAX_PER_CASTLE[castleId - 1];
  232. }
  233. public boolean isTooCloseToAnotherTicket(int x, int y, int z)
  234. {
  235. for (L2ItemInstance item : _droppedTickets)
  236. {
  237. double dx = x - item.getX();
  238. double dy = y - item.getY();
  239. double dz = z - item.getZ();
  240. if ((dx * dx + dy * dy + dz * dz) < 25 * 25)
  241. return true;
  242. }
  243. return false;
  244. }
  245. /**
  246. * addTicket actions
  247. * 1) find the npc that needs to be saved in the mercenary spawns, given this item
  248. * 2) Use the passed character's location info to add the spawn
  249. * 3) create a copy of the item to drop in the world
  250. * returns the id of the mercenary npc that was added to the spawn
  251. * returns -1 if this fails.
  252. * @param itemId
  253. * @param activeChar
  254. * @param messages
  255. * @return
  256. */
  257. public int addTicket(int itemId, L2PcInstance activeChar, String[] messages)
  258. {
  259. int x = activeChar.getX();
  260. int y = activeChar.getY();
  261. int z = activeChar.getZ();
  262. int heading = activeChar.getHeading();
  263. Castle castle = CastleManager.getInstance().getCastle(activeChar);
  264. if (castle == null) //this should never happen at this point
  265. return -1;
  266. for (int i = 0; i < ITEM_IDS.length; i++)
  267. {
  268. if (ITEM_IDS[i] == itemId) // Find the index of the item used
  269. {
  270. spawnMercenary(NPC_IDS[i], x, y, z, 3000, messages, 0);
  271. // Hire merc for this castle. NpcId is at the same index as the item used.
  272. castle.getSiege().getSiegeGuardManager().hireMerc(x, y, z, heading, NPC_IDS[i]);
  273. // create the ticket in the gameworld
  274. L2ItemInstance dropticket = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
  275. dropticket.setLocation(L2ItemInstance.ItemLocation.VOID);
  276. dropticket.dropMe(null, x, y, z);
  277. dropticket.setDropTime(0); //avoids it from beeing removed by the auto item destroyer
  278. L2World.getInstance().storeObject(dropticket); //add to the world
  279. // and keep track of this ticket in the list
  280. _droppedTickets.add(dropticket);
  281. return NPC_IDS[i];
  282. }
  283. }
  284. return -1;
  285. }
  286. private void spawnMercenary(int npcId, int x, int y, int z, int despawnDelay, String[] messages, int chatDelay)
  287. {
  288. L2NpcTemplate template = NpcTable.getInstance().getTemplate(npcId);
  289. if (template != null)
  290. {
  291. final L2DefenderInstance npc = new L2DefenderInstance(IdFactory.getInstance().getNextId(), template);
  292. npc.setCurrentHpMp(npc.getMaxHp(), npc.getMaxMp());
  293. npc.setDecayed(false);
  294. npc.spawnMe(x, y, (z + 20));
  295. if (messages != null && messages.length > 0)
  296. AutoChatHandler.getInstance().registerChat(npc, messages, chatDelay);
  297. if (despawnDelay > 0)
  298. npc.scheduleDespawn(despawnDelay);
  299. }
  300. }
  301. /**
  302. * Delete all tickets from a castle;
  303. * remove the items from the world and remove references to them from this class
  304. * @param castleId
  305. */
  306. public void deleteTickets(int castleId)
  307. {
  308. Iterator<L2ItemInstance> it = _droppedTickets.iterator();
  309. while (it.hasNext())
  310. {
  311. L2ItemInstance item = it.next();
  312. if ((item != null) && (getTicketCastleId(item.getItemId()) == castleId))
  313. {
  314. item.decayMe();
  315. L2World.getInstance().removeObject(item);
  316. it.remove();
  317. }
  318. }
  319. }
  320. /**
  321. * remove a single ticket and its associated spawn from the world
  322. * (used when the castle lord picks up a ticket, for example)
  323. * @param item
  324. */
  325. public void removeTicket(L2ItemInstance item)
  326. {
  327. int itemId = item.getItemId();
  328. int npcId = -1;
  329. // find the FIRST ticket itemId with spawns the saved NPC in the saved location
  330. for (int i = 0; i < ITEM_IDS.length; i++)
  331. if (ITEM_IDS[i] == itemId) // Find the index of the item used
  332. {
  333. npcId = NPC_IDS[i];
  334. break;
  335. }
  336. // find the castle where this item is
  337. Castle castle = CastleManager.getInstance().getCastleById(getTicketCastleId(itemId));
  338. if (npcId > 0 && castle != null)
  339. {
  340. (new SiegeGuardManager(castle)).removeMerc(npcId, item.getX(), item.getY(), item.getZ());
  341. }
  342. _droppedTickets.remove(item);
  343. }
  344. public int[] getItemIds()
  345. {
  346. return ITEM_IDS;
  347. }
  348. public final List<L2ItemInstance> getDroppedTickets()
  349. {
  350. return _droppedTickets;
  351. }
  352. public static final MercTicketManager getInstance()
  353. {
  354. return SingletonHolder._instance;
  355. }
  356. private static class SingletonHolder
  357. {
  358. protected static final MercTicketManager _instance = new MercTicketManager();
  359. }
  360. }