Public feed #890474

WolfDragon

public text v1 · immutable
Raw Edit
·published 2008-02-03 19:47 UTC
rendered paste body
/* Copyright (C) 2006,2007 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/>
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License as published by
* the Free Software Foundation; either version 2 of the License, or
* (at your option) any later version.
*
* This program is distributed in the hope that it will be useful,
* but WITHOUT ANY WARRANTY; without even the implied warranty of
* MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
* GNU General Public License for more details.
*
* You should have received a copy of the GNU General Public License
* along with this program; if not, write to the Free Software
* Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307  USA
*/

/* ScriptData
SDName: Boss_Shade_of_Akama
SD%Complete: 0
SDComment: VERIFY SCRIPT
SDCategory: Black Temple
EndScriptData */

#include "../../sc_defines.h"
#include "../../../../../shared/WorldPacket.h"
#include "../../../../../game/TargetedMovementGenerator.h"
#include "../../../../../game/Player.h"
#include "../../../../../game/GossipDef.h"

struct Location
{
    float x, y, o;
};

static Location ChannelerLocations[]=
{
    {463.161285, 401.219757, 3.141592},
    {457.377625, 391.227661, 2.106461},
    {446.012421, 391.227661, 1.071904},
    {439.533783, 401.219757, 0.000000},
    {446.012421, 411.211853, 5.210546},
    {457.377625, 411.211853, 4.177494}
};

static Location SpawnLocations[]=
{
    {498.652740, 461.728119, 0},
    {398.652740, 337.368103, 0}
};

// Locations
#define Z1              118.543144
#define Z2              120.783768

#define Z_SPAWN         113.537949

// Texts
#define SOUND_LOW_HEALTH            11386
#define SAY_LOW_HEALTH              "No! Not yet..."
#define SOUND_EVEN_LOWER_HEALTH     11385
#define SAY_EVEN_LOWER_HEALTH       "I will not last much longer..."

// Gossips
#define GOSSIP_ITEM                 "It is time to free your soul from Illidan's grasp!"

// Spells
#define SPELL_PASSIVE_SHADOWFORM    41913
#define SPELL_BEAM                  39123
#define SPELL_RED_BEAM              36295
#define SPELL_DESTRUCTIVE_POISON    40874
#define SPELL_LIGHTNING_BOLT        42024
#define SPELL_AKAMA_SOUL_EXPEL      40855
#define SPELL_AKAMA_SOUL_RETRIEVE   40447

// Channeler entry
#define CREATURE_CHANNELER          23421

const uint32 spawnEntries[4]=
{
    {23216},
    {23523},
    {23318},
    {23524}
};

struct MANGOS_DLL_DECL mob_ashtongue_channelerAI : public ScriptedAI
{
    mob_ashtongue_channelerAI(Creature* c) : ScriptedAI(c)
    {
        SetVariables();
    }

    uint64 ShadeGUID;

    bool InCombat;

    void SetVariables()
    {
        ShadeGUID = 0;
    }

    void EnterEvadeMode()
    {
        SetVariables();
        InCombat = false;

        m_creature->RemoveAllAuras();
        m_creature->DeleteThreatList();
        m_creature->CombatStop();
        DoGoHome();
    }

    void SetShadeGUID(uint64 guid)
    {
        ShadeGUID = guid;
        outstring_log("DEBUG: Shade GUID: %u set in Ashtongue Channeler script. Channeler GUID: %u", ShadeGUID, m_creature->GetGUID());
    }

    void JustDied(Unit* killer);

    void AttackStart(Unit* who)
    {
        if(!InCombat) InCombat = true;
    }

    void MoveInLineOfSight(Unit* who)
    {
        if(!InCombat) InCombat = true;
    }

    void UpdateAI(const uint32 diff)
    {
        if(InCombat)
            m_creature->SetInCombat();
    }

    void DamageTaken(Unit *done_by, uint32 &damage);
};

struct MANGOS_DLL_DECL boss_shade_of_akamaAI : public ScriptedAI
{
    boss_shade_of_akamaAI(Creature* c) : ScriptedAI(c)
    {
        SetVariables();
    }

    uint64 ChannelerGUID[6];
    uint64 AkamaGUID;

    uint32 DeathCount;
    uint32 Countcheck;

