NpcTable.java 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162116311641165116611671168116911701171117211731174
  1. /*
  2. * Copyright (C) 2004-2013 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J Server is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package com.l2jserver.gameserver.datatables;
  20. import java.sql.Connection;
  21. import java.sql.PreparedStatement;
  22. import java.sql.ResultSet;
  23. import java.sql.SQLException;
  24. import java.util.ArrayList;
  25. import java.util.Collection;
  26. import java.util.HashMap;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Map.Entry;
  30. import java.util.logging.Level;
  31. import java.util.logging.Logger;
  32. import org.w3c.dom.NamedNodeMap;
  33. import org.w3c.dom.Node;
  34. import com.l2jserver.Config;
  35. import com.l2jserver.L2DatabaseFactory;
  36. import com.l2jserver.gameserver.engines.DocumentParser;
  37. import com.l2jserver.gameserver.model.Elementals;
  38. import com.l2jserver.gameserver.model.L2DropData;
  39. import com.l2jserver.gameserver.model.L2MinionData;
  40. import com.l2jserver.gameserver.model.L2NpcAIData;
  41. import com.l2jserver.gameserver.model.StatsSet;
  42. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  43. import com.l2jserver.gameserver.model.base.ClassId;
  44. import com.l2jserver.gameserver.model.items.type.L2WeaponType;
  45. import com.l2jserver.gameserver.model.skills.L2Skill;
  46. import com.l2jserver.gameserver.model.stats.BaseStats;
  47. public class NpcTable extends DocumentParser
  48. {
  49. private static final Logger _log = Logger.getLogger(NpcTable.class.getName());
  50. private static final Map<Integer, L2NpcTemplate> _npcs = new HashMap<>();
  51. // SQL Queries
  52. private static final String SELECT_NPC_ALL = "SELECT * FROM npc ORDER BY id";
  53. private static final String SELECT_NPC_BY_ID = "SELECT * FROM npc WHERE id = ?";
  54. private static final String SELECT_SKILLS_ALL = "SELECT * FROM npcskills ORDER BY npcid";
  55. private static final String SELECT_SKILLS_BY_ID = "SELECT * FROM npcskills WHERE npcid = ?";
  56. private static final String SELECT_DROPLIST_ALL = "SELECT * FROM droplist ORDER BY mobId, category, chance";
  57. private static final String SELECT_DROPLIST_BY_ID = "SELECT * FROM droplist WHERE mobId = ? ORDER BY mobId, category, chance";
  58. private static final String SELECT_NPC_AI_ALL = "SELECT * FROM npcaidata ORDER BY npcId";
  59. private static final String SELECT_NPC_AI_BY_ID = "SELECT * FROM npcaidata WHERE npcId = ?";
  60. private static final String SELECT_NPC_ELEMENTALS_ALL = "SELECT * FROM npc_elementals ORDER BY npc_id";
  61. private static final String SELECT_NPC_ELEMENTALS_BY_ID = "SELECT * FROM npc_elementals WHERE npc_id = ?";
  62. private static final String SELECT_MINION_ALL = "SELECT * FROM minions ORDER BY boss_id";
  63. private static final String SELECT_MINION_BY_ID = "SELECT * FROM minions WHERE boss_id = ?";
  64. // Custom SQL queries
  65. private static final String CUSTOM_SELECT_NPC_ALL = "SELECT * FROM custom_npc ORDER BY id";
  66. private static final String CUSTOM_SELECT_NPC_BY_ID = "SELECT * FROM custom_npc WHERE id = ?";
  67. private static final String CUSTOM_SELECT_SKILLS_ALL = "SELECT * FROM custom_npcskills ORDER BY npcid";
  68. private static final String CUSTOM_SELECT_SKILLS_BY_ID = "SELECT * FROM custom_npcskills WHERE npcid = ?";
  69. private static final String CUSTOM_SELECT_DROPLIST_ALL = "SELECT * FROM custom_droplist ORDER BY mobId, category, chance";
  70. private static final String CUSTOM_SELECT_DROPLIST_BY_ID = "SELECT * FROM custom_droplist WHERE mobId = ? ORDER BY mobId, category, chance";
  71. private static final String CUSTOM_SELECT_NPC_AI_ALL = "SELECT * FROM custom_npcaidata ORDER BY npcId";
  72. private static final String CUSTOM_SELECT_NPC_AI_BY_ID = "SELECT * FROM custom_npcaidata WHERE npcId = ?";
  73. private static final String CUSTOM_SELECT_NPC_ELEMENTALS_ALL = "SELECT * FROM custom_npc_elementals ORDER BY npc_id";
  74. private static final String CUSTOM_SELECT_NPC_ELEMENTALS_BY_ID = "SELECT * FROM custom_npc_elementals WHERE npc_id = ?";
  75. /**
  76. * Instantiates a new npc table.
  77. */
  78. protected NpcTable()
  79. {
  80. _npcs.clear();
  81. restoreNpcData();
  82. load();
  83. }
  84. @Override
  85. public synchronized void load()
  86. {
  87. parseDirectory("data/stats/npcs");
  88. }
  89. @Override
  90. protected void parseDocument()
  91. {
  92. NamedNodeMap attrs;
  93. StatsSet set;
  94. for (Node n = getCurrentDocument().getFirstChild(); n != null; n = n.getNextSibling())
  95. {
  96. if ("list".equals(n.getNodeName()))
  97. {
  98. for (Node d = n.getFirstChild(); d != null; d = d.getNextSibling())
  99. {
  100. if ("npc".equals(d.getNodeName()))
  101. {
  102. attrs = d.getAttributes();
  103. int id = parseInt(attrs, "id");
  104. if (_npcs.containsKey(id))
  105. {
  106. L2NpcTemplate template = _npcs.get(id);
  107. set = new StatsSet();
  108. for (Node c = d.getFirstChild(); c != null; c = c.getNextSibling())
  109. {
  110. if ((c.getNodeName() == null) || c.getNodeName().startsWith("#"))
  111. {
  112. continue;
  113. }
  114. attrs = c.getAttributes();
  115. switch (c.getNodeName())
  116. {
  117. case "base_attack":
  118. {
  119. final String type = parseString(attrs, "type");
  120. final int range = parseInt(attrs, "range");
  121. final L2WeaponType weaponType = L2WeaponType.findByName(type);
  122. template.setBaseAttackType(weaponType);
  123. template.setBaseAttackRange(range);
  124. break;
  125. }
  126. case "base_attribute":
  127. {
  128. for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
  129. {
  130. attrs = b.getAttributes();
  131. if ("attack".equals(b.getNodeName()))
  132. {
  133. template.setBaseFire(parseInt(attrs, "fire"));
  134. template.setBaseWater(parseInt(attrs, "water"));
  135. template.setBaseEarth(parseInt(attrs, "earth"));
  136. template.setBaseWind(parseInt(attrs, "wind"));
  137. template.setBaseHoly(parseInt(attrs, "holy"));
  138. template.setBaseDark(parseInt(attrs, "dark"));
  139. }
  140. else if ("defend".equals(b.getNodeName()))
  141. {
  142. template.setBaseFireRes(parseInt(attrs, "fire"));
  143. template.setBaseWaterRes(parseInt(attrs, "water"));
  144. template.setBaseEarthRes(parseInt(attrs, "earth"));
  145. template.setBaseWindRes(parseInt(attrs, "wind"));
  146. template.setBaseHolyRes(parseInt(attrs, "holy"));
  147. template.setBaseDarkRes(parseInt(attrs, "dark"));
  148. template.setBaseElementRes(parseInt(attrs, "unknown"));
  149. }
  150. }
  151. break;
  152. }
  153. case "npc_ai":
  154. {
  155. for (Node b = c.getFirstChild(); b != null; b = b.getNextSibling())
  156. {
  157. attrs = b.getAttributes();
  158. if ("ai_param".equals(b.getNodeName()))
  159. {
  160. set.set(parseString(attrs, "name"), parseString(attrs, "val"));
  161. }
  162. }
  163. template.setParameters(set);
  164. break;
  165. }
  166. }
  167. }
  168. }
  169. }
  170. }
  171. }
  172. }
  173. }
  174. /**
  175. * Restore npc data.
  176. */
  177. private void restoreNpcData()
  178. {
  179. loadNpcs(0);
  180. loadNpcsSkills(0);
  181. loadNpcsDrop(0);
  182. loadNpcsSkillLearn(0);
  183. loadMinions(0);
  184. loadNpcsAI(0);
  185. loadNpcsElement(0);
  186. }
  187. /**
  188. * Fill npc table.
  189. * @param NpcData the npc data
  190. * @throws Exception the exception
  191. */
  192. private void fillNpcTable(ResultSet NpcData) throws Exception
  193. {
  194. StatsSet npcDat = new StatsSet();
  195. int id = NpcData.getInt("id");
  196. int idTemp = NpcData.getInt("idTemplate");
  197. assert idTemp < 1000000;
  198. npcDat.set("npcId", id);
  199. npcDat.set("idTemplate", idTemp);
  200. int level = NpcData.getInt("level");
  201. npcDat.set("level", level);
  202. npcDat.set("client_class", NpcData.getString("class"));
  203. npcDat.set("baseShldDef", 0);
  204. npcDat.set("baseShldRate", 0);
  205. npcDat.set("baseCritRate", NpcData.getInt("critical"));
  206. npcDat.set("name", NpcData.getString("name"));
  207. npcDat.set("serverSideName", NpcData.getBoolean("serverSideName"));
  208. npcDat.set("title", NpcData.getString("title"));
  209. npcDat.set("serverSideTitle", NpcData.getBoolean("serverSideTitle"));
  210. npcDat.set("collision_radius", NpcData.getDouble("collision_radius"));
  211. npcDat.set("collision_height", NpcData.getDouble("collision_height"));
  212. npcDat.set("sex", NpcData.getString("sex"));
  213. npcDat.set("type", NpcData.getString("type"));
  214. npcDat.set("baseAtkRange", NpcData.getInt("attackrange"));
  215. npcDat.set("rewardExp", NpcData.getInt("exp"));
  216. npcDat.set("rewardSp", NpcData.getInt("sp"));
  217. npcDat.set("basePAtkSpd", NpcData.getInt("atkspd"));
  218. npcDat.set("baseMAtkSpd", NpcData.getInt("matkspd"));
  219. npcDat.set("rhand", NpcData.getInt("rhand"));
  220. npcDat.set("lhand", NpcData.getInt("lhand"));
  221. npcDat.set("enchant", NpcData.getInt("enchant"));
  222. npcDat.set("baseWalkSpd", NpcData.getInt("walkspd"));
  223. npcDat.set("baseRunSpd", NpcData.getInt("runspd"));
  224. // constants, until we have stats in DB
  225. npcDat.safeSet("baseSTR", NpcData.getInt("str"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
  226. npcDat.safeSet("baseCON", NpcData.getInt("con"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
  227. npcDat.safeSet("baseDEX", NpcData.getInt("dex"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
  228. npcDat.safeSet("baseINT", NpcData.getInt("int"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
  229. npcDat.safeSet("baseWIT", NpcData.getInt("wit"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
  230. npcDat.safeSet("baseMEN", NpcData.getInt("men"), 0, BaseStats.MAX_STAT_VALUE, "Loading npc template id: " + NpcData.getInt("idTemplate"));
  231. npcDat.set("baseHpMax", NpcData.getDouble("hp"));
  232. npcDat.set("baseCpMax", 0);
  233. npcDat.set("baseMpMax", NpcData.getDouble("mp"));
  234. npcDat.set("baseHpReg", NpcData.getFloat("hpreg") > 0 ? NpcData.getFloat("hpreg") : 1.5 + ((level - 1) / 10.0));
  235. npcDat.set("baseMpReg", NpcData.getFloat("mpreg") > 0 ? NpcData.getFloat("mpreg") : 0.9 + (0.3 * ((level - 1) / 10.0)));
  236. npcDat.set("basePAtk", NpcData.getInt("patk"));
  237. npcDat.set("basePDef", NpcData.getInt("pdef"));
  238. npcDat.set("baseMAtk", NpcData.getInt("matk"));
  239. npcDat.set("baseMDef", NpcData.getInt("mdef"));
  240. npcDat.set("dropHerbGroup", NpcData.getInt("dropHerbGroup"));
  241. // Default element resists
  242. npcDat.set("baseFireRes", 20);
  243. npcDat.set("baseWindRes", 20);
  244. npcDat.set("baseWaterRes", 20);
  245. npcDat.set("baseEarthRes", 20);
  246. npcDat.set("baseHolyRes", 20);
  247. npcDat.set("baseDarkRes", 20);
  248. final L2NpcTemplate template = getTemplate(id);
  249. if (template == null)
  250. {
  251. _npcs.put(id, new L2NpcTemplate(npcDat));
  252. }
  253. else
  254. {
  255. template.set(npcDat);
  256. }
  257. }
  258. /**
  259. * Reload npc.
  260. * @param id of the NPC to reload.
  261. * @param base reloads base npc data.
  262. * @param ai reloads AI npc data.
  263. * @param element reloads elemental npc data
  264. * @param skills reloads skills npc data.
  265. * @param drops reloads drop npc data
  266. * @param minions reloads minions npc data.
  267. */
  268. public void reloadNpc(int id, boolean base, boolean ai, boolean element, boolean skills, boolean drops, boolean minions)
  269. {
  270. try
  271. {
  272. if (base)
  273. {
  274. loadNpcs(id);
  275. }
  276. if (ai)
  277. {
  278. loadNpcsAI(id);
  279. }
  280. if (element)
  281. {
  282. loadNpcsElement(id);
  283. }
  284. if (skills)
  285. {
  286. loadNpcsSkills(id);
  287. loadNpcsSkillLearn(id);
  288. }
  289. if (drops)
  290. {
  291. loadNpcsDrop(id);
  292. }
  293. if (minions)
  294. {
  295. loadMinions(id);
  296. }
  297. }
  298. catch (Exception e)
  299. {
  300. _log.log(Level.WARNING, getClass().getSimpleName() + ": Could not reload data for NPC " + id + ": " + e.getMessage(), e);
  301. }
  302. }
  303. /**
  304. * Just wrapper.
  305. */
  306. public void reloadAllNpc()
  307. {
  308. restoreNpcData();
  309. }
  310. /**
  311. * Save npc into the database.
  312. * @param npc the npc
  313. */
  314. public void saveNpc(StatsSet npc)
  315. {
  316. final int npcId = npc.getInteger("npcId");
  317. final StringBuilder npcAttributes = new StringBuilder();
  318. final ArrayList<Object> npcAttributeValues = new ArrayList<>();
  319. final StringBuilder npcaidataAttributes = new StringBuilder();
  320. final ArrayList<Object> npcaidataAttributeValues = new ArrayList<>();
  321. final StringBuilder npcElementAttributes = new StringBuilder();
  322. final ArrayList<Object> npcElementAttributeValues = new ArrayList<>();
  323. for (Entry<String, Object> entry : npc.getSet().entrySet())
  324. {
  325. switch (entry.getKey())
  326. {
  327. case "npcId":
  328. break;
  329. case "serverSideName":
  330. case "serverSideTitle":
  331. case "sex":
  332. case "enchant":
  333. case "level":
  334. case "str":
  335. case "con":
  336. case "dex":
  337. case "int":
  338. case "wit":
  339. case "men":
  340. case "critical":
  341. case "dropHerbGroup":
  342. case "atkspd":
  343. case "matkspd":
  344. case "attackrange":
  345. case "rhand":
  346. case "lhand":
  347. case "idTemplate":
  348. case "exp":
  349. case "sp":
  350. case "collision_radius":
  351. case "collision_height":
  352. case "walkspd":
  353. case "runspd":
  354. case "patk":
  355. case "pdef":
  356. case "matk":
  357. case "mdef":
  358. case "hp":
  359. case "mp":
  360. case "hpreg":
  361. case "mpreg":
  362. case "type":
  363. case "title":
  364. case "name":
  365. {
  366. appendEntry(npcAttributes, entry.getKey());
  367. npcAttributeValues.add(entry.getValue());
  368. break;
  369. }
  370. case "canMove":
  371. case "targetable":
  372. case "showName":
  373. case "isChaos":
  374. case "dodge":
  375. case "minSkillChance":
  376. case "maxSkillChance":
  377. case "minRangeChance":
  378. case "maxRangeChance":
  379. case "ssChance":
  380. case "spsChance":
  381. case "aggro":
  382. case "clanRange":
  383. case "enemyRange":
  384. case "primarySkillId":
  385. case "minRangeSkill":
  386. case "maxRangeSkill":
  387. case "soulShot":
  388. case "spiritShot":
  389. case "clan":
  390. case "enemyClan":
  391. case "aiType":
  392. {
  393. appendEntry(npcaidataAttributes, entry.getKey());
  394. npcaidataAttributeValues.add(entry.getValue());
  395. break;
  396. }
  397. case "elemAtkType":
  398. case "elemAtkValue":
  399. case "fireDefValue":
  400. case "waterDefValue":
  401. case "windDefValue":
  402. case "earthDefValue":
  403. case "holyDefValue":
  404. case "darkDefValue":
  405. {
  406. appendEntry(npcElementAttributes, entry.getKey());
  407. npcElementAttributeValues.add(entry.getValue());
  408. break;
  409. }
  410. default:
  411. {
  412. _log.warning("Unknown stat " + entry.getKey() + " can't set.");
  413. return;
  414. }
  415. }
  416. }
  417. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  418. {
  419. int updated = 0;
  420. if (Config.CUSTOM_NPC_TABLE)
  421. {
  422. updated += performUpdate(npcAttributes, "custom_npc", "id", npcAttributeValues, npcId, con);
  423. updated += performUpdate(npcaidataAttributes, "custom_npcaidata", "npcId", npcaidataAttributeValues, npcId, con);
  424. updated += performUpdate(npcElementAttributes, "custom_npc_elementals", "npc_id", npcElementAttributeValues, npcId, con);
  425. }
  426. if (updated == 0)
  427. {
  428. updated += performUpdate(npcAttributes, "npc", "id", npcAttributeValues, npcId, con);
  429. updated += performUpdate(npcaidataAttributes, "npcaidata", "npcId", npcaidataAttributeValues, npcId, con);
  430. updated += performUpdate(npcElementAttributes, "npc_elementals", "npc_id", npcElementAttributeValues, npcId, con);
  431. }
  432. if (updated > 0)
  433. {
  434. reloadNpc(npcId, !npcAttributeValues.isEmpty(), !npcaidataAttributeValues.isEmpty(), !npcElementAttributeValues.isEmpty(), false, false, false);
  435. }
  436. }
  437. catch (Exception e)
  438. {
  439. _log.log(Level.WARNING, getClass().getSimpleName() + ": Could not store new NPC data in database: " + e.getMessage(), e);
  440. }
  441. }
  442. /**
  443. * Append entry.
  444. * @param sb the string builder to append the attribute and value.
  445. * @param attribute the attribute to append.
  446. */
  447. private final void appendEntry(StringBuilder sb, String attribute)
  448. {
  449. if (sb.length() > 0)
  450. {
  451. sb.append(", ");
  452. }
  453. sb.append("`");
  454. sb.append(attribute);
  455. sb.append("` = ?");
  456. }
  457. /**
  458. * Perform update.
  459. * @param sb the string builder with the parameters
  460. * @param table the table to update.
  461. * @param key the key of the table.
  462. * @param values the values of keys.
  463. * @param npcId the Npc Id.
  464. * @param con the current database connection.
  465. * @return the count of updated NPCs.
  466. * @throws SQLException the SQL exception.
  467. */
  468. private final int performUpdate(StringBuilder sb, String table, String key, Collection<Object> values, int npcId, Connection con) throws SQLException
  469. {
  470. int updated = 0;
  471. if ((sb != null) && !sb.toString().isEmpty())
  472. {
  473. final StringBuilder sbQuery = new StringBuilder(sb.length() + 28);
  474. sbQuery.append("UPDATE ");
  475. sbQuery.append(table);
  476. sbQuery.append(" SET ");
  477. sbQuery.append(sb.toString());
  478. sbQuery.append(" WHERE ");
  479. sbQuery.append(key);
  480. sbQuery.append(" = ?");
  481. try (PreparedStatement ps = con.prepareStatement(sbQuery.toString()))
  482. {
  483. int i = 1;
  484. for (Object value : values)
  485. {
  486. ps.setObject(i, value);
  487. i++;
  488. }
  489. ps.setInt(i, npcId);
  490. updated = ps.executeUpdate();
  491. }
  492. }
  493. return updated;
  494. }
  495. /**
  496. * Gets the template.
  497. * @param id the template Id to get.
  498. * @return the template for the given id.
  499. */
  500. public L2NpcTemplate getTemplate(int id)
  501. {
  502. return _npcs.get(id);
  503. }
  504. /**
  505. * Gets the template by name.
  506. * @param name of the template to get.
  507. * @return the template for the given name.
  508. */
  509. public L2NpcTemplate getTemplateByName(String name)
  510. {
  511. for (L2NpcTemplate npcTemplate : _npcs.values())
  512. {
  513. if (npcTemplate.getName().equalsIgnoreCase(name))
  514. {
  515. return npcTemplate;
  516. }
  517. }
  518. return null;
  519. }
  520. /**
  521. * Gets the all of level.
  522. * @param lvls of all the templates to get.
  523. * @return the template list for the given level.
  524. */
  525. public List<L2NpcTemplate> getAllOfLevel(int... lvls)
  526. {
  527. final List<L2NpcTemplate> list = new ArrayList<>();
  528. for (int lvl : lvls)
  529. {
  530. for (L2NpcTemplate t : _npcs.values())
  531. {
  532. if (t.getLevel() == lvl)
  533. {
  534. list.add(t);
  535. }
  536. }
  537. }
  538. return list;
  539. }
  540. /**
  541. * Gets the all monsters of level.
  542. * @param lvls of all the monster templates to get.
  543. * @return the template list for the given level.
  544. */
  545. public List<L2NpcTemplate> getAllMonstersOfLevel(int... lvls)
  546. {
  547. final List<L2NpcTemplate> list = new ArrayList<>();
  548. for (int lvl : lvls)
  549. {
  550. for (L2NpcTemplate t : _npcs.values())
  551. {
  552. if ((t.getLevel() == lvl) && t.isType("L2Monster"))
  553. {
  554. list.add(t);
  555. }
  556. }
  557. }
  558. return list;
  559. }
  560. /**
  561. * Gets the all npc starting with.
  562. * @param letters of all the NPC templates which its name start with.
  563. * @return the template list for the given letter.
  564. */
  565. public List<L2NpcTemplate> getAllNpcStartingWith(String... letters)
  566. {
  567. final List<L2NpcTemplate> list = new ArrayList<>();
  568. for (String letter : letters)
  569. {
  570. for (L2NpcTemplate t : _npcs.values())
  571. {
  572. if (t.getName().startsWith(letter) && t.isType("L2Npc"))
  573. {
  574. list.add(t);
  575. }
  576. }
  577. }
  578. return list;
  579. }
  580. /**
  581. * Gets the all npc of class type.
  582. * @param classTypes of all the templates to get.
  583. * @return the template list for the given class type.
  584. */
  585. public List<L2NpcTemplate> getAllNpcOfClassType(String... classTypes)
  586. {
  587. final List<L2NpcTemplate> list = new ArrayList<>();
  588. for (String classType : classTypes)
  589. {
  590. for (L2NpcTemplate t : _npcs.values())
  591. {
  592. if (t.isType(classType))
  593. {
  594. list.add(t);
  595. }
  596. }
  597. }
  598. return list;
  599. }
  600. /**
  601. * Load npcs.
  602. * @param id the id
  603. */
  604. public void loadNpcs(int id)
  605. {
  606. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  607. {
  608. int count = loadNpcs(con, id, false);
  609. int ccount = 0;
  610. if (Config.CUSTOM_NPC_TABLE)
  611. {
  612. ccount = loadNpcs(con, id, true);
  613. }
  614. _log.info(getClass().getSimpleName() + ": Loaded " + count + " (Custom: " + ccount + ") NPC template(s).");
  615. }
  616. catch (Exception e)
  617. {
  618. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC AI Data: " + e.getMessage(), e);
  619. }
  620. }
  621. /**
  622. * Id equals to zero or lesser means all.
  623. * @param con the database connection
  624. * @param id of the NPC to load.
  625. * @param isCustom the is custom
  626. * @return the loaded NPC count
  627. */
  628. public int loadNpcs(Connection con, int id, boolean isCustom)
  629. {
  630. int count = 0;
  631. try
  632. {
  633. final String query = isCustom ? ((id > 0) ? CUSTOM_SELECT_NPC_BY_ID : CUSTOM_SELECT_NPC_ALL) : ((id > 0) ? SELECT_NPC_BY_ID : SELECT_NPC_ALL);
  634. try (PreparedStatement ps = con.prepareStatement(query))
  635. {
  636. if (id > 0)
  637. {
  638. ps.setInt(1, id);
  639. }
  640. try (ResultSet rs = ps.executeQuery())
  641. {
  642. while (rs.next())
  643. {
  644. fillNpcTable(rs);
  645. count++;
  646. }
  647. }
  648. }
  649. }
  650. catch (Exception e)
  651. {
  652. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error creating NPC table.", e);
  653. }
  654. return count;
  655. }
  656. /**
  657. * Id equals to zero or lesser means all.
  658. * @param id of the NPC to load it's skills.
  659. */
  660. public void loadNpcsSkills(int id)
  661. {
  662. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  663. {
  664. int count = loadNpcsSkills(con, id, false);
  665. int ccount = 0;
  666. if (Config.CUSTOM_NPC_SKILLS_TABLE)
  667. {
  668. ccount = loadNpcsSkills(con, id, true);
  669. }
  670. _log.info(getClass().getSimpleName() + ": Loaded " + count + " (Custom: " + ccount + ") NPC skills.");
  671. }
  672. catch (Exception e)
  673. {
  674. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC AI Data: " + e.getMessage(), e);
  675. }
  676. }
  677. /**
  678. * Load npcs skills.
  679. * @param con the database connection
  680. * @param id the NPC Id
  681. * @param isCustom the is custom
  682. * @return the loaded NPC count
  683. */
  684. private int loadNpcsSkills(Connection con, int id, boolean isCustom)
  685. {
  686. int count = 0;
  687. final String query = isCustom ? ((id > 0) ? CUSTOM_SELECT_SKILLS_BY_ID : CUSTOM_SELECT_SKILLS_ALL) : ((id > 0) ? SELECT_SKILLS_BY_ID : SELECT_SKILLS_ALL);
  688. try (PreparedStatement ps = con.prepareStatement(query))
  689. {
  690. if (id > 0)
  691. {
  692. ps.setInt(1, id);
  693. if (!isCustom)
  694. {
  695. final L2NpcTemplate template = getTemplate(id);
  696. if (template != null)
  697. {
  698. template.resetSkills();
  699. }
  700. }
  701. }
  702. else if (!isCustom)
  703. {
  704. // Reset all template's skills.
  705. for (L2NpcTemplate template : _npcs.values())
  706. {
  707. template.resetSkills();
  708. }
  709. }
  710. try (ResultSet rs = ps.executeQuery())
  711. {
  712. L2NpcTemplate npcDat = null;
  713. L2Skill npcSkill = null;
  714. while (rs.next())
  715. {
  716. int mobId = rs.getInt("npcid");
  717. npcDat = _npcs.get(mobId);
  718. if (npcDat == null)
  719. {
  720. _log.warning(getClass().getSimpleName() + ": Skill data for undefined NPC. npcId: " + mobId);
  721. continue;
  722. }
  723. int skillId = rs.getInt("skillid");
  724. int level = rs.getInt("level");
  725. if (skillId == L2Skill.SKILL_NPC_RACE)
  726. {
  727. npcDat.setRace(level);
  728. }
  729. npcSkill = SkillTable.getInstance().getInfo(skillId, level);
  730. if (npcSkill == null)
  731. {
  732. continue;
  733. }
  734. count++;
  735. npcDat.addSkill(npcSkill);
  736. }
  737. }
  738. }
  739. catch (Exception e)
  740. {
  741. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC skills table.", e);
  742. }
  743. return count;
  744. }
  745. /**
  746. * Id equals to zero or lesser means all.
  747. * @param id of the NPC to load it's drops.
  748. */
  749. public void loadNpcsDrop(int id)
  750. {
  751. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  752. {
  753. int count = loadNpcsDrop(con, id, false);
  754. int ccount = 0;
  755. if (Config.CUSTOM_DROPLIST_TABLE)
  756. {
  757. ccount = loadNpcsDrop(con, id, true);
  758. }
  759. _log.info(getClass().getSimpleName() + ": Loaded " + count + " (Custom: " + ccount + ") drops.");
  760. }
  761. catch (Exception e)
  762. {
  763. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC AI Data: " + e.getMessage(), e);
  764. }
  765. }
  766. /**
  767. * Id equals to zero or lesser means all.
  768. * @param id of the NPC to load it's skill learn list.
  769. */
  770. public void loadNpcsSkillLearn(int id)
  771. {
  772. if (id > 0)
  773. {
  774. final List<ClassId> teachInfo = SkillLearnData.getInstance().getSkillLearnData(id);
  775. final L2NpcTemplate template = _npcs.get(id);
  776. if ((teachInfo != null) && (template != null))
  777. {
  778. template.addTeachInfo(teachInfo);
  779. }
  780. }
  781. else
  782. {
  783. for (L2NpcTemplate template : _npcs.values())
  784. {
  785. final List<ClassId> teachInfo = SkillLearnData.getInstance().getSkillLearnData(template.getNpcId());
  786. if (teachInfo != null)
  787. {
  788. template.addTeachInfo(teachInfo);
  789. }
  790. }
  791. }
  792. }
  793. /**
  794. * Load npcs drop.
  795. * @param con the con
  796. * @param id the id
  797. * @param isCustom the is custom
  798. * @return the int
  799. */
  800. public int loadNpcsDrop(Connection con, int id, boolean isCustom)
  801. {
  802. int count = 0;
  803. final String query = isCustom ? ((id > 0) ? CUSTOM_SELECT_DROPLIST_BY_ID : CUSTOM_SELECT_DROPLIST_ALL) : ((id > 0) ? SELECT_DROPLIST_BY_ID : SELECT_DROPLIST_ALL);
  804. try (PreparedStatement ps = con.prepareStatement(query))
  805. {
  806. if (id > 0)
  807. {
  808. ps.setInt(1, id);
  809. if (!isCustom)
  810. {
  811. final L2NpcTemplate template = getTemplate(id);
  812. if (template != null)
  813. {
  814. template.resetDroplist();
  815. }
  816. }
  817. }
  818. else if (!isCustom)
  819. {
  820. // Reset all template's droplist.
  821. for (L2NpcTemplate template : _npcs.values())
  822. {
  823. template.resetDroplist();
  824. }
  825. }
  826. try (ResultSet rs = ps.executeQuery())
  827. {
  828. L2DropData dropDat = null;
  829. L2NpcTemplate npcDat = null;
  830. while (rs.next())
  831. {
  832. int mobId = rs.getInt("mobId");
  833. npcDat = _npcs.get(mobId);
  834. if (npcDat == null)
  835. {
  836. _log.warning(getClass().getSimpleName() + ": Drop data for undefined NPC. npcId: " + mobId);
  837. continue;
  838. }
  839. dropDat = new L2DropData();
  840. dropDat.setItemId(rs.getInt("itemId"));
  841. dropDat.setMinDrop(rs.getInt("min"));
  842. dropDat.setMaxDrop(rs.getInt("max"));
  843. dropDat.setChance(rs.getInt("chance"));
  844. int category = rs.getInt("category");
  845. if (ItemTable.getInstance().getTemplate(dropDat.getItemId()) == null)
  846. {
  847. _log.warning(getClass().getSimpleName() + ": Drop data for undefined item template! NpcId: " + mobId + " itemId: " + dropDat.getItemId());
  848. continue;
  849. }
  850. count++;
  851. npcDat.addDropData(dropDat, category);
  852. }
  853. }
  854. }
  855. catch (Exception e)
  856. {
  857. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC dropdata. ", e);
  858. }
  859. return count;
  860. }
  861. /**
  862. * Id equals to zero or lesser means all.
  863. * @param id of the NPC to load it's minions.
  864. */
  865. public void loadMinions(int id)
  866. {
  867. final String query = (id > 0) ? SELECT_MINION_BY_ID : SELECT_MINION_ALL;
  868. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  869. PreparedStatement statement = con.prepareStatement(query))
  870. {
  871. if (id > 0)
  872. {
  873. statement.setInt(1, id);
  874. }
  875. int count = 0;
  876. try (ResultSet rset = statement.executeQuery())
  877. {
  878. L2MinionData minionDat = null;
  879. L2NpcTemplate npcDat = null;
  880. int raidId;
  881. while (rset.next())
  882. {
  883. raidId = rset.getInt("boss_id");
  884. npcDat = _npcs.get(raidId);
  885. if (npcDat == null)
  886. {
  887. _log.warning(getClass().getSimpleName() + ": Minion references undefined boss NPC. Boss NpcId: " + raidId);
  888. continue;
  889. }
  890. minionDat = new L2MinionData();
  891. minionDat.setMinionId(rset.getInt("minion_id"));
  892. minionDat.setAmountMin(rset.getInt("amount_min"));
  893. minionDat.setAmountMax(rset.getInt("amount_max"));
  894. npcDat.addMinionData(minionDat);
  895. count++;
  896. }
  897. }
  898. _log.info(getClass().getSimpleName() + ": Loaded " + count + " Minions.");
  899. }
  900. catch (Exception e)
  901. {
  902. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error loading minion data.", e);
  903. }
  904. }
  905. /**
  906. * Id equals to zero or lesser means all.
  907. * @param id of the NPC to load it's AI data.
  908. */
  909. public void loadNpcsAI(int id)
  910. {
  911. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  912. {
  913. int count = loadNpcAi(con, id, false);
  914. int ccount = 0;
  915. if (Config.CUSTOM_NPC_TABLE)
  916. {
  917. ccount = loadNpcAi(con, id, true);
  918. }
  919. _log.info(getClass().getSimpleName() + ": Loaded " + count + " (Custom: " + ccount + ") AI Data.");
  920. }
  921. catch (Exception e)
  922. {
  923. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC AI Data: " + e.getMessage(), e);
  924. }
  925. }
  926. /**
  927. * Method that give the parameters will load one or all NPC AI from normal or custom tables.
  928. * @param con the database connection
  929. * @param id the NPC Id
  930. * @param isCustom if {@code true} the data will be loaded from the custom table
  931. * @return the count of NPC loaded
  932. */
  933. private int loadNpcAi(Connection con, int id, boolean isCustom)
  934. {
  935. int count = 0;
  936. final String query = isCustom ? ((id > 0) ? CUSTOM_SELECT_NPC_AI_BY_ID : CUSTOM_SELECT_NPC_AI_ALL) : ((id > 0) ? SELECT_NPC_AI_BY_ID : SELECT_NPC_AI_ALL);
  937. try (PreparedStatement ps = con.prepareStatement(query))
  938. {
  939. if (id > 0)
  940. {
  941. ps.setInt(1, id);
  942. }
  943. try (ResultSet rs = ps.executeQuery())
  944. {
  945. L2NpcAIData npcAIDat = null;
  946. L2NpcTemplate npcDat = null;
  947. int npcId;
  948. while (rs.next())
  949. {
  950. npcId = rs.getInt("npcId");
  951. npcDat = _npcs.get(npcId);
  952. if (npcDat == null)
  953. {
  954. _log.severe(getClass().getSimpleName() + ": AI Data Error with id : " + npcId);
  955. continue;
  956. }
  957. npcAIDat = new L2NpcAIData();
  958. npcAIDat.setPrimarySkillId(rs.getInt("primarySkillId"));
  959. npcAIDat.setMinSkillChance(rs.getInt("minSkillChance"));
  960. npcAIDat.setMaxSkillChance(rs.getInt("maxSkillChance"));
  961. npcAIDat.setAggro(rs.getInt("aggro"));
  962. npcAIDat.setCanMove(rs.getInt("canMove"));
  963. npcAIDat.setShowName(rs.getInt("showName") == 1);
  964. npcAIDat.setTargetable(rs.getInt("targetable") == 1);
  965. npcAIDat.setSoulShot(rs.getInt("soulshot"));
  966. npcAIDat.setSpiritShot(rs.getInt("spiritshot"));
  967. npcAIDat.setSoulShotChance(rs.getInt("ssChance"));
  968. npcAIDat.setSpiritShotChance(rs.getInt("spsChance"));
  969. npcAIDat.setIsChaos(rs.getInt("isChaos"));
  970. npcAIDat.setShortRangeSkill(rs.getInt("minRangeSkill"));
  971. npcAIDat.setShortRangeChance(rs.getInt("minRangeChance"));
  972. npcAIDat.setLongRangeSkill(rs.getInt("maxRangeSkill"));
  973. npcAIDat.setLongRangeChance(rs.getInt("maxRangeChance"));
  974. npcAIDat.setClan(rs.getString("clan"));
  975. npcAIDat.setClanRange(rs.getInt("clanRange"));
  976. npcAIDat.setEnemyClan(rs.getString("enemyClan"));
  977. npcAIDat.setEnemyRange(rs.getInt("enemyRange"));
  978. npcAIDat.setDodge(rs.getInt("dodge"));
  979. npcAIDat.setAi(rs.getString("aiType"));
  980. npcDat.setAIData(npcAIDat);
  981. count++;
  982. }
  983. }
  984. }
  985. catch (SQLException e)
  986. {
  987. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC AI Data: " + e.getMessage(), e);
  988. }
  989. return count;
  990. }
  991. /**
  992. * Id equals to zero or lesser means all.
  993. * @param id of the NPC to load it's element data.
  994. */
  995. public void loadNpcsElement(int id)
  996. {
  997. try (Connection con = L2DatabaseFactory.getInstance().getConnection())
  998. {
  999. int count = loadNpcsElement(con, id, false);
  1000. int ccount = 0;
  1001. if (Config.CUSTOM_NPC_TABLE)
  1002. {
  1003. ccount = loadNpcsElement(con, id, true);
  1004. }
  1005. _log.info(getClass().getSimpleName() + ": Loaded " + count + " (Custom: " + ccount + ") Elementals Data.");
  1006. }
  1007. catch (Exception e)
  1008. {
  1009. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC AI Data: " + e.getMessage(), e);
  1010. }
  1011. }
  1012. /**
  1013. * Load npcs element.
  1014. * @param con the con
  1015. * @param id the id
  1016. * @param isCustom the is custom
  1017. * @return the int
  1018. */
  1019. private int loadNpcsElement(Connection con, int id, boolean isCustom)
  1020. {
  1021. int count = 0;
  1022. final String query = isCustom ? ((id > 0) ? CUSTOM_SELECT_NPC_ELEMENTALS_BY_ID : CUSTOM_SELECT_NPC_ELEMENTALS_ALL) : ((id > 0) ? SELECT_NPC_ELEMENTALS_BY_ID : SELECT_NPC_ELEMENTALS_ALL);
  1023. try (PreparedStatement ps = con.prepareStatement(query))
  1024. {
  1025. if (id > 0)
  1026. {
  1027. ps.setInt(1, id);
  1028. }
  1029. try (ResultSet rset = ps.executeQuery())
  1030. {
  1031. L2NpcTemplate npcDat = null;
  1032. int npcId;
  1033. while (rset.next())
  1034. {
  1035. npcId = rset.getInt("npc_id");
  1036. npcDat = _npcs.get(npcId);
  1037. if (npcDat == null)
  1038. {
  1039. _log.severe("NPCElementals: Elementals Error with id : " + npcId);
  1040. continue;
  1041. }
  1042. switch (rset.getByte("elemAtkType"))
  1043. {
  1044. case Elementals.FIRE:
  1045. npcDat.setBaseFire(rset.getInt("elemAtkValue"));
  1046. break;
  1047. case Elementals.WATER:
  1048. npcDat.setBaseWater(rset.getInt("elemAtkValue"));
  1049. break;
  1050. case Elementals.EARTH:
  1051. npcDat.setBaseEarth(rset.getInt("elemAtkValue"));
  1052. break;
  1053. case Elementals.WIND:
  1054. npcDat.setBaseWind(rset.getInt("elemAtkValue"));
  1055. break;
  1056. case Elementals.HOLY:
  1057. npcDat.setBaseHoly(rset.getInt("elemAtkValue"));
  1058. break;
  1059. case Elementals.DARK:
  1060. npcDat.setBaseDark(rset.getInt("elemAtkValue"));
  1061. break;
  1062. default:
  1063. _log.severe("NPCElementals: Elementals Error with id : " + npcId + "; unknown elementType: " + rset.getByte("elemAtkType"));
  1064. continue;
  1065. }
  1066. npcDat.setBaseFireRes(rset.getInt("fireDefValue"));
  1067. npcDat.setBaseWaterRes(rset.getInt("waterDefValue"));
  1068. npcDat.setBaseEarthRes(rset.getInt("earthDefValue"));
  1069. npcDat.setBaseWindRes(rset.getInt("windDefValue"));
  1070. npcDat.setBaseHolyRes(rset.getInt("holyDefValue"));
  1071. npcDat.setBaseDarkRes(rset.getInt("darkDefValue"));
  1072. count++;
  1073. }
  1074. }
  1075. }
  1076. catch (Exception e)
  1077. {
  1078. _log.log(Level.SEVERE, getClass().getSimpleName() + ": Error reading NPC Elementals Data: " + e.getMessage(), e);
  1079. }
  1080. return count;
  1081. }
  1082. /**
  1083. * Gets the single instance of NpcTable.
  1084. * @return single instance of NpcTable
  1085. */
  1086. public static NpcTable getInstance()
  1087. {
  1088. return SingletonHolder._instance;
  1089. }
  1090. private static class SingletonHolder
  1091. {
  1092. protected static final NpcTable _instance = new NpcTable();
  1093. }
  1094. }