/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * 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 <http://www.gnu.org/licenses/>.
 */

using Framework.Constants;
using Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Scripting;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Diagnostics.Contracts;

namespace Scripts.Northrend.Ulduar
{
    namespace FlameLeviathan
    {
        struct Spells
        {
            public const uint Pursued = 62374;
            public const uint GatheringSpeed = 62375;
            public const uint BatteringRam = 62376;
            public const uint FlameVents = 62396;
            public const uint MissileBarrage = 62400;
            public const uint SystemsShutdown = 62475;
            public const uint OverloadCircuit = 62399;
            public const uint StartTheEngine = 62472;
            public const uint SearingFlame = 62402;
            public const uint Blaze = 62292;
            public const uint TarPassive = 62288;
            public const uint SmokeTrail = 63575;
            public const uint Electroshock = 62522;
            public const uint Napalm = 63666;
            public const uint InvisAndStealthDetect = 18950; // Passive
                                                             //Tower Additional Spells
            public const uint ThorimSHammer = 62911; // Tower Of Storms
            public const uint MimironSInferno = 62909; // Tower Of Flames
            public const uint HodirSFury = 62533; // Tower Of Frost
            public const uint FreyaSWard = 62906; // Tower Of Nature
            public const uint FreyaSummons = 62947; // Tower Of Nature
                                                    //Tower Ap & Health Spells
            public const uint BuffTowerOfStorms = 65076;
            public const uint BuffTowerOfFlames = 65075;
            public const uint BuffTowerOfFrost = 65077;
            public const uint BuffTowerOfLife = 64482;
            //Additional Spells
            public const uint Lash = 65062;
            public const uint FreyaSWardEffect1 = 62947;
            public const uint FreyaSWardEffect2 = 62907;
            public const uint AutoRepair = 62705;
            public const uint DummyBlue = 63294;
            public const uint DummyGreen = 63295;
            public const uint DummyYellow = 63292;
            public const uint LiquidPyrite = 62494;
            public const uint DustyExplosion = 63360;
            public const uint DustCloudImpact = 54740;
            public const uint StealthDetection = 18950;
            public const uint RideVehicle = 46598;
        }

        struct CreatureIds
        {
            public const uint Seat = 33114;
            public const uint Mechanolift = 33214;
            public const uint Liquid = 33189;
            public const uint Container = 33218;
            public const uint ThorimBeacon = 33365;
            public const uint MimironBeacon = 33370;
            public const uint HodirBeacon = 33212;
            public const uint FreyaBeacon = 33367;
            public const uint ThorimTargetBeacon = 33364;
            public const uint MimironTargetBeacon = 33369;
            public const uint HodirTargetBeacon = 33108;
            public const uint FreyaTargetBeacon = 33366;
            public const uint Lorekeeper = 33686; // Hard Mode Starter
            public const uint BranzBronzbeard = 33579;
            public const uint Delorah = 33701;
            public const uint UlduarGauntletGenerator = 33571; // Trigger Tied To Towers
        }

        struct Towers
        {
            public const uint ofStorms = 194377;
            public const uint ofFlames = 194371;
            public const uint ofFrost = 194370;
            public const uint ofLife = 194375;
        }

        struct Events
        {
            public const int Pursue = 1;
            public const int Missile = 2;
            public const int Vent = 3;
            public const int Speed = 4;
            public const int Summon = 5;
            public const int Shutdown = 6;
            public const int Repair = 7;
            public const int ThorimSHammer = 8;    // Tower Of Storms
            public const int MimironSInferno = 9;    // Tower Of Flames
            public const int HodirSFury = 10;   // Tower Of Frost
            public const int FreyaSWard = 11;   // Tower Of Nature
        }

        struct Seats
        {
            public const int Player = 0;
            public const int Turret = 1;
            public const int Device = 2;
            public const int Cannon = 7;
        }

        struct Vehicles
        {
            public const uint Siege = 33060;
            public const uint Chopper = 33062;
            public const uint Demolisher = 33109;
        }

        struct Leviathan
        {
            public const int DataShutout = 29112912; // 2911, 2912 are achievement IDs
            public const int DataOrbitAchievements = 1;
            public const int VehicleSpawns = 5;
            public const int FreyaSpawns = 4;

            public const int ActionStartHardMode = 5;
            public const int ActionSpawnVehicles = 6;
            // Amount of seats depending on Raid mode
            public const int TwoSeats = 2;
            public const int FourSeats = 4;

            //Postions
            public static Position Center = new Position(354.8771f, -12.90240f, 409.803650f);
            public static Position InfernoStart = new Position(390.93f, -13.91f, 409.81f);

            public static Position[] PosSiege =
            {
                new Position(-814.59f, -64.54f, 429.92f, 5.969f),
                new Position(-784.37f, -33.31f, 429.92f, 5.096f),
                new Position(-808.99f, -52.10f, 429.92f, 5.668f),
                new Position(-798.59f, -44.00f, 429.92f, 5.663f),
                new Position(-812.83f, -77.71f, 429.92f, 0.046f),
            };

            public static Position[] PosChopper =
            {
                new Position(-717.83f, -106.56f, 430.02f, 0.122f),
                new Position(-717.83f, -114.23f, 430.44f, 0.122f),
                new Position(-717.83f, -109.70f, 430.22f, 0.122f),
                new Position(-718.45f, -118.24f, 430.26f, 0.052f),
                new Position(-718.45f, -123.58f, 430.41f, 0.085f),
            };

            public static Position[] PosDemolisher =
            {
                new Position(-724.12f, -176.64f, 430.03f, 2.543f),
                new Position(-766.70f, -225.03f, 430.50f, 1.710f),
                new Position(-729.54f, -186.26f, 430.12f, 1.902f),
                new Position(-756.01f, -219.23f, 430.50f, 2.369f),
                new Position(-798.01f, -227.24f, 429.84f, 1.446f),
            };

            public static Position[] FreyaBeacons =
            {
                new Position(377.02f, -119.10f, 409.81f),
                new Position(185.62f, -119.10f, 409.81f),
                new Position(377.02f, 54.78f, 409.81f),
                new Position(185.62f, 54.78f, 409.81f),
            };
        }

        struct Says
        {
            public const uint Aggro = 0;
            public const uint Slay = 1;
            public const uint Death = 2;
            public const uint Target = 3;
            public const uint Hardmode = 4;
            public const uint TowerNone = 5;
            public const uint TowerFrost = 6;
            public const uint TowerFlame = 7;
            public const uint TowerNature = 8;
            public const uint TowerStorm = 9;
            public const uint PlayerRiding = 10;
            public const uint Overload = 11;
            public const uint EmotePursue = 12;
            public const uint EmoteOverload = 13;
            public const int EmoteRepair = 14;
        }

