Quest.java 71 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552553554555556557558559560561562563564565566567568569570571572573574575576577578579580581582583584585586587588589590591592593594595596597598599600601602603604605606607608609610611612613614615616617618619620621622623624625626627628629630631632633634635636637638639640641642643644645646647648649650651652653654655656657658659660661662663664665666667668669670671672673674675676677678679680681682683684685686687688689690691692693694695696697698699700701702703704705706707708709710711712713714715716717718719720721722723724725726727728729730731732733734735736737738739740741742743744745746747748749750751752753754755756757758759760761762763764765766767768769770771772773774775776777778779780781782783784785786787788789790791792793794795796797798799800801802803804805806807808809810811812813814815816817818819820821822823824825826827828829830831832833834835836837838839840841842843844845846847848849850851852853854855856857858859860861862863864865866867868869870871872873874875876877878879880881882883884885886887888889890891892893894895896897898899900901902903904905906907908909910911912913914915916917918919920921922923924925926927928929930931932933934935936937938939940941942943944945946947948949950951952953954955956957958959960961962963964965966967968969970971972973974975976977978979980981982983984985986987988989990991992993994995996997998999100010011002100310041005100610071008100910101011101210131014101510161017101810191020102110221023102410251026102710281029103010311032103310341035103610371038103910401041104210431044104510461047104810491050105110521053105410551056105710581059106010611062106310641065106610671068106910701071107210731074107510761077107810791080108110821083108410851086108710881089109010911092109310941095109610971098109911001101110211031104110511061107110811091110111111121113111411151116111711181119112011211122112311241125112611271128112911301131113211331134113511361137113811391140114111421143114411451146114711481149115011511152115311541155115611571158115911601161116211631164116511661167116811691170117111721173117411751176117711781179118011811182118311841185118611871188118911901191119211931194119511961197119811991200120112021203120412051206120712081209121012111212121312141215121612171218121912201221122212231224122512261227122812291230123112321233123412351236123712381239124012411242124312441245124612471248124912501251125212531254125512561257125812591260126112621263126412651266126712681269127012711272127312741275127612771278127912801281128212831284128512861287128812891290129112921293129412951296129712981299130013011302130313041305130613071308130913101311131213131314131513161317131813191320132113221323132413251326132713281329133013311332133313341335133613371338133913401341134213431344134513461347134813491350135113521353135413551356135713581359136013611362136313641365136613671368136913701371137213731374137513761377137813791380138113821383138413851386138713881389139013911392139313941395139613971398139914001401140214031404140514061407140814091410141114121413141414151416141714181419142014211422142314241425142614271428142914301431143214331434143514361437143814391440144114421443144414451446144714481449145014511452145314541455145614571458145914601461146214631464146514661467146814691470147114721473147414751476147714781479148014811482148314841485148614871488148914901491149214931494149514961497149814991500150115021503150415051506150715081509151015111512151315141515151615171518151915201521152215231524152515261527152815291530153115321533153415351536153715381539154015411542154315441545154615471548154915501551155215531554155515561557155815591560156115621563156415651566156715681569157015711572157315741575157615771578157915801581158215831584158515861587158815891590159115921593159415951596159715981599160016011602160316041605160616071608160916101611161216131614161516161617161816191620162116221623162416251626162716281629163016311632163316341635163616371638163916401641164216431644164516461647164816491650165116521653165416551656165716581659166016611662166316641665166616671668166916701671167216731674167516761677167816791680168116821683168416851686168716881689169016911692169316941695169616971698169917001701170217031704170517061707170817091710171117121713171417151716171717181719172017211722172317241725172617271728172917301731173217331734173517361737173817391740174117421743174417451746174717481749175017511752175317541755175617571758175917601761176217631764176517661767176817691770177117721773177417751776177717781779178017811782178317841785178617871788178917901791179217931794179517961797179817991800180118021803180418051806180718081809181018111812181318141815181618171818181918201821182218231824182518261827182818291830183118321833183418351836183718381839184018411842184318441845184618471848184918501851185218531854185518561857185818591860186118621863186418651866186718681869187018711872187318741875187618771878187918801881188218831884188518861887188818891890189118921893189418951896189718981899190019011902190319041905190619071908190919101911191219131914191519161917191819191920192119221923192419251926192719281929193019311932193319341935193619371938193919401941194219431944194519461947194819491950195119521953195419551956195719581959196019611962196319641965196619671968196919701971197219731974197519761977197819791980198119821983198419851986198719881989199019911992199319941995199619971998199920002001200220032004200520062007200820092010201120122013201420152016201720182019202020212022202320242025202620272028202920302031203220332034203520362037203820392040204120422043204420452046204720482049205020512052205320542055205620572058205920602061206220632064206520662067206820692070207120722073207420752076207720782079208020812082208320842085208620872088208920902091209220932094209520962097209820992100210121022103210421052106210721082109211021112112211321142115211621172118211921202121212221232124212521262127212821292130213121322133213421352136213721382139214021412142214321442145214621472148214921502151215221532154215521562157215821592160216121622163216421652166216721682169217021712172217321742175217621772178217921802181218221832184218521862187218821892190219121922193219421952196219721982199220022012202220322042205220622072208220922102211221222132214221522162217221822192220222122222223222422252226222722282229223022312232223322342235223622372238223922402241224222432244224522462247224822492250225122522253225422552256225722582259226022612262226322642265226622672268226922702271227222732274227522762277227822792280228122822283228422852286228722882289229022912292229322942295229622972298229923002301230223032304230523062307230823092310231123122313231423152316231723182319232023212322232323242325232623272328232923302331233223332334233523362337233823392340234123422343234423452346234723482349235023512352235323542355235623572358235923602361236223632364236523662367236823692370237123722373237423752376237723782379238023812382238323842385238623872388238923902391239223932394239523962397239823992400240124022403240424052406240724082409241024112412241324142415241624172418241924202421242224232424242524262427242824292430243124322433243424352436243724382439244024412442244324442445244624472448244924502451245224532454245524562457245824592460246124622463246424652466246724682469247024712472247324742475247624772478247924802481248224832484248524862487248824892490249124922493249424952496
  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.Collection;
  20. import java.util.Map;
  21. import java.util.concurrent.locks.ReentrantReadWriteLock;
  22. import java.util.logging.Level;
  23. import java.util.logging.Logger;
  24. import javolution.util.FastList;
  25. import javolution.util.FastMap;
  26. import com.l2jserver.Config;
  27. import com.l2jserver.L2DatabaseFactory;
  28. import com.l2jserver.gameserver.GameTimeController;
  29. import com.l2jserver.gameserver.ThreadPoolManager;
  30. import com.l2jserver.gameserver.cache.HtmCache;
  31. import com.l2jserver.gameserver.datatables.ItemTable;
  32. import com.l2jserver.gameserver.datatables.NpcTable;
  33. import com.l2jserver.gameserver.idfactory.IdFactory;
  34. import com.l2jserver.gameserver.instancemanager.QuestManager;
  35. import com.l2jserver.gameserver.instancemanager.ZoneManager;
  36. import com.l2jserver.gameserver.model.L2DropData;
  37. import com.l2jserver.gameserver.model.L2Object;
  38. import com.l2jserver.gameserver.model.L2Party;
  39. import com.l2jserver.gameserver.model.L2Spawn;
  40. import com.l2jserver.gameserver.model.Location;
  41. import com.l2jserver.gameserver.model.actor.L2Character;
  42. import com.l2jserver.gameserver.model.actor.L2Npc;
  43. import com.l2jserver.gameserver.model.actor.L2Trap;
  44. import com.l2jserver.gameserver.model.actor.instance.L2MonsterInstance;
  45. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  46. import com.l2jserver.gameserver.model.actor.instance.L2TrapInstance;
  47. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  48. import com.l2jserver.gameserver.model.itemcontainer.PcInventory;
  49. import com.l2jserver.gameserver.model.items.L2Item;
  50. import com.l2jserver.gameserver.model.items.instance.L2ItemInstance;
  51. import com.l2jserver.gameserver.model.olympiad.CompetitionType;
  52. import com.l2jserver.gameserver.model.skills.L2Skill;
  53. import com.l2jserver.gameserver.model.stats.Stats;
  54. import com.l2jserver.gameserver.model.zone.L2ZoneType;
  55. import com.l2jserver.gameserver.network.SystemMessageId;
  56. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  57. import com.l2jserver.gameserver.network.serverpackets.InventoryUpdate;
  58. import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
  59. import com.l2jserver.gameserver.network.serverpackets.NpcQuestHtmlMessage;
  60. import com.l2jserver.gameserver.network.serverpackets.PlaySound;
  61. import com.l2jserver.gameserver.network.serverpackets.StatusUpdate;
  62. import com.l2jserver.gameserver.network.serverpackets.SystemMessage;
  63. import com.l2jserver.gameserver.scripting.ManagedScript;
  64. import com.l2jserver.gameserver.scripting.ScriptManager;
  65. import com.l2jserver.gameserver.util.MinionList;
  66. import com.l2jserver.util.Rnd;
  67. import com.l2jserver.util.Util;
  68. /**
  69. * @author Luis Arias
  70. */
  71. public class Quest extends ManagedScript
  72. {
  73. protected static final Logger _log = Logger.getLogger(Quest.class.getName());
  74. /** HashMap containing events from String value of the event */
  75. private static Map<String, Quest> _allEventsS = new FastMap<String, Quest>();
  76. /** HashMap containing lists of timers from the name of the timer */
  77. private final Map<String, FastList<QuestTimer>> _allEventTimers = new FastMap<String, FastList<QuestTimer>>().shared();
  78. private final ReentrantReadWriteLock _rwLock = new ReentrantReadWriteLock();
  79. private final int _questId;
  80. private final String _name;
  81. private final String _descr;
  82. private final byte _initialState = State.CREATED;
  83. protected boolean _onEnterWorld = false;
  84. private boolean _isCustom = false;
  85. private boolean _isOlympiadUse = false;
  86. // NOTE: questItemIds will be overridden by child classes. Ideally, it should be
  87. // protected instead of public. However, quest scripts written in Jython will
  88. // have trouble with protected, as Jython only knows private and public...
  89. // In fact, protected will typically be considered private thus breaking the scripts.
  90. // Leave this as public as a workaround.
  91. public int[] questItemIds = null;
  92. private static final String DEFAULT_NO_QUEST_MSG =
  93. "<html><body>You are either not on a quest that involves this NPC, or you don't meet this NPC's minimum quest requirements.</body></html>";
  94. private static final String DEFAULT_ALREADY_COMPLETED_MSG =
  95. "<html><body>This quest has already been completed.</body></html>";
  96. private static final int RESET_HOUR = 6;
  97. private static final int RESET_MINUTES = 30;
  98. /**
  99. * @return the reset hour for a daily quest, could be overridden on a script.
  100. */
  101. public int getResetHour()
  102. {
  103. return RESET_HOUR;
  104. }
  105. /**
  106. * @return the reset minutes for a daily quest, could be overridden on a script.
  107. */
  108. public int getResetMinutes()
  109. {
  110. return RESET_MINUTES;
  111. }
  112. /**
  113. * Return collection view of the values contains in the allEventS
  114. * @return Collection<Quest>
  115. */
  116. public static Collection<Quest> findAllEvents()
  117. {
  118. return _allEventsS.values();
  119. }
  120. /**
  121. * (Constructor)Add values to class variables and put the quest in HashMaps.
  122. * @param questId : int pointing out the ID of the quest
  123. * @param name : String corresponding to the name of the quest
  124. * @param descr : String for the description of the quest
  125. */
  126. public Quest(int questId, String name, String descr)
  127. {
  128. _questId = questId;
  129. _name = name;
  130. _descr = descr;
  131. if (questId != 0)
  132. {
  133. QuestManager.getInstance().addQuest(this);
  134. }
  135. else
  136. {
  137. _allEventsS.put(name, this);
  138. }
  139. init_LoadGlobalData();
  140. }
  141. /**
  142. * The function init_LoadGlobalData is, by default, called by the constructor of all quests.
  143. * Children of this class can implement this function in order to define what variables
  144. * to load and what structures to save them in. By default, nothing is loaded.
  145. */
  146. protected void init_LoadGlobalData()
  147. {
  148. }
  149. /**
  150. * The function saveGlobalData is, by default, called at shutdown, for all quests, by the QuestManager.
  151. * Children of this class can implement this function in order to convert their structures
  152. * into <var, value> tuples and make calls to save them to the database, if needed.
  153. * By default, nothing is saved.
  154. */
  155. public void saveGlobalData()
  156. {
  157. }
  158. public static enum TrapAction
  159. {
  160. TRAP_TRIGGERED,
  161. TRAP_DETECTED,
  162. TRAP_DISARMED
  163. }
  164. public static enum QuestEventType
  165. {
  166. ON_FIRST_TALK(false), // control the first dialog shown by NPCs when they are clicked (some quests must override the default npc action)
  167. QUEST_START(true), // onTalk action from start npcs
  168. ON_TALK(true), // onTalk action from npcs participating in a quest
  169. ON_ATTACK(true), // onAttack action triggered when a mob gets attacked by someone
  170. ON_KILL(true), // onKill action triggered when a mob gets killed.
  171. ON_SPAWN(true), // onSpawn action triggered when an NPC is spawned or respawned.
  172. ON_SKILL_SEE(true), // NPC or Mob saw a person casting a skill (regardless what the target is).
  173. ON_FACTION_CALL(true), // NPC or Mob saw a person casting a skill (regardless what the target is).
  174. ON_AGGRO_RANGE_ENTER(true), // a person came within the Npc/Mob's range
  175. ON_SPELL_FINISHED(true), // on spell finished action when npc finish casting skill
  176. ON_SKILL_LEARN(false), // control the AcquireSkill dialog from quest script
  177. ON_ENTER_ZONE(true), // on zone enter
  178. ON_EXIT_ZONE(true), // on zone exit
  179. ON_TRAP_ACTION(true), // on zone exit
  180. ON_ITEM_USE(true);
  181. // control whether this event type is allowed for the same npc template in multiple quests
  182. // or if the npc must be registered in at most one quest for the specified event
  183. private boolean _allowMultipleRegistration;
  184. QuestEventType(boolean allowMultipleRegistration)
  185. {
  186. _allowMultipleRegistration = allowMultipleRegistration;
  187. }
  188. public boolean isMultipleRegistrationAllowed()
  189. {
  190. return _allowMultipleRegistration;
  191. }
  192. }
  193. /**
  194. * Return ID of the quest
  195. * @return int
  196. */
  197. public int getQuestIntId()
  198. {
  199. return _questId;
  200. }
  201. /**
  202. * Add a new QuestState to the database and return it.
  203. * @param player
  204. * @return QuestState : QuestState created
  205. */
  206. public QuestState newQuestState(L2PcInstance player)
  207. {
  208. QuestState qs = new QuestState(this, player, getInitialState());
  209. return qs;
  210. }
  211. /**
  212. * Return initial state of the quest
  213. * @return State
  214. */
  215. public byte getInitialState()
  216. {
  217. return _initialState;
  218. }
  219. /**
  220. * Return name of the quest
  221. * @return String
  222. */
  223. public String getName()
  224. {
  225. return _name;
  226. }
  227. /**
  228. * Return description of the quest
  229. * @return String
  230. */
  231. public String getDescr()
  232. {
  233. return _descr;
  234. }
  235. /**
  236. * Add a timer to the quest, if it doesn't exist already
  237. * @param name name of the timer (also passed back as "event" in onAdvEvent)
  238. * @param time time in ms for when to fire the timer
  239. * @param npc npc associated with this timer (can be null)
  240. * @param player player associated with this timer (can be null)
  241. */
  242. public void startQuestTimer(String name, long time, L2Npc npc, L2PcInstance player)
  243. {
  244. startQuestTimer(name, time, npc, player, false);
  245. }
  246. /**
  247. * Add a timer to the quest, if it doesn't exist already. If the timer is repeatable,
  248. * it will auto-fire automatically, at a fixed rate, until explicitly canceled.
  249. * @param name name of the timer (also passed back as "event" in onAdvEvent)
  250. * @param time time in ms for when to fire the timer
  251. * @param npc npc associated with this timer (can be null)
  252. * @param player player associated with this timer (can be null)
  253. * @param repeating indicates if the timer is repeatable or one-time.
  254. */
  255. public void startQuestTimer(String name, long time, L2Npc npc, L2PcInstance player, boolean repeating)
  256. {
  257. // Add quest timer if timer doesn't already exist
  258. FastList<QuestTimer> timers = getQuestTimers(name);
  259. // no timer exists with the same name, at all
  260. if (timers == null)
  261. {
  262. timers = new FastList<QuestTimer>();
  263. timers.add(new QuestTimer(this, name, time, npc, player, repeating));
  264. _allEventTimers.put(name, timers);
  265. }
  266. // a timer with this name exists, but may not be for the same set of npc and player
  267. else
  268. {
  269. // if there exists a timer with this name, allow the timer only if the [npc, player] set is unique
  270. // nulls act as wildcards
  271. if (getQuestTimer(name, npc, player) == null)
  272. {
  273. try
  274. {
  275. _rwLock.writeLock().lock();
  276. timers.add(new QuestTimer(this, name, time, npc, player, repeating));
  277. }
  278. finally
  279. {
  280. _rwLock.writeLock().unlock();
  281. }
  282. }
  283. }
  284. }
  285. public QuestTimer getQuestTimer(String name, L2Npc npc, L2PcInstance player)
  286. {
  287. FastList<QuestTimer> qt = getQuestTimers(name);
  288. if (qt == null || qt.isEmpty())
  289. return null;
  290. try
  291. {
  292. _rwLock.readLock().lock();
  293. for (QuestTimer timer : qt)
  294. {
  295. if (timer != null)
  296. {
  297. if (timer.isMatch(this, name, npc, player))
  298. return timer;
  299. }
  300. }
  301. }
  302. finally
  303. {
  304. _rwLock.readLock().unlock();
  305. }
  306. return null;
  307. }
  308. private FastList<QuestTimer> getQuestTimers(String name)
  309. {
  310. return _allEventTimers.get(name);
  311. }
  312. public void cancelQuestTimers(String name)
  313. {
  314. FastList<QuestTimer> timers = getQuestTimers(name);
  315. if (timers == null)
  316. return;
  317. try
  318. {
  319. _rwLock.writeLock().lock();
  320. for (QuestTimer timer : timers)
  321. {
  322. if (timer != null)
  323. {
  324. timer.cancel();
  325. }
  326. }
  327. }
  328. finally
  329. {
  330. _rwLock.writeLock().unlock();
  331. }
  332. }
  333. public void cancelQuestTimer(String name, L2Npc npc, L2PcInstance player)
  334. {
  335. QuestTimer timer = getQuestTimer(name, npc, player);
  336. if (timer != null)
  337. timer.cancel();
  338. }
  339. public void removeQuestTimer(QuestTimer timer)
  340. {
  341. if (timer == null)
  342. return;
  343. FastList<QuestTimer> timers = getQuestTimers(timer.getName());
  344. if (timers == null)
  345. return;
  346. try
  347. {
  348. _rwLock.writeLock().lock();
  349. timers.remove(timer);
  350. }
  351. finally
  352. {
  353. _rwLock.writeLock().unlock();
  354. }
  355. }
  356. // these are methods to call from java
  357. public final boolean notifyAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet, L2Skill skill)
  358. {
  359. String res = null;
  360. try
  361. {
  362. res = onAttack(npc, attacker, damage, isPet, skill);
  363. }
  364. catch (Exception e)
  365. {
  366. return showError(attacker, e);
  367. }
  368. return showResult(attacker, res);
  369. }
  370. public final boolean notifyDeath(L2Character killer, L2Character victim, QuestState qs)
  371. {
  372. String res = null;
  373. try
  374. {
  375. res = onDeath(killer, victim, qs);
  376. }
  377. catch (Exception e)
  378. {
  379. return showError(qs.getPlayer(), e);
  380. }
  381. return showResult(qs.getPlayer(), res);
  382. }
  383. public final boolean notifyItemUse(L2Item item, L2PcInstance player)
  384. {
  385. String res = null;
  386. try
  387. {
  388. res = onItemUse(item, player);
  389. }
  390. catch(Exception e)
  391. {
  392. return showError(player, e);
  393. }
  394. return showResult(player, res);
  395. }
  396. public final boolean notifySpellFinished(L2Npc instance, L2PcInstance player, L2Skill skill)
  397. {
  398. String res = null;
  399. try
  400. {
  401. res = onSpellFinished(instance, player, skill);
  402. }
  403. catch (Exception e)
  404. {
  405. return showError(player, e);
  406. }
  407. return showResult(player, res);
  408. }
  409. /**
  410. * Notify quest script when something happens with a trap
  411. * @param trap the trap instance which triggers the notification
  412. * @param trigger the character which makes effect on the trap
  413. * @param action 0: trap casting its skill. 1: trigger detects the trap. 2: trigger removes the trap
  414. * @return
  415. */
  416. public final boolean notifyTrapAction(L2Trap trap, L2Character trigger, TrapAction action)
  417. {
  418. String res = null;
  419. try
  420. {
  421. res = onTrapAction(trap, trigger, action);
  422. }
  423. catch (Exception e)
  424. {
  425. if (trigger.getActingPlayer() != null)
  426. return showError(trigger.getActingPlayer(), e);
  427. _log.log(Level.WARNING, "Exception on onTrapAction() in notifyTrapAction(): " + e.getMessage(), e);
  428. return true;
  429. }
  430. if (trigger.getActingPlayer() != null)
  431. return showResult(trigger.getActingPlayer(), res);
  432. return false;
  433. }
  434. public final boolean notifySpawn(L2Npc npc)
  435. {
  436. try
  437. {
  438. onSpawn(npc);
  439. }
  440. catch (Exception e)
  441. {
  442. _log.log(Level.WARNING, "Exception on onSpawn() in notifySpawn(): " + e.getMessage(), e);
  443. return true;
  444. }
  445. return false;
  446. }
  447. public final boolean notifyEvent(String event, L2Npc npc, L2PcInstance player)
  448. {
  449. String res = null;
  450. try
  451. {
  452. res = onAdvEvent(event, npc, player);
  453. }
  454. catch (Exception e)
  455. {
  456. return showError(player, e);
  457. }
  458. return showResult(player, res);
  459. }
  460. public final boolean notifyEnterWorld(L2PcInstance player)
  461. {
  462. String res = null;
  463. try
  464. {
  465. res = onEnterWorld(player);
  466. }
  467. catch (Exception e)
  468. {
  469. return showError(player, e);
  470. }
  471. return showResult(player, res);
  472. }
  473. public final boolean notifyKill(L2Npc npc, L2PcInstance killer, boolean isPet)
  474. {
  475. String res = null;
  476. try
  477. {
  478. res = onKill(npc, killer, isPet);
  479. }
  480. catch (Exception e)
  481. {
  482. return showError(killer, e);
  483. }
  484. return showResult(killer, res);
  485. }
  486. public final boolean notifyTalk(L2Npc npc, QuestState qs)
  487. {
  488. String res = null;
  489. try
  490. {
  491. res = onTalk(npc, qs.getPlayer());
  492. }
  493. catch (Exception e)
  494. {
  495. return showError(qs.getPlayer(), e);
  496. }
  497. qs.getPlayer().setLastQuestNpcObject(npc.getObjectId());
  498. return showResult(qs.getPlayer(), res);
  499. }
  500. // override the default NPC dialogs when a quest defines this for the given NPC
  501. public final boolean notifyFirstTalk(L2Npc npc, L2PcInstance player)
  502. {
  503. String res = null;
  504. try
  505. {
  506. res = onFirstTalk(npc, player);
  507. }
  508. catch (Exception e)
  509. {
  510. return showError(player, e);
  511. }
  512. // if the quest returns text to display, display it.
  513. if (res != null && res.length() > 0)
  514. return showResult(player, res);
  515. // else tell the player that
  516. player.sendPacket(ActionFailed.STATIC_PACKET);
  517. // note: if the default html for this npc needs to be shown, onFirstTalk should
  518. // call npc.showChatWindow(player) and then return null.
  519. return true;
  520. }
  521. public final boolean notifyAcquireSkillList(L2Npc npc, L2PcInstance player)
  522. {
  523. String res = null;
  524. try
  525. {
  526. res = onAcquireSkillList(npc, player);
  527. }
  528. catch (Exception e)
  529. {
  530. return showError(player, e);
  531. }
  532. return showResult(player, res);
  533. }
  534. public final boolean notifyAcquireSkillInfo(L2Npc npc, L2PcInstance player, L2Skill skill)
  535. {
  536. String res = null;
  537. try
  538. {
  539. res = onAcquireSkillInfo(npc, player, skill);
  540. }
  541. catch (Exception e)
  542. {
  543. return showError(player, e);
  544. }
  545. return showResult(player, res);
  546. }
  547. public final boolean notifyAcquireSkill(L2Npc npc, L2PcInstance player, L2Skill skill)
  548. {
  549. String res = null;
  550. try
  551. {
  552. res = onAcquireSkill(npc, player, skill);
  553. if (res == "true")
  554. return true;
  555. else if (res == "false")
  556. return false;
  557. }
  558. catch (Exception e)
  559. {
  560. return showError(player, e);
  561. }
  562. return showResult(player, res);
  563. }
  564. public class TmpOnSkillSee implements Runnable
  565. {
  566. private final L2Npc _npc;
  567. private final L2PcInstance _caster;
  568. private final L2Skill _skill;
  569. private final L2Object[] _targets;
  570. private final boolean _isPet;
  571. public TmpOnSkillSee(L2Npc npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet)
  572. {
  573. _npc = npc;
  574. _caster = caster;
  575. _skill = skill;
  576. _targets = targets;
  577. _isPet = isPet;
  578. }
  579. @Override
  580. public void run()
  581. {
  582. String res = null;
  583. try
  584. {
  585. res = onSkillSee(_npc, _caster, _skill, _targets, _isPet);
  586. }
  587. catch (Exception e)
  588. {
  589. showError(_caster, e);
  590. }
  591. showResult(_caster, res);
  592. }
  593. }
  594. public final boolean notifySkillSee(L2Npc npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet)
  595. {
  596. ThreadPoolManager.getInstance().executeAi(new TmpOnSkillSee(npc, caster, skill, targets, isPet));
  597. return true;
  598. }
  599. public final boolean notifyFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isPet)
  600. {
  601. String res = null;
  602. try
  603. {
  604. res = onFactionCall(npc, caller, attacker, isPet);
  605. }
  606. catch (Exception e)
  607. {
  608. return showError(attacker, e);
  609. }
  610. return showResult(attacker, res);
  611. }
  612. public class TmpOnAggroEnter implements Runnable
  613. {
  614. private final L2Npc _npc;
  615. private final L2PcInstance _pc;
  616. private final boolean _isPet;
  617. public TmpOnAggroEnter(L2Npc npc, L2PcInstance pc, boolean isPet)
  618. {
  619. _npc = npc;
  620. _pc = pc;
  621. _isPet = isPet;
  622. }
  623. @Override
  624. public void run()
  625. {
  626. String res = null;
  627. try
  628. {
  629. res = onAggroRangeEnter(_npc, _pc, _isPet);
  630. }
  631. catch (Exception e)
  632. {
  633. showError(_pc, e);
  634. }
  635. showResult(_pc, res);
  636. }
  637. }
  638. public final boolean notifyAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isPet)
  639. {
  640. ThreadPoolManager.getInstance().executeAi(new TmpOnAggroEnter(npc, player, isPet));
  641. return true;
  642. }
  643. public final boolean notifyEnterZone(L2Character character, L2ZoneType zone)
  644. {
  645. L2PcInstance player = character.getActingPlayer();
  646. String res = null;
  647. try
  648. {
  649. res = this.onEnterZone(character, zone);
  650. }
  651. catch (Exception e)
  652. {
  653. if (player != null)
  654. return showError(player, e);
  655. }
  656. if (player != null)
  657. return showResult(player, res);
  658. return true;
  659. }
  660. public final boolean notifyExitZone(L2Character character, L2ZoneType zone)
  661. {
  662. L2PcInstance player = character.getActingPlayer();
  663. String res = null;
  664. try
  665. {
  666. res = this.onExitZone(character, zone);
  667. }
  668. catch (Exception e)
  669. {
  670. if (player != null)
  671. return showError(player, e);
  672. }
  673. if (player != null)
  674. return showResult(player, res);
  675. return true;
  676. }
  677. // these are methods that java calls to invoke scripts
  678. public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet)
  679. {
  680. return null;
  681. }
  682. public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet, L2Skill skill)
  683. {
  684. return onAttack(npc, attacker, damage, isPet);
  685. }
  686. public String onDeath(L2Character killer, L2Character victim, QuestState qs)
  687. {
  688. if (killer instanceof L2Npc)
  689. return onAdvEvent("", (L2Npc) killer, qs.getPlayer());
  690. return onAdvEvent("", null, qs.getPlayer());
  691. }
  692. public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
  693. {
  694. // if not overridden by a subclass, then default to the returned value of the simpler (and older) onEvent override
  695. // if the player has a state, use it as parameter in the next call, else return null
  696. QuestState qs = player.getQuestState(getName());
  697. if (qs != null)
  698. return onEvent(event, qs);
  699. return null;
  700. }
  701. public String onEvent(String event, QuestState qs)
  702. {
  703. return null;
  704. }
  705. public String onKill(L2Npc npc, L2PcInstance killer, boolean isPet)
  706. {
  707. return null;
  708. }
  709. public String onTalk(L2Npc npc, L2PcInstance talker)
  710. {
  711. return null;
  712. }
  713. public String onFirstTalk(L2Npc npc, L2PcInstance player)
  714. {
  715. return null;
  716. }
  717. public String onAcquireSkillList(L2Npc npc, L2PcInstance player)
  718. {
  719. return null;
  720. }
  721. public String onAcquireSkillInfo(L2Npc npc, L2PcInstance player, L2Skill skill)
  722. {
  723. return null;
  724. }
  725. public String onAcquireSkill(L2Npc npc, L2PcInstance player, L2Skill skill)
  726. {
  727. return null;
  728. }
  729. public String onItemUse(L2Item item, L2PcInstance player)
  730. {
  731. return null;
  732. }
  733. public String onSkillSee(L2Npc npc, L2PcInstance caster, L2Skill skill, L2Object[] targets, boolean isPet)
  734. {
  735. return null;
  736. }
  737. public String onSpellFinished(L2Npc npc, L2PcInstance player, L2Skill skill)
  738. {
  739. return null;
  740. }
  741. public String onTrapAction(L2Trap trap, L2Character trigger, TrapAction action)
  742. {
  743. return null;
  744. }
  745. public String onSpawn(L2Npc npc)
  746. {
  747. return null;
  748. }
  749. public String onFactionCall(L2Npc npc, L2Npc caller, L2PcInstance attacker, boolean isPet)
  750. {
  751. return null;
  752. }
  753. public String onAggroRangeEnter(L2Npc npc, L2PcInstance player, boolean isPet)
  754. {
  755. return null;
  756. }
  757. public String onEnterWorld(L2PcInstance player)
  758. {
  759. return null;
  760. }
  761. public String onEnterZone(L2Character character, L2ZoneType zone)
  762. {
  763. return null;
  764. }
  765. public String onExitZone(L2Character character, L2ZoneType zone)
  766. {
  767. return null;
  768. }
  769. /**
  770. * Show message error to player who has an access level greater than 0
  771. * @param player : L2PcInstance
  772. * @param t : Throwable
  773. * @return boolean
  774. */
  775. public boolean showError(L2PcInstance player, Throwable t)
  776. {
  777. _log.log(Level.WARNING, this.getScriptFile().getAbsolutePath(), t);
  778. if (t.getMessage() == null)
  779. {
  780. _log.warning(getClass().getSimpleName() + ": " + t.getMessage());
  781. }
  782. if (player != null && player.getAccessLevel().isGm())
  783. {
  784. String res = "<html><body><title>Script error</title>" + Util.getStackTrace(t) + "</body></html>";
  785. return showResult(player, res);
  786. }
  787. return false;
  788. }
  789. /**
  790. * Show a message to player.<BR><BR>
  791. * <U><I>Concept : </I></U><BR>
  792. * 3 cases are managed according to the value of the parameter "res" :<BR>
  793. * <LI><U>"res" ends with string ".html" :</U> an HTML is opened in order to be shown in a dialog box</LI>
  794. * <LI><U>"res" starts with "<html>" :</U> the message hold in "res" is shown in a dialog box</LI>
  795. * <LI><U>otherwise :</U> the message held in "res" is shown in chat box</LI>
  796. * @param player the player to show the result
  797. * @param res the message to show at the player
  798. * @return boolean
  799. */
  800. public boolean showResult(L2PcInstance player, String res)
  801. {
  802. if (res == null || res.isEmpty() || player == null)
  803. return true;
  804. if (res.endsWith(".htm") || res.endsWith(".html"))
  805. {
  806. showHtmlFile(player, res);
  807. }
  808. else if (res.startsWith("<html>"))
  809. {
  810. NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
  811. npcReply.setHtml(res);
  812. npcReply.replace("%playername%", player.getName());
  813. player.sendPacket(npcReply);
  814. player.sendPacket(ActionFailed.STATIC_PACKET);
  815. }
  816. else
  817. {
  818. player.sendMessage(res);
  819. }
  820. return false;
  821. }
  822. /**
  823. * Add quests to the L2PCInstance of the player.<BR><BR>
  824. * <U><I>Action : </U></I><BR>
  825. * Add state of quests, drops and variables for quests in the HashMap _quest of L2PcInstance
  826. * @param player : Player who is entering the world
  827. */
  828. public final static void playerEnter(L2PcInstance player)
  829. {
  830. Connection con = null;
  831. try
  832. {
  833. // Get list of quests owned by the player from database
  834. con = L2DatabaseFactory.getInstance().getConnection();
  835. PreparedStatement invalidQuestData = con.prepareStatement("DELETE FROM character_quests WHERE charId=? and name=?");
  836. PreparedStatement invalidQuestDataVar = con.prepareStatement("delete FROM character_quests WHERE charId=? and name=? and var=?");
  837. PreparedStatement statement = con.prepareStatement("SELECT name,value FROM character_quests WHERE charId=? AND var=?");
  838. statement.setInt(1, player.getObjectId());
  839. statement.setString(2, "<state>");
  840. ResultSet rs = statement.executeQuery();
  841. while (rs.next())
  842. {
  843. // Get ID of the quest and ID of its state
  844. String questId = rs.getString("name");
  845. String statename = rs.getString("value");
  846. // Search quest associated with the ID
  847. Quest q = QuestManager.getInstance().getQuest(questId);
  848. if (q == null)
  849. {
  850. _log.finer("Unknown quest " + questId + " for player " + player.getName());
  851. if (Config.AUTODELETE_INVALID_QUEST_DATA)
  852. {
  853. invalidQuestData.setInt(1, player.getObjectId());
  854. invalidQuestData.setString(2, questId);
  855. invalidQuestData.executeUpdate();
  856. }
  857. continue;
  858. }
  859. // Create a new QuestState for the player that will be added to the player's list of quests
  860. new QuestState(q, player, State.getStateId(statename));
  861. }
  862. rs.close();
  863. invalidQuestData.close();
  864. statement.close();
  865. // Get list of quests owned by the player from the DB in order to add variables used in the quest.
  866. statement = con.prepareStatement("SELECT name,var,value FROM character_quests WHERE charId=? AND var<>?");
  867. statement.setInt(1, player.getObjectId());
  868. statement.setString(2, "<state>");
  869. rs = statement.executeQuery();
  870. while (rs.next())
  871. {
  872. String questId = rs.getString("name");
  873. String var = rs.getString("var");
  874. String value = rs.getString("value");
  875. // Get the QuestState saved in the loop before
  876. QuestState qs = player.getQuestState(questId);
  877. if (qs == null)
  878. {
  879. _log.finer("Lost variable " + var + " in quest " + questId + " for player " + player.getName());
  880. if (Config.AUTODELETE_INVALID_QUEST_DATA)
  881. {
  882. invalidQuestDataVar.setInt(1, player.getObjectId());
  883. invalidQuestDataVar.setString(2, questId);
  884. invalidQuestDataVar.setString(3, var);
  885. invalidQuestDataVar.executeUpdate();
  886. }
  887. continue;
  888. }
  889. // Add parameter to the quest
  890. qs.setInternal(var, value);
  891. }
  892. rs.close();
  893. invalidQuestDataVar.close();
  894. statement.close();
  895. }
  896. catch (Exception e)
  897. {
  898. _log.log(Level.WARNING, "could not insert char quest:", e);
  899. }
  900. finally
  901. {
  902. L2DatabaseFactory.close(con);
  903. }
  904. // events
  905. for (String name : _allEventsS.keySet())
  906. {
  907. player.processQuestEvent(name, "enter");
  908. }
  909. }
  910. /**
  911. * Insert (or Update) in the database variables that need to stay persistant for this quest after a reboot.
  912. * This function is for storage of values that do not related to a specific player but are
  913. * global for all characters. For example, if we need to disable a quest-gatekeeper until
  914. * a certain time (as is done with some grand-boss gatekeepers), we can save that time in the DB.
  915. * @param var : String designating the name of the variable for the quest
  916. * @param value : String designating the value of the variable for the quest
  917. */
  918. public final void saveGlobalQuestVar(String var, String value)
  919. {
  920. Connection con = null;
  921. try
  922. {
  923. con = L2DatabaseFactory.getInstance().getConnection();
  924. PreparedStatement statement;
  925. statement = con.prepareStatement("REPLACE INTO quest_global_data (quest_name,var,value) VALUES (?,?,?)");
  926. statement.setString(1, getName());
  927. statement.setString(2, var);
  928. statement.setString(3, value);
  929. statement.executeUpdate();
  930. statement.close();
  931. }
  932. catch (Exception e)
  933. {
  934. _log.log(Level.WARNING, "could not insert global quest variable:", e);
  935. }
  936. finally
  937. {
  938. L2DatabaseFactory.close(con);
  939. }
  940. }
  941. /**
  942. * Read from the database a previously saved variable for this quest.
  943. * Due to performance considerations, this function should best be used only when the quest is first loaded.
  944. * Subclasses of this class can define structures into which these loaded values can be saved.
  945. * However, on-demand usage of this function throughout the script is not prohibited, only not recommended.
  946. * Values read from this function were entered by calls to "saveGlobalQuestVar"
  947. * @param var : String designating the name of the variable for the quest
  948. * @return String : String representing the loaded value for the passed var, or an empty string if the var was invalid
  949. */
  950. public final String loadGlobalQuestVar(String var)
  951. {
  952. String result = "";
  953. Connection con = null;
  954. try
  955. {
  956. con = L2DatabaseFactory.getInstance().getConnection();
  957. PreparedStatement statement;
  958. statement = con.prepareStatement("SELECT value FROM quest_global_data WHERE quest_name = ? AND var = ?");
  959. statement.setString(1, getName());
  960. statement.setString(2, var);
  961. ResultSet rs = statement.executeQuery();
  962. if (rs.first())
  963. result = rs.getString(1);
  964. rs.close();
  965. statement.close();
  966. }
  967. catch (Exception e)
  968. {
  969. _log.log(Level.WARNING, "could not load global quest variable:", e);
  970. }
  971. finally
  972. {
  973. L2DatabaseFactory.close(con);
  974. }
  975. return result;
  976. }
  977. /**
  978. * Permanently delete from the database a global quest variable that was previously saved for this quest.
  979. * @param var : String designating the name of the variable for the quest
  980. */
  981. public final void deleteGlobalQuestVar(String var)
  982. {
  983. Connection con = null;
  984. try
  985. {
  986. con = L2DatabaseFactory.getInstance().getConnection();
  987. PreparedStatement statement;
  988. statement = con.prepareStatement("DELETE FROM quest_global_data WHERE quest_name = ? AND var = ?");
  989. statement.setString(1, getName());
  990. statement.setString(2, var);
  991. statement.executeUpdate();
  992. statement.close();
  993. }
  994. catch (Exception e)
  995. {
  996. _log.log(Level.WARNING, "could not delete global quest variable:", e);
  997. }
  998. finally
  999. {
  1000. L2DatabaseFactory.close(con);
  1001. }
  1002. }
  1003. /**
  1004. * Permanently delete from the database all global quest variables that was previously saved for this quest.
  1005. */
  1006. public final void deleteAllGlobalQuestVars()
  1007. {
  1008. Connection con = null;
  1009. try
  1010. {
  1011. con = L2DatabaseFactory.getInstance().getConnection();
  1012. PreparedStatement statement;
  1013. statement = con.prepareStatement("DELETE FROM quest_global_data WHERE quest_name = ?");
  1014. statement.setString(1, getName());
  1015. statement.executeUpdate();
  1016. statement.close();
  1017. }
  1018. catch (Exception e)
  1019. {
  1020. _log.log(Level.WARNING, "could not delete global quest variables:", e);
  1021. }
  1022. finally
  1023. {
  1024. L2DatabaseFactory.close(con);
  1025. }
  1026. }
  1027. /**
  1028. * Insert in the database the quest for the player.
  1029. * @param qs : QuestState pointing out the state of the quest
  1030. * @param var : String designating the name of the variable for the quest
  1031. * @param value : String designating the value of the variable for the quest
  1032. */
  1033. public static void createQuestVarInDb(QuestState qs, String var, String value)
  1034. {
  1035. Connection con = null;
  1036. try
  1037. {
  1038. con = L2DatabaseFactory.getInstance().getConnection();
  1039. PreparedStatement statement;
  1040. statement = con.prepareStatement("INSERT INTO character_quests (charId,name,var,value) VALUES (?,?,?,?) ON DUPLICATE KEY UPDATE value=?");
  1041. statement.setInt(1, qs.getPlayer().getObjectId());
  1042. statement.setString(2, qs.getQuestName());
  1043. statement.setString(3, var);
  1044. statement.setString(4, value);
  1045. statement.setString(5, value);
  1046. statement.executeUpdate();
  1047. statement.close();
  1048. }
  1049. catch (Exception e)
  1050. {
  1051. _log.log(Level.WARNING, "could not insert char quest:", e);
  1052. }
  1053. finally
  1054. {
  1055. L2DatabaseFactory.close(con);
  1056. }
  1057. }
  1058. /**
  1059. * Update the value of the variable "var" for the quest.<BR><BR>
  1060. * <U><I>Actions :</I></U><BR>
  1061. * The selection of the right record is made with :
  1062. * <LI>charId = qs.getPlayer().getObjectID()</LI>
  1063. * <LI>name = qs.getQuest().getName()</LI>
  1064. * <LI>var = var</LI>
  1065. * <BR><BR>
  1066. * The modification made is :
  1067. * <LI>value = parameter value</LI>
  1068. * @param qs : Quest State
  1069. * @param var : String designating the name of the variable for quest
  1070. * @param value : String designating the value of the variable for quest
  1071. */
  1072. public static void updateQuestVarInDb(QuestState qs, String var, String value)
  1073. {
  1074. Connection con = null;
  1075. try
  1076. {
  1077. con = L2DatabaseFactory.getInstance().getConnection();
  1078. PreparedStatement statement;
  1079. statement = con.prepareStatement("UPDATE character_quests SET value=? WHERE charId=? AND name=? AND var = ?");
  1080. statement.setString(1, value);
  1081. statement.setInt(2, qs.getPlayer().getObjectId());
  1082. statement.setString(3, qs.getQuestName());
  1083. statement.setString(4, var);
  1084. statement.executeUpdate();
  1085. statement.close();
  1086. }
  1087. catch (Exception e)
  1088. {
  1089. _log.log(Level.WARNING, "could not update char quest:", e);
  1090. }
  1091. finally
  1092. {
  1093. L2DatabaseFactory.close(con);
  1094. }
  1095. }
  1096. /**
  1097. * Delete a variable of player's quest from the database.
  1098. * @param qs : object QuestState pointing out the player's quest
  1099. * @param var : String designating the variable characterizing the quest
  1100. */
  1101. public static void deleteQuestVarInDb(QuestState qs, String var)
  1102. {
  1103. Connection con = null;
  1104. try
  1105. {
  1106. con = L2DatabaseFactory.getInstance().getConnection();
  1107. PreparedStatement statement;
  1108. statement = con.prepareStatement("DELETE FROM character_quests WHERE charId=? AND name=? AND var=?");
  1109. statement.setInt(1, qs.getPlayer().getObjectId());
  1110. statement.setString(2, qs.getQuestName());
  1111. statement.setString(3, var);
  1112. statement.executeUpdate();
  1113. statement.close();
  1114. }
  1115. catch (Exception e)
  1116. {
  1117. _log.log(Level.WARNING, "could not delete char quest:", e);
  1118. }
  1119. finally
  1120. {
  1121. L2DatabaseFactory.close(con);
  1122. }
  1123. }
  1124. /**
  1125. * Delete the player's quest from database.
  1126. * @param qs : QuestState pointing out the player's quest
  1127. */
  1128. public static void deleteQuestInDb(QuestState qs)
  1129. {
  1130. Connection con = null;
  1131. try
  1132. {
  1133. con = L2DatabaseFactory.getInstance().getConnection();
  1134. PreparedStatement statement;
  1135. statement = con.prepareStatement("DELETE FROM character_quests WHERE charId=? AND name=?");
  1136. statement.setInt(1, qs.getPlayer().getObjectId());
  1137. statement.setString(2, qs.getQuestName());
  1138. statement.executeUpdate();
  1139. statement.close();
  1140. }
  1141. catch (Exception e)
  1142. {
  1143. _log.log(Level.WARNING, "could not delete char quest:", e);
  1144. }
  1145. finally
  1146. {
  1147. L2DatabaseFactory.close(con);
  1148. }
  1149. }
  1150. /**
  1151. * Create a record in database for quest.<BR><BR>
  1152. * <U><I>Actions :</I></U><BR>
  1153. * Use fucntion createQuestVarInDb() with following parameters :<BR>
  1154. * <LI>QuestState : parameter sq that puts in fields of database :
  1155. * <UL type="square">
  1156. * <LI>charId : ID of the player</LI>
  1157. * <LI>name : name of the quest</LI>
  1158. * </UL>
  1159. * </LI>
  1160. * <LI>var : string "&lt;state&gt;" as the name of the variable for the quest</LI>
  1161. * <LI>val : string corresponding at the ID of the state (in fact, initial state)</LI>
  1162. * @param qs : QuestState
  1163. */
  1164. public static void createQuestInDb(QuestState qs)
  1165. {
  1166. createQuestVarInDb(qs, "<state>", State.getStateName(qs.getState()));
  1167. }
  1168. /**
  1169. * Update informations regarding quest in database.<BR>
  1170. * <U><I>Actions :</I></U><BR>
  1171. * <LI>Get ID state of the quest recorded in object qs</LI>
  1172. * <LI>Test if quest is completed. If true, add a star (*) before the ID state</LI>
  1173. * <LI>Save in database the ID state (with or without the star) for the variable called "&lt;state&gt;" of the quest</LI>
  1174. * @param qs : QuestState
  1175. */
  1176. public static void updateQuestInDb(QuestState qs)
  1177. {
  1178. String val = State.getStateName(qs.getState());
  1179. updateQuestVarInDb(qs, "<state>", val);
  1180. }
  1181. /**
  1182. * Return default html page "You are either not on a quest that involves this NPC.."
  1183. * @param player
  1184. * @return
  1185. */
  1186. public static String getNoQuestMsg(L2PcInstance player)
  1187. {
  1188. final String result = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/noquest.htm");
  1189. if (result != null && result.length() > 0)
  1190. return result;
  1191. return DEFAULT_NO_QUEST_MSG;
  1192. }
  1193. /**
  1194. * Return default html page "This quest has already been completed."
  1195. * @param player
  1196. * @return
  1197. */
  1198. public static String getAlreadyCompletedMsg(L2PcInstance player)
  1199. {
  1200. final String result = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/alreadycompleted.htm");
  1201. if (result != null && result.length() > 0)
  1202. return result;
  1203. return DEFAULT_ALREADY_COMPLETED_MSG;
  1204. }
  1205. /**
  1206. * Add this quest to the list of quests that the passed mob will respond to for the specified Event type.<BR><BR>
  1207. * @param npcId : id of the NPC to register
  1208. * @param eventType : type of event being registered
  1209. * @return L2NpcTemplate : Npc Template corresponding to the npcId, or null if the id is invalid
  1210. */
  1211. public L2NpcTemplate addEventId(int npcId, QuestEventType eventType)
  1212. {
  1213. try
  1214. {
  1215. L2NpcTemplate t = NpcTable.getInstance().getTemplate(npcId);
  1216. if (t != null)
  1217. {
  1218. t.addQuestEvent(eventType, this);
  1219. }
  1220. return t;
  1221. }
  1222. catch (Exception e)
  1223. {
  1224. _log.log(Level.WARNING, "Exception on addEventId(): " + e.getMessage(), e);
  1225. return null;
  1226. }
  1227. }
  1228. /**
  1229. * Add the quest to the NPC's startQuest
  1230. * @param npcIds
  1231. * @return L2NpcTemplate : Start NPC
  1232. */
  1233. public L2NpcTemplate[] addStartNpc(int ...npcIds)
  1234. {
  1235. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1236. int i = 0;
  1237. for (int npcId : npcIds)
  1238. value[i++] = addEventId(npcId, QuestEventType.QUEST_START);
  1239. return value;
  1240. }
  1241. public L2NpcTemplate addStartNpc(int npcId)
  1242. {
  1243. return addEventId(npcId, QuestEventType.QUEST_START);
  1244. }
  1245. /**
  1246. * Add the quest to the NPC's first-talk (default action dialog)
  1247. * @param npcIds
  1248. * @return L2NpcTemplate : Start NPC
  1249. */
  1250. public L2NpcTemplate[] addFirstTalkId(int ...npcIds)
  1251. {
  1252. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1253. int i = 0;
  1254. for (int npcId : npcIds)
  1255. value[i++] = addEventId(npcId, QuestEventType.ON_FIRST_TALK);
  1256. return value;
  1257. }
  1258. public L2NpcTemplate addFirstTalkId(int npcId)
  1259. {
  1260. return addEventId(npcId, QuestEventType.ON_FIRST_TALK);
  1261. }
  1262. /**
  1263. * Add the NPC to the AcquireSkill dialog
  1264. * @param npcIds
  1265. * @return L2NpcTemplate : NPC
  1266. */
  1267. public L2NpcTemplate[] addAcquireSkillId(int ...npcIds)
  1268. {
  1269. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1270. int i = 0;
  1271. for (int npcId : npcIds)
  1272. value[i++] = addEventId(npcId, QuestEventType.ON_SKILL_LEARN);
  1273. return value;
  1274. }
  1275. public L2NpcTemplate addAcquireSkillId(int npcId)
  1276. {
  1277. return addEventId(npcId, QuestEventType.ON_SKILL_LEARN);
  1278. }
  1279. /**
  1280. * Add this quest to the list of quests that the passed mob will respond to for Attack Events.<BR><BR>
  1281. * @param npcIds
  1282. * @return int : attackId
  1283. */
  1284. public L2NpcTemplate[] addAttackId(int ...npcIds)
  1285. {
  1286. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1287. int i = 0;
  1288. for (int npcId : npcIds)
  1289. value[i++] = addEventId(npcId, QuestEventType.ON_ATTACK);
  1290. return value;
  1291. }
  1292. public L2NpcTemplate addAttackId(int npcId)
  1293. {
  1294. return addEventId(npcId, QuestEventType.ON_ATTACK);
  1295. }
  1296. /**
  1297. * Add this quest to the list of quests that the passed mob will respond to for Kill Events.<BR><BR>
  1298. * @param killIds
  1299. * @return int : killId
  1300. */
  1301. public L2NpcTemplate[] addKillId(int ...killIds)
  1302. {
  1303. L2NpcTemplate[] value = new L2NpcTemplate[killIds.length];
  1304. int i = 0;
  1305. for (int killId : killIds)
  1306. value[i++] = addEventId(killId, QuestEventType.ON_KILL);
  1307. return value;
  1308. }
  1309. public L2NpcTemplate addKillId(int npcId)
  1310. {
  1311. return addEventId(npcId, QuestEventType.ON_KILL);
  1312. }
  1313. /**
  1314. * Add this quest to the list of quests that the passed npc will respond to for Talk Events.<BR><BR>
  1315. * @param talkIds : ID of the NPC
  1316. * @return int : ID of the NPC
  1317. */
  1318. public L2NpcTemplate[] addTalkId(int ...talkIds)
  1319. {
  1320. L2NpcTemplate[] value = new L2NpcTemplate[talkIds.length];
  1321. int i = 0;
  1322. for (int talkId : talkIds)
  1323. value[i++] = addEventId(talkId, QuestEventType.ON_TALK);
  1324. return value;
  1325. }
  1326. public L2NpcTemplate addTalkId(int npcId)
  1327. {
  1328. return addEventId(npcId, QuestEventType.ON_TALK);
  1329. }
  1330. /**
  1331. * Add this quest to the list of quests that the passed npc will respond to for Spawn Events.<BR><BR>
  1332. * @param npcIds : ID of the NPC
  1333. * @return int : ID of the NPC
  1334. */
  1335. public L2NpcTemplate[] addSpawnId(int ...npcIds)
  1336. {
  1337. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1338. int i = 0;
  1339. for (int npcId : npcIds)
  1340. value[i++] = addEventId(npcId, QuestEventType.ON_SPAWN);
  1341. return value;
  1342. }
  1343. public L2NpcTemplate addSpawnId(int npcId)
  1344. {
  1345. return addEventId(npcId, QuestEventType.ON_SPAWN);
  1346. }
  1347. /**
  1348. * Add this quest to the list of quests that the passed npc will respond to for Skill-See Events.<BR><BR>
  1349. * @param npcIds : ID of the NPC
  1350. * @return int : ID of the NPC
  1351. */
  1352. public L2NpcTemplate[] addSkillSeeId(int ...npcIds)
  1353. {
  1354. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1355. int i = 0;
  1356. for (int npcId : npcIds)
  1357. value[i++] = addEventId(npcId, QuestEventType.ON_SKILL_SEE);
  1358. return value;
  1359. }
  1360. public L2NpcTemplate addSkillSeeId(int npcId)
  1361. {
  1362. return addEventId(npcId, QuestEventType.ON_SKILL_SEE);
  1363. }
  1364. public L2NpcTemplate[] addSpellFinishedId(int ...npcIds)
  1365. {
  1366. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1367. int i = 0;
  1368. for (int npcId : npcIds)
  1369. value[i++] = addEventId(npcId, QuestEventType.ON_SPELL_FINISHED);
  1370. return value;
  1371. }
  1372. public L2NpcTemplate addSpellFinishedId(int npcId)
  1373. {
  1374. return addEventId(npcId, QuestEventType.ON_SPELL_FINISHED);
  1375. }
  1376. public L2NpcTemplate[] addTrapActionId(int ...npcIds)
  1377. {
  1378. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1379. int i = 0;
  1380. for (int npcId : npcIds)
  1381. value[i++] = addEventId(npcId, QuestEventType.ON_TRAP_ACTION);
  1382. return value;
  1383. }
  1384. public L2NpcTemplate addTrapActionId(int npcId)
  1385. {
  1386. return addEventId(npcId, QuestEventType.ON_TRAP_ACTION);
  1387. }
  1388. /**
  1389. * Add this quest to the list of quests that the passed npc will respond to for Faction Call Events.<BR><BR>
  1390. * @param npcIds : ID of the NPC
  1391. * @return int : ID of the NPC
  1392. */
  1393. public L2NpcTemplate[] addFactionCallId(int ...npcIds)
  1394. {
  1395. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1396. int i = 0;
  1397. for (int npcId : npcIds)
  1398. value[i++] = addEventId(npcId, QuestEventType.ON_FACTION_CALL);
  1399. return value;
  1400. }
  1401. public L2NpcTemplate addFactionCallId(int npcId)
  1402. {
  1403. return addEventId(npcId, QuestEventType.ON_FACTION_CALL);
  1404. }
  1405. /**
  1406. * Add this quest to the list of quests that the passed npc will respond to for Character See Events.<BR><BR>
  1407. * @param npcIds : ID of the NPC
  1408. * @return int : ID of the NPC
  1409. */
  1410. public L2NpcTemplate[] addAggroRangeEnterId(int ...npcIds)
  1411. {
  1412. L2NpcTemplate[] value = new L2NpcTemplate[npcIds.length];
  1413. int i = 0;
  1414. for (int npcId : npcIds)
  1415. value[i++] = addEventId(npcId, QuestEventType.ON_AGGRO_RANGE_ENTER);
  1416. return value;
  1417. }
  1418. public L2NpcTemplate addAggroRangeEnterId(int npcId)
  1419. {
  1420. return addEventId(npcId, QuestEventType.ON_AGGRO_RANGE_ENTER);
  1421. }
  1422. public L2ZoneType[] addEnterZoneId(int ...zoneIds)
  1423. {
  1424. L2ZoneType[] value = new L2ZoneType[zoneIds.length];
  1425. int i = 0;
  1426. for (int zoneId : zoneIds)
  1427. {
  1428. try
  1429. {
  1430. L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId);
  1431. if (zone != null)
  1432. {
  1433. zone.addQuestEvent(Quest.QuestEventType.ON_ENTER_ZONE, this);
  1434. }
  1435. value[i++] = zone;
  1436. }
  1437. catch (Exception e)
  1438. {
  1439. _log.log(Level.WARNING, "Exception on addEnterZoneId(): " + e.getMessage(), e);
  1440. continue;
  1441. }
  1442. }
  1443. return value;
  1444. }
  1445. public L2ZoneType addEnterZoneId(int zoneId)
  1446. {
  1447. try
  1448. {
  1449. L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId);
  1450. if (zone != null)
  1451. {
  1452. zone.addQuestEvent(Quest.QuestEventType.ON_ENTER_ZONE, this);
  1453. }
  1454. return zone;
  1455. }
  1456. catch (Exception e)
  1457. {
  1458. _log.log(Level.WARNING, "Exception on addEnterZoneId(): " + e.getMessage(), e);
  1459. return null;
  1460. }
  1461. }
  1462. public L2ZoneType[] addExitZoneId(int ...zoneIds)
  1463. {
  1464. L2ZoneType[] value = new L2ZoneType[zoneIds.length];
  1465. int i = 0;
  1466. for (int zoneId : zoneIds)
  1467. {
  1468. try
  1469. {
  1470. L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId);
  1471. if (zone != null)
  1472. {
  1473. zone.addQuestEvent(Quest.QuestEventType.ON_EXIT_ZONE, this);
  1474. }
  1475. value[i++] = zone;
  1476. }
  1477. catch (Exception e)
  1478. {
  1479. _log.log(Level.WARNING, "Exception on addEnterZoneId(): " + e.getMessage(), e);
  1480. continue;
  1481. }
  1482. }
  1483. return value;
  1484. }
  1485. public L2ZoneType addExitZoneId(int zoneId)
  1486. {
  1487. try
  1488. {
  1489. L2ZoneType zone = ZoneManager.getInstance().getZoneById(zoneId);
  1490. if (zone != null)
  1491. {
  1492. zone.addQuestEvent(Quest.QuestEventType.ON_EXIT_ZONE, this);
  1493. }
  1494. return zone;
  1495. }
  1496. catch (Exception e)
  1497. {
  1498. _log.log(Level.WARNING, "Exception on addExitZoneId(): " + e.getMessage(), e);
  1499. return null;
  1500. }
  1501. }
  1502. // returns a random party member's L2PcInstance for the passed player's party
  1503. // returns the passed player if he has no party.
  1504. public L2PcInstance getRandomPartyMember(L2PcInstance player)
  1505. {
  1506. // NPE prevention. If the player is null, there is nothing to return
  1507. if (player == null)
  1508. return null;
  1509. if ((player.getParty() == null) || (player.getParty().getPartyMembers().isEmpty()))
  1510. return player;
  1511. L2Party party = player.getParty();
  1512. return party.getPartyMembers().get(Rnd.get(party.getPartyMembers().size()));
  1513. }
  1514. /**
  1515. * Auxilary function for party quests.
  1516. * Note: This function is only here because of how commonly it may be used by quest developers.
  1517. * For any variations on this function, the quest script can always handle things on its own
  1518. * @param player the instance of a player whose party is to be searched
  1519. * @param value the value of the "cond" variable that must be matched
  1520. * @return L2PcInstance: L2PcInstance for a random party member that matches the specified
  1521. * condition, or null if no match.
  1522. */
  1523. public L2PcInstance getRandomPartyMember(L2PcInstance player, String value)
  1524. {
  1525. return getRandomPartyMember(player, "cond", value);
  1526. }
  1527. /**
  1528. * Auxilary function for party quests.
  1529. * Note: This function is only here because of how commonly it may be used by quest developers.
  1530. * For any variations on this function, the quest script can always handle things on its own
  1531. * @param player the instance of a player whose party is to be searched
  1532. * @param var
  1533. * @param value a tuple specifying a quest condition that must be satisfied for a party member to be considered.
  1534. * @return L2PcInstance: L2PcInstance for a random party member that matches the specified
  1535. * condition, or null if no match. If the var is null, any random party
  1536. * member is returned (i.e. no condition is applied).
  1537. * The party member must be within 1500 distance from the target of the reference
  1538. * player, or if no target exists, 1500 distance from the player itself.
  1539. */
  1540. public L2PcInstance getRandomPartyMember(L2PcInstance player, String var, String value)
  1541. {
  1542. // if no valid player instance is passed, there is nothing to check...
  1543. if (player == null)
  1544. return null;
  1545. // for null var condition, return any random party member.
  1546. if (var == null)
  1547. return getRandomPartyMember(player);
  1548. // normal cases...if the player is not in a party, check the player's state
  1549. QuestState temp = null;
  1550. L2Party party = player.getParty();
  1551. // if this player is not in a party, just check if this player instance matches the conditions itself
  1552. if ((party == null) || (party.getPartyMembers().isEmpty()))
  1553. {
  1554. temp = player.getQuestState(getName());
  1555. if ((temp != null) && (temp.get(var) != null) && (temp.get(var)).equalsIgnoreCase(value))
  1556. return player; // match
  1557. return null; // no match
  1558. }
  1559. // if the player is in a party, gather a list of all matching party members (possibly
  1560. // including this player)
  1561. FastList<L2PcInstance> candidates = new FastList<L2PcInstance>();
  1562. // get the target for enforcing distance limitations.
  1563. L2Object target = player.getTarget();
  1564. if (target == null)
  1565. target = player;
  1566. for (L2PcInstance partyMember : party.getPartyMembers())
  1567. {
  1568. if (partyMember == null)
  1569. continue;
  1570. temp = partyMember.getQuestState(getName());
  1571. if ((temp != null) && (temp.get(var) != null) && (temp.get(var)).equalsIgnoreCase(value) && partyMember.isInsideRadius(target, 1500, true, false))
  1572. candidates.add(partyMember);
  1573. }
  1574. // if there was no match, return null...
  1575. if (candidates.isEmpty())
  1576. return null;
  1577. // if a match was found from the party, return one of them at random.
  1578. return candidates.get(Rnd.get(candidates.size()));
  1579. }
  1580. /**
  1581. * Auxilary function for party quests.
  1582. * Note: This function is only here because of how commonly it may be used by quest developers.
  1583. * For any variations on this function, the quest script can always handle things on its own
  1584. * @param player the instance of a player whose party is to be searched
  1585. * @param state the state in which the party member's queststate must be in order to be considered.
  1586. * @return L2PcInstance: L2PcInstance for a random party member that matches the specified
  1587. * condition, or null if no match. If the var is null, any random party
  1588. * member is returned (i.e. no condition is applied).
  1589. */
  1590. public L2PcInstance getRandomPartyMemberState(L2PcInstance player, byte state)
  1591. {
  1592. // if no valid player instance is passed, there is nothing to check...
  1593. if (player == null)
  1594. return null;
  1595. // normal cases...if the player is not in a partym check the player's state
  1596. QuestState temp = null;
  1597. L2Party party = player.getParty();
  1598. // if this player is not in a party, just check if this player instance matches the conditions itself
  1599. if ((party == null) || (party.getPartyMembers().isEmpty()))
  1600. {
  1601. temp = player.getQuestState(getName());
  1602. if ((temp != null) && (temp.getState() == state))
  1603. return player; // match
  1604. return null; // no match
  1605. }
  1606. // if the player is in a party, gather a list of all matching party members (possibly
  1607. // including this player)
  1608. FastList<L2PcInstance> candidates = new FastList<L2PcInstance>();
  1609. // get the target for enforcing distance limitations.
  1610. L2Object target = player.getTarget();
  1611. if (target == null)
  1612. target = player;
  1613. for (L2PcInstance partyMember : party.getPartyMembers())
  1614. {
  1615. if (partyMember == null)
  1616. continue;
  1617. temp = partyMember.getQuestState(getName());
  1618. if ((temp != null) && (temp.getState() == state) && partyMember.isInsideRadius(target, 1500, true, false))
  1619. candidates.add(partyMember);
  1620. }
  1621. // if there was no match, return null...
  1622. if (candidates.isEmpty())
  1623. return null;
  1624. // if a match was found from the party, return one of them at random.
  1625. return candidates.get(Rnd.get(candidates.size()));
  1626. }
  1627. /**
  1628. * Show HTML file to client
  1629. * @param player
  1630. * @param fileName
  1631. * @return String : message sent to client
  1632. */
  1633. public String showHtmlFile(L2PcInstance player, String fileName)
  1634. {
  1635. boolean questwindow = true;
  1636. if (fileName.endsWith(".html"))
  1637. questwindow = false;
  1638. int questId = getQuestIntId();
  1639. //Create handler to file linked to the quest
  1640. String content = getHtm(player.getHtmlPrefix(), fileName);
  1641. if (player.getTarget() != null)
  1642. content = content.replaceAll("%objectId%", String.valueOf(player.getTarget().getObjectId()));
  1643. //Send message to client if message not empty
  1644. if (content != null)
  1645. {
  1646. if (questwindow && questId > 0 && questId < 20000 && questId != 999)
  1647. {
  1648. NpcQuestHtmlMessage npcReply = new NpcQuestHtmlMessage(5,questId);
  1649. npcReply.setHtml(content);
  1650. npcReply.replace("%playername%", player.getName());
  1651. player.sendPacket(npcReply);
  1652. }
  1653. else
  1654. {
  1655. NpcHtmlMessage npcReply = new NpcHtmlMessage(5);
  1656. npcReply.setHtml(content);
  1657. npcReply.replace("%playername%", player.getName());
  1658. player.sendPacket(npcReply);
  1659. }
  1660. player.sendPacket(ActionFailed.STATIC_PACKET);
  1661. }
  1662. return content;
  1663. }
  1664. /**
  1665. * Return HTML file contents
  1666. * @param prefix player's language prefix.
  1667. * @param fileName the html file to be get.
  1668. * @return
  1669. */
  1670. public String getHtm(String prefix, String fileName)
  1671. {
  1672. String content = HtmCache.getInstance().getHtm(prefix, "data/scripts/" + getDescr().toLowerCase() + "/" + getName() + "/" + fileName);
  1673. if (content == null)
  1674. {
  1675. content = HtmCache.getInstance().getHtm(prefix, "data/scripts/quests/Q" + getName() + "/" + fileName);
  1676. if (content == null)
  1677. content = HtmCache.getInstance().getHtmForce(prefix, "data/scripts/quests/" + getName() + "/" + fileName);
  1678. }
  1679. return content;
  1680. }
  1681. /**
  1682. * Add a temporary (quest) spawn
  1683. * @param npcId
  1684. * @param cha
  1685. * @return instance of newly spawned npc
  1686. */
  1687. public L2Npc addSpawn(int npcId, L2Character cha)
  1688. {
  1689. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), false, 0, false);
  1690. }
  1691. /**
  1692. * Add a temporary (quest) spawn
  1693. * @param npcId
  1694. * @param cha
  1695. * @param isSummonSpawn
  1696. * @return instance of newly spawned npc with summon animation
  1697. */
  1698. public L2Npc addSpawn(int npcId, L2Character cha, boolean isSummonSpawn)
  1699. {
  1700. return addSpawn(npcId, cha.getX(), cha.getY(), cha.getZ(), cha.getHeading(), false, 0, isSummonSpawn);
  1701. }
  1702. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffSet, long despawnDelay)
  1703. {
  1704. return addSpawn(npcId, x, y, z, heading, randomOffSet, despawnDelay, false);
  1705. }
  1706. public L2Npc addSpawn(int npcId, Location loc, boolean randomOffSet, long despawnDelay)
  1707. {
  1708. return addSpawn(npcId, loc.getX(), loc.getY(), loc.getZ(), loc.getHeading(), randomOffSet, despawnDelay, false);
  1709. }
  1710. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, long despawnDelay, boolean isSummonSpawn)
  1711. {
  1712. return addSpawn(npcId, x, y, z, heading, randomOffset, despawnDelay, isSummonSpawn, 0);
  1713. }
  1714. public L2Npc addSpawn(int npcId, Location loc, boolean randomOffset, long despawnDelay, boolean isSummonSpawn)
  1715. {
  1716. return addSpawn(npcId, loc.getX(), loc.getY(), loc.getZ(), loc.getHeading(), randomOffset, despawnDelay, isSummonSpawn, 0);
  1717. }
  1718. public L2Npc addSpawn(int npcId, int x, int y, int z, int heading, boolean randomOffset, long despawnDelay, boolean isSummonSpawn, int instanceId)
  1719. {
  1720. L2Npc result = null;
  1721. try
  1722. {
  1723. L2NpcTemplate template = NpcTable.getInstance().getTemplate(npcId);
  1724. if (template != null)
  1725. {
  1726. // Sometimes, even if the quest script specifies some xyz (for example npc.getX() etc) by the time the code
  1727. // reaches here, xyz have become 0! Also, a questdev might have purposely set xy to 0,0...however,
  1728. // the spawn code is coded such that if x=y=0, it looks into location for the spawn loc! This will NOT work
  1729. // with quest spawns! For both of the above cases, we need a fail-safe spawn. For this, we use the
  1730. // default spawn location, which is at the player's loc.
  1731. if ((x == 0) && (y == 0))
  1732. {
  1733. _log.log(Level.SEVERE, "Failed to adjust bad locks for quest spawn! Spawn aborted!");
  1734. return null;
  1735. }
  1736. if (randomOffset)
  1737. {
  1738. int offset;
  1739. offset = Rnd.get(2); // Get the direction of the offset
  1740. if (offset == 0)
  1741. {
  1742. offset = -1;
  1743. } // make offset negative
  1744. offset *= Rnd.get(50, 100);
  1745. x += offset;
  1746. offset = Rnd.get(2); // Get the direction of the offset
  1747. if (offset == 0)
  1748. {
  1749. offset = -1;
  1750. } // make offset negative
  1751. offset *= Rnd.get(50, 100);
  1752. y += offset;
  1753. }
  1754. L2Spawn spawn = new L2Spawn(template);
  1755. spawn.setInstanceId(instanceId);
  1756. spawn.setHeading(heading);
  1757. spawn.setLocx(x);
  1758. spawn.setLocy(y);
  1759. spawn.setLocz(z + 20);
  1760. spawn.stopRespawn();
  1761. result = spawn.spawnOne(isSummonSpawn);
  1762. if (despawnDelay > 0)
  1763. result.scheduleDespawn(despawnDelay);
  1764. return result;
  1765. }
  1766. }
  1767. catch (Exception e1)
  1768. {
  1769. _log.warning("Could not spawn Npc " + npcId);
  1770. }
  1771. return null;
  1772. }
  1773. public L2Trap addTrap(int trapId, int x, int y, int z, int heading, L2Skill skill, int instanceId)
  1774. {
  1775. L2NpcTemplate TrapTemplate = NpcTable.getInstance().getTemplate(trapId);
  1776. L2Trap trap = new L2TrapInstance(IdFactory.getInstance().getNextId(), TrapTemplate, instanceId, -1, skill);
  1777. trap.setCurrentHp(trap.getMaxHp());
  1778. trap.setCurrentMp(trap.getMaxMp());
  1779. trap.setIsInvul(true);
  1780. trap.setHeading(heading);
  1781. //L2World.getInstance().storeObject(trap);
  1782. trap.spawnMe(x, y, z);
  1783. return trap;
  1784. }
  1785. public L2Npc addMinion(L2MonsterInstance master, int minionId)
  1786. {
  1787. return MinionList.spawnMinion(master, minionId);
  1788. }
  1789. public int[] getRegisteredItemIds()
  1790. {
  1791. return questItemIds;
  1792. }
  1793. @Override
  1794. public String getScriptName()
  1795. {
  1796. return this.getName();
  1797. }
  1798. @Override
  1799. public void setActive(boolean status)
  1800. {
  1801. // TODO implement me
  1802. }
  1803. @Override
  1804. public boolean reload()
  1805. {
  1806. unload();
  1807. return super.reload();
  1808. }
  1809. @Override
  1810. public boolean unload()
  1811. {
  1812. return unload(true);
  1813. }
  1814. public boolean unload(boolean removeFromList)
  1815. {
  1816. this.saveGlobalData();
  1817. // cancel all pending timers before reloading.
  1818. // if timers ought to be restarted, the quest can take care of it
  1819. // with its code (example: save global data indicating what timer must
  1820. // be restarted).
  1821. for (FastList<QuestTimer> timers : _allEventTimers.values())
  1822. for (QuestTimer timer : timers)
  1823. timer.cancel();
  1824. _allEventTimers.clear();
  1825. if (removeFromList)
  1826. return QuestManager.getInstance().removeQuest(this);
  1827. return true;
  1828. }
  1829. @Override
  1830. public ScriptManager<?> getScriptManager()
  1831. {
  1832. return QuestManager.getInstance();
  1833. }
  1834. public void setOnEnterWorld(boolean val)
  1835. {
  1836. _onEnterWorld = val;
  1837. }
  1838. public boolean getOnEnterWorld()
  1839. {
  1840. return _onEnterWorld;
  1841. }
  1842. /**
  1843. * @return true if the quest script is a custom quest.
  1844. */
  1845. public boolean isCustomQuest()
  1846. {
  1847. return _isCustom;
  1848. }
  1849. public void setOlympiadUse(boolean val)
  1850. {
  1851. _isOlympiadUse = val;
  1852. }
  1853. public boolean isOlympiadUse()
  1854. {
  1855. return _isOlympiadUse;
  1856. }
  1857. /**
  1858. * @param val if true the quest script will be set as custom quest.
  1859. */
  1860. public void setIsCustom(boolean val)
  1861. {
  1862. _isCustom = val;
  1863. }
  1864. public final void notifyOlympiadWin(L2PcInstance winner, CompetitionType type)
  1865. {
  1866. try
  1867. {
  1868. onOlympiadWin(winner, type);
  1869. }
  1870. catch (Exception e)
  1871. {
  1872. showError(winner, e);
  1873. }
  1874. }
  1875. public final void notifyOlympiadLoose(L2PcInstance looser, CompetitionType type)
  1876. {
  1877. try
  1878. {
  1879. onOlympiadLoose(looser, type);
  1880. }
  1881. catch (Exception e)
  1882. {
  1883. showError(looser, e);
  1884. }
  1885. }
  1886. public void onOlympiadWin(L2PcInstance winner, CompetitionType type)
  1887. {
  1888. }
  1889. public void onOlympiadLoose(L2PcInstance winner, CompetitionType type)
  1890. {
  1891. }
  1892. /**
  1893. * Return the quantity of one sort of item hold by the player
  1894. * @param player
  1895. * @param itemId : ID of the item wanted to be count
  1896. * @return long
  1897. */
  1898. public long getQuestItemsCount(L2PcInstance player, int itemId)
  1899. {
  1900. long count = 0;
  1901. for (L2ItemInstance item : player.getInventory().getItems())
  1902. if (item != null && item.getItemId() == itemId)
  1903. count += item.getCount();
  1904. return count;
  1905. }
  1906. /**
  1907. * @param player
  1908. * @param itemId the item Id of the item you're looking for
  1909. * @return true if item exists in player's inventory, false - if not
  1910. */
  1911. public boolean hasQuestItems(L2PcInstance player, int itemId)
  1912. {
  1913. return player.getInventory().getItemByItemId(itemId) != null;
  1914. }
  1915. /**
  1916. * Return the level of enchantment on the weapon of the player(Done specifically for weapon SA's)
  1917. * @param player
  1918. * @param itemId : ID of the item to check enchantment
  1919. * @return int
  1920. */
  1921. public int getEnchantLevel(L2PcInstance player, int itemId)
  1922. {
  1923. L2ItemInstance enchanteditem = player.getInventory().getItemByItemId(itemId);
  1924. if (enchanteditem == null)
  1925. return 0;
  1926. return enchanteditem.getEnchantLevel();
  1927. }
  1928. /**
  1929. * Give adena to the player
  1930. * @param player
  1931. * @param count
  1932. * @param applyRates
  1933. */
  1934. public void giveAdena(L2PcInstance player, long count, boolean applyRates)
  1935. {
  1936. giveItems(player, PcInventory.ADENA_ID, count, applyRates ? 0 : 1);
  1937. }
  1938. /**
  1939. * Give reward to player using multiplier's
  1940. * @param player
  1941. * @param itemId
  1942. * @param count
  1943. */
  1944. public void rewardItems(L2PcInstance player, int itemId, long count)
  1945. {
  1946. if (count <= 0)
  1947. return;
  1948. L2ItemInstance _tmpItem = ItemTable.getInstance().createDummyItem(itemId);
  1949. if (_tmpItem == null)
  1950. return;
  1951. if (itemId == PcInventory.ADENA_ID)
  1952. {
  1953. count = (long) (count * Config.RATE_QUEST_REWARD_ADENA);
  1954. }
  1955. else if (Config.RATE_QUEST_REWARD_USE_MULTIPLIERS)
  1956. {
  1957. if(_tmpItem.isEtcItem())
  1958. {
  1959. switch (_tmpItem.getEtcItem().getItemType())
  1960. {
  1961. case POTION:
  1962. count = (long) (count * Config.RATE_QUEST_REWARD_POTION);
  1963. break;
  1964. case SCRL_ENCHANT_WP:
  1965. case SCRL_ENCHANT_AM:
  1966. case SCROLL:
  1967. count = (long) (count * Config.RATE_QUEST_REWARD_SCROLL);
  1968. break;
  1969. case RECIPE:
  1970. count = (long) (count * Config.RATE_QUEST_REWARD_RECIPE);
  1971. break;
  1972. case MATERIAL:
  1973. count = (long) (count * Config.RATE_QUEST_REWARD_MATERIAL);
  1974. break;
  1975. default:
  1976. count = (long) (count * Config.RATE_QUEST_REWARD);
  1977. }
  1978. }
  1979. }
  1980. else
  1981. {
  1982. count = (long) (count * Config.RATE_QUEST_REWARD);
  1983. }
  1984. // Add items to player's inventory
  1985. L2ItemInstance item = player.getInventory().addItem("Quest", itemId, count, player, player.getTarget());
  1986. if (item == null)
  1987. return;
  1988. // If item for reward is gold, send message of gold reward to client
  1989. if (itemId == PcInventory.ADENA_ID)
  1990. {
  1991. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA);
  1992. smsg.addItemNumber(count);
  1993. player.sendPacket(smsg);
  1994. }
  1995. // Otherwise, send message of object reward to client
  1996. else
  1997. {
  1998. if (count > 1)
  1999. {
  2000. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
  2001. smsg.addItemName(item);
  2002. smsg.addItemNumber(count);
  2003. player.sendPacket(smsg);
  2004. }
  2005. else
  2006. {
  2007. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
  2008. smsg.addItemName(item);
  2009. player.sendPacket(smsg);
  2010. }
  2011. }
  2012. // send packets
  2013. StatusUpdate su = new StatusUpdate(player);
  2014. su.addAttribute(StatusUpdate.CUR_LOAD, player.getCurrentLoad());
  2015. player.sendPacket(su);
  2016. }
  2017. /**
  2018. * Give item/reward to the player
  2019. * @param player
  2020. * @param itemId
  2021. * @param count
  2022. */
  2023. public void giveItems(L2PcInstance player, int itemId, long count)
  2024. {
  2025. giveItems(player, itemId, count, 0);
  2026. }
  2027. public void giveItems(L2PcInstance player, int itemId, long count, int enchantlevel)
  2028. {
  2029. if (count <= 0)
  2030. return;
  2031. // If item for reward is adena (ID=57), modify count with rate for quest reward if rates available
  2032. if (itemId == PcInventory.ADENA_ID && !(enchantlevel > 0))
  2033. count = (long) (count * Config.RATE_QUEST_REWARD_ADENA);
  2034. // Add items to player's inventory
  2035. L2ItemInstance item = player.getInventory().addItem("Quest", itemId, count, player, player.getTarget());
  2036. if (item == null)
  2037. return;
  2038. // set enchant level for item if that item is not adena
  2039. if (enchantlevel > 0 && itemId != PcInventory.ADENA_ID)
  2040. item.setEnchantLevel(enchantlevel);
  2041. // If item for reward is gold, send message of gold reward to client
  2042. if (itemId == PcInventory.ADENA_ID)
  2043. {
  2044. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA);
  2045. smsg.addItemNumber(count);
  2046. player.sendPacket(smsg);
  2047. }
  2048. // Otherwise, send message of object reward to client
  2049. else
  2050. {
  2051. if (count > 1)
  2052. {
  2053. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
  2054. smsg.addItemName(item);
  2055. smsg.addItemNumber(count);
  2056. player.sendPacket(smsg);
  2057. }
  2058. else
  2059. {
  2060. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
  2061. smsg.addItemName(item);
  2062. player.sendPacket(smsg);
  2063. }
  2064. }
  2065. // send packets
  2066. StatusUpdate su = new StatusUpdate(player);
  2067. su.addAttribute(StatusUpdate.CUR_LOAD, player.getCurrentLoad());
  2068. player.sendPacket(su);
  2069. }
  2070. public void giveItems(L2PcInstance player, int itemId, long count, byte attributeId, int attributeLevel)
  2071. {
  2072. if (count <= 0)
  2073. return;
  2074. // Add items to player's inventory
  2075. L2ItemInstance item = player.getInventory().addItem("Quest", itemId, count, player, player.getTarget());
  2076. if (item == null)
  2077. return;
  2078. // set enchant level for item if that item is not adena
  2079. if (attributeId >= 0 && attributeLevel > 0)
  2080. {
  2081. item.setElementAttr(attributeId, attributeLevel);
  2082. if (item.isEquipped())
  2083. item.updateElementAttrBonus(player);
  2084. InventoryUpdate iu = new InventoryUpdate();
  2085. iu.addModifiedItem(item);
  2086. player.sendPacket(iu);
  2087. }
  2088. // If item for reward is gold, send message of gold reward to client
  2089. if (itemId == PcInventory.ADENA_ID)
  2090. {
  2091. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S1_ADENA);
  2092. smsg.addItemNumber(count);
  2093. player.sendPacket(smsg);
  2094. }
  2095. // Otherwise, send message of object reward to client
  2096. else
  2097. {
  2098. if (count > 1)
  2099. {
  2100. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_S2_S1_S);
  2101. smsg.addItemName(item);
  2102. smsg.addItemNumber(count);
  2103. player.sendPacket(smsg);
  2104. }
  2105. else
  2106. {
  2107. SystemMessage smsg = SystemMessage.getSystemMessage(SystemMessageId.EARNED_ITEM_S1);
  2108. smsg.addItemName(item);
  2109. player.sendPacket(smsg);
  2110. }
  2111. }
  2112. // send packets
  2113. StatusUpdate su = new StatusUpdate(player);
  2114. su.addAttribute(StatusUpdate.CUR_LOAD, player.getCurrentLoad());
  2115. player.sendPacket(su);
  2116. }
  2117. /**
  2118. * Drop Quest item using Config.RATE_QUEST_DROP
  2119. * @param player
  2120. * @param itemId int Item Identifier of the item to be dropped
  2121. * @param count (minCount, maxCount) long Quantity of items to be dropped
  2122. * @param neededCount Quantity of items needed for quest
  2123. * @param dropChance int Base chance of drop, same as in droplist
  2124. * @param sound boolean indicating whether to play sound
  2125. * @return boolean indicating whether player has requested number of items
  2126. */
  2127. public boolean dropQuestItems(L2PcInstance player, int itemId, int count, long neededCount, int dropChance, boolean sound)
  2128. {
  2129. return dropQuestItems(player, itemId, count, count, neededCount, dropChance, sound);
  2130. }
  2131. public boolean dropQuestItems(L2PcInstance player, int itemId, int minCount, int maxCount, long neededCount, int dropChance, boolean sound)
  2132. {
  2133. dropChance *= Config.RATE_QUEST_DROP / ((player.getParty() != null) ? player.getParty().getMemberCount() : 1);
  2134. long currentCount = getQuestItemsCount(player, itemId);
  2135. if (neededCount > 0 && currentCount >= neededCount)
  2136. return true;
  2137. if (currentCount >= neededCount)
  2138. return true;
  2139. long itemCount = 0;
  2140. int random = Rnd.get(L2DropData.MAX_CHANCE);
  2141. while (random < dropChance)
  2142. {
  2143. // Get the item quantity dropped
  2144. if (minCount < maxCount)
  2145. itemCount += Rnd.get(minCount, maxCount);
  2146. else if (minCount == maxCount)
  2147. itemCount += minCount;
  2148. else
  2149. itemCount++;
  2150. // Prepare for next iteration if dropChance > L2DropData.MAX_CHANCE
  2151. dropChance -= L2DropData.MAX_CHANCE;
  2152. }
  2153. if (itemCount > 0)
  2154. {
  2155. // if over neededCount, just fill the gap
  2156. if (neededCount > 0 && currentCount + itemCount > neededCount)
  2157. itemCount = neededCount - currentCount;
  2158. // Inventory slot check
  2159. if (!player.getInventory().validateCapacityByItemId(itemId))
  2160. return false;
  2161. // Give the item to Player
  2162. player.addItem("Quest", itemId, itemCount, player.getTarget(), true);
  2163. if (sound)
  2164. playSound(player, (currentCount + itemCount < neededCount) ? "Itemsound.quest_itemget" : "Itemsound.quest_middle");
  2165. }
  2166. return (neededCount > 0 && currentCount + itemCount >= neededCount);
  2167. }
  2168. /**
  2169. * Remove items from player's inventory when talking to NPC in order to have rewards.<BR><BR>
  2170. * <U><I>Actions :</I></U>
  2171. * <LI>Destroy quantity of items wanted</LI>
  2172. * <LI>Send new inventory list to player</LI>
  2173. * @param player
  2174. * @param itemId : Identifier of the item
  2175. * @param count : Quantity of items to destroy
  2176. */
  2177. public void takeItems(L2PcInstance player, int itemId, long count)
  2178. {
  2179. // Get object item from player's inventory list
  2180. L2ItemInstance item = player.getInventory().getItemByItemId(itemId);
  2181. if (item == null)
  2182. return;
  2183. // Tests on count value in order not to have negative value
  2184. if (count < 0 || count > item.getCount())
  2185. count = item.getCount();
  2186. // Destroy the quantity of items wanted
  2187. if (item.isEquipped())
  2188. {
  2189. L2ItemInstance[] unequiped = player.getInventory().unEquipItemInBodySlotAndRecord(item.getItem().getBodyPart());
  2190. InventoryUpdate iu = new InventoryUpdate();
  2191. for (L2ItemInstance itm: unequiped)
  2192. iu.addModifiedItem(itm);
  2193. player.sendPacket(iu);
  2194. player.broadcastUserInfo();
  2195. }
  2196. player.destroyItemByItemId("Quest", itemId, count, player, true);
  2197. }
  2198. /**
  2199. * Send a packet in order to play sound at client terminal
  2200. * @param player
  2201. * @param sound
  2202. */
  2203. public void playSound(L2PcInstance player, String sound)
  2204. {
  2205. player.sendPacket(new PlaySound(sound));
  2206. }
  2207. /**
  2208. * Add XP and SP as quest reward
  2209. * @param player
  2210. * @param exp
  2211. * @param sp
  2212. */
  2213. public void addExpAndSp(L2PcInstance player, int exp, int sp)
  2214. {
  2215. player.addExpAndSp((int) player.calcStat(Stats.EXPSP_RATE, exp * Config.RATE_QUEST_REWARD_XP, null, null), (int) player.calcStat(Stats.EXPSP_RATE, sp * Config.RATE_QUEST_REWARD_SP, null, null));
  2216. }
  2217. /**
  2218. * Return random value
  2219. * @param max : max value for randomization
  2220. * @return int
  2221. */
  2222. public int getRandom(int max)
  2223. {
  2224. return Rnd.get(max);
  2225. }
  2226. /**
  2227. * @param player
  2228. * @param loc
  2229. * @return number of ticks from GameTimeController
  2230. */
  2231. public int getItemEquipped(L2PcInstance player, int loc)
  2232. {
  2233. return player.getInventory().getPaperdollItemId(loc);
  2234. }
  2235. /**
  2236. * Return the number of ticks from the GameTimeController
  2237. * @return int
  2238. */
  2239. public int getGameTicks()
  2240. {
  2241. return GameTimeController.getGameTicks();
  2242. }
  2243. }