diff --git a/data/sql/updates/pending_db_world/rev_1674942757744333000.sql b/data/sql/updates/pending_db_world/rev_1674942757744333000.sql new file mode 100644 index 000000000..d056524c8 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1674942757744333000.sql @@ -0,0 +1,55 @@ +-- +SET @ATTUMEN_UNMOUNTED := 15550; +SET @ATTUMEN_MOUNTED := 16152; +SET @MIDNIGHT := 16151; +SET @GMIDNIGHT := 135159; + +DELETE FROM `creature_text` WHERE `CreatureID` IN (@MIDNIGHT, @ATTUMEN_UNMOUNTED, @ATTUMEN_MOUNTED); +INSERT INTO `creature_text` (`CreatureID`, `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; +UPDATE `creature_template` SET `flags_extra` = `flags_extra`|2097152 WHERE `entry` = 16152; diff --git a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp index ca23e547b..96f405c56 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/boss_midnight.cpp @@ -19,64 +19,290 @@ #include "ScriptedCreature.h" #include "SpellInfo.h" #include "SpellScript.h" +#include "Unit.h" #include "karazhan.h" -enum eSay +enum Texts { - SAY_ATTUMEN1_APPEAR = 0, - SAY_ATTUMEN1_MOUNT = 2, + SAY_KILL = 0, + SAY_RANDOM = 1, + SAY_DISARMED = 2, + SAY_MIDNIGHT_KILL = 3, + SAY_APPEAR = 4, + SAY_MOUNT = 5, - SAY_ATTUMEN2_DEATH = 0, + SAY_DEATH = 3, - SAY_ATTUMEN_KILL = 1, - SAY_ATTUMEN_DISARM = 3, - SAY_ATTUMEN_RANDOM = 4, - SAY_ATTUMEN_MIDNIGHT_KILL = 5, - - SAY_MIDNIGHT_EMOTE = 0 -}; - -enum eSpells -{ // Midnight - SPELL_KNOCKDOWN = 29711, - SPELL_SUMMON_ATTUMEN = 29714, - SPELL_SUMMON_ATTUMEN_MOUNTED = 29799, + EMOTE_CALL_ATTUMEN = 0, + EMOTE_MOUNT_UP = 1 +}; +enum Spells +{ // Attumen - SPELL_SHADOW_CLEAVE = 29832, - SPELL_INTANGIBLE_PRESENCE = 29833, - SPELL_SPAWN_SMOKE1 = 29802, + SPELL_SHADOWCLEAVE = 29832, + SPELL_INTANGIBLE_PRESENCE = 29833, + SPELL_SPAWN_SMOKE = 10389, + SPELL_CHARGE = 29847, - // Attumen 2 - SPELL_CHARGE_MIDNIGHT = 29847, - SPELL_SPAWN_SMOKE2 = 10389, - - // Generic - SPELL_MOUNT_TARGET_ATTUMEN = 29769, - SPELL_MOUNT_TARGET_MIDNIGHT = 29770 + // Midnight + SPELL_KNOCKDOWN = 29711, + SPELL_SUMMON_ATTUMEN = 29714, + SPELL_MOUNT = 29770, + SPELL_SUMMON_ATTUMEN_MOUNTED = 29799 }; -enum eEvents +enum Phases { - EVENT_CHECK_HEALTH_95 = 1, - EVENT_CHECK_HEALTH_25 = 2, - EVENT_SPELL_KNOCKDOWN = 3, - EVENT_SUMMON_ATTUMEN_MOUNTED = 4, - - EVENT_SPELL_SHADOW_CLEAVE = 10, - EVENT_SPELL_INTANGIBLE_PRESENCE = 11, - EVENT_RANDOM_YELL = 12, - - EVENT_SPELL_CHARGE = 20, - - EVENT_KILL_TALK = 30 + PHASE_NONE, + PHASE_ATTUMEN_ENGAGES, + PHASE_MOUNTED }; -enum eMisc +enum Actions { - POINT_MOVE_TO_MIDNIGHT = 1, - DATA_ATTUMEN_READY = 1 + ACTION_SET_MIDNIGHT_PHASE, +}; + +class boss_attumen : public CreatureScript +{ +public: + boss_attumen() : CreatureScript("boss_attumen") { } + + struct boss_attumenAI : public BossAI + { + boss_attumenAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) + { + Initialize(); + } + + void Initialize() + { + _phase = PHASE_NONE; + } + + void Reset() override + { + Initialize(); + } + + bool CanMeleeHit() + { + return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); + } + + void EnterEvadeMode(EvadeReason why) override + { + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + midnight->AI()->EnterEvadeMode(why); + } + + me->DespawnOrUnsummon(); + } + + void ScheduleTasks() override + { + 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(SelectTargetMethod::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 DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) 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 = instance->GetCreature(DATA_MIDNIGHT)) + { + midnight->AI()->DoCastAOE(SPELL_MOUNT, true); + } + } + } + + void KilledUnit(Unit* /*victim*/) override + { + Talk(SAY_KILL); + } + + void JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED) + { + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + if (midnight->GetHealth() > me->GetHealth()) + { + summon->SetHealth(midnight->GetHealth()); + } + else + { + summon->SetHealth(me->GetHealth()); + } + + summon->AI()->DoZoneInCombat(); + } + } + + BossAI::JustSummoned(summon); + } + + void IsSummonedBy(WorldObject* summoner) override + { + if (summoner->GetEntry() == NPC_MIDNIGHT) + { + _phase = PHASE_ATTUMEN_ENGAGES; + } + + if (summoner->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN) + { + _phase = PHASE_MOUNTED; + DoCastSelf(SPELL_SPAWN_SMOKE); + + scheduler.Schedule(Seconds(10), Seconds(25), [this](TaskContext task) + { + Unit* target = nullptr; + std::vector target_list; + + for (auto* ref : me->GetThreatMgr().GetUnsortedThreatList()) + { + target = ref->GetVictim(); + if (target && !target->IsWithinDist(me, 8.00f, false) && target->IsWithinDist(me, 25.0f, false)) + target_list.push_back(target); + + target = nullptr; + } + + if (!target_list.empty()) + { + target = Acore::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 (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + midnight->KillSelf(); + } + + _JustDied(); + } + + void UpdateAI(uint32 diff) override + { + if (_phase != PHASE_NONE) + { + if (!UpdateVictim()) + { + return; + } + } + + if (!CanMeleeHit()) + { + BossAI::EnterEvadeMode(EvadeReason::EVADE_REASON_BOUNDARY); + } + + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); + } + + void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override + { + if (spellInfo->Mechanic == MECHANIC_DISARM) + { + Talk(SAY_DISARMED); + } + + if (spellInfo->Id == SPELL_MOUNT) + { + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + _phase = PHASE_NONE; + scheduler.CancelAll(); + + midnight->AI()->DoAction(ACTION_SET_MIDNIGHT_PHASE); + midnight->AttackStop(); + midnight->RemoveAllAttackers(); + midnight->SetReactState(REACT_PASSIVE); + midnight->GetMotionMaster()->MoveFollow(me, 2.0f, 0.0f); + midnight->AI()->Talk(EMOTE_MOUNT_UP); + + me->AttackStop(); + me->RemoveAllAttackers(); + me->SetReactState(REACT_PASSIVE); + me->GetMotionMaster()->MoveFollow(midnight, 2.0f, 0.0f); + Talk(SAY_MOUNT); + + scheduler.Schedule(Seconds(1), [this](TaskContext task) + { + if (Creature* midnight = instance->GetCreature(DATA_MIDNIGHT)) + { + if (me->IsWithinDist2d(midnight, 5.0f)) + { + DoCastAOE(SPELL_SUMMON_ATTUMEN_MOUNTED); + me->DespawnOrUnsummon(1s, 0s); + midnight->SetVisible(false); + } + else + { + midnight->GetMotionMaster()->MoveFollow(me, 2.0f, 0.0f); + me->GetMotionMaster()->MoveFollow(midnight, 2.0f, 0.0f); + task.Repeat(); + } + } + }); + } + } + } + + private: + uint8 _phase; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetKarazhanAI(creature); + } }; class boss_midnight : public CreatureScript @@ -86,114 +312,109 @@ public: struct boss_midnightAI : public BossAI { - boss_midnightAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN) { } + boss_midnightAI(Creature* creature) : BossAI(creature, DATA_ATTUMEN), _phase(PHASE_NONE) { } void Reset() override { BossAI::Reset(); me->SetVisible(true); - _healthPct = 100.0f; + me->SetReactState(REACT_DEFENSIVE); + } + + bool CanMeleeHit() + { + return me->GetVictim() && (me->GetVictim()->GetPositionZ() < 53.0f || me->GetVictim()->GetDistance(me->GetHomePosition()) < 50.0f); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage, DamageEffectType /*damageType*/, SpellSchoolMask /*damageSchoolMask*/) override + { + // Midnight never dies, let health fall to 1 and prevent further damage. + if (damage >= me->GetHealth()) + { + damage = me->GetHealth() - 1; + } + + if (_phase == PHASE_NONE && me->HealthBelowPctDamaged(95, damage)) + { + _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 JustSummoned(Creature* summon) override + { + if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN) + { + summon->AI()->AttackStart(me->GetVictim()); + summon->AI()->Talk(SAY_APPEAR); + } + + BossAI::JustSummoned(summon); + } + + void DoAction(int32 actionId) override + { + if (actionId == ACTION_SET_MIDNIGHT_PHASE) + { + _phase = PHASE_MOUNTED; + } } void EnterCombat(Unit* who) override { BossAI::EnterCombat(who); - events.ScheduleEvent(EVENT_CHECK_HEALTH_95, 0); - events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 6000); - DoZoneInCombat(); + + scheduler.Schedule(Seconds(15), Seconds(25), [this](TaskContext task) + { + DoCastVictim(SPELL_KNOCKDOWN); + task.Repeat(Seconds(15), Seconds(25)); + }); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + me->DespawnOnEvade(10s); + _phase = PHASE_NONE; } void KilledUnit(Unit* /*victim*/) override { - if (Creature* attumen = summons.GetCreatureWithEntry(NPC_ATTUMEN_THE_HUNTSMAN)) - attumen->AI()->Talk(SAY_ATTUMEN_MIDNIGHT_KILL); - } - - void JustSummoned(Creature* summon) override - { - summons.Summon(summon); - summon->SetInCombatWithZone(); - - if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED) + if (_phase == PHASE_ATTUMEN_ENGAGES) { - summon->SetHealth(summon->CountPctFromMaxHealth(_healthPct)); - summon->CastSpell(summon, SPELL_SPAWN_SMOKE2, true); - } - else - summon->CastSpell(summon, SPELL_SPAWN_SMOKE1, true); - } - - void SetData(uint32 type, uint32 /*data*/) override - { - if (type == DATA_ATTUMEN_READY) - events.ScheduleEvent(EVENT_SUMMON_ATTUMEN_MOUNTED, 0); - } - - void SummonedCreatureDies(Creature* summon, Unit* /*killer*/) override - { - if (summon->GetEntry() == NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED) - { - summons.clear(); - me->KillSelf(); + if (Creature* attumen = instance->GetCreature(DATA_ATTUMEN)) + { + Talk(SAY_MIDNIGHT_KILL, attumen); + } } } void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) - return; - - events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (events.ExecuteEvent()) + if (_phase != PHASE_MOUNTED) { - case EVENT_CHECK_HEALTH_95: - if (me->HealthBelowPct(96)) - { - me->CastSpell(me, SPELL_SUMMON_ATTUMEN, true); - events.ScheduleEvent(EVENT_CHECK_HEALTH_25, 0); - break; - } - events.ScheduleEvent(EVENT_CHECK_HEALTH_95, 500); - break; - case EVENT_CHECK_HEALTH_25: - if (me->HealthBelowPct(25)) - { - Talk(SAY_MIDNIGHT_EMOTE); - me->CastSpell(me, SPELL_MOUNT_TARGET_ATTUMEN, true); - break; - } - events.ScheduleEvent(EVENT_CHECK_HEALTH_25, 500); - break; - case EVENT_SPELL_KNOCKDOWN: - me->CastSpell(me->GetVictim(), SPELL_KNOCKDOWN, false); - events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 20000); - break; - case EVENT_SUMMON_ATTUMEN_MOUNTED: - if (Creature* attumen = summons.GetCreatureWithEntry(NPC_ATTUMEN_THE_HUNTSMAN)) - { - _healthPct = std::max(me->GetHealthPct(), attumen->GetHealthPct()); - attumen->DespawnOrUnsummon(); - } + if (!UpdateVictim()) + { + return; + } - me->CastSpell(me, SPELL_SUMMON_ATTUMEN_MOUNTED, true); - me->SetVisible(false); - break; + if (!CanMeleeHit()) + { + BossAI::EnterEvadeMode(EvadeReason::EVADE_REASON_BOUNDARY); + } } - if (me->IsVisible()) - DoMeleeAttackIfReady(); - } - - bool CheckEvadeIfOutOfCombatArea() const override - { - return me->GetHomePosition().GetExactDist2d(me) > 50.0f || me->GetPositionZ() > 60.0f; + scheduler.Update(diff, + std::bind(&BossAI::DoMeleeAttackIfReady, this)); } private: - float _healthPct; + uint8 _phase; }; CreatureAI* GetAI(Creature* creature) const override @@ -202,224 +423,6 @@ public: } }; -class boss_attumen : public CreatureScript -{ -public: - boss_attumen() : CreatureScript("boss_attumen") { } - - struct boss_attumenAI : public ScriptedAI - { - boss_attumenAI(Creature* creature) : ScriptedAI(creature) - { - } - - void Reset() override - { - _events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_ATTUMEN1_APPEAR); - _events.ScheduleEvent(EVENT_CHECK_HEALTH_25, 0); - _events.ScheduleEvent(EVENT_SPELL_SHADOW_CLEAVE, 6000); - _events.ScheduleEvent(EVENT_SPELL_INTANGIBLE_PRESENCE, 15000); - _events.ScheduleEvent(EVENT_RANDOM_YELL, urand(25000, 45000)); - } - - void KilledUnit(Unit* /*victim*/) override - { - if (_events.GetNextEventTime(EVENT_KILL_TALK) == 0) - { - _events.ScheduleEvent(EVENT_KILL_TALK, 5000); - Talk(SAY_ATTUMEN_KILL); - } - } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override - { - if (spellInfo->Mechanic == MECHANIC_DISARM && _events.GetNextEventTime(EVENT_KILL_TALK) == 0) - { - _events.ScheduleEvent(EVENT_KILL_TALK, 5000); - Talk(SAY_ATTUMEN_DISARM); - } - else if (spellInfo->Id == SPELL_MOUNT_TARGET_ATTUMEN) - { - me->CastSpell(me, SPELL_MOUNT_TARGET_MIDNIGHT, true); - } - } - - void SpellHitTarget(Unit* target, SpellInfo const* spellInfo) override - { - if (spellInfo->Id == SPELL_MOUNT_TARGET_MIDNIGHT) - { - Talk(SAY_ATTUMEN1_MOUNT); - _events.Reset(); - me->GetMotionMaster()->MovePoint(POINT_MOVE_TO_MIDNIGHT, target->GetPositionX() + 2.0f * cos(target->GetAngle(me)), target->GetPositionY() + 2.0f * std::sin(target->GetAngle(me)), target->GetPositionZ() + 0.2f, true, true, MOTION_SLOT_CONTROLLED); - } - } - - void MovementInform(uint32 type, uint32 point) override - { - if (type == POINT_MOTION_TYPE && point == POINT_MOVE_TO_MIDNIGHT) - { - if (TempSummon* summon = me->ToTempSummon()) - if (Unit* midnight = summon->GetSummonerUnit()) - midnight->GetAI()->SetData(DATA_ATTUMEN_READY, 0); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (_events.ExecuteEvent()) - { - case EVENT_SPELL_SHADOW_CLEAVE: - me->CastSpell(me->GetVictim(), SPELL_SHADOW_CLEAVE, false); - _events.ScheduleEvent(EVENT_SPELL_SHADOW_CLEAVE, urand(9000, 14000)); - break; - case EVENT_SPELL_INTANGIBLE_PRESENCE: - me->CastSpell(me->GetVictim(), SPELL_INTANGIBLE_PRESENCE, false); - _events.ScheduleEvent(EVENT_SPELL_INTANGIBLE_PRESENCE, 30000); - break; - case EVENT_RANDOM_YELL: - Talk(SAY_ATTUMEN_RANDOM); - _events.ScheduleEvent(EVENT_RANDOM_YELL, urand(30000, 70000)); - break; - case EVENT_CHECK_HEALTH_25: - if (me->HealthBelowPct(25)) - { - me->CastSpell(me, SPELL_MOUNT_TARGET_MIDNIGHT, true); - break; - } - _events.ScheduleEvent(EVENT_CHECK_HEALTH_25, 500); - break; - } - - DoMeleeAttackIfReady(); - } - - private: - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetKarazhanAI(creature); - } -}; - -class boss_attumen_midnight : public CreatureScript -{ -public: - boss_attumen_midnight() : CreatureScript("boss_attumen_midnight") { } - - struct boss_attumen_midnightAI : public ScriptedAI - { - boss_attumen_midnightAI(Creature* creature) : ScriptedAI(creature) - { - } - - void Reset() override - { - _events.Reset(); - } - - void EnterCombat(Unit* /*who*/) override - { - _events.ScheduleEvent(EVENT_SPELL_SHADOW_CLEAVE, 6000); - _events.ScheduleEvent(EVENT_SPELL_INTANGIBLE_PRESENCE, 15000); - _events.ScheduleEvent(EVENT_RANDOM_YELL, urand(25000, 45000)); - _events.ScheduleEvent(EVENT_SPELL_CHARGE, 20000); - _events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 11000); - } - - void KilledUnit(Unit* /*victim*/) override - { - if (_events.GetNextEventTime(EVENT_KILL_TALK) == 0) - { - _events.ScheduleEvent(EVENT_KILL_TALK, 5000); - Talk(SAY_ATTUMEN_KILL); - } - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_ATTUMEN2_DEATH); - } - - void SpellHit(Unit* /*caster*/, SpellInfo const* spellInfo) override - { - if (spellInfo->Mechanic == MECHANIC_DISARM && _events.GetNextEventTime(EVENT_KILL_TALK) == 0) - { - _events.ScheduleEvent(EVENT_KILL_TALK, 5000); - Talk(SAY_ATTUMEN_DISARM); - } - } - - void MovementInform(uint32 type, uint32 point) override - { - if (type == POINT_MOTION_TYPE && point == POINT_MOVE_TO_MIDNIGHT) - { - if (TempSummon* summon = me->ToTempSummon()) - if (Unit* midnight = summon->GetSummonerUnit()) - midnight->GetAI()->SetData(DATA_ATTUMEN_READY, 0); - } - } - - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; - - _events.Update(diff); - if (me->HasUnitState(UNIT_STATE_CASTING)) - return; - - switch (_events.ExecuteEvent()) - { - case EVENT_SPELL_SHADOW_CLEAVE: - me->CastSpell(me->GetVictim(), SPELL_SHADOW_CLEAVE, false); - _events.ScheduleEvent(EVENT_SPELL_SHADOW_CLEAVE, urand(9000, 14000)); - break; - case EVENT_SPELL_INTANGIBLE_PRESENCE: - me->CastSpell(me->GetVictim(), SPELL_INTANGIBLE_PRESENCE, false); - _events.ScheduleEvent(EVENT_SPELL_INTANGIBLE_PRESENCE, 30000); - break; - case EVENT_RANDOM_YELL: - Talk(SAY_ATTUMEN_RANDOM); - _events.ScheduleEvent(EVENT_RANDOM_YELL, urand(30000, 70000)); - break; - case EVENT_SPELL_CHARGE: - if (Unit* target = SelectTarget(SelectTargetMethod::MinDistance, 0, 24.0f, true)) - me->CastSpell(target, SPELL_CHARGE_MIDNIGHT, false); - _events.ScheduleEvent(EVENT_SPELL_CHARGE, 20000); - break; - case EVENT_SPELL_KNOCKDOWN: - me->CastSpell(me->GetVictim(), SPELL_KNOCKDOWN, false); - _events.ScheduleEvent(EVENT_SPELL_KNOCKDOWN, 20000); - break; - } - - DoMeleeAttackIfReady(); - } - - private: - EventMap _events; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetKarazhanAI(creature); - } -}; - class spell_midnight_fixate : public SpellScriptLoader { public: @@ -460,6 +463,5 @@ void AddSC_boss_attumen() { new boss_midnight(); new boss_attumen(); - new boss_attumen_midnight(); new spell_midnight_fixate(); } diff --git a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp index e6d6115e7..ddcb44139 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp +++ b/src/server/scripts/EasternKingdoms/Karazhan/instance_karazhan.cpp @@ -32,6 +32,12 @@ const Position OptionalSpawn[] = { -10899.903320f, -2085.573730f, 49.474449f, 1.38f } // Rokad the Ravager }; +ObjectData const creatureData[] = +{ + { NPC_ATTUMEN_THE_HUNTSMAN, DATA_ATTUMEN }, + { NPC_MIDNIGHT, DATA_MIDNIGHT } +}; + class instance_karazhan : public InstanceMapScript { public: @@ -48,6 +54,7 @@ public: { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); // 1 - OZ, 2 - HOOD, 3 - RAJ, this never gets altered. OperaEvent = urand(EVENT_OZ, EVENT_RAJ); diff --git a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h index 5cce320c7..3da5b5775 100644 --- a/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h +++ b/src/server/scripts/EasternKingdoms/Karazhan/karazhan.h @@ -59,7 +59,8 @@ enum KZDataTypes DATA_MASTERS_TERRACE_DOOR_2 = 28, DATA_GO_SIDE_ENTRANCE_DOOR = 29, DATA_PRINCE = 30, - DATA_SPAWN_OPERA_DECORATIONS = 31 + DATA_SPAWN_OPERA_DECORATIONS = 31, + DATA_MIDNIGHT = 32 }; enum KZOperaEvents @@ -71,27 +72,28 @@ enum KZOperaEvents enum KZMiscCreatures { - NPC_HYAKISS_THE_LURKER = 16179, - NPC_ROKAD_THE_RAVAGER = 16181, - NPC_SHADIKITH_THE_GLIDER = 16180, - NPC_TERESTIAN_ILLHOOF = 15688, - NPC_MOROES = 15687, - NPC_ATTUMEN_THE_HUNTSMAN = 15550, + NPC_HYAKISS_THE_LURKER = 16179, + NPC_ROKAD_THE_RAVAGER = 16181, + NPC_SHADIKITH_THE_GLIDER = 16180, + NPC_TERESTIAN_ILLHOOF = 15688, + NPC_MOROES = 15687, + NPC_ATTUMEN_THE_HUNTSMAN = 15550, NPC_ATTUMEN_THE_HUNTSMAN_MOUNTED = 16152, - NPC_NIGHTBANE = 17225, + NPC_MIDNIGHT = 16151, + NPC_NIGHTBANE = 17225, // Trash - NPC_COLDMIST_WIDOW = 16171, - NPC_COLDMIST_STALKER = 16170, - NPC_SHADOWBAT = 16173, - NPC_VAMPIRIC_SHADOWBAT = 16175, - NPC_GREATER_SHADOWBAT = 16174, - NPC_PHASE_HOUND = 16178, - NPC_DREADBEAST = 16177, - NPC_SHADOWBEAST = 16176, - NPC_KILREK = 17229, - NPC_RELAY = 17645, - NPC_BARNES = 16812 + NPC_COLDMIST_WIDOW = 16171, + NPC_COLDMIST_STALKER = 16170, + NPC_SHADOWBAT = 16173, + NPC_VAMPIRIC_SHADOWBAT = 16175, + NPC_GREATER_SHADOWBAT = 16174, + NPC_PHASE_HOUND = 16178, + NPC_DREADBEAST = 16177, + NPC_SHADOWBEAST = 16176, + NPC_KILREK = 17229, + NPC_RELAY = 17645, + NPC_BARNES = 16812 }; enum KZGameObjectIds