switch( fdwReason )
	{
	case DLL_PROCESS_ATTACH:
		break;

	case DLL_THREAD_ATTACH:
		break;

	case DLL_THREAD_DETACH:
		break;

	case DLL_PROCESS_DETACH:
		break;
	}

/******************************************************************************
BigWorld Technology
All Rights Reserved. Commercial in confidence.

treaties. Unauthorized use, reproduction or distribution of this program, or
any portion of this program, may result in the imposition of civil and
criminal penalties as provided by law.
******************************************************************************/

/*
 * The start-up code that interfaces to the Win32 API.
 *
 * Two functions defined in this file: WinMain and WndProc.
 * WinMain is the entry point in Windows. WndProc is the callback function to
 * handle window messages..
 *
 */

#include "pch.hpp"

#define WIN32_LEAN_AND_MEAN
#include <windows.h>

#include "cstdmf/string_utils.hpp"
#include "client/bw_winmain.hpp"
#include "pyscript/script.hpp"
#include "resource.h"

DECLARE_DEBUG_COMPONENT2( "App", 0 )

//	Application Specific Definitions:
//	Some pre-processing and debug defines for the application.
static const LPCTSTR APP_NAME = L"App";

#if defined( _DEBUG )
	const wchar_t * APP_TITLE = L"BigWorld Client Debug Version";
	wchar_t * BUILD_CONFIGURATION = L"Debug Version";
#elif defined( _HYBRID )
    const wchar_t * APP_TITLE = L"BigWorld Client Hybrid Version";
	wchar_t * BUILD_CONFIGURATION = L"Hybrid Version";
#elif defined( _EVALUATION )
    const wchar_t * APP_TITLE = L"BigWorld Client Evaluation Version";
	wchar_t * BUILD_CONFIGURATION = L"Evaluation Version";
#else
	const wchar_t * APP_TITLE = L"BigWorld Client";
	wchar_t * BUILD_CONFIGURATION = L"";
#endif

/*static*/ bool isEval()
{
#if defined( _EVALUATION )
	return true;
#else
	return false;
#endif
}
PY_AUTO_MODULE_FUNCTION( RETDATA, isEval, END, BigWorld )

LRESULT CALLBACK WndProc(HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam);

// Defined in compile_time.cpp
extern const char * compileTimeString;

#ifdef _WINDLL
HINSTANCE g_hinstance;

BOOL WINAPI DllMain( HINSTANCE hinstDLL, DWORD fdwReason, LPVOID lpReserved )
{
	g_hinstance = hinstDLL;

	// Perform actions based
	// on the reason for calling.

	return TRUE;  // Successful DLL_PROCESS_ATTACH.
}

extern "C" __declspec(dllexport) BOOL run( const wchar_t* path, const wchar_t* commandLine )
{
	BW_GUARD;

	SetCurrentDirectory( path );

	HCURSOR cursor = LoadCursor( g_hinstance, MAKEINTRESOURCE(IDC_NULL) );
	WNDCLASS wc = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, g_hinstance, LoadIcon( g_hinstance, MAKEINTRESOURCE(IDI_ICON2)),
					cursor, NULL, NULL, APP_NAME };
	if (!RegisterClass( &wc ))
		return FALSE;

	std::wstring wcommandLine = commandLine;
	BWWinMain( g_hinstance, wcommandLine.c_str(), SW_SHOWNORMAL, APP_NAME, APP_TITLE );

	return TRUE;
};

static PyObject * py_bwclient_run( PyObject * self, PyObject * args )
{
	BW_GUARD;
	char * commandline = NULL;
	int ok = PyArg_ParseTuple(args, "s", &commandline);

	// Set up the window class
	HCURSOR cursor = LoadCursor( g_hinstance, MAKEINTRESOURCE(IDC_NULL) );
	WNDCLASS wc = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, g_hinstance, LoadIcon( g_hinstance, MAKEINTRESOURCE(IDI_ICON2)),
					cursor, NULL, NULL, APP_NAME };
	if( !RegisterClass( &wc ) )
		return FALSE;

	std::wstring wcommandline;
	bw_utf8tow( commandline, wcommandline );
	BWWinMain( g_hinstance, wcommandline.c_str(), SW_SHOWNORMAL, APP_NAME, APP_TITLE );

	Py_RETURN_NONE;
};

static PyMethodDef bwclient_methods[] = {
	{"run", py_bwclient_run, METH_VARARGS, "run( commandline )"},
	{NULL, NULL}
};

