2
0

MercTicketManager.java 18 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440
  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 net.sf.l2j.gameserver.instancemanager;
  16. import java.sql.PreparedStatement;
  17. import java.sql.ResultSet;
  18. import java.util.List;
  19. import java.util.logging.Logger;
  20. import javolution.util.FastList;
  21. import net.sf.l2j.L2DatabaseFactory;
  22. import net.sf.l2j.gameserver.ThreadPoolManager;
  23. import net.sf.l2j.gameserver.datatables.NpcTable;
  24. import net.sf.l2j.gameserver.idfactory.IdFactory;
  25. import net.sf.l2j.gameserver.model.AutoChatHandler;
  26. import net.sf.l2j.gameserver.model.L2ItemInstance;
  27. import net.sf.l2j.gameserver.model.L2World;
  28. import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance;
  29. import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance;
  30. import net.sf.l2j.gameserver.model.entity.Castle;
  31. import net.sf.l2j.gameserver.templates.L2NpcTemplate;
  32. /**
  33. * @author yellowperil & Fulminus
  34. * This class is similar to the SiegeGuardManager, except it handles
  35. * the loading of the mercenary tickets that are dropped on castle floors
  36. * by the castle lords.
  37. * These tickets (aka badges) need to be readded after each server reboot
  38. * except when the server crashed in the middle of an ongoig siege.
  39. * In addition, this class keeps track of the added tickets, in order to
  40. * properly limit the number of mercenaries in each castle and the
  41. * number of mercenaries from each mercenary type.
  42. * Finally, we provide auxilary functions to identify the castle in
  43. * which each item (and its corresponding NPC) belong to, in order to
  44. * help avoid mixing them up.
  45. *
  46. */
  47. public class MercTicketManager
  48. {
  49. protected static final Logger _log = Logger.getLogger(CastleManager.class.getName());
  50. // =========================================================
  51. private static MercTicketManager _instance;
  52. public static final MercTicketManager getInstance()
  53. {
  54. //CastleManager.getInstance();
  55. if (_instance == null)
  56. {
  57. _log.info("Initializing MercTicketManager");
  58. _instance = new MercTicketManager();
  59. _instance.load();
  60. }
  61. return _instance;
  62. }
  63. // =========================================================
  64. // =========================================================
  65. // Data Field
  66. private List<L2ItemInstance> _droppedTickets; // to keep track of items on the ground
  67. //TODO move all these values into siege.properties
  68. // max tickets per merc type = 10 + (castleid * 2)?
  69. // max ticker per castle = 40 + (castleid * 20)?
  70. private static final int[] MAX_MERC_PER_TYPE = {
  71. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Gludio
  72. 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // Dion
  73. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Giran
  74. 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Oren
  75. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Aden
  76. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Innadril
  77. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Goddard
  78. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Rune
  79. 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 // Schuttgart
  80. };
  81. private static final int[] MERCS_MAX_PER_CASTLE = {
  82. 100, // Gludio
  83. 150, // Dion
  84. 200, // Giran
  85. 300, // Oren
  86. 400, // Aden
  87. 400, // Innadril
  88. 400, // Goddard
  89. 400, // Rune
  90. 400 // Schuttgart
  91. };
  92. private static final int[] ITEM_IDS = {
  93. 3960, 3961, 3962, 3963, 3964, 3965, 3966, 3967, 3968, 3969, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, // Gludio
  94. 3973, 3974, 3975, 3976, 3977, 3978, 3979, 3980, 3981, 3982, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, // Dion
  95. 3986, 3987, 3988, 3989, 3990, 3991, 3992, 3993, 3994, 3995, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, // Giran
  96. 3999, 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, // Oren
  97. 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 6090, 6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, // Aden
  98. 5205, 5206, 5207, 5208, 5209, 5210, 5211, 5212, 5213, 5214, 6105, 6106, 6107, 6108, 6109, 6110, 6111, 6112, 6113, 6114, // Innadril
  99. 6779, 6780, 6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 6800, 6801, // Goddard
  100. 7973, 7974, 7975, 7976, 7977, 7978, 7979, 7980, 7981, 7982, 7988, 7989, 7990, 7991, 7992, 7993, 7994, 7995, 7996, 7997, // Rune
  101. 7918, 7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7931, 7932, 7933, 7934, 7935, 7936, 7937, 7938, 7939, 7940 // Schuttgart
  102. };
  103. private static final int[] NPC_IDS = {
  104. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Gludio
  105. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Dion
  106. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Giran
  107. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Oren
  108. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Aden
  109. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Innadril
  110. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Goddard
  111. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Rune
  112. 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039 // Schuttgart
  113. };
  114. // =========================================================
  115. // Constructor
  116. public MercTicketManager()
  117. {
  118. }
  119. // =========================================================
  120. // Method - Public
  121. // returns the castleId for the passed ticket item id
  122. public int getTicketCastleId(int itemId)
  123. {
  124. if ((itemId >= ITEM_IDS[0] && itemId <= ITEM_IDS[9]) || (itemId >= ITEM_IDS[10] && itemId <= ITEM_IDS[19]))
  125. return 1; // Gludio
  126. if ((itemId >= ITEM_IDS[20] && itemId <= ITEM_IDS[29]) || (itemId >= ITEM_IDS[30] && itemId <= ITEM_IDS[39]))
  127. return 2; // Dion
  128. if ((itemId >= ITEM_IDS[40] && itemId <= ITEM_IDS[49]) || (itemId >= ITEM_IDS[50] && itemId <= ITEM_IDS[59]))
  129. return 3; // Giran
  130. if ((itemId >= ITEM_IDS[60] && itemId <= ITEM_IDS[69]) || (itemId >= ITEM_IDS[70] && itemId <= ITEM_IDS[79]))
  131. return 4; // Oren
  132. if ((itemId >= ITEM_IDS[80] && itemId <= ITEM_IDS[89]) || (itemId >= ITEM_IDS[90] && itemId <= ITEM_IDS[99]))
  133. return 5; // Aden
  134. if ((itemId >= ITEM_IDS[100] && itemId <= ITEM_IDS[109]) || (itemId >= ITEM_IDS[110] && itemId <= ITEM_IDS[119]))
  135. return 6; // Innadril
  136. if ((itemId >= ITEM_IDS[120] && itemId <= ITEM_IDS[129]) || (itemId >= ITEM_IDS[130] && itemId <= ITEM_IDS[139]))
  137. return 7; // Goddard
  138. if ((itemId >= ITEM_IDS[140] && itemId <= ITEM_IDS[149]) || (itemId >= ITEM_IDS[150] && itemId <= ITEM_IDS[159]))
  139. return 8; // Rune
  140. if ((itemId >= ITEM_IDS[160] && itemId <= ITEM_IDS[169]) || (itemId >= ITEM_IDS[170] && itemId <= ITEM_IDS[179]))
  141. return 9; // Schuttgart
  142. return -1;
  143. }
  144. public void reload()
  145. {
  146. getDroppedTickets().clear();
  147. load();
  148. }
  149. // =========================================================
  150. // Method - Private
  151. private final void load()
  152. {
  153. java.sql.Connection con = null;
  154. // load merc tickets into the world
  155. try
  156. {
  157. PreparedStatement statement;
  158. ResultSet rs;
  159. con = L2DatabaseFactory.getInstance().getConnection();
  160. statement = con.prepareStatement("SELECT * FROM castle_siege_guards Where isHired = 1");
  161. rs = statement.executeQuery();
  162. int npcId;
  163. int itemId;
  164. int x,y,z;
  165. int mercPlaced[] = new int[20];
  166. // start index to begin the search for the itemId corresponding to this NPC
  167. // this will help with:
  168. // a) skip unnecessary iterations in the search loop
  169. // b) avoid finding the wrong itemId whenever tickets of different spawn the same npc!
  170. int startindex = 0;
  171. while (rs.next())
  172. {
  173. npcId = rs.getInt("npcId");
  174. x = rs.getInt("x");
  175. y = rs.getInt("y");
  176. z = rs.getInt("z");
  177. Castle castle = CastleManager.getInstance().getCastle(x,y,z);
  178. if(castle != null)
  179. {
  180. startindex = 20*(castle.getCastleId()-1);
  181. mercPlaced[castle.getCastleId()-1] += 1;
  182. if (mercPlaced[castle.getCastleId()-1] > MERCS_MAX_PER_CASTLE[castle.getCastleId()-1])
  183. break;
  184. }
  185. // find the FIRST ticket itemId with spawns the saved NPC in the saved location
  186. for (int i=startindex;i<NPC_IDS.length;i++)
  187. if (NPC_IDS[i] == npcId) // Find the index of the item used
  188. {
  189. // only handle tickets if a siege is not ongoing in this npc's castle
  190. if((castle != null) && !(castle.getSiege().getIsInProgress()))
  191. {
  192. itemId = ITEM_IDS[i];
  193. // create the ticket in the gameworld
  194. L2ItemInstance dropticket = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
  195. dropticket.setLocation(L2ItemInstance.ItemLocation.INVENTORY);
  196. dropticket.dropMe(null, x, y, z);
  197. dropticket.setDropTime(0); //avoids it from beeing removed by the auto item destroyer
  198. L2World.getInstance().storeObject(dropticket);
  199. getDroppedTickets().add(dropticket);
  200. }
  201. break;
  202. }
  203. }
  204. statement.close();
  205. _log.info("Loaded: " + getDroppedTickets().size() + " Mercenary Tickets");
  206. }
  207. catch (Exception e)
  208. {
  209. _log.warning("Exception: loadMercenaryData(): " + e.getMessage());
  210. e.printStackTrace();
  211. }
  212. finally {try { con.close(); } catch (Exception e) {}}
  213. }
  214. // =========================================================
  215. // Property - Public
  216. /**
  217. * Checks if the passed item has reached the limit of number of dropped
  218. * tickets that this SPECIFIC item may have in its castle
  219. */
  220. public boolean isAtTypeLimit(int itemId)
  221. {
  222. int limit = -1;
  223. // find the max value for this item
  224. for (int i=0;i<ITEM_IDS.length;i++)
  225. if (ITEM_IDS[i] == itemId) // Find the index of the item used
  226. {
  227. limit = MAX_MERC_PER_TYPE[i];
  228. break;
  229. }
  230. if (limit <= 0)
  231. return true;
  232. int count = 0;
  233. L2ItemInstance ticket;
  234. for(int i=0; i<getDroppedTickets().size(); i++)
  235. {
  236. ticket = getDroppedTickets().get(i);
  237. if ( ticket != null && ticket.getItemId() == itemId)
  238. count++;
  239. }
  240. if(count >= limit)
  241. return true;
  242. return false;
  243. }
  244. /**
  245. * Checks if the passed item belongs to a castle which has reached its limit
  246. * of number of dropped tickets.
  247. */
  248. public boolean isAtCasleLimit(int itemId)
  249. {
  250. int castleId = getTicketCastleId(itemId);
  251. if (castleId <= 0)
  252. return true;
  253. int limit = MERCS_MAX_PER_CASTLE[castleId-1];
  254. if (limit <= 0)
  255. return true;
  256. int count = 0;
  257. L2ItemInstance ticket;
  258. for(int i=0; i<getDroppedTickets().size(); i++)
  259. {
  260. ticket = getDroppedTickets().get(i);
  261. if ( (ticket != null) && (getTicketCastleId(ticket.getItemId()) == castleId) )
  262. count++;
  263. }
  264. if(count >= limit)
  265. return true;
  266. return false;
  267. }
  268. public int getMaxAllowedMerc(int castleId)
  269. {
  270. return MERCS_MAX_PER_CASTLE[castleId-1];
  271. }
  272. public boolean isTooCloseToAnotherTicket(int x, int y, int z)
  273. {
  274. for (L2ItemInstance item : getDroppedTickets())
  275. {
  276. double dx = x - item.getX();
  277. double dy = y - item.getY();
  278. double dz = z - item.getZ();
  279. if ((dx*dx + dy*dy + dz*dz) < 25*25) return true;
  280. }
  281. return false;
  282. }
  283. /**
  284. * addTicket actions
  285. * 1) find the npc that needs to be saved in the mercenary spawns, given this item
  286. * 2) Use the passed character's location info to add the spawn
  287. * 3) create a copy of the item to drop in the world
  288. * returns the id of the mercenary npc that was added to the spawn
  289. * returns -1 if this fails.
  290. */
  291. public int addTicket(int itemId, L2PcInstance activeChar, String[] messages)
  292. {
  293. int x = activeChar.getX();
  294. int y = activeChar.getY();
  295. int z = activeChar.getZ();
  296. int heading = activeChar.getHeading();
  297. Castle castle = CastleManager.getInstance().getCastle(activeChar);
  298. if (castle == null) //this should never happen at this point
  299. return -1;
  300. for (int i = 0; i < ITEM_IDS.length; i++)
  301. {
  302. if (ITEM_IDS[i] == itemId) // Find the index of the item used
  303. {
  304. spawnMercenary(NPC_IDS[i], x, y, z, 3000, messages, 0);
  305. // Hire merc for this caslte. NpcId is at the same index as the item used.
  306. castle.getSiege().getSiegeGuardManager().hireMerc(x, y, z, heading, NPC_IDS[i]);
  307. // create the ticket in the gameworld
  308. L2ItemInstance dropticket = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
  309. dropticket.setLocation(L2ItemInstance.ItemLocation.INVENTORY);
  310. dropticket.dropMe(null, x, y, z);
  311. dropticket.setDropTime(0); //avoids it from beeing removed by the auto item destroyer
  312. L2World.getInstance().storeObject(dropticket); //add to the world
  313. // and keep track of this ticket in the list
  314. _droppedTickets.add(dropticket);
  315. return NPC_IDS[i];
  316. }
  317. }
  318. return -1;
  319. }
  320. private void spawnMercenary(int npcId, int x, int y, int z, int despawnDelay, String[] messages, int chatDelay)
  321. {
  322. L2NpcTemplate template = NpcTable.getInstance().getTemplate(npcId);
  323. if (template != null)
  324. {
  325. final L2SiegeGuardInstance npc = new L2SiegeGuardInstance(IdFactory.getInstance().getNextId(), template);
  326. npc.setCurrentHpMp(npc.getMaxHp(), npc.getMaxMp());
  327. npc.setDecayed(false);
  328. npc.spawnMe(x, y, (z+20));
  329. if (messages != null && messages.length >0 )
  330. AutoChatHandler.getInstance().registerChat(npc, messages, chatDelay);
  331. if (despawnDelay > 0)
  332. {
  333. ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() {
  334. public void run()
  335. {
  336. npc.deleteMe();
  337. }
  338. }, despawnDelay);
  339. }
  340. }
  341. }
  342. /**
  343. * Delete all tickets from a castle;
  344. * remove the items from the world and remove references to them from this class
  345. */
  346. public void deleteTickets(int castleId)
  347. {
  348. int i = 0;
  349. while ( i<getDroppedTickets().size() )
  350. {
  351. L2ItemInstance item = getDroppedTickets().get(i);
  352. if ((item != null) && (getTicketCastleId(item.getItemId()) == castleId))
  353. {
  354. item.decayMe();
  355. L2World.getInstance().removeObject(item);
  356. // remove from the list
  357. getDroppedTickets().remove(i);
  358. }
  359. else
  360. i++;
  361. }
  362. }
  363. /**
  364. * remove a single ticket and its associated spawn from the world
  365. * (used when the castle lord picks up a ticket, for example)
  366. */
  367. public void removeTicket(L2ItemInstance item)
  368. {
  369. int itemId = item.getItemId();
  370. int npcId = -1;
  371. // find the FIRST ticket itemId with spawns the saved NPC in the saved location
  372. for (int i=0;i<ITEM_IDS.length;i++)
  373. if (ITEM_IDS[i] == itemId) // Find the index of the item used
  374. {
  375. npcId = NPC_IDS[i];
  376. break;
  377. }
  378. // find the castle where this item is
  379. Castle castle = CastleManager.getInstance().getCastleById(getTicketCastleId(itemId));
  380. if (npcId > 0 && castle != null)
  381. {
  382. (new SiegeGuardManager(castle)).removeMerc(npcId, item.getX(), item.getY(), item.getZ());
  383. }
  384. getDroppedTickets().remove(item);
  385. }
  386. public int[] getItemIds()
  387. {
  388. return ITEM_IDS;
  389. }
  390. public final List<L2ItemInstance> getDroppedTickets()
  391. {
  392. if (_droppedTickets == null) _droppedTickets = new FastList<L2ItemInstance>();
  393. return _droppedTickets;
  394. }
  395. }