/* * 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 silentium.gameserver.instancemanager; import gnu.trove.map.hash.TIntIntHashMap; import gnu.trove.map.hash.TIntObjectHashMap; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.sql.SQLException; import java.util.ArrayList; import java.util.List; import java.util.Map; import javolution.util.FastMap; import org.slf4j.Logger; import org.slf4j.LoggerFactory; import silentium.commons.database.DatabaseFactory; import silentium.gameserver.model.actor.L2Character; import silentium.gameserver.model.actor.instance.L2GrandBossInstance; import silentium.gameserver.model.zone.type.L2BossZone; import silentium.gameserver.tables.NpcTable; import silentium.gameserver.templates.StatsSet; /** * @author DaRkRaGe Revised by Emperorc */ public class GrandBossManager { /* * ========================================================= This class handles all Grand Bosses: <ul> <li>25333-25338 Anakazel</li> * <li>29001 Queen Ant</li> <li>29006 Core</li> <li>29014 Orfen</li> <li>29019 Antharas</li> <li>29020 Baium</li> <li>29022 Zaken</li> * <li>29028 Valakas</li> <li>29045 Frintezza</li> <li>29046-29047 Scarlet van Halisha</li> </ul> It handles the saving of hp, mp, location, * and status of all Grand Bosses. It also manages the zones associated with the Grand Bosses. NOTE: The current version does NOT spawn the * Grand Bosses, it just stores and retrieves the values on reboot/startup, for AI scripts to utilize as needed. */ private static final String DELETE_GRAND_BOSS_LIST = "DELETE FROM grandboss_list"; private static final String INSERT_GRAND_BOSS_LIST = "INSERT INTO grandboss_list (player_id,zone) VALUES (?,?)"; private static final String UPDATE_GRAND_BOSS_DATA = "UPDATE grandboss_data set loc_x = ?, loc_y = ?, loc_z = ?, heading = ?, respawn_time = ?, currentHP = ?, currentMP = ?, status = ? where boss_id = ?"; private static final String UPDATE_GRAND_BOSS_DATA2 = "UPDATE grandboss_data set status = ? where boss_id = ?"; protected static Logger _log = LoggerFactory.getLogger(GrandBossManager.class.getName()); protected static Map<Integer, L2GrandBossInstance> _bosses; protected static TIntObjectHashMap<StatsSet> _storedInfo; private TIntIntHashMap _bossStatus; private List<L2BossZone> _zones; public static GrandBossManager getInstance() { return SingletonHolder._instance; } protected GrandBossManager() { init(); } private void init() { _zones = new ArrayList<>(); _bosses = new FastMap<>(); _storedInfo = new TIntObjectHashMap<>(); _bossStatus = new TIntIntHashMap(); try (Connection con = DatabaseFactory.getConnection()) { PreparedStatement statement = con.prepareStatement("SELECT * from grandboss_data ORDER BY boss_id"); ResultSet rset = statement.executeQuery(); while (rset.next()) { // Read all info from DB, and store it for AI to read and decide what to do // faster than accessing DB in real time StatsSet info = new StatsSet(); int bossId = rset.getInt("boss_id"); info.set("loc_x", rset.getInt("loc_x")); info.set("loc_y", rset.getInt("loc_y")); info.set("loc_z", rset.getInt("loc_z")); info.set("heading", rset.getInt("heading")); info.set("respawn_time", rset.getLong("respawn_time")); double HP = rset.getDouble("currentHP"); // jython doesn't recognize doubles int true_HP = (int) HP; // so use java's ability to type cast info.set("currentHP", true_HP); // to convert double to int double MP = rset.getDouble("currentMP"); int true_MP = (int) MP; info.set("currentMP", true_MP); int status = rset.getInt("status"); _bossStatus.put(bossId, status); _storedInfo.put(bossId, info); info = null; } rset.close(); statement.close(); } catch (SQLException e) { _log.warn("GrandBossManager: Could not load grandboss_data table: " + e.getMessage(), e); } catch (Exception e) { _log.warn("Error while initializing GrandBossManager: " + e.getMessage(), e); } } /* * Zone Functions */ public void initZones() { FastMap<Integer, List<Integer>> zones = new FastMap<>(); if (_zones == null) { _log.warn("GrandBossManager: Could not read Grand Boss zone data"); return; } for (L2BossZone zone : _zones) { if (zone == null) continue; zones.put(zone.getId(), new ArrayList<Integer>()); } try (Connection con = DatabaseFactory.getConnection()) { PreparedStatement statement = con.prepareStatement("SELECT * from grandboss_list ORDER BY player_id"); ResultSet rset = statement.executeQuery(); while (rset.next()) { int id = rset.getInt("player_id"); int zone_id = rset.getInt("zone"); zones.get(zone_id).add(id); } rset.close(); statement.close(); _log.info("GrandBossManager: Initialized " + _zones.size() + " GrandBosses zones."); } catch (SQLException e) { _log.warn("GrandBossManager: Could not load grandboss_list table: " + e.getMessage(), e); } catch (Exception e) { _log.warn("Error while initializing GrandBoss zones: " + e.getMessage(), e); } for (L2BossZone zone : _zones) { if (zone == null) continue; zone.setAllowedPlayers(zones.get(zone.getId())); } zones.clear(); } public void addZone(L2BossZone zone) { if (_zones != null) _zones.add(zone); } public final L2BossZone getZone(L2Character character) { if (_zones != null) { for (L2BossZone temp : _zones) { if (temp.isCharacterInZone(character)) return temp; } } return null; } public final L2BossZone getZone(int x, int y, int z) { if (_zones != null) { for (L2BossZone temp : _zones) { if (temp.isInsideZone(x, y, z)) return temp; } } return null; } /* * The rest */ public int getBossStatus(int bossId) { return _bossStatus.get(bossId); } public void setBossStatus(int bossId, int status) { _bossStatus.put(bossId, status); _log.info(getClass().getSimpleName() + ": Updated " + NpcTable.getInstance().getTemplate(bossId).getName() + " (npcID: " + bossId + ") status to " + status); updateDb(bossId, true); } /* * Adds a L2GrandBossInstance to the list of bosses. */ public void addBoss(L2GrandBossInstance boss) { if (boss != null) { _bosses.put(boss.getNpcId(), boss); } } public L2GrandBossInstance getBoss(int bossId) { return _bosses.get(bossId); } public StatsSet getStatsSet(int bossId) { return _storedInfo.get(bossId); } public void setStatsSet(int bossId, StatsSet info) { _storedInfo.put(bossId, info); updateDb(bossId, false); } private void storeToDb() { try (Connection con = DatabaseFactory.getConnection()) { PreparedStatement deleteStatement = con.prepareStatement(DELETE_GRAND_BOSS_LIST); deleteStatement.executeUpdate(); deleteStatement.close(); PreparedStatement insertStatement = con.prepareStatement(INSERT_GRAND_BOSS_LIST); for (L2BossZone zone : _zones) { if (zone == null) continue; Integer id = zone.getId(); List<Integer> list = zone.getAllowedPlayers(); if (list == null || list.isEmpty()) continue; for (Integer player : list) { insertStatement.setInt(1, player); insertStatement.setInt(2, id); insertStatement.executeUpdate(); insertStatement.clearParameters(); } } insertStatement.close(); PreparedStatement updateStatement1 = con.prepareStatement(UPDATE_GRAND_BOSS_DATA2); PreparedStatement updateStatement2 = con.prepareStatement(UPDATE_GRAND_BOSS_DATA); for (Integer bossId : _storedInfo.keys()) { L2GrandBossInstance boss = _bosses.get(bossId); StatsSet info = _storedInfo.get(bossId); if (boss == null || info == null) { updateStatement1.setInt(1, _bossStatus.get(bossId)); updateStatement1.setInt(2, bossId); updateStatement1.executeUpdate(); updateStatement1.clearParameters(); } else { updateStatement2.setInt(1, boss.getX()); updateStatement2.setInt(2, boss.getY()); updateStatement2.setInt(3, boss.getZ()); updateStatement2.setInt(4, boss.getHeading()); updateStatement2.setLong(5, info.getLong("respawn_time")); double hp = boss.getCurrentHp(); double mp = boss.getCurrentMp(); if (boss.isDead()) { hp = boss.getMaxHp(); mp = boss.getMaxMp(); } updateStatement2.setDouble(6, hp); updateStatement2.setDouble(7, mp); updateStatement2.setInt(8, _bossStatus.get(bossId)); updateStatement2.setInt(9, bossId); updateStatement2.executeUpdate(); updateStatement2.clearParameters(); } } updateStatement1.close(); updateStatement2.close(); } catch (SQLException e) { _log.warn("GrandBossManager: Couldn't store grandbosses to database:" + e.getMessage(), e); } } private void updateDb(int bossId, boolean statusOnly) { try (Connection con = DatabaseFactory.getConnection()) { L2GrandBossInstance boss = _bosses.get(bossId); StatsSet info = _storedInfo.get(bossId); PreparedStatement statement = null; if (statusOnly || boss == null || info == null) { statement = con.prepareStatement(UPDATE_GRAND_BOSS_DATA2); statement.setInt(1, _bossStatus.get(bossId)); statement.setInt(2, bossId); } else { statement = con.prepareStatement(UPDATE_GRAND_BOSS_DATA); statement.setInt(1, boss.getX()); statement.setInt(2, boss.getY()); statement.setInt(3, boss.getZ()); statement.setInt(4, boss.getHeading()); statement.setLong(5, info.getLong("respawn_time")); double hp = boss.getCurrentHp(); double mp = boss.getCurrentMp(); if (boss.isDead()) { hp = boss.getMaxHp(); mp = boss.getMaxMp(); } statement.setDouble(6, hp); statement.setDouble(7, mp); statement.setInt(8, _bossStatus.get(bossId)); statement.setInt(9, bossId); } statement.executeUpdate(); statement.close(); } catch (SQLException e) { _log.warn("GrandBossManager: Couldn't update grandbosses to database:" + e.getMessage(), e); } } /** * Saves all Grand Boss info and then clears all info from memory, including all schedules. */ public void cleanUp() { storeToDb(); _bosses.clear(); _storedInfo.clear(); _bossStatus.clear(); _zones.clear(); } public List<L2BossZone> getZones() { return _zones; } private static class SingletonHolder { protected static final GrandBossManager _instance = new GrandBossManager(); } public int size() { return _storedInfo.size(); } }