    uint32 ReduceHealthTimer;
    uint32 EnrageTimer;
    uint32 SummonTimer;
    uint32 ResetTimer;

    bool Enrage;
    bool SummonedChannelers;
    bool IsBanished;
    bool InCombat;
    bool HasKilledAkama;

    void SetVariables()
    {
        if(SummonedChannelers)
        {
            for(uint8 i = 0; i < 7; i++)
            {
                Creature* Channeler = NULL;
                Channeler = ((Creature*)Unit::GetUnit((*m_creature), ChannelerGUID[i]));
                if(Channeler && Channeler->isAlive())
                {
                    Channeler->SetVisibility(VISIBILITY_OFF);
                    Channeler->DealDamage(Channeler, Channeler->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_NORMAL, NULL, false);
                }
                ChannelerGUID[i] = 0;
            }
            SummonedChannelers = false;
        }

        AkamaGUID = 0;

        DeathCount = 0;
        Countcheck = 0;

        EnrageTimer = 600000;
        SummonTimer = 20000;
        ReduceHealthTimer = 0;
        ResetTimer = 60000;

        Enrage = false;
        IsBanished = true;
        InCombat = false;
        HasKilledAkama = false;

        m_creature->ApplySpellImmune(0, IMMUNITY_MECHANIC, SPELL_AURA_MOD_TAUNT, true);
        m_creature->SetVisibility(VISIBILITY_ON);
        m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
        m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
        m_creature->GetMotionMaster()->Clear(false);
        m_creature->GetMotionMaster()->Idle();
        m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_STUN);
    }

    void EnterEvadeMode()
    {
        SetVariables();

        m_creature->DeleteThreatList();
        m_creature->RemoveAllAuras();
        DoCast(m_creature, SPELL_PASSIVE_SHADOWFORM);
        m_creature->CombatStop();
        DoGoHome();
    }

    void AttackStart(Unit* who)
    {
        if(!who || IsBanished)
            return;
        
        if(who->isTargetableForAttack() && who != m_creature)
                DoStartMeleeAttack(who);
    }

    void MoveInLineOfSight(Unit* who)
    {
        if(who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who))
        {
            float attackRadius = m_creature->GetAttackDistance(who);
            if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who))
            {
                if(who->HasStealthAura())
                    who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);

                if(who && who->isAlive())
                    m_creature->AddThreat(who, 1.0f);
            }
        }
    }

    void SummonCreature()
    {
        uint32 entry = spawnEntries[rand()%4];
        uint32 random = rand()%2;
        float X = SpawnLocations[random].x;
        float Y = SpawnLocations[random].y;
        m_creature->SummonCreature(entry, X, Y, Z_SPAWN, 0, TEMPSUMMON_CORPSE_DESPAWN, 0);
    }

    void SetSelectableChannelers()
    {
        if(!SummonedChannelers) 
            return;

        for(uint8 i = 0; i < 7; i++)
        {
            Unit* Channeler = Unit::GetUnit((*m_creature), ChannelerGUID[i]);
            if(Channeler && Channeler->isAlive())
            {
                Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                outstring_log("DEBUG: Shade of Akama Event - Removing unselectable flag from Channeler %u. GUID: %u",i, ChannelerGUID[i]);
            }
            else error_log("ERROR: Shade of Akama event - unable to find Channeler to set selectable. ChannelerGUID: %u", ChannelerGUID[i]);
        }
    }

    void SetAkamaGUID(uint64 guid)
    {
        AkamaGUID = guid;
        outstring_log("DEBUG: Shade of Akama - setting Akama's GUID to %u", guid);
    }

    void UpdateAI(const uint32 diff)
    {
        if(!SummonedChannelers)
        {
            for(uint8 i = 0; i < 6; i++)
            {
                ChannelerGUID[i] = 0;
                Creature* Channeler = NULL;
                float x = ChannelerLocations[i].x;
                float y = ChannelerLocations[i].y;
                float o = ChannelerLocations[i].o;
                Channeler = m_creature->SummonCreature(CREATURE_CHANNELER, x, y, m_creature->GetPositionZ(), o, TEMPSUMMON_DEAD_DESPAWN, 1000);
                if(Channeler)
                {
                    outstring_log("DEBUG: Shade of Akama Encounter: Channeler %u summoned at X: %f, Y: %f, Z: %f, with orientation %f", i, x, y, m_creature->GetPositionZ(), o);
                    Channeler->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    Channeler->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    Channeler->CastSpell(m_creature, SPELL_BEAM, false);
                    ((mob_ashtongue_channelerAI*)Channeler->AI())->SetShadeGUID(m_creature->GetGUID());
                    ChannelerGUID[i] = Channeler->GetGUID();
                }
                else // Debug
                    error_log("ERROR: Shade Of Akama Encounter: Unable to spawn Channeler %u! X: %f, Y: %f, o: %f", i, x, y, o);
            }
            if(!m_creature->HasAura(SPELL_PASSIVE_SHADOWFORM, 0)) DoCast(m_creature, SPELL_PASSIVE_SHADOWFORM, true);
            SummonedChannelers = true;
        }

        if(!InCombat)
            return;

        if(m_creature->getThreatManager().getThreatList().empty())
            EnterEvadeMode();

        if(IsBanished)
        {
            if(!m_creature->GetMotionMaster()->empty())
            {
                m_creature->GetMotionMaster()->Clear(false);
                m_creature->GetMotionMaster()->Idle();
            }

            if(SummonTimer < diff)
            {
                SummonCreature();
                SummonCreature();
                SummonTimer = Enrage ? 10000 : 15000; // Spawn them faster if enraged
            }else SummonTimer -= diff;

            if(DeathCount > Countcheck)
            {
                outstring_log("DEBUG: Shade of Akama - Death Count increment detected. DeathCount: %u", DeathCount);
                Countcheck = DeathCount;
            }

            if(DeathCount >= 6)
            {
                IsBanished = false;
                for(uint8 i = 0; i < 7; i++)
                    ChannelerGUID[i] = 0;
                if(AkamaGUID)
                {
                    Unit* Akama = Unit::GetUnit((*m_creature), AkamaGUID);
                    if(Akama && Akama->isAlive())
                    {
                        m_creature->GetMotionMaster()->Clear(false);
                        m_creature->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE);
                        m_creature->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                        m_creature->AddThreat(Akama, 1000000.0f); // Crazy amount of threat
                        Akama->AddThreat(m_creature, 1000000.0f);
                        m_creature->Attack(Akama);
                        Akama->Attack(m_creature);
                    }else // Debug
                        error_log("ERROR: Shade of Akama free but Akama is dead or missing");
                }
            }

            // Only run this if we are not enraged
            if(!Enrage)
            {
                if(EnrageTimer < diff)
                    Enrage = true;
                else EnrageTimer -= diff;
            }
        }
        else // No longer banished, let's fight Akama now
        {
            if(ReduceHealthTimer < diff)
            {
                if(AkamaGUID)
                {
                    Unit* Akama = Unit::GetUnit((*m_creature), AkamaGUID);
                    if(Akama && Akama->isAlive())
                    {
                        Akama->SetHealth(Akama->GetHealth() - (Akama->GetMaxHealth()*0.1)); // 10% less health each time
                        ReduceHealthTimer = Enrage ? 5000 : 12000; // Reduce health more rapidly if enraged
                    }
                    else
                    {
                        HasKilledAkama = true; // Akama is dead or missing, we stop fighting and disappear
                        m_creature->SetVisibility(VISIBILITY_OFF);
                        m_creature->SetHealth(m_creature->GetMaxHealth());
                        m_creature->RemoveAllAuras();
                        DoCast(m_creature, SPELL_PASSIVE_SHADOWFORM);
                        m_creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE);
                    }
                }
                else // Debug
                    error_log("ERROR: Shade of Akama Event - Shade is free but Akama's GUID is missing");
            }else ReduceHealthTimer -= diff;

            if(HasKilledAkama)
            {
                if(ResetTimer < diff)
                    EnterEvadeMode(); // Reset after a little while after killing Akama
                else ResetTimer -= diff;
            }

            DoMeleeAttackIfReady();
        }
    }
};