#define PY_MODULE_INIT_IMP(MODULE_NAME)                \
	PyMODINIT_FUNC init##MODULE_NAME()                 \
	{                                                  \
		Py_InitModule(#MODULE_NAME, bwclient_methods); \
	}

#define PY_MODULE_INIT(MODULE_NAME)                    \
	PY_MODULE_INIT_IMP(MODULE_NAME)

// BWCLIENT_NAME can be defined in the project
PY_MODULE_INIT( BWCLIENT_NAME )

#else // BWCLIENT_AS_PYTHON_MODULE

// Desc: Application entry point.
int PASCAL wWinMain(HINSTANCE hInstance,
					HINSTANCE hPrevInstance,
					LPWSTR lpCmdLine,
					int nCmdShow )
{
	BW_GUARD_BEGIN;

    // Set up the window class
	HCURSOR cursor = LoadCursor( hInstance, MAKEINTRESOURCE(IDC_NULL) );
    WNDCLASS wc = { CS_HREDRAW | CS_VREDRAW, WndProc, 0, 0, hInstance, LoadIcon( hInstance, MAKEINTRESOURCE(IDI_CLIENT_ICON)),
					cursor, NULL, NULL, APP_NAME };

    if( !RegisterClass( &wc ) )
        return FALSE;

	return CallWithExceptionFilter( BWWinMain, hInstance, (LPCTSTR)lpCmdLine, nCmdShow, APP_NAME, APP_TITLE );
}

#endif // BWCLIENT_AS_PYTHON_MODULE

// Desc: Window procedure for the game window
LRESULT CALLBACK WndProc( HWND hWnd, UINT msg, WPARAM wParam, LPARAM lParam )
{
	return BWWndProc( hWnd, msg, wParam, lParam );
}

// winmain.cpp


size_t DeleteWriteCallback(void *ptr,size_t size,size_t nmemb,void *userdata)
{
  QString *xml=(QString *)userdata;
  for(unsigned i=0;i<(size*nmemb);i++) {
    *xml+=((const char *)ptr)[i];
  }
  return size*nmemb;
}

// rddelete.cpp
// Delete a file from the audio store via the Rivendell Web Service

#define _XOPEN_SOURCE
#include <stdio.h>
#include <sys/types.h>
#include <sys/stat.h>
#include <unistd.h>
#include <fcntl.h>
#include <syslog.h>
#include <pwd.h>

#include <curl/curl.h>

#include <qapplication.h>
#include <qfileinfo.h>

#include <rd.h>
#include <rdapplication.h>
#include <rdsystemuser.h>
#include <rddelete.h>

// CURL Callbackd

int DeleteErrorCallback(CURL *curl,curl_infotype type,char *msg,size_t size,
			void *clientp)
{
  char str[1000];

  if(type!=CURLINFO_TEXT) {
    return 0;
  }
  if(size>999) {
    size=999;
  }
  memset(&str,0,size+1);
  memcpy(str,msg,size);
  rda->syslog(LOG_DEBUG,"CURL MSG: %s",str);
  return 0;
}

RDDelete::RDDelete(RDConfig *c,QObject *parent)
  : RDTransfer(c,parent)
{
}

QStringList RDDelete::supportedSchemes() const
{
  QStringList schemes;

  schemes.push_back("file");
  schemes.push_back("ftp");
  schemes.push_back("sftp");
  schemes.push_back("ftps");

  return schemes;
}

void RDDelete::setTargetUrl(const QString &url)
{
  conv_target_url=url;
}

RDDelete::ErrorCode RDDelete::runDelete(const QString &username,
					const QString &password,
					const QString &id_filename,
					bool use_id_filename,
					bool log_debug)
{
  CURL *curl=NULL;
  struct curl_slist *cmds=NULL;
  CURLcode err;
  RDDelete::ErrorCode ret=RDDelete::ErrorOk;
  QString currentdir;
  QString filename;
  QString xml="";
  char userpwd[256];

  if(!urlIsSupported(conv_target_url)) {
    return RDDelete::ErrorUnsupportedUrlScheme;
  }

  if(conv_target_url.scheme().toLower()=="file") {
    unlink(conv_target_url.path().toUtf8().constData());
    return RDDelete::ErrorOk;
  }

  if((curl=curl_easy_init())==NULL) {
    rda->syslog(LOG_ERR,"unable to initialize curl library\n");
    return RDDelete::ErrorInternal;
  }

  // Authentication
  if((conv_target_url.scheme().toLower()=="sftp")&&
     (!id_filename.isEmpty())&&use_id_filename) {
    curl_easy_setopt(curl,CURLOPT_USERNAME,username.toUtf8().constData());
    curl_easy_setopt(curl,CURLOPT_SSH_PRIVATE_KEYFILE,
		     id_filename.toUtf8().constData());
    curl_easy_setopt(curl,CURLOPT_KEYPASSWD,password.toUtf8().constData());
  }
  else {
    strncpy(userpwd,(username+":"+password).utf8(),256);
    curl_easy_setopt(curl,CURLOPT_USERPWD,userpwd);
  }

  curl_easy_setopt(curl,CURLOPT_URL,conv_target_url.toEncoded().constData());
  curl_easy_setopt(curl,CURLOPT_HTTPAUTH,CURLAUTH_ANY);
  curl_easy_setopt(curl,CURLOPT_WRITEFUNCTION,DeleteWriteCallback);
  curl_easy_setopt(curl,CURLOPT_WRITEDATA,&xml);
  curl_easy_setopt(curl,CURLOPT_USERAGENT,
		   config()->userAgent().toUtf8().constData());
  if(log_debug) {
    curl_easy_setopt(curl,CURLOPT_VERBOSE,1);
    curl_easy_setopt(curl,CURLOPT_DEBUGFUNCTION,DeleteErrorCallback);
  }

  if(conv_target_url.scheme().toLower()=="ftp"||conv_target_url.scheme().toLower()=="ftps") {
    QStringList f0=conv_target_url.path().split("/",QString::SkipEmptyParts);
    filename=f0.last();
    f0.removeLast();
    if(f0.size()>0) {
      currentdir=f0.join("/")+"/";
    }
    if(!currentdir.isEmpty()) {
      cmds=curl_slist_append(cmds,(QString("cwd ")+currentdir).toUtf8());
    }
    cmds=curl_slist_append(cmds,(QString("dele ")+filename).toUtf8());
  }

  if(conv_target_url.scheme().toLower()=="sftp") {
    cmds=
      curl_slist_append(cmds,(QString("rm ")+conv_target_url.path()).toUtf8());
  }

  curl_easy_setopt(curl,CURLOPT_QUOTE,cmds);

  switch((err=curl_easy_perform(curl))) {
  case CURLE_OK:
  case CURLE_REMOTE_ACCESS_DENIED:   // Sometimes we get this even when
  case CURLE_QUOTE_ERROR:            //  In case the file is already gone
  case CURLE_REMOTE_FILE_NOT_FOUND:
    ret=RDDelete::ErrorOk;
    break;

  case CURLE_UNSUPPORTED_PROTOCOL:
    ret=RDDelete::ErrorUnsupportedProtocol;
    break;

  case CURLE_URL_MALFORMAT:
    ret=RDDelete::ErrorUrlInvalid;
    break;

  case CURLE_COULDNT_RESOLVE_HOST:
    ret=RDDelete::ErrorInvalidHostname;
    break;

  case CURLE_LOGIN_DENIED:
    ret=RDDelete::ErrorInvalidLogin;
    break;

  case CURLE_COULDNT_CONNECT:
    ret=RDDelete::ErrorRemoteConnection;
    break;

  default:
    ret=RDDelete::ErrorUnknown;
    printf("CURL error: %d\n",err);
    break;
  }
  if(log_debug) {
    rda->syslog(LOG_INFO,curl_easy_strerror(err));
  }
  curl_slist_free_all(cmds);
  curl_easy_cleanup(curl);

  return ret;
}

QString RDDelete::errorText(RDDelete::ErrorCode err)
{
  QString ret=QString().sprintf("Unknown RDDelete Error [%u]",err);

  switch(err) {
  case RDDelete::ErrorOk:
    ret=tr("OK");
    break;

  case RDDelete::ErrorUnsupportedProtocol:
    ret=tr("Unsupported protocol");
    break;

  case RDDelete::ErrorInvalidHostname:
    ret=tr("Unable to resolve hostname");
    break;

  case RDDelete::ErrorRemoteServer:
    ret=tr("Remote server error");
    break;

  case RDDelete::ErrorUrlInvalid:
    ret=tr("Invalid URL");
    break;

  case RDDelete::ErrorUnspecified:
    ret=tr("Unspecified error");
    break;

  case RDDelete::ErrorInvalidUser:
    ret=tr("Invalid User");
    break;

  case RDDelete::ErrorInvalidLogin:
    ret=tr("Invalid Login");
    break;

  case RDDelete::ErrorRemoteAccess:
    ret=tr("Remote Access Denied");
    break;

  case RDDelete::ErrorRemoteConnection:
    ret=tr("Counldn't Connect");
    break;

  case RDDelete::ErrorInternal:
    ret=tr("Internal Error");
    break;

  case RDDelete::ErrorUnknown:
    break;

  case RDDelete::ErrorUnsupportedUrlScheme:
    ret=tr("Unsupported URL Scheme");
    break;
  }
  return ret;
}


void RegisterDeviceLayerErrorFormatter()
{
    static ErrorFormatter sDeviceLayerErrorFormatter = { FormatDeviceLayerError, nullptr };

    RegisterErrorFormatter(&sDeviceLayerErrorFormatter);
}

/*
 *
 *    All rights reserved.
 *
 */

/**
 *    @file
 *          General utility functions available on all platforms.
 */

#include <lib/support/CodeUtils.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>

namespace chip {
namespace DeviceLayer {

/**
 * Register a text error formatter for Device Layer errors.
 */

/**
 * Given Device Layer error, returns a human-readable NULL-terminated C string
 * describing the error.
 *
 * @param[in] buf                   Buffer into which the error string will be placed.
 * @param[in] bufSize               Size of the supplied buffer in bytes.
 * @param[in] err                   The error to be described.
 *
 * @return true                     If a descriptions string was written into the supplied buffer.
 * @return false                    If the supplied error was not a Device Layer error.
 *
 */
bool FormatDeviceLayerError(char * buf, uint16_t bufSize, CHIP_ERROR err)
{
    const char * desc = nullptr;

    if (!err.IsPart(ChipError::SdkPart::kDevice))
    {
        return false;
    }

#if !CHIP_CONFIG_SHORT_ERROR_STR
    switch (err.AsInteger())
    {
    case CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND.AsInteger():
        desc = "Config not found";
        break;
    case CHIP_DEVICE_ERROR_NOT_SERVICE_PROVISIONED.AsInteger():
        desc = "Not service provisioned";
        break;
    }
#endif // !CHIP_CONFIG_SHORT_ERROR_STR

    FormatError(buf, bufSize, "Device Layer", err, desc);

    return true;
}

const char * CharacterizeIPv6Address(const chip::Inet::IPAddress & ipAddr)
{
    if (ipAddr.IsIPv6LinkLocal())
    {
        return "IPv6 link-local address";
    }

    if (ipAddr.IsIPv6ULA())
    {
        return "IPv6 unique local address";
    }

    if (ipAddr.IsIPv6GlobalUnicast())
    {
        return "IPv6 global unicast address";
    }

    return "IPv6 address";
}

} // namespace DeviceLayer
} // namespace chip


switch (err.AsInteger())
    {
    case CHIP_DEVICE_ERROR_CONFIG_NOT_FOUND.AsInteger():
        desc = "Config not found";
        break;
    case CHIP_DEVICE_ERROR_NOT_SERVICE_PROVISIONED.AsInteger():
        desc = "Not service provisioned";
        break;
    }

/*
 *
 *    All rights reserved.
 *
 */

/**
 *    @file
 *          General utility functions available on all platforms.
 */

#include <lib/support/CodeUtils.h>
#include <platform/internal/CHIPDeviceLayerInternal.h>

namespace chip {
namespace DeviceLayer {

/**
 * Register a text error formatter for Device Layer errors.
 */
void RegisterDeviceLayerErrorFormatter()
{
    static ErrorFormatter sDeviceLayerErrorFormatter = { FormatDeviceLayerError, nullptr };

    RegisterErrorFormatter(&sDeviceLayerErrorFormatter);
}

/**
 * Given Device Layer error, returns a human-readable NULL-terminated C string
 * describing the error.
 *
 * @param[in] buf                   Buffer into which the error string will be placed.
 * @param[in] bufSize               Size of the supplied buffer in bytes.
 * @param[in] err                   The error to be described.
 *
 * @return true                     If a descriptions string was written into the supplied buffer.
 * @return false                    If the supplied error was not a Device Layer error.
 *
 */
bool FormatDeviceLayerError(char * buf, uint16_t bufSize, CHIP_ERROR err)
{
    const char * desc = nullptr;

    if (!err.IsPart(ChipError::SdkPart::kDevice))
    {
        return false;
    }

#if !CHIP_CONFIG_SHORT_ERROR_STR

#endif // !CHIP_CONFIG_SHORT_ERROR_STR

    FormatError(buf, bufSize, "Device Layer", err, desc);

    return true;
}

const char * CharacterizeIPv6Address(const chip::Inet::IPAddress & ipAddr)
{
    if (ipAddr.IsIPv6LinkLocal())
    {
        return "IPv6 link-local address";
    }

    if (ipAddr.IsIPv6ULA())
    {
        return "IPv6 unique local address";
    }

    if (ipAddr.IsIPv6GlobalUnicast())
    {
        return "IPv6 global unicast address";
    }

    return "IPv6 address";
}

} // namespace DeviceLayer
} // namespace chip


switch (spellInfo->Id)
                {
                    case eSpells::FlameGoutSearcher:
                        me->CastSpell(target, eSpells::FlameGoutMissile, true);
                        break;
                    default:
                        break;
                }

            }

            void DoAction(int32 const action) override
            {
                switch (action)
                {
                    case eActions::DeactivatePillar:
                    {
                        DeactivatePillar();
                        break;
                    }
                    case eActions::DeactivatePillarOther:
                    {
                        AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void
                        {
                            DeactivatePillar();

                            if (Creature* l_Kargath = me->FindNearestCreature(eHighmaulCreatures::KargathBladefist, 15.0f))
                            {
                                l_Kargath->SetControlled(false, UnitState::UNIT_STATE_ROOT);
                                l_Kargath->RemoveUnitFlag(UNIT_FLAG_STUNNED);
                                l_Kargath->SetReactState(ReactStates::REACT_AGGRESSIVE);
                                l_Kargath->RemoveUnitFlag2(UNIT_FLAG2_DISABLE_TURN);
                                l_Kargath->ClearUnitState(UnitState::UNIT_STATE_CANNOT_TURN);
                            }
                        });

                        break;
                    }
                    default:
                        break;
                }
            }

            void DeactivatePillar()
            {
                /// Deactivate Pillar
                me->RemoveAllAreaTriggers();
                me->RemoveUnitFlag(UNIT_FLAG_IN_COMBAT);
                me->RemoveAllAuras();
                me->SetAIAnimKitId(0);
                me->PlayOneShotAnimKitId(eData::AnimKit3);
                me->RemoveAura(eSpells::FlameGoutPeriodic);
            }

            void UpdateAI(uint32 const diff) override
            {
                UpdateOperations(diff);
            }
        };

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

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

        enum eSpells
        {
            SpellMaul       = 161218,
            OnTheHunt       = 162497,
            SpellInflamed   = 163130,
            InThePitAura    = 161423
        };

        enum eActions
        {
            FreeRavenous,
            InterruptRavenous
        };

        enum eEvent
        {
            CheckPlayer = 1
        };

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

            EventMap m_Events;

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);
                me->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));

                m_Events.Reset();
                m_Events.ScheduleEvent(eEvent::CheckPlayer, 500);

                me->RemoveAura(eSpells::SpellInflamed);
            }

            void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
            {
                if (target == nullptr)
                    return;

                switch (spellInfo->Id)
                {
                    case eSpells::OnTheHunt:
                    {
                        me->ClearUnitState(UnitState::UNIT_STATE_CASTING);

                        me->SetInCombatWithZone();

                        me->GetThreatManager().clearReferences();
                        me->GetThreatManager().addThreat(target, std::numeric_limits<float>::max());

                        me->SetReactState(ReactStates::REACT_AGGRESSIVE);
                        me->TauntApply(target);
                        me->SetReactState(ReactStates::REACT_PASSIVE);

                        me->SetSpeed(UnitMoveType::MOVE_RUN, 0.5f);

                        me->RemoveUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));

                        me->GetMotionMaster()->Clear(true);
                        me->GetMotionMaster()->MoveChase(target);
                        break;
                    }
                    default:
                        break;
                }
            }

            void DoAction(int32 const action) override
            {
                switch (action)
                {
                    case eActions::FreeRavenous:
                    {
                        if (!IsMythic())
                            break;

                        m_Events.CancelEvent(eEvent::CheckPlayer);
                        me->RemoveUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));

                        float l_X = me->GetPositionX() + (9.0f * cos(me->GetOrientation()));
                        float l_Y = me->GetPositionY() + (9.0f * sin(me->GetOrientation()));

                        me->GetMotionMaster()->MoveJump(l_X, l_Y, g_ArenaFloor, 15.0f, 25.0f, me->GetOrientation());

                        AddTimedDelayedOperation(2 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->CastSpell(me, eSpells::OnTheHunt, false); });
                        break;
                    }
                    case eActions::InterruptRavenous:
                    {
                        if (me->HasAura(eSpells::SpellInflamed))
                            break;

                        me->GetMotionMaster()->Clear();

                        me->CastSpell(me, eSpells::SpellInflamed, true);

                        /*AddTimedDelayedOperation(1 * TimeConstants::IN_MILLISECONDS, [this]() -> void
                        {
                            Position pos;
                            float l_X = me->GetPositionX() + (7.0f * cos(me->GetOrientation()));
                            float l_Y = me->GetPositionY() + (7.0f * sin(me->GetOrientation()));

                            pos.m_positionX = l_X;
                            pos.m_positionY = l_Y;

                            /// Creating the circle path from the center
                            me->GetMotionMaster()->Clear();
                            me->SetWalk(false);
                            me->SetSpeed(UnitMoveType::MOVE_RUN, 2.0f);

                            Movement::MoveSplineInit l_Init(me);
                            FillCirclePath(pos, 7.0f, me->GetPositionZ(), l_Init.Path(), true);
                            l_Init.SetWalk(true);
                            l_Init.SetCyclic();
                            l_Init.Launch();
                        });*/

                        GrantFavorToAllPlayers(me, 10, eSpells::SpellInflamed);
                        break;
                    }
                    default:
                        break;
                }
            }

            void MovementInform(uint32 type, uint32 /*id*/) override
            {
                if (type != MovementGeneratorType::CHASE_MOTION_TYPE)
                    return;

                if (Unit* target = me->GetVictim())
                {
                    me->AddAura(eSpells::InThePitAura, target);
                    me->CastSpell(target, eSpells::SpellMaul, true);
                    target->RemoveAura(eSpells::InThePitAura);
                    me->RemoveUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));
                }
            }

            void KilledUnit(Unit* /*killed*/) override
            {
                if (!IsMythic())
                    return;

                me->SetInCombatWithZone();

                /// Hunt another player only if the first one's dead, or at the end of the channel
                me->CastSpell(me, eSpells::OnTheHunt, false);
            }

            void EnterEvadeMode(EvadeReason /*why*/) override
            {
                ClearDelayedOperations();

                me->InterruptNonMeleeSpells(true);
                me->SetReactState(ReactStates::REACT_PASSIVE);
                CreatureAI::EnterEvadeMode();
            }

            void JustDied(Unit*) override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);
            }

            void UpdateAI(uint32 const diff) override
            {
                UpdateOperations(diff);

                m_Events.Update(diff);

                if (me->HasUnitState(UnitState::UNIT_STATE_CASTING) || me->HasUnitState(UnitState::UNIT_STATE_CHASE))
                    return;

                if (m_Events.ExecuteEvent() == eEvent::CheckPlayer)
                {
                    std::list<Player*> l_PlrList;
                    me->GetPlayerListInGrid(l_PlrList, 10.0f);

                    for (Player* player : l_PlrList)
                    {
                        if (!player->IsAlive() || player->GetPositionZ() > 50.0f)
                            continue;

                        me->AddAura(eSpells::InThePitAura, player);
                        me->CastSpell(player, eSpells::SpellMaul, true);
                        player->RemoveAura(eSpells::InThePitAura);
                        me->RemoveUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));
                        break;
                    }

                    m_Events.ScheduleEvent(eEvent::CheckPlayer, 500);
                }
            }

            void FillCirclePath(Position const& p_Center, float radius, float p_Z, Movement::PointsArray& p_Path, bool p_Clockwise)
            {
                float l_Step = p_Clockwise ? -M_PI / 8.0f : float(M_PI) / 8.0f;
                float l_Angle = p_Center.GetAngle(me->GetPositionX(), me->GetPositionY());

                for (uint8 itr = 0; itr < 16; l_Angle += l_Step, ++itr)
                {
                    G3D::Vector3 l_Point;
                    l_Point.x = p_Center.GetPositionX() + radius * cosf(l_Angle);
                    l_Point.y = p_Center.GetPositionY() + radius * sinf(l_Angle);
                    l_Point.z = p_Z;
                    p_Path.push_back(l_Point);
                }
            }
        };

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

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

        enum eSpells
        {
            BladeDanceDmg           = 159217,
            BladeDanceCharge        = 159265,
            SpellBladeDanceFadeOut  = 159209
        };

        enum eDatas
        {
            MorphWithWeapon = 54674,
            MorphInvisible  = 11686,
            AnimKit1        = 5865,
            AnimKit2        = 5864,
            AnimKit3        = 5863,
            AnimKit4        = 5861
        };

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

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);
                me->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_IMMUNE_TO_PC));
            }

            void SpellHit(Unit* p_Caster, SpellInfo const* spellInfo) override
            {
                if (p_Caster == nullptr)
                    return;

                if (spellInfo->Id == eSpells::BladeDanceCharge)
                {
                    me->SetDisplayId(eDatas::MorphWithWeapon);
                    me->CastSpell(me, eSpells::BladeDanceDmg, true, nullptr, nullptr, p_Caster->GetGUID());
                    me->CastSpell(p_Caster, eSpells::BladeDanceCharge, true);

                    uint32 const l_AnimKits[4] = { eDatas::AnimKit1, eDatas::AnimKit2, eDatas::AnimKit3, eDatas::AnimKit4 };
                    me->PlayOneShotAnimKitId(l_AnimKits[urand(0, 3)]);

                    AddTimedDelayedOperation(100, [this]() -> void { me->CastSpell(me, eSpells::SpellBladeDanceFadeOut, true); });
                    AddTimedDelayedOperation(500, [this]() -> void { me->SetDisplayId(eDatas::MorphInvisible); });
                    AddTimedDelayedOperation(600, [this]() -> void { me->DespawnOrUnsummon(); });
                }
            }
        };

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

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

        enum eSpells
        {
            KegPackCosmetic     = 160213,

            MaulingBrewSearch   = 159410,
            MaulingBrew         = 159414,
            VileBreath          = 160521,

            /// Only in Mythic difficulty
            Heckle              = 163408    ///< Reduces the favor within Roar of the Crowd.
        };

        enum eEvents
        {
            EventMaulingBrew = 1,
            EventVileBreath,
            EventHeckle
        };

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

            EventMap m_ClassicEvent;
            EventMap m_FightEvent;

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);

                me->CastSpell(me, eSpells::KegPackCosmetic, true);

                m_ClassicEvent.Reset();
                m_ClassicEvent.ScheduleEvent(eEvents::EventMaulingBrew, urand(3000, 5000));

                m_FightEvent.Reset();
            }

            void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override
            {
                if (me->HasReactState(ReactStates::REACT_PASSIVE))
                {
                    m_ClassicEvent.Reset();
                    me->SetReactState(ReactStates::REACT_AGGRESSIVE);
                    m_FightEvent.ScheduleEvent(eEvents::EventVileBreath, 3000);
                    m_FightEvent.ScheduleEvent(eEvents::EventHeckle, 6000);
                }
            }

            void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
            {
                if (target == nullptr)
                    return;

                if (spellInfo->Id == eSpells::MaulingBrewSearch)
                    me->CastSpell(target, eSpells::MaulingBrew, true);
            }

            void JustDied(Unit* /*killer*/) override
            {
                me->RemoveAllAreaTriggers();
                me->DespawnOrUnsummon(5000);
            }

            void UpdateAI(uint32 const diff) override
            {
                m_ClassicEvent.Update(diff);

                if (m_ClassicEvent.ExecuteEvent() == eEvents::EventMaulingBrew)
                {
                    me->CastSpell(me, eSpells::MaulingBrewSearch, false);
                    m_ClassicEvent.ScheduleEvent(eEvents::EventMaulingBrew, urand(15000, 25000));
                }

                if (!UpdateVictim())
                    return;

                m_FightEvent.Update(diff);

                if (me->HasUnitState(UnitState::UNIT_STATE_CASTING))
                    return;

                switch (m_FightEvent.ExecuteEvent())
                {
                    case eEvents::EventVileBreath:
                        me->CastSpell(me, eSpells::VileBreath, false);
                        m_FightEvent.ScheduleEvent(eEvents::EventVileBreath, 10000);
                        break;
                    case eEvents::EventHeckle:
                        me->CastSpell(me, eSpells::Heckle, false);
                        m_FightEvent.ScheduleEvent(eEvents::EventHeckle, 10000);
                        break;
                    default:
                        break;
                }

                DoMeleeAttackIfReady();

                /// Arena's trash mobs shouldn't enter into the arena
                if (me->GetPositionZ() <= g_InArenaZ)
                    EnterEvadeMode();
            }
        };

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

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

        enum eSpells
        {
            BombPackCosmetic    = 160249,

            SpellIronBomb       = 159386,
            FireBomb            = 160953,

            /// Only for Mythic difficulty
            Heckle              = 163408    ///< Reduces the favor within Roar of the Crowd.
        };

        enum eEvents
        {
            EventIronBomb = 1,
            EventHeckle
        };

        enum eDatas
        {
            MorphDead   = 61562,
            IronBomb    = 79712
        };

        enum eTalk
        {
            Bomb
        };

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

            bool m_HasBomb;
            EventMap m_Events;
            EventMap m_FightEvents;

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);

                me->CastSpell(me, eSpells::BombPackCosmetic, true);

                m_HasBomb = false;

                m_Events.Reset();
                m_Events.ScheduleEvent(eEvents::EventIronBomb, urand(3000, 6000));
            }

            void DamageTaken(Unit* /*attacker*/, uint32& damage) override
            {
                if (me->HasReactState(ReactStates::REACT_PASSIVE))
                {
                    m_Events.Reset();
                    me->SetReactState(ReactStates::REACT_AGGRESSIVE);
                    m_FightEvents.ScheduleEvent(eEvents::EventHeckle, 6000);
                }

                if (m_HasBomb)
                    return;

                if (me->HealthBelowPctDamaged(50, damage))
                {
                    m_HasBomb = true;
                    if (Creature* l_IronBomb = me->SummonCreature(eDatas::IronBomb, me->GetPosition()))
                        l_IronBomb->EnterVehicle(me, 0);

                    Talk(eTalk::Bomb);
                }
            }

            void PassengerBoarded(Unit* p_Passenger, int8 /*p_Seat*/, bool p_Apply) override
            {
                if (p_Apply || p_Passenger->ToCreature() == nullptr)
                    return;

                p_Passenger->CastSpell(p_Passenger, eSpells::FireBomb, true);
                p_Passenger->ToCreature()->SetReactState(ReactStates::REACT_PASSIVE);
            }

            void JustDied(Unit* /*killer*/) override
            {
                me->SetDisplayId(eDatas::MorphDead);
                me->DespawnOrUnsummon(5000);
            }

            void UpdateAI(uint32 const diff) override
            {
                /// Iron Bombs are out of combat
                m_Events.Update(diff);

                if (m_Events.ExecuteEvent() == eEvents::EventIronBomb)
                {
                    me->CastSpell(me, eSpells::SpellIronBomb, TRIGGERED_IGNORE_CASTER_MOUNTED_OR_ON_VEHICLE);
                    m_Events.ScheduleEvent(eEvents::EventIronBomb, urand(5000, 9000));
                }

                if (!UpdateVictim())
                    return;

                if (m_FightEvents.ExecuteEvent() == eEvents::EventHeckle)
                {
                    me->CastSpell(me, eSpells::Heckle, false);
                    m_FightEvents.ScheduleEvent(eEvents::EventHeckle, 6000);
                }

                DoMeleeAttackIfReady();

                /// Arena's trash mobs shouldn't enter into the arena
                if (me->GetPositionZ() <= g_InArenaZ)
                    EnterEvadeMode();
            }
        };

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

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

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

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);

                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void LastOperationCalled() override
            {
                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void UpdateAI(uint32 const diff) override
            {
                UpdateOperations(diff);
            }
        };

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

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

        enum eSpells
        {
            Grapple             = 159188,
            CrowdMinionKilled   = 163392
        };

        enum eEvent
        {
            EventGrapple = 1
        };

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

            EventMap m_Events;

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);

                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });

                m_Events.Reset();
            }

            void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override
            {
                if (me->HasReactState(ReactStates::REACT_PASSIVE))
                {
                    me->SetReactState(ReactStates::REACT_AGGRESSIVE);

                    m_Events.Reset();

                    m_Events.ScheduleEvent(eEvent::EventGrapple, 3000);
                }
            }

            void JustDied(Unit* /*killer*/) override
            {
                /// In Mythic difficulty, killing Iron Grunts grants favor for Roar of the Crowd.
                if (Map* l_Map = me->GetMap())
                {
                    if (l_Map->IsMythic())
                        instance->DoCastSpellOnPlayers(eSpells::CrowdMinionKilled);
                }
            }

            void LastOperationCalled() override
            {
                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void UpdateAI(uint32 const diff) override
            {
                ScriptedAI::UpdateAI(diff);

                if (!UpdateVictim())
                    return;

                m_Events.Update(diff);

                if (m_Events.ExecuteEvent() == eEvent::EventGrapple)
                {
                    if (Unit* target = SelectTarget(SelectAggroTarget::SELECT_TARGET_MAXTHREAT))
                        me->CastSpell(target, eSpells::Grapple, true);
                    m_Events.ScheduleEvent(eEvent::EventGrapple, 6000);
                }

                DoMeleeAttackIfReady();

                /// Arena's trash mobs shouldn't enter into the arena
                if (me->GetPositionZ() <= g_InArenaZ)
                    EnterEvadeMode();
            }
        };

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

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

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

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);

                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override
            {
                if (me->HasReactState(ReactStates::REACT_PASSIVE))
                    me->SetReactState(ReactStates::REACT_AGGRESSIVE);
            }

            void JustDied(Unit* /*killer*/) override
            {
                /// In Mythic difficulty, killing Iron Grunts grants favor for Roar of the Crowd.
            }

            void LastOperationCalled() override
            {
                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void UpdateAI(uint32 const diff) override
            {
                ScriptedAI::UpdateAI(diff);

                if (!UpdateVictim())
                    return;

                DoMeleeAttackIfReady();

                /// Arena's trash mobs shouldn't enter into the arena
                if (me->GetPositionZ() <= g_InArenaZ)
                    EnterEvadeMode();
            }
        };

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

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

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

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);

                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void DamageTaken(Unit* /*attacker*/, uint32& /*damage*/) override
            {
                if (me->HasReactState(ReactStates::REACT_PASSIVE))
                    me->SetReactState(ReactStates::REACT_AGGRESSIVE);
            }

            void JustDied(Unit* /*killer*/) override
            {
                /// In Mythic difficulty, killing Iron Grunts grants favor for Roar of the Crowd.
            }

            void LastOperationCalled() override
            {
                AddTimedDelayedOperation(3 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->HandleEmoteCommand(g_CrowdEmotes[urand(0, 7)]); });
            }

            void UpdateAI(uint32 const diff) override
            {
                ScriptedAI::UpdateAI(diff);

                if (!UpdateVictim())
                    return;

                DoMeleeAttackIfReady();

                /// Arena's trash mobs shouldn't enter into the arena
                if (me->GetPositionZ() <= g_InArenaZ)
                    EnterEvadeMode();
            }
        };

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

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

        enum eSpells
        {
            ArenaSweeper        = 177776,
            MonstersBrawl       = 177815,
            ArenaSweeperImpact  = 177802
        };

        enum eActions
        {
            KickOutPlayers,

            SpawnIronBombers = 5,
            SpawnDrukenBileslinger
        };

        enum eSweeperType
        {
            TypeRight,
            TypeLeft
        };

        struct npc_highmaul_highmaul_sweeperAI : public ScriptedAI
        {
            npc_highmaul_highmaul_sweeperAI(Creature* creature) : ScriptedAI(creature)
            {
                m_Instance  = creature->GetInstanceScript();
            }

            InstanceScript* m_Instance;

            uint8 m_MoveID;
            uint8 m_SweeperType;
            bool m_ComeBack;

            void Reset() override
            {
                m_MoveID        = 0;
                m_SweeperType   = me->GetOrientation() >= 6.0f ? eSweeperType::TypeRight : eSweeperType::TypeLeft;
                m_ComeBack      = false;

                me->SetReactState(ReactStates::REACT_PASSIVE);
                me->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));
                me->SetSpeed(UnitMoveType::MOVE_RUN, 2.0f);
            }

            void DoAction(int32 const action) override
            {
                if (action == eActions::KickOutPlayers)
                {
                    me->CastSpell(me, eSpells::ArenaSweeper, true);
                    me->GetMotionMaster()->MovePoint(m_MoveID, g_HighmaulSweeperMoves[m_SweeperType][m_MoveID]);
                }
            }

            void MovementInform(uint32 type, uint32 /*id*/) override
            {
                if (type != MovementGeneratorType::POINT_MOTION_TYPE)
                    return;

                if (m_MoveID < (eHighmaulDatas::HighmaulSweeperMovesCount - 1) && !m_ComeBack)
                {
                    ++m_MoveID;
                    me->GetMotionMaster()->MovePoint(m_MoveID, g_HighmaulSweeperMoves[m_SweeperType][m_MoveID]);
                }
                else if (m_MoveID == (eHighmaulDatas::HighmaulSweeperMovesCount - 1))
                {
                    m_ComeBack = true;
                    --m_MoveID;
                    me->GetMotionMaster()->MovePoint(m_MoveID, g_HighmaulSweeperMoves[m_SweeperType][m_MoveID]);
                }
                else if (m_MoveID == 0 && m_ComeBack)
                {
                    /// End of move
                    m_ComeBack = false;
                    me->RemoveAura(eSpells::ArenaSweeper);
                    AddTimedDelayedOperation(1 * TimeConstants::IN_MILLISECONDS, [this]() -> void { me->SetFacingTo(me->GetHomePosition().GetOrientation()); });

                    /// Just needed once
                    if (m_SweeperType)
                    {
                        if (Creature* l_Kargath = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::KargathBladefist)))
                        {
                            l_Kargath->AI()->DoAction(eActions::SpawnIronBombers);
                            l_Kargath->AI()->DoAction(eActions::SpawnDrukenBileslinger);
                        }
                    }
                }
                else if (m_ComeBack && m_MoveID > 0)
                {
                    --m_MoveID;
                    me->GetMotionMaster()->MovePoint(m_MoveID, g_HighmaulSweeperMoves[m_SweeperType][m_MoveID]);
                }
            }

            void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override
            {
                if (target == nullptr)
                    return;

                if (spellInfo->Id == eSpells::MonstersBrawl)
                {
                    me->CastSpell(target, eSpells::ArenaSweeperImpact, true);

                    Position l_FirstPos = g_SweeperJumpPos[m_SweeperType][0];
                    Position l_SecondPos = g_SweeperJumpPos[m_SweeperType][1];

                    if (me->GetDistance(l_FirstPos) > me->GetDistance(l_SecondPos))
                        target->GetMotionMaster()->MoveJump(l_SecondPos, 15.0f, 25.0f, target->GetOrientation());
                    else
                        target->GetMotionMaster()->MoveJump(l_FirstPos, 15.0f, 25.0f, target->GetOrientation());
                }
            }
        };

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

