/*
 * This file is part of the AzerothCore Project. See AUTHORS file for Copyright information
 *
 * This program is free software; you can redistribute it and/or modify it
 * under the terms of the GNU Affero 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 Affero 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/>.
 */

#include "CellImpl.h"
#include "GameEventMgr.h"
#include "GameObjectAI.h"
#include "GridNotifiers.h"
#include "Group.h"
#include "LFGMgr.h"
#include "PassiveAI.h"
#include "ScriptedCreature.h"
#include "ScriptedGossip.h"
#include "ScriptMgr.h"
#include "SpellAuraEffects.h"
#include "SpellAuras.h"
#include "SpellScript.h"
#include "TaskScheduler.h"

///////////////////////////////////////
////// GOS
///////////////////////////////////////

///////////////////////////////////////
////// NPCS
///////////////////////////////////////
enum kegThrowers
{
    QUEST_THERE_AND_BACK_AGAIN_A                = 11122,
    QUEST_THERE_AND_BACK_AGAIN_H                = 11412,
    RAM_DISPLAY_ID                              = 22630,
    NPC_FLYNN_FIREBREW                          = 24364,
    NPC_BOK_DROPCERTAIN                         = 24527,
    ITEM_PORTABLE_BREWFEST_KEG                  = 33797,
    SPELL_THROW_KEG                             = 43660,
    SPELL_RAM_AURA                              = 43883,
    SPELL_ADD_TOKENS                            = 44501,
    SPELL_COOLDOWN_CHECKER                      = 43755,
    NPC_RAM_MASTER_RAY                          = 24497,
    NPC_NEILL_RAMSTEIN                          = 23558,
    KEG_KILL_CREDIT                             = 24337,
};

class npc_brewfest_keg_thrower : public CreatureScript
{
public:
    npc_brewfest_keg_thrower() : CreatureScript("npc_brewfest_keg_thrower") { }

    struct npc_brewfest_keg_throwerAI : public ScriptedAI
    {
        npc_brewfest_keg_throwerAI(Creature* creature) : ScriptedAI(creature)
        {
        }

        void MoveInLineOfSight(Unit* who) override
        {
            if (me->GetDistance(who) < 10.0f && who->GetTypeId() == TYPEID_PLAYER && who->GetMountID() == RAM_DISPLAY_ID)
            {
                if (!who->ToPlayer()->HasItemCount(ITEM_PORTABLE_BREWFEST_KEG)) // portable brewfest keg
                    me->CastSpell(who, SPELL_THROW_KEG, true);          // throw keg
            }
        }

        bool CanBeSeen(const Player* player) override
        {
            if (player->GetMountID() == RAM_DISPLAY_ID)
                return true;

            return false;
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_brewfest_keg_throwerAI(creature);
    }
};

class npc_brewfest_keg_reciver : public CreatureScript
{
public:
    npc_brewfest_keg_reciver() : CreatureScript("npc_brewfest_keg_reciver") { }

    struct npc_brewfest_keg_reciverAI : public ScriptedAI
    {
        npc_brewfest_keg_reciverAI(Creature* creature) : ScriptedAI(creature)
        {
        }

        void MoveInLineOfSight(Unit* who) override
        {
            if (me->GetDistance(who) < 10.0f && who->GetTypeId() == TYPEID_PLAYER && who->GetMountID() == RAM_DISPLAY_ID)
            {
                Player* player = who->ToPlayer();
                if (player->HasItemCount(ITEM_PORTABLE_BREWFEST_KEG)) // portable brewfest keg
                {
                    player->KilledMonsterCredit(KEG_KILL_CREDIT);
                    player->CastSpell(me, SPELL_THROW_KEG, true);          // throw keg
                    player->DestroyItemCount(ITEM_PORTABLE_BREWFEST_KEG, 1, true);

                    // Additional Work
                    uint32 spellCooldown = player->GetSpellCooldownDelay(SPELL_COOLDOWN_CHECKER) / IN_MILLISECONDS;
                    if (spellCooldown > (HOUR * 18 - 900)) // max aproximated time - 12 minutes
                    {
                        if (Aura* aur = player->GetAura(SPELL_RAM_AURA))
                        {
                            int32 diff = aur->GetApplyTime() - (time(nullptr) - (HOUR * 18) + spellCooldown);
                            if (diff > 10) // aura applied later
                                return;

                            aur->SetDuration(aur->GetDuration() + 30000);
                            player->CastSpell(player, SPELL_ADD_TOKENS, true);
                        }
                    }
                }
            }
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_brewfest_keg_reciverAI(creature);
    }

    bool OnGossipSelect(Player* player, Creature* /*creature*/, uint32 /*uiSender*/, uint32 uiAction) override
    {
        switch (uiAction)
        {
            case GOSSIP_ACTION_INFO_DEF+1:
                CloseGossipMenuFor(player);
                player->AddSpellCooldown(SPELL_COOLDOWN_CHECKER, 0, 18 * HOUR * IN_MILLISECONDS);
                player->CastSpell(player, 43883, true);
                player->CastSpell(player, 44262, true);
                break;
        }
        return true;
    }

    bool OnGossipHello(Player* player, Creature* creature) override
    {
        if (creature->IsQuestGiver())
            player->PrepareQuestMenu(creature->GetGUID());

        if (!player->HasSpellCooldown(SPELL_COOLDOWN_CHECKER) && player->GetQuestRewardStatus(player->GetTeamId() == TEAM_ALLIANCE ? QUEST_THERE_AND_BACK_AGAIN_A : QUEST_THERE_AND_BACK_AGAIN_H))
            AddGossipItemFor(player, GOSSIP_ICON_CHAT, "Do you have additional work?", GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);

        SendGossipMenuFor(player, (creature->GetEntry() == NPC_NEILL_RAMSTEIN ? 8934 : 8976), creature->GetGUID());
        return true;
    }
};

enum barkTrigger
{
    QUEST_BARK_FOR_DROHN                = 11407,
    QUEST_BARK_FOR_VOODOO               = 11408,
    QUEST_BARK_FOR_BARLEY               = 11293,
    QUEST_BARK_FOR_THUNDERBREW          = 11294,
};

class npc_brewfest_bark_trigger : public CreatureScript
{
public:
    npc_brewfest_bark_trigger() : CreatureScript("npc_brewfest_bark_trigger") { }

    struct npc_brewfest_bark_triggerAI : public ScriptedAI
    {
        npc_brewfest_bark_triggerAI(Creature* creature) : ScriptedAI(creature)
        {
        }

        void MoveInLineOfSight(Unit* who) override
        {
            if (me->GetDistance(who) < 10.0f && who->GetTypeId() == TYPEID_PLAYER && who->GetMountID() == RAM_DISPLAY_ID)
            {
                bool allow = false;
                uint32 quest = 0;
                Player* player = who->ToPlayer();
                // Kalimdor
                if (me->GetMapId() == 1)
                {
                    if (player->GetQuestStatus(QUEST_BARK_FOR_DROHN) == QUEST_STATUS_INCOMPLETE)
                    {
                        allow = true;
                        quest = QUEST_BARK_FOR_DROHN;
                    }
                    else if (player->GetQuestStatus(QUEST_BARK_FOR_VOODOO) == QUEST_STATUS_INCOMPLETE)
                    {
                        allow = true;
                        quest = QUEST_BARK_FOR_VOODOO;
                    }
                }
                else if (me->GetMapId() == 0)
                {
                    if (player->GetQuestStatus(QUEST_BARK_FOR_BARLEY) == QUEST_STATUS_INCOMPLETE)
                    {
                        allow = true;
                        quest = QUEST_BARK_FOR_BARLEY;
                    }
                    else if (player->GetQuestStatus(QUEST_BARK_FOR_THUNDERBREW) == QUEST_STATUS_INCOMPLETE)
                    {
                        allow = true;
                        quest = QUEST_BARK_FOR_THUNDERBREW;
                    }
                }

                if (allow)
                {
                    QuestStatusMap::iterator itr = player->getQuestStatusMap().find(quest);
                    if (itr == player->getQuestStatusMap().end())
                        return;

                    QuestStatusData& q_status = itr->second;
                    if (q_status.CreatureOrGOCount[me->GetEntry() - 24202] == 0)
                    {
                        player->KilledMonsterCredit(me->GetEntry());
                        player->Say(GetTextFor(me->GetEntry(), quest).c_str(), LANG_UNIVERSAL, player);
                    }
                }
            }
        }