        [Script]
        class boss_flame_leviathan : CreatureScript
        {
            public boss_flame_leviathan() : base("boss_flame_leviathan") { }

            class boss_flame_leviathanAI : BossAI
            {
                public boss_flame_leviathanAI(Creature creature) : base(creature, BossIds.Leviathan)
                {
                    vehicle = creature.GetVehicleKit();
                }

                public override void InitializeAI()
                {
                    Contract.Assert(vehicle);
                    if (!me.IsDead())
                        Reset();

                    ActiveTowersCount = 4;
                    Shutdown = 0;
                    ActiveTowers = false;
                    towerOfStorms = false;
                    towerOfLife = false;
                    towerOfFlames = false;
                    towerOfFrost = false;
                    Shutout = true;
                    Unbroken = true;

                    DoCast(Spells.InvisAndStealthDetect);

                    me.SetFlag(UnitFields.Flags, UnitFlags.NonAttackable | UnitFlags.NotSelectable | UnitFlags.Stunned);
                    me.SetReactState(ReactStates.Passive);
                }

                public override void Reset()
                {
                    _Reset();
                    //resets shutdown counter to 0.  2 or 4 depending on raid mode
                    Shutdown = 0;
                    _pursueTarget.Clear();

                    me.SetReactState(ReactStates.Defensive);
                }

                public override void EnterCombat(Unit who)
                {
                    _EnterCombat();
                    me.SetReactState(ReactStates.Passive);
                    _events.ScheduleEvent(Events.Pursue, 1);
                    _events.ScheduleEvent(Events.Missile, RandomHelper.URand(1500, 4 * Time.InMilliseconds));
                    _events.ScheduleEvent(Events.Vent, 20 * Time.InMilliseconds);
                    _events.ScheduleEvent(Events.Shutdown, 150 * Time.InMilliseconds);
                    _events.ScheduleEvent(Events.Speed, 15 * Time.InMilliseconds);
                    _events.ScheduleEvent(Events.Summon, 1 * Time.InMilliseconds);
                    ActiveTower(); //void ActiveTower
                }

                void ActiveTower()
                {
                    if (ActiveTowers)
                    {
                        if (towerOfStorms)
                        {
                            me.AddAura(Spells.BuffTowerOfStorms, me);
                            _events.ScheduleEvent(Events.ThorimSHammer, 35 * Time.InMilliseconds);
                        }

                        if (towerOfFlames)
                        {
                            me.AddAura(Spells.BuffTowerOfFlames, me);
                            _events.ScheduleEvent(Events.MimironSInferno, 70 * Time.InMilliseconds);
                        }

                        if (towerOfFrost)
                        {
                            me.AddAura(Spells.BuffTowerOfFrost, me);
                            _events.ScheduleEvent(Events.HodirSFury, 105 * Time.InMilliseconds);
                        }

                        if (towerOfLife)
                        {
                            me.AddAura(Spells.BuffTowerOfLife, me);
                            _events.ScheduleEvent(Events.FreyaSWard, 140 * Time.InMilliseconds);
                        }

                        if (!towerOfLife && !towerOfFrost && !towerOfFlames && !towerOfStorms)
                            Talk(Says.TowerNone);
                        else
                            Talk(Says.Hardmode);
                    }
                    else
                        Talk(Says.Aggro);
                }

                public override void JustDied(Unit killer)
                {
                    _JustDied();
                    // Set Field Flags 67108928 = 64 | 67108864 = UNIT_FLAG_UNK_6 | UNIT_FLAG_SKINNABLE
                    // Set DynFlags 12
                    // Set NPCFlags 0
                    Talk(Says.Death);
                }

                public override void SpellHit(Unit caster, SpellInfo spell)
                {
                    if (spell.Id == Spells.StartTheEngine)
                        vehicle.InstallAllAccessories(false);

                    if (spell.Id == Spells.Electroshock)
                        me.InterruptSpell(CurrentSpellTypes.Channeled);

                    if (spell.Id == Spells.OverloadCircuit)
                        ++Shutdown;
                }

                public override uint GetData(uint type)
                {
                    switch (type)
                    {
                        case Leviathan.DataShutout:
                            return (uint)(Shutout ? 1 : 0);
                        case InstanceAchievementData.DataUnbroken:
                            return (uint)(Unbroken ? 1 : 0);
                        case Leviathan.DataOrbitAchievements:
                            if (ActiveTowers) // Only on HardMode
                                return ActiveTowersCount;
                            break;
                        default:
                            break;
                    }

                    return 0;
                }

                public override void SetData(uint id, uint data)
                {
                    if (id == InstanceAchievementData.DataUnbroken)
                        Unbroken = data != 0 ? true : false;
                }

                public override void UpdateAI(uint diff)
                {
                    if (!UpdateVictim())
                        return;

                    _events.Update(diff);

                    if (Shutdown == RaidMode(Leviathan.TwoSeats, Leviathan.FourSeats))
                    {
                        Shutdown = 0;
                        _events.ScheduleEvent(Events.Shutdown, 4000);
                        me.RemoveAurasDueToSpell(Spells.OverloadCircuit);
                        me.InterruptNonMeleeSpells(true);
                        return;
                    }

                    if (me.HasUnitState(UnitState.Casting))
                        return;
                    _events.ExecuteEvents(eventId =>
                    {
                        switch (eventId)
                        {
                            case Events.Pursue:
                                Talk(Says.Target);
                                DoCast(Spells.Pursued);  // Will select target in spellscript
                            _events.ScheduleEvent(Events.Pursue, 35 * Time.InMilliseconds);
                                break;
                            case Events.Missile:
                                DoCast(me, Spells.MissileBarrage, true);
                                _events.ScheduleEvent(Events.Missile, 2 * Time.InMilliseconds);
                                break;
                            case Events.Vent:
                                DoCastAOE(Spells.FlameVents);
                                _events.ScheduleEvent(Events.Vent, 20 * Time.InMilliseconds);
                                break;
                            case Events.Speed:
                                DoCastAOE(Spells.GatheringSpeed);
                                _events.ScheduleEvent(Events.Speed, 15 * Time.InMilliseconds);
                                break;
                            case Events.Summon:
                                if (summons.Count < 15)
                                {
                                    Creature lift = DoSummonFlyer(CreatureIds.Mechanolift, me, 30.0f, 50.0f, 0);
                                    if (lift)
                                        lift.GetMotionMaster().MoveRandom(100);
                                }
                                _events.ScheduleEvent(Events.Summon, 2 * Time.InMilliseconds);
                                break;
                            case Events.Shutdown:
                                Talk(Says.Overload);
                                Talk(Says.EmoteOverload);
                                me.CastSpell(me, Spells.SystemsShutdown, true);
                                if (Shutout)
                                    Shutout = false;
                                _events.ScheduleEvent(Events.Repair, 4000);
                                _events.DelayEvents(20 * Time.InMilliseconds, 0);
                                break;
                            case Events.Repair:
                                Talk(Says.EmoteRepair);
                                me.ClearUnitState(UnitState.Stunned | UnitState.Root);
                                _events.ScheduleEvent(Events.Shutdown, 150 * Time.InMilliseconds);
                                _events.CancelEvent(Events.Repair);
                                break;
                            case Events.ThorimSHammer: // Tower of Storms
                            for (byte i = 0; i < 7; ++i)
                                {
                                    Creature thorim = DoSummon(CreatureIds.ThorimBeacon, me, RandomHelper.URand(20, 60), 20000, TempSummonType.TimedDespawn);
                                    if (thorim)
                                        thorim.GetMotionMaster().MoveRandom(100);
                                }
                                Talk(Says.TowerStorm);
                                _events.CancelEvent(Events.ThorimSHammer);
                                break;
                            case Events.MimironSInferno: // Tower of Flames
                            me.SummonCreature(CreatureIds.MimironBeacon, Leviathan.InfernoStart);
                                Talk(Says.TowerFlame);
                                _events.CancelEvent(Events.MimironSInferno);
                                break;
                            case Events.HodirSFury:      // Tower of Frost
                            for (byte i = 0; i < 7; ++i)
                                {
                                    Creature hodir = DoSummon(CreatureIds.HodirBeacon, me, 50f, 0);
                                    if (hodir)
                                        hodir.GetMotionMaster().MoveRandom(100);
                                }
                                Talk(Says.TowerFrost);
                                _events.CancelEvent(Events.HodirSFury);
                                break;
                            case Events.FreyaSWard:    // Tower of Nature
                            Talk(Says.TowerNature);
                                for (int i = 0; i < 4; ++i)
                                    me.SummonCreature(CreatureIds.FreyaBeacon, Leviathan.FreyaBeacons[i]);

                                Unit target = SelectTarget(SelectAggroTarget.Random);
                                if (target)
                                    DoCast(target, Spells.FreyaSWard);
                                _events.CancelEvent(Events.FreyaSWard);
                                break;
                        }
                    });

                    DoBatteringRamIfReady();
                }