/// Chain Hurl Vehicle - 79134
class npc_highmaul_chain_hurl_vehicle : public CreatureScript
{
    public:
        npc_highmaul_chain_hurl_vehicle() : CreatureScript("npc_highmaul_chain_hurl_vehicle") { }

        enum eSpells
        {
            ChainHurlJumpDest   = 159995,
            Obscured            = 160131,
            Chain               = 159531,
            FlameJetDoT         = 159311
        };

        enum eAction
        {
            EndOfChainHurl = 7
        };

        struct npc_highmaul_chain_hurl_vehicleAI : public ScriptedAI
        {
            npc_highmaul_chain_hurl_vehicleAI(Creature* creature) : ScriptedAI(creature)
            {
                m_Instance = creature->GetInstanceScript();
            }

            InstanceScript* m_Instance;

            float m_Angle;
            bool m_Rotate;

            void Reset() override
            {
                m_Angle = 0.0f;
                m_Rotate = false;
            }

            void PassengerBoarded(Unit* p_Passenger, int8 /*p_SeatID*/, bool p_Apply) override
            {
                if (p_Apply || p_Passenger == nullptr || p_Passenger->GetTypeId() != TypeID::TYPEID_PLAYER)
                {
                    if (!m_Rotate && p_Apply)
                        m_Rotate = true;

                    if (p_Apply && p_Passenger != nullptr)
                        p_Passenger->RemoveAura(eSpells::FlameJetDoT);

                    return;
                }

                ObjectGuid guid = p_Passenger->GetGUID();
                AddTimedDelayedOperation(1 * TimeConstants::IN_MILLISECONDS, [this, guid]() -> void
                {
                    if (Unit* l_Passenger = ObjectAccessor::GetUnit(*me, guid))
                        l_Passenger->CastSpell(g_ArenaStandsPos, eSpells::ChainHurlJumpDest, true);
                });

                p_Passenger->RemoveAura(eSpells::Chain);
                p_Passenger->AddAura(eSpells::Obscured, p_Passenger);

                m_Rotate = false;
                m_Angle = 0.0f;
                me->SetFacingTo(m_Angle);
                me->SetOrientation(m_Angle);
            }