void mob_ashtongue_channelerAI::DamageTaken(Unit *done_by, uint32 &damage)
{
    if(damage > m_creature->GetHealth())
    {
        if(ShadeGUID)
        {
            Creature* Shade = NULL;
            Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID));
            if(Shade && Shade->isAlive())
            {
                ((boss_shade_of_akamaAI*)Shade->AI())->DeathCount++;
                outstring_log("DEBUG: Shade of Akama event - Channeler death detected. Incrementing Death Count. Channeler GUID: %u", m_creature->GetGUID());
            }
            else // Debug
                error_log("ERROR: Shade of Akama event- Ashtongue Channeler was killed but Shade of Akama is not found. Killer: %u, %c", done_by->GetGUID(), done_by->GetName());
        }
        else error_log("ERROR: Shade of Akama event - Ashtongue Channeler cannot increment Deathcount. ShadeGUID is invalid. Channeler GUID: %u", m_creature->GetGUID());
    }
}

void mob_ashtongue_channelerAI::JustDied(Unit* killer)
{
    outstring_log("DEBUG: Ashtongue Channeler killed");
    if(ShadeGUID)
    {
        Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID));
        if(Shade && Shade->isAlive())
        {
            ((boss_shade_of_akamaAI*)Shade->AI())->DeathCount++;
            outstring_log("DEBUG: Shade of Akama event: Channeler death detected. Death count incremented via JustDied. Channeler GUID: %u", m_creature->GetGUID());
        }else error_log("ERROR: Shade of Akama event: Channeler dead but Shade is dead or non-existant");
    }else error_log("ERROR: Shade of Akama event: Channeler dead but Shade GUID is invalid.");
}

