QuestState.java 37 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146
  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.Calendar;
  20. import java.util.HashMap;
  21. import java.util.Map;
  22. import java.util.logging.Level;
  23. import java.util.logging.Logger;
  24. import com.l2jserver.L2DatabaseFactory;
  25. import com.l2jserver.gameserver.cache.HtmCache;
  26. import com.l2jserver.gameserver.instancemanager.QuestManager;
  27. import com.l2jserver.gameserver.model.actor.L2Character;
  28. import com.l2jserver.gameserver.model.actor.L2Npc;
  29. import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
  30. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  31. import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
  32. import com.l2jserver.gameserver.network.serverpackets.ExShowQuestMark;
  33. import com.l2jserver.gameserver.network.serverpackets.PlaySound;
  34. import com.l2jserver.gameserver.network.serverpackets.QuestList;
  35. import com.l2jserver.gameserver.network.serverpackets.TutorialCloseHtml;
  36. import com.l2jserver.gameserver.network.serverpackets.TutorialEnableClientEvent;
  37. import com.l2jserver.gameserver.network.serverpackets.TutorialShowHtml;
  38. import com.l2jserver.gameserver.network.serverpackets.TutorialShowQuestionMark;
  39. import com.l2jserver.gameserver.util.Util;
  40. /**
  41. * @author Luis Arias
  42. */
  43. public final class QuestState
  44. {
  45. protected static final Logger _log = Logger.getLogger(QuestState.class.getName());
  46. /** The name of the quest of this QuestState */
  47. private final String _questName;
  48. /** The "owner" of this QuestState object */
  49. private final L2PcInstance _player;
  50. /** The current state of the quest */
  51. private byte _state;
  52. /** A map of key->value pairs containing the quest state variables and their values */
  53. private Map<String, String> _vars;
  54. /**
  55. * boolean flag letting QuestStateManager know to exit quest when cleaning up
  56. */
  57. private boolean _isExitQuestOnCleanUp = false;
  58. /**
  59. * This enumerate represent the different quest types.
  60. */
  61. public static enum QuestType
  62. {
  63. REPEATABLE,
  64. ONE_TIME,
  65. DAILY
  66. }
  67. /**
  68. * Constructor of the QuestState. Creates the QuestState object and sets the player's progress of the quest to this QuestState.
  69. * @param quest the {@link Quest} object associated with the QuestState
  70. * @param player the owner of this {@link QuestState} object
  71. * @param state the initial state of the quest
  72. */
  73. public QuestState(Quest quest, L2PcInstance player, byte state)
  74. {
  75. _questName = quest.getName();
  76. _player = player;
  77. _state = state;
  78. player.setQuestState(this);
  79. }
  80. /**
  81. * @return the name of the quest of this QuestState
  82. */
  83. public String getQuestName()
  84. {
  85. return _questName;
  86. }
  87. /**
  88. * @return the {@link Quest} object of this QuestState
  89. */
  90. public Quest getQuest()
  91. {
  92. return QuestManager.getInstance().getQuest(_questName);
  93. }
  94. /**
  95. * @return the {@link L2PcInstance} object of the owner of this QuestState
  96. */
  97. public L2PcInstance getPlayer()
  98. {
  99. return _player;
  100. }
  101. /**
  102. * @return the current State of this QuestState
  103. * @see com.l2jserver.gameserver.model.quest.State
  104. */
  105. public byte getState()
  106. {
  107. return _state;
  108. }
  109. /**
  110. * @return {@code true} if the State of this QuestState is CREATED, {@code false} otherwise
  111. * @see com.l2jserver.gameserver.model.quest.State
  112. */
  113. public boolean isCreated()
  114. {
  115. return (_state == State.CREATED);
  116. }
  117. /**
  118. * @return {@code true} if the State of this QuestState is STARTED, {@code false} otherwise
  119. * @see com.l2jserver.gameserver.model.quest.State
  120. */
  121. public boolean isStarted()
  122. {
  123. return (_state == State.STARTED);
  124. }
  125. /**
  126. * @return {@code true} if the State of this QuestState is COMPLETED, {@code false} otherwise
  127. * @see com.l2jserver.gameserver.model.quest.State
  128. */
  129. public boolean isCompleted()
  130. {
  131. return (_state == State.COMPLETED);
  132. }
  133. /**
  134. * @param state the new state of the quest to set
  135. * @return {@code true} if state was changed, {@code false} otherwise
  136. * @see #setState(byte state, boolean saveInDb)
  137. * @see com.l2jserver.gameserver.model.quest.State
  138. */
  139. public boolean setState(byte state)
  140. {
  141. return setState(state, true);
  142. }
  143. /**
  144. * Change the state of this quest to the specified value.
  145. * @param state the new state of the quest to set
  146. * @param saveInDb if {@code true}, will save the state change in the database
  147. * @return {@code true} if state was changed, {@code false} otherwise
  148. * @see com.l2jserver.gameserver.model.quest.State
  149. */
  150. public boolean setState(byte state, boolean saveInDb)
  151. {
  152. if (_state == state)
  153. {
  154. return false;
  155. }
  156. final boolean newQuest = isCreated();
  157. _state = state;
  158. if (saveInDb)
  159. {
  160. if (newQuest)
  161. {
  162. Quest.createQuestInDb(this);
  163. }
  164. else
  165. {
  166. Quest.updateQuestInDb(this);
  167. }
  168. }
  169. _player.sendPacket(new QuestList());
  170. return true;
  171. }
  172. /**
  173. * Add parameter used in quests.
  174. * @param var : String pointing out the name of the variable for quest
  175. * @param val : String pointing out the value of the variable for quest
  176. * @return String (equal to parameter "val")
  177. */
  178. public String setInternal(String var, String val)
  179. {
  180. if (_vars == null)
  181. {
  182. _vars = new HashMap<>();
  183. }
  184. if (val == null)
  185. {
  186. val = "";
  187. }
  188. _vars.put(var, val);
  189. return val;
  190. }
  191. /**
  192. * Return value of parameter "val" after adding the couple (var,val) in class variable "vars".<br>
  193. * Actions:<br>
  194. * <ul>
  195. * <li>Initialize class variable "vars" if is null.</li>
  196. * <li>Initialize parameter "val" if is null</li>
  197. * <li>Add/Update couple (var,val) in class variable FastMap "vars"</li>
  198. * <li>If the key represented by "var" exists in FastMap "vars", the couple (var,val) is updated in the database.<br>
  199. * The key is known as existing if the preceding value of the key (given as result of function put()) is not null.<br>
  200. * If the key doesn't exist, the couple is added/created in the database</li>
  201. * <ul>
  202. * @param var : String indicating the name of the variable for quest
  203. * @param val : String indicating the value of the variable for quest
  204. * @return String (equal to parameter "val")
  205. */
  206. public String set(String var, String val)
  207. {
  208. if (_vars == null)
  209. {
  210. _vars = new HashMap<>();
  211. }
  212. if (val == null)
  213. {
  214. val = "";
  215. }
  216. // FastMap.put() returns previous value associated with specified key, or null if there was no mapping for key.
  217. String old = _vars.put(var, val);
  218. if (old != null)
  219. {
  220. Quest.updateQuestVarInDb(this, var, val);
  221. }
  222. else
  223. {
  224. Quest.createQuestVarInDb(this, var, val);
  225. }
  226. if ("cond".equals(var))
  227. {
  228. try
  229. {
  230. int previousVal = 0;
  231. try
  232. {
  233. previousVal = Integer.parseInt(old);
  234. }
  235. catch (Exception ex)
  236. {
  237. previousVal = 0;
  238. }
  239. setCond(Integer.parseInt(val), previousVal);
  240. }
  241. catch (Exception e)
  242. {
  243. _log.log(Level.WARNING, _player.getName() + ", " + getQuestName() + " cond [" + val + "] is not an integer. Value stored, but no packet was sent: " + e.getMessage(), e);
  244. }
  245. }
  246. return val;
  247. }
  248. /**
  249. * Internally handles the progression of the quest so that it is ready for sending appropriate packets to the client.<br>
  250. * <u><i>Actions :</i></u><br>
  251. * <ul>
  252. * <li>Check if the new progress number resets the quest to a previous (smaller) step.</li>
  253. * <li>If not, check if quest progress steps have been skipped.</li>
  254. * <li>If skipped, prepare the variable completedStateFlags appropriately to be ready for sending to clients.</li>
  255. * <li>If no steps were skipped, flags do not need to be prepared...</li>
  256. * <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>
  257. * </ul>
  258. * @param cond the current quest progress condition (0 - 31 including)
  259. * @param old the previous quest progress condition to check against
  260. */
  261. private void setCond(int cond, int old)
  262. {
  263. if (cond == old)
  264. {
  265. return;
  266. }
  267. int completedStateFlags = 0;
  268. // cond 0 and 1 do not need completedStateFlags. Also, if cond > 1, the 1st step must
  269. // always exist (i.e. it can never be skipped). So if cond is 2, we can still safely
  270. // assume no steps have been skipped.
  271. // Finally, more than 31 steps CANNOT be supported in any way with skipping.
  272. if ((cond < 3) || (cond > 31))
  273. {
  274. unset("__compltdStateFlags");
  275. }
  276. else
  277. {
  278. completedStateFlags = getInt("__compltdStateFlags");
  279. }
  280. // case 1: No steps have been skipped so far...
  281. if (completedStateFlags == 0)
  282. {
  283. // check if this step also doesn't skip anything. If so, no further work is needed
  284. // also, in this case, no work is needed if the state is being reset to a smaller value
  285. // in those cases, skip forward to informing the client about the change...
  286. // ELSE, if we just now skipped for the first time...prepare the flags!!!
  287. if (cond > (old + 1))
  288. {
  289. // set the most significant bit to 1 (indicates that there exist skipped states)
  290. // also, ensure that the least significant bit is an 1 (the first step is never skipped, no matter
  291. // what the cond says)
  292. completedStateFlags = 0x80000001;
  293. // since no flag had been skipped until now, the least significant bits must all
  294. // be set to 1, up until "old" number of bits.
  295. completedStateFlags |= ((1 << old) - 1);
  296. // now, just set the bit corresponding to the passed cond to 1 (current step)
  297. completedStateFlags |= (1 << (cond - 1));
  298. set("__compltdStateFlags", String.valueOf(completedStateFlags));
  299. }
  300. }
  301. // case 2: There were exist previously skipped steps
  302. // if this is a push back to a previous step, clear all completion flags ahead
  303. else if (cond < old)
  304. {
  305. // note, this also unsets the flag indicating that there exist skips
  306. completedStateFlags &= ((1 << cond) - 1);
  307. // now, check if this resulted in no steps being skipped any more
  308. if (completedStateFlags == ((1 << cond) - 1))
  309. {
  310. unset("__compltdStateFlags");
  311. }
  312. else
  313. {
  314. // set the most significant bit back to 1 again, to correctly indicate that this skips states.
  315. // also, ensure that the least significant bit is an 1 (the first step is never skipped, no matter
  316. // what the cond says)
  317. completedStateFlags |= 0x80000001;
  318. set("__compltdStateFlags", String.valueOf(completedStateFlags));
  319. }
  320. }
  321. // If this moves forward, it changes nothing on previously skipped steps.
  322. // Just mark this state and we are done.
  323. else
  324. {
  325. completedStateFlags |= (1 << (cond - 1));
  326. set("__compltdStateFlags", String.valueOf(completedStateFlags));
  327. }
  328. // send a packet to the client to inform it of the quest progress (step change)
  329. _player.sendPacket(new QuestList());
  330. final Quest q = getQuest();
  331. if (!q.isCustomQuest() && (cond > 0))
  332. {
  333. _player.sendPacket(new ExShowQuestMark(q.getQuestIntId()));
  334. }
  335. }
  336. /**
  337. * Removes a quest variable from the list of existing quest variables.
  338. * @param var the name of the variable to remove
  339. * @return the previous value of the variable or {@code null} if none were found
  340. */
  341. public String unset(String var)
  342. {
  343. if (_vars == null)
  344. {
  345. return null;
  346. }
  347. String old = _vars.remove(var);
  348. if (old != null)
  349. {
  350. Quest.deleteQuestVarInDb(this, var);
  351. }
  352. return old;
  353. }
  354. /**
  355. * Insert (or update) in the database variables that need to stay persistent for this player after a reboot. This function is for storage of values that are not related to a specific quest but are global instead, i.e. can be used by any script.
  356. * @param var the name of the variable to save
  357. * @param value the value of the variable
  358. */
  359. // TODO: these methods should not be here, they could be used by other classes to save some variables, but they can't because they require to create a QuestState first.
  360. public final void saveGlobalQuestVar(String var, String value)
  361. {
  362. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  363. PreparedStatement statement = con.prepareStatement("REPLACE INTO character_quest_global_data (charId, var, value) VALUES (?, ?, ?)"))
  364. {
  365. statement.setInt(1, _player.getObjectId());
  366. statement.setString(2, var);
  367. statement.setString(3, value);
  368. statement.executeUpdate();
  369. }
  370. catch (Exception e)
  371. {
  372. _log.log(Level.WARNING, "Could not insert player's global quest variable: " + e.getMessage(), e);
  373. }
  374. }
  375. /**
  376. * Read from the database a previously saved variable for this quest.<br>
  377. * Due to performance considerations, this function should best be used only when the quest is first loaded.<br>
  378. * Subclasses of this class can define structures into which these loaded values can be saved.<br>
  379. * However, on-demand usage of this function throughout the script is not prohibited, only not recommended.<br>
  380. * Values read from this function were entered by calls to "saveGlobalQuestVar".
  381. * @param var the name of the variable whose value to get
  382. * @return the value of the variable or an empty string if the variable does not exist in the database
  383. */
  384. // TODO: these methods should not be here, they could be used by other classes to save some variables, but they can't because they require to create a QuestState first.
  385. public final String getGlobalQuestVar(String var)
  386. {
  387. String result = "";
  388. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  389. PreparedStatement ps = con.prepareStatement("SELECT value FROM character_quest_global_data WHERE charId = ? AND var = ?"))
  390. {
  391. ps.setInt(1, _player.getObjectId());
  392. ps.setString(2, var);
  393. try (ResultSet rs = ps.executeQuery())
  394. {
  395. if (rs.first())
  396. {
  397. result = rs.getString(1);
  398. }
  399. }
  400. }
  401. catch (Exception e)
  402. {
  403. _log.log(Level.WARNING, "Could not load player's global quest variable: " + e.getMessage(), e);
  404. }
  405. return result;
  406. }
  407. /**
  408. * Permanently delete a global quest variable from the database.
  409. * @param var the name of the variable to delete
  410. */
  411. public final void deleteGlobalQuestVar(String var)
  412. {
  413. try (Connection con = L2DatabaseFactory.getInstance().getConnection();
  414. PreparedStatement statement = con.prepareStatement("DELETE FROM character_quest_global_data WHERE charId = ? AND var = ?"))
  415. {
  416. statement.setInt(1, _player.getObjectId());
  417. statement.setString(2, var);
  418. statement.executeUpdate();
  419. }
  420. catch (Exception e)
  421. {
  422. _log.log(Level.WARNING, "could not delete player's global quest variable; charId = " + _player.getObjectId() + ", variable name = " + var + ". Exception: " + e.getMessage(), e);
  423. }
  424. }
  425. /**
  426. * @param var the name of the variable to get
  427. * @return the value of the variable from the list of quest variables
  428. */
  429. public String get(String var)
  430. {
  431. if (_vars == null)
  432. {
  433. return null;
  434. }
  435. return _vars.get(var);
  436. }
  437. /**
  438. * @param var the name of the variable to get
  439. * @return the integer value of the variable or 0 if the variable does not exist or its value is not an integer
  440. */
  441. public int getInt(String var)
  442. {
  443. if (_vars == null)
  444. {
  445. return 0;
  446. }
  447. final String variable = _vars.get(var);
  448. if ((variable == null) || (variable.length() == 0))
  449. {
  450. return 0;
  451. }
  452. int varint = 0;
  453. try
  454. {
  455. varint = Integer.parseInt(variable);
  456. }
  457. catch (NumberFormatException nfe)
  458. {
  459. _log.log(Level.INFO, "Quest " + getQuestName() + ", method getInt(" + var + "), tried to parse a non-integer value (" + variable + "). Char ID: " + _player.getObjectId(), nfe);
  460. }
  461. return varint;
  462. }
  463. /**
  464. * Checks if the quest state progress ({@code cond}) is at the specified step.
  465. * @param condition the condition to check against
  466. * @return {@code true} if the quest condition is equal to {@code condition}, {@code false} otherwise
  467. * @see #getInt(String var)
  468. */
  469. public boolean isCond(int condition)
  470. {
  471. return (getInt("cond") == condition);
  472. }
  473. /**
  474. * Sets the quest state progress ({@code cond}) to the specified step.
  475. * @param value the new value of the quest state progress
  476. * @return this {@link QuestState} object
  477. * @see #set(String var, String val)
  478. */
  479. public QuestState setCond(int value)
  480. {
  481. if (isStarted())
  482. {
  483. set("cond", String.valueOf(value));
  484. }
  485. return this;
  486. }
  487. /**
  488. * Sets the quest state progress ({@code cond}) to the specified step.
  489. * @param value the new value of the quest state progress
  490. * @param playQuestMiddle if {@code true}, plays "ItemSound.quest_middle"
  491. * @return this {@link QuestState} object
  492. * @see #set(String var, String val)
  493. * @see #setCond(int value)
  494. */
  495. public QuestState setCond(int value, boolean playQuestMiddle)
  496. {
  497. if (!isStarted())
  498. {
  499. return this;
  500. }
  501. set("cond", String.valueOf(value));
  502. if (playQuestMiddle)
  503. {
  504. playSound("ItemSound.quest_middle");
  505. }
  506. return this;
  507. }
  508. /**
  509. * Add player to get notification of characters death
  510. * @param character the {@link L2Character} object of the character to get notification of death
  511. */
  512. public void addNotifyOfDeath(L2Character character)
  513. {
  514. if ((character == null) || !(character instanceof L2PcInstance))
  515. {
  516. return;
  517. }
  518. ((L2PcInstance) character).addNotifyQuestOfDeath(this);
  519. }
  520. // TODO: This all remains because of backward compatibility, should be cleared when all scripts are rewritten in java
  521. /**
  522. * Return the quantity of one sort of item hold by the player
  523. * @param itemId the ID of the item wanted to be count
  524. * @return long
  525. */
  526. public long getQuestItemsCount(int itemId)
  527. {
  528. return getQuest().getQuestItemsCount(getPlayer(), itemId);
  529. }
  530. /**
  531. * @param itemId the ID of the item required
  532. * @return true if item exists in player's inventory, false - if not
  533. */
  534. public boolean hasQuestItems(int itemId)
  535. {
  536. return getQuest().hasQuestItems(getPlayer(), itemId);
  537. }
  538. /**
  539. * Return the level of enchantment on the weapon of the player(Done specifically for weapon SA's)
  540. * @param itemId : ID of the item to check enchantment
  541. * @return int
  542. */
  543. public int getEnchantLevel(int itemId)
  544. {
  545. return getQuest().getEnchantLevel(getPlayer(), itemId);
  546. }
  547. /**
  548. * Give adena to the player
  549. * @param count
  550. * @param applyRates
  551. */
  552. public void giveAdena(long count, boolean applyRates)
  553. {
  554. giveItems(PcInventory.ADENA_ID, count, applyRates ? 0 : 1);
  555. }
  556. /**
  557. * Give reward to player using multiplier's
  558. * @param itemId
  559. * @param count
  560. */
  561. public void rewardItems(int itemId, long count)
  562. {
  563. getQuest().rewardItems(getPlayer(), itemId, count);
  564. }
  565. /**
  566. * Give item/reward to the player
  567. * @param itemId
  568. * @param count
  569. */
  570. public void giveItems(int itemId, long count)
  571. {
  572. giveItems(itemId, count, 0);
  573. }
  574. public void giveItems(int itemId, long count, int enchantlevel)
  575. {
  576. getQuest().giveItems(getPlayer(), itemId, count, enchantlevel);
  577. }
  578. public void giveItems(int itemId, long count, byte attributeId, int attributeLevel)
  579. {
  580. getQuest().giveItems(getPlayer(), itemId, count, attributeId, attributeLevel);
  581. }
  582. /**
  583. * Drop Quest item using Config.RATE_QUEST_DROP
  584. * @param itemId int Item Identifier of the item to be dropped
  585. * @param count (minCount, maxCount) long Quantity of items to be dropped
  586. * @param neededCount Quantity of items needed for quest
  587. * @param dropChance int Base chance of drop, same as in droplist
  588. * @param sound boolean indicating whether to play sound
  589. * @return boolean indicating whether player has requested number of items
  590. */
  591. public boolean dropQuestItems(int itemId, int count, long neededCount, int dropChance, boolean sound)
  592. {
  593. return dropQuestItems(itemId, count, count, neededCount, dropChance, sound);
  594. }
  595. public boolean dropQuestItems(int itemId, int minCount, int maxCount, long neededCount, int dropChance, boolean sound)
  596. {
  597. return getQuest().dropQuestItems(getPlayer(), itemId, minCount, maxCount, neededCount, dropChance, sound);
  598. }
  599. // TODO: More radar functions need to be added when the radar class is complete.
  600. // BEGIN STUFF THAT WILL PROBABLY BE CHANGED
  601. public void addRadar(int x, int y, int z)
  602. {
  603. _player.getRadar().addMarker(x, y, z);
  604. }
  605. public void removeRadar(int x, int y, int z)
  606. {
  607. _player.getRadar().removeMarker(x, y, z);
  608. }
  609. public void clearRadar()
  610. {
  611. _player.getRadar().removeAllMarkers();
  612. }
  613. // END STUFF THAT WILL PROBABLY BE CHANGED
  614. /**
  615. * Remove items from player's inventory when talking to NPC in order to have rewards.<br>
  616. * Actions:<br>
  617. * <ul>
  618. * <li>Destroy quantity of items wanted</li>
  619. * <li>Send new inventory list to player</li>
  620. * </ul>
  621. * @param itemId : Identifier of the item
  622. * @param count : Quantity of items to destroy
  623. */
  624. public void takeItems(int itemId, long count)
  625. {
  626. getQuest().takeItems(getPlayer(), itemId, count);
  627. }
  628. /**
  629. * Send a packet in order to play sound at client terminal
  630. * @param sound
  631. */
  632. public void playSound(String sound)
  633. {
  634. getQuest().playSound(getPlayer(), sound);
  635. }
  636. /**
  637. * Add XP and SP as quest reward
  638. * @param exp
  639. * @param sp
  640. */
  641. public void addExpAndSp(int exp, int sp)
  642. {
  643. getQuest().addExpAndSp(getPlayer(), exp, sp);
  644. }
  645. /**
  646. * @param loc
  647. * @return number of ticks from GameTimeController
  648. */
  649. public int getItemEquipped(int loc)
  650. {
  651. return getQuest().getItemEquipped(getPlayer(), loc);
  652. }
  653. /**
  654. * Return the number of ticks from the GameTimeController
  655. * @return int
  656. */
  657. public int getGameTicks()
  658. {
  659. return getQuest().getGameTicks();
  660. }
  661. /**
  662. * @return {@code true} if quest is to be exited on clean up by QuestStateManager, {@code false} otherwise
  663. */
  664. public final boolean isExitQuestOnCleanUp()
  665. {
  666. return _isExitQuestOnCleanUp;
  667. }
  668. /**
  669. * @param isExitQuestOnCleanUp {@code true} if quest is to be exited on clean up by QuestStateManager, {@code false} otherwise
  670. */
  671. public void setIsExitQuestOnCleanUp(boolean isExitQuestOnCleanUp)
  672. {
  673. _isExitQuestOnCleanUp = isExitQuestOnCleanUp;
  674. }
  675. /**
  676. * Start a timed event for a quest.<br>
  677. * Will call an event in onEvent/onAdvEvent.
  678. * @param name the name of the timer/event
  679. * @param time time in milliseconds till the event is executed
  680. */
  681. public void startQuestTimer(String name, long time)
  682. {
  683. getQuest().startQuestTimer(name, time, null, getPlayer(), false);
  684. }
  685. /**
  686. * Start a timed event for a quest.<br>
  687. * Will call an event in onEvent/onAdvEvent.
  688. * @param name the name of the timer/event
  689. * @param time time in milliseconds till the event is executed
  690. * @param npc the L2Npc associated with this event
  691. */
  692. public void startQuestTimer(String name, long time, L2Npc npc)
  693. {
  694. getQuest().startQuestTimer(name, time, npc, getPlayer(), false);
  695. }
  696. /**
  697. * Start a repeating timed event for a quest.<br>
  698. * Will call an event in onEvent/onAdvEvent.
  699. * @param name the name of the timer/event
  700. * @param time time in milliseconds till the event is executed/repeated
  701. */
  702. public void startRepeatingQuestTimer(String name, long time)
  703. {
  704. getQuest().startQuestTimer(name, time, null, getPlayer(), true);
  705. }
  706. /**
  707. * Start a repeating timed event for a quest.<br>
  708. * Will call an event in onEvent/onAdvEvent.
  709. * @param name the name of the timer/event
  710. * @param time time in milliseconds till the event is executed/repeated
  711. * @param npc the L2Npc associated with this event
  712. */
  713. public void startRepeatingQuestTimer(String name, long time, L2Npc npc)
  714. {
  715. getQuest().startQuestTimer(name, time, npc, getPlayer(), true);
  716. }
  717. /**
  718. * @param name the name of the QuestTimer required
  719. * @return the {@link QuestTimer} object with the specified name or {@code null} if it doesn't exist
  720. */
  721. public final QuestTimer getQuestTimer(String name)
  722. {
  723. return getQuest().getQuestTimer(name, null, getPlayer());
  724. }
  725. // --- Spawn methods ---
  726. /**
  727. * Add a temporary spawn of the specified npc.<br>
  728. * Player's coordinates will be used for the spawn.
  729. * @param npcId the ID of the npc to spawn
  730. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  731. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  732. */
  733. public L2Npc addSpawn(int npcId)
  734. {
  735. return addSpawn(npcId, _player.getX(), _player.getY(), _player.getZ(), 0, false, 0, false);
  736. }
  737. /**
  738. * Add a temporary spawn of the specified npc.<br>
  739. * Player's coordinates will be used for the spawn.
  740. * @param npcId the ID of the npc to spawn
  741. * @param despawnDelay time in milliseconds till the npc is despawned (default: 0)
  742. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  743. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  744. */
  745. public L2Npc addSpawn(int npcId, int despawnDelay)
  746. {
  747. return addSpawn(npcId, _player.getX(), _player.getY(), _player.getZ(), 0, false, despawnDelay, false);
  748. }
  749. /**
  750. * Add a temporary spawn of the specified npc.
  751. * @param npcId the ID of the npc to spawn
  752. * @param x the X coordinate of the npc spawn location
  753. * @param y the Y coordinate of the npc spawn location
  754. * @param z the Z coordinate (height) of the npc spawn location
  755. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  756. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  757. */
  758. public L2Npc addSpawn(int npcId, int x, int y, int z)
  759. {
  760. return addSpawn(npcId, x, y, z, 0, false, 0, false);
  761. }
  762. /**
  763. * Add a temporary spawn of the specified npc.
  764. * @param npcId the ID of the npc to spawn
  765. * @param x the X coordinate of the npc spawn location
  766. * @param y the Y coordinate of the npc spawn location
  767. * @param z the Z coordinate (height) of the npc spawn location
  768. * @param despawnDelay time in milliseconds till the npc is despawned (default: 0)
  769. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  770. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  771. */
  772. public L2Npc addSpawn(int npcId, int x, int y, int z, int despawnDelay)
  773. {
  774. return addSpawn(npcId, x, y, z, 0, false, despawnDelay, false);
  775. }
  776. /**
  777. * Add a temporary spawn of the specified npc.
  778. * @param npcId the ID of the npc to spawn
  779. * @param cha the character whose coordinates will be used for the npc spawn
  780. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  781. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  782. */
  783. public L2Npc addSpawn(int npcId, L2Character cha)
  784. {
  785. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), true, 0, false);
  786. }
  787. /**
  788. * Add a temporary spawn of the specified npc.
  789. * @param npcId the ID of the npc to spawn
  790. * @param cha the character whose coordinates will be used for the npc spawn
  791. * @param despawnDelay time in milliseconds till the npc is despawned (default: 0)
  792. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  793. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  794. */
  795. public L2Npc addSpawn(int npcId, L2Character cha, int despawnDelay)
  796. {
  797. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), true, despawnDelay, false);
  798. }
  799. /**
  800. * Add a temporary spawn of the specified npc.
  801. * @param npcId the ID of the npc to spawn
  802. * @param cha the character whose coordinates will be used for the npc spawn
  803. * @param randomOffset if {@code true}, adds +/- 50~100 to X/Y coordinates of the spawn location
  804. * @param despawnDelay time in milliseconds till the npc is despawned (default: 0)
  805. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  806. * @see #addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  807. */
  808. public L2Npc addSpawn(int npcId, L2Character cha, boolean randomOffset, int despawnDelay)
  809. {
  810. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), randomOffset, despawnDelay, false);
  811. }
  812. /**
  813. * Add a temporary spawn of the specified npc.
  814. * @param npcId the ID of the npc to spawn
  815. * @param x the X coordinate of the npc spawn location
  816. * @param y the Y coordinate of the npc spawn location
  817. * @param z the Z coordinate (height) of the npc spawn location
  818. * @param heading the heading of the npc
  819. * @param randomOffset if {@code true}, adds +/- 50~100 to X/Y coordinates of the spawn location
  820. * @param despawnDelay time in milliseconds till the npc is despawned (default: 0)
  821. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  822. * @see #addSpawn(int, int, int, int, int, boolean, int, boolean)
  823. */
  824. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay)
  825. {
  826. return addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, false);
  827. }
  828. /**
  829. * Add a temporary spawn of the specified npc.
  830. * @param npcId the ID of the npc to spawn
  831. * @param x the X coordinate of the npc spawn location
  832. * @param y the Y coordinate of the npc spawn location
  833. * @param z the Z coordinate (height) of the npc spawn location
  834. * @param heading the heading of the npc
  835. * @param randomOffset if {@code true}, adds +/- 50~100 to X/Y coordinates of the spawn location
  836. * @param despawnDelay time in milliseconds till the npc is despawned (default: 0)
  837. * @param isSummonSpawn if {@code true}, displays a summon animation on npc spawn (default: {@code false})
  838. * @return the {@link L2Npc} object of the newly spawned npc or {@code null} if the npc doesn't exist
  839. * @see #addSpawn(int)
  840. * @see #addSpawn(int, int)
  841. * @see #addSpawn(int, L2Character)
  842. * @see #addSpawn(int, L2Character, int)
  843. * @see #addSpawn(int, int, int, int)
  844. * @see #addSpawn(int, L2Character, boolean, int)
  845. * @see #addSpawn(int, int, int, int, int)
  846. * @see #addSpawn(int, int, int, int, int, boolean, int)
  847. * @see #addSpawn(int, int, int, int, int, boolean, int, boolean)
  848. */
  849. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  850. {
  851. return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, isSummonSpawn);
  852. }
  853. /**
  854. * @param fileName the name of the file you want to show. Must be in the same folder (or subfolder) as script
  855. * @return a String containing the contents of the specified HTML file
  856. */
  857. public String showHtmlFile(String fileName)
  858. {
  859. return getQuest().showHtmlFile(getPlayer(), fileName);
  860. }
  861. /**
  862. * Set condition to 1, state to STARTED and play the "ItemSound.quest_accept".<br>
  863. * Works only if state is CREATED and the quest is not a custom quest.
  864. * @return the newly created {@code QuestState} object
  865. */
  866. public QuestState startQuest()
  867. {
  868. if (isCreated() && !getQuest().isCustomQuest())
  869. {
  870. set("cond", "1");
  871. setState(State.STARTED);
  872. playSound("ItemSound.quest_accept");
  873. }
  874. return this;
  875. }
  876. /**
  877. * Finishes the quest and removes all quest items associated with this quest from the player's inventory.<br>
  878. * If {@code type} is {@code QuestType.ONE_TIME}, also removes all other quest data associated with this quest.
  879. * @param type the {@link QuestType} of the quest
  880. * @return this {@link QuestState} object
  881. * @see #exitQuest(QuestType type, boolean playExitQuest)
  882. * @see #exitQuest(boolean repeatable)
  883. * @see #exitQuest(boolean repeatable, boolean playExitQuest)
  884. */
  885. public QuestState exitQuest(QuestType type)
  886. {
  887. switch (type)
  888. {
  889. case DAILY:
  890. {
  891. exitQuest(false);
  892. setRestartTime();
  893. break;
  894. }
  895. // case ONE_TIME:
  896. // case REPEATABLE:
  897. default:
  898. {
  899. exitQuest(type == QuestType.REPEATABLE);
  900. break;
  901. }
  902. }
  903. return this;
  904. }
  905. /**
  906. * Finishes the quest and removes all quest items associated with this quest from the player's inventory.<br>
  907. * If {@code type} is {@code QuestType.ONE_TIME}, also removes all other quest data associated with this quest.
  908. * @param type the {@link QuestType} of the quest
  909. * @param playExitQuest if {@code true}, plays "ItemSound.quest_finish"
  910. * @return this {@link QuestState} object
  911. * @see #exitQuest(QuestType type)
  912. * @see #exitQuest(boolean repeatable)
  913. * @see #exitQuest(boolean repeatable, boolean playExitQuest)
  914. */
  915. public QuestState exitQuest(QuestType type, boolean playExitQuest)
  916. {
  917. exitQuest(type);
  918. if (playExitQuest)
  919. {
  920. playSound("ItemSound.quest_finish");
  921. }
  922. return this;
  923. }
  924. /**
  925. * Finishes the quest and removes all quest items associated with this quest from the player's inventory.<br>
  926. * If {@code repeatable} is set to {@code false}, also removes all other quest data associated with this quest.
  927. * @param repeatable if {@code true}, deletes all data and variables of this quest, otherwise keeps them
  928. * @return this {@link QuestState} object
  929. * @see #exitQuest(QuestType type)
  930. * @see #exitQuest(QuestType type, boolean playExitQuest)
  931. * @see #exitQuest(boolean repeatable, boolean playExitQuest)
  932. */
  933. public QuestState exitQuest(boolean repeatable)
  934. {
  935. _player.removeNotifyQuestOfDeath(this);
  936. if (!isStarted())
  937. {
  938. return this;
  939. }
  940. setState(State.COMPLETED);
  941. // Clean registered quest items
  942. int[] itemIdList = getQuest().getRegisteredItemIds();
  943. if (itemIdList != null)
  944. {
  945. for (int element : itemIdList)
  946. {
  947. takeItems(element, -1);
  948. }
  949. }
  950. Quest.deleteQuestInDb(this, repeatable);
  951. if (repeatable)
  952. {
  953. _player.delQuestState(getQuestName());
  954. }
  955. else
  956. {
  957. Quest.updateQuestInDb(this);
  958. }
  959. _vars = null;
  960. return this;
  961. }
  962. /**
  963. * Finishes the quest and removes all quest items associated with this quest from the player's inventory.<br>
  964. * If {@code repeatable} is set to {@code false}, also removes all other quest data associated with this quest.
  965. * @param repeatable if {@code true}, deletes all data and variables of this quest, otherwise keeps them
  966. * @param playExitQuest if {@code true}, plays "ItemSound.quest_finish"
  967. * @return this {@link QuestState} object
  968. * @see #exitQuest(QuestType type)
  969. * @see #exitQuest(QuestType type, boolean playExitQuest)
  970. * @see #exitQuest(boolean repeatable)
  971. */
  972. public QuestState exitQuest(boolean repeatable, boolean playExitQuest)
  973. {
  974. exitQuest(repeatable);
  975. if (playExitQuest)
  976. {
  977. playSound("ItemSound.quest_finish");
  978. }
  979. return this;
  980. }
  981. public void showQuestionMark(int number)
  982. {
  983. _player.sendPacket(new TutorialShowQuestionMark(number));
  984. }
  985. public void playTutorialVoice(String voice)
  986. {
  987. _player.sendPacket(new PlaySound(2, voice, 0, 0, _player.getX(), _player.getY(), _player.getZ()));
  988. }
  989. public void showTutorialHTML(String html)
  990. {
  991. String text = HtmCache.getInstance().getHtm(_player.getHtmlPrefix(), "data/scripts/quests/255_Tutorial/" + html);
  992. if (text == null)
  993. {
  994. _log.warning("missing html page data/scripts/quests/255_Tutorial/" + html);
  995. text = "<html><body>File data/scripts/quests/255_Tutorial/" + html + " not found or file is empty.</body></html>";
  996. }
  997. _player.sendPacket(new TutorialShowHtml(text));
  998. }
  999. public void closeTutorialHtml()
  1000. {
  1001. _player.sendPacket(TutorialCloseHtml.STATIC_PACKET);
  1002. }
  1003. public void onTutorialClientEvent(int number)
  1004. {
  1005. _player.sendPacket(new TutorialEnableClientEvent(number));
  1006. }
  1007. public void dropItem(L2MonsterInstance npc, L2PcInstance player, int itemId, int count)
  1008. {
  1009. npc.dropItem(player, itemId, count);
  1010. }
  1011. /**
  1012. * Set the restart time for the daily quests.<br>
  1013. * The time is hardcoded at {@link Quest#getResetHour()} hours, {@link Quest#getResetMinutes()} minutes of the following day.<br>
  1014. * It can be overridden in scripts (quests).
  1015. */
  1016. public void setRestartTime()
  1017. {
  1018. final Calendar reDo = Calendar.getInstance();
  1019. if (reDo.get(Calendar.HOUR_OF_DAY) >= getQuest().getResetHour())
  1020. {
  1021. reDo.add(Calendar.DATE, 1);
  1022. }
  1023. reDo.set(Calendar.HOUR_OF_DAY, getQuest().getResetHour());
  1024. reDo.set(Calendar.MINUTE, getQuest().getResetMinutes());
  1025. set("restartTime", String.valueOf(reDo.getTimeInMillis()));
  1026. }
  1027. /**
  1028. * Check if a daily quest is available to be started over.
  1029. * @return {@code true} if the quest is available, {@code false} otherwise.
  1030. */
  1031. public boolean isNowAvailable()
  1032. {
  1033. final String val = get("restartTime");
  1034. return ((val == null) || !Util.isDigit(val)) || (Long.parseLong(val) <= System.currentTimeMillis());
  1035. }
  1036. }