/*
 * Copyright (C) 2012-2017 CypherCore <http://github.com/CypherCore>
 * 
 * This program is free software: you can redistribute it and/or modify
 * it under the terms of the GNU General Public License as published by
 * the Free Software Foundation, either version 3 of the License, or
 * (at your option) any later version.
 *
 * This program is distributed in the hope that it will be useful,
 * but WITHOUT ANY WARRANTY; without even the implied warranty of
 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
 * GNU General Public License for more details.
 *
 * You should have received a copy of the GNU General Public License
 * along with this program.  If not, see <http://www.gnu.org/licenses/>.
 */

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

namespace Scripts.EasternKingdoms.Karazhan.OperaEvent
{
    #region Wizard of Oz
    struct WizardOfOz
    {
        public const uint SayDorotheeDeath = 0;
        public const uint SayDorotheeSummon = 1;
        public const uint SayDorotheeTitoDeath = 2;
        public const uint SayDorotheeAggro = 3;

        public const uint SayRoarAggro = 0;
        public const uint SayRoarDeath = 1;
        public const uint SayRoarSlay = 2;

        public const uint SayStrawmanAggro = 0;
        public const uint SayStrawmanDeath = 1;
        public const uint SayStrawmanSlay = 2;

        public const uint SayTinheadAggro = 0;
        public const uint SayTinheadDeath = 1;
        public const uint SayTinheadSlay = 2;
        public const uint EmoteRust = 3;

        public const uint SayCroneAggro = 0;
        public const uint SayCroneDeath = 1;
        public const uint SayCroneSlay = 2;

        // Dorothee
        public const uint SpellWaterbolt = 31012;
        public const uint SpellScream = 31013;
        public const uint SpellSummontito = 31014;

        // Tito
        public const uint SpellYipping = 31015;

        // Strawman
        public const uint SpellBrainBash = 31046;
        public const uint SpellBrainWipe = 31069;
        public const uint SpellBurningStraw = 31075;

        // Tinhead
        public const uint SpellCleave = 31043;
        public const uint SpellRust = 31086;

        // Roar
        public const uint SpellMangle = 31041;
        public const uint SpellShred = 31042;
        public const uint SpellFrightenedScream = 31013;

        // Crone
        public const uint SpellChainLightning = 32337;

        // Cyclone
        public const uint SpellKnockback = 32334;
        public const uint SpellCycloneVisual = 32332;

        public const uint NpcTito = 17548;
        public const uint NpcCyclone = 18412;
        public const uint NpcCrone = 18168;
    }

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

        public void SummonCroneIfReady(InstanceScript instance, Creature creature)
        {
            instance.SetData(DataTypes.OperaOzDeathcount, (uint)EncounterState.Special);  // Increment DeathCount

            if (instance.GetData(DataTypes.OperaOzDeathcount) == 4)
            {
                Creature pCrone = creature.SummonCreature(WizardOfOz.NpcCrone, -10891.96f, -1755.95f, creature.GetPositionZ(), 4.64f, TempSummonType.TimedOrDeadDespawn, Time.Hour * 2 * Time.InMilliseconds);
                if (pCrone)
                {
                    if (creature.GetVictim())
                        pCrone.GetAI().AttackStart(creature.GetVictim());
                }
            }
        }

