/*
* Copyright (C) 2004-2013 L2J Server
*
* This file is part of L2J Server.
*
* L2J Server 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.
*
* L2J Server 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 com.l2jserver.gameserver.datatables;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.ArrayList;
import java.util.Collection;
import java.util.HashMap;
import java.util.List;
import java.util.Map;
import java.util.Map.Entry;
import java.util.logging.Level;
import java.util.logging.Logger;
import org.w3c.dom.NamedNodeMap;
import org.w3c.dom.Node;
import com.l2jserver.Config;
import com.l2jserver.L2DatabaseFactory;
import com.l2jserver.gameserver.engines.DocumentParser;
import com.l2jserver.gameserver.model.Elementals;
import com.l2jserver.gameserver.model.L2DropData;
import com.l2jserver.gameserver.model.L2MinionData;
import com.l2jserver.gameserver.model.L2NpcAIData;
import com.l2jserver.gameserver.model.StatsSet;
import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
import com.l2jserver.gameserver.model.base.ClassId;
import com.l2jserver.gameserver.model.items.type.L2WeaponType;
import com.l2jserver.gameserver.model.skills.L2Skill;
import com.l2jserver.gameserver.model.stats.BaseStats;
public class NpcTable extends DocumentParser
{
private static final Logger _log = Logger.getLogger(NpcTable.class.getName());
private static final Map _npcs = new HashMap<>();
// SQL Queries
private static final String SELECT_NPC_ALL = "SELECT * FROM npc ORDER BY id";
private static final String SELECT_NPC_BY_ID = "SELECT * FROM npc WHERE id = ?";
private static final String SELECT_SKILLS_ALL = "SELECT * FROM npcskills ORDER BY npcid";
private static final String SELECT_SKILLS_BY_ID = "SELECT * FROM npcskills WHERE npcid = ?";
private static final String SELECT_DROPLIST_ALL = "SELECT * FROM droplist ORDER BY mobId, category, chance";
private static final String SELECT_DROPLIST_BY_ID = "SELECT * FROM droplist WHERE mobId = ? ORDER BY mobId, category, chance";
private static final String SELECT_NPC_AI_ALL = "SELECT * FROM npcaidata ORDER BY npcId";
private static final String SELECT_NPC_AI_BY_ID = "SELECT * FROM npcaidata WHERE npcId = ?";
private static final String SELECT_NPC_ELEMENTALS_ALL = "SELECT * FROM npc_elementals ORDER BY npc_id";
private static final String SELECT_NPC_ELEMENTALS_BY_ID = "SELECT * FROM npc_elementals WHERE npc_id = ?";
private static final String SELECT_MINION_ALL = "SELECT * FROM minions ORDER BY boss_id";
private static final String SELECT_MINION_BY_ID = "SELECT * FROM minions WHERE boss_id = ?";
// Custom SQL queries
private static final String CUSTOM_SELECT_NPC_ALL = "SELECT * FROM custom_npc ORDER BY id";
private static final String CUSTOM_SELECT_NPC_BY_ID = "SELECT * FROM custom_npc WHERE id = ?";
private static final String CUSTOM_SELECT_SKILLS_ALL = "SELECT * FROM custom_npcskills ORDER BY npcid";
private static final String CUSTOM_SELECT_SKILLS_BY_ID = "SELECT * FROM custom_npcskills WHERE npcid = ?";
private static final String CUSTOM_SELECT_DROPLIST_ALL = "SELECT * FROM custom_droplist ORDER BY mobId, category, chance";
private static final String CUSTOM_SELECT_DROPLIST_BY_ID = "SELECT * FROM custom_droplist WHERE mobId = ? ORDER BY mobId, category, chance";
private static final String CUSTOM_SELECT_NPC_AI_ALL = "SELECT * FROM custom_npcaidata ORDER BY npcId";
private static final String CUSTOM_SELECT_NPC_AI_BY_ID = "SELECT * FROM custom_npcaidata WHERE npcId = ?";
private static final String CUSTOM_SELECT_NPC_ELEMENTALS_ALL = "SELECT * FROM custom_npc_elementals ORDER BY npc_id";
private static final String CUSTOM_SELECT_NPC_ELEMENTALS_BY_ID = "SELECT * FROM custom_npc_elementals WHERE npc_id = ?";
/**
* Instantiates a new npc table.
*/
protected NpcTable()
{
_npcs.clear();
restoreNpcData();
load();
}
@Override
public synchronized void load()
{
parseDirectory("data/stats/npcs");
}
@Override
protected void parseDocument()
{
NamedNodeMap attrs;
StatsSet set;
for (Node n = getCurrentDocument().getFirstChild(); n != null; n = n.getNextSibling())
{
if ("list".equals(n.getNodeName()))
{
for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
{
if ("npc".equals(d.getNodeName()))
{
attrs = d.getAttributes();
int id = parseInt(attrs, "id");
if (_npcs.containsKey(id))
{
L2NpcTemplate template = _npcs.get(id);
set = new StatsSet();
for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
{
if ((c.getNodeName() == null) || c.getNodeName().startsWith("#"))
{
continue;
}
attrs = c.getAttributes();
switch (c.getNodeName())
{
case "base_attack":
{
final String type = parseString(attrs, "type");
final int range = parseInt(attrs, "range");
final L2WeaponType weaponType = L2WeaponType.findByName(type);
template.setBaseAttackType(weaponType);
template.setBaseAttackRange(range);
break;
}
case "base_attribute":
{
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
attrs = b.getAttributes();
if ("attack".equals(b.getNodeName()))
{
template.setBaseFire(parseInt(attrs, "fire"));
template.setBaseWater(parseInt(attrs, "water"));
template.setBaseEarth(parseInt(attrs, "earth"));
template.setBaseWind(parseInt(attrs, "wind"));
template.setBaseHoly(parseInt(attrs, "holy"));
template.setBaseDark(parseInt(attrs, "dark"));
}
else if ("defend".equals(b.getNodeName()))
{
template.setBaseFireRes(parseInt(attrs, "fire"));
template.setBaseWaterRes(parseInt(attrs, "water"));
template.setBaseEarthRes(parseInt(attrs, "earth"));
template.setBaseWindRes(parseInt(attrs, "wind"));
template.setBaseHolyRes(parseInt(attrs, "holy"));
template.setBaseDarkRes(parseInt(attrs, "dark"));
template.setBaseElementRes(parseInt(attrs, "unknown"));
}
}
break;
}
case "npc_ai":
{
for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
{
attrs = b.getAttributes();
if ("ai_param".equals(b.getNodeName()))
{
set.set(parseString(attrs, "name"), parseString(attrs, "val"));
}
}
template.setParameters(set);
break;
}
}
}
}
}
}
}
}
}
/**
* Restore npc data.
*/
private void restoreNpcData()
{
loadNpcs(0);
loadNpcsSkills(0);
loadNpcsDrop(0);
loadNpcsSkillLearn(0);
loadMinions(0);
loadNpcsAI(0);
loadNpcsElement(0);
}
/**
* Fill npc table.
* @param NpcData the npc data
* @throws Exception the exception
*/
private void fillNpcTable(ResultSet NpcData) throws Exception
{
StatsSet npcDat = new StatsSet();
int id = NpcData.getInt("id");
int idTemp = NpcData.getInt("idTemplate");
assert idTemp < 1000000;
npcDat.set("npcId", id);
npcDat.set("idTemplate", idTemp);
int level = NpcData.getInt("level");
npcDat.set("level", level);
npcDat.set("client_class", NpcData.getString("class"));
npcDat.set("baseShldDef", 0);
npcDat.set("baseShldRate", 0);
npcDat.set("baseCritRate", NpcData.getInt("critical"));
npcDat.set("name", NpcData.getString("name"));
npcDat.set("serverSideName", NpcData.getBoolean("serverSideName"));
npcDat.set("title", NpcData.getString("title"));
npcDat.set("serverSideTitle", NpcData.getBoolean("serverSideTitle"));
npcDat.set("collision_radius", NpcData.getDouble("collision_radius"));
npcDat.set("collision_height", NpcData.getDouble("collision_height"));
npcDat.set("sex", NpcData.getString("sex"));
npcDat.set("type", NpcData.getString("type"));
npcDat.set("baseAtkRange", NpcData.getInt("attackrange"));
npcDat.set("rewardExp", NpcData.getInt("exp"));
npcDat.set("rewardSp", NpcData.getInt("sp"));
npcDat.set("basePAtkSpd", NpcData.getInt("atkspd"));
npcDat.set("baseMAtkSpd", NpcData.getInt("matkspd"));
npcDat.set("rhand", NpcData.getInt("rhand"));
npcDat.set("lhand", NpcData.getInt("lhand"));
npcDat.set("enchant", NpcData.getInt("enchant"));
npcDat.set("baseWalkSpd", NpcData.getInt("walkspd"));
npcDat.set("baseRunSpd", NpcData.getInt("runspd"));
// constants, until we have stats in DB
npcDat.safeSet("baseSTR", NpcData.getInt("str"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
npcDat.safeSet("baseCON", NpcData.getInt("con"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
npcDat.safeSet("baseDEX", NpcData.getInt("dex"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
npcDat.safeSet("baseINT", NpcData.getInt("int"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
npcDat.safeSet("baseWIT", NpcData.getInt("wit"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
npcDat.safeSet("baseMEN", NpcData.getInt("men"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
npcDat.set("baseHpMax", NpcData.getDouble("hp"));
npcDat.set("baseCpMax", 0);
npcDat.set("baseMpMax", NpcData.getDouble("mp"));
npcDat.set("baseHpReg", NpcData.getFloat("hpreg") > 0 ? NpcData.getFloat("hpreg") : 1.5 + ((level - 1) / 10.0));
npcDat.set("baseMpReg", NpcData.getFloat("mpreg") > 0 ? NpcData.getFloat("mpreg") : 0.9 + (0.3 * ((level - 1) / 10.0)));
npcDat.set("basePAtk", NpcData.getInt("patk"));
npcDat.set("basePDef", NpcData.getInt("pdef"));
npcDat.set("baseMAtk", NpcData.getInt("matk"));
npcDat.set("baseMDef", NpcData.getInt("mdef"));
npcDat.set("dropHerbGroup", NpcData.getInt("dropHerbGroup"));
// Default element resists
npcDat.set("baseFireRes", 20);
npcDat.set("baseWindRes", 20);
npcDat.set("baseWaterRes", 20);
npcDat.set("baseEarthRes", 20);
npcDat.set("baseHolyRes", 20);
npcDat.set("baseDarkRes", 20);
final L2NpcTemplate template = getTemplate(id);
if (template == null)
{
_npcs.put(id, new L2NpcTemplate(npcDat));
}
else
{
template.set(npcDat);
}
}
/**
* Reload npc.
* @param id of the NPC to reload.
* @param base reloads base npc data.
* @param ai reloads AI npc data.
* @param element reloads elemental npc data
* @param skills reloads skills npc data.
* @param drops reloads drop npc data
* @param minions reloads minions npc data.
*/
public void reloadNpc(int id, boolean base, boolean ai, boolean element, boolean skills, boolean drops, boolean minions)
{
try
{
if (base)
{
loadNpcs(id);
}
if (ai)
{
loadNpcsAI(id);
}
if (element)
{
loadNpcsElement(id);
}
if (skills)
{
loadNpcsSkills(id);
loadNpcsSkillLearn(id);
}
if (drops)
{
loadNpcsDrop(id);
}
if (minions)
{
loadMinions(id);
}
}
catch (Exception e)
{
_log.log(Level.WARNING, getClass().getSimpleName() + ": Could not reload data for NPC " + id + ": " + e.getMessage(), e);
}
}
/**
* Just wrapper.
*/
public void reloadAllNpc()
{
restoreNpcData();
}
/**
* Save npc into the database.
* @param npc the npc
*/
public void saveNpc(StatsSet npc)
{
final int npcId = npc.getInteger("npcId");
final StringBuilder npcAttributes = new StringBuilder();
final ArrayList