        std::string GetTextFor(uint32  /*entry*/, uint32 questId)
        {
            std::string str = "";
            switch (questId)
            {
                case QUEST_BARK_FOR_DROHN:
                case QUEST_BARK_FOR_VOODOO:
                    {
                        switch (urand(0, 3))
                        {
                            case 0:
                                str = "Join with your brothers and sisters at " + std::string(questId == QUEST_BARK_FOR_DROHN ? "Drohn's Distillery" : "T'chali's Voodoo Brewery") + " and drink for the horde!";
                                break;
                            case 1:
                                str = "If you think an orc can hit hard, check out their brew, it hits even harder! See for yourself at " + std::string(questId == QUEST_BARK_FOR_DROHN ? "Drohn's Distillery" : "T'chali's Voodoo Brewery") + ", only at Brewfest!";
                                break;
                            case 2:
                                str = "Celebrate Brewfest with orcs that know what a good drink really is! Check out " + std::string(questId == QUEST_BARK_FOR_DROHN ? "Drohn's Distillery" : "T'chali's Voodoo Brewery") + " at Brewfest!";
                                break;
                            case 3:
                                str = std::string(questId == QUEST_BARK_FOR_DROHN ? "Drohn's Distillery" : "T'chali's Voodoo Brewery") + "  knows how to party hard! Check them out at Brewfest!";
                                break;
                        }
                        break;
                    }
                case QUEST_BARK_FOR_BARLEY:
                case QUEST_BARK_FOR_THUNDERBREW:
                    {
                        switch (urand(0, 3))
                        {
                            case 0:
                                str = "Join with your brothers and sisters at " + std::string(questId == QUEST_BARK_FOR_BARLEY ? "Barleybrews" : "Thunderbrews") + " and drink for the alliance!";
                                break;
                            case 1:
                                str = "If you think an dwarf can hit hard, check out their brew, it hits even harder! See for yourself at " + std::string(questId == QUEST_BARK_FOR_BARLEY ? "Barleybrews" : "Thunderbrews") + ", only at Brewfest!";
                                break;
                            case 2:
                                str = "Celebrate Brewfest with dwarves that know what a good drink really is! Check out " + std::string(questId == QUEST_BARK_FOR_BARLEY ? "Barleybrews" : "Thunderbrews") + " at Brewfest!";
                                break;
                            case 3:
                                str = std::string(questId == QUEST_BARK_FOR_BARLEY ? "Barleybrews" : "Thunderbrews") + "  knows how to party hard! Check them out at Brewfest!";
                                break;
                        }
                        break;
                    }
            }

            return str;
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_brewfest_bark_triggerAI(creature);
    }
};

enum darkIronAttack
{
    // Gos
    GO_MOLE_MACHINE                     = 195305,

    // Npcs
    NPC_BARLEYBREW_KEG                  = 23700,
    NPC_THUNDERBREW_KEG                 = 23702,
    NPC_GORDOK_KEG                      = 23706,
    NPC_VOODOO_KEG                      = 24373,
    NPC_DROHN_KEG                       = 24372,
    NPC_MOLE_MACHINE_TRIGGER            = 23894,
    NPC_DARK_IRON_GUZZLER               = 23709,
    NPC_NORMAL_DROHN                    = 24492,
    NPC_NORMAL_VOODOO                   = 21349,
    NPC_NORMAL_BARLEYBREW               = 23683,
    NPC_NORMAL_THUNDERBREW              = 23684,
    NPC_NORMAL_GORDOK                   = 23685,
    NPC_EVENT_GENERATOR                 = 23703,
    NPC_SUPER_BREW_TRIGGER              = 23808,
    NPC_DARK_IRON_HERALD                = 24536,
    NPC_BREWFEST_REVELER                = 24484,

    // Events
    EVENT_CHECK_HOUR                    = 1,
    EVENT_SPAWN_MOLE_MACHINE            = 2,
    EVENT_PRE_FINISH_ATTACK             = 3,
    EVENT_FINISH_ATTACK                 = 4,
    EVENT_BARTENDER_SAY                 = 5,

    // Spells
    SPELL_THROW_MUG_TO_PLAYER           = 42300,
    SPELL_ADD_MUG                       = 42518,
    SPELL_SPAWN_MOLE_MACHINE            = 43563,
    SPELL_KEG_MARKER                    = 42761,
    SPELL_PLAYER_MUG                    = 42436,
    SPELL_REPORT_DEATH                  = 42655,
    SPELL_CREATE_SUPER_BREW             = 42715,
    SPELL_DRUNKEN_MASTER                = 42696,
    SPELL_SUMMON_PLANS_A                = 48145,
    SPELL_SUMMON_PLANS_H                = 49318,

    // Dark Irons
    SPELL_ATTACK_KEG                    = 42393,
    SPELL_KNOCKBACK_AURA                = 42676,
    SPELL_MUG_BOUNCE_BACK               = 42522,
};

class npc_dark_iron_attack_generator : public CreatureScript
{
public:
    npc_dark_iron_attack_generator() : CreatureScript("npc_dark_iron_attack_generator") { }

    struct npc_dark_iron_attack_generatorAI : public ScriptedAI
    {
        npc_dark_iron_attack_generatorAI(Creature* creature) : ScriptedAI(creature), summons(me)
        {
        }

        EventMap events;
        SummonList summons;
        uint32 kegCounter, guzzlerCounter;
        uint8 thrown;
        GuidVector revelerGUIDs;

        void Reset() override
        {
            for (ObjectGuid const& guid : revelerGUIDs)
            {
                if (Creature* reveler = ObjectAccessor::GetCreature(*me, guid))
                {
                    reveler->SetRespawnDelay(5 * MINUTE);
                    reveler->Respawn();
                }
            }
            revelerGUIDs.clear();

            summons.DespawnAll();
            events.Reset();
            events.ScheduleEvent(EVENT_CHECK_HOUR, 2000);
            kegCounter = 0;
            guzzlerCounter = 0;
            thrown = 0;
        }

        // DARK IRON ATTACK EVENT
        void MoveInLineOfSight(Unit*  /*who*/) override {}
        void EnterCombat(Unit*) override {}

        void SpellHit(Unit* caster, const SpellInfo* spellInfo) override
        {
            if (spellInfo->Id == SPELL_REPORT_DEATH)
            {
                if (caster->GetEntry() == NPC_DARK_IRON_GUZZLER)
                    guzzlerCounter++;
                else
                {
                    kegCounter++;
                    if (kegCounter == 3)
                        FinishEventDueToLoss();
                }
            }
        }

        void UpdateAI(uint32 diff) override
        {
            events.Update(diff);
            switch (events.ExecuteEvent())
            {
                case EVENT_CHECK_HOUR:
                    {
                        // determine hour
                        if (AllowStart())
                        {
                            PrepareEvent();
                            events.RepeatEvent(300000);
                            return;
                        }
                        events.RepeatEvent(2000);
                        break;
                    }
                case EVENT_SPAWN_MOLE_MACHINE:
                    {
                        if (me->GetMapId() == 1) // Kalimdor
                        {
                            float rand = 8 + rand_norm() * 12;
                            float angle = rand_norm() * 2 * M_PI;
                            float x = 1201.8f + rand * cos(angle);
                            float y = -4299.6f + rand * sin(angle);
                            if (Creature* cr = me->SummonCreature(NPC_MOLE_MACHINE_TRIGGER, x, y, 21.3f, 0.0f))
                                cr->CastSpell(cr, SPELL_SPAWN_MOLE_MACHINE, true);
                        }
                        else if (me->GetMapId() == 0) // EK
                        {
                            float rand = rand_norm() * 20;
                            float angle = rand_norm() * 2 * M_PI;
                            float x = -5157.1f + rand * cos(angle);
                            float y = -598.98f + rand * sin(angle);
                            if (Creature* cr = me->SummonCreature(NPC_MOLE_MACHINE_TRIGGER, x, y, 398.11f, 0.0f))
                                cr->CastSpell(cr, SPELL_SPAWN_MOLE_MACHINE, true);
                        }
                        events.RepeatEvent(3000);
                        break;
                    }
                case EVENT_PRE_FINISH_ATTACK:
                    {
                        events.CancelEvent(EVENT_SPAWN_MOLE_MACHINE);
                        events.ScheduleEvent(EVENT_FINISH_ATTACK, 20000);
                        break;
                    }
                case EVENT_FINISH_ATTACK:
                    {
                        FinishAttackDueToWin();
                        events.RescheduleEvent(EVENT_CHECK_HOUR, 60000);
                        break;
                    }
                case EVENT_BARTENDER_SAY:
                    {
                        events.RepeatEvent(12000);
                        Creature* sayer = GetRandomBartender();
                        if (!sayer)
                            return;

                        thrown++;
                        if (thrown == 3)
                        {
                            thrown = 0;
                            sayer->Say("SOMEONE TRY THIS SUPER BREW!", LANG_UNIVERSAL);
                            //sayer->CastSpell(sayer, SPELL_CREATE_SUPER_BREW, true);
                            sayer->SummonCreature(NPC_SUPER_BREW_TRIGGER, sayer->GetPositionX() + 15 * cos(sayer->GetOrientation()), sayer->GetPositionY() + 15 * sin(sayer->GetOrientation()), sayer->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 30000);
                        }
                        else
                        {
                            if (urand(0, 1))
                                sayer->Say("Chug and chuck! Chug and chuck!", LANG_UNIVERSAL);
                            else
                                sayer->Say("Down the free brew and pelt the Guzzlers with your mug!", LANG_UNIVERSAL);
                        }

                        break;
                    }
            }
        }

        void FinishEventDueToLoss()
        {
            if (Creature* herald = me->FindNearestCreature(NPC_DARK_IRON_HERALD, 100.0f))
            {
                char amount[500];
                sprintf(amount, "We did it boys! Now back to the Grim Guzzler and we'll drink to the %u that were injured!", guzzlerCounter);
                herald->Yell(amount, LANG_UNIVERSAL);
            }

            Reset();
            events.RescheduleEvent(EVENT_CHECK_HOUR, 60000);
        }

        void FinishAttackDueToWin()
        {
            if (Creature* herald = me->FindNearestCreature(NPC_DARK_IRON_HERALD, 100.0f))
            {
                char amount[500];
                sprintf(amount, "RETREAT!! We've already lost %u and we can't afford to lose any more!!", guzzlerCounter);
                herald->Yell(amount, LANG_UNIVERSAL);
            }

            me->CastSpell(me, (me->GetMapId() == 1 ? SPELL_SUMMON_PLANS_H : SPELL_SUMMON_PLANS_A), true);
            Reset();
        }