                public override void SpellHitTarget(Unit target, SpellInfo spell)
                {
                    if (spell.Id == Spells.Pursued)
                        _pursueTarget = target.GetGUID();
                }

                public override void DoAction(int action)
                {
                    if (action != 0 && action <= 4) // Tower destruction, debuff leviathan loot and reduce active tower count
                    {
                        if (me.HasLootMode(LootModes.Default | LootModes.HardMode1 | LootModes.HardMode2 | LootModes.HardMode3 | LootModes.HardMode4) && ActiveTowersCount == 4)
                            me.RemoveLootMode(LootModes.HardMode4);

                        if (me.HasLootMode(LootModes.Default | LootModes.HardMode1 | LootModes.HardMode2 | LootModes.HardMode3) && ActiveTowersCount == 3)
                            me.RemoveLootMode(LootModes.HardMode3);

                        if (me.HasLootMode(LootModes.Default | LootModes.HardMode1 | LootModes.HardMode2) && ActiveTowersCount == 2)
                            me.RemoveLootMode(LootModes.HardMode2);

                        if (me.HasLootMode(LootModes.Default | LootModes.HardMode1) && ActiveTowersCount == 1)
                            me.RemoveLootMode(LootModes.HardMode1);
                    }

                    switch (action)
                    {
                        case LeviathanActions.TowerOfStormDestroyed:
                            if (towerOfStorms)
                            {
                                towerOfStorms = false;
                                --ActiveTowersCount;
                            }
                            break;
                        case LeviathanActions.TowerOfFrostDestroyed:
                            if (towerOfFrost)
                            {
                                towerOfFrost = false;
                                --ActiveTowersCount;
                            }
                            break;
                        case LeviathanActions.TowerOfFlamesDestroyed:
                            if (towerOfFlames)
                            {
                                towerOfFlames = false;
                                --ActiveTowersCount;
                            }
                            break;
                        case LeviathanActions.TowerOfLifeDestroyed:
                            if (towerOfLife)
                            {
                                towerOfLife = false;
                                --ActiveTowersCount;
                            }
                            break;
                        case Leviathan.ActionStartHardMode:  // Activate hard-mode enable all towers, apply buffs on leviathan
                            ActiveTowers = true;
                            towerOfStorms = true;
                            towerOfLife = true;
                            towerOfFlames = true;
                            towerOfFrost = true;
                            me.SetLootMode(LootModes.Default | LootModes.HardMode1 | LootModes.HardMode2 | LootModes.HardMode3 | LootModes.HardMode4);
                            break;
                        case LeviathanActions.MoveToCenterPosition: // Triggered by 2 Collossus near door
                            if (!me.IsDead())
                            {
                                me.SetHomePosition(Leviathan.Center);
                                me.GetMotionMaster().MoveCharge(Leviathan.Center.GetPositionX(), Leviathan.Center.GetPositionY(), Leviathan.Center.GetPositionZ()); // position center
                                me.SetReactState(ReactStates.Aggressive);
                                me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable | UnitFlags.NotSelectable | UnitFlags.Stunned);
                                return;
                            }
                            break;
                        default:
                            break;
                    }
                }

                //! Copypasta from DoSpellAttackIfReady, only difference is the target - it cannot be selected trough GetVictim this way -
                //! I also removed the spellInfo check
                void DoBatteringRamIfReady()
                {
                    if (me.isAttackReady())
                    {
                        Unit target = Global.ObjAccessor.GetUnit(me, _pursueTarget);
                        if (me.IsWithinCombatRange(target, 30.0f))
                        {
                            DoCast(target, Spells.BatteringRam);
                            me.resetAttackTimer();
                        }
                    }
                }

                Vehicle vehicle;
                byte ActiveTowersCount;
                byte Shutdown;
                bool ActiveTowers;
                bool towerOfStorms;
                bool towerOfLife;
                bool towerOfFlames;
                bool towerOfFrost;
                bool Shutout;
                bool Unbroken;
                ObjectGuid _pursueTarget;
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return instance_ulduar.GetUlduarInstanceAI<boss_flame_leviathanAI>(creature);
            }
        }

        [Script]
        class boss_flame_leviathan_seat : CreatureScript
        {
            public boss_flame_leviathan_seat() : base("boss_flame_leviathan_seat") { }

            class boss_flame_leviathan_seatAI : ScriptedAI
            {
                public boss_flame_leviathan_seatAI(Creature creature)
                    : base(creature)
                {
                    vehicle = creature.GetVehicleKit();
                    Contract.Assert(vehicle);
                    me.SetReactState(ReactStates.Passive);
                    me.SetDisplayId(me.GetCreatureTemplate().ModelId2);
                    instance = creature.GetInstanceScript();
                }

                InstanceScript instance;
                Vehicle vehicle;

