QuestState.java 35 KB

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