        void PrepareEvent()
        {
            std::list<Creature*> revelers;
            GetCreatureListWithEntryInGrid(revelers, me, NPC_BREWFEST_REVELER, 100.f);
            for (Creature* reveler : revelers)
            {
                revelerGUIDs.push_back(reveler->GetGUID());
                reveler->SetRespawnDelay(MONTH);
                reveler->AI()->SetData(0, me->GetMapId());
            }

            Creature* cr;
            if (me->GetMapId() == 1) // Kalimdor
            {
                if ((cr = me->SummonCreature(NPC_DROHN_KEG, 1183.69f, -4315.15f, 21.1875f, 0.750492f)))
                {
                    cr->SetReactState(REACT_PASSIVE);
                    summons.Summon(cr);
                    revelerGUIDs.push_back(cr->GetGUID());
                }
                if ((cr = me->SummonCreature(NPC_VOODOO_KEG, 1182.42f, -4272.45f, 21.1182f, -1.02974f)))
                {
                    cr->SetReactState(REACT_PASSIVE);
                    summons.Summon(cr);
                    revelerGUIDs.push_back(cr->GetGUID());
                }
                if ((cr = me->SummonCreature(NPC_GORDOK_KEG, 1223.78f, -4296.48f, 21.1707f, -2.86234f)))
                {
                    cr->SetReactState(REACT_PASSIVE);
                    summons.Summon(cr);
                    revelerGUIDs.push_back(cr->GetGUID());
                }
            }
            else if (me->GetMapId() == 0) // Eastern Kingdom
            {
                if ((cr = me->SummonCreature(NPC_BARLEYBREW_KEG, -5187.23f, -599.779f, 397.176f, 0.017453f)))
                {
                    cr->SetReactState(REACT_PASSIVE);
                    summons.Summon(cr);
                    revelerGUIDs.push_back(cr->GetGUID());
                }
                if ((cr = me->SummonCreature(NPC_THUNDERBREW_KEG, -5160.05f, -632.632f, 397.178f, 1.39626f)))
                {
                    cr->SetReactState(REACT_PASSIVE);
                    summons.Summon(cr);
                    revelerGUIDs.push_back(cr->GetGUID());
                }
                if ((cr = me->SummonCreature(NPC_GORDOK_KEG, -5145.75f, -575.667f, 397.176f, -2.28638f)))
                {
                    cr->SetReactState(REACT_PASSIVE);
                    summons.Summon(cr);
                    revelerGUIDs.push_back(cr->GetGUID());
                }
            }

            if ((cr = me->SummonCreature(NPC_DARK_IRON_HERALD, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, TEMPSUMMON_TIMED_DESPAWN, 300000)))
                summons.Summon(cr);

            kegCounter = 0;
            guzzlerCounter = 0;
            thrown = 0;

            events.ScheduleEvent(EVENT_SPAWN_MOLE_MACHINE, 1500);
            events.ScheduleEvent(EVENT_PRE_FINISH_ATTACK, 280000);
            events.ScheduleEvent(EVENT_BARTENDER_SAY, 5000);
        }

        bool AllowStart()
        {
            time_t curtime = time(nullptr);
            tm strDate;
            localtime_r(&curtime, &strDate);

            if (strDate.tm_min == 0 || strDate.tm_min == 30)
                return true;

            return false;
        }

        Creature* GetRandomBartender()
        {
            uint32 entry = 0;
            switch (urand(0, 2))
            {
                case 0:
                    entry = (me->GetMapId() == 1 ? NPC_NORMAL_DROHN : NPC_NORMAL_THUNDERBREW);
                    break;
                case 1:
                    entry = (me->GetMapId() == 1 ? NPC_NORMAL_VOODOO : NPC_NORMAL_BARLEYBREW);
                    break;
                case 2:
                    entry = NPC_NORMAL_GORDOK;
                    break;
            }

            return me->FindNearestCreature(entry, 100.0f);
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_dark_iron_attack_generatorAI(creature);
    }
};

class npc_dark_iron_attack_mole_machine : public CreatureScript
{
public:
    npc_dark_iron_attack_mole_machine() : CreatureScript("npc_dark_iron_attack_mole_machine") { }

    struct npc_dark_iron_attack_mole_machineAI : public ScriptedAI
    {
        npc_dark_iron_attack_mole_machineAI(Creature* creature) : ScriptedAI(creature)
        {
        }

        void EnterCombat(Unit*) override {}
        void MoveInLineOfSight(Unit*) override {}
        void AttackStart(Unit*) override {}

        uint32 goTimer, summonTimer;
        void Reset() override
        {
            goTimer = 1;
            summonTimer = 0;
        }

        void UpdateAI(uint32 diff) override
        {
            if (goTimer)
            {
                goTimer += diff;
                if (goTimer >= 3000)
                {
                    goTimer = 0;
                    summonTimer++;
                    if (GameObject* drill = me->SummonGameObject(GO_MOLE_MACHINE, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), M_PI / 4, 0.0f, 0.0f, 0.0f, 0.0f, 8))
                    {
                        //drill->SetGoAnimProgress(0);
                        drill->SetLootState(GO_READY);
                        drill->UseDoorOrButton(8);
                    }
                }
            }
            if (summonTimer)
            {
                summonTimer += diff;
                if (summonTimer >= 2000 && summonTimer < 10000)
                {
                    me->SummonCreature(NPC_DARK_IRON_GUZZLER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000);
                    summonTimer = 10000;
                }
                if (summonTimer >= 13000 && summonTimer < 20000)
                {
                    me->SummonCreature(NPC_DARK_IRON_GUZZLER, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 6000);
                    summonTimer = 0;
                    me->DespawnOrUnsummon(3000);
                }
            }
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_dark_iron_attack_mole_machineAI(creature);
    }
};

class npc_dark_iron_guzzler : public CreatureScript
{
public:
    npc_dark_iron_guzzler() : CreatureScript("npc_dark_iron_guzzler") { }

    struct npc_dark_iron_guzzlerAI : public ScriptedAI
    {
        npc_dark_iron_guzzlerAI(Creature* creature) : ScriptedAI(creature)
        {
            me->SetReactState(REACT_PASSIVE);
            attacking = false;
        }

        uint32 timer;
        ObjectGuid targetGUID;
        bool attacking;

        void EnterCombat(Unit*) override {}
        void MoveInLineOfSight(Unit*) override {}
        void AttackStart(Unit*) override {}

        void DamageTaken(Unit*, uint32& damage, DamageEffectType, SpellSchoolMask) override
        {
            damage = 0;
        }

        void MovementInform(uint32 type, uint32 /*id*/) override
        {
            if (type != FOLLOW_MOTION_TYPE)
            {
                return;
            }

            if (Unit* target = GetTarget())
            {
                timer = 0;
                attacking = true;
                me->CastSpell(target, SPELL_ATTACK_KEG, false);
            }
        }

        void FindNextKeg()
        {
            uint32 entry[3] = {0, 0, 0};
            uint32 shuffled[3] = {0, 0, 0};

            if (me->GetMapId() == 1) // Kalimdor
            {
                entry[0] = NPC_DROHN_KEG;
                entry[1] = NPC_VOODOO_KEG;
                entry[2] = NPC_GORDOK_KEG;
            }
            else// if (me->GetMapId() == 0) // EK
            {
                entry[0] = NPC_THUNDERBREW_KEG;
                entry[1] = NPC_BARLEYBREW_KEG;
                entry[2] = NPC_GORDOK_KEG;
            }

            for (uint8 i = 0; i < 3; ++i)
            {
                uint8 index = 0;
                do
                    index = urand(0, 2);
                while (shuffled[index]);

                shuffled[index] = entry[i];
            }

            attacking = false;

            for (uint8 i = 0; i < 3; ++i)
                if (Creature* cr = me->FindNearestCreature(shuffled[i], 100.0f))
                {
                    cr->SetWalk(true);
                    me->GetMotionMaster()->MoveFollow(cr, 1.0f, cr->GetAngle(me));
                    targetGUID = cr->GetGUID();
                    return;
                }

            // no kegs found
            me->DisappearAndDie();
        }

        Unit* GetTarget() { return ObjectAccessor::GetUnit(*me, targetGUID); }

        void Reset() override
        {
            timer = 0;
            targetGUID.Clear();
            me->SetWalk(true);
            FindNextKeg();
            me->ApplySpellImmune(SPELL_ATTACK_KEG, IMMUNITY_ID, SPELL_ATTACK_KEG, true);
            SayText();
            me->CastSpell(me, SPELL_KNOCKBACK_AURA, true);
        }

        void SayText()
        {
            if (!urand(0, 20))
            {
                switch (urand(0, 4))
                {
                    case 0:
                        me->Say("Drink it all boys!", LANG_UNIVERSAL);
                        break;
                    case 1:
                        me->Say("DRINK! BRAWL! DRINK! BRAWL!", LANG_UNIVERSAL);
                        break;
                    case 2:
                        me->Say("Did someone say, \"Free Brew\"?", LANG_UNIVERSAL);
                        break;
                    case 3:
                        me->Say("No one expects the Dark Iron dwarves!", LANG_UNIVERSAL);
                        break;
                    case 4:
                        me->Say("It's not a party without some crashers!", LANG_UNIVERSAL);
                        break;
                }
            }
        }

        void KilledUnit(Unit* who) override
        {
            who->CastSpell(who, SPELL_REPORT_DEATH, true);
        }

