QuestState.java 31 KB

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