                public override void PassengerBoarded(Unit who, sbyte seatId, bool apply)
                {
                    if (!me.GetVehicle())
                        return;

                    if (seatId == Seats.Player)
                    {
                        Creature leviathan = me.GetVehicleCreatureBase();
                        if (!apply)
                            return;
                        else if (leviathan)
                            leviathan.GetAI().Talk(Says.PlayerRiding);

                        Unit turretPassenger = me.GetVehicleKit().GetPassenger(Seats.Turret);
                        if (turretPassenger)
                        {
                            Creature turret = turretPassenger.ToCreature();
                            if (turret)
                            {
                                turret.SetFaction(me.GetVehicleBase().getFaction());
                                turret.SetUInt32Value(UnitFields.Flags, 0); // unselectable
                                turret.GetAI().AttackStart(who);
                            }
                        }
                        Unit devicePassenger = me.GetVehicleKit().GetPassenger(Seats.Device);
                        if (devicePassenger)
                        {
                            Creature device = devicePassenger.ToCreature();
                            if (device)
                            {
                                device.SetFlag64(UnitFields.NpcFlags, NPCFlags.SpellClick);
                                device.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                            }
                        }

                        me.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                    }
                    else if (seatId == Seats.Turret)
                    {
                        if (apply)
                            return;

                        Unit device = vehicle.GetPassenger(Seats.Device);
                        if (device)
                        {
                            device.SetFlag64(UnitFields.NpcFlags, NPCFlags.SpellClick);
                            device.SetUInt32Value(UnitFields.Flags, 0); // unselectable
                        }
                    }
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return instance_ulduar.GetUlduarInstanceAI<boss_flame_leviathan_seatAI>(creature);
            }
        }

        [Script]
        class boss_flame_leviathan_defense_cannon : CreatureScript
        {
            public boss_flame_leviathan_defense_cannon() : base("boss_flame_leviathan_defense_cannon") { }

            class boss_flame_leviathan_defense_cannonAI : ScriptedAI
            {
                public boss_flame_leviathan_defense_cannonAI(Creature creature)
                    : base(creature)
                {
                }

                uint NapalmTimer;

                public override void Reset()
                {
                    NapalmTimer = 5 * Time.InMilliseconds;
                    DoCast(me, Spells.StealthDetection);
                }

                public override void UpdateAI(uint diff)
                {
                    if (!UpdateVictim())
                        return;

                    if (NapalmTimer <= diff)
                    {
                        Unit target = SelectTarget(SelectAggroTarget.Random, 0);
                        if (target)
                            if (CanAIAttack(target))
                                DoCast(target, Spells.Napalm, true);

                        NapalmTimer = 5000;
                    }
                    else
                        NapalmTimer -= diff;
                }

                public override bool CanAIAttack(Unit who)
                {
                    if (!who.IsTypeId(TypeId.Player) || !who.GetVehicle() || who.GetVehicleBase().GetEntry() == CreatureIds.Seat)
                        return false;
                    return true;
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new boss_flame_leviathan_defense_cannonAI(creature);
            }
        }

        [Script]
        class boss_flame_leviathan_defense_turret : CreatureScript
        {
            public boss_flame_leviathan_defense_turret() : base("boss_flame_leviathan_defense_turret") { }

            class boss_flame_leviathan_defense_turretAI : TurretAI
            {
                public boss_flame_leviathan_defense_turretAI(Creature creature) : base(creature) { }

                public override void DamageTaken(Unit who, ref uint damage)
                {
                    if (!CanAIAttack(who))
                        damage = 0;
                }

                public override bool CanAIAttack(Unit who)
                {
                    if (!who.IsTypeId(TypeId.Player) || !who.GetVehicle() || who.GetVehicleBase().GetEntry() != CreatureIds.Seat)
                        return false;
                    return true;
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new boss_flame_leviathan_defense_turretAI(creature);
            }
        }

        [Script]
        class boss_flame_leviathan_overload_device : CreatureScript
        {
            public boss_flame_leviathan_overload_device() : base("boss_flame_leviathan_overload_device") { }

            class boss_flame_leviathan_overload_deviceAI : PassiveAI
            {
                public boss_flame_leviathan_overload_deviceAI(Creature creature)
                    : base(creature)
                {
                }

                public override void OnSpellClick(Unit clicker, ref bool result)
                {
                    if (!result)
                        return;

                    if (me.GetVehicle())
                    {
                        me.RemoveFlag64(UnitFields.NpcFlags, NPCFlags.SpellClick);
                        me.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);

                        Unit player = me.GetVehicle().GetPassenger(Seats.Player);
                        if (player)
                        {
                            me.GetVehicleBase().CastSpell(player, Spells.SmokeTrail, true);
                            player.GetMotionMaster().MoveKnockbackFrom(me.GetVehicleBase().GetPositionX(), me.GetVehicleBase().GetPositionY(), 30, 30);
                            player.ExitVehicle();
                        }
                    }
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new boss_flame_leviathan_overload_deviceAI(creature);
            }
        }

        [Script]
        class boss_flame_leviathan_safety_container : CreatureScript
        {
            public boss_flame_leviathan_safety_container() : base("boss_flame_leviathan_safety_container") { }

            class boss_flame_leviathan_safety_containerAI : PassiveAI
            {
                public boss_flame_leviathan_safety_containerAI(Creature creature)
                    : base(creature)
                {
                }

                public override void JustDied(Unit killer)
                {
                    float x, y, z;
                    me.GetPosition(out x, out y, out z);
                    z = me.GetMap().GetHeight(me.GetPhases(), x, y, z);
                    me.GetMotionMaster().MovePoint(0, x, y, z);
                    me.SetPosition(x, y, z, 0);
                }

                public override void UpdateAI(uint diff)
                {
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new boss_flame_leviathan_safety_containerAI(creature);
            }
        }

        [Script]
        class npc_mechanolift : CreatureScript
        {
            public npc_mechanolift() : base("npc_mechanolift") { }

            class npc_mechanoliftAI : PassiveAI
            {
                public npc_mechanoliftAI(Creature creature) : base(creature)
                {
                    Contract.Assert(me.GetVehicleKit());
                }

                uint MoveTimer;

                public override void Reset()
                {
                    MoveTimer = 0;
                    me.GetMotionMaster().MoveRandom(50);
                }

                public override void JustDied(Unit killer)
                {
                    me.GetMotionMaster().MoveTargetedHome();
                    DoCast(Spells.DustyExplosion);
                    Creature liquid = DoSummon(CreatureIds.Liquid, me, 0f);
                    if (liquid)
                    {
                        liquid.CastSpell(liquid, Spells.LiquidPyrite, true);
                        liquid.CastSpell(liquid, Spells.DustCloudImpact, true);
                    }
                }