        void SpellHit(Unit*  /*caster*/, const SpellInfo* spellInfo) override
        {
            if (me->IsAlive() && spellInfo->Id == SPELL_PLAYER_MUG)
            {
                me->CastSpell(me, SPELL_MUG_BOUNCE_BACK, true);
                Unit::Kill(me, me);
                me->CastSpell(me, SPELL_REPORT_DEATH, true);
            }
        }

        void UpdateAI(uint32 diff) override
        {
            timer += diff;
            if (timer < 2000)
                return;

            timer = 0;
            if (targetGUID)
            {
                Unit* target = GetTarget();
                if (target && target->IsAlive())
                {
                    if (attacking)
                    {
                        me->CastSpell(target, SPELL_ATTACK_KEG, false);
                    }
                }
                else
                {
                    FindNextKeg();
                }
            }
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_dark_iron_guzzlerAI(creature);
    }
};

class npc_brewfest_super_brew_trigger : public CreatureScript
{
public:
    npc_brewfest_super_brew_trigger() : CreatureScript("npc_brewfest_super_brew_trigger") { }

    struct npc_brewfest_super_brew_triggerAI : public ScriptedAI
    {
        npc_brewfest_super_brew_triggerAI(Creature* creature) : ScriptedAI(creature)
        {
        }

        uint32 timer;
        void EnterCombat(Unit*) override {}
        void MoveInLineOfSight(Unit*  /*who*/) override
        {
        }

        void AttackStart(Unit*) override {}

        void Reset() override
        {
            timer = 0;
            me->SummonGameObject(186478, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), 0.0f, 0.0f, 0.0f, 0.0f, 0.0f, 30000);
        }

        void UpdateAI(uint32 diff) override
        {
            timer += diff;
            if (timer >= 500)
            {
                timer = 0;
                Player* player = nullptr;
                Acore::AnyPlayerInObjectRangeCheck checker(me, 2.0f);
                Acore::PlayerSearcher<Acore::AnyPlayerInObjectRangeCheck> searcher(me, player, checker);
                Cell::VisitWorldObjects(me, searcher, 2.0f);
                if (player)
                {
                    player->CastSpell(player, SPELL_DRUNKEN_MASTER, true);
                    me->RemoveAllGameObjects();
                    Unit::Kill(me, me);
                }
            }
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_brewfest_super_brew_triggerAI(creature);
    }
};

///////////////////////////////////////
////// SPELLS
///////////////////////////////////////

enum ramRacing
{
    SPELL_TROT              = 42992,
    SPELL_CANTER            = 42993,
    SPELL_GALLOP            = 42994,
    SPELL_RAM_FATIGUE       = 43052,
    SPELL_RAM_EXHAUSTED     = 43332,

    CREDIT_TROT             = 24263,
    CREDIT_CANTER           = 24264,
    CREDIT_GALLOP           = 24265,

    RACING_RAM_MODEL        = 22630,
};

class spell_brewfest_main_ram_buff : public SpellScriptLoader
{
public:
    spell_brewfest_main_ram_buff() : SpellScriptLoader("spell_brewfest_main_ram_buff") { }

    class spell_brewfest_main_ram_buff_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_brewfest_main_ram_buff_AuraScript)

        uint8 privateLevel;
        uint32 questTick;
        bool Load() override
        {
            questTick = 0;
            privateLevel = 0;
            return true;
        }

        void HandleEffectPeriodic(AuraEffect const* aurEff)
        {
            Unit* caster = GetCaster();
            if (!caster || !caster->IsMounted() || !caster->ToPlayer())
                return;

            if (caster->GetMountID() != RACING_RAM_MODEL)
                return;

            Aura* aur = caster->GetAura(42924);
            if (!aur)
            {
                caster->CastSpell(caster, 42924, true);
                return;
            }

            // Check if exhausted
            if (caster->GetAura(SPELL_RAM_EXHAUSTED))
            {
                if (privateLevel)
                {
                    caster->RemoveAurasDueToSpell(SPELL_CANTER);
                    caster->RemoveAurasDueToSpell(SPELL_GALLOP);
                }

                aur->SetStackAmount(1);
                return;
            }

            uint32 stack = aur->GetStackAmount();
            uint8 mode = 0;
            switch (privateLevel)
            {
                case 0:
                    if (stack > 1)
                    {
                        questTick = 0;
                        caster->CastSpell(caster, SPELL_TROT, true);
                        privateLevel++;
                        mode = 1; // unapply
                        break;
                    }
                    // just walking, fatiuge handling
                    if (Aura* fatigueAura = caster->GetAura(SPELL_RAM_FATIGUE))
                    {
                        fatigueAura->ModStackAmount(-4);
                    }
                    break;
                case 1:
                    // One click to maintain speed, more to increase
                    if (stack < 2)
                    {
                        caster->RemoveAurasDueToSpell(SPELL_TROT);
                        questTick = 0;
                        privateLevel--;
                        mode = 2; // apply
                    }
                    else if (stack > 2)
                    {
                        questTick = 0;
                        caster->CastSpell(caster, SPELL_CANTER, true);
                        privateLevel++;
                    }
                    else if (questTick++ > 3)
                        caster->ToPlayer()->KilledMonsterCredit(CREDIT_TROT);
                    break;
                case 2:
                    // Two - three clicks to maintains speed, less to decrease, more to increase
                    if (stack < 3)
                    {
                        caster->CastSpell(caster, SPELL_TROT, true);
                        privateLevel--;
                        questTick = 0;
                    }
                    else if (stack > 4)
                    {
                        caster->CastSpell(caster, SPELL_GALLOP, true);
                        privateLevel++;
                        questTick = 0;
                    }
                    else if (questTick++ > 3)
                        caster->ToPlayer()->KilledMonsterCredit(CREDIT_CANTER);
                    break;
                case 3:
                    // Four or more clicks to maintains speed, less to decrease
                    if (stack < 5)
                    {
                        caster->CastSpell(caster, SPELL_CANTER, true);
                        privateLevel--;
                        questTick = 0;
                    }
                    else if (questTick++ > 3)
                        caster->ToPlayer()->KilledMonsterCredit(CREDIT_GALLOP);
                    break;
            }

            // Set to base amount
            aur->SetStackAmount(1);

            // apply/unapply effect 1
            if (mode)
                if (Aura* base = aurEff->GetBase())
                    if (AuraEffect* aEff = base->GetEffect(EFFECT_0))
                    {
                        aEff->SetAmount(mode == 1 ? 0 : -50);
                        caster->UpdateSpeed(MOVE_RUN, true);
                    }
        }

        void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            if (Unit* target = GetTarget())
                target->RemoveAurasDueToSpell(SPELL_RAM_FATIGUE);
        }

        void Register() override
        {
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_brewfest_main_ram_buff_AuraScript::HandleEffectPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
            OnEffectRemove += AuraEffectRemoveFn(spell_brewfest_main_ram_buff_AuraScript::HandleEffectRemove, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
        }
    };

    AuraScript* GetAuraScript() const override
    {
        return new spell_brewfest_main_ram_buff_AuraScript();
    }
};

class spell_brewfest_ram_fatigue : public SpellScriptLoader
{
public:
    spell_brewfest_ram_fatigue() : SpellScriptLoader("spell_brewfest_ram_fatigue") { }

    class spell_brewfest_ram_fatigue_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_brewfest_ram_fatigue_AuraScript)

        void HandleEffectPeriodic(AuraEffect const* aurEff)
        {
            int8 fatigue = 0;
            switch (aurEff->GetId())
            {
                case SPELL_TROT:
                    fatigue = -2;
                    break;
                case SPELL_CANTER:
                    fatigue = 1;
                    break;
                case SPELL_GALLOP:
                    fatigue = 5;
                    break;
            }
            if (Unit* target = GetTarget())
            {
                if (Aura* aur = target->GetAura(SPELL_RAM_FATIGUE))
                {
                    aur->ModStackAmount(fatigue);
                    if (aur->GetStackAmount() >= 100)
                        target->CastSpell(target, SPELL_RAM_EXHAUSTED, true);
                }
                else
                    target->CastSpell(target, SPELL_RAM_FATIGUE, true);
            }
        }

        void HandleEffectRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            if (Unit* target = GetTarget())
            {
                if (Aura* aur = target->GetAura(SPELL_RAM_FATIGUE))
                    aur->ModStackAmount(-15);
            }
        }

        void Register() override
        {
            if (m_scriptSpellId != 43332)
                OnEffectPeriodic += AuraEffectPeriodicFn(spell_brewfest_ram_fatigue_AuraScript::HandleEffectPeriodic, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
            else
                OnEffectRemove += AuraEffectRemoveFn(spell_brewfest_ram_fatigue_AuraScript::HandleEffectRemove, EFFECT_0, SPELL_AURA_MOD_DECREASE_SPEED, AURA_EFFECT_HANDLE_REAL);
        }
    };

    AuraScript* GetAuraScript() const override
    {
        return new spell_brewfest_ram_fatigue_AuraScript();
    }
};

class spell_brewfest_apple_trap : public SpellScriptLoader
{
public:
    spell_brewfest_apple_trap() : SpellScriptLoader("spell_brewfest_apple_trap") { }

    class spell_brewfest_apple_trap_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_brewfest_apple_trap_SpellScript);

        void HandleDummyEffect(SpellEffIndex /*effIndex*/)
        {
            if (Unit* target = GetHitUnit())
                if (Aura* aur = target->GetAura(SPELL_RAM_FATIGUE))
                    aur->Remove();
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_brewfest_apple_trap_SpellScript::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_brewfest_apple_trap_SpellScript();
    };
};

