/*
* 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;
}
}