/*
 * 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 Framework.Dynamic;
using Framework.GameMath;
using Game;
using Game.AI;
using Game.Entities;
using Game.Maps;
using Game.Movement;
using Game.Network.Packets;
using Game.Scripting;
using Game.Spells;
using System;
using System.Collections.Generic;
using System.Linq;

namespace Scripts.Northrend.IcecrownCitadel
{
    struct GunshipTexts
    {
        // High Overlord Saurfang
        public const uint SaySaurfangIntro1 = 0;
        public const uint SaySaurfangIntro2 = 1;
        public const uint SaySaurfangIntro3 = 2;
        public const uint SaySaurfangIntro4 = 3;
        public const uint SaySaurfangIntro5 = 4;
        public const uint SaySaurfangIntro6 = 5;
        public const uint SaySaurfangIntroA = 6;
        public const uint SaySaurfangBoard = 7;
        public const uint SaySaurfangEnterSkybreaker = 8;
        public const uint SaySaurfangAxethrowers = 9;
        public const uint SaySaurfangRocketeers = 10;
        public const uint SaySaurfangMages = 11;
        public const byte SaySaurfangVictory = 12;
        public const byte SaySaurfangWipe = 13;

        // Muradin Bronzebeard
        public const uint SayMuradinIntro1 = 0;
        public const uint SayMuradinIntro2 = 1;
        public const uint SayMuradinIntro3 = 2;
        public const uint SayMuradinIntro4 = 3;
        public const uint SayMuradinIntro5 = 4;
        public const uint SayMuradinIntro6 = 5;
        public const uint SayMuradinIntro7 = 6;
        public const uint SayMuradinIntroH = 7;
        public const uint SayMuradinBoard = 8;
        public const uint SayMuradinEnterOrgrimmsHammer = 9;
        public const uint SayMuradinRifleman = 10;
        public const uint SayMuradinMortar = 11;
        public const uint SayMuradinSorcerers = 12;
        public const byte SayMuradinVictory = 13;
        public const byte SayMuradinWipe = 14;

        public const byte SayZafodRocketPackActive = 0;
        public const byte SayZafodRocketPackDisabled = 1;

        public const byte SayOverheat = 0;
    }

    struct GunshipEvents
    {
        // High Overlord Saurfang
        public const uint IntroH1 = 1;
        public const uint IntroH2 = 2;
        public const uint IntroSummonSkybreaker = 3;
        public const uint IntroH3 = 4;
        public const uint IntroH4 = 5;
        public const uint IntroH5 = 6;
        public const uint IntroH6 = 7;

        // Muradin Bronzebeard
        public const uint IntroA1 = 1;
        public const uint IntroA2 = 2;
        public const uint IntroSummonOrgrimsHammer = 3;
        public const uint IntroA3 = 4;
        public const uint IntroA4 = 5;
        public const uint IntroA5 = 6;
        public const uint IntroA6 = 7;
        public const uint IntroA7 = 8;

        public const uint KeepPlayerInCombat = 9;
        public const uint SummonMage = 10;
        public const uint Adds = 11;
        public const uint AddsBoardYell = 12;
        public const uint CheckRifleman = 13;
        public const uint CheckMortar = 14;
        public const uint Cleave = 15;

        public const uint Bladestorm = 16;
        public const uint WoundingStrike = 17;
    }

    struct GunshipSpells
    {
        // Applied On Friendly Transport Npcs
        public const uint FriendlyBossDamageMod = 70339;
        public const uint CheckForPlayers = 70332;
        public const uint GunshipFallTeleport = 67335;
        public const uint TeleportPlayersOnResetA = 70446;
        public const uint TeleportPlayersOnResetH = 71284;
        public const uint TeleportPlayersOnVictory = 72340;
        public const uint Achievement = 72959;
        public const uint AwardReputationBossKill = 73843;

        // Murading Bronzebeard
        // High Overlord Saurfang
        public const uint BattleFury = 69637;
        public const uint RendingThrow = 70309;
        public const uint Cleave = 15284;
        public const uint TasteOfBlood = 69634;

        // Applied On Enemy Npcs
        public const uint MeleeTargetingOnSkybreaker = 70219;
        public const uint MeleeTargetingOnOrgrimsHammer = 70294;

        // Gunship Hull
        public const uint ExplosionWipe = 72134;
        public const uint ExplosionVictory = 72137;

        // Hostile Npcs
        public const uint TeleportToEnemyShip = 70104;
        public const uint BattleExperience = 71201;
        public const uint Experienced = 71188;
        public const uint Veteran = 71193;
        public const uint Elite = 71195;
        public const uint AddsBerserk = 72525;

        // Skybreaker Sorcerer
        // Kor'Kron Battle-Mage
        public const uint ShadowChanneling = 43897;
        public const uint BelowZero = 69705;

        // Skybreaker Rifleman
        // Kor'Kron Axethrower
        public const uint Shoot = 70162;
        public const uint HurlAxe = 70161;
        public const uint BurningPitchA = 70403;
        public const uint BurningPitchH = 70397;
        public const uint BurningPitch = 69660;

        // Skybreaker Mortar Soldier
        // Kor'Kron Rocketeer
        public const uint RocketArtilleryA = 70609;
        public const uint RocketArtilleryH = 69678;
        public const uint BurningPitchDamageA = 70383;
        public const uint BurningPitchDamageH = 70374;

        // Skybreaker Marine
        // Kor'Kron Reaver
        public const uint DesperateResolve = 69647;

        // Skybreaker Sergeant
        // Kor'Kron Sergeant
        public const uint Bladestorm = 69652;
        public const uint WoundingStrike = 69651;

        //
        public const uint LockPlayersAndTapChest = 72347;
        public const uint OnSkybreakerDeck = 70120;
        public const uint OnOrgrimsHammerDeck = 70121;

        // Rocket Pack
        public const uint RocketPackDamage = 69193;
        public const uint RocketBurst = 69192;
        public const uint RocketPackUseable = 70348;

        // Alliance Gunship Cannon
        // Team.Horde Gunship Cannon
        public const uint Overheat = 69487;
        public const uint EjectAllPassengersBelowZero = 68576;
        public const uint EjectAllPassengersWipe = 50630;
    }

    struct GunshipMiscData
    {
        public const uint ItemGoblinRocketPack = 49278;

        public const byte PhaseCombat = 0;
        public const byte PhaseIntro = 1;

        public const uint MusicEncounter = 17289;

        public static Position SkybreakerAddsSpawnPos = new Position(15.91131f, 0.0f, 20.4628f, MathFunctions.PI);
        public static Position OrgrimsHammerAddsSpawnPos = new Position(60.728395f, 0.0f, 38.93467f, MathFunctions.PI);

        // Team.Horde encounter
        public static Position SkybreakerTeleportPortal = new Position(6.666975f, 0.013001f, 20.87888f, 0.0f);
        public static Position OrgrimsHammerTeleportExit = new Position(7.461699f, 0.158853f, 35.72989f, 0.0f);

        // Alliance encounter
        public static Position OrgrimsHammerTeleportPortal = new Position(47.550990f, -0.101778f, 37.61111f, 0.0f);
        public static Position SkybreakerTeleportExit = new Position(-17.55738f, -0.090421f, 21.18366f, 0.0f);

        public static SlotInfo[] SkybreakerSlotInfo =
        {
            new SlotInfo(CreatureIds.SkybreakerSorcerer, -9.479858f, 0.05663967f, 20.77026f, 4.729842f, 0 ),

            new SlotInfo(CreatureIds.SkybreakerSorcerer,  6.385986f,  4.978760f, 20.55417f, 4.694936f, 0 ),
            new SlotInfo(CreatureIds.SkybreakerSorcerer,  6.579102f, -4.674561f, 20.55060f, 1.553343f, 0 ),

            new SlotInfo(CreatureIds.SkybreakerRifleman,  -29.563900f, -17.95801f, 20.73837f, 4.747295f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerRifleman,  -18.017210f, -18.82056f, 20.79150f, 4.747295f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerRifleman,  -9.1193850f, -18.79102f, 20.58887f, 4.712389f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerRifleman,  -0.3364258f, -18.87183f, 20.56824f, 4.712389f, 30 ),

            new SlotInfo(CreatureIds.SkybreakerRifleman,  -34.705810f, -17.67261f, 20.51523f, 4.729842f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerRifleman,  -23.562010f, -18.28564f, 20.67859f, 4.729842f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerRifleman,  -13.602780f, -18.74268f, 20.59622f, 4.712389f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerRifleman,  -4.3350220f, -18.84619f, 20.58234f, 4.712389f, 30 ),

            new SlotInfo(CreatureIds.SkybreakerMortarSoldier,  -31.70142f, 18.02783f, 20.77197f, 4.712389f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerMortarSoldier,  -9.368652f, 18.75806f, 20.65335f, 4.712389f, 30 ),

            new SlotInfo(CreatureIds.SkybreakerMortarSoldier,  -20.40851f, 18.40381f, 20.50647f, 4.694936f, 30 ),
            new SlotInfo(CreatureIds.SkybreakerMortarSoldier,  0.1585693f, 18.11523f, 20.41949f, 4.729842f, 30 ),

            new SlotInfo(CreatureIds.SkybreakerMarine, SkybreakerTeleportPortal, 0 ),
            new SlotInfo(CreatureIds.SkybreakerMarine, SkybreakerTeleportPortal, 0 ),

            new SlotInfo(CreatureIds.SkybreakerMarine, SkybreakerTeleportPortal, 0 ),
            new SlotInfo(CreatureIds.SkybreakerMarine, SkybreakerTeleportPortal, 0 ),
            
            new SlotInfo(CreatureIds.SkybreakerSergeant, SkybreakerTeleportPortal, 0 ),

            new SlotInfo(CreatureIds.SkybreakerSergeant, SkybreakerTeleportPortal, 0 )
        };
        public static SlotInfo[] OrgrimsHammerSlotInfo =
        {
            new SlotInfo(CreatureIds.KorKronBattleMage, 13.58548f, 0.3867192f, 34.99243f, 1.53589f, 0 ),

            new SlotInfo(CreatureIds.KorKronBattleMage, 47.29290f, -4.308941f, 37.55550f, 1.570796f, 0 ),
            new SlotInfo(CreatureIds.KorKronBattleMage, 47.34621f,  4.032004f, 37.70952f, 4.817109f, 0 ),
        
            new SlotInfo(CreatureIds.KorKronAxeThrower, -12.09280f, 27.65942f, 33.58557f, 1.53589f, 30 ),
            new SlotInfo(CreatureIds.KorKronAxeThrower, -3.170555f, 28.30652f, 34.21082f, 1.53589f, 30 ),
            new SlotInfo(CreatureIds.KorKronAxeThrower, 14.928040f, 26.18018f, 35.47803f, 1.53589f, 30 ),
            new SlotInfo(CreatureIds.KorKronAxeThrower, 24.703310f, 25.36584f, 35.97845f, 1.53589f, 30 ),

            new SlotInfo(CreatureIds.KorKronAxeThrower, -16.65302f, 27.59668f, 33.18726f, 1.53589f, 30 ),
            new SlotInfo(CreatureIds.KorKronAxeThrower, -8.084572f, 28.21448f, 33.93805f, 1.53589f, 30 ),
            new SlotInfo(CreatureIds.KorKronAxeThrower, 7.594765f, 27.41968f, 35.00775f, 1.53589f, 30 ),
            new SlotInfo(CreatureIds.KorKronAxeThrower, 20.763390f, 25.58215f, 35.75287f, 1.53589f, 30 ),

            new SlotInfo(CreatureIds.KorKronRocketeer, -11.44849f, -25.71838f, 33.64343f, 1.518436f, 30 ),
            new SlotInfo(CreatureIds.KorKronRocketeer, 12.30336f, -25.69653f, 35.32373f, 1.518436f, 30 ),

            new SlotInfo(CreatureIds.KorKronRocketeer, -0.05931854f, -25.46399f, 34.50592f, 1.518436f, 30 ),
            new SlotInfo(CreatureIds.KorKronRocketeer, 27.62149000f, -23.48108f, 36.12708f, 1.518436f, 30 ),

            new SlotInfo(CreatureIds.KorKronReaver, OrgrimsHammerTeleportPortal, 0 ),
            new SlotInfo(CreatureIds.KorKronReaver, OrgrimsHammerTeleportPortal, 0 ),

            new SlotInfo(CreatureIds.KorKronReaver, OrgrimsHammerTeleportPortal, 0 ),
            new SlotInfo(CreatureIds.KorKronReaver, OrgrimsHammerTeleportPortal, 0 ),
        
            new SlotInfo(CreatureIds.KorKronSergeant, OrgrimsHammerTeleportPortal, 0 ),

            new SlotInfo(CreatureIds.KorKronSergeant, OrgrimsHammerTeleportPortal, 0 )
        };

        public const uint MuradinExitPathSize = 10;
        public static Vector3[] MuradinExitPath =
        {
            new Vector3(8.130936f, -0.2699585f, 20.31728f ),
            new Vector3(6.380936f, -0.2699585f, 20.31728f ),
            new Vector3(3.507703f, 0.02986573f, 20.78463f ),
            new Vector3(-2.767633f, 3.743143f, 20.37663f ),
            new Vector3(-4.017633f, 4.493143f, 20.12663f ),
            new Vector3(-7.242224f, 6.856013f, 20.03468f ),
            new Vector3(-7.742224f, 8.606013f, 20.78468f ),
            new Vector3(-7.992224f, 9.856013f, 21.28468f ),
            new Vector3(-12.24222f, 23.10601f, 21.28468f ),
            new Vector3(-14.88477f, 25.20844f, 21.59985f )
        };

        public const uint SaurfangExitPathSize = 13;
        public static Vector3[] SaurfangExitPath =
        {
            new Vector3(30.43987f, 0.1475817f, 36.10674f ),
            new Vector3(21.36141f, -3.056458f, 35.42970f ),  
            new Vector3(19.11141f, -3.806458f, 35.42970f ),
            new Vector3(19.01736f, -3.299440f, 35.39428f ),
            new Vector3(18.6747f, -5.862823f, 35.66611f ),
            new Vector3(18.6747f, -7.862823f, 35.66611f ),
            new Vector3(18.1747f, -17.36282f, 35.66611f ),
            new Vector3(18.1747f, -22.61282f, 35.66611f ),
            new Vector3(17.9247f, -24.36282f, 35.41611f ),
            new Vector3(17.9247f, -26.61282f, 35.66611f ),
            new Vector3(17.9247f, -27.86282f, 35.66611f ),
            new Vector3(17.9247f, -29.36282f, 35.66611f ),
            new Vector3(15.33203f, -30.42621f, 35.93796f )
        };
    }

    struct EncounterActions
    {
        public const int SpawnMage = 1;
        public const int SpawnAllAdds = 2;
        public const int ClearSlot = 3;
        public const int SetSlot = 4;
        public const int ShipVisits = 5;
    }

    enum PassengerSlots
    {
        // Freezing The Cannons
        FreezeMage = 0,

        // Channeling The Portal, Refilled With Adds That Board Player'S Ship
        Mage1 = 1,
        Mage2 = 2,

        // Rifleman
        Rifleman1 = 3,
        Rifleman2 = 4,
        Rifleman3 = 5,
        Rifleman4 = 6,

        // Additional Rifleman On 25 Man
        Rifleman5 = 7,
        Rifleman6 = 8,
        Rifleman7 = 9,
        Rifleman8 = 10,

        // Mortar
        Mortar1 = 11,
        Mortar2 = 12,

        // Additional Spawns On 25 Man
        Mortar3 = 13,
        Mortar4 = 14,

        // Marines
        Marine1 = 15,
        Marine2 = 16,

        // Additional Spawns On 25 Man
        Marine3 = 17,
        Marine4 = 18,

        // Sergeants
        Sergeant1 = 19,

        // Additional Spawns On 25 Man
        Sergeant2 = 20,

        Max
    }

    class SlotInfo
    {
        public SlotInfo(uint _entry, float x, float y, float z, float o, uint _cooldown)
        {
            Entry = _entry;
            TargetPosition = new Position(x, y, z);
            Cooldown = _cooldown;
        }
        public SlotInfo(uint _entry, Position pos, uint _cooldown)
        {
            Entry = _entry;
            TargetPosition = pos;
            Cooldown = _cooldown;
        }

        public uint Entry;
        public Position TargetPosition;
        public uint Cooldown;
    }

    class PassengerController
    {
        public PassengerController()
        {
            ResetSlots(Team.Horde);
        }

        public void SetTransport(Transport transport) { _transport = transport; }

        public void ResetSlots(Team team)
        {
            _transport = null;
            _spawnPoint = team == Team.Horde ? GunshipMiscData.OrgrimsHammerAddsSpawnPos : GunshipMiscData.SkybreakerAddsSpawnPos;
            _slotInfo = team == Team.Horde ? GunshipMiscData.OrgrimsHammerSlotInfo : GunshipMiscData.SkybreakerSlotInfo;
        }

        public bool SummonCreatures(PassengerSlots first, PassengerSlots last)
        {
            if (!_transport)
                return false;

            bool summoned = false;
            long now = Time.UnixTime;
            for (int i = (int)first; i <= (int)last; ++i)
            {
                if (_respawnCooldowns[i] > now)
                    continue;

                if (!_controlledSlots[i].IsEmpty())
                {
                    Creature current = ObjectAccessor.GetCreature(_transport, _controlledSlots[i]);
                    if (current && current.IsAlive())
                        continue;
                }
                Creature passenger = _transport.SummonPassenger(_slotInfo[i].Entry, SelectSpawnPoint(), TempSummonType.CorpseTimedDespawn, null, 15000);
                if (passenger)
                {
                    _controlledSlots[i] = passenger.GetGUID();
                    _respawnCooldowns[i] = 0L;
                    passenger.GetAI().SetData(EncounterActions.SetSlot, (uint)i);
                    summoned = true;
                }
            }

            return summoned;
        }

        public void ClearSlot(PassengerSlots slot)
        {
            _controlledSlots[(int)slot].Clear();
            _respawnCooldowns[(int)slot] = Time.UnixTime + _slotInfo[(int)slot].Cooldown;
        }

        public bool SlotsNeedRefill(PassengerSlots first, PassengerSlots last)
        {
            for (int i = (int)first; i <= (int)last; ++i)
                if (_controlledSlots[i].IsEmpty())
                    return true;

            return false;
        }

        Position SelectSpawnPoint()
        {
            float angle = RandomHelper.FRand(-MathFunctions.PI * 0.5f, MathFunctions.PI * 0.5f);
            return new Position(_spawnPoint.GetPositionX() + 2.0f * (float)Math.Cos(angle), _spawnPoint.GetPositionY() + 2.0f * (float)Math.Sin(angle),
                _spawnPoint.GetPositionZ(), _spawnPoint.GetOrientation());
        }

        Transport _transport;
        ObjectGuid[] _controlledSlots = new ObjectGuid[(int)PassengerSlots.Max];
        long[] _respawnCooldowns = new long[(int)PassengerSlots.Max];
        Position _spawnPoint;
        SlotInfo[] _slotInfo;
    }

    class DelayedMovementEvent : BasicEvent
    {
        public DelayedMovementEvent(Creature owner, Position dest)
        {
            _owner = owner;
            _dest = dest;
        }

        public override bool Execute(ulong e_time, uint p_time)
        {
            if (!_owner.IsAlive())
                return true;

            _owner.GetMotionMaster().MovePoint(EventId.ChargePrepath, _owner, false);

            MoveSplineInit init = new MoveSplineInit(_owner);
            init.DisableTransportPathTransformations();
            init.MoveTo(_dest.GetPositionX(), _dest.GetPositionY(), _dest.GetPositionZ(), false);
            init.Launch();

            return true;
        }

        Creature _owner;
        Position _dest;
    }

    class ResetEncounterEvent : BasicEvent
    {
        public ResetEncounterEvent(Unit caster, uint spellId, ObjectGuid otherTransport)
        {
            _caster = caster;
            _spellId = spellId;
            _otherTransport = otherTransport;
        }

        public override bool Execute(ulong e_time, uint p_time)
        {
            _caster.CastSpell(_caster, _spellId, true);
            _caster.GetTransport().AddObjectToRemoveList();

            Transport go = Global.ObjAccessor.FindTransport(_otherTransport);
            if (go)
                go.AddObjectToRemoveList();

            return true;
        }

        Unit _caster;
        uint _spellId;
        ObjectGuid _otherTransport;
    }

    class BattleExperienceEvent : BasicEvent
    {
        public BattleExperienceEvent(Creature creature)
        {
            _creature = creature;
            _level = 0;
        }

        public override bool Execute(ulong e_time, uint p_time)
        {
            if (!_creature.IsAlive())
                return true;

            _creature.RemoveAurasDueToSpell(ExperiencedSpells[_level]);
            ++_level;

            _creature.CastSpell(_creature, ExperiencedSpells[_level], TriggerCastFlags.FullMask);
            if (_level < (_creature.GetMap().IsHeroic() ? 4 : 3))
            {
                _creature.m_Events.AddEvent(this, e_time + ExperiencedTimes[_level]);
                return false;
            }

            return true;
        }

        Creature _creature;
        int _level;

        public static uint[] ExperiencedSpells = { 0, GunshipSpells.Experienced, GunshipSpells.Veteran, GunshipSpells.Elite, GunshipSpells.AddsBerserk };
        public static uint[] ExperiencedTimes = { 100000, 70000, 60000, 90000, 0 };
    }

    class gunship_npc_AI : ScriptedAI
    {
        public gunship_npc_AI(Creature creature)
            : base(creature)
        {
            Instance = creature.GetInstanceScript();
            Slot = null;
            Index = 0xFFFFFFFF;

            BurningPitchId = Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.BurningPitchA : GunshipSpells.BurningPitchH;
            me.setRegeneratingHealth(false);
        }

        public override void SetData(uint type, uint data)
        {
            if (type == EncounterActions.SetSlot && data < (int)PassengerSlots.Max)
            {
                SetSlotInfo(data);

                me.SetReactState(ReactStates.Passive);

                float x, y, z, o;
                Slot.TargetPosition.GetPosition(out x, out y, out z, out o);

                me.SetTransportHomePosition(Slot.TargetPosition);
                float hx = x, hy = y, hz = z, ho = o;
                me.GetTransport().CalculatePassengerPosition(ref hx, ref hy, ref hz, ref ho);
                me.SetHomePosition(hx, hy, hz, ho);

                me.GetMotionMaster().MovePoint(EventId.ChargePrepath, Slot.TargetPosition, false);

                MoveSplineInit init = new MoveSplineInit(me);
                init.DisableTransportPathTransformations();
                init.MoveTo(x, y, z, false);
                init.Launch();
            }
        }

        public override void EnterEvadeMode(EvadeReason why)
        {
            if (!me.IsAlive() || !me.IsInCombat())
                return;

            me.DeleteThreatList();
            me.CombatStop(true);
            me.GetMotionMaster().MoveTargetedHome();
        }

        public override void JustDied(Unit killer)
        {
            if (Slot != null)
            {
                Creature captain = me.FindNearestCreature(Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? CreatureIds.IGBMuradinBrozebeard : CreatureIds.IGBHighOverlordSaurfang, 200.0f);
                if (captain)
                    captain.GetAI().SetData(EncounterActions.ClearSlot, Index);
            }
        }

        public override void MovementInform(MovementGeneratorType type, uint id)
        {
            if (type != MovementGeneratorType.Point)
                return;

            if (id == EventId.ChargePrepath && Slot != null)
            {
                me.SetFacingTo(Slot.TargetPosition.GetOrientation());
                me.m_Events.AddEvent(new BattleExperienceEvent(me), me.m_Events.CalculateTime(BattleExperienceEvent.ExperiencedTimes[0]));
                DoCast(me, GunshipSpells.BattleExperience, true);
                me.SetReactState(ReactStates.Aggressive);
            }
        }

        public override bool CanAIAttack(Unit target)
        {
            if (Instance.GetBossState(Bosses.GunshipBattle) != EncounterState.InProgress)
                return false;

            return target.HasAura(Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.OnOrgrimsHammerDeck : GunshipSpells.OnSkybreakerDeck);
        }

        public void SetSlotInfo(uint index)
        {
            Index = index;
            Slot = ((Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipMiscData.SkybreakerSlotInfo : GunshipMiscData.OrgrimsHammerSlotInfo)[Index]);
        }

        public bool SelectVictim()
        {
            if (Instance.GetBossState(Bosses.GunshipBattle) != EncounterState.InProgress)
            {
                EnterEvadeMode(EvadeReason.Other);
                return false;
            }

            if (!me.HasReactState(ReactStates.Passive))
            {
                Unit victim = me.SelectVictim();
                if (victim)
                    AttackStart(victim);
                return me.GetVictim();
            }
            else if (me.GetThreatManager().isThreatListEmpty())
            {
                EnterEvadeMode(EvadeReason.Other);
                return false;
            }

            return true;
        }

        public void TriggerBurningPitch()
        {
            if (Instance.GetBossState(Bosses.GunshipBattle) == EncounterState.InProgress &&
                !me.HasUnitState(UnitState.Casting) && !me.HasReactState(ReactStates.Passive) &&
                !me.GetSpellHistory().HasCooldown(BurningPitchId))
            {
                DoCastAOE(BurningPitchId, true);
                me.GetSpellHistory().AddCooldown(BurningPitchId, 0, TimeSpan.FromMilliseconds(RandomHelper.URand(3000, 4000)));
            }
        }

        public InstanceScript Instance;
        public SlotInfo Slot;
        public uint Index;
        public uint BurningPitchId;
    }

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

        class npc_gunshipAI : NullCreatureAI
        {
            public npc_gunshipAI(Creature creature) : base(creature)
            {
                _teamInInstance = (Team)creature.GetInstanceScript().GetData(DataTypes.TeamInInstance);
                _summonedFirstMage = false;
                _died = false;

                me.setRegeneratingHealth(false);
            }

            public override void DamageTaken(Unit source, ref uint damage)
            {
                if (damage >= me.GetHealth())
                {
                    JustDied(null);
                    damage = (uint)me.GetHealth() - 1;
                    return;
                }

                if (_summonedFirstMage)
                    return;

                if (me.GetTransport().GetEntry() != (_teamInInstance == Team.Horde ? GameObjectIds.TheSkybreaker_H : GameObjectIds.OrgrimsHammer_A))
                    return;

                if (!me.HealthBelowPctDamaged(90, damage))
                    return;

                _summonedFirstMage = true;
                Creature captain = me.FindNearestCreature(_teamInInstance == Team.Horde ? CreatureIds.IGBMuradinBrozebeard : CreatureIds.IGBHighOverlordSaurfang, 100.0f);
                if (captain)
                    captain.GetAI().DoAction(EncounterActions.SpawnMage);
            }

            public override void JustDied(Unit killer)
            {
                if (_died)
                    return;

                _died = true;

                bool isVictory = me.GetTransport().GetEntry() == GameObjectIds.TheSkybreaker_H || me.GetTransport().GetEntry() == GameObjectIds.OrgrimsHammer_A;
                InstanceScript instance = me.GetInstanceScript();
                instance.SetBossState(Bosses.GunshipBattle, isVictory ? EncounterState.Done : EncounterState.Fail);
                Creature creature = me.FindNearestCreature(me.GetEntry() == CreatureIds.OrgrimsHammer ? CreatureIds.TheSkybreaker : CreatureIds.OrgrimsHammer, 200.0f);
                if (creature)
                {
                    instance.SendEncounterUnit(EncounterFrameType.Disengage, creature);
                    creature.RemoveAurasDueToSpell(GunshipSpells.CheckForPlayers);
                }

                instance.SendEncounterUnit(EncounterFrameType.Disengage, me);
                me.RemoveAurasDueToSpell(GunshipSpells.CheckForPlayers);

                me.GetMap().SetZoneMusic(AreaIds.IcecrownCitadel, 0);
                List<Creature> creatures = new List<Creature>();
                me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.MartyrStalkerIGBSaurfang, MapConst.SizeofGrids);
                foreach (var stalker in creatures)
                {
                    stalker.RemoveAllAuras();
                    stalker.DeleteThreatList();
                    stalker.CombatStop(true);
                }


                uint explosionSpell = isVictory ? GunshipSpells.ExplosionVictory : GunshipSpells.ExplosionWipe;
                creatures.Clear();
                me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.GunshipHull, 200.0f);
                foreach (var hull in creatures)
                {
                    if (hull.GetTransport() != me.GetTransport())
                        continue;

                    hull.CastSpell(hull, explosionSpell, TriggerCastFlags.FullMask);
                }

                creatures.Clear();
                me.GetCreatureListWithEntryInGrid(creatures, _teamInInstance == Team.Horde ? CreatureIds.HordeGunshipCannon : CreatureIds.AllianceGunshipCannon, 200.0f);
                foreach (var cannon in creatures)
                {
                    if (isVictory)
                    {
                        cannon.CastSpell(cannon, GunshipSpells.EjectAllPassengersBelowZero, TriggerCastFlags.FullMask);
                        cannon.RemoveVehicleKit();
                    }
                    else
                        cannon.CastSpell(cannon, GunshipSpells.EjectAllPassengersWipe, TriggerCastFlags.FullMask);
                }

                uint creatureEntry = CreatureIds.IGBMuradinBrozebeard;
                byte textId = isVictory ? GunshipTexts.SayMuradinVictory : GunshipTexts.SayMuradinWipe;
                if (_teamInInstance == Team.Horde)
                {
                    creatureEntry = CreatureIds.IGBHighOverlordSaurfang;
                    textId = isVictory ? GunshipTexts.SaySaurfangVictory : GunshipTexts.SaySaurfangWipe;
                }
                creature = me.FindNearestCreature(creatureEntry, 100.0f);
                if (creature)
                    creature.GetAI().Talk(textId);

                if (isVictory)
                {
                    Transport go = Global.ObjAccessor.FindTransport(instance.GetGuidData(Bosses.GunshipBattle));
                    if (go)
                        go.EnableMovement(true);

                    me.GetTransport().EnableMovement(true);
                    Creature ship = me.FindNearestCreature(_teamInInstance == Team.Horde ? CreatureIds.OrgrimsHammer : CreatureIds.TheSkybreaker, 200.0f);
                    if (ship)
                    {
                        ship.CastSpell(ship, GunshipSpells.TeleportPlayersOnVictory, TriggerCastFlags.FullMask);
                        ship.CastSpell(ship, GunshipSpells.Achievement, TriggerCastFlags.FullMask);
                        ship.CastSpell(ship, GunshipSpells.AwardReputationBossKill, TriggerCastFlags.FullMask);
                    }

                    creatures.Clear();
                    me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.SkybreakerMarine, 200.0f);
                    me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.SkybreakerSergeant, 200.0f);
                    me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.KorKronReaver, 200.0f);
                    me.GetCreatureListWithEntryInGrid(creatures, CreatureIds.KorKronSergeant, 200.0f);
                    foreach (var obj in creatures)
                        obj.DespawnOrUnsummon(1);
                }
                else
                {
                    uint teleportSpellId = _teamInInstance == Team.Horde ? GunshipSpells.TeleportPlayersOnResetH : GunshipSpells.TeleportPlayersOnResetA;
                    me.m_Events.AddEvent(new ResetEncounterEvent(me, teleportSpellId, me.GetInstanceScript().GetGuidData(DataTypes.EnemyGunship)),
                        me.m_Events.CalculateTime(8000));
                }

                instance.SetBossState(Bosses.GunshipBattle, isVictory ? EncounterState.Done : EncounterState.Fail);
            }

            public override void SetGUID(ObjectGuid guid, int id = 0)
            {
                if (id != EncounterActions.ShipVisits)
                    return;

                if (!_shipVisits.ContainsKey(guid))
                    _shipVisits[guid] = 1;
                else
                    ++_shipVisits[guid];
            }

            public override uint GetData(uint id)
            {
                if (id != EncounterActions.ShipVisits)
                    return 0;

                uint max = 0;
                foreach (var count in _shipVisits.Values)
                    max = Math.Max(max, count);

                return max;
            }

            Team _teamInInstance;
            Dictionary<ObjectGuid, uint> _shipVisits = new Dictionary<ObjectGuid, uint>();
            bool _summonedFirstMage;
            bool _died;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            if (!creature.GetTransport())
                return null;

            return InstanceIcecrownCitadel.GetInstanceAI<npc_gunshipAI>(creature);
        }
    }

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

        class npc_high_overlord_saurfang_igbAI : ScriptedAI
        {
            public npc_high_overlord_saurfang_igbAI(Creature creature)
                : base(creature)
            {
                _instance = creature.GetInstanceScript();

                _controller.ResetSlots(Team.Horde);
                _controller.SetTransport(creature.GetTransport());
                me.setRegeneratingHealth(false);
                me.m_CombatDistance = 70.0f;
            }

            public override void InitializeAI()
            {
                base.InitializeAI();

                _events.Reset();
                _firstMageCooldown = Time.UnixTime + 60;
                _axethrowersYellCooldown = 0L;
                _rocketeersYellCooldown = 0L;
            }

            public override void EnterCombat(Unit target)
            {
                _events.SetPhase(GunshipMiscData.PhaseCombat);
                DoCast(me, _instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.FriendlyBossDamageMod : GunshipSpells.MeleeTargetingOnOrgrimsHammer, true);
                DoCast(me, GunshipSpells.BattleFury, true);
                _events.ScheduleEvent(GunshipEvents.Cleave, RandomHelper.URand(2000, 10000));
            }

            public override void EnterEvadeMode(EvadeReason why)
            {
                if (!me.IsAlive())
                    return;

                me.DeleteThreatList();
                me.CombatStop(true);
                me.GetMotionMaster().MoveTargetedHome();

                Reset();
            }

            public override void DoAction(int action)
            {
                if (action == Actions.EnemyGunshipTalk)
                {
                    Creature muradin = me.FindNearestCreature(CreatureIds.IGBMuradinBrozebeard, 100.0f);
                    if (muradin)
                        muradin.GetAI().DoAction(EncounterActions.SpawnAllAdds);

                    Talk(GunshipTexts.SaySaurfangIntro5);
                    _events.ScheduleEvent(GunshipEvents.IntroH5, 4000);
                    _events.ScheduleEvent(GunshipEvents.IntroH6, 11000);
                    _events.ScheduleEvent(GunshipEvents.KeepPlayerInCombat, 1);

                    _instance.SetBossState(Bosses.GunshipBattle, EncounterState.InProgress);
                    // Combat starts now
                    Creature skybreaker = me.FindNearestCreature(CreatureIds.TheSkybreaker, 100.0f);
                    if (skybreaker)
                        _instance.SendEncounterUnit(EncounterFrameType.Engage, skybreaker, 1);

                    Creature orgrimsHammer = me.FindNearestCreature(CreatureIds.OrgrimsHammer, 100.0f);
                    if (orgrimsHammer)
                    {
                        _instance.SendEncounterUnit(EncounterFrameType.Engage, orgrimsHammer, 2);
                        orgrimsHammer.CastSpell(orgrimsHammer, GunshipSpells.CheckForPlayers, TriggerCastFlags.FullMask);
                    }

                    me.GetMap().SetZoneMusic(AreaIds.IcecrownCitadel, GunshipMiscData.MusicEncounter);
                }
                else if (action == EncounterActions.SpawnMage)
                {
                    long now = Time.UnixTime;
                    if (_firstMageCooldown > now)
                        _events.ScheduleEvent(GunshipEvents.SummonMage, (uint)(_firstMageCooldown - now) * Time.InMilliseconds);
                    else
                        _events.ScheduleEvent(GunshipEvents.SummonMage, 1);
                }
                else if (action == EncounterActions.SpawnAllAdds)
                {
                    _events.ScheduleEvent(GunshipEvents.Adds, 12000);
                    _events.ScheduleEvent(GunshipEvents.CheckRifleman, 13000);
                    _events.ScheduleEvent(GunshipEvents.CheckMortar, 13000);
                    if (Is25ManRaid())
                        _controller.SummonCreatures(PassengerSlots.Mage1, PassengerSlots.Mortar4);
                    else
                    {
                        _controller.SummonCreatures(PassengerSlots.Mage1, PassengerSlots.Mage2);
                        _controller.SummonCreatures(PassengerSlots.Mortar1, PassengerSlots.Mortar2);
                        _controller.SummonCreatures(PassengerSlots.Rifleman1, PassengerSlots.Rifleman4);
                    }
                }
                else if (action == Actions.ExitShip)
                {
                    Position pos = new Position(GunshipMiscData.SaurfangExitPath[GunshipMiscData.SaurfangExitPathSize - 1].X, GunshipMiscData.SaurfangExitPath[GunshipMiscData.SaurfangExitPathSize - 1].Y, GunshipMiscData.SaurfangExitPath[GunshipMiscData.SaurfangExitPathSize - 1].Z);
                    me.GetMotionMaster().MovePoint(EventId.ChargePrepath, pos, false);

                    var path = GunshipMiscData.SaurfangExitPath;//, SaurfangExitPath + SaurfangExitPathSize);

                    MoveSplineInit init = new MoveSplineInit(me);
                    init.DisableTransportPathTransformations();
                    init.MovebyPath(path, 0);
                    init.Launch();

                    me.DespawnOrUnsummon(18000);
                }
            }

            public override void SetData(uint type, uint data)
            {
                if (type == EncounterActions.ClearSlot)
                {
                    _controller.ClearSlot((PassengerSlots)data);
                    if (data == (uint)PassengerSlots.FreezeMage)
                        _events.ScheduleEvent(GunshipEvents.SummonMage, RandomHelper.URand(30000, 33500));
                }
            }

            public override void sGossipSelect(Player player, uint menuId, uint gossipListId)
            {
                me.RemoveFlag64(UnitFields.NpcFlags, NPCFlags.Gossip);
                me.GetTransport().EnableMovement(true);
                _events.SetPhase(GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroH1, 5000, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroH2, 16000, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroSummonSkybreaker, 24600, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroH3, 29600, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroH4, 39200, 0, GunshipMiscData.PhaseIntro);
            }

            public override void DamageTaken(Unit u, ref uint damage)
            {
                if (me.HealthBelowPctDamaged(65, damage) && !me.HasAura(GunshipSpells.TasteOfBlood))
                    DoCast(me, GunshipSpells.TasteOfBlood, true);

                if (damage > me.GetHealth())
                    damage = (uint)me.GetHealth() - 1;
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim() && !_events.IsInPhase(GunshipMiscData.PhaseIntro) && _instance.GetBossState(Bosses.GunshipBattle) != EncounterState.InProgress)
                    return;

                _events.Update(diff);

                _events.ExecuteEvents(eventId =>
                {
                    switch (eventId)
                    {
                        case GunshipEvents.IntroH1:
                            Talk(GunshipTexts.SaySaurfangIntro1);
                            break;
                        case GunshipEvents.IntroH2:
                            Talk(GunshipTexts.SaySaurfangIntro2);
                            break;
                        case GunshipEvents.IntroSummonSkybreaker:
                            Global.TransportMgr.CreateTransport(GameObjectIds.TheSkybreaker_H, 0, me.GetMap());
                            break;
                        case GunshipEvents.IntroH3:
                            Talk(GunshipTexts.SaySaurfangIntro3);
                            break;
                        case GunshipEvents.IntroH4:
                            Talk(GunshipTexts.SaySaurfangIntro4);
                            break;
                        case GunshipEvents.IntroH5:
                            Creature muradin = me.FindNearestCreature(CreatureIds.IGBMuradinBrozebeard, 100.0f);
                            if (muradin)
                                muradin.GetAI().Talk(GunshipTexts.SayMuradinIntroH);
                            break;
                        case GunshipEvents.IntroH6:
                            Talk(GunshipTexts.SaySaurfangIntro6);
                            break;
                        case GunshipEvents.KeepPlayerInCombat:
                            if (_instance.GetBossState(Bosses.GunshipBattle) == EncounterState.InProgress)
                            {
                                _instance.DoCastSpellOnPlayers(GunshipSpells.LockPlayersAndTapChest);
                                _events.ScheduleEvent(GunshipEvents.KeepPlayerInCombat, RandomHelper.URand(5000, 8000));
                            }
                            break;
                        case GunshipEvents.SummonMage:
                            Talk(GunshipTexts.SaySaurfangMages);
                            _controller.SummonCreatures(PassengerSlots.FreezeMage, PassengerSlots.FreezeMage);
                            break;
                        case GunshipEvents.Adds:
                            Talk(GunshipTexts.SaySaurfangEnterSkybreaker);
                            _controller.SummonCreatures(PassengerSlots.Mage1, PassengerSlots.Mage2);
                            _controller.SummonCreatures(PassengerSlots.Marine1, Is25ManRaid() ? PassengerSlots.Marine4 : PassengerSlots.Marine2);
                            _controller.SummonCreatures(PassengerSlots.Sergeant1, Is25ManRaid() ? PassengerSlots.Sergeant2 : PassengerSlots.Sergeant1);
                            Transport orgrimsHammer = me.GetTransport();
                            if (orgrimsHammer)
                                orgrimsHammer.SummonPassenger(CreatureIds.TeleportPortal, GunshipMiscData.OrgrimsHammerTeleportPortal, TempSummonType.TimedDespawn, null, 21000);

                            Transport skybreaker = Global.ObjAccessor.FindTransport(_instance.GetGuidData(Bosses.GunshipBattle));
                            if (skybreaker)
                                skybreaker.SummonPassenger(CreatureIds.TeleportExit, GunshipMiscData.SkybreakerTeleportExit, TempSummonType.TimedDespawn, null, 23000);

                            _events.ScheduleEvent(GunshipEvents.AddsBoardYell, 6000);
                            _events.ScheduleEvent(GunshipEvents.Adds, 60000);
                            break;
                        case GunshipEvents.AddsBoardYell:
                            muradin = me.FindNearestCreature(CreatureIds.IGBMuradinBrozebeard, 200.0f);
                            if (muradin)
                                muradin.GetAI().Talk(GunshipTexts.SayMuradinBoard);
                            break;
                        case GunshipEvents.CheckRifleman:
                            if (_controller.SummonCreatures(PassengerSlots.Rifleman1, Is25ManRaid() ? PassengerSlots.Rifleman8 : PassengerSlots.Rifleman4))
                            {
                                if (_axethrowersYellCooldown < Time.UnixTime)
                                {
                                    Talk(GunshipTexts.SaySaurfangAxethrowers);
                                    _axethrowersYellCooldown = Time.UnixTime + 5;
                                }
                            }
                            _events.ScheduleEvent(GunshipEvents.CheckRifleman, 1000);
                            break;
                        case GunshipEvents.CheckMortar:
                            if (_controller.SummonCreatures(PassengerSlots.Mortar1, Is25ManRaid() ? PassengerSlots.Mortar4 : PassengerSlots.Mortar2))
                            {
                                if (_rocketeersYellCooldown < Time.UnixTime)
                                {
                                    Talk(GunshipTexts.SaySaurfangRocketeers);
                                    _rocketeersYellCooldown = Time.UnixTime + 5;
                                }
                            }
                            _events.ScheduleEvent(GunshipEvents.CheckMortar, 1000);
                            break;
                        case GunshipEvents.Cleave:
                            DoCastVictim(GunshipSpells.Cleave);
                            _events.ScheduleEvent(GunshipEvents.Cleave, RandomHelper.URand(2000, 10000));
                            break;
                        default:
                            break;
                    }
                });

                if (me.IsWithinMeleeRange(me.GetVictim()))
                    DoMeleeAttackIfReady();
                else if (me.isAttackReady())
                {
                    DoCastVictim(GunshipSpells.RendingThrow);
                    me.resetAttackTimer();
                }
            }

            public override bool CanAIAttack(Unit target)
            {
                return target.HasAura(GunshipSpells.OnOrgrimsHammerDeck) || !target.IsControlledByPlayer();
            }

            PassengerController _controller = new PassengerController();
            InstanceScript _instance;
            long _firstMageCooldown;
            long _axethrowersYellCooldown;
            long _rocketeersYellCooldown;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_high_overlord_saurfang_igbAI>(creature);
        }
    }

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

        class npc_muradin_bronzebeard_igbAI : ScriptedAI
        {
            public npc_muradin_bronzebeard_igbAI(Creature creature)
                : base(creature)
            {
                _instance = creature.GetInstanceScript();

                _controller.ResetSlots(Team.Alliance);
                _controller.SetTransport(creature.GetTransport());
                me.setRegeneratingHealth(false);
                me.m_CombatDistance = 70.0f;
            }

            public override void InitializeAI()
            {
                base.InitializeAI();

                _events.Reset();
                _firstMageCooldown = Time.UnixTime + 60;
                _riflemanYellCooldown = 0L;
                _mortarYellCooldown = 0L;
            }

            public override void EnterCombat(Unit target)
            {
                _events.SetPhase(GunshipMiscData.PhaseCombat);
                DoCast(me, _instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Alliance ? GunshipSpells.FriendlyBossDamageMod : GunshipSpells.MeleeTargetingOnSkybreaker, true);
                DoCast(me, GunshipSpells.BattleFury, true);
                _events.ScheduleEvent(GunshipEvents.Cleave, RandomHelper.URand(2000, 10000));
            }

            public override void EnterEvadeMode(EvadeReason why)
            {
                if (!me.IsAlive())
                    return;

                me.DeleteThreatList();
                me.CombatStop(true);
                me.GetMotionMaster().MoveTargetedHome();

                Reset();
            }

            public override void DoAction(int action)
            {
                if (action == Actions.EnemyGunshipTalk)
                {
                    Creature muradin = me.FindNearestCreature(CreatureIds.IGBHighOverlordSaurfang, 100.0f);
                    if (muradin)
                        muradin.GetAI().DoAction(EncounterActions.SpawnAllAdds);

                    Talk(GunshipTexts.SayMuradinIntro6);
                    _events.ScheduleEvent(GunshipEvents.IntroA6, 5000);
                    _events.ScheduleEvent(GunshipEvents.IntroA7, 11000);
                    _events.ScheduleEvent(GunshipEvents.KeepPlayerInCombat, 1);

                    _instance.SetBossState(Bosses.GunshipBattle, EncounterState.InProgress);
                    // Combat starts now
                    Creature orgrimsHammer = me.FindNearestCreature(CreatureIds.OrgrimsHammer, 100.0f);
                    if (orgrimsHammer)
                        _instance.SendEncounterUnit(EncounterFrameType.Engage, orgrimsHammer, 1);

                    Creature skybreaker = me.FindNearestCreature(CreatureIds.TheSkybreaker, 100.0f);
                    if (skybreaker)
                    {
                        _instance.SendEncounterUnit(EncounterFrameType.Engage, skybreaker, 2);
                        skybreaker.CastSpell(skybreaker, GunshipSpells.CheckForPlayers, TriggerCastFlags.FullMask);
                    }

                    me.GetMap().SetZoneMusic(AreaIds.IcecrownCitadel, GunshipMiscData.MusicEncounter);
                }
                else if (action == EncounterActions.SpawnMage)
                {
                    long now = Time.UnixTime;
                    if (_firstMageCooldown < now)
                        _events.ScheduleEvent(GunshipEvents.SummonMage, (uint)(now - _firstMageCooldown) * Time.InMilliseconds);
                    else
                        _events.ScheduleEvent(GunshipEvents.SummonMage, 1);
                }
                else if (action == EncounterActions.SpawnAllAdds)
                {
                    _events.ScheduleEvent(GunshipEvents.Adds, 12000);
                    _events.ScheduleEvent(GunshipEvents.CheckRifleman, 13000);
                    _events.ScheduleEvent(GunshipEvents.CheckMortar, 13000);
                    if (Is25ManRaid())
                        _controller.SummonCreatures(PassengerSlots.Mage1, PassengerSlots.Mortar4);
                    else
                    {
                        _controller.SummonCreatures(PassengerSlots.Mage1, PassengerSlots.Mage2);
                        _controller.SummonCreatures(PassengerSlots.Mortar1, PassengerSlots.Mortar2);
                        _controller.SummonCreatures(PassengerSlots.Rifleman1, PassengerSlots.Rifleman4);
                    }
                }
            }

            public override void SetData(uint type, uint data)
            {
                if (type == EncounterActions.ClearSlot)
                {
                    _controller.ClearSlot((PassengerSlots)data);
                    if (data == (uint)PassengerSlots.FreezeMage)
                        _events.ScheduleEvent(GunshipEvents.SummonMage, RandomHelper.URand(30000, 33500));
                }
            }

            public override void sGossipSelect(Player player, uint menuId, uint gossipListId)
            {
                me.RemoveFlag64(UnitFields.NpcFlags, NPCFlags.Gossip);
                me.GetTransport().EnableMovement(true);
                _events.SetPhase(GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroA1, 5000);
                _events.ScheduleEvent(GunshipEvents.IntroA2, 10000, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroSummonOrgrimsHammer, 28000, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroA3, 33000, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroA4, 39000, 0, GunshipMiscData.PhaseIntro);
                _events.ScheduleEvent(GunshipEvents.IntroA5, 45000, 0, GunshipMiscData.PhaseIntro);
            }

            public override void DamageTaken(Unit u, ref uint damage)
            {
                if (me.HealthBelowPctDamaged(65, damage) && me.HasAura(GunshipSpells.TasteOfBlood))
                    DoCast(me, GunshipSpells.TasteOfBlood, true);

                if (damage >= me.GetHealth())
                    damage = (uint)me.GetHealth() - 1;
            }

            public override void UpdateAI(uint diff)
            {
                if (!UpdateVictim() && !_events.IsInPhase(GunshipMiscData.PhaseIntro) && _instance.GetBossState(Bosses.GunshipBattle) != EncounterState.InProgress)
                    return;

                _events.Update(diff);

                _events.ExecuteEvents(eventId =>
                {
                    switch (eventId)
                    {
                        case GunshipEvents.IntroA1:
                            Talk(GunshipTexts.SayMuradinIntro1);
                            break;
                        case GunshipEvents.IntroA2:
                            Talk(GunshipTexts.SayMuradinIntro2);
                            break;
                        case GunshipEvents.IntroSummonOrgrimsHammer:
                            Global.TransportMgr.CreateTransport(GameObjectIds.OrgrimsHammer_A, 0, me.GetMap());
                            break;
                        case GunshipEvents.IntroA3:
                            Talk(GunshipTexts.SayMuradinIntro3);
                            break;
                        case GunshipEvents.IntroA4:
                            Talk(GunshipTexts.SayMuradinIntro4);
                            break;
                        case GunshipEvents.IntroA5:
                            Talk(GunshipTexts.SayMuradinIntro5);
                            break;
                        case GunshipEvents.IntroA6:
                            Creature saurfang = me.FindNearestCreature(CreatureIds.IGBHighOverlordSaurfang, 100.0f);
                            if (saurfang)
                                saurfang.GetAI().Talk(GunshipTexts.SaySaurfangIntroA);
                            break;
                        case GunshipEvents.IntroA7:
                            Talk(GunshipTexts.SayMuradinIntro7);
                            break;
                        case GunshipEvents.KeepPlayerInCombat:
                            if (_instance.GetBossState(Bosses.GunshipBattle) == EncounterState.InProgress)
                            {
                                _instance.DoCastSpellOnPlayers(GunshipSpells.LockPlayersAndTapChest);
                                _events.ScheduleEvent(GunshipEvents.KeepPlayerInCombat, RandomHelper.URand(5000, 8000));
                            }
                            break;
                        case GunshipEvents.SummonMage:
                            Talk(GunshipTexts.SayMuradinSorcerers);
                            _controller.SummonCreatures(PassengerSlots.FreezeMage, PassengerSlots.FreezeMage);
                            break;
                        case GunshipEvents.Adds:
                            Talk(GunshipTexts.SayMuradinEnterOrgrimmsHammer);
                            _controller.SummonCreatures(PassengerSlots.Mage1, PassengerSlots.Mage2);
                            _controller.SummonCreatures(PassengerSlots.Marine1, Is25ManRaid() ? PassengerSlots.Marine4 : PassengerSlots.Marine2);
                            _controller.SummonCreatures(PassengerSlots.Sergeant1, Is25ManRaid() ? PassengerSlots.Sergeant2 : PassengerSlots.Sergeant1);

                            Transport skybreaker = me.GetTransport();
                            if (skybreaker)
                                skybreaker.SummonPassenger(CreatureIds.TeleportPortal, GunshipMiscData.SkybreakerTeleportPortal, TempSummonType.TimedDespawn, null, 21000);

                            Transport go = Global.ObjAccessor.FindTransport(_instance.GetGuidData(Bosses.GunshipBattle));
                            if (go)
                                go.SummonPassenger(CreatureIds.TeleportExit, GunshipMiscData.OrgrimsHammerTeleportExit, TempSummonType.TimedDespawn, null, 23000);

                            _events.ScheduleEvent(GunshipEvents.AddsBoardYell, 6000);
                            _events.ScheduleEvent(GunshipEvents.Adds, 60000);
                            break;
                        case GunshipEvents.AddsBoardYell:
                            saurfang = me.FindNearestCreature(CreatureIds.IGBHighOverlordSaurfang, 200.0f);
                            if (saurfang)
                                saurfang.GetAI().Talk(GunshipTexts.SaySaurfangBoard);
                            break;
                        case GunshipEvents.CheckRifleman:
                            if (_controller.SummonCreatures(PassengerSlots.Rifleman1, Is25ManRaid() ? PassengerSlots.Rifleman8 : PassengerSlots.Rifleman4))
                            {
                                if (_riflemanYellCooldown < Time.UnixTime)
                                {
                                    Talk(GunshipTexts.SayMuradinRifleman);
                                    _riflemanYellCooldown = Time.UnixTime + 5;
                                }
                            }
                            _events.ScheduleEvent(GunshipEvents.CheckRifleman, 1000);
                            break;
                        case GunshipEvents.CheckMortar:
                            if (_controller.SummonCreatures(PassengerSlots.Mortar1, Is25ManRaid() ? PassengerSlots.Mortar4 : PassengerSlots.Mortar2))
                            {
                                if (_mortarYellCooldown < Time.UnixTime)
                                {
                                    Talk(GunshipTexts.SayMuradinMortar);
                                    _mortarYellCooldown = Time.UnixTime + 5;
                                }
                            }
                            _events.ScheduleEvent(GunshipEvents.CheckMortar, 1000);
                            break;
                        case GunshipEvents.Cleave:
                            DoCastVictim(GunshipSpells.Cleave);
                            _events.ScheduleEvent(GunshipEvents.Cleave, RandomHelper.URand(2000, 10000));
                            break;
                        default:
                            break;
                    }
                });

                if (me.IsWithinMeleeRange(me.GetVictim()))
                    DoMeleeAttackIfReady();
                else if (me.isAttackReady())
                {
                    DoCastVictim(GunshipSpells.RendingThrow);
                    me.resetAttackTimer();
                }
            }

            public override bool CanAIAttack(Unit target)
            {
                if (_instance.GetBossState(Bosses.GunshipBattle) != EncounterState.InProgress)
                    return false;

                return target.HasAura(GunshipSpells.OnSkybreakerDeck) || !target.IsControlledByPlayer();
            }

            PassengerController _controller = new PassengerController();
            InstanceScript _instance;
            long _firstMageCooldown;
            long _riflemanYellCooldown;
            long _mortarYellCooldown;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_muradin_bronzebeard_igbAI>(creature);
        }
    }

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

        class npc_zafod_boomboxAI : gunship_npc_AI
        {
            public npc_zafod_boomboxAI(Creature creature)
                : base(creature) { }

            public override void Reset()
            {
                me.SetReactState(ReactStates.Passive);
            }

            public override void sGossipSelect(Player player, uint menuId, uint gossipListId)
            {
                player.AddItem(GunshipMiscData.ItemGoblinRocketPack, 1);
                player.PlayerTalkClass.SendCloseGossip();
            }

            public override void UpdateAI(uint diff)
            {
                UpdateVictim();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_zafod_boomboxAI>(creature);
        }
    }

    class npc_gunship_boarding_addAI : gunship_npc_AI
    {
        public npc_gunship_boarding_addAI(Creature creature) : base(creature)
        {
            me.m_CombatDistance = 80.0f;
            _usedDesperateResolve = false;
        }

        public override void SetData(uint type, uint data)
        {
            // detach from captain
            if (type == EncounterActions.SetSlot)
            {
                SetSlotInfo(data);

                me.SetReactState(ReactStates.Passive);

                me.m_Events.AddEvent(new DelayedMovementEvent(me, Slot.TargetPosition), me.m_Events.CalculateTime(3000 * (Index - (int)PassengerSlots.Marine1)));

                Creature captain = me.FindNearestCreature(Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? CreatureIds.IGBMuradinBrozebeard : CreatureIds.IGBHighOverlordSaurfang, 200.0f);
                if (captain)
                    captain.GetAI().SetData(EncounterActions.ClearSlot, Index);
            }
        }

        public override void MovementInform(MovementGeneratorType type, uint id)
        {
            if (type != MovementGeneratorType.Point)
                return;

            if (id == EventId.ChargePrepath && Slot != null)
            {
                Position otherTransportPos = Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipMiscData.OrgrimsHammerTeleportExit : GunshipMiscData.SkybreakerTeleportExit;
                float x, y, z, o;
                otherTransportPos.GetPosition(out x, out y, out z, out o);

                Transport myTransport = me.GetTransport();
                if (!myTransport)
                    return;

                Transport destTransport = Global.ObjAccessor.FindTransport(Instance.GetGuidData(Bosses.GunshipBattle));
                if (destTransport)
                    destTransport.CalculatePassengerPosition(ref x, ref y, ref z, ref o);

                float angle = RandomHelper.URand(0, MathFunctions.PI * 2.0f);
                x += 2.0f * (float)Math.Cos(angle);
                y += 2.0f * (float)Math.Sin(angle);

                me.SetHomePosition(x, y, z, o);
                myTransport.CalculatePassengerOffset(ref x, ref y, ref z, ref o);
                me.SetTransportHomePosition(x, y, z, o);

                me.m_Events.AddEvent(new BattleExperienceEvent(me), me.m_Events.CalculateTime(BattleExperienceEvent.ExperiencedTimes[0]));
                DoCast(me, GunshipSpells.BattleExperience, true);
                DoCast(me, GunshipSpells.TeleportToEnemyShip, true);
                DoCast(me, Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.MeleeTargetingOnOrgrimsHammer : GunshipSpells.MeleeTargetingOnSkybreaker, true);
                me.GetSpellHistory().AddCooldown(BurningPitchId, 0, TimeSpan.FromSeconds(3));

                List<Player> players = new List<Player>();
                var check = new UnitAuraCheck<Player>(true, Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.OnOrgrimsHammerDeck : GunshipSpells.OnSkybreakerDeck);
                var searcher = new PlayerListSearcher(me, players, check);
                Cell.VisitWorldObjects(me, searcher, 200.0f);

                players.RemoveAll(player => me._IsTargetAcceptable(player) || !me.CanStartAttack(player, true));

                if (!players.Empty())
                {
                    players.Sort(new ObjectDistanceOrderPred(me));
                    foreach (var pl in players)
                        me.AddThreat(pl, 1.0f);

                    AttackStart(players.First());
                }

                me.SetReactState(ReactStates.Aggressive);
            }
        }
        public override void DamageTaken(Unit attacker, ref uint damage)
        {
            if (_usedDesperateResolve)
                return;

            if (!me.HealthBelowPctDamaged(25, damage))
                return;

            _usedDesperateResolve = true;
            DoCast(me, GunshipSpells.DesperateResolve, true);
        }

        public override void UpdateAI(uint diff)
        {
            if (!SelectVictim())
            {
                TriggerBurningPitch();
                return;
            }

            if (!HasAttackablePlayerNearby())
                TriggerBurningPitch();

            DoMeleeAttackIfReady();
        }

        public override bool CanAIAttack(Unit target)
        {
            uint spellId = GunshipSpells.OnSkybreakerDeck;
            uint creatureEntry = CreatureIds.IGBMuradinBrozebeard;
            if (Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde)
            {
                spellId = GunshipSpells.OnOrgrimsHammerDeck;
                creatureEntry = CreatureIds.IGBHighOverlordSaurfang;
            }

            return target.HasAura(spellId) || target.GetEntry() == creatureEntry;
        }

        public bool HasAttackablePlayerNearby()
        {
            List<Player> players = new List<Player>();
            var check = new UnitAuraCheck<Player>(true, Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.OnOrgrimsHammerDeck : GunshipSpells.OnSkybreakerDeck);
            var searcher = new PlayerListSearcher(me, players, check);
            Cell.VisitWorldObjects(me, searcher, 200.0f);

            players.RemoveAll(player => !me._IsTargetAcceptable(player) || !me.CanStartAttack(player, true));

            return !players.Empty();
        }

        bool _usedDesperateResolve;
    }

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

        class npc_gunship_boarding_leaderAI : npc_gunship_boarding_addAI
        {
            public npc_gunship_boarding_leaderAI(Creature creature)
                : base(creature) { }

            public override void EnterCombat(Unit target)
            {
                base.EnterCombat(target);
                _events.ScheduleEvent(GunshipEvents.Bladestorm, RandomHelper.URand(13000, 18000));
                _events.ScheduleEvent(GunshipEvents.WoundingStrike, RandomHelper.URand(8000, 10000));
            }

            public override void UpdateAI(uint diff)
            {
                if (!SelectVictim())
                {
                    TriggerBurningPitch();
                    return;
                }

                _events.Update(diff);

                if (me.HasUnitState(UnitState.Casting) || me.HasAura(GunshipSpells.Bladestorm))
                    return;

                if (!HasAttackablePlayerNearby())
                    TriggerBurningPitch();

                _events.ExecuteEvents(eventId =>
                {
                    switch (eventId)
                    {
                        case GunshipEvents.Bladestorm:
                            DoCastAOE(GunshipSpells.Bladestorm);
                            _events.ScheduleEvent(GunshipEvents.Bladestorm, RandomHelper.URand(25000, 30000));
                            break;
                        case GunshipEvents.WoundingStrike:
                            DoCastVictim(GunshipSpells.WoundingStrike);
                            _events.ScheduleEvent(GunshipEvents.WoundingStrike, RandomHelper.URand(9000, 13000));
                            break;
                        default:
                            break;
                    }
                });

                DoMeleeAttackIfReady();
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_gunship_boarding_leaderAI>(creature);
        }
    }

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

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_gunship_boarding_addAI>(creature);
        }
    }

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

        class npc_gunship_gunnerAI : gunship_npc_AI
        {
            public npc_gunship_gunnerAI(Creature creature)
                : base(creature)
            {
                creature.m_CombatDistance = 200.0f;
            }

            public override void AttackStart(Unit target)
            {
                me.Attack(target, false);
            }

            public override void MovementInform(MovementGeneratorType type, uint id)
            {
                base.MovementInform(type, id);
                if (type == MovementGeneratorType.Point && id == EventId.ChargePrepath)
                    me.SetControlled(true, UnitState.Root);
            }

            public override void UpdateAI(uint diff)
            {
                if (!SelectVictim())
                {
                    TriggerBurningPitch();
                    return;
                }

                DoSpellAttackIfReady(me.GetEntry() == CreatureIds.SkybreakerRifleman ? GunshipSpells.Shoot : GunshipSpells.HurlAxe);
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_gunship_gunnerAI>(creature);
        }
    }

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

        class npc_gunship_rocketeerAI : gunship_npc_AI
        {
            public npc_gunship_rocketeerAI(Creature creature)
                : base(creature)
            {
                creature.m_CombatDistance = 200.0f;
            }

            public override void MovementInform(MovementGeneratorType type, uint id)
            {
                base.MovementInform(type, id);
                if (type == MovementGeneratorType.Point && id == EventId.ChargePrepath)
                    me.SetControlled(true, UnitState.Root);
            }

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

                if (me.HasUnitState(UnitState.Casting))
                    return;

                uint spellId = me.GetEntry() == CreatureIds.SkybreakerMortarSoldier ? GunshipSpells.RocketArtilleryA : GunshipSpells.RocketArtilleryH;
                if (me.GetSpellHistory().HasCooldown(spellId))
                    return;

                DoCastAOE(spellId, true);
                me.GetSpellHistory().AddCooldown(spellId, 0, TimeSpan.FromSeconds(9));
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_gunship_rocketeerAI>(creature);
        }
    }

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

        class npc_gunship_mageAI : gunship_npc_AI
        {
            public npc_gunship_mageAI(Creature creature) : base(creature)
            {
                me.SetReactState(ReactStates.Passive);
            }

            public override void EnterEvadeMode(EvadeReason why) { }

            public override void MovementInform(MovementGeneratorType type, uint id)
            {
                if (type != MovementGeneratorType.Point)
                    return;

                if (id == EventId.ChargePrepath && Slot != null)
                {
                    SlotInfo[] slots = Instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipMiscData.SkybreakerSlotInfo : GunshipMiscData.OrgrimsHammerSlotInfo;
                    me.SetFacingTo(slots[Index].TargetPosition.GetOrientation());
                    switch ((PassengerSlots)Index)
                    {
                        case PassengerSlots.FreezeMage:
                            DoCastAOE(GunshipSpells.BelowZero);
                            break;
                        case PassengerSlots.Mage1:
                        case PassengerSlots.Mage2:
                            DoCastAOE(GunshipSpells.ShadowChanneling);
                            break;
                        default:
                            break;
                    }

                    me.SetControlled(true, UnitState.Root);
                }
            }

            public override void UpdateAI(uint diff)
            {
                UpdateVictim();
            }

            public override bool CanAIAttack(Unit target)
            {
                return true;
            }
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return InstanceIcecrownCitadel.GetInstanceAI<npc_gunship_mageAI>(creature);
        }
    }

    /** @HACK This AI only resets MOVEMENTFLAG_ROOT on the vehicle.
              Currently the core always removes MOVEMENTFLAG_ROOT sent from client packets to prevent cheaters from freezing clients of other players
              but it actually is a valid flag - needs more research to fix both freezes and keep the flag as is (see WorldSession.ReadMovementInfo)

    Example packet:
    ClientToServer: CMSG_FORCE_MOVE_ROOT_ACK (0x00E9) Length: 67 ConnectionIndex: 0 Time: 03/04/2010 03:57:55.000 Number: 471326
    Guid:
    Movement Counter: 80
    Movement Flags: OnTransport, Root (2560)
    Extra Movement Flags: None (0)
    Time: 52291611
    Position: X: -396.0302 Y: 2482.906 Z: 249.86
    Orientation: 1.468665
    Transport GUID: Full: 0x1FC0000000000460 Type: MOTransport Low: 1120
    Transport Position: X: -6.152398 Y: -23.49037 Z: 21.64464 O: 4.827727
    Transport Time: 9926
    Transport Seat: 255
    Fall Time: 824
    */
    [Script]
    class npc_gunship_cannon : CreatureScript
    {
        public npc_gunship_cannon() : base("npc_gunship_cannon") { }

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

            public override void OnCharmed(bool apply) { }

            public override void PassengerBoarded(Unit passenger, sbyte seat, bool apply)
            {
                if (!apply)
                {
                    me.SetControlled(false, UnitState.Root);
                    me.SetControlled(true, UnitState.Root);
                }
            }
        }

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

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

        class spell_igb_rocket_pack_AuraScript : AuraScript
        {
            public override bool Validate(SpellInfo spellInfo)
            {
                return ValidateSpellInfo(GunshipSpells.RocketPackDamage, GunshipSpells.RocketBurst);
            }

            void HandlePeriodic(AuraEffect aurEff)
            {
                if (GetTarget().moveSpline.Finalized())
                    Remove(AuraRemoveMode.Expire);
            }

            void HandleRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                SpellInfo damageInfo = Global.SpellMgr.GetSpellInfo(GunshipSpells.RocketPackDamage);
                GetTarget().CastCustomSpell(GunshipSpells.RocketPackDamage, SpellValueMod.BasePoint0, (int)(2 * (damageInfo.GetEffect(0).CalcValue() + aurEff.GetTickNumber() * aurEff.GetPeriod())), null, TriggerCastFlags.FullMask);
                GetTarget().CastSpell(null, GunshipSpells.RocketBurst, TriggerCastFlags.FullMask);
            }

            public override void Register()
            {
                OnEffectPeriodic.Add(new EffectPeriodicHandler(HandlePeriodic, 0, AuraType.PeriodicDummy));
                OnEffectRemove.Add(new EffectApplyHandler(HandleRemove, 0, AuraType.PeriodicDummy, AuraEffectHandleModes.Real));
            }
        }

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

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

        class spell_igb_rocket_pack_useable_AuraScript : AuraScript
        {
            public override bool Load()
            {
                return GetOwner().GetInstanceScript() != null;
            }

            bool CheckAreaTarget(Unit target)
            {
                return target.IsTypeId(TypeId.Player) && GetOwner().GetInstanceScript().GetBossState(Bosses.GunshipBattle) != EncounterState.Done;
            }

            void HandleApply(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                Creature owner = GetOwner().ToCreature();
                if (owner)
                {
                    Player target = GetTarget().ToPlayer();
                    if (target)
                        if (target.HasItemCount(GunshipMiscData.ItemGoblinRocketPack, 1))
                            Global.CreatureTextMgr.SendChat(owner, GunshipTexts.SayZafodRocketPackActive, target, ChatMsg.Addon, Language.Addon, CreatureTextRange.Normal, 0, Team.Other, false, target);
                }
            }

            void HandleRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                Creature owner = GetOwner().ToCreature();
                if (owner)
                {
                    Player target = GetTarget().ToPlayer();
                    if (target)
                        if (target.HasItemCount(GunshipMiscData.ItemGoblinRocketPack, 1))
                            Global.CreatureTextMgr.SendChat(owner, GunshipTexts.SayZafodRocketPackDisabled, target, ChatMsg.Addon, Language.Addon, CreatureTextRange.Normal, 0, Team.Other, false, target);
                }
            }

            public override void Register()
            {
                DoCheckAreaTarget.Add(new CheckAreaTargetHandler(CheckAreaTarget));
                AfterEffectApply.Add(new EffectApplyHandler(HandleApply, 0, AuraType.Dummy, AuraEffectHandleModes.Real));
                AfterEffectRemove.Add(new EffectApplyHandler(HandleRemove, 0, AuraType.Dummy, AuraEffectHandleModes.Real));
            }
        }

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

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

        class spell_igb_on_gunship_deck_AuraScript : AuraScript
        {
            public override bool Load()
            {
                InstanceScript instance = GetOwner().GetInstanceScript();
                if (instance != null)
                    _teamInInstance = (Team)instance.GetData(DataTypes.TeamInInstance);
                else
                    _teamInInstance = 0;
                return true;
            }

            bool CheckAreaTarget(Unit unit)
            {
                return unit.IsTypeId(TypeId.Player);
            }

            void HandleApply(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                if (GetSpellInfo().Id == (_teamInInstance == Team.Horde ? GunshipSpells.OnSkybreakerDeck : GunshipSpells.OnOrgrimsHammerDeck))
                {
                    Creature gunship = GetOwner().FindNearestCreature(_teamInInstance == Team.Horde ? CreatureIds.OrgrimsHammer : CreatureIds.TheSkybreaker, 200.0f);
                    if (gunship)
                        gunship.GetAI().SetGUID(GetTarget().GetGUID(), EncounterActions.ShipVisits);
                }
            }

            public override void Register()
            {
                DoCheckAreaTarget.Add(new CheckAreaTargetHandler(CheckAreaTarget));
                AfterEffectApply.Add(new EffectApplyHandler(HandleApply, 0, AuraType.Dummy, AuraEffectHandleModes.Real));
            }

            Team _teamInInstance;
        }

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

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

        class spell_igb_periodic_trigger_with_power_cost_AuraScript : AuraScript
        {
            void HandlePeriodicTick(AuraEffect aurEff)
            {
                PreventDefaultAction();
                GetTarget().CastSpell(GetTarget(), GetSpellInfo().GetEffect(0).TriggerSpell, (TriggerCastFlags.FullMask & ~TriggerCastFlags.IgnorePowerAndReagentCost));
            }

            public override void Register()
            {
                OnEffectPeriodic.Add(new EffectPeriodicHandler(HandlePeriodicTick, 0, AuraType.PeriodicTriggerSpell));
            }
        }

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

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

        class spell_igb_cannon_blast_SpellScript : SpellScript
        {
            public override bool Load()
            {
                return GetCaster().IsTypeId(TypeId.Unit);
            }

            void CheckEnergy()
            {
                if (GetCaster().GetPower(PowerType.Energy) >= 100)
                {
                    GetCaster().CastSpell(GetCaster(), GunshipSpells.Overheat, TriggerCastFlags.FullMask);
                    Vehicle vehicle = GetCaster().GetVehicleKit();
                    if (vehicle)
                    {
                        Unit passenger = vehicle.GetPassenger(0);
                        if (passenger)
                            Global.CreatureTextMgr.SendChat(GetCaster().ToCreature(), GunshipTexts.SayOverheat, passenger);
                    }
                }
            }

            public override void Register()
            {
                AfterHit.Add(new HitHandler(CheckEnergy));
            }
        }

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

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

        class spell_igb_incinerating_blast_SpellScript : SpellScript
        {
            void StoreEnergy()
            {
                _energyLeft = (uint)GetCaster().GetPower(PowerType.Energy) - 10;
            }

            void RemoveEnergy()
            {
                GetCaster().SetPower(PowerType.Energy, 0);
            }

            void CalculateDamage(uint effIndex)
            {
                SetEffectValue((int)(GetEffectValue() + _energyLeft * _energyLeft * 8));
            }

            public override void Register()
            {
                OnCast.Add(new CastHandler(StoreEnergy));
                AfterCast.Add(new CastHandler(RemoveEnergy));
                OnEffectLaunchTarget.Add(new EffectHandler(CalculateDamage, 1, SpellEffectName.SchoolDamage));
            }

            uint _energyLeft;
        }

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

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

        class spell_igb_overheat_AuraScript : AuraScript
        {
            public override bool Load()
            {
                if (GetAura().GetAuraType() != AuraObjectType.Unit)
                    return false;
                return GetUnitOwner().IsVehicle();
            }

            void SendClientControl(bool value)
            {
                Vehicle vehicle = GetUnitOwner().GetVehicleKit();
                if (vehicle)
                {
                    Unit passenger = vehicle.GetPassenger(0);
                    if (passenger)
                    {
                        Player player = passenger.ToPlayer();
                        if (player)
                        {
                            ControlUpdate data = new ControlUpdate();
                            data.Guid = GetUnitOwner().GetGUID();
                            data.On = value;
                            player.SendPacket(data);
                        }
                    }
                }
            }

            void HandleApply(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                SendClientControl(false);
            }

            void HandleRemove(AuraEffect aurEff, AuraEffectHandleModes mode)
            {
                SendClientControl(true);
            }

            public override void Register()
            {
                AfterEffectApply.Add(new EffectApplyHandler(HandleApply, 0, AuraType.PeriodicTriggerSpell, AuraEffectHandleModes.Real));
                AfterEffectRemove.Add(new EffectApplyHandler(HandleRemove, 0, AuraType.PeriodicTriggerSpell, AuraEffectHandleModes.Real));
            }
        }

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

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

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

                GetHitUnit().CastSpell(GetHitUnit(), GunshipSpells.EjectAllPassengersBelowZero, TriggerCastFlags.FullMask);
            }

            public override void Register()
            {
                BeforeHit.Add(new BeforeHitHandler(RemovePassengers));
            }
        }

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

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

        class spell_igb_teleport_to_enemy_ship_SpellScript : SpellScript
        {
            void RelocateTransportOffset(uint effIndex)
            {
                Position dest = GetHitDest();
                Unit target = GetHitUnit();
                if (dest == null || !target || !target.GetTransport())
                    return;

                float x, y, z, o;
                dest.GetPosition(out x, out y, out z, out o);
                target.GetTransport().CalculatePassengerOffset(ref x, ref y, ref z, ref o);
                target.m_movementInfo.transport.pos.Relocate(x, y, z, o);
            }

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

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

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

        class spell_igb_burning_pitch_selector_SpellScript : SpellScript
        {
            void FilterTargets(List<WorldObject> targets)
            {
                Team team = Team.Horde;
                InstanceScript instance = GetCaster().GetInstanceScript();
                if (instance != null)
                    team = (Team)instance.GetData(DataTypes.TeamInInstance);

                targets.RemoveAll(target =>
                {
                    Transport transport = target.GetTransport();
                    if (transport)
                        return transport.GetEntry() != (team == Team.Horde ? GameObjectIds.OrgrimsHammer_H : GameObjectIds.TheSkybreaker_A);
                    return true;
                });

                if (!targets.Empty())
                {
                    WorldObject target = targets.PickRandom();
                    targets.Clear();
                    targets.Add(target);
                }
            }

            void HandleDummy(uint effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                GetCaster().CastSpell(GetHitUnit(), (uint)GetEffectValue(), TriggerCastFlags.None);
            }

            public override void Register()
            {
                OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 0, Targets.UnitSrcAreaEntry));
                OnEffectHitTarget.Add(new EffectHandler(HandleDummy, 0, SpellEffectName.Dummy));
            }
        }

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

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

        class spell_igb_burning_pitch_SpellScript : SpellScript
        {
            void HandleDummy(uint effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                GetCaster().CastCustomSpell((uint)GetEffectValue(), SpellValueMod.BasePoint0, 8000, null, TriggerCastFlags.FullMask);
                GetHitUnit().CastSpell(GetHitUnit(), GunshipSpells.BurningPitch, TriggerCastFlags.FullMask);
            }

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

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

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

        class spell_igb_rocket_artillery_SpellScript : SpellScript
        {
            void SelectRandomTarget(List<WorldObject> targets)
            {
                if (!targets.Empty())
                {
                    WorldObject target = targets.PickRandom();
                    targets.Clear();
                    targets.Add(target);
                }
            }

            void HandleScript(uint effIndex)
            {
                PreventHitDefaultEffect(effIndex);
                GetCaster().CastSpell(GetHitUnit(), (uint)GetEffectValue(), TriggerCastFlags.None);
            }

            public override void Register()
            {
                OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(SelectRandomTarget, 0, Targets.UnitSrcAreaEntry));
                OnEffectHitTarget.Add(new EffectHandler(HandleScript, 0, SpellEffectName.ScriptEffect));
            }
        }

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

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

        class spell_igb_rocket_artillery_explosion_SpellScript : SpellScript
        {
            void DamageGunship(uint effIndex)
            {
                InstanceScript instance = GetCaster().GetInstanceScript();
                if (instance != null)
                    GetCaster().CastCustomSpell(instance.GetData(DataTypes.TeamInInstance) == (uint)Team.Horde ? GunshipSpells.BurningPitchDamageH : GunshipSpells.BurningPitchDamageA, SpellValueMod.BasePoint0, 5000, null, TriggerCastFlags.FullMask);
            }

            public override void Register()
            {
                OnEffectHit.Add(new EffectHandler(DamageGunship, 0, SpellEffectName.TriggerMissile));
            }
        }

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

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

        class spell_igb_gunship_fall_teleport_SpellScript : SpellScript
        {
            public override bool Load()
            {
                return GetCaster().GetInstanceScript() != null;
            }

            void SelectTransport(ref WorldObject target)
            {
                InstanceScript instance = target.GetInstanceScript();
                if (instance != null)
                    target = Global.ObjAccessor.FindTransport(instance.GetGuidData(Bosses.GunshipBattle));
            }

            void RelocateDest(uint effIndex)
            {
                if (GetCaster().GetInstanceScript().GetData(DataTypes.TeamInInstance) == (uint)Team.Horde)
                    GetHitDest().RelocateOffset(new Position(0.0f, 0.0f, 36.0f, 0.0f));
                else
                    GetHitDest().RelocateOffset(new Position(0.0f, 0.0f, 21.0f, 0.0f));
            }

            public override void Register()
            {
                OnObjectTargetSelect.Add(new ObjectTargetSelectHandler(SelectTransport, 0, Targets.DestNearbyEntry));
                OnEffectLaunch.Add(new EffectHandler(RelocateDest, 0, SpellEffectName.TeleportUnitsOld));
            }
        }

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

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

        class spell_igb_check_for_players_SpellScript : SpellScript
        {
            public override bool Load()
            {
                _playerCount = 0;
                return GetCaster().IsTypeId(TypeId.Unit);
            }

            void CountTargets(List<WorldObject> targets)
            {
                _playerCount = (uint)targets.Count;
            }

            void TriggerWipe()
            {
                if (_playerCount == 0)
                    GetCaster().ToCreature().GetAI().JustDied(null);
            }

            void TeleportPlayer(uint effIndex)
            {
                if (GetHitUnit().GetPositionZ() < GetCaster().GetPositionZ() - 10.0f)
                    GetHitUnit().CastSpell(GetHitUnit(), GunshipSpells.GunshipFallTeleport, TriggerCastFlags.FullMask);
            }

            public override void Register()
            {
                OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(CountTargets, 0, Targets.UnitSrcAreaEntry));
                AfterCast.Add(new CastHandler(TriggerWipe));
                OnEffectHitTarget.Add(new EffectHandler(TeleportPlayer, 0, SpellEffectName.Dummy));
            }

            uint _playerCount;
        }

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

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

        class spell_igb_teleport_players_on_victory_SpellScript : SpellScript
        {
            public override bool Load()
            {
                return GetCaster().GetInstanceScript() != null;
            }

            void FilterTargets(List<WorldObject> targets)
            {
                InstanceScript instance = GetCaster().GetInstanceScript();
                targets.RemoveAll(target => target.GetTransGUID() != instance.GetGuidData(DataTypes.EnemyGunship));
            }

            public override void Register()
            {
                OnObjectAreaTargetSelect.Add(new ObjectAreaTargetSelectHandler(FilterTargets, 1, Targets.UnitDestAreaEntry));
            }
        }

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

    [Script] // 71201 - Battle Experience - proc should never happen, handled in script
    class spell_igb_battle_experience_check : SpellScriptLoader
    {
        public spell_igb_battle_experience_check() : base("spell_igb_battle_experience_check") { }

        class spell_igb_battle_experience_check_AuraScript : AuraScript
        {

            bool CheckProc(ProcEventInfo eventInfo)
            {
                return false;
            }

            public override void Register()
            {
                DoCheckProc.Add(new CheckProcHandler(CheckProc));
            }
        }

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

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

        public override bool OnCheck(Player source, Unit target)
        {
            return target.GetAI() != null && target.GetAI().GetData(EncounterActions.ShipVisits) <= 2;
        }
    }
}