                public override void MovementInform(MovementGeneratorType type, uint id)
                {
                    if (type == MovementGeneratorType.Point && id == 1)
                    {
                        Creature container = me.FindNearestCreature(CreatureIds.Container, 5, true);
                        if (container)
                            container.EnterVehicle(me);
                    }
                }

                public override void UpdateAI(uint diff)
                {
                    if (MoveTimer <= diff)
                    {
                        if (me.GetVehicleKit().HasEmptySeat(-1))
                        {
                            Creature container = me.FindNearestCreature(CreatureIds.Container, 50, true);
                            if (container && !container.GetVehicle())
                                me.GetMotionMaster().MovePoint(1, container.GetPositionX(), container.GetPositionY(), container.GetPositionZ());
                        }

                        MoveTimer = 30000; //check next 30 seconds
                    }
                    else
                        MoveTimer -= diff;
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_mechanoliftAI(creature);
            }
        }

        [Script]
        class npc_pool_of_tar : CreatureScript
        {
            public npc_pool_of_tar() : base("npc_pool_of_tar") { }

            class npc_pool_of_tarAI : ScriptedAI
            {
                public npc_pool_of_tarAI(Creature creature)
                    : base(creature)
                {
                    me.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                    me.SetReactState(ReactStates.Passive);
                    me.CastSpell(me, Spells.TarPassive, true);
                }

                public override void DamageTaken(Unit who, ref uint damage)
                {
                    damage = 0;
                }

                public override void SpellHit(Unit caster, SpellInfo spell)
                {
                    if (spell.SchoolMask.HasAnyFlag(SpellSchoolMask.Fire) && !me.HasAura(Spells.Blaze))
                        me.CastSpell(me, Spells.Blaze, true);
                }

                public override void UpdateAI(uint diff) { }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_pool_of_tarAI(creature);
            }
        }

        [Script]
        class npc_colossus : CreatureScript
        {
            public npc_colossus() : base("npc_colossus") { }

            class npc_colossusAI : ScriptedAI
            {
                public npc_colossusAI(Creature creature)
                    : base(creature)
                {
                    instance = creature.GetInstanceScript();
                }

                InstanceScript instance;

                public override void JustDied(Unit killer)
                {
                    if (me.GetHomePosition().IsInDist(Leviathan.Center, 50.0f))
                        instance.SetData(InstanceData.Colossus, instance.GetData(InstanceData.Colossus) + 1);
                }

                public override void UpdateAI(uint diff)
                {
                    if (!UpdateVictim())
                        return;

                    DoMeleeAttackIfReady();
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return GetInstanceAI<npc_colossusAI>(creature);
            }
        }

        [Script]
        class npc_thorims_hammer : CreatureScript
        {
            public npc_thorims_hammer() : base("npc_thorims_hammer") { }

            class npc_thorims_hammerAI : ScriptedAI
            {
                public npc_thorims_hammerAI(Creature creature)
                    : base(creature)
                {
                    me.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                    me.CastSpell(me, Spells.DummyBlue, true);
                }

                public override void MoveInLineOfSight(Unit who)
                {
                    if (who.IsTypeId(TypeId.Player) && who.IsVehicle() && me.IsInRange(who, 0, 10, false))
                    {
                        Creature trigger = DoSummonFlyer(CreatureIds.ThorimTargetBeacon, me, 20, 0, 1000, TempSummonType.TimedDespawn);
                        if (trigger)
                            trigger.CastSpell(who, Spells.ThorimSHammer, true);
                    }
                }

                public override void UpdateAI(uint diff)
                {
                    if (!me.HasAura(Spells.DummyBlue))
                        me.CastSpell(me, Spells.DummyBlue, true);

                    UpdateVictim();
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_thorims_hammerAI(creature);
            }
        }

        [Script]
        class npc_mimirons_inferno : CreatureScript
        {
            public npc_mimirons_inferno() : base("npc_mimirons_inferno") { }

            class npc_mimirons_infernoAI : npc_escortAI
            {
                public npc_mimirons_infernoAI(Creature creature)
                    : base(creature)
                {
                    me.SetFlag(UnitFields.Flags, UnitFlags.NonAttackable | UnitFlags.NotSelectable);
                    me.CastSpell(me, Spells.DummyYellow, true);
                    me.SetReactState(ReactStates.Passive);
                }

                public override void WaypointReached(uint waypointId)
                {

                }

                public override void Reset()
                {
                    infernoTimer = 2000;
                }

                uint infernoTimer;

                public override void UpdateAI(uint diff)
                {
                    base.UpdateAI(diff);

                    if (!HasEscortState(eEscortState.Escorting))
                        Start(false, true, ObjectGuid.Empty, null, false, true);
                    else
                    {
                        if (infernoTimer <= diff)
                        {
                            Creature trigger = DoSummonFlyer(CreatureIds.MimironTargetBeacon, me, 20, 0, 1000, TempSummonType.TimedDespawn);
                            if (trigger)
                            {
                                trigger.CastSpell(me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), Spells.MimironSInferno, true);
                                infernoTimer = 2000;
                            }
                        }
                        else
                            infernoTimer -= diff;

                        if (!me.HasAura(Spells.DummyYellow))
                            me.CastSpell(me, Spells.DummyYellow, true);
                    }
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_mimirons_infernoAI(creature);
            }
        }

        [Script]
        class npc_hodirs_fury : CreatureScript
        {
            public npc_hodirs_fury() : base("npc_hodirs_fury") { }

            class npc_hodirs_furyAI : ScriptedAI
            {
                public npc_hodirs_furyAI(Creature creature)
                    : base(creature)
                {
                    me.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                    me.CastSpell(me, Spells.DummyGreen, true);
                }

                public override void MoveInLineOfSight(Unit who)
                {
                    if (who.IsTypeId(TypeId.Player) && who.IsVehicle() && me.IsInRange(who, 0, 5, false))
                    {
                        Creature trigger = DoSummonFlyer(CreatureIds.HodirTargetBeacon, me, 20, 0, 1000, TempSummonType.TimedDespawn);
                        if (trigger)
                            trigger.CastSpell(who, Spells.HodirSFury, true);
                    }
                }

                public override void UpdateAI(uint diff)
                {
                    if (!me.HasAura(Spells.DummyGreen))
                        me.CastSpell(me, Spells.DummyGreen, true);

                    UpdateVictim();
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_hodirs_furyAI(creature);
            }
        }

        [Script]
        class npc_freyas_ward : CreatureScript
        {
            public npc_freyas_ward() : base("npc_freyas_ward") { }

            class npc_freyas_wardAI : ScriptedAI
            {
                public npc_freyas_wardAI(Creature creature)
                    : base(creature)
                {
                    me.CastSpell(me, Spells.DummyGreen, true);
                }

                uint summonTimer;

                public override void Reset()
                {
                    summonTimer = 5000;
                }

