package instance; import java.util.concurrent.Future; import java.util.concurrent.atomic.AtomicBoolean; import java.util.concurrent.atomic.AtomicInteger; import java.util.concurrent.atomic.AtomicReference; import com.aionemu.commons.utils.Rnd; import com.aionemu.gameserver.ai.NpcAI; import com.aionemu.gameserver.ai.manager.WalkManager; import com.aionemu.gameserver.instance.handlers.GeneralInstanceHandler; import com.aionemu.gameserver.instance.handlers.InstanceID; import com.aionemu.gameserver.model.EmotionType; import com.aionemu.gameserver.model.Race; import com.aionemu.gameserver.model.gameobjects.Creature; import com.aionemu.gameserver.model.gameobjects.Npc; import com.aionemu.gameserver.model.gameobjects.VisibleObject; import com.aionemu.gameserver.model.gameobjects.player.Player; import com.aionemu.gameserver.model.gameobjects.state.CreatureState; import com.aionemu.gameserver.network.aion.serverpackets.SM_EMOTION; import com.aionemu.gameserver.network.aion.serverpackets.SM_SYSTEM_MESSAGE; import com.aionemu.gameserver.services.RespawnService; import com.aionemu.gameserver.services.player.PlayerReviveService; import com.aionemu.gameserver.services.teleport.TeleportService; import com.aionemu.gameserver.skillengine.SkillEngine; import com.aionemu.gameserver.skillengine.model.Effect; import com.aionemu.gameserver.utils.PacketSendUtility; import com.aionemu.gameserver.utils.ThreadPoolManager; import com.aionemu.gameserver.world.WorldMapInstance; import com.aionemu.gameserver.world.WorldPosition; /** * Quick Difficulty Guide: * Difficulty 5: Hard Mode * - Players have to kill the twin bosses in under 5 minutes within a 15s gap * - Players have to kill Orissan in his second vulnerable phase * - Players have to defeat 5 waves to defend their siege weapon * - Players have to dispel 3 buffs of Beritra and he'll transform into a dragon * Difficulty 3: Normal Mode * - Players have failed to kill the twin bosses in under 5 minutes or failed to kill Orissan in his second vulnerable phase * - Players have to kill a weakened Orissan in his second vulnerable phase (if they already failed the twin bosses) * - Players have to defeat 3 waves to defend their siege weapon * - Beritra remains human and NPCs will help players removing "Everlasting Life" * Difficulty 1: Easy Mode * - Players have failed to kill the twin bosses in under 5 minutes and failed to kill the weakened Orissan in his second vulnerable phase * - Players have to defeat 1 wave to defend their siege weapon * - Beritra remains human and NPCs will immediately help players removing all buffs * Difficulty 0: Fail Run * - Players have failed to defend their siege weapon, so no Beritra for them * * @author Estrayl */ @InstanceID(301390000) public class DrakenspireDepthsInstance extends GeneralInstanceHandler { private static final int HARD_MODE = 5; private static final int NORMAL_MODE = 3; private static final int EASY_MODE = 1; private static final int FAILED = 0; private final AtomicInteger difficulty = new AtomicInteger(HARD_MODE); private final AtomicBoolean isStageActive = new AtomicBoolean(); private final AtomicInteger waveSurvivors = new AtomicInteger(8); // increases the final boss time by 15s for each survivor private final AtomicInteger eventCounter = new AtomicInteger(); private final AtomicReference race = new AtomicReference<>(); private Future currentEventTask, additionalEventTask; // re-usable for all stages public DrakenspireDepthsInstance(WorldMapInstance instance) { super(instance); } @Override public void onInstanceDestroy() { cancelTasks(currentEventTask, additionalEventTask); } @Override public void onEnterInstance(Player player) { if (race.compareAndSet(null, player.getRace())) { spawn(race.get() == Race.ELYOS ? 209678 : 209743, 353.443f, 185.629f, 1686.1825f, (byte) 60); spawn(race.get() == Race.ELYOS ? 209679 : 209744, 353.443f, 179.259f, 1686.1825f, (byte) 60); spawn(race.get() == Race.ELYOS ? 209680 : 209745, 347.099f, 192.390f, 1686.1825f, (byte) 90); spawn(race.get() == Race.ELYOS ? 209680 : 209745, 351.295f, 192.437f, 1686.1825f, (byte) 90); spawn(race.get() == Race.ELYOS ? 209681 : 209746, 347.099f, 173.774f, 1686.1825f, (byte) 30); spawn(race.get() == Race.ELYOS ? 209681 : 209746, 351.295f, 173.821f, 1686.1825f, (byte) 30); } } @Override public void onSpawn(VisibleObject object) { if (object instanceof Npc) { switch (((Npc) object).getNpcId()) { case 236230: // Immortal Orissan case 236233: sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_03(), 2500); break; case 236231: // Exhausted Orissan case 236234: sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_05(), 2500); scheduleOrissansNextImmortality(); break; case 702719: // Empyrean Lord's Siege Weapon case 702720: sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_01(), 3000); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_02(), 6000); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_03(), 9000); break; case 236204: // Flamesquelch Destroyer case 236205: // Flamesquelch Burnsmark case 236206: // Flamesquelch Rangerblaze case 236216: case 236217: case 236218: case 236219: case 236220: startWalking((Npc) object, CreatureState.WALK_MODE); break; case 236243: // Commander Virtsha sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_08()); break; case 236247: // Dragon Beritra // TODO: scheduleBeritrasDespawn(); spawn(702699, 153.250f, 516.473f, 1751.0494f, (byte) 0, 383); // Pillar remains break; case 855460: case 855461: case 855462: case 855463: case 855464: // Drakenspire Protector case 855465: case 855466: case 855467: case 855468: case 855469: sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_GUARDIAN_01()); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_GUARDIAN_02(), 3000); break; } } } @Override public void onDie(Npc npc) { super.onDie(npc); int npcId = npc.getNpcId(); switch (npcId) { case 236225: case 236226: Npc oppositeProtector = getNpc(npcId == 236225 ? 236226 : 236225); if (oppositeProtector == null || oppositeProtector.isDead()) onTwinsComplete(); break; case 236227: // Lava Protector case 236228: // Heatvent Protector if (getNpc(npcId == 236227 ? 855709 : 855708) != null) { onTwinsComplete(); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_07()); } else { spawn(npcId == 236227 ? 855708 : 855709, npc.getX(), npc.getY(), npc.getZ(), (byte) 0); // Sphere sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_RESSURECT_02()); setAdditionalEventTask(ThreadPoolManager.getInstance().schedule(this::onTwinRespawn, 15000)); } break; case 236223: // Fetid Phantomscorch Chimera case 833012: case 833013: case 833015: return; case 236224: // Rapacious Kadena instance.setDoorState(375, true); break; case 236661: // Rapacious Kadena instance.setDoorState(376, true); break; case 236662: // Rapacious Kadena instance.setDoorState(378, true); break; case 236231: // Exhausted Orissan case 236234: onOrissanComplete(); deleteAliveNpcs(855607, 855608, 855699, 855700); break; case 236239: // Wave Commanders case 236240: case 236241: case 236242: case 236243: if (eventCounter.incrementAndGet() >= difficulty.get()) // difficulty is equivalent to the necessary kills onWaveEventComplete(true); break; case 236244: // Beritra Easy Mode case 236245: // Beritra Normal Mode WorldPosition pos = npc.getPosition(); spawn(npcId == 236244 ? 833012 : 833013, pos.getX(), pos.getY(), pos.getZ(), pos.getHeading()); spawnExit(); break; case 236246: // Beritra Hard Mode case 236247: // Beritra Dragon // TODO: spawn exit and NPCs spawnExit(); break; case 236248: case 236249: if (waveSurvivors.decrementAndGet() == 0) onWaveEventComplete(false); break; // exploding flame 856459 skill use case 731580: RespawnService.scheduleDecayTask(npc, 4000); switch (npc.getSpawn().getStaticId()) { case 332 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_09()); case 522, 603 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_08()); } } } @Override public void onAggro(Npc npc) { switch (npc.getNpcId()) { case 236227: // Lava Protector case 236228: // Heatvent Protector if (isStageActive.compareAndSet(false, true)) { sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_01()); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_02(), 2500); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_03(), 5000); onTwinsFightStart(); } break; } } private void onTwinsFightStart() { setCurrentEventTask(ThreadPoolManager.getInstance().scheduleAtFixedRate(new Runnable() { private int twinProgressCount; @Override public void run() { switch (++twinProgressCount) { case 2 -> { instance.setDoorState(380, true); // one of the best doors which closes if you set it open instance.setDoorState(374, true); instance.setDoorState(377, true); instance.setDoorState(379, true); instance.setDoorState(391, true); } case 8 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_04()); case 9 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_05(), 5000); case 10 -> { cancelTasks(currentEventTask, additionalEventTask); onTwinsFail(); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_06()); } } } }, 30000, 10 * 1000)); } private void onTwinRespawn() { if (getNpc(855708) != null) spawn(236227, 531.0885f, 212.43806f, 1683.4116f, (byte) 60); if (getNpc(855709) != null) spawn(236228, 530.8584f, 151.8681f, 1683.4116f, (byte) 60); deleteAliveNpcs(855708, 855709); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_TWIN_RESSURECT_03()); } private void onTwinsComplete() { cancelTasks(currentEventTask, additionalEventTask); deleteAliveNpcs(855708, 855709); ThreadPoolManager.getInstance().schedule(() -> { sp(race.get() == Race.ELYOS ? 209690 : 209755, 543.35f, 149.81f, 1681.82f, (byte) 0, "301390000_NPCPathFunction_Npc_Path03", 4000); sp(race.get() == Race.ELYOS ? 209690 : 209755, 543.45f, 208.97f, 1681.82f, (byte) 0, "301390000_NPCPathFunction_Npc_Path01", 4000); sp(race.get() == Race.ELYOS ? 209693 : 209758, 543.43f, 155.08f, 1681.82f, (byte) 0, "301390000_NPCPathFunction_Npc_Path04", 4000); sp(race.get() == Race.ELYOS ? 209693 : 209758, 543.43f, 214.24f, 1681.82f, (byte) 0, "301390000_NPCPathFunction_Npc_Path02", 4000); ThreadPoolManager.getInstance().schedule(() -> { spawn(race.get() == Race.ELYOS ? 209695 : 209760, 582.61f, 183.52f, 1683.73f, (byte) 0); spawn(race.get() == Race.ELYOS ? 209694 : 209759, 582.55f, 178.02f, 1683.73f, (byte) 0); }, 16000); }, 2000); if (difficulty.get() == HARD_MODE) spawn(236232, 812.0238f, 568.3602f, 1701.045f, (byte) 92); // HM Orissan else spawn(236229, 812.0238f, 568.3602f, 1701.045f, (byte) 92); // NM Orissan isStageActive.set(false); } private void onTwinsFail() { if (difficulty.compareAndSet(HARD_MODE, NORMAL_MODE)) { deleteAliveNpcs(236227, 236228, 855708, 855709); spawn(236225, 531.0885f, 212.4381f, 1683.412f, (byte) 60); // Fountless Lava Protector spawn(236226, 530.8584f, 151.8681f, 1683.412f, (byte) 60); // Fountless Heatvent Protector } } /** * Immortality phases are always 90s + 10s buff duration, vice versa; his vulnerability phase differs. * So let's handle only this phase. * 1st = 120s; 2nd = 228s, 3rd = 228s, 4th endless */ private void scheduleOrissansNextImmortality() { int delay; switch (eventCounter.incrementAndGet()) { case 1 -> delay = 120000; case 2 -> { sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_06(), 5000); delay = 228000; } case 3 -> { int prevDiff = difficulty.get(); switch (difficulty.getAndSet(prevDiff - 2)) { case NORMAL_MODE -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_07(), 5000); case EASY_MODE -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_08(), 5000); } delay = 240000; // Let's give them a few seconds more } default -> { // Maybe retail has lowered the difficulty to easy mode at this point (if normal mode was present) switch (difficulty.get()) { case NORMAL_MODE -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_09(), 5000); case EASY_MODE -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_10(), 5000); } return; } } setCurrentEventTask(ThreadPoolManager.getInstance().schedule(() -> { Npc orissan = getNpc(difficulty.get() == HARD_MODE ? 236234 : 236231); if (orissan != null) // should not happen SkillEngine.getInstance().getSkill(orissan, 21635, 1, orissan).useSkill(); // Summon Crystal }, delay)); } private void onOrissanComplete() { cancelTasks(currentEventTask); ThreadPoolManager.getInstance().schedule(() -> { spawn(race.get() == Race.ELYOS ? 209705 : 209770, 816.18f, 517.78f, 1707.41f, (byte) 33); spawn(race.get() == Race.ELYOS ? 209705 : 209770, 820.82f, 517.82f, 1707.41f, (byte) 33); spawn(race.get() == Race.ELYOS ? 209704 : 209769, 816.00f, 521.35f, 1706.88f, (byte) 33); spawn(race.get() == Race.ELYOS ? 209704 : 209769, 820.48f, 521.49f, 1706.88f, (byte) 33); spawn(race.get() == Race.ELYOS ? 209711 : 209776, 811.00f, 587.83f, 1701.045f, (byte) 32); spawn(race.get() == Race.ELYOS ? 209709 : 209774, 807.50f, 583.54f, 1701.045f, (byte) 32); spawn(race.get() == Race.ELYOS ? 209710 : 209775, 814.85f, 583.56f, 1701.045f, (byte) 32); spawn(race.get() == Race.ELYOS ? 209708 : 209773, 811.11f, 582.62f, 1701.045f, (byte) 32); }, 4000); eventCounter.set(0); spawn(206405, 639.50f, 866.50f, 1600.88f, (byte) 0); } private void onWaveEventStart() { sp(race.get() == Race.ELYOS ? 209720 : 209785, 635.53f, 886.83f, 1600.72f, (byte) 30, "301390000_NPCPathFunction_Npc_Path06", CreatureState.WALK_MODE, 7000); spawn(race.get() == Race.ELYOS ? 209731 : 209796, 639.05f, 895.86f, 1600.41f, (byte) 30); ThreadPoolManager.getInstance().schedule(() -> { sp(race.get() == Race.ELYOS ? 209722 : 209787, 637.75f, 847.98f, 1599.94f, (byte) 30, "301390000_NPCPathFunction_Npc_Path07", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 637.75f, 847.98f, 1599.94f, (byte) 30, "301390000_NPCPathFunction_Npc_Path08", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 637.75f, 847.98f, 1599.94f, (byte) 30, "301390000_NPCPathFunction_Npc_Path09", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 637.75f, 847.98f, 1599.94f, (byte) 30, "301390000_NPCPathFunction_Npc_Path10", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 633.95f, 847.58f, 1599.87f, (byte) 30, "301390000_NPCPathFunction_Npc_Path13", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 633.95f, 847.58f, 1599.87f, (byte) 30, "301390000_NPCPathFunction_Npc_Path14", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 633.95f, 847.58f, 1599.87f, (byte) 30, "301390000_NPCPathFunction_Npc_Path15", 2000); sp(race.get() == Race.ELYOS ? 209722 : 209787, 633.95f, 847.58f, 1599.87f, (byte) 30, "301390000_NPCPathFunction_Npc_Path16", 2000); ThreadPoolManager.getInstance().schedule(() -> { deleteAliveNpcs(race.get() == Race.ELYOS ? 209720 : 209785); spawn(race.get() == Race.ELYOS ? 209723 : 209788, 636.09f, 879.98f, 1600.82f, (byte) 33); // Main Npc spawn(race.get() == Race.ELYOS ? 702719 : 702720, 635.68f, 883.03f, 1603.91f, (byte) 29); // Siege Weapon sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_01(), 3000); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_02(), 6000); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_03(), 9000); scheduleWaveStarts(); }, 12000); }, 18000); } private void scheduleWaveStarts() { setCurrentEventTask(ThreadPoolManager.getInstance().scheduleAtFixedRate(new Runnable() { private int waveCount; @Override public void run() { switch (++waveCount) { case 1 -> { instance.setDoorState(267, true); instance.setDoorState(271, true); spawn(731581, 578.55f, 819.27f, 1609.42f, (byte) 0, 84); spawn(731581, 690.49f, 822.41f, 1609.57f, (byte) 0, 405); spawn(731581, 635.39f, 784.05f, 1596.72f, (byte) 0, 548); } case 2 -> { if (difficulty.get() == HARD_MODE) sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_05()); } case 3 -> { instance.setDoorState(7, true); instance.setDoorState(310, true); spawn(731581, 707.34f, 876.80f, 1603.69f, (byte) 0, 398); spawn(731581, 570.70f, 877.51f, 1599.80f, (byte) 0, 401); if (difficulty.get() == HARD_MODE) sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_06()); } case 4 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_07()); case 5 -> { instance.setDoorState(210, true); instance.setDoorState(312, true); spawn(731581, 694.01f, 935.97f, 1618.09f, (byte) 0, 399); spawn(731581, 572.94f, 940.04f, 1620.04f, (byte) 0, 407); } } if (waveCount <= difficulty.get()) scheduleCommanderWave(waveCount); } }, 6000, 60000)); } private void scheduleCommanderWave(int waveCount) { setAdditionalEventTask(ThreadPoolManager.getInstance().schedule(() -> { sp(Rnd.get(236216, 236220), 634.80f, 790.95f, 1596.80f, (byte) 30, "301390000_Wave_Commander_Left", 0); sp(Rnd.get(236216, 236220), 634.80f, 790.95f, 1596.80f, (byte) 30, "301390000_Wave_Commander_Right", 0); if (waveCount >= 3) sp(Rnd.get(236216, 236220), 634.80f, 790.95f, 1596.80f, (byte) 30, "301390000_Wave_Commander_Middle", 0); if (waveCount >= 4) { sp(Rnd.get(236216, 236220), 634.80f, 790.95f, 1596.80f, (byte) 30, "301390000_Wave_Commander_Left", 4000); sp(Rnd.get(236216, 236220), 634.80f, 790.95f, 1596.80f, (byte) 30, "301390000_Wave_Commander_Right", 4000); } sp(236243 - difficulty.get() + waveCount, 634.80f, 790.95f, 1596.80f, (byte) 30, "301390000_Wave_Commander_Middle", 3000); // Boss }, 28000)); } private void onWaveEventComplete(boolean isSuccessful) { cancelTasks(currentEventTask, additionalEventTask); deleteAliveNpcs(236204, 236205, 236206, 236216, 236217, 236218, 236219, 236220, 236239, 236240, 236241, 236242, 236243, 731581); if (isSuccessful) { SkillEngine.getInstance().getSkill(getNpc(race.get() == Race.ELYOS ? 702719 : 702720), 20838, 1, getNpc(731580)).useSkill(); switch (waveSurvivors.get()) { case 8 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_BONUS_04()); case 7, 6 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_BONUS_03()); case 5, 4, 3 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_BONUS_02()); case 2, 1 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_WAVE_BONUS_01()); } switch (difficulty.get()) { // case 5 -> spawn(236246, 152f, 518f, 1749.6f, (byte) 55); TODO: Implementation case HARD_MODE, NORMAL_MODE -> spawn(236245, 152.38f, 518.68f, 1749.6f, (byte) 55); case EASY_MODE -> spawn(236244, 152.38f, 518.68f, 1749.6f, (byte) 55); } } else { deleteAliveNpcs(702719, 702720); // TODO: find sys msg for fail } } private void spawnExit() { Race r = race.get(); switch (difficulty.get()) { case FAILED -> { spawn(r == Race.ELYOS ? 209741 : 209806, 155.390f, 516.622f, 1750f, (byte) 0); // Masionel | Parsia spawn(r == Race.ELYOS ? 209742 : 209807, 155.361f, 520.999f, 1750f, (byte) 0); // Detachment Entry Soldier } case EASY_MODE -> { spawn(r == Race.ELYOS ? 209740 : 209805, 155.390f, 516.622f, 1750f, (byte) 0); // Masionel | Parsia spawn(r == Race.ELYOS ? 209742 : 209807, 155.361f, 520.999f, 1750f, (byte) 0); // Detachment Entry Soldier } case NORMAL_MODE -> { spawn(r == Race.ELYOS ? 209739 : 209804, 155.390f, 516.622f, 1750f, (byte) 0); // Masionel | Parsia spawn(r == Race.ELYOS ? 209742 : 209807, 155.361f, 520.999f, 1750f, (byte) 0); // Detachment Entry Soldier } case HARD_MODE -> { spawn(r == Race.ELYOS ? 209738 : 209803, 140.656f, 518.922f, 1750f, (byte) 0); // Masionel | Parsia spawn(r == Race.ELYOS ? 209742 : 209807, 144.452f, 516.218f, 1749.4784f, (byte) 0); // Detachment Entry Soldier spawn(r == Race.ELYOS ? 209742 : 209807, 144.433f, 521.472f, 1749.4768f, (byte) 0); // Detachment Entry Soldier } } // Exits spawn(731548, 320.005f, 183.281f, 1687.2552f, (byte) 60); // Start Exit spawn(731548, 545.194f, 153.283f, 1681.8224f, (byte) 60); // Protector Exit spawn(731548, 545.325f, 212.691f, 1681.8224f, (byte) 60); // Protector Exit spawn(731548, 810.652f, 591.986f, 1701.0449f, (byte) 90); // Orissan Exit spawn(731548, 137.524f, 518.576f, 1749.4153f, (byte) 0); // Beritra Exit } @Override public void onCreatureDetected(Npc detector, Creature detected) { if (!(detected instanceof Player)) return; if (detector.getNpcId() == 206405) { if (isStageActive.compareAndSet(false, true)) { onWaveEventStart(); detector.getController().delete(); } } } @Override public void onStartEffect(Effect effect) { switch (effect.getSkillId()) { case 21635 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_01()); // Summon Crystal case 21885 -> sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_IMMORTAL_04()); // Weaken Ascension Domination case 21610 -> { // Dark Affinity sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_VRITRA_HUMAN_01()); sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_VRITRA_HUMAN_02(), 3000); } } } @Override public void onEndEffect(Effect effect) { switch (effect.getSkillId()) { case 21635, 21885 -> { // Weaken Ascension Domination int nextId; if (effect.getSkillId() == 21635) nextId = difficulty.get() == HARD_MODE ? 236233 : 236230; else nextId = difficulty.get() == HARD_MODE ? 236234 : 236231; WorldPosition pos = effect.getEffected().getPosition(); effect.getEffected().getController().delete(); spawn(nextId, pos.getX(), pos.getY(), pos.getZ(), pos.getHeading()); } case 21618 -> { // Dragon Lord's Authority // TODO: sendMsg(SM_SYSTEM_MESSAGE.STR_MSG_IDSEAL_VRITRA_HUMAN_03()); => only if it ends naturally } } } @Override public void leaveInstance(Player player) { TeleportService.moveToInstanceExit(player, mapId, player.getRace()); } @Override public boolean onReviveEvent(Player player) { PlayerReviveService.revive(player, 25, 25, false, 0); player.getGameStats().updateStatsAndSpeedVisually(); PacketSendUtility.sendPacket(player, SM_SYSTEM_MESSAGE.STR_REBIRTH_MASSAGE_ME()); TeleportService.teleportTo(player, instance, 361.706f, 182.503f, 1684.290f, (byte) 0); return true; } private synchronized void setCurrentEventTask(Future task) { cancelTasks(currentEventTask); currentEventTask = task; } private synchronized void setAdditionalEventTask(Future task) { cancelTasks(additionalEventTask); additionalEventTask = task; } private synchronized void cancelTasks(Future... tasks) { for (Future task : tasks) if (task != null && !task.isDone()) task.cancel(true); } private void sp(int id, float x, float y, float z, byte h, String walkerId, int delay) { sp(id, x, y, z, h, walkerId, CreatureState.ACTIVE, delay); } private void sp(int id, float x, float y, float z, byte h, String walkerId, CreatureState state, int delay) { ThreadPoolManager.getInstance().schedule(() -> { Npc npc = (Npc) spawn(id, x, y, z, h); npc.getSpawn().setWalkerId(walkerId); startWalking(npc, state); }, delay); } private void startWalking(Npc npc, CreatureState state) { ThreadPoolManager.getInstance().schedule(() -> { if (npc == null) return; WalkManager.startWalking((NpcAI) npc.getAi()); if (state == CreatureState.ACTIVE) { npc.unsetState(CreatureState.WALK_MODE); npc.setState(state); PacketSendUtility.broadcastPacket(npc, new SM_EMOTION(npc, EmotionType.CHANGE_SPEED)); } }, 2000); } @Override public boolean isBoss(Npc npc) { return switch (npc.getNpcId()) { case 236227, 236228, 236231, 236234, 236244, 236245, 236247 -> true; default -> false; }; } }