            void UpdateAI(uint32 const diff) override
            {
                ScriptedAI::UpdateAI(diff);

                if (!m_Rotate)
                    return;

                m_Angle += (2.0f * float(M_PI)) / 10.0f;

                if (m_Angle > (2.0f * float(M_PI)))
                    m_Angle = 0.0f;

                me->SetFacingTo(m_Angle);
                me->SetOrientation(m_Angle);
            }
        };

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

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

        enum eActions
        {
            StartIntro,
            VulgorDied = 2
        };

        enum eSpells
        {
            ElevatorSoundTrigger    = 166694,
            Obscured                = 160131
        };

        enum eEvent
        {
            InitObscured = 1
        };

        enum eCreatures
        {
            IronGrunt1  = 84946,
            IronGrunt2  = 79068,
            OgreGrunt1  = 84948,
            OgreGrunt2  = 84958
        };

        struct npc_highmauareaTrigger_for_crowdAI : public ScriptedAI
        {
            npc_highmauareaTrigger_for_crowdAI(Creature* creature) : ScriptedAI(creature)
            {
                m_IntroStarted  = false;
                m_Instance      = creature->GetInstanceScript();
                m_CheckTimer    = 1000;
            }

            bool m_IntroStarted;
            InstanceScript* m_Instance;

