QuestState.java 36 KB

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