QuestLink.java 9.8 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320
  1. /*
  2. * Copyright (C) 2004-2014 L2J DataPack
  3. *
  4. * This file is part of L2J DataPack.
  5. *
  6. * L2J DataPack is free software: you can redistribute it and/or modify
  7. * it under the terms of the GNU General Public License as published by
  8. * the Free Software Foundation, either version 3 of the License, or
  9. * (at your option) any later version.
  10. *
  11. * L2J DataPack is distributed in the hope that it will be useful,
  12. * but WITHOUT ANY WARRANTY; without even the implied warranty of
  13. * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
  14. * General Public License for more details.
  15. *
  16. * You should have received a copy of the GNU General Public License
  17. * along with this program. If not, see <http://www.gnu.org/licenses/>.
  18. */
  19. package handlers.bypasshandlers;
  20. import java.util.ArrayList;
  21. import java.util.HashSet;
  22. import java.util.List;
  23. import java.util.Set;
  24. import java.util.logging.Level;
  25. import com.l2jserver.gameserver.datatables.NpcData;
  26. import com.l2jserver.gameserver.handler.IBypassHandler;
  27. import com.l2jserver.gameserver.instancemanager.QuestManager;
  28. import com.l2jserver.gameserver.model.actor.L2Character;
  29. import com.l2jserver.gameserver.model.actor.L2Npc;
  30. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  31. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  32. import com.l2jserver.gameserver.model.events.EventType;
  33. import com.l2jserver.gameserver.model.events.listeners.AbstractEventListener;
  34. import com.l2jserver.gameserver.model.quest.Quest;
  35. import com.l2jserver.gameserver.model.quest.QuestState;
  36. import com.l2jserver.gameserver.network.SystemMessageId;
  37. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  38. import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
  39. import com.l2jserver.util.StringUtil;
  40. public class QuestLink implements IBypassHandler
  41. {
  42. private static final String[] COMMANDS =
  43. {
  44. "Quest"
  45. };
  46. @Override
  47. public boolean useBypass(String command, L2PcInstance activeChar, L2Character target)
  48. {
  49. String quest = "";
  50. try
  51. {
  52. quest = command.substring(5).trim();
  53. }
  54. catch (IndexOutOfBoundsException ioobe)
  55. {
  56. }
  57. if (quest.length() == 0)
  58. {
  59. showQuestWindow(activeChar, (L2Npc) target);
  60. }
  61. else
  62. {
  63. int questNameEnd = quest.indexOf(" ");
  64. if (questNameEnd == -1)
  65. {
  66. showQuestWindow(activeChar, (L2Npc) target, quest);
  67. }
  68. else
  69. {
  70. activeChar.processQuestEvent(quest.substring(0, questNameEnd), quest.substring(questNameEnd).trim());
  71. }
  72. }
  73. return true;
  74. }
  75. /**
  76. * Open a choose quest window on client with all quests available of the L2NpcInstance.<br>
  77. * <b><u>Actions</u>:</b><br>
  78. * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li>
  79. * @param player The L2PcInstance that talk with the L2NpcInstance
  80. * @param npc The table containing quests of the L2NpcInstance
  81. * @param quests
  82. */
  83. public static void showQuestChooseWindow(L2PcInstance player, L2Npc npc, Quest[] quests)
  84. {
  85. final StringBuilder sb = StringUtil.startAppend(150, "<html><body>");
  86. String state = "";
  87. String color = "";
  88. int questId = -1;
  89. for (Quest quest : quests)
  90. {
  91. if (quest == null)
  92. {
  93. continue;
  94. }
  95. final QuestState qs = player.getQuestState(quest.getScriptName());
  96. if ((qs == null) || qs.isCreated())
  97. {
  98. state = quest.isCustomQuest() ? "" : "01";
  99. if (quest.canStartQuest(player))
  100. {
  101. color = "bbaa88";
  102. }
  103. else
  104. {
  105. color = "a62f31";
  106. }
  107. }
  108. else if (qs.isStarted())
  109. {
  110. state = quest.isCustomQuest() ? " (In Progress)" : "02";
  111. color = "ffdd66";
  112. }
  113. else if (qs.isCompleted())
  114. {
  115. state = quest.isCustomQuest() ? " (Done)" : "03";
  116. color = "787878";
  117. }
  118. StringUtil.append(sb, "<a action=\"bypass -h npc_", String.valueOf(npc.getObjectId()), "_Quest ", quest.getName(), "\">[");
  119. StringUtil.append(sb, "<font color=\"" + color + "\">");
  120. if (quest.isCustomQuest())
  121. {
  122. StringUtil.append(sb, quest.getDescr(), state);
  123. }
  124. else
  125. {
  126. questId = quest.getId();
  127. if (quest.getId() > 10000)
  128. {
  129. questId -= 5000;
  130. }
  131. else if (questId == 146)
  132. {
  133. questId = 640;
  134. }
  135. StringUtil.append(sb, "<fstring>", String.valueOf(questId), state, "</fstring>");
  136. }
  137. sb.append("]</font></a><br>");
  138. }
  139. sb.append("</body></html>");
  140. // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
  141. npc.insertObjectIdAndShowChatWindow(player, sb.toString());
  142. }
  143. /**
  144. * Open a quest window on client with the text of the L2NpcInstance.<br>
  145. * <b><u>Actions</u>:</b><br>
  146. * <ul>
  147. * <li>Get the text of the quest state in the folder data/scripts/quests/questId/stateId.htm</li>
  148. * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li>
  149. * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet</li>
  150. * </ul>
  151. * @param player the L2PcInstance that talk with the {@code npc}
  152. * @param npc the L2NpcInstance that chats with the {@code player}
  153. * @param questId the Id of the quest to display the message
  154. */
  155. public static void showQuestWindow(L2PcInstance player, L2Npc npc, String questId)
  156. {
  157. String content = null;
  158. final Quest q = QuestManager.getInstance().getQuest(questId);
  159. // Get the state of the selected quest
  160. final QuestState qs = player.getQuestState(questId);
  161. if (q != null)
  162. {
  163. if (((q.getId() >= 1) && (q.getId() < 20000)) && ((player.getWeightPenalty() >= 3) || !player.isInventoryUnder90(true)))
  164. {
  165. player.sendPacket(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT);
  166. return;
  167. }
  168. if (qs == null)
  169. {
  170. if ((q.getId() >= 1) && (q.getId() < 20000))
  171. {
  172. // Too many ongoing quests.
  173. if (player.getAllActiveQuests().length > 40)
  174. {
  175. final NpcHtmlMessage html = new NpcHtmlMessage(npc.getObjectId());
  176. html.setFile(player.getHtmlPrefix(), "data/html/fullquest.html");
  177. player.sendPacket(html);
  178. return;
  179. }
  180. }
  181. }
  182. q.notifyTalk(npc, player);
  183. }
  184. else
  185. {
  186. content = Quest.getNoQuestMsg(player); // no quests found
  187. }
  188. // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
  189. if (content != null)
  190. {
  191. npc.insertObjectIdAndShowChatWindow(player, content);
  192. }
  193. // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
  194. player.sendPacket(ActionFailed.STATIC_PACKET);
  195. }
  196. /**
  197. * @param player
  198. * @param npcId The Identifier of the NPC
  199. * @return a table containing all QuestState from the table _quests in which the L2PcInstance must talk to the NPC.
  200. */
  201. private static List<QuestState> getQuestsForTalk(final L2PcInstance player, int npcId)
  202. {
  203. // Create a QuestState table that will contain all QuestState to modify
  204. final List<QuestState> states = new ArrayList<>();
  205. final L2NpcTemplate template = NpcData.getInstance().getTemplate(npcId);
  206. if (template == null)
  207. {
  208. _log.log(Level.WARNING, QuestLink.class.getSimpleName() + ": " + player.getName() + " requested quests for talk on non existing npc " + npcId);
  209. return states;
  210. }
  211. // Go through the QuestState of the L2PcInstance quests
  212. for (AbstractEventListener listener : template.getListeners(EventType.ON_NPC_TALK))
  213. {
  214. if (listener.getOwner() instanceof Quest)
  215. {
  216. final Quest quest = (Quest) listener.getOwner();
  217. // Copy the current L2PcInstance QuestState in the QuestState table
  218. final QuestState st = player.getQuestState(quest.getName());
  219. if (st != null)
  220. {
  221. states.add(st);
  222. }
  223. }
  224. }
  225. // Return a table containing all QuestState to modify
  226. return states;
  227. }
  228. /**
  229. * Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.
  230. * @param player the L2PcInstance that talk with the {@code npc}.
  231. * @param npc the L2NpcInstance that chats with the {@code player}.
  232. */
  233. public static void showQuestWindow(L2PcInstance player, L2Npc npc)
  234. {
  235. boolean conditionMeet = false;
  236. // collect awaiting quests and start points
  237. final Set<Quest> options = new HashSet<>();
  238. // Quests are limited between 1 and 999 because those are the quests that are supported by the client.
  239. // By limiting them there, we are allowed to create custom quests at higher IDs without interfering
  240. for (QuestState state : getQuestsForTalk(player, npc.getId()))
  241. {
  242. final Quest quest = state.getQuest();
  243. if (quest == null)
  244. {
  245. _log.log(Level.WARNING, player + " Requested incorrect quest state for non existing quest: " + state.getQuestName());
  246. continue;
  247. }
  248. if ((quest.getId() > 0) && (quest.getId() < 20000))
  249. {
  250. options.add(quest);
  251. if (quest.canStartQuest(player))
  252. {
  253. conditionMeet = true;
  254. }
  255. }
  256. }
  257. for (AbstractEventListener listener : npc.getListeners(EventType.ON_NPC_QUEST_START))
  258. {
  259. if (listener.getOwner() instanceof Quest)
  260. {
  261. final Quest quest = (Quest) listener.getOwner();
  262. if ((quest.getId() > 0) && (quest.getId() < 20000))
  263. {
  264. options.add(quest);
  265. if (quest.canStartQuest(player))
  266. {
  267. conditionMeet = true;
  268. }
  269. }
  270. }
  271. }
  272. if (!conditionMeet)
  273. {
  274. showQuestWindow(player, npc, "");
  275. }
  276. else if (options.size() > 1)
  277. {
  278. showQuestChooseWindow(player, npc, options.toArray(new Quest[options.size()]));
  279. }
  280. else if (options.size() == 1)
  281. {
  282. showQuestWindow(player, npc, options.stream().findFirst().get().getName());
  283. }
  284. else
  285. {
  286. showQuestWindow(player, npc, "");
  287. }
  288. }
  289. @Override
  290. public String[] getBypassList()
  291. {
  292. return COMMANDS;
  293. }
  294. }