diff options
Diffstat (limited to 'src/server/scripts')
5 files changed, 756 insertions, 9 deletions
diff --git a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp new file mode 100644 index 00000000000..a863bc0f5a2 --- /dev/null +++ b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/boss_lockmaw.cpp @@ -0,0 +1,565 @@ +/* +* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information +* +* This program is free software; you can redistribute it and/or modify it +* under the terms of the GNU 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, see <http://www.gnu.org/licenses/>. +*/ + +#include "ScriptMgr.h" +#include "Containers.h" +#include "InstanceScript.h" +#include "lost_city_of_the_tolvir.h" +#include "MotionMaster.h" +#include "ObjectAccessor.h" +#include "ScriptedCreature.h" +#include "SpellScript.h" + +enum LockmawSpells +{ +    // Lockmaw +    SPELL_VISCOUS_POISON        = 81630, +    SPELL_SCENT_OF_BLOOD        = 81690, +    SPELL_VENOMOUS_RAGE         = 81706, +    SPELL_DUST_FLAIL            = 81652, +    SPELL_DUST_FLAIL_CHANNELED  = 81642, + +    // Add Stalker +    SPELL_SUMMON_CROCOLISK      = 84242, +    SPELL_AUGH_1                = 84809, // Casts Paralytic Blow Dart +    SPELL_AUGH_2                = 84808, // Casts Whirlwind + +    // Adds +    SPELL_STEALTHED             = 84244, + +    // Dust Flail +    SPELL_DUST_FLAIL_PERIODIC   = 81646, + +    // Augh (Add) +    SPELL_PARALYTIC_BLOW_DART   = 84799, +    SPELL_SMOKE_BOMB            = 84768, +    SPELL_RANDOM_AGGRO_TAUNT    = 50230, +    SPELL_WHIRLWIND             = 84784, + +    // Augh (Boss) +    SPELL_FRENZY                = 91415, +    SPELL_DRAGONS_BREATH        = 83776, +    SPELL_WHIRLWIND_BOSS        = 91408 +}; + +enum LockmawEvents +{ +    // Lockmaw +    EVENT_VISCOUS_POISON = 1, +    EVENT_SCENT_OF_BLOOD, +    EVENT_VENOMOUS_RAGE, +    EVENT_DUST_FLAIL, +    EVENT_CHANNEL_DUST_FLAIL, +    EVENT_TURN_AGGRESSIVE, +    EVENT_SUMMON_AUGH_ADD, + +    // Augh (Add) +    EVENT_MOVE_TOWARDS_BOSS, +    EVENT_HARASS_PLAYERS, +    EVENT_PARALYTIC_BLOW_DART, +    EVENT_SCREAM, +    EVENT_SMOKE_BOMB, +    EVENT_WHIRLWIND, +    EVENT_STEALTHED, +    EVENT_LEAVE_ARENA, + +    // Augh (Boss) +    EVENT_SAY_INTRO_2, +    EVENT_SET_EMOTE_STATE, +    EVENT_TURN_ATTACKABLE, +    EVENT_SAY_INTRO_3, +    EVENT_SAY_INTRO_4, +    EVENT_SAY_INTRO_5, +    EVENT_DRAGONS_BREATH +}; + +enum LockmawPhases +{ +    // Augh (Boss) +    PHASE_INTRO     = 1, +    PHASE_COMBAT    = 2 +}; + +enum LockmawTexts +{ +    // Augh (Adds) +    SAY_HARASS_PLAYER       = 0, +    SAY_SCREAM              = 1, + +    // Augh (Boss) +    SAY_ANNOUNCE_APPEARANCE = 0, +    SAY_INTRO_1             = 1, +    SAY_INTRO_2             = 2, +    SAY_INTRO_3             = 3, +    SAY_INTRO_4             = 4, +    SAY_INTRO_5             = 5 +}; + +enum class LockmawAughAddType : uint8 +{ +    ParalyticBlowDart   = 0, +    Whirlwind           = 1 +}; + +enum LockmawMovemPoints +{ +    POINT_PARALYTIC_BLOW_DART   = 0, +    POINT_AUGH_HOME_POSITION    = 0 +}; + +// Instead of using a fixate or taunt aura, the Crocolisks use a flat threat amount instead +static constexpr float SCENT_OF_BLOOD_THREAT_AMOUNT = 500000.f; +static Position const AughRespawnPosition = { -11062.5f, -1662.39f, 0.7606202f, 0.8028514f }; + +struct boss_lockmaw : public BossAI +{ +    boss_lockmaw(Creature* creature) : BossAI(creature, BOSS_LOCKMAW), _enraged(false), _aughAddType(LockmawAughAddType::ParalyticBlowDart) { } + +    void JustEngagedWith(Unit* who) override +    { +        BossAI::JustEngagedWith(who); +        instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); + +        events.ScheduleEvent(EVENT_VISCOUS_POISON, 10s); +        events.ScheduleEvent(EVENT_SCENT_OF_BLOOD, 5s); +        events.ScheduleEvent(EVENT_DUST_FLAIL, 18s); +        events.ScheduleEvent(EVENT_SUMMON_AUGH_ADD, 8s); +    } + +    void EnterEvadeMode(EvadeReason /*why*/) override +    { +        summons.DespawnAll(); +        instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); +        _EnterEvadeMode(); +        _DespawnAtEvade(); +    } + +    void JustDied(Unit* killer) override +    { +        BossAI::JustDied(killer); +        instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); +        instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_SCENT_OF_BLOOD); +        instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_VISCOUS_POISON); +    } + +    void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override +    { +        if (damage >= me->GetHealth()) +            return; + +        if (!_enraged && me->HealthBelowPctDamaged(30, damage)) +        { +            events.ScheduleEvent(EVENT_VENOMOUS_RAGE, 1ms); +            _enraged = true; +        } +    } + +    bool CanAIAttack(Unit const* victim) const override +    { +        // Patch 4.0.6 (2011-02-08): Lockmaw no longer tolerates fighting in his treasure room. +        return victim && victim->IsOutdoors(); +    } + +    void JustSummoned(Creature* summon) override +    { +        summons.Summon(summon); + +        switch (summon->GetEntry()) +        { +            case NPC_DUST_FLAIL_CASTER: +                summon->CastSpell(nullptr, SPELL_DUST_FLAIL_PERIODIC); +                break; +            default: +                break; +        } +    } + +    void UpdateAI(uint32 diff) override +    { +        if (!UpdateVictim()) +            return; + +        events.Update(diff); + +        if (me->HasUnitState(UNIT_STATE_CASTING)) +            return; + +        while (uint32 eventId = events.ExecuteEvent()) +        { +            switch (eventId) +            { +                case EVENT_VISCOUS_POISON: +                    if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 45.f, true, 0.f)) +                        DoCast(target, SPELL_VISCOUS_POISON); +                    else +                        DoCastVictim(SPELL_VISCOUS_POISON); + +                    events.Repeat(30s); +                    break; +                case EVENT_SCENT_OF_BLOOD: +                    // just a occasional cleanup of despawned summons to keep the container slim +                    summons.RemoveNotExisting(); +                    DoCastAOE(SPELL_SCENT_OF_BLOOD); +                    events.Repeat(30s); +                    break; +                case EVENT_VENOMOUS_RAGE: +                    DoCastSelf(SPELL_VENOMOUS_RAGE); +                    break; +                case EVENT_DUST_FLAIL: +                    me->AttackStop(); +                    me->SetReactState(REACT_PASSIVE); +                    DoCastSelf(SPELL_DUST_FLAIL); +                    events.ScheduleEvent(EVENT_CHANNEL_DUST_FLAIL, 1s); +                    events.Repeat(30s); +                    break; +                case EVENT_CHANNEL_DUST_FLAIL: +                    if (Creature const* dustFlail = instance->GetCreature(DATA_DUST_FLAIL)) +                    { +                        // because splines need one update tick to update the position, we have to set the orientation manually because we need it NOW. +                        me->SetOrientation(me->GetAbsoluteAngle(dustFlail)); +                        me->SetFacingToObject(dustFlail); +                        DoCastSelf(SPELL_DUST_FLAIL_CHANNELED); +                        events.ScheduleEvent(EVENT_TURN_AGGRESSIVE, 7s); +                    } +                    else +                        me->SetReactState(REACT_AGGRESSIVE); +                    break; +                case EVENT_TURN_AGGRESSIVE: +                    me->SetReactState(REACT_AGGRESSIVE); +                    break; +                case EVENT_SUMMON_AUGH_ADD: +                    instance->SetData(DATA_SHUFFLE_ADD_STALKERS, 0); +                    if (Creature* addStalker = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_ADD_STALKER_1))) +                    { +                        addStalker->CastSpell(nullptr, _aughAddType == LockmawAughAddType::ParalyticBlowDart ? SPELL_AUGH_1 : SPELL_AUGH_2); +                        events.Repeat(_aughAddType == LockmawAughAddType::ParalyticBlowDart ? 20s : 40s); +                        _aughAddType = _aughAddType == LockmawAughAddType::ParalyticBlowDart ? LockmawAughAddType::Whirlwind : LockmawAughAddType::ParalyticBlowDart; +                    } +                    break; +                default: +                    break; +            } +            if (me->HasUnitState(UNIT_STATE_CASTING)) +                return; +        } + +        DoMeleeAttackIfReady(); +    } +private: +    bool _enraged; +    LockmawAughAddType _aughAddType; +}; + +struct npc_lockmaw_frenzied_crocolisk : public ScriptedAI +{ +    npc_lockmaw_frenzied_crocolisk(Creature* creature) : ScriptedAI(creature) { } + +    void JustAppeared() override +    { +        DoZoneInCombat(); +        DoCastSelf(SPELL_STEALTHED); +        me->SetCorpseDelay(5); // Frenzied Crocolisks despawn 5 seconds after death + +        if (Unit* victim = SelectTarget(SelectTargetMethod::Random, 0, 500.f, true, true, SPELL_SCENT_OF_BLOOD)) +            AddThreat(victim, SCENT_OF_BLOOD_THREAT_AMOUNT); +    } +}; + +static constexpr float AUGH_DISTANCE_TO_BOSS = 20.f; + +struct npc_lockmaw_augh_add : public ScriptedAI +{ +    npc_lockmaw_augh_add(Creature* creature) : ScriptedAI(creature), _instance(nullptr) { } + +    void InitializeAI() override +    { +        _instance = me->GetInstanceScript(); + +        if (me->GetEntry() == NPC_AUGH_ADD_1) +            me->SetReactState(REACT_PASSIVE); +    } + +    void JustAppeared() override +    { +        if (me->GetEntry() == NPC_AUGH_ADD_1) +        { +            DoCastSelf(SPELL_STEALTHED); +            _events.ScheduleEvent(EVENT_STEALTHED, 1ms); +            _events.ScheduleEvent(EVENT_MOVE_TOWARDS_BOSS, 2s); +        } +        else +        { +            DoZoneInCombat(); +            _events.ScheduleEvent(EVENT_STEALTHED, 2s); +            _events.ScheduleEvent(EVENT_WHIRLWIND, 3s); +        } +    } + +    void MovementInform(uint32 motionType, uint32 pointId) override +    { +        if (motionType != POINT_MOTION_TYPE && pointId != POINT_PARALYTIC_BLOW_DART) +            return; + +        _events.ScheduleEvent(EVENT_HARASS_PLAYERS, 1ms); +        _events.ScheduleEvent(EVENT_PARALYTIC_BLOW_DART, 1s); +    } + +    void UpdateAI(uint32 diff) override +    { +        // Augh does perform events even if he is not engaged, so we do not return here +        UpdateVictim(); + +        _events.Update(diff); +        while (uint32 eventId = _events.ExecuteEvent()) +        { +            switch (eventId) +            { +                case EVENT_MOVE_TOWARDS_BOSS: +                { +                    DoZoneInCombat(); +                    Creature* lockmaw = _instance->GetCreature(BOSS_LOCKMAW); +                    if (!lockmaw) +                    { +                        // This should never happen at this stage, but just to be sure.... +                        me->DespawnOrUnsummon(); +                        return; +                    } +                    me->GetMotionMaster()->MovePoint(POINT_PARALYTIC_BLOW_DART, lockmaw->GetPosition(), true, std::nullopt, std::nullopt, MovementWalkRunSpeedSelectionMode::ForceWalk, AUGH_DISTANCE_TO_BOSS); +                    break; +                } +                case EVENT_HARASS_PLAYERS: +                    Talk(SAY_HARASS_PLAYER); +                    break; +                case EVENT_PARALYTIC_BLOW_DART: +                    DoCastAOE(SPELL_PARALYTIC_BLOW_DART, CastSpellExtraArgs().AddSpellMod(SPELLVALUE_MAX_TARGETS, 1)); +                    _events.ScheduleEvent(EVENT_SCREAM, 3s); +                    break; +                case EVENT_SCREAM: +                    Talk(SAY_SCREAM); +                    _events.ScheduleEvent(EVENT_SMOKE_BOMB, 1s); +                    break; +                case EVENT_SMOKE_BOMB: +                    DoCastAOE(SPELL_SMOKE_BOMB); +                    me->DespawnOrUnsummon(3s + 500ms); +                    break; +                case EVENT_WHIRLWIND: +                    me->SetReactState(REACT_AGGRESSIVE); +                    DoCastAOE(SPELL_RANDOM_AGGRO_TAUNT); +                    DoCastSelf(SPELL_WHIRLWIND); +                    _events.ScheduleEvent(EVENT_LEAVE_ARENA, 20s); +                    break; +                case EVENT_STEALTHED: +                    DoCastSelf(SPELL_STEALTHED); +                    break; +                case EVENT_LEAVE_ARENA: +                    me->AttackStop(); +                    me->SetReactState(REACT_PASSIVE); +                    if (Creature* addStalker = me->FindNearestCreature(NPC_ADD_STALKER, 200.f)) +                        me->GetMotionMaster()->MovePoint(0, addStalker->GetPosition()); +                    me->DespawnOrUnsummon(4s); +                    break; +                default: +                    break; +            } +        } +    } + +private: +    EventMap _events; +    InstanceScript* _instance; +}; + +struct npc_lockmaw_augh_boss : public ScriptedAI +{ +    npc_lockmaw_augh_boss(Creature* creature) : ScriptedAI(creature), _instance(nullptr) { } + +    void InitializeAI() override +    { +        _instance = me->GetInstanceScript(); +        if (_instance->GetData(DATA_HEROIC_AUGH_DESPAWNED)) +            me->Relocate(AughRespawnPosition); +        else +            me->SetUnitFlag(UNIT_FLAG_UNINTERACTIBLE); + +        me->SetReactState(REACT_PASSIVE); +    } + +    void JustAppeared() override +    { +        if (!_instance->GetData(DATA_HEROIC_AUGH_DESPAWNED)) +        { +            Talk(SAY_ANNOUNCE_APPEARANCE); +            me->GetMotionMaster()->MovePoint(POINT_AUGH_HOME_POSITION, AughRespawnPosition, true, std::nullopt, std::nullopt, MovementWalkRunSpeedSelectionMode::ForceWalk); +        } +    } + +    void JustEngagedWith(Unit* /*who*/) override +    { +        me->SetReactState(REACT_AGGRESSIVE); +        me->SetEmoteState(EMOTE_ONESHOT_NONE); +        DoCastSelf(SPELL_FRENZY); +        _events.SetPhase(PHASE_COMBAT); +        _events.ScheduleEvent(EVENT_PARALYTIC_BLOW_DART, 8s); +        _events.ScheduleEvent(EVENT_WHIRLWIND, 9s); +        _events.ScheduleEvent(EVENT_DRAGONS_BREATH, 6s); +    } + +    void EnterEvadeMode(EvadeReason /*why*/) override +    { +        _instance->SetData(DATA_HEROIC_AUGH_DESPAWNED, 1); +        me->DespawnOrUnsummon(0s, 30s); +    } + +    void MovementInform(uint32 motionType, uint32 pointId) override +    { +        if (motionType != POINT_MOTION_TYPE && pointId != POINT_AUGH_HOME_POSITION) +            return; + +        me->SetFacingTo(AughRespawnPosition.GetOrientation()); +        Talk(SAY_INTRO_1); + +        _events.SetPhase(PHASE_INTRO); +        _events.ScheduleEvent(EVENT_SAY_INTRO_2, 3s, 0, PHASE_INTRO); +    } + +    void UpdateAI(uint32 diff) override +    { +        if (!UpdateVictim() && !_events.IsInPhase(PHASE_INTRO)) +            return; + +        _events.Update(diff); + +        if (me->HasUnitState(UNIT_STATE_CASTING)) +            return; + +        while (uint32 eventId = _events.ExecuteEvent()) +        { +            switch (eventId) +            { +                case EVENT_SAY_INTRO_2: +                    Talk(SAY_INTRO_2); +                    _events.ScheduleEvent(EVENT_SET_EMOTE_STATE, 3s + 500ms, 0, PHASE_INTRO); +                    break; +                case EVENT_SET_EMOTE_STATE: +                    me->SetEmoteState(EMOTE_STATE_SPELL_CHANNEL_OMNI); +                    _events.ScheduleEvent(EVENT_TURN_ATTACKABLE, 3s + 500ms, 0, PHASE_INTRO); +                    break; +                case EVENT_TURN_ATTACKABLE: +                    me->RemoveUnitFlag(UNIT_FLAG_UNINTERACTIBLE); +                    _events.ScheduleEvent(EVENT_SAY_INTRO_3, 11s, 0, PHASE_INTRO); +                    break; +                case EVENT_SAY_INTRO_3: +                    Talk(SAY_INTRO_3); +                    _events.ScheduleEvent(EVENT_SAY_INTRO_4, 8s + 500ms, 0, PHASE_INTRO); +                    break; +                case EVENT_SAY_INTRO_4: +                    Talk(SAY_INTRO_4); +                    _events.ScheduleEvent(EVENT_SAY_INTRO_5, 8s + 500ms, 0, PHASE_INTRO); +                    break; +                case EVENT_SAY_INTRO_5: +                    Talk(SAY_INTRO_5); +                    _events.ScheduleEvent(EVENT_SAY_INTRO_3, 21s, 0, PHASE_INTRO); +                    break; +                case EVENT_PARALYTIC_BLOW_DART: +                    DoCastAOE(SPELL_PARALYTIC_BLOW_DART, CastSpellExtraArgs().AddSpellMod(SPELLVALUE_MAX_TARGETS, 1)); +                    _events.Repeat(10s, 15s); +                    break; +                case EVENT_WHIRLWIND: +                    DoCastAOE(SPELL_RANDOM_AGGRO_TAUNT); +                    DoCastSelf(SPELL_WHIRLWIND_BOSS); +                    _events.Repeat(29s); +                    break; +                case EVENT_DRAGONS_BREATH: +                    DoCastAOE(SPELL_DRAGONS_BREATH); +                    _events.Repeat(28s); +                    break; +                default: +                    break; +            } + +            if (me->HasUnitState(UNIT_STATE_CASTING)) +                return; +        } + +        DoMeleeAttackIfReady(); +    } + +private: +    EventMap _events; +    InstanceScript* _instance; +}; + +class spell_lockmaw_scent_of_blood : public SpellScript +{ +    bool Validate(SpellInfo const* /*spellInfo*/) override +    { +        return ValidateSpellInfo({ SPELL_SUMMON_CROCOLISK }); +    } + +    void SelectTarget(std::list<WorldObject*>& targets) +    { +        if (targets.size() <= 1) +            return; + +        std::list<WorldObject*> targetsCopy = targets; +        targetsCopy.remove_if([caster = GetCaster()](WorldObject const* target) +        { +            return caster->GetVictim() != target; +        }); + +        if (!targetsCopy.empty()) +        { +            Trinity::Containers::RandomResize(targetsCopy, 1); +            targets = targetsCopy; +        } +        else +            Trinity::Containers::RandomResize(targets, 1); +    } + +    void HandleCrocoliskSummoning() +    { +        Unit* caster = GetCaster(); +        if (!caster) +            return; + +        InstanceScript* instance = caster->GetInstanceScript(); +        if (!instance) +            return; + +        // Shuffle the stored add stalkers so that we will get four random add stalkers in the next step below +        instance->SetData(DATA_SHUFFLE_ADD_STALKERS, 0); + +        for (uint32 i = DATA_ADD_STALKER_1; i <= DATA_ADD_STALKER_4; ++i) +            if (Creature* addStalker = ObjectAccessor::GetCreature(*caster, instance->GetGuidData(i))) +                addStalker->CastSpell(nullptr, SPELL_SUMMON_CROCOLISK); +    } + +    void Register() override +    { +        OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_lockmaw_scent_of_blood::SelectTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); +        AfterCast += SpellCastFn(spell_lockmaw_scent_of_blood::HandleCrocoliskSummoning); +    } +}; + +void AddSC_boss_lockmaw() +{ +    RegisterLostCityOfTheTolvirAI(boss_lockmaw); +    RegisterLostCityOfTheTolvirAI(npc_lockmaw_frenzied_crocolisk); +    RegisterLostCityOfTheTolvirAI(npc_lockmaw_augh_add); +    RegisterLostCityOfTheTolvirAI(npc_lockmaw_augh_boss); +    RegisterSpellScript(spell_lockmaw_scent_of_blood); +} diff --git a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp index 7a01516ad20..88aba2b8666 100644 --- a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp +++ b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/instance_lost_city_of_the_tolvir.cpp @@ -16,27 +16,43 @@   */  #include "ScriptMgr.h" +#include "Containers.h" +#include "Creature.h" +#include "CreatureAI.h" +#include "EventMap.h"  #include "InstanceScript.h"  #include "lost_city_of_the_tolvir.h" +#include "Map.h"  static constexpr ObjectData const creatureData[] =  {      { NPC_GENERAL_HUSAM,        BOSS_GENERAL_HUSAM       }, -    { NPC_LOCKMAW,              DATA_LOCKMAW             }, -    { NPC_AUGH,                 DATA_AUGH                }, +    { NPC_LOCKMAW,              BOSS_LOCKMAW             },      { NPC_HIGH_PROPHET_BARIM,   BOSS_HIGH_PROPHET_BARIM  },      { NPC_SIAMAT,               BOSS_SIAMAT              }, +    { NPC_DUST_FLAIL,           DATA_DUST_FLAIL          },      { 0,                        0                        } // End  };  static constexpr DungeonEncounterData const encounters[] =  {      { BOSS_GENERAL_HUSAM,       {{ 1052 }} }, -    { BOSS_LOCKMAW_AND_AUGH,    {{ 1054 }} }, +    { BOSS_LOCKMAW,             {{ 1054 }} },      { BOSS_HIGH_PROPHET_BARIM,  {{ 1053 }} },      { BOSS_SIAMAT,              {{ 1055 }} }  }; +enum LCTEvents +{ +    EVENT_SPAWN_HEROIC_AUGH = 1 +}; + +enum LCTSpawnGroups +{ +    SPAWN_GROUP_ID_SIAMAT_WIND_TUNNEL   = 1036, +    SPAWN_GROUP_ID_AUGH_HEROIC          = 1037 +}; +  class instance_lost_city_of_the_tolvir : public InstanceMapScript  {  public: @@ -44,13 +60,136 @@ public:      struct instance_lost_city_of_the_tolvir_InstanceMapScript : public InstanceScript      { -        instance_lost_city_of_the_tolvir_InstanceMapScript(InstanceMap* map) : InstanceScript(map) +        instance_lost_city_of_the_tolvir_InstanceMapScript(InstanceMap* map) : InstanceScript(map), _heroicAughDespawned(false)          {              SetHeaders(DataHeader);              SetBossNumber(EncounterCount);              LoadDungeonEncounterData(encounters);              LoadObjectData(creatureData, nullptr);          } + +        void OnCreatureCreate(Creature* creature) override +        { +            InstanceScript::OnCreatureCreate(creature); + +            switch (creature->GetEntry()) +            { +                case NPC_ADD_STALKER: +                    _lockmawAddStalkerGUIDs.push_back(creature->GetGUID()); +                    break; +                case NPC_FRENZIED_CROCOLISK: +                case NPC_AUGH_ADD_1: +                case NPC_AUGH_ADD_2: +                    // Some adds are spawned by Add Stalkers so we have to register them via instance script for Lockmaw to clean up +                    if (Creature* lockmaw = GetCreature(BOSS_LOCKMAW)) +                        if (CreatureAI* ai = lockmaw->AI()) +                            ai->JustSummoned(creature); +                    break; +                default: +                    break; +            } +        } + +        bool SetBossState(uint32 id, EncounterState state) override +        { +            if (!InstanceScript::SetBossState(id, state)) +                return false; + +            switch (id) +            { +                case BOSS_LOCKMAW: +                    if (state == DONE && (instance->IsHeroic() || instance->IsTimewalking())) +                        _events.ScheduleEvent(EVENT_SPAWN_HEROIC_AUGH, 1ms); +                    break; +                default: +                    break; +            } + +            if (GetBossState(BOSS_GENERAL_HUSAM) == DONE && GetBossState(BOSS_LOCKMAW) == DONE && GetBossState(BOSS_HIGH_PROPHET_BARIM) == DONE) +                EnableSiamat(); + +            return true; +        } + +        void SetData(uint32 type, uint32 value) override +        { +            switch (type) +            { +                case DATA_SHUFFLE_ADD_STALKERS: +                    Trinity::Containers::RandomShuffle(_lockmawAddStalkerGUIDs); +                    break; +                case DATA_HEROIC_AUGH_DESPAWNED: +                    _heroicAughDespawned = value != 0 ? true : false; +                    break; +                default: +                    break; +            } +        } + +        uint32 GetData(uint32 type) const override +        { +            switch (type) +            { +                case DATA_HEROIC_AUGH_DESPAWNED: +                    return static_cast<uint8>(_heroicAughDespawned); +                default: +                    return 0; +            } + +            return 0; +        } + +        void Update(uint32 diff) override +        { +            InstanceScript::Update(diff); + +            _events.Update(diff); +            while (uint32 eventId = _events.ExecuteEvent()) +            { +                switch (eventId) +                { +                    case EVENT_SPAWN_HEROIC_AUGH: +                        instance->SpawnGroupSpawn(SPAWN_GROUP_ID_AUGH_HEROIC); +                        break; +                    default: +                        break; +                } +            } +        } + +        void AfterDataLoad() override +        { +            if (GetBossState(BOSS_GENERAL_HUSAM) == DONE && GetBossState(BOSS_LOCKMAW) == DONE && GetBossState(BOSS_HIGH_PROPHET_BARIM) == DONE) +                EnableSiamat(); +        } + +        ObjectGuid GetGuidData(uint32 type) const override +        { +            switch (type) +            { +                case DATA_ADD_STALKER_1: +                case DATA_ADD_STALKER_2: +                case DATA_ADD_STALKER_3: +                case DATA_ADD_STALKER_4: +                    if (_lockmawAddStalkerGUIDs.size() >= (type - DATA_ADD_STALKER_1 + 1)) +                        return _lockmawAddStalkerGUIDs[(type - DATA_ADD_STALKER_1)]; +                    else +                        return ObjectGuid::Empty; +                default: +                    return InstanceScript::GetGuidData(type); +            } + +            return InstanceScript::GetGuidData(type); +        } +    private: +        EventMap _events; +        std::vector<ObjectGuid> _lockmawAddStalkerGUIDs; +        bool _heroicAughDespawned; + +        void EnableSiamat() +        { +            instance->SpawnGroupSpawn(SPAWN_GROUP_ID_SIAMAT_WIND_TUNNEL); +        }      };      InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h index d397617ac29..b700defb391 100644 --- a/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h +++ b/src/server/scripts/Kalimdor/LostCityOfTheTolvir/lost_city_of_the_tolvir.h @@ -29,12 +29,17 @@ enum LCTData  {      // Encounters      BOSS_GENERAL_HUSAM      = 0, -    BOSS_LOCKMAW_AND_AUGH   = 1, +    BOSS_LOCKMAW            = 1,      BOSS_HIGH_PROPHET_BARIM = 2,      BOSS_SIAMAT             = 3, -    DATA_LOCKMAW, -    DATA_AUGH +    DATA_SHUFFLE_ADD_STALKERS, +    DATA_ADD_STALKER_1, +    DATA_ADD_STALKER_2, +    DATA_ADD_STALKER_3, +    DATA_ADD_STALKER_4, +    DATA_DUST_FLAIL, +    DATA_HEROIC_AUGH_DESPAWNED  };  enum LCTCreatureIds @@ -42,7 +47,6 @@ enum LCTCreatureIds      // Bosses      NPC_GENERAL_HUSAM               = 44577,      NPC_LOCKMAW                     = 43614, -    NPC_AUGH                        = 49045,      NPC_HIGH_PROPHET_BARIM          = 43612,      NPC_SIAMAT                      = 44819, @@ -52,7 +56,15 @@ enum LCTCreatureIds      NPC_SHOCKWAVE_STALKER           = 44711,      NPC_TOLVIR_LAND_MINE_TARGET     = 44840,      NPC_TOLVIR_LAND_MINE_VEHICLE    = 44798, -    NPC_TOLVIR_LAND_MINE_CASTER     = 44796 +    NPC_TOLVIR_LAND_MINE_CASTER     = 44796, + +    /*Lockmaw*/ +    NPC_ADD_STALKER                 = 45124, +    NPC_FRENZIED_CROCOLISK          = 43658, +    NPC_DUST_FLAIL                  = 43655, +    NPC_DUST_FLAIL_CASTER           = 43650, +    NPC_AUGH_ADD_1                  = 45379, +    NPC_AUGH_ADD_2                  = 45378,  };  template <class AI, class T> diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp index eb5b043da2f..277348eefc8 100644 --- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp +++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp @@ -97,6 +97,7 @@ void AddSC_npc_anubisath_sentinel();  void AddSC_instance_temple_of_ahnqiraj();  // The Lost City of the Tol'vir  void AddSC_boss_general_husam(); +void AddSC_boss_lockmaw();  void AddSC_instance_lost_city_of_the_tolvir();  // Wailing caverns  void AddSC_wailing_caverns(); @@ -221,6 +222,7 @@ void AddKalimdorScripts()      AddSC_instance_temple_of_ahnqiraj();      // The Lost City of the Tol'vir      AddSC_boss_general_husam(); +    AddSC_boss_lockmaw();      AddSC_instance_lost_city_of_the_tolvir();      // Wailing caverns      AddSC_wailing_caverns(); diff --git a/src/server/scripts/Spells/spell_generic.cpp b/src/server/scripts/Spells/spell_generic.cpp index fb16c4a9581..da6a028a78f 100644 --- a/src/server/scripts/Spells/spell_generic.cpp +++ b/src/server/scripts/Spells/spell_generic.cpp @@ -5330,6 +5330,34 @@ class spell_gen_major_healing_cooldown_modifier_aura : public AuraScript      }  }; +// 50230 - Random Aggro (Taunt) +class spell_gen_random_aggro_taunt : public SpellScript +{ +    bool Validate(SpellInfo const* spellInfo) override +    { +        return ValidateSpellEffect({ { spellInfo->Id, EFFECT_0 } }) && ValidateSpellInfo({ static_cast<uint32>(spellInfo->GetEffect(EFFECT_0).BasePoints) }); +    } + +    void SelectRandomTarget(std::list<WorldObject*>& targets) +    { +        if (targets.empty()) +            return; + +        Trinity::Containers::RandomResize(targets, 1); +    } + +    void HandleTauntEffect(SpellEffIndex effIndex) +    { +        GetHitUnit()->CastSpell(GetCaster(), static_cast<uint32>(GetSpellInfo()->GetEffect(effIndex).BasePoints), CastSpellExtraArgs(TRIGGERED_FULL_MASK)); +    } + +    void Register() override +    { +        OnObjectAreaTargetSelect += SpellObjectAreaTargetSelectFn(spell_gen_random_aggro_taunt::SelectRandomTarget, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); +        OnEffectHitTarget += SpellEffectFn(spell_gen_random_aggro_taunt::HandleTauntEffect, EFFECT_0, SPELL_EFFECT_SCRIPT_EFFECT); +    } +}; +  void AddSC_generic_spell_scripts()  {      RegisterSpellScript(spell_gen_absorb0_hitlimit1); @@ -5505,4 +5533,5 @@ void AddSC_generic_spell_scripts()      RegisterSpellScript(spell_gen_waiting_to_resurrect);      RegisterSpellScript(spell_gen_major_healing_cooldown_modifier);      RegisterSpellScript(spell_gen_major_healing_cooldown_modifier_aura); +    RegisterSpellScript(spell_gen_random_aggro_taunt);  }  | 