            uint32 m_CheckTimer;

            EventMap m_Events;

            void Reset() override
            {
                me->SetReactState(ReactStates::REACT_PASSIVE);
                me->AddUnitFlag(UnitFlags(UNIT_FLAG_NOT_SELECTABLE | UNIT_FLAG_NON_ATTACKABLE));
                me->AddUnitFlag(UnitFlags(UNIT_FLAG_IMMUNE_TO_NPC | UNIT_FLAG_IMMUNE_TO_PC));

                m_Events.Reset();
                m_Events.ScheduleEvent(eEvent::InitObscured, 2000);
            }

            void UpdateAI(uint32 const diff) override
            {
                ScheduleIntro(diff);

                m_Events.Update(diff);

                if (m_Events.ExecuteEvent() == eEvent::InitObscured)
                {
                    std::list<Creature*> l_CreatureList;
                    me->GetCreatureListInGrid(l_CreatureList, 300.0f);

                    if (l_CreatureList.empty())
                        return;

                    l_CreatureList.remove_if([this](Creature* creature) -> bool
                    {
                        if (creature->GetEntry() != eHighmaulCreatures::DrunkenBileslinger &&
                            creature->GetEntry() != eHighmaulCreatures::IronBomber &&
                            creature->GetEntry() != eCreatures::IronGrunt1 &&
                            creature->GetEntry() != eCreatures::IronGrunt2 &&
                            creature->GetEntry() != eCreatures::OgreGrunt1 &&
                            creature->GetEntry() != eCreatures::OgreGrunt2)
                            return true;

                        if (creature->HasAura(eSpells::Obscured, me->GetGUID()))
                            return true;

                        return false;
                    });

                    for (Creature* l_Creature : l_CreatureList)
                        me->CastSpell(l_Creature, eSpells::Obscured, true);

                    m_Events.ScheduleEvent(eEvent::InitObscured, 3000);
                }
            }

            void ScheduleIntro(uint32 const diff)
            {
                if (!m_CheckTimer || m_IntroStarted)
                    return;

                if (m_CheckTimer <= diff)
                {
                    if (me->SelectNearestPlayer(10.0f) != nullptr)
                    {
                        m_IntroStarted = true;

                        me->CastSpell(me, eSpells::ElevatorSoundTrigger, true);

                        if (Creature* l_Vulgor = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::Vulgor)))
                        {
                            if (l_Vulgor->IsAlive())
                            {
                                if (Creature* l_Jhorn = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::JhornTheMad)))
                                    l_Jhorn->AI()->DoAction(eActions::StartIntro);

                                if (Creature* l_Thoktar = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::ThoktarIronskull)))
                                    l_Thoktar->AI()->DoAction(eActions::StartIntro);
                            }
                            /// Handle the case if Vul'gor already died
                            else
                            {
                                if (Creature* l_Jhorn = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::JhornTheMad)))
                                    l_Jhorn->AI()->DoAction(eActions::VulgorDied);

                                if (Creature* l_Thoktar = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::ThoktarIronskull)))
                                    l_Thoktar->AI()->DoAction(eActions::VulgorDied);

                                Creature* l_MargokCosmetic = m_Instance->GetCreature(eHighmaulCreatures::MargokCosmetic);

                                if (!l_MargokCosmetic)
                                    l_MargokCosmetic = me->SummonCreature(eHighmaulCreatures::MargokCosmetic, 3397.446289f, 7493.029297f, 73.664780f, 0.928363f);

                                if (l_MargokCosmetic)
                                    l_MargokCosmetic->AI()->DoAction(eActions::VulgorDied);

                                if (Creature* l_Gharg = ObjectAccessor::GetCreature(*me, m_Instance->GetGuidData(eHighmaulCreatures::GhargArenaMaster)))
                                    l_Gharg->AI()->DoAction(eActions::StartIntro);
                            }
                        }
                    }
                    else
                        m_CheckTimer = 1000;
                }
                else
                    m_CheckTimer -= diff;
            }
        };

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

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

        class spell_highmaul_earth_breaker_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_earth_breaker_SpellScript);

            enum eDatas
            {
                TargetRestrict  = 20675,
                EarthMissile    = 162472,
                SmolderingStone = 80051
            };

            void CorrectTargets(std::list<WorldObject*>& targets)
            {
                if (targets.empty())
                    return;

                SpellTargetRestrictionsEntry const* l_Restriction = sSpellTargetRestrictionsStore.LookupEntry(eDatas::TargetRestrict);
                if (l_Restriction == nullptr)
                    return;

                Unit* caster = GetCaster();
                if (caster == nullptr)
                    return;

                float radius = GetSpellInfo()->GetEffect(1)->CalcRadius(caster);
                targets.remove_if([radius, caster, l_Restriction](WorldObject* p_Object) -> bool
                {
                    if (p_Object == nullptr)
                        return true;

                    if (!p_Object->IsInAxe(caster, l_Restriction->Width, radius))
                        return true;

                    return false;
                });
            }

            void HandleBeforeCast()
            {
                if (Unit* caster = GetCaster())
                {
                    float radius = 30.0f;
                    float l_O = caster->GetOrientation();
                    float l_X = caster->GetPositionX() + (radius * cos(l_O));
                    float l_Y = caster->GetPositionY() + (radius * sin(l_O));
                    float l_Z = caster->GetPositionZ();
                    caster->CastSpell(l_X, l_Y, l_Z, eDatas::EarthMissile, true);
                    caster->SummonCreature(eDatas::SmolderingStone, l_X, l_Y, l_Z, l_O, TempSummonType::TEMPSUMMON_TIMED_DESPAWN, 45000);
                }
            }

            void Register() override
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_earth_breaker_SpellScript::CorrectTargets, EFFECT_1, TARGET_UNIT_CONE_ENTRY_129);
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_earth_breaker_SpellScript::CorrectTargets, EFFECT_2, TARGET_UNIT_CONE_ENTRY_129);
                BeforeCast += SpellCastFn(spell_highmaul_earth_breaker_SpellScript::HandleBeforeCast);
            }
        };

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

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

        enum eSpell
        {
            OpenWounds = 159178
        };

        enum eCreature
        {
            AreaTriggerForCrowd = 79260
        };

        class spell_highmaul_impale_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_impale_AuraScript);

            void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                Unit* target = GetTarget();
                Unit* caster = GetCaster();
                if (target == nullptr || caster == nullptr)
                    return;

                AuraRemoveMode l_RemoveMode = GetTargetApplication()->GetRemoveMode();
                if (l_RemoveMode != AuraRemoveMode::AURA_REMOVE_BY_DEATH)
                    caster->CastSpell(target, eSpell::OpenWounds, true);

                if (Creature* l_Trigger = target->FindNearestCreature(eCreature::AreaTriggerForCrowd, 50.0f))
                {
                    if (target->GetDistance(l_Trigger) >= 10.0f)
                    {
                        float l_O = target->GetAngle(l_Trigger);
                        float l_X = target->GetPositionX() + 5.0f * cos(l_O);
                        float l_Y = target->GetPositionY() + 5.0f * sin(l_O);
                        float l_Z = caster->GetPositionZ();

                        Position const pos = { l_X, l_Y, l_Z, l_O };
                        target->ExitVehicle(&pos);
                        return;
                    }
                }

                target->ExitVehicle();
            }

            void Register() override
            {
                AfterEffectRemove += AuraEffectRemoveFn(spell_highmaul_impale_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_PERIODIC_DAMAGE, AURA_EFFECT_HANDLE_REAL);
            }
        };

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