                public override void UpdateAI(uint diff)
                {
                    if (summonTimer <= diff)
                    {
                        DoCast(Spells.FreyaSWardEffect1);
                        DoCast(Spells.FreyaSWardEffect2);
                        summonTimer = 20000;
                    }
                    else
                        summonTimer -= diff;

                    if (!me.HasAura(Spells.DummyGreen))
                        me.CastSpell(me, Spells.DummyGreen, true);

                    UpdateVictim();
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_freyas_wardAI(creature);
            }
        }

        [Script]
        class npc_freya_ward_summon : CreatureScript
        {
            public npc_freya_ward_summon() : base("npc_freya_ward_summon") { }

            class npc_freya_ward_summonAI : ScriptedAI
            {
                public npc_freya_ward_summonAI(Creature creature)
                    : base(creature)
                {
                    creature.GetMotionMaster().MoveRandom(100);
                }

                uint lashTimer;

                public override void Reset()
                {
                    lashTimer = 5000;
                }

                public override void UpdateAI(uint diff)
                {
                    if (!UpdateVictim())
                        return;

                    if (lashTimer <= diff)
                    {
                        DoCast(Spells.Lash);
                        lashTimer = 20000;
                    }
                    else
                        lashTimer -= diff;

                    DoMeleeAttackIfReady();
                }
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_freya_ward_summonAI(creature);
            }
        }

        [Script]
        class npc_lorekeeper : CreatureScript
        {
            string GOSSIP_ITEM_1 = "Activate secondary defensive systems";
            string GOSSIP_ITEM_2 = "Confirmed";

            public npc_lorekeeper() : base("npc_lorekeeper") { }

            class npc_lorekeeperAI : ScriptedAI
            {
                public npc_lorekeeperAI(Creature creature)
                    : base(creature) { }

                public override void DoAction(int action)
                {
                    // Start encounter
                    if (action == Leviathan.ActionSpawnVehicles)
                    {
                        for (int i = 0; i < RaidMode(2, 5); ++i)
                            DoSummon(Vehicles.Siege, Leviathan.PosSiege[i], 3000, TempSummonType.CorpseTimedDespawn);
                        for (int i = 0; i < RaidMode(2, 5); ++i)
                            DoSummon(Vehicles.Chopper, Leviathan.PosChopper[i], 3000, TempSummonType.CorpseTimedDespawn);
                        for (int i = 0; i < RaidMode(2, 5); ++i)
                            DoSummon(Vehicles.Demolisher, Leviathan.PosDemolisher[i], 3000, TempSummonType.CorpseTimedDespawn);
                        return;
                    }
                }
            }

            public override bool OnGossipSelect(Player player, Creature creature, uint sender, uint action)
            {
                player.CLOSE_GOSSIP_MENU();
                InstanceScript instance = creature.GetInstanceScript();
                if (instance == null)
                    return true;

                switch (action)
                {
                    case eTradeskill.GossipActionInfoDef + 1:
                        player.PrepareGossipMenu(creature);
                        instance.instance.LoadGrid(364, -16); //make sure leviathan is loaded

                        player.ADD_GOSSIP_ITEM(GossipOptionIcon.Chat, GOSSIP_ITEM_2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 2);
                        player.SEND_GOSSIP_MENU(player.GetGossipTextId(creature), creature.GetGUID());
                        break;
                    case eTradeskill.GossipActionInfoDef + 2:
                        Creature leviathan = instance.instance.GetCreature(instance.GetGuidData(BossIds.Leviathan));
                        if (leviathan)
                        {
                            leviathan.GetAI().DoAction(Leviathan.ActionStartHardMode);
                            creature.SetVisible(false);
                            creature.GetAI().DoAction(Leviathan.ActionSpawnVehicles); // spawn the vehicles
                            Creature Delorah = creature.FindNearestCreature(CreatureIds.Delorah, 1000, true);
                            if (Delorah)
                            {
                                Creature Branz = creature.FindNearestCreature(CreatureIds.BranzBronzbeard, 1000, true);
                                if (Branz)
                                {
                                    Delorah.GetMotionMaster().MovePoint(0, Branz.GetPositionX() - 4, Branz.GetPositionY(), Branz.GetPositionZ());
                                    // @todo Delorah.AI().Talk(xxxx, Branz); when reached at branz
                                }
                            }
                        }
                        break;
                }

                return true;
            }

            public override bool OnGossipHello(Player player, Creature creature)
            {
                InstanceScript instance = creature.GetInstanceScript();
                if (instance != null && instance.GetData(BossIds.Leviathan) != (uint)EncounterState.Done && player)
                {
                    player.PrepareGossipMenu(creature);

                    player.ADD_GOSSIP_ITEM(GossipOptionIcon.Chat, GOSSIP_ITEM_1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1);
                    player.SEND_GOSSIP_MENU(player.GetGossipTextId(creature), creature.GetGUID());
                }
                return true;
            }

            public override CreatureAI GetAI(Creature creature)
            {
                return new npc_lorekeeperAI(creature);
            }
        }

        [Script]
        class go_ulduar_tower : GameObjectScript
        {
            public go_ulduar_tower() : base("go_ulduar_tower") { }

            public override void OnDestroyed(GameObject go, Player player)
            {
                InstanceScript instance = go.GetInstanceScript();
                if (instance == null)
                    return;

                switch (go.GetEntry())
                {
                    case Towers.ofStorms:
                        instance.ProcessEvent(go, InstanceEventIds.TowerOfStormDestroyed);
                        break;
                    case Towers.ofFlames:
                        instance.ProcessEvent(go, InstanceEventIds.TowerOfFlamesDestroyed);
                        break;
                    case Towers.ofFrost:
                        instance.ProcessEvent(go, InstanceEventIds.TowerOfFrostDestroyed);
                        break;
                    case Towers.ofLife:
                        instance.ProcessEvent(go, InstanceEventIds.TowerOfLifeDestroyed);
                        break;
                }

                Creature trigger = go.FindNearestCreature(CreatureIds.UlduarGauntletGenerator, 15.0f, true);
                if (trigger)
                    trigger.DisappearAndDie();
            }
        }

        [Script]
        class achievement_three_car_garage_demolisher : AchievementCriteriaScript
        {
            public achievement_three_car_garage_demolisher() : base("achievement_three_car_garage_demolisher") { }

            public override bool OnCheck(Player source, Unit target)
            {
                Creature vehicle = source.GetVehicleCreatureBase();
                if (vehicle)
                {
                    if (vehicle.GetEntry() == Vehicles.Demolisher)
                        return true;
                }

                return false;
            }
        }

        [Script]
        class achievement_three_car_garage_chopper : AchievementCriteriaScript
        {
            public achievement_three_car_garage_chopper() : base("achievement_three_car_garage_chopper") { }

