/* * This file is part of the L2J Mobius project. * * This program is free software: you can redistribute it and/or modify * it under the terms of the GNU General Public License as published by * the Free Software Foundation, either version 3 of the License, or * (at your option) any later version. * * This program is distributed in the hope that it will be useful, * but WITHOUT ANY WARRANTY; without even the implied warranty of * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU * General Public License for more details. * * You should have received a copy of the GNU General Public License * along with this program. If not, see . */ package instances.ChambersOfDelusion; import java.util.HashMap; import java.util.List; import java.util.Map; import java.util.concurrent.ScheduledFuture; import java.util.concurrent.TimeUnit; import java.util.logging.Level; import java.util.stream.IntStream; import com.l2jmobius.Config; import com.l2jmobius.commons.util.CommonUtil; import com.l2jmobius.gameserver.ThreadPoolManager; import com.l2jmobius.gameserver.enums.ChatType; import com.l2jmobius.gameserver.model.L2Object; import com.l2jmobius.gameserver.model.L2World; import com.l2jmobius.gameserver.model.Location; import com.l2jmobius.gameserver.model.StatsSet; import com.l2jmobius.gameserver.model.actor.L2Npc; import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance; import com.l2jmobius.gameserver.model.holders.SkillHolder; import com.l2jmobius.gameserver.model.instancezone.Instance; import com.l2jmobius.gameserver.model.skills.Skill; import com.l2jmobius.gameserver.network.NpcStringId; import com.l2jmobius.gameserver.network.serverpackets.Earthquake; import instances.AbstractInstance; /** * Chambers of Delusion. * @author GKR */ public final class ChamberOfDelusion extends AbstractInstance { // NPCs private static final Map ENTRANCE_GATEKEEPER = new HashMap<>(); private static final int[] ROOM_GATEKEEPERS = IntStream.range(32664, 32702).toArray(); private static final int[] AENKINEL = { 25690, // East 25691, // West 25692, // South 25693, // North 25694, // Square 25695, // Tower }; private static final int[] BOX = { 18838, // East, West, South, North 18820, // Square 18823, // Tower }; // Items private static final int ENRIA = 4042; private static final int ASOFE = 4043; private static final int THONS = 4044; private static final int LEONARD = 9628; private static final int DELUSION_MARK = 15311; // Skills private static final SkillHolder SUCCESS_SKILL = new SkillHolder(5758, 1); private static final SkillHolder FAIL_SKILL = new SkillHolder(5376, 4); // Timers private static final int ROOM_CHANGE_INTERVAL = 480; // 8 min private static final int ROOM_CHANGE_RANDOM_TIME = 120; // 2 min static { ENTRANCE_GATEKEEPER.put(32658, 127); // East ENTRANCE_GATEKEEPER.put(32659, 128); // West ENTRANCE_GATEKEEPER.put(32660, 129); // South ENTRANCE_GATEKEEPER.put(32661, 130); // North ENTRANCE_GATEKEEPER.put(32662, 131); // Square ENTRANCE_GATEKEEPER.put(32663, 132); // Tower } public ChamberOfDelusion() { addStartNpc(ENTRANCE_GATEKEEPER.keySet()); addStartNpc(ROOM_GATEKEEPERS); addTalkId(ENTRANCE_GATEKEEPER.keySet()); addTalkId(ROOM_GATEKEEPERS); addKillId(AENKINEL); addAttackId(BOX); addSpellFinishedId(BOX); addEventReceivedId(BOX); addInstanceCreatedId(ENTRANCE_GATEKEEPER.values()); addInstanceDestroyId(ENTRANCE_GATEKEEPER.values()); } @Override public void onInstanceCreated(Instance instance, L2PcInstance player) { // Choose start room changeRoom(instance); // Start banish task final ScheduledFuture banishTask = ThreadPoolManager.getInstance().scheduleGeneralAtFixedRate(() -> { if (instance.getRemainingTime() < 60000) { final ScheduledFuture task = instance.getParameters().getObject("banishTask", ScheduledFuture.class); task.cancel(false); } else { for (int objId : instance.getAllowed()) { final L2PcInstance pl = L2World.getInstance().getPlayer(objId); if ((pl != null) && pl.isOnline() && !pl.isInParty()) { instance.finishInstance(0); break; } } } }, 60000, 60000); instance.setParameter("banishTask", banishTask); } @Override public void onInstanceDestroy(Instance instance) { // Stop room change task stopRoomChangeTask(instance); // Stop banish task final ScheduledFuture banish = instance.getParameters().getObject("banishTask", ScheduledFuture.class); if (banish != null) { banish.cancel(true); instance.setParameter("banishTask", null); } } @Override protected void onEnter(L2PcInstance player, Instance instance, boolean firstEnter) { // Teleport player to instance super.onEnter(player, instance, firstEnter); // Take items if (firstEnter) { if (hasQuestItems(player, DELUSION_MARK)) { takeItems(player, DELUSION_MARK, -1); } if (player.getParty().isLeader(player)) { giveItems(player, DELUSION_MARK, 1); } } } @Override protected void teleportPlayerIn(L2PcInstance player, Instance instance) { final int room = instance.getParameters().getInt("currentRoom"); final Location loc = instance.getEnterLocations().get(room); player.teleToLocation(loc, instance); } @Override public String onAdvEvent(String event, L2Npc npc, L2PcInstance player) { String htmltext = null; final Instance world = npc.getInstanceWorld(); if ((player != null) && (world != null) && CommonUtil.contains(ROOM_GATEKEEPERS, npc.getId())) { switch (event) { case "next_room": { if (player.isInParty()) { htmltext = "no_party.html"; } else if (player.getParty().isLeader(player)) { htmltext = "no_leader.html"; } else if (!hasQuestItems(player, DELUSION_MARK)) { htmltext = "no_item.html"; } else { takeItems(player, DELUSION_MARK, 1); stopRoomChangeTask(world); changeRoom(world); } break; } case "go_out": { if (player.isInParty()) { htmltext = "no_party.html"; } else if (player.getParty().isLeader(player)) { htmltext = "no_leader.html"; } else { world.finishInstance(0); } break; } case "look_party": { if (player.isInParty() && world.isAllowed(player)) { teleportPlayerIn(player, world); } break; } } } return htmltext; } @Override public String onTalk(L2Npc npc, L2PcInstance player) { final int npcId = npc.getId(); if (ENTRANCE_GATEKEEPER.containsKey(npcId)) { enterInstance(player, npc, ENTRANCE_GATEKEEPER.get(npcId)); } return null; } @Override public String onSpellFinished(L2Npc npc, L2PcInstance player, Skill skill) { if (!npc.isDead() && CommonUtil.contains(BOX, npc.getId()) && ((skill.getId() == FAIL_SKILL.getSkillId()) || (skill.getId() == SUCCESS_SKILL.getSkillId()))) { npc.doDie(player); } return super.onSpellFinished(npc, player, skill); } @Override public String onEventReceived(String eventName, L2Npc sender, L2Npc receiver, L2Object reference) { switch (eventName) { case "SCE_LUCKY": receiver.setBusy(true); receiver.doCast(SUCCESS_SKILL.getSkill()); break; case "SCE_DREAM_FIRE_IN_THE_HOLE": receiver.setBusy(true); receiver.doCast(FAIL_SKILL.getSkill()); break; } return null; } @Override public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isPet, Skill skill) { if (!npc.isBusy() && (npc.getCurrentHp() < (npc.getMaxHp() / 10))) { npc.setBusy(true); if (getRandom(100) < 25) // 25% chance to reward { if (getRandom(100) < 33) { npc.dropItem(attacker, ENRIA, (int) (3 * Config.RATE_QUEST_DROP)); } if (getRandom(100) < 50) { npc.dropItem(attacker, THONS, (int) (4 * Config.RATE_QUEST_DROP)); } if (getRandom(100) < 50) { npc.dropItem(attacker, ASOFE, (int) (4 * Config.RATE_QUEST_DROP)); } if (getRandom(100) < 16) { npc.dropItem(attacker, LEONARD, (int) (2 * Config.RATE_QUEST_DROP)); } npc.broadcastEvent("SCE_LUCKY", 2000, null); npc.doCast(SUCCESS_SKILL.getSkill()); } else { npc.broadcastEvent("SCE_DREAM_FIRE_IN_THE_HOLE", 2000, null); } } return super.onAttack(npc, attacker, damage, isPet, skill); } @Override public String onKill(L2Npc npc, L2PcInstance player, boolean isPet) { final Instance world = player.getInstanceWorld(); if (world != null) { if (isBigChamber(world)) { world.setReenterTime(); if (world.getRemainingTime() > (Config.INSTANCE_FINISH_TIME * 60000)) { world.finishInstance(); } } else { stopRoomChangeTask(world); scheduleRoomChange(world, true); } world.spawnGroup("boxes"); } return super.onKill(npc, player, isPet); } private boolean isBigChamber(Instance world) { return world.getTemplateParameters().getBoolean("isBigRoom"); } private boolean isBossRoom(Instance world, int room) { return room == (world.getEnterLocations().size() - 1); } protected void changeRoom(Instance world) { final StatsSet params = world.getParameters(); final List locations = world.getEnterLocations(); int newRoom = params.getInt("currentRoom", 0); if (isBigChamber(world)) { // Do nothing, if there are raid room of Sqare or Tower Chamber if (isBossRoom(world, newRoom)) { return; } // Teleport to raid room 10 min or lesser before instance end time for Tower and Square Chambers else if (world.getRemainingTime() < 600000) { newRoom = locations.size() - 1; } } // 10% chance for teleport to raid room if not here already for Northern, Southern, Western and Eastern Chambers else if (!isBossRoom(world, newRoom) && (getRandom(100) < 10)) { newRoom = locations.size() - 1; } else { // otherwise teleport to another room, except current while (newRoom == params.getInt("currentRoom", 0)) { newRoom = getRandom(locations.size() - 1); } } world.setParameter("currentRoom", newRoom); // Teleport players into new room world.getPlayers().forEach(p -> teleportPlayerIn(p, world)); // Do not schedule room change for Square and Tower Chambers, if raid room is reached if (isBigChamber(world) && isBossRoom(world, newRoom)) { // Add 20 min to instance time if raid room is reached world.setDuration((int) (TimeUnit.MILLISECONDS.toMinutes(world.getRemainingTime() + 1200000))); // Broadcast instance duration change (NPC message) final int raidGatekeeper = world.getTemplateParameters().getInt("raidGatekeeper"); final L2Npc gatekeeper = world.getNpc(raidGatekeeper); if (gatekeeper != null) { gatekeeper.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.MINUTES_ARE_ADDED_TO_THE_REMAINING_TIME_IN_THE_INSTANT_ZONE); } } else { scheduleRoomChange(world, false); } } protected void scheduleRoomChange(Instance world, boolean bossRoom) { // Schedule next room change only if remaining time is enough final long nextInterval = (bossRoom) ? 60000L : (ROOM_CHANGE_INTERVAL + getRandom(ROOM_CHANGE_RANDOM_TIME)) * 1000L; if (world.getRemainingTime() > nextInterval) { final ScheduledFuture roomChangeTask = ThreadPoolManager.getInstance().scheduleGeneral(() -> { try { // Send earthquake packet for (L2PcInstance player : world.getPlayers()) { player.sendPacket(new Earthquake(player, 20, 10)); } // Wait for a while Thread.sleep(5000); // Change room changeRoom(world); } catch (Exception e) { _log.log(Level.WARNING, "Error occured in room change task: ", e); } }, nextInterval - 5000); world.setParameter("roomChangeTask", roomChangeTask); } } protected void stopRoomChangeTask(Instance world) { final ScheduledFuture task = world.getParameters().getObject("roomChangeTask", ScheduledFuture.class); if (task != null) { task.cancel(true); world.setParameter("roomChangeTask", null); } } public static void main(String[] args) { new ChamberOfDelusion(); } }