        public bool TitoDied;
        public ObjectGuid DorotheeGUID;
        public uint AggroTimer;
    }

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

        public class boss_dorotheeAI : WizardofOzBase
        {
            public boss_dorotheeAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                AggroTimer = 500;

                WaterBoltTimer = 5000;
                FearTimer = 15000;
                SummonTitoTimer = 47500;

                SummonedTito = false;
                TitoDied = false;
            }

            public override void EnterCombat(Unit who)
            {
                Talk(WizardOfOz.SayDorotheeAggro);
            }

            public override void JustReachedHome()
            {
                me.DespawnOrUnsummon();
            }

            public override void JustDied(Unit killer)
            {
                Talk(WizardOfOz.SayDorotheeDeath);

                SummonCroneIfReady(instance, me);
            }

            public override void AttackStart(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.AttackStart(who);
            }

            public override void MoveInLineOfSight(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.MoveInLineOfSight(who);
            }

            public override void UpdateAI(uint diff)
            {
                if (AggroTimer != 0)
                {
                    if (AggroTimer <= diff)
                    {
                        me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                        AggroTimer = 0;
                    }
                    else AggroTimer -= diff;
                }

                if (!UpdateVictim())
                    return;

                if (WaterBoltTimer <= diff)
                {
                    DoCast(SelectTarget(SelectAggroTarget.Random, 0), WizardOfOz.SpellWaterbolt);
                    WaterBoltTimer = (uint)(TitoDied ? 1500 : 5000);
                }
                else WaterBoltTimer -= diff;

                if (FearTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellScream);
                    FearTimer = 30000;
                }
                else FearTimer -= diff;

                if (!SummonedTito)
                {
                    if (SummonTitoTimer <= diff)
                        SummonTito();
                    else SummonTitoTimer -= diff;
                }

                DoMeleeAttackIfReady();
            }

            void SummonTito()
            {
                Creature pTito = me.SummonCreature(WizardOfOz.NpcTito, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedDespawnOOC, 30000);
                if (pTito)
                {
                    Talk(WizardOfOz.SayDorotheeSummon);
                    DorotheeGUID = me.GetGUID();
                    pTito.GetAI().AttackStart(me.GetVictim());
                    SummonedTito = true;
                    TitoDied = false;
                }
            }

            InstanceScript instance;

            uint WaterBoltTimer;
            uint FearTimer;
            uint SummonTitoTimer;

            bool SummonedTito;
        }

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

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

        public class npc_titoAI : WizardofOzBase
        {
            public npc_titoAI(Creature creature) : base(creature) { }

            public override void Reset()
            {
                DorotheeGUID.Clear();
                YipTimer = 10000;
            }

            public override void EnterCombat(Unit who) { }

            public override void JustDied(Unit killer)
            {
                if (!DorotheeGUID.IsEmpty())
                {
                    Creature Dorothee = ObjectAccessor.GetCreature(me, DorotheeGUID);
                    if (Dorothee && Dorothee.IsAlive())
                    {
                        TitoDied = true;
                        Talk(WizardOfOz.SayDorotheeTitoDeath, Dorothee);
                    }
                }
            }

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

                if (YipTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellYipping);
                    YipTimer = 10000;
                }
                else YipTimer -= diff;

                DoMeleeAttackIfReady();
            }

            uint YipTimer;
        }

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

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

        class boss_strawmanAI : WizardofOzBase
        {
            public boss_strawmanAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                AggroTimer = 13000;
                BrainBashTimer = 5000;
                BrainWipeTimer = 7000;
            }

            public override void AttackStart(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.AttackStart(who);
            }

            public override void MoveInLineOfSight(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.MoveInLineOfSight(who);
            }

            public override void EnterCombat(Unit who)
            {
                Talk(WizardOfOz.SayStrawmanAggro);
            }

            public override void SpellHit(Unit caster, SpellInfo Spell)
            {
                if ((Spell.SchoolMask == SpellSchoolMask.Fire) && ((RandomHelper.randChance() % 10) == 0))
                {
                    DoCast(me, WizardOfOz.SpellBurningStraw, true);
                }
            }

            public override void JustDied(Unit killer)
            {
                Talk(WizardOfOz.SayStrawmanDeath);

                SummonCroneIfReady(instance, me);
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(WizardOfOz.SayStrawmanSlay);
            }

            public override void UpdateAI(uint diff)
            {
                if (AggroTimer != 0)
                {
                    if (AggroTimer <= diff)
                    {
                        me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                        AggroTimer = 0;
                    }
                    else AggroTimer -= diff;
                }

                if (!UpdateVictim())
                    return;

                if (BrainBashTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellBrainBash);
                    BrainBashTimer = 15000;
                }
                else BrainBashTimer -= diff;

                if (BrainWipeTimer <= diff)
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100, true);
                    if (target)
                        DoCast(target, WizardOfOz.SpellBrainWipe);
                    BrainWipeTimer = 20000;
                }
                else BrainWipeTimer -= diff;

                DoMeleeAttackIfReady();
            }

            InstanceScript instance;

            uint BrainBashTimer;
            uint BrainWipeTimer;
        }

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

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

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

        class boss_tinheadAI : WizardofOzBase
        {
            public boss_tinheadAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                AggroTimer = 15000;
                CleaveTimer = 5000;
                RustTimer = 30000;

                RustCount = 0;
            }

            public override void EnterCombat(Unit who)
            {
                Talk(WizardOfOz.SayTinheadAggro);
            }

            public override void JustReachedHome()
            {
                me.DespawnOrUnsummon();
            }

            public override void AttackStart(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.AttackStart(who);
            }

            public override void MoveInLineOfSight(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.MoveInLineOfSight(who);
            }

            public override void JustDied(Unit killer)
            {
                Talk(WizardOfOz.SayTinheadDeath);

                SummonCroneIfReady(instance, me);
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(WizardOfOz.SayTinheadSlay);
            }

            public override void UpdateAI(uint diff)
            {
                if (AggroTimer != 0)
                {
                    if (AggroTimer <= diff)
                    {
                        me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                        AggroTimer = 0;
                    }
                    else AggroTimer -= diff;
                }

                if (!UpdateVictim())
                    return;

                if (CleaveTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellCleave);
                    CleaveTimer = 5000;
                }
                else CleaveTimer -= diff;

                if (RustCount < 8)
                {
                    if (RustTimer <= diff)
                    {
                        ++RustCount;
                        Talk(WizardOfOz.EmoteRust);
                        DoCast(me, WizardOfOz.SpellRust);
                        RustTimer = 6000;
                    }
                    else RustTimer -= diff;
                }

                DoMeleeAttackIfReady();
            }

            InstanceScript instance;

            uint CleaveTimer;
            uint RustTimer;

            byte RustCount;
        }
    }

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

        class boss_roarAI : WizardofOzBase
        {
            public boss_roarAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                AggroTimer = 20000;
                MangleTimer = 5000;
                ShredTimer = 10000;
                ScreamTimer = 15000;
            }

            public override void MoveInLineOfSight(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.MoveInLineOfSight(who);
            }

            public override void AttackStart(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.AttackStart(who);
            }

            public override void EnterCombat(Unit who)
            {
                Talk(WizardOfOz.SayRoarAggro);
            }

            public override void JustReachedHome()
            {
                me.DespawnOrUnsummon();
            }

            public override void JustDied(Unit killer)
            {
                Talk(WizardOfOz.SayRoarDeath);

                SummonCroneIfReady(instance, me);
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(WizardOfOz.SayRoarSlay);
            }

            public override void UpdateAI(uint diff)
            {
                if (AggroTimer != 0)
                {
                    if (AggroTimer <= diff)
                    {
                        me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                        AggroTimer = 0;
                    }
                    else AggroTimer -= diff;
                }

                if (!UpdateVictim())
                    return;

                if (MangleTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellMangle);
                    MangleTimer = RandomHelper.URand(5000, 8000);
                }
                else MangleTimer -= diff;

                if (ShredTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellShred);
                    ShredTimer = RandomHelper.URand(10000, 15000);
                }
                else ShredTimer -= diff;

                if (ScreamTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellFrightenedScream);
                    ScreamTimer = RandomHelper.URand(20000, 30000);
                }
                else ScreamTimer -= diff;

                DoMeleeAttackIfReady();
            }

            InstanceScript instance;

            uint MangleTimer;
            uint ShredTimer;
            uint ScreamTimer;
        }

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

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

        class boss_croneAI : WizardofOzBase
        {
            public boss_croneAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
            }

            public override void Reset()
            {
                CycloneTimer = 30000;
                ChainLightningTimer = 10000;
            }

            public override void JustReachedHome()
            {
                me.DespawnOrUnsummon();
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(WizardOfOz.SayCroneSlay);
            }

            public override void EnterCombat(Unit who)
            {
                Talk(WizardOfOz.SayCroneAggro);
                me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                me.RemoveFlag(UnitFields.Flags, UnitFlags.ImmuneToPc);
            }

            public override void JustDied(Unit killer)
            {
                Talk(WizardOfOz.SayCroneDeath);

                instance.SetData(karazhanConst.BossOpera, (uint)EncounterState.Done);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorleft), true);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorright), true);

                GameObject pSideEntrance = instance.instance.GetGameObject(instance.GetGuidData(DataTypes.GoSideEntranceDoor));
                if (pSideEntrance)
                    pSideEntrance.RemoveFlag(GameObjectFields.Flags, GameObjectFlags.Locked);
            }

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

                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);

                if (CycloneTimer <= diff)
                {
                    Creature Cyclone = DoSpawnCreature(WizardOfOz.NpcCyclone, RandomHelper.FRand(0, 9), RandomHelper.FRand(0, 9), 0, 0, TempSummonType.TimedDespawn, 15000);
                    if (Cyclone)
                        Cyclone.CastSpell(Cyclone, WizardOfOz.SpellCycloneVisual, true);
                    CycloneTimer = 30000;
                }
                else CycloneTimer -= diff;

                if (ChainLightningTimer <= diff)
                {
                    DoCastVictim(WizardOfOz.SpellChainLightning);
                    ChainLightningTimer = 15000;
                }
                else ChainLightningTimer -= diff;

                DoMeleeAttackIfReady();
            }

            InstanceScript instance;

            uint CycloneTimer;
            uint ChainLightningTimer;
        }

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

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

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

            public override void Reset()
            {
                MoveTimer = 1000;
            }

            public override void EnterCombat(Unit who) { }

            public override void MoveInLineOfSight(Unit who) { }

            public override void UpdateAI(uint diff)
            {
                if (!me.HasAura(WizardOfOz.SpellKnockback))
                    DoCast(me, WizardOfOz.SpellKnockback, true);

                if (MoveTimer <= diff)
                {
                    Position pos = me.GetRandomNearPosition(10);
                    me.GetMotionMaster().MovePoint(0, pos);
                    MoveTimer = RandomHelper.URand(5000, 8000);
                }
                else MoveTimer -= diff;
            }

            uint MoveTimer;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return new npc_cycloneAI(creature);
        }
    }
    #endregion

    #region Red Riding Hood
    struct RedRidingHood
    {
        public const uint SayWolfAggro = 0;
        public const uint SayWolfSlay = 1;
        public const uint SayWolfHood = 2;
        public const uint SoundWolfDeath = 9275;

        public const uint SpellLittleRedRidingHood = 30768;
        public const uint SpellTerrifyingHowl = 30752;
        public const uint SpellWideSwipe = 30761;

        public const uint NpcBigBadWolf = 17521;
    }

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

        public override bool OnGossipSelect(Player player, Creature creature, uint sender, uint action)
        {
            player.PlayerTalkClass.ClearMenus();
            if (action == eTradeskill.GossipActionInfoDef)
            {
                Creature pBigBadWolf = creature.SummonCreature(RedRidingHood.NpcBigBadWolf, 0.0f, 0.0f, 0.0f, 0.0f, TempSummonType.TimedOrDeadDespawn, Time.Hour * 2 * Time.InMilliseconds);
                if (pBigBadWolf)
                    pBigBadWolf.GetAI().AttackStart(player);

                creature.DespawnOrUnsummon();
            }

            return true;
        }

        public override bool OnGossipHello(Player player, Creature creature)
        {
            player.ADD_GOSSIP_ITEM(GossipOptionIcon.Chat, "What phat lewtz you have grandmother?", eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef);
            player.SEND_GOSSIP_MENU(8990, creature.GetGUID());

            return true;
        }
    }

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

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

            public override void Reset()
            {
                ChaseTimer = 30000;
                FearTimer = RandomHelper.URand(25000, 35000);
                SwipeTimer = 5000;

                HoodGUID.Clear();
                TempThreat = 0;

                IsChasing = false;
            }

            public override void EnterCombat(Unit who)
            {
                Talk(RedRidingHood.SayWolfAggro);
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(RedRidingHood.SayWolfSlay);
            }

            public override void JustReachedHome()
            {
                me.DespawnOrUnsummon();
            }

            public override void JustDied(Unit killer)
            {
                DoPlaySoundToSet(me, RedRidingHood.SoundWolfDeath);

                instance.SetData(karazhanConst.BossOpera, (uint)EncounterState.Done);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorleft), true);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorright), true);

                GameObject pSideEntrance = instance.instance.GetGameObject(instance.GetGuidData(DataTypes.GoSideEntranceDoor));
                if (pSideEntrance)
                    pSideEntrance.RemoveFlag(GameObjectFields.Flags, GameObjectFlags.Locked);
            }

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

                DoMeleeAttackIfReady();

                if (ChaseTimer <= diff)
                {
                    if (!IsChasing)
                    {
                        Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100, true);
                        if (target)
                        {
                            Talk(RedRidingHood.SayWolfHood);
                            DoCast(target, RedRidingHood.SpellLittleRedRidingHood, true);
                            TempThreat = DoGetThreat(target);
                            if (TempThreat != 0.0f)
                                DoModifyThreatPercent(target, -100);
                            HoodGUID = target.GetGUID();
                            me.AddThreat(target, 1000000.0f);
                            ChaseTimer = 20000;
                            IsChasing = true;
                        }
                    }
                    else
                    {
                        IsChasing = false;
                        Unit target = Global.ObjAccessor.GetUnit(me, HoodGUID);
                        if (target)
                        {
                            HoodGUID.Clear();
                            if (DoGetThreat(target) != 0f)
                                DoModifyThreatPercent(target, -100);
                            me.AddThreat(target, TempThreat);
                            TempThreat = 0;
                        }

                        ChaseTimer = 40000;
                    }
                }
                else ChaseTimer -= diff;

                if (IsChasing)
                    return;

                if (FearTimer <= diff)
                {
                    DoCastVictim(RedRidingHood.SpellTerrifyingHowl);
                    FearTimer = RandomHelper.URand(25000, 35000);
                }
                else FearTimer -= diff;

                if (SwipeTimer <= diff)
                {
                    DoCastVictim(RedRidingHood.SpellWideSwipe);
                    SwipeTimer = RandomHelper.URand(25000, 30000);
                }
                else SwipeTimer -= diff;
            }

            InstanceScript instance;

            uint ChaseTimer;
            uint FearTimer;
            uint SwipeTimer;

            ObjectGuid HoodGUID;
            float TempThreat;

            bool IsChasing;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<boss_bigbadwolfAI>(creature);
        }
    }
    #endregion

    #region Romeo & Juliet
    struct JulianneRomulo
    {
        public const uint SayJulianneAggro = 0;
        public const uint SayJulianneEnter = 1;
        public const uint SayJulianneDeath01 = 2;
        public const uint SayJulianneDeath02 = 3;
        public const uint SayJulianneResurrect = 4;
        public const uint SayJulianneSlay = 5;

        public const uint SayRomuloAggro = 0;
        public const uint SayRomuloDeath = 1;
        public const uint SayRomuloEnter = 2;
        public const uint SayRomuloResurrect = 3;
        public const uint SayRomuloSlay = 4;

        public const uint SpellBlindingPassion = 30890;
        public const uint SpellDevotion = 30887;
        public const uint SpellEternalAffection = 30878;
        public const uint SpellPowerfulAttraction = 30889;
        public const uint SpellDrinkPoison = 30907;

        public const uint SpellBackwardLunge = 30815;
        public const uint SpellDaring = 30841;
        public const uint SpellDeadlySwathe = 30817;
        public const uint SpellPoisonThrust = 30822;

        public const uint SpellUndyingLove = 30951;
        public const uint SpellResVisual = 24171;

        public const uint NpcRomulo = 17533;
        public const int RomuloX = -10900;
        public const int RomuloY = -1758;
    }

    public enum RAJPhase
    {
        Julianne = 0,
        Romulo = 1,
        Both = 2,
    }

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

        public override void JustReachedHome()
        {
            me.DespawnOrUnsummon();
        }

        public override void MoveInLineOfSight(Unit who)
        {
            if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                return;

            base.MoveInLineOfSight(who);
        }

        public void PretendToDie()
        {
            me.InterruptNonMeleeSpells(true);
            me.RemoveAllAuras();
            me.SetHealth(0);
            me.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
            me.GetMotionMaster().MovementExpired(false);
            me.GetMotionMaster().MoveIdle();
            me.SetStandState(UnitStandStateType.Dead);
        }

        public void Resurrect(Creature target)
        {
            target.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
            target.SetFullHealth();
            target.SetStandState(UnitStandStateType.Stand);
            target.CastSpell(target, JulianneRomulo.SpellResVisual, true);
            if (target.GetVictim())
            {
                target.GetMotionMaster().MoveChase(target.GetVictim());
                target.GetAI().AttackStart(target.GetVictim());
            }
            else
                target.GetMotionMaster().Initialize();
        }

        public InstanceScript instance;

        public ObjectGuid JulianneGUID;
        public ObjectGuid RomuloGUID;

        public uint EntryYellTimer;
        public uint AggroYellTimer;

        public RAJPhase Phase;

        public uint ResurrectSelfTimer;
        public uint ResurrectTimer;
        public bool JulianneDead;
        public bool RomuloDead;

        public bool IsFakingDeath;
    }

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

        public class boss_julianneAI : julianne_romulobase
        {
            public boss_julianneAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
                EntryYellTimer = 1000;
                AggroYellTimer = 10000;
                IsFakingDeath = false;
            }

            public override void Reset()
            {
                RomuloGUID.Clear();
                Phase = RAJPhase.Julianne;

                BlindingPassionTimer = 30000;
                DevotionTimer = 15000;
                EternalAffectionTimer = 25000;
                PowerfulAttractionTimer = 5000;
                SummonRomuloTimer = 10000;
                DrinkPoisonTimer = 0;
                ResurrectSelfTimer = 0;

                if (IsFakingDeath)
                {
                    Resurrect(me);
                    IsFakingDeath = false;
                }

                SummonedRomulo = false;
                RomuloDead = false;
            }

            public override void EnterCombat(Unit who) { }

            public override void AttackStart(Unit who)
            {
                if (me.HasFlag(UnitFields.Flags, UnitFlags.NonAttackable))
                    return;

                base.AttackStart(who);
            }

            public override void SpellHit(Unit caster, SpellInfo Spell)
            {
                if (Spell.Id == JulianneRomulo.SpellDrinkPoison)
                {
                    Talk(JulianneRomulo.SayJulianneDeath01);
                    DrinkPoisonTimer = 2500;
                }
            }

            public override void DamageTaken(Unit done_by, ref uint damage)
            {
                if (damage < me.GetHealth())
                    return;

                //anything below only used if incoming damage will kill

                if (Phase == RAJPhase.Julianne)
                {
                    damage = 0;

                    //this means already drinking, so return
                    if (IsFakingDeath)
                        return;

                    me.InterruptNonMeleeSpells(true);
                    DoCast(me, JulianneRomulo.SpellDrinkPoison);

                    IsFakingDeath = true;
                    //IS THIS USEFULL? Creature Julianne = (Global.ObjAccessor.GetCreature(me, JulianneGUID));
                    return;
                }

                if (Phase == RAJPhase.Romulo)
                {
                    Log.outError(LogFilter.Scripts, "boss_julianneAI: cannot take damage in PHASE_ROMULO, why was i here?");
                    damage = 0;
                    return;
                }

                if (Phase == RAJPhase.Both)
                {
                    Creature Romulo;
                    //if this is true then we have to kill romulo too
                    if (RomuloDead)
                    {
                        Romulo = ObjectAccessor.GetCreature(me, RomuloGUID);
                        if (Romulo)
                        {
                            Romulo.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                            Romulo.GetMotionMaster().Clear();
                            Romulo.setDeathState(DeathState.JustDied);
                            Romulo.CombatStop(true);
                            Romulo.DeleteThreatList();
                            Romulo.SetUInt32Value(ObjectFields.DynamicFlags, (uint)UnitDynFlags.Lootable);
                        }

                        return;
                    }

                    //if not already returned, then romulo is alive and we can pretend die
                    Romulo = ObjectAccessor.GetCreature(me, RomuloGUID);
                    if (Romulo)
                    {
                        PretendToDie();
                        IsFakingDeath = true;
                        ((julianne_romulobase)Romulo.GetAI()).ResurrectTimer = 10000;
                        ((julianne_romulobase)Romulo.GetAI()).JulianneDead = true;
                        damage = 0;
                        return;
                    }
                }
                Log.outError(LogFilter.Scripts, "boss_julianneAI: DamageTaken reach end of code, that should not happen.");
            }

            public override void JustDied(Unit killer)
            {
                Talk(JulianneRomulo.SayJulianneDeath02);

                instance.SetData(karazhanConst.BossOpera, (uint)EncounterState.Done);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorleft), true);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorright), true);

                GameObject pSideEntrance = instance.instance.GetGameObject(instance.GetGuidData(DataTypes.GoSideEntranceDoor));
                if (pSideEntrance)
                    pSideEntrance.RemoveFlag(GameObjectFields.Flags, GameObjectFlags.Locked);
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(JulianneRomulo.SayJulianneSlay);
            }

            public override void UpdateAI(uint diff)
            {
                if (EntryYellTimer != 0)
                {
                    if (EntryYellTimer <= diff)
                    {
                        Talk(JulianneRomulo.SayJulianneEnter);
                        EntryYellTimer = 0;
                    }
                    else EntryYellTimer -= diff;
                }

                if (AggroYellTimer != 0)
                {
                    if (AggroYellTimer <= diff)
                    {
                        Talk(JulianneRomulo.SayJulianneAggro);
                        me.RemoveFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                        me.SetFaction(16);
                        AggroYellTimer = 0;
                    }
                    else AggroYellTimer -= diff;
                }

                if (DrinkPoisonTimer != 0)
                {
                    //will do this 2secs after spell hit. this is time to display visual as expected
                    if (DrinkPoisonTimer <= diff)
                    {
                        PretendToDie();
                        Phase = RAJPhase.Romulo;
                        SummonRomuloTimer = 10000;
                        DrinkPoisonTimer = 0;
                    }
                    else DrinkPoisonTimer -= diff;
                }

                if (Phase == RAJPhase.Romulo && !SummonedRomulo)
                {
                    if (SummonRomuloTimer <= diff)
                    {
                        Creature pRomulo = me.SummonCreature(JulianneRomulo.NpcRomulo, JulianneRomulo.RomuloX, JulianneRomulo.RomuloY, me.GetPositionZ(), 0, TempSummonType.TimedOrDeadDespawn, Time.Hour * 2 * Time.InMilliseconds);
                        if (pRomulo)
                        {
                            RomuloGUID = pRomulo.GetGUID();
                            ((julianne_romulobase)pRomulo.GetAI()).JulianneGUID = me.GetGUID();
                            ((julianne_romulobase)pRomulo.GetAI()).Phase = RAJPhase.Romulo;
                            DoZoneInCombat(pRomulo);

                            pRomulo.SetFaction(16);
                        }
                        SummonedRomulo = true;
                    }
                    else SummonRomuloTimer -= diff;
                }

                if (ResurrectSelfTimer != 0)
                {
                    if (ResurrectSelfTimer <= diff)
                    {
                        Resurrect(me);
                        Phase = RAJPhase.Both;
                        IsFakingDeath = false;

                        if (me.GetVictim())
                            AttackStart(me.GetVictim());

                        ResurrectSelfTimer = 0;
                        ResurrectTimer = 1000;
                    }
                    else ResurrectSelfTimer -= diff;
                }

                if (!UpdateVictim() || IsFakingDeath)
                    return;

                if (RomuloDead)
                {
                    if (ResurrectTimer <= diff)
                    {
                        Creature Romulo = ObjectAccessor.GetCreature(me, RomuloGUID);
                        if (Romulo && ((julianne_romulobase)Romulo.GetAI()).IsFakingDeath)
                        {
                            Talk(JulianneRomulo.SayJulianneResurrect);
                            Resurrect(Romulo);
                            ((julianne_romulobase)Romulo.GetAI()).IsFakingDeath = false;
                            RomuloDead = false;
                            ResurrectTimer = 10000;
                        }
                    }
                    else ResurrectTimer -= diff;
                }

                if (BlindingPassionTimer <= diff)
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100, true);
                    if (target)
                        DoCast(target, JulianneRomulo.SpellBlindingPassion);
                    BlindingPassionTimer = RandomHelper.URand(30000, 45000);
                }
                else BlindingPassionTimer -= diff;

                if (DevotionTimer <= diff)
                {
                    DoCast(me, JulianneRomulo.SpellDevotion);
                    DevotionTimer = RandomHelper.URand(15000, 45000);
                }
                else DevotionTimer -= diff;

                if (PowerfulAttractionTimer <= diff)
                {
                    DoCast(SelectTarget(SelectAggroTarget.Random, 0), JulianneRomulo.SpellPowerfulAttraction);
                    PowerfulAttractionTimer = RandomHelper.URand(5000, 30000);
                }
                else PowerfulAttractionTimer -= diff;

                if (EternalAffectionTimer <= diff)
                {
                    if (RandomHelper.URand(0, 1) != 0 && SummonedRomulo)
                    {
                        Creature Romulo = ObjectAccessor.GetCreature(me, RomuloGUID);
                        if (Romulo && Romulo.IsAlive() && !RomuloDead)
                            DoCast(Romulo, JulianneRomulo.SpellEternalAffection);
                    }
                    else DoCast(me, JulianneRomulo.SpellEternalAffection);

                    EternalAffectionTimer = RandomHelper.URand(45000, 60000);
                }
                else EternalAffectionTimer -= diff;

                DoMeleeAttackIfReady();
            }

            uint BlindingPassionTimer;
            uint DevotionTimer;
            uint EternalAffectionTimer;
            uint PowerfulAttractionTimer;
            uint SummonRomuloTimer;
            uint DrinkPoisonTimer;

            bool SummonedRomulo;
        }

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

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

        public class boss_romuloAI : julianne_romulobase
        {
            public boss_romuloAI(Creature creature) : base(creature)
            {
                instance = creature.GetInstanceScript();
                EntryYellTimer = 8000;
                AggroYellTimer = 15000;
            }

            public override void Reset()
            {
                JulianneGUID.Clear();
                Phase = RAJPhase.Romulo;

                BackwardLungeTimer = 15000;
                DaringTimer = 20000;
                DeadlySwatheTimer = 25000;
                PoisonThrustTimer = 10000;
                ResurrectTimer = 10000;

                IsFakingDeath = false;
                JulianneDead = false;
            }

            public override void DamageTaken(Unit attacker, ref uint damage)
            {
                if (damage < me.GetHealth())
                    return;

                //anything below only used if incoming damage will kill

                if (Phase == RAJPhase.Romulo)
                {
                    Talk(JulianneRomulo.SayRomuloDeath);
                    PretendToDie();
                    IsFakingDeath = true;
                    Phase = RAJPhase.Both;

                    Creature Julianne = ObjectAccessor.GetCreature(me, JulianneGUID);
                    if (Julianne)
                    {
                        ((julianne_romulobase)Julianne.GetAI()).RomuloDead = true;
                        ((julianne_romulobase)Julianne.GetAI()).ResurrectSelfTimer = 10000;
                    }

                    damage = 0;
                    return;
                }

                if (Phase == RAJPhase.Both)
                {
                    Creature Julianne;
                    if (JulianneDead)
                    {
                        Julianne = ObjectAccessor.GetCreature(me, JulianneGUID);
                        if (Julianne)
                        {
                            Julianne.RemoveFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                            Julianne.GetMotionMaster().Clear();
                            Julianne.setDeathState(DeathState.JustDied);
                            Julianne.CombatStop(true);
                            Julianne.DeleteThreatList();
                            Julianne.SetUInt32Value(ObjectFields.DynamicFlags, (uint)UnitDynFlags.Lootable);
                        }
                        return;
                    }

                    Julianne = ObjectAccessor.GetCreature(me, JulianneGUID);
                    if (Julianne)
                    {
                        PretendToDie();
                        IsFakingDeath = true;
                        ((julianne_romulobase)Julianne.GetAI()).ResurrectTimer = 10000;
                        ((julianne_romulobase)Julianne.GetAI()).RomuloDead = true;
                        damage = 0;
                        return;
                    }
                }

                Log.outError(LogFilter.Scripts, "boss_romuloAI: DamageTaken reach end of code, that should not happen.");
            }

            public override void EnterCombat(Unit who)
            {
                Talk(JulianneRomulo.SayRomuloAggro);
                if (!JulianneGUID.IsEmpty())
                {
                    Creature Julianne = (ObjectAccessor.GetCreature(me, JulianneGUID));
                    if (Julianne && Julianne.GetVictim())
                    {
                        me.AddThreat(Julianne.GetVictim(), 1.0f);
                        AttackStart(Julianne.GetVictim());
                    }
                }
            }

            public override void JustDied(Unit killer)
            {
                Talk(JulianneRomulo.SayRomuloDeath);

                instance.SetData(karazhanConst.BossOpera, (uint)EncounterState.Done);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorleft), true);
                instance.HandleGameObject(instance.GetGuidData(DataTypes.GoStagedoorright), true);

                GameObject pSideEntrance = instance.instance.GetGameObject(instance.GetGuidData(DataTypes.GoSideEntranceDoor));
                if (pSideEntrance)
                    pSideEntrance.RemoveFlag(GameObjectFields.Flags, GameObjectFlags.Locked);
            }

            public override void KilledUnit(Unit victim)
            {
                Talk(JulianneRomulo.SayRomuloSlay);
            }

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

                if (JulianneDead)
                {
                    if (ResurrectTimer <= diff)
                    {
                        Creature Julianne = (ObjectAccessor.GetCreature(me, JulianneGUID));
                        if (Julianne && ((julianne_romulobase)Julianne.GetAI()).IsFakingDeath)
                        {
                            Talk(JulianneRomulo.SayRomuloResurrect);
                            Resurrect(Julianne);
                            ((julianne_romulobase)Julianne.GetAI()).IsFakingDeath = false;
                            JulianneDead = false;
                            ResurrectTimer = 10000;
                        }
                    }
                    else ResurrectTimer -= diff;
                }

                if (BackwardLungeTimer <= diff)
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 1, 100, true);
                    if (target && !me.HasInArc(MathFunctions.PI, target))
                    {
                        DoCast(target, JulianneRomulo.SpellBackwardLunge);
                        BackwardLungeTimer = RandomHelper.URand(15000, 30000);
                    }
                }
                else BackwardLungeTimer -= diff;

                if (DaringTimer <= diff)
                {
                    DoCast(me, JulianneRomulo.SpellDaring);
                    DaringTimer = RandomHelper.URand(20000, 40000);
                }
                else DaringTimer -= diff;

                if (DeadlySwatheTimer <= diff)
                {
                    Unit target = SelectTarget(SelectAggroTarget.Random, 0, 100, true);
                    if (target)
                        DoCast(target, JulianneRomulo.SpellDeadlySwathe);
                    DeadlySwatheTimer = RandomHelper.URand(15000, 25000);
                }
                else DeadlySwatheTimer -= diff;

                if (PoisonThrustTimer <= diff)
                {
                    DoCastVictim(JulianneRomulo.SpellPoisonThrust);
                    PoisonThrustTimer = RandomHelper.URand(10000, 20000);
                }
                else PoisonThrustTimer -= diff;

                DoMeleeAttackIfReady();
            }

            uint BackwardLungeTimer;
            uint DaringTimer;
            uint DeadlySwatheTimer;
            uint PoisonThrustTimer;
        }

        public override CreatureAI GetAI(Creature creature)
        {
            return GetInstanceAI<boss_romuloAI>(creature);
        }
    }
    #endregion

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

        class npc_barnesAI : npc_escortAI
        {
            public npc_barnesAI(Creature creature) : base(creature)
            {
                Initialize();
                instance = creature.GetInstanceScript();
            }

            void Initialize()
            {
                m_uiSpotlightGUID.Clear();

                TalkCount = 0;
                TalkTimer = 2000;
                WipeTimer = 5000;

                PerformanceReady = false;
            }

            public override void Reset()
            {
                Initialize();

                m_uiEventId = instance.GetData(DataTypes.OperaPerformance);
            }

            public void StartEvent()
            {
                instance.SetData(karazhanConst.BossOpera, (uint)EncounterState.InProgress);

                //resets count for this event, in case earlier failed
                if (m_uiEventId == OperaEvents.Oz)
                    instance.SetData(DataTypes.OperaOzDeathcount, (uint)EncounterState.InProgress);

                Start(false, false);
            }

            public override void EnterCombat(Unit who) { }

            public override void WaypointReached(uint waypointId)
            {
                switch (waypointId)
                {
                    case 0:
                        DoCast(me, karazhanConst.SpellTuxedo, false);
                        instance.DoUseDoorOrButton(instance.GetGuidData(DataTypes.GoStagedoorleft));
                        break;
                    case 4:
                        TalkCount = 0;
                        SetEscortPaused(true);

                        Creature spotlight = me.SummonCreature(karazhanConst.NpcSpotlight, me.GetPositionX(), me.GetPositionY(), me.GetPositionZ(), 0.0f, TempSummonType.TimedOrDeadDespawn, 60000);
                        if (spotlight)
                        {
                            spotlight.SetFlag(UnitFields.Flags, UnitFlags.NotSelectable);
                            spotlight.CastSpell(spotlight, karazhanConst.SpellSpotlight, false);
                            m_uiSpotlightGUID = spotlight.GetGUID();
                        }
                        break;
                    case 8:
                        instance.DoUseDoorOrButton(instance.GetGuidData(DataTypes.GoStagedoorleft));
                        PerformanceReady = true;
                        break;
                    case 9:
                        PrepareEncounter();
                        instance.DoUseDoorOrButton(instance.GetGuidData(DataTypes.GoCurtains));
                        break;
                }
            }

            void Talk(uint count)
            {
                int text = 0;

                switch (m_uiEventId)
                {
                    case OperaEvents.Oz:
                        if (karazhanConst.OzDialogue[count].TextId != 0)
                            text = karazhanConst.OzDialogue[count].TextId;
                        if (karazhanConst.OzDialogue[count].Timer != 0)
                            TalkTimer = karazhanConst.OzDialogue[count].Timer;
                        break;

                    case OperaEvents.Hood:
                        if (karazhanConst.HoodDialogue[count].TextId != 0)
                            text = karazhanConst.HoodDialogue[count].TextId;
                        if (karazhanConst.HoodDialogue[count].Timer != 0)
                            TalkTimer = karazhanConst.HoodDialogue[count].Timer;
                        break;

                    case OperaEvents.RAJ:
                        if (karazhanConst.RAJDialogue[count].TextId != 0)
                            text = karazhanConst.RAJDialogue[count].TextId;
                        if (karazhanConst.RAJDialogue[count].Timer != 0)
                            TalkTimer = karazhanConst.RAJDialogue[count].Timer;
                        break;
                }

                if (text != 0)
                    base.Talk((uint)text);
            }

            void PrepareEncounter()
            {
                int index = 0;
                int count = 0;

                switch (m_uiEventId)
                {
                    case OperaEvents.Oz:
                        index = 0;
                        count = 4;
                        break;
                    case OperaEvents.Hood:
                        index = 4;
                        count = index + 1;
                        break;
                    case OperaEvents.RAJ:
                        index = 5;
                        count = index + 1;
                        break;
                }

                for (; index < count; ++index)
                {
                    uint entry = (uint)karazhanConst.Spawns[index][0];
                    float PosX = karazhanConst.Spawns[index][1];

                    Creature creature = me.SummonCreature(entry, PosX, karazhanConst.SPAWN_Y, karazhanConst.SPAWN_Z, karazhanConst.SPAWN_O, TempSummonType.TimedOrDeadDespawn, Time.Hour * 2 * Time.InMilliseconds);
                    if (creature)
                    {
                        // In case database has bad flags
                        creature.SetUInt32Value(UnitFields.Flags, 0);
                        creature.SetFlag(UnitFields.Flags, UnitFlags.NonAttackable);
                    }
                }

                RaidWiped = false;
            }

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

                if (HasEscortState(eEscortState.Paused))
                {
                    if (TalkTimer <= diff)
                    {
                        if (TalkCount > 3)
                        {
                            Creature pSpotlight = ObjectAccessor.GetCreature(me, m_uiSpotlightGUID);
                            if (pSpotlight)
                                pSpotlight.DespawnOrUnsummon();

                            SetEscortPaused(false);
                            return;
                        }

                        Talk(TalkCount);
                        ++TalkCount;
                    }
                    else
                        TalkTimer -= diff;
                }

                if (PerformanceReady)
                {
                    if (!RaidWiped)
                    {
                        if (WipeTimer <= diff)
                        {
                            var PlayerList = me.GetMap().GetPlayers();
                            if (PlayerList.Empty())
                                return;

                            RaidWiped = true;
                            foreach (var player in PlayerList)
                            {
                                if (player.IsAlive() && !player.IsGameMaster())
                                {
                                    RaidWiped = false;
                                    break;
                                }
                            }

                            if (RaidWiped)
                            {
                                RaidWiped = true;
                                EnterEvadeMode();
                                return;
                            }

                            WipeTimer = 15000;
                        }
                        else
                            WipeTimer -= diff;
                    }
                }
            }

            InstanceScript instance;

            ObjectGuid m_uiSpotlightGUID;

            uint TalkCount;
            uint TalkTimer;
            uint WipeTimer;
            public uint m_uiEventId;

            bool PerformanceReady;
            public bool RaidWiped;
        }

        public override bool OnGossipSelect(Player player, Creature creature, uint sender, uint action)
        {
            player.PlayerTalkClass.ClearMenus();
            npc_barnesAI pBarnesAI = (npc_barnesAI)creature.GetAI();

            switch (action)
            {
                case eTradeskill.GossipActionInfoDef + 1:
                    player.ADD_GOSSIP_ITEM(GossipOptionIcon.Chat, karazhanConst.OZ_GOSSIP2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 2);
                    player.SEND_GOSSIP_MENU(8971, creature.GetGUID());
                    break;
                case eTradeskill.GossipActionInfoDef + 2:
                    player.CLOSE_GOSSIP_MENU();
                    pBarnesAI.StartEvent();
                    break;
                case eTradeskill.GossipActionInfoDef + 3:
                    player.CLOSE_GOSSIP_MENU();
                    pBarnesAI.m_uiEventId = OperaEvents.Oz;
                    Log.outInfo(LogFilter.Scripts, "player (GUID {0}) manually set Opera event to EVENT_OZ", player.GetGUID());
                    break;
                case eTradeskill.GossipActionInfoDef + 4:
                    player.CLOSE_GOSSIP_MENU();
                    pBarnesAI.m_uiEventId = OperaEvents.Hood;
                    Log.outInfo(LogFilter.Scripts, "player (GUID {0}) manually set Opera event to EVENT_HOOD", player.GetGUID());
                    break;
                case eTradeskill.GossipActionInfoDef + 5:
                    player.CLOSE_GOSSIP_MENU();
                    pBarnesAI.m_uiEventId = OperaEvents.RAJ;
                    Log.outInfo(LogFilter.Scripts, "player (GUID {0}) manually set Opera event to EVENT_RAJ", player.GetGUID());
                    break;
            }

            return true;
        }

        public override bool OnGossipHello(Player player, Creature creature)
        {
            InstanceScript instance = creature.GetInstanceScript();
            if (instance != null)
            {
                // Check for death of Moroes and if opera event is not done already
                if (instance.GetData(karazhanConst.BossMoroes) == (uint)EncounterState.Done && instance.GetData(karazhanConst.BossOpera) != (uint)EncounterState.Done)
                {
                    player.ADD_GOSSIP_ITEM(GossipOptionIcon.Chat, karazhanConst.OZ_GOSSIP1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 1);

                    if (player.IsGameMaster())
                    {
                        player.ADD_GOSSIP_ITEM(GossipOptionIcon.Dot, karazhanConst.OZ_GM_GOSSIP1, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 3);
                        player.ADD_GOSSIP_ITEM(GossipOptionIcon.Dot, karazhanConst.OZ_GM_GOSSIP2, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 4);
                        player.ADD_GOSSIP_ITEM(GossipOptionIcon.Dot, karazhanConst.OZ_GM_GOSSIP3, eTradeskill.GossipSenderMain, eTradeskill.GossipActionInfoDef + 5);
                    }

                    npc_barnesAI pBarnesAI = (npc_barnesAI)creature.GetAI();
                    if (pBarnesAI != null)
                    {
                        if (!pBarnesAI.RaidWiped)
                            player.SEND_GOSSIP_MENU(8970, creature.GetGUID());
                        else
                            player.SEND_GOSSIP_MENU(8975, creature.GetGUID());

                        return true;
                    }
                }
            }

            player.SEND_GOSSIP_MENU(8978, creature.GetGUID());
            return true;
        }

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