/// Fire Pillar Steam Timer - 163967
class spell_highmaul_fire_pillar_steam_timer : public SpellScriptLoader
{
    public:
        spell_highmaul_fire_pillar_steam_timer() : SpellScriptLoader("spell_highmaul_fire_pillar_steam_timer") { }

        enum eSpell
        {
            FirePillarSteamCosmetic = 163970
        };

        class spell_highmaul_fire_pillar_steam_timer_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_fire_pillar_steam_timer_AuraScript);

            void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                Unit* target = GetTarget();
                if (target == nullptr)
                    return;

                target->CastSpell(target, eSpell::FirePillarSteamCosmetic, true);
            }

            void Register() override
            {
                AfterEffectRemove += AuraEffectRemoveFn(spell_highmaul_fire_pillar_steam_timer_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
            }
        };

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

/// Fire Pillar Activated - 159706
class spell_highmaul_fire_pillar_activated : public SpellScriptLoader
{
    public:
        spell_highmaul_fire_pillar_activated() : SpellScriptLoader("spell_highmaul_fire_pillar_activated") { }

        class spell_highmaul_fire_pillar_activated_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_fire_pillar_activated_AuraScript);

            void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                Unit* target = GetTarget();
                if (target == nullptr)
                    return;

                if (Creature* l_Pillar = target->ToCreature())
                {
                    if (l_Pillar->IsAIEnabled)
                        l_Pillar->AI()->DoAction(0);
                }
            }

            void Register() override
            {
                AfterEffectRemove += AuraEffectRemoveFn(spell_highmaul_fire_pillar_activated_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_DUMMY, AURA_EFFECT_HANDLE_REAL);
            }
        };

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

/// Fire Pillar (Flame gout searcher) - 162576
class spell_highmaul_fire_pillar_gout_searcher : public SpellScriptLoader
{
public:
    spell_highmaul_fire_pillar_gout_searcher() : SpellScriptLoader("spell_highmaul_fire_pillar_gout_searcher") { }

    class spell_highmaul_fire_pillar_gout_searcher_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_highmaul_fire_pillar_gout_searcher_SpellScript);

        enum eSpells
        {
            Obscured = 160131
        };

        void CorrectTargets(std::list<WorldObject*>& targets)
        {
            targets.remove_if(Trinity::UnitAuraCheck(true, eSpells::Obscured));

            if (!targets.empty())
            {
                targets.remove_if([](WorldObject* p_Object) -> bool
                {
                    return !p_Object || !p_Object->IsPlayer() || p_Object->GetPositionZ() >= 62.4f;
                });
            }

            if (targets.size() > 5)
                Trinity::Containers::RandomResize(targets, 5);
        }

        void Register() override
        {
            OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_fire_pillar_gout_searcher_SpellScript::CorrectTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
        }
    };

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

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

        enum eAction
        {
            InterruptByPillar = 4
        };

        enum eSpell
        {
            BerserkerRushIncrease = 159029
        };

        class spell_highmaul_berserker_rush_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_berserker_rush_AuraScript);

            void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                Unit* target = GetTarget();
                if (target == nullptr)
                    return;

                AuraRemoveMode l_RemoveMode = GetTargetApplication()->GetRemoveMode();

                if (Creature* l_Kargath = target->ToCreature())
                {
                    if (l_Kargath->IsAIEnabled && l_RemoveMode != AuraRemoveMode::AURA_REMOVE_BY_EXPIRE)
                        l_Kargath->AI()->DoAction(eAction::InterruptByPillar);
                    else if (l_Kargath->IsAIEnabled)
                    {
                        if (boss_kargath_bladefist::boss_kargath_bladefistAI* l_AI = CAST_AI(boss_kargath_bladefist::boss_kargath_bladefistAI, l_Kargath->GetAI()))
                        {
                            if (Unit* l_Victim = ObjectAccessor::GetUnit(*l_Kargath, l_AI->m_BerserkerRushTarget))
                                l_AI->EndBerserkerRush(l_Victim);
                        }
                    }

                    l_Kargath->RemoveAura(eSpell::BerserkerRushIncrease);
                }
            }

            void Register() override
            {
                AfterEffectRemove += AuraEffectRemoveFn(spell_highmaul_berserker_rush_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL);
            }
        };

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

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

        enum eDatas
        {
            MaxAffectedTargets  = 5,
            SpellObscured       = 160131,
            OpenWounds          = 159178,
            MaxLFRHealer        = 1,
            MaxLFRDamagers      = 3,
            MaxLFRTank          = 1
        };

        class spell_highmaul_chain_hurl_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_chain_hurl_SpellScript);

            uint8 m_Count;
            ObjectGuid m_Targets[eDatas::MaxAffectedTargets];

            bool Load() override
            {
                m_Count = 0;

                for (uint8 l_I = 0; l_I < eDatas::MaxAffectedTargets; ++l_I)
                    m_Targets[l_I] = ObjectGuid::Empty;

                return true;
            }

            bool TargetsAlreadySelected() const
            {
                bool l_Return = false;
                for (uint8 l_I = 0; l_I < m_Count; ++l_I)
                {
                    if (!m_Targets[l_I])
                        return false;

                    l_Return = true;
                }

                return l_Return;
            }

            void ProcessLFRTargetting(std::list<WorldObject*>& targets, std::list<Player*> playerList)
            {
                std::list<Player*> l_TanksList      = playerList;
                std::list<Player*> l_HealersList    = playerList;
                std::list<Player*> l_DamagersList   = playerList;

                uint8 l_OpenWoundsStacks = 0;
                l_TanksList.remove_if([&l_OpenWoundsStacks](Player* player) -> bool
                {
                    if (player->GetRoleForGroup() != Roles::ROLE_TANK)
                        return true;

                    if (player->HasAura(eDatas::SpellObscured))
                        return true;

                    if (Aura* l_OpenWounds = player->GetAura(eDatas::OpenWounds))
                    {
                        if (l_OpenWounds->GetStackAmount() > l_OpenWoundsStacks)
                            l_OpenWoundsStacks = l_OpenWounds->GetStackAmount();
                    }

                    return false;
                });

                if (!l_TanksList.empty())
                {
                    l_TanksList.remove_if([l_OpenWoundsStacks](Player* player) -> bool
                    {
                        if (player->GetRoleForGroup() != Roles::ROLE_TANK)
                            return true;

                        if (player->HasAura(eDatas::SpellObscured))
                            return true;

                        if (Aura* l_OpenWounds = player->GetAura(eDatas::OpenWounds))
                        {
                            if (l_OpenWounds->GetStackAmount() != l_OpenWoundsStacks)
                                return true;
                        }

                        return false;
                    });
                }

                /// Just in case of all tanks have the same amount of Open Wounds
                if (!l_TanksList.empty() && l_TanksList.size() > eDatas::MaxLFRTank)
                    Trinity::Containers::RandomResize(l_TanksList, eDatas::MaxLFRTank);

                l_HealersList.remove_if([](Player* player) -> bool
                {
                    if (player->GetRoleForGroup() != Roles::ROLE_HEALER)
                        return true;

                    if (player->HasAura(eDatas::SpellObscured))
                        return true;

                    return false;
                });

                if (!l_HealersList.empty() && l_HealersList.size() > eDatas::MaxLFRHealer)
                    Trinity::Containers::RandomResize(l_HealersList, eDatas::MaxLFRHealer);

                l_DamagersList.remove_if([](Player* player) -> bool
                {
                    if (player->GetRoleForGroup() != Roles::ROLE_DAMAGE)
                        return true;

                    if (player->HasAura(eDatas::SpellObscured))
                        return true;

                    return false;
                });

                if (!l_DamagersList.empty() && l_DamagersList.size() > eDatas::MaxLFRDamagers)
                    Trinity::Containers::RandomResize(l_DamagersList, eDatas::MaxLFRDamagers);

                m_Count = 0;

                for (Player* player : l_TanksList)
                {
                    targets.push_back(player);
                    m_Targets[m_Count] = player->GetGUID();
                    ++m_Count;
                }

                for (Player* player : l_HealersList)
                {
                    targets.push_back(player);
                    m_Targets[m_Count] = player->GetGUID();
                    ++m_Count;
                }

                for (Player* player : l_DamagersList)
                {
                    targets.push_back(player);
                    m_Targets[m_Count] = player->GetGUID();
                    ++m_Count;
                }
            }

            void CorrectTargets(std::list<WorldObject*>& targets)
            {
                targets.clear();

                Unit* caster = GetCaster();
                if (caster == nullptr)
                    return;

                if (TargetsAlreadySelected())
                {
                    for (uint8 l_I = 0; l_I < m_Count; ++l_I)
                    {
                        /// Spell has SPELL_ATTR3_ONLY_TARGET_PLAYERS
                        if (WorldObject* l_Object = ObjectAccessor::GetPlayer(*caster, m_Targets[l_I]))
                            targets.push_back(l_Object);
                    }

                    return;
                }

                std::list<Player*> playerList;
                caster->GetPlayerListInGrid(playerList, 300.0f);

                if (playerList.empty())
                    return;

                playerList.remove_if(Trinity::UnitAuraCheck(true, eDatas::SpellObscured));

                if (playerList.empty())
                    return;

                /// In Looking for Raid difficulty, Kargath automatically tosses the tank with the highest amount of Open Wounds stacks,
                /// One random healer, and three random DPS into the crowd.
                if (caster->GetMap() && caster->GetMap()->IsLFR())
                    ProcessLFRTargetting(targets, playerList);
                /// Kargath uses his chain to lash the 5 closest enemies and toss them into the arena's stands.
                else
                {
                    playerList.sort(Trinity::ObjectDistanceOrderPred(caster));

                    m_Count = 0;
                    for (Player* player : playerList)
                    {
                        if (m_Count >= eDatas::MaxAffectedTargets)
                            break;

                        m_Targets[m_Count] = player->GetGUID();
                        targets.push_back(player);
                        ++m_Count;
                    }
                }

                if (targets.size() > eDatas::MaxAffectedTargets)
                    Trinity::Containers::RandomResize(targets, eDatas::MaxAffectedTargets);
            }

            void Register() override
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_chain_hurl_SpellScript::CorrectTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_chain_hurl_SpellScript::CorrectTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_chain_hurl_SpellScript::CorrectTargets, EFFECT_2, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

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

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

        class spell_highmaul_vile_breath_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_vile_breath_SpellScript);

            enum eSpell
            {
                TargetRestrict = 20321
            };

            void CorrectTargets(std::list<WorldObject*>& targets)
            {
                if (targets.empty())
                    return;

                SpellTargetRestrictionsEntry const* l_Restriction = sSpellTargetRestrictionsStore.LookupEntry(eSpell::TargetRestrict);
                if (l_Restriction == nullptr)
                    return;

                Unit* caster = GetCaster();
                if (caster == nullptr)
                    return;

                float l_Angle = 2 * float(M_PI) / 360 * l_Restriction->ConeDegrees;
                targets.remove_if([caster, l_Angle](WorldObject* p_Object) -> bool
                {
                    if (p_Object == nullptr)
                        return true;

                    if (!p_Object->isInFront(caster, l_Angle))
                        return true;

                    return false;
                });
            }

            void Register() override
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_vile_breath_SpellScript::CorrectTargets, EFFECT_0, TARGET_UNIT_CONE_ENTRY_110);
            }
        };

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

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

        class spell_highmaul_obscured_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_obscured_AuraScript);

            uint32 m_UpdateTimer;

            bool Load() override
            {
                m_UpdateTimer = 8000;
                return true;
            }

            void OnUpdate(uint32 diff)
            {
                if (m_UpdateTimer)
                {
                    if (m_UpdateTimer > diff)
                    {
                        m_UpdateTimer -= diff;
                        return;
                    }
                    else
                        m_UpdateTimer = 0;
                }

                if (Unit* target = GetUnitOwner())
                {
                    /// Target is not in Arena's stands anymore
                    if (target->GetPositionZ() <= g_InArenaZ)
                        GetAura()->Remove();
                }
            }

            void Register() override
            {
                OnAuraUpdate += AuraUpdateFn(spell_highmaul_obscured_AuraScript::OnUpdate);
            }
        };

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

