L2Event.java 16 KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177178179180181182183184185186187188189190191192193194195196197198199200201202203204205206207208209210211212213214215216217218219220221222223224225226227228229230231232233234235236237238239240241242243244245246247248249250251252253254255256257258259260261262263264265266267268269270271272273274275276277278279280281282283284285286287288289290291292293294295296297298299300301302303304305306307308309310311312313314315316317318319320321322323324325326327328329330331332333334335336337338339340341342343344345346347348349350351352353354355356357358359360361362363364365366367368369370371372373374375376377378379380381382383384385386387388389390391392393394395396397398399400401402403404405406407408409410411412413414415416417418419420421422423424425426427428429430431432433434435436437438439440441442443444445446447448449450451452453454455456457458459460461462463464465466467468469470471472473474475476477478479480481482483484485486487488489490491492493494495496497498499500501502503504505506507508509510511512513514515516517518519520521522523524525526527528529530531532533534535536537538539540541542543544545546547548549550551552
  1. /*
  2. * Copyright (C) 2004-2015 L2J Server
  3. *
  4. * This file is part of L2J Server.
  5. *
  6. * L2J Server 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 Server 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 com.l2jserver.gameserver.model.entity;
  20. import java.io.BufferedReader;
  21. import java.io.FileReader;
  22. import java.util.ArrayList;
  23. import java.util.Comparator;
  24. import java.util.HashMap;
  25. import java.util.LinkedHashMap;
  26. import java.util.LinkedList;
  27. import java.util.List;
  28. import java.util.Map;
  29. import java.util.Map.Entry;
  30. import java.util.logging.Level;
  31. import java.util.logging.Logger;
  32. import javolution.util.FastList;
  33. import javolution.util.FastMap;
  34. import com.l2jserver.Config;
  35. import com.l2jserver.gameserver.cache.HtmCache;
  36. import com.l2jserver.gameserver.data.xml.impl.NpcData;
  37. import com.l2jserver.gameserver.datatables.SpawnTable;
  38. import com.l2jserver.gameserver.instancemanager.AntiFeedManager;
  39. import com.l2jserver.gameserver.model.L2Spawn;
  40. import com.l2jserver.gameserver.model.L2World;
  41. import com.l2jserver.gameserver.model.actor.L2Npc;
  42. import com.l2jserver.gameserver.model.actor.instance.L2PcInstance;
  43. import com.l2jserver.gameserver.model.holders.PlayerEventHolder;
  44. import com.l2jserver.gameserver.network.serverpackets.CharInfo;
  45. import com.l2jserver.gameserver.network.serverpackets.ExBrExtraUserInfo;
  46. import com.l2jserver.gameserver.network.serverpackets.MagicSkillUse;
  47. import com.l2jserver.gameserver.network.serverpackets.NpcHtmlMessage;
  48. import com.l2jserver.gameserver.network.serverpackets.UserInfo;
  49. /**
  50. * @since $Revision: 1.3.4.1 $ $Date: 2005/03/27 15:29:32 $ This ancient thingie got reworked by Nik at $Date: 2011/05/17 21:51:39 $ Yeah, for 6 years no one bothered reworking this buggy event engine.
  51. */
  52. public class L2Event
  53. {
  54. protected static final Logger _log = Logger.getLogger(L2Event.class.getName());
  55. public static EventState eventState = EventState.OFF;
  56. public static String _eventName = "";
  57. public static String _eventCreator = "";
  58. public static String _eventInfo = "";
  59. public static int _teamsNumber = 0;
  60. public static final Map<Integer, String> _teamNames = new FastMap<>();
  61. public static final List<L2PcInstance> _registeredPlayers = new FastList<>();
  62. public static final Map<Integer, List<L2PcInstance>> _teams = new FastMap<>();
  63. public static int _npcId = 0;
  64. // public static final List<L2Npc> _npcs = new FastList<L2Npc>();
  65. private static final Map<L2PcInstance, PlayerEventHolder> _connectionLossData = new FastMap<>();
  66. public enum EventState
  67. {
  68. OFF, // Not running
  69. STANDBY, // Waiting for participants to register
  70. ON // Registration is over and the event has started.
  71. }
  72. /**
  73. * @param player
  74. * @return The team ID where the player is in, or -1 if player is null or team not found.
  75. */
  76. public static int getPlayerTeamId(L2PcInstance player)
  77. {
  78. if (player == null)
  79. {
  80. return -1;
  81. }
  82. for (Entry<Integer, List<L2PcInstance>> team : _teams.entrySet())
  83. {
  84. if (team.getValue().contains(player))
  85. {
  86. return team.getKey();
  87. }
  88. }
  89. return -1;
  90. }
  91. public static List<L2PcInstance> getTopNKillers(int n)
  92. {
  93. final Map<L2PcInstance, Integer> tmp = new HashMap<>();
  94. for (List<L2PcInstance> teamList : _teams.values())
  95. {
  96. for (L2PcInstance player : teamList)
  97. {
  98. if (player.getEventStatus() == null)
  99. {
  100. continue;
  101. }
  102. tmp.put(player, player.getEventStatus().getKills().size());
  103. }
  104. }
  105. sortByValue(tmp);
  106. // If the map size is less than "n", n will be as much as the map size
  107. if (tmp.size() <= n)
  108. {
  109. return new ArrayList<>(tmp.keySet());
  110. }
  111. final List<L2PcInstance> toReturn = new ArrayList<>(tmp.keySet());
  112. return toReturn.subList(1, n);
  113. }
  114. public static void showEventHtml(L2PcInstance player, String objectid)
  115. {
  116. // TODO: work on this
  117. if (eventState == EventState.STANDBY)
  118. {
  119. try
  120. {
  121. final String htmContent;
  122. final NpcHtmlMessage html = new NpcHtmlMessage(Integer.parseInt(objectid));
  123. if (_registeredPlayers.contains(player))
  124. {
  125. htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/mods/EventEngine/Participating.htm");
  126. }
  127. else
  128. {
  129. htmContent = HtmCache.getInstance().getHtm(player.getHtmlPrefix(), "data/html/mods/EventEngine/Participation.htm");
  130. }
  131. if (htmContent != null)
  132. {
  133. html.setHtml(htmContent);
  134. }
  135. html.replace("%objectId%", objectid); // Yeah, we need this.
  136. html.replace("%eventName%", _eventName);
  137. html.replace("%eventCreator%", _eventCreator);
  138. html.replace("%eventInfo%", _eventInfo);
  139. player.sendPacket(html);
  140. }
  141. catch (Exception e)
  142. {
  143. _log.log(Level.WARNING, "Exception on showEventHtml(): " + e.getMessage(), e);
  144. }
  145. }
  146. }
  147. /**
  148. * Spawns an event participation NPC near the player. The npc id used to spawning is L2Event._npcId
  149. * @param target
  150. */
  151. public static void spawnEventNpc(L2PcInstance target)
  152. {
  153. try
  154. {
  155. final L2Spawn spawn = new L2Spawn(_npcId);
  156. spawn.setX(target.getX() + 50);
  157. spawn.setY(target.getY() + 50);
  158. spawn.setZ(target.getZ());
  159. spawn.setAmount(1);
  160. spawn.setHeading(target.getHeading());
  161. spawn.stopRespawn();
  162. SpawnTable.getInstance().addNewSpawn(spawn, false);
  163. spawn.init();
  164. spawn.getLastSpawn().setCurrentHp(999999999);
  165. spawn.getLastSpawn().setTitle(_eventName);
  166. spawn.getLastSpawn().setEventMob(true);
  167. // spawn.getLastSpawn().decayMe();
  168. // spawn.getLastSpawn().spawnMe(spawn.getLastSpawn().getX(), spawn.getLastSpawn().getY(), spawn.getLastSpawn().getZ());
  169. spawn.getLastSpawn().broadcastPacket(new MagicSkillUse(spawn.getLastSpawn(), spawn.getLastSpawn(), 1034, 1, 1, 1));
  170. // _npcs.add(spawn.getLastSpawn());
  171. }
  172. catch (Exception e)
  173. {
  174. _log.log(Level.WARNING, "Exception on spawn(): " + e.getMessage(), e);
  175. }
  176. }
  177. /**
  178. * Zoey76: TODO: Rewrite this in a way that doesn't iterate over all spawns.
  179. */
  180. public static void unspawnEventNpcs()
  181. {
  182. SpawnTable.getInstance().forEachSpawn(spawn ->
  183. {
  184. L2Npc npc = spawn.getLastSpawn();
  185. if ((npc != null) && npc.isEventMob())
  186. {
  187. npc.deleteMe();
  188. spawn.stopRespawn();
  189. SpawnTable.getInstance().deleteSpawn(spawn, false);
  190. }
  191. return true;
  192. });
  193. }
  194. /**
  195. * @param player
  196. * @return False: If player is null, his event status is null or the event state is off. True: if the player is inside the _registeredPlayers list while the event state is STANDBY. If the event state is ON, it will check if the player is inside in one of the teams.
  197. */
  198. public static boolean isParticipant(L2PcInstance player)
  199. {
  200. if ((player == null) || (player.getEventStatus() == null))
  201. {
  202. return false;
  203. }
  204. switch (eventState)
  205. {
  206. case OFF:
  207. return false;
  208. case STANDBY:
  209. return _registeredPlayers.contains(player);
  210. case ON:
  211. for (List<L2PcInstance> teamList : _teams.values())
  212. {
  213. if (teamList.contains(player))
  214. {
  215. return true;
  216. }
  217. }
  218. }
  219. return false;
  220. }
  221. /**
  222. * Adds the player to the list of participants. If the event state is NOT STANDBY, the player wont be registered.
  223. * @param player
  224. */
  225. public static void registerPlayer(L2PcInstance player)
  226. {
  227. if (eventState != EventState.STANDBY)
  228. {
  229. player.sendMessage("The registration period for this event is over.");
  230. return;
  231. }
  232. if ((Config.L2JMOD_DUALBOX_CHECK_MAX_L2EVENT_PARTICIPANTS_PER_IP == 0) || AntiFeedManager.getInstance().tryAddPlayer(AntiFeedManager.L2EVENT_ID, player, Config.L2JMOD_DUALBOX_CHECK_MAX_L2EVENT_PARTICIPANTS_PER_IP))
  233. {
  234. _registeredPlayers.add(player);
  235. }
  236. else
  237. {
  238. player.sendMessage("You have reached the maximum allowed participants per IP.");
  239. return;
  240. }
  241. }
  242. /**
  243. * Removes the player from the participating players and the teams and restores his init stats before he registered at the event (loc, pvp, pk, title etc)
  244. * @param player
  245. */
  246. public static void removeAndResetPlayer(L2PcInstance player)
  247. {
  248. try
  249. {
  250. if (isParticipant(player))
  251. {
  252. if (player.isDead())
  253. {
  254. player.restoreExp(100.0);
  255. player.doRevive();
  256. player.setCurrentHpMp(player.getMaxHp(), player.getMaxMp());
  257. player.setCurrentCp(player.getMaxCp());
  258. }
  259. player.getPoly().setPolyInfo(null, "1");
  260. player.decayMe();
  261. player.spawnMe(player.getX(), player.getY(), player.getZ());
  262. CharInfo info1 = new CharInfo(player);
  263. player.broadcastPacket(info1);
  264. UserInfo info2 = new UserInfo(player);
  265. player.sendPacket(info2);
  266. player.broadcastPacket(new ExBrExtraUserInfo(player));
  267. player.stopTransformation(true);
  268. }
  269. if (player.getEventStatus() != null)
  270. {
  271. player.getEventStatus().restorePlayerStats();
  272. }
  273. player.setEventStatus(null);
  274. _registeredPlayers.remove(player);
  275. int teamId = getPlayerTeamId(player);
  276. if (_teams.containsKey(teamId))
  277. {
  278. _teams.get(teamId).remove(player);
  279. }
  280. }
  281. catch (Exception e)
  282. {
  283. _log.log(Level.WARNING, "Error at unregisterAndResetPlayer in the event:" + e.getMessage(), e);
  284. }
  285. }
  286. /**
  287. * The player's event status will be saved at _connectionLossData
  288. * @param player
  289. */
  290. public static void savePlayerEventStatus(L2PcInstance player)
  291. {
  292. _connectionLossData.put(player, player.getEventStatus());
  293. }
  294. /**
  295. * If _connectionLossData contains the player, it will restore the player's event status. Also it will remove the player from the _connectionLossData.
  296. * @param player
  297. */
  298. public static void restorePlayerEventStatus(L2PcInstance player)
  299. {
  300. if (_connectionLossData.containsKey(player))
  301. {
  302. player.setEventStatus(_connectionLossData.get(player));
  303. _connectionLossData.remove(player);
  304. }
  305. }
  306. /**
  307. * If the event is ON or STANDBY, it will not start. Sets the event state to STANDBY and spawns registration NPCs
  308. * @return a string with information if the event participation has been successfully started or not.
  309. */
  310. public static String startEventParticipation()
  311. {
  312. try
  313. {
  314. switch (eventState)
  315. {
  316. case ON:
  317. return "Cannot start event, it is already on.";
  318. case STANDBY:
  319. return "Cannot start event, it is on standby mode.";
  320. case OFF: // Event is off, so no problem turning it on.
  321. eventState = EventState.STANDBY;
  322. break;
  323. }
  324. // Register the event at AntiFeedManager and clean it for just in case if the event is already registered.
  325. AntiFeedManager.getInstance().registerEvent(AntiFeedManager.L2EVENT_ID);
  326. AntiFeedManager.getInstance().clear(AntiFeedManager.L2EVENT_ID);
  327. // Just in case
  328. unspawnEventNpcs();
  329. _registeredPlayers.clear();
  330. // _npcs.clear();
  331. if (NpcData.getInstance().getTemplate(_npcId) == null)
  332. {
  333. return "Cannot start event, invalid npc id.";
  334. }
  335. try (FileReader fr = new FileReader(Config.DATAPACK_ROOT + "/data/events/" + _eventName);
  336. BufferedReader br = new BufferedReader(fr))
  337. {
  338. _eventCreator = br.readLine();
  339. _eventInfo = br.readLine();
  340. }
  341. List<L2PcInstance> temp = new FastList<>();
  342. for (L2PcInstance player : L2World.getInstance().getPlayers())
  343. {
  344. if (!player.isOnline())
  345. {
  346. continue;
  347. }
  348. if (!temp.contains(player))
  349. {
  350. spawnEventNpc(player);
  351. temp.add(player);
  352. }
  353. for (L2PcInstance playertemp : player.getKnownList().getKnownPlayers().values())
  354. {
  355. if ((Math.abs(playertemp.getX() - player.getX()) < 1000) && (Math.abs(playertemp.getY() - player.getY()) < 1000) && (Math.abs(playertemp.getZ() - player.getZ()) < 1000))
  356. {
  357. temp.add(playertemp);
  358. }
  359. }
  360. }
  361. }
  362. catch (Exception e)
  363. {
  364. _log.warning("L2Event: " + e.getMessage());
  365. return "Cannot start event participation, an error has occured.";
  366. }
  367. return "The event participation has been successfully started.";
  368. }
  369. /**
  370. * If the event is ON or OFF, it will not start. Sets the event state to ON, creates the teams, adds the registered players ordered by level at the teams and adds a new event status to the players.
  371. * @return a string with information if the event has been successfully started or not.
  372. */
  373. public static String startEvent()
  374. {
  375. try
  376. {
  377. switch (eventState)
  378. {
  379. case ON:
  380. return "Cannot start event, it is already on.";
  381. case STANDBY:
  382. eventState = EventState.ON;
  383. break;
  384. case OFF: // Event is off, so no problem turning it on.
  385. return "Cannot start event, it is off. Participation start is required.";
  386. }
  387. // Clean the things we will use, just in case.
  388. unspawnEventNpcs();
  389. _teams.clear();
  390. _connectionLossData.clear();
  391. // Insert empty lists at _teams.
  392. for (int i = 0; i < _teamsNumber; i++)
  393. {
  394. _teams.put(i + 1, new FastList<L2PcInstance>());
  395. }
  396. int i = 0;
  397. while (!_registeredPlayers.isEmpty())
  398. {
  399. // Get the player with the biggest level
  400. int max = 0;
  401. L2PcInstance biggestLvlPlayer = null;
  402. for (L2PcInstance player : _registeredPlayers)
  403. {
  404. if (player == null)
  405. {
  406. continue;
  407. }
  408. if (max < player.getLevel())
  409. {
  410. max = player.getLevel();
  411. biggestLvlPlayer = player;
  412. }
  413. }
  414. if (biggestLvlPlayer == null)
  415. {
  416. continue;
  417. }
  418. _registeredPlayers.remove(biggestLvlPlayer);
  419. _teams.get(i + 1).add(biggestLvlPlayer);
  420. biggestLvlPlayer.setEventStatus();
  421. i = (i + 1) % _teamsNumber;
  422. }
  423. }
  424. catch (Exception e)
  425. {
  426. _log.warning("L2Event: " + e.getMessage());
  427. return "Cannot start event, an error has occured.";
  428. }
  429. return "The event has been successfully started.";
  430. }
  431. /**
  432. * If the event state is OFF, it will not finish. Sets the event state to OFF, unregisters and resets the players, unspawns and clers the event NPCs, clears the teams, registered players, connection loss data, sets the teams number to 0, sets the event name to empty.
  433. * @return a string with information if the event has been successfully stopped or not.
  434. */
  435. public static String finishEvent()
  436. {
  437. switch (eventState)
  438. {
  439. case OFF:
  440. return "Cannot finish event, it is already off.";
  441. case STANDBY:
  442. for (L2PcInstance player : _registeredPlayers)
  443. {
  444. removeAndResetPlayer(player);
  445. }
  446. unspawnEventNpcs();
  447. // _npcs.clear();
  448. _registeredPlayers.clear();
  449. _teams.clear();
  450. _connectionLossData.clear();
  451. _teamsNumber = 0;
  452. _eventName = "";
  453. eventState = EventState.OFF;
  454. return "The event has been stopped at STANDBY mode, all players unregistered and all event npcs unspawned.";
  455. case ON:
  456. for (List<L2PcInstance> teamList : _teams.values())
  457. {
  458. for (L2PcInstance player : teamList)
  459. {
  460. removeAndResetPlayer(player);
  461. }
  462. }
  463. eventState = EventState.OFF;
  464. AntiFeedManager.getInstance().clear(AntiFeedManager.TVT_ID);
  465. unspawnEventNpcs(); // Just in case
  466. // _npcs.clear();
  467. _registeredPlayers.clear();
  468. _teams.clear();
  469. _connectionLossData.clear();
  470. _teamsNumber = 0;
  471. _eventName = "";
  472. _npcId = 0;
  473. _eventCreator = "";
  474. _eventInfo = "";
  475. return "The event has been stopped, all players unregistered and all event npcs unspawned.";
  476. }
  477. return "The event has been successfully finished.";
  478. }
  479. private static final Map<L2PcInstance, Integer> sortByValue(Map<L2PcInstance, Integer> unsortMap)
  480. {
  481. final List<Entry<L2PcInstance, Integer>> list = new LinkedList<>(unsortMap.entrySet());
  482. list.sort(Comparator.comparing(Entry::getValue));
  483. final Map<L2PcInstance, Integer> sortedMap = new LinkedHashMap<>();
  484. for (Entry<L2PcInstance, Integer> entry : list)
  485. {
  486. sortedMap.put(entry.getKey(), entry.getValue());
  487. }
  488. return sortedMap;
  489. }
  490. }