/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ package net.sf.l2j.gameserver.instancemanager; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.List; import java.util.logging.Logger; import javolution.util.FastList; import net.sf.l2j.L2DatabaseFactory; import net.sf.l2j.gameserver.ThreadPoolManager; import net.sf.l2j.gameserver.datatables.NpcTable; import net.sf.l2j.gameserver.idfactory.IdFactory; import net.sf.l2j.gameserver.model.AutoChatHandler; import net.sf.l2j.gameserver.model.L2ItemInstance; import net.sf.l2j.gameserver.model.L2World; import net.sf.l2j.gameserver.model.actor.instance.L2PcInstance; import net.sf.l2j.gameserver.model.actor.instance.L2SiegeGuardInstance; import net.sf.l2j.gameserver.model.entity.Castle; import net.sf.l2j.gameserver.templates.L2NpcTemplate; /** * @author yellowperil & Fulminus * 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. * These tickets (aka badges) need to be readded after each server reboot * except when the server crashed in the middle of an ongoig siege. * 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. * Finally, we provide auxilary functions to identify the castle in * which each item (and its corresponding NPC) belong to, in order to * help avoid mixing them up. * */ public class MercTicketManager { protected static final Logger _log = Logger.getLogger(CastleManager.class.getName()); // ========================================================= private static MercTicketManager _instance; public static final MercTicketManager getInstance() { //CastleManager.getInstance(); if (_instance == null) { _log.info("Initializing MercTicketManager"); _instance = new MercTicketManager(); _instance.load(); } return _instance; } // ========================================================= // ========================================================= // Data Field private List _droppedTickets; // to keep track of items on the ground //TODO move all these values into siege.properties // max tickets per merc type = 10 + (castleid * 2)? // max ticker per castle = 40 + (castleid * 20)? private static final int[] MAX_MERC_PER_TYPE = { 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Gludio 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, 15, // Dion 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Giran 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, 10, // Oren 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Aden 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Innadril 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Goddard 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, // Rune 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20, 20 // Schuttgart }; private static final int[] MERCS_MAX_PER_CASTLE = { 100, // Gludio 150, // Dion 200, // Giran 300, // Oren 400, // Aden 400, // Innadril 400, // Goddard 400, // Rune 400 // Schuttgart }; private static final int[] ITEM_IDS = { 3960, 3961, 3962, 3963, 3964, 3965, 3966, 3967, 3968, 3969, 6038, 6039, 6040, 6041, 6042, 6043, 6044, 6045, 6046, 6047, // Gludio 3973, 3974, 3975, 3976, 3977, 3978, 3979, 3980, 3981, 3982, 6051, 6052, 6053, 6054, 6055, 6056, 6057, 6058, 6059, 6060, // Dion 3986, 3987, 3988, 3989, 3990, 3991, 3992, 3993, 3994, 3995, 6064, 6065, 6066, 6067, 6068, 6069, 6070, 6071, 6072, 6073, // Giran 3999, 4000, 4001, 4002, 4003, 4004, 4005, 4006, 4007, 4008, 6077, 6078, 6079, 6080, 6081, 6082, 6083, 6084, 6085, 6086, // Oren 4012, 4013, 4014, 4015, 4016, 4017, 4018, 4019, 4020, 4021, 6090, 6091, 6092, 6093, 6094, 6095, 6096, 6097, 6098, 6099, // Aden 5205, 5206, 5207, 5208, 5209, 5210, 5211, 5212, 5213, 5214, 6105, 6106, 6107, 6108, 6109, 6110, 6111, 6112, 6113, 6114, // Innadril 6779, 6780, 6781, 6782, 6783, 6784, 6785, 6786, 6787, 6788, 6792, 6793, 6794, 6795, 6796, 6797, 6798, 6799, 6800, 6801, // Goddard 7973, 7974, 7975, 7976, 7977, 7978, 7979, 7980, 7981, 7982, 7988, 7989, 7990, 7991, 7992, 7993, 7994, 7995, 7996, 7997, // Rune 7918, 7919, 7920, 7921, 7922, 7923, 7924, 7925, 7926, 7927, 7931, 7932, 7933, 7934, 7935, 7936, 7937, 7938, 7939, 7940 // Schuttgart }; private static final int[] NPC_IDS = { 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Gludio 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Dion 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Giran 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Oren 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Aden 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Innadril 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Goddard 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039, // Rune 35010, 35011, 35012, 35013, 35014, 35015, 35016, 35017, 35018, 35019, 35030,35031,35032,35033,35034,35035,35036,35037,35038,35039 // Schuttgart }; // ========================================================= // Constructor public MercTicketManager() { } // ========================================================= // Method - Public // returns the castleId for the passed ticket item id public int getTicketCastleId(int itemId) { if ((itemId >= ITEM_IDS[0] && itemId <= ITEM_IDS[9]) || (itemId >= ITEM_IDS[10] && itemId <= ITEM_IDS[19])) return 1; // Gludio if ((itemId >= ITEM_IDS[20] && itemId <= ITEM_IDS[29]) || (itemId >= ITEM_IDS[30] && itemId <= ITEM_IDS[39])) return 2; // Dion if ((itemId >= ITEM_IDS[40] && itemId <= ITEM_IDS[49]) || (itemId >= ITEM_IDS[50] && itemId <= ITEM_IDS[59])) return 3; // Giran if ((itemId >= ITEM_IDS[60] && itemId <= ITEM_IDS[69]) || (itemId >= ITEM_IDS[70] && itemId <= ITEM_IDS[79])) return 4; // Oren if ((itemId >= ITEM_IDS[80] && itemId <= ITEM_IDS[89]) || (itemId >= ITEM_IDS[90] && itemId <= ITEM_IDS[99])) return 5; // Aden if ((itemId >= ITEM_IDS[100] && itemId <= ITEM_IDS[109]) || (itemId >= ITEM_IDS[110] && itemId <= ITEM_IDS[119])) return 6; // Innadril if ((itemId >= ITEM_IDS[120] && itemId <= ITEM_IDS[129]) || (itemId >= ITEM_IDS[130] && itemId <= ITEM_IDS[139])) return 7; // Goddard if ((itemId >= ITEM_IDS[140] && itemId <= ITEM_IDS[149]) || (itemId >= ITEM_IDS[150] && itemId <= ITEM_IDS[159])) return 8; // Rune if ((itemId >= ITEM_IDS[160] && itemId <= ITEM_IDS[169]) || (itemId >= ITEM_IDS[170] && itemId <= ITEM_IDS[179])) return 9; // Schuttgart return -1; } public void reload() { getDroppedTickets().clear(); load(); } // ========================================================= // Method - Private private final void load() { java.sql.Connection con = null; // load merc tickets into the world try { PreparedStatement statement; ResultSet rs; con = L2DatabaseFactory.getInstance().getConnection(); statement = con.prepareStatement("SELECT * FROM castle_siege_guards Where isHired = 1"); rs = statement.executeQuery(); int npcId; int itemId; int x,y,z; int mercPlaced[] = new int[20]; // start index to begin the search for the itemId corresponding to this NPC // this will help with: // a) skip unnecessary iterations in the search loop // b) avoid finding the wrong itemId whenever tickets of different spawn the same npc! int startindex = 0; while (rs.next()) { npcId = rs.getInt("npcId"); x = rs.getInt("x"); y = rs.getInt("y"); z = rs.getInt("z"); Castle castle = CastleManager.getInstance().getCastle(x,y,z); if(castle != null) { startindex = 20*(castle.getCastleId()-1); mercPlaced[castle.getCastleId()-1] += 1; if (mercPlaced[castle.getCastleId()-1] > MERCS_MAX_PER_CASTLE[castle.getCastleId()-1]) break; } // find the FIRST ticket itemId with spawns the saved NPC in the saved location for (int i=startindex;i= limit) return true; return false; } /** * Checks if the passed item belongs to a castle which has reached its limit * of number of dropped tickets. */ public boolean isAtCasleLimit(int itemId) { int castleId = getTicketCastleId(itemId); if (castleId <= 0) return true; int limit = MERCS_MAX_PER_CASTLE[castleId-1]; if (limit <= 0) return true; int count = 0; L2ItemInstance ticket; for(int i=0; i= limit) return true; return false; } public int getMaxAllowedMerc(int castleId) { return MERCS_MAX_PER_CASTLE[castleId-1]; } public boolean isTooCloseToAnotherTicket(int x, int y, int z) { for (L2ItemInstance item : getDroppedTickets()) { double dx = x - item.getX(); double dy = y - item.getY(); double dz = z - item.getZ(); if ((dx*dx + dy*dy + dz*dz) < 25*25) return true; } return false; } /** * addTicket actions * 1) find the npc that needs to be saved in the mercenary spawns, given this item * 2) Use the passed character's location info to add the spawn * 3) create a copy of the item to drop in the world * returns the id of the mercenary npc that was added to the spawn * returns -1 if this fails. */ public int addTicket(int itemId, L2PcInstance activeChar, String[] messages) { int x = activeChar.getX(); int y = activeChar.getY(); int z = activeChar.getZ(); int heading = activeChar.getHeading(); Castle castle = CastleManager.getInstance().getCastle(activeChar); if (castle == null) //this should never happen at this point return -1; for (int i = 0; i < ITEM_IDS.length; i++) { if (ITEM_IDS[i] == itemId) // Find the index of the item used { spawnMercenary(NPC_IDS[i], x, y, z, 3000, messages, 0); // Hire merc for this caslte. NpcId is at the same index as the item used. castle.getSiege().getSiegeGuardManager().hireMerc(x, y, z, heading, NPC_IDS[i]); // create the ticket in the gameworld L2ItemInstance dropticket = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId); dropticket.setLocation(L2ItemInstance.ItemLocation.INVENTORY); dropticket.dropMe(null, x, y, z); dropticket.setDropTime(0); //avoids it from beeing removed by the auto item destroyer L2World.getInstance().storeObject(dropticket); //add to the world // and keep track of this ticket in the list _droppedTickets.add(dropticket); return NPC_IDS[i]; } } return -1; } private void spawnMercenary(int npcId, int x, int y, int z, int despawnDelay, String[] messages, int chatDelay) { L2NpcTemplate template = NpcTable.getInstance().getTemplate(npcId); if (template != null) { final L2SiegeGuardInstance npc = new L2SiegeGuardInstance(IdFactory.getInstance().getNextId(), template); npc.setCurrentHpMp(npc.getMaxHp(), npc.getMaxMp()); npc.setDecayed(false); npc.spawnMe(x, y, (z+20)); if (messages != null && messages.length >0 ) AutoChatHandler.getInstance().registerChat(npc, messages, chatDelay); if (despawnDelay > 0) { ThreadPoolManager.getInstance().scheduleGeneral(new Runnable() { public void run() { npc.deleteMe(); } }, despawnDelay); } } } /** * Delete all tickets from a castle; * remove the items from the world and remove references to them from this class */ public void deleteTickets(int castleId) { int i = 0; while ( i 0 && castle != null) { (new SiegeGuardManager(castle)).removeMerc(npcId, item.getX(), item.getY(), item.getZ()); } getDroppedTickets().remove(item); } public int[] getItemIds() { return ITEM_IDS; } public final List getDroppedTickets() { if (_droppedTickets == null) _droppedTickets = new FastList(); return _droppedTickets; } }