struct MANGOS_DLL_DECL npc_akamaAI : public ScriptedAI
{
    npc_akamaAI(Creature* c) : ScriptedAI(c)
    {
        SetVariables();
        pInstance = ((ScriptedInstance*)c->GetInstanceData());
    }

    ScriptedInstance* pInstance;

    uint64 ShadeGUID;

    uint32 DestructivePoisonTimer;
    uint32 LightningBoltTimer;
    uint32 CheckTimer;
    uint32 CastSoulRetrieveTimer;

    bool InCombat;
    bool EventBegun;
    bool ShadeHasDied;
    bool HasYelledOnce;
    bool HasYelledTwice;

    void SetVariables()
    {
        DestructivePoisonTimer = 5000;
        LightningBoltTimer = 10000;
        CheckTimer = 2000;
        CastSoulRetrieveTimer = 0;

        InCombat = false;
        EventBegun = false;
        ShadeHasDied = false;
        HasYelledOnce = false;
        HasYelledTwice = false;
    }

    void EnterEvadeMode()
    {
        SetVariables();

        m_creature->RemoveAllAuras();
        m_creature->DeleteThreatList();
        m_creature->CombatStop();
        DoGoHome();
    }

    void AttackStart(Unit* who)
    {
        if(!who)
            return;

        if(who->isTargetableForAttack() && who!=m_creature)
            DoStartMeleeAttack(who);
    }

    void MoveInLineOfSight(Unit* who)
    {
        if(!who)
            return;
        
        if (who->isTargetableForAttack() && who->isInAccessablePlaceFor(m_creature) && m_creature->IsHostileTo(who))
        {
            float attackRadius = m_creature->GetAttackDistance(who);
            if (m_creature->IsWithinDistInMap(who, attackRadius) && m_creature->GetDistanceZ(who) <= CREATURE_Z_ATTACK_RANGE && m_creature->IsWithinLOSInMap(who))
            {
                if(who->HasStealthAura())
                    who->RemoveSpellsCausingAura(SPELL_AURA_MOD_STEALTH);

                DoStartMeleeAttack(who);
            }
        }
    }