class spell_q11117_catch_the_wild_wolpertinger : public SpellScriptLoader
{
public:
    spell_q11117_catch_the_wild_wolpertinger() : SpellScriptLoader("spell_q11117_catch_the_wild_wolpertinger") { }

    class spell_q11117_catch_the_wild_wolpertinger_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_q11117_catch_the_wild_wolpertinger_SpellScript);

        SpellCastResult CheckTarget()
        {
            if (Unit* caster = GetCaster())
                if (caster->ToPlayer())
                    if (Unit* target = caster->ToPlayer()->GetSelectedUnit())
                        if (target->GetEntry() == 23487 && target->IsAlive())
                            return SPELL_CAST_OK;

            return SPELL_FAILED_BAD_TARGETS;
        }

        void HandleDummyEffect(SpellEffIndex /*effIndex*/)
        {
            if (GetCaster() && GetCaster()->ToPlayer())
            {
                GetCaster()->ToPlayer()->AddItem(32906, 1);
                if (Unit* target = GetCaster()->ToPlayer()->GetSelectedUnit())
                    target->ToCreature()->DespawnOrUnsummon(500);
            }
        }

        void Register() override
        {
            OnCheckCast += SpellCheckCastFn(spell_q11117_catch_the_wild_wolpertinger_SpellScript::CheckTarget);
            OnEffectHitTarget += SpellEffectFn(spell_q11117_catch_the_wild_wolpertinger_SpellScript::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_q11117_catch_the_wild_wolpertinger_SpellScript();
    };
};

enum fillKeg
{
    GREEN_EMPTY_KEG             = 37892,
    BLUE_EMPTY_KEG              = 33016,
    YELLOW_EMPTY_KEG            = 32912,
};

class spell_brewfest_fill_keg : public SpellScriptLoader
{
public:
    spell_brewfest_fill_keg() : SpellScriptLoader("spell_brewfest_fill_keg") { }

    class spell_brewfest_fill_keg_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_brewfest_fill_keg_SpellScript);

        void HandleAfterHit()
        {
            if (GetCaster() && GetCaster()->ToPlayer())
            {
                if (Item* itemCaster = GetCastItem())
                {
                    Player* player = GetCaster()->ToPlayer();
                    uint32 item = 0;
                    switch (itemCaster->GetEntry())
                    {
                        case GREEN_EMPTY_KEG:
                        case BLUE_EMPTY_KEG:
                            item = itemCaster->GetEntry() + urand(1, 5); // 5 items, id in range empty+1-5
                            break;
                        case YELLOW_EMPTY_KEG:
                            if (uint8 num = urand(0, 4))
                                item = 32916 + num;
                            else
                                item = 32915;
                            break;
                    }

                    if (item && player->AddItem(item, 1)) // ensure filled keg is stored
                    {
                        player->DestroyItemCount(itemCaster->GetEntry(), 1, true);
                        GetSpell()->m_CastItem = nullptr;
                        GetSpell()->m_castItemGUID.Clear();
                    }
                }
            }
        }

        void Register() override
        {
            AfterHit += SpellHitFn(spell_brewfest_fill_keg_SpellScript::HandleAfterHit);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_brewfest_fill_keg_SpellScript();
    };
};

class spell_brewfest_unfill_keg : public SpellScriptLoader
{
public:
    spell_brewfest_unfill_keg() : SpellScriptLoader("spell_brewfest_unfill_keg") { }

    class spell_brewfest_unfill_keg_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_brewfest_unfill_keg_SpellScript);

        uint32 GetEmptyEntry(uint32 baseEntry)
        {
            switch (baseEntry)
            {
                case 37893:
                case 37894:
                case 37895:
                case 37896:
                case 37897:
                    return GREEN_EMPTY_KEG;
                case 33017:
                case 33018:
                case 33019:
                case 33020:
                case 33021:
                    return BLUE_EMPTY_KEG;
                case 32915:
                case 32917:
                case 32918:
                case 32919:
                case 32920:
                    return YELLOW_EMPTY_KEG;
            }

            return 0;
        }

        void HandleAfterHit()
        {
            if (GetCaster() && GetCaster()->ToPlayer())
            {
                if (Item* itemCaster = GetCastItem())
                {
                    uint32 item = GetEmptyEntry(itemCaster->GetEntry());
                    Player* player = GetCaster()->ToPlayer();

                    if (item && player->AddItem(item, 1)) // ensure filled keg is stored
                    {
                        player->DestroyItemCount(itemCaster->GetEntry(), 1, true);
                        GetSpell()->m_CastItem = nullptr;
                        GetSpell()->m_castItemGUID.Clear();
                    }
                }
            }
        }

        void Register() override
        {
            AfterHit += SpellHitFn(spell_brewfest_unfill_keg_SpellScript::HandleAfterHit);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_brewfest_unfill_keg_SpellScript();
    };
};

class spell_brewfest_toss_mug : public SpellScriptLoader
{
public:
    spell_brewfest_toss_mug() : SpellScriptLoader("spell_brewfest_toss_mug") { }

    class spell_brewfest_toss_mug_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_brewfest_toss_mug_SpellScript);

        SpellCastResult CheckCast()
        {
            if (Unit* caster = GetCaster())
            {
                float z = caster->GetMapHeight(caster->GetPositionX() + 14 * cos(caster->GetOrientation()), caster->GetPositionY() + 14 * sin(caster->GetOrientation()), caster->GetPositionZ());
                WorldLocation pPosition = WorldLocation(caster->GetMapId(), caster->GetPositionX() + 14 * cos(caster->GetOrientation()), caster->GetPositionY() + 14 * sin(caster->GetOrientation()), z, caster->GetOrientation());
                SetExplTargetDest(pPosition);
            }

            return SPELL_CAST_OK;
        }

        void FilterTargets(std::list<WorldObject*>& targets)
        {
            Unit* caster = GetCaster();
            if (!caster)
                return;

            WorldObject* target = nullptr;
            for (std::list<WorldObject*>::iterator itr = targets.begin(); itr != targets.end(); ++itr)
                if (caster->HasInLine((*itr), 2.0f))
                {
                    target = (*itr);
                    break;
                }

            targets.clear();
            if (target)
                targets.push_back(target);

            targets.push_back(caster);
        }

        void HandleBeforeHit(SpellMissInfo missInfo)
        {
            if (missInfo != SPELL_MISS_NONE)
            {
                return;
            }

            if (Unit* target = GetHitUnit())
            {
                if (!GetCaster() || target->GetGUID() == GetCaster()->GetGUID())
                    return;

                WorldLocation pPosition = WorldLocation(target->GetMapId(), target->GetPositionX(), target->GetPositionY(), target->GetPositionZ() + 4.0f, target->GetOrientation());
                SetExplTargetDest(pPosition);
            }
        }

        void HandleScriptEffect(SpellEffIndex /*effIndex*/)
        {
            Unit* caster = GetCaster();
            if (!caster)
                return;

            if (!GetHitUnit() || GetHitUnit()->GetGUID() != caster->GetGUID())
                return;

            std::vector<Creature*> bakers;
            if (caster->GetMapId() == 1) // Kalimdor
            {
                if (Creature* creature = caster->FindNearestCreature(NPC_NORMAL_VOODOO, 40.0f))
                {
                    bakers.push_back(creature);
                }

                if (Creature* creature = caster->FindNearestCreature(NPC_NORMAL_DROHN, 40.0f))
                {
                    bakers.push_back(creature);
                }

                if (Creature* creature = caster->FindNearestCreature(NPC_NORMAL_GORDOK, 40.0f))
                {
                    bakers.push_back(creature);
                }
            }
            else // EK
            {
                if (Creature* creature = caster->FindNearestCreature(NPC_NORMAL_THUNDERBREW, 40.0f))
                {
                    bakers.push_back(creature);
                }

                if (Creature* creature = caster->FindNearestCreature(NPC_NORMAL_BARLEYBREW, 40.0f))
                {
                    bakers.push_back(creature);
                }

                if (Creature* creature = caster->FindNearestCreature(NPC_NORMAL_GORDOK, 40.0f))
                {
                    bakers.push_back(creature);
                }
            }

            if (!bakers.empty())
            {
                std::sort(bakers.begin(), bakers.end(), Acore::ObjectDistanceOrderPred(caster));
                if (Creature* creature = *bakers.begin())
                {
                    creature->CastSpell(caster, SPELL_THROW_MUG_TO_PLAYER, true);
                }
            }
        }

        void Register() override
        {
            OnCheckCast += SpellCheckCastFn(spell_brewfest_toss_mug_SpellScript::CheckCast);
            OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_brewfest_toss_mug_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
            BeforeHit += BeforeSpellHitFn(spell_brewfest_toss_mug_SpellScript::HandleBeforeHit);
            OnEffectHitTarget += SpellEffectFn(spell_brewfest_toss_mug_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_brewfest_toss_mug_SpellScript();
    };
};

class spell_brewfest_add_mug : public SpellScriptLoader
{
public:
    spell_brewfest_add_mug() : SpellScriptLoader("spell_brewfest_add_mug") { }

    class spell_brewfest_add_mug_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_brewfest_add_mug_SpellScript);

        void HandleDummyEffect(SpellEffIndex /*effIndex*/)
        {
            if (Unit* target = GetHitUnit())
                target->CastSpell(target, SPELL_ADD_MUG, true);
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_brewfest_add_mug_SpellScript::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_brewfest_add_mug_SpellScript();
    };
};

