diff options
author | Nyeriah <sarah.trysan@live.com> | 2016-07-28 00:48:20 -0300 |
---|---|---|
committer | Nyeriah <sarah.trysan@live.com> | 2016-07-28 00:48:20 -0300 |
commit | 4c4d92faa3b9e82fbabffb03eb63761b282f03a2 (patch) | |
tree | e9bf9b816259ab3023eb32315d826fbb5ce94787 | |
parent | da204ad78c0a960da9fe3ea009ac91f8f39b6af2 (diff) |
Scripts/Karazhan: Rework Attumen the Huntsman's script
Change log:
- Timers are more accurate
- Corrected Charge ability ID based on 6.x researches
- Added Midnight's missing emotes
- Fixed issue with instance being stuck in combat in case of wipes
- Solved the rare scenario where Attumen would be summoned multiple times
- Visual spell on 3rd phase transition added
- Attumen and Midnight can now be damaged during 3rd phase transition
- Midnight calls for the aid of every horse still alive in the stables
- Use proper spells to summon Attumen and handle transitions
- Now despawns on evade
Video of encounter after changes: https://www.youtube.com/watch?v=KX0rqaBeSzw
3 files changed, 310 insertions, 217 deletions
diff --git a/sql/updates/world/3.3.5/2016_07_28_00_world.sql b/sql/updates/world/3.3.5/2016_07_28_00_world.sql new file mode 100644 index 00000000000..89d9d616efb --- /dev/null +++ b/sql/updates/world/3.3.5/2016_07_28_00_world.sql @@ -0,0 +1,53 @@ +SET @ATTUMEN_UNMOUNTED := 15550; +SET @ATTUMEN_MOUNTED := 16152; +SET @MIDNIGHT := 16151; +SET @GMIDNIGHT := 135159; + +DELETE FROM `creature_text` WHERE `entry` IN (@MIDNIGHT, @ATTUMEN_UNMOUNTED, @ATTUMEN_MOUNTED); +INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES +(@ATTUMEN_UNMOUNTED, 0, 0, 'It was... inevitable.', 14, 0, 100, 0, 0, 9169, 13460, 0, 'attumen SAY_KILL1'), +(@ATTUMEN_UNMOUNTED, 0, 1, 'Another trophy to add to my collection!', 14, 0, 100, 0, 0, 9300, 15333, 0,'attumen SAY_KILL2'), +(@ATTUMEN_UNMOUNTED, 1, 0, 'Such easy sport.', 14, 0, 100, 0, 0, 9170, 0, 0,'attumen SAY_RANDOM1'), +(@ATTUMEN_UNMOUNTED, 1, 1, 'Amateurs! Do not think you can best me! I kill for a living.', 14, 0, 100, 0, 0, 9304, 0, 0, 'attumen SAY_RANDOM2'), +(@ATTUMEN_UNMOUNTED, 2, 0, 'Weapons are merely a convenience for a warrior of my skill!', 14, 0, 100, 0, 0, 9166, 13490, 0, 'attumen SAY_DISARMED'), +(@ATTUMEN_UNMOUNTED, 3, 0, 'Well done, Midnight!', 14, 0, 100, 0, 0, 9173, 15334, 0, 'attumen SAY_MIDNIGHT_KILL'), +(@ATTUMEN_UNMOUNTED, 4, 0, 'Cowards! Wretches!', 14, 0, 100, 0, 0, 9167, 13459, 0, 'attumen SAY_APPEAR1'), +(@ATTUMEN_UNMOUNTED, 4, 1, 'Who dares attack the steed of the Huntsman?', 14, 0, 100, 0, 0, 9298, 15378, 0, 'attumen SAY_APPEAR2'), +(@ATTUMEN_UNMOUNTED, 4, 2, 'Perhaps you would rather test yourselves against a more formidable opponent?!', 14, 0, 100, 0, 0, 9299, 15379, 0, 'attumen SAY_APPEAR3'), +(@ATTUMEN_UNMOUNTED, 5, 0, 'Come Midnight, let\'s disperse this petty rabble!', 14, 0, 100, 0, 0, 9168, 13456, 0, 'attumen SAY_MOUNT'), + +(@ATTUMEN_MOUNTED, 0, 0, 'It was... inevitable.', 14, 0, 100, 0, 0, 9169, 13460, 0, 'attumen SAY_KILL1'), +(@ATTUMEN_MOUNTED, 0, 1, 'Another trophy to add to my collection!', 14, 0, 100, 0, 0, 9300, 15333, 0,'attumen SAY_KILL2'), +(@ATTUMEN_MOUNTED, 1, 0, 'Such easy sport.', 14, 0, 100, 0, 0, 9170, 0, 0,'attumen SAY_RANDOM1'), +(@ATTUMEN_MOUNTED, 1, 1, 'Amateurs! Do not think you can best me! I kill for a living.', 14, 0, 100, 0, 0, 9304, 0, 0, 'attumen SAY_RANDOM2'), +(@ATTUMEN_MOUNTED, 2, 0, 'Weapons are merely a convenience for a warrior of my skill!', 14, 0, 100, 0, 0, 9166, 13490, 0, 'attumen SAY_DISARMED'), +(@ATTUMEN_MOUNTED, 3, 0, 'Always knew... someday I would become... the hunted.', 14, 0, 100, 0, 0, 9165, 13462, 0, 'attumen SAY_DEATH'), + +(@MIDNIGHT, 0, 0, '%s calls for her master!', 16, 0, 100, 0, 0, 0, 13439, 0, 'midnight EMOTE_CALL_ATTUMEN'), +(@MIDNIGHT, 1, 0, '%s rushes to her master\'s aid.', 16, 0, 100, 0, 0, 0, 13455, 0, 'midnight EMOTE_MOUNT_UP'); + +DELETE FROM `conditions` WHERE `SourceTypeOrReferenceId` = 13 AND `SourceEntry` = 29770; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ErrorTextId`, `ScriptName`, `Comment`) VALUES +(13, 1, 29770, 0, 0, 31, 0, 3, 15550, 0, 0, 0, 0, '', 'Mount spell only hit Attumen the Huntsmen'); + +DELETE FROM `creature_formations` WHERE `leaderGUID` = 135159; +INSERT INTO `creature_formations` (`leaderGUID`, `memberGUID`, `dist`, `angle`, `groupAI`, `point_1`, `point_2`) VALUES +(@GMIDNIGHT, @GMIDNIGHT, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135828, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135836, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135829, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135839, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135833, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135840, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135834, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135830, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135841, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135842, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135831, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135837, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135838, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135832, 0, 0, 1, 0, 0), +(@GMIDNIGHT, 135835, 0, 0, 1, 0, 0); + +UPDATE `creature_template` SET `ScriptName` = 'boss_attumen', `lootid` = @ATTUMEN_MOUNTED WHERE `entry` = @ATTUMEN_MOUNTED; +UPDATE `creature_loot_template` SET `entry` = @ATTUMEN_MOUNTED WHERE `entry` = @ATTUMEN_UNMOUNTED; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index 43ef7e006ef..b0c0cecbc07 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -28,25 +28,42 @@ EndScriptData */ #include "SpellInfo.h" #include "karazhan.h" -enum Midnight +enum Texts { - SAY_MIDNIGHT_KILL = 0, - SAY_APPEAR = 1, - SAY_MOUNT = 2, - - SAY_KILL = 0, - SAY_DISARMED = 1, - SAY_DEATH = 2, - SAY_RANDOM = 3, - - SPELL_SHADOWCLEAVE = 29832, - SPELL_INTANGIBLE_PRESENCE = 29833, - SPELL_BERSERKER_CHARGE = 26561, //Only when mounted + SAY_KILL = 0, + SAY_RANDOM = 1, + SAY_DISARMED = 2, + SAY_MIDNIGHT_KILL = 3, + SAY_APPEAR = 4, + SAY_MOUNT = 5, + + SAY_DEATH = 3, + + // Midnight + EMOTE_CALL_ATTUMEN = 0, + EMOTE_MOUNT_UP = 1 +}; - MOUNTED_DISPLAYID = 16040, +enum Spells +{ + // Attumen + SPELL_SHADOWCLEAVE = 29832, + SPELL_INTANGIBLE_PRESENCE = 29833, + SPELL_SPAWN_SMOKE = 10389, + SPELL_CHARGE = 29847, + + // Midnight + SPELL_KNOCKDOWN = 29711, + SPELL_SUMMON_ATTUMEN = 29714, + SPELL_MOUNT = 29770, + SPELL_SUMMON_ATTUMEN_MOUNTED = 29799 +}; - //Attumen (@todo Use the summoning spell instead of Creature id. It works, but is not convenient for us) - SUMMON_ATTUMEN = 15550, +enum Phases +{ + PHASE_NONE, + PHASE_ATTUMEN_ENGAGES, + PHASE_MOUNTED }; class boss_attumen : public CreatureScript @@ -54,75 +71,209 @@ class boss_attumen : public CreatureScript public: boss_attumen() : CreatureScript("boss_attumen") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_attumenAI(creature); - } - - struct boss_attumenAI : public ScriptedAI + struct boss_attumenAI : public BossAI { - boss_attumenAI(Creature* creature) : ScriptedAI(creature) + boss_attumenAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) { Initialize(); - - Phase = 1; - - CleaveTimer = urand(10000, 15000); - CurseTimer = 30000; - RandomYellTimer = urand(30000, 60000); //Occasionally yell - ChargeTimer = 20000; - } + } void Initialize() { - ResetTimer = 0; - Midnight.Clear(); + _midnightGUID.Clear(); + _phase = PHASE_NONE; } - ObjectGuid Midnight; - uint8 Phase; - uint32 CleaveTimer; - uint32 CurseTimer; - uint32 RandomYellTimer; - uint32 ChargeTimer; //only when mounted - uint32 ResetTimer; - void Reset() override { Initialize(); + BossAI::Reset(); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + BossAI::_DespawnAtEvade(10, midnight); + + me->DespawnOrUnsummon(); } - void EnterEvadeMode(EvadeReason why) override + void ScheduleTasks() override { - ScriptedAI::EnterEvadeMode(why); - ResetTimer = 2000; + scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) + { + DoCastVictim(SPELL_SHADOWCLEAVE); + task.Repeat(Seconds(15), Seconds(25)); + }); + + scheduler.Schedule(Seconds(25), Seconds(45), [this](TaskContext task) + { + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0)) + DoCast(target,SPELL_INTANGIBLE_PRESENCE); + + task.Repeat(Seconds(25), Seconds(45)); + }); + + scheduler.Schedule(Seconds(30), Seconds(60), [this](TaskContext task) + { + Talk(SAY_RANDOM); + task.Repeat(Seconds(30), Seconds(60)); + }); } - void EnterCombat(Unit* /*who*/) override { } + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override + { + // Attumen does not die until he mounts Midnight, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth() && _phase != PHASE_MOUNTED) + damage = me->GetHealth() - 1; + + if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) + { + _phase = PHASE_NONE; + + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + midnight->AI()->DoCastAOE(SPELL_MOUNT, true); + } + } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_KILL); } - void JustDied(Unit* /*killer*/) override + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ATTUMEN_MOUNTED) + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + { + if (midnight->GetHealth() > me->GetHealth()) + summon->SetHealth(midnight->GetHealth()); + else + summon->SetHealth(me->GetHealth()); + + summon->AI()->DoZoneInCombat(); + } + } + + void IsSummonedBy(Unit* summoner) override + { + if (summoner->GetEntry() == NPC_ATTUMEN_UNMOUNTED) + { + _phase = PHASE_MOUNTED; + DoCastSelf(SPELL_SPAWN_SMOKE); + + scheduler.Schedule(Seconds(10), Seconds(25), [this](TaskContext task) + { + Unit* target = nullptr; + ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList(); + std::vector<Unit*> target_list; + + for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) + { + if (target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid())) + if (!target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false)) + target_list.push_back(target); + + target = nullptr; + } + + if (!target_list.empty()) + target = Trinity::Containers::SelectRandomContainerElement(target_list); + + DoCast(target, SPELL_CHARGE); + task.Repeat(Seconds(10), Seconds(25)); + }); + + scheduler.Schedule(Seconds(25), Seconds(35), [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(Seconds(25), Seconds(35)); + }); + } + } + + void JustDied(Unit* killer) override { Talk(SAY_DEATH); - if (Unit* midnight = ObjectAccessor::GetUnit(*me, Midnight)) + if (Unit* midnight = ObjectAccessor::GetUnit(*me, _midnightGUID)) midnight->KillSelf(); - if (InstanceScript* instance = me->GetInstanceScript()) - instance->SetBossState(DATA_ATTUMEN, DONE); + BossAI::JustDied(killer); } - void UpdateAI(uint32 diff) override; + void SetGUID(ObjectGuid guid, int32 data) override + { + if (data == NPC_MIDNIGHT) + { + _midnightGUID = guid; + _phase = PHASE_ATTUMEN_ENGAGES; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && _phase != PHASE_NONE) + return; + + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } void SpellHit(Unit* /*source*/, const SpellInfo* spell) override { if (spell->Mechanic == MECHANIC_DISARM) Talk(SAY_DISARMED); + + if (spell->Id == SPELL_MOUNT) + { + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + { + _phase = PHASE_NONE; + scheduler.CancelAll(); + + midnight->AttackStop(); + midnight->RemoveAllAttackers(); + midnight->SetReactState(REACT_PASSIVE); + midnight->GetMotionMaster()->MoveChase(me); + midnight->AI()->Talk(EMOTE_MOUNT_UP); + + me->AttackStop(); + me->RemoveAllAttackers(); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MoveChase(midnight); + Talk(SAY_MOUNT); + + scheduler.Schedule(Seconds(3), [this](TaskContext task) + { + if (Creature* midnight = ObjectAccessor::GetCreature(*me, _midnightGUID)) + { + if (me->IsWithinMeleeRange(midnight)) + { + DoCastAOE(SPELL_SUMMON_ATTUMEN_MOUNTED); + me->SetVisible(false); + midnight->SetVisible(false); + } + else + { + midnight->GetMotionMaster()->MoveChase(me); + me->GetMotionMaster()->MoveChase(midnight); + task.Repeat(Seconds(3)); + } + } + }); + } + } } + + private: + ObjectGuid _midnightGUID; + uint8 _phase; }; + + CreatureAI* GetAI(Creature* creature) const override + { + return new boss_attumenAI(creature); + } }; class boss_midnight : public CreatureScript @@ -130,214 +281,100 @@ class boss_midnight : public CreatureScript public: boss_midnight() : CreatureScript("boss_midnight") { } - CreatureAI* GetAI(Creature* creature) const override - { - return new boss_midnightAI(creature); - } - - struct boss_midnightAI : public ScriptedAI + struct boss_midnightAI : public BossAI { - boss_midnightAI(Creature* creature) : ScriptedAI(creature) + boss_midnightAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) { Initialize(); } void Initialize() { - Phase = 1; - Attumen.Clear(); - Mount_Timer = 0; + _phase = PHASE_NONE; } - ObjectGuid Attumen; - uint8 Phase; - uint32 Mount_Timer; - void Reset() override { Initialize(); - - me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); + BossAI::Reset(); me->SetVisible(true); + me->SetReactState(REACT_DEFENSIVE); } - void EnterCombat(Unit* /*who*/) override + void DamageTaken(Unit* /*attacker*/, uint32 &damage) override { - if (InstanceScript* instance = me->GetInstanceScript()) - instance->SetBossState(DATA_ATTUMEN, IN_PROGRESS); - } + // Midnight never dies, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth()) + damage = me->GetHealth() - 1; - void KilledUnit(Unit* /*victim*/) override - { - if (Phase == 2) + if (_phase == PHASE_NONE && me->HealthBelowPctDamaged(95, damage)) { - if (Unit* unit = ObjectAccessor::GetUnit(*me, Attumen)) - Talk(SAY_MIDNIGHT_KILL, unit); + _phase = PHASE_ATTUMEN_ENGAGES; + Talk(EMOTE_CALL_ATTUMEN); + DoCastAOE(SPELL_SUMMON_ATTUMEN); + } + else if (_phase == PHASE_ATTUMEN_ENGAGES && me->HealthBelowPctDamaged(25, damage)) + { + _phase = PHASE_MOUNTED; + DoCastAOE(SPELL_MOUNT, true); } } - void UpdateAI(uint32 diff) override + void JustSummoned(Creature* summon) override { - if (!UpdateVictim()) - return; - - if (Phase == 1 && HealthBelowPct(95)) - { - Phase = 2; - if (Creature* attumen = me->SummonCreature(SUMMON_ATTUMEN, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 30000)) - { - Attumen = attumen->GetGUID(); - attumen->AI()->AttackStart(me->GetVictim()); - SetMidnight(attumen, me->GetGUID()); - Talk(SAY_APPEAR, attumen); - } - } - else if (Phase == 2 && HealthBelowPct(25)) - { - if (Unit* pAttumen = ObjectAccessor::GetUnit(*me, Attumen)) - Mount(pAttumen); - } - else if (Phase == 3) + if (summon->GetEntry() == NPC_ATTUMEN_UNMOUNTED) { - if (Mount_Timer) - { - if (Mount_Timer <= diff) - { - Mount_Timer = 0; - me->SetVisible(false); - me->GetMotionMaster()->MoveIdle(); - if (Unit* pAttumen = ObjectAccessor::GetUnit(*me, Attumen)) - { - pAttumen->SetDisplayId(MOUNTED_DISPLAYID); - pAttumen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - if (pAttumen->GetVictim()) - { - pAttumen->GetMotionMaster()->MoveChase(pAttumen->GetVictim()); - pAttumen->SetTarget(pAttumen->EnsureVictim()->GetGUID()); - } - pAttumen->SetObjectScale(1); - } - } else Mount_Timer -= diff; - } + _attumenGUID = summon->GetGUID(); + summon->AI()->SetGUID(me->GetGUID(), NPC_MIDNIGHT); + summon->AI()->AttackStart(me->GetVictim()); + summon->AI()->Talk(SAY_APPEAR); } - - if (Phase != 3) - DoMeleeAttackIfReady(); } - void Mount(Unit* pAttumen) + void EnterCombat(Unit* who) override { - Talk(SAY_MOUNT, pAttumen); - Phase = 3; - me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pAttumen->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - float angle = me->GetAngle(pAttumen); - float distance = me->GetDistance2d(pAttumen); - float newX = me->GetPositionX() + std::cos(angle)*(distance/2); - float newY = me->GetPositionY() + std::sin(angle)*(distance/2); - float newZ = 50; - //me->Relocate(newX, newY, newZ, angle); - //me->SendMonsterMove(newX, newY, newZ, 0, true, 1000); - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MovePoint(0, newX, newY, newZ); - distance += 10; - newX = me->GetPositionX() + std::cos(angle)*(distance/2); - newY = me->GetPositionY() + std::sin(angle)*(distance/2); - pAttumen->GetMotionMaster()->Clear(); - pAttumen->GetMotionMaster()->MovePoint(0, newX, newY, newZ); - //pAttumen->Relocate(newX, newY, newZ, -angle); - //pAttumen->SendMonsterMove(newX, newY, newZ, 0, true, 1000); - Mount_Timer = 1000; + BossAI::EnterCombat(who); + + scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(Seconds(15), Seconds(25)); + }); } - void SetMidnight(Creature* pAttumen, ObjectGuid value) + void EnterEvadeMode(EvadeReason /*why*/) override { - ENSURE_AI(boss_attumen::boss_attumenAI, pAttumen->AI())->Midnight = value; + BossAI::_DespawnAtEvade(10); } - }; -}; -void boss_attumen::boss_attumenAI::UpdateAI(uint32 diff) -{ - if (ResetTimer) - { - if (ResetTimer <= diff) + void KilledUnit(Unit* /*victim*/) override { - ResetTimer = 0; - Unit* pMidnight = ObjectAccessor::GetUnit(*me, Midnight); - if (pMidnight) + if (_phase == PHASE_ATTUMEN_ENGAGES) { - pMidnight->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE); - pMidnight->SetVisible(true); + if (Unit* unit = ObjectAccessor::GetUnit(*me, _attumenGUID)) + Talk(SAY_MIDNIGHT_KILL, unit); } - Midnight.Clear(); - me->SetVisible(false); - me->KillSelf(); - } else ResetTimer -= diff; - } - - //Return since we have no target - if (!UpdateVictim()) - return; - - if (me->HasFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE)) - return; - - if (CleaveTimer <= diff) - { - DoCastVictim(SPELL_SHADOWCLEAVE); - CleaveTimer = urand(10000, 15000); - } else CleaveTimer -= diff; + } - if (CurseTimer <= diff) - { - DoCastVictim(SPELL_INTANGIBLE_PRESENCE); - CurseTimer = 30000; - } else CurseTimer -= diff; + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() || _phase == PHASE_MOUNTED) + return; - if (RandomYellTimer <= diff) - { - Talk(SAY_RANDOM); - RandomYellTimer = urand(30000, 60000); - } else RandomYellTimer -= diff; + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } - if (me->GetUInt32Value(UNIT_FIELD_DISPLAYID) == MOUNTED_DISPLAYID) - { - if (ChargeTimer <= diff) - { - Unit* target = NULL; - ThreatContainer::StorageType const &t_list = me->getThreatManager().getThreatList(); - std::vector<Unit*> target_list; - for (ThreatContainer::StorageType::const_iterator itr = t_list.begin(); itr != t_list.end(); ++itr) - { - target = ObjectAccessor::GetUnit(*me, (*itr)->getUnitGuid()); - if (target && !target->IsWithinDist(me, ATTACK_DISTANCE, false)) - target_list.push_back(target); - target = NULL; - } - if (!target_list.empty()) - target = *(target_list.begin() + rand32() % target_list.size()); + private: + ObjectGuid _attumenGUID; + uint8 _phase; + }; - DoCast(target, SPELL_BERSERKER_CHARGE); - ChargeTimer = 20000; - } else ChargeTimer -= diff; - } - else + CreatureAI* GetAI(Creature* creature) const override { - if (HealthBelowPct(25)) - { - Creature* pMidnight = ObjectAccessor::GetCreature(*me, Midnight); - if (pMidnight && pMidnight->GetTypeId() == TYPEID_UNIT) - { - ENSURE_AI(boss_midnight::boss_midnightAI, (pMidnight->AI()))->Mount(me); - me->SetHealth(pMidnight->GetHealth()); - DoResetThreat(); - } - } + return new boss_midnightAI(creature); } - - DoMeleeAttackIfReady(); -} +}; void AddSC_boss_attumen() { diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index 05de9e43a91..2dc3750dc5b 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -70,6 +70,9 @@ enum MiscCreatures NPC_SHADIKITH_THE_GLIDER = 16180, NPC_TERESTIAN_ILLHOOF = 15688, NPC_MOROES = 15687, + NPC_ATTUMEN_UNMOUNTED = 15550, + NPC_ATTUMEN_MOUNTED = 16152, + NPC_MIDNIGHT = 16151, // Trash NPC_COLDMIST_WIDOW = 16171, |