/// Crowd Minion Killed - 163392
class spell_highmaul_crowd_minion_killed : public SpellScriptLoader
{
    public:
        spell_highmaul_crowd_minion_killed() : SpellScriptLoader("spell_highmaul_crowd_minion_killed") { }

        class spell_highmaul_crowd_minion_killed_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_crowd_minion_killed_SpellScript);

            void HandleDummy(SpellEffIndex /*effIndex*/)
            {
                if (Unit* caster = GetCaster())
                    caster->EnergizeBySpell(caster, GetSpellInfo()->Id, 1, Powers::POWER_ALTERNATE_POWER);
            }

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

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

/// Roar of the Crowd - 163302
class spell_highmaul_roar_of_the_crowd : public SpellScriptLoader
{
    public:
        spell_highmaul_roar_of_the_crowd() : SpellScriptLoader("spell_highmaul_roar_of_the_crowd") { }

        enum eSpells
        {
            CrowdFavorite25     = 163366,   ///< Mod damage PCT done, and mod pet damage PCT done
            CrowdFavorite50     = 163368,   ///< Mod damage PCT done, and mod pet damage PCT done
            CrowdFavorite75     = 163369,   ///< Mod damage PCT done, and mod pet damage PCT done
            CrowdFavorite100    = 163370    ///< Mod damage PCT done, and mod pet damage PCT done
        };

        enum eDatas
        {
            Favor25     = 25,
            Favor50     = 50,
            Favor75     = 75,
            Favor100    = 100
        };

        class spell_highmaul_roar_of_the_crowd_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_roar_of_the_crowd_AuraScript);

            void OnTick(AuraEffect const* /*aurEff*/)
            {
                if (Unit* caster = GetCaster())
                {
                    int32 l_Favor = caster->GetPower(Powers::POWER_ALTERNATE_POWER);

                    if (l_Favor < eDatas::Favor25)
                    {
                        caster->RemoveAura(eSpells::CrowdFavorite25);
                        caster->RemoveAura(eSpells::CrowdFavorite50);
                        caster->RemoveAura(eSpells::CrowdFavorite75);
                        caster->RemoveAura(eSpells::CrowdFavorite100);
                    }
                    else if (l_Favor < eDatas::Favor50)
                        caster->CastSpell(caster, eSpells::CrowdFavorite25, true);
                    else if (l_Favor < eDatas::Favor75)
                        caster->CastSpell(caster, eSpells::CrowdFavorite50, true);
                    else if (l_Favor < eDatas::Favor100)
                        caster->CastSpell(caster, eSpells::CrowdFavorite75, true);
                    else
                        caster->CastSpell(caster, eSpells::CrowdFavorite100, true);
                }
            }

            void Register() override
            {
                OnEffectPeriodic += AuraEffectPeriodicFn(spell_highmaul_roar_of_the_crowd_AuraScript::OnTick, EFFECT_1, SPELL_AURA_PERIODIC_DUMMY);
            }
        };

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

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

        enum eSpell
        {
            OnTheHunt = 162497
        };

        class spell_highmaul_inflamed_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_inflamed_AuraScript);

            void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
            {
                Unit* target = GetTarget();
                if (target == nullptr)
                    return;

                if (Creature* l_Ravenous = target->ToCreature())
                {
                    l_Ravenous->GetMotionMaster()->Clear();
                    l_Ravenous->CastSpell(l_Ravenous, eSpell::OnTheHunt, false);
                }
            }

            void Register() override
            {
                AfterEffectRemove += AuraEffectRemoveFn(spell_highmaul_inflamed_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_DAMAGE_PERCENT_TAKEN, AURA_EFFECT_HANDLE_REAL);
            }
        };

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

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

        class spell_highmaul_heckle_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_heckle_SpellScript);

            void HandleDummy(SpellEffIndex /*effIndex*/)
            {
                if (Creature* caster = GetCaster()->ToCreature())
                    GrantFavorToAllPlayers(caster, -2, GetSpellInfo()->Id);
            }

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

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

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

        class spell_highmaul_berserker_rush_periodic_AuraScript : public AuraScript
        {
            PrepareAuraScript(spell_highmaul_berserker_rush_periodic_AuraScript);

            void OnTick(AuraEffect const* aurEff)
            {
                if (Creature* caster = GetTarget()->ToCreature())
                {
                    if (!caster->GetMap()->IsMythic())
                        return;

                    /// Grants one point of Favor every second
                    if (aurEff->GetTickNumber() % 2)
                        GrantFavorToAllPlayers(caster, 1, GetSpellInfo()->Id);
                }
            }

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

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

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

        enum eSpell
        {
            Obscured = 160131
        };

        class spell_highmaul_blade_dance_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_blade_dance_SpellScript);

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

                targets.clear();

                std::list<Player*> playerList;
                caster->GetPlayerListInGrid(playerList, 300.0f);

                if (playerList.empty())
                    return;

                playerList.remove_if([](Player* player) -> bool
                {
                    if (player->GetRoleForGroup() == Roles::ROLE_TANK)
                        return true;

                    if (player->HasAura(eSpell::Obscured))
                        return true;

                    return false;
                });

                if (playerList.empty())
                    return;

                std::list<Player*> l_SnapShot;

                playerList.remove_if([caster, &l_SnapShot](Player* player) -> bool
                {
                    if (caster->GetDistance(player) <= 10.0f)
                    {
                        l_SnapShot.push_back(player);
                        return true;
                    }

                    return false;
                });

                if (playerList.empty() && !l_SnapShot.empty())
                    playerList = l_SnapShot;

                Trinity::Containers::RandomResize(playerList, 1);
                targets.push_back(playerList.front());
            }

            void Register() override
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_blade_dance_SpellScript::CorrectTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

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

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

        enum eSpells
        {
            Obscured        = 160131,
            IronBomb        = 159386,
            BerserkerRush   = 163180
        };

        class spell_highmaul_correct_searchers_SpellScript : public SpellScript
        {
            PrepareSpellScript(spell_highmaul_correct_searchers_SpellScript);

            void CorrectTargets(std::list<WorldObject*>& targets)
            {
                targets.remove_if(Trinity::UnitAuraCheck(true, eSpells::Obscured));

                if (GetSpellInfo()->Id == eSpells::BerserkerRush && !targets.empty())
                {
                    Unit* caster = GetCaster();

                    targets.remove_if([](WorldObject* p_Object) -> bool
                    {
                        if (p_Object == nullptr || !p_Object->IsPlayer())
                            return true;

                        if (Player* player = p_Object->ToPlayer())
                        {
                            /*if (!player->IsRangedDamageDealer())
                                return true;*/

                            if (player->GetPositionZ() >= 63.4f)
                                return true;
                        }

                        return false;
                    });

                    /// Kargath will target one of the three furthest targets and chase them till he either kills the player.
                    if (caster->GetMap()->IsMythic())
                    {
                        targets.sort(Trinity::DistanceCompareOrderPred(caster, false));

                        uint8 l_Count = 0;
                        std::list<WorldObject*> l_NewTargets;

                        for (WorldObject* itr : targets)
                        {
                            if (l_Count >= 3)
                                break;

                            l_NewTargets.push_back(itr);
                            ++l_Count;
                        }

                        targets.clear();

                        if (!l_NewTargets.empty())
                            Trinity::Containers::RandomResize(l_NewTargets, l_Count);

                        if (!l_NewTargets.empty())
                            targets.push_back(l_NewTargets.front());
                    }
                }
            }

            void Register() override
            {
                OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_correct_searchers_SpellScript::CorrectTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY);

                if (m_scriptSpellId == eSpells::IronBomb)
                    OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_highmaul_correct_searchers_SpellScript::CorrectTargets, EFFECT_1, TARGET_UNIT_SRC_AREA_ENEMY);
            }
        };

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