//
enum brewBubble
{
    SPELL_BUBBLE_BUILD_UP           = 49828,
};

class npc_brew_bubble : public CreatureScript
{
public:
    npc_brew_bubble() : CreatureScript("npc_brew_bubble") { }

    struct npc_brew_bubbleAI : public NullCreatureAI
    {
        npc_brew_bubbleAI(Creature* creature) : NullCreatureAI(creature)
        {
        }

        uint32 timer;

        void Reset() override
        {
            me->SetReactState(REACT_AGGRESSIVE);
            me->GetMotionMaster()->MoveRandom(15.0f);
            timer = 0;
        }

        void DoAction(int32) override
        {
            timer = 0;
        }

        void MoveInLineOfSight(Unit* target) override
        {
            if (target->GetEntry() == me->GetEntry())
                if (me->IsWithinDist(target, 1.0f))
                {
                    uint8 stacksMe = me->GetAuraCount(SPELL_BUBBLE_BUILD_UP);
                    uint8 stacksTarget = target->GetAuraCount(SPELL_BUBBLE_BUILD_UP);
                    if (stacksMe >= stacksTarget)
                    {
                        if (Aura* aura = me->GetAura(SPELL_BUBBLE_BUILD_UP))
                            aura->ModStackAmount(stacksTarget + 1);
                        else
                            me->AddAura(SPELL_BUBBLE_BUILD_UP, me);

                        target->ToCreature()->DespawnOrUnsummon();
                        DoAction(0);
                    }
                    else if (Aura* aura = target->GetAura(SPELL_BUBBLE_BUILD_UP))
                    {
                        aura->ModStackAmount(stacksMe);

                        target->ToCreature()->AI()->DoAction(0);
                        me->DespawnOrUnsummon();
                    }
                }
        }

        void UpdateAI(uint32 diff) override
        {
            timer += diff;
            if (timer >= 25000)
            {
                timer = 0;
                me->DespawnOrUnsummon();
            }
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_brew_bubbleAI(creature);
    }
};

enum BrewfestRevelerEnum
{
    FACTION_ALLIANCE    = 1934,
    FACTION_HORDE       = 1935,
    FACTION_FRIENDLY    = 35,

    SPELL_BREWFEST_REVELER_TRANSFORM_GOBLIN_MALE    = 44003,
    SPELL_BREWFEST_REVELER_TRANSFORM_GOBLIN_FEMALE  = 44004,
    SPELL_BREWFEST_REVELER_TRANSFORM_BE             = 43907,
    SPELL_BREWFEST_REVELER_TRANSFORM_ORC            = 43914,
    SPELL_BREWFEST_REVELER_TRANSFORM_TAUREN         = 43915,
    SPELL_BREWFEST_REVELER_TRANSFORM_TROLL          = 43916,
    SPELL_BREWFEST_REVELER_TRANSFORM_UNDEAD         = 43917
};

class spell_brewfest_reveler_transform : public SpellScriptLoader
{
public:
    spell_brewfest_reveler_transform() : SpellScriptLoader("spell_brewfest_reveler_transform") {}

    class spell_brewfest_reveler_transform_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_brewfest_reveler_transform_AuraScript);

        void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            uint32 factionId = FACTION_ALLIANCE;
            switch (m_scriptSpellId)
            {
                case SPELL_BREWFEST_REVELER_TRANSFORM_BE:
                case SPELL_BREWFEST_REVELER_TRANSFORM_ORC:
                case SPELL_BREWFEST_REVELER_TRANSFORM_TAUREN:
                case SPELL_BREWFEST_REVELER_TRANSFORM_TROLL:
                case SPELL_BREWFEST_REVELER_TRANSFORM_UNDEAD:
                    factionId = FACTION_HORDE;
                    break;
                case SPELL_BREWFEST_REVELER_TRANSFORM_GOBLIN_MALE:
                case SPELL_BREWFEST_REVELER_TRANSFORM_GOBLIN_FEMALE:
                    factionId = FACTION_FRIENDLY;
                    break;
                default:
                    break;
            }

            GetTarget()->setFaction(factionId);
        }

        void Register() override
        {
            AfterEffectApply += AuraEffectApplyFn(spell_brewfest_reveler_transform_AuraScript::OnApply, EFFECT_0, SPELL_AURA_TRANSFORM, AURA_EFFECT_HANDLE_REAL);
        }
    };

    AuraScript* GetAuraScript() const override
    {
        return new spell_brewfest_reveler_transform_AuraScript();
    }
};

class spell_brewfest_relay_race_force_cast : public SpellScriptLoader
{
public:
    spell_brewfest_relay_race_force_cast() : SpellScriptLoader("spell_brewfest_relay_race_force_cast") {}

    class spell_brewfest_relay_race_force_cast_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_brewfest_relay_race_force_cast_SpellScript);

        SpellCastResult CheckItem()
        {
            if (Unit* target = GetExplTargetUnit())
            {
                if (SpellInfo const* triggeredSpellInfo = sSpellMgr->GetSpellInfo(GetSpellInfo()->Effects[EFFECT_0].TriggerSpell))
                {
                    if (Player* player = target->ToPlayer())
                    {
                        if (player->HasItemCount(triggeredSpellInfo->Reagent[0]))
                        {
                            return SPELL_CAST_OK;
                        }
                    }
                }
            }

            return SPELL_FAILED_DONT_REPORT;
        }

        void Register() override
        {
            OnCheckCast += SpellCheckCastFn(spell_brewfest_relay_race_force_cast_SpellScript::CheckItem);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_brewfest_relay_race_force_cast_SpellScript();
    }
};

enum DirebrewSays
{
    SAY_INTRO                   = 0,
    SAY_INTRO1                  = 1,
    SAY_INTRO2                  = 2,
    SAY_INSULT                  = 3,
    SAY_ANTAGONIST_1            = 0,
    SAY_ANTAGONIST_2            = 1,
    SAY_ANTAGONIST_COMBAT       = 2
};

enum DirebrewActions
{
    ACTION_START_FIGHT          = -1,
    ACTION_ANTAGONIST_SAY_1     = -2,
    ACTION_ANTAGONIST_SAY_2     = -3,
    ACTION_ANTAGONIST_HOSTILE   = -4
};

enum DirebrewNpcs
{
    NPC_ILSA_DIREBREW           = 26764,
    NPC_URSULA_DIREBREW         = 26822,
    NPC_ANTAGONIST              = 23795
};

enum DirebrewSpells
{
    SPELL_MOLE_MACHINE_EMERGE           = 50313,
    SPELL_DIREBREW_DISARM_PRE_CAST      = 47407,
    SPELL_MOLE_MACHINE_TARGET_PICKER    = 47691,
    SPELL_MOLE_MACHINE_MINION_SUMMONER  = 47690,
    SPELL_DIREBREW_DISARM_GROW          = 47409,
    SPELL_DIREBREW_DISARM               = 47310,
    SPELL_CHUCK_MUG                     = 50276,
    SPELL_PORT_TO_COREN                 = 52850,
    SPELL_SEND_MUG_CONTROL_AURA         = 47369,
    SPELL_SEND_MUG_TARGET_PICKER        = 47370,
    SPELL_SEND_FIRST_MUG                = 47333,
    SPELL_SEND_SECOND_MUG               = 47339,
    SPELL_REQUEST_SECOND_MUG            = 47344,
    SPELL_HAS_DARK_BREWMAIDENS_BREW     = 47331,
    SPELL_BARRELED_CONTROL_AURA         = 50278,
    SPELL_BARRELED                      = 47442
};

enum DirebrewPhases
{
    PHASE_ALL = 1,
    PHASE_INTRO,
    PHASE_ONE,
    PHASE_TWO,
    PHASE_THREE
};

enum DirebrewEvents
{
    EVENT_INTRO_1 = 1,
    EVENT_INTRO_2,
    EVENT_INTRO_3,
    EVENT_DIREBREW_DISARM,
    EVENT_SUMMON_MOLE_MACHINE,
    EVENT_RESPAWN_ILSA,
    EVENT_RESPAWN_URSULA
};

enum DirebrewMisc
{
    GOSSIP_ID                           = 11388,
    GO_MOLE_MACHINE_TRAP                = 188509,
    GOSSIP_OPTION_FIGHT                 = 0,
    GOSSIP_OPTION_APOLOGIZE             = 1,
    DATA_TARGET_GUID                    = 1,
    MAX_ANTAGONISTS                     = 3,
    FACTION_GOBLIN_DARK_IRON_BAR_PATRON = 736,
    DATA_COREN                          = 33,
    GO_MACHINE_SUMMONER                 = 188508
};

Position const AntagonistPos[3] =
{
    { 895.3782f, -132.1722f, -49.66423f, 2.6529f   },
    { 893.9837f, -133.2879f, -49.66541f, 2.583087f },
    { 896.2667f, -130.483f,  -49.66249f, 2.600541f }
};

class npc_coren_direbrew : public CreatureScript
{
public:
    npc_coren_direbrew() : CreatureScript("npc_coren_direbrew") {}

    struct npc_coren_direbrewAI : public ScriptedAI
    {
        npc_coren_direbrewAI(Creature* creature) : ScriptedAI(creature), _summons(me) {}

