diff --git a/sql/updates/world/4.3.4/2020_02_22_02_world.sql b/sql/updates/world/4.3.4/2020_02_22_02_world.sql new file mode 100644 index 0000000000..8be0f670ed --- /dev/null +++ b/sql/updates/world/4.3.4/2020_02_22_02_world.sql @@ -0,0 +1,6 @@ +-- Interrogator Vishas +UPDATE `creature_template` SET `flags_extra`= `flags_extra` | 0x800; +UPDATE `creature_template_addon` SET `auras`= '8981' WHERE `entry`= 3983; + +-- Bloodmage Thalnos +UPDATE `creature_template` SET `flags_extra`= 0 WHERE `entry`= 4543; diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp index 275eac7d76..c353c97206 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_bloodmage_thalnos.cpp @@ -22,109 +22,163 @@ enum Yells { SAY_AGGRO = 0, - SAY_HEALTH = 1, - SAY_KILL = 2 + SAY_HEALTH_BELOW_50 = 1, + SAY_SLAY = 2 }; enum Spells { - SPELL_FLAMESHOCK = 8053, - SPELL_SHADOWBOLT = 1106, - SPELL_FLAMESPIKE = 8814, - SPELL_FIRENOVA = 16079 + SPELL_SHADOW_BOLT = 20825, + SPELL_FIRE_NOVA = 12470, + SPELL_FLAME_SHOCK = 23038, + SPELL_FLAME_SPIKE = 8814 }; enum Events { - EVENT_FLAME_SHOCK = 1, - EVENT_SHADOW_BOLT, - EVENT_FLAME_SPIKE, - EVENT_FIRE_NOVA + EVENT_SHADOW_BOLT = 1, + EVENT_FIRE_NOVA, + EVENT_FLAME_SHOCK, + EVENT_FLAME_SPIKE }; -class boss_bloodmage_thalnos : public CreatureScript +static constexpr uint8 const MaxChainedEvents = 4; + +struct boss_bloodmage_thalnos : public BossAI { - public: - boss_bloodmage_thalnos() : CreatureScript("boss_bloodmage_thalnos") { } + boss_bloodmage_thalnos(Creature* creature) : BossAI(creature, DATA_BLOODMAGE_THALNOS), _triggeredText(false) + { + InitializeEventCooldowns(); + } - struct boss_bloodmage_thalnosAI : public BossAI + void Reset() override + { + _triggeredText = false; + InitializeEventCooldowns(); + _Reset(); + } + + void JustEngagedWith(Unit* who) override + { + Talk(SAY_AGGRO); + _JustEngagedWith(); + events.ScheduleEvent(EVENT_FLAME_SPIKE, 8s, 10s); + events.ScheduleEvent(EVENT_SHADOW_BOLT, 1ms); + } + + void KilledUnit(Unit* victim) override + { + if (victim->IsPlayer()) + Talk(SAY_SLAY); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (!_triggeredText && me->HealthBelowPctDamaged(50, damage)) { - boss_bloodmage_thalnosAI(Creature* creature) : BossAI(creature, DATA_BLOODMAGE_THALNOS) - { - _hpYell = false; - } - - void Reset() override - { - _hpYell = false; - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - _JustEngagedWith(); - events.ScheduleEvent(EVENT_FLAME_SHOCK, 10000); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 2000); - events.ScheduleEvent(EVENT_FLAME_SPIKE, 8000); - events.ScheduleEvent(EVENT_FIRE_NOVA, 40000); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - } - - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_KILL); - } - - void DamageTaken(Unit* /*attacker*/, uint32& damage) override - { - if (!_hpYell && me->HealthBelowPctDamaged(35, damage)) - { - Talk(SAY_HEALTH); - _hpYell = true; - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_FLAME_SHOCK: - DoCastVictim(SPELL_FLAMESHOCK); - events.ScheduleEvent(EVENT_FLAME_SHOCK, urand(10000, 15000)); - break; - case EVENT_SHADOW_BOLT: - DoCastVictim(SPELL_SHADOWBOLT); - events.ScheduleEvent(EVENT_SHADOW_BOLT, 2000); - break; - case EVENT_FLAME_SPIKE: - DoCastVictim(SPELL_FLAMESPIKE); - events.ScheduleEvent(EVENT_FLAME_SPIKE, 30000); - break; - case EVENT_FIRE_NOVA: - DoCastVictim(SPELL_FIRENOVA); - events.ScheduleEvent(EVENT_FIRE_NOVA, 40000); - break; - default: - break; - } - } - - private: - bool _hpYell; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetScarletMonasteryAI(creature); + Talk(SAY_HEALTH_BELOW_50); + _triggeredText = true; } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADOW_BOLT: + DoCastVictim(SPELL_SHADOW_BOLT); + ScheduleNextEvent(); + break; + case EVENT_FLAME_SHOCK: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.f, true, -SPELL_FLAME_SHOCK)) + DoCast(target, SPELL_FLAME_SHOCK); + else + DoCastVictim(SPELL_FLAME_SHOCK); + + ScheduleNextEvent(); + break; + case EVENT_FIRE_NOVA: + DoCastAOE(SPELL_FIRE_NOVA); + ScheduleNextEvent(); + break; + case EVENT_FLAME_SPIKE: + DoCastVictim(SPELL_FLAME_SPIKE); + events.Repeat(3min); + break; + default: + break; + } + } + + if (!me->HasSpellFocus()) + DoMeleeAttackIfReady(); + } + +private: + bool _triggeredText; + int8 _remainingEventCycles[MaxChainedEvents]; + + void InitializeEventCooldowns() + { + _remainingEventCycles[EVENT_SHADOW_BOLT] = 0; + _remainingEventCycles[EVENT_FLAME_SHOCK] = 1; + _remainingEventCycles[EVENT_FIRE_NOVA] = 3; + } + + void UpdateEventCooldowns() + { + for (uint8 i : { EVENT_FIRE_NOVA, EVENT_FLAME_SHOCK }) + { + if (_remainingEventCycles[i] == 0) + { + if (i == EVENT_FLAME_SHOCK) + _remainingEventCycles[i] = 2; + else if (i == EVENT_FIRE_NOVA) + _remainingEventCycles[i] = 3; + } + else + _remainingEventCycles[i]--; + } + } + + uint32 GetNextEventId() const + { + std::vector possibleEventIds; + for (uint8 i : { EVENT_SHADOW_BOLT, EVENT_FIRE_NOVA, EVENT_FLAME_SHOCK }) + { + // We have an long overdue event pending. Prioritize it. + if (_remainingEventCycles[i] <= -3) + return i; + + // Otherwise pick a random event that's ready for execution + if (_remainingEventCycles[i] == 0) + possibleEventIds.push_back(i); + } + + if (!possibleEventIds.empty()) + return Trinity::Containers::SelectRandomContainerElement(possibleEventIds); + + return 0; + } + + void ScheduleNextEvent() + { + UpdateEventCooldowns(); + events.ScheduleEvent(GetNextEventId(), 3s); + } }; void AddSC_boss_bloodmage_thalnos() { - new boss_bloodmage_thalnos(); + RegisterScarletMonastryCreatureAI(boss_bloodmage_thalnos); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp index 587a85a8ee..162fa505e3 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/boss_interrogator_vishas.cpp @@ -24,15 +24,14 @@ enum Says { SAY_AGGRO = 0, - SAY_HEALTH1 = 1, - SAY_HEALTH2 = 2, - SAY_KILL = 3, - SAY_TRIGGER_VORREL = 0 + SAY_HEALTH_BELOW_75 = 1, + SAY_HEALTH_BELOW_25 = 2, + SAY_KILL = 3 }; enum Spells { - SPELL_SHADOW_WORD_PAIN = 2767 + SPELL_SHADOW_WORD_PAIN = 14032 }; enum Events @@ -40,88 +39,74 @@ enum Events EVENT_SHADOW_WORD_PAIN = 1 }; -class boss_interrogator_vishas : public CreatureScript +struct boss_interrogator_vishas : public BossAI { - public: - boss_interrogator_vishas() : CreatureScript("boss_interrogator_vishas") { } + boss_interrogator_vishas(Creature* creature) : BossAI(creature, DATA_INTERROGATOR_VISHAS), _textCount(0) { } - struct boss_interrogator_vishasAI : public BossAI + void Reset() override + { + _textCount = 0; + _Reset(); + } + + void JustEngagedWith(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _JustEngagedWith(); + + events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 6s, 8s); + } + + void KilledUnit(Unit* victim) override + { + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } + + void DamageTaken(Unit* /*attacker*/, uint32& damage) override + { + if (me->HealthBelowPctDamaged(75, damage) && _textCount == 0) { - boss_interrogator_vishasAI(Creature* creature) : BossAI(creature, DATA_INTERROGATOR_VISHAS) - { - Initialize(); - } - - void Initialize() - { - _yellCount = 0; - } - - void Reset() override - { - Initialize(); - _Reset(); - } - - void JustEngagedWith(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - _JustEngagedWith(); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, 5000); - } - - void KilledUnit(Unit* victim) override - { - if (victim->GetTypeId() == TYPEID_PLAYER) - Talk(SAY_KILL); - } - - void JustDied(Unit* /*killer*/) override - { - _JustDied(); - if (Creature* vorrel = ObjectAccessor::GetCreature(*me, instance->GetGuidData(DATA_VORREL))) - vorrel->AI()->Talk(SAY_TRIGGER_VORREL); - } - - void DamageTaken(Unit* /*attacker*/, uint32 &damage) override - { - if (me->HealthBelowPctDamaged(60, damage) && _yellCount < 1) - { - Talk(SAY_HEALTH1); - ++_yellCount; - } - - if (me->HealthBelowPctDamaged(30, damage) && _yellCount < 2) - { - Talk(SAY_HEALTH2); - ++_yellCount; - } - } - - void ExecuteEvent(uint32 eventId) override - { - switch (eventId) - { - case EVENT_SHADOW_WORD_PAIN: - DoCastVictim(SPELL_SHADOW_WORD_PAIN); - events.ScheduleEvent(EVENT_SHADOW_WORD_PAIN, urand(5000, 15000)); - break; - default: - break; - } - } - - private: - uint8 _yellCount; - }; - - CreatureAI* GetAI(Creature* creature) const override - { - return GetScarletMonasteryAI(creature); + Talk(SAY_HEALTH_BELOW_75); + ++_textCount; } + + if (me->HealthBelowPctDamaged(25, damage) && _textCount == 1) + { + Talk(SAY_HEALTH_BELOW_25); + ++_textCount; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + return; + + events.Update(diff); + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_SHADOW_WORD_PAIN: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 30.f, true, -SPELL_SHADOW_WORD_PAIN)) + DoCast(target, SPELL_SHADOW_WORD_PAIN); + events.Repeat(7s, 11s); + break; + default: + break; + } + } + + DoMeleeAttackIfReady(); + } + +private: + uint8 _textCount; }; void AddSC_boss_interrogator_vishas() { - new boss_interrogator_vishas(); + RegisterScarletMonastryCreatureAI(boss_interrogator_vishas); } diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp index 6d70f5b762..0eae446d29 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/instance_scarlet_monastery.cpp @@ -22,6 +22,13 @@ #include "Map.h" #include "scarlet_monastery.h" +ObjectData const creatureData[] = +{ + { BOSS_INTERROGATOR_VISHAS, DATA_INTERROGATOR_VISHAS }, + { BOSS_BLOODMAGE_THALNOS, DATA_BLOODMAGE_THALNOS }, + { 0, 0 } // END +}; + DoorData const doorData[] = { { GO_HIGH_INQUISITORS_DOOR, DATA_MOGRAINE_AND_WHITE_EVENT, DOOR_TYPE_ROOM }, @@ -39,6 +46,7 @@ class instance_scarlet_monastery : public InstanceMapScript { SetHeaders(DataHeader); SetBossNumber(EncounterCount); + LoadObjectData(creatureData, nullptr); LoadDoorData(doorData); HorsemanAdds.clear(); diff --git a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h index 36d2569dbf..b09d986000 100644 --- a/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h +++ b/src/server/scripts/EasternKingdoms/ScarletMonastery/scarlet_monastery.h @@ -27,33 +27,37 @@ uint32 const EncounterCount = 10; enum SMDataTypes { - DATA_MOGRAINE_AND_WHITE_EVENT = 1, - DATA_MOGRAINE = 2, - DATA_WHITEMANE = 3, + // Bosses + DATA_INTERROGATOR_VISHAS = 1, + DATA_BLOODMAGE_THALNOS = 2, - DATA_HORSEMAN_EVENT = 4, - DATA_PUMPKIN_SHRINE = 5, - - DATA_VORREL = 6, - DATA_ARCANIST_DOAN = 7, - DATA_AZSHIR = 8, - DATA_BLOODMAGE_THALNOS = 9, - DATA_HEROD = 10, - DATA_HIGH_INQUISITOR_FAIRBANKS = 11, - DATA_HOUNDMASTER_LOKSEY = 12, - DATA_INTERROGATOR_VISHAS = 13, - DATA_SCORN = 14 + DATA_MOGRAINE_AND_WHITE_EVENT, + DATA_MOGRAINE, + DATA_WHITEMANE, + DATA_HORSEMAN_EVENT, + DATA_PUMPKIN_SHRINE, + DATA_VORREL, + DATA_ARCANIST_DOAN, + DATA_AZSHIR, + DATA_HEROD, + DATA_HIGH_INQUISITOR_FAIRBANKS, + DATA_HOUNDMASTER_LOKSEY, + DATA_SCORN }; enum SMCreatureIds { - NPC_MOGRAINE = 3976, - NPC_WHITEMANE = 3977, - NPC_VORREL = 3981, + // Bosses + BOSS_INTERROGATOR_VISHAS = 3983, + BOSS_BLOODMAGE_THALNOS = 4543, - NPC_HORSEMAN = 23682, - NPC_HEAD = 23775, - NPC_PUMPKIN = 23694 + NPC_MOGRAINE = 3976, + NPC_WHITEMANE = 3977, + NPC_VORREL = 3981, + + NPC_HORSEMAN = 23682, + NPC_HEAD = 23775, + NPC_PUMPKIN = 23694 }; enum SMGameObjectIds @@ -68,4 +72,7 @@ inline AI* GetScarletMonasteryAI(T* obj) return GetInstanceAI(obj, SMScriptName); } +#define RegisterScarletMonastryCreatureAI(ai_name) RegisterCreatureAIWithFactory(ai_name, GetScarletMonasteryAI) + + #endif // SCARLET_M_