QuestLink.java 10 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332
  1. /*
  2. * Copyright (C) 2004-2015 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.data.xml.impl.NpcData;
  26. import com.l2jserver.gameserver.handler.IBypassHandler;
  27. import com.l2jserver.gameserver.instancemanager.QuestManager;
  28. import com.l2jserver.gameserver.model.L2World;
  29. import com.l2jserver.gameserver.model.actor.L2Character;
  30. import com.l2jserver.gameserver.model.actor.L2Npc;
  31. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  32. import com.l2jserver.gameserver.model.actor.templates.L2NpcTemplate;
  33. import com.l2jserver.gameserver.model.events.EventType;
  34. import com.l2jserver.gameserver.model.events.listeners.AbstractEventListener;
  35. import com.l2jserver.gameserver.model.quest.Quest;
  36. import com.l2jserver.gameserver.model.quest.QuestState;
  37. import com.l2jserver.gameserver.network.SystemMessageId;
  38. import com.l2jserver.gameserver.network.serverpackets.ActionFailed;
  39. import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
  40. import com.l2jserver.util.StringUtil;
  41. public class QuestLink implements IBypassHandler
  42. {
  43. private static final int TO_LEAD_AND_BE_LED = 118;
  44. private static final int THE_LEADER_AND_THE_FOLLOWER = 123;
  45. private static final String[] COMMANDS =
  46. {
  47. "Quest"
  48. };
  49. @Override
  50. public boolean useBypass(String command, L2PcInstance activeChar, L2Character target)
  51. {
  52. String quest = "";
  53. try
  54. {
  55. quest = command.substring(5).trim();
  56. }
  57. catch (IndexOutOfBoundsException ioobe)
  58. {
  59. }
  60. if (quest.length() == 0)
  61. {
  62. showQuestWindow(activeChar, (L2Npc) target);
  63. }
  64. else
  65. {
  66. int questNameEnd = quest.indexOf(" ");
  67. if (questNameEnd == -1)
  68. {
  69. showQuestWindow(activeChar, (L2Npc) target, quest);
  70. }
  71. else
  72. {
  73. activeChar.processQuestEvent(quest.substring(0, questNameEnd), quest.substring(questNameEnd).trim());
  74. }
  75. }
  76. return true;
  77. }
  78. /**
  79. * Open a choose quest window on client with all quests available of the L2NpcInstance.<br>
  80. * <b><u>Actions</u>:</b><br>
  81. * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li>
  82. * @param player The L2PcInstance that talk with the L2NpcInstance
  83. * @param npc The table containing quests of the L2NpcInstance
  84. * @param quests
  85. */
  86. private static void showQuestChooseWindow(L2PcInstance player, L2Npc npc, Quest[] quests)
  87. {
  88. final StringBuilder sb = StringUtil.startAppend(150, "<html><body>");
  89. String state = "";
  90. String color = "";
  91. int questId = -1;
  92. for (Quest quest : quests)
  93. {
  94. if (quest == null)
  95. {
  96. continue;
  97. }
  98. final QuestState qs = player.getQuestState(quest.getName());
  99. if ((qs == null) || qs.isCreated())
  100. {
  101. state = quest.isCustomQuest() ? "" : "01";
  102. if (quest.canStartQuest(player))
  103. {
  104. color = "bbaa88";
  105. }
  106. else
  107. {
  108. color = "a62f31";
  109. }
  110. }
  111. else if (qs.isStarted())
  112. {
  113. state = quest.isCustomQuest() ? " (In Progress)" : "02";
  114. color = "ffdd66";
  115. }
  116. else if (qs.isCompleted())
  117. {
  118. state = quest.isCustomQuest() ? " (Done)" : "03";
  119. color = "787878";
  120. }
  121. StringUtil.append(sb, "<a action=\"bypass -h npc_", String.valueOf(npc.getObjectId()), "_Quest ", quest.getName(), "\">");
  122. StringUtil.append(sb, "<font color=\"" + color + "\">[");
  123. if (quest.isCustomQuest())
  124. {
  125. StringUtil.append(sb, quest.getDescr(), state);
  126. }
  127. else
  128. {
  129. questId = quest.getId();
  130. if (questId > 10000)
  131. {
  132. questId -= 5000;
  133. }
  134. else if (questId == 146)
  135. {
  136. questId = 640;
  137. }
  138. StringUtil.append(sb, "<fstring>", String.valueOf(questId), state, "</fstring>");
  139. }
  140. sb.append("]</font></a><br>");
  141. if ((player.getApprentice() > 0) && (L2World.getInstance().getPlayer(player.getApprentice()) != null))
  142. {
  143. if (questId == TO_LEAD_AND_BE_LED)
  144. {
  145. sb.append("<a action=\"bypass -h Quest Q00118_ToLeadAndBeLed sponsor\"><font color=\"" + color + "\">[<fstring>" + questId + state + "</fstring> (Sponsor)]</font></a><br>");
  146. }
  147. if (questId == THE_LEADER_AND_THE_FOLLOWER)
  148. {
  149. sb.append("<a action=\"bypass -h Quest Q00123_TheLeaderAndTheFollower sponsor\"><font color=\"" + color + "\">[<fstring>" + questId + state + "</fstring> (Sponsor)]</font></a><br>");
  150. }
  151. }
  152. }
  153. sb.append("</body></html>");
  154. // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
  155. npc.insertObjectIdAndShowChatWindow(player, sb.toString());
  156. }
  157. /**
  158. * Open a quest window on client with the text of the L2NpcInstance.<br>
  159. * <b><u>Actions</u>:</b><br>
  160. * <ul>
  161. * <li>Get the text of the quest state in the folder data/scripts/quests/questId/stateId.htm</li>
  162. * <li>Send a Server->Client NpcHtmlMessage containing the text of the L2NpcInstance to the L2PcInstance</li>
  163. * <li>Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet</li>
  164. * </ul>
  165. * @param player the L2PcInstance that talk with the {@code npc}
  166. * @param npc the L2NpcInstance that chats with the {@code player}
  167. * @param questId the Id of the quest to display the message
  168. */
  169. public static void showQuestWindow(L2PcInstance player, L2Npc npc, String questId)
  170. {
  171. String content = null;
  172. final Quest q = QuestManager.getInstance().getQuest(questId);
  173. // Get the state of the selected quest
  174. final QuestState qs = player.getQuestState(questId);
  175. if (q != null)
  176. {
  177. if (((q.getId() >= 1) && (q.getId() < 20000)) && ((player.getWeightPenalty() >= 3) || !player.isInventoryUnder90(true)))
  178. {
  179. player.sendPacket(SystemMessageId.INVENTORY_LESS_THAN_80_PERCENT);
  180. return;
  181. }
  182. if (qs == null)
  183. {
  184. if ((q.getId() >= 1) && (q.getId() < 20000))
  185. {
  186. // Too many ongoing quests.
  187. if (player.getAllActiveQuests().length > 40)
  188. {
  189. final NpcHtmlMessage html = new NpcHtmlMessage(npc.getObjectId());
  190. html.setFile(player.getHtmlPrefix(), "data/html/fullquest.html");
  191. player.sendPacket(html);
  192. return;
  193. }
  194. }
  195. }
  196. q.notifyTalk(npc, player);
  197. }
  198. else
  199. {
  200. content = Quest.getNoQuestMsg(player); // no quests found
  201. }
  202. // Send a Server->Client packet NpcHtmlMessage to the L2PcInstance in order to display the message of the L2NpcInstance
  203. if (content != null)
  204. {
  205. npc.insertObjectIdAndShowChatWindow(player, content);
  206. }
  207. // Send a Server->Client ActionFailed to the L2PcInstance in order to avoid that the client wait another packet
  208. player.sendPacket(ActionFailed.STATIC_PACKET);
  209. }
  210. /**
  211. * @param player
  212. * @param npcId The Identifier of the NPC
  213. * @return a table containing all QuestState from the table _quests in which the L2PcInstance must talk to the NPC.
  214. */
  215. private static List<QuestState> getQuestsForTalk(final L2PcInstance player, int npcId)
  216. {
  217. // Create a QuestState table that will contain all QuestState to modify
  218. final List<QuestState> states = new ArrayList<>();
  219. final L2NpcTemplate template = NpcData.getInstance().getTemplate(npcId);
  220. if (template == null)
  221. {
  222. _log.log(Level.WARNING, QuestLink.class.getSimpleName() + ": " + player.getName() + " requested quests for talk on non existing npc " + npcId);
  223. return states;
  224. }
  225. // Go through the QuestState of the L2PcInstance quests
  226. for (AbstractEventListener listener : template.getListeners(EventType.ON_NPC_TALK))
  227. {
  228. if (listener.getOwner() instanceof Quest)
  229. {
  230. final Quest quest = (Quest) listener.getOwner();
  231. // Copy the current L2PcInstance QuestState in the QuestState table
  232. final QuestState st = player.getQuestState(quest.getName());
  233. if (st != null)
  234. {
  235. states.add(st);
  236. }
  237. }
  238. }
  239. // Return a table containing all QuestState to modify
  240. return states;
  241. }
  242. /**
  243. * Collect awaiting quests/start points and display a QuestChooseWindow (if several available) or QuestWindow.
  244. * @param player the L2PcInstance that talk with the {@code npc}.
  245. * @param npc the L2NpcInstance that chats with the {@code player}.
  246. */
  247. public static void showQuestWindow(L2PcInstance player, L2Npc npc)
  248. {
  249. boolean conditionMeet = false;
  250. final Set<Quest> options = new HashSet<>();
  251. for (QuestState state : getQuestsForTalk(player, npc.getId()))
  252. {
  253. final Quest quest = state.getQuest();
  254. if (quest == null)
  255. {
  256. _log.log(Level.WARNING, player + " Requested incorrect quest state for non existing quest: " + state.getQuestName());
  257. continue;
  258. }
  259. if ((quest.getId() > 0) && (quest.getId() < 20000))
  260. {
  261. options.add(quest);
  262. if (quest.canStartQuest(player))
  263. {
  264. conditionMeet = true;
  265. }
  266. }
  267. }
  268. for (AbstractEventListener listener : npc.getListeners(EventType.ON_NPC_QUEST_START))
  269. {
  270. if (listener.getOwner() instanceof Quest)
  271. {
  272. final Quest quest = (Quest) listener.getOwner();
  273. if ((quest.getId() > 0) && (quest.getId() < 20000))
  274. {
  275. options.add(quest);
  276. if (quest.canStartQuest(player))
  277. {
  278. conditionMeet = true;
  279. }
  280. }
  281. }
  282. }
  283. if (!conditionMeet)
  284. {
  285. showQuestWindow(player, npc, "");
  286. }
  287. else if ((options.size() > 1) || ((player.getApprentice() > 0) && (L2World.getInstance().getPlayer(player.getApprentice()) != null) && options.stream().anyMatch(q -> q.getId() == TO_LEAD_AND_BE_LED)))
  288. {
  289. showQuestChooseWindow(player, npc, options.toArray(new Quest[options.size()]));
  290. }
  291. else if (options.size() == 1)
  292. {
  293. showQuestWindow(player, npc, options.stream().findFirst().get().getName());
  294. }
  295. else
  296. {
  297. showQuestWindow(player, npc, "");
  298. }
  299. }
  300. @Override
  301. public String[] getBypassList()
  302. {
  303. return COMMANDS;
  304. }
  305. }