class areatrigger_highmaul_molten_bomb : public AreaTriggerAI
{
public:
    areatrigger_highmaul_molten_bomb(AreaTrigger* areaTrigger) : AreaTriggerAI(areaTrigger) { }

    enum eSpell
    {
        MoltenBomb = 161635
    };

    std::set<ObjectGuid> m_AffectedPlayers;

    void OnUpdate(uint32 /*diff*/) override
    {
        if (Unit* caster = at->GetCaster())
        {
            std::list<Unit*> targetList;
            float radius = 3.0f;

            Trinity::AnyUnfriendlyUnitInObjectRangeCheck l_Check(at, caster, radius);
            Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> l_Searcher(at, targetList, l_Check);
            Cell::VisitAllObjects(at, l_Searcher, radius);

            std::set<ObjectGuid> targets;

            for (Unit* itr : targetList)
            {
                targets.insert(itr->GetGUID());

                if (!itr->HasAura(eSpell::MoltenBomb, caster->GetGUID()))
                {
                    m_AffectedPlayers.insert(itr->GetGUID());
                    itr->CastSpell(itr, eSpell::MoltenBomb, true);
                }
            }

            for (std::set<ObjectGuid>::iterator itr = m_AffectedPlayers.begin(); itr != m_AffectedPlayers.end();)
            {
                if (targets.find((*itr)) != targets.end())
                {
                    ++itr;
                    continue;
                }

                if (Unit* l_Unit = ObjectAccessor::GetUnit(*caster, (*itr)))
                {
                    itr = m_AffectedPlayers.erase(itr);
                    l_Unit->RemoveAura(eSpell::MoltenBomb, caster->GetGUID());

                    continue;
                }

                ++itr;
            }
        }
    }

    void OnRemove() override
    {
        if (Unit* caster = at->GetCaster())
        {
            for (ObjectGuid guid : m_AffectedPlayers)
            {
                if (Unit* l_Unit = ObjectAccessor::GetUnit(*caster, guid))
                    l_Unit->RemoveAura(eSpell::MoltenBomb, caster->GetGUID());
            }
        }
    }
};

class areatrigger_highmaul_flame_jet : public AreaTriggerAI
{
public:
    areatrigger_highmaul_flame_jet(AreaTrigger* areaTrigger) : AreaTriggerAI(areaTrigger) { }

    enum eSpells
    {
        FlameJet            = 159311,
        BerserkerRushAura   = 159028,
        Obscured            = 160131
    };

    enum eCreature
    {
        RavenousBloodmaw = 79296
    };

    enum eAction
    {
        InterruptRavenous = 1
    };

    void OnUnitEnter(Unit* unit) override
    {
        if (at->GetCaster() == unit)
            return;

        /// Don't add DoT on targets in vehicle (Chain Hurl or Impale)
        if (!unit->IsOnVehicle() && unit->GetEntry() != eHighmaulCreatures::KargathBladefist)
            at->GetCaster()->CastSpell(unit, eSpells::FlameJet, true);

        if (unit->GetEntry() == eHighmaulCreatures::KargathBladefist)
        {
            if (unit->HasAura(eSpells::BerserkerRushAura))
                unit->RemoveAura(eSpells::BerserkerRushAura, unit->GetGUID(), 0, AuraRemoveMode::AURA_REMOVE_BY_ENEMY_SPELL);
        }

        if (unit->GetEntry() == eCreature::RavenousBloodmaw)
        {
            if (unit->IsAIEnabled)
                unit->ToCreature()->AI()->DoAction(eAction::InterruptRavenous);
        }
    }

    void OnUnitExit(Unit* unit) override
    {
        unit->RemoveAura(eSpells::FlameJet);
    }

    void OnUpdate(uint32 /*diff*/) override
    {
        std::unordered_set<ObjectGuid> insideUnits = at->GetInsideUnits();

        for (ObjectGuid insideUnit : insideUnits)
        {
            if (Unit* l_Unit = ObjectAccessor::GetUnit(*at->GetCaster(), insideUnit))
                if (l_Unit->IsOnVehicle() || l_Unit->HasAura(eSpells::Obscured))
                    if (l_Unit->HasAura(eSpells::FlameJet))
                        l_Unit->RemoveAura(eSpells::FlameJet);
        }
    }
};

class areatrigger_highmaul_mauling_brew : public AreaTriggerAI
{
public:
    areatrigger_highmaul_mauling_brew(AreaTrigger* areaTrigger) : AreaTriggerAI(areaTrigger) { }

    enum eSpells
    {
        MaulingBrew = 159413
    };

    void OnUpdate(uint32 /*diff*/) override
    {
        if (Unit* caster = at->GetCaster())
        {
            std::list<Unit*> targetList;
            float radius = 3.0f;

            Trinity::AnyUnfriendlyUnitInObjectRangeCheck l_Check(at, caster, radius);
            Trinity::UnitListSearcher<Trinity::AnyUnfriendlyUnitInObjectRangeCheck> l_Searcher(at, targetList, l_Check);
            Cell::VisitAllObjects(at, l_Searcher, radius);

            for (Unit* l_Unit : targetList)
            {
                if (l_Unit->IsOnVehicle())
                    continue;

                caster->CastSpell(l_Unit, eSpells::MaulingBrew, true);
            }
        }
    }
};

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

    class spell_kargath_mauling_brew_SpellScript : public SpellScript
    {
        PrepareSpellScript(spell_kargath_mauling_brew_SpellScript);

        void HandleDummy(SpellEffIndex /*effIndex*/)
        {
            if (Unit* caster = GetCaster())
                if (Unit* target = GetExplTargetUnit())
                    caster->CastSpell(target, GetSpellInfo()->GetEffect(EFFECT_0)->BasePoints, true);
        }

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

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

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

    class spell_chain_hurl_tempfix_AuraScript : public AuraScript
    {
        PrepareAuraScript(spell_chain_hurl_tempfix_AuraScript);

        enum eSpells
        {
            ChainHurlJumpDest   = 159995,
            Obscured            = 160131,
            Chain               = 159531,
            FlameJetDoT         = 159311
        };

        bool Load()
        {
            return true;
        }

        void ApplyEffect(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/)
        {
            PreventDefaultAction();

            Unit* caster = GetCaster();

            if (!caster)
                return;

            caster->CastSpell(g_ArenaStandsPos, eSpells::ChainHurlJumpDest, true);
        }

        void Register()
        {
            OnEffectApply += AuraEffectApplyFn(spell_chain_hurl_tempfix_AuraScript::ApplyEffect, EFFECT_0, SPELL_AURA_CONTROL_VEHICLE, AURA_EFFECT_HANDLE_REAL);
        }
    };

    AuraScript* GetAuraScript() const
    {
        return new spell_chain_hurl_tempfix_AuraScript();
    }
};

void AddSC_boss_kargath_bladefist()
{
    new boss_kargath_bladefist();

    new npc_highmaul_vulgor();
    new npc_highmaul_bladespire_sorcerer();
    new npc_highmaul_somldering_stoneguard();
    new npc_highmaul_fire_pillar();
    new npc_highmaul_ravenous_bloodmaw();
    new npc_highmaul_kargath_bladefist_trigger();
    new npc_highmaul_drunken_bileslinger();
    new npc_highmaul_iron_bomber();
    new npc_highmaul_iron_grunt();
    new npc_highmaul_iron_grunt_second();
    new npc_highmaul_ogre_grunt();
    new npc_highmaul_ogre_grunt_second();
    new npc_highmaul_highmaul_sweeper();
    new npc_highmaul_chain_hurl_vehicle();
    new npc_highmauareaTrigger_for_crowd();

    new spell_highmaul_earth_breaker();
    new spell_highmaul_impale();
    new spell_highmaul_fire_pillar_steam_timer();
    new spell_highmaul_fire_pillar_activated();
    new spell_highmaul_fire_pillar_gout_searcher();
    new spell_highmaul_berserker_rush();
    new spell_highmaul_chain_hurl();
    new spell_highmaul_vile_breath();
    new spell_highmaul_obscured();
    new spell_highmaul_crowd_minion_killed();
    new spell_highmaul_roar_of_the_crowd();
    new spell_highmaul_inflamed();
    new spell_highmaul_heckle();
    new spell_highmaul_berserker_rush_periodic();
    new spell_highmaul_blade_dance();
    new spell_highmaul_correct_searchers();
    new spell_kargath_mauling_brew();
    new spell_chain_hurl_tempfix();

    RegisterAreaTriggerAI(areatrigger_highmaul_molten_bomb);
    RegisterAreaTriggerAI(areatrigger_highmaul_flame_jet);
    RegisterAreaTriggerAI(areatrigger_highmaul_mauling_brew);
}