QuestState.java 35 KB

1234567891011121314151617181920212223242526272829303132333435363738394041424344454647484950515253545556575859606162636465666768697071727374757677787980818283848586878889909192939495969798991001011021031041051061071081091101111121131141151161171181191201211221231241251261271281291301311321331341351361371381391401411421431441451461471481491501511521531541551561571581591601611621631641651661671681691701711721731741751761771781791801811821831841851861871881891901911921931941951961971981992002012022032042052062072082092102112122132142152162172182192202212222232242252262272282292302312322332342352362372382392402412422432442452462472482492502512522532542552562572582592602612622632642652662672682692702712722732742752762772782792802812822832842852862872882892902912922932942952962972982993003013023033043053063073083093103113123133143153163173183193203213223233243253263273283293303313323333343353363373383393403413423433443453463473483493503513523533543553563573583593603613623633643653663673683693703713723733743753763773783793803813823833843853863873883893903913923933943953963973983994004014024034044054064074084094104114124134144154164174184194204214224234244254264274284294304314324334344354364374384394404414424434444454464474484494504514524534544554564574584594604614624634644654664674684694704714724734744754764774784794804814824834844854864874884894904914924934944954964974984995005015025035045055065075085095105115125135145155165175185195205215225235245255265275285295305315325335345355365375385395405415425435445455465475485495505515525535545555565575585595605615625635645655665675685695705715725735745755765775785795805815825835845855865875885895905915925935945955965975985996006016026036046056066076086096106116126136146156166176186196206216226236246256266276286296306316326336346356366376386396406416426436446456466476486496506516526536546556566576586596606616626636646656666676686696706716726736746756766776786796806816826836846856866876886896906916926936946956966976986997007017027037047057067077087097107117127137147157167177187197207217227237247257267277287297307317327337347357367377387397407417427437447457467477487497507517527537547557567577587597607617627637647657667677687697707717727737747757767777787797807817827837847857867877887897907917927937947957967977987998008018028038048058068078088098108118128138148158168178188198208218228238248258268278288298308318328338348358368378388398408418428438448458468478488498508518528538548558568578588598608618628638648658668678688698708718728738748758768778788798808818828838848858868878888898908918928938948958968978988999009019029039049059069079089099109119129139149159169179189199209219229239249259269279289299309319329339349359369379389399409419429439449459469479489499509519529539549559569579589599609619629639649659669679689699709719729739749759769779789799809819829839849859869879889899909919929939949959969979989991000100110021003100410051006100710081009101010111012101310141015101610171018101910201021102210231024102510261027102810291030103110321033103410351036103710381039104010411042104310441045104610471048104910501051105210531054105510561057105810591060106110621063106410651066106710681069107010711072107310741075107610771078107910801081108210831084108510861087108810891090109110921093109410951096109710981099110011011102110311041105110611071108110911101111111211131114111511161117111811191120112111221123112411251126112711281129113011311132113311341135113611371138113911401141114211431144114511461147114811491150115111521153115411551156115711581159116011611162
  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.finer(getPlayer().getName() + ", " + getQuestName() + " cond [" + val + "] is not an integer. Value stored, but no packet was sent: " + 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);
  365. }
  366. finally
  367. {
  368. try
  369. {
  370. con.close();
  371. }
  372. catch (Exception e)
  373. {
  374. e.printStackTrace();
  375. }
  376. }
  377. }
  378. /**
  379. * Read from the database a previously saved variable for this quest.
  380. * Due to performance considerations, this function should best be used only when the quest is first loaded.
  381. * Subclasses of this class can define structures into which these loaded values can be saved.
  382. * However, on-demand usage of this function throughout the script is not prohibited, only not recommended.
  383. * Values read from this function were entered by calls to "saveGlobalQuestVar"
  384. * @param var : String designating the name of the variable for the quest
  385. * @return String : String representing the loaded value for the passed var, or an empty string if the var was invalid
  386. */
  387. public final String getGlobalQuestVar(String var)
  388. {
  389. String result = "";
  390. Connection con = null;
  391. try
  392. {
  393. con = L2DatabaseFactory.getInstance().getConnection();
  394. PreparedStatement statement;
  395. statement = con.prepareStatement("SELECT value FROM character_quest_global_data WHERE charId = ? AND var = ?");
  396. statement.setInt(1, _player.getObjectId());
  397. statement.setString(2, var);
  398. ResultSet rs = statement.executeQuery();
  399. if (rs.first())
  400. result = rs.getString(1);
  401. rs.close();
  402. statement.close();
  403. }
  404. catch (Exception e)
  405. {
  406. _log.log(Level.WARNING, "could not load player's global quest variable:", e);
  407. }
  408. finally
  409. {
  410. try
  411. {
  412. con.close();
  413. }
  414. catch (Exception e)
  415. {
  416. e.printStackTrace();
  417. }
  418. }
  419. return result;
  420. }
  421. /**
  422. * Permanently delete from the database one of the player's global quest variable that was previously saved.
  423. * @param var : String designating the name of the variable
  424. */
  425. public final void deleteGlobalQuestVar(String var)
  426. {
  427. Connection con = null;
  428. try
  429. {
  430. con = L2DatabaseFactory.getInstance().getConnection();
  431. PreparedStatement statement;
  432. statement = con.prepareStatement("DELETE FROM character_quest_global_data WHERE charId = ? AND var = ?");
  433. statement.setInt(1, _player.getObjectId());
  434. statement.setString(2, var);
  435. statement.executeUpdate();
  436. statement.close();
  437. }
  438. catch (Exception e)
  439. {
  440. _log.log(Level.WARNING, "could not delete player's global quest variable:", e);
  441. }
  442. finally
  443. {
  444. try
  445. {
  446. con.close();
  447. }
  448. catch (Exception e)
  449. {
  450. e.printStackTrace();
  451. }
  452. }
  453. }
  454. /**
  455. * Return the value of the variable of quest represented by "var"
  456. * @param var : name of the variable of quest
  457. * @return Object
  458. */
  459. public Object get(String var)
  460. {
  461. if (_vars == null)
  462. return null;
  463. return _vars.get(var);
  464. }
  465. /**
  466. * Return the value of the variable of quest represented by "var"
  467. * @param var : String designating the variable for the quest
  468. * @return int
  469. */
  470. public int getInt(String var)
  471. {
  472. int varint = 0;
  473. try
  474. {
  475. varint = Integer.parseInt(_vars.get(var));
  476. }
  477. catch (Exception e)
  478. {
  479. _log.finer(getPlayer().getName() + ": variable " + var + " isn't an integer: " + varint + e);
  480. // if (Config.AUTODELETE_INVALID_QUEST_DATA)
  481. // exitQuest(true);
  482. }
  483. return varint;
  484. }
  485. /**
  486. * Add player to get notification of characters death
  487. * @param character : L2Character of the character to get notification of death
  488. */
  489. public void addNotifyOfDeath(L2Character character)
  490. {
  491. if (character == null || !(character instanceof L2PcInstance))
  492. return;
  493. ((L2PcInstance)character).addNotifyQuestOfDeath(this);
  494. }
  495. /**
  496. * Return the quantity of one sort of item hold by the player
  497. * @param itemId : ID of the item wanted to be count
  498. * @return long
  499. */
  500. public long getQuestItemsCount(int itemId)
  501. {
  502. long count = 0;
  503. for (L2ItemInstance item : getPlayer().getInventory().getItems())
  504. if (item != null && item.getItemId() == itemId)
  505. count += item.getCount();
  506. return count;
  507. }
  508. /**
  509. * Return the level of enchantment on the weapon of the player(Done specifically for weapon SA's)
  510. * @param itemId : ID of the item to check enchantment
  511. * @return int
  512. */
  513. public int getEnchantLevel(int itemId)
  514. {
  515. L2ItemInstance enchanteditem = getPlayer().getInventory().getItemByItemId(itemId);
  516. if (enchanteditem == null)
  517. return 0;
  518. return enchanteditem.getEnchantLevel();
  519. }
  520. /**
  521. * Give adena to the player
  522. * @param count
  523. * @param applyRates
  524. */
  525. public void giveAdena(long count, boolean applyRates)
  526. {
  527. giveItems(57, count, applyRates ? 0 : 1);
  528. }
  529. /**
  530. * Give reward to player using multiplier's
  531. * @param itemId
  532. * @param count
  533. */
  534. public void rewardItems(int itemId, long count)
  535. {
  536. if (count <= 0)
  537. return;
  538. L2ItemInstance _tmpItem = ItemTable.getInstance().createDummyItem(itemId);
  539. if (_tmpItem == null)
  540. return;
  541. if (itemId == 57)
  542. {
  543. count = (long) (count * Config.RATE_QUEST_REWARD_ADENA);
  544. }
  545. else if (Config.RATE_QUEST_REWARD_USE_MULTIPLIERS)
  546. {
  547. if(_tmpItem.isEtcItem())
  548. {
  549. L2EtcItemType _type = _tmpItem.getEtcItem().getItemType();
  550. if (_type == L2EtcItemType.POTION)
  551. count = (long) (count * Config.RATE_QUEST_REWARD_POTION);
  552. else if (_type == L2EtcItemType.SCROLL)
  553. count = (long) (count * Config.RATE_QUEST_REWARD_SCROLL);
  554. else if (_type == L2EtcItemType.RECEIPE)
  555. count = (long) (count * Config.RATE_QUEST_REWARD_RECIPE);
  556. else if (_type == L2EtcItemType.MATERIAL)
  557. count = (long) (count * Config.RATE_QUEST_REWARD_MATERIAL);
  558. else
  559. count = (long) (count * Config.RATE_QUEST_REWARD);
  560. }
  561. }
  562. else
  563. {
  564. count = (long) (count * Config.RATE_QUEST_REWARD);
  565. }
  566. // Add items to player's inventory
  567. L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
  568. if (item == null)
  569. return;
  570. // If item for reward is gold, send message of gold reward to client
  571. if (itemId == 57)
  572. {
  573. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ADENA);
  574. smsg.addItemNumber(count);
  575. getPlayer().sendPacket(smsg);
  576. }
  577. // Otherwise, send message of object reward to client
  578. else
  579. {
  580. if (count > 1)
  581. {
  582. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
  583. smsg.addItemName(item);
  584. smsg.addItemNumber(count);
  585. getPlayer().sendPacket(smsg);
  586. }
  587. else
  588. {
  589. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ITEM);
  590. smsg.addItemName(item);
  591. getPlayer().sendPacket(smsg);
  592. }
  593. }
  594. // send packets
  595. StatusUpdate su = new StatusUpdate(getPlayer().getObjectId());
  596. su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
  597. getPlayer().sendPacket(su);
  598. }
  599. /**
  600. * Give item/reward to the player
  601. * @param itemId
  602. * @param count
  603. */
  604. public void giveItems(int itemId, long count)
  605. {
  606. giveItems(itemId, count, 0);
  607. }
  608. public void giveItems(int itemId, long count, int enchantlevel)
  609. {
  610. if (count <= 0)
  611. return;
  612. // If item for reward is adena (ID=57), modify count with rate for quest reward if rates available
  613. if (itemId == 57 && !(enchantlevel > 0))
  614. count = (long) (count * Config.RATE_QUEST_REWARD_ADENA);
  615. // Add items to player's inventory
  616. L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
  617. if (item == null)
  618. return;
  619. // set enchant level for item if that item is not adena
  620. if (enchantlevel > 0 && itemId != 57)
  621. item.setEnchantLevel(enchantlevel);
  622. // If item for reward is gold, send message of gold reward to client
  623. if (itemId == 57)
  624. {
  625. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ADENA);
  626. smsg.addItemNumber(count);
  627. getPlayer().sendPacket(smsg);
  628. }
  629. // Otherwise, send message of object reward to client
  630. else
  631. {
  632. if (count > 1)
  633. {
  634. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
  635. smsg.addItemName(item);
  636. smsg.addItemNumber(count);
  637. getPlayer().sendPacket(smsg);
  638. }
  639. else
  640. {
  641. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ITEM);
  642. smsg.addItemName(item);
  643. getPlayer().sendPacket(smsg);
  644. }
  645. }
  646. // send packets
  647. StatusUpdate su = new StatusUpdate(getPlayer().getObjectId());
  648. su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
  649. getPlayer().sendPacket(su);
  650. }
  651. public void giveItems(int itemId, long count, byte attributeId, int attributeLevel)
  652. {
  653. if (count <= 0)
  654. return;
  655. // Add items to player's inventory
  656. L2ItemInstance item = getPlayer().getInventory().addItem("Quest", itemId, count, getPlayer(), getPlayer().getTarget());
  657. if (item == null)
  658. return;
  659. // set enchant level for item if that item is not adena
  660. if (attributeId >= 0 && attributeLevel > 0)
  661. {
  662. item.setElementAttr(attributeId, attributeLevel);
  663. if (item.isEquipped())
  664. item.updateElementAttrBonus(getPlayer());
  665. InventoryUpdate iu = new InventoryUpdate();
  666. iu.addModifiedItem(item);
  667. getPlayer().sendPacket(iu);
  668. }
  669. // If item for reward is gold, send message of gold reward to client
  670. if (itemId == 57)
  671. {
  672. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ADENA);
  673. smsg.addItemNumber(count);
  674. getPlayer().sendPacket(smsg);
  675. }
  676. // Otherwise, send message of object reward to client
  677. else
  678. {
  679. if (count > 1)
  680. {
  681. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_S2_S1_S);
  682. smsg.addItemName(item);
  683. smsg.addItemNumber(count);
  684. getPlayer().sendPacket(smsg);
  685. }
  686. else
  687. {
  688. SystemMessage smsg = new SystemMessage(SystemMessageId.EARNED_ITEM);
  689. smsg.addItemName(item);
  690. getPlayer().sendPacket(smsg);
  691. }
  692. }
  693. // send packets
  694. StatusUpdate su = new StatusUpdate(getPlayer().getObjectId());
  695. su.addAttribute(StatusUpdate.CUR_LOAD, getPlayer().getCurrentLoad());
  696. getPlayer().sendPacket(su);
  697. }
  698. /**
  699. * Drop Quest item using Config.RATE_QUEST_DROP
  700. * @param itemId : int Item Identifier of the item to be dropped
  701. * @param count(minCount, maxCount) : long Quantity of items to be dropped
  702. * @param neededCount : Quantity of items needed for quest
  703. * @param dropChance : int Base chance of drop, same as in droplist
  704. * @param sound : boolean indicating whether to play sound
  705. * @return boolean indicating whether player has requested number of items
  706. */
  707. public boolean dropQuestItems(int itemId, int count, long neededCount, int dropChance, boolean sound)
  708. {
  709. return dropQuestItems(itemId, count, count, neededCount, dropChance, sound);
  710. }
  711. public boolean dropQuestItems(int itemId, int minCount, int maxCount, long neededCount, int dropChance, boolean sound)
  712. {
  713. dropChance *= Config.RATE_QUEST_DROP / ((getPlayer().getParty() != null) ? getPlayer().getParty().getMemberCount() : 1);
  714. long currentCount = getQuestItemsCount(itemId);
  715. if (neededCount > 0 && currentCount >= neededCount)
  716. return true;
  717. if (currentCount >= neededCount)
  718. return true;
  719. long itemCount = 0;
  720. int random = Rnd.get(L2DropData.MAX_CHANCE);
  721. while (random < dropChance)
  722. {
  723. // Get the item quantity dropped
  724. if (minCount < maxCount)
  725. itemCount += Rnd.get(minCount, maxCount);
  726. else if (minCount == maxCount)
  727. itemCount += minCount;
  728. else
  729. itemCount++;
  730. // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
  731. dropChance -= L2DropData.MAX_CHANCE;
  732. }
  733. if (itemCount > 0)
  734. {
  735. // if over neededCount, just fill the gap
  736. if (neededCount > 0 && currentCount + itemCount > neededCount)
  737. itemCount = neededCount - currentCount;
  738. // Inventory slot check
  739. if (!getPlayer().getInventory().validateCapacityByItemId(itemId))
  740. return false;
  741. // Give the item to Player
  742. getPlayer().addItem("Quest", itemId, itemCount, getPlayer().getTarget(), true);
  743. if (sound)
  744. playSound((currentCount + itemCount < neededCount) ? "Itemsound.quest_itemget" : "Itemsound.quest_middle");
  745. }
  746. return (neededCount > 0 && currentCount + itemCount >= neededCount);
  747. }
  748. //TODO: More radar functions need to be added when the radar class is complete.
  749. // BEGIN STUFF THAT WILL PROBABLY BE CHANGED
  750. public void addRadar(int x, int y, int z)
  751. {
  752. getPlayer().getRadar().addMarker(x, y, z);
  753. }
  754. public void removeRadar(int x, int y, int z)
  755. {
  756. getPlayer().getRadar().removeMarker(x, y, z);
  757. }
  758. public void clearRadar()
  759. {
  760. getPlayer().getRadar().removeAllMarkers();
  761. }
  762. // END STUFF THAT WILL PROBABLY BE CHANGED
  763. /**
  764. * Remove items from player's inventory when talking to NPC in order to have rewards.<BR><BR>
  765. * <U><I>Actions :</I></U>
  766. * <LI>Destroy quantity of items wanted</LI>
  767. * <LI>Send new inventory list to player</LI>
  768. * @param itemId : Identifier of the item
  769. * @param count : Quantity of items to destroy
  770. */
  771. public void takeItems(int itemId, long count)
  772. {
  773. // Get object item from player's inventory list
  774. L2ItemInstance item = getPlayer().getInventory().getItemByItemId(itemId);
  775. if (item == null)
  776. return;
  777. // Tests on count value in order not to have negative value
  778. if (count < 0 || count > item.getCount())
  779. count = item.getCount();
  780. // Destroy the quantity of items wanted
  781. if (itemId == 57)
  782. getPlayer().reduceAdena("Quest", count, getPlayer(), true);
  783. else
  784. {
  785. if (item.isEquipped())
  786. {
  787. L2ItemInstance[] unequiped = getPlayer().getInventory().unEquipItemInBodySlotAndRecord(item.getItem().getBodyPart());
  788. InventoryUpdate iu = new InventoryUpdate();
  789. for (L2ItemInstance itm: unequiped)
  790. iu.addModifiedItem(itm);
  791. getPlayer().sendPacket(iu);
  792. getPlayer().broadcastUserInfo();
  793. }
  794. getPlayer().destroyItemByItemId("Quest", itemId, count, getPlayer(), true);
  795. }
  796. }
  797. /**
  798. * Send a packet in order to play sound at client terminal
  799. * @param sound
  800. */
  801. public void playSound(String sound)
  802. {
  803. getPlayer().sendPacket(new PlaySound(sound));
  804. }
  805. /**
  806. * Add XP and SP as quest reward
  807. * @param exp
  808. * @param sp
  809. */
  810. public void addExpAndSp(int exp, int sp)
  811. {
  812. 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));
  813. }
  814. /**
  815. * Return random value
  816. * @param max : max value for randomisation
  817. * @return int
  818. */
  819. public int getRandom(int max)
  820. {
  821. return Rnd.get(max);
  822. }
  823. /**
  824. * Return number of ticks from GameTimeController
  825. * @return int
  826. */
  827. public int getItemEquipped(int loc)
  828. {
  829. return getPlayer().getInventory().getPaperdollItemId(loc);
  830. }
  831. /**
  832. * Return the number of ticks from the GameTimeController
  833. * @return int
  834. */
  835. public int getGameTicks()
  836. {
  837. return GameTimeController.getGameTicks();
  838. }
  839. /**
  840. * Return true if quest is to exited on clean up by QuestStateManager
  841. * @return boolean
  842. */
  843. public final boolean isExitQuestOnCleanUp()
  844. {
  845. return _isExitQuestOnCleanUp;
  846. }
  847. /**
  848. * Return the QuestTimer object with the specified name
  849. * @return QuestTimer<BR> Return null if name does not exist
  850. */
  851. public void setIsExitQuestOnCleanUp(boolean isExitQuestOnCleanUp)
  852. {
  853. _isExitQuestOnCleanUp = isExitQuestOnCleanUp;
  854. }
  855. /**
  856. * Start a timer for quest.<BR><BR>
  857. * @param name<BR> The name of the timer. Will also be the value for event of onEvent
  858. * @param time<BR> The milisecond value the timer will elapse
  859. */
  860. public void startQuestTimer(String name, long time)
  861. {
  862. getQuest().startQuestTimer(name, time, null, getPlayer(), false);
  863. }
  864. public void startQuestTimer(String name, long time, L2Npc npc)
  865. {
  866. getQuest().startQuestTimer(name, time, npc, getPlayer(), false);
  867. }
  868. public void startRepeatingQuestTimer(String name, long time)
  869. {
  870. getQuest().startQuestTimer(name, time, null, getPlayer(), true);
  871. }
  872. public void startRepeatingQuestTimer(String name, long time, L2Npc npc)
  873. {
  874. getQuest().startQuestTimer(name, time, npc, getPlayer(), true);
  875. }
  876. /**
  877. * Return the QuestTimer object with the specified name
  878. * @return QuestTimer<BR> Return null if name does not exist
  879. */
  880. public final QuestTimer getQuestTimer(String name)
  881. {
  882. return getQuest().getQuestTimer(name, null, getPlayer());
  883. }
  884. /**
  885. * Add spawn for player instance
  886. * Return object id of newly spawned npc
  887. */
  888. public L2Npc addSpawn(int npcId)
  889. {
  890. return addSpawn(npcId, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ(), 0, false, 0);
  891. }
  892. public L2Npc addSpawn(int npcId, int despawnDelay)
  893. {
  894. return addSpawn(npcId, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ(), 0, false, despawnDelay);
  895. }
  896. public L2Npc addSpawn(int npcId, int x, int y, int z)
  897. {
  898. return addSpawn(npcId, x, y, z, 0, false, 0);
  899. }
  900. /**
  901. * Add spawn for player instance
  902. * Will despawn after the spawn length expires
  903. * Uses player's coords and heading.
  904. * Adds a little randomization in the x y coords
  905. * Return object id of newly spawned npc
  906. */
  907. public L2Npc addSpawn(int npcId, L2Character cha)
  908. {
  909. return addSpawn(npcId, cha, true, 0);
  910. }
  911. public L2Npc addSpawn(int npcId, L2Character cha, int despawnDelay)
  912. {
  913. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), true, despawnDelay);
  914. }
  915. /**
  916. * Add spawn for player instance
  917. * Will despawn after the spawn length expires
  918. * Return object id of newly spawned npc
  919. */
  920. public L2Npc addSpawn(int npcId, int x, int y, int z, int despawnDelay)
  921. {
  922. return addSpawn(npcId, x, y, z, 0, false, despawnDelay);
  923. }
  924. /**
  925. * Add spawn for player instance
  926. * Inherits coords and heading from specified L2Character instance.
  927. * It could be either the player, or any killed/attacked mob
  928. * Return object id of newly spawned npc
  929. */
  930. public L2Npc addSpawn(int npcId, L2Character cha, boolean randomOffset, int despawnDelay)
  931. {
  932. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), randomOffset, despawnDelay);
  933. }
  934. /**
  935. * Add spawn for player instance
  936. * Return object id of newly spawned npc
  937. */
  938. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay)
  939. {
  940. return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, false);
  941. }
  942. /**
  943. * Add spawn for player instance
  944. * Return object id of newly spawned npc
  945. */
  946. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, int despawnDelay, boolean isSummonSpawn)
  947. {
  948. return getQuest().addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, isSummonSpawn);
  949. }
  950. public String showHtmlFile(String fileName)
  951. {
  952. return getQuest().showHtmlFile(getPlayer(), fileName);
  953. }
  954. /**
  955. * Destroy element used by quest when quest is exited
  956. * @param repeatable
  957. * @return QuestState
  958. */
  959. public QuestState exitQuest(boolean repeatable)
  960. {
  961. // remove this quest from the notifyDeath list of this character if its on this list
  962. _player.removeNotifyQuestOfDeath(this);
  963. if (isCompleted() || isCreated())
  964. return this;
  965. // Say quest is completed
  966. setState(State.COMPLETED);
  967. // Clean registered quest items
  968. int[] itemIdList = getQuest().getRegisteredItemIds();
  969. if (itemIdList != null)
  970. {
  971. for (int i = 0; i < itemIdList.length; i++)
  972. takeItems(itemIdList[i], -1);
  973. }
  974. // If quest is repeatable, delete quest from list of quest of the player and from database (quest CAN be created again => repeatable)
  975. if (repeatable)
  976. {
  977. getPlayer().delQuestState(getQuestName());
  978. Quest.deleteQuestInDb(this);
  979. _vars = null;
  980. }
  981. else
  982. {
  983. // Otherwise, delete variables for quest and update database (quest CANNOT be created again => not repeatable)
  984. if (_vars != null)
  985. {
  986. for (String var : _vars.keySet())
  987. unset(var);
  988. }
  989. Quest.updateQuestInDb(this);
  990. }
  991. return this;
  992. }
  993. public void showQuestionMark(int number)
  994. {
  995. getPlayer().sendPacket(new TutorialShowQuestionMark(number));
  996. }
  997. public void playTutorialVoice(String voice)
  998. {
  999. getPlayer().sendPacket(new PlaySound(2, voice, 0, 0, getPlayer().getX(), getPlayer().getY(), getPlayer().getZ()));
  1000. }
  1001. public void showTutorialHTML(String html)
  1002. {
  1003. String text = HtmCache.getInstance().getHtm(getPlayer().getHtmlPrefix(), "data/scripts/quests/255_Tutorial/" + html);
  1004. if (text == null)
  1005. {
  1006. _log.warning("missing html page data/scripts/quests/255_Tutorial/" + html);
  1007. text = "<html><body>File data/scripts/quests/255_Tutorial/" + html + " not found or file is empty.</body></html>";
  1008. }
  1009. getPlayer().sendPacket(new TutorialShowHtml(text));
  1010. }
  1011. public void closeTutorialHtml()
  1012. {
  1013. getPlayer().sendPacket(new TutorialCloseHtml());
  1014. }
  1015. public void onTutorialClientEvent(int number)
  1016. {
  1017. getPlayer().sendPacket(new TutorialEnableClientEvent(number));
  1018. }
  1019. public void dropItem(L2MonsterInstance npc, L2PcInstance player, int itemId, int count)
  1020. {
  1021. npc.dropItem(player, itemId, count);
  1022. }
  1023. }