QuestState.java 32 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059
  1. /*
  2. * This program is free software: you can redistribute it and/or modify it under
  3. * the terms of the GNU General Public License as published by the Free Software
  4. * Foundation, either version 3 of the License, or (at your option) any later
  5. * version.
  6. *
  7. * This program is distributed in the hope that it will be useful, but WITHOUT
  8. * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS
  9. * FOR A PARTICULAR PURPOSE. See the GNU General Public License for more
  10. * details.
  11. *
  12. * You should have received a copy of the GNU General Public License along with
  13. * this program. If not, see <http://www.gnu.org/licenses/>.
  14. */
  15. package com.l2jserver.gameserver.model.quest;
  16. import java.sql.Connection;
  17. import java.sql.PreparedStatement;
  18. import java.sql.ResultSet;
  19. import java.util.Map;
  20. import java.util.logging.Level;
  21. import java.util.logging.Logger;
  22. import com.l2jserver.Config;
  23. import com.l2jserver.L2DatabaseFactory;
  24. import com.l2jserver.gameserver.GameTimeController;
  25. import com.l2jserver.gameserver.cache.HtmCache;
  26. import com.l2jserver.gameserver.instancemanager.QuestManager;
  27. import com.l2jserver.gameserver.model.L2DropData;
  28. import com.l2jserver.gameserver.model.L2ItemInstance;
  29. import com.l2jserver.gameserver.model.actor.L2Character;
  30. import com.l2jserver.gameserver.model.actor.L2Npc;
  31. import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
  32. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  33. import com.l2jserver.gameserver.network.SystemMessageId;
  34. import com.l2jserver.gameserver.network.serverpackets.ExShowQuestMark;
  35. import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
  36. import com.l2jserver.gameserver.network.serverpackets.PlaySound;
  37. import com.l2jserver.gameserver.network.serverpackets.QuestList;
  38. import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
  39. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  40. import com.l2jserver.gameserver.network.serverpackets.TutorialCloseHtml;
  41. import com.l2jserver.gameserver.network.serverpackets.TutorialEnableClientEvent;
  42. import com.l2jserver.gameserver.network.serverpackets.TutorialShowHtml;
  43. import com.l2jserver.gameserver.network.serverpackets.TutorialShowQuestionMark;
  44. import com.l2jserver.gameserver.skills.Stats;
  45. import com.l2jserver.util.Rnd;
  46. import javolution.util.FastMap;
  47. /**
  48. * @author Luis Arias
  49. */
  50. public final class QuestState
  51. {
  52. protected static final Logger _log = Logger.getLogger(Quest.class.getName());
  53. /** Quest associated to the QuestState */
  54. private final String _questName;
  55. /** Player who engaged the quest */
  56. private final L2PcInstance _player;
  57. /** State of the quest */
  58. private byte _state;
  59. /** List of couples (variable for quest,value of the variable for quest) */
  60. private Map<String, String> _vars;
  61. /** Boolean flag letting QuestStateManager know to exit quest when cleaning up */
  62. private boolean _isExitQuestOnCleanUp = false;
  63. /**
  64. * Constructor of the QuestState : save the quest in the list of quests of the player.<BR/><BR/>
  65. *
  66. * <U><I>Actions :</U></I><BR/>
  67. * <LI>Save informations in the object QuestState created (Quest, Player, Completion, State)</LI>
  68. * <LI>Add the QuestState in the player's list of quests by using setQuestState()</LI>
  69. * <LI>Add drops gotten by the quest</LI>
  70. * <BR/>
  71. * @param quest : quest associated with the QuestState
  72. * @param player : L2PcInstance pointing out the player
  73. * @param state : state of the quest
  74. * @param completed : boolean for completion of the quest
  75. */
  76. QuestState(Quest quest, L2PcInstance player, byte state)
  77. {
  78. _questName = quest.getName();
  79. _player = player;
  80. // Save the state of the quest for the player in the player's list of quest onwed
  81. getPlayer().setQuestState(this);
  82. // set the state of the quest
  83. _state = state;
  84. }
  85. public String getQuestName()
  86. {
  87. return _questName;
  88. }
  89. /**
  90. * Return the quest
  91. * @return Quest
  92. */
  93. public Quest getQuest()
  94. {
  95. return QuestManager.getInstance().getQuest(_questName);
  96. }
  97. /**
  98. * Return the L2PcInstance
  99. * @return L2PcInstance
  100. */
  101. public L2PcInstance getPlayer()
  102. {
  103. return _player;
  104. }
  105. /**
  106. * Return the state of the quest
  107. * @return State
  108. */
  109. public byte getState()
  110. {
  111. return _state;
  112. }
  113. /**
  114. * Return true if quest completed, false otherwise
  115. * @return boolean
  116. */
  117. public boolean isCompleted()
  118. {
  119. return (getState() == State.COMPLETED);
  120. }
  121. /**
  122. * Return true if quest started, false otherwise
  123. * @return boolean
  124. */
  125. public boolean isStarted()
  126. {
  127. return (getState() == State.STARTED);
  128. }
  129. /**
  130. * Return state of the quest after its initialization.<BR><BR>
  131. * <U><I>Actions :</I></U>
  132. * <LI>Remove drops from previous state</LI>
  133. * <LI>Set new state of the quest</LI>
  134. * <LI>Add drop for new state</LI>
  135. * <LI>Update information in database</LI>
  136. * <LI>Send packet QuestList to client</LI>
  137. * @param state
  138. * @return object
  139. */
  140. public Object setState(byte state)
  141. {
  142. // set new state if it is not already in that state
  143. if (_state != state)
  144. {
  145. _state = state;
  146. Quest.updateQuestInDb(this);
  147. QuestList ql = new QuestList();
  148. getPlayer().sendPacket(ql);
  149. }
  150. return state;
  151. }
  152. /**
  153. * Add parameter used in quests.
  154. * @param var : String pointing out the name of the variable for quest
  155. * @param val : String pointing out the value of the variable for quest
  156. * @return String (equal to parameter "val")
  157. */
  158. public String setInternal(String var, String val)
  159. {
  160. if (_vars == null)
  161. _vars = new FastMap<String, String>();
  162. if (val == null)
  163. val = "";
  164. _vars.put(var, val);
  165. return val;
  166. }
  167. /**
  168. * Return value of parameter "val" after adding the couple (var,val) in class variable "vars".<BR><BR>
  169. * <U><I>Actions :</I></U><BR>
  170. * <LI>Initialize class variable "vars" if is null</LI>
  171. * <LI>Initialize parameter "val" if is null</LI>
  172. * <LI>Add/Update couple (var,val) in class variable FastMap "vars"</LI>
  173. * <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
  174. * existing if the preceding value of the key (given as result of function put()) is not null.<BR>
  175. * If the key doesn't exist, the couple is added/created in the database</LI>
  176. * @param var : String indicating the name of the variable for quest
  177. * @param val : String indicating the value of the variable for quest
  178. * @return String (equal to parameter "val")
  179. */
  180. public String set(String var, String val)
  181. {
  182. if (_vars == null)
  183. _vars = new FastMap<String, String>();
  184. if (val == null)
  185. val = "";
  186. // FastMap.put() returns previous value associated with specified key, or null if there was no mapping for key.
  187. String old = _vars.put(var, val);
  188. if (old != null)
  189. Quest.updateQuestVarInDb(this, var, val);
  190. else
  191. Quest.createQuestVarInDb(this, var, val);
  192. if ("cond".equals(var))
  193. {
  194. try
  195. {
  196. int previousVal = 0;
  197. try
  198. {
  199. previousVal = Integer.parseInt(old);
  200. }
  201. catch (Exception ex)
  202. {
  203. previousVal = 0;
  204. }
  205. setCond(Integer.parseInt(val), previousVal);
  206. }
  207. catch (Exception e)
  208. {
  209. _log.finer(getPlayer().getName() + ", " + getQuestName() + " cond [" + val + "] is not an integer. Value stored, but no packet was sent: " + e);
  210. }
  211. }
  212. return val;
  213. }
  214. /**
  215. * Internally handles the progression of the quest so that it is ready for sending
  216. * appropriate packets to the client<BR><BR>
  217. * <U><I>Actions :</I></U><BR>
  218. * <LI>Check if the new progress number resets the quest to a previous (smaller) step</LI>
  219. * <LI>If not, check if quest progress steps have been skipped</LI>
  220. * <LI>If skipped, prepare the variable completedStateFlags appropriately to be ready for sending to clients</LI>
  221. * <LI>If no steps were skipped, flags do not need to be prepared...</LI>
  222. * <LI>If the passed step resets the quest to a previous step, reset such that steps after the parameter are not
  223. * considered, while skipped steps before the parameter, if any, maintain their info</LI>
  224. * @param cond : int indicating the step number for the current quest progress (as will be shown to the client)
  225. * @param old : int indicating the previously noted step
  226. *
  227. * For more info on the variable communicating the progress steps to the client, please see
  228. * @link com.l2jserver.loginserver.serverpacket.QuestList
  229. */
  230. private void setCond(int cond, int old)
  231. {
  232. int completedStateFlags = 0; // initializing...
  233. // if there is no change since last setting, there is nothing to do here
  234. if (cond == old)
  235. return;
  236. // cond 0 and 1 do not need completedStateFlags. Also, if cond > 1, the 1st step must
  237. // always exist (i.e. it can never be skipped). So if cond is 2, we can still safely
  238. // assume no steps have been skipped.
  239. // Finally, more than 31 steps CANNOT be supported in any way with skipping.
  240. if (cond < 3 || cond > 31)
  241. {
  242. unset("__compltdStateFlags");
  243. }
  244. else
  245. completedStateFlags = getInt("__compltdStateFlags");
  246. // case 1: No steps have been skipped so far...
  247. if (completedStateFlags == 0)
  248. {
  249. // check if this step also doesn't skip anything. If so, no further work is needed
  250. // also, in this case, no work is needed if the state is being reset to a smaller value
  251. // in those cases, skip forward to informing the client about the change...
  252. // ELSE, if we just now skipped for the first time...prepare the flags!!!
  253. if (cond > (old + 1))
  254. {
  255. // set the most significant bit to 1 (indicates that there exist skipped states)
  256. // also, ensure that the least significant bit is an 1 (the first step is never skipped, no matter
  257. // what the cond says)
  258. completedStateFlags = 0x80000001;
  259. // since no flag had been skipped until now, the least significant bits must all
  260. // be set to 1, up until "old" number of bits.
  261. completedStateFlags |= ((1 << old) - 1);
  262. // now, just set the bit corresponding to the passed cond to 1 (current step)
  263. completedStateFlags |= (1 << (cond - 1));
  264. set("__compltdStateFlags", String.valueOf(completedStateFlags));
  265. }
  266. }
  267. // case 2: There were exist previously skipped steps
  268. else
  269. {
  270. // if this is a push back to a previous step, clear all completion flags ahead
  271. if (cond < old)
  272. {
  273. completedStateFlags &= ((1 << cond) - 1); // note, this also unsets the flag indicating that there exist skips
  274. //now, check if this resulted in no steps being skipped any more
  275. if (completedStateFlags == ((1 << cond) - 1))
  276. unset("__compltdStateFlags");
  277. else
  278. {
  279. // set the most significant bit back to 1 again, to correctly indicate that this skips states.
  280. // also, ensure that the least significant bit is an 1 (the first step is never skipped, no matter
  281. // what the cond says)
  282. completedStateFlags |= 0x80000001;
  283. set("__compltdStateFlags", String.valueOf(completedStateFlags));
  284. }
  285. }
  286. // if this moves forward, it changes nothing on previously skipped steps...so just mark this
  287. // state and we are done
  288. else
  289. {
  290. completedStateFlags |= (1 << (cond - 1));
  291. set("__compltdStateFlags", String.valueOf(completedStateFlags));
  292. }
  293. }
  294. // send a packet to the client to inform it of the quest progress (step change)
  295. QuestList ql = new QuestList();
  296. getPlayer().sendPacket(ql);
  297. int questId = getQuest().getQuestIntId();
  298. if (questId > 0 && questId < 19999 && cond > 0)
  299. getPlayer().sendPacket(new ExShowQuestMark(questId));
  300. }
  301. /**
  302. * Remove the variable of quest from the list of variables for the quest.<BR><BR>
  303. * <U><I>Concept : </I></U>
  304. * Remove the variable of quest represented by "var" from the class variable FastMap "vars" and from the database.
  305. * @param var : String designating the variable for the quest to be deleted
  306. * @return String pointing out the previous value associated with the variable "var"
  307. */
  308. public String unset(String var)
  309. {
  310. if (_vars == null)
  311. return null;
  312. String old = _vars.remove(var);
  313. if (old != null)
  314. Quest.deleteQuestVarInDb(this, var);
  315. return old;
  316. }
  317. /**
  318. * Insert (or Update) in the database variables that need to stay persistant for this player after a reboot.
  319. * This function is for storage of values that do not related to a specific quest but are
  320. * global for all quests. For example, player's can get only once the adena and XP reward for
  321. * the first class quests, but they can make more than one first class quest.
  322. * @param var : String designating the name of the variable for the quest
  323. * @param value : String designating the value of the variable for the quest
  324. */
  325. public final void saveGlobalQuestVar(String var, String value)
  326. {
  327. Connection con = null;
  328. try
  329. {
  330. con = L2DatabaseFactory.getInstance().getConnection();
  331. PreparedStatement statement;
  332. statement = con.prepareStatement("REPLACE INTO character_quest_global_data (charId,var,value) VALUES (?,?,?)");
  333. statement.setInt(1, _player.getObjectId());
  334. statement.setString(2, var);
  335. statement.setString(3, value);
  336. statement.executeUpdate();
  337. statement.close();
  338. }
  339. catch (Exception e)
  340. {
  341. _log.log(Level.WARNING, "could not insert player's global quest variable:", e);
  342. }
  343. finally
  344. {
  345. try
  346. {
  347. con.close();
  348. }
  349. catch (Exception e)
  350. {
  351. e.printStackTrace();
  352. }
  353. }
  354. }
  355. /**
  356. * Read from the database a previously saved variable for this quest.
  357. * Due to performance considerations, this function should best be used only when the quest is first loaded.
  358. * Subclasses of this class can define structures into which these loaded values can be saved.
  359. * However, on-demand usage of this function throughout the script is not prohibited, only not recommended.
  360. * Values read from this function were entered by calls to "saveGlobalQuestVar"
  361. * @param var : String designating the name of the variable for the quest
  362. * @return String : String representing the loaded value for the passed var, or an empty string if the var was invalid
  363. */
  364. public final String getGlobalQuestVar(String var)
  365. {
  366. String result = "";
  367. Connection con = null;
  368. try
  369. {
  370. con = L2DatabaseFactory.getInstance().getConnection();
  371. PreparedStatement statement;
  372. statement = con.prepareStatement("SELECT value FROM character_quest_global_data WHERE charId = ? AND var = ?");
  373. statement.setInt(1, _player.getObjectId());
  374. statement.setString(2, var);
  375. ResultSet rs = statement.executeQuery();
  376. if (rs.first())
  377. result = rs.getString(1);
  378. rs.close();
  379. statement.close();
  380. }
  381. catch (Exception e)
  382. {
  383. _log.log(Level.WARNING, "could not load player's global quest variable:", e);
  384. }
  385. finally
  386. {
  387. try
  388. {
  389. con.close();
  390. }
  391. catch (Exception e)
  392. {
  393. e.printStackTrace();
  394. }
  395. }
  396. return result;
  397. }
  398. /**
  399. * Permanently delete from the database one of the player's global quest variable that was previously saved.
  400. * @param var : String designating the name of the variable
  401. */
  402. public final void deleteGlobalQuestVar(String var)
  403. {
  404. Connection con = null;
  405. try
  406. {
  407. con = L2DatabaseFactory.getInstance().getConnection();
  408. PreparedStatement statement;
  409. statement = con.prepareStatement("DELETE FROM character_quest_global_data WHERE charId = ? AND var = ?");
  410. statement.setInt(1, _player.getObjectId());
  411. statement.setString(2, var);
  412. statement.executeUpdate();
  413. statement.close();
  414. }
  415. catch (Exception e)
  416. {
  417. _log.log(Level.WARNING, "could not delete player's global quest variable:", e);
  418. }
  419. finally
  420. {
  421. try
  422. {
  423. con.close();
  424. }
  425. catch (Exception e)
  426. {
  427. e.printStackTrace();
  428. }
  429. }
  430. }
  431. /**
  432. * Return the value of the variable of quest represented by "var"
  433. * @param var : name of the variable of quest
  434. * @return Object
  435. */
  436. public Object get(String var)
  437. {
  438. if (_vars == null)
  439. return null;
  440. return _vars.get(var);
  441. }
  442. /**
  443. * Return the value of the variable of quest represented by "var"
  444. * @param var : String designating the variable for the quest
  445. * @return int
  446. */
  447. public int getInt(String var)
  448. {
  449. int varint = 0;
  450. try
  451. {
  452. varint = Integer.parseInt(_vars.get(var));
  453. }
  454. catch (Exception e)
  455. {
  456. _log.finer(getPlayer().getName() + ": variable " + var + " isn't an integer: " + varint + e);
  457. // if (Config.AUTODELETE_INVALID_QUEST_DATA)
  458. // exitQuest(true);
  459. }
  460. return varint;
  461. }
  462. /**
  463. * Add player to get notification of characters death
  464. * @param character : L2Character of the character to get notification of death
  465. */
  466. public void addNotifyOfDeath(L2Character character)
  467. {
  468. if (character == null || !(character instanceof L2PcInstance))
  469. return;
  470. ((L2PcInstance)character).addNotifyQuestOfDeath(this);
  471. }
  472. /**
  473. * Return the quantity of one sort of item hold by the player
  474. * @param itemId : ID of the item wanted to be count
  475. * @return long
  476. */
  477. public long getQuestItemsCount(int itemId)
  478. {
  479. long count = 0;
  480. for (L2ItemInstance item : getPlayer().getInventory().getItems())
  481. if (item != null && item.getItemId() == itemId)
  482. count += item.getCount();
  483. return count;
  484. }
  485. /**
  486. * Return the level of enchantment on the weapon of the player(Done specifically for weapon SA's)
  487. * @param itemId : ID of the item to check enchantment
  488. * @return int
  489. */
  490. public int getEnchantLevel(int itemId)
  491. {
  492. L2ItemInstance enchanteditem = getPlayer().getInventory().getItemByItemId(itemId);
  493. if (enchanteditem == null)
  494. return 0;
  495. return enchanteditem.getEnchantLevel();
  496. }
  497. /**
  498. * Give adena to the player
  499. * @param count
  500. * @param applyRates
  501. */
  502. public void giveAdena(long count, boolean applyRates)
  503. {
  504. giveItems(57, count, applyRates ? 0 : 1);
  505. }
  506. /**
  507. * Give item/reward to the player
  508. * @param itemId
  509. * @param count
  510. */
  511. public void giveItems(int itemId, long count)
  512. {
  513. giveItems(itemId, count, 0);
  514. }
  515. public void giveItems(int itemId, long count, int enchantlevel)
  516. {
  517. if (count <= 0)
  518. return;
  519. // If item for reward is adena (ID=57), modify count with rate for quest reward if rates available
  520. if (itemId == 57 && !(enchantlevel > 0))
  521. count = (long) (count * Config.RATE_QUESTS_REWARD);
  522. // Add items to player's inventory
  523. L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
  524. if (item == null)
  525. return;
  526. // set enchant level for item if that item is not adena
  527. if (enchantlevel > 0 && itemId != 57)
  528. item.setEnchantLevel(enchantlevel);
  529. // If item for reward is gold, send message of gold reward to client
  530. if (itemId == 57)
  531. {
  532. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ADENA);
  533. smsg.addItemNumber(count);
  534. getPlayer().sendPacket(smsg);
  535. }
  536. // Otherwise, send message of object reward to client
  537. else
  538. {
  539. if (count > 1)
  540. {
  541. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
  542. smsg.addItemName(item);
  543. smsg.addItemNumber(count);
  544. getPlayer().sendPacket(smsg);
  545. }
  546. else
  547. {
  548. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ITEM);
  549. smsg.addItemName(item);
  550. getPlayer().sendPacket(smsg);
  551. }
  552. }
  553. // send packets
  554. StatusUpdate su = new StatusUpdate(getPlayer().getObjectId());
  555. su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
  556. getPlayer().sendPacket(su);
  557. }
  558. public void giveItems(int itemId, long count, byte attributeId, int attributeLevel)
  559. {
  560. if (count <= 0)
  561. return;
  562. // Add items to player's inventory
  563. L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
  564. if (item == null)
  565. return;
  566. // set enchant level for item if that item is not adena
  567. if (attributeId >= 0 && attributeLevel > 0)
  568. {
  569. item.setElementAttr(attributeId, attributeLevel);
  570. if (item.isEquipped())
  571. item.updateElementAttrBonus(getPlayer());
  572. InventoryUpdate iu = new InventoryUpdate();
  573. iu.addModifiedItem(item);
  574. getPlayer().sendPacket(iu);
  575. }
  576. // If item for reward is gold, send message of gold reward to client
  577. if (itemId == 57)
  578. {
  579. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ADENA);
  580. smsg.addItemNumber(count);
  581. getPlayer().sendPacket(smsg);
  582. }
  583. // Otherwise, send message of object reward to client
  584. else
  585. {
  586. if (count > 1)
  587. {
  588. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
  589. smsg.addItemName(item);
  590. smsg.addItemNumber(count);
  591. getPlayer().sendPacket(smsg);
  592. }
  593. else
  594. {
  595. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ITEM);
  596. smsg.addItemName(item);
  597. getPlayer().sendPacket(smsg);
  598. }
  599. }
  600. // send packets
  601. StatusUpdate su = new StatusUpdate(getPlayer().getObjectId());
  602. su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
  603. getPlayer().sendPacket(su);
  604. }
  605. /**
  606. * Drop Quest item using Config.RATE_DROP_QUEST
  607. * @param itemId : int Item Identifier of the item to be dropped
  608. * @param count(minCount, maxCount) : long Quantity of items to be dropped
  609. * @param neededCount : Quantity of items needed for quest
  610. * @param dropChance : int Base chance of drop, same as in droplist
  611. * @param sound : boolean indicating whether to play sound
  612. * @return boolean indicating whether player has requested number of items
  613. */
  614. public boolean dropQuestItems(int itemId, int count, long neededCount, int dropChance, boolean sound)
  615. {
  616. return dropQuestItems(itemId, count, count, neededCount, dropChance, sound);
  617. }
  618. public boolean dropQuestItems(int itemId, int minCount, int maxCount, long neededCount, int dropChance, boolean sound)
  619. {
  620. dropChance *= Config.RATE_DROP_QUEST / ((getPlayer().getParty() != null) ? getPlayer().getParty().getMemberCount() : 1);
  621. long currentCount = getQuestItemsCount(itemId);
  622. if (neededCount > 0 && currentCount >= neededCount)
  623. return true;
  624. if (currentCount >= neededCount)
  625. return true;
  626. long itemCount = 0;
  627. int random = Rnd.get(L2DropData.MAX_CHANCE);
  628. while (random < dropChance)
  629. {
  630. // Get the item quantity dropped
  631. if (minCount < maxCount)
  632. itemCount += Rnd.get(minCount, maxCount);
  633. else if (minCount == maxCount)
  634. itemCount += minCount;
  635. else
  636. itemCount++;
  637. // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
  638. dropChance -= L2DropData.MAX_CHANCE;
  639. }
  640. if (itemCount > 0)
  641. {
  642. // if over neededCount, just fill the gap
  643. if (neededCount > 0 && currentCount + itemCount > neededCount)
  644. itemCount = neededCount - currentCount;
  645. // Inventory slot check
  646. if (!getPlayer().getInventory().validateCapacityByItemId(itemId))
  647. return false;
  648. // Give the item to Player
  649. getPlayer().addItem("Quest", itemId, itemCount, getPlayer().getTarget(), true);
  650. if (sound)
  651. playSound((currentCount + itemCount < neededCount) ? "Itemsound.quest_itemget" : "Itemsound.quest_middle");
  652. }
  653. return (neededCount > 0 && currentCount + itemCount >= neededCount);
  654. }
  655. //TODO: More radar functions need to be added when the radar class is complete.
  656. // BEGIN STUFF THAT WILL PROBABLY BE CHANGED
  657. public void addRadar(int x, int y, int z)
  658. {
  659. getPlayer().getRadar().addMarker(x, y, z);
  660. }
  661. public void removeRadar(int x, int y, int z)
  662. {
  663. getPlayer().getRadar().removeMarker(x, y, z);
  664. }
  665. public void clearRadar()
  666. {
  667. getPlayer().getRadar().removeAllMarkers();
  668. }
  669. // END STUFF THAT WILL PROBABLY BE CHANGED
  670. /**
  671. * Remove items from player's inventory when talking to NPC in order to have rewards.<BR><BR>
  672. * <U><I>Actions :</I></U>
  673. * <LI>Destroy quantity of items wanted</LI>
  674. * <LI>Send new inventory list to player</LI>
  675. * @param itemId : Identifier of the item
  676. * @param count : Quantity of items to destroy
  677. */
  678. public void takeItems(int itemId, long count)
  679. {
  680. // Get object item from player's inventory list
  681. L2ItemInstance item = getPlayer().getInventory().getItemByItemId(itemId);
  682. if (item == null)
  683. return;
  684. // Tests on count value in order not to have negative value
  685. if (count < 0 || count > item.getCount())
  686. count = item.getCount();
  687. // Destroy the quantity of items wanted
  688. if (itemId == 57)
  689. getPlayer().reduceAdena("Quest", count, getPlayer(), true);
  690. else
  691. {
  692. if (item.isEquipped())
  693. {
  694. L2ItemInstance[] unequiped = getPlayer().getInventory().unEquipItemInBodySlotAndRecord(item.getItem().getBodyPart());
  695. InventoryUpdate iu = new InventoryUpdate();
  696. for (L2ItemInstance itm: unequiped)
  697. iu.addModifiedItem(itm);
  698. getPlayer().sendPacket(iu);
  699. getPlayer().broadcastUserInfo();
  700. }
  701. getPlayer().destroyItemByItemId("Quest", itemId, count, getPlayer(), true);
  702. }
  703. }
  704. /**
  705. * Send a packet in order to play sound at client terminal
  706. * @param sound
  707. */
  708. public void playSound(String sound)
  709. {
  710. getPlayer().sendPacket(new PlaySound(sound));
  711. }
  712. /**
  713. * Add XP and SP as quest reward
  714. * @param exp
  715. * @param sp
  716. */
  717. public void addExpAndSp(int exp, int sp)
  718. {
  719. getPlayer().addExpAndSp((int) getPlayer().calcStat(Stats.EXPSP_RATE, exp * Config.RATE_QUESTS_REWARD, null, null), (int) getPlayer().calcStat(Stats.EXPSP_RATE, sp * Config.RATE_QUESTS_REWARD, null, null));
  720. }
  721. /**
  722. * Return random value
  723. * @param max : max value for randomisation
  724. * @return int
  725. */
  726. public int getRandom(int max)
  727. {
  728. return Rnd.get(max);
  729. }
  730. /**
  731. * Return number of ticks from GameTimeController
  732. * @return int
  733. */
  734. public int getItemEquipped(int loc)
  735. {
  736. return getPlayer().getInventory().getPaperdollItemId(loc);
  737. }
  738. /**
  739. * Return the number of ticks from the GameTimeController
  740. * @return int
  741. */
  742. public int getGameTicks()
  743. {
  744. return GameTimeController.getGameTicks();
  745. }
  746. /**
  747. * Return true if quest is to exited on clean up by QuestStateManager
  748. * @return boolean
  749. */
  750. public final boolean isExitQuestOnCleanUp()
  751. {
  752. return _isExitQuestOnCleanUp;
  753. }
  754. /**
  755. * Return the QuestTimer object with the specified name
  756. * @return QuestTimer<BR> Return null if name does not exist
  757. */
  758. public void setIsExitQuestOnCleanUp(boolean isExitQuestOnCleanUp)
  759. {
  760. _isExitQuestOnCleanUp = isExitQuestOnCleanUp;
  761. }
  762. /**
  763. * Start a timer for quest.<BR><BR>
  764. * @param name<BR> The name of the timer. Will also be the value for event of onEvent
  765. * @param time<BR> The milisecond value the timer will elapse
  766. */
  767. public void startQuestTimer(String name, long time)
  768. {
  769. getQuest().startQuestTimer(name, time, null, getPlayer(), false);
  770. }
  771. public void startQuestTimer(String name, long time, L2Npc npc)
  772. {
  773. getQuest().startQuestTimer(name, time, npc, getPlayer(), false);
  774. }
  775. public void startRepeatingQuestTimer(String name, long time)
  776. {
  777. getQuest().startQuestTimer(name, time, null, getPlayer(), true);
  778. }
  779. public void startRepeatingQuestTimer(String name, long time, L2Npc npc)
  780. {
  781. getQuest().startQuestTimer(name, time, npc, getPlayer(), true);
  782. }
  783. /**
  784. * Return the QuestTimer object with the specified name
  785. * @return QuestTimer<BR> Return null if name does not exist
  786. */
  787. public final QuestTimer getQuestTimer(String name)
  788. {
  789. return getQuest().getQuestTimer(name, null, getPlayer());
  790. }
  791. /**
  792. * Add spawn for player instance
  793. * Return object id of newly spawned npc
  794. */
  795. public L2Npc addSpawn(int npcId)
  796. {
  797. return addSpawn(npcId, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ(), 0, false, 0);
  798. }
  799. public L2Npc addSpawn(int npcId, int despawnDelay)
  800. {
  801. return addSpawn(npcId, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ(), 0, false, despawnDelay);
  802. }
  803. public L2Npc addSpawn(int npcId, int x, int y, int z)
  804. {
  805. return addSpawn(npcId, x, y, z, 0, false, 0);
  806. }
  807. /**
  808. * Add spawn for player instance
  809. * Will despawn after the spawn length expires
  810. * Uses player's coords and heading.
  811. * Adds a little randomization in the x y coords
  812. * Return object id of newly spawned npc
  813. */
  814. public L2Npc addSpawn(int npcId, L2Character cha)
  815. {
  816. return addSpawn(npcId, cha, true, 0);
  817. }
  818. public L2Npc addSpawn(int npcId, L2Character cha, int despawnDelay)
  819. {
  820. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), true, despawnDelay);
  821. }
  822. /**
  823. * Add spawn for player instance
  824. * Will despawn after the spawn length expires
  825. * Return object id of newly spawned npc
  826. */
  827. public L2Npc addSpawn(int npcId, int x, int y, int z, int despawnDelay)
  828. {
  829. return addSpawn(npcId, x, y, z, 0, false, despawnDelay);
  830. }
  831. /**
  832. * Add spawn for player instance
  833. * Inherits coords and heading from specified L2Character instance.
  834. * It could be either the player, or any killed/attacked mob
  835. * Return object id of newly spawned npc
  836. */
  837. public L2Npc addSpawn(int npcId, L2Character cha, boolean randomOffset, int despawnDelay)
  838. {
  839. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), randomOffset, despawnDelay);
  840. }
  841. /**
  842. * Add spawn for player instance
  843. * Return object id of newly spawned npc
  844. */
  845. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay)
  846. {
  847. return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, false);
  848. }
  849. /**
  850. * Add spawn for player instance
  851. * Return object id of newly spawned npc
  852. */
  853. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  854. {
  855. return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, isSummonSpawn);
  856. }
  857. public String showHtmlFile(String fileName)
  858. {
  859. return getQuest().showHtmlFile(getPlayer(), fileName);
  860. }
  861. /**
  862. * Destroy element used by quest when quest is exited
  863. * @param repeatable
  864. * @return QuestState
  865. */
  866. public QuestState exitQuest(boolean repeatable)
  867. {
  868. // remove this quest from the notifyDeath list of this character if its on this list
  869. _player.removeNotifyQuestOfDeath(this);
  870. if (isCompleted())
  871. return this;
  872. // Say quest is completed
  873. setState(State.COMPLETED);
  874. // Clean registered quest items
  875. int[] itemIdList = getQuest().getRegisteredItemIds();
  876. if (itemIdList != null)
  877. {
  878. for (int i = 0; i < itemIdList.length; i++)
  879. takeItems(itemIdList[i], -1);
  880. }
  881. // If quest is repeatable, delete quest from list of quest of the player and from database (quest CAN be created again => repeatable)
  882. if (repeatable)
  883. {
  884. getPlayer().delQuestState(getQuestName());
  885. Quest.deleteQuestInDb(this);
  886. _vars = null;
  887. }
  888. else
  889. {
  890. // Otherwise, delete variables for quest and update database (quest CANNOT be created again => not repeatable)
  891. if (_vars != null)
  892. {
  893. for (String var : _vars.keySet())
  894. unset(var);
  895. }
  896. Quest.updateQuestInDb(this);
  897. }
  898. return this;
  899. }
  900. public void showQuestionMark(int number)
  901. {
  902. getPlayer().sendPacket(new TutorialShowQuestionMark(number));
  903. }
  904. public void playTutorialVoice(String voice)
  905. {
  906. getPlayer().sendPacket(new PlaySound(2, voice, 0, 0, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ()));
  907. }
  908. public void showTutorialHTML(String html)
  909. {
  910. String text = HtmCache.getInstance().getHtm("data/scripts/quests/255_Tutorial/" + html);
  911. if (text == null)
  912. {
  913. _log.warning("missing html page data/scripts/quests/255_Tutorial/" + html);
  914. text = "<html><body>File data/scripts/quests/255_Tutorial/" + html + " not found or file is empty.</body></html>";
  915. }
  916. getPlayer().sendPacket(new TutorialShowHtml(text));
  917. }
  918. public void closeTutorialHtml()
  919. {
  920. getPlayer().sendPacket(new TutorialCloseHtml());
  921. }
  922. public void onTutorialClientEvent(int number)
  923. {
  924. getPlayer().sendPacket(new TutorialEnableClientEvent(number));
  925. }
  926. public void dropItem(L2MonsterInstance npc, L2PcInstance player, int itemId, int count)
  927. {
  928. npc.dropItem(player, itemId, count);
  929. }
  930. }