Generator:
+The generator is now supplying power to the sealed structure.
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/19477.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/19477.html
new file mode 100644
index 0000000000..540ff6ac54
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/19477.html
@@ -0,0 +1,3 @@
+Generator:
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html
new file mode 100644
index 0000000000..ba01eab970
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+On the Lindvior there is an attack!
+At this time it's impossible to enter the altar.
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html
new file mode 100644
index 0000000000..e3eac44ec7
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+Lindvior? You're too late, friend.
+A group of warriors drove him off not long ago.
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html
new file mode 100644
index 0000000000..40de9b961e
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+You are overcome by a voice, a voice so powerful you are helpless as it speaks:
+(The players who belong to an association can only enter through the Association Leader.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html
new file mode 100644
index 0000000000..9af5ff8c7a
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html
@@ -0,0 +1,3 @@
+Rune Castle Patrol Kato:
+(A command channel needs at least minimu: %min% and maximum: %max% members to challenge Lindvior.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html
new file mode 100644
index 0000000000..309c938f02
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html
@@ -0,0 +1,3 @@
+Rune Castle Patrol Kato:
+(A command channel members level must be minimum: %minlvl% Lvl or higher to challenge Lindvior.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html
new file mode 100644
index 0000000000..bddff0b348
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html
@@ -0,0 +1,6 @@
+Rune Castle Patrol Kato:
+Dire times dictated that I come myself from Rune Castle to help those in need here.
+What do you need?
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java
new file mode 100644
index 0000000000..3b54f9dfa8
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java
@@ -0,0 +1,129 @@
+/*
+ * 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 ai.bosses.Lindvior.KatoSicanus;
+
+import java.util.List;
+
+import com.l2jmobius.Config;
+import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
+import com.l2jmobius.gameserver.model.L2Party;
+import com.l2jmobius.gameserver.model.Location;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Kato Sicanus Teleporter AI
+ * @author Gigi
+ * @date 2017-07-13 - [22:17:16]
+ */
+public class KatoSicanus extends AbstractNpcAI
+{
+ // NPCs
+ private static final int KATO_SICANUS = 33881;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int INVISIBLE = 8572;
+ // Location
+ private static final Location LINDVIOR_LOCATION = new Location(46929, -28807, -1400);
+
+ public KatoSicanus()
+ {
+ addFirstTalkId(KATO_SICANUS);
+ addTalkId(KATO_SICANUS);
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("teleport"))
+ {
+ final int status = GrandBossManager.getInstance().getBossStatus(LINDVIOR_RAID);
+ if (player.isGM())
+ {
+ player.teleToLocation(LINDVIOR_LOCATION, true);
+ addSpawn(INVISIBLE, 46707, -28586, -1400, 0, false, 60000, false);
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, 1);
+ }
+ else
+ {
+ if (status == 2)
+ {
+ return "33881-1.html";
+ }
+ if (status == 3)
+ {
+ return "33881-2.html";
+ }
+ if (!player.isInParty())
+ {
+ return "33881-3.html";
+ }
+ final L2Party party = player.getParty();
+ final boolean isInCC = party.isInCommandChannel();
+ final List members = (isInCC) ? party.getCommandChannel().getMembers() : party.getMembers();
+ final boolean isPartyLeader = (isInCC) ? party.getCommandChannel().isLeader(player) : party.isLeader(player);
+ if (!isPartyLeader)
+ {
+ return "33881-3.html";
+ }
+ if ((members.size() < Config.LINDVIOR_MIN_PLAYERS) || (members.size() > Config.LINDVIOR_MAX_PLAYERS))
+ {
+ final NpcHtmlMessage packet = new NpcHtmlMessage(npc.getObjectId());
+ packet.setHtml(getHtm(player.getHtmlPrefix(), "33881-4.html"));
+ packet.replace("%min%", Integer.toString(Config.LINDVIOR_MIN_PLAYERS));
+ packet.replace("%max%", Integer.toString(Config.LINDVIOR_MAX_PLAYERS));
+ player.sendPacket(packet);
+ return null;
+ }
+ for (L2PcInstance member : members)
+ {
+ if (member.getLevel() < Config.LINDVIOR_MIN_PLAYER_LVL)
+ {
+ final NpcHtmlMessage packet = new NpcHtmlMessage(npc.getObjectId());
+ packet.setHtml(getHtm(player.getHtmlPrefix(), "33881-5.html"));
+ packet.replace("%minlvl%", Integer.toString(Config.LINDVIOR_MIN_PLAYER_LVL));
+ player.sendPacket(packet);
+ return null;
+ }
+ }
+ for (L2PcInstance member : members)
+ {
+ if (member.isInsideRadius(npc, 1500, true, false))
+ {
+ member.teleToLocation(LINDVIOR_LOCATION, true);
+ addSpawn(INVISIBLE, 46707, -28586, -1400, 0, false, 0, false);
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, 1);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onFirstTalk(L2Npc npc, L2PcInstance player)
+ {
+ return "33881.html";
+ }
+
+ public static void main(String[] args)
+ {
+ new KatoSicanus();
+ }
+}
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java
new file mode 100644
index 0000000000..d6247a96c6
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java
@@ -0,0 +1,976 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+
+import com.l2jmobius.Config;
+import com.l2jmobius.commons.util.Rnd;
+import com.l2jmobius.gameserver.ThreadPoolManager;
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.datatables.SpawnTable;
+import com.l2jmobius.gameserver.enums.ChatType;
+import com.l2jmobius.gameserver.enums.Movie;
+import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
+import com.l2jmobius.gameserver.instancemanager.ZoneManager;
+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.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
+import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.events.EventType;
+import com.l2jmobius.gameserver.model.events.ListenerRegisterType;
+import com.l2jmobius.gameserver.model.events.annotations.Id;
+import com.l2jmobius.gameserver.model.events.annotations.RegisterEvent;
+import com.l2jmobius.gameserver.model.events.annotations.RegisterType;
+import com.l2jmobius.gameserver.model.events.impl.character.OnCreatureDamageReceived;
+import com.l2jmobius.gameserver.model.holders.SkillHolder;
+import com.l2jmobius.gameserver.model.skills.Skill;
+import com.l2jmobius.gameserver.model.zone.L2ZoneType;
+import com.l2jmobius.gameserver.model.zone.type.L2NoSummonFriendZone;
+import com.l2jmobius.gameserver.network.NpcStringId;
+import com.l2jmobius.gameserver.network.serverpackets.Earthquake;
+import com.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+import com.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
+import com.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+import com.l2jmobius.gameserver.network.serverpackets.SocialAction;
+import com.l2jmobius.gameserver.network.serverpackets.SpecialCamera;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Lindvior Boss
+ * @author Gigi
+ * @date 2017-07-11 - [17:42:53]
+ *
+ * @VIDEO - https://www.youtube.com/watch?v=VknjOjRO9Cw
+ */
+public class Lindvior extends AbstractNpcAI
+{
+ // Monsters
+ private static final int LINDVIOR_FAKE = 19423;
+ private static final int LINDVIOR_GROUND = 25899;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int LINDVIOR_FLY = 19424;
+ private static final int NPC_GENERATOR = 19477;
+ private static final int GENERATOR_GUARD = 19479;
+ private static final int NPC_ATTACKER_GENERATORS = 25897;
+ private static final int NPC_ATTACKER_GENERATORS_1 = 25895;
+ private static final int LYN_DRACO_ATTACKER_GENERATORS = 29241;
+ private static final int NPC_ATTACKER_SMALL_VORTEX = 25898;
+ private static final int NPC_ATTACKER_BIG_VORTEX = 19427;
+ private static final int INVISIBLE = 8572;
+ private static final int LIONEL_HUNTER = 33886;
+ private static final int LINDVIOR_CAMERA = 19428;
+ // Zone
+ private final static int ZONE_ID = 12107;
+ static final Location CENTER_LOCATION = new Location(46424, -26200, -1400);
+ // Skills
+ private static final SkillHolder SKILL_RECHARGE_POSIBLE = new SkillHolder(15605, 1);
+ private static final SkillHolder RECHARGE = new SkillHolder(15606, 1);
+ private static final SkillHolder SKILL_REFLECT = new SkillHolder(15592, 1);
+ // Item
+ private static final int LINDVIORS_SCALE = 37495;
+ // Trigers
+ private static final int FIRST_STAGE_EVENT_TRIGGER = 21170112;
+ private static final int SECOND_STAGE_EVENT_TRIGGER = 21170100;
+ private static final int ALL_GENERATORS_CONNECTED_EFFECT = 21170110;
+ private static final int RED_ZONE_EFFECT = 21170120;
+ // Status
+ private static final int ALIVE = 0;
+ private static final int FIGHTING = 2;
+ private static final int DEAD = 3;
+ // Tasks
+ protected ScheduledFuture> _socialTask;
+ protected ScheduledFuture> _mobsSpawnTask;
+ protected ScheduledFuture> _collapseTask;
+ protected ScheduledFuture> _announceTask;
+ protected ScheduledFuture> _announceProtect;
+ protected ScheduledFuture> _skillCastTask;
+ protected ScheduledFuture> _LynDracoTask;
+ protected ScheduledFuture> _smallVortexesTask;
+ protected ScheduledFuture> _bigVortexesTask;
+ protected L2NoSummonFriendZone _zoneLair;
+ protected L2GrandBossInstance _lindvior = null;
+ protected L2Npc _lindvior2 = null;
+ protected L2Npc _dummyLindvior;
+ protected L2Npc _vortex = null;
+ protected L2Npc _lionel = null;
+ protected List _guardSpawn = new ArrayList<>();
+ protected List _generatorSpawn = new ArrayList<>();
+ protected List _monsterSpawn = new ArrayList<>();
+ protected List _LinDracoSpawn = new ArrayList<>();
+ protected int _activeMask = 0;
+ protected int _chargedMask = 0;
+ protected int _status = 0;
+
+ private static final Location[] CONTROL_GENERATOR_SPAWNS =
+ {
+ new Location(45288, -30360, -1432, 0),
+ new Location(48486, -27175, -1432, 0),
+ new Location(45272, -23976, -1432, 0),
+ new Location(42088, -27160, -1432, 0)
+ };
+ private static final Location[] SCHEME_GENERATOR_SPAWNS =
+ {
+ new Location(48440, -26824, -1438, 0),
+ new Location(48392, -27448, -1438, 0),
+ new Location(42136, -27480, -1438, 0),
+ new Location(42136, -26840, -1438, 0),
+ new Location(44936, -24024, -1438, 0),
+ new Location(45592, -24008, -1438, 0),
+ new Location(45608, -30312, -1438, 0),
+ new Location(44984, -30360, -1438, 0)
+ };
+ private static final Location[] ATTACKER_GENERATOR_SPAWNS =
+ {
+ new Location(44863, -24272, -1413, 33713),
+ new Location(45675, -24272, -1413, 33713),
+ new Location(45675, -30057, -1413, 64987),
+ new Location(44863, -30057, -1413, 64987),
+ new Location(42350, -27563, -1413, 46871),
+ new Location(42350, -26809, -1413, 46871),
+ new Location(48220, -26809, -1413, 16383),
+ new Location(48220, -27563, -1413, 16383)
+ };
+ private static final Location[] LYN_DRACO_SPAWNS =
+ {
+ new Location(45300, -28402, -1400, 48845),
+ new Location(46379, -27178, -1400, 1154),
+ new Location(45292, -26043, -1400, 13027),
+ new Location(44215, -27172, -1400, 33966)
+ };
+ private static final Location[] ATTACKER_SMALL_VORTEX_SPAWNS = new Location[]
+ {
+ new Location(46256, -30159, -1430, 57430),
+ new Location(45155, -29987, -1430, 14860),
+ new Location(46219, -27704, -1430, 1744),
+ new Location(46135, -28995, -1430, 43626),
+ new Location(43973, -28265, -1430, 16516),
+ new Location(46782, -29065, -1430, 63368),
+ new Location(47214, -29836, -1430, 46966),
+ new Location(44754, -29120, -1430, 56118),
+ new Location(47089, -28198, -1430, 8537),
+ new Location(44992, -28152, -1430, 11592),
+ new Location(44737, -24885, -1430, 3146),
+ new Location(46096, -24976, -1430, 49650),
+ new Location(46972, -25911, -1430, 62925),
+ new Location(46977, -27136, -1430, 2150),
+ new Location(42889, -24767, -1430, 10246),
+ new Location(47299, -25256, -1430, 1453),
+ new Location(44204, -25026, -1430, 39225),
+ new Location(42875, -28035, -1430, 34755),
+ new Location(41963, -26031, -1430, 18822),
+ new Location(43171, -25942, -1430, 44279),
+ new Location(41874, -27174, -1430, 56030),
+ new Location(44983, -26082, -1430, 7042),
+ new Location(46145, -26804, -1430, 24394),
+ new Location(46148, -26019, -1430, 34151),
+ new Location(45161, -24275, -1430, 39262),
+ new Location(47288, -24141, -1430, 21644),
+ new Location(43722, -26174, -1430, 11001),
+ new Location(44942, -27169, -1430, 39703),
+ new Location(46105, -24170, -1430, 28224),
+ new Location(49084, -27206, -1430, 41996),
+ new Location(48159, -27091, -1430, 62682),
+ new Location(48094, -28789, -1430, 49189),
+ new Location(48958, -27844, -1430, 59758),
+ new Location(43828, -23981, -1430, 10994),
+ new Location(48165, -25777, -1430, 53084),
+ new Location(48267, -28086, -1430, 9266),
+ new Location(43268, -28981, -1430, 23736),
+ new Location(44155, -29821, -1430, 39281),
+ new Location(43991, -29275, -1430, 27277),
+ new Location(44057, -27133, -1430, 64484),
+ new Location(43257, -26764, -1430, 14161),
+ new Location(42300, -25194, -1430, 7811),
+ new Location(42091, -27981, -1430, 30628),
+ new Location(47854, -24735, -1430, 14438)
+ };
+ // @formatter:off
+ private static final int[][] GENERATOR_TRIGERS =
+ {
+ {21170102, 21170103},
+ {21170104, 21170105},
+ {21170106, 21170107},
+ {21170108, 21170109}
+ };
+ protected final int _chargedValues[] = new int[] {0, 0, 0, 0};
+ private static final int[] LINDVIOR_SERVITOR = {25895, 25896, 25897, 29242, 29241, 29243};
+ // @formatter:on
+ private static final NpcStringId[] GUARD_MSG =
+ {
+ NpcStringId.ACTIVATE_THE_GENERATOR_HURRY,
+ NpcStringId.WE_WILL_HOLD_OFF_LINDVIOR_S_MINIONS,
+ };
+ private static final NpcStringId[] GUARD_MSG_1 =
+ {
+ NpcStringId.HOLD_ONTO_THE_GENERATOR_TO_ACTIVATE_THE_CHARGE_SKILL,
+ NpcStringId.THE_GENERATOR_IS_CONNECTED_TO_THE_CANNON,
+ };
+
+ public Lindvior()
+ {
+ super();
+ addAttackId(LINDVIOR_GROUND, LINDVIOR_FLY, LINDVIOR_RAID);
+ addEnterZoneId(ZONE_ID);
+ addExitZoneId(ZONE_ID);
+ addKillId(LINDVIOR_RAID, NPC_GENERATOR);
+ addSkillSeeId(NPC_GENERATOR);
+ addSpawnId(NPC_ATTACKER_GENERATORS, NPC_ATTACKER_GENERATORS_1, LYN_DRACO_ATTACKER_GENERATORS, GENERATOR_GUARD, NPC_GENERATOR);
+ addFirstTalkId(NPC_GENERATOR);
+ addSeeCreatureId(INVISIBLE);
+ _zoneLair = ZoneManager.getInstance().getZoneById(ZONE_ID, L2NoSummonFriendZone.class);
+ // Unlock
+ final StatsSet info = GrandBossManager.getInstance().getStatsSet(LINDVIOR_RAID);
+ final long time = info.getLong("respawn_time") - System.currentTimeMillis();
+ if (time > 0)
+ {
+ startQuestTimer("unlock_lindvior", time, null, null);
+ }
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ // Anti BUGGERS
+ if (!_zoneLair.isInsideZone(attacker))
+ {
+ attacker.doDie(null);
+ _log.warning(getName() + ": Character: " + attacker.getName() + " attacked: " + npc.getName() + " out of the boss zone!");
+ }
+ if (!_zoneLair.isInsideZone(npc))
+ {
+ npc.teleToLocation(CENTER_LOCATION, true);
+ _log.warning(getName() + ": Character: " + attacker.getName() + " attacked: " + npc.getName() + " wich is out of the boss zone!");
+ }
+
+ double percent = ((npc.getCurrentHp() - damage) / npc.getMaxHp()) * 100;
+ if ((percent <= 80) && (_status == 0))
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(RED_ZONE_EFFECT, true));
+ _zoneLair.getPlayersInside().stream().forEach(p ->
+ {
+ startQuestTimer("stop_red_zone", 10000, _lindvior, p);
+ p.broadcastPacket(new ExShowScreenMessage(NpcStringId.A_FEARSOME_POWER_EMANATES_FROM_LINDVIOR, ExShowScreenMessage.TOP_CENTER, 2000, true));
+ });
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_FLY, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.8);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true);
+ addSpawn(LINDVIOR_SERVITOR[0], loc, true);
+ }
+ _status = 1;
+ }
+ else if ((percent <= 75) && (_status == 1))
+ {
+ _bigVortexesTask = ThreadPoolManager.schedule(() -> spawnServitor(1, 300, _lindvior.getLocation(), NPC_ATTACKER_BIG_VORTEX), 1000);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(LINDVIOR_SERVITOR[1], loc, true);
+ }
+ _zoneLair.getPlayersInside().stream().forEach(p -> p.broadcastPacket(new ExShowScreenMessage(NpcStringId.A_GIGANTIC_WHIRLWIND_HAS_APPEARED, ExShowScreenMessage.TOP_CENTER, 2000, true)));
+ _status = 2;
+ }
+ else if ((percent <= 60) && (_status == 2))
+ {
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_GROUND, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.6);
+ spawnServitor(10, 2000, _lindvior.getLocation(), LINDVIOR_SERVITOR);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(LINDVIOR_SERVITOR[2], loc, true);
+ }
+ _skillCastTask = ThreadPoolManager.scheduleAtFixedRate(() -> _lindvior.doCast(SKILL_REFLECT.getSkill()), 5000, 80000);
+ _status = 3;
+ }
+ else if ((percent <= 40) && (_status == 3))
+ {
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_FLY, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.4);
+
+ if (SpawnTable.getInstance().getSpawns(NPC_ATTACKER_BIG_VORTEX) != null)
+ {
+ if ((_vortex != null) && (_vortex.getId() == NPC_ATTACKER_SMALL_VORTEX))
+ {
+ _vortex.getSpawn().stopRespawn();
+ _vortex.deleteMe();
+ }
+ }
+ _status = 4;
+ }
+ else if ((percent <= 35) && (_status == 4))
+ {
+ _smallVortexesTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true, 60000);
+ addSpawn(LINDVIOR_SERVITOR[3], loc, true);
+ }
+
+ }, 20000, 60000);
+ _status = 5;
+ }
+ else if ((percent <= 20) && (_status == 5))
+ {
+ if (_smallVortexesTask != null)
+ {
+ _smallVortexesTask.cancel(true);
+ _smallVortexesTask = null;
+ }
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.LINDVIOR_HAS_LANDED, 2, 5000, true));
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_RAID, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.2);
+ _bigVortexesTask = ThreadPoolManager.schedule(() -> spawnServitor(1, 300, _lindvior.getLocation(), NPC_ATTACKER_BIG_VORTEX), 1000);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true);
+ addSpawn(LINDVIOR_SERVITOR[4], loc, true);
+ addSpawn(LINDVIOR_SERVITOR[3], loc, true);
+ }
+ _collapseTask = ThreadPoolManager.schedule(Lindvior.this::Clean, 600000);
+ _status = 6;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ protected void Clean()
+ {
+ _status = 0;
+ if (_socialTask != null)
+ {
+ _socialTask.cancel(false);
+ _socialTask = null;
+ }
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(false);
+ _announceTask = null;
+ }
+ if (_announceProtect != null)
+ {
+ _announceProtect.cancel(false);
+ _announceProtect = null;
+ }
+ if (_skillCastTask != null)
+ {
+ _skillCastTask.cancel(false);
+ _skillCastTask = null;
+ }
+ if (_LynDracoTask != null)
+ {
+ _LynDracoTask.cancel(false);
+ _LynDracoTask = null;
+ }
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(false);
+ _mobsSpawnTask = null;
+ }
+ if (_collapseTask != null)
+ {
+ _collapseTask.cancel(false);
+ _collapseTask = null;
+ }
+ if (_bigVortexesTask != null)
+ {
+ _bigVortexesTask.cancel(false);
+ _bigVortexesTask = null;
+ }
+ if (_smallVortexesTask != null)
+ {
+ _smallVortexesTask.cancel(false);
+ _smallVortexesTask = null;
+ }
+ if (_lionel != null)
+ {
+ _lionel.deleteMe();
+ }
+ _zoneLair.getCharactersInside().forEach(mob ->
+ {
+ if (mob.isNpc())
+ {
+ mob.deleteMe();
+ mob.setIsDead(true);
+ }
+ });
+ }
+
+ private void Fail(boolean clean)
+ {
+ if (clean)
+ {
+ Clean();
+ }
+ _zoneLair.oustAllPlayers();
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, false));
+ cancelQuestTimers("attack_generator");
+ if (GrandBossManager.getInstance().getBossStatus(LINDVIOR_RAID) != 3)
+ {
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, ALIVE);
+ }
+ }
+
+ @Override
+ public synchronized String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
+ {
+ if ((skill.getId() == 15606) && (npc.getId() == NPC_GENERATOR))
+ {
+ synchronized (_chargedValues)
+ {
+ int index = npc.getScriptValue();
+ if (!hasFlag(_chargedMask, 1 << index))
+ {
+ _chargedValues[index] += caster.isGM() ? (30 / 4) + 2 : (1 / 4) + 2;
+ _chargedValues[index] = Math.min(_chargedValues[index], 120);
+
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 3000, player ->
+ {
+ player.sendPacket(new ExShowScreenMessage(NpcStringId.S1_HAS_CHARGED_THE_CANNON, ExShowScreenMessage.TOP_CENTER, 10000, true, caster.getName()));
+ player.sendPacket(new ExSendUIEvent(player, ExSendUIEvent.TYPE_NORNIL, _chargedValues[index], 120, NpcStringId.CHARGING));
+ });
+ if (_chargedValues[index] >= 120)
+ {
+ _chargedMask |= 1 << index;
+ _chargedValues[index] = 0;
+ }
+
+ if (hasFlag(_chargedMask, 0xf))
+ {
+ nextStage(3);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ switch (npc.getId())
+ {
+ case NPC_ATTACKER_GENERATORS:
+ case NPC_ATTACKER_GENERATORS_1:
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2GuardInstance.class, 800, cha ->
+ {
+
+ if (cha.getId() == GENERATOR_GUARD)
+ {
+ npc.reduceCurrentHp(1, cha, null);
+ cha.reduceCurrentHp(1, npc, null);
+ }
+ if (cha.getId() == NPC_GENERATOR)
+ {
+ ((L2Attackable) npc).addDamageHate(cha, 500, 98);
+ }
+ });
+ break;
+ }
+ case LYN_DRACO_ATTACKER_GENERATORS:
+ {
+ ((L2Attackable) npc).setCanStopAttackByTime(false);
+ ((L2Attackable) npc).setCanReturnToSpawnPoint(false);
+ startQuestTimer("attack_generator", 10000, npc, null, true);
+ break;
+ }
+ case GENERATOR_GUARD:
+ {
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ ((L2GuardInstance) npc).setIsInvul(true);
+ break;
+ }
+ case NPC_GENERATOR:
+ {
+ npc.disableCoreAI(true);
+ npc.setDisplayEffect(1);
+ npc.setRandomWalking(false);
+ npc.setIsInvul(true); // Can't get damage now
+ _activeMask = 0;
+ _chargedMask = 0;
+ break;
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character player, boolean isSummon)
+ {
+ setLindviorSpawnTask();
+ npc.getSpawn().stopRespawn();
+ npc.deleteMe();
+ return super.onSeeCreature(npc, player, isSummon);
+ }
+
+ private void nextStage(int _taskId)
+ {
+ switch (_taskId)
+ {
+ case 1: // Spawn Generators
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, true));
+ int i = 0;
+ L2GuardInstance guard;
+ for (Location loc : CONTROL_GENERATOR_SPAWNS)
+ {
+ guard = (L2GuardInstance) addSpawn(NPC_GENERATOR, loc, true);
+ guard.setDisplayEffect(0x01);
+ guard.setScriptValue(i++);
+ _generatorSpawn.add(guard);
+ }
+
+ L2Npc npc;
+ for (Location loc : SCHEME_GENERATOR_SPAWNS)
+ {
+ npc = addSpawn(GENERATOR_GUARD, loc, true);
+ npc.setRandomWalking(false);
+ _guardSpawn.add(npc);
+ }
+
+ _mobsSpawnTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : ATTACKER_GENERATOR_SPAWNS)
+ {
+ if (getRandom(10) <= 5)
+ {
+ _monsterSpawn.add(addSpawn(NPC_ATTACKER_GENERATORS, loc, true));
+ }
+ else
+ {
+ _monsterSpawn.add(addSpawn(NPC_ATTACKER_GENERATORS_1, loc, true));
+ }
+ }
+ }, 30000, 80000);
+
+ _dummyLindvior = addSpawn(LINDVIOR_CAMERA, 45259, -27115, -638, 41325, false, 0, false);
+ _announceTask = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(player -> player.sendPacket(new ExShowScreenMessage(NpcStringId.YOU_MUST_ACTIVATE_THE_4_GENERATORS, ExShowScreenMessage.TOP_CENTER, 7000, true))), 10000, 20000);
+ break;
+ }
+ case 2: // After activation of 4 generators, we wait to be charged
+ {
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(true);
+ _announceTask = null;
+ }
+ _generatorSpawn.forEach(npc ->
+ {
+ npc.setDisplayEffect(1);
+ npc.setIsInvul(false);
+ npc.broadcastInfo();
+ });
+
+ _zoneLair.getPlayersInside().forEach(player ->
+ {
+ player.sendPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, false));
+ cancelQuestTimers("NPC_SHOUT");
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.ALL_4_GENERATORS_MUST_BE_ACTIVATED);
+ });
+ _announceProtect = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(p -> player.sendPacket(new ExShowScreenMessage(NpcStringId.PROTECT_THE_GENERATOR, ExShowScreenMessage.TOP_CENTER, 7000, true))), 10000, 18000);
+ _zoneLair.broadcastPacket(new SpecialCamera(_dummyLindvior, 3300, 200, 20, 11000, 10500, 0, 8, 1, 0, 0));
+ _generatorSpawn.forEach(npc -> npc.sendInfo(player));
+ startQuestTimer("show_movie", 13000, null, null);
+ startQuestTimer("start_charge", 35000, null, null);
+ startQuestTimer("show_shield_animation", 2000, null, null);
+ });
+ break;
+ }
+ case 3: // After charging all the generators
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(ALL_GENERATORS_CONNECTED_EFFECT, true));
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(true);
+ _announceTask = null;
+ }
+ if (_announceProtect != null)
+ {
+ _announceProtect.cancel(false);
+ _announceProtect = null;
+ }
+ if (_skillCastTask != null)
+ {
+ _skillCastTask.cancel(true);
+ _skillCastTask = null;
+ }
+ if (_LynDracoTask != null)
+ {
+ _LynDracoTask.cancel(true);
+ _LynDracoTask = null;
+ }
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(true);
+ _mobsSpawnTask = null;
+ }
+ _monsterSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _LinDracoSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _generatorSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _guardSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ cancelQuestTimers("attack_generator");
+ _lindvior2.setIsDead(true);
+ _lindvior2.deleteMe();
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, FIGHTING);
+ _lionel = addSpawn(LIONEL_HUNTER, 42630, -48231, -792, 855, false, 0, false);
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_GROUND, CENTER_LOCATION, false, 0, true);
+ _zoneLair.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _zoneLair.getPlayersInside().forEach(_lindvior::sendInfo);
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.LINDVIOR_HAS_FALLEN_FROM_THE_SKY, ExShowScreenMessage.TOP_CENTER, 7000));
+ _mobsSpawnTask = ThreadPoolManager.scheduleAtFixedRate(() -> spawnServitor(2, 1000, _lindvior.getLocation(), LINDVIOR_SERVITOR), 60000, 180000);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ switch (event)
+ {
+ case "unlock_lindvior":
+ {
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, ALIVE);
+ break;
+ }
+ case "stage_1_activate_generator":
+ {
+ int index = npc.getScriptValue();
+ if (!hasFlag(_activeMask, 1 << index))
+ {
+ _activeMask |= 1 << index;
+ npc.setDisplayEffect(0x02);
+ sendEventTrigger(true, GENERATOR_TRIGERS[index]);
+ _zoneLair.getPlayersInside().stream().forEach(p -> p.broadcastPacket(new Earthquake(p.getX(), p.getY(), p.getZ(), 20, 10)));
+ if (hasFlag(_activeMask, 0xf))
+ {
+ nextStage(2);
+ }
+ }
+ break;
+ }
+ case "show_shield_animation": // zone brodcat shield event triger
+ {
+ _zoneLair.getPlayersInside().forEach(p ->
+ {
+ p.sendPacket(new OnEventTrigger(SECOND_STAGE_EVENT_TRIGGER, true));
+ });
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.THE_GENERATOR_IS_CONNECTED_TO_THE_CANNON);
+ });
+ break;
+ }
+ case "show_movie": // zone brodcat Lindvior scene movie
+ {
+ _zoneLair.getPlayersInside().forEach(p ->
+ {
+ playMovie(p, Movie.SC_LIND_OPENING);
+ });
+ _dummyLindvior.deleteMe();
+ _lindvior2 = addSpawn(LINDVIOR_FAKE, CENTER_LOCATION, false, 0, false);
+ _lindvior2.setTargetable(false);
+ _announceTask = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(p -> p.sendPacket(new ExShowScreenMessage(NpcStringId.CHARGE_THE_CANNON_USING_THE_GENERATOR, ExShowScreenMessage.TOP_CENTER, 7000, true))), 40000, 20000);
+ break;
+ }
+ case "start_charge":
+ {
+ _skillCastTask = ThreadPoolManager.scheduleAtFixedRate(() -> _generatorSpawn.forEach(generators ->
+ {
+ int index = generators.getScriptValue();
+ if (!generators.isCastingNow() && (generators.getEffectList().getBuffInfoBySkillId(SKILL_RECHARGE_POSIBLE.getSkillId()) == null) && !hasFlag(_chargedMask, 1 << index))
+ {
+ // TODO Need core implemented combo skill packet.
+ // On this moment player automatic charge generator if distance generator and player <= 900
+ generators.doCast(SKILL_RECHARGE_POSIBLE.getSkill());
+ L2World.getInstance().forEachVisibleObjectInRange(generators, L2PcInstance.class, 900, p ->
+ {
+ p.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+ p.setTarget(generators);
+ p.doCast(RECHARGE.getSkill());
+ });
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.setTarget(generators);
+ guard.doCast(RECHARGE.getSkill());
+ guard.setIsInvul(false);
+ if (!guard.isDead())
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, GUARD_MSG_1[getRandom(GUARD_MSG_1.length)]);
+ }
+ });
+ }
+ }), 10000, 20000);
+ _LynDracoTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : LYN_DRACO_SPAWNS)
+ {
+ _LinDracoSpawn.add(addSpawn(LYN_DRACO_ATTACKER_GENERATORS, loc, true));
+ }
+ }, 20000, 60000);
+ break;
+ }
+ case "stop_red_zone":
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(RED_ZONE_EFFECT, false));
+ break;
+ }
+ case "attack_generator":
+ {
+ if ((npc != null) && !npc.isDead())
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2GuardInstance.class, 3000, generator ->
+ {
+ if (generator.getId() == NPC_GENERATOR)
+ {
+ npc.setTarget(generator);
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, generator.getLocation());
+ if (npc.distFromMe(generator) < 500)
+ {
+ npc.reduceCurrentHp(1, generator, null);
+ generator.reduceCurrentHp(1, npc, null);
+ }
+ }
+ });
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
+ {
+ if (npc.getId() == LINDVIOR_RAID)
+ {
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.HONORABLE_WARRIORS_HAVE_DRIVEN_OFF_LINDVIOR_THE_EVIL_WIND_DRAGON, ExShowScreenMessage.TOP_CENTER, 10000, true));
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, DEAD);
+ final long respawnTime = (Config.LINDVIOR_SPAWN_INTERVAL + getRandom(-Config.LINDVIOR_SPAWN_RANDOM, Config.LINDVIOR_SPAWN_RANDOM)) * 3600000;
+ final StatsSet info = GrandBossManager.getInstance().getStatsSet(LINDVIOR_RAID);
+ info.set("respawn_time", System.currentTimeMillis() + respawnTime);
+ GrandBossManager.getInstance().setStatsSet(LINDVIOR_RAID, info);
+ startQuestTimer("unlock_lindvior", respawnTime, null, null);
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(true);
+ _mobsSpawnTask = null;
+ }
+ _zoneLair.getCharactersInside().stream().filter(L2Character::isNpc).forEach(mob -> mob.deleteMe());
+ ThreadPoolManager.schedule(() -> npc.decayMe(), 10000);
+ _zoneLair.broadcastPacket(new OnEventTrigger(SECOND_STAGE_EVENT_TRIGGER, false));
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, true));
+ _lionel.deleteMe();
+ }
+ else if (npc.getId() == NPC_GENERATOR)
+ {
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.THE_GENERATOR_HAS_BEEN_DESTROYED, ExShowScreenMessage.TOP_CENTER, 5000, true));
+ Clean();
+ _collapseTask = ThreadPoolManager.schedule(() -> Fail(false), 20000);
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onFirstTalk(L2Npc npc, L2PcInstance player)
+ {
+ if (npc.getId() == NPC_GENERATOR)
+ {
+ return npc.getDisplayEffect() == 1 ? "19477.html" : "19477-01.html";
+ }
+ return super.onFirstTalk(npc, player);
+ }
+
+ @Override
+ public String onEnterZone(L2Character character, L2ZoneType zone)
+ {
+ if (zone.getId() == ZONE_ID)
+ {
+ if (_collapseTask != null)
+ {
+ _collapseTask.cancel(true);
+ _collapseTask = null;
+ }
+ }
+ return super.onEnterZone(character, zone);
+ }
+
+ @Override
+ public String onExitZone(L2Character character, L2ZoneType zone)
+ {
+ if (zone.getId() == ZONE_ID)
+ {
+ if (zone.getPlayersInside().isEmpty())
+ {
+ _collapseTask = ThreadPoolManager.schedule(() -> Fail(true), 900000);
+ }
+ }
+ return super.onExitZone(character, zone);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("NPC_SHOUT"))
+ {
+ if ((npc != null) && !npc.isDead())
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, GUARD_MSG[getRandom(GUARD_MSG.length)]);
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ }
+ }
+ }
+
+ private void sendEventTrigger(boolean status, int... triggers)
+ {
+ IClientOutgoingPacket[] pakets = new IClientOutgoingPacket[triggers.length];
+ for (int i = 0; i < triggers.length; i++)
+ {
+ pakets[i] = new OnEventTrigger(triggers[i], status);
+ }
+ for (IClientOutgoingPacket packet : pakets)
+ {
+ _zoneLair.broadcastPacket(packet);
+ }
+ }
+
+ private void spawnServitor(int count, int radius, Location loc, int... npcIds)
+ {
+ int x = loc.getX(), y = loc.getY();
+ if (radius > 0)
+ {
+ x += Rnd.get(-radius, radius);
+ y += Rnd.get(-radius, radius);
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ _monsterSpawn.add(addSpawn(npcIds[getRandom(npcIds.length)], x, y, loc.getZ(), loc.getHeading(), true, 0, true));
+ }
+ }
+
+ private static boolean hasFlag(int val, int flag)
+ {
+ return (val & flag) == flag;
+ }
+
+ public void setLindviorSpawnTask()
+ {
+ synchronized (this)
+ {
+ if (_socialTask == null)
+ {
+ _socialTask = ThreadPoolManager.schedule(() -> nextStage(1), 3000);
+ }
+ }
+ }
+
+ @RegisterEvent(EventType.ON_CREATURE_DAMAGE_RECEIVED)
+ @RegisterType(ListenerRegisterType.NPC)
+ @Id(LINDVIOR_FLY)
+ @Id(LINDVIOR_RAID)
+ @Id(LINDVIOR_GROUND)
+ public void onCreatureDamageReceived(OnCreatureDamageReceived event)
+ {
+ _zoneLair.getPlayersInside().stream().forEach(p ->
+ {
+ switch (_status)
+ {
+ case 0:
+ case 1:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 1, -1, 0.015, true);
+ break;
+ }
+ case 2:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 2, -1, 0.015, true);
+ break;
+ }
+ case 3:
+ case 4:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 3, -1, 0.015, true);
+ break;
+ }
+ case 5:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 4, -1, 0.015, true);
+ break;
+ }
+ }
+ });
+ }
+
+ public static void main(String[] args)
+ {
+ new Lindvior();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java
new file mode 100644
index 0000000000..d60c088f9f
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java
@@ -0,0 +1,175 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.holders.SkillHolder;
+
+import ai.AbstractNpcAI;
+
+/**
+ * LindviorBoss AI
+ * @author Gigi
+ * @date 2017-08-02 - [11:05:21]
+ */
+public class LindviorBoss extends AbstractNpcAI
+{
+ // Boss
+ private static final int LINDVIOR_GROUND = 25899;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int LINDVIOR_FLY = 19424;
+ // Skills
+ private static final SkillHolder SKILL_FLY_UP = new SkillHolder(15278, 1);
+ private static final SkillHolder SKILL_RABIES = new SkillHolder(15269, 1);
+ private static final SkillHolder SKILL_FLY = new SkillHolder(15279, 1);
+ private static final SkillHolder MASS_HELL_BINDING = new SkillHolder(11052, 6);
+ private static final SkillHolder MIGHTY_WIND_STRIKE = new SkillHolder(15274, 1);
+ private static final SkillHolder WIND_PULL = new SkillHolder(15591, 1);
+ private static final SkillHolder LINDVIORS_JUMP = new SkillHolder(15430, 1);
+ private static final SkillHolder BODY_SLAM = new SkillHolder(15271, 1);
+ private static final SkillHolder SOAR = new SkillHolder(15279, 1);
+ private static final SkillHolder WIND_BREAT = new SkillHolder(15272, 1);
+ private static final SkillHolder TAIL_SWIPE = new SkillHolder(15273, 1);
+ private static final SkillHolder TORNADO = new SkillHolder(15275, 1);
+ private static final SkillHolder LINDVIORS_ATTACK = new SkillHolder(15600, 1);
+ // Chances
+ private final static int CHANCE_MIGHTY_WIND_STRIKE = 9;
+ private final static int CHANCE_WIND_PULL = 4;
+ private final static int CHANCE_LINDVIORS_JUMP = 7;
+ private final static int CHANCE_BODY_SLAM = 2;
+ private final static int CHANCE_SOAR = 8;
+ private final static int CHANCE_WIND_BREAT = 3;
+ private final static int CHANCE_TAIL_SWIPE = 5;
+ private final static int CHANCE_TORNADO = 6;
+ private final static int CHANCE_LINDVIORS_ATTACK = 1;
+
+ public LindviorBoss()
+ {
+ super();
+ addAttackId(LINDVIOR_GROUND, LINDVIOR_FLY, LINDVIOR_RAID);
+ addSpawnId(LINDVIOR_FLY);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ double percent = ((npc.getCurrentHp() - damage) / npc.getMaxHp()) * 100;
+ final int chance = getRandom(100);
+ switch (npc.getId())
+ {
+ case LINDVIOR_GROUND:
+ {
+ if ((percent <= 80) && npc.isScriptValue(0))
+ {
+
+ npc.doCast(SKILL_FLY_UP.getSkill());
+ npc.doCast(SKILL_RABIES.getSkill());
+ npc.setScriptValue(1);
+ }
+ else if ((percent <= 40) && (npc.isScriptValue(1)))
+ {
+ npc.doCast(SKILL_FLY.getSkill());
+ npc.setScriptValue(2);
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_MIGHTY_WIND_STRIKE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(MIGHTY_WIND_STRIKE.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_PULL))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_PULL.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_LINDVIORS_JUMP))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(LINDVIORS_JUMP.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_BODY_SLAM))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(BODY_SLAM.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TAIL_SWIPE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TAIL_SWIPE.getSkill());
+ }
+ break;
+ }
+ case LINDVIOR_FLY:
+ {
+ if (!npc.isCastingNow() && (chance <= CHANCE_SOAR))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(SOAR.getSkill());
+ }
+ break;
+ }
+ case LINDVIOR_RAID:
+ {
+ if ((percent <= 20) && (npc.isScriptValue(0)))
+ {
+ npc.doCast(SKILL_FLY.getSkill());
+ npc.setScriptValue(1);
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_BREAT))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_BREAT.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_PULL))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_PULL.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TAIL_SWIPE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TAIL_SWIPE.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TORNADO))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TORNADO.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_LINDVIORS_ATTACK))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(LINDVIORS_ATTACK.getSkill());
+ }
+ break;
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(true);
+ npc.doCast(MASS_HELL_BINDING.getSkill());
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new LindviorBoss();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java
new file mode 100644
index 0000000000..846ada3442
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java
@@ -0,0 +1,67 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import com.l2jmobius.gameserver.enums.ChatType;
+import com.l2jmobius.gameserver.instancemanager.WalkingManager;
+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.network.NpcStringId;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Lionel Hunter AI
+ * @author Gigi
+ * @date 2017-07-23 - [22:54:59]
+ */
+public class LionelHunter extends AbstractNpcAI
+{
+ // Npc
+ private static final int LIONEL_HUNTER = 33886;
+ // Misc
+ private static final String ROUTE_NAME = "Rune_Lionel";
+
+ public LionelHunter()
+ {
+ addSpawnId(LIONEL_HUNTER);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("NPC_SHOUT") && (npc != null))
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.WE_JUST_LOCATED_LINDVIOR_THOSE_WHO_ARE_WILLING_TO_FIGHT_CAN_DO_SO_AT_ANY_TIME_NOW);
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ }
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ WalkingManager.getInstance().startMoving(npc, ROUTE_NAME);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new LionelHunter();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java
new file mode 100644
index 0000000000..d9bf024d24
--- /dev/null
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java
@@ -0,0 +1,176 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import java.util.Collection;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.L2World;
+import com.l2jmobius.gameserver.model.Location;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.network.serverpackets.FlyToLocation;
+import com.l2jmobius.gameserver.network.serverpackets.ValidateLocation;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Vortex AI
+ * @author Gigi
+ * @date 2017-07-23 - [10:32:50]
+ */
+public class Vortex extends AbstractNpcAI
+{
+ private static final int SMALL_VORTEX = 25898;
+ private static final int BIG_VORTEX = 19427;
+
+ public Vortex()
+ {
+ super();
+ addSeeCreatureId(SMALL_VORTEX, BIG_VORTEX);
+ addSpawnId(SMALL_VORTEX, BIG_VORTEX);
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ switch (event)
+ {
+ case "rnd_small":
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 250, attackers ->
+ {
+ if ((attackers != null) && !attackers.isDead() && !attackers.isAlikeDead())
+ {
+ attackers.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+ final int radians = (int) Math.toRadians(npc.calculateDirectionTo(attackers));
+ final int x = (int) (attackers.getX() + (600 * Math.cos(radians)));
+ final int y = (int) (attackers.getY() + (600 * Math.sin(radians)));
+ final int z = attackers.getZ();
+ final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld());
+ attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800));
+ attackers.setXYZ(loc);
+ attackers.broadcastPacket(new ValidateLocation(attackers));
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, player);
+ startQuestTimer("stop_knock_down", 5000, npc, attackers);
+ startQuestTimer("despawn_small", 5000, npc, null);
+ }
+ });
+ break;
+ }
+ case "rnd_big":
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 500, attackers ->
+ {
+ if ((attackers != null) && !attackers.isDead() && !attackers.isAlikeDead())
+ {
+ attackers.setCurrentHp(1.0);
+ attackers.setCurrentMp(1.0);
+ attackers.setCurrentCp(1.0);
+ startQuestTimer("despawn_big", 600000, npc, null);
+ }
+ });
+ break;
+ }
+ case "despawn_small":
+ {
+ if (npc != null)
+ {
+ cancelQuestTimers("rnd_small");
+ npc.getSpawn().stopRespawn();
+ npc.doDie(null);
+ }
+ break;
+ }
+ case "despawn_big":
+ {
+ if (npc != null)
+ {
+ cancelQuestTimers("despawn_big");
+ npc.getSpawn().stopRespawn();
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character player, boolean isSummon)
+ {
+ switch (npc.getId())
+ {
+ case SMALL_VORTEX:
+ {
+ startQuestTimer("rnd_small", 5000, npc, null, true);
+ break;
+ }
+ case BIG_VORTEX:
+ {
+ startQuestTimer("rnd_big", 10000, npc, null, true);
+ break;
+ }
+ }
+ return super.onSeeCreature(npc, player, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ switch (npc.getId())
+ {
+ case SMALL_VORTEX:
+ {
+ attackRandomTarget(npc);
+ npc.setRandomWalking(true);
+ npc.setIsRunning(true);
+ break;
+ }
+ case BIG_VORTEX:
+ {
+ attackRandomTarget(npc);
+ npc.setRandomWalking(true);
+ npc.setIsRunning(true);
+ break;
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ private void attackRandomTarget(L2Npc npc)
+ {
+ final Collection players = L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class);
+ {
+ if ((players == null) || players.isEmpty())
+ {
+ return;
+ }
+ if (players.size() > 0)
+ {
+ addAttackPlayerDesire(npc, players.stream().findAny().get());
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new Vortex();
+ }
+}
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/08500-08599.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/08500-08599.xml
index 759a91fb29..bbcefbf96b 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/08500-08599.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/08500-08599.xml
@@ -1954,7 +1954,7 @@
-
+
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19400-19499.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19400-19499.xml
index 8d70de0309..bc7107be8a 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19400-19499.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/19400-19499.xml
@@ -825,58 +825,76 @@
-
+ DRAGONFEMALE
-
-
-
+
+
-
+
-
-
+
+
-
+
+
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
-
+ DRAGONFEMALE
-
-
-
+
+
-
+
-
-
+
+
-
+
+
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -984,7 +1002,7 @@
-
+
@@ -2357,7 +2375,7 @@
-
+ ETCFEMALE
@@ -2373,7 +2391,10 @@
-
+
+
+
+
@@ -2401,7 +2422,7 @@
-
+ ETCFEMALE
@@ -2412,7 +2433,7 @@
-
+
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/25800-25899.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/25800-25899.xml
index 7f8dad3878..22cc2e00b9 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/25800-25899.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/25800-25899.xml
@@ -4791,7 +4791,7 @@
-
+ DRAGONFEMALE
@@ -4801,8 +4801,8 @@
-
-
+
+
@@ -4822,7 +4822,7 @@
-
+ DRAGONFEMALE
@@ -4853,7 +4853,7 @@
-
+ DRAGONFEMALE
@@ -4877,6 +4877,12 @@
+
+
+
+
+
+
@@ -4884,15 +4890,18 @@
-
+
+
+
+ DRAGONFEMALE
-
-
+
+
@@ -4910,29 +4919,29 @@
-
+ true
+
-
-
+ DRAGONFEMALE
-
-
+
+
+
-
-
+
+
-
-
+
+
-
@@ -4941,11 +4950,16 @@
+
+
+
+
+
-
-
+
+
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/29200-29299.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/29200-29299.xml
index 21fb5237c1..aa344a1818 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/29200-29299.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/npcs/29200-29299.xml
@@ -2018,13 +2018,13 @@
-
-
+ DRAGONFEMALE
+
-
+
@@ -2038,9 +2038,19 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2135,7 +2145,7 @@
-
+ DRAGONFEMALE
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/skills/15600-15699.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/skills/15600-15699.xml
index 89ca9ee0d9..321cb09e4a 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/skills/15600-15699.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/stats/skills/15600-15699.xml
@@ -49,23 +49,38 @@
21800
-
-
+ icon.skill0005
+ 20A2
+ 1000
+ 10001
- 25000
+ 10000
+ SELF
+ RANGE
+ NOT_FRIEND
- icon.skill1449
+ icon.skill14444A1
+ 190010000-2
- 200
+ 200
+ TARGET
+ SINGLE
+
+
+
+ 19477
+
+
+
diff --git a/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/no_summon_friend.xml b/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/no_summon_friend.xml
index a6b09689a0..7b5203b963 100644
--- a/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/no_summon_friend.xml
+++ b/L2J_Mobius_1.0_Ertheia/dist/game/data/zones/no_summon_friend.xml
@@ -167,4 +167,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/Config.java b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/Config.java
index 40be605ad7..e450b23b2f 100644
--- a/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/Config.java
+++ b/L2J_Mobius_1.0_Ertheia/java/com/l2jmobius/Config.java
@@ -891,6 +891,13 @@ public final class Config
public static int KELBIM_SPAWN_INTERVAL;
public static int KELBIM_SPAWN_RANDOM;
+ // Lindvior
+ public static int LINDVIOR_SPAWN_INTERVAL;
+ public static int LINDVIOR_SPAWN_RANDOM;
+ public static int LINDVIOR_MIN_PLAYERS;
+ public static int LINDVIOR_MAX_PLAYERS;
+ public static int LINDVIOR_MIN_PLAYER_LVL;
+
// Anakim
public static int ANAKIM_MIN_PLAYERS;
public static int ANAKIM_MAX_PLAYERS;
@@ -2223,6 +2230,11 @@ public final class Config
LILITH_SPAWN_RANDOM = GrandBossSettings.getInt("RandomOfLilithSpawn", 148);
LILITH_MIN_PLAYER_LVL = GrandBossSettings.getInt("LilithMinPlayerLvl", 85);
LILITH_MAX_PLAYER_LVL = GrandBossSettings.getInt("LilithMaxPlayerLvl", 89);
+ LINDVIOR_SPAWN_INTERVAL = GrandBossSettings.getInt("IntervalOfLindviorSpawn", 264);
+ LINDVIOR_SPAWN_RANDOM = GrandBossSettings.getInt("RandomOfLindviorSpawn", 72);
+ LINDVIOR_MIN_PLAYERS = GrandBossSettings.getInt("LindviorMinPlayers", 49);
+ LINDVIOR_MAX_PLAYERS = GrandBossSettings.getInt("LindviorMaxPlayers", 112);
+ LINDVIOR_MIN_PLAYER_LVL = GrandBossSettings.getInt("LindviorMinPlayerLvl", 99);
// Gracia Seeds
final PropertiesParser GraciaSeedsSettings = new PropertiesParser(GRACIASEEDS_CONFIG_FILE);
diff --git a/L2J_Mobius_1.0_Ertheia/readme.txt b/L2J_Mobius_1.0_Ertheia/readme.txt
index f6a9bfe6ad..0b4eeb2a37 100644
--- a/L2J_Mobius_1.0_Ertheia/readme.txt
+++ b/L2J_Mobius_1.0_Ertheia/readme.txt
@@ -20,7 +20,6 @@ What is done
TODO
-Modify all skill levels/sublevels based on client
--Lindvior
-Trasken
-Kamaloka
-Provisional Clan Halls
diff --git a/L2J_Mobius_2.5_Underground/dist/db_installer/sql/game/grandboss_data.sql b/L2J_Mobius_2.5_Underground/dist/db_installer/sql/game/grandboss_data.sql
index 27ff367d22..68a2deaa1e 100644
--- a/L2J_Mobius_2.5_Underground/dist/db_installer/sql/game/grandboss_data.sql
+++ b/L2J_Mobius_2.5_Underground/dist/db_installer/sql/game/grandboss_data.sql
@@ -17,9 +17,10 @@ INSERT IGNORE INTO `grandboss_data` (`boss_id`,`loc_x`,`loc_y`,`loc_z`,`heading`
(29006, 17726, 108915, -6480, 0, 622493.58388, 3793.536), -- Core
(29014, 55024, 17368, -5412, 10126, 622493.58388, 3793.536), -- Orfen
(29020, 116033, 17447, 10107, -25348, 4068372, 39960), -- Baium
-(29028, -105200, -253104, -15264, 0, 62041918, 2248572), -- Valakas
(29068, 185708, 114298, -8221,32768, 62802301, 1998000), -- Antharas
+(29028, -105200, -253104, -15264, 0, 62041918, 2248572), -- Valakas
+(29240, 0, 0, 0, 0, 288282589, 47100), -- Lindvior
(29118, 0, 0, 0, 0, 4109288, 1220547), -- Beleth
(25286, 185080, -12613, -5499, 16550, 556345880, 86847), -- Anakim
(25283, 185062, -9605, -5499, 15640, 486021997, 79600), -- Lilith
-(26124, 0, 0, 0, 0, 13945521, 50920); -- Kelbim
\ No newline at end of file
+(26124, 0, 0, 0, 0, 13945521, 50920); -- Kelbim
diff --git a/L2J_Mobius_2.5_Underground/dist/game/config/GrandBoss.ini b/L2J_Mobius_2.5_Underground/dist/game/config/GrandBoss.ini
index 21b6037556..3689173278 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/config/GrandBoss.ini
+++ b/L2J_Mobius_2.5_Underground/dist/game/config/GrandBoss.ini
@@ -144,4 +144,23 @@ LilithMaxPlayers = 120
LilithMinPlayerLvl = 85
# Maximum players Level for enter to Lilith. Retail: 89
-LilithMaxPlayerLvl = 89
\ No newline at end of file
+LilithMaxPlayerLvl = 89
+
+# ---------------------------------------------------------------------------
+# Lindvior
+# ---------------------------------------------------------------------------
+
+# Interval time of Lindvior. Value is hour. Range 1-480. Retail: 264
+IntervalOfLindviorSpawn = 264
+
+# Random interval. Range 1-192. Retail: 72
+RandomOfLindviorSpawn = 72
+
+# Minimal count of players for enter to Lindvior. Retail: 49
+LindviorMinPlayers = 49
+
+# Maximum count of players for enter to Lindvior. Retail: 112
+LindviorMaxPlayers = 112
+
+# Minimum players Level for enter to Lindvior. Retail: 99
+LindviorMinPlayerLvl = 99
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml b/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml
index 5cbbf5261f..dc0e5372c2 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/Routes.xml
@@ -2352,4 +2352,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java
index 790799b0ad..b1a37e194f 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java
@@ -75,7 +75,7 @@ public class AltarOfSacrifice extends AbstractNpcAI
@Override
public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
{
- if (creature.isPlayer() && _jenas_guard.isScriptValue(0))
+ if ((creature != null) && creature.isPlayer() && _jenas_guard.isScriptValue(0))
{
startQuestTimer("msg_text", 3000, npc, null);
_jenas_guard.setScriptValue(1);
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/19477-01.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/19477-01.html
new file mode 100644
index 0000000000..5f9fc4431e
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/19477-01.html
@@ -0,0 +1,3 @@
+Generator:
+The generator is now supplying power to the sealed structure.
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/19477.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/19477.html
new file mode 100644
index 0000000000..540ff6ac54
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/19477.html
@@ -0,0 +1,3 @@
+Generator:
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html
new file mode 100644
index 0000000000..ba01eab970
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+On the Lindvior there is an attack!
+At this time it's impossible to enter the altar.
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html
new file mode 100644
index 0000000000..e3eac44ec7
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+Lindvior? You're too late, friend.
+A group of warriors drove him off not long ago.
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html
new file mode 100644
index 0000000000..40de9b961e
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+You are overcome by a voice, a voice so powerful you are helpless as it speaks:
+(The players who belong to an association can only enter through the Association Leader.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html
new file mode 100644
index 0000000000..9af5ff8c7a
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html
@@ -0,0 +1,3 @@
+Rune Castle Patrol Kato:
+(A command channel needs at least minimu: %min% and maximum: %max% members to challenge Lindvior.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html
new file mode 100644
index 0000000000..309c938f02
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html
@@ -0,0 +1,3 @@
+Rune Castle Patrol Kato:
+(A command channel members level must be minimum: %minlvl% Lvl or higher to challenge Lindvior.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html
new file mode 100644
index 0000000000..bddff0b348
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html
@@ -0,0 +1,6 @@
+Rune Castle Patrol Kato:
+Dire times dictated that I come myself from Rune Castle to help those in need here.
+What do you need?
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java
new file mode 100644
index 0000000000..3b54f9dfa8
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java
@@ -0,0 +1,129 @@
+/*
+ * 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 ai.bosses.Lindvior.KatoSicanus;
+
+import java.util.List;
+
+import com.l2jmobius.Config;
+import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
+import com.l2jmobius.gameserver.model.L2Party;
+import com.l2jmobius.gameserver.model.Location;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Kato Sicanus Teleporter AI
+ * @author Gigi
+ * @date 2017-07-13 - [22:17:16]
+ */
+public class KatoSicanus extends AbstractNpcAI
+{
+ // NPCs
+ private static final int KATO_SICANUS = 33881;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int INVISIBLE = 8572;
+ // Location
+ private static final Location LINDVIOR_LOCATION = new Location(46929, -28807, -1400);
+
+ public KatoSicanus()
+ {
+ addFirstTalkId(KATO_SICANUS);
+ addTalkId(KATO_SICANUS);
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("teleport"))
+ {
+ final int status = GrandBossManager.getInstance().getBossStatus(LINDVIOR_RAID);
+ if (player.isGM())
+ {
+ player.teleToLocation(LINDVIOR_LOCATION, true);
+ addSpawn(INVISIBLE, 46707, -28586, -1400, 0, false, 60000, false);
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, 1);
+ }
+ else
+ {
+ if (status == 2)
+ {
+ return "33881-1.html";
+ }
+ if (status == 3)
+ {
+ return "33881-2.html";
+ }
+ if (!player.isInParty())
+ {
+ return "33881-3.html";
+ }
+ final L2Party party = player.getParty();
+ final boolean isInCC = party.isInCommandChannel();
+ final List members = (isInCC) ? party.getCommandChannel().getMembers() : party.getMembers();
+ final boolean isPartyLeader = (isInCC) ? party.getCommandChannel().isLeader(player) : party.isLeader(player);
+ if (!isPartyLeader)
+ {
+ return "33881-3.html";
+ }
+ if ((members.size() < Config.LINDVIOR_MIN_PLAYERS) || (members.size() > Config.LINDVIOR_MAX_PLAYERS))
+ {
+ final NpcHtmlMessage packet = new NpcHtmlMessage(npc.getObjectId());
+ packet.setHtml(getHtm(player.getHtmlPrefix(), "33881-4.html"));
+ packet.replace("%min%", Integer.toString(Config.LINDVIOR_MIN_PLAYERS));
+ packet.replace("%max%", Integer.toString(Config.LINDVIOR_MAX_PLAYERS));
+ player.sendPacket(packet);
+ return null;
+ }
+ for (L2PcInstance member : members)
+ {
+ if (member.getLevel() < Config.LINDVIOR_MIN_PLAYER_LVL)
+ {
+ final NpcHtmlMessage packet = new NpcHtmlMessage(npc.getObjectId());
+ packet.setHtml(getHtm(player.getHtmlPrefix(), "33881-5.html"));
+ packet.replace("%minlvl%", Integer.toString(Config.LINDVIOR_MIN_PLAYER_LVL));
+ player.sendPacket(packet);
+ return null;
+ }
+ }
+ for (L2PcInstance member : members)
+ {
+ if (member.isInsideRadius(npc, 1500, true, false))
+ {
+ member.teleToLocation(LINDVIOR_LOCATION, true);
+ addSpawn(INVISIBLE, 46707, -28586, -1400, 0, false, 0, false);
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, 1);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onFirstTalk(L2Npc npc, L2PcInstance player)
+ {
+ return "33881.html";
+ }
+
+ public static void main(String[] args)
+ {
+ new KatoSicanus();
+ }
+}
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java
new file mode 100644
index 0000000000..d6247a96c6
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java
@@ -0,0 +1,976 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+
+import com.l2jmobius.Config;
+import com.l2jmobius.commons.util.Rnd;
+import com.l2jmobius.gameserver.ThreadPoolManager;
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.datatables.SpawnTable;
+import com.l2jmobius.gameserver.enums.ChatType;
+import com.l2jmobius.gameserver.enums.Movie;
+import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
+import com.l2jmobius.gameserver.instancemanager.ZoneManager;
+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.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
+import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.events.EventType;
+import com.l2jmobius.gameserver.model.events.ListenerRegisterType;
+import com.l2jmobius.gameserver.model.events.annotations.Id;
+import com.l2jmobius.gameserver.model.events.annotations.RegisterEvent;
+import com.l2jmobius.gameserver.model.events.annotations.RegisterType;
+import com.l2jmobius.gameserver.model.events.impl.character.OnCreatureDamageReceived;
+import com.l2jmobius.gameserver.model.holders.SkillHolder;
+import com.l2jmobius.gameserver.model.skills.Skill;
+import com.l2jmobius.gameserver.model.zone.L2ZoneType;
+import com.l2jmobius.gameserver.model.zone.type.L2NoSummonFriendZone;
+import com.l2jmobius.gameserver.network.NpcStringId;
+import com.l2jmobius.gameserver.network.serverpackets.Earthquake;
+import com.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+import com.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
+import com.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+import com.l2jmobius.gameserver.network.serverpackets.SocialAction;
+import com.l2jmobius.gameserver.network.serverpackets.SpecialCamera;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Lindvior Boss
+ * @author Gigi
+ * @date 2017-07-11 - [17:42:53]
+ *
+ * @VIDEO - https://www.youtube.com/watch?v=VknjOjRO9Cw
+ */
+public class Lindvior extends AbstractNpcAI
+{
+ // Monsters
+ private static final int LINDVIOR_FAKE = 19423;
+ private static final int LINDVIOR_GROUND = 25899;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int LINDVIOR_FLY = 19424;
+ private static final int NPC_GENERATOR = 19477;
+ private static final int GENERATOR_GUARD = 19479;
+ private static final int NPC_ATTACKER_GENERATORS = 25897;
+ private static final int NPC_ATTACKER_GENERATORS_1 = 25895;
+ private static final int LYN_DRACO_ATTACKER_GENERATORS = 29241;
+ private static final int NPC_ATTACKER_SMALL_VORTEX = 25898;
+ private static final int NPC_ATTACKER_BIG_VORTEX = 19427;
+ private static final int INVISIBLE = 8572;
+ private static final int LIONEL_HUNTER = 33886;
+ private static final int LINDVIOR_CAMERA = 19428;
+ // Zone
+ private final static int ZONE_ID = 12107;
+ static final Location CENTER_LOCATION = new Location(46424, -26200, -1400);
+ // Skills
+ private static final SkillHolder SKILL_RECHARGE_POSIBLE = new SkillHolder(15605, 1);
+ private static final SkillHolder RECHARGE = new SkillHolder(15606, 1);
+ private static final SkillHolder SKILL_REFLECT = new SkillHolder(15592, 1);
+ // Item
+ private static final int LINDVIORS_SCALE = 37495;
+ // Trigers
+ private static final int FIRST_STAGE_EVENT_TRIGGER = 21170112;
+ private static final int SECOND_STAGE_EVENT_TRIGGER = 21170100;
+ private static final int ALL_GENERATORS_CONNECTED_EFFECT = 21170110;
+ private static final int RED_ZONE_EFFECT = 21170120;
+ // Status
+ private static final int ALIVE = 0;
+ private static final int FIGHTING = 2;
+ private static final int DEAD = 3;
+ // Tasks
+ protected ScheduledFuture> _socialTask;
+ protected ScheduledFuture> _mobsSpawnTask;
+ protected ScheduledFuture> _collapseTask;
+ protected ScheduledFuture> _announceTask;
+ protected ScheduledFuture> _announceProtect;
+ protected ScheduledFuture> _skillCastTask;
+ protected ScheduledFuture> _LynDracoTask;
+ protected ScheduledFuture> _smallVortexesTask;
+ protected ScheduledFuture> _bigVortexesTask;
+ protected L2NoSummonFriendZone _zoneLair;
+ protected L2GrandBossInstance _lindvior = null;
+ protected L2Npc _lindvior2 = null;
+ protected L2Npc _dummyLindvior;
+ protected L2Npc _vortex = null;
+ protected L2Npc _lionel = null;
+ protected List _guardSpawn = new ArrayList<>();
+ protected List _generatorSpawn = new ArrayList<>();
+ protected List _monsterSpawn = new ArrayList<>();
+ protected List _LinDracoSpawn = new ArrayList<>();
+ protected int _activeMask = 0;
+ protected int _chargedMask = 0;
+ protected int _status = 0;
+
+ private static final Location[] CONTROL_GENERATOR_SPAWNS =
+ {
+ new Location(45288, -30360, -1432, 0),
+ new Location(48486, -27175, -1432, 0),
+ new Location(45272, -23976, -1432, 0),
+ new Location(42088, -27160, -1432, 0)
+ };
+ private static final Location[] SCHEME_GENERATOR_SPAWNS =
+ {
+ new Location(48440, -26824, -1438, 0),
+ new Location(48392, -27448, -1438, 0),
+ new Location(42136, -27480, -1438, 0),
+ new Location(42136, -26840, -1438, 0),
+ new Location(44936, -24024, -1438, 0),
+ new Location(45592, -24008, -1438, 0),
+ new Location(45608, -30312, -1438, 0),
+ new Location(44984, -30360, -1438, 0)
+ };
+ private static final Location[] ATTACKER_GENERATOR_SPAWNS =
+ {
+ new Location(44863, -24272, -1413, 33713),
+ new Location(45675, -24272, -1413, 33713),
+ new Location(45675, -30057, -1413, 64987),
+ new Location(44863, -30057, -1413, 64987),
+ new Location(42350, -27563, -1413, 46871),
+ new Location(42350, -26809, -1413, 46871),
+ new Location(48220, -26809, -1413, 16383),
+ new Location(48220, -27563, -1413, 16383)
+ };
+ private static final Location[] LYN_DRACO_SPAWNS =
+ {
+ new Location(45300, -28402, -1400, 48845),
+ new Location(46379, -27178, -1400, 1154),
+ new Location(45292, -26043, -1400, 13027),
+ new Location(44215, -27172, -1400, 33966)
+ };
+ private static final Location[] ATTACKER_SMALL_VORTEX_SPAWNS = new Location[]
+ {
+ new Location(46256, -30159, -1430, 57430),
+ new Location(45155, -29987, -1430, 14860),
+ new Location(46219, -27704, -1430, 1744),
+ new Location(46135, -28995, -1430, 43626),
+ new Location(43973, -28265, -1430, 16516),
+ new Location(46782, -29065, -1430, 63368),
+ new Location(47214, -29836, -1430, 46966),
+ new Location(44754, -29120, -1430, 56118),
+ new Location(47089, -28198, -1430, 8537),
+ new Location(44992, -28152, -1430, 11592),
+ new Location(44737, -24885, -1430, 3146),
+ new Location(46096, -24976, -1430, 49650),
+ new Location(46972, -25911, -1430, 62925),
+ new Location(46977, -27136, -1430, 2150),
+ new Location(42889, -24767, -1430, 10246),
+ new Location(47299, -25256, -1430, 1453),
+ new Location(44204, -25026, -1430, 39225),
+ new Location(42875, -28035, -1430, 34755),
+ new Location(41963, -26031, -1430, 18822),
+ new Location(43171, -25942, -1430, 44279),
+ new Location(41874, -27174, -1430, 56030),
+ new Location(44983, -26082, -1430, 7042),
+ new Location(46145, -26804, -1430, 24394),
+ new Location(46148, -26019, -1430, 34151),
+ new Location(45161, -24275, -1430, 39262),
+ new Location(47288, -24141, -1430, 21644),
+ new Location(43722, -26174, -1430, 11001),
+ new Location(44942, -27169, -1430, 39703),
+ new Location(46105, -24170, -1430, 28224),
+ new Location(49084, -27206, -1430, 41996),
+ new Location(48159, -27091, -1430, 62682),
+ new Location(48094, -28789, -1430, 49189),
+ new Location(48958, -27844, -1430, 59758),
+ new Location(43828, -23981, -1430, 10994),
+ new Location(48165, -25777, -1430, 53084),
+ new Location(48267, -28086, -1430, 9266),
+ new Location(43268, -28981, -1430, 23736),
+ new Location(44155, -29821, -1430, 39281),
+ new Location(43991, -29275, -1430, 27277),
+ new Location(44057, -27133, -1430, 64484),
+ new Location(43257, -26764, -1430, 14161),
+ new Location(42300, -25194, -1430, 7811),
+ new Location(42091, -27981, -1430, 30628),
+ new Location(47854, -24735, -1430, 14438)
+ };
+ // @formatter:off
+ private static final int[][] GENERATOR_TRIGERS =
+ {
+ {21170102, 21170103},
+ {21170104, 21170105},
+ {21170106, 21170107},
+ {21170108, 21170109}
+ };
+ protected final int _chargedValues[] = new int[] {0, 0, 0, 0};
+ private static final int[] LINDVIOR_SERVITOR = {25895, 25896, 25897, 29242, 29241, 29243};
+ // @formatter:on
+ private static final NpcStringId[] GUARD_MSG =
+ {
+ NpcStringId.ACTIVATE_THE_GENERATOR_HURRY,
+ NpcStringId.WE_WILL_HOLD_OFF_LINDVIOR_S_MINIONS,
+ };
+ private static final NpcStringId[] GUARD_MSG_1 =
+ {
+ NpcStringId.HOLD_ONTO_THE_GENERATOR_TO_ACTIVATE_THE_CHARGE_SKILL,
+ NpcStringId.THE_GENERATOR_IS_CONNECTED_TO_THE_CANNON,
+ };
+
+ public Lindvior()
+ {
+ super();
+ addAttackId(LINDVIOR_GROUND, LINDVIOR_FLY, LINDVIOR_RAID);
+ addEnterZoneId(ZONE_ID);
+ addExitZoneId(ZONE_ID);
+ addKillId(LINDVIOR_RAID, NPC_GENERATOR);
+ addSkillSeeId(NPC_GENERATOR);
+ addSpawnId(NPC_ATTACKER_GENERATORS, NPC_ATTACKER_GENERATORS_1, LYN_DRACO_ATTACKER_GENERATORS, GENERATOR_GUARD, NPC_GENERATOR);
+ addFirstTalkId(NPC_GENERATOR);
+ addSeeCreatureId(INVISIBLE);
+ _zoneLair = ZoneManager.getInstance().getZoneById(ZONE_ID, L2NoSummonFriendZone.class);
+ // Unlock
+ final StatsSet info = GrandBossManager.getInstance().getStatsSet(LINDVIOR_RAID);
+ final long time = info.getLong("respawn_time") - System.currentTimeMillis();
+ if (time > 0)
+ {
+ startQuestTimer("unlock_lindvior", time, null, null);
+ }
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ // Anti BUGGERS
+ if (!_zoneLair.isInsideZone(attacker))
+ {
+ attacker.doDie(null);
+ _log.warning(getName() + ": Character: " + attacker.getName() + " attacked: " + npc.getName() + " out of the boss zone!");
+ }
+ if (!_zoneLair.isInsideZone(npc))
+ {
+ npc.teleToLocation(CENTER_LOCATION, true);
+ _log.warning(getName() + ": Character: " + attacker.getName() + " attacked: " + npc.getName() + " wich is out of the boss zone!");
+ }
+
+ double percent = ((npc.getCurrentHp() - damage) / npc.getMaxHp()) * 100;
+ if ((percent <= 80) && (_status == 0))
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(RED_ZONE_EFFECT, true));
+ _zoneLair.getPlayersInside().stream().forEach(p ->
+ {
+ startQuestTimer("stop_red_zone", 10000, _lindvior, p);
+ p.broadcastPacket(new ExShowScreenMessage(NpcStringId.A_FEARSOME_POWER_EMANATES_FROM_LINDVIOR, ExShowScreenMessage.TOP_CENTER, 2000, true));
+ });
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_FLY, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.8);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true);
+ addSpawn(LINDVIOR_SERVITOR[0], loc, true);
+ }
+ _status = 1;
+ }
+ else if ((percent <= 75) && (_status == 1))
+ {
+ _bigVortexesTask = ThreadPoolManager.schedule(() -> spawnServitor(1, 300, _lindvior.getLocation(), NPC_ATTACKER_BIG_VORTEX), 1000);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(LINDVIOR_SERVITOR[1], loc, true);
+ }
+ _zoneLair.getPlayersInside().stream().forEach(p -> p.broadcastPacket(new ExShowScreenMessage(NpcStringId.A_GIGANTIC_WHIRLWIND_HAS_APPEARED, ExShowScreenMessage.TOP_CENTER, 2000, true)));
+ _status = 2;
+ }
+ else if ((percent <= 60) && (_status == 2))
+ {
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_GROUND, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.6);
+ spawnServitor(10, 2000, _lindvior.getLocation(), LINDVIOR_SERVITOR);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(LINDVIOR_SERVITOR[2], loc, true);
+ }
+ _skillCastTask = ThreadPoolManager.scheduleAtFixedRate(() -> _lindvior.doCast(SKILL_REFLECT.getSkill()), 5000, 80000);
+ _status = 3;
+ }
+ else if ((percent <= 40) && (_status == 3))
+ {
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_FLY, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.4);
+
+ if (SpawnTable.getInstance().getSpawns(NPC_ATTACKER_BIG_VORTEX) != null)
+ {
+ if ((_vortex != null) && (_vortex.getId() == NPC_ATTACKER_SMALL_VORTEX))
+ {
+ _vortex.getSpawn().stopRespawn();
+ _vortex.deleteMe();
+ }
+ }
+ _status = 4;
+ }
+ else if ((percent <= 35) && (_status == 4))
+ {
+ _smallVortexesTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true, 60000);
+ addSpawn(LINDVIOR_SERVITOR[3], loc, true);
+ }
+
+ }, 20000, 60000);
+ _status = 5;
+ }
+ else if ((percent <= 20) && (_status == 5))
+ {
+ if (_smallVortexesTask != null)
+ {
+ _smallVortexesTask.cancel(true);
+ _smallVortexesTask = null;
+ }
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.LINDVIOR_HAS_LANDED, 2, 5000, true));
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_RAID, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.2);
+ _bigVortexesTask = ThreadPoolManager.schedule(() -> spawnServitor(1, 300, _lindvior.getLocation(), NPC_ATTACKER_BIG_VORTEX), 1000);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true);
+ addSpawn(LINDVIOR_SERVITOR[4], loc, true);
+ addSpawn(LINDVIOR_SERVITOR[3], loc, true);
+ }
+ _collapseTask = ThreadPoolManager.schedule(Lindvior.this::Clean, 600000);
+ _status = 6;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ protected void Clean()
+ {
+ _status = 0;
+ if (_socialTask != null)
+ {
+ _socialTask.cancel(false);
+ _socialTask = null;
+ }
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(false);
+ _announceTask = null;
+ }
+ if (_announceProtect != null)
+ {
+ _announceProtect.cancel(false);
+ _announceProtect = null;
+ }
+ if (_skillCastTask != null)
+ {
+ _skillCastTask.cancel(false);
+ _skillCastTask = null;
+ }
+ if (_LynDracoTask != null)
+ {
+ _LynDracoTask.cancel(false);
+ _LynDracoTask = null;
+ }
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(false);
+ _mobsSpawnTask = null;
+ }
+ if (_collapseTask != null)
+ {
+ _collapseTask.cancel(false);
+ _collapseTask = null;
+ }
+ if (_bigVortexesTask != null)
+ {
+ _bigVortexesTask.cancel(false);
+ _bigVortexesTask = null;
+ }
+ if (_smallVortexesTask != null)
+ {
+ _smallVortexesTask.cancel(false);
+ _smallVortexesTask = null;
+ }
+ if (_lionel != null)
+ {
+ _lionel.deleteMe();
+ }
+ _zoneLair.getCharactersInside().forEach(mob ->
+ {
+ if (mob.isNpc())
+ {
+ mob.deleteMe();
+ mob.setIsDead(true);
+ }
+ });
+ }
+
+ private void Fail(boolean clean)
+ {
+ if (clean)
+ {
+ Clean();
+ }
+ _zoneLair.oustAllPlayers();
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, false));
+ cancelQuestTimers("attack_generator");
+ if (GrandBossManager.getInstance().getBossStatus(LINDVIOR_RAID) != 3)
+ {
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, ALIVE);
+ }
+ }
+
+ @Override
+ public synchronized String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
+ {
+ if ((skill.getId() == 15606) && (npc.getId() == NPC_GENERATOR))
+ {
+ synchronized (_chargedValues)
+ {
+ int index = npc.getScriptValue();
+ if (!hasFlag(_chargedMask, 1 << index))
+ {
+ _chargedValues[index] += caster.isGM() ? (30 / 4) + 2 : (1 / 4) + 2;
+ _chargedValues[index] = Math.min(_chargedValues[index], 120);
+
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 3000, player ->
+ {
+ player.sendPacket(new ExShowScreenMessage(NpcStringId.S1_HAS_CHARGED_THE_CANNON, ExShowScreenMessage.TOP_CENTER, 10000, true, caster.getName()));
+ player.sendPacket(new ExSendUIEvent(player, ExSendUIEvent.TYPE_NORNIL, _chargedValues[index], 120, NpcStringId.CHARGING));
+ });
+ if (_chargedValues[index] >= 120)
+ {
+ _chargedMask |= 1 << index;
+ _chargedValues[index] = 0;
+ }
+
+ if (hasFlag(_chargedMask, 0xf))
+ {
+ nextStage(3);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ switch (npc.getId())
+ {
+ case NPC_ATTACKER_GENERATORS:
+ case NPC_ATTACKER_GENERATORS_1:
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2GuardInstance.class, 800, cha ->
+ {
+
+ if (cha.getId() == GENERATOR_GUARD)
+ {
+ npc.reduceCurrentHp(1, cha, null);
+ cha.reduceCurrentHp(1, npc, null);
+ }
+ if (cha.getId() == NPC_GENERATOR)
+ {
+ ((L2Attackable) npc).addDamageHate(cha, 500, 98);
+ }
+ });
+ break;
+ }
+ case LYN_DRACO_ATTACKER_GENERATORS:
+ {
+ ((L2Attackable) npc).setCanStopAttackByTime(false);
+ ((L2Attackable) npc).setCanReturnToSpawnPoint(false);
+ startQuestTimer("attack_generator", 10000, npc, null, true);
+ break;
+ }
+ case GENERATOR_GUARD:
+ {
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ ((L2GuardInstance) npc).setIsInvul(true);
+ break;
+ }
+ case NPC_GENERATOR:
+ {
+ npc.disableCoreAI(true);
+ npc.setDisplayEffect(1);
+ npc.setRandomWalking(false);
+ npc.setIsInvul(true); // Can't get damage now
+ _activeMask = 0;
+ _chargedMask = 0;
+ break;
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character player, boolean isSummon)
+ {
+ setLindviorSpawnTask();
+ npc.getSpawn().stopRespawn();
+ npc.deleteMe();
+ return super.onSeeCreature(npc, player, isSummon);
+ }
+
+ private void nextStage(int _taskId)
+ {
+ switch (_taskId)
+ {
+ case 1: // Spawn Generators
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, true));
+ int i = 0;
+ L2GuardInstance guard;
+ for (Location loc : CONTROL_GENERATOR_SPAWNS)
+ {
+ guard = (L2GuardInstance) addSpawn(NPC_GENERATOR, loc, true);
+ guard.setDisplayEffect(0x01);
+ guard.setScriptValue(i++);
+ _generatorSpawn.add(guard);
+ }
+
+ L2Npc npc;
+ for (Location loc : SCHEME_GENERATOR_SPAWNS)
+ {
+ npc = addSpawn(GENERATOR_GUARD, loc, true);
+ npc.setRandomWalking(false);
+ _guardSpawn.add(npc);
+ }
+
+ _mobsSpawnTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : ATTACKER_GENERATOR_SPAWNS)
+ {
+ if (getRandom(10) <= 5)
+ {
+ _monsterSpawn.add(addSpawn(NPC_ATTACKER_GENERATORS, loc, true));
+ }
+ else
+ {
+ _monsterSpawn.add(addSpawn(NPC_ATTACKER_GENERATORS_1, loc, true));
+ }
+ }
+ }, 30000, 80000);
+
+ _dummyLindvior = addSpawn(LINDVIOR_CAMERA, 45259, -27115, -638, 41325, false, 0, false);
+ _announceTask = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(player -> player.sendPacket(new ExShowScreenMessage(NpcStringId.YOU_MUST_ACTIVATE_THE_4_GENERATORS, ExShowScreenMessage.TOP_CENTER, 7000, true))), 10000, 20000);
+ break;
+ }
+ case 2: // After activation of 4 generators, we wait to be charged
+ {
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(true);
+ _announceTask = null;
+ }
+ _generatorSpawn.forEach(npc ->
+ {
+ npc.setDisplayEffect(1);
+ npc.setIsInvul(false);
+ npc.broadcastInfo();
+ });
+
+ _zoneLair.getPlayersInside().forEach(player ->
+ {
+ player.sendPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, false));
+ cancelQuestTimers("NPC_SHOUT");
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.ALL_4_GENERATORS_MUST_BE_ACTIVATED);
+ });
+ _announceProtect = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(p -> player.sendPacket(new ExShowScreenMessage(NpcStringId.PROTECT_THE_GENERATOR, ExShowScreenMessage.TOP_CENTER, 7000, true))), 10000, 18000);
+ _zoneLair.broadcastPacket(new SpecialCamera(_dummyLindvior, 3300, 200, 20, 11000, 10500, 0, 8, 1, 0, 0));
+ _generatorSpawn.forEach(npc -> npc.sendInfo(player));
+ startQuestTimer("show_movie", 13000, null, null);
+ startQuestTimer("start_charge", 35000, null, null);
+ startQuestTimer("show_shield_animation", 2000, null, null);
+ });
+ break;
+ }
+ case 3: // After charging all the generators
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(ALL_GENERATORS_CONNECTED_EFFECT, true));
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(true);
+ _announceTask = null;
+ }
+ if (_announceProtect != null)
+ {
+ _announceProtect.cancel(false);
+ _announceProtect = null;
+ }
+ if (_skillCastTask != null)
+ {
+ _skillCastTask.cancel(true);
+ _skillCastTask = null;
+ }
+ if (_LynDracoTask != null)
+ {
+ _LynDracoTask.cancel(true);
+ _LynDracoTask = null;
+ }
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(true);
+ _mobsSpawnTask = null;
+ }
+ _monsterSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _LinDracoSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _generatorSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _guardSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ cancelQuestTimers("attack_generator");
+ _lindvior2.setIsDead(true);
+ _lindvior2.deleteMe();
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, FIGHTING);
+ _lionel = addSpawn(LIONEL_HUNTER, 42630, -48231, -792, 855, false, 0, false);
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_GROUND, CENTER_LOCATION, false, 0, true);
+ _zoneLair.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _zoneLair.getPlayersInside().forEach(_lindvior::sendInfo);
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.LINDVIOR_HAS_FALLEN_FROM_THE_SKY, ExShowScreenMessage.TOP_CENTER, 7000));
+ _mobsSpawnTask = ThreadPoolManager.scheduleAtFixedRate(() -> spawnServitor(2, 1000, _lindvior.getLocation(), LINDVIOR_SERVITOR), 60000, 180000);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ switch (event)
+ {
+ case "unlock_lindvior":
+ {
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, ALIVE);
+ break;
+ }
+ case "stage_1_activate_generator":
+ {
+ int index = npc.getScriptValue();
+ if (!hasFlag(_activeMask, 1 << index))
+ {
+ _activeMask |= 1 << index;
+ npc.setDisplayEffect(0x02);
+ sendEventTrigger(true, GENERATOR_TRIGERS[index]);
+ _zoneLair.getPlayersInside().stream().forEach(p -> p.broadcastPacket(new Earthquake(p.getX(), p.getY(), p.getZ(), 20, 10)));
+ if (hasFlag(_activeMask, 0xf))
+ {
+ nextStage(2);
+ }
+ }
+ break;
+ }
+ case "show_shield_animation": // zone brodcat shield event triger
+ {
+ _zoneLair.getPlayersInside().forEach(p ->
+ {
+ p.sendPacket(new OnEventTrigger(SECOND_STAGE_EVENT_TRIGGER, true));
+ });
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.THE_GENERATOR_IS_CONNECTED_TO_THE_CANNON);
+ });
+ break;
+ }
+ case "show_movie": // zone brodcat Lindvior scene movie
+ {
+ _zoneLair.getPlayersInside().forEach(p ->
+ {
+ playMovie(p, Movie.SC_LIND_OPENING);
+ });
+ _dummyLindvior.deleteMe();
+ _lindvior2 = addSpawn(LINDVIOR_FAKE, CENTER_LOCATION, false, 0, false);
+ _lindvior2.setTargetable(false);
+ _announceTask = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(p -> p.sendPacket(new ExShowScreenMessage(NpcStringId.CHARGE_THE_CANNON_USING_THE_GENERATOR, ExShowScreenMessage.TOP_CENTER, 7000, true))), 40000, 20000);
+ break;
+ }
+ case "start_charge":
+ {
+ _skillCastTask = ThreadPoolManager.scheduleAtFixedRate(() -> _generatorSpawn.forEach(generators ->
+ {
+ int index = generators.getScriptValue();
+ if (!generators.isCastingNow() && (generators.getEffectList().getBuffInfoBySkillId(SKILL_RECHARGE_POSIBLE.getSkillId()) == null) && !hasFlag(_chargedMask, 1 << index))
+ {
+ // TODO Need core implemented combo skill packet.
+ // On this moment player automatic charge generator if distance generator and player <= 900
+ generators.doCast(SKILL_RECHARGE_POSIBLE.getSkill());
+ L2World.getInstance().forEachVisibleObjectInRange(generators, L2PcInstance.class, 900, p ->
+ {
+ p.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+ p.setTarget(generators);
+ p.doCast(RECHARGE.getSkill());
+ });
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.setTarget(generators);
+ guard.doCast(RECHARGE.getSkill());
+ guard.setIsInvul(false);
+ if (!guard.isDead())
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, GUARD_MSG_1[getRandom(GUARD_MSG_1.length)]);
+ }
+ });
+ }
+ }), 10000, 20000);
+ _LynDracoTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : LYN_DRACO_SPAWNS)
+ {
+ _LinDracoSpawn.add(addSpawn(LYN_DRACO_ATTACKER_GENERATORS, loc, true));
+ }
+ }, 20000, 60000);
+ break;
+ }
+ case "stop_red_zone":
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(RED_ZONE_EFFECT, false));
+ break;
+ }
+ case "attack_generator":
+ {
+ if ((npc != null) && !npc.isDead())
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2GuardInstance.class, 3000, generator ->
+ {
+ if (generator.getId() == NPC_GENERATOR)
+ {
+ npc.setTarget(generator);
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, generator.getLocation());
+ if (npc.distFromMe(generator) < 500)
+ {
+ npc.reduceCurrentHp(1, generator, null);
+ generator.reduceCurrentHp(1, npc, null);
+ }
+ }
+ });
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
+ {
+ if (npc.getId() == LINDVIOR_RAID)
+ {
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.HONORABLE_WARRIORS_HAVE_DRIVEN_OFF_LINDVIOR_THE_EVIL_WIND_DRAGON, ExShowScreenMessage.TOP_CENTER, 10000, true));
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, DEAD);
+ final long respawnTime = (Config.LINDVIOR_SPAWN_INTERVAL + getRandom(-Config.LINDVIOR_SPAWN_RANDOM, Config.LINDVIOR_SPAWN_RANDOM)) * 3600000;
+ final StatsSet info = GrandBossManager.getInstance().getStatsSet(LINDVIOR_RAID);
+ info.set("respawn_time", System.currentTimeMillis() + respawnTime);
+ GrandBossManager.getInstance().setStatsSet(LINDVIOR_RAID, info);
+ startQuestTimer("unlock_lindvior", respawnTime, null, null);
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(true);
+ _mobsSpawnTask = null;
+ }
+ _zoneLair.getCharactersInside().stream().filter(L2Character::isNpc).forEach(mob -> mob.deleteMe());
+ ThreadPoolManager.schedule(() -> npc.decayMe(), 10000);
+ _zoneLair.broadcastPacket(new OnEventTrigger(SECOND_STAGE_EVENT_TRIGGER, false));
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, true));
+ _lionel.deleteMe();
+ }
+ else if (npc.getId() == NPC_GENERATOR)
+ {
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.THE_GENERATOR_HAS_BEEN_DESTROYED, ExShowScreenMessage.TOP_CENTER, 5000, true));
+ Clean();
+ _collapseTask = ThreadPoolManager.schedule(() -> Fail(false), 20000);
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onFirstTalk(L2Npc npc, L2PcInstance player)
+ {
+ if (npc.getId() == NPC_GENERATOR)
+ {
+ return npc.getDisplayEffect() == 1 ? "19477.html" : "19477-01.html";
+ }
+ return super.onFirstTalk(npc, player);
+ }
+
+ @Override
+ public String onEnterZone(L2Character character, L2ZoneType zone)
+ {
+ if (zone.getId() == ZONE_ID)
+ {
+ if (_collapseTask != null)
+ {
+ _collapseTask.cancel(true);
+ _collapseTask = null;
+ }
+ }
+ return super.onEnterZone(character, zone);
+ }
+
+ @Override
+ public String onExitZone(L2Character character, L2ZoneType zone)
+ {
+ if (zone.getId() == ZONE_ID)
+ {
+ if (zone.getPlayersInside().isEmpty())
+ {
+ _collapseTask = ThreadPoolManager.schedule(() -> Fail(true), 900000);
+ }
+ }
+ return super.onExitZone(character, zone);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("NPC_SHOUT"))
+ {
+ if ((npc != null) && !npc.isDead())
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, GUARD_MSG[getRandom(GUARD_MSG.length)]);
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ }
+ }
+ }
+
+ private void sendEventTrigger(boolean status, int... triggers)
+ {
+ IClientOutgoingPacket[] pakets = new IClientOutgoingPacket[triggers.length];
+ for (int i = 0; i < triggers.length; i++)
+ {
+ pakets[i] = new OnEventTrigger(triggers[i], status);
+ }
+ for (IClientOutgoingPacket packet : pakets)
+ {
+ _zoneLair.broadcastPacket(packet);
+ }
+ }
+
+ private void spawnServitor(int count, int radius, Location loc, int... npcIds)
+ {
+ int x = loc.getX(), y = loc.getY();
+ if (radius > 0)
+ {
+ x += Rnd.get(-radius, radius);
+ y += Rnd.get(-radius, radius);
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ _monsterSpawn.add(addSpawn(npcIds[getRandom(npcIds.length)], x, y, loc.getZ(), loc.getHeading(), true, 0, true));
+ }
+ }
+
+ private static boolean hasFlag(int val, int flag)
+ {
+ return (val & flag) == flag;
+ }
+
+ public void setLindviorSpawnTask()
+ {
+ synchronized (this)
+ {
+ if (_socialTask == null)
+ {
+ _socialTask = ThreadPoolManager.schedule(() -> nextStage(1), 3000);
+ }
+ }
+ }
+
+ @RegisterEvent(EventType.ON_CREATURE_DAMAGE_RECEIVED)
+ @RegisterType(ListenerRegisterType.NPC)
+ @Id(LINDVIOR_FLY)
+ @Id(LINDVIOR_RAID)
+ @Id(LINDVIOR_GROUND)
+ public void onCreatureDamageReceived(OnCreatureDamageReceived event)
+ {
+ _zoneLair.getPlayersInside().stream().forEach(p ->
+ {
+ switch (_status)
+ {
+ case 0:
+ case 1:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 1, -1, 0.015, true);
+ break;
+ }
+ case 2:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 2, -1, 0.015, true);
+ break;
+ }
+ case 3:
+ case 4:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 3, -1, 0.015, true);
+ break;
+ }
+ case 5:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 4, -1, 0.015, true);
+ break;
+ }
+ }
+ });
+ }
+
+ public static void main(String[] args)
+ {
+ new Lindvior();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java
new file mode 100644
index 0000000000..d60c088f9f
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java
@@ -0,0 +1,175 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.holders.SkillHolder;
+
+import ai.AbstractNpcAI;
+
+/**
+ * LindviorBoss AI
+ * @author Gigi
+ * @date 2017-08-02 - [11:05:21]
+ */
+public class LindviorBoss extends AbstractNpcAI
+{
+ // Boss
+ private static final int LINDVIOR_GROUND = 25899;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int LINDVIOR_FLY = 19424;
+ // Skills
+ private static final SkillHolder SKILL_FLY_UP = new SkillHolder(15278, 1);
+ private static final SkillHolder SKILL_RABIES = new SkillHolder(15269, 1);
+ private static final SkillHolder SKILL_FLY = new SkillHolder(15279, 1);
+ private static final SkillHolder MASS_HELL_BINDING = new SkillHolder(11052, 6);
+ private static final SkillHolder MIGHTY_WIND_STRIKE = new SkillHolder(15274, 1);
+ private static final SkillHolder WIND_PULL = new SkillHolder(15591, 1);
+ private static final SkillHolder LINDVIORS_JUMP = new SkillHolder(15430, 1);
+ private static final SkillHolder BODY_SLAM = new SkillHolder(15271, 1);
+ private static final SkillHolder SOAR = new SkillHolder(15279, 1);
+ private static final SkillHolder WIND_BREAT = new SkillHolder(15272, 1);
+ private static final SkillHolder TAIL_SWIPE = new SkillHolder(15273, 1);
+ private static final SkillHolder TORNADO = new SkillHolder(15275, 1);
+ private static final SkillHolder LINDVIORS_ATTACK = new SkillHolder(15600, 1);
+ // Chances
+ private final static int CHANCE_MIGHTY_WIND_STRIKE = 9;
+ private final static int CHANCE_WIND_PULL = 4;
+ private final static int CHANCE_LINDVIORS_JUMP = 7;
+ private final static int CHANCE_BODY_SLAM = 2;
+ private final static int CHANCE_SOAR = 8;
+ private final static int CHANCE_WIND_BREAT = 3;
+ private final static int CHANCE_TAIL_SWIPE = 5;
+ private final static int CHANCE_TORNADO = 6;
+ private final static int CHANCE_LINDVIORS_ATTACK = 1;
+
+ public LindviorBoss()
+ {
+ super();
+ addAttackId(LINDVIOR_GROUND, LINDVIOR_FLY, LINDVIOR_RAID);
+ addSpawnId(LINDVIOR_FLY);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ double percent = ((npc.getCurrentHp() - damage) / npc.getMaxHp()) * 100;
+ final int chance = getRandom(100);
+ switch (npc.getId())
+ {
+ case LINDVIOR_GROUND:
+ {
+ if ((percent <= 80) && npc.isScriptValue(0))
+ {
+
+ npc.doCast(SKILL_FLY_UP.getSkill());
+ npc.doCast(SKILL_RABIES.getSkill());
+ npc.setScriptValue(1);
+ }
+ else if ((percent <= 40) && (npc.isScriptValue(1)))
+ {
+ npc.doCast(SKILL_FLY.getSkill());
+ npc.setScriptValue(2);
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_MIGHTY_WIND_STRIKE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(MIGHTY_WIND_STRIKE.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_PULL))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_PULL.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_LINDVIORS_JUMP))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(LINDVIORS_JUMP.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_BODY_SLAM))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(BODY_SLAM.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TAIL_SWIPE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TAIL_SWIPE.getSkill());
+ }
+ break;
+ }
+ case LINDVIOR_FLY:
+ {
+ if (!npc.isCastingNow() && (chance <= CHANCE_SOAR))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(SOAR.getSkill());
+ }
+ break;
+ }
+ case LINDVIOR_RAID:
+ {
+ if ((percent <= 20) && (npc.isScriptValue(0)))
+ {
+ npc.doCast(SKILL_FLY.getSkill());
+ npc.setScriptValue(1);
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_BREAT))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_BREAT.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_PULL))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_PULL.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TAIL_SWIPE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TAIL_SWIPE.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TORNADO))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TORNADO.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_LINDVIORS_ATTACK))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(LINDVIORS_ATTACK.getSkill());
+ }
+ break;
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(true);
+ npc.doCast(MASS_HELL_BINDING.getSkill());
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new LindviorBoss();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java
new file mode 100644
index 0000000000..846ada3442
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java
@@ -0,0 +1,67 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import com.l2jmobius.gameserver.enums.ChatType;
+import com.l2jmobius.gameserver.instancemanager.WalkingManager;
+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.network.NpcStringId;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Lionel Hunter AI
+ * @author Gigi
+ * @date 2017-07-23 - [22:54:59]
+ */
+public class LionelHunter extends AbstractNpcAI
+{
+ // Npc
+ private static final int LIONEL_HUNTER = 33886;
+ // Misc
+ private static final String ROUTE_NAME = "Rune_Lionel";
+
+ public LionelHunter()
+ {
+ addSpawnId(LIONEL_HUNTER);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("NPC_SHOUT") && (npc != null))
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.WE_JUST_LOCATED_LINDVIOR_THOSE_WHO_ARE_WILLING_TO_FIGHT_CAN_DO_SO_AT_ANY_TIME_NOW);
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ }
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ WalkingManager.getInstance().startMoving(npc, ROUTE_NAME);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new LionelHunter();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java
new file mode 100644
index 0000000000..d9bf024d24
--- /dev/null
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java
@@ -0,0 +1,176 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import java.util.Collection;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.L2World;
+import com.l2jmobius.gameserver.model.Location;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.network.serverpackets.FlyToLocation;
+import com.l2jmobius.gameserver.network.serverpackets.ValidateLocation;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Vortex AI
+ * @author Gigi
+ * @date 2017-07-23 - [10:32:50]
+ */
+public class Vortex extends AbstractNpcAI
+{
+ private static final int SMALL_VORTEX = 25898;
+ private static final int BIG_VORTEX = 19427;
+
+ public Vortex()
+ {
+ super();
+ addSeeCreatureId(SMALL_VORTEX, BIG_VORTEX);
+ addSpawnId(SMALL_VORTEX, BIG_VORTEX);
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ switch (event)
+ {
+ case "rnd_small":
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 250, attackers ->
+ {
+ if ((attackers != null) && !attackers.isDead() && !attackers.isAlikeDead())
+ {
+ attackers.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+ final int radians = (int) Math.toRadians(npc.calculateDirectionTo(attackers));
+ final int x = (int) (attackers.getX() + (600 * Math.cos(radians)));
+ final int y = (int) (attackers.getY() + (600 * Math.sin(radians)));
+ final int z = attackers.getZ();
+ final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld());
+ attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800));
+ attackers.setXYZ(loc);
+ attackers.broadcastPacket(new ValidateLocation(attackers));
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, player);
+ startQuestTimer("stop_knock_down", 5000, npc, attackers);
+ startQuestTimer("despawn_small", 5000, npc, null);
+ }
+ });
+ break;
+ }
+ case "rnd_big":
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 500, attackers ->
+ {
+ if ((attackers != null) && !attackers.isDead() && !attackers.isAlikeDead())
+ {
+ attackers.setCurrentHp(1.0);
+ attackers.setCurrentMp(1.0);
+ attackers.setCurrentCp(1.0);
+ startQuestTimer("despawn_big", 600000, npc, null);
+ }
+ });
+ break;
+ }
+ case "despawn_small":
+ {
+ if (npc != null)
+ {
+ cancelQuestTimers("rnd_small");
+ npc.getSpawn().stopRespawn();
+ npc.doDie(null);
+ }
+ break;
+ }
+ case "despawn_big":
+ {
+ if (npc != null)
+ {
+ cancelQuestTimers("despawn_big");
+ npc.getSpawn().stopRespawn();
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character player, boolean isSummon)
+ {
+ switch (npc.getId())
+ {
+ case SMALL_VORTEX:
+ {
+ startQuestTimer("rnd_small", 5000, npc, null, true);
+ break;
+ }
+ case BIG_VORTEX:
+ {
+ startQuestTimer("rnd_big", 10000, npc, null, true);
+ break;
+ }
+ }
+ return super.onSeeCreature(npc, player, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ switch (npc.getId())
+ {
+ case SMALL_VORTEX:
+ {
+ attackRandomTarget(npc);
+ npc.setRandomWalking(true);
+ npc.setIsRunning(true);
+ break;
+ }
+ case BIG_VORTEX:
+ {
+ attackRandomTarget(npc);
+ npc.setRandomWalking(true);
+ npc.setIsRunning(true);
+ break;
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ private void attackRandomTarget(L2Npc npc)
+ {
+ final Collection players = L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class);
+ {
+ if ((players == null) || players.isEmpty())
+ {
+ return;
+ }
+ if (players.size() > 0)
+ {
+ addAttackPlayerDesire(npc, players.stream().findAny().get());
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new Vortex();
+ }
+}
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/08500-08599.xml b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/08500-08599.xml
index 75fd3434dd..5799fc23b8 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/08500-08599.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/08500-08599.xml
@@ -1954,7 +1954,7 @@
-
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19400-19499.xml b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19400-19499.xml
index c55d343d88..ae0149fcf5 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19400-19499.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/19400-19499.xml
@@ -825,58 +825,76 @@
-
+ DRAGONFEMALE
-
-
-
+
+
-
+
-
-
+
+
-
+
+
-
-
+
-
-
+
+
+
+
+
+
+
+
+
+
-
+ DRAGONFEMALE
-
-
-
+
+
-
+
-
-
+
+
-
+
+
-
-
+
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -984,7 +1002,7 @@
-
+
@@ -2357,7 +2375,7 @@
-
+ ETCFEMALE
@@ -2373,7 +2391,10 @@
-
+
+
+
+
@@ -2401,7 +2422,7 @@
-
+ ETCFEMALE
@@ -2412,7 +2433,7 @@
-
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/25800-25899.xml b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/25800-25899.xml
index 699514cf2e..48950deb04 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/25800-25899.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/25800-25899.xml
@@ -4800,7 +4800,7 @@
-
+ DRAGONFEMALE
@@ -4810,8 +4810,8 @@
-
-
+
+
@@ -4831,7 +4831,7 @@
-
+ DRAGONFEMALE
@@ -4862,7 +4862,7 @@
-
+ DRAGONFEMALE
@@ -4886,6 +4886,12 @@
+
+
+
+
+
+
@@ -4893,15 +4899,18 @@
-
+
+
+
+ DRAGONFEMALE
-
-
+
+
@@ -4919,29 +4928,29 @@
-
+ true
+
-
-
+ DRAGONFEMALE
-
-
+
+
+
-
-
+
+
-
-
+
+
-
@@ -4950,11 +4959,16 @@
+
+
+
+
+
-
-
+
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/29200-29299.xml b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/29200-29299.xml
index da4bfc1ef0..9c4612664c 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/29200-29299.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/stats/npcs/29200-29299.xml
@@ -2046,13 +2046,13 @@
-
-
+ DRAGONFEMALE
+
-
+
@@ -2066,91 +2066,123 @@
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
-
-
-
-
-
-
+
+
+
+
+
+
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
-
+
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
-
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
+
@@ -2163,7 +2195,7 @@
-
+ DRAGONFEMALE
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/stats/skills/15600-15699.xml b/L2J_Mobius_2.5_Underground/dist/game/data/stats/skills/15600-15699.xml
index 2e0705da15..c7d1a933ea 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/stats/skills/15600-15699.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/stats/skills/15600-15699.xml
@@ -49,23 +49,38 @@
21800
-
-
+ icon.skill0005
+ 20A2
+ 1000
+ 10001
- 25000
+ 10000
+ SELF
+ RANGE
+ NOT_FRIEND
- icon.skill1449
+ icon.skill14444A1
+ 190010000-2
- 200
+ 200
+ TARGET
+ SINGLE
+
+
+
+ 19477
+
+
+
diff --git a/L2J_Mobius_2.5_Underground/dist/game/data/zones/no_summon_friend.xml b/L2J_Mobius_2.5_Underground/dist/game/data/zones/no_summon_friend.xml
index a6b09689a0..7b5203b963 100644
--- a/L2J_Mobius_2.5_Underground/dist/game/data/zones/no_summon_friend.xml
+++ b/L2J_Mobius_2.5_Underground/dist/game/data/zones/no_summon_friend.xml
@@ -167,4 +167,18 @@
+
+
+
+
+
+
+
+
+
+
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/Config.java b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/Config.java
index b0a99b5244..3bd48421fc 100644
--- a/L2J_Mobius_2.5_Underground/java/com/l2jmobius/Config.java
+++ b/L2J_Mobius_2.5_Underground/java/com/l2jmobius/Config.java
@@ -893,6 +893,13 @@ public final class Config
public static int KELBIM_SPAWN_INTERVAL;
public static int KELBIM_SPAWN_RANDOM;
+ // Lindvior
+ public static int LINDVIOR_SPAWN_INTERVAL;
+ public static int LINDVIOR_SPAWN_RANDOM;
+ public static int LINDVIOR_MIN_PLAYERS;
+ public static int LINDVIOR_MAX_PLAYERS;
+ public static int LINDVIOR_MIN_PLAYER_LVL;
+
// Anakim
public static int ANAKIM_MIN_PLAYERS;
public static int ANAKIM_MAX_PLAYERS;
@@ -2227,6 +2234,11 @@ public final class Config
LILITH_SPAWN_RANDOM = GrandBossSettings.getInt("RandomOfLilithSpawn", 148);
LILITH_MIN_PLAYER_LVL = GrandBossSettings.getInt("LilithMinPlayerLvl", 85);
LILITH_MAX_PLAYER_LVL = GrandBossSettings.getInt("LilithMaxPlayerLvl", 89);
+ LINDVIOR_SPAWN_INTERVAL = GrandBossSettings.getInt("IntervalOfLindviorSpawn", 264);
+ LINDVIOR_SPAWN_RANDOM = GrandBossSettings.getInt("RandomOfLindviorSpawn", 72);
+ LINDVIOR_MIN_PLAYERS = GrandBossSettings.getInt("LindviorMinPlayers", 49);
+ LINDVIOR_MAX_PLAYERS = GrandBossSettings.getInt("LindviorMaxPlayers", 112);
+ LINDVIOR_MIN_PLAYER_LVL = GrandBossSettings.getInt("LindviorMinPlayerLvl", 99);
// Gracia Seeds
final PropertiesParser GraciaSeedsSettings = new PropertiesParser(GRACIASEEDS_CONFIG_FILE);
diff --git a/L2J_Mobius_2.5_Underground/readme.txt b/L2J_Mobius_2.5_Underground/readme.txt
index d1719fb4a5..38ebed6388 100644
--- a/L2J_Mobius_2.5_Underground/readme.txt
+++ b/L2J_Mobius_2.5_Underground/readme.txt
@@ -19,7 +19,6 @@ What is done
-Addition of many retail AIs
TODO list
--Lindvior
-Trasken
-Underground five man dungeons
-Kamaloka
diff --git a/L2J_Mobius_3.0_Helios/dist/db_installer/sql/game/grandboss_data.sql b/L2J_Mobius_3.0_Helios/dist/db_installer/sql/game/grandboss_data.sql
index 27ff367d22..68a2deaa1e 100644
--- a/L2J_Mobius_3.0_Helios/dist/db_installer/sql/game/grandboss_data.sql
+++ b/L2J_Mobius_3.0_Helios/dist/db_installer/sql/game/grandboss_data.sql
@@ -17,9 +17,10 @@ INSERT IGNORE INTO `grandboss_data` (`boss_id`,`loc_x`,`loc_y`,`loc_z`,`heading`
(29006, 17726, 108915, -6480, 0, 622493.58388, 3793.536), -- Core
(29014, 55024, 17368, -5412, 10126, 622493.58388, 3793.536), -- Orfen
(29020, 116033, 17447, 10107, -25348, 4068372, 39960), -- Baium
-(29028, -105200, -253104, -15264, 0, 62041918, 2248572), -- Valakas
(29068, 185708, 114298, -8221,32768, 62802301, 1998000), -- Antharas
+(29028, -105200, -253104, -15264, 0, 62041918, 2248572), -- Valakas
+(29240, 0, 0, 0, 0, 288282589, 47100), -- Lindvior
(29118, 0, 0, 0, 0, 4109288, 1220547), -- Beleth
(25286, 185080, -12613, -5499, 16550, 556345880, 86847), -- Anakim
(25283, 185062, -9605, -5499, 15640, 486021997, 79600), -- Lilith
-(26124, 0, 0, 0, 0, 13945521, 50920); -- Kelbim
\ No newline at end of file
+(26124, 0, 0, 0, 0, 13945521, 50920); -- Kelbim
diff --git a/L2J_Mobius_3.0_Helios/dist/game/config/GrandBoss.ini b/L2J_Mobius_3.0_Helios/dist/game/config/GrandBoss.ini
index 21b6037556..3689173278 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/config/GrandBoss.ini
+++ b/L2J_Mobius_3.0_Helios/dist/game/config/GrandBoss.ini
@@ -144,4 +144,23 @@ LilithMaxPlayers = 120
LilithMinPlayerLvl = 85
# Maximum players Level for enter to Lilith. Retail: 89
-LilithMaxPlayerLvl = 89
\ No newline at end of file
+LilithMaxPlayerLvl = 89
+
+# ---------------------------------------------------------------------------
+# Lindvior
+# ---------------------------------------------------------------------------
+
+# Interval time of Lindvior. Value is hour. Range 1-480. Retail: 264
+IntervalOfLindviorSpawn = 264
+
+# Random interval. Range 1-192. Retail: 72
+RandomOfLindviorSpawn = 72
+
+# Minimal count of players for enter to Lindvior. Retail: 49
+LindviorMinPlayers = 49
+
+# Maximum count of players for enter to Lindvior. Retail: 112
+LindviorMaxPlayers = 112
+
+# Minimum players Level for enter to Lindvior. Retail: 99
+LindviorMinPlayerLvl = 99
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml b/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml
index 5cbbf5261f..dc0e5372c2 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/Routes.xml
@@ -2352,4 +2352,8 @@
+
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java
index 790799b0ad..b1a37e194f 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/areas/Rune/AltarOfSacrifice/AltarOfSacrifice.java
@@ -75,7 +75,7 @@ public class AltarOfSacrifice extends AbstractNpcAI
@Override
public String onSeeCreature(L2Npc npc, L2Character creature, boolean isSummon)
{
- if (creature.isPlayer() && _jenas_guard.isScriptValue(0))
+ if ((creature != null) && creature.isPlayer() && _jenas_guard.isScriptValue(0))
{
startQuestTimer("msg_text", 3000, npc, null);
_jenas_guard.setScriptValue(1);
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/19477-01.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/19477-01.html
new file mode 100644
index 0000000000..5f9fc4431e
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/19477-01.html
@@ -0,0 +1,3 @@
+Generator:
+The generator is now supplying power to the sealed structure.
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/19477.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/19477.html
new file mode 100644
index 0000000000..540ff6ac54
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/19477.html
@@ -0,0 +1,3 @@
+Generator:
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html
new file mode 100644
index 0000000000..ba01eab970
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-1.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+On the Lindvior there is an attack!
+At this time it's impossible to enter the altar.
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html
new file mode 100644
index 0000000000..e3eac44ec7
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-2.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+Lindvior? You're too late, friend.
+A group of warriors drove him off not long ago.
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html
new file mode 100644
index 0000000000..40de9b961e
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-3.html
@@ -0,0 +1,4 @@
+Rune Castle Patrol Kato:
+You are overcome by a voice, a voice so powerful you are helpless as it speaks:
+(The players who belong to an association can only enter through the Association Leader.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html
new file mode 100644
index 0000000000..9af5ff8c7a
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-4.html
@@ -0,0 +1,3 @@
+Rune Castle Patrol Kato:
+(A command channel needs at least minimu: %min% and maximum: %max% members to challenge Lindvior.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html
new file mode 100644
index 0000000000..309c938f02
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881-5.html
@@ -0,0 +1,3 @@
+Rune Castle Patrol Kato:
+(A command channel members level must be minimum: %minlvl% Lvl or higher to challenge Lindvior.)
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html
new file mode 100644
index 0000000000..bddff0b348
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/33881.html
@@ -0,0 +1,6 @@
+Rune Castle Patrol Kato:
+Dire times dictated that I come myself from Rune Castle to help those in need here.
+What do you need?
+
+
+
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java
new file mode 100644
index 0000000000..3b54f9dfa8
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/KatoSicanus/KatoSicanus.java
@@ -0,0 +1,129 @@
+/*
+ * 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 ai.bosses.Lindvior.KatoSicanus;
+
+import java.util.List;
+
+import com.l2jmobius.Config;
+import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
+import com.l2jmobius.gameserver.model.L2Party;
+import com.l2jmobius.gameserver.model.Location;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.network.serverpackets.NpcHtmlMessage;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Kato Sicanus Teleporter AI
+ * @author Gigi
+ * @date 2017-07-13 - [22:17:16]
+ */
+public class KatoSicanus extends AbstractNpcAI
+{
+ // NPCs
+ private static final int KATO_SICANUS = 33881;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int INVISIBLE = 8572;
+ // Location
+ private static final Location LINDVIOR_LOCATION = new Location(46929, -28807, -1400);
+
+ public KatoSicanus()
+ {
+ addFirstTalkId(KATO_SICANUS);
+ addTalkId(KATO_SICANUS);
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("teleport"))
+ {
+ final int status = GrandBossManager.getInstance().getBossStatus(LINDVIOR_RAID);
+ if (player.isGM())
+ {
+ player.teleToLocation(LINDVIOR_LOCATION, true);
+ addSpawn(INVISIBLE, 46707, -28586, -1400, 0, false, 60000, false);
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, 1);
+ }
+ else
+ {
+ if (status == 2)
+ {
+ return "33881-1.html";
+ }
+ if (status == 3)
+ {
+ return "33881-2.html";
+ }
+ if (!player.isInParty())
+ {
+ return "33881-3.html";
+ }
+ final L2Party party = player.getParty();
+ final boolean isInCC = party.isInCommandChannel();
+ final List members = (isInCC) ? party.getCommandChannel().getMembers() : party.getMembers();
+ final boolean isPartyLeader = (isInCC) ? party.getCommandChannel().isLeader(player) : party.isLeader(player);
+ if (!isPartyLeader)
+ {
+ return "33881-3.html";
+ }
+ if ((members.size() < Config.LINDVIOR_MIN_PLAYERS) || (members.size() > Config.LINDVIOR_MAX_PLAYERS))
+ {
+ final NpcHtmlMessage packet = new NpcHtmlMessage(npc.getObjectId());
+ packet.setHtml(getHtm(player.getHtmlPrefix(), "33881-4.html"));
+ packet.replace("%min%", Integer.toString(Config.LINDVIOR_MIN_PLAYERS));
+ packet.replace("%max%", Integer.toString(Config.LINDVIOR_MAX_PLAYERS));
+ player.sendPacket(packet);
+ return null;
+ }
+ for (L2PcInstance member : members)
+ {
+ if (member.getLevel() < Config.LINDVIOR_MIN_PLAYER_LVL)
+ {
+ final NpcHtmlMessage packet = new NpcHtmlMessage(npc.getObjectId());
+ packet.setHtml(getHtm(player.getHtmlPrefix(), "33881-5.html"));
+ packet.replace("%minlvl%", Integer.toString(Config.LINDVIOR_MIN_PLAYER_LVL));
+ player.sendPacket(packet);
+ return null;
+ }
+ }
+ for (L2PcInstance member : members)
+ {
+ if (member.isInsideRadius(npc, 1500, true, false))
+ {
+ member.teleToLocation(LINDVIOR_LOCATION, true);
+ addSpawn(INVISIBLE, 46707, -28586, -1400, 0, false, 0, false);
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, 1);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onFirstTalk(L2Npc npc, L2PcInstance player)
+ {
+ return "33881.html";
+ }
+
+ public static void main(String[] args)
+ {
+ new KatoSicanus();
+ }
+}
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java
new file mode 100644
index 0000000000..d6247a96c6
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Lindvior.java
@@ -0,0 +1,976 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import java.util.ArrayList;
+import java.util.List;
+import java.util.concurrent.ScheduledFuture;
+
+import com.l2jmobius.Config;
+import com.l2jmobius.commons.util.Rnd;
+import com.l2jmobius.gameserver.ThreadPoolManager;
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.datatables.SpawnTable;
+import com.l2jmobius.gameserver.enums.ChatType;
+import com.l2jmobius.gameserver.enums.Movie;
+import com.l2jmobius.gameserver.instancemanager.GrandBossManager;
+import com.l2jmobius.gameserver.instancemanager.ZoneManager;
+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.L2Attackable;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2GrandBossInstance;
+import com.l2jmobius.gameserver.model.actor.instance.L2GuardInstance;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.events.EventType;
+import com.l2jmobius.gameserver.model.events.ListenerRegisterType;
+import com.l2jmobius.gameserver.model.events.annotations.Id;
+import com.l2jmobius.gameserver.model.events.annotations.RegisterEvent;
+import com.l2jmobius.gameserver.model.events.annotations.RegisterType;
+import com.l2jmobius.gameserver.model.events.impl.character.OnCreatureDamageReceived;
+import com.l2jmobius.gameserver.model.holders.SkillHolder;
+import com.l2jmobius.gameserver.model.skills.Skill;
+import com.l2jmobius.gameserver.model.zone.L2ZoneType;
+import com.l2jmobius.gameserver.model.zone.type.L2NoSummonFriendZone;
+import com.l2jmobius.gameserver.network.NpcStringId;
+import com.l2jmobius.gameserver.network.serverpackets.Earthquake;
+import com.l2jmobius.gameserver.network.serverpackets.ExSendUIEvent;
+import com.l2jmobius.gameserver.network.serverpackets.ExShowScreenMessage;
+import com.l2jmobius.gameserver.network.serverpackets.IClientOutgoingPacket;
+import com.l2jmobius.gameserver.network.serverpackets.OnEventTrigger;
+import com.l2jmobius.gameserver.network.serverpackets.SocialAction;
+import com.l2jmobius.gameserver.network.serverpackets.SpecialCamera;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Lindvior Boss
+ * @author Gigi
+ * @date 2017-07-11 - [17:42:53]
+ *
+ * @VIDEO - https://www.youtube.com/watch?v=VknjOjRO9Cw
+ */
+public class Lindvior extends AbstractNpcAI
+{
+ // Monsters
+ private static final int LINDVIOR_FAKE = 19423;
+ private static final int LINDVIOR_GROUND = 25899;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int LINDVIOR_FLY = 19424;
+ private static final int NPC_GENERATOR = 19477;
+ private static final int GENERATOR_GUARD = 19479;
+ private static final int NPC_ATTACKER_GENERATORS = 25897;
+ private static final int NPC_ATTACKER_GENERATORS_1 = 25895;
+ private static final int LYN_DRACO_ATTACKER_GENERATORS = 29241;
+ private static final int NPC_ATTACKER_SMALL_VORTEX = 25898;
+ private static final int NPC_ATTACKER_BIG_VORTEX = 19427;
+ private static final int INVISIBLE = 8572;
+ private static final int LIONEL_HUNTER = 33886;
+ private static final int LINDVIOR_CAMERA = 19428;
+ // Zone
+ private final static int ZONE_ID = 12107;
+ static final Location CENTER_LOCATION = new Location(46424, -26200, -1400);
+ // Skills
+ private static final SkillHolder SKILL_RECHARGE_POSIBLE = new SkillHolder(15605, 1);
+ private static final SkillHolder RECHARGE = new SkillHolder(15606, 1);
+ private static final SkillHolder SKILL_REFLECT = new SkillHolder(15592, 1);
+ // Item
+ private static final int LINDVIORS_SCALE = 37495;
+ // Trigers
+ private static final int FIRST_STAGE_EVENT_TRIGGER = 21170112;
+ private static final int SECOND_STAGE_EVENT_TRIGGER = 21170100;
+ private static final int ALL_GENERATORS_CONNECTED_EFFECT = 21170110;
+ private static final int RED_ZONE_EFFECT = 21170120;
+ // Status
+ private static final int ALIVE = 0;
+ private static final int FIGHTING = 2;
+ private static final int DEAD = 3;
+ // Tasks
+ protected ScheduledFuture> _socialTask;
+ protected ScheduledFuture> _mobsSpawnTask;
+ protected ScheduledFuture> _collapseTask;
+ protected ScheduledFuture> _announceTask;
+ protected ScheduledFuture> _announceProtect;
+ protected ScheduledFuture> _skillCastTask;
+ protected ScheduledFuture> _LynDracoTask;
+ protected ScheduledFuture> _smallVortexesTask;
+ protected ScheduledFuture> _bigVortexesTask;
+ protected L2NoSummonFriendZone _zoneLair;
+ protected L2GrandBossInstance _lindvior = null;
+ protected L2Npc _lindvior2 = null;
+ protected L2Npc _dummyLindvior;
+ protected L2Npc _vortex = null;
+ protected L2Npc _lionel = null;
+ protected List _guardSpawn = new ArrayList<>();
+ protected List _generatorSpawn = new ArrayList<>();
+ protected List _monsterSpawn = new ArrayList<>();
+ protected List _LinDracoSpawn = new ArrayList<>();
+ protected int _activeMask = 0;
+ protected int _chargedMask = 0;
+ protected int _status = 0;
+
+ private static final Location[] CONTROL_GENERATOR_SPAWNS =
+ {
+ new Location(45288, -30360, -1432, 0),
+ new Location(48486, -27175, -1432, 0),
+ new Location(45272, -23976, -1432, 0),
+ new Location(42088, -27160, -1432, 0)
+ };
+ private static final Location[] SCHEME_GENERATOR_SPAWNS =
+ {
+ new Location(48440, -26824, -1438, 0),
+ new Location(48392, -27448, -1438, 0),
+ new Location(42136, -27480, -1438, 0),
+ new Location(42136, -26840, -1438, 0),
+ new Location(44936, -24024, -1438, 0),
+ new Location(45592, -24008, -1438, 0),
+ new Location(45608, -30312, -1438, 0),
+ new Location(44984, -30360, -1438, 0)
+ };
+ private static final Location[] ATTACKER_GENERATOR_SPAWNS =
+ {
+ new Location(44863, -24272, -1413, 33713),
+ new Location(45675, -24272, -1413, 33713),
+ new Location(45675, -30057, -1413, 64987),
+ new Location(44863, -30057, -1413, 64987),
+ new Location(42350, -27563, -1413, 46871),
+ new Location(42350, -26809, -1413, 46871),
+ new Location(48220, -26809, -1413, 16383),
+ new Location(48220, -27563, -1413, 16383)
+ };
+ private static final Location[] LYN_DRACO_SPAWNS =
+ {
+ new Location(45300, -28402, -1400, 48845),
+ new Location(46379, -27178, -1400, 1154),
+ new Location(45292, -26043, -1400, 13027),
+ new Location(44215, -27172, -1400, 33966)
+ };
+ private static final Location[] ATTACKER_SMALL_VORTEX_SPAWNS = new Location[]
+ {
+ new Location(46256, -30159, -1430, 57430),
+ new Location(45155, -29987, -1430, 14860),
+ new Location(46219, -27704, -1430, 1744),
+ new Location(46135, -28995, -1430, 43626),
+ new Location(43973, -28265, -1430, 16516),
+ new Location(46782, -29065, -1430, 63368),
+ new Location(47214, -29836, -1430, 46966),
+ new Location(44754, -29120, -1430, 56118),
+ new Location(47089, -28198, -1430, 8537),
+ new Location(44992, -28152, -1430, 11592),
+ new Location(44737, -24885, -1430, 3146),
+ new Location(46096, -24976, -1430, 49650),
+ new Location(46972, -25911, -1430, 62925),
+ new Location(46977, -27136, -1430, 2150),
+ new Location(42889, -24767, -1430, 10246),
+ new Location(47299, -25256, -1430, 1453),
+ new Location(44204, -25026, -1430, 39225),
+ new Location(42875, -28035, -1430, 34755),
+ new Location(41963, -26031, -1430, 18822),
+ new Location(43171, -25942, -1430, 44279),
+ new Location(41874, -27174, -1430, 56030),
+ new Location(44983, -26082, -1430, 7042),
+ new Location(46145, -26804, -1430, 24394),
+ new Location(46148, -26019, -1430, 34151),
+ new Location(45161, -24275, -1430, 39262),
+ new Location(47288, -24141, -1430, 21644),
+ new Location(43722, -26174, -1430, 11001),
+ new Location(44942, -27169, -1430, 39703),
+ new Location(46105, -24170, -1430, 28224),
+ new Location(49084, -27206, -1430, 41996),
+ new Location(48159, -27091, -1430, 62682),
+ new Location(48094, -28789, -1430, 49189),
+ new Location(48958, -27844, -1430, 59758),
+ new Location(43828, -23981, -1430, 10994),
+ new Location(48165, -25777, -1430, 53084),
+ new Location(48267, -28086, -1430, 9266),
+ new Location(43268, -28981, -1430, 23736),
+ new Location(44155, -29821, -1430, 39281),
+ new Location(43991, -29275, -1430, 27277),
+ new Location(44057, -27133, -1430, 64484),
+ new Location(43257, -26764, -1430, 14161),
+ new Location(42300, -25194, -1430, 7811),
+ new Location(42091, -27981, -1430, 30628),
+ new Location(47854, -24735, -1430, 14438)
+ };
+ // @formatter:off
+ private static final int[][] GENERATOR_TRIGERS =
+ {
+ {21170102, 21170103},
+ {21170104, 21170105},
+ {21170106, 21170107},
+ {21170108, 21170109}
+ };
+ protected final int _chargedValues[] = new int[] {0, 0, 0, 0};
+ private static final int[] LINDVIOR_SERVITOR = {25895, 25896, 25897, 29242, 29241, 29243};
+ // @formatter:on
+ private static final NpcStringId[] GUARD_MSG =
+ {
+ NpcStringId.ACTIVATE_THE_GENERATOR_HURRY,
+ NpcStringId.WE_WILL_HOLD_OFF_LINDVIOR_S_MINIONS,
+ };
+ private static final NpcStringId[] GUARD_MSG_1 =
+ {
+ NpcStringId.HOLD_ONTO_THE_GENERATOR_TO_ACTIVATE_THE_CHARGE_SKILL,
+ NpcStringId.THE_GENERATOR_IS_CONNECTED_TO_THE_CANNON,
+ };
+
+ public Lindvior()
+ {
+ super();
+ addAttackId(LINDVIOR_GROUND, LINDVIOR_FLY, LINDVIOR_RAID);
+ addEnterZoneId(ZONE_ID);
+ addExitZoneId(ZONE_ID);
+ addKillId(LINDVIOR_RAID, NPC_GENERATOR);
+ addSkillSeeId(NPC_GENERATOR);
+ addSpawnId(NPC_ATTACKER_GENERATORS, NPC_ATTACKER_GENERATORS_1, LYN_DRACO_ATTACKER_GENERATORS, GENERATOR_GUARD, NPC_GENERATOR);
+ addFirstTalkId(NPC_GENERATOR);
+ addSeeCreatureId(INVISIBLE);
+ _zoneLair = ZoneManager.getInstance().getZoneById(ZONE_ID, L2NoSummonFriendZone.class);
+ // Unlock
+ final StatsSet info = GrandBossManager.getInstance().getStatsSet(LINDVIOR_RAID);
+ final long time = info.getLong("respawn_time") - System.currentTimeMillis();
+ if (time > 0)
+ {
+ startQuestTimer("unlock_lindvior", time, null, null);
+ }
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ // Anti BUGGERS
+ if (!_zoneLair.isInsideZone(attacker))
+ {
+ attacker.doDie(null);
+ _log.warning(getName() + ": Character: " + attacker.getName() + " attacked: " + npc.getName() + " out of the boss zone!");
+ }
+ if (!_zoneLair.isInsideZone(npc))
+ {
+ npc.teleToLocation(CENTER_LOCATION, true);
+ _log.warning(getName() + ": Character: " + attacker.getName() + " attacked: " + npc.getName() + " wich is out of the boss zone!");
+ }
+
+ double percent = ((npc.getCurrentHp() - damage) / npc.getMaxHp()) * 100;
+ if ((percent <= 80) && (_status == 0))
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(RED_ZONE_EFFECT, true));
+ _zoneLair.getPlayersInside().stream().forEach(p ->
+ {
+ startQuestTimer("stop_red_zone", 10000, _lindvior, p);
+ p.broadcastPacket(new ExShowScreenMessage(NpcStringId.A_FEARSOME_POWER_EMANATES_FROM_LINDVIOR, ExShowScreenMessage.TOP_CENTER, 2000, true));
+ });
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_FLY, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.8);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true);
+ addSpawn(LINDVIOR_SERVITOR[0], loc, true);
+ }
+ _status = 1;
+ }
+ else if ((percent <= 75) && (_status == 1))
+ {
+ _bigVortexesTask = ThreadPoolManager.schedule(() -> spawnServitor(1, 300, _lindvior.getLocation(), NPC_ATTACKER_BIG_VORTEX), 1000);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(LINDVIOR_SERVITOR[1], loc, true);
+ }
+ _zoneLair.getPlayersInside().stream().forEach(p -> p.broadcastPacket(new ExShowScreenMessage(NpcStringId.A_GIGANTIC_WHIRLWIND_HAS_APPEARED, ExShowScreenMessage.TOP_CENTER, 2000, true)));
+ _status = 2;
+ }
+ else if ((percent <= 60) && (_status == 2))
+ {
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_GROUND, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.6);
+ spawnServitor(10, 2000, _lindvior.getLocation(), LINDVIOR_SERVITOR);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(LINDVIOR_SERVITOR[2], loc, true);
+ }
+ _skillCastTask = ThreadPoolManager.scheduleAtFixedRate(() -> _lindvior.doCast(SKILL_REFLECT.getSkill()), 5000, 80000);
+ _status = 3;
+ }
+ else if ((percent <= 40) && (_status == 3))
+ {
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_FLY, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.4);
+
+ if (SpawnTable.getInstance().getSpawns(NPC_ATTACKER_BIG_VORTEX) != null)
+ {
+ if ((_vortex != null) && (_vortex.getId() == NPC_ATTACKER_SMALL_VORTEX))
+ {
+ _vortex.getSpawn().stopRespawn();
+ _vortex.deleteMe();
+ }
+ }
+ _status = 4;
+ }
+ else if ((percent <= 35) && (_status == 4))
+ {
+ _smallVortexesTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true, 60000);
+ addSpawn(LINDVIOR_SERVITOR[3], loc, true);
+ }
+
+ }, 20000, 60000);
+ _status = 5;
+ }
+ else if ((percent <= 20) && (_status == 5))
+ {
+ if (_smallVortexesTask != null)
+ {
+ _smallVortexesTask.cancel(true);
+ _smallVortexesTask = null;
+ }
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.LINDVIOR_HAS_LANDED, 2, 5000, true));
+ _lindvior.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _lindvior.setIsDead(true);
+ _lindvior.deleteMe();
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_RAID, _lindvior.getLocation(), false, 0, false);
+ _lindvior.setCurrentHp(_lindvior.getMaxHp() * 0.2);
+ _bigVortexesTask = ThreadPoolManager.schedule(() -> spawnServitor(1, 300, _lindvior.getLocation(), NPC_ATTACKER_BIG_VORTEX), 1000);
+ for (Location loc : ATTACKER_SMALL_VORTEX_SPAWNS)
+ {
+ addSpawn(NPC_ATTACKER_SMALL_VORTEX, loc, true);
+ addSpawn(LINDVIOR_SERVITOR[4], loc, true);
+ addSpawn(LINDVIOR_SERVITOR[3], loc, true);
+ }
+ _collapseTask = ThreadPoolManager.schedule(Lindvior.this::Clean, 600000);
+ _status = 6;
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ protected void Clean()
+ {
+ _status = 0;
+ if (_socialTask != null)
+ {
+ _socialTask.cancel(false);
+ _socialTask = null;
+ }
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(false);
+ _announceTask = null;
+ }
+ if (_announceProtect != null)
+ {
+ _announceProtect.cancel(false);
+ _announceProtect = null;
+ }
+ if (_skillCastTask != null)
+ {
+ _skillCastTask.cancel(false);
+ _skillCastTask = null;
+ }
+ if (_LynDracoTask != null)
+ {
+ _LynDracoTask.cancel(false);
+ _LynDracoTask = null;
+ }
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(false);
+ _mobsSpawnTask = null;
+ }
+ if (_collapseTask != null)
+ {
+ _collapseTask.cancel(false);
+ _collapseTask = null;
+ }
+ if (_bigVortexesTask != null)
+ {
+ _bigVortexesTask.cancel(false);
+ _bigVortexesTask = null;
+ }
+ if (_smallVortexesTask != null)
+ {
+ _smallVortexesTask.cancel(false);
+ _smallVortexesTask = null;
+ }
+ if (_lionel != null)
+ {
+ _lionel.deleteMe();
+ }
+ _zoneLair.getCharactersInside().forEach(mob ->
+ {
+ if (mob.isNpc())
+ {
+ mob.deleteMe();
+ mob.setIsDead(true);
+ }
+ });
+ }
+
+ private void Fail(boolean clean)
+ {
+ if (clean)
+ {
+ Clean();
+ }
+ _zoneLair.oustAllPlayers();
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, false));
+ cancelQuestTimers("attack_generator");
+ if (GrandBossManager.getInstance().getBossStatus(LINDVIOR_RAID) != 3)
+ {
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, ALIVE);
+ }
+ }
+
+ @Override
+ public synchronized String onSkillSee(L2Npc npc, L2PcInstance caster, Skill skill, L2Object[] targets, boolean isSummon)
+ {
+ if ((skill.getId() == 15606) && (npc.getId() == NPC_GENERATOR))
+ {
+ synchronized (_chargedValues)
+ {
+ int index = npc.getScriptValue();
+ if (!hasFlag(_chargedMask, 1 << index))
+ {
+ _chargedValues[index] += caster.isGM() ? (30 / 4) + 2 : (1 / 4) + 2;
+ _chargedValues[index] = Math.min(_chargedValues[index], 120);
+
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 3000, player ->
+ {
+ player.sendPacket(new ExShowScreenMessage(NpcStringId.S1_HAS_CHARGED_THE_CANNON, ExShowScreenMessage.TOP_CENTER, 10000, true, caster.getName()));
+ player.sendPacket(new ExSendUIEvent(player, ExSendUIEvent.TYPE_NORNIL, _chargedValues[index], 120, NpcStringId.CHARGING));
+ });
+ if (_chargedValues[index] >= 120)
+ {
+ _chargedMask |= 1 << index;
+ _chargedValues[index] = 0;
+ }
+
+ if (hasFlag(_chargedMask, 0xf))
+ {
+ nextStage(3);
+ }
+ }
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ switch (npc.getId())
+ {
+ case NPC_ATTACKER_GENERATORS:
+ case NPC_ATTACKER_GENERATORS_1:
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2GuardInstance.class, 800, cha ->
+ {
+
+ if (cha.getId() == GENERATOR_GUARD)
+ {
+ npc.reduceCurrentHp(1, cha, null);
+ cha.reduceCurrentHp(1, npc, null);
+ }
+ if (cha.getId() == NPC_GENERATOR)
+ {
+ ((L2Attackable) npc).addDamageHate(cha, 500, 98);
+ }
+ });
+ break;
+ }
+ case LYN_DRACO_ATTACKER_GENERATORS:
+ {
+ ((L2Attackable) npc).setCanStopAttackByTime(false);
+ ((L2Attackable) npc).setCanReturnToSpawnPoint(false);
+ startQuestTimer("attack_generator", 10000, npc, null, true);
+ break;
+ }
+ case GENERATOR_GUARD:
+ {
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ ((L2GuardInstance) npc).setIsInvul(true);
+ break;
+ }
+ case NPC_GENERATOR:
+ {
+ npc.disableCoreAI(true);
+ npc.setDisplayEffect(1);
+ npc.setRandomWalking(false);
+ npc.setIsInvul(true); // Can't get damage now
+ _activeMask = 0;
+ _chargedMask = 0;
+ break;
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character player, boolean isSummon)
+ {
+ setLindviorSpawnTask();
+ npc.getSpawn().stopRespawn();
+ npc.deleteMe();
+ return super.onSeeCreature(npc, player, isSummon);
+ }
+
+ private void nextStage(int _taskId)
+ {
+ switch (_taskId)
+ {
+ case 1: // Spawn Generators
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, true));
+ int i = 0;
+ L2GuardInstance guard;
+ for (Location loc : CONTROL_GENERATOR_SPAWNS)
+ {
+ guard = (L2GuardInstance) addSpawn(NPC_GENERATOR, loc, true);
+ guard.setDisplayEffect(0x01);
+ guard.setScriptValue(i++);
+ _generatorSpawn.add(guard);
+ }
+
+ L2Npc npc;
+ for (Location loc : SCHEME_GENERATOR_SPAWNS)
+ {
+ npc = addSpawn(GENERATOR_GUARD, loc, true);
+ npc.setRandomWalking(false);
+ _guardSpawn.add(npc);
+ }
+
+ _mobsSpawnTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : ATTACKER_GENERATOR_SPAWNS)
+ {
+ if (getRandom(10) <= 5)
+ {
+ _monsterSpawn.add(addSpawn(NPC_ATTACKER_GENERATORS, loc, true));
+ }
+ else
+ {
+ _monsterSpawn.add(addSpawn(NPC_ATTACKER_GENERATORS_1, loc, true));
+ }
+ }
+ }, 30000, 80000);
+
+ _dummyLindvior = addSpawn(LINDVIOR_CAMERA, 45259, -27115, -638, 41325, false, 0, false);
+ _announceTask = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(player -> player.sendPacket(new ExShowScreenMessage(NpcStringId.YOU_MUST_ACTIVATE_THE_4_GENERATORS, ExShowScreenMessage.TOP_CENTER, 7000, true))), 10000, 20000);
+ break;
+ }
+ case 2: // After activation of 4 generators, we wait to be charged
+ {
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(true);
+ _announceTask = null;
+ }
+ _generatorSpawn.forEach(npc ->
+ {
+ npc.setDisplayEffect(1);
+ npc.setIsInvul(false);
+ npc.broadcastInfo();
+ });
+
+ _zoneLair.getPlayersInside().forEach(player ->
+ {
+ player.sendPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, false));
+ cancelQuestTimers("NPC_SHOUT");
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.ALL_4_GENERATORS_MUST_BE_ACTIVATED);
+ });
+ _announceProtect = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(p -> player.sendPacket(new ExShowScreenMessage(NpcStringId.PROTECT_THE_GENERATOR, ExShowScreenMessage.TOP_CENTER, 7000, true))), 10000, 18000);
+ _zoneLair.broadcastPacket(new SpecialCamera(_dummyLindvior, 3300, 200, 20, 11000, 10500, 0, 8, 1, 0, 0));
+ _generatorSpawn.forEach(npc -> npc.sendInfo(player));
+ startQuestTimer("show_movie", 13000, null, null);
+ startQuestTimer("start_charge", 35000, null, null);
+ startQuestTimer("show_shield_animation", 2000, null, null);
+ });
+ break;
+ }
+ case 3: // After charging all the generators
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(ALL_GENERATORS_CONNECTED_EFFECT, true));
+ if (_announceTask != null)
+ {
+ _announceTask.cancel(true);
+ _announceTask = null;
+ }
+ if (_announceProtect != null)
+ {
+ _announceProtect.cancel(false);
+ _announceProtect = null;
+ }
+ if (_skillCastTask != null)
+ {
+ _skillCastTask.cancel(true);
+ _skillCastTask = null;
+ }
+ if (_LynDracoTask != null)
+ {
+ _LynDracoTask.cancel(true);
+ _LynDracoTask = null;
+ }
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(true);
+ _mobsSpawnTask = null;
+ }
+ _monsterSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _LinDracoSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _generatorSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ _guardSpawn.forEach(npc ->
+ {
+ if (npc != null)
+ {
+ npc.deleteMe();
+ }
+ });
+ cancelQuestTimers("attack_generator");
+ _lindvior2.setIsDead(true);
+ _lindvior2.deleteMe();
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, FIGHTING);
+ _lionel = addSpawn(LIONEL_HUNTER, 42630, -48231, -792, 855, false, 0, false);
+
+ _lindvior = (L2GrandBossInstance) addSpawn(LINDVIOR_GROUND, CENTER_LOCATION, false, 0, true);
+ _zoneLair.broadcastPacket(new SocialAction(_lindvior.getObjectId(), 1));
+ _zoneLair.getPlayersInside().forEach(_lindvior::sendInfo);
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.LINDVIOR_HAS_FALLEN_FROM_THE_SKY, ExShowScreenMessage.TOP_CENTER, 7000));
+ _mobsSpawnTask = ThreadPoolManager.scheduleAtFixedRate(() -> spawnServitor(2, 1000, _lindvior.getLocation(), LINDVIOR_SERVITOR), 60000, 180000);
+ break;
+ }
+ }
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ switch (event)
+ {
+ case "unlock_lindvior":
+ {
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, ALIVE);
+ break;
+ }
+ case "stage_1_activate_generator":
+ {
+ int index = npc.getScriptValue();
+ if (!hasFlag(_activeMask, 1 << index))
+ {
+ _activeMask |= 1 << index;
+ npc.setDisplayEffect(0x02);
+ sendEventTrigger(true, GENERATOR_TRIGERS[index]);
+ _zoneLair.getPlayersInside().stream().forEach(p -> p.broadcastPacket(new Earthquake(p.getX(), p.getY(), p.getZ(), 20, 10)));
+ if (hasFlag(_activeMask, 0xf))
+ {
+ nextStage(2);
+ }
+ }
+ break;
+ }
+ case "show_shield_animation": // zone brodcat shield event triger
+ {
+ _zoneLair.getPlayersInside().forEach(p ->
+ {
+ p.sendPacket(new OnEventTrigger(SECOND_STAGE_EVENT_TRIGGER, true));
+ });
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.THE_GENERATOR_IS_CONNECTED_TO_THE_CANNON);
+ });
+ break;
+ }
+ case "show_movie": // zone brodcat Lindvior scene movie
+ {
+ _zoneLair.getPlayersInside().forEach(p ->
+ {
+ playMovie(p, Movie.SC_LIND_OPENING);
+ });
+ _dummyLindvior.deleteMe();
+ _lindvior2 = addSpawn(LINDVIOR_FAKE, CENTER_LOCATION, false, 0, false);
+ _lindvior2.setTargetable(false);
+ _announceTask = ThreadPoolManager.scheduleAtFixedRate(() -> _zoneLair.getPlayersInside().forEach(p -> p.sendPacket(new ExShowScreenMessage(NpcStringId.CHARGE_THE_CANNON_USING_THE_GENERATOR, ExShowScreenMessage.TOP_CENTER, 7000, true))), 40000, 20000);
+ break;
+ }
+ case "start_charge":
+ {
+ _skillCastTask = ThreadPoolManager.scheduleAtFixedRate(() -> _generatorSpawn.forEach(generators ->
+ {
+ int index = generators.getScriptValue();
+ if (!generators.isCastingNow() && (generators.getEffectList().getBuffInfoBySkillId(SKILL_RECHARGE_POSIBLE.getSkillId()) == null) && !hasFlag(_chargedMask, 1 << index))
+ {
+ // TODO Need core implemented combo skill packet.
+ // On this moment player automatic charge generator if distance generator and player <= 900
+ generators.doCast(SKILL_RECHARGE_POSIBLE.getSkill());
+ L2World.getInstance().forEachVisibleObjectInRange(generators, L2PcInstance.class, 900, p ->
+ {
+ p.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+ p.setTarget(generators);
+ p.doCast(RECHARGE.getSkill());
+ });
+ _guardSpawn.stream().forEach(guard ->
+ {
+ guard.setTarget(generators);
+ guard.doCast(RECHARGE.getSkill());
+ guard.setIsInvul(false);
+ if (!guard.isDead())
+ {
+ guard.broadcastSay(ChatType.NPC_GENERAL, GUARD_MSG_1[getRandom(GUARD_MSG_1.length)]);
+ }
+ });
+ }
+ }), 10000, 20000);
+ _LynDracoTask = ThreadPoolManager.scheduleAtFixedRate(() ->
+ {
+ for (Location loc : LYN_DRACO_SPAWNS)
+ {
+ _LinDracoSpawn.add(addSpawn(LYN_DRACO_ATTACKER_GENERATORS, loc, true));
+ }
+ }, 20000, 60000);
+ break;
+ }
+ case "stop_red_zone":
+ {
+ _zoneLair.broadcastPacket(new OnEventTrigger(RED_ZONE_EFFECT, false));
+ break;
+ }
+ case "attack_generator":
+ {
+ if ((npc != null) && !npc.isDead())
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2GuardInstance.class, 3000, generator ->
+ {
+ if (generator.getId() == NPC_GENERATOR)
+ {
+ npc.setTarget(generator);
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_MOVE_TO, generator.getLocation());
+ if (npc.distFromMe(generator) < 500)
+ {
+ npc.reduceCurrentHp(1, generator, null);
+ generator.reduceCurrentHp(1, npc, null);
+ }
+ }
+ });
+ }
+ break;
+ }
+ }
+ return null;
+ }
+
+ @Override
+ public String onKill(L2Npc npc, L2PcInstance killer, boolean isSummon)
+ {
+ if (npc.getId() == LINDVIOR_RAID)
+ {
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.HONORABLE_WARRIORS_HAVE_DRIVEN_OFF_LINDVIOR_THE_EVIL_WIND_DRAGON, ExShowScreenMessage.TOP_CENTER, 10000, true));
+ GrandBossManager.getInstance().setBossStatus(LINDVIOR_RAID, DEAD);
+ final long respawnTime = (Config.LINDVIOR_SPAWN_INTERVAL + getRandom(-Config.LINDVIOR_SPAWN_RANDOM, Config.LINDVIOR_SPAWN_RANDOM)) * 3600000;
+ final StatsSet info = GrandBossManager.getInstance().getStatsSet(LINDVIOR_RAID);
+ info.set("respawn_time", System.currentTimeMillis() + respawnTime);
+ GrandBossManager.getInstance().setStatsSet(LINDVIOR_RAID, info);
+ startQuestTimer("unlock_lindvior", respawnTime, null, null);
+ if (_mobsSpawnTask != null)
+ {
+ _mobsSpawnTask.cancel(true);
+ _mobsSpawnTask = null;
+ }
+ _zoneLair.getCharactersInside().stream().filter(L2Character::isNpc).forEach(mob -> mob.deleteMe());
+ ThreadPoolManager.schedule(() -> npc.decayMe(), 10000);
+ _zoneLair.broadcastPacket(new OnEventTrigger(SECOND_STAGE_EVENT_TRIGGER, false));
+ _zoneLair.broadcastPacket(new OnEventTrigger(FIRST_STAGE_EVENT_TRIGGER, true));
+ _lionel.deleteMe();
+ }
+ else if (npc.getId() == NPC_GENERATOR)
+ {
+ _zoneLair.broadcastPacket(new ExShowScreenMessage(NpcStringId.THE_GENERATOR_HAS_BEEN_DESTROYED, ExShowScreenMessage.TOP_CENTER, 5000, true));
+ Clean();
+ _collapseTask = ThreadPoolManager.schedule(() -> Fail(false), 20000);
+ }
+ return super.onKill(npc, killer, isSummon);
+ }
+
+ @Override
+ public String onFirstTalk(L2Npc npc, L2PcInstance player)
+ {
+ if (npc.getId() == NPC_GENERATOR)
+ {
+ return npc.getDisplayEffect() == 1 ? "19477.html" : "19477-01.html";
+ }
+ return super.onFirstTalk(npc, player);
+ }
+
+ @Override
+ public String onEnterZone(L2Character character, L2ZoneType zone)
+ {
+ if (zone.getId() == ZONE_ID)
+ {
+ if (_collapseTask != null)
+ {
+ _collapseTask.cancel(true);
+ _collapseTask = null;
+ }
+ }
+ return super.onEnterZone(character, zone);
+ }
+
+ @Override
+ public String onExitZone(L2Character character, L2ZoneType zone)
+ {
+ if (zone.getId() == ZONE_ID)
+ {
+ if (zone.getPlayersInside().isEmpty())
+ {
+ _collapseTask = ThreadPoolManager.schedule(() -> Fail(true), 900000);
+ }
+ }
+ return super.onExitZone(character, zone);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("NPC_SHOUT"))
+ {
+ if ((npc != null) && !npc.isDead())
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, GUARD_MSG[getRandom(GUARD_MSG.length)]);
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ }
+ }
+ }
+
+ private void sendEventTrigger(boolean status, int... triggers)
+ {
+ IClientOutgoingPacket[] pakets = new IClientOutgoingPacket[triggers.length];
+ for (int i = 0; i < triggers.length; i++)
+ {
+ pakets[i] = new OnEventTrigger(triggers[i], status);
+ }
+ for (IClientOutgoingPacket packet : pakets)
+ {
+ _zoneLair.broadcastPacket(packet);
+ }
+ }
+
+ private void spawnServitor(int count, int radius, Location loc, int... npcIds)
+ {
+ int x = loc.getX(), y = loc.getY();
+ if (radius > 0)
+ {
+ x += Rnd.get(-radius, radius);
+ y += Rnd.get(-radius, radius);
+ }
+
+ for (int i = 0; i < count; i++)
+ {
+ _monsterSpawn.add(addSpawn(npcIds[getRandom(npcIds.length)], x, y, loc.getZ(), loc.getHeading(), true, 0, true));
+ }
+ }
+
+ private static boolean hasFlag(int val, int flag)
+ {
+ return (val & flag) == flag;
+ }
+
+ public void setLindviorSpawnTask()
+ {
+ synchronized (this)
+ {
+ if (_socialTask == null)
+ {
+ _socialTask = ThreadPoolManager.schedule(() -> nextStage(1), 3000);
+ }
+ }
+ }
+
+ @RegisterEvent(EventType.ON_CREATURE_DAMAGE_RECEIVED)
+ @RegisterType(ListenerRegisterType.NPC)
+ @Id(LINDVIOR_FLY)
+ @Id(LINDVIOR_RAID)
+ @Id(LINDVIOR_GROUND)
+ public void onCreatureDamageReceived(OnCreatureDamageReceived event)
+ {
+ _zoneLair.getPlayersInside().stream().forEach(p ->
+ {
+ switch (_status)
+ {
+ case 0:
+ case 1:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 1, -1, 0.015, true);
+ break;
+ }
+ case 2:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 2, -1, 0.015, true);
+ break;
+ }
+ case 3:
+ case 4:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 3, -1, 0.015, true);
+ break;
+ }
+ case 5:
+ {
+ giveItemRandomly(p, null, LINDVIORS_SCALE, 4, -1, 0.015, true);
+ break;
+ }
+ }
+ });
+ }
+
+ public static void main(String[] args)
+ {
+ new Lindvior();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java
new file mode 100644
index 0000000000..d60c088f9f
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/LindviorBoss.java
@@ -0,0 +1,175 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.model.holders.SkillHolder;
+
+import ai.AbstractNpcAI;
+
+/**
+ * LindviorBoss AI
+ * @author Gigi
+ * @date 2017-08-02 - [11:05:21]
+ */
+public class LindviorBoss extends AbstractNpcAI
+{
+ // Boss
+ private static final int LINDVIOR_GROUND = 25899;
+ private static final int LINDVIOR_RAID = 29240;
+ private static final int LINDVIOR_FLY = 19424;
+ // Skills
+ private static final SkillHolder SKILL_FLY_UP = new SkillHolder(15278, 1);
+ private static final SkillHolder SKILL_RABIES = new SkillHolder(15269, 1);
+ private static final SkillHolder SKILL_FLY = new SkillHolder(15279, 1);
+ private static final SkillHolder MASS_HELL_BINDING = new SkillHolder(11052, 6);
+ private static final SkillHolder MIGHTY_WIND_STRIKE = new SkillHolder(15274, 1);
+ private static final SkillHolder WIND_PULL = new SkillHolder(15591, 1);
+ private static final SkillHolder LINDVIORS_JUMP = new SkillHolder(15430, 1);
+ private static final SkillHolder BODY_SLAM = new SkillHolder(15271, 1);
+ private static final SkillHolder SOAR = new SkillHolder(15279, 1);
+ private static final SkillHolder WIND_BREAT = new SkillHolder(15272, 1);
+ private static final SkillHolder TAIL_SWIPE = new SkillHolder(15273, 1);
+ private static final SkillHolder TORNADO = new SkillHolder(15275, 1);
+ private static final SkillHolder LINDVIORS_ATTACK = new SkillHolder(15600, 1);
+ // Chances
+ private final static int CHANCE_MIGHTY_WIND_STRIKE = 9;
+ private final static int CHANCE_WIND_PULL = 4;
+ private final static int CHANCE_LINDVIORS_JUMP = 7;
+ private final static int CHANCE_BODY_SLAM = 2;
+ private final static int CHANCE_SOAR = 8;
+ private final static int CHANCE_WIND_BREAT = 3;
+ private final static int CHANCE_TAIL_SWIPE = 5;
+ private final static int CHANCE_TORNADO = 6;
+ private final static int CHANCE_LINDVIORS_ATTACK = 1;
+
+ public LindviorBoss()
+ {
+ super();
+ addAttackId(LINDVIOR_GROUND, LINDVIOR_FLY, LINDVIOR_RAID);
+ addSpawnId(LINDVIOR_FLY);
+ }
+
+ @Override
+ public String onAttack(L2Npc npc, L2PcInstance attacker, int damage, boolean isSummon)
+ {
+ double percent = ((npc.getCurrentHp() - damage) / npc.getMaxHp()) * 100;
+ final int chance = getRandom(100);
+ switch (npc.getId())
+ {
+ case LINDVIOR_GROUND:
+ {
+ if ((percent <= 80) && npc.isScriptValue(0))
+ {
+
+ npc.doCast(SKILL_FLY_UP.getSkill());
+ npc.doCast(SKILL_RABIES.getSkill());
+ npc.setScriptValue(1);
+ }
+ else if ((percent <= 40) && (npc.isScriptValue(1)))
+ {
+ npc.doCast(SKILL_FLY.getSkill());
+ npc.setScriptValue(2);
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_MIGHTY_WIND_STRIKE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(MIGHTY_WIND_STRIKE.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_PULL))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_PULL.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_LINDVIORS_JUMP))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(LINDVIORS_JUMP.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_BODY_SLAM))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(BODY_SLAM.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TAIL_SWIPE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TAIL_SWIPE.getSkill());
+ }
+ break;
+ }
+ case LINDVIOR_FLY:
+ {
+ if (!npc.isCastingNow() && (chance <= CHANCE_SOAR))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(SOAR.getSkill());
+ }
+ break;
+ }
+ case LINDVIOR_RAID:
+ {
+ if ((percent <= 20) && (npc.isScriptValue(0)))
+ {
+ npc.doCast(SKILL_FLY.getSkill());
+ npc.setScriptValue(1);
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_BREAT))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_BREAT.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_WIND_PULL))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(WIND_PULL.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TAIL_SWIPE))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TAIL_SWIPE.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_TORNADO))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(TORNADO.getSkill());
+ }
+ else if (!npc.isCastingNow() && (chance <= CHANCE_LINDVIORS_ATTACK))
+ {
+ npc.setTarget(attacker);
+ npc.doCast(LINDVIORS_ATTACK.getSkill());
+ }
+ break;
+ }
+ }
+ return super.onAttack(npc, attacker, damage, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ npc.setRandomWalking(true);
+ npc.doCast(MASS_HELL_BINDING.getSkill());
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new LindviorBoss();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java
new file mode 100644
index 0000000000..846ada3442
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/LionelHunter.java
@@ -0,0 +1,67 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import com.l2jmobius.gameserver.enums.ChatType;
+import com.l2jmobius.gameserver.instancemanager.WalkingManager;
+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.network.NpcStringId;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Lionel Hunter AI
+ * @author Gigi
+ * @date 2017-07-23 - [22:54:59]
+ */
+public class LionelHunter extends AbstractNpcAI
+{
+ // Npc
+ private static final int LIONEL_HUNTER = 33886;
+ // Misc
+ private static final String ROUTE_NAME = "Rune_Lionel";
+
+ public LionelHunter()
+ {
+ addSpawnId(LIONEL_HUNTER);
+ }
+
+ @Override
+ public void onTimerEvent(String event, StatsSet params, L2Npc npc, L2PcInstance player)
+ {
+ if (event.equals("NPC_SHOUT") && (npc != null))
+ {
+ npc.broadcastSay(ChatType.NPC_GENERAL, NpcStringId.WE_JUST_LOCATED_LINDVIOR_THOSE_WHO_ARE_WILLING_TO_FIGHT_CAN_DO_SO_AT_ANY_TIME_NOW);
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ }
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ getTimers().addTimer("NPC_SHOUT", (10 + getRandom(5)) * 1000, npc, null);
+ WalkingManager.getInstance().startMoving(npc, ROUTE_NAME);
+ return super.onSpawn(npc);
+ }
+
+ public static void main(String[] args)
+ {
+ new LionelHunter();
+ }
+}
\ No newline at end of file
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java
new file mode 100644
index 0000000000..d9bf024d24
--- /dev/null
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/scripts/ai/bosses/Lindvior/Vortex.java
@@ -0,0 +1,176 @@
+/*
+ * 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 ai.bosses.Lindvior;
+
+import java.util.Collection;
+
+import com.l2jmobius.gameserver.ai.CtrlIntention;
+import com.l2jmobius.gameserver.geoengine.GeoEngine;
+import com.l2jmobius.gameserver.model.L2World;
+import com.l2jmobius.gameserver.model.Location;
+import com.l2jmobius.gameserver.model.actor.L2Character;
+import com.l2jmobius.gameserver.model.actor.L2Npc;
+import com.l2jmobius.gameserver.model.actor.instance.L2PcInstance;
+import com.l2jmobius.gameserver.network.serverpackets.FlyToLocation;
+import com.l2jmobius.gameserver.network.serverpackets.ValidateLocation;
+
+import ai.AbstractNpcAI;
+
+/**
+ * Vortex AI
+ * @author Gigi
+ * @date 2017-07-23 - [10:32:50]
+ */
+public class Vortex extends AbstractNpcAI
+{
+ private static final int SMALL_VORTEX = 25898;
+ private static final int BIG_VORTEX = 19427;
+
+ public Vortex()
+ {
+ super();
+ addSeeCreatureId(SMALL_VORTEX, BIG_VORTEX);
+ addSpawnId(SMALL_VORTEX, BIG_VORTEX);
+ }
+
+ @Override
+ public String onAdvEvent(String event, L2Npc npc, L2PcInstance player)
+ {
+ switch (event)
+ {
+ case "rnd_small":
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 250, attackers ->
+ {
+ if ((attackers != null) && !attackers.isDead() && !attackers.isAlikeDead())
+ {
+ attackers.getAI().setIntention(CtrlIntention.AI_INTENTION_IDLE);
+ final int radians = (int) Math.toRadians(npc.calculateDirectionTo(attackers));
+ final int x = (int) (attackers.getX() + (600 * Math.cos(radians)));
+ final int y = (int) (attackers.getY() + (600 * Math.sin(radians)));
+ final int z = attackers.getZ();
+ final Location loc = GeoEngine.getInstance().canMoveToTargetLoc(attackers.getX(), attackers.getY(), attackers.getZ(), x, y, z, attackers.getInstanceWorld());
+ attackers.broadcastPacket(new FlyToLocation(attackers, x, y, z, FlyToLocation.FlyType.THROW_UP, 800, 800, 800));
+ attackers.setXYZ(loc);
+ attackers.broadcastPacket(new ValidateLocation(attackers));
+ npc.getAI().setIntention(CtrlIntention.AI_INTENTION_ATTACK, player);
+ startQuestTimer("stop_knock_down", 5000, npc, attackers);
+ startQuestTimer("despawn_small", 5000, npc, null);
+ }
+ });
+ break;
+ }
+ case "rnd_big":
+ {
+ L2World.getInstance().forEachVisibleObjectInRange(npc, L2PcInstance.class, 500, attackers ->
+ {
+ if ((attackers != null) && !attackers.isDead() && !attackers.isAlikeDead())
+ {
+ attackers.setCurrentHp(1.0);
+ attackers.setCurrentMp(1.0);
+ attackers.setCurrentCp(1.0);
+ startQuestTimer("despawn_big", 600000, npc, null);
+ }
+ });
+ break;
+ }
+ case "despawn_small":
+ {
+ if (npc != null)
+ {
+ cancelQuestTimers("rnd_small");
+ npc.getSpawn().stopRespawn();
+ npc.doDie(null);
+ }
+ break;
+ }
+ case "despawn_big":
+ {
+ if (npc != null)
+ {
+ cancelQuestTimers("despawn_big");
+ npc.getSpawn().stopRespawn();
+ npc.deleteMe();
+ }
+ break;
+ }
+ }
+ return super.onAdvEvent(event, npc, player);
+ }
+
+ @Override
+ public String onSeeCreature(L2Npc npc, L2Character player, boolean isSummon)
+ {
+ switch (npc.getId())
+ {
+ case SMALL_VORTEX:
+ {
+ startQuestTimer("rnd_small", 5000, npc, null, true);
+ break;
+ }
+ case BIG_VORTEX:
+ {
+ startQuestTimer("rnd_big", 10000, npc, null, true);
+ break;
+ }
+ }
+ return super.onSeeCreature(npc, player, isSummon);
+ }
+
+ @Override
+ public String onSpawn(L2Npc npc)
+ {
+ switch (npc.getId())
+ {
+ case SMALL_VORTEX:
+ {
+ attackRandomTarget(npc);
+ npc.setRandomWalking(true);
+ npc.setIsRunning(true);
+ break;
+ }
+ case BIG_VORTEX:
+ {
+ attackRandomTarget(npc);
+ npc.setRandomWalking(true);
+ npc.setIsRunning(true);
+ break;
+ }
+ }
+ return super.onSpawn(npc);
+ }
+
+ private void attackRandomTarget(L2Npc npc)
+ {
+ final Collection players = L2World.getInstance().getVisibleObjects(npc, L2PcInstance.class);
+ {
+ if ((players == null) || players.isEmpty())
+ {
+ return;
+ }
+ if (players.size() > 0)
+ {
+ addAttackPlayerDesire(npc, players.stream().findAny().get());
+ }
+ }
+ }
+
+ public static void main(String[] args)
+ {
+ new Vortex();
+ }
+}
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/08500-08599.xml b/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/08500-08599.xml
index 75fd3434dd..5799fc23b8 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/08500-08599.xml
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/08500-08599.xml
@@ -1954,7 +1954,7 @@
-
+
diff --git a/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19400-19499.xml b/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19400-19499.xml
index 6be32ec2b6..6118ac9716 100644
--- a/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19400-19499.xml
+++ b/L2J_Mobius_3.0_Helios/dist/game/data/stats/npcs/19400-19499.xml
@@ -825,58 +825,76 @@