-JRGames-

[Mod] Spoil Event System

Dec 24th, 2025 (edited)
129
0
Never
Not a member of Pastebin yet? Sign Up, it unlocks many cool features!
text 43.73 KB | None | 0 0
  1. diff --git a/aCis_gameserver/data/html/admin/eventos.htm b/aCis_gameserver/data/html/admin/eventos.htm
  2. index 25a894c..10d1f7a 100644
  3. --- a/aCis_gameserver/data/html/admin/eventos.htm
  4. +++ b/aCis_gameserver/data/html/admin/eventos.htm
  5. @@ -1,63 +1,103 @@
  6. + <tr>
  7. + <td align=left>
  8. + <font color=9FB6CD>Spoil Event:</font>
  9. + </td>
  10. + <td align=center><a action="bypass -h admin_spoilevent_start">
  11. + <font color=FFF000>Start</font>
  12. + </a></td>
  13. + <td align=center><a action="bypass -h admin_spoilevent_stop">
  14. + <font color=FF0000>Stop</font>
  15. + </a></td>
  16. + <td align=center><a action="bypass -h admin_spoilevent_status">Status</a></td>
  17. + </tr>
  18. + <tr></tr>
  19. + No newline at end of file
  20. diff --git a/aCis_gameserver/data/xml/adminCommands.xml b/aCis_gameserver/data/xml/adminCommands.xml
  21. index d3d3c1b..2573280 100644
  22. --- a/aCis_gameserver/data/xml/adminCommands.xml
  23. +++ b/aCis_gameserver/data/xml/adminCommands.xml
  24. @@ -60,6 +60,12 @@
  25. <aCar name="admin_soloboss_next" accessLevel="1" />
  26. <aCar name="admin_soloboss_info" accessLevel="1" />
  27.  
  28. + <!-- Spoil Event -->
  29. + <aCar name="admin_spoilevent" accessLevel="1" />
  30. + <aCar name="admin_spoilevent_start" accessLevel="1" />
  31. + <aCar name="admin_spoilevent_stop" accessLevel="1" />
  32. + <aCar name="admin_spoilevent_status" accessLevel="1" />
  33. +
  34. <!-- ADMIN TOURNAMENT -->
  35. <aCar name="admin_tour" accessLevel="7"/>
  36. <aCar name="admin_tour_reset" accessLevel="7"/>
  37. diff --git a/aCis_gameserver/data/xml/events/SpoilEvent.xml b/aCis_gameserver/data/xml/events/SpoilEvent.xml
  38. new file mode 100644
  39. index 0000000..8b3d462
  40. --- /dev/null
  41. +++ b/aCis_gameserver/data/xml/events/SpoilEvent.xml
  42. @@ -0,0 +1,30 @@
  43. +<?xml version="1.0" encoding="UTF-8"?>
  44. +<list enabled="true">
  45. + <event name="SpoilEvent" durationMinutes="60" blockAutofarm="true" allowAutoRescheduleAfterManualStop="false">
  46. + <schedule startTimes="20:00,23:00,02:00" />
  47. +
  48. + <!-- Allowed Classes Configuration -->
  49. + <!-- Use "all" to allow any class inside the zone. -->
  50. + <!-- Use specific Class IDs separated by commas (e.g., "53,54,117") to restrict access. -->
  51. + <!-- Default (if removed): 53,54,117 (Scavenger, Bounty Hunter, Fortune Seeker) -->
  52. + <classes allowed="53,54,117" />
  53. +
  54. + <!-- Spoil Mobs -->
  55. + <monster id="21084" respawnSeconds="10"> <!-- Example: Lilim Knight -->
  56. + <spawn x="82984" y="140104" z="-3552"/>
  57. + <spawn x="82136" y="140056" z="-3568"/>
  58. + <spawn x="82456" y="139704" z="-3568"/>
  59. + </monster>
  60. +
  61. + <!-- Gatekeepers -->
  62. + <gatekeeper id="30080"> <!-- Example: Monument -->
  63. + <spawn x="83320" y="148632" z="-3408"/>
  64. + </gatekeeper>
  65. +
  66. + <!-- Teleport Location (Where players go when accepting the popup) -->
  67. + <teleport x="82984" y="140104" z="-3552" />
  68. +
  69. + <!-- Return Location (if player is not allowed in zone) -->
  70. + <return x="82488" y="149064" z="-3464"/> <!-- Giran -->
  71. + </event>
  72. +</list>
  73. diff --git a/aCis_gameserver/data/xml/zones/SpoilZone.xml b/aCis_gameserver/data/xml/zones/SpoilZone.xml
  74. new file mode 100644
  75. index 0000000..4b72ce4
  76. --- /dev/null
  77. +++ b/aCis_gameserver/data/xml/zones/SpoilZone.xml
  78. @@ -0,0 +1,17 @@
  79. +<?xml version="1.0" encoding="UTF-8"?>
  80. +<list>
  81. + <!-- Example Spoil Zone -->
  82. + <zone shape="Cylinder" type="SpoilZone" minZ="-3560" maxZ="-3560" rad="2500"> <!-- spoil1 -->
  83. + <node x="83220" y="140200"/>
  84. + </zone>
  85. +
  86. +</list>
  87. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/GameServer.java b/aCis_gameserver/java/net/sf/l2j/gameserver/GameServer.java
  88. index 618d515..01e0c42 100644
  89. --- a/aCis_gameserver/java/net/sf/l2j/gameserver/GameServer.java
  90. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/GameServer.java
  91. @@ -78,6 +78,8 @@ import net.sf.l2j.gameserver.data.xml.StaticObjectData;
  92. import net.sf.l2j.gameserver.data.xml.SummonItemData;
  93. import net.sf.l2j.gameserver.data.xml.TeleportLocationData;
  94. import net.sf.l2j.gameserver.data.xml.WalkerRouteData;
  95. +import net.sf.l2j.gameserver.events.spoil.SpoilEventData;
  96. +import net.sf.l2j.gameserver.events.spoil.SpoilEventScheduler;
  97. import net.sf.l2j.gameserver.geoengine.GeoEngine;
  98. import net.sf.l2j.gameserver.handler.AdminCommandHandler;
  99. import net.sf.l2j.gameserver.handler.BypassHandler;
  100. @@ -313,6 +315,8 @@ public class GameServer
  101. TvTManager.getInstance();
  102.  
  103. SoloBossEvent.getInstance().init();
  104. + SpoilEventData.getInstance();
  105. + SpoilEventScheduler.getInstance().init();
  106.  
  107. if (Config.CKM_ENABLED)
  108. CharacterKillingManager.getInstance().init();
  109.  
  110. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/enums/ZoneId.java b/aCis_gameserver/java/net/sf/l2j/gameserver/enums/ZoneId.java
  111. index 9a5b012..7648da8 100644
  112. --- a/aCis_gameserver/java/net/sf/l2j/gameserver/enums/ZoneId.java
  113. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/enums/ZoneId.java
  114. @@ -22,7 +22,9 @@ public enum ZoneId
  115. NO_RESTART(17),
  116. SCRIPT(18),
  117. BOSS(19),
  118. - TOURNAMENT(20);
  119. + TOURNAMENT(20),
  120. + SPOIL_AREA(21),
  121. + FLAG_ZONE(22);
  122.  
  123. private final int _id;
  124.  
  125. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventData.java b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventData.java
  126. new file mode 100644
  127. index 0000000..f0e12a3
  128. --- /dev/null
  129. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventData.java
  130. @@ -0,0 +1,225 @@
  131. +package net.sf.l2j.gameserver.events.spoil;
  132. +
  133. +import java.nio.file.Path;
  134. +import java.util.ArrayList;
  135. +import java.util.List;
  136. +
  137. +import net.sf.l2j.commons.data.xml.IXmlReader;
  138. +
  139. +import net.sf.l2j.gameserver.model.location.Location;
  140. +
  141. +import org.w3c.dom.Document;
  142. +import org.w3c.dom.NamedNodeMap;
  143. +
  144. +/**
  145. + * @author Jhon
  146. + */
  147. +public class SpoilEventData implements IXmlReader
  148. +{
  149. + private boolean _enabled = false;
  150. + private int _durationMinutes = 60;
  151. + private boolean _blockAutofarm = true;
  152. + private boolean _allowAutoReschedule = false;
  153. + private Location _returnLoc = null;
  154. + private Location _teleportLoc = null;
  155. +
  156. + private final List<String> _scheduleTimes = new ArrayList<>();
  157. + private final List<SpawnInfo> _monsterSpawns = new ArrayList<>();
  158. + private final List<SpawnInfo> _gkSpawns = new ArrayList<>();
  159. + // Default: Scavenger(53), Bounty Hunter(54), Fortune Seeker(117)
  160. + private final List<Integer> _allowedClassIds = new ArrayList<>();
  161. + private boolean _allClassesAllowed = false;
  162. + {
  163. + _allowedClassIds.add(53);
  164. + _allowedClassIds.add(54);
  165. + _allowedClassIds.add(117);
  166. + }
  167. +
  168. + public static class SpawnInfo
  169. + {
  170. + public final int npcId;
  171. + public final int respawnDelay;
  172. + public final List<Location> locations = new ArrayList<>();
  173. +
  174. + public SpawnInfo(int id, int respawn)
  175. + {
  176. + npcId = id;
  177. + respawnDelay = respawn;
  178. + }
  179. + }
  180. +
  181. + protected SpoilEventData()
  182. + {
  183. + load();
  184. + }
  185. +
  186. + @Override
  187. + public void load()
  188. + {
  189. + _scheduleTimes.clear();
  190. + _monsterSpawns.clear();
  191. + _gkSpawns.clear();
  192. + _allowedClassIds.clear();
  193. + _allClassesAllowed = false;
  194. + _allowedClassIds.add(53);
  195. + _allowedClassIds.add(54);
  196. + _allowedClassIds.add(117);
  197. +
  198. + parseFile("./data/xml/events/SpoilEvent.xml");
  199. + LOGGER.info("SpoilEventData: Loaded {} schedule times, {} monster types, {} GK types.", _scheduleTimes.size(), _monsterSpawns.size(), _gkSpawns.size());
  200. + }
  201. +
  202. + @Override
  203. + public void parseDocument(Document doc, Path path)
  204. + {
  205. + forEach(doc, "list", listNode ->
  206. + {
  207. + forEach(listNode, "event", eventNode ->
  208. + {
  209. + final NamedNodeMap attrs = eventNode.getAttributes();
  210. + _enabled = parseBoolean(listNode.getAttributes(), "enabled", true); // Check list enabled first, or event enabled? User example has list enabled=true. I will check both or stick to list.
  211. + // Actually user xml has <list enabled="true"> and inside <event ...>.
  212. + // Let's check the list enabled attribute.
  213. + if (!_enabled)
  214. + return;
  215. +
  216. + _durationMinutes = parseInteger(attrs, "durationMinutes", 60);
  217. + _blockAutofarm = parseBoolean(attrs, "blockAutofarm", true);
  218. + _allowAutoReschedule = parseBoolean(attrs, "allowAutoRescheduleAfterManualStop", false);
  219. +
  220. + forEach(eventNode, "classes", classNode ->
  221. + {
  222. + final String classes = parseString(classNode.getAttributes(), "allowed");
  223. + if (classes != null && !classes.isEmpty())
  224. + {
  225. + if (classes.equalsIgnoreCase("all"))
  226. + {
  227. + _allClassesAllowed = true;
  228. + _allowedClassIds.clear();
  229. + }
  230. + else
  231. + {
  232. + _allowedClassIds.clear(); // Override defaults if config exists
  233. + for (String id : classes.split(","))
  234. + _allowedClassIds.add(Integer.parseInt(id.trim()));
  235. + }
  236. + }
  237. + });
  238. +
  239. + forEach(eventNode, "schedule", scheduleNode ->
  240. + {
  241. + final String times = parseString(scheduleNode.getAttributes(), "startTimes");
  242. + if (times != null && !times.isEmpty())
  243. + {
  244. + for (String time : times.split(","))
  245. + _scheduleTimes.add(time.trim());
  246. + }
  247. + });
  248. +
  249. + forEach(eventNode, "monster", mobNode ->
  250. + {
  251. + final NamedNodeMap mobAttrs = mobNode.getAttributes();
  252. + final int id = parseInteger(mobAttrs, "id");
  253. + final int respawn = parseInteger(mobAttrs, "respawnSeconds", 60);
  254. +
  255. + final SpawnInfo info = new SpawnInfo(id, respawn);
  256. +
  257. + forEach(mobNode, "spawn", spawnNode ->
  258. + {
  259. + info.locations.add(parseLocation(spawnNode));
  260. + });
  261. + _monsterSpawns.add(info);
  262. + });
  263. +
  264. + forEach(eventNode, "gatekeeper", gkNode ->
  265. + {
  266. + final NamedNodeMap gkAttrs = gkNode.getAttributes();
  267. + final int id = parseInteger(gkAttrs, "id");
  268. +
  269. + final SpawnInfo info = new SpawnInfo(id, 0); // GK usually doesn't respawn or instant? Let's say 0 (standard or non-respawn)
  270. +
  271. + forEach(gkNode, "spawn", spawnNode ->
  272. + {
  273. + info.locations.add(parseLocation(spawnNode));
  274. + });
  275. + _gkSpawns.add(info);
  276. + });
  277. +
  278. + forEach(eventNode, "return", returnNode ->
  279. + {
  280. + _returnLoc = parseLocation(returnNode);
  281. + });
  282. +
  283. + forEach(eventNode, "teleport", arg ->
  284. + {
  285. + _teleportLoc = parseLocation(arg);
  286. + });
  287. + });
  288. + });
  289. + }
  290. +
  291. + public boolean isEnabled()
  292. + {
  293. + return _enabled;
  294. + }
  295. +
  296. + public int getDurationMinutes()
  297. + {
  298. + return _durationMinutes;
  299. + }
  300. +
  301. + public boolean isBlockAutofarm()
  302. + {
  303. + return _blockAutofarm;
  304. + }
  305. +
  306. + public boolean isAllowAutoReschedule()
  307. + {
  308. + return _allowAutoReschedule;
  309. + }
  310. +
  311. + public Location getReturnLoc()
  312. + {
  313. + return _returnLoc;
  314. + }
  315. +
  316. + public Location getTeleportLoc()
  317. + {
  318. + return _teleportLoc;
  319. + }
  320. +
  321. + public List<String> getScheduleTimes()
  322. + {
  323. + return _scheduleTimes;
  324. + }
  325. +
  326. + public List<SpawnInfo> getMonsterSpawns()
  327. + {
  328. + return _monsterSpawns;
  329. + }
  330. +
  331. + public List<SpawnInfo> getGkSpawns()
  332. + {
  333. + return _gkSpawns;
  334. + }
  335. +
  336. + public List<Integer> getAllowedClassIds()
  337. + {
  338. + return _allowedClassIds;
  339. + }
  340. +
  341. + public boolean isAllClassesAllowed()
  342. + {
  343. + return _allClassesAllowed;
  344. + }
  345. +
  346. + public static SpoilEventData getInstance()
  347. + {
  348. + return SingletonHolder.INSTANCE;
  349. + }
  350. +
  351. + private static class SingletonHolder
  352. + {
  353. + protected static final SpoilEventData INSTANCE = new SpoilEventData();
  354. + }
  355. +}
  356. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventManager.java b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventManager.java
  357. new file mode 100644
  358. index 0000000..ca0d13f
  359. --- /dev/null
  360. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventManager.java
  361. @@ -0,0 +1,312 @@
  362. +package net.sf.l2j.gameserver.events.spoil;
  363. +
  364. +import java.util.concurrent.ScheduledFuture;
  365. +
  366. +import net.sf.l2j.commons.concurrent.ThreadPool;
  367. +import net.sf.l2j.commons.logging.CLogger;
  368. +
  369. +import net.sf.l2j.gameserver.model.World;
  370. +import net.sf.l2j.gameserver.data.manager.ZoneManager;
  371. +import net.sf.l2j.gameserver.model.actor.Player;
  372. +import net.sf.l2j.gameserver.model.location.Location;
  373. +import net.sf.l2j.gameserver.model.zone.type.SpoilZone;
  374. +import net.sf.l2j.gameserver.network.serverpackets.ConfirmDlg;
  375. +import net.sf.l2j.gameserver.network.serverpackets.ExShowScreenMessage;
  376. +import net.sf.l2j.gameserver.network.serverpackets.ExShowScreenMessage.SMPOS;
  377. +import net.sf.l2j.gameserver.network.SystemMessageId;
  378. +import net.sf.l2j.gameserver.model.entity.Events.CaptureTheFlag.CTFEvent;
  379. +import net.sf.l2j.gameserver.model.entity.Events.DeathMatch.DMEvent;
  380. +import net.sf.l2j.gameserver.model.entity.Events.TeamVsTeam.TvTEvent;
  381. +
  382. +/**
  383. + * @author Jhon
  384. + */
  385. +public class SpoilEventManager
  386. +{
  387. + private static final CLogger LOGGER = new CLogger(SpoilEventManager.class.getName());
  388. +
  389. + private boolean _active = false;
  390. + private boolean _registrationOpen = false;
  391. + private ScheduledFuture<?> _endTask = null;
  392. + private ScheduledFuture<?> _countdownTask = null;
  393. + private ScheduledFuture<?> _registrationTask = null;
  394. + private long _endTime = 0;
  395. +
  396. + protected SpoilEventManager()
  397. + {
  398. + }
  399. +
  400. + public synchronized void startEvent()
  401. + {
  402. + if (_active)
  403. + {
  404. + LOGGER.warn("SpoilEvent: Attempt to start event but it is already active.");
  405. + return;
  406. + }
  407. +
  408. + if (!SpoilEventData.getInstance().isEnabled())
  409. + {
  410. + LOGGER.warn("SpoilEvent: Attempt to start event but it is disabled in config.");
  411. + return;
  412. + }
  413. +
  414. + _active = true;
  415. +
  416. + // Spawn NPCs
  417. + SpoilEventSpawner.getInstance().spawnAll();
  418. +
  419. + // Announcement
  420. + World.announceToOnlinePlayers("[Spoil Event]: The event has started!");
  421. + World.announceToOnlinePlayers("[Spoil Event]: Duration: " + SpoilEventData.getInstance().getDurationMinutes() + " minute(s)!");
  422. + World.toAllOnlinePlayers(new ExShowScreenMessage("Spoil Event Started! Teleport Now!", 5000, SMPOS.TOP_CENTER, true));
  423. +
  424. + // Schedule end
  425. + final long durationMillis = SpoilEventData.getInstance().getDurationMinutes() * 60 * 1000L;
  426. + _endTime = System.currentTimeMillis() + durationMillis;
  427. +
  428. + if (_endTask != null)
  429. + _endTask.cancel(false);
  430. +
  431. + _endTask = ThreadPool.schedule(() -> stopEvent(false), durationMillis);
  432. +
  433. + // Schedule countdowns
  434. + scheduleCountdown(durationMillis);
  435. +
  436. + // Open Registration/Popup for 30 seconds
  437. + _registrationOpen = true;
  438. + _registrationTask = ThreadPool.schedule(() -> _registrationOpen = false, 30000);
  439. +
  440. + // Broadcast Popup to eligible players
  441. + for (Player player : World.getInstance().getPlayers())
  442. + {
  443. + if (player == null || player.isOnline() == false)
  444. + continue;
  445. +
  446. + // Skip if in other events or busy
  447. + if (player.isInOlympiadMode() || player.isAlikeDead() || player.isTeleporting() || player.isInObserverMode() || player.isInStoreMode() || TvTEvent.isPlayerParticipant(player.getObjectId()) || CTFEvent.isPlayerParticipant(player.getObjectId()) || DMEvent.isPlayerParticipant(player.getObjectId()))
  448. + continue;
  449. +
  450. + // Skip unauthorized classes if restriction is on
  451. + if (!SpoilEventData.getInstance().isAllClassesAllowed() && !SpoilEventData.getInstance().getAllowedClassIds().contains(player.getClassId().getId()) && !player.isGM())
  452. + continue;
  453. +
  454. + ConfirmDlg confirm = new ConfirmDlg(SystemMessageId.S1.getId());
  455. + confirm.addString("Spoil Event started! Do you wish to join?");
  456. + confirm.addTime(30000);
  457. + confirm.addRequesterId(player.getObjectId());
  458. + player.sendPacket(confirm);
  459. + }
  460. +
  461. + // Validate players already inside (anti-camping non-spoilers)
  462. + for (SpoilZone zone : ZoneManager.getInstance().getAllZones(SpoilZone.class))
  463. + {
  464. + for (Player player : zone.getKnownTypeInside(Player.class))
  465. + {
  466. + if (player != null)
  467. + {
  468. + zone.revalidateInZone(player); // Trigger onEnter check again or manual check?
  469. + // SpoilZone.onEnter checks logic. But setInsideZone might stay true.
  470. + // Let's call a manual check method to be safe, or just reuse logic.
  471. + // Simpler: Check rules manually here or call zone.onEnter(player) effectively?
  472. + // onEnter guards with 'isActive' which is now true. So logic works.
  473. + // But onEnter also checks if player is already inside internal list?
  474. + // ZoneType: onEnter often checks if (!isInsideZone(object)).
  475. + // So we might need to manually check requirements here.
  476. +
  477. + if (!player.isGM() && player.getClassId().getId() != 53 && // Scavenger
  478. + player.getClassId().getId() != 54 && // Bounty Hunter
  479. + player.getClassId().getId() != 117) // Fortune Seeker
  480. + {
  481. + player.sendMessage("Spoil Event started! Non-spoilers are moved out.");
  482. + Location loc = SpoilEventData.getInstance().getReturnLoc();
  483. + if (loc != null)
  484. + player.teleportTo(loc.getX(), loc.getY(), loc.getZ(), 0);
  485. + else
  486. + player.teleportTo(81260, 148607, -3440, 0);
  487. + }
  488. + }
  489. + }
  490. + }
  491. + }
  492. +
  493. + public synchronized void stopEvent(boolean manual)
  494. + {
  495. + if (!_active)
  496. + return;
  497. +
  498. + _active = false;
  499. + _endTime = 0;
  500. +
  501. + // Cancel tasks
  502. + if (_endTask != null)
  503. + {
  504. + _endTask.cancel(false);
  505. + _endTask = null;
  506. + }
  507. +
  508. + if (_registrationTask != null)
  509. + {
  510. + _registrationTask.cancel(false);
  511. + _registrationTask = null;
  512. + _registrationOpen = false;
  513. + }
  514. +
  515. + if (_countdownTask != null)
  516. + {
  517. + _countdownTask.cancel(false);
  518. + _countdownTask = null;
  519. + }
  520. +
  521. + // Despawn NPCs
  522. + SpoilEventSpawner.getInstance().despawnAll();
  523. +
  524. + // Teleport players out of the zone
  525. + for (SpoilZone zone : ZoneManager.getInstance().getAllZones(SpoilZone.class))
  526. + {
  527. + for (Player player : zone.getKnownTypeInside(Player.class))
  528. + {
  529. + if (player != null && !player.isGM()) // Optionally keep GMs?
  530. + {
  531. + Location loc = SpoilEventData.getInstance().getReturnLoc();
  532. + if (loc != null)
  533. + player.teleportTo(loc.getX(), loc.getY(), loc.getZ(), 0);
  534. + else
  535. + player.teleportTo(81260, 148607, -3440, 0);
  536. +
  537. + player.sendMessage("Spoil Event has ended. You have been teleported out.");
  538. + }
  539. + }
  540. + }
  541. +
  542. + // Announcement
  543. + World.announceToOnlinePlayers("[Spoil Event]: The event has finished!");
  544. + World.toAllOnlinePlayers(new ExShowScreenMessage("Spoil Event Finished!", 5000, SMPOS.TOP_CENTER, true));
  545. +
  546. + // Reschedule next
  547. + if (!manual || SpoilEventData.getInstance().isAllowAutoReschedule())
  548. + {
  549. + SpoilEventScheduler.getInstance().scheduleNext();
  550. + }
  551. + }
  552. +
  553. + private void scheduleCountdown(long remainingMillis)
  554. + {
  555. + // Simple recursive countdown scheduler or just scheduling multiple tasks?
  556. + // To be safe and clean, we can schedule unique tasks for each checkpoint.
  557. + // Checkpoints: 30m, 15m, 10m, 5m, 3m, 2m, 1m, 30s, 15s, 10s, 5s, 3s, 2s, 1s
  558. +
  559. + int[] checks =
  560. + {
  561. + 30 * 60,
  562. + 15 * 60,
  563. + 10 * 60,
  564. + 5 * 60,
  565. + 3 * 60,
  566. + 2 * 60,
  567. + 60,
  568. + 30,
  569. + 15,
  570. + 10,
  571. + 5,
  572. + 3,
  573. + 2,
  574. + 1
  575. + };
  576. +
  577. + for (int sec : checks)
  578. + {
  579. + long notifyMillis = remainingMillis - (sec * 1000L);
  580. + if (notifyMillis > 0)
  581. + {
  582. + ThreadPool.schedule(() ->
  583. + {
  584. + if (_active)
  585. + {
  586. + String timeMsg = (sec >= 60) ? (sec / 60) + " minute(s)" : sec + " second(s)";
  587. + World.announceToOnlinePlayers("[Spoil Event]: " + timeMsg + " remaining!");
  588. + }
  589. + }, notifyMillis);
  590. + }
  591. + }
  592. + }
  593. +
  594. + public boolean isActive()
  595. + {
  596. + return _active;
  597. + }
  598. +
  599. + public long getEndTime()
  600. + {
  601. + return _endTime;
  602. + }
  603. +
  604. + public static SpoilEventManager getInstance()
  605. + {
  606. + return SingletonHolder.INSTANCE;
  607. + }
  608. +
  609. + public boolean isRegistrationOpen()
  610. + {
  611. + return _registrationOpen;
  612. + }
  613. +
  614. + public void registerPlayer(Player player)
  615. + {
  616. + if (!_active || player == null)
  617. + return;
  618. +
  619. + // Re-check conditions in case they changed since popup
  620. + if (player.isInOlympiadMode() || player.isAlikeDead() || player.isTeleporting() || player.isInObserverMode() || player.isInStoreMode())
  621. + {
  622. + player.sendMessage("You cannot join the event in your current state.");
  623. + return;
  624. + }
  625. +
  626. + if (!SpoilEventData.getInstance().isAllClassesAllowed() && !SpoilEventData.getInstance().getAllowedClassIds().contains(player.getClassId().getId()) && !player.isGM())
  627. + {
  628. + player.sendMessage("Your class is not allowed in this event.");
  629. + return;
  630. + }
  631. +
  632. + // Teleport to Gatekeeper spawn or random spawn?
  633. + // Typically events teleport to a starting area.
  634. + // Let's use the first GK spawn location if available, or just the first monster spawn area?
  635. + // Actually, users usually walk in. If we teleport, we should teleport to a safe spot inside.
  636. + // Let's us the RETURN location? No, that's for exit.
  637. + // Let's pick the first Monster or GK location as a "drop in" point.
  638. + // Ideally we'd have a specific "enter" location in XML, but we don't.
  639. + // We will peek at monster spawns and pick one.
  640. +
  641. + // Use configured teleport location
  642. + Location targetLoc = SpoilEventData.getInstance().getTeleportLoc();
  643. +
  644. + // Fallback if not configured (try monster spawns -> GK spawns)
  645. + if (targetLoc == null)
  646. + {
  647. + if (!SpoilEventData.getInstance().getMonsterSpawns().isEmpty())
  648. + {
  649. + Location l = SpoilEventData.getInstance().getMonsterSpawns().get(0).locations.get(0);
  650. + targetLoc = new Location(l.getX(), l.getY(), l.getZ());
  651. + }
  652. + else if (!SpoilEventData.getInstance().getGkSpawns().isEmpty())
  653. + {
  654. + Location l = SpoilEventData.getInstance().getGkSpawns().get(0).locations.get(0);
  655. + targetLoc = new Location(l.getX(), l.getY(), l.getZ());
  656. + }
  657. + }
  658. +
  659. + if (targetLoc != null)
  660. + {
  661. + player.teleportTo(targetLoc.getX(), targetLoc.getY(), targetLoc.getZ(), 0);
  662. + }
  663. + else
  664. + {
  665. + player.sendMessage("No spawn location found for event.");
  666. + }
  667. + }
  668. +
  669. + private static class SingletonHolder
  670. + {
  671. + protected static final SpoilEventManager INSTANCE = new SpoilEventManager();
  672. + }
  673. +}
  674. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventScheduler.java b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventScheduler.java
  675. new file mode 100644
  676. index 0000000..7e60483
  677. --- /dev/null
  678. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventScheduler.java
  679. @@ -0,0 +1,119 @@
  680. +package net.sf.l2j.gameserver.events.spoil;
  681. +
  682. +import java.util.Calendar;
  683. +import java.util.Collections;
  684. +import java.util.List;
  685. +
  686. +import net.sf.l2j.commons.concurrent.ThreadPool;
  687. +import net.sf.l2j.commons.logging.CLogger;
  688. +
  689. +/**
  690. + * @author Jhon
  691. + */
  692. +public class SpoilEventScheduler
  693. +{
  694. + private static final CLogger LOGGER = new CLogger(SpoilEventScheduler.class.getName());
  695. +
  696. + private String _nextEventTime = "N/A";
  697. + private long _nextEventMillis = 0;
  698. +
  699. + protected SpoilEventScheduler()
  700. + {
  701. + }
  702. +
  703. + public void init()
  704. + {
  705. + if (!SpoilEventData.getInstance().isEnabled())
  706. + return;
  707. +
  708. + scheduleNext();
  709. + }
  710. +
  711. + public void scheduleNext()
  712. + {
  713. + final List<String> times = SpoilEventData.getInstance().getScheduleTimes();
  714. + if (times.isEmpty())
  715. + {
  716. + _nextEventTime = "None";
  717. + return;
  718. + }
  719. +
  720. + long currentTime = System.currentTimeMillis();
  721. + long nextTime = 0;
  722. + String nextTimeString = "";
  723. +
  724. + // Sort times to ensure correct order if XML is mixed
  725. + Collections.sort(times);
  726. +
  727. + Calendar test = Calendar.getInstance();
  728. + test.setLenient(true);
  729. +
  730. + // Find closest future time
  731. + boolean found = false;
  732. + for (String t : times)
  733. + {
  734. + String[] split = t.split(":");
  735. + if (split.length != 2)
  736. + continue;
  737. +
  738. + test.setTimeInMillis(currentTime);
  739. + test.set(Calendar.HOUR_OF_DAY, Integer.parseInt(split[0]));
  740. + test.set(Calendar.MINUTE, Integer.parseInt(split[1]));
  741. + test.set(Calendar.SECOND, 0);
  742. + test.set(Calendar.MILLISECOND, 0);
  743. +
  744. + if (test.getTimeInMillis() > currentTime)
  745. + {
  746. + nextTime = test.getTimeInMillis();
  747. + nextTimeString = t;
  748. + found = true;
  749. + break;
  750. + }
  751. + }
  752. +
  753. + // If no time found for today, pick the first time for tomorrow
  754. + if (!found)
  755. + {
  756. + String t = times.get(0);
  757. + String[] split = t.split(":");
  758. +
  759. + test.setTimeInMillis(currentTime);
  760. + test.add(Calendar.DAY_OF_YEAR, 1); // Tomorrow
  761. + test.set(Calendar.HOUR_OF_DAY, Integer.parseInt(split[0]));
  762. + test.set(Calendar.MINUTE, Integer.parseInt(split[1]));
  763. + test.set(Calendar.SECOND, 0);
  764. + test.set(Calendar.MILLISECOND, 0);
  765. +
  766. + nextTime = test.getTimeInMillis();
  767. + nextTimeString = t;
  768. + }
  769. +
  770. + _nextEventTime = nextTimeString;
  771. + _nextEventMillis = nextTime;
  772. +
  773. + final long delay = nextTime - currentTime;
  774. + LOGGER.info("SpoilEvent: Next event scheduled at {} (in {} ms).", _nextEventTime, delay);
  775. +
  776. + ThreadPool.schedule(SpoilEventManager.getInstance()::startEvent, delay);
  777. + }
  778. +
  779. + public String getNextEventTimeString()
  780. + {
  781. + return _nextEventTime;
  782. + }
  783. +
  784. + public long getNextStartTime()
  785. + {
  786. + return _nextEventMillis;
  787. + }
  788. +
  789. + public static SpoilEventScheduler getInstance()
  790. + {
  791. + return SingletonHolder.INSTANCE;
  792. + }
  793. +
  794. + private static class SingletonHolder
  795. + {
  796. + protected static final SpoilEventScheduler INSTANCE = new SpoilEventScheduler();
  797. + }
  798. +}
  799. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventSpawner.java b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventSpawner.java
  800. new file mode 100644
  801. index 0000000..fe665c7
  802. --- /dev/null
  803. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/events/spoil/SpoilEventSpawner.java
  804. @@ -0,0 +1,111 @@
  805. +package net.sf.l2j.gameserver.events.spoil;
  806. +
  807. +import java.util.ArrayList;
  808. +import java.util.List;
  809. +
  810. +import net.sf.l2j.commons.logging.CLogger;
  811. +
  812. +import net.sf.l2j.gameserver.data.sql.SpawnTable;
  813. +import net.sf.l2j.gameserver.data.xml.NpcData;
  814. +import net.sf.l2j.gameserver.events.spoil.SpoilEventData.SpawnInfo;
  815. +import net.sf.l2j.gameserver.model.actor.Npc;
  816. +import net.sf.l2j.gameserver.model.actor.template.NpcTemplate;
  817. +import net.sf.l2j.gameserver.model.location.Location;
  818. +import net.sf.l2j.gameserver.model.spawn.L2Spawn;
  819. +
  820. +/**
  821. + * @author Jhon
  822. + */
  823. +public class SpoilEventSpawner
  824. +{
  825. + private static final CLogger LOGGER = new CLogger(SpoilEventSpawner.class.getName());
  826. +
  827. + private final List<L2Spawn> _activeSpawns = new ArrayList<>();
  828. +
  829. + protected SpoilEventSpawner()
  830. + {
  831. + }
  832. +
  833. + /**
  834. + * Spawns all configured monsters and gatekeepers.
  835. + */
  836. + public void spawnAll()
  837. + {
  838. + spawnGroup(SpoilEventData.getInstance().getMonsterSpawns());
  839. + spawnGroup(SpoilEventData.getInstance().getGkSpawns());
  840. +
  841. + LOGGER.info("SpoilEvent: Spawned {} NPCs.", _activeSpawns.size());
  842. + }
  843. +
  844. + private void spawnGroup(List<SpawnInfo> group)
  845. + {
  846. + for (SpawnInfo info : group)
  847. + {
  848. + final NpcTemplate template = NpcData.getInstance().getTemplate(info.npcId);
  849. + if (template == null)
  850. + {
  851. + LOGGER.warn("SpoilEvent: Invalid NPC ID {}, skipping.", info.npcId);
  852. + continue;
  853. + }
  854. +
  855. + for (Location loc : info.locations)
  856. + {
  857. + try
  858. + {
  859. + final L2Spawn spawn = new L2Spawn(template);
  860. + spawn.setLoc(loc.getX(), loc.getY(), loc.getZ(), 0);
  861. + spawn.setRespawnDelay(info.respawnDelay);
  862. +
  863. + SpawnTable.getInstance().addSpawn(spawn, false);
  864. + spawn.doSpawn(false);
  865. +
  866. + if (info.respawnDelay > 0)
  867. + spawn.setRespawnState(true);
  868. + else
  869. + spawn.setRespawnState(false);
  870. +
  871. + _activeSpawns.add(spawn);
  872. + }
  873. + catch (Exception e)
  874. + {
  875. + LOGGER.error("SpoilEvent: Failed to spawn NPC {}.", e, info.npcId);
  876. + }
  877. + }
  878. + }
  879. + }
  880. +
  881. + /**
  882. + * Despawns all event NPCs and clears the list.
  883. + */
  884. + public void despawnAll()
  885. + {
  886. + if (_activeSpawns.isEmpty())
  887. + return;
  888. +
  889. + for (L2Spawn spawn : _activeSpawns)
  890. + {
  891. + if (spawn == null)
  892. + continue;
  893. +
  894. + spawn.setRespawnState(false);
  895. + SpawnTable.getInstance().deleteSpawn(spawn, false);
  896. +
  897. + final Npc npc = spawn.getNpc();
  898. + if (npc != null)
  899. + npc.deleteMe();
  900. + }
  901. +
  902. + LOGGER.info("SpoilEvent: Despawned {} NPCs.", _activeSpawns.size());
  903. + _activeSpawns.clear();
  904. + }
  905. +
  906. + public static SpoilEventSpawner getInstance()
  907. + {
  908. + return SingletonHolder.INSTANCE;
  909. + }
  910. +
  911. + private static class SingletonHolder
  912. + {
  913. + protected static final SpoilEventSpawner INSTANCE = new SpoilEventSpawner();
  914. + }
  915. +}
  916. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java b/aCis_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
  917. index 7d373be..d5b9ba4 100644
  918. --- a/aCis_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
  919. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/handler/AdminCommandHandler.java
  920. @@ -58,6 +58,7 @@ import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminSiege;
  921. import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminSkill;
  922. import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminSoloBoss;
  923. import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminSpawn;
  924. +import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminSpoilEvent;
  925. import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminTarget;
  926. import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminTeleport;
  927. import net.sf.l2j.gameserver.handler.admincommandhandlers.AdminTvTEvent;
  928. @@ -133,6 +134,7 @@ public class AdminCommandHandler
  929. registerHandler(new AdminZoneCreate());
  930. registerHandler(new AdminClanFull());
  931. registerHandler(new AdminSetHero());
  932. + registerHandler(new AdminSpoilEvent());
  933. }
  934.  
  935. public void registerHandler(IAdminCommandHandler handler)
  936. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminSpoilEvent.java b/aCis_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminSpoilEvent.java
  937. new file mode 100644
  938. index 0000000..a53e75a
  939. --- /dev/null
  940. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/handler/admincommandhandlers/AdminSpoilEvent.java
  941. @@ -0,0 +1,72 @@
  942. +package net.sf.l2j.gameserver.handler.admincommandhandlers;
  943. +
  944. +import net.sf.l2j.gameserver.events.spoil.SpoilEventManager;
  945. +import net.sf.l2j.gameserver.events.spoil.SpoilEventScheduler;
  946. +import net.sf.l2j.gameserver.handler.IAdminCommandHandler;
  947. +import net.sf.l2j.gameserver.model.actor.Player;
  948. +
  949. +/**
  950. + * @author Jhon
  951. + */
  952. +public class AdminSpoilEvent implements IAdminCommandHandler
  953. +{
  954. + private static final String[] ADMIN_COMMANDS =
  955. + {
  956. + "admin_spoilevent",
  957. + "admin_spoilevent_start",
  958. + "admin_spoilevent_stop",
  959. + "admin_spoilevent_status"
  960. + };
  961. +
  962. + @Override
  963. + public boolean useAdminCommand(String command, Player activeChar)
  964. + {
  965. + if (activeChar == null)
  966. + return false;
  967. +
  968. + if (command.equals("admin_spoilevent") || command.equals("admin_spoilevent_status"))
  969. + {
  970. + showStatus(activeChar);
  971. + }
  972. + else if (command.equals("admin_spoilevent_start"))
  973. + {
  974. + if (SpoilEventManager.getInstance().isActive())
  975. + activeChar.sendMessage("Spoil Event is already active.");
  976. + else
  977. + {
  978. + activeChar.sendMessage("Starting Spoil Event manually...");
  979. + SpoilEventManager.getInstance().startEvent();
  980. + }
  981. + }
  982. + else if (command.equals("admin_spoilevent_stop"))
  983. + {
  984. + if (!SpoilEventManager.getInstance().isActive())
  985. + activeChar.sendMessage("Spoil Event is not active.");
  986. + else
  987. + {
  988. + activeChar.sendMessage("Stopping Spoil Event manually...");
  989. + SpoilEventManager.getInstance().stopEvent(true);
  990. + }
  991. + }
  992. +
  993. + return true;
  994. + }
  995. +
  996. + private static void showStatus(Player activeChar)
  997. + {
  998. + boolean active = SpoilEventManager.getInstance().isActive();
  999. + activeChar.sendMessage("Spoil Event Status: " + (active ? "ACTIVE" : "INACTIVE"));
  1000. + if (active)
  1001. + {
  1002. + long remaining = SpoilEventManager.getInstance().getEndTime() - System.currentTimeMillis();
  1003. + activeChar.sendMessage("Time Remaining: " + (remaining / 60000) + " minutes.");
  1004. + }
  1005. + activeChar.sendMessage("Next Scheduled: " + SpoilEventScheduler.getInstance().getNextEventTimeString());
  1006. + }
  1007. +
  1008. + @Override
  1009. + public String[] getAdminCommandList()
  1010. + {
  1011. + return ADMIN_COMMANDS;
  1012. + }
  1013. +}
  1014. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/handler/voicedcommandhandlers/VoicedMenuSettings.java b/aCis_gameserver/java/net/sf/l2j/gameserver/handler/voicedcommandhandlers/VoicedMenuSettings.java
  1015. index 7c1d236..649f20f 100644
  1016. --- a/aCis_gameserver/java/net/sf/l2j/gameserver/handler/voicedcommandhandlers/VoicedMenuSettings.java
  1017. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/handler/voicedcommandhandlers/VoicedMenuSettings.java
  1018. @@ -15,6 +15,8 @@ import java.util.List;
  1019. import java.util.TimeZone;
  1020.  
  1021. import net.sf.l2j.gameserver.model.entity.Events.SoloBossEvent;
  1022. +import net.sf.l2j.gameserver.events.spoil.SpoilEventManager;
  1023. +import net.sf.l2j.gameserver.events.spoil.SpoilEventScheduler;
  1024. import net.sf.l2j.gameserver.model.entity.Events.CaptureTheFlag.CTFEvent;
  1025. import net.sf.l2j.gameserver.model.entity.Events.CaptureTheFlag.CTFManager;
  1026. import net.sf.l2j.gameserver.model.entity.Events.DeathMatch.DMEvent;
  1027. @@ -127,6 +129,7 @@ public class VoicedMenuSettings implements IVoicedCommandHandler
  1028. html.replace("%tournament%", getEventStatus(TournamentManager.getInstance().getCurrentFights().size() > 0, TournamentManager.getInstance().getNextStartTime()));
  1029. html.replace("%soloboss%", getEventStatus(SoloBossEvent.getInstance().getCurrentNpcId() != 0, SoloBossEvent.getInstance().getNextStartTime()));
  1030. html.replace("%partyfarm%", getEventStatus(PartyFarm.is_started(), InitialPartyFarm.getInstance().getNextStartTime()));
  1031. + html.replace("%spoilevent%", getEventStatus(SpoilEventManager.getInstance().isActive(), SpoilEventScheduler.getInstance().getNextStartTime()));
  1032.  
  1033. String nextEventString = "No events";
  1034.  
  1035. @@ -144,6 +147,8 @@ public class VoicedMenuSettings implements IVoicedCommandHandler
  1036. nextEventString = "Solo Boss is now in progress!";
  1037. else if (PartyFarm.is_started())
  1038. nextEventString = "Party Farm is now in progress!";
  1039. + else if (SpoilEventManager.getInstance().isActive())
  1040. + nextEventString = "Spoil Event is now in progress!";
  1041. else
  1042. {
  1043. long currentTime = System.currentTimeMillis();
  1044. @@ -154,6 +159,7 @@ public class VoicedMenuSettings implements IVoicedCommandHandler
  1045. long nextTour = TournamentManager.getInstance().getNextStartTime();
  1046. long nextSolo = SoloBossEvent.getInstance().getNextStartTime();
  1047. long nextFarm = InitialPartyFarm.getInstance().getNextStartTime();
  1048. + long nextSpoil = SpoilEventScheduler.getInstance().getNextStartTime();
  1049.  
  1050. List<EventInfo> events = new ArrayList<>();
  1051. if (nextTvT > currentTime)
  1052. @@ -170,6 +176,8 @@ public class VoicedMenuSettings implements IVoicedCommandHandler
  1053. events.add(new EventInfo("Solo Boss will start in", nextSolo));
  1054. if (nextFarm > currentTime)
  1055. events.add(new EventInfo("Party Farm will start in", nextFarm));
  1056. + if (nextSpoil > currentTime)
  1057. + events.add(new EventInfo("Spoil Event will start in", nextSpoil));
  1058.  
  1059. if (!events.isEmpty())
  1060. {
  1061.  
  1062. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/model/zone/type/SpoilZone.java b/aCis_gameserver/java/net/sf/l2j/gameserver/model/zone/type/SpoilZone.java
  1063. new file mode 100644
  1064. index 0000000..f1836ce
  1065. --- /dev/null
  1066. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/model/zone/type/SpoilZone.java
  1067. @@ -0,0 +1,60 @@
  1068. +package net.sf.l2j.gameserver.model.zone.type;
  1069. +
  1070. +import net.sf.l2j.gameserver.enums.ZoneId;
  1071. +import net.sf.l2j.gameserver.events.spoil.SpoilEventData;
  1072. +import net.sf.l2j.gameserver.events.spoil.SpoilEventManager;
  1073. +import net.sf.l2j.gameserver.model.actor.Creature;
  1074. +import net.sf.l2j.gameserver.model.actor.Player;
  1075. +import net.sf.l2j.gameserver.model.location.Location;
  1076. +import net.sf.l2j.gameserver.model.zone.ZoneType;
  1077. +
  1078. +/**
  1079. + * @author Jhon
  1080. + */
  1081. +public class SpoilZone extends ZoneType
  1082. +{
  1083. + public SpoilZone(int id)
  1084. + {
  1085. + super(id);
  1086. + }
  1087. +
  1088. + @Override
  1089. + protected void onEnter(Creature character)
  1090. + {
  1091. + // Only check rules if event is running
  1092. + if (!SpoilEventManager.getInstance().isActive())
  1093. + {
  1094. + character.setInsideZone(ZoneId.SPOIL_AREA, true);
  1095. + return;
  1096. + }
  1097. + if (character instanceof Player)
  1098. + {
  1099. + Player player = (Player) character;
  1100. +
  1101. + // 1. Check Class Restriction
  1102. + if (!player.isGM() && !SpoilEventData.getInstance().isAllClassesAllowed() && !SpoilEventData.getInstance().getAllowedClassIds().contains(player.getClassId().getId()))
  1103. + {
  1104. + player.sendMessage("Only authorized classes are allowed in this area.");
  1105. + teleportOut(player);
  1106. + return;
  1107. + }
  1108. +
  1109. + character.setInsideZone(ZoneId.SPOIL_AREA, true);
  1110. + }
  1111. + }
  1112. +
  1113. + @Override
  1114. + protected void onExit(Creature character)
  1115. + {
  1116. + character.setInsideZone(ZoneId.SPOIL_AREA, false);
  1117. + }
  1118. +
  1119. + private static void teleportOut(Player player)
  1120. + {
  1121. + Location loc = SpoilEventData.getInstance().getReturnLoc(); // This will error because SpoilEventData.getInstance() is static but method was instance context? No, static context is fine for getInstance().
  1122. + if (loc != null)
  1123. + player.teleportTo(loc.getX(), loc.getY(), loc.getZ(), 0);
  1124. + else
  1125. + player.teleportTo(81260, 148607, -3440, 0); // Default Giran fallback
  1126. + }
  1127. +}
  1128. diff --git a/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/DlgAnswer.java b/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/DlgAnswer.java
  1129. index 90e1548..ad1cb97 100644
  1130. --- a/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/DlgAnswer.java
  1131. +++ b/aCis_gameserver/java/net/sf/l2j/gameserver/network/clientpackets/DlgAnswer.java
  1132. @@ -13,6 +13,7 @@ import net.sf.l2j.gameserver.model.entity.Events.LastMan.LMEvent;
  1133. import net.sf.l2j.gameserver.model.entity.Events.LastMan.LMManager;
  1134. import net.sf.l2j.gameserver.model.entity.Events.TeamVsTeam.TvTEvent;
  1135. import net.sf.l2j.gameserver.model.entity.Events.TeamVsTeam.TvTManager;
  1136. +import net.sf.l2j.gameserver.events.spoil.SpoilEventManager;
  1137. import net.sf.l2j.gameserver.model.entity.Tournament.TournamentManager;
  1138. import net.sf.l2j.gameserver.model.entity.Tournament.Data.TournamentTeam;
  1139. import net.sf.l2j.gameserver.model.group.Party;
  1140. @@ -63,12 +64,10 @@ public final class DlgAnswer extends L2GameClientPacket
  1141. LMEvent.onBypass("lm_event_participation", activeChar);
  1142. else if (TvTManager.getInstance().register_tvt == true)
  1143. TvTEvent.onBypass("tvt_event_participation", activeChar);
  1144. + else if (SpoilEventManager.getInstance().isRegistrationOpen())
  1145. + SpoilEventManager.getInstance().registerPlayer(activeChar);
  1146. + else if (SoloBossEvent.getInstance().getCurrentNpcId() > 0)
  1147. + SoloBossEvent.getInstance().teleportPlayerToEvent(activeChar);
  1148. }
  1149.  
  1150. else if (_messageId == SystemMessageId.S1.getId())
  1151.  
Advertisement
Add Comment
Please, Sign In to add comment