Merged with released L2J-Unity files.

This commit is contained in:
mobiusdev
2016-06-12 01:34:09 +00:00
parent e003e87887
commit 635557f5da
18352 changed files with 3245113 additions and 2892959 deletions
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -1,127 +1,128 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.util.ArrayList;
import java.util.Date;
import java.util.List;
import com.l2jmobius.gameserver.script.DateRange;
import com.l2jmobius.gameserver.script.EventDrop;
/**
* This class manage drop of Special Events created by GM for a defined period.<br>
* During a Special Event all L2Attackable can drop extra Items.<br>
* Those extra Items are defined in the table <b>allNpcDateDrops</b>.<br>
* Each Special Event has a start and end date to stop to drop extra Items automatically.
*/
public class EventDroplist
{
/**
* The table containing all DataDrop object
*/
private static final List<DateDrop> ALL_NPC_DATE_DROPS = new ArrayList<>();
public static class DateDrop
{
protected final DateRange _dateRange;
private final EventDrop _eventDrop;
public DateDrop(DateRange dateRange, EventDrop eventDrop)
{
_dateRange = dateRange;
_eventDrop = eventDrop;
}
/**
* @return the _eventDrop
*/
public EventDrop getEventDrop()
{
return _eventDrop;
}
/**
* @return the _dateRange
*/
public DateRange getDateRange()
{
return _dateRange;
}
}
/**
* Create and Init a new DateDrop then add it to the allNpcDateDrops of EventDroplist .
* @param itemIdList The table containing all item identifier of this DateDrop
* @param count The table containing min and max value of this DateDrop
* @param chance The chance to obtain this drop
* @param dateRange The DateRange object to add to this DateDrop
*/
public void addGlobalDrop(int[] itemIdList, int[] count, int chance, DateRange dateRange)
{
ALL_NPC_DATE_DROPS.add(new DateDrop(dateRange, new EventDrop(itemIdList, count[0], count[1], chance)));
}
/**
* @param itemId the item Id for the drop
* @param min the minimum drop count
* @param max the maximum drop count
* @param chance the drop chance
* @param dateRange the event drop rate range
*/
public void addGlobalDrop(int itemId, long min, long max, int chance, DateRange dateRange)
{
ALL_NPC_DATE_DROPS.add(new DateDrop(dateRange, new EventDrop(itemId, min, max, chance)));
}
/**
* Adds an event drop for a given date range.
* @param dateRange the date range.
* @param eventDrop the event drop.
*/
public void addGlobalDrop(DateRange dateRange, EventDrop eventDrop)
{
ALL_NPC_DATE_DROPS.add(new DateDrop(dateRange, eventDrop));
}
/**
* @return all DateDrop of EventDroplist allNpcDateDrops within the date range.
*/
public List<DateDrop> getAllDrops()
{
final List<DateDrop> list = new ArrayList<>();
final Date currentDate = new Date();
for (DateDrop drop : ALL_NPC_DATE_DROPS)
{
if (drop._dateRange.isWithinRange(currentDate))
{
list.add(drop);
}
}
return list;
}
public static EventDroplist getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EventDroplist _instance = new EventDroplist();
}
}
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.util.Date;
import java.util.LinkedList;
import java.util.List;
import java.util.concurrent.CopyOnWriteArrayList;
import com.l2jmobius.gameserver.script.DateRange;
import com.l2jmobius.gameserver.script.EventDrop;
/**
* This class manage drop of Special Events created by GM for a defined period.<br>
* During a Special Event all L2Attackable can drop extra Items.<br>
* Those extra Items are defined in the table <b>allNpcDateDrops</b>.<br>
* Each Special Event has a start and end date to stop to drop extra Items automatically.
*/
public class EventDroplist
{
/**
* The table containing all DataDrop object
*/
private static final List<DateDrop> _allNpcDateDrops = new CopyOnWriteArrayList<>();
public static class DateDrop
{
protected final DateRange _dateRange;
private final EventDrop _eventDrop;
public DateDrop(DateRange dateRange, EventDrop eventDrop)
{
_dateRange = dateRange;
_eventDrop = eventDrop;
}
/**
* @return the _eventDrop
*/
public EventDrop getEventDrop()
{
return _eventDrop;
}
/**
* @return the _dateRange
*/
public DateRange getDateRange()
{
return _dateRange;
}
}
/**
* Create and Init a new DateDrop then add it to the allNpcDateDrops of EventDroplist .
* @param itemIdList The table containing all item identifier of this DateDrop
* @param count The table containing min and max value of this DateDrop
* @param chance The chance to obtain this drop
* @param dateRange The DateRange object to add to this DateDrop
*/
public void addGlobalDrop(int[] itemIdList, int[] count, int chance, DateRange dateRange)
{
_allNpcDateDrops.add(new DateDrop(dateRange, new EventDrop(itemIdList, count[0], count[1], chance)));
}
/**
* @param itemId the item Id for the drop
* @param min the minimum drop count
* @param max the maximum drop count
* @param chance the drop chance
* @param dateRange the event drop rate range
*/
public void addGlobalDrop(int itemId, long min, long max, int chance, DateRange dateRange)
{
_allNpcDateDrops.add(new DateDrop(dateRange, new EventDrop(itemId, min, max, chance)));
}
/**
* Adds an event drop for a given date range.
* @param dateRange the date range.
* @param eventDrop the event drop.
*/
public void addGlobalDrop(DateRange dateRange, EventDrop eventDrop)
{
_allNpcDateDrops.add(new DateDrop(dateRange, eventDrop));
}
/**
* @return all DateDrop of EventDroplist allNpcDateDrops within the date range.
*/
public List<DateDrop> getAllDrops()
{
final List<DateDrop> list = new LinkedList<>();
final Date currentDate = new Date();
for (DateDrop drop : _allNpcDateDrops)
{
if (drop._dateRange.isWithinRange(currentDate))
{
list.add(drop);
}
}
return list;
}
public static EventDroplist getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final EventDroplist _instance = new EventDroplist();
}
}
@@ -1,427 +1,448 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import static com.l2jmobius.gameserver.model.itemcontainer.Inventory.ADENA_ID;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.LogRecord;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.ThreadPoolManager;
import com.l2jmobius.gameserver.data.xml.impl.EnchantItemBonusData;
import com.l2jmobius.gameserver.engines.DocumentEngine;
import com.l2jmobius.gameserver.enums.ItemLocation;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.instance.L2EventMonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.item.OnItemCreate;
import com.l2jmobius.gameserver.model.items.L2Armor;
import com.l2jmobius.gameserver.model.items.L2EtcItem;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.items.L2Weapon;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.util.GMAudit;
/**
* This class serves as a container for all item templates in the game.
*/
public class ItemTable
{
private static Logger LOGGER = Logger.getLogger(ItemTable.class.getName());
private static Logger LOGGER_ITEMS = Logger.getLogger("item");
public static final Map<String, Integer> SLOTS = new HashMap<>();
private L2Item[] _allTemplates;
private final Map<Integer, L2EtcItem> _etcItems = new HashMap<>();
private final Map<Integer, L2Armor> _armors = new HashMap<>();
private final Map<Integer, L2Weapon> _weapons = new HashMap<>();
static
{
SLOTS.put("shirt", L2Item.SLOT_UNDERWEAR);
SLOTS.put("lbracelet", L2Item.SLOT_L_BRACELET);
SLOTS.put("rbracelet", L2Item.SLOT_R_BRACELET);
SLOTS.put("talisman", L2Item.SLOT_DECO);
SLOTS.put("chest", L2Item.SLOT_CHEST);
SLOTS.put("fullarmor", L2Item.SLOT_FULL_ARMOR);
SLOTS.put("head", L2Item.SLOT_HEAD);
SLOTS.put("hair", L2Item.SLOT_HAIR);
SLOTS.put("hairall", L2Item.SLOT_HAIRALL);
SLOTS.put("underwear", L2Item.SLOT_UNDERWEAR);
SLOTS.put("back", L2Item.SLOT_BACK);
SLOTS.put("neck", L2Item.SLOT_NECK);
SLOTS.put("legs", L2Item.SLOT_LEGS);
SLOTS.put("feet", L2Item.SLOT_FEET);
SLOTS.put("gloves", L2Item.SLOT_GLOVES);
SLOTS.put("chest,legs", L2Item.SLOT_CHEST | L2Item.SLOT_LEGS);
SLOTS.put("belt", L2Item.SLOT_BELT);
SLOTS.put("rhand", L2Item.SLOT_R_HAND);
SLOTS.put("lhand", L2Item.SLOT_L_HAND);
SLOTS.put("lrhand", L2Item.SLOT_LR_HAND);
SLOTS.put("rear;lear", L2Item.SLOT_R_EAR | L2Item.SLOT_L_EAR);
SLOTS.put("rfinger;lfinger", L2Item.SLOT_R_FINGER | L2Item.SLOT_L_FINGER);
SLOTS.put("wolf", L2Item.SLOT_WOLF);
SLOTS.put("greatwolf", L2Item.SLOT_GREATWOLF);
SLOTS.put("hatchling", L2Item.SLOT_HATCHLING);
SLOTS.put("strider", L2Item.SLOT_STRIDER);
SLOTS.put("babypet", L2Item.SLOT_BABYPET);
SLOTS.put("brooch", L2Item.SLOT_BROOCH);
SLOTS.put("brooch_jewel", L2Item.SLOT_BROOCH_JEWEL);
SLOTS.put("none", L2Item.SLOT_NONE);
// retail compatibility
SLOTS.put("onepiece", L2Item.SLOT_FULL_ARMOR);
SLOTS.put("hair2", L2Item.SLOT_HAIR2);
SLOTS.put("dhair", L2Item.SLOT_HAIRALL);
SLOTS.put("alldress", L2Item.SLOT_ALLDRESS);
SLOTS.put("deco1", L2Item.SLOT_DECO);
SLOTS.put("waist", L2Item.SLOT_BELT);
}
/**
* @return a reference to this ItemTable object
*/
public static ItemTable getInstance()
{
return SingletonHolder._instance;
}
protected ItemTable()
{
load();
}
private void load()
{
int highest = 0;
_armors.clear();
_etcItems.clear();
_weapons.clear();
for (L2Item item : DocumentEngine.getInstance().loadItems())
{
if (highest < item.getId())
{
highest = item.getId();
}
if (item instanceof L2EtcItem)
{
_etcItems.put(item.getId(), (L2EtcItem) item);
}
else if (item instanceof L2Armor)
{
_armors.put(item.getId(), (L2Armor) item);
}
else
{
_weapons.put(item.getId(), (L2Weapon) item);
}
}
buildFastLookupTable(highest);
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _etcItems.size() + " Etc Items");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _armors.size() + " Armor Items");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + _weapons.size() + " Weapon Items");
LOGGER.log(Level.INFO, getClass().getSimpleName() + ": Loaded: " + (_etcItems.size() + _armors.size() + _weapons.size()) + " Items in total.");
}
/**
* Builds a variable in which all items are putting in in function of their ID.
* @param size
*/
private void buildFastLookupTable(int size)
{
// Create a FastLookUp Table called _allTemplates of size : value of the highest item ID
LOGGER.info(getClass().getSimpleName() + ": Highest item id used:" + size);
_allTemplates = new L2Item[size + 1];
// Insert armor item in Fast Look Up Table
for (L2Armor item : _armors.values())
{
_allTemplates[item.getId()] = item;
}
// Insert weapon item in Fast Look Up Table
for (L2Weapon item : _weapons.values())
{
_allTemplates[item.getId()] = item;
}
// Insert etcItem item in Fast Look Up Table
for (L2EtcItem item : _etcItems.values())
{
_allTemplates[item.getId()] = item;
}
}
/**
* Returns the item corresponding to the item ID
* @param id : int designating the item
* @return L2Item
*/
public L2Item getTemplate(int id)
{
return (id >= _allTemplates.length) || (id < 0) ? null : _allTemplates[id];
}
/**
* Create the L2ItemInstance corresponding to the Item Identifier and quantitiy add logs the activity. <B><U> Actions</U> :</B>
* <li>Create and Init the L2ItemInstance corresponding to the Item Identifier and quantity</li>
* <li>Add the L2ItemInstance object to _allObjects of L2world</li>
* <li>Logs Item creation according to log settings</li>
* @param process : String Identifier of process triggering this action
* @param itemId : int Item Identifier of the item to be created
* @param count : int Quantity of items to be created for stackable items
* @param actor : L2PcInstance Player requesting the item creation
* @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
* @return L2ItemInstance corresponding to the new item
*/
public L2ItemInstance createItem(String process, int itemId, long count, L2PcInstance actor, Object reference)
{
// Create and Init the L2ItemInstance corresponding to the Item Identifier
final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
if (process.equalsIgnoreCase("loot"))
{
ScheduledFuture<?> itemLootShedule;
if ((reference instanceof L2Attackable) && ((L2Attackable) reference).isRaid()) // loot privilege for raids
{
if ((((L2Attackable) reference).getFirstCommandChannelAttacked() != null) && !Config.AUTO_LOOT_RAIDS)
{
item.setOwnerId(((L2Attackable) reference).getFirstCommandChannelAttacked().getLeaderObjectId());
itemLootShedule = ThreadPoolManager.getInstance().scheduleGeneral(new ResetOwner(item), Config.LOOT_RAIDS_PRIVILEGE_INTERVAL);
item.setItemLootShedule(itemLootShedule);
}
}
else if (!Config.AUTO_LOOT || ((reference instanceof L2EventMonsterInstance) && ((L2EventMonsterInstance) reference).eventDropOnGround()))
{
item.setOwnerId(actor.getObjectId());
itemLootShedule = ThreadPoolManager.getInstance().scheduleGeneral(new ResetOwner(item), 15000);
item.setItemLootShedule(itemLootShedule);
}
}
if (Config.DEBUG)
{
LOGGER.fine(getClass().getSimpleName() + ": Item created oid:" + item.getObjectId() + " itemid:" + itemId);
}
// Add the L2ItemInstance object to _allObjects of L2world
L2World.getInstance().storeObject(item);
// Set Item parameters
if (item.isStackable() && (count > 1))
{
item.setCount(count);
}
if (Config.LOG_ITEMS && !process.equals("Reset") && (!Config.LOG_ITEMS_SMALL_LOG || (Config.LOG_ITEMS_SMALL_LOG && (item.isEquipable() || (item.getId() == ADENA_ID)))))
{
final LogRecord record = new LogRecord(Level.INFO, "CREATE:" + process);
record.setLoggerName("item");
record.setParameters(new Object[]
{
item,
actor,
reference
});
LOGGER_ITEMS.log(record);
}
if ((actor != null) && actor.isGM())
{
String referenceName = "no-reference";
if (reference instanceof L2Object)
{
referenceName = ((L2Object) reference).getName() != null ? ((L2Object) reference).getName() : "no-name";
}
else if (reference instanceof String)
{
referenceName = (String) reference;
}
if (Config.GMAUDIT)
{
GMAudit.auditGMAction(actor.getName() + " [" + actor.getObjectId() + "]", process + "(id: " + itemId + " count: " + count + " name: " + item.getItemName() + " objId: " + item.getObjectId() + ")", actor.getTarget() != null ? actor.getTarget().getName() : "no-target", "L2Object referencing this action is: " + referenceName);
}
}
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnItemCreate(process, item, actor, reference), item.getItem());
return item;
}
public L2ItemInstance createItem(String process, int itemId, int count, L2PcInstance actor)
{
return createItem(process, itemId, count, actor, null);
}
/**
* Destroys the L2ItemInstance.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Sets L2ItemInstance parameters to be unusable</li>
* <li>Removes the L2ItemInstance object to _allObjects of L2world</li>
* <li>Logs Item deletion according to log settings</li>
* </ul>
* @param process a string identifier of process triggering this action.
* @param item the item instance to be destroyed.
* @param actor the player requesting the item destroy.
* @param reference the object referencing current action like NPC selling item or previous item in transformation.
*/
public void destroyItem(String process, L2ItemInstance item, L2PcInstance actor, Object reference)
{
synchronized (item)
{
final long old = item.getCount();
item.setCount(0);
item.setOwnerId(0);
item.setItemLocation(ItemLocation.VOID);
item.setLastChange(L2ItemInstance.REMOVED);
L2World.getInstance().removeObject(item);
IdFactory.getInstance().releaseId(item.getObjectId());
if (Config.LOG_ITEMS && (!Config.LOG_ITEMS_SMALL_LOG || (Config.LOG_ITEMS_SMALL_LOG && (item.isEquipable() || (item.getId() == ADENA_ID)))))
{
final LogRecord record = new LogRecord(Level.INFO, "DELETE:" + process);
record.setLoggerName("item");
record.setParameters(new Object[]
{
item,
"PrevCount(" + old + ")",
actor,
reference
});
LOGGER_ITEMS.log(record);
}
if ((actor != null) && actor.isGM())
{
String referenceName = "no-reference";
if (reference instanceof L2Object)
{
referenceName = ((L2Object) reference).getName() != null ? ((L2Object) reference).getName() : "no-name";
}
else if (reference instanceof String)
{
referenceName = (String) reference;
}
if (Config.GMAUDIT)
{
GMAudit.auditGMAction(actor.getName() + " [" + actor.getObjectId() + "]", process + "(id: " + item.getId() + " count: " + item.getCount() + " itemObjId: " + item.getObjectId() + ")", actor.getTarget() != null ? actor.getTarget().getName() : "no-target", "L2Object referencing this action is: " + referenceName);
}
}
// if it's a pet control item, delete the pet as well
if (item.getItem().isPetItem())
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?"))
{
// Delete the pet in db
statement.setInt(1, item.getObjectId());
statement.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "could not delete pet objectid:", e);
}
}
}
}
public void reload()
{
load();
EnchantItemBonusData.getInstance().load();
}
protected static class ResetOwner implements Runnable
{
L2ItemInstance _item;
public ResetOwner(L2ItemInstance item)
{
_item = item;
}
@Override
public void run()
{
_item.setOwnerId(0);
_item.setItemLootShedule(null);
}
}
public Set<Integer> getAllArmorsId()
{
return _armors.keySet();
}
public Collection<L2Armor> getAllArmors()
{
return _armors.values();
}
public Set<Integer> getAllWeaponsId()
{
return _weapons.keySet();
}
public Collection<L2Weapon> getAllWeapons()
{
return _weapons.values();
}
public Set<Integer> getAllEtcItemsId()
{
return _etcItems.keySet();
}
public Collection<L2EtcItem> getAllEtcItems()
{
return _etcItems.values();
}
public L2Item[] getAllItems()
{
return _allTemplates;
}
public int getArraySize()
{
return _allTemplates.length;
}
private static class SingletonHolder
{
protected static final ItemTable _instance = new ItemTable();
}
}
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import static com.l2jmobius.gameserver.model.itemcontainer.Inventory.ADENA_ID;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.util.Collection;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.concurrent.ScheduledFuture;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.ThreadPoolManager;
import com.l2jmobius.gameserver.data.xml.impl.EnchantItemHPBonusData;
import com.l2jmobius.gameserver.engines.DocumentEngine;
import com.l2jmobius.gameserver.enums.ItemLocation;
import com.l2jmobius.gameserver.idfactory.IdFactory;
import com.l2jmobius.gameserver.model.L2Object;
import com.l2jmobius.gameserver.model.L2World;
import com.l2jmobius.gameserver.model.actor.L2Attackable;
import com.l2jmobius.gameserver.model.actor.instance.L2EventMonsterInstance;
import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
import com.l2jmobius.gameserver.model.events.EventDispatcher;
import com.l2jmobius.gameserver.model.events.impl.item.OnItemCreate;
import com.l2jmobius.gameserver.model.items.L2Armor;
import com.l2jmobius.gameserver.model.items.L2EtcItem;
import com.l2jmobius.gameserver.model.items.L2Item;
import com.l2jmobius.gameserver.model.items.L2Weapon;
import com.l2jmobius.gameserver.model.items.instance.L2ItemInstance;
import com.l2jmobius.gameserver.util.GMAudit;
/**
* This class serves as a container for all item templates in the game.
*/
public class ItemTable
{
private static Logger LOGGER = Logger.getLogger(ItemTable.class.getName());
private static Logger LOGGER_ITEMS = Logger.getLogger("item");
public static final Map<String, Integer> _slots = new HashMap<>();
private L2Item[] _allTemplates;
private final Map<Integer, L2EtcItem> _etcItems;
private final Map<Integer, L2Armor> _armors;
private final Map<Integer, L2Weapon> _weapons;
static
{
_slots.put("shirt", L2Item.SLOT_UNDERWEAR);
_slots.put("lbracelet", L2Item.SLOT_L_BRACELET);
_slots.put("rbracelet", L2Item.SLOT_R_BRACELET);
_slots.put("talisman", L2Item.SLOT_DECO);
_slots.put("chest", L2Item.SLOT_CHEST);
_slots.put("fullarmor", L2Item.SLOT_FULL_ARMOR);
_slots.put("head", L2Item.SLOT_HEAD);
_slots.put("hair", L2Item.SLOT_HAIR);
_slots.put("hairall", L2Item.SLOT_HAIRALL);
_slots.put("underwear", L2Item.SLOT_UNDERWEAR);
_slots.put("back", L2Item.SLOT_BACK);
_slots.put("neck", L2Item.SLOT_NECK);
_slots.put("legs", L2Item.SLOT_LEGS);
_slots.put("feet", L2Item.SLOT_FEET);
_slots.put("gloves", L2Item.SLOT_GLOVES);
_slots.put("chest,legs", L2Item.SLOT_CHEST | L2Item.SLOT_LEGS);
_slots.put("belt", L2Item.SLOT_BELT);
_slots.put("rhand", L2Item.SLOT_R_HAND);
_slots.put("lhand", L2Item.SLOT_L_HAND);
_slots.put("lrhand", L2Item.SLOT_LR_HAND);
_slots.put("rear;lear", L2Item.SLOT_R_EAR | L2Item.SLOT_L_EAR);
_slots.put("rfinger;lfinger", L2Item.SLOT_R_FINGER | L2Item.SLOT_L_FINGER);
_slots.put("wolf", L2Item.SLOT_WOLF);
_slots.put("greatwolf", L2Item.SLOT_GREATWOLF);
_slots.put("hatchling", L2Item.SLOT_HATCHLING);
_slots.put("strider", L2Item.SLOT_STRIDER);
_slots.put("babypet", L2Item.SLOT_BABYPET);
_slots.put("brooch", L2Item.SLOT_BROOCH);
_slots.put("brooch_jewel", L2Item.SLOT_BROOCH_JEWEL);
_slots.put("none", L2Item.SLOT_NONE);
// retail compatibility
_slots.put("onepiece", L2Item.SLOT_FULL_ARMOR);
_slots.put("hair2", L2Item.SLOT_HAIR2);
_slots.put("dhair", L2Item.SLOT_HAIRALL);
_slots.put("alldress", L2Item.SLOT_ALLDRESS);
_slots.put("deco1", L2Item.SLOT_DECO);
_slots.put("waist", L2Item.SLOT_BELT);
}
/**
* @return a reference to this ItemTable object
*/
public static ItemTable getInstance()
{
return SingletonHolder._instance;
}
protected ItemTable()
{
_etcItems = new ConcurrentHashMap<>();
_armors = new ConcurrentHashMap<>();
_weapons = new ConcurrentHashMap<>();
load();
}
private void load()
{
int highest = 0;
_armors.clear();
_etcItems.clear();
_weapons.clear();
for (L2Item item : DocumentEngine.getInstance().loadItems())
{
if (highest < item.getId())
{
highest = item.getId();
}
if (item instanceof L2EtcItem)
{
_etcItems.put(item.getId(), (L2EtcItem) item);
}
else if (item instanceof L2Armor)
{
_armors.put(item.getId(), (L2Armor) item);
}
else
{
_weapons.put(item.getId(), (L2Weapon) item);
}
}
buildFastLookupTable(highest);
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _etcItems.size() + " Etc Items");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _armors.size() + " Armor Items");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + _weapons.size() + " Weapon Items");
LOGGER.info(getClass().getSimpleName() + ": Loaded: " + (_etcItems.size() + _armors.size() + _weapons.size()) + " Items in total.");
}
/**
* Builds a variable in which all items are putting in in function of their ID.
* @param size
*/
private void buildFastLookupTable(int size)
{
// Create a FastLookUp Table called _allTemplates of size : value of the highest item ID
LOGGER.info(getClass().getSimpleName() + ": Highest item id used: " + size);
_allTemplates = new L2Item[size + 1];
// Insert armor item in Fast Look Up Table
for (L2Armor item : _armors.values())
{
_allTemplates[item.getId()] = item;
}
// Insert weapon item in Fast Look Up Table
for (L2Weapon item : _weapons.values())
{
_allTemplates[item.getId()] = item;
}
// Insert etcItem item in Fast Look Up Table
for (L2EtcItem item : _etcItems.values())
{
_allTemplates[item.getId()] = item;
}
}
/**
* Returns the item corresponding to the item ID
* @param id : int designating the item
* @return L2Item
*/
public L2Item getTemplate(int id)
{
if ((id >= _allTemplates.length) || (id < 0))
{
return null;
}
return _allTemplates[id];
}
/**
* Create the L2ItemInstance corresponding to the Item Identifier and quantitiy add logs the activity. <B><U> Actions</U> :</B>
* <li>Create and Init the L2ItemInstance corresponding to the Item Identifier and quantity</li>
* <li>Add the L2ItemInstance object to _allObjects of L2world</li>
* <li>Logs Item creation according to log settings</li>
* @param process : String Identifier of process triggering this action
* @param itemId : int Item Identifier of the item to be created
* @param count : int Quantity of items to be created for stackable items
* @param actor : L2PcInstance Player requesting the item creation
* @param reference : Object Object referencing current action like NPC selling item or previous item in transformation
* @return L2ItemInstance corresponding to the new item
*/
public L2ItemInstance createItem(String process, int itemId, long count, L2PcInstance actor, Object reference)
{
// Create and Init the L2ItemInstance corresponding to the Item Identifier
final L2ItemInstance item = new L2ItemInstance(IdFactory.getInstance().getNextId(), itemId);
if (process.equalsIgnoreCase("loot"))
{
ScheduledFuture<?> itemLootShedule;
if ((reference instanceof L2Attackable) && ((L2Attackable) reference).isRaid()) // loot privilege for raids
{
final L2Attackable raid = (L2Attackable) reference;
// if in CommandChannel and was killing a World/RaidBoss
if ((raid.getFirstCommandChannelAttacked() != null) && !Config.AUTO_LOOT_RAIDS)
{
item.setOwnerId(raid.getFirstCommandChannelAttacked().getLeaderObjectId());
itemLootShedule = ThreadPoolManager.getInstance().scheduleGeneral(new ResetOwner(item), Config.LOOT_RAIDS_PRIVILEGE_INTERVAL);
item.setItemLootShedule(itemLootShedule);
}
}
else if (!Config.AUTO_LOOT || ((reference instanceof L2EventMonsterInstance) && ((L2EventMonsterInstance) reference).eventDropOnGround()))
{
item.setOwnerId(actor.getObjectId());
itemLootShedule = ThreadPoolManager.getInstance().scheduleGeneral(new ResetOwner(item), 15000);
item.setItemLootShedule(itemLootShedule);
}
}
if (Config.DEBUG)
{
LOGGER.finer(getClass().getSimpleName() + ": Item created: " + item);
}
// Add the L2ItemInstance object to _allObjects of L2world
L2World.getInstance().storeObject(item);
// Set Item parameters
if (item.isStackable() && (count > 1))
{
item.setCount(count);
}
if (Config.LOG_ITEMS && !process.equals("Reset"))
{
if (!Config.LOG_ITEMS_SMALL_LOG || (Config.LOG_ITEMS_SMALL_LOG && (item.isEquipable() || (item.getId() == ADENA_ID))))
{
if (item.getEnchantLevel() > 0)
{
LOGGER_ITEMS.info("CREATE:" + process + ", item " + item.getObjectId() + ":+" + item.getEnchantLevel() + " " + item.getItem().getName() + "(" + item.getCount() + "), " + actor + ", " + reference);
}
else
{
LOGGER_ITEMS.info("CREATE:" + process + ", item " + item.getObjectId() + ":" + item.getItem().getName() + "(" + item.getCount() + "), " + actor + ", " + reference);
}
}
}
if (actor != null)
{
if (actor.isGM())
{
String referenceName = "no-reference";
if (reference instanceof L2Object)
{
referenceName = (((L2Object) reference).getName() != null ? ((L2Object) reference).getName() : "no-name");
}
else if (reference instanceof String)
{
referenceName = (String) reference;
}
final String targetName = (actor.getTarget() != null ? actor.getTarget().getName() : "no-target");
if (Config.GMAUDIT)
{
GMAudit.auditGMAction(actor.getName() + " [" + actor.getObjectId() + "]", process + "(id: " + itemId + " count: " + count + " name: " + item.getItemName() + " objId: " + item.getObjectId() + ")", targetName, "L2Object referencing this action is: " + referenceName);
}
}
}
// Notify to scripts
EventDispatcher.getInstance().notifyEventAsync(new OnItemCreate(process, item, actor, reference), item.getItem());
return item;
}
public L2ItemInstance createItem(String process, int itemId, int count, L2PcInstance actor)
{
return createItem(process, itemId, count, actor, null);
}
/**
* Destroys the L2ItemInstance.<br>
* <B><U> Actions</U> :</B>
* <ul>
* <li>Sets L2ItemInstance parameters to be unusable</li>
* <li>Removes the L2ItemInstance object to _allObjects of L2world</li>
* <li>Logs Item deletion according to log settings</li>
* </ul>
* @param process a string identifier of process triggering this action.
* @param item the item instance to be destroyed.
* @param actor the player requesting the item destroy.
* @param reference the object referencing current action like NPC selling item or previous item in transformation.
*/
public void destroyItem(String process, L2ItemInstance item, L2PcInstance actor, Object reference)
{
synchronized (item)
{
final long old = item.getCount();
item.setCount(0);
item.setOwnerId(0);
item.setItemLocation(ItemLocation.VOID);
item.setLastChange(L2ItemInstance.REMOVED);
L2World.getInstance().removeObject(item);
IdFactory.getInstance().releaseId(item.getObjectId());
if (Config.LOG_ITEMS)
{
if (!Config.LOG_ITEMS_SMALL_LOG || (Config.LOG_ITEMS_SMALL_LOG && (item.isEquipable() || (item.getId() == ADENA_ID))))
{
if (item.getEnchantLevel() > 0)
{
LOGGER_ITEMS.info("DELETE:" + process + ", item " + item.getObjectId() + ":+" + item.getEnchantLevel() + " " + item.getItem().getName() + "(" + item.getCount() + "), PrevCount(" + old + "), " + actor + ", " + reference);
}
else
{
LOGGER_ITEMS.info("DELETE:" + process + ", item " + item.getObjectId() + ":" + item.getItem().getName() + "(" + item.getCount() + "), PrevCount(" + old + "), " + actor + ", " + reference);
}
}
}
if (actor != null)
{
if (actor.isGM())
{
String referenceName = "no-reference";
if (reference instanceof L2Object)
{
referenceName = (((L2Object) reference).getName() != null ? ((L2Object) reference).getName() : "no-name");
}
else if (reference instanceof String)
{
referenceName = (String) reference;
}
final String targetName = (actor.getTarget() != null ? actor.getTarget().getName() : "no-target");
if (Config.GMAUDIT)
{
GMAudit.auditGMAction(actor.getName() + " [" + actor.getObjectId() + "]", process + "(id: " + item.getId() + " count: " + item.getCount() + " itemObjId: " + item.getObjectId() + ")", targetName, "L2Object referencing this action is: " + referenceName);
}
}
}
// if it's a pet control item, delete the pet as well
if (item.getItem().isPetItem())
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement statement = con.prepareStatement("DELETE FROM pets WHERE item_obj_id=?"))
{
// Delete the pet in db
statement.setInt(1, item.getObjectId());
statement.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not delete pet objectid:", e);
}
}
}
}
public void reload()
{
load();
EnchantItemHPBonusData.getInstance().load();
}
protected static class ResetOwner implements Runnable
{
L2ItemInstance _item;
public ResetOwner(L2ItemInstance item)
{
_item = item;
}
@Override
public void run()
{
_item.setOwnerId(0);
_item.setItemLootShedule(null);
}
}
public Set<Integer> getAllArmorsId()
{
return _armors.keySet();
}
public Collection<L2Armor> getAllArmors()
{
return _armors.values();
}
public Set<Integer> getAllWeaponsId()
{
return _weapons.keySet();
}
public Collection<L2Weapon> getAllWeapons()
{
return _weapons.values();
}
public Set<Integer> getAllEtcItemsId()
{
return _etcItems.keySet();
}
public Collection<L2EtcItem> getAllEtcItems()
{
return _etcItems.values();
}
public L2Item[] getAllItems()
{
return _allTemplates;
}
public int getArraySize()
{
return _allTemplates.length;
}
private static class SingletonHolder
{
protected static final ItemTable _instance = new ItemTable();
}
}
@@ -1,293 +1,291 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Level;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.InstanceListManager;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.model.actor.instance.L2MerchantInstance;
import com.l2jmobius.gameserver.model.entity.Castle;
/**
* @author KenM
*/
public class MerchantPriceConfigTable implements InstanceListManager
{
// Zoey76: TODO: Implement using IXmlReader.
private static Logger LOGGER = Logger.getLogger(MerchantPriceConfigTable.class.getName());
public static MerchantPriceConfigTable getInstance()
{
return SingletonHolder._instance;
}
private static final String MPCS_FILE = "MerchantPriceConfig.xml";
private final Map<Integer, MerchantPriceConfig> _mpcs = new HashMap<>();
private MerchantPriceConfig _defaultMpc;
public MerchantPriceConfig getMerchantPriceConfig(L2MerchantInstance npc)
{
for (MerchantPriceConfig mpc : _mpcs.values())
{
if ((npc.getWorldRegion() != null) && npc.getWorldRegion().containsZone(mpc.getZoneId()))
{
return mpc;
}
}
return _defaultMpc;
}
public MerchantPriceConfig getMerchantPriceConfig(int id)
{
return _mpcs.get(id);
}
public void loadXML() throws SAXException, IOException, ParserConfigurationException
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringComments(true);
final File file = new File(Config.DATAPACK_ROOT + "/" + MPCS_FILE);
if (!file.exists())
{
return;
}
int defaultPriceConfigId;
final Document doc = factory.newDocumentBuilder().parse(file);
Node n = doc.getDocumentElement();
final Node dpcNode = n.getAttributes().getNamedItem("defaultPriceConfig");
if (dpcNode == null)
{
throw new IllegalStateException("merchantPriceConfig must define an 'defaultPriceConfig'");
}
defaultPriceConfigId = Integer.parseInt(dpcNode.getNodeValue());
MerchantPriceConfig mpc;
for (n = n.getFirstChild(); n != null; n = n.getNextSibling())
{
mpc = parseMerchantPriceConfig(n);
if (mpc != null)
{
_mpcs.put(mpc.getId(), mpc);
}
}
final MerchantPriceConfig defaultMpc = getMerchantPriceConfig(defaultPriceConfigId);
if (defaultMpc == null)
{
throw new IllegalStateException("'defaultPriceConfig' points to an non-loaded priceConfig");
}
_defaultMpc = defaultMpc;
}
private MerchantPriceConfig parseMerchantPriceConfig(Node n)
{
if (!n.getNodeName().equals("priceConfig"))
{
return null;
}
final int id;
final int baseTax;
int castleId = -1;
int zoneId = -1;
final String name;
Node node = n.getAttributes().getNamedItem("id");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'id'");
}
id = Integer.parseInt(node.getNodeValue());
node = n.getAttributes().getNamedItem("name");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'name'");
}
name = node.getNodeValue();
node = n.getAttributes().getNamedItem("baseTax");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'baseTax'");
}
baseTax = Integer.parseInt(node.getNodeValue());
node = n.getAttributes().getNamedItem("castleId");
if (node != null)
{
castleId = Integer.parseInt(node.getNodeValue());
}
node = n.getAttributes().getNamedItem("zoneId");
if (node != null)
{
zoneId = Integer.parseInt(node.getNodeValue());
}
return new MerchantPriceConfig(id, name, baseTax, castleId, zoneId);
}
@Override
public void loadInstances()
{
try
{
loadXML();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _mpcs.size() + " merchant price configs.");
}
catch (Exception e)
{
LOGGER.log(Level.SEVERE, getClass().getSimpleName() + ": Failed loading MerchantPriceConfigTable. Reason: " + e.getMessage(), e);
}
}
@Override
public void updateReferences()
{
for (MerchantPriceConfig mpc : _mpcs.values())
{
mpc.updateReferences();
}
}
@Override
public void activateInstances()
{
}
/**
* @author KenM
*/
public static final class MerchantPriceConfig
{
private final int _id;
private final String _name;
private final int _baseTax;
private final int _castleId;
private Castle _castle;
private final int _zoneId;
public MerchantPriceConfig(int id, String name, int baseTax, int castleId, int zoneId)
{
_id = id;
_name = name;
_baseTax = baseTax;
_castleId = castleId;
_zoneId = zoneId;
}
/**
* @return Returns the id.
*/
public int getId()
{
return _id;
}
/**
* @return Returns the name.
*/
public String getName()
{
return _name;
}
/**
* @return Returns the baseTax.
*/
public int getBaseTax()
{
return _baseTax;
}
/**
* @return Returns the baseTax / 100.0.
*/
public double getBaseTaxRate()
{
return _baseTax / 100.0;
}
/**
* @return Returns the castle.
*/
public Castle getCastle()
{
return _castle;
}
/**
* @return Returns the zoneId.
*/
public int getZoneId()
{
return _zoneId;
}
public boolean hasCastle()
{
return getCastle() != null;
}
public double getCastleTaxRate()
{
return hasCastle() ? getCastle().getTaxRate() : 0.0;
}
public int getTotalTax()
{
return hasCastle() ? (getCastle().getTaxPercent() + getBaseTax()) : getBaseTax();
}
public double getTotalTaxRate()
{
return getTotalTax() / 100.0;
}
public void updateReferences()
{
if (_castleId > 0)
{
_castle = CastleManager.getInstance().getCastleById(_castleId);
}
}
}
private static class SingletonHolder
{
protected static final MerchantPriceConfigTable _instance = new MerchantPriceConfigTable();
}
}
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.io.File;
import java.io.IOException;
import java.util.HashMap;
import java.util.Map;
import java.util.logging.Logger;
import javax.xml.parsers.DocumentBuilderFactory;
import javax.xml.parsers.ParserConfigurationException;
import org.w3c.dom.Document;
import org.w3c.dom.Node;
import org.xml.sax.SAXException;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.InstanceListManager;
import com.l2jmobius.gameserver.enums.TaxType;
import com.l2jmobius.gameserver.instancemanager.CastleManager;
import com.l2jmobius.gameserver.instancemanager.ZoneManager;
import com.l2jmobius.gameserver.model.actor.instance.L2MerchantInstance;
import com.l2jmobius.gameserver.model.entity.Castle;
/**
* @author KenM
*/
public class MerchantPriceConfigTable implements InstanceListManager
{
// Zoey76: TODO: Implement using IGameXmlReader.
private static Logger LOGGER = Logger.getLogger(MerchantPriceConfigTable.class.getName());
public static MerchantPriceConfigTable getInstance()
{
return SingletonHolder._instance;
}
private static final String MPCS_FILE = "MerchantPriceConfig.xml";
private final Map<Integer, MerchantPriceConfig> _mpcs = new HashMap<>();
private MerchantPriceConfig _defaultMpc;
public MerchantPriceConfig getMerchantPriceConfig(L2MerchantInstance npc)
{
for (MerchantPriceConfig mpc : _mpcs.values())
{
if (ZoneManager.getInstance().getRegion(npc).getZones().containsKey(mpc.getZoneId()))
{
return mpc;
}
}
return _defaultMpc;
}
public MerchantPriceConfig getMerchantPriceConfig(int id)
{
return _mpcs.get(id);
}
public void loadXML() throws SAXException, IOException, ParserConfigurationException
{
final DocumentBuilderFactory factory = DocumentBuilderFactory.newInstance();
factory.setValidating(false);
factory.setIgnoringComments(true);
final File file = new File(Config.DATAPACK_ROOT + "/data/" + MPCS_FILE);
if (file.exists())
{
int defaultPriceConfigId;
final Document doc = factory.newDocumentBuilder().parse(file);
Node n = doc.getDocumentElement();
final Node dpcNode = n.getAttributes().getNamedItem("defaultPriceConfig");
if (dpcNode == null)
{
throw new IllegalStateException("merchantPriceConfig must define an 'defaultPriceConfig'");
}
defaultPriceConfigId = Integer.parseInt(dpcNode.getNodeValue());
MerchantPriceConfig mpc;
for (n = n.getFirstChild(); n != null; n = n.getNextSibling())
{
mpc = parseMerchantPriceConfig(n);
if (mpc != null)
{
_mpcs.put(mpc.getId(), mpc);
}
}
final MerchantPriceConfig defaultMpc = this.getMerchantPriceConfig(defaultPriceConfigId);
if (defaultMpc == null)
{
throw new IllegalStateException("'defaultPriceConfig' points to an non-loaded priceConfig");
}
_defaultMpc = defaultMpc;
}
}
private MerchantPriceConfig parseMerchantPriceConfig(Node n)
{
if (n.getNodeName().equals("priceConfig"))
{
final int id;
final int baseTax;
int castleId = -1;
int zoneId = -1;
final String name;
Node node = n.getAttributes().getNamedItem("id");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'id'");
}
id = Integer.parseInt(node.getNodeValue());
node = n.getAttributes().getNamedItem("name");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'name'");
}
name = node.getNodeValue();
node = n.getAttributes().getNamedItem("baseTax");
if (node == null)
{
throw new IllegalStateException("Must define the priceConfig 'baseTax'");
}
baseTax = Integer.parseInt(node.getNodeValue());
node = n.getAttributes().getNamedItem("castleId");
if (node != null)
{
castleId = Integer.parseInt(node.getNodeValue());
}
node = n.getAttributes().getNamedItem("zoneId");
if (node != null)
{
zoneId = Integer.parseInt(node.getNodeValue());
}
return new MerchantPriceConfig(id, name, baseTax, castleId, zoneId);
}
return null;
}
@Override
public void loadInstances()
{
try
{
loadXML();
LOGGER.info("Loaded " + _mpcs.size() + " merchant price configs.");
}
catch (Exception e)
{
LOGGER.severe("Failed loading MerchantPriceConfigTable: " + e);
}
}
@Override
public void updateReferences()
{
for (MerchantPriceConfig mpc : _mpcs.values())
{
mpc.updateReferences();
}
}
@Override
public void activateInstances()
{
}
/**
* @author KenM
*/
public static final class MerchantPriceConfig
{
private final int _id;
private final String _name;
private final int _baseTax;
private final int _castleId;
private Castle _castle;
private final int _zoneId;
public MerchantPriceConfig(int id, String name, int baseTax, int castleId, int zoneId)
{
_id = id;
_name = name;
_baseTax = baseTax;
_castleId = castleId;
_zoneId = zoneId;
}
/**
* @return Returns the id.
*/
public int getId()
{
return _id;
}
/**
* @return Returns the name.
*/
public String getName()
{
return _name;
}
/**
* @return Returns the baseTax.
*/
public int getBaseTax()
{
return _baseTax;
}
/**
* @return Returns the baseTax / 100.0.
*/
public double getBaseTaxRate()
{
return _baseTax / 100.0;
}
/**
* @return Returns the castle.
*/
public Castle getCastle()
{
return _castle;
}
/**
* @return Returns the zoneId.
*/
public int getZoneId()
{
return _zoneId;
}
public boolean hasCastle()
{
return getCastle() != null;
}
public double getCastleTaxRate()
{
return hasCastle() ? getCastle().getTaxRate() : 0.0;
}
public int getTotalTax(TaxType taxType)
{
return hasCastle() ? (getCastle().getTaxPercent(taxType) + getBaseTax()) : getBaseTax();
}
public double getTotalTaxRate(TaxType taxType)
{
return getTotalTax(taxType) / 100.0;
}
public void updateReferences()
{
if (_castleId > 0)
{
_castle = CastleManager.getInstance().getCastleById(_castleId);
}
}
}
private static class SingletonHolder
{
protected static final MerchantPriceConfigTable _instance = new MerchantPriceConfigTable();
}
}
@@ -1,138 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.util.HashMap;
import java.util.Map;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.actor.L2Npc;
import com.l2jmobius.util.Rnd;
/**
* This class holds parameter, specific to certain NPCs.<br>
* It can be either general parameters overridden for certain NPC instance instead of template parameters(aggro range, for example), or some optional parameters, handled by datapack scripts.<br>
* @author GKR
*/
public class NpcPersonalAIData
{
private final Map<String, Map<String, Integer>> _AIData = new HashMap<>();
/**
* Instantiates a new table.
*/
protected NpcPersonalAIData()
{
}
/**
* Stores data for given spawn.
* @param spawnDat spawn to process
* @param data Map of AI values
*/
public void storeData(L2Spawn spawnDat, Map<String, Integer> data)
{
if ((data == null) || data.isEmpty())
{
return;
}
if (spawnDat.getName() == null)
{
spawnDat.setName(Long.toString(Rnd.nextLong()));
}
_AIData.put(spawnDat.getName(), data);
}
/**
* Gets AI value with given spawnName and paramName
* @param spawnName spawn name to check
* @param paramName parameter to check
* @return value of given parameter for given spawn name
*/
public int getAIValue(String spawnName, String paramName)
{
return hasAIValue(spawnName, paramName) ? _AIData.get(spawnName).get(paramName) : -1;
}
/**
* Verifies if there is AI value with given spawnName and paramName
* @param spawnName spawn name to check
* @param paramName parameter name to check
* @return {@code true} if parameter paramName is set for spawn spawnName, {@code false} otherwise
*/
public boolean hasAIValue(String spawnName, String paramName)
{
return (spawnName != null) && _AIData.containsKey(spawnName) && _AIData.get(spawnName).containsKey(paramName);
}
/**
* Initializes npc parameters by specified values.
* @param npc NPC to process
* @param spawn link to NPC's spawn
* @param spawnName name of spawn
*/
public void initializeNpcParameters(L2Npc npc, L2Spawn spawn, String spawnName)
{
if (!_AIData.containsKey(spawnName))
{
return;
}
final Map<String, Integer> map = _AIData.get(spawnName);
try
{
for (String key : map.keySet())
{
switch (key)
{
case "disableRandomAnimation":
{
npc.setRandomAnimationEnabled(map.get(key) == 0);
break;
}
case "disableRandomWalk":
{
npc.setIsNoRndWalk(map.get(key) == 1);
spawn.setIsNoRndWalk(map.get(key) == 1);
break;
}
}
}
}
catch (Exception e)
{
// Do nothing
}
}
/**
* Gets the single instance of NpcTable.
* @return single instance of NpcTable
*/
public static NpcPersonalAIData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final NpcPersonalAIData _instance = new NpcPersonalAIData();
}
}
@@ -1,184 +0,0 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.util.HashMap;
import java.util.HashSet;
import java.util.LinkedList;
import java.util.List;
import java.util.Map;
import java.util.Set;
import java.util.logging.Level;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.gameserver.data.xml.impl.SkillTreesData;
import com.l2jmobius.gameserver.engines.DocumentEngine;
import com.l2jmobius.gameserver.model.skills.CommonSkill;
import com.l2jmobius.gameserver.model.skills.Skill;
/**
* Skill data.
*/
public final class SkillData
{
private static Logger LOGGER = Logger.getLogger(SkillData.class.getName());
private final Map<Integer, Skill> _skills = new HashMap<>();
private final Map<Integer, Integer> _skillMaxLevel = new HashMap<>();
private final Set<Integer> _enchantable = new HashSet<>();
protected SkillData()
{
load();
}
public void reload()
{
load();
// Reload Skill Tree as well.
SkillTreesData.getInstance().load();
}
private void load()
{
final Map<Integer, Skill> _temp = new HashMap<>();
DocumentEngine.getInstance().loadAllSkills(_temp);
_skills.clear();
_skills.putAll(_temp);
_skillMaxLevel.clear();
_enchantable.clear();
for (Skill skill : _skills.values())
{
final int skillId = skill.getId();
final int skillLvl = skill.getLevel();
if (skillLvl > 99)
{
if (!_enchantable.contains(skillId))
{
_enchantable.add(skillId);
}
continue;
}
// only non-enchanted skills
if (skillLvl > getMaxLevel(skillId))
{
_skillMaxLevel.put(skillId, skillLvl);
}
}
}
/**
* Provides the skill hash
* @param skill The L2Skill to be hashed
* @return getSkillHashCode(skill.getId(), skill.getLevel())
*/
public static int getSkillHashCode(Skill skill)
{
return getSkillHashCode(skill.getId(), skill.getLevel());
}
/**
* Centralized method for easier change of the hashing sys
* @param skillId The Skill Id
* @param skillLevel The Skill Level
* @return The Skill hash number
*/
public static int getSkillHashCode(int skillId, int skillLevel)
{
return (skillId * 1031) + skillLevel;
}
public Skill getSkill(int skillId, int level)
{
final Skill result = _skills.get(getSkillHashCode(skillId, level));
if (result != null)
{
return result;
}
// skill/level not found, fix for transformation scripts
final int maxLvl = getMaxLevel(skillId);
// requested level too high
if ((maxLvl > 0) && (level > maxLvl))
{
if (Config.DEBUG)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": call to unexisting skill level id: " + skillId + " requested level: " + level + " max level: " + maxLvl, new Throwable());
}
return _skills.get(getSkillHashCode(skillId, maxLvl));
}
LOGGER.warning(getClass().getSimpleName() + ": No skill info found for skill id " + skillId + " and skill level " + level + ".");
return null;
}
public int getMaxLevel(int skillId)
{
final Integer maxLevel = _skillMaxLevel.get(skillId);
return maxLevel != null ? maxLevel : 0;
}
/**
* Verifies if the given skill ID correspond to an enchantable skill.
* @param skillId the skill ID
* @return {@code true} if the skill is enchantable, {@code false} otherwise
*/
public boolean isEnchantable(int skillId)
{
return _enchantable.contains(skillId);
}
/**
* @param addNoble
* @param hasCastle
* @return an array with siege skills. If addNoble == true, will add also Advanced headquarters.
*/
public List<Skill> getSiegeSkills(boolean addNoble, boolean hasCastle)
{
final List<Skill> temp = new LinkedList<>();
temp.add(_skills.get(SkillData.getSkillHashCode(CommonSkill.IMPRIT_OF_LIGHT.getId(), 1)));
temp.add(_skills.get(SkillData.getSkillHashCode(CommonSkill.IMPRIT_OF_DARKNESS.getId(), 1)));
temp.add(_skills.get(SkillData.getSkillHashCode(247, 1))); // Build Headquarters
if (addNoble)
{
temp.add(_skills.get(SkillData.getSkillHashCode(326, 1))); // Build Advanced Headquarters
}
if (hasCastle)
{
temp.add(_skills.get(SkillData.getSkillHashCode(844, 1))); // Outpost Construction
temp.add(_skills.get(SkillData.getSkillHashCode(845, 1))); // Outpost Demolition
}
return temp;
}
public static SkillData getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SkillData _instance = new SkillData();
}
}
@@ -1,528 +1,342 @@
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collections;
import java.util.HashMap;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.Document;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
import com.l2jmobius.gameserver.instancemanager.DayNightSpawnManager;
import com.l2jmobius.gameserver.instancemanager.ZoneManager;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jmobius.util.data.xml.IXmlReader;
/**
* Spawn data retriever.
* @author Zoey76
*/
public final class SpawnTable implements IXmlReader
{
private static final Logger LOGGER = Logger.getLogger(SpawnTable.class.getName());
// SQL
private static final String SELECT_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM spawnlist";
private static final String SELECT_CUSTOM_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM custom_spawnlist";
private static final Map<Integer, Set<L2Spawn>> _spawnTable = new ConcurrentHashMap<>();
private int _xmlSpawnCount = 0;
/**
* Wrapper to load all spawns.
*/
@Override
public void load()
{
if (Config.ALT_DEV_NO_SPAWNS)
{
return;
}
fillSpawnTable(false);
final int spawnCount = _spawnTable.size();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + spawnCount + " npc spawns.");
if (Config.CUSTOM_SPAWNLIST_TABLE)
{
fillSpawnTable(true);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + (_spawnTable.size() - spawnCount) + " custom npc spawns.");
}
// Load XML list
parseDatapackDirectory("spawnlist", false);
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _xmlSpawnCount + " npc spawns from XML.");
}
/**
* Verifies if the template exists and it's spawnable.
* @param npcId the NPC ID
* @return {@code true} if the NPC ID belongs to an spawnable template, {@code false} otherwise
*/
private boolean checkTemplate(int npcId)
{
final L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(npcId);
if (npcTemplate != null)
{
return !npcTemplate.isType("L2SiegeGuard") && !npcTemplate.isType("L2RaidBoss") && (Config.ALLOW_CLASS_MASTERS || !npcTemplate.isType("L2ClassMaster"));
}
LOGGER.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + npcId + ".");
// Don't spawn
return false;
}
@Override
public void parseDocument(Document doc)
{
NamedNodeMap attrs;
for (Node list = doc.getFirstChild(); list != null; list = list.getNextSibling())
{
if (list.getNodeName().equalsIgnoreCase("list"))
{
attrs = list.getAttributes();
// skip disabled spawnlists
if (!Boolean.parseBoolean(attrs.getNamedItem("enabled").getNodeValue()))
{
continue;
}
for (Node param = list.getFirstChild(); param != null; param = param.getNextSibling())
{
attrs = param.getAttributes();
if (param.getNodeName().equalsIgnoreCase("spawn"))
{
String territoryName = null;
String spawnName = null;
Map<String, Integer> map = null;
// Check, if spawn name specified
if (attrs.getNamedItem("name") != null)
{
spawnName = parseString(attrs, "name");
}
// Check, if spawn territory specified and exists
if ((attrs.getNamedItem("zone") != null) && (ZoneManager.getInstance().getSpawnTerritory(attrs.getNamedItem("zone").getNodeValue()) != null))
{
territoryName = parseString(attrs, "zone");
}
for (Node npctag = param.getFirstChild(); npctag != null; npctag = npctag.getNextSibling())
{
attrs = npctag.getAttributes();
// Check if there are any AI parameters
if (npctag.getNodeName().equalsIgnoreCase("AIData"))
{
attrs = npctag.getAttributes();
if (map == null)
{
map = new HashMap<>();
}
for (Node c = npctag.getFirstChild(); c != null; c = c.getNextSibling())
{
// Skip odd nodes
if (c.getNodeName().equals("#text"))
{
continue;
}
int val;
switch (c.getNodeName())
{
case "disableRandomAnimation":
case "disableRandomWalk":
{
val = Boolean.parseBoolean(c.getTextContent()) ? 1 : 0;
break;
}
default:
{
val = Integer.parseInt(c.getTextContent());
}
}
map.put(c.getNodeName(), val);
}
}
// Check for NPC spawns
else if (npctag.getNodeName().equalsIgnoreCase("npc"))
{
// mandatory
final int templateId = parseInteger(attrs, "id");
// coordinates are optional, if territory is specified; mandatory otherwise
int x = 0;
int y = 0;
int z = 0;
try
{
x = parseInteger(attrs, "x");
y = parseInteger(attrs, "y");
z = parseInteger(attrs, "z");
}
catch (NullPointerException npe)
{
// x, y, z can be unspecified, if this spawn is territory based, do nothing
}
if ((x == 0) && (y == 0) && (territoryName == null)) // Both coordinates and zone are unspecified
{
LOGGER.warning("XML Spawnlist: Spawn could not be initialized, both coordinates and zone are unspecified for ID " + templateId);
continue;
}
final StatsSet spawnInfo = new StatsSet();
spawnInfo.set("npcTemplateid", templateId);
spawnInfo.set("x", x);
spawnInfo.set("y", y);
spawnInfo.set("z", z);
spawnInfo.set("territoryName", territoryName);
spawnInfo.set("spawnName", spawnName);
// trying to read optional parameters
if (attrs.getNamedItem("heading") != null)
{
spawnInfo.set("heading", parseInteger(attrs, "heading"));
}
if (attrs.getNamedItem("count") != null)
{
spawnInfo.set("count", parseInteger(attrs, "count"));
}
if (attrs.getNamedItem("respawnDelay") != null)
{
spawnInfo.set("respawnDelay", parseInteger(attrs, "respawnDelay"));
}
if (attrs.getNamedItem("respawnRandom") != null)
{
spawnInfo.set("respawnRandom", parseInteger(attrs, "respawnRandom"));
}
if (attrs.getNamedItem("periodOfDay") != null)
{
final String period = attrs.getNamedItem("periodOfDay").getNodeValue();
if (period.equalsIgnoreCase("day") || period.equalsIgnoreCase("night"))
{
spawnInfo.set("periodOfDay", period.equalsIgnoreCase("day") ? 1 : 2);
}
}
_xmlSpawnCount += addSpawn(spawnInfo, map);
}
}
}
}
}
}
}
/**
* Retrieves spawn data from database.
* @param isCustom if {@code true} the spawns are loaded as custom from custom spawn table
* @return the spawn count
*/
private int fillSpawnTable(boolean isCustom)
{
int npcSpawnCount = 0;
try (Connection con = DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(isCustom ? SELECT_CUSTOM_SPAWNS : SELECT_SPAWNS))
{
while (rs.next())
{
final StatsSet spawnInfo = new StatsSet();
final int npcId = rs.getInt("npc_templateid");
// Check basic requirements first
if (!checkTemplate(npcId))
{
// Don't spawn
continue;
}
spawnInfo.set("npcTemplateid", npcId);
spawnInfo.set("count", rs.getInt("count"));
spawnInfo.set("x", rs.getInt("locx"));
spawnInfo.set("y", rs.getInt("locy"));
spawnInfo.set("z", rs.getInt("locz"));
spawnInfo.set("heading", rs.getInt("heading"));
spawnInfo.set("respawnDelay", rs.getInt("respawn_delay"));
spawnInfo.set("respawnRandom", rs.getInt("respawn_random"));
spawnInfo.set("locId", rs.getInt("loc_id"));
spawnInfo.set("periodOfDay", rs.getInt("periodOfDay"));
spawnInfo.set("isCustomSpawn", isCustom);
npcSpawnCount += addSpawn(spawnInfo);
}
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Spawn could not be initialized: " + e.getMessage(), e);
}
return npcSpawnCount;
}
/**
* Creates NPC spawn
* @param spawnInfo StatsSet of spawn parameters
* @param AIData Map of specific AI parameters for this spawn
* @return count NPC instances, spawned by this spawn
*/
private int addSpawn(StatsSet spawnInfo, Map<String, Integer> AIData)
{
L2Spawn spawnDat;
int ret = 0;
try
{
spawnDat = new L2Spawn(spawnInfo.getInt("npcTemplateid"));
spawnDat.setAmount(spawnInfo.getInt("count", 1));
spawnDat.setX(spawnInfo.getInt("x", 0));
spawnDat.setY(spawnInfo.getInt("y", 0));
spawnDat.setZ(spawnInfo.getInt("z", 0));
spawnDat.setHeading(spawnInfo.getInt("heading", -1));
spawnDat.setRespawnDelay(spawnInfo.getInt("respawnDelay", 0), spawnInfo.getInt("respawnRandom", 0));
spawnDat.setLocationId(spawnInfo.getInt("locId", 0));
final String territoryName = spawnInfo.getString("territoryName", "");
final String spawnName = spawnInfo.getString("spawnName", "");
spawnDat.setCustom(spawnInfo.getBoolean("isCustomSpawn", false));
if (!spawnName.isEmpty())
{
spawnDat.setName(spawnName);
}
if (!territoryName.isEmpty())
{
spawnDat.setSpawnTerritory(ZoneManager.getInstance().getSpawnTerritory(territoryName));
}
// Register AI Data for this spawn
NpcPersonalAIData.getInstance().storeData(spawnDat, AIData);
switch (spawnInfo.getInt("periodOfDay", 0))
{
case 0: // default
{
ret += spawnDat.init();
break;
}
case 1: // Day
{
DayNightSpawnManager.getInstance().addDayCreature(spawnDat);
ret = 1;
break;
}
case 2: // Night
{
DayNightSpawnManager.getInstance().addNightCreature(spawnDat);
ret = 1;
break;
}
}
addSpawn(spawnDat);
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, "Spawn could not be initialized: " + e.getMessage(), e);
}
return ret;
}
/**
* Wrapper for {@link #addSpawn(StatsSet, Map)}.
* @param spawnInfo StatsSet of spawn parameters
* @return count NPC instances, spawned by this spawn
*/
private int addSpawn(StatsSet spawnInfo)
{
return addSpawn(spawnInfo, null);
}
/**
* Gets the spawn data.
* @return the spawn data
*/
public Map<Integer, Set<L2Spawn>> getSpawnTable()
{
return _spawnTable;
}
/**
* Gets the spawns for the NPC Id.
* @param npcId the NPC Id
* @return the spawn set for the given npcId
*/
public Set<L2Spawn> getSpawns(int npcId)
{
return _spawnTable.getOrDefault(npcId, Collections.emptySet());
}
/**
* Gets the spawn count for the given NPC ID.
* @param npcId the NPC Id
* @return the spawn count
*/
public int getSpawnCount(int npcId)
{
return getSpawns(npcId).size();
}
/**
* Finds a spawn for the given NPC ID.
* @param npcId the NPC Id
* @return a spawn for the given NPC ID or {@code null}
*/
public L2Spawn findAny(int npcId)
{
return getSpawns(npcId).stream().findFirst().orElse(null);
}
/**
* Adds a new spawn to the spawn table.
* @param spawn the spawn to add
* @param storeInDb if {@code true} it'll be saved in the database
*/
public void addNewSpawn(L2Spawn spawn, boolean storeInDb)
{
addSpawn(spawn);
if (!storeInDb)
{
return;
}
final String spawnTable = spawn.isCustom() && Config.CUSTOM_SPAWNLIST_TABLE ? "custom_spawnlist" : "spawnlist";
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement insert = con.prepareStatement("INSERT INTO " + spawnTable + "(count,npc_templateid,locx,locy,locz,heading,respawn_delay,respawn_random,loc_id) values(?,?,?,?,?,?,?,?,?)"))
{
insert.setInt(1, spawn.getAmount());
insert.setInt(2, spawn.getId());
insert.setInt(3, spawn.getX());
insert.setInt(4, spawn.getY());
insert.setInt(5, spawn.getZ());
insert.setInt(6, spawn.getHeading());
insert.setInt(7, spawn.getRespawnDelay() / 1000);
insert.setInt(8, spawn.getRespawnMaxDelay() - spawn.getRespawnMinDelay());
insert.setInt(9, spawn.getLocationId());
insert.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Could not store spawn in the DB:" + e.getMessage(), e);
}
}
/**
* Delete an spawn from the spawn table.
* @param spawn the spawn to delete
* @param updateDb if {@code true} database will be updated
*/
public void deleteSpawn(L2Spawn spawn, boolean updateDb)
{
if (!removeSpawn(spawn))
{
return;
}
if (updateDb)
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement delete = con.prepareStatement("DELETE FROM " + (spawn.isCustom() ? "custom_spawnlist" : "spawnlist") + " WHERE locx=? AND locy=? AND locz=? AND npc_templateid=? AND heading=?"))
{
delete.setInt(1, spawn.getX());
delete.setInt(2, spawn.getY());
delete.setInt(3, spawn.getZ());
delete.setInt(4, spawn.getId());
delete.setInt(5, spawn.getHeading());
delete.execute();
}
catch (Exception e)
{
LOGGER.log(Level.WARNING, getClass().getSimpleName() + ": Spawn " + spawn + " could not be removed from DB: " + e.getMessage(), e);
}
}
}
/**
* Add a spawn to the spawn set if present, otherwise add a spawn set and add the spawn to the newly created spawn set.
* @param spawn the NPC spawn to add
*/
private void addSpawn(L2Spawn spawn)
{
_spawnTable.computeIfAbsent(spawn.getId(), k -> ConcurrentHashMap.newKeySet(1)).add(spawn);
}
/**
* Remove a spawn from the spawn set, if the spawn set is empty, remove it as well.
* @param spawn the NPC spawn to remove
* @return {@code true} if the spawn was successfully removed, {@code false} otherwise
*/
private boolean removeSpawn(L2Spawn spawn)
{
final Set<L2Spawn> set = _spawnTable.get(spawn.getId());
if (set == null)
{
return false;
}
final boolean removed = set.remove(spawn);
if (set.isEmpty())
{
_spawnTable.remove(spawn.getId());
}
return removed;
}
/**
* Execute a procedure over all spawns.<br>
* <font size="4" color="red">Do not use it!</font>
* @param function the function to execute
* @return {@code true} if all procedures were executed, {@code false} otherwise
*/
public boolean forEachSpawn(Function<L2Spawn, Boolean> function)
{
for (Set<L2Spawn> set : _spawnTable.values())
{
for (L2Spawn spawn : set)
{
if (!function.apply(spawn))
{
return false;
}
}
}
return true;
}
public static SpawnTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SpawnTable _instance = new SpawnTable();
}
}
/*
* This file is part of the L2J Mobius project.
*
* 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 <http://www.gnu.org/licenses/>.
*/
package com.l2jmobius.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.Statement;
import java.util.Collections;
import java.util.Map;
import java.util.Set;
import java.util.concurrent.ConcurrentHashMap;
import java.util.function.Function;
import java.util.logging.Logger;
import com.l2jmobius.Config;
import com.l2jmobius.commons.database.DatabaseFactory;
import com.l2jmobius.gameserver.data.xml.impl.NpcData;
import com.l2jmobius.gameserver.model.L2Spawn;
import com.l2jmobius.gameserver.model.StatsSet;
import com.l2jmobius.gameserver.model.actor.templates.L2NpcTemplate;
/**
* Spawn data retriever.
* @author Zoey76
*/
public final class SpawnTable
{
private static final Logger LOGGER = Logger.getLogger(SpawnTable.class.getName());
// SQL
private static final String SELECT_CUSTOM_SPAWNS = "SELECT count, npc_templateid, locx, locy, locz, heading, respawn_delay, respawn_random, loc_id, periodOfDay FROM custom_spawnlist";
private static final String INSERT_CUSTOM_SPAWN = "INSERT INTO custom_spawnlist (count,npc_templateid,locx,locy,locz,heading,respawn_delay,respawn_random,loc_id) values(?,?,?,?,?,?,?,?,?)";
private static final String DELETE_CUSTOM_SPAWN = "DELETE FROM custom_spawnlist WHERE locx=? AND locy=? AND locz=? AND npc_templateid=? AND heading=?";
private static final Map<Integer, Set<L2Spawn>> _spawnTable = new ConcurrentHashMap<>();
/**
* Wrapper to load all spawns.
*/
public void load()
{
if (!Config.ALT_DEV_NO_SPAWNS && Config.CUSTOM_SPAWNLIST_TABLE)
{
fillSpawnTable();
LOGGER.info(getClass().getSimpleName() + ": Loaded " + _spawnTable.size() + " custom npc spawns.");
}
}
private boolean checkTemplate(int npcId)
{
final L2NpcTemplate npcTemplate = NpcData.getInstance().getTemplate(npcId);
if (npcTemplate == null)
{
LOGGER.warning(getClass().getSimpleName() + ": Data missing in NPC table for ID: " + npcId + ".");
return false;
}
if (npcTemplate.isType("L2SiegeGuard") || npcTemplate.isType("L2RaidBoss"))
{
// Don't spawn
return false;
}
return true;
}
/**
* Retrieves spawn data from database.
* @return the spawn count
*/
private int fillSpawnTable()
{
int npcSpawnCount = 0;
try (Connection con = DatabaseFactory.getInstance().getConnection();
Statement s = con.createStatement();
ResultSet rs = s.executeQuery(SELECT_CUSTOM_SPAWNS))
{
while (rs.next())
{
final StatsSet spawnInfo = new StatsSet();
final int npcId = rs.getInt("npc_templateid");
// Check basic requirements first
if (!checkTemplate(npcId))
{
// Don't spawn
continue;
}
spawnInfo.set("npcTemplateid", npcId);
spawnInfo.set("count", rs.getInt("count"));
spawnInfo.set("x", rs.getInt("locx"));
spawnInfo.set("y", rs.getInt("locy"));
spawnInfo.set("z", rs.getInt("locz"));
spawnInfo.set("heading", rs.getInt("heading"));
spawnInfo.set("respawnDelay", rs.getInt("respawn_delay"));
spawnInfo.set("respawnRandom", rs.getInt("respawn_random"));
spawnInfo.set("locId", rs.getInt("loc_id"));
spawnInfo.set("periodOfDay", rs.getInt("periodOfDay"));
npcSpawnCount += addSpawn(spawnInfo);
}
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Spawn could not be initialized: " + e);
}
return npcSpawnCount;
}
/**
* Creates NPC spawn
* @param spawnInfo StatsSet of spawn parameters
* @param AIData Map of specific AI parameters for this spawn
* @return count NPC instances, spawned by this spawn
*/
private int addSpawn(StatsSet spawnInfo, Map<String, Integer> AIData)
{
L2Spawn spawnDat;
int ret = 0;
try
{
spawnDat = new L2Spawn(spawnInfo.getInt("npcTemplateid"));
spawnDat.setAmount(spawnInfo.getInt("count", 1));
spawnDat.setX(spawnInfo.getInt("x", 0));
spawnDat.setY(spawnInfo.getInt("y", 0));
spawnDat.setZ(spawnInfo.getInt("z", 0));
spawnDat.setHeading(spawnInfo.getInt("heading", -1));
spawnDat.setRespawnDelay(spawnInfo.getInt("respawnDelay", 0), spawnInfo.getInt("respawnRandom", 0));
spawnDat.setLocationId(spawnInfo.getInt("locId", 0));
final String spawnName = spawnInfo.getString("spawnName", "");
if (!spawnName.isEmpty())
{
spawnDat.setName(spawnName);
}
addSpawn(spawnDat);
ret += spawnDat.init();
}
catch (Exception e)
{
// problem with initializing spawn, go to next one
LOGGER.warning(getClass().getSimpleName() + ": Spawn could not be initialized: " + e);
}
return ret;
}
/**
* Wrapper for {@link #addSpawn(StatsSet, Map)}.
* @param spawnInfo StatsSet of spawn parameters
* @return count NPC instances, spawned by this spawn
*/
private int addSpawn(StatsSet spawnInfo)
{
return addSpawn(spawnInfo, null);
}
/**
* Gets the spawn data.
* @return the spawn data
*/
public Map<Integer, Set<L2Spawn>> getSpawnTable()
{
return _spawnTable;
}
/**
* Gets the spawns for the NPC Id.
* @param npcId the NPC Id
* @return the spawn set for the given npcId
*/
public Set<L2Spawn> getSpawns(int npcId)
{
return _spawnTable.getOrDefault(npcId, Collections.emptySet());
}
/**
* Gets the spawn count for the given NPC ID.
* @param npcId the NPC Id
* @return the spawn count
*/
public int getSpawnCount(int npcId)
{
return getSpawns(npcId).size();
}
/**
* Gets a spawn for the given NPC ID.
* @param npcId the NPC Id
* @return a spawn for the given NPC ID or {@code null}
*/
public L2Spawn getAnySpawn(int npcId)
{
return getSpawns(npcId).stream().findFirst().orElse(null);
}
/**
* Adds a new spawn to the spawn table.
* @param spawn the spawn to add
* @param storeInDb if {@code true} it'll be saved in the database
*/
public void addNewSpawn(L2Spawn spawn, boolean storeInDb)
{
addSpawn(spawn);
if (storeInDb)
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(INSERT_CUSTOM_SPAWN))
{
ps.setInt(1, spawn.getAmount());
ps.setInt(2, spawn.getId());
ps.setInt(3, spawn.getX());
ps.setInt(4, spawn.getY());
ps.setInt(5, spawn.getZ());
ps.setInt(6, spawn.getHeading());
ps.setInt(7, spawn.getRespawnDelay() / 1000);
ps.setInt(8, spawn.getRespawnMaxDelay() - spawn.getRespawnMinDelay());
ps.setInt(9, spawn.getLocationId());
ps.execute();
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Could not store spawn in the DB: " + e);
}
}
}
/**
* Delete an spawn from the spawn table.
* @param spawn the spawn to delete
* @param updateDb if {@code true} database will be updated
*/
public void deleteSpawn(L2Spawn spawn, boolean updateDb)
{
if (!removeSpawn(spawn))
{
return;
}
if (updateDb)
{
try (Connection con = DatabaseFactory.getInstance().getConnection();
PreparedStatement ps = con.prepareStatement(DELETE_CUSTOM_SPAWN))
{
ps.setInt(1, spawn.getX());
ps.setInt(2, spawn.getY());
ps.setInt(3, spawn.getZ());
ps.setInt(4, spawn.getId());
ps.setInt(5, spawn.getHeading());
ps.execute();
}
catch (Exception e)
{
LOGGER.warning(getClass().getSimpleName() + ": Spawn " + spawn + " could not be removed from DB: " + e);
}
}
}
/**
* Add a spawn to the spawn set if present, otherwise add a spawn set and add the spawn to the newly created spawn set.
* @param spawn the NPC spawn to add
*/
private void addSpawn(L2Spawn spawn)
{
_spawnTable.computeIfAbsent(spawn.getId(), k -> ConcurrentHashMap.newKeySet(1)).add(spawn);
}
/**
* Remove a spawn from the spawn set, if the spawn set is empty, remove it as well.
* @param spawn the NPC spawn to remove
* @return {@code true} if the spawn was successfully removed, {@code false} otherwise
*/
private boolean removeSpawn(L2Spawn spawn)
{
final Set<L2Spawn> set = _spawnTable.get(spawn.getId());
if (set != null)
{
final boolean removed = set.remove(spawn);
if (set.isEmpty())
{
_spawnTable.remove(spawn.getId());
}
set.forEach(this::notifyRemoved);
return removed;
}
notifyRemoved(spawn);
return false;
}
private void notifyRemoved(L2Spawn spawn)
{
if ((spawn != null) && (spawn.getLastSpawn() != null) && (spawn.getNpcSpawnTemplate() != null))
{
spawn.getNpcSpawnTemplate().notifyDespawnNpc(spawn.getLastSpawn());
}
}
/**
* Execute a procedure over all spawns.<br>
* <font size="4" color="red">Do not use it!</font>
* @param function the function to execute
* @return {@code true} if all procedures were executed, {@code false} otherwise
*/
public boolean forEachSpawn(Function<L2Spawn, Boolean> function)
{
for (Set<L2Spawn> set : _spawnTable.values())
{
for (L2Spawn spawn : set)
{
if (!function.apply(spawn))
{
return false;
}
}
}
return true;
}
public static SpawnTable getInstance()
{
return SingletonHolder._instance;
}
private static class SingletonHolder
{
protected static final SpawnTable _instance = new SpawnTable();
}
}