/*
* 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.model.quest;
import java.sql.Connection;
import java.sql.PreparedStatement;
import java.sql.ResultSet;
import java.util.Map;
import javolution.util.FastMap;
import org.slf4j.Logger;
import org.slf4j.LoggerFactory;
import silentium.commons.database.DatabaseFactory;
import silentium.commons.utils.Rnd;
import silentium.gameserver.GameTimeController;
import silentium.gameserver.configs.MainConfig;
import silentium.gameserver.data.html.HtmCache;
import silentium.gameserver.data.html.StaticHtmPath;
import silentium.gameserver.instancemanager.QuestManager;
import silentium.gameserver.model.L2DropData;
import silentium.gameserver.model.L2ItemInstance;
import silentium.gameserver.model.actor.L2Character;
import silentium.gameserver.model.actor.L2Npc;
import silentium.gameserver.model.actor.instance.L2MonsterInstance;
import silentium.gameserver.model.actor.instance.L2PcInstance;
import silentium.gameserver.network.SystemMessageId;
import silentium.gameserver.network.serverpackets.ExShowQuestMark;
import silentium.gameserver.network.serverpackets.InventoryUpdate;
import silentium.gameserver.network.serverpackets.PlaySound;
import silentium.gameserver.network.serverpackets.QuestList;
import silentium.gameserver.network.serverpackets.StatusUpdate;
import silentium.gameserver.network.serverpackets.SystemMessage;
import silentium.gameserver.network.serverpackets.TutorialCloseHtml;
import silentium.gameserver.network.serverpackets.TutorialEnableClientEvent;
import silentium.gameserver.network.serverpackets.TutorialShowHtml;
import silentium.gameserver.network.serverpackets.TutorialShowQuestionMark;
import silentium.gameserver.skills.Stats;
import silentium.gameserver.tables.ItemTable;
/**
* @author Luis Arias
*/
public final class QuestState
{
protected static final Logger _log = LoggerFactory.getLogger(Quest.class.getName());
// Static sounds
public static final String SOUND_ACCEPT = "ItemSound.quest_accept";
public static final String SOUND_ITEMGET = "ItemSound.quest_itemget";
public static final String SOUND_MIDDLE = "ItemSound.quest_middle";
public static final String SOUND_FINISH = "ItemSound.quest_finish";
public static final String SOUND_GIVEUP = "ItemSound.quest_giveup";
public static final String SOUND_JACKPOT = "ItemSound.quest_jackpot";
public static final String SOUND_FANFARE = "ItemSound.quest_fanfare_2";
// State
public static final byte CREATED = State.CREATED;
public static final byte STARTED = State.STARTED;
public static final byte COMPLETED = State.COMPLETED;
/** Quest associated to the QuestState */
private final String _questName;
/** Player who engaged the quest */
private final L2PcInstance _player;
/** State of the quest */
private byte _state;
/** List of couples (variable for quest,value of the variable for quest) */
private Map<String, String> _vars;
/** boolean flag letting QuestStateManager know to exit quest when cleaning up */
private boolean _isExitQuestOnCleanUp = false;
/**
* Constructor of the QuestState : save the quest in the list of quests of the player.<BR/>
* <BR/>
* <U><I>Actions :</U></I><BR/>
* <LI>Save informations in the object QuestState created (Quest, Player, Completion, State)</LI> <LI>Add the QuestState in the player's list
* of quests by using setQuestState()</LI> <LI>Add drops gotten by the quest</LI> <BR/>
*
* @param quest
* : quest associated with the QuestState
* @param player
* : L2PcInstance pointing out the player
* @param state
* : state of the quest
*/
QuestState(Quest quest, L2PcInstance player, byte state)
{
_questName = quest.getName();
_player = player;
// Save the state of the quest for the player in the player's list of quest onwed
getPlayer().setQuestState(this);
// set the state of the quest
_state = state;
}
public String getQuestName()
{
return _questName;
}
/**
* Return the quest
*
* @return Quest
*/
public Quest getQuest()
{
return QuestManager.getInstance().getQuest(_questName);
}
/**
* Return the L2PcInstance
*
* @return L2PcInstance
*/
public L2PcInstance getPlayer()
{
return _player;
}
/**
* Return the state of the quest
*
* @return State
*/
public byte getState()
{
return _state;
}
/**
* Return true if quest just created, false otherwise
*
* @return
*/
public boolean isCreated()
{
return (getState() == State.CREATED);
}
/**
* Return true if quest completed, false otherwise
*
* @return boolean
*/
public boolean isCompleted()
{
return (getState() == State.COMPLETED);
}
/**
* Return true if quest started, false otherwise
*
* @return boolean
*/
public boolean isStarted()
{
return (getState() == State.STARTED);
}
/**
* Return state of the quest after its initialization.<BR>
* <BR>
* <U><I>Actions :</I></U> <LI>Remove drops from previous state</LI> <LI>Set new state of the quest</LI> <LI>Add drop for new state</LI> <LI>
* Update information in database</LI> <LI>Send packet QuestList to client</LI>
*
* @param state
* @return object
*/
public Object setState(byte state)
{
// set new state if it is not already in that state
if (_state != state)
{
final boolean newQuest = isCreated();
_state = state;
if (newQuest)
Quest.createQuestInDb(this);
else
Quest.updateQuestInDb(this);
getPlayer().sendPacket(new QuestList());
}
return state;
}
public Object setStateAndNotSave(byte state)
{
// set new state if it is not already in that state
if (_state != state)
{
_state = state;
getPlayer().sendPacket(new QuestList());
}
return state;
}
/**
* Add parameter used in quests.
*
* @param var
* : String pointing out the name of the variable for quest
* @param val
* : String pointing out the value of the variable for quest
* @return String (equal to parameter "val")
*/
public String setInternal(String var, String val)
{
if (_vars == null)
_vars = new FastMap<>();
if (val == null)
val = "";
_vars.put(var, val);
return val;
}
/**
* Return value of parameter "val" after adding the couple (var,val) in class variable "vars".<BR>
* <BR>
* <U><I>Actions :</I></U><BR>
* <LI>Initialize class variable "vars" if is null</LI> <LI>Initialize parameter "val" if is null</LI> <LI>Add/Update couple (var,val) in
* class variable FastMap "vars"</LI> <LI>If the key represented by "var" exists in FastMap "vars", the couple (var,val) is updated in the
* database. The key is known as existing if the preceding value of the key (given as result of function put()) is not null.<BR>
* If the key doesn't exist, the couple is added/created in the database</LI>
*
* @param var
* : String indicating the name of the variable for quest
* @param val
* : String indicating the value of the variable for quest
* @return String (equal to parameter "val")
*/
public String set(String var, String val)
{
if (_vars == null)
_vars = new FastMap<>();
if (val == null)
val = "";
// FastMap.put() returns previous value associated with specified key, or null if there was no mapping for key.
String old = _vars.put(var, val);
if (old != null)
Quest.updateQuestVarInDb(this, var, val);
else
Quest.createQuestVarInDb(this, var, val);
if ("cond".equals(var))
{
try
{
int previousVal = 0;
try
{
previousVal = Integer.parseInt(old);
}
catch (Exception ex)
{
previousVal = 0;
}
setCond(Integer.parseInt(val), previousVal);
}
catch (Exception e)
{
_log.warn(getPlayer().getName() + ", " + getQuestName() + " cond [" + val + "] is not an integer. Value stored, but no packet was sent: " + e.getMessage(), e);
}
}
return val;
}
/**
* Internally handles the progression of the quest so that it is ready for sending appropriate packets to the client<BR>
* <BR>
* <U><I>Actions :</I></U><BR>
* <LI>Check if the new progress number resets the quest to a previous (smaller) step</LI> <LI>If not, check if quest progress steps have
* been skipped</LI> <LI>If skipped, prepare the variable completedStateFlags appropriately to be ready for sending to clients</LI> <LI>If no
* steps were skipped, flags do not need to be prepared...</LI> <LI>If the passed step resets the quest to a previous step, reset such that
* steps after the parameter are not considered, while skipped steps before the parameter, if any, maintain their info</LI>
*
* @param cond
* : int indicating the step number for the current quest progress (as will be shown to the client)
* @param old
* : int indicating the previously noted step For more info on the variable communicating the progress steps to the client, please
* see
*/
private void setCond(int cond, int old)
{
int completedStateFlags = 0; // initializing...
// if there is no change since last setting, there is nothing to do here
if (cond == old)
return;
// cond 0 and 1 do not need completedStateFlags. Also, if cond > 1, the 1st step must
// always exist (i.e. it can never be skipped). So if cond is 2, we can still safely
// assume no steps have been skipped.
// Finally, more than 31 steps CANNOT be supported in any way with skipping.
if (cond < 3 || cond > 31)
{
unset("__compltdStateFlags");
}
else
completedStateFlags = getInt("__compltdStateFlags");
// case 1: No steps have been skipped so far...
if (completedStateFlags == 0)
{
// check if this step also doesn't skip anything. If so, no further work is needed
// also, in this case, no work is needed if the state is being reset to a smaller value
// in those cases, skip forward to informing the client about the change...
// ELSE, if we just now skipped for the first time...prepare the flags!!!
if (cond > (old + 1))
{
// set the most significant bit to 1 (indicates that there exist skipped states)
// also, ensure that the least significant bit is an 1 (the first step is never skipped, no matter
// what the cond says)
completedStateFlags = 0x80000001;
// since no flag had been skipped until now, the least significant bits must all
// be set to 1, up until "old" number of bits.
completedStateFlags |= ((1 << old) - 1);
// now, just set the bit corresponding to the passed cond to 1 (current step)
completedStateFlags |= (1 << (cond - 1));
set("__compltdStateFlags", String.valueOf(completedStateFlags));
}
}
// case 2: There were exist previously skipped steps
else
{
// if this is a push back to a previous step, clear all completion flags ahead
if (cond < old)
{
completedStateFlags &= ((1 << cond) - 1); // note, this also unsets the flag indicating that there exist skips
// now, check if this resulted in no steps being skipped any more
if (completedStateFlags == ((1 << cond) - 1))
unset("__compltdStateFlags");
else
{
// set the most significant bit back to 1 again, to correctly indicate that this skips states.
// also, ensure that the least significant bit is an 1 (the first step is never skipped, no matter
// what the cond says)
completedStateFlags |= 0x80000001;
set("__compltdStateFlags", String.valueOf(completedStateFlags));
}
}
// if this moves forward, it changes nothing on previously skipped steps...so just mark this
// state and we are done
else
{
completedStateFlags |= (1 << (cond - 1));
set("__compltdStateFlags", String.valueOf(completedStateFlags));
}
}
// send a packet to the client to inform it of the quest progress (step change)
QuestList ql = new QuestList();
getPlayer().sendPacket(ql);
int questId = getQuest().getQuestIntId();
if (questId > 0 && questId < 19999 && cond > 0)
getPlayer().sendPacket(new ExShowQuestMark(questId));
}
/**
* Remove the variable of quest from the list of variables for the quest.<BR>
* <BR>
* <U><I>Concept : </I></U> Remove the variable of quest represented by "var" from the class variable FastMap "vars" and from the database.
*
* @param var
* : String designating the variable for the quest to be deleted
* @return String pointing out the previous value associated with the variable "var"
*/
public String unset(String var)
{
if (_vars == null)
return null;
String old = _vars.remove(var);
if (old != null)
Quest.deleteQuestVarInDb(this, var);
return old;
}
/**
* Insert (or Update) in the database variables that need to stay persistant for this player after a reboot. This function is for storage of
* values that do not related to a specific quest but are global for all quests. For example, player's can get only once the adena and XP
* reward for the first class quests, but they can make more than one first class 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 final void saveGlobalQuestVar(String var, String value)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("REPLACE INTO character_quest_global_data (charId,var,value) VALUES (?,?,?)");
statement.setInt(1, _player.getObjectId());
statement.setString(2, var);
statement.setString(3, value);
statement.executeUpdate();
statement.close();
}
catch (Exception e)
{
_log.warn("Could not insert player's global quest variable: " + e.getMessage(), e);
}
}
/**
* 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 getGlobalQuestVar(String var)
{
String result = "";
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("SELECT value FROM character_quest_global_data WHERE charId = ? AND var = ?");
statement.setInt(1, _player.getObjectId());
statement.setString(2, var);
ResultSet rs = statement.executeQuery();
if (rs.first())
result = rs.getString(1);
rs.close();
statement.close();
}
catch (Exception e)
{
_log.warn("Could not load player's global quest variable: " + e.getMessage(), e);
}
return result;
}
/**
* Permanently delete from the database one of the player's global quest variable that was previously saved.
*
* @param var
* : String designating the name of the variable
*/
public final void deleteGlobalQuestVar(String var)
{
try (Connection con = DatabaseFactory.getConnection())
{
PreparedStatement statement = con.prepareStatement("DELETE FROM character_quest_global_data WHERE charId = ? AND var = ?");
statement.setInt(1, _player.getObjectId());
statement.setString(2, var);
statement.executeUpdate();
statement.close();
}
catch (Exception e)
{
_log.warn("could not delete player's global quest variable: " + e.getMessage(), e);
}
}
/**
* Return the value of the variable of quest represented by "var"
*
* @param var
* : name of the variable of quest
* @return String
*/
public String get(String var)
{
if (_vars == null)
return null;
return _vars.get(var);
}
/**
* Return the value of the variable of quest represented by "var"
*
* @param var
* : String designating the variable for the quest
* @return int
*/
public int getInt(String var)
{
if (_vars == null)
return 0;
final String variable = _vars.get(var);
if (variable == null || variable.length() == 0)
return 0;
int varint = 0;
try
{
varint = Integer.parseInt(variable);
}
catch (Exception e)
{
_log.debug(getPlayer().getName() + ": variable " + var + " isn't an integer: " + varint + " ! " + e.getMessage(), e);
}
return varint;
}
/**
* Add player to get notification of characters death
*
* @param character
* : L2Character of the character to get notification of death
*/
public void addNotifyOfDeath(L2Character character)
{
if (character == null || !(character instanceof L2PcInstance))
return;
((L2PcInstance) character).addNotifyQuestOfDeath(this);
}
/**
* @param itemId
* : ID of the item wanted to be count
* @return the quantity of one sort of item hold by the player
*/
public int getQuestItemsCount(int itemId)
{
int count = 0;
for (L2ItemInstance item : getPlayer().getInventory().getItems())
if (item != null && item.getItemId() == itemId)
count += item.getCount();
return count;
}
/**
* @param itemId
* : ID of the item you're looking for
* @return true if item exists in player's inventory, false - if not
*/
public boolean hasQuestItems(int itemId)
{
return getPlayer().getInventory().getItemByItemId(itemId) != null;
}
/**
* Return the level of enchantment on the weapon of the player(Done specifically for weapon SA's)
*
* @param itemId
* : ID of the item to check enchantment
* @return int
*/
public int getEnchantLevel(int itemId)
{
L2ItemInstance enchanteditem = getPlayer().getInventory().getItemByItemId(itemId);
if (enchanteditem == null)
return 0;
return enchanteditem.getEnchantLevel();
}
/**
* Give adena to the player
*
* @param count
* @param applyRates
*/
public void giveAdena(int count, boolean applyRates)
{
giveItems(57, count, applyRates ? 0 : 1);
}
/**
* Reward player using quest reward config multiplier's.
*
* @param itemId
* : the item to reward.
* @param count
* : the amount to reward, before multiplier.
*/
public void rewardItems(int itemId, int count)
{
if (count <= 0)
return;
L2ItemInstance _tmpItem = ItemTable.getInstance().createDummyItem(itemId);
if (_tmpItem == null)
return;
if (itemId == 57)
count = (int) (count * MainConfig.RATE_QUEST_REWARD_ADENA);
else
count = (int) (count * MainConfig.RATE_QUEST_REWARD);
// Add items to player's inventory
L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
if (item == null)
return;
// If item for reward is gold, send message of gold reward to client
if (itemId == 57)
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA);
smsg.addItemNumber(count);
getPlayer().sendPacket(smsg);
}
// Otherwise, send message of object reward to client
else
{
if (count > 1)
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
smsg.addItemName(item.getItemId());
smsg.addItemNumber(count);
getPlayer().sendPacket(smsg);
}
else
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
smsg.addItemName(item.getItemId());
getPlayer().sendPacket(smsg);
}
}
// send packets
StatusUpdate su = new StatusUpdate(getPlayer());
su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
getPlayer().sendPacket(su);
}
/**
* Give item/reward to the player
*
* @param itemId
* @param count
*/
public void giveItems(int itemId, int count)
{
giveItems(itemId, count, 0);
}
public void giveItems(int itemId, int count, int enchantlevel)
{
if (count <= 0)
return;
// If item for reward is adena (ID=57), modify count with rate for quest reward if rates available
if (itemId == 57 && !(enchantlevel > 0))
count = (int) (count * MainConfig.RATE_QUEST_REWARD_ADENA);
// Add items to player's inventory
L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
if (item == null)
return;
// set enchant level for item if that item is not adena
if (enchantlevel > 0 && itemId != 57)
item.setEnchantLevel(enchantlevel);
// If item for reward is gold, send message of gold reward to client
if (itemId == 57)
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA);
smsg.addItemNumber(count);
getPlayer().sendPacket(smsg);
}
// Otherwise, send message of object reward to client
else
{
if (count > 1)
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
smsg.addItemName(item.getItemId());
smsg.addItemNumber(count);
getPlayer().sendPacket(smsg);
}
else
{
SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
smsg.addItemName(item.getItemId());
getPlayer().sendPacket(smsg);
}
}
// send packets
StatusUpdate su = new StatusUpdate(getPlayer());
su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
getPlayer().sendPacket(su);
}
/**
* Drop Quest item using MainConfig.RATE_QUEST_DROP and random number.<br>
* Dropped item amount is always the same.
*
* @param itemId
* : int Item Identifier of the item to be dropped
* @param count
* : Quantity of items to be dropped
* @param neededCount
* : Quantity of items needed for quest
* @param dropChance
* : Base chance of drop, same as in droplist
* @return boolean indicating whether player has requested number of items
*/
public boolean dropQuestItems(int itemId, int count, int neededCount, int dropChance)
{
return dropQuestItems(itemId, count, count, neededCount, dropChance);
}
/**
* Drop Quest item using MainConfig.RATE_QUEST_DROP and random number.
*
* @param itemId
* : int Item Identifier of the item to be dropped
* @param minCount
* : Minimum quantity of items to be dropped
* @param maxCount
* : Maximum quantity of items to be dropped
* @param neededCount
* : Quantity of items needed for quest
* @param dropChance
* : Base chance of drop, same as in droplist
* @return boolean indicating whether player has requested number of items
*/
public boolean dropQuestItems(int itemId, int minCount, int maxCount, int neededCount, int dropChance)
{
dropChance *= MainConfig.RATE_QUEST_DROP / ((getPlayer().getParty() != null) ? getPlayer().getParty().getMemberCount() : 1);
int currentCount = getQuestItemsCount(itemId);
if (neededCount > 0 && currentCount >= neededCount)
return true;
if (currentCount >= neededCount)
return true;
int itemCount = 0;
int random = Rnd.get(L2DropData.MAX_CHANCE);
while (random < dropChance)
{
// Get the item quantity dropped
if (minCount < maxCount)
itemCount += Rnd.get(minCount, maxCount);
else if (minCount == maxCount)
itemCount += minCount;
else
itemCount++;
// Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
dropChance -= L2DropData.MAX_CHANCE;
}
if (itemCount > 0)
{
// if over neededCount, just fill the gap
if (neededCount > 0 && currentCount + itemCount > neededCount)
itemCount = neededCount - currentCount;
// Inventory slot check
if (!getPlayer().getInventory().validateCapacityByItemId(itemId))
return false;
// Give the item to Player
getPlayer().addItem("Quest", itemId, itemCount, getPlayer().getTarget(), true);
// Play the sound
playSound((currentCount + itemCount < neededCount) ? SOUND_ITEMGET : SOUND_MIDDLE);
}
return (neededCount > 0 && currentCount + itemCount >= neededCount);
}
/**
* Drop Quest item with 100% luck.<br>
* Dropped item amount is always the same.
*
* @param itemId
* : int Item Identifier of the item to be dropped
* @param count
* : (minCount, maxCount) : Quantity of items to be dropped
* @param neededCount
* : Quantity of items needed for quest
* @return boolean indicating whether player has requested number of items
*/
public boolean dropAlwaysQuestItems(int itemId, int count, int neededCount)
{
return dropAlwaysQuestItems(itemId, count, count, neededCount);
}
/**
* Drop Quest item with 100% luck.
*
* @param itemId
* : Item Identifier of the item to be dropped
* @param minCount
* : Minimum quantity of items to be dropped
* @param maxCount
* : Maximum quantity of items to be dropped
* @param neededCount
* : Quantity of items needed for quest
* @return boolean indicating whether player has requested number of items
*/
public boolean dropAlwaysQuestItems(int itemId, int minCount, int maxCount, int neededCount)
{
int currentCount = getQuestItemsCount(itemId);
if (neededCount > 0 && currentCount >= neededCount)
return true;
if (currentCount >= neededCount)
return true;
int itemCount = 0;
// Get the item quantity dropped
if (minCount < maxCount)
itemCount += Rnd.get(minCount, maxCount);
else if (minCount == maxCount)
itemCount += minCount;
else
itemCount++;
if (itemCount > 0)
{
// if over neededCount, just fill the gap
if (neededCount > 0 && currentCount + itemCount > neededCount)
itemCount = neededCount - currentCount;
// Inventory slot check
if (!getPlayer().getInventory().validateCapacityByItemId(itemId))
return false;
// Give the item to Player
getPlayer().addItem("Quest", itemId, itemCount, getPlayer().getTarget(), true);
// Play the sound
playSound((currentCount + itemCount < neededCount) ? SOUND_ITEMGET : SOUND_MIDDLE);
}
return (neededCount > 0 && currentCount + itemCount >= neededCount);
}
// TODO: More radar functions need to be added when the radar class is complete.
// BEGIN STUFF THAT WILL PROBABLY BE CHANGED
public void addRadar(int x, int y, int z)
{
getPlayer().getRadar().addMarker(x, y, z);
}
public void removeRadar(int x, int y, int z)
{
getPlayer().getRadar().removeMarker(x, y, z);
}
public void clearRadar()
{
getPlayer().getRadar().removeAllMarkers();
}
// END STUFF THAT WILL PROBABLY BE CHANGED
/**
* Remove items from player's inventory when talking to NPC in order to have rewards.<BR>
* <BR>
* <U><I>Actions :</I></U> <LI>Destroy quantity of items wanted</LI> <LI>Send new inventory list to player</LI>
*
* @param itemId
* : Identifier of the item
* @param count
* : Quantity of items to destroy
*/
public void takeItems(int itemId, int count)
{
// Get object item from player's inventory list
L2ItemInstance item = getPlayer().getInventory().getItemByItemId(itemId);
if (item == null)
return;
// Tests on count value in order not to have negative value
if (count < 0 || count > item.getCount())
count = item.getCount();
// Destroy the quantity of items wanted
if (item.isEquipped())
{
L2ItemInstance[] unequiped = getPlayer().getInventory().unEquipItemInBodySlotAndRecord(item.getItem().getBodyPart());
InventoryUpdate iu = new InventoryUpdate();
for (L2ItemInstance itm : unequiped)
iu.addModifiedItem(itm);
getPlayer().sendPacket(iu);
getPlayer().broadcastUserInfo();
}
getPlayer().destroyItemByItemId("Quest", itemId, count, getPlayer(), true);
}
/**
* Send a packet in order to play sound at client terminal
*
* @param sound
*/
public void playSound(String sound)
{
getPlayer().sendPacket(new PlaySound(sound));
}
/**
* Add XP and SP as quest reward
*
* @param exp
* @param sp
*/
public void addExpAndSp(int exp, int sp)
{
getPlayer().addExpAndSp((int) getPlayer().calcStat(Stats.EXPSP_RATE, exp * MainConfig.RATE_QUEST_REWARD_XP, null, null), (int) getPlayer().calcStat(Stats.EXPSP_RATE, sp * MainConfig.RATE_QUEST_REWARD_SP, null, null));
}
/**
* Return random value
*
* @param max
* : max value for randomisation
* @return int
*/
public int getRandom(int max)
{
return Rnd.get(max);
}
/**
* @param loc
* A paperdoll slot to check.
* @return the id of the item in the loc paperdoll slot.
*/
public int getItemEquipped(int loc)
{
return getPlayer().getInventory().getPaperdollItemId(loc);
}
/**
* @return the number of ticks from the GameTimeController
*/
public static int getGameTicks()
{
return GameTimeController.getGameTicks();
}
/**
* @return true if quest is to exited on clean up by QuestStateManager
*/
public final boolean isExitQuestOnCleanUp()
{
return _isExitQuestOnCleanUp;
}
/**
* @param isExitQuestOnCleanUp
*/
public void setIsExitQuestOnCleanUp(boolean isExitQuestOnCleanUp)
{
_isExitQuestOnCleanUp = isExitQuestOnCleanUp;
}
/**
* Start a timer for quest.<BR>
* <BR>
*
* @param name
* The name of the timer. Will also be the value for event of onEvent
* @param time
* The milisecond value the timer will elapse
*/
public void startQuestTimer(String name, long time)
{
getQuest().startQuestTimer(name, time, null, getPlayer(), false);
}
public void startQuestTimer(String name, long time, L2Npc npc)
{
getQuest().startQuestTimer(name, time, npc, getPlayer(), false);
}
public void startRepeatingQuestTimer(String name, long time)
{
getQuest().startQuestTimer(name, time, null, getPlayer(), true);
}
public void startRepeatingQuestTimer(String name, long time, L2Npc npc)
{
getQuest().startQuestTimer(name, time, npc, getPlayer(), true);
}
/**
* Return the QuestTimer object with the specified name
*
* @param name
* A name to check.
* @return QuestTimer<BR>
* Return null if name does not exist
*/
public final QuestTimer getQuestTimer(String name)
{
return getQuest().getQuestTimer(name, null, getPlayer());
}
/**
* Add spawn for player instance.
*
* @param npcId
* template of the npc.
* @return the newly created instance.
*/
public L2Npc addSpawn(int npcId)
{
return addSpawn(npcId, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ(), 0, false, 0);
}
public L2Npc addSpawn(int npcId, int despawnDelay)
{
return addSpawn(npcId, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ(), 0, false, despawnDelay);
}
public L2Npc addSpawn(int npcId, int x, int y, int z)
{
return addSpawn(npcId, x, y, z, 0, false, 0);
}
/**
* Add spawn for player instance Will despawn after the spawn length expires Uses player's coords and heading. Adds a little randomization in
* the x y coords
*
* @param npcId
* @param cha
* @return instance of the newly spawned npc.
*/
public L2Npc addSpawn(int npcId, L2Character cha)
{
return addSpawn(npcId, cha, true, 0);
}
public L2Npc addSpawn(int npcId, L2Character cha, int despawnDelay)
{
return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), true, despawnDelay);
}
/**
* Add spawn for player instance Will despawn after the spawn length expires Return object id of newly spawned npc
*
* @param npcId
* @param x
* @param y
* @param z
* @param despawnDelay
* @return
*/
public L2Npc addSpawn(int npcId, int x, int y, int z, int despawnDelay)
{
return addSpawn(npcId, x, y, z, 0, false, despawnDelay);
}
/**
* Add spawn for player instance Inherits coords and heading from specified L2Character instance. It could be either the player, or any
* killed/attacked mob Return object id of newly spawned npc
*
* @param npcId
* @param cha
* @param randomOffset
* @param despawnDelay
* @return
*/
public L2Npc addSpawn(int npcId, L2Character cha, boolean randomOffset, int despawnDelay)
{
return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), randomOffset, despawnDelay);
}
/**
* Add spawn for player instance Return object id of newly spawned npc
*
* @param npcId
* @param x
* @param y
* @param z
* @param heading
* @param randomOffset
* @param despawnDelay
* @return
*/
public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay)
{
return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, true);
}
/**
* Add spawn for player instance Return object id of newly spawned npc
*
* @param npcId
* @param x
* @param y
* @param z
* @param heading
* @param randomOffset
* @param despawnDelay
* @param isSummonSpawn
* @return
*/
public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
{
return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, isSummonSpawn);
}
public String showHtmlFile(String fileName)
{
return getQuest().showHtmlFile(getPlayer(), fileName);
}
/**
* Destroy element used by quest when quest is exited
*
* @param repeatable
* @return QuestState
*/
public QuestState exitQuest(boolean repeatable)
{
// remove this quest from the notifyDeath list of this character if its on this list
_player.removeNotifyQuestOfDeath(this);
if (isCompleted() || isCreated())
return this;
// Say quest is completed
setState(State.COMPLETED);
// Clean registered quest items
int[] itemIdList = getQuest().getRegisteredItemIds();
if (itemIdList != null)
{
for (int element : itemIdList)
takeItems(element, -1);
}
// If quest is repeatable, delete quest from list of quest of the player and from database (quest CAN be created again =>
// repeatable)
if (repeatable)
{
getPlayer().delQuestState(getQuestName());
Quest.deleteQuestInDb(this);
_vars = null;
}
else
{
// Otherwise, delete variables for quest and update database (quest CANNOT be created again => not repeatable)
if (_vars != null)
{
for (String var : _vars.keySet())
unset(var);
}
Quest.updateQuestInDb(this);
}
return this;
}
public void showQuestionMark(int number)
{
getPlayer().sendPacket(new TutorialShowQuestionMark(number));
}
public void playTutorialVoice(String voice)
{
getPlayer().sendPacket(new PlaySound(2, voice, 0, 0, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ()));
}
public void showTutorialHTML(String html)
{
String text = HtmCache.getInstance().getHtm(StaticHtmPath.QuestHtmPath + "Tutorial/" + html);
if (text == null)
{
_log.warn("Missing html page " + StaticHtmPath.QuestHtmPath + "Tutorial/" + html);
text = "<html><body>File " + StaticHtmPath.QuestHtmPath + "Tutorial/" + html + " not found or file is empty.</body></html>";
}
getPlayer().sendPacket(new TutorialShowHtml(text));
}
public void closeTutorialHtml()
{
getPlayer().sendPacket(TutorialCloseHtml.STATIC_PACKET);
}
public void onTutorialClientEvent(int number)
{
getPlayer().sendPacket(new TutorialEnableClientEvent(number));
}
public static void dropItem(L2MonsterInstance npc, L2PcInstance player, int itemId, int count)
{
npc.dropItem(player, itemId, count);
}
}