diff options
Diffstat (limited to 'src')
-rw-r--r-- | src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_ymiron.cpp | 403 | ||||
-rw-r--r-- | src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h | 14 |
2 files changed, 192 insertions, 225 deletions
diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_ymiron.cpp b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_ymiron.cpp index 60b898e1786..ab70c51bec1 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_ymiron.cpp +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/boss_ymiron.cpp @@ -15,22 +15,16 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* Script Data Start -SDName: Boss ymiron -SDAuthor: LordVanMartin -SD%Complete: -SDComment: -SDCategory: -Script Data End */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "utgarde_pinnacle.h" #include "SpellInfo.h" +#include "SpellScript.h" enum Spells { SPELL_BANE = 48294, + SPELL_BANE_HIT = 59203, // Checked for King's Bane achievement. SPELL_DARK_SLASH = 48292, SPELL_FETID_ROT = 48291, SPELL_SCREAMS_OF_THE_DEAD = 51750, @@ -47,8 +41,7 @@ enum Spells SPELL_SPIRIT_FOUNT = 48380 }; -//not in db -enum Yells +enum Texts { SAY_AGGRO = 0, SAY_SLAY = 1, @@ -59,18 +52,27 @@ enum Yells SAY_SUMMON_TORGYN = 6 }; -enum Creatures +enum Events +{ + EVENT_BANE = 1, + EVENT_FETID_ROT, + EVENT_DARK_SLASH, + EVENT_ANCESTORS_VENGEANCE, + EVENT_RESUME_COMBAT, // Handles react state and schedules the next event after roleplay ends. + EVENT_BJORN_SPIRIT_FOUNT, + EVENT_HALDOR_SPIRIT_STRIKE, + EVENT_RANULF_SPIRIT_BURST, + EVENT_TORGYN_SUMMON_AVENGING_SPIRITS +}; + +enum EventGroups +{ + EVENT_GROUP_BASE_SPELLS = 1 +}; + +enum MovePoints { - NPC_BJORN = 27303, - NPC_BJORN_VISUAL = 27304, - NPC_HALDOR = 27307, - NPC_HALDOR_VISUAL = 27310, - NPC_RANULF = 27308, - NPC_RANULF_VISUAL = 27311, - NPC_TORGYN = 27309, - NPC_TORGYN_VISUAL = 27312, - NPC_SPIRIT_FOUNT = 27339, - NPC_AVENGING_SPIRIT = 27386 + POINT_BOAT }; struct ActiveBoatStruct @@ -78,14 +80,15 @@ struct ActiveBoatStruct uint32 npc; int32 say; float MoveX, MoveY, MoveZ, SpawnX, SpawnY, SpawnZ, SpawnO; + uint32 event; }; static ActiveBoatStruct ActiveBoat[4] = { - {NPC_BJORN_VISUAL, SAY_SUMMON_BJORN, 404.379f, -335.335f, 104.756f, 413.594f, -335.408f, 107.995f, 3.157f}, - {NPC_HALDOR_VISUAL, SAY_SUMMON_HALDOR, 380.813f, -335.069f, 104.756f, 369.994f, -334.771f, 107.995f, 6.232f}, - {NPC_RANULF_VISUAL, SAY_SUMMON_RANULF, 381.546f, -314.362f, 104.756f, 370.841f, -314.426f, 107.995f, 6.232f}, - {NPC_TORGYN_VISUAL, SAY_SUMMON_TORGYN, 404.310f, -314.761f, 104.756f, 413.992f, -314.703f, 107.995f, 3.157f} + {NPC_BJORN_VISUAL, SAY_SUMMON_BJORN, 404.379f, -335.335f, 104.756f, 413.594f, -335.408f, 107.995f, 3.157f, EVENT_BJORN_SPIRIT_FOUNT}, + {NPC_HALDOR_VISUAL, SAY_SUMMON_HALDOR, 380.813f, -335.069f, 104.756f, 369.994f, -334.771f, 107.995f, 6.232f, EVENT_HALDOR_SPIRIT_STRIKE}, + {NPC_RANULF_VISUAL, SAY_SUMMON_RANULF, 381.546f, -314.362f, 104.756f, 370.841f, -314.426f, 107.995f, 6.232f, EVENT_RANULF_SPIRIT_BURST}, + {NPC_TORGYN_VISUAL, SAY_SUMMON_TORGYN, 404.310f, -314.761f, 104.756f, 413.992f, -314.703f, 107.995f, 3.157f, EVENT_TORGYN_SUMMON_AVENGING_SPIRITS} }; enum Misc @@ -103,89 +106,48 @@ public: boss_ymironAI(Creature* creature) : BossAI(creature, DATA_KING_YMIRON) { Initialize(); + // This ensures a random sequence of ancestors. Not sure if the order should change on reset or not, reason why this is left out of Initialize(). for (int i = 0; i < 4; ++i) - m_uiActiveOrder[i] = i; + ActiveOrder[i] = i; for (int i = 0; i < 3; ++i) { int r = i + (rand32() % (4 - i)); - int temp = m_uiActiveOrder[i]; - m_uiActiveOrder[i] = m_uiActiveOrder[r]; - m_uiActiveOrder[r] = temp; + int temp = ActiveOrder[i]; + ActiveOrder[i] = ActiveOrder[r]; + ActiveOrder[r] = temp; } } void Initialize() { - m_bIsWalking = false; - m_bIsPause = false; - m_bIsActiveWithBJORN = false; - m_bIsActiveWithHALDOR = false; - m_bIsActiveWithRANULF = false; - m_bIsActiveWithTORGYN = false; kingsBane = true; - - m_uiFetidRot_Timer = urand(8000, 13000); - m_uiBane_Timer = urand(18000, 23000); - m_uiDarkSlash_Timer = urand(28000, 33000); - m_uiAncestors_Vengeance_Timer = DUNGEON_MODE(60000, 45000); - m_uiPause_Timer = 0; - - m_uiAbility_BJORN_Timer = 0; - m_uiAbility_HALDOR_Timer = 0; - m_uiAbility_RANULF_Timer = 0; - m_uiAbility_TORGYN_Timer = 0; - - m_uiActivedNumber = 0; - m_uiHealthAmountModifier = 1; - m_uiHealthAmountMultipler = DUNGEON_MODE(20, 25); - - m_uiActivedCreatureGUID = 0; - m_uiOrbGUID = 0; + ActivedNumber = 0; + HealthAmountModifier = 1; + HealthAmountMultipler = DUNGEON_MODE(20, 25); + ActiveAncestorGUID = 0; + SpiritFountGUID = 0; } - bool m_bIsWalking; - bool m_bIsPause; - bool m_bIsActiveWithBJORN; - bool m_bIsActiveWithHALDOR; - bool m_bIsActiveWithRANULF; - bool m_bIsActiveWithTORGYN; - bool kingsBane; // Achievement King's Bane - - uint8 m_uiActiveOrder[4]; - uint8 m_uiActivedNumber; - - uint32 m_uiFetidRot_Timer; - uint32 m_uiBane_Timer; - uint32 m_uiDarkSlash_Timer; - uint32 m_uiAncestors_Vengeance_Timer; - - uint32 m_uiAbility_BJORN_Timer; - uint32 m_uiAbility_HALDOR_Timer; - uint32 m_uiAbility_RANULF_Timer; - uint32 m_uiAbility_TORGYN_Timer; - - uint32 m_uiPause_Timer; - uint32 m_uiHealthAmountModifier; - uint32 m_uiHealthAmountMultipler; - - uint64 m_uiActivedCreatureGUID; - uint64 m_uiOrbGUID; - void Reset() override { _Reset(); Initialize(); + me->SetReactState(REACT_AGGRESSIVE); } void EnterCombat(Unit* /*who*/) override { _EnterCombat(); Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_BANE, urand(18000, 23000), EVENT_GROUP_BASE_SPELLS); + events.ScheduleEvent(EVENT_FETID_ROT, urand(8000, 13000), EVENT_GROUP_BASE_SPELLS); + events.ScheduleEvent(EVENT_DARK_SLASH, urand(28000, 33000), EVENT_GROUP_BASE_SPELLS); + events.ScheduleEvent(EVENT_ANCESTORS_VENGEANCE, DUNGEON_MODE(60000, 45000), EVENT_GROUP_BASE_SPELLS); } void SpellHitTarget(Unit* who, SpellInfo const* spell) override { - if (who && who->GetTypeId() == TYPEID_PLAYER && spell->Id == 59302) + if (who && who->GetTypeId() == TYPEID_PLAYER && spell->Id == SPELL_BANE_HIT) kingsBane = false; } @@ -197,161 +159,115 @@ public: return 0; } - void UpdateAI(uint32 diff) override + void MovementInform(uint32 type, uint32 pointId) override { - if (m_bIsWalking) + if (type != POINT_MOTION_TYPE) + return; + + if (pointId == POINT_BOAT) // Check might not be needed. { - if (m_uiPause_Timer <= diff) + Talk(ActiveBoat[ActiveOrder[ActivedNumber]].say); + if (Creature* ancestor = me->SummonCreature(ActiveBoat[ActiveOrder[ActivedNumber]].npc, ActiveBoat[ActiveOrder[ActivedNumber]].SpawnX, ActiveBoat[ActiveOrder[ActivedNumber]].SpawnY, ActiveBoat[ActiveOrder[ActivedNumber]].SpawnZ, ActiveBoat[ActiveOrder[ActivedNumber]].SpawnO, TEMPSUMMON_CORPSE_DESPAWN, 0)) { - Talk(ActiveBoat[m_uiActiveOrder[m_uiActivedNumber]].say); - DoCast(me, SPELL_CHANNEL_YMIRON_TO_SPIRIT); // should be on spirit - if (Creature* temp = me->SummonCreature(ActiveBoat[m_uiActiveOrder[m_uiActivedNumber]].npc, ActiveBoat[m_uiActiveOrder[m_uiActivedNumber]].SpawnX, ActiveBoat[m_uiActiveOrder[m_uiActivedNumber]].SpawnY, ActiveBoat[m_uiActiveOrder[m_uiActivedNumber]].SpawnZ, ActiveBoat[m_uiActiveOrder[m_uiActivedNumber]].SpawnO, TEMPSUMMON_CORPSE_DESPAWN, 0)) - { - m_uiActivedCreatureGUID = temp->GetGUID(); - temp->CastSpell(me, SPELL_CHANNEL_SPIRIT_TO_YMIRON, true); - temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - temp->SetDisableGravity(true); - switch (m_uiActiveOrder[m_uiActivedNumber]) - { - case 0: m_bIsActiveWithBJORN = true; break; - case 1: m_bIsActiveWithHALDOR = true; break; - case 2: m_bIsActiveWithRANULF = true; break; - case 3: m_bIsActiveWithTORGYN = true; break; - } - } - - m_bIsPause = true; - m_bIsWalking = false; - m_uiPause_Timer = 3000; - } else m_uiPause_Timer -= diff; - return; + DoCast(ancestor, SPELL_CHANNEL_YMIRON_TO_SPIRIT); + ancestor->CastSpell(me, SPELL_CHANNEL_SPIRIT_TO_YMIRON, true); + ancestor->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); + ancestor->SetDisableGravity(true); + ActiveAncestorGUID = ancestor->GetGUID(); + } + events.ScheduleEvent(EVENT_RESUME_COMBAT, 5000); } - else if (m_bIsPause) + } + + void JustSummoned(Creature* summon) override + { + switch (summon->GetEntry()) { - if (m_uiPause_Timer <= diff) - { - m_uiAbility_BJORN_Timer = 5000; - m_uiAbility_HALDOR_Timer = 5000; - m_uiAbility_RANULF_Timer = 5000; - m_uiAbility_TORGYN_Timer = 5000; - - m_bIsPause = false; - m_uiPause_Timer = 0; - } else m_uiPause_Timer -= diff; - return; + case NPC_SPIRIT_FOUNT: + summon->CastSpell(summon, SPELL_SPIRIT_FOUNT, true); + summon->SetDisplayId(11686); + SpiritFountGUID = summon->GetGUID(); + break; + case NPC_AVENGING_SPIRIT: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + { + summon->AddThreat(target, 0.0f); + summon->AI()->AttackStart(target); + } + break; + default: + break; } - //Return since we have no target - if (!UpdateVictim()) - return; + summons.Summon(summon); + } - if (!m_bIsPause) + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPctDamaged(100 - HealthAmountMultipler * HealthAmountModifier, damage)) { - // Normal spells ------------------------------------------------------------------------ - if (m_uiBane_Timer <= diff) - { - DoCast(me, SPELL_BANE); - m_uiBane_Timer = urand(20000, 25000); - } else m_uiBane_Timer -= diff; + uint8 Order = HealthAmountModifier - 1; + ++HealthAmountModifier; - if (m_uiFetidRot_Timer <= diff) - { - DoCastVictim(SPELL_FETID_ROT); - m_uiFetidRot_Timer = urand(10000, 15000); - } else m_uiFetidRot_Timer -= diff; + me->InterruptNonMeleeSpells(true); + DoCast(me, SPELL_SCREAMS_OF_THE_DEAD); - if (m_uiDarkSlash_Timer <= diff) - { - DoCastVictim(SPELL_DARK_SLASH); - m_uiDarkSlash_Timer = urand(30000, 35000); - } else m_uiDarkSlash_Timer -= diff; + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MovePoint(POINT_BOAT, ActiveBoat[ActiveOrder[Order]].MoveX, ActiveBoat[ActiveOrder[Order]].MoveY, ActiveBoat[ActiveOrder[Order]].MoveZ); - if (m_uiAncestors_Vengeance_Timer <= diff) - { - DoCast(me, SPELL_ANCESTORS_VENGEANCE); - m_uiAncestors_Vengeance_Timer = DUNGEON_MODE(urand(60000, 65000), urand(45000, 50000)); - } else m_uiAncestors_Vengeance_Timer -= diff; + DespawnBoatGhosts(ActiveAncestorGUID); + DespawnBoatGhosts(SpiritFountGUID); - // Abilities ------------------------------------------------------------------------------ - if (m_bIsActiveWithBJORN && m_uiAbility_BJORN_Timer <= diff) - { - //DoCast(me, SPELL_SUMMON_SPIRIT_FOUNT); // works fine, but using summon has better control - if (Creature* temp = me->SummonCreature(NPC_SPIRIT_FOUNT, 385.0f + rand32() % 10, -330.0f + rand32() % 10, 104.756f, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 180000)) - { - temp->SetSpeed(MOVE_RUN, 0.4f); - temp->CastSpell(temp, SPELL_SPIRIT_FOUNT, true); - temp->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE); - temp->SetDisplayId(11686); - m_uiOrbGUID = temp->GetGUID(); - } - m_bIsActiveWithBJORN = false; // only one orb - } else m_uiAbility_BJORN_Timer -= diff; + events.CancelEvent(ActiveBoat[ActiveOrder[ActivedNumber]].event); // Cancels the event started on the previous transition. + events.DelayEvents(10000, EVENT_GROUP_BASE_SPELLS); - if (m_bIsActiveWithHALDOR && m_uiAbility_HALDOR_Timer <= diff) - { - DoCastVictim(SPELL_SPIRIT_STRIKE); - m_uiAbility_HALDOR_Timer = 5000; // overtime - } else m_uiAbility_HALDOR_Timer -= diff; + ActivedNumber = Order; + } + } - if (m_bIsActiveWithRANULF && m_uiAbility_RANULF_Timer <= diff) - { + void ExecuteEvent(uint32 eventId) override + { + switch (eventId) + { + case EVENT_BANE: + DoCast(SPELL_BANE); + events.ScheduleEvent(EVENT_BANE, urand(20000, 25000)); + break; + case EVENT_FETID_ROT: + DoCastVictim(SPELL_FETID_ROT); + events.ScheduleEvent(EVENT_FETID_ROT, urand(10000, 15000)); + break; + case EVENT_DARK_SLASH: + DoCastVictim(SPELL_DARK_SLASH); + events.ScheduleEvent(EVENT_DARK_SLASH, urand(30000, 35000)); + break; + case EVENT_ANCESTORS_VENGEANCE: + DoCast(me, SPELL_ANCESTORS_VENGEANCE); + events.ScheduleEvent(EVENT_ANCESTORS_VENGEANCE, DUNGEON_MODE(urand(60000, 65000), urand(45000, 50000))); + break; + case EVENT_RESUME_COMBAT: + me->SetReactState(REACT_AGGRESSIVE); + events.ScheduleEvent(ActiveBoat[ActiveOrder[ActivedNumber]].event, 5000); + break; + case EVENT_BJORN_SPIRIT_FOUNT: + DoCast(SPELL_SUMMON_SPIRIT_FOUNT); + break; + case EVENT_HALDOR_SPIRIT_STRIKE: + DoCastVictim(SPELL_SPIRIT_STRIKE); + events.ScheduleEvent(EVENT_HALDOR_SPIRIT_STRIKE, 5000); + break; + case EVENT_RANULF_SPIRIT_BURST: DoCast(me, SPELL_SPIRIT_BURST); - m_uiAbility_RANULF_Timer = 10000; // overtime - } else m_uiAbility_RANULF_Timer -= diff; - - if (m_bIsActiveWithTORGYN && m_uiAbility_TORGYN_Timer <= diff) - { - float x, y, z; - x = me->GetPositionX()-5; - y = me->GetPositionY()-5; - z = me->GetPositionZ(); + events.ScheduleEvent(EVENT_RANULF_SPIRIT_BURST, 10000); + break; + case EVENT_TORGYN_SUMMON_AVENGING_SPIRITS: for (uint8 i = 0; i < 4; ++i) - { - //DoCast(me, SPELL_SUMMON_AVENGING_SPIRIT); // works fine, but using summon has better control - if (Creature* temp = me->SummonCreature(NPC_AVENGING_SPIRIT, x + rand32() % 10, y + rand32() % 10, z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30000)) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - { - temp->AddThreat(target, 0.0f); - temp->AI()->AttackStart(target); - } - } - } - m_uiAbility_TORGYN_Timer = 15000; // overtime - } else m_uiAbility_TORGYN_Timer -= diff; - - // Health check ----------------------------------------------------------------------------- - if (me->HealthBelowPct(100 - m_uiHealthAmountMultipler * m_uiHealthAmountModifier)) - { - uint8 m_uiOrder = m_uiHealthAmountModifier - 1; - ++m_uiHealthAmountModifier; - - me->InterruptNonMeleeSpells(true); - DoCast(me, SPELL_SCREAMS_OF_THE_DEAD); - me->GetMotionMaster()->Clear(); - me->StopMoving(); - me->AttackStop(); - me->GetMotionMaster()->MovePoint(0, ActiveBoat[m_uiActiveOrder[m_uiOrder]].MoveX, ActiveBoat[m_uiActiveOrder[m_uiOrder]].MoveY, ActiveBoat[m_uiActiveOrder[m_uiOrder]].MoveZ); - - DespawnBoatGhosts(m_uiActivedCreatureGUID); - DespawnBoatGhosts(m_uiOrbGUID); - - m_bIsActiveWithBJORN = false; - m_bIsActiveWithHALDOR = false; - m_bIsActiveWithRANULF = false; - m_bIsActiveWithTORGYN = false; - - m_uiBane_Timer += 8000; - m_uiFetidRot_Timer += 8000; - m_uiDarkSlash_Timer += 8000; - m_uiAncestors_Vengeance_Timer += 8000; - - m_uiActivedNumber = m_uiOrder; - m_bIsWalking = true; - m_uiPause_Timer = 2000; - return; - } - DoMeleeAttackIfReady(); + DoCast(SPELL_SUMMON_AVENGING_SPIRIT); + events.ScheduleEvent(EVENT_TORGYN_SUMMON_AVENGING_SPIRITS, 15000); + break; + default: + break; } } @@ -367,14 +283,24 @@ public: Talk(SAY_SLAY); } - void DespawnBoatGhosts(uint64& m_uiCreatureGUID) + void DespawnBoatGhosts(uint64& CreatureGUID) { - if (m_uiCreatureGUID) - if (Creature* temp = ObjectAccessor::GetCreature(*me, m_uiCreatureGUID)) + // @todo: fire visual after ancestor despawns. + if (CreatureGUID) + if (Creature* temp = ObjectAccessor::GetCreature(*me, CreatureGUID)) temp->DisappearAndDie(); - m_uiCreatureGUID = 0; + CreatureGUID = 0; } + + private: + bool kingsBane; // Achievement King's Bane + uint8 ActiveOrder[4]; + uint8 ActivedNumber; + uint32 HealthAmountModifier; + uint32 HealthAmountMultipler; + uint64 ActiveAncestorGUID; + uint64 SpiritFountGUID; }; CreatureAI* GetAI(Creature* creature) const override @@ -383,6 +309,34 @@ public: } }; +// 48292 - Dark Slash +class spell_ymiron_dark_slash : public SpellScriptLoader +{ + public: + spell_ymiron_dark_slash() : SpellScriptLoader("spell_ymiron_dark_slash") { } + + class spell_ymiron_dark_slash_SpellScript : public SpellScript + { + PrepareSpellScript(spell_ymiron_dark_slash_SpellScript); + + void HandleDamageCalc(SpellEffIndex /*effIndex*/) + { + if (Unit* target = GetHitUnit()) + SetHitDamage(target->GetHealth() / 2); + } + + void Register() override + { + OnEffectHitTarget += SpellEffectFn(spell_ymiron_dark_slash_SpellScript::HandleDamageCalc, EFFECT_0, SPELL_EFFECT_SCHOOL_DAMAGE); + } + }; + + SpellScript* GetSpellScript() const override + { + return new spell_ymiron_dark_slash_SpellScript(); + } +}; + class achievement_kings_bane : public AchievementCriteriaScript { public: @@ -404,5 +358,6 @@ class achievement_kings_bane : public AchievementCriteriaScript void AddSC_boss_ymiron() { new boss_ymiron(); + new spell_ymiron_dark_slash(); new achievement_kings_bane(); } diff --git a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h index cccfcede80b..5373446ba02 100644 --- a/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h +++ b/src/server/scripts/Northrend/UtgardeKeep/UtgardePinnacle/utgarde_pinnacle.h @@ -59,7 +59,19 @@ enum CreatureIds NPC_RAVENOUS_FURBOLG = 26684, NPC_MASSIVE_JORMUNGAR = 26685, NPC_FEROCIOUS_RHINO = 26686, - NPC_PALEHOOF_ORB = 26688 + NPC_PALEHOOF_ORB = 26688, + + // Ymiron + NPC_BJORN = 27303, + NPC_BJORN_VISUAL = 27304, + NPC_HALDOR = 27307, + NPC_HALDOR_VISUAL = 27310, + NPC_RANULF = 27308, + NPC_RANULF_VISUAL = 27311, + NPC_TORGYN = 27309, + NPC_TORGYN_VISUAL = 27312, + NPC_SPIRIT_FOUNT = 27339, + NPC_AVENGING_SPIRIT = 27386 }; enum GameObjectIds |