            public override bool OnCheck(Player source, Unit target)
            {
                Creature vehicle = source.GetVehicleCreatureBase();
                if (vehicle)
                {
                    if (vehicle.GetEntry() == Vehicles.Chopper)
                        return true;
                }

                return false;
            }
        }

        [Script]
        class achievement_three_car_garage_siege : AchievementCriteriaScript
        {
            public achievement_three_car_garage_siege() : base("achievement_three_car_garage_siege") { }

            public override bool OnCheck(Player source, Unit target)
            {
                Creature vehicle = source.GetVehicleCreatureBase();
                if (vehicle)
                {
                    if (vehicle.GetEntry() == Vehicles.Siege)
                        return true;
                }

                return false;
            }
        }

        [Script]
        class achievement_shutout : AchievementCriteriaScript
        {
            public achievement_shutout() : base("achievement_shutout") { }

            public override bool OnCheck(Player source, Unit target)
            {
                if (target)
                {
                    Creature leviathan = target.ToCreature();
                    if (leviathan)
                        if (leviathan.GetAI().GetData(Leviathan.DataShutout) != 0)
                            return true;
                }

                return false;
            }
        }

        [Script]
        class achievement_unbroken : AchievementCriteriaScript
        {
            public achievement_unbroken() : base("achievement_unbroken") { }

            public override bool OnCheck(Player source, Unit target)
            {
                if (target)
                {
                    InstanceScript instance = target.GetInstanceScript();
                    if (instance != null)
                        return instance.GetData(InstanceAchievementData.DataUnbroken) != 0;
                }

                return false;
            }
        }

        [Script]
        class achievement_orbital_bombardment : AchievementCriteriaScript
        {
            public achievement_orbital_bombardment() : base("achievement_orbital_bombardment") { }

            public override bool OnCheck(Player source, Unit target)
            {
                if (!target)
                    return false;

                Creature leviathan = target.ToCreature();
                if (leviathan)
                    if (leviathan.GetAI().GetData(Leviathan.DataOrbitAchievements) >= 1)
                        return true;

                return false;
            }
        }

        [Script]
        class achievement_orbital_devastation : AchievementCriteriaScript
        {
            public achievement_orbital_devastation() : base("achievement_orbital_devastation") { }

            public override bool OnCheck(Player source, Unit target)
            {
                if (!target)
                    return false;
                Creature leviathan = target.ToCreature();
                if (leviathan)
                    if (leviathan.GetAI().GetData(Leviathan.DataOrbitAchievements) >= 2)
                        return true;

                return false;
            }
        }

        [Script]
        class achievement_nuked_from_orbit : AchievementCriteriaScript
        {
            public achievement_nuked_from_orbit() : base("achievement_nuked_from_orbit") { }

            public override bool OnCheck(Player source, Unit target)
            {
                if (!target)
                    return false;

                Creature leviathan = target.ToCreature();
                if (leviathan)
                    if (leviathan.GetAI().GetData(Leviathan.DataOrbitAchievements) >= 3)
                        return true;

                return false;
            }
        }

        [Script]
        class achievement_orbit_uary : AchievementCriteriaScript
        {
            public achievement_orbit_uary() : base("achievement_orbit_uary") { }

            public override bool OnCheck(Player source, Unit target)
            {
                if (!target)
                    return false;

                Creature leviathan = target.ToCreature();
                if (leviathan)
                    if (leviathan.GetAI().GetData(Leviathan.DataOrbitAchievements) == 4)
                        return true;

                return false;
            }
        }

        [Script]
        class spell_load_into_catapult : SpellScriptLoader
        {
            enum Spells
            {
                SPELL_PASSENGER_LOADED = 62340,
            }

            public spell_load_into_catapult() : base("spell_load_into_catapult") { }

            class spell_load_into_catapult_AuraScript : AuraScript
            {
                void OnApply(AuraEffect aurEff, AuraEffectHandleModes mode)
                {
                    Unit owner = GetOwner().ToUnit();
                    if (!owner)
                        return;

                    owner.CastSpell(owner, 62340, true);
                }

                void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
                {
                    Unit owner = GetOwner().ToUnit();
                    if (!owner)
                        return;

                    owner.RemoveAurasDueToSpell(62340);
                }

                public override void Register()
                {
                    OnEffectApply.Add(new EffectApplyHandler(OnApply, 0, AuraType.ControlVehicle, AuraEffectHandleModes.Real));
                    OnEffectRemove.Add(new EffectApplyHandler(OnRemove, 0, AuraType.ControlVehicle, AuraEffectHandleModes.RealOrReapplyMask));
                }
            }

            public override AuraScript GetAuraScript()
            {
                return new spell_load_into_catapult_AuraScript();
            }
        }

        [Script]
        class spell_auto_repair : SpellScriptLoader
        {
            public spell_auto_repair() : base("spell_auto_repair") { }

            class spell_auto_repair_SpellScript : SpellScript
            {
                void CheckCooldownForTarget(SpellMissInfo missInfo)
                {
                    if (missInfo != SpellMissInfo.None)
                        return;

                    if (GetHitUnit().HasAuraEffect(62705, 2))   // Check presence of dummy aura indicating cooldown
                    {
                        PreventHitEffect(0);
                        PreventHitDefaultEffect(1);
                        PreventHitDefaultEffect(2);
                        //! Currently this doesn't work: if we call PreventHitAura(), the existing aura will be removed
                        //! because of recent aura refreshing changes. Since removing the existing aura negates the idea
                        //! of a cooldown marker, we just let the dummy aura refresh itself without executing the other SpellEffectName.
                        //! The spelleffects can be executed by letting the dummy aura expire naturally.
                        //! This is a temporary solution only.
                    }
                }

                void HandleScript(uint eff)
                {
                    Vehicle vehicle = GetHitUnit().GetVehicleKit();
                    if (!vehicle)
                        return;

                    Player driver = vehicle.GetPassenger(0) ? vehicle.GetPassenger(0).ToPlayer() : null;
                    if (!driver)
                        return;

                    driver.TextEmote(Says.EmoteRepair, driver, true);

                    InstanceScript instance = driver.GetInstanceScript();
                    if (instance == null)
                        return;

                    // Actually should/could use basepoints (100) for this spell effect as percentage of health, but oh well.
                    vehicle.GetBase().SetFullHealth();

                    // For achievement
                    instance.SetData(InstanceAchievementData.DataUnbroken, 0);
                }

                public override void Register()
                {
                    OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.ScriptEffect));
                    BeforeHit.Add(new BeforeHitHandler(CheckCooldownForTarget));
                }
            }

            public override SpellScript GetSpellScript()
            {
                return new spell_auto_repair_SpellScript();
            }
        }

        [Script]
        class spell_systems_shutdown : SpellScriptLoader
        {
            public spell_systems_shutdown() : base("spell_systems_shutdown") { }

            class spell_systems_shutdown_AuraScript : AuraScript
            {
                void OnApply(AuraEffect aurEff, AuraEffectHandleModes mode)
                {
                    Creature owner = GetOwner().ToCreature();
                    if (!owner)
                        return;

                    //! This could probably in the SPELL_EFFECT_SEND_EVENT handler too:
                    owner.AddUnitState(UnitState.Stunned | UnitState.Root);
                    owner.SetFlag(UnitFields.Flags, UnitFlags.Stunned);
                    owner.RemoveAurasDueToSpell(Spells.GatheringSpeed);
                }

                void OnRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
                {
                    Creature owner = GetOwner().ToCreature();
                    if (!owner)
                        return;

                    owner.RemoveFlag(UnitFields.Flags, UnitFlags.Stunned);
                }

                public override void Register()
                {
                    OnEffectApply.Add(new EffectApplyHandler(OnApply, 0, AuraType.ModDamagePercentTaken, AuraEffectHandleModes.Real));
                    OnEffectRemove.Add(new EffectApplyHandler(OnRemove, 0, AuraType.ModDamagePercentTaken, AuraEffectHandleModes.Real));
                }
            }

            public override AuraScript GetAuraScript()
            {
                return new spell_systems_shutdown_AuraScript();
            }
        }

        [Script]
        class spell_pursue : SpellScriptLoader
        {
            public spell_pursue() : base("spell_pursue") { }

            const uint AREA_FORMATION_GROUNDS = 4652;

            class spell_pursue_SpellScript : SpellScript
            {
                public override bool Load()
                {
                    _target = null;
                    return true;
                }

                void FilterTargets(List<WorldObject> targets)
                {
                    targets.RemoveAll(new Predicate<WorldObject>(target =>
                    {
                    //! No players, only vehicles (@todo check if blizzlike)
                    Creature creatureTarget = target.ToCreature();
                        if (!creatureTarget)
                            return true;

                    //! NPC entries must match
                    if (creatureTarget.GetEntry() != Vehicles.Demolisher && creatureTarget.GetEntry() != Vehicles.Siege)
                            return true;

                    //! NPC must be a valid vehicle installation
                    Vehicle vehicle = creatureTarget.GetVehicleKit();
                        if (!vehicle)
                            return true;

                    //! Entity needs to be in appropriate area
                    if (target.GetAreaId() != AREA_FORMATION_GROUNDS)
                            return true;

                    //! Vehicle must be in use by player
                    bool playerFound = false;
                        foreach (var seat in vehicle.Seats.Values)
                        {
                            if (seat.Passenger.Guid.IsPlayer())
                            {
                                playerFound = true;
                                break;
                            }
                        }

                        return !playerFound;
                    }));

                    if (targets.Empty())
                    {
                        Creature caster = GetCaster().ToCreature();
                        if (caster)
                            caster.GetAI().EnterEvadeMode();
                    }
                    else
                    {
                        //! In the end, only one target should be selected
                        _target = targets.PickRandom();
                        FilterTargetsSubsequently(targets);
                    }
                }

                void FilterTargetsSubsequently(List<WorldObject> targets)
                {
                    targets.Clear();
                    if (_target)
                        targets.Add(_target);
                }

                void HandleScript(uint eff)
                {
                    Creature caster = GetCaster().ToCreature();
                    if (!caster)
                        return;

                    caster.GetAI().AttackStart(GetHitUnit());    // Chase target

                    foreach (var seat in caster.GetVehicleKit().Seats.Values)
                    {
                        Player passenger = Global.ObjAccessor.GetPlayer(caster, seat.Passenger.Guid);
                        if (passenger)
                        {
                            caster.GetAI().Talk(Says.EmotePursue, passenger);
                            return;
                        }
                    }
                }

                public override void Register()
                {
                    OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEnemy));
                    OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargetsSubsequently, 1, Targets.UnitSrcAreaEnemy));
                    OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.ApplyAura));
                }

                WorldObject _target;
            }

            public override SpellScript GetSpellScript()
            {
                return new spell_pursue_SpellScript();
            }
        }

        [Script]
        class spell_vehicle_throw_passenger : SpellScriptLoader
        {
            public spell_vehicle_throw_passenger() : base("spell_vehicle_throw_passenger") { }

            class spell_vehicle_throw_passenger_SpellScript : SpellScript
            {
                void HandleScript(uint effIndex)
                {
                    Spell baseSpell = GetSpell();
                    SpellCastTargets targets = baseSpell.m_targets;
                    int damage = GetEffectValue();
                    if (targets.HasTraj())
                    {
                        Vehicle vehicle = GetCaster().GetVehicleKit();
                        if (vehicle)
                        {
                            Unit passenger = vehicle.GetPassenger((sbyte)(damage - 1));
                            if (passenger)
                            {
                                // use 99 because it is 3d search
                                List<WorldObject> targetList = new List<WorldObject>();
                                var check = new WorldObjectSpellAreaTargetCheck(99, GetExplTargetDest(), GetCaster(), GetCaster(), GetSpellInfo(), SpellTargetCheckTypes.Default, null);
                                var searcher = new WorldObjectListSearcher(GetCaster(), targetList, check);
                                Cell.VisitAllObjects(GetCaster(), searcher, 99.0f);
                                float minDist = 99 * 99;
                                Unit target = null;
                                foreach (var obj in targetList)
                                {
                                    Unit unit = obj.ToUnit();
                                    if (unit)
                                    {
                                        if (unit.GetEntry() == CreatureIds.Seat)
                                        {
                                            Vehicle seat = unit.GetVehicleKit();
                                            if (seat)
                                            {
                                                if (!seat.GetPassenger(0))
                                                {
                                                    Unit device = seat.GetPassenger(2);
                                                    if (device)
                                                        if (!device.GetCurrentSpell(CurrentSpellTypes.Channeled))
                                                        {
                                                            float dist = unit.GetExactDistSq(targets.GetDstPos());
                                                            if (dist < minDist)
                                                            {
                                                                minDist = dist;
                                                                target = unit;
                                                            }
                                                        }
                                                }
                                            }
                                        }
                                    }
                                }
                                if (target && target.IsWithinDist2d(targets.GetDstPos(), GetSpellInfo().GetEffect(effIndex).CalcRadius() * 2)) // now we use *2 because the location of the seat is not correct
                                    passenger.EnterVehicle(target, 0);
                                else
                                {
                                    passenger.ExitVehicle();
                                    passenger.GetMotionMaster().MoveJump(targets.GetDstPos(), targets.GetSpeedXY(), targets.GetSpeedZ());
                                }
                            }
                        }
                    }
                }

                public override void Register()
                {
                    OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.Dummy));
                }
            }

            public override SpellScript GetSpellScript()
            {
                return new spell_vehicle_throw_passenger_SpellScript();
            }
        }
    }
}
