diff --git a/data/sql/updates/pending_db_world/rev_1635438058134962700.sql b/data/sql/updates/pending_db_world/rev_1635438058134962700.sql new file mode 100644 index 000000000..f58a3823e --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1635438058134962700.sql @@ -0,0 +1,30 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1635438058134962700'); + +SET @NPC_REND := 10429; +DELETE FROM `creature_summon_groups` WHERE `summonerId` = 10429; +INSERT INTO `creature_summon_groups` (`summonerId`, `summonerType`, `groupId`, `entry`, `position_x`, `position_y`, `position_z`, `orientation`, `summonType`, `summonTime`) VALUES +(@NPC_REND, 0, 0, 10447, 202.511, -421.307, 110.9877, 0.0, 4, 30000), +(@NPC_REND, 0, 0, 10442, 204.015, -418.443, 110.989, 0.0, 4, 30000), +(@NPC_REND, 0, 0, 10442, 203.142, -423.999, 110.986, 0.0, 4, 30000), +(@NPC_REND, 0, 0, 10442, 201.008, -416.648, 110.974, 0.0, 4, 30000), +(@NPC_REND, 0, 1, 10447, 209.8637, -428.2729, 110.9877, 0.0, 4, 30000), +(@NPC_REND, 0, 1, 10442, 209.3122, -430.8724, 110.9814, 0.0, 4, 30000), +(@NPC_REND, 0, 1, 10442, 211.3309, -425.9111, 111.0006, 0.0, 4, 30000), +(@NPC_REND, 0, 2, 10742, 208.6493, -424.5787, 110.9872, 0.0, 4, 30000), +(@NPC_REND, 0, 2, 10447, 203.9482, -428.9446, 110.982, 0.0, 4, 30000), +(@NPC_REND, 0, 2, 10442, 203.3441, -426.8668, 110.9772, 0.0, 4, 30000), +(@NPC_REND, 0, 2, 10442, 206.3079, -424.7509, 110.9943, 0.0, 4, 30000), +(@NPC_REND, 0, 3, 10742, 212.3541, -412.6826, 111.0352, 0.0, 4, 30000), +(@NPC_REND, 0, 3, 10447, 212.5754, -410.2841, 111.0296, 0.0, 4, 30000), +(@NPC_REND, 0, 3, 10442, 212.3449, -414.8659, 111.0348, 0.0, 4, 30000), +(@NPC_REND, 0, 3, 10442, 210.6568, -412.1552, 111.0124, 0.0, 4, 30000), +(@NPC_REND, 0, 4, 10742, 210.2188, -410.6686, 111.0211, 0.0, 4, 30000), +(@NPC_REND, 0, 4, 10447, 209.4078, -414.13, 111.0264, 0.0, 4, 30000), +(@NPC_REND, 0, 4, 10442, 208.0858, -409.3145, 111.0118, 0.0, 4, 30000), +(@NPC_REND, 0, 4, 10442, 207.9811, -413.0728, 111.0098, 0.0, 4, 30000), +(@NPC_REND, 0, 4, 10442, 208.0854, -412.1505, 111.0057, 0.0, 4, 30000), +(@NPC_REND, 0, 5, 10742, 213.9138, -426.512, 111.0013, 0.0, 4, 30000), +(@NPC_REND, 0, 5, 10447, 213.7121, -429.8102, 110.9888, 0.0, 4, 30000), +(@NPC_REND, 0, 5, 10447, 213.7157, -424.4268, 111.009, 0.0, 4, 30000), +(@NPC_REND, 0, 5, 10442, 210.8935, -423.913, 111.0125, 0.0, 4, 30000), +(@NPC_REND, 0, 5, 10442, 212.2642, -430.7648, 110.9807, 0.0, 4, 30000); diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 93c7bdca2..10b4d1ae1 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -135,6 +135,38 @@ Creature* SummonList::GetCreatureWithEntry(uint32 entry) const return nullptr; } +bool SummonList::IsAnyCreatureAlive() const +{ + for (auto const& guid : storage_) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + { + if (summon->IsAlive()) + { + return true; + } + } + } + + return false; +} + +bool SummonList::IsAnyCreatureInCombat() const +{ + for (auto const& guid : storage_) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + { + if (summon->IsInCombat()) + { + return true; + } + } + } + + return false; +} + ScriptedAI::ScriptedAI(Creature* creature) : CreatureAI(creature), me(creature), IsFleeing(false), diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 6e12ea01a..9c5532479 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -89,6 +89,8 @@ public: void Despawn(Creature const* summon) { storage_.remove(summon->GetGUID()); } void DespawnEntry(uint32 entry); void DespawnAll(); + bool IsAnyCreatureAlive() const; + bool IsAnyCreatureInCombat() const; template void DespawnIf(T const& predicate) diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp index 3efa45b5c..24e0461a4 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_gyth.cpp @@ -55,11 +55,9 @@ public: { boss_gythAI(Creature* creature) : BossAI(creature, DATA_GYTH) { } - bool SummonedRend; - void Reset() override { - SummonedRend = false; + _summonedRend = false; if (instance->GetBossState(DATA_GYTH) == IN_PROGRESS) { instance->SetBossState(DATA_GYTH, NOT_STARTED); @@ -78,21 +76,15 @@ public: events.ScheduleEvent(EVENT_KNOCK_AWAY, urand(12000, 18000)); } - void JustDied(Unit* /*killer*/) override + void EnterEvadeMode() override { - instance->SetBossState(DATA_GYTH, DONE); + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); + BossAI::EnterEvadeMode(); } - void SetData(uint32 /*type*/, uint32 data) override + void IsSummonedBy(Unit* /*summoner*/) override { - switch (data) - { - case 1: - events.ScheduleEvent(EVENT_SUMMONED_1, 1000); - break; - default: - break; - } + events.ScheduleEvent(EVENT_SUMMONED_1, 1000); } void JustSummoned(Creature* summon) override @@ -101,15 +93,26 @@ public: summon->AI()->AttackStart(me->SelectVictim()); } - void UpdateAI(uint32 diff) override + // Prevent clearing summon list, otherwise Rend despawns if the drake is killed first. + void JustDied(Unit* /*killer*/) override { } + + void DamageTaken(Unit* /*aggressor*/, uint32& damage, DamageEffectType /*type*/, SpellSchoolMask /*school*/) override { - if (!SummonedRend && HealthBelowPct(25)) + if (!_summonedRend && me->HealthBelowPctDamaged(25, damage)) { + if (damage >= me->GetHealth()) + { + // Let creature fall to 1 HP but prevent it from dying before boss is summoned. + damage = me->GetHealth() - 1; + } DoCast(me, SPELL_SUMMON_REND); me->RemoveAura(SPELL_REND_MOUNTS); - SummonedRend = true; + _summonedRend = true; } + } + void UpdateAI(uint32 diff) override + { if (!UpdateVictim()) { events.Update(diff); @@ -122,8 +125,6 @@ public: me->AddAura(SPELL_REND_MOUNTS, me); if (GameObject* portcullis = me->FindNearestGameObject(GO_DR_PORTCULLIS, 40.0f)) portcullis->UseDoorOrButton(); - if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 75.0f, true)) - victor->AI()->SetData(1, 1); events.ScheduleEvent(EVENT_SUMMONED_2, 2000); break; case EVENT_SUMMONED_2: @@ -164,6 +165,9 @@ public: } DoMeleeAttackIfReady(); } + + private: + bool _summonedRend; }; CreatureAI* GetAI(Creature* creature) const override diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp index 9d50c305c..2a7b438ee 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/boss_rend_blackhand.cpp @@ -64,63 +64,6 @@ enum Misc REND_PATH_2 = 1379681, }; -struct Wave -{ - uint32 entry; - float x_pos; - float y_pos; - float z_pos; -}; - -static Wave Wave1[] = // 22 sec -{ - { 10447, 202.511f, -421.307f, 110.9877f }, - { 10442, 204.015f, -418.443f, 110.989f }, - { 10442, 203.142f, -423.999f, 110.986f }, - { 10442, 201.008f, -416.648f, 110.974f } -}; - -static Wave Wave2[] = // 22 sec -{ - { 10447, 209.8637f, -428.2729f, 110.9877f }, - { 10442, 209.3122f, -430.8724f, 110.9814f }, - { 10442, 211.3309f, -425.9111f, 111.0006f } -}; - -static Wave Wave3[] = // 60 sec -{ - { 10742, 208.6493f, -424.5787f, 110.9872f }, - { 10447, 203.9482f, -428.9446f, 110.982f, }, - { 10442, 203.3441f, -426.8668f, 110.9772f }, - { 10442, 206.3079f, -424.7509f, 110.9943f } -}; - -static Wave Wave4[] = // 49 sec -{ - { 10742, 212.3541f, -412.6826f, 111.0352f }, - { 10447, 212.5754f, -410.2841f, 111.0296f }, - { 10442, 212.3449f, -414.8659f, 111.0348f }, - { 10442, 210.6568f, -412.1552f, 111.0124f } -}; - -static Wave Wave5[] = // 60 sec -{ - { 10742, 210.2188f, -410.6686f, 111.0211f }, - { 10447, 209.4078f, -414.13f, 111.0264f }, - { 10442, 208.0858f, -409.3145f, 111.0118f }, - { 10442, 207.9811f, -413.0728f, 111.0098f }, - { 10442, 208.0854f, -412.1505f, 111.0057f } -}; - -static Wave Wave6[] = // 27 sec -{ - { 10742, 213.9138f, -426.512f, 111.0013f }, - { 10447, 213.7121f, -429.8102f, 110.9888f }, - { 10447, 213.7157f, -424.4268f, 111.009f, }, - { 10442, 210.8935f, -423.913f, 111.0125f }, - { 10442, 212.2642f, -430.7648f, 110.9807f } -}; - /*Position const GythLoc = { 211.762f, -397.5885f, 111.1817f, 4.747295f }; Position const Teleport1Loc = { 194.2993f, -474.0814f, 121.4505f, -0.01225555f }; Position const Teleport2Loc = { 216.485f, -434.93f, 110.888f, -0.01225555f };*/ @@ -136,12 +79,7 @@ enum Events EVENT_TURN_TO_FACING_1 = 7, EVENT_TURN_TO_FACING_2 = 8, EVENT_TURN_TO_FACING_3 = 9, - EVENT_WAVE_1 = 10, - EVENT_WAVE_2 = 11, - EVENT_WAVE_3 = 12, - EVENT_WAVE_4 = 13, - EVENT_WAVE_5 = 14, - EVENT_WAVE_6 = 15, + EVENT_SPAWN_WAVE = 10, EVENT_WAVES_TEXT_1 = 16, EVENT_WAVES_TEXT_2 = 17, EVENT_WAVES_TEXT_3 = 18, @@ -184,6 +122,7 @@ public: gythEvent = false; victorGUID.Clear(); waveDoorGUID.Clear(); + _currentWave = 0; summons.DespawnAll(); @@ -196,13 +135,12 @@ public: instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, NOT_STARTED); } - void SummonWave(Wave* wave, uint32 size) + void SummonedCreatureDies(Creature* /*creature*/, Unit* /*killer*/) override { - for (uint8 i = 0; i < size; ++i) - me->SummonCreature(wave[i].entry, wave[i].x_pos, wave[i].y_pos, wave[i].z_pos, M_PI); - - if (GameObject* waveDoor = me->GetMap()->GetGameObject(waveDoorGUID)) - waveDoor->UseDoorOrButton(); + if (!summons.IsAnyCreatureAlive()) + { + events.ScheduleEvent(EVENT_WAVES_TEXT_1 + _currentWave, 10 * IN_MILLISECONDS); + } } void JustSummoned(Creature* summon) override @@ -211,15 +149,11 @@ public: if (summon->GetEntry() == NPC_GYTH) { - summon->AI()->SetData(1, 1); me->DespawnOrUnsummon(); return; } - if (Unit* target = SelectTargetFromPlayerList(100.0f)) - summon->AI()->AttackStart(target); - else - Reset(); + summon->AI()->DoZoneInCombat(nullptr, 100.0f); } void EnterCombat(Unit* /*who*/) override @@ -230,6 +164,19 @@ public: events.ScheduleEvent(EVENT_MORTAL_STRIKE, urand(17000, 19000)); } + void EnterEvadeMode() override + { + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); + instance->SetBossState(DATA_GYTH, FAIL); + BossAI::EnterEvadeMode(); + me->DespawnOrUnsummon(); + } + + void IsSummonedBy(Unit* /*summoner*/) override + { + Talk(EMOTE_BLACKHAND_DISMOUNT); + } + void JustDied(Unit* /*killer*/) override { _JustDied(); @@ -242,6 +189,16 @@ public: instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, DONE); } + void SummonedCreatureDespawn(Creature* creature) override + { + if (creature->IsAlive() && !summons.IsAnyCreatureInCombat()) + { + instance->SetBossState(DATA_WARCHIEF_REND_BLACKHAND, FAIL); + } + + BossAI::SummonedCreatureDespawn(creature); + } + void SetData(uint32 type, uint32 data) override { if (type == AREATRIGGER && data == AREATRIGGER_BLACKROCK_STADIUM) @@ -250,15 +207,15 @@ public: { gythEvent = true; - Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f, false); + if (Creature* victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f)) + { + if (!victor->IsAlive()) + { + victor->Respawn(true); + } - if (victor) - victor->Respawn(); - else - victor = me->FindNearestCreature(NPC_LORD_VICTOR_NEFARIUS, 5.0f, true); - - if (victor) victorGUID = victor->GetGUID(); + } if (GameObject* portcullis = me->FindNearestGameObject(GO_DR_PORTCULLIS, 50.0f)) waveDoorGUID = portcullis->GetGUID(); @@ -312,9 +269,8 @@ public: case EVENT_START_3: if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) victor->AI()->Talk(SAY_NEFARIUS_1); - events.ScheduleEvent(EVENT_WAVE_1, 2000); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 2000); events.ScheduleEvent(EVENT_TURN_TO_REND, 4000); - events.ScheduleEvent(EVENT_WAVES_TEXT_1, 20000); break; case EVENT_TURN_TO_REND: if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) @@ -352,39 +308,34 @@ public: me->HandleEmoteCommand(EMOTE_ONESHOT_TALK); events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4000); events.ScheduleEvent(EVENT_WAVES_EMOTE_1, 5000); - events.ScheduleEvent(EVENT_WAVE_2, 2000); - events.ScheduleEvent(EVENT_WAVES_TEXT_2, 20000); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3000); break; case EVENT_WAVES_TEXT_2: events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0); if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) victor->AI()->Talk(SAY_NEFARIUS_3); events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4000); - events.ScheduleEvent(EVENT_WAVE_3, 2000); - events.ScheduleEvent(EVENT_WAVES_TEXT_3, 20000); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3000); break; case EVENT_WAVES_TEXT_3: events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0); if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) victor->AI()->Talk(SAY_NEFARIUS_4); events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4000); - events.ScheduleEvent(EVENT_WAVE_4, 2000); - events.ScheduleEvent(EVENT_WAVES_TEXT_4, 20000); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3000); break; case EVENT_WAVES_TEXT_4: Talk(SAY_BLACKHAND_1); events.ScheduleEvent(EVENT_WAVES_EMOTE_2, 4000); events.ScheduleEvent(EVENT_TURN_TO_FACING_3, 8000); - events.ScheduleEvent(EVENT_WAVE_5, 2000); - events.ScheduleEvent(EVENT_WAVES_TEXT_5, 20000); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3000); break; case EVENT_WAVES_TEXT_5: events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0); if (Creature* victor = ObjectAccessor::GetCreature(*me, victorGUID)) victor->AI()->Talk(SAY_NEFARIUS_5); events.ScheduleEvent(EVENT_TURN_TO_FACING_1, 4000); - events.ScheduleEvent(EVENT_WAVE_6, 2000); - events.ScheduleEvent(EVENT_WAVES_COMPLETE_TEXT_1, 20000); + events.ScheduleEvent(EVENT_SPAWN_WAVE, 3000); break; case EVENT_WAVES_COMPLETE_TEXT_1: events.ScheduleEvent(EVENT_TURN_TO_PLAYER, 0); @@ -421,23 +372,8 @@ public: me->NearTeleportTo(216.485f, -434.93f, 110.888f, -0.01225555f); me->SummonCreature(NPC_GYTH, 211.762f, -397.5885f, 111.1817f, 4.747295f); break; - case EVENT_WAVE_1: - SummonWave(Wave1, 4); - break; - case EVENT_WAVE_2: - SummonWave(Wave2, 3); - break; - case EVENT_WAVE_3: - SummonWave(Wave3, 4); - break; - case EVENT_WAVE_4: - SummonWave(Wave4, 4); - break; - case EVENT_WAVE_5: - SummonWave(Wave5, 5); - break; - case EVENT_WAVE_6: - SummonWave(Wave6, 5); + case EVENT_SPAWN_WAVE: + SummonWave(); break; default: break; @@ -474,8 +410,21 @@ public: DoMeleeAttackIfReady(); } + void SummonWave() + { + me->SummonCreatureGroup(_currentWave); + + if (GameObject* waveDoor = me->GetMap()->GetGameObject(waveDoorGUID)) + { + waveDoor->UseDoorOrButton(); + } + + ++_currentWave; + } + private: bool gythEvent; + uint8 _currentWave; ObjectGuid victorGUID; ObjectGuid waveDoorGUID; }; diff --git a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp index 40dc0f11e..74289cbe1 100644 --- a/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp +++ b/src/server/scripts/EasternKingdoms/BlackrockMountain/BlackrockSpire/instance_blackrock_spire.cpp @@ -42,6 +42,11 @@ enum EventIds EVENT_UROK_DOOMHOWL_SPAWN_IN = 8 }; +enum Texts +{ + SAY_NEFARIUS_REND_WIPE = 11 +}; + class instance_blackrock_spire : public InstanceMapScript { public: @@ -104,7 +109,11 @@ public: creature->DisappearAndDie(); break; case NPC_WARCHIEF_REND_BLACKHAND: - WarchiefRendBlackhand = creature->GetGUID(); + if (GetBossState(DATA_GYTH) != IN_PROGRESS) + { + WarchiefRendBlackhand = creature->GetGUID(); + } + if (GetBossState(DATA_GYTH) == DONE) creature->DisappearAndDie(); break; @@ -252,6 +261,19 @@ public: case DATA_OVERLORD_WYRMTHALAK: case DATA_PYROGAURD_EMBERSEER: case DATA_WARCHIEF_REND_BLACKHAND: + if (state == FAIL) + { + if (Creature* rend = instance->GetCreature(WarchiefRendBlackhand)) + { + rend->Respawn(true); + } + + if (Creature* nefarius = instance->GetCreature(LordVictorNefarius)) + { + nefarius->AI()->Talk(SAY_NEFARIUS_REND_WIPE); + } + } + break; case DATA_GYTH: case DATA_THE_BEAST: case DATA_GENERAL_DRAKKISATH: