/* * This program is free software: you can redistribute it and/or modify it under * the terms of the GNU General Public License as published by the Free Software * Foundation, either version 3 of the License, or (at your option) any later * version. * * This program is distributed in the hope that it will be useful, but WITHOUT * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more * details. * * You should have received a copy of the GNU General Public License along with * this program. If not, see . */ package com.l2jserver.gameserver.model.quest; import java.sql.Connection; import java.sql.PreparedStatement; import java.sql.ResultSet; import java.util.Collection; import java.util.Map; import java.util.concurrent.locks.ReentrantReadWriteLock; import java.util.logging.Level; import java.util.logging.Logger; import javolution.util.FastList; import javolution.util.FastMap; import com.l2jserver.Config; import com.l2jserver.L2DatabaseFactory; import com.l2jserver.gameserver.ThreadPoolManager; import com.l2jserver.gameserver.cache.HtmCache; import com.l2jserver.gameserver.datatables.NpcTable; import com.l2jserver.gameserver.idfactory.IdFactory; import com.l2jserver.gameserver.instancemanager.QuestManager; import com.l2jserver.gameserver.instancemanager.ZoneManager; import com.l2jserver.gameserver.model.L2Object; import com.l2jserver.gameserver.model.L2Party; import com.l2jserver.gameserver.model.L2Skill; import com.l2jserver.gameserver.model.L2Spawn; import com.l2jserver.gameserver.model.Location; import com.l2jserver.gameserver.model.actor.L2Character; import com.l2jserver.gameserver.model.actor.L2Npc; import com.l2jserver.gameserver.model.actor.L2Trap; import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance; import com.l2jserver.gameserver.model.actor.instance.L2PcInstance; import com.l2jserver.gameserver.model.actor.instance.L2TrapInstance; import com.l2jserver.gameserver.model.zone.L2ZoneType; import com.l2jserver.gameserver.network.serverpackets.ActionFailed; import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage; import com.l2jserver.gameserver.network.serverpackets.NpcQuestHtmlMessage; import com.l2jserver.gameserver.scripting.ManagedScript; import com.l2jserver.gameserver.scripting.ScriptManager; import com.l2jserver.gameserver.templates.chars.L2NpcTemplate; import com.l2jserver.gameserver.templates.item.L2Item; import com.l2jserver.gameserver.util.MinionList; import com.l2jserver.util.Rnd; import com.l2jserver.util.Util; /** * @author Luis Arias */ public class Quest extends ManagedScript { protected static final Logger _log = Logger.getLogger(Quest.class.getName()); /** HashMap containing events from String value of the event */ private static Map _allEventsS = new FastMap(); /** HashMap containing lists of timers from the name of the timer */ private final Map> _allEventTimers = new FastMap>().shared(); private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock(); private final int _questId; private final String _name; private final String _descr; private final byte _initialState = State.CREATED; protected boolean _onEnterWorld = false; private boolean _isCustom = false; // NOTE: questItemIds will be overridden by child classes. Ideally, it should be // protected instead of public. However, quest scripts written in Jython will // have trouble with protected, as Jython only knows private and public... // In fact, protected will typically be considered private thus breaking the scripts. // Leave this as public as a workaround. public int[] questItemIds = null; private static final String DEFAULT_NO_QUEST_MSG = "You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements."; private static final String DEFAULT_ALREADY_COMPLETED_MSG = "This quest has already been completed."; /** * Return collection view of the values contains in the allEventS * @return Collection */ public static Collection findAllEvents() { return _allEventsS.values(); } /** * (Constructor)Add values to class variables and put the quest in HashMaps. * @param questId : int pointing out the ID of the quest * @param name : String corresponding to the name of the quest * @param descr : String for the description of the quest */ public Quest(int questId, String name, String descr) { _questId = questId; _name = name; _descr = descr; if (questId != 0) { QuestManager.getInstance().addQuest(this); } else { _allEventsS.put(name, this); } init_LoadGlobalData(); } /** * The function init_LoadGlobalData is, by default, called by the constructor of all quests. * Children of this class can implement this function in order to define what variables * to load and what structures to save them in. By default, nothing is loaded. */ protected void init_LoadGlobalData() { } /** * The function saveGlobalData is, by default, called at shutdown, for all quests, by the QuestManager. * Children of this class can implement this function in order to convert their structures * into tuples and make calls to save them to the database, if needed. * By default, nothing is saved. */ public void saveGlobalData() { } public static enum TrapAction { TRAP_TRIGGERED, TRAP_DETECTED, TRAP_DISARMED } public static enum QuestEventType { ON_FIRST_TALK(false), // control the first dialog shown by NPCs when they are clicked (some quests must override the default npc action) QUEST_START(true), // onTalk action from start npcs ON_TALK(true), // onTalk action from npcs participating in a quest ON_ATTACK(true), // onAttack action triggered when a mob gets attacked by someone ON_KILL(true), // onKill action triggered when a mob gets killed. ON_SPAWN(true), // onSpawn action triggered when an NPC is spawned or respawned. ON_SKILL_SEE(true), // NPC or Mob saw a person casting a skill (regardless what the target is). ON_FACTION_CALL(true), // NPC or Mob saw a person casting a skill (regardless what the target is). ON_AGGRO_RANGE_ENTER(true), // a person came within the Npc/Mob's range ON_SPELL_FINISHED(true), // on spell finished action when npc finish casting skill ON_SKILL_LEARN(false), // control the AcquireSkill dialog from quest script ON_ENTER_ZONE(true), // on zone enter ON_EXIT_ZONE(true), // on zone exit ON_TRAP_ACTION(true), // on zone exit ON_ITEM_USE(true); // control whether this event type is allowed for the same npc template in multiple quests // or if the npc must be registered in at most one quest for the specified event private boolean _allowMultipleRegistration; QuestEventType(boolean allowMultipleRegistration) { _allowMultipleRegistration = allowMultipleRegistration; } public boolean isMultipleRegistrationAllowed() { return _allowMultipleRegistration; } } /** * Return ID of the quest * @return int */ public int getQuestIntId() { return _questId; } /** * Add a new QuestState to the database and return it. * @param player * @return QuestState : QuestState created */ public QuestState newQuestState(L2PcInstance player) { QuestState qs = new QuestState(this, player, getInitialState()); return qs; } /** * Return initial state of the quest * @return State */ public byte getInitialState() { return _initialState; } /** * Return name of the quest * @return String */ public String getName() { return _name; } /** * Return description of the quest * @return String */ public String getDescr() { return _descr; } /** * Add a timer to the quest, if it doesn't exist already * @param name name of the timer (also passed back as "event" in onAdvEvent) * @param time time in ms for when to fire the timer * @param npc npc associated with this timer (can be null) * @param player player associated with this timer (can be null) */ public void startQuestTimer(String name, long time, L2Npc npc, L2PcInstance player) { startQuestTimer(name, time, npc, player, false); } /** * Add a timer to the quest, if it doesn't exist already. If the timer is repeatable, * it will auto-fire automatically, at a fixed rate, until explicitly canceled. * @param name name of the timer (also passed back as "event" in onAdvEvent) * @param time time in ms for when to fire the timer * @param npc npc associated with this timer (can be null) * @param player player associated with this timer (can be null) * @param repeating indicates if the timer is repeatable or one-time. */ public void startQuestTimer(String name, long time, L2Npc npc, L2PcInstance player, boolean repeating) { // Add quest timer if timer doesn't already exist FastList timers = getQuestTimers(name); // no timer exists with the same name, at all if (timers == null) { timers = new FastList(); timers.add(new QuestTimer(this, name, time, npc, player, repeating)); _allEventTimers.put(name, timers); } // a timer with this name exists, but may not be for the same set of npc and player else { // if there exists a timer with this name, allow the timer only if the [npc, player] set is unique // nulls act as wildcards if (getQuestTimer(name, npc, player) == null) { try { _rwLock.writeLock().lock(); timers.add(new QuestTimer(this, name, time, npc, player, repeating)); } finally { _rwLock.writeLock().unlock(); } } } } public QuestTimer getQuestTimer(String name, L2Npc npc, L2PcInstance player) { FastList qt = getQuestTimers(name); if (qt == null || qt.isEmpty()) return null; try { _rwLock.readLock().lock(); for (QuestTimer timer : qt) { if (timer != null) { if (timer.isMatch(this, name, npc, player)) return timer; } } } finally { _rwLock.readLock().unlock(); } return null; } private FastList getQuestTimers(String name) { return _allEventTimers.get(name); } public void cancelQuestTimers(String name) { FastList timers = getQuestTimers(name); if (timers == null) return; try { _rwLock.writeLock().lock(); for (QuestTimer timer : timers) { if (timer != null) { timer.cancel(); } } } finally { _rwLock.writeLock().unlock(); } } public void cancelQuestTimer(String name, L2Npc npc, L2PcInstance player) { QuestTimer timer = getQuestTimer(name, npc, player); if (timer != null) timer.cancel(); } public void removeQuestTimer(QuestTimer timer) { if (timer == null) return; FastList timers = getQuestTimers(timer.getName()); if (timers == null) return; try { _rwLock.writeLock().lock(); timers.remove(timer); } finally { _rwLock.writeLock().unlock(); } } // these are methods to call from java public final boolean notifyAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet, L2Skill skill) { String res = null; try { res = onAttack(npc, attacker, damage, isPet, skill); } catch (Exception e) { return showError(attacker, e); } return showResult(attacker, res); } public final boolean notifyDeath(L2Character killer, L2Character victim, QuestState qs) { String res = null; try { res = onDeath(killer, victim, qs); } catch (Exception e) { return showError(qs.getPlayer(), e); } return showResult(qs.getPlayer(), res); } public final boolean notifyItemUse(L2Item item, L2PcInstance player) { String res = null; try { res = onItemUse(item, player); } catch(Exception e) { return showError(player, e); } return showResult(player, res); } public final boolean notifySpellFinished(L2Npc instance, L2PcInstance player, L2Skill skill) { String res = null; try { res = onSpellFinished(instance, player, skill); } catch (Exception e) { return showError(player, e); } return showResult(player, res); } /** * Notify quest script when something happens with a trap * @param trap the trap instance which triggers the notification * @param trigger the character which makes effect on the trap * @param action 0: trap casting its skill. 1: trigger detects the trap. 2: trigger removes the trap * @return */ public final boolean notifyTrapAction(L2Trap trap, L2Character trigger, TrapAction action) { String res = null; try { res = onTrapAction(trap, trigger, action); } catch (Exception e) { if (trigger.getActingPlayer() != null) return showError(trigger.getActingPlayer(), e); _log.log(Level.WARNING, "Exception on onTrapAction() in notifyTrapAction(): " + e.getMessage(), e); return true; } if (trigger.getActingPlayer() != null) return showResult(trigger.getActingPlayer(), res); return false; } public final boolean notifySpawn(L2Npc npc) { try { onSpawn(npc); } catch (Exception e) { _log.log(Level.WARNING, "Exception on onSpawn() in notifySpawn(): " + e.getMessage(), e); return true; } return false; } public final boolean notifyEvent(String event, L2Npc npc, L2PcInstance player) { String res = null; try { res = onAdvEvent(event, npc, player); } catch (Exception e) { return showError(player, e); } return showResult(player, res); } public final boolean notifyEnterWorld(L2PcInstance player) { String res = null; try { res = onEnterWorld(player); } catch (Exception e) { return showError(player, e); } return showResult(player, res); } public final boolean notifyKill(L2Npc npc, L2PcInstance killer, boolean isPet) { String res = null; try { res = onKill(npc, killer, isPet); } catch (Exception e) { return showError(killer, e); } return showResult(killer, res); } public final boolean notifyTalk(L2Npc npc, QuestState qs) { String res = null; try { res = onTalk(npc, qs.getPlayer()); } catch (Exception e) { return showError(qs.getPlayer(), e); } qs.getPlayer().setLastQuestNpcObject(npc.getObjectId()); return showResult(qs.getPlayer(), res); } // override the default NPC dialogs when a quest defines this for the given NPC public final boolean notifyFirstTalk(L2Npc npc, L2PcInstance player) { String res = null; try { res = onFirstTalk(npc, player); } catch (Exception e) { return showError(player, e); } // if the quest returns text to display, display it. if (res != null && res.length() > 0) return showResult(player, res); // else tell the player that player.sendPacket(ActionFailed.STATIC_PACKET); // note: if the default html for this npc needs to be shown, onFirstTalk should // call npc.showChatWindow(player) and then return null. return true; } public final boolean notifyAcquireSkillList(L2Npc npc, L2PcInstance player) { String res = null; try { res = onAcquireSkillList(npc, player); } catch (Exception e) { return showError(player, e); } return showResult(player, res); } public final boolean notifyAcquireSkillInfo(L2Npc npc, L2PcInstance player, L2Skill skill) { String res = null; try { res = onAcquireSkillInfo(npc, player, skill); } catch (Exception e) { return showError(player, e); } return showResult(player, res); } public final boolean notifyAcquireSkill(L2Npc npc, L2PcInstance player, L2Skill skill) { String res = null; try { res = onAcquireSkill(npc, player, skill); if (res == "true") return true; else if (res == "false") return false; } catch (Exception e) { return showError(player, e); } return showResult(player, res); } public class TmpOnSkillSee implements Runnable { private final L2Npc _npc; private final L2PcInstance _caster; private final L2Skill _skill; private final L2Object[] _targets; private final boolean _isPet; public TmpOnSkillSee(L2Npc npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet) { _npc = npc; _caster = caster; _skill = skill; _targets = targets; _isPet = isPet; } @Override public void run() { String res = null; try { res = onSkillSee(_npc, _caster, _skill, _targets, _isPet); } catch (Exception e) { showError(_caster, e); } showResult(_caster, res); } } public final boolean notifySkillSee(L2Npc npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet) { ThreadPoolManager.getInstance().executeAi(new TmpOnSkillSee(npc, caster, skill, targets, isPet)); return true; } public final boolean notifyFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isPet) { String res = null; try { res = onFactionCall(npc, caller, attacker, isPet); } catch (Exception e) { return showError(attacker, e); } return showResult(attacker, res); } public class TmpOnAggroEnter implements Runnable { private final L2Npc _npc; private final L2PcInstance _pc; private final boolean _isPet; public TmpOnAggroEnter(L2Npc npc, L2PcInstance pc, boolean isPet) { _npc = npc; _pc = pc; _isPet = isPet; } @Override public void run() { String res = null; try { res = onAggroRangeEnter(_npc, _pc, _isPet); } catch (Exception e) { showError(_pc, e); } showResult(_pc, res); } } public final boolean notifyAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isPet) { ThreadPoolManager.getInstance().executeAi(new TmpOnAggroEnter(npc, player, isPet)); return true; } public final boolean notifyEnterZone(L2Character character, L2ZoneType zone) { L2PcInstance player = character.getActingPlayer(); String res = null; try { res = this.onEnterZone(character, zone); } catch (Exception e) { if (player != null) return showError(player, e); } if (player != null) return showResult(player, res); return true; } public final boolean notifyExitZone(L2Character character, L2ZoneType zone) { L2PcInstance player = character.getActingPlayer(); String res = null; try { res = this.onExitZone(character, zone); } catch (Exception e) { if (player != null) return showError(player, e); } if (player != null) return showResult(player, res); return true; } // these are methods that java calls to invoke scripts public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet) { return null; } public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet, L2Skill skill) { return onAttack(npc, attacker, damage, isPet); } public String onDeath(L2Character killer, L2Character victim, QuestState qs) { if (killer instanceof L2Npc) return onAdvEvent("", (L2Npc) killer, qs.getPlayer()); return onAdvEvent("", null, qs.getPlayer()); } public String onAdvEvent(String event, L2Npc npc, L2PcInstance player) { // if not overridden by a subclass, then default to the returned value of the simpler (and older) onEvent override // if the player has a state, use it as parameter in the next call, else return null QuestState qs = player.getQuestState(getName()); if (qs != null) return onEvent(event, qs); return null; } public String onEvent(String event, QuestState qs) { return null; } public String onKill(L2Npc npc, L2PcInstance killer, boolean isPet) { return null; } public String onTalk(L2Npc npc, L2PcInstance talker) { return null; } public String onFirstTalk(L2Npc npc, L2PcInstance player) { return null; } public String onAcquireSkillList(L2Npc npc, L2PcInstance player) { return null; } public String onAcquireSkillInfo(L2Npc npc, L2PcInstance player, L2Skill skill) { return null; } public String onAcquireSkill(L2Npc npc, L2PcInstance player, L2Skill skill) { return null; } public String onItemUse(L2Item item, L2PcInstance player) { return null; } public String onSkillSee(L2Npc npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet) { return null; } public String onSpellFinished(L2Npc npc, L2PcInstance player, L2Skill skill) { return null; } public String onTrapAction(L2Trap trap, L2Character trigger, TrapAction action) { return null; } public String onSpawn(L2Npc npc) { return null; } public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isPet) { return null; } public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isPet) { return null; } public String onEnterWorld(L2PcInstance player) { return null; } public String onEnterZone(L2Character character, L2ZoneType zone) { return null; } public String onExitZone(L2Character character, L2ZoneType zone) { return null; } /** * Show message error to player who has an access level greater than 0 * @param player : L2PcInstance * @param t : Throwable * @return boolean */ public boolean showError(L2PcInstance player, Throwable t) { _log.log(Level.WARNING, this.getScriptFile().getAbsolutePath(), t); if (t.getMessage() == null) t.printStackTrace(); if (player != null && player.getAccessLevel().isGm()) { String res = "Script error" + Util.getStackTrace(t) + ""; return showResult(player, res); } return false; } /** * Show a message to player.

* Concept :
* 3 cases are managed according to the value of the parameter "res" :
*
  • "res" ends with string ".html" : an HTML is opened in order to be shown in a dialog box
  • *
  • "res" starts with "" : the message hold in "res" is shown in a dialog box
  • *
  • otherwise : the message held in "res" is shown in chat box
  • * @param player the player to show the result * @param res the message to show at the player * @return boolean */ public boolean showResult(L2PcInstance player, String res) { if (res == null || res.isEmpty() || player == null) return true; if (res.endsWith(".htm") || res.endsWith(".html")) { showHtmlFile(player, res); } else if (res.startsWith("")) { NpcHtmlMessage npcReply = new NpcHtmlMessage(5); npcReply.setHtml(res); npcReply.replace("%playername%", player.getName()); player.sendPacket(npcReply); player.sendPacket(ActionFailed.STATIC_PACKET); } else { player.sendMessage(res); } return false; } /** * Add quests to the L2PCInstance of the player.

    * Action :
    * Add state of quests, drops and variables for quests in the HashMap _quest of L2PcInstance * @param player : Player who is entering the world */ public final static void playerEnter(L2PcInstance player) { Connection con = null; try { // Get list of quests owned by the player from database con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement invalidQuestData = con.prepareStatement("DELETE FROM character_quests WHERE charId=? and name=?"); PreparedStatement invalidQuestDataVar = con.prepareStatement("delete FROM character_quests WHERE charId=? and name=? and var=?"); PreparedStatement statement = con.prepareStatement("SELECT name,value FROM character_quests WHERE charId=? AND var=?"); statement.setInt(1, player.getObjectId()); statement.setString(2, ""); ResultSet rs = statement.executeQuery(); while (rs.next()) { // Get ID of the quest and ID of its state String questId = rs.getString("name"); String statename = rs.getString("value"); // Search quest associated with the ID Quest q = QuestManager.getInstance().getQuest(questId); if (q == null) { _log.finer("Unknown quest " + questId + " for player " + player.getName()); if (Config.AUTODELETE_INVALID_QUEST_DATA) { invalidQuestData.setInt(1, player.getObjectId()); invalidQuestData.setString(2, questId); invalidQuestData.executeUpdate(); } continue; } // Create a new QuestState for the player that will be added to the player's list of quests new QuestState(q, player, State.getStateId(statename)); } rs.close(); invalidQuestData.close(); statement.close(); // Get list of quests owned by the player from the DB in order to add variables used in the quest. statement = con.prepareStatement("SELECT name,var,value FROM character_quests WHERE charId=? AND var<>?"); statement.setInt(1, player.getObjectId()); statement.setString(2, ""); rs = statement.executeQuery(); while (rs.next()) { String questId = rs.getString("name"); String var = rs.getString("var"); String value = rs.getString("value"); // Get the QuestState saved in the loop before QuestState qs = player.getQuestState(questId); if (qs == null) { _log.finer("Lost variable " + var + " in quest " + questId + " for player " + player.getName()); if (Config.AUTODELETE_INVALID_QUEST_DATA) { invalidQuestDataVar.setInt(1, player.getObjectId()); invalidQuestDataVar.setString(2, questId); invalidQuestDataVar.setString(3, var); invalidQuestDataVar.executeUpdate(); } continue; } // Add parameter to the quest qs.setInternal(var, value); } rs.close(); invalidQuestDataVar.close(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not insert char quest:", e); } finally { L2DatabaseFactory.close(con); } // events for (String name : _allEventsS.keySet()) { player.processQuestEvent(name, "enter"); } } /** * Insert (or Update) in the database variables that need to stay persistant for this quest after a reboot. * This function is for storage of values that do not related to a specific player but are * global for all characters. For example, if we need to disable a quest-gatekeeper until * a certain time (as is done with some grand-boss gatekeepers), we can save that time in the DB. * @param var : String designating the name of the variable for the quest * @param value : String designating the value of the variable for the quest */ public final void saveGlobalQuestVar(String var, String value) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("REPLACE INTO quest_global_data (quest_name,var,value) VALUES (?,?,?)"); statement.setString(1, getName()); statement.setString(2, var); statement.setString(3, value); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not insert global quest variable:", e); } finally { L2DatabaseFactory.close(con); } } /** * Read from the database a previously saved variable for this quest. * Due to performance considerations, this function should best be used only when the quest is first loaded. * Subclasses of this class can define structures into which these loaded values can be saved. * However, on-demand usage of this function throughout the script is not prohibited, only not recommended. * Values read from this function were entered by calls to "saveGlobalQuestVar" * @param var : String designating the name of the variable for the quest * @return String : String representing the loaded value for the passed var, or an empty string if the var was invalid */ public final String loadGlobalQuestVar(String var) { String result = ""; Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("SELECT value FROM quest_global_data WHERE quest_name = ? AND var = ?"); statement.setString(1, getName()); statement.setString(2, var); ResultSet rs = statement.executeQuery(); if (rs.first()) result = rs.getString(1); rs.close(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not load global quest variable:", e); } finally { L2DatabaseFactory.close(con); } return result; } /** * Permanently delete from the database a global quest variable that was previously saved for this quest. * @param var : String designating the name of the variable for the quest */ public final void deleteGlobalQuestVar(String var) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("DELETE FROM quest_global_data WHERE quest_name = ? AND var = ?"); statement.setString(1, getName()); statement.setString(2, var); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not delete global quest variable:", e); } finally { L2DatabaseFactory.close(con); } } /** * Permanently delete from the database all global quest variables that was previously saved for this quest. */ public final void deleteAllGlobalQuestVars() { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("DELETE FROM quest_global_data WHERE quest_name = ?"); statement.setString(1, getName()); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not delete global quest variables:", e); } finally { L2DatabaseFactory.close(con); } } /** * Insert in the database the quest for the player. * @param qs : QuestState pointing out the state of the quest * @param var : String designating the name of the variable for the quest * @param value : String designating the value of the variable for the quest */ public static void createQuestVarInDb(QuestState qs, String var, String value) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("INSERT INTO character_quests (charId,name,var,value) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE value=?"); statement.setInt(1, qs.getPlayer().getObjectId()); statement.setString(2, qs.getQuestName()); statement.setString(3, var); statement.setString(4, value); statement.setString(5, value); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not insert char quest:", e); } finally { L2DatabaseFactory.close(con); } } /** * Update the value of the variable "var" for the quest.

    * Actions :
    * The selection of the right record is made with : *
  • charId = qs.getPlayer().getObjectID()
  • *
  • name = qs.getQuest().getName()
  • *
  • var = var
  • *

    * The modification made is : *
  • value = parameter value
  • * @param qs : Quest State * @param var : String designating the name of the variable for quest * @param value : String designating the value of the variable for quest */ public static void updateQuestVarInDb(QuestState qs, String var, String value) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("UPDATE character_quests SET value=? WHERE charId=? AND name=? AND var = ?"); statement.setString(1, value); statement.setInt(2, qs.getPlayer().getObjectId()); statement.setString(3, qs.getQuestName()); statement.setString(4, var); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not update char quest:", e); } finally { L2DatabaseFactory.close(con); } } /** * Delete a variable of player's quest from the database. * @param qs : object QuestState pointing out the player's quest * @param var : String designating the variable characterizing the quest */ public static void deleteQuestVarInDb(QuestState qs, String var) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("DELETE FROM character_quests WHERE charId=? AND name=? AND var=?"); statement.setInt(1, qs.getPlayer().getObjectId()); statement.setString(2, qs.getQuestName()); statement.setString(3, var); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not delete char quest:", e); } finally { L2DatabaseFactory.close(con); } } /** * Delete the player's quest from database. * @param qs : QuestState pointing out the player's quest */ public static void deleteQuestInDb(QuestState qs) { Connection con = null; try { con = L2DatabaseFactory.getInstance().getConnection(); PreparedStatement statement; statement = con.prepareStatement("DELETE FROM character_quests WHERE charId=? AND name=?"); statement.setInt(1, qs.getPlayer().getObjectId()); statement.setString(2, qs.getQuestName()); statement.executeUpdate(); statement.close(); } catch (Exception e) { _log.log(Level.WARNING, "could not delete char quest:", e); } finally { L2DatabaseFactory.close(con); } } /** * Create a record in database for quest.

    * Actions :
    * Use fucntion createQuestVarInDb() with following parameters :
    *
  • QuestState : parameter sq that puts in fields of database : *
      *
    • charId : ID of the player
    • *
    • name : name of the quest
    • *
    *
  • *
  • var : string "<state>" as the name of the variable for the quest
  • *
  • val : string corresponding at the ID of the state (in fact, initial state)
  • * @param qs : QuestState */ public static void createQuestInDb(QuestState qs) { createQuestVarInDb(qs, "", State.getStateName(qs.getState())); } /** * Update informations regarding quest in database.
    * Actions :
    *
  • Get ID state of the quest recorded in object qs
  • *
  • Test if quest is completed. If true, add a star (*) before the ID state
  • *
  • Save in database the ID state (with or without the star) for the variable called "<state>" of the quest
  • * @param qs : QuestState */ public static void updateQuestInDb(QuestState qs) { String val = State.getStateName(qs.getState()); updateQuestVarInDb(qs, "", val); } /** * Return default html page "You are either not on a quest that involves this NPC.." * @param player * @return */ public static String getNoQuestMsg(L2PcInstance player) { final String result = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/noquest.htm"); if (result != null && result.length() > 0) return result; return DEFAULT_NO_QUEST_MSG; } /** * Return default html page "This quest has already been completed." * @param player * @return */ public static String getAlreadyCompletedMsg(L2PcInstance player) { final String result = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/alreadycompleted.htm"); if (result != null && result.length() > 0) return result; return DEFAULT_ALREADY_COMPLETED_MSG; } /** * Add this quest to the list of quests that the passed mob will respond to for the specified Event type.

    * @param npcId : id of the NPC to register * @param eventType : type of event being registered * @return L2NpcTemplate : Npc Template corresponding to the npcId, or null if the id is invalid */ public L2NpcTemplate addEventId(int npcId, QuestEventType eventType) { try { L2NpcTemplate t = NpcTable.getInstance().getTemplate(npcId); if (t != null) { t.addQuestEvent(eventType, this); } return t; } catch (Exception e) { _log.log(Level.WARNING, "Exception on addEventId(): " + e.getMessage(), e); return null; } } /** * Add the quest to the NPC's startQuest * @param npcIds * @return L2NpcTemplate : Start NPC */ public L2NpcTemplate[] addStartNpc(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.QUEST_START); return value; } public L2NpcTemplate addStartNpc(int npcId) { return addEventId(npcId, QuestEventType.QUEST_START); } /** * Add the quest to the NPC's first-talk (default action dialog) * @param npcIds * @return L2NpcTemplate : Start NPC */ public L2NpcTemplate[] addFirstTalkId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_FIRST_TALK); return value; } public L2NpcTemplate addFirstTalkId(int npcId) { return addEventId(npcId, QuestEventType.ON_FIRST_TALK); } /** * Add the NPC to the AcquireSkill dialog * @param npcIds * @return L2NpcTemplate : NPC */ public L2NpcTemplate[] addAcquireSkillId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_SKILL_LEARN); return value; } public L2NpcTemplate addAcquireSkillId(int npcId) { return addEventId(npcId, QuestEventType.ON_SKILL_LEARN); } /** * Add this quest to the list of quests that the passed mob will respond to for Attack Events.

    * @param npcIds * @return int : attackId */ public L2NpcTemplate[] addAttackId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_ATTACK); return value; } public L2NpcTemplate addAttackId(int npcId) { return addEventId(npcId, QuestEventType.ON_ATTACK); } /** * Add this quest to the list of quests that the passed mob will respond to for Kill Events.

    * @param killIds * @return int : killId */ public L2NpcTemplate[] addKillId(int ...killIds) { L2NpcTemplate[] value = new L2NpcTemplate[killIds.length]; int i = 0; for (int killId : killIds) value[i++] = addEventId(killId, QuestEventType.ON_KILL); return value; } public L2NpcTemplate addKillId(int npcId) { return addEventId(npcId, QuestEventType.ON_KILL); } /** * Add this quest to the list of quests that the passed npc will respond to for Talk Events.

    * @param talkIds : ID of the NPC * @return int : ID of the NPC */ public L2NpcTemplate[] addTalkId(int ...talkIds) { L2NpcTemplate[] value = new L2NpcTemplate[talkIds.length]; int i = 0; for (int talkId : talkIds) value[i++] = addEventId(talkId, QuestEventType.ON_TALK); return value; } public L2NpcTemplate addTalkId(int npcId) { return addEventId(npcId, QuestEventType.ON_TALK); } /** * Add this quest to the list of quests that the passed npc will respond to for Spawn Events.

    * @param npcIds : ID of the NPC * @return int : ID of the NPC */ public L2NpcTemplate[] addSpawnId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_SPAWN); return value; } public L2NpcTemplate addSpawnId(int npcId) { return addEventId(npcId, QuestEventType.ON_SPAWN); } /** * Add this quest to the list of quests that the passed npc will respond to for Skill-See Events.

    * @param npcIds : ID of the NPC * @return int : ID of the NPC */ public L2NpcTemplate[] addSkillSeeId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_SKILL_SEE); return value; } public L2NpcTemplate addSkillSeeId(int npcId) { return addEventId(npcId, QuestEventType.ON_SKILL_SEE); } public L2NpcTemplate[] addSpellFinishedId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_SPELL_FINISHED); return value; } public L2NpcTemplate addSpellFinishedId(int npcId) { return addEventId(npcId, QuestEventType.ON_SPELL_FINISHED); } public L2NpcTemplate[] addTrapActionId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_TRAP_ACTION); return value; } public L2NpcTemplate addTrapActionId(int npcId) { return addEventId(npcId, QuestEventType.ON_TRAP_ACTION); } /** * Add this quest to the list of quests that the passed npc will respond to for Faction Call Events.

    * @param npcIds : ID of the NPC * @return int : ID of the NPC */ public L2NpcTemplate[] addFactionCallId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_FACTION_CALL); return value; } public L2NpcTemplate addFactionCallId(int npcId) { return addEventId(npcId, QuestEventType.ON_FACTION_CALL); } /** * Add this quest to the list of quests that the passed npc will respond to for Character See Events.

    * @param npcIds : ID of the NPC * @return int : ID of the NPC */ public L2NpcTemplate[] addAggroRangeEnterId(int ...npcIds) { L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length]; int i = 0; for (int npcId : npcIds) value[i++] = addEventId(npcId, QuestEventType.ON_AGGRO_RANGE_ENTER); return value; } public L2NpcTemplate addAggroRangeEnterId(int npcId) { return addEventId(npcId, QuestEventType.ON_AGGRO_RANGE_ENTER); } public L2ZoneType[] addEnterZoneId(int ...zoneIds) { L2ZoneType[] value = new L2ZoneType[zoneIds.length]; int i = 0; for (int zoneId : zoneIds) { try { L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId); if (zone != null) { zone.addQuestEvent(Quest.QuestEventType.ON_ENTER_ZONE, this); } value[i++] = zone; } catch (Exception e) { _log.log(Level.WARNING, "Exception on addEnterZoneId(): " + e.getMessage(), e); continue; } } return value; } public L2ZoneType addEnterZoneId(int zoneId) { try { L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId); if (zone != null) { zone.addQuestEvent(Quest.QuestEventType.ON_ENTER_ZONE, this); } return zone; } catch (Exception e) { _log.log(Level.WARNING, "Exception on addEnterZoneId(): " + e.getMessage(), e); return null; } } public L2ZoneType[] addExitZoneId(int ...zoneIds) { L2ZoneType[] value = new L2ZoneType[zoneIds.length]; int i = 0; for (int zoneId : zoneIds) { try { L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId); if (zone != null) { zone.addQuestEvent(Quest.QuestEventType.ON_EXIT_ZONE, this); } value[i++] = zone; } catch (Exception e) { _log.log(Level.WARNING, "Exception on addEnterZoneId(): " + e.getMessage(), e); continue; } } return value; } public L2ZoneType addExitZoneId(int zoneId) { try { L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId); if (zone != null) { zone.addQuestEvent(Quest.QuestEventType.ON_EXIT_ZONE, this); } return zone; } catch (Exception e) { _log.log(Level.WARNING, "Exception on addExitZoneId(): " + e.getMessage(), e); return null; } } // returns a random party member's L2PcInstance for the passed player's party // returns the passed player if he has no party. public L2PcInstance getRandomPartyMember(L2PcInstance player) { // NPE prevention. If the player is null, there is nothing to return if (player == null) return null; if ((player.getParty() == null) || (player.getParty().getPartyMembers().isEmpty())) return player; L2Party party = player.getParty(); return party.getPartyMembers().get(Rnd.get(party.getPartyMembers().size())); } /** * Auxilary function for party quests. * Note: This function is only here because of how commonly it may be used by quest developers. * For any variations on this function, the quest script can always handle things on its own * @param player the instance of a player whose party is to be searched * @param value the value of the "cond" variable that must be matched * @return L2PcInstance: L2PcInstance for a random party member that matches the specified * condition, or null if no match. */ public L2PcInstance getRandomPartyMember(L2PcInstance player, String value) { return getRandomPartyMember(player, "cond", value); } /** * Auxilary function for party quests. * Note: This function is only here because of how commonly it may be used by quest developers. * For any variations on this function, the quest script can always handle things on its own * @param player the instance of a player whose party is to be searched * @param var * @param value a tuple specifying a quest condition that must be satisfied for a party member to be considered. * @return L2PcInstance: L2PcInstance for a random party member that matches the specified * condition, or null if no match. If the var is null, any random party * member is returned (i.e. no condition is applied). * The party member must be within 1500 distance from the target of the reference * player, or if no target exists, 1500 distance from the player itself. */ public L2PcInstance getRandomPartyMember(L2PcInstance player, String var, String value) { // if no valid player instance is passed, there is nothing to check... if (player == null) return null; // for null var condition, return any random party member. if (var == null) return getRandomPartyMember(player); // normal cases...if the player is not in a party, check the player's state QuestState temp = null; L2Party party = player.getParty(); // if this player is not in a party, just check if this player instance matches the conditions itself if ((party == null) || (party.getPartyMembers().isEmpty())) { temp = player.getQuestState(getName()); if ((temp != null) && (temp.get(var) != null) && (temp.get(var)).equalsIgnoreCase(value)) return player; // match return null; // no match } // if the player is in a party, gather a list of all matching party members (possibly // including this player) FastList candidates = new FastList(); // get the target for enforcing distance limitations. L2Object target = player.getTarget(); if (target == null) target = player; for (L2PcInstance partyMember : party.getPartyMembers()) { if (partyMember == null) continue; temp = partyMember.getQuestState(getName()); if ((temp != null) && (temp.get(var) != null) && (temp.get(var)).equalsIgnoreCase(value) && partyMember.isInsideRadius(target, 1500, true, false)) candidates.add(partyMember); } // if there was no match, return null... if (candidates.isEmpty()) return null; // if a match was found from the party, return one of them at random. return candidates.get(Rnd.get(candidates.size())); } /** * Auxilary function for party quests. * Note: This function is only here because of how commonly it may be used by quest developers. * For any variations on this function, the quest script can always handle things on its own * @param player the instance of a player whose party is to be searched * @param state the state in which the party member's queststate must be in order to be considered. * @return L2PcInstance: L2PcInstance for a random party member that matches the specified * condition, or null if no match. If the var is null, any random party * member is returned (i.e. no condition is applied). */ public L2PcInstance getRandomPartyMemberState(L2PcInstance player, byte state) { // if no valid player instance is passed, there is nothing to check... if (player == null) return null; // normal cases...if the player is not in a partym check the player's state QuestState temp = null; L2Party party = player.getParty(); // if this player is not in a party, just check if this player instance matches the conditions itself if ((party == null) || (party.getPartyMembers().isEmpty())) { temp = player.getQuestState(getName()); if ((temp != null) && (temp.getState() == state)) return player; // match return null; // no match } // if the player is in a party, gather a list of all matching party members (possibly // including this player) FastList candidates = new FastList(); // get the target for enforcing distance limitations. L2Object target = player.getTarget(); if (target == null) target = player; for (L2PcInstance partyMember : party.getPartyMembers()) { if (partyMember == null) continue; temp = partyMember.getQuestState(getName()); if ((temp != null) && (temp.getState() == state) && partyMember.isInsideRadius(target, 1500, true, false)) candidates.add(partyMember); } // if there was no match, return null... if (candidates.isEmpty()) return null; // if a match was found from the party, return one of them at random. return candidates.get(Rnd.get(candidates.size())); } /** * Show HTML file to client * @param player * @param fileName * @return String : message sent to client */ public String showHtmlFile(L2PcInstance player, String fileName) { boolean questwindow = true; if (fileName.endsWith(".html")) questwindow = false; int questId = getQuestIntId(); //Create handler to file linked to the quest String content = getHtm(player.getHtmlPrefix(), fileName); if (player.getTarget() != null) content = content.replaceAll("%objectId%", String.valueOf(player.getTarget().getObjectId())); //Send message to client if message not empty if (content != null) { if (questwindow && questId > 0 && questId < 20000 && questId != 999) { NpcQuestHtmlMessage npcReply = new NpcQuestHtmlMessage(5,questId); npcReply.setHtml(content); npcReply.replace("%playername%", player.getName()); player.sendPacket(npcReply); } else { NpcHtmlMessage npcReply = new NpcHtmlMessage(5); npcReply.setHtml(content); npcReply.replace("%playername%", player.getName()); player.sendPacket(npcReply); } player.sendPacket(ActionFailed.STATIC_PACKET); } return content; } /** * Return HTML file contents * @param prefix player's language prefix. * @param fileName the html file to be get. * @return */ public String getHtm(String prefix, String fileName) { String content = HtmCache.getInstance().getHtm(prefix, "data/scripts/" + getDescr().toLowerCase() + "/" + getName() + "/" + fileName); if (content == null) { content = HtmCache.getInstance().getHtm(prefix, "data/scripts/quests/Q" + getName() + "/" + fileName); if (content == null) content = HtmCache.getInstance().getHtmForce(prefix, "data/scripts/quests/" + getName() + "/" + fileName); } return content; } // Method - Public /** * Add a temporary (quest) spawn * @param npcId * @param cha * @return instance of newly spawned npc */ public L2Npc addSpawn(int npcId, L2Character cha) { return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), false, 0, false); } /** * Add a temporary (quest) spawn * @param npcId * @param cha * @param isSummonSpawn * @return instance of newly spawned npc with summon animation */ public L2Npc addSpawn(int npcId, L2Character cha, boolean isSummonSpawn) { return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), false, 0, isSummonSpawn); } public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffSet, long despawnDelay) { return addSpawn(npcId, x, y, z, heading, randomOffSet, despawnDelay, false); } public L2Npc addSpawn(int npcId, Location loc, boolean randomOffSet, long despawnDelay) { return addSpawn(npcId, loc.getX(), loc.getY(), loc.getZ(), loc.getHeading(), randomOffSet, despawnDelay, false); } public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, long despawnDelay, boolean isSummonSpawn) { return addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, isSummonSpawn, 0); } public L2Npc addSpawn(int npcId, Location loc, boolean randomOffset, long despawnDelay, boolean isSummonSpawn) { return addSpawn(npcId, loc.getX(), loc.getY(), loc.getZ(), loc.getHeading(), randomOffset, despawnDelay, isSummonSpawn, 0); } public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, long despawnDelay, boolean isSummonSpawn, int instanceId) { L2Npc result = null; try { L2NpcTemplate template = NpcTable.getInstance().getTemplate(npcId); if (template != null) { // Sometimes, even if the quest script specifies some xyz (for example npc.getX() etc) by the time the code // reaches here, xyz have become 0! Also, a questdev might have purposely set xy to 0,0...however, // the spawn code is coded such that if x=y=0, it looks into location for the spawn loc! This will NOT work // with quest spawns! For both of the above cases, we need a fail-safe spawn. For this, we use the // default spawn location, which is at the player's loc. if ((x == 0) && (y == 0)) { _log.log(Level.SEVERE, "Failed to adjust bad locks for quest spawn! Spawn aborted!"); return null; } if (randomOffset) { int offset; offset = Rnd.get(2); // Get the direction of the offset if (offset == 0) { offset = -1; } // make offset negative offset *= Rnd.get(50, 100); x += offset; offset = Rnd.get(2); // Get the direction of the offset if (offset == 0) { offset = -1; } // make offset negative offset *= Rnd.get(50, 100); y += offset; } L2Spawn spawn = new L2Spawn(template); spawn.setInstanceId(instanceId); spawn.setHeading(heading); spawn.setLocx(x); spawn.setLocy(y); spawn.setLocz(z + 20); spawn.stopRespawn(); result = spawn.spawnOne(isSummonSpawn); if (despawnDelay > 0) result.scheduleDespawn(despawnDelay); return result; } } catch (Exception e1) { _log.warning("Could not spawn Npc " + npcId); } return null; } public L2Trap addTrap(int trapId, int x, int y, int z, int heading, L2Skill skill, int instanceId) { L2NpcTemplate TrapTemplate = NpcTable.getInstance().getTemplate(trapId); L2Trap trap = new L2TrapInstance(IdFactory.getInstance().getNextId(), TrapTemplate, instanceId, -1, skill); trap.setCurrentHp(trap.getMaxHp()); trap.setCurrentMp(trap.getMaxMp()); trap.setIsInvul(true); trap.setHeading(heading); //L2World.getInstance().storeObject(trap); trap.spawnMe(x, y, z); return trap; } public L2Npc addMinion(L2MonsterInstance master, int minionId) { return MinionList.spawnMinion(master, minionId); } public int[] getRegisteredItemIds() { return questItemIds; } @Override public String getScriptName() { return this.getName(); } @Override public void setActive(boolean status) { // TODO implement me } @Override public boolean reload() { unload(); return super.reload(); } @Override public boolean unload() { return unload(true); } public boolean unload(boolean removeFromList) { this.saveGlobalData(); // cancel all pending timers before reloading. // if timers ought to be restarted, the quest can take care of it // with its code (example: save global data indicating what timer must // be restarted). for (FastList timers : _allEventTimers.values()) for (QuestTimer timer : timers) timer.cancel(); _allEventTimers.clear(); if (removeFromList) return QuestManager.getInstance().removeQuest(this); return true; } @Override public ScriptManager getScriptManager() { return QuestManager.getInstance(); } public void setOnEnterWorld(boolean val) { _onEnterWorld = val; } public boolean getOnEnterWorld() { return _onEnterWorld; } /** * @param val if true the quest script will be set as custom quest. */ public void setIsCustom(boolean val) { _isCustom = val; } /** * @return true if the quest script is a custom quest. */ public boolean isCustomQuest() { return _isCustom; } }