    void BeginEvent(Player* pl)
    {
        if(!pInstance)
        {
            error_log("ERROR: Unable to begin Shade of Akama event- Instance Data not set properly");
            return;
        }
        
        ShadeGUID = pInstance->GetData64("ShadeOfAkama");
        if(!ShadeGUID)
        {
            error_log("ERROR: Unable to begin Shade of Akama event- ShadeGUID is invalid");
            return;
        }

        Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID));
        if(Shade)
        {
            pInstance->SetData("ShadeOfAkamaEvent", 1); // In Progress
            m_creature->SetUInt32Value(UNIT_NPC_FLAGS, 0); // Prevent players from trying to restart event
            ((boss_shade_of_akamaAI*)Shade->AI())->SetAkamaGUID(m_creature->GetGUID());
            ((boss_shade_of_akamaAI*)Shade->AI())->InCombat = true;
            ((boss_shade_of_akamaAI*)Shade->AI())->SetSelectableChannelers();
            if(pl)
                Shade->AddThreat(pl, 1.0f);

            outstring_log("DEBUG: Shade of Akama Event Begun. Shade GUID: %u, Akama GUID from Shade: %u, Akama GUID: %u. Player: %u, %c", ShadeGUID, ((boss_shade_of_akamaAI*)Shade->AI())->AkamaGUID, m_creature->GetGUID(), pl->GetGUID(), pl->GetName());
            // DoCast(Shade, SPELL_AKAMA_SOUL_EXPEL);
        }
    }

    void UpdateAI(const uint32 diff)
    {
        if(CastSoulRetrieveTimer < diff)
        {
            if(ShadeGUID)
            {
                Creature* Shade = ((Creature*)Unit::GetUnit((*m_creature), ShadeGUID));
                if(Shade && Shade->isAlive() && ((boss_shade_of_akamaAI*)Shade->AI())->IsBanished)
                    DoCast(Shade, SPELL_AKAMA_SOUL_RETRIEVE);
                CastSoulRetrieveTimer = 500;
            }
        }else CastSoulRetrieveTimer -= diff;

        if(ShadeHasDied)
        {
            // TODO: Cosmetics for shade's death
        }

        if(!m_creature->getVictim() || !m_creature->SelectHostilTarget())
            return;

        if(DestructivePoisonTimer < diff)
        {
            // SPELL_DESTRUCTIVE_POISON is self-cast only for some reason so we make our target cast it on itself
            m_creature->getVictim()->CastSpell(m_creature->getVictim(), SPELL_DESTRUCTIVE_POISON, true);
            DestructivePoisonTimer = 15000;
        }else DestructivePoisonTimer -= diff;

        if(LightningBoltTimer < diff)
        {
            DoCast(m_creature->getVictim(), SPELL_LIGHTNING_BOLT);
            LightningBoltTimer = 10000;
        }else LightningBoltTimer -= diff;

        if(!ShadeHasDied)
        {
            if(CheckTimer < diff)
            {
                if(ShadeGUID)
                {
                    Unit* Shade = Unit::GetUnit((*m_creature), ShadeGUID);
                    if(Shade && (!Shade->isAlive()))
                        ShadeHasDied = true;
                }
            }else CheckTimer -= diff;
        }

        DoMeleeAttackIfReady();
    }
};

CreatureAI* GetAI_boss_shade_of_akama(Creature *_Creature)
{
    return new boss_shade_of_akamaAI (_Creature);
}

CreatureAI* GetAI_mob_ashtongue_channeler(Creature *_Creature)
{
    return new mob_ashtongue_channelerAI (_Creature);
}

CreatureAI* GetAI_npc_akama_shade(Creature *_Creature)
{
    return new npc_akamaAI (_Creature);
}

bool GossipSelect_npc_akama(Player *player, Creature *_Creature, uint32 sender, uint32 action )
{
    if (action == GOSSIP_ACTION_INFO_DEF + 1)//Fight time
    {
        player->PlayerTalkClass->CloseGossip();
        ((npc_akamaAI*)_Creature->AI())->BeginEvent(player);
    }

    return true;
}

bool GossipHello_npc_akama(Player *player, Creature *_Creature)
{    
    if(player->isAlive())
    {
        player->ADD_GOSSIP_ITEM( 0, GOSSIP_ITEM, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1);
        player->PlayerTalkClass->SendGossipMenu(907, _Creature->GetGUID());
    }

    return true;
}

void AddSC_boss_shade_of_akama()
{
    Script *newscript;
    newscript = new Script;
    newscript->Name="boss_shade_of_akama";
    newscript->GetAI = GetAI_boss_shade_of_akama;
    m_scripts[nrscripts++] = newscript;

    newscript = new Script;
    newscript->Name="mob_ashtongue_channeler";
    newscript->GetAI = GetAI_mob_ashtongue_channeler;
    m_scripts[nrscripts++] = newscript;
       
    newscript = new Script;
    newscript->Name="npc_akama_shade";
    newscript->GetAI = GetAI_npc_akama_shade;
    newscript->pGossipHello = &GossipHello_npc_akama;
    newscript->pGossipSelect = &GossipSelect_npc_akama;
    m_scripts[nrscripts++] = newscript;
}