        void sGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override
        {
            if (menuId != GOSSIP_ID)
            {
                return;
            }

            if (gossipListId == GOSSIP_OPTION_FIGHT)
            {
                Talk(SAY_INSULT, player);
                DoAction(ACTION_START_FIGHT);
            }
            else if (gossipListId == GOSSIP_OPTION_APOLOGIZE)
            {
                CloseGossipMenuFor(player);
            }
        }

        bool CanBeSeen(Player const* player) override
        {
            if (player->IsGameMaster())
            {
                return true;
            }

            Group const* group = player->GetGroup();
            return group && sLFGMgr->GetDungeon(group->GetGUID()) == lfg::LFG_DUNGEON_COREN_DIREBREW;
        }

        void Reset() override
        {
            _events.Reset();
            _summons.DespawnAll();
            me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
            me->setFaction(FACTION_FRIENDLY);
            _events.SetPhase(PHASE_ALL);

            for (uint8 i = 0; i < MAX_ANTAGONISTS; ++i)
            {
                me->SummonCreature(NPC_ANTAGONIST, AntagonistPos[i], TEMPSUMMON_DEAD_DESPAWN);
            }

            std::list<GameObject*> machines;
            GetGameObjectListWithEntryInGrid(machines, me, GO_MACHINE_SUMMONER, 60.f);
            for (GameObject* go : machines)
            {
                go->Delete();
            }
        }

        void MoveInLineOfSight(Unit* who) override
        {
            if (!_events.IsInPhase(PHASE_ALL) || who->GetTypeId() != TYPEID_PLAYER)
            {
                return;
            }

            _events.SetPhase(PHASE_INTRO);
            _events.ScheduleEvent(EVENT_INTRO_1, 6 * IN_MILLISECONDS, 0, PHASE_INTRO);
            Talk(SAY_INTRO);
        }

        void JustSummoned(Creature* creature) override
        {
            _summons.Summon(creature);
        }

        void DoAction(int32 action) override
        {
            if (action == ACTION_START_FIGHT)
            {
                _events.SetPhase(PHASE_ONE);
                me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                me->setFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON);
                DoZoneInCombat();

                EntryCheckPredicate pred(NPC_ANTAGONIST);
                _summons.DoAction(ACTION_ANTAGONIST_HOSTILE, pred);

                _events.ScheduleEvent(EVENT_SUMMON_MOLE_MACHINE, 15 * IN_MILLISECONDS);
                _events.ScheduleEvent(EVENT_DIREBREW_DISARM, 20 * IN_MILLISECONDS);
            }
        }

        void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override
        {
            if (me->HealthBelowPctDamaged(66, damage) && _events.IsInPhase(PHASE_ONE))
            {
                _events.SetPhase(PHASE_TWO);
                SummonSister(NPC_ILSA_DIREBREW);
            }
            else if (me->HealthBelowPctDamaged(33, damage) && _events.IsInPhase(PHASE_TWO))
            {
                _events.SetPhase(PHASE_THREE);
                SummonSister(NPC_URSULA_DIREBREW);
            }
        }

        void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override
        {
            if (summon->GetEntry() == NPC_ILSA_DIREBREW)
            {
                _events.ScheduleEvent(EVENT_RESPAWN_ILSA, 1 * IN_MILLISECONDS);
            }
            else if (summon->GetEntry() == NPC_URSULA_DIREBREW)
            {
                _events.ScheduleEvent(EVENT_RESPAWN_URSULA, 1 * IN_MILLISECONDS);
            }
        }

        void JustDied(Unit* /*killer*/) override
        {
            _events.Reset();
            _summons.DespawnAll();

            Map::PlayerList const& players = me->GetMap()->GetPlayers();
            if (!players.isEmpty())
            {
                if (Group* group = players.begin()->GetSource()->GetGroup())
                {
                    if (group->isLFGGroup())
                    {
                        sLFGMgr->FinishDungeon(group->GetGUID(), 287, me->GetMap());
                    }
                }
            }
        }

        void SummonSister(uint32 entry)
        {
            if (Creature* sister = me->SummonCreature(entry, me->GetPosition(), TEMPSUMMON_DEAD_DESPAWN))
            {
                DoZoneInCombat(sister);
            }
        }

        void UpdateAI(uint32 diff) override
        {
            if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO))
            {
                return;
            }

            _events.Update(diff);

            if (me->HasUnitState(UNIT_STATE_CASTING))
            {
                return;
            }

            while (uint32 eventId = _events.ExecuteEvent())
            {
                switch (eventId)
                {
                    case EVENT_INTRO_1:
                        Talk(SAY_INTRO1);
                        _events.ScheduleEvent(EVENT_INTRO_2, 4 * IN_MILLISECONDS, 0, PHASE_INTRO);
                        break;
                    case EVENT_INTRO_2:
                    {
                        EntryCheckPredicate pred(NPC_ANTAGONIST);
                        _summons.DoAction(ACTION_ANTAGONIST_SAY_1, pred);
                        _events.ScheduleEvent(EVENT_INTRO_3, 3 * IN_MILLISECONDS, 0, PHASE_INTRO);
                        break;
                    }
                    case EVENT_INTRO_3:
                    {
                        Talk(SAY_INTRO2);
                        EntryCheckPredicate pred(NPC_ANTAGONIST);
                        _summons.DoAction(ACTION_ANTAGONIST_SAY_2, pred);
                        break;
                    }
                    case EVENT_RESPAWN_ILSA:
                        SummonSister(NPC_ILSA_DIREBREW);
                        break;
                    case EVENT_RESPAWN_URSULA:
                        SummonSister(NPC_URSULA_DIREBREW);
                        break;
                    case EVENT_SUMMON_MOLE_MACHINE:
                    {
                        me->CastCustomSpell(SPELL_MOLE_MACHINE_TARGET_PICKER, SPELLVALUE_MAX_TARGETS, 1, nullptr, true);
                        _events.RepeatEvent(15 * IN_MILLISECONDS);
                        break;
                    }
                    case EVENT_DIREBREW_DISARM:
                        DoCastSelf(SPELL_DIREBREW_DISARM_PRE_CAST, true);
                        _events.RepeatEvent(20 * IN_MILLISECONDS);
                        break;
                    default:
                        break;
                }

                if (me->HasUnitState(UNIT_STATE_CASTING))
                {
                    return;
                }
            }

            DoMeleeAttackIfReady();
        }

    private:
        EventMap _events;
        SummonList _summons;
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_coren_direbrewAI(creature);
    }
};

class npc_coren_direbrew_sisters : public CreatureScript
{
public:
    npc_coren_direbrew_sisters() : CreatureScript("npc_coren_direbrew_sisters") {}

    struct npc_coren_direbrew_sistersAI : public ScriptedAI
    {
        npc_coren_direbrew_sistersAI(Creature* creature) : ScriptedAI(creature) {}

        void SetGUID(ObjectGuid guid, int32 id) override
        {
            if (id == DATA_TARGET_GUID)
            {
                _targetGUID = guid;
            }
        }

        ObjectGuid GetGUID(int32 data) const override
        {
            if (data == DATA_TARGET_GUID)
            {
                return _targetGUID;
            }

            return ObjectGuid::Empty;
        }

        void EnterCombat(Unit* /*who*/) override
        {
            DoCastSelf(SPELL_PORT_TO_COREN);

            if (me->GetEntry() == NPC_URSULA_DIREBREW)
            {
                DoCastSelf(SPELL_BARRELED_CONTROL_AURA);
            }
            else
            {
                DoCastSelf(SPELL_SEND_MUG_CONTROL_AURA);
            }

            _scheduler.SetValidator([this]
            {
                return !me->HasUnitState(UNIT_STATE_CASTING);
            })
            .Schedule(Seconds(2), [this](TaskContext mugChuck)
            {
                if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 0.0f, false, -SPELL_HAS_DARK_BREWMAIDENS_BREW))
                {
                    DoCast(target, SPELL_CHUCK_MUG);
                }

                mugChuck.Repeat(Seconds(4));
            });
        }

        void UpdateAI(uint32 diff) override
        {
            _scheduler.Update(diff, [this]
            {
                DoMeleeAttackIfReady();
            });
        }

    private:
        ObjectGuid _targetGUID;
        TaskScheduler _scheduler;
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_coren_direbrew_sistersAI(creature);
    }
};

class npc_direbrew_minion : public CreatureScript
{
public:
    npc_direbrew_minion() : CreatureScript("npc_direbrew_minion") {}

    struct npc_direbrew_minionAI : public ScriptedAI
    {
        npc_direbrew_minionAI(Creature* creature) : ScriptedAI(creature), _instance(creature->GetInstanceScript()) {}

        void Reset() override
        {
            me->setFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON);
            DoZoneInCombat();
        }

        void IsSummonedBy(Unit* /*summoner*/) override
        {
            if (Creature* coren = ObjectAccessor::GetCreature(*me, _instance->GetGuidData(DATA_COREN)))
            {
                coren->AI()->JustSummoned(me);
            }
        }

    private:
        InstanceScript* _instance;
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_direbrew_minionAI(creature);
    }
};

class npc_direbrew_antagonist : public CreatureScript
{
public:
    npc_direbrew_antagonist() : CreatureScript("npc_direbrew_antagonist") {}

    struct npc_direbrew_antagonistAI : public ScriptedAI
    {
        npc_direbrew_antagonistAI(Creature* creature) : ScriptedAI(creature) {}

