diff options
6 files changed, 421 insertions, 400 deletions
diff --git a/sql/updates/world/master/2018_02_11_05_world_2016_11_26_00_world.sql b/sql/updates/world/master/2018_02_11_05_world_2016_11_26_00_world.sql index 67c687b1034..36049ecccd2 100644 --- a/sql/updates/world/master/2018_02_11_05_world_2016_11_26_00_world.sql +++ b/sql/updates/world/master/2018_02_11_05_world_2016_11_26_00_world.sql @@ -7,9 +7,9 @@ DELETE FROM `spell_script_names` WHERE `ScriptName`= 'spell_najentus_needle_spin INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES (39992,'spell_najentus_needle_spine'); -DELETE FROM `creature_text` WHERE `entry`=22887 AND (`groupid` IN(5,6) OR (`groupid`=4 AND `id`=1)); -INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +DELETE FROM `creature_text` WHERE `CreatureID`=22887 AND (`GroupID` IN(5,6) OR (`GroupID`=4 AND `ID`=1)); +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES (22887,4,1,'My patience has run out! Die! Die!',14,0,100,0,0,11458,21096,0,"High Warlord Naj'entus SAY_ENRAGE2"), (22887,5,0,'Lord Illidan will... crush you!',14,0,100,0,0,11459,21093,0,"High Warlord Naj'entus SAY_DEATH"); -UPDATE `creature_text` SET `text`='Stick around...',`BroadcastTextId`=21089 WHERE `entry`=22887 AND `groupid`=1 AND `id`=0; +UPDATE `creature_text` SET `Text`='Stick around...',`BroadcastTextId`=21089 WHERE `CreatureID`=22887 AND `GroupID`=1 AND `ID`=0; diff --git a/sql/updates/world/master/2018_02_11_07_world_2016_11_26_02_world.sql b/sql/updates/world/master/2018_02_11_07_world_2016_11_26_02_world.sql index b9cdadf6bf7..db6f5470a91 100644 --- a/sql/updates/world/master/2018_02_11_07_world_2016_11_26_02_world.sql +++ b/sql/updates/world/master/2018_02_11_07_world_2016_11_26_02_world.sql @@ -17,7 +17,7 @@ INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry (13,3,40603,0,0,31,0,3,22948,0,0,0,0,'','Effect_0 and Effect_1 - Hits Gurtogg Bloodboil'); -- Fix Texts -UPDATE `creature_text` SET `text`='I hunger.',`BroadcastTextId`=21744 WHERE `entry`=22948 AND `groupid`=2 AND `id`=1; -DELETE FROM `creature_text` WHERE `entry`=22948 AND `groupid`IN(3,4); -INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +UPDATE `creature_text` SET `Text`='I hunger.',`BroadcastTextId`=21744 WHERE `CreatureID`=22948 AND `GroupID`=2 AND `ID`=1; +DELETE FROM `creature_text` WHERE `CreatureID`=22948 AND `GroupID`IN(3,4); +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES (22948,3,0,'I\'ll rip the meat from your bones!',14,0,100,0,0,11438,21745,0,'bloodboil SAY_ENRAGE'); diff --git a/sql/updates/world/master/2018_02_11_08_world_2016_11_26_03_world.sql b/sql/updates/world/master/2018_02_11_08_world_2016_11_26_03_world.sql index 36882e654c8..e6af3773be1 100644 --- a/sql/updates/world/master/2018_02_11_08_world_2016_11_26_03_world.sql +++ b/sql/updates/world/master/2018_02_11_08_world_2016_11_26_03_world.sql @@ -1,4 +1,4 @@ -DELETE FROM `creature_text` WHERE entry=22984; -INSERT INTO `creature_text` (`entry`,`groupid`,`id`,`text`,`type`,`language`,`probability`,`emote`,`duration`,`sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +DELETE FROM `creature_text` WHERE CreatureID=22984; +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES (22984, 0, 0, 'You hear a loud rumble of metal grinding on stone...', 16, 0, 100, 0, 0, 0, 18600, 2, "Black Temple Trigger - when High Warlord Naj'entus dies - Emote"), (22984, 1, 0, 'The door to The Den of Mortal Delights has opened.', 16, 0, 100, 0, 0, 0, 21494, 2, 'Black Temple Trigger - when Lower Temple Defeated - Emote'); diff --git a/sql/updates/world/master/2018_02_11_09_world_2016_11_26_04_world.sql b/sql/updates/world/master/2018_02_11_09_world_2016_11_26_04_world.sql new file mode 100644 index 00000000000..2ab7c3baff3 --- /dev/null +++ b/sql/updates/world/master/2018_02_11_09_world_2016_11_26_04_world.sql @@ -0,0 +1,39 @@ +-- Add Flying InhabitType for Doom Blossom. +UPDATE `creature_template` SET `InhabitType`=`InhabitType`|4 WHERE `entry`= 23123; +-- Add missing spells to Ghost (Vengeful Spirit) +UPDATE `creature_template` SET `spell1`=40325, `spell3`=40157, `spell4`=40175, `spell5`=40314, `spell7`=40322 WHERE `entry`=23109; +-- Add missing auras in Shadowy Construct +DELETE FROM `creature_template_addon` WHERE `entry`=23111; +INSERT INTO `creature_template_addon` (`entry`, `path_id`, `mount`, `bytes1`, `bytes2`, `emote`, `auras`) VALUES +(23111, 0, 0, 0, 0, 0, '40326 40334'); + +-- Teron Gorefiend texts +DELETE FROM `creature_text` where `CreatureID`= 22871; +INSERT INTO `creature_text` (`CreatureID`,`GroupID`,`ID`,`Text`,`Type`,`Language`,`Probability`,`Emote`,`Duration`,`Sound`,`BroadcastTextId`,`TextRange`,`comment`) VALUES +(22871, 0, 0, 'I was the first you know. For me the wheel of death has spun many times. So much time has passed... I have a lot of catching up to do.' , 14, 0, 100, 0, 0, 11512, 21098, 0, 'Teron Gorefiend SAY_INTRO'), +(22871, 1, 0, 'Vengeance is mine!', 14, 0, 100, 0, 0, 11513, 21097, 0, 'Teron Gorefiend SAY_AGGRO'), +(22871, 2, 0, 'I have use for you...', 14, 0, 100, 0, 0, 11514, 21099, 0, 'Teron Gorefiend SAY_SLAY1'), +(22871, 2, 1, 'It gets worse.', 14, 0, 100, 0, 0, 11515, 21100, 0, 'Teron Gorefiend SAY_SLAY2'), +(22871, 3, 0, 'What are you afraid of?', 14, 0, 100, 0, 0, 11517, 21102, 0, 'Teron Gorefiend SAY_INCINERATE1'), +(22871, 3, 1, 'You will show the proper respect!', 14, 0, 100, 0, 0, 11520, 21105, 0, 'Teron Gorefiend SAY_INCINERATE2'), +(22871, 4, 0, "Death really isn't so bad.", 14, 0, 100, 0, 0, 11516, 21101, 0, 'Teron Gorefiend SAY_DOOM_BLOSSOM1'), +(22871, 4, 1, 'I have something for you...', 14, 0, 100, 0, 0, 11519, 21104, 0, 'Teron Gorefiend SAY_DOOM_BLOSSOM2'), +(22871, 5, 0, 'Give in.', 14, 0, 100, 0, 0, 11518, 21103, 0, 'Teron Gorefiend SPELL_CRUSHING_SHADOWS'), +(22871, 6, 0, 'The wheel... spins... again.', 14, 0, 100, 0, 0, 11521, 21106, 0, 'Teron Gorefiend SAY_DEATH'); + +-- Area Trigger +DELETE FROM `areatrigger_scripts` WHERE `entry`=4665; +INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES +(4665,'at_teron_gorefiend_entrance'); + +-- Spell scripts +DELETE FROM `spell_script_names` WHERE `ScriptName` IN('spell_teron_gorefiend_spiritual_vengeance','spell_teron_gorefiend_shadow_of_death','spell_teron_gorefiend_shadow_of_death_remove'); +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(40268,'spell_teron_gorefiend_spiritual_vengeance'), +(40251,'spell_teron_gorefiend_shadow_of_death'), +(41999,'spell_teron_gorefiend_shadow_of_death_remove'); + +-- Conditions +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId`=13 AND `SourceEntry`=40268; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13,3,40268,0,0,31,0,3,23109,0,0,0,0,'','Effect_0 and Effect_1 hits Vengeful Spirit'); diff --git a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp index 02be44b5782..35f3cc54ed6 100644 --- a/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp +++ b/src/server/scripts/Outland/BlackTemple/boss_teron_gorefiend.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2018 TrinityCore <https://www.trinitycore.org/> - * Copyright (C) 2006-2009 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 @@ -16,519 +15,502 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Teron_Gorefiend -SD%Complete: 60 -SDComment: Requires Mind Control support for Ghosts. -SDCategory: Black Temple -EndScriptData */ - #include "ScriptMgr.h" #include "black_temple.h" #include "InstanceScript.h" #include "MotionMaster.h" #include "ObjectAccessor.h" +#include "PassiveAI.h" #include "ScriptedCreature.h" +#include "SpellScript.h" +#include "SpellAuraEffects.h" #include "TemporarySummon.h" -enum DoomBlossom +enum Says { - //Speech'n'sound - SAY_INTRO = 0, - SAY_AGGRO = 1, - SAY_SLAY = 2, - SAY_SPELL = 3, - SAY_SPECIAL = 4, - SAY_ENRAGE = 5, - SAY_DEATH = 6, - - //Spells - SPELL_INCINERATE = 40239, - SPELL_CRUSHING_SHADOWS = 40243, - SPELL_SHADOWBOLT = 40185, - SPELL_PASSIVE_SHADOWFORM = 40326, - SPELL_SHADOW_OF_DEATH = 40251, - SPELL_BERSERK = 45078, - SPELL_ATROPHY = 40327, // Shadowy Constructs use this when they get within melee range of a player - - CREATURE_DOOM_BLOSSOM = 23123, - CREATURE_SHADOWY_CONSTRUCT = 23111 + SAY_INTRO = 0, + SAY_AGGRO = 1, + SAY_SLAY = 2, + SAY_INCINERATE = 3, + SAY_BLOSSOM = 4, + SAY_CRUSHING = 5, + SAY_DEATH = 6 }; -class npc_doom_blossom : public CreatureScript +enum Spells { -public: - npc_doom_blossom() : CreatureScript("npc_doom_blossom") { } + //Teron + SPELL_INCINERATE = 40239, + SPELL_CRUSHING_SHADOWS = 40243, + SPELL_SHADOW_OF_DEATH = 40251, + SPELL_SHADOW_OF_DEATH_REMOVE = 41999, + SPELL_BERSERK = 45078, + SPELL_SUMMON_DOOM_BLOSSOM = 40188, + + //Doom Blossom + SPELL_SUMMON_BLOSSOM_MOVE_TARGET = 40186, + SPELL_SHADOWBOLT = 40185, + + //Shadow Construct + SPELL_ATROPHY = 40327, + + //Player + SPELL_SUMMON_SPIRIT = 40266, + SPELL_SPIRITUAL_VENGEANCE = 40268, + SPELL_POSSESS_SPIRIT_IMMUNE = 40282, + SPELL_SUMMON_SKELETRON_1 = 40270, + SPELL_SUMMON_SKELETRON_2 = 41948, + SPELL_SUMMON_SKELETRON_3 = 41949, + SPELL_SUMMON_SKELETRON_4 = 41950, + + //Vengeful Spirit + SPELL_SPIRIT_STRIKE = 40325, + SPELL_SPIRIT_CHAINS = 40175, + SPELL_SPIRIT_VOLLEY = 40314, + SPELL_SPIRIT_SHIELD = 40322, + SPELL_SPIRIT_LANCE = 40157 +}; - CreatureAI* GetAI(Creature* creature) const override - { - return GetBlackTempleAI<npc_doom_blossomAI>(creature); - } +enum Npcs +{ + NPC_DOOM_BLOSSOM = 23123, + NPC_SHADOWY_CONSTRUCT = 23111, + NPC_VENGEFUL_SPIRIT = 23109 //Npc controlled by player +}; + +enum Events +{ + EVENT_ENRAGE = 1, + EVENT_INCINERATE, + EVENT_SUMMON_DOOM_BLOSSOM, + EVENT_SHADOW_DEATH, + EVENT_CRUSHING_SHADOWS, + EVENT_FINISH_INTRO +}; - struct npc_doom_blossomAI : public ScriptedAI +enum Actions +{ + ACTION_START_INTRO = 1 +}; + +uint32 const SkeletronSpells[4] = +{ + SPELL_SUMMON_SKELETRON_1, + SPELL_SUMMON_SKELETRON_2, + SPELL_SUMMON_SKELETRON_3, + SPELL_SUMMON_SKELETRON_4 +}; + +class boss_teron_gorefiend : public CreatureScript +{ +public: + boss_teron_gorefiend() : CreatureScript("boss_teron_gorefiend") { } + + struct boss_teron_gorefiendAI : public BossAI { - npc_doom_blossomAI(Creature* creature) : ScriptedAI(creature) + boss_teron_gorefiendAI(Creature* creature) : BossAI(creature, DATA_TERON_GOREFIEND), _intro(false) { - Initialize(); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->SetReactState(REACT_PASSIVE); } - void Initialize() + void EnterCombat(Unit* /*who*/) override { - CheckTeronTimer = 5000; - ShadowBoltTimer = 12000; - TeronGUID.Clear(); + _EnterCombat(); + Talk(SAY_AGGRO); + events.ScheduleEvent(EVENT_ENRAGE, Minutes(10)); + events.ScheduleEvent(EVENT_INCINERATE, Seconds(12)); + events.ScheduleEvent(EVENT_SUMMON_DOOM_BLOSSOM, Seconds(8)); + events.ScheduleEvent(EVENT_SHADOW_DEATH, Seconds(8)); + events.ScheduleEvent(EVENT_CRUSHING_SHADOWS, Seconds(18)); } - uint32 CheckTeronTimer; - uint32 ShadowBoltTimer; - ObjectGuid TeronGUID; - - void Reset() override + void EnterEvadeMode(EvadeReason /*why*/) override { - Initialize(); + DoCast(SPELL_SHADOW_OF_DEATH_REMOVE); + summons.DespawnAll(); + _DespawnAtEvade(); } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } + void DoAction(int32 action) override + { + if (action == ACTION_START_INTRO && !_intro && me->IsAlive()) + { + _intro = true; + Talk(SAY_INTRO); + events.ScheduleEvent(EVENT_FINISH_INTRO, Seconds(20)); + } + } + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_SLAY); + } - void Despawn() + void JustDied(Unit* /*killer*/) override { - me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); - me->RemoveCorpse(); + Talk(SAY_DEATH); + DoCast(SPELL_SHADOW_OF_DEATH_REMOVE); + _JustDied(); } void UpdateAI(uint32 diff) override { - if (CheckTeronTimer <= diff) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) { - if (!TeronGUID.IsEmpty()) + switch (eventId) { - DoZoneInCombat(); - - Creature* Teron = (ObjectAccessor::GetCreature((*me), TeronGUID)); - if ((Teron) && (!Teron->IsAlive() || Teron->IsInEvadeMode())) - Despawn(); + case EVENT_ENRAGE: + DoCast(SPELL_BERSERK); + break; + case EVENT_INCINERATE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_INCINERATE); + Talk(SAY_INCINERATE); + events.Repeat(Seconds(12), Seconds(20)); + break; + case EVENT_SUMMON_DOOM_BLOSSOM: + DoCastSelf(SPELL_SUMMON_DOOM_BLOSSOM, true); + Talk(SAY_BLOSSOM); + events.Repeat(Seconds(30), Seconds(40)); + break; + case EVENT_SHADOW_DEATH: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1, 100.0f, true, -SPELL_SPIRITUAL_VENGEANCE)) + DoCast(target, SPELL_SHADOW_OF_DEATH); + events.Repeat(Seconds(30), Seconds(35)); + break; + case EVENT_CRUSHING_SHADOWS: + me->CastCustomSpell(SPELL_CRUSHING_SHADOWS, SPELLVALUE_MAX_TARGETS, 5, me); + Talk(SAY_CRUSHING); + events.Repeat(Seconds(18), Seconds(30)); + break; + case EVENT_FINISH_INTRO: + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + me->SetReactState(REACT_AGGRESSIVE); + break; + default: + break; } - else - Despawn(); - CheckTeronTimer = 5000; - } else CheckTeronTimer -= diff; + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } - if (ShadowBoltTimer < diff && me->IsInCombat()) - { - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_SHADOWBOLT); - ShadowBoltTimer = 10000; - } else ShadowBoltTimer -= diff; - return; - } + if (!UpdateVictim()) + return; - void SetTeronGUID(ObjectGuid guid) - { - TeronGUID = guid; + DoMeleeAttackIfReady(); } + private: + bool _intro; }; -}; - -class npc_shadowy_construct : public CreatureScript -{ -public: - npc_shadowy_construct() : CreatureScript("npc_shadowy_construct") { } CreatureAI* GetAI(Creature* creature) const override { - return GetBlackTempleAI<npc_shadowy_constructAI>(creature); + return GetBlackTempleAI<boss_teron_gorefiendAI>(creature); } +}; - struct npc_shadowy_constructAI : public ScriptedAI - { - npc_shadowy_constructAI(Creature* creature) : ScriptedAI(creature) - { - Initialize(); - } +class npc_doom_blossom : public CreatureScript +{ +public: + npc_doom_blossom() : CreatureScript("npc_doom_blossom") { } - void Initialize() + struct npc_doom_blossomAI : public NullCreatureAI + { + npc_doom_blossomAI(Creature* creature) : NullCreatureAI(creature), _instance(me->GetInstanceScript()) { - GhostGUID.Clear(); - TeronGUID.Clear(); - - CheckPlayerTimer = 2000; - CheckTeronTimer = 5000; + /* Workaround - Until SMSG_SET_PLAY_HOVER_ANIM be implemented */ + me->SetDisableGravity(true); + Position pos; + pos.Relocate(me); + pos.m_positionZ += 8.0f; + me->GetMotionMaster()->MoveTakeoff(0, pos); } - ObjectGuid GhostGUID; - ObjectGuid TeronGUID; - - uint32 CheckPlayerTimer; - uint32 CheckTeronTimer; - void Reset() override { - Initialize(); - } - - void EnterCombat(Unit* /*who*/) override { } - - void MoveInLineOfSight(Unit* who) override - - { - if (!who || (!who->IsAlive()) || (who->GetGUID() == GhostGUID)) - return; - - ScriptedAI::MoveInLineOfSight(who); - } - - /* Comment it out for now. NOTE TO FUTURE DEV: UNCOMMENT THIS OUT ONLY AFTER MIND CONTROL IS IMPLEMENTED - void DamageTaken(Unit* done_by, uint32 &damage) override - { - if (done_by->GetGUID() != GhostGUID) - damage = 0; // Only the ghost can deal damage. - } - */ - - void CheckPlayers() - { - ThreatContainer::StorageType const &threatlist = me->getThreatManager().getThreatList(); - if (threatlist.empty()) - return; // No threat list. Don't continue. - ThreatContainer::StorageType::const_iterator itr = threatlist.begin(); - std::list<Unit*> targets; - for (; itr != threatlist.end(); ++itr) - { - Unit* unit = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if (unit && unit->IsAlive()) - targets.push_back(unit); - } - targets.sort(Trinity::ObjectDistanceOrderPred(me)); - Unit* target = targets.front(); - if (target && me->IsWithinDistInMap(target, me->GetAttackDistance(target))) + DoCast(SPELL_SUMMON_BLOSSOM_MOVE_TARGET); + _scheduler.CancelAll(); + me->SetInCombatWithZone(); + _scheduler.Schedule(Seconds(12), [this](TaskContext shadowBolt) { - DoCast(target, SPELL_ATROPHY); - AttackStart(target); - } + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target, SPELL_SHADOWBOLT); + + shadowBolt.Repeat(Seconds(2)); + }); } void UpdateAI(uint32 diff) override { - if (CheckPlayerTimer <= diff) - { - CheckPlayers(); - CheckPlayerTimer = 3000; - } else CheckPlayerTimer -= diff; - - if (CheckTeronTimer <= diff) - { - Creature* Teron = (ObjectAccessor::GetCreature((*me), TeronGUID)); - if (!Teron || !Teron->IsAlive() || Teron->IsInEvadeMode()) - me->DealDamage(me, me->GetHealth(), nullptr, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, nullptr, false); + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; - CheckTeronTimer = 5000; - } else CheckTeronTimer -= diff; + _scheduler.Update(diff); } - }; -}; -class boss_teron_gorefiend : public CreatureScript -{ -public: - boss_teron_gorefiend() : CreatureScript("boss_teron_gorefiend") { } + private: + TaskScheduler _scheduler; + InstanceScript* _instance; + }; CreatureAI* GetAI(Creature* creature) const override { - return GetBlackTempleAI<boss_teron_gorefiendAI>(creature); + return GetBlackTempleAI<npc_doom_blossomAI>(creature); } +}; - struct boss_teron_gorefiendAI : public BossAI +class npc_shadowy_construct : public CreatureScript +{ +public: + npc_shadowy_construct() : CreatureScript("npc_shadowy_construct") { } + struct npc_shadowy_constructAI : public ScriptedAI { - boss_teron_gorefiendAI(Creature* creature) : BossAI(creature, DATA_TERON_GOREFIEND) + npc_shadowy_constructAI(Creature* creature) : ScriptedAI(creature), _instance(me->GetInstanceScript()) { - Initialize(); + //This creature must be immune everything, except spells of Vengeful Spirit. + me->ApplySpellImmune(0, IMMUNITY_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, true); + me->ApplySpellImmune(0, IMMUNITY_SCHOOL, SPELL_SCHOOL_MASK_MAGIC, true); } - void Initialize() - { - IncinerateTimer = urand(20000, 31000); - SummonDoomBlossomTimer = 12000; - EnrageTimer = 600000; - CrushingShadowsTimer = 22000; - SummonShadowsTimer = 60000; - RandomYellTimer = 50000; - - AggroTimer = 20000; - AggroTargetGUID.Clear(); - Intro = false; - Done = false; - } - - uint32 IncinerateTimer; - uint32 SummonDoomBlossomTimer; - uint32 EnrageTimer; - uint32 CrushingShadowsTimer; - uint32 SummonShadowsTimer; - uint32 RandomYellTimer; - uint32 AggroTimer; - - ObjectGuid AggroTargetGUID; - ObjectGuid GhostGUID; // Player that gets killed by Shadow of Death and gets turned into a ghost - - bool Intro; - bool Done; - void Reset() override { - _Reset(); - Initialize(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - // Start off unattackable so that the intro is done properly - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - } - - void EnterCombat(Unit* /*who*/) override { } - - void MoveInLineOfSight(Unit* who) override + if (_instance->GetBossState(DATA_TERON_GOREFIEND) != IN_PROGRESS) + { + me->DespawnOrUnsummon(); + return; + } - { - if (!Intro && who->GetTypeId() == TYPEID_PLAYER && me->CanCreatureAttack(who)) + targetGUID.Clear(); + _scheduler.CancelAll(); + _scheduler.Schedule(Seconds(12), [this](TaskContext atrophy) + { + DoCastVictim(SPELL_ATROPHY); + atrophy.Repeat(Seconds(10), Seconds(12)); + }); + _scheduler.Schedule(Milliseconds(200), [this](TaskContext checkPlayer) { - if (me->IsWithinDistInMap(who, VISIBLE_RANGE) && me->IsWithinLOSInMap(who)) + if (Unit* target = ObjectAccessor::GetUnit(*me, targetGUID)) { - instance->SetBossState(DATA_TERON_GOREFIEND, IN_PROGRESS); - - me->GetMotionMaster()->Clear(false); - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - Talk(SAY_INTRO); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_TALK); - AggroTargetGUID = who->GetGUID(); - Intro = true; + if (!target->IsAlive() || !me->CanCreatureAttack(target)) + SelectNewTarget(); } - } - if (Done) - ScriptedAI::MoveInLineOfSight(who); - } + else + SelectNewTarget(); - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_SLAY); - } + checkPlayer.Repeat(Seconds(1)); + }); - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - _JustDied(); - } + if (Creature* teron = _instance->GetCreature(DATA_TERON_GOREFIEND)) + teron->AI()->JustSummoned(me); - float CalculateRandomLocation(float Loc, uint32 radius) - { - float coord = Loc; - switch (urand(0, 1)) - { - case 0: - coord += rand32() % radius; - break; - case 1: - coord -= rand32() % radius; - break; - } - return coord; + SelectNewTarget(); } - void SetThreatList(Creature* blossom) + void UpdateAI(uint32 diff) override { - if (!blossom) + if (me->HasUnitState(UNIT_STATE_CASTING)) return; - ThreatContainer::StorageType const &threatlist = me->getThreatManager().getThreatList(); - ThreatContainer::StorageType::const_iterator i = threatlist.begin(); - for (i = threatlist.begin(); i != threatlist.end(); ++i) + _scheduler.Update(diff, [this] { - Unit* unit = ObjectAccessor::GetUnit(*me, (*i)->getUnitGuid()); - if (unit && unit->IsAlive()) - { - float threat = DoGetThreat(unit); - blossom->AddThreat(unit, threat); - } - } + DoMeleeAttackIfReady(); + }); } - void MindControlGhost() + void SelectNewTarget() { - /************************************************************************/ - /** NOTE FOR FUTURE DEVELOPER: PROPERLY IMPLEMENT THE GHOST PORTION *****/ - /** ONLY AFTER TrinIty FULLY IMPLEMENTS MIND CONTROL ABILITIES *****/ - /** THE CURRENT CODE IN THIS FUNCTION IS ONLY THE BEGINNING OF *****/ - /** WHAT IS FULLY NECESSARY FOR GOREFIEND TO BE 100% COMPLETE *****/ - /************************************************************************/ - - Unit* ghost = nullptr; - if (!GhostGUID.IsEmpty()) - ghost = ObjectAccessor::GetUnit(*me, GhostGUID); - if (ghost && ghost->IsAlive() && ghost->HasAura(SPELL_SHADOW_OF_DEATH)) + if (Creature* teron = _instance->GetCreature(DATA_TERON_GOREFIEND)) { - /*float x, y, z; - ghost->GetPosition(x, y, z); - if (Creature* control = me->SummonCreature(CREATURE_GHOST, x, y, z, 0, TEMPSUMMON_TIMED_DESAWN, 30000)) + if (Unit* target = teron->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0, 100.0f, true, -SPELL_SPIRITUAL_VENGEANCE)) { - if (Player* player = ghost->ToPlayer()) - player->Possess(control); - ghost->DealDamage(ghost, ghost->GetHealth(), NULL, DIRECT_DAMAGE, SPELL_SCHOOL_MASK_NORMAL, NULL, - false); - }*/ - for (uint8 i = 0; i < 4; ++i) + DoResetThreat(); + AttackStart(target); + me->AddThreat(target, 1000000.0f); + targetGUID = target->GetGUID(); + } + //He only should target Vengeful Spirits if has no other player available + else if (Unit* target = teron->AI()->SelectTarget(SELECT_TARGET_RANDOM, 0)) { - Creature* Construct = nullptr; - float X = CalculateRandomLocation(ghost->GetPositionX(), 10); - float Y = CalculateRandomLocation(ghost->GetPositionY(), 10); - Construct = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, Y, ghost->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 45000); - if (Construct) - { - Construct->CastSpell(Construct, SPELL_PASSIVE_SHADOWFORM, true); - SetThreatList(Construct); // Use same function as Doom Blossom to set Threat List. - ENSURE_AI(npc_shadowy_construct::npc_shadowy_constructAI, Construct->AI())->GhostGUID = GhostGUID; - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - if (!target) // someone's trying to solo. - target = me->GetVictim(); - - if (target) - Construct->GetMotionMaster()->MoveChase(target); - } + DoResetThreat(); + AttackStart(target); + me->AddThreat(target, 1000000.0f); + targetGUID = target->GetGUID(); } } } - void UpdateAI(uint32 diff) override + private: + TaskScheduler _scheduler; + InstanceScript* _instance; + ObjectGuid targetGUID; + + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetBlackTempleAI<npc_shadowy_constructAI>(creature); + } +}; + +class at_teron_gorefiend_entrance : public AreaTriggerScript +{ +public: + at_teron_gorefiend_entrance() : AreaTriggerScript("at_teron_gorefiend_entrance") { } + + bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/, bool entered) override + { + if (!entered) + return true; + + if (InstanceScript* instance = player->GetInstanceScript()) + if (Creature* teron = instance->GetCreature(DATA_TERON_GOREFIEND)) + teron->AI()->DoAction(ACTION_START_INTRO); + + return true; + } +}; + +class spell_teron_gorefiend_shadow_of_death : public SpellScriptLoader +{ + public: + spell_teron_gorefiend_shadow_of_death() : SpellScriptLoader("spell_teron_gorefiend_shadow_of_death") { } + + class spell_teron_gorefiend_shadow_of_death_AuraScript : public AuraScript { - if (Intro && !Done) + PrepareAuraScript(spell_teron_gorefiend_shadow_of_death_AuraScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override { - if (AggroTimer <= diff) + return ValidateSpellInfo( { - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - Talk(SAY_AGGRO); - me->SetUInt32Value(UNIT_NPC_EMOTESTATE, EMOTE_STATE_NONE); - Done = true; - if (!AggroTargetGUID.IsEmpty()) - { - Unit* unit = ObjectAccessor::GetUnit(*me, AggroTargetGUID); - if (unit) - AttackStart(unit); - - DoZoneInCombat(); - } - else - { - EnterEvadeMode(); - return; - } - } else AggroTimer -= diff; + SPELL_SUMMON_SPIRIT, + SPELL_POSSESS_SPIRIT_IMMUNE, + SPELL_SPIRITUAL_VENGEANCE, + SPELL_SUMMON_SKELETRON_1, + SPELL_SUMMON_SKELETRON_2, + SPELL_SUMMON_SKELETRON_3, + SPELL_SUMMON_SKELETRON_4 + }); } - if (!UpdateVictim() || !Done) - return; - - if (SummonShadowsTimer <= diff) + void Absorb(AuraEffect* /*aurEff*/, DamageInfo& /*dmgInfo*/, uint32& /*absorbAmount*/) { - //MindControlGhost(); - - for (uint8 i = 0; i < 2; ++i) - { - float X = CalculateRandomLocation(me->GetPositionX(), 10); - if (Creature* shadow = me->SummonCreature(CREATURE_SHADOWY_CONSTRUCT, X, me->GetPositionY(), me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_CORPSE_DESPAWN, 0)) - { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1)) - shadow->AI()->AttackStart(target); - else if (Unit* victim = me->GetVictim()) - shadow->AI()->AttackStart(victim); - } - } - SummonShadowsTimer = 60000; - } else SummonShadowsTimer -= diff; + PreventDefaultAction(); + } - if (SummonDoomBlossomTimer <= diff) + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + if (GetTargetApplication()->GetRemoveMode() == AURA_REMOVE_BY_EXPIRE) { - float X = CalculateRandomLocation(target->GetPositionX(), 20); - float Y = CalculateRandomLocation(target->GetPositionY(), 20); - float Z = target->GetPositionZ(); - me->UpdateGroundPositionZ(X, Y, Z); - Creature* DoomBlossom = me->SummonCreature(CREATURE_DOOM_BLOSSOM, X, Y, Z, 0, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 20000); - if (DoomBlossom) - { - DoomBlossom->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); - DoomBlossom->setFaction(me->getFaction()); - DoomBlossom->AddThreat(target, 1.0f); - ENSURE_AI(npc_doom_blossom::npc_doom_blossomAI, DoomBlossom->AI())->SetTeronGUID(me->GetGUID()); - target->CombatStart(DoomBlossom); - SetThreatList(DoomBlossom); - SummonDoomBlossomTimer = 35000; - } + Unit* target = GetTarget(); + target->CastSpell(target, SPELL_SUMMON_SPIRIT, true); + + for (uint8 i = 0; i < 4; ++i) + target->CastSpell(target, SkeletronSpells[i], true); + + target->CastSpell(target, SPELL_POSSESS_SPIRIT_IMMUNE, true); + target->CastSpell(target, SPELL_SPIRITUAL_VENGEANCE, true); } - } else SummonDoomBlossomTimer -= diff; + } - if (IncinerateTimer <= diff) + void Register() override { - Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 1); - if (!target) - target = me->GetVictim(); + OnEffectAbsorb += AuraEffectAbsorbFn(spell_teron_gorefiend_shadow_of_death_AuraScript::Absorb, EFFECT_0); + AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_shadow_of_death_AuraScript::OnRemove, EFFECT_1, SPELL_AURA_OVERRIDE_CLASS_SCRIPTS, AURA_EFFECT_HANDLE_REAL); + } + }; - if (target) - { - Talk(SAY_SPECIAL); - DoCast(target, SPELL_INCINERATE); - IncinerateTimer = urand(20, 51) * 1000; - } - } else IncinerateTimer -= diff; + AuraScript* GetAuraScript() const override + { + return new spell_teron_gorefiend_shadow_of_death_AuraScript(); + } +}; + +class spell_teron_gorefiend_spiritual_vengeance : public SpellScriptLoader +{ + public: + spell_teron_gorefiend_spiritual_vengeance() : SpellScriptLoader("spell_teron_gorefiend_spiritual_vengeance") { } - if (CrushingShadowsTimer <= diff) + class spell_teron_gorefiend_spiritual_vengeance_AuraScript : public AuraScript + { + PrepareAuraScript(spell_teron_gorefiend_spiritual_vengeance_AuraScript); + + void OnRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) { - if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) - DoCast(target, SPELL_CRUSHING_SHADOWS); - CrushingShadowsTimer = urand(10, 26) * 1000; - } else CrushingShadowsTimer -= diff; + GetTarget()->KillSelf(); + } - /*** NOTE FOR FUTURE DEV: UNCOMMENT BELOW ONLY IF MIND CONTROL IS FULLY IMPLEMENTED **/ - /*if (ShadowOfDeathTimer <= diff) + void Register() override { - Unit* target = SelectUnit(SELECT_TARGET_RANDOM, 1); + AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_spiritual_vengeance_AuraScript::OnRemove, EFFECT_0, SPELL_AURA_MOD_POSSESS, AURA_EFFECT_HANDLE_REAL); + AfterEffectRemove += AuraEffectRemoveFn(spell_teron_gorefiend_spiritual_vengeance_AuraScript::OnRemove, EFFECT_2, SPELL_AURA_MOD_PACIFY_SILENCE, AURA_EFFECT_HANDLE_REAL); + } + }; - if (!target) - target = me->GetVictim(); + AuraScript* GetAuraScript() const override + { + return new spell_teron_gorefiend_spiritual_vengeance_AuraScript(); + } +}; - if (target && target->IsAlive() && target->GetTypeId() == TYPEID_PLAYER) - { - DoCast(target, SPELL_SHADOW_OF_DEATH); - GhostGUID = target->GetGUID(); - ShadowOfDeathTimer = 30000; - SummonShadowsTimer = 53000; // Make it VERY close but slightly less so that we can check if the aura is still on the player - } - } else ShadowOfDeathTimer -= diff;*/ +class spell_teron_gorefiend_shadow_of_death_remove : public SpellScriptLoader +{ + public: + spell_teron_gorefiend_shadow_of_death_remove() : SpellScriptLoader("spell_teron_gorefiend_shadow_of_death_remove") { } - if (RandomYellTimer <= diff) + class spell_teron_gorefiend_shadow_of_death_remove_SpellScript : public SpellScript + { + PrepareSpellScript(spell_teron_gorefiend_shadow_of_death_remove_SpellScript); + + bool Validate(SpellInfo const* /*spellInfo*/) override { - Talk(SAY_SPELL); - RandomYellTimer = urand(50, 101) * 1000; - } else RandomYellTimer -= diff; + return ValidateSpellInfo( + { + SPELL_SHADOW_OF_DEATH, + SPELL_POSSESS_SPIRIT_IMMUNE, + SPELL_SPIRITUAL_VENGEANCE + }); + } - if (!me->HasAura(SPELL_BERSERK)) + void RemoveAuras() { - if (EnrageTimer <= diff) + Unit* target = GetHitUnit(); + + target->RemoveAurasDueToSpell(SPELL_POSSESS_SPIRIT_IMMUNE); + target->RemoveAurasDueToSpell(SPELL_SPIRITUAL_VENGEANCE); + target->RemoveAurasDueToSpell(SPELL_SHADOW_OF_DEATH); + } + + void Register() override { - DoCast(me, SPELL_BERSERK); - Talk(SAY_ENRAGE); - } else EnrageTimer -= diff; + OnHit += SpellHitFn(spell_teron_gorefiend_shadow_of_death_remove_SpellScript::RemoveAuras); } - DoMeleeAttackIfReady(); + }; + + SpellScript* GetSpellScript() const override + { + return new spell_teron_gorefiend_shadow_of_death_remove_SpellScript(); } - }; }; void AddSC_boss_teron_gorefiend() { + new boss_teron_gorefiend(); new npc_doom_blossom(); new npc_shadowy_construct(); - new boss_teron_gorefiend(); + new at_teron_gorefiend_entrance(); + new spell_teron_gorefiend_shadow_of_death(); + new spell_teron_gorefiend_spiritual_vengeance(); + new spell_teron_gorefiend_shadow_of_death_remove(); } diff --git a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp index 3f2f0efb061..eaf9f192e29 100644 --- a/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp +++ b/src/server/scripts/Outland/BlackTemple/instance_black_temple.cpp @@ -139,7 +139,7 @@ class instance_black_temple : public InstanceMapScript bool CheckDenOfMortalDoor() { - for (DataTypes boss : {DATA_SHADE_OF_AKAMA, DATA_TERON_GOREFIEND, DATA_RELIQUARY_OF_SOULS, DATA_GURTOGG_BLOODBOIL}) + for (BTDataTypes boss : {DATA_SHADE_OF_AKAMA, DATA_TERON_GOREFIEND, DATA_RELIQUARY_OF_SOULS, DATA_GURTOGG_BLOODBOIL}) if (GetBossState(boss) != DONE) return false; return true; |