        void DoAction(int32 action) override
        {
            switch (action)
            {
                case ACTION_ANTAGONIST_SAY_1:
                    Talk(SAY_ANTAGONIST_1);
                    break;
                case ACTION_ANTAGONIST_SAY_2:
                    Talk(SAY_ANTAGONIST_2);
                    break;
                case ACTION_ANTAGONIST_HOSTILE:
                    me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
                    me->setFaction(FACTION_GOBLIN_DARK_IRON_BAR_PATRON);
                    DoZoneInCombat();
                    break;
                default:
                    break;
            }
        }

        void EnterCombat(Unit* who) override
        {
            Talk(SAY_ANTAGONIST_COMBAT, who);
            ScriptedAI::EnterCombat(who);
        }
    };

    CreatureAI* GetAI(Creature* creature) const override
    {
        return new npc_direbrew_antagonistAI(creature);
    }
};

class go_direbrew_mole_machine : public GameObjectScript
{
public:
    go_direbrew_mole_machine() : GameObjectScript("go_direbrew_mole_machine") { }

    struct go_direbrew_mole_machineAI : public GameObjectAI
    {
        go_direbrew_mole_machineAI(GameObject* go) : GameObjectAI(go) { }

        void Reset() override
        {
            go->SetLootState(GO_READY);

            _scheduler.Schedule(Seconds(1), [this](TaskContext /*context*/)
            {
                go->UseDoorOrButton();
                go->CastSpell(nullptr, SPELL_MOLE_MACHINE_EMERGE);
            })
            .Schedule(Seconds(4), [this](TaskContext /*context*/)
            {
                if (GameObject* trap = go->GetLinkedTrap())
                {
                    trap->UseDoorOrButton();
                    trap->SetLootState(GO_READY);
                }
            });
        }

        void UpdateAI(uint32 diff) override
        {
            _scheduler.Update(diff);
        }

    private:
        TaskScheduler _scheduler;
    };

    GameObjectAI* GetAI(GameObject* go) const override
    {
        return new go_direbrew_mole_machineAI(go);
    }
};

// 47691 - Summon Mole Machine Target Picker
class spell_direbrew_summon_mole_machine_target_picker : public SpellScriptLoader
{
public:
    spell_direbrew_summon_mole_machine_target_picker() : SpellScriptLoader("spell_direbrew_summon_mole_machine_target_picker") {}

    class spell_direbrew_summon_mole_machine_target_picker_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_direbrew_summon_mole_machine_target_picker_SpellScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_MOLE_MACHINE_MINION_SUMMONER });
        }

        void HandleScriptEffect(SpellEffIndex /*effIndex*/)
        {
            GetCaster()->CastSpell(GetHitUnit(), SPELL_MOLE_MACHINE_MINION_SUMMONER, true);
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_direbrew_summon_mole_machine_target_picker_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_direbrew_summon_mole_machine_target_picker_SpellScript();
    };
};

// 47370 - Send Mug Target Picker
class spell_send_mug_target_picker : public SpellScriptLoader
{
public:
    spell_send_mug_target_picker() : SpellScriptLoader("spell_send_mug_target_picker") {}

    class spell_send_mug_target_picker_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_send_mug_target_picker_SpellScript);

        void FilterTargets(std::list<WorldObject*>& targets)
        {
            Unit* caster = GetCaster();

            targets.remove_if(Acore::UnitAuraCheck(true, SPELL_HAS_DARK_BREWMAIDENS_BREW));

            if (targets.size() > 1)
            {
                targets.remove_if([caster](WorldObject* obj)
                {
                    if (obj->GetGUID() == caster->GetAI()->GetGUID(DATA_TARGET_GUID))
                    {
                        return true;
                    }

                    return false;
                });
            }

            if (targets.empty())
            {
                return;
            }

            WorldObject* target = Acore::Containers::SelectRandomContainerElement(targets);
            targets.clear();
            targets.push_back(target);
        }

        void HandleDummy(SpellEffIndex /*effIndex*/)
        {
            Unit* caster = GetCaster();
            caster->GetAI()->SetGUID(GetHitUnit()->GetGUID(), DATA_TARGET_GUID);
            caster->CastSpell(GetHitUnit(), SPELL_SEND_FIRST_MUG, true);
        }

        void Register() override
        {
            OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_send_mug_target_picker_SpellScript::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENTRY);
            OnEffectHitTarget += SpellEffectFn(spell_send_mug_target_picker_SpellScript::HandleDummy, EFFECT_0, SPELL_EFFECT_DUMMY);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_send_mug_target_picker_SpellScript();
    };
};

// 47344 - Request Second Mug
class spell_request_second_mug : public SpellScriptLoader
{
public:
    spell_request_second_mug() : SpellScriptLoader("spell_request_second_mug") {}

    class spell_request_second_mug_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_request_second_mug_SpellScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_SEND_SECOND_MUG });
        }

        void HandleScriptEffect(SpellEffIndex /*effIndex*/)
        {
            GetHitUnit()->CastSpell(GetCaster(), SPELL_SEND_SECOND_MUG, true);
        }

        void Register() override
        {
            OnEffectHitTarget += SpellEffectFn(spell_request_second_mug_SpellScript::HandleScriptEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT);
        }
    };

    SpellScript* GetSpellScript() const override
    {
        return new spell_request_second_mug_SpellScript();
    };
};

// 47369 - Send Mug Control Aura
class spell_send_mug_control_aura : public SpellScriptLoader
{
public:
    spell_send_mug_control_aura() : SpellScriptLoader("spell_send_mug_control_aura") {}

    class spell_send_mug_control_aura_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_send_mug_control_aura_AuraScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_SEND_MUG_TARGET_PICKER });
        }

        void PeriodicTick(AuraEffect const* /*aurEff*/)
        {
            GetTarget()->CastSpell(GetTarget(), SPELL_SEND_MUG_TARGET_PICKER, true);
        }

        void Register() override
        {
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_send_mug_control_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_DUMMY);
        }
    };

    AuraScript* GetAuraScript() const override
    {
        return new spell_send_mug_control_aura_AuraScript();
    };
};

// 50278 - Barreled Control Aura
class spell_barreled_control_aura : public SpellScriptLoader
{
public:
    spell_barreled_control_aura() : SpellScriptLoader("spell_barreled_control_aura") {}

    class spell_barreled_control_aura_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_barreled_control_aura_AuraScript);

        void PeriodicTick(AuraEffect const* /*aurEff*/)
        {
            PreventDefaultAction();
            GetTarget()->CastSpell(nullptr, SPELL_BARRELED);
        }

        void Register() override
        {
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_barreled_control_aura_AuraScript::PeriodicTick, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL);
        }
    };

    AuraScript* GetAuraScript() const override
    {
        return new spell_barreled_control_aura_AuraScript();
    };
};

// 47407 - Direbrew's Disarm (precast)
class spell_direbrew_disarm : public SpellScriptLoader
{
public:
    spell_direbrew_disarm() : SpellScriptLoader("spell_direbrew_disarm") {}

    class spell_direbrew_disarm_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_direbrew_disarm_AuraScript);

        bool Validate(SpellInfo const* /*spellInfo*/) override
        {
            return ValidateSpellInfo({ SPELL_DIREBREW_DISARM, SPELL_DIREBREW_DISARM_GROW });
        }

        void PeriodicTick(AuraEffect const* /*aurEff*/)
        {
            if (Aura* aura = GetTarget()->GetAura(SPELL_DIREBREW_DISARM_GROW))
            {
                aura->SetStackAmount(aura->GetStackAmount() + 1);
                aura->SetDuration(aura->GetDuration() - 1500);
            }
        }

        void OnApply(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            GetTarget()->CastSpell(GetTarget(), SPELL_DIREBREW_DISARM_GROW, true);
            GetTarget()->CastSpell(GetTarget(), SPELL_DIREBREW_DISARM);
        }

        void Register() override
        {
            OnEffectPeriodic += AuraEffectPeriodicFn(spell_direbrew_disarm_AuraScript::PeriodicTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
            OnEffectApply += AuraEffectRemoveFn(spell_direbrew_disarm_AuraScript::OnApply, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY, AURA_EFFECT_HANDLE_REAL);
        }
    };

    AuraScript* GetAuraScript() const override
    {
        return new spell_direbrew_disarm_AuraScript();
    };
};

void AddSC_event_brewfest_scripts()
{
    // Npcs
    new npc_brewfest_keg_thrower();
    new npc_brewfest_keg_reciver();
    new npc_brewfest_bark_trigger();
    new npc_dark_iron_attack_generator();
    new npc_dark_iron_attack_mole_machine();
    new npc_dark_iron_guzzler();
    new npc_brewfest_super_brew_trigger();

    // Spells
    // ram
    new spell_brewfest_main_ram_buff();
    new spell_brewfest_ram_fatigue();
    new spell_brewfest_apple_trap();
    // other
    new spell_q11117_catch_the_wild_wolpertinger();
    new spell_brewfest_fill_keg();
    new spell_brewfest_unfill_keg();
    new spell_brewfest_toss_mug();
    new spell_brewfest_add_mug();
    new spell_brewfest_reveler_transform();
    new spell_brewfest_relay_race_force_cast();

    // beer effect
    new npc_brew_bubble();

    // Coren Direbrew
    new npc_coren_direbrew();
    new npc_coren_direbrew_sisters();
    new npc_direbrew_minion();
    new npc_direbrew_antagonist();
    new go_direbrew_mole_machine();
    new spell_direbrew_summon_mole_machine_target_picker();
    new spell_send_mug_target_picker();
    new spell_request_second_mug();
    new spell_send_mug_control_aura();
    new spell_barreled_control_aura();
    new spell_direbrew_disarm();
}
