diff --git a/data/sql/updates/pending_db_world/rev_1663022353547851500.sql b/data/sql/updates/pending_db_world/rev_1663022353547851500.sql new file mode 100644 index 000000000..fd8b41ff3 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1663022353547851500.sql @@ -0,0 +1,41 @@ +-- +DELETE FROM `spell_target_position` WHERE `ID` IN (25865, 25866, 25867, 25868, 25869, 25870, 25871, 25872, 25873, 25874, 25875, 25876, 25877, 25878, 25879, 25880, 25881, 25882, 25883, 25884); +INSERT INTO `spell_target_position` (`ID`, `MapID`, `PositionX`, `PositionY`, `PositionZ`, `Orientation`) VALUES +(25865, 531, -8023.59, 964.772, -41.229, 5.376), +(25866, 531, -8042.13, 911.263, -42.841, 6.200), +(25867, 531, -7999.36, 860.525, -47.206, 1.417), +(25868, 531, -7971.30, 862.581, -48.099, 2.148), +(25869, 531, -7943.21, 903.804, -48.473, 3.078), +(25870, 531, -7954.38, 958.562, -41.609, 3.962), +(25871, 531, -7997.19, 979.192, -41.653, 4.896), +(25872, 531, -8037.89, 929.679, -43.416, 6.024), +(25873, 531, -8015.41, 867.734, -45.607, 1.103), +(25874, 531, -7982.80, 857.172, -48.212, 1.500), +(25875, 531, -7952.12, 883.183, -48.194, 2.430), +(25876, 531, -7947.22, 939.122, -44.014, 3.718), +(25877, 531, -7975.77, 974.820, -41.584, 4.417), +(25878, 531, -8032.75, 948.274, -41.919, 5.595), +(25879, 531, -8037.08, 887.268, -43.581, 0.675), +(25880, 531, -7992.21, 857.500, -47.762, 1.664), +(25881, 531, -7960.71, 872.483, -48.759, 2.360), +(25882, 531, -7942.98, 918.616, -46.401, 3.306), +(25883, 531, -7964.32, 967.879, -42.112, 4.087), +(25884, 531, -8015.24, 976.553, -41.647, 4.947); + +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_explode_trigger' AND `spell_id` = 25938; +DELETE FROM `spell_script_names` WHERE `ScriptName` = 'spell_summon_toxin_slime' AND `spell_id` = 26584; +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(25938, 'spell_explode_trigger'), +(26584, 'spell_summon_toxin_slime'); + +UPDATE `creature_template` SET `flags_extra` = `flags_extra`|1073741824 WHERE `entry` = 15667; + +UPDATE `creature_template_addon` SET `auras` = '25994' WHERE `entry` = 15299; + +UPDATE `creature_template` SET `modelid1` = 19595, `unit_flags` = `unit_flags`|33554432, `flags_extra` = `flags_extra`|64, `AIName` = '', `ScriptName` = 'npc_toxic_slime' WHERE `entry` = 15925; + +DELETE FROM `creature_template_movement` WHERE `CreatureId` = 15925; +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`, `Chase`, `Random`, `InteractionPauseTimer`) VALUES +(15925, 0, 0, 0, 1, 0, 0, 0); + +UPDATE `spell_dbc` SET `Effect_1` = 28, `EffectBasePoints_1` = 1, `EffectDieSides_1` = 0, `EffectMiscValueB_1` = 64 WHERE `ID` = 26577; diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp index 85b3c39f5..e1c6eedca 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.cpp @@ -153,6 +153,22 @@ bool SummonList::IsAnyCreatureAlive() const return false; } +bool SummonList::IsAnyCreatureWithEntryAlive(uint32 entry) const +{ + for (auto const& guid : storage_) + { + if (Creature* summon = ObjectAccessor::GetCreature(*me, guid)) + { + if (summon->GetEntry() == entry && summon->IsAlive()) + { + return true; + } + } + } + + return false; +} + bool SummonList::IsAnyCreatureInCombat() const { for (auto const& guid : storage_) diff --git a/src/server/game/AI/ScriptedAI/ScriptedCreature.h b/src/server/game/AI/ScriptedAI/ScriptedCreature.h index 0114a785b..bf6a86d4d 100644 --- a/src/server/game/AI/ScriptedAI/ScriptedCreature.h +++ b/src/server/game/AI/ScriptedAI/ScriptedCreature.h @@ -91,6 +91,7 @@ public: void DespawnEntry(uint32 entry); void DespawnAll(); bool IsAnyCreatureAlive() const; + bool IsAnyCreatureWithEntryAlive(uint32 entry) const; bool IsAnyCreatureInCombat() const; template diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_viscidus.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_viscidus.cpp index e5e0834d1..857b41192 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_viscidus.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/boss_viscidus.cpp @@ -17,34 +17,37 @@ #include "ScriptMgr.h" #include "ScriptedCreature.h" -#include "SpellInfo.h" +#include "SpellScript.h" +#include "TaskScheduler.h" #include "temple_of_ahnqiraj.h" enum Spells { + // Viscidus - Glob of Viscidus SPELL_POISON_SHOCK = 25993, SPELL_POISONBOLT_VOLLEY = 25991, - SPELL_TOXIN = 26575, + SPELL_SUMMON_TOXIN_SLIME = 26584, + SPELL_SUMMON_TOXIN_SLIME_2 = 26577, SPELL_VISCIDUS_SLOWED = 26034, SPELL_VISCIDUS_SLOWED_MORE = 26036, SPELL_VISCIDUS_FREEZE = 25937, SPELL_REJOIN_VISCIDUS = 25896, - SPELL_VISCIDUS_EXPLODE = 25938, - SPELL_VISCIDUS_SUICIDE = 26003, - SPELL_VISCIDUS_SHRINKS = 25893, // Removed from client, in world.spell_dbc + SPELL_EXPLODE_TRIGGER = 25938, + SPELL_VISCIDUS_SHRINKS = 25893, // Server-side + SPELL_INVIS_SELF = 25905, + SPELL_VISCIDUS_GROWS = 25897, + SPELL_STUN_SELF = 25900, - SPELL_MEMBRANE_VISCIDUS = 25994, // damage reduction spell - removed from DBC - SPELL_VISCIDUS_WEAKNESS = 25926, // aura which procs at damage - should trigger the slow spells - removed from DBC - SPELL_VISCIDUS_GROWS = 25897, // removed from DBC - SPELL_SUMMON_GLOBS = 25885, // summons npc 15667 using spells from 25865 to 25884; All spells have target coords - removed from DBC - SPELL_VISCIDUS_TELEPORT = 25904, // removed from DBC + // Toxic slime + SPELL_TOXIN = 26575, }; enum Events { EVENT_POISONBOLT_VOLLEY = 1, EVENT_POISON_SHOCK = 2, - EVENT_RESET_PHASE = 3 + EVENT_TOXIN = 3, + EVENT_RESET_PHASE = 4 }; enum Phases @@ -81,52 +84,98 @@ enum MovePoints ROOM_CENTER = 1 }; -Position const ViscidusCoord = { -7992.36f, 908.19f, -52.62f, 1.68f }; /// @todo Visci isn't in room middle -float const RoomRadius = 40.0f; /// @todo Not sure if its correct +enum Misc +{ + MAX_GLOB_SPAWN = 20, +}; + +Position const roomCenter = { -7992.36f, 908.19f, -52.62f, 1.68f }; + +Position const resetPoint = { -7992.0f, 1041.0f, -23.84f }; + +std::array const spawnGlobSpells = { 25865, 25866, 25867, 25868, 25869, 25870, 25871, 25872, 25873, 25874, 25875, 25876, 25877, 25878, 25879, 25880, 25881, 25882, 25883, 25884 }; struct boss_viscidus : public BossAI { - boss_viscidus(Creature* creature) : BossAI(creature, DATA_VISCIDUS) { } + boss_viscidus(Creature* creature) : BossAI(creature, DATA_VISCIDUS) + { + me->LowerPlayerDamageReq(me->GetMaxHealth()); + } + + bool CheckInRoom() override + { + if (me->GetExactDist2d(resetPoint) <= 10.f) + { + EnterEvadeMode(EVADE_REASON_BOUNDARY); + return false; + } + + return true; + } void Reset() override { - _Reset(); - _hitcounter = 0; - _phase = PHASE_FROST; + BossAI::Reset(); + events.Reset(); + SoftReset(); + _scheduler.CancelAll(); + me->RemoveAurasDueToSpell(SPELL_VISCIDUS_SHRINKS); } - void DamageTaken(Unit* attacker, uint32& /*damage*/, DamageEffectType, SpellSchoolMask) override + void SoftReset() { + _hitcounter = 0; + me->RemoveAurasDueToSpell(SPELL_STUN_SELF); + me->SetReactState(REACT_AGGRESSIVE); + _phase = PHASE_FROST; + me->RemoveAurasDueToSpell(SPELL_INVIS_SELF); + } + + void DamageTaken(Unit* attacker, uint32& damage, DamageEffectType effType, SpellSchoolMask) override + { + if (me->HealthBelowPct(5)) + damage = 0; + if (!attacker || _phase != PHASE_MELEE) return; - ++_hitcounter; + if (effType == DIRECT_DAMAGE) + ++_hitcounter; if (attacker->HasUnitState(UNIT_STATE_MELEE_ATTACKING) && _hitcounter >= HITCOUNTER_EXPLODE) { + if (me->GetHealthPct() <= 5.f) + { + Unit::Kill(attacker, me); + return; + } + Talk(EMOTE_EXPLODE); + me->SetReactState(REACT_PASSIVE); events.Reset(); _phase = PHASE_GLOB; - DoCast(me, SPELL_VISCIDUS_EXPLODE); - me->SetVisible(false); - me->RemoveAura(SPELL_TOXIN); me->RemoveAura(SPELL_VISCIDUS_FREEZE); - - uint8 NumGlobes = me->GetHealthPct() / 5.0f; - for (uint8 i = 0; i < NumGlobes; ++i) - { - float Angle = i * 2 * M_PI / NumGlobes; - float X = ViscidusCoord.GetPositionX() + std::cos(Angle) * RoomRadius; - float Y = ViscidusCoord.GetPositionY() + std::sin(Angle) * RoomRadius; - float Z = -35.0f; - - if (TempSummon* Glob = me->SummonCreature(NPC_GLOB_OF_VISCIDUS, X, Y, Z)) + DoCastSelf(SPELL_STUN_SELF, true); + me->AttackStop(); + me->CastStop(); + me->HandleEmoteCommand(EMOTE_ONESHOT_FLYDEATH); // not found in sniff, this is the best one I found + _scheduler + .Schedule(2500ms, [this](TaskContext /*context*/) { - Glob->UpdateAllowedPositionZ(X, Y, Z); - Glob->NearTeleportTo(X, Y, Z, 0.0f); - Glob->GetMotionMaster()->MovePoint(ROOM_CENTER, ViscidusCoord); - } - } + DoCastSelf(SPELL_EXPLODE_TRIGGER, true); + }) + .Schedule(3000ms, [this](TaskContext /*context*/) + { + DoCastSelf(SPELL_INVIS_SELF, true); + me->SetAuraStack(SPELL_VISCIDUS_SHRINKS, me, 20); + me->LowerPlayerDamageReq(me->GetMaxHealth()); + me->SetHealth(me->GetMaxHealth() * 0.01f); // set 1% health + DoResetThreat(); + me->NearTeleportTo(roomCenter.GetPositionX(), + roomCenter.GetPositionY(), + roomCenter.GetPositionZ(), + roomCenter.GetOrientation()); + }); } else if (_hitcounter == HITCOUNTER_SHATTER) Talk(EMOTE_SHATTER); @@ -136,7 +185,12 @@ struct boss_viscidus : public BossAI void SpellHit(Unit* /*caster*/, SpellInfo const* spell) override { - if ((spell->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) && _phase == PHASE_FROST && me->GetHealthPct() > 5.0f) + if (spell->Id == SPELL_REJOIN_VISCIDUS) + { + me->RemoveAuraFromStack(SPELL_VISCIDUS_SHRINKS); + } + + if ((spell->GetSchoolMask() & SPELL_SCHOOL_MASK_FROST) && _phase == PHASE_FROST) { ++_hitcounter; @@ -145,85 +199,89 @@ struct boss_viscidus : public BossAI _hitcounter = 0; Talk(EMOTE_FROZEN); _phase = PHASE_MELEE; - DoCast(me, SPELL_VISCIDUS_FREEZE); me->RemoveAura(SPELL_VISCIDUS_SLOWED_MORE); - events.ScheduleEvent(EVENT_RESET_PHASE, 15000); + DoCastSelf(SPELL_VISCIDUS_FREEZE); + events.ScheduleEvent(EVENT_RESET_PHASE, 15s); } - else if (_hitcounter >= HITCOUNTER_SLOW_MORE) + else if (_hitcounter == HITCOUNTER_SLOW_MORE) { Talk(EMOTE_FREEZE); me->RemoveAura(SPELL_VISCIDUS_SLOWED); - DoCast(me, SPELL_VISCIDUS_SLOWED_MORE); + DoCastSelf(SPELL_VISCIDUS_SLOWED_MORE); } - else if (_hitcounter >= HITCOUNTER_SLOW) + else if (_hitcounter == HITCOUNTER_SLOW) { Talk(EMOTE_SLOW); - DoCast(me, SPELL_VISCIDUS_SLOWED); + DoCastSelf(SPELL_VISCIDUS_SLOWED); } } } - void EnterCombat(Unit* /*who*/) override + void SummonedCreatureDies(Creature* summon, Unit* killer) override { - _EnterCombat(); - events.Reset(); + if (summon->GetEntry() != NPC_GLOB_OF_VISCIDUS) + return; + + if (killer && killer->GetEntry() == NPC_GLOB_OF_VISCIDUS) + { + if (_phase == PHASE_GLOB) + { + _phase = PHASE_FROST; + me->RemoveAurasDueToSpell(SPELL_INVIS_SELF); + } + + int32 heal = me->GetMaxHealth() * 0.05f; + me->CastCustomSpell(me, SPELL_VISCIDUS_GROWS, &heal, nullptr, nullptr, true); + } + + if (!summons.IsAnyCreatureWithEntryAlive(NPC_GLOB_OF_VISCIDUS)) // all globs were killed + { + SoftReset(); + InitSpells(); + me->LowerPlayerDamageReq(me->GetMaxHealth()); + } + } + + void EnterCombat(Unit* who) override + { + BossAI::EnterCombat(who); InitSpells(); } void InitSpells() { - DoCast(me, SPELL_TOXIN); + events.ScheduleEvent(EVENT_TOXIN, 15s, 20s); events.ScheduleEvent(EVENT_POISONBOLT_VOLLEY, 10s, 15s); events.ScheduleEvent(EVENT_POISON_SHOCK, 7s, 12s); } - void EnterEvadeMode(EvadeReason why) override - { - summons.DespawnAll(); - ScriptedAI::EnterEvadeMode(why); - } - - void JustDied(Unit* /*killer*/) override - { - DoCast(me, SPELL_VISCIDUS_SUICIDE); - summons.DespawnAll(); - } - void UpdateAI(uint32 diff) override { - if (!UpdateVictim()) + if (!UpdateVictim() || !CheckInRoom()) return; - if (_phase == PHASE_GLOB && summons.empty()) - { - DoResetThreat(); - me->NearTeleportTo(ViscidusCoord.GetPositionX(), - ViscidusCoord.GetPositionY(), - ViscidusCoord.GetPositionZ(), - ViscidusCoord.GetOrientation()); - - _hitcounter = 0; - _phase = PHASE_FROST; - InitSpells(); - me->SetVisible(true); - } - events.Update(diff); + _scheduler.Update(diff); while (uint32 eventId = events.ExecuteEvent()) { switch (eventId) { case EVENT_POISONBOLT_VOLLEY: - DoCast(me, SPELL_POISONBOLT_VOLLEY); + DoCastSelf(SPELL_POISONBOLT_VOLLEY); events.ScheduleEvent(EVENT_POISONBOLT_VOLLEY, 10s, 15s); break; case EVENT_POISON_SHOCK: - DoCast(me, SPELL_POISON_SHOCK); + DoCastSelf(SPELL_POISON_SHOCK); events.ScheduleEvent(EVENT_POISON_SHOCK, 7s, 12s); break; + case EVENT_TOXIN: + DoCastRandomTarget(SPELL_SUMMON_TOXIN_SLIME); + events.ScheduleEvent(EVENT_TOXIN, 15s, 20s); + break; case EVENT_RESET_PHASE: _hitcounter = 0; + me->RemoveAura(SPELL_VISCIDUS_FREEZE); _phase = PHASE_FROST; break; default: @@ -231,54 +289,118 @@ struct boss_viscidus : public BossAI } } - if (_phase != PHASE_GLOB) + if (_phase != PHASE_GLOB && me->GetReactState() == REACT_AGGRESSIVE) DoMeleeAttackIfReady(); } private: uint8 _hitcounter; - Phases _phase; + uint8 _phase; + TaskScheduler _scheduler; }; struct boss_glob_of_viscidus : public ScriptedAI { - boss_glob_of_viscidus(Creature* creature) : ScriptedAI(creature) { } - - void JustDied(Unit* /*killer*/) override + boss_glob_of_viscidus(Creature* creature) : ScriptedAI(creature) { - InstanceScript* instance = me->GetInstanceScript(); + me->SetReactState(REACT_PASSIVE); + } - if (Creature* viscidus = me->GetMap()->GetCreature(instance->GetGuidData(DATA_VISCIDUS))) - { - if (BossAI* viscidusAI = dynamic_cast(viscidus->GetAI())) - viscidusAI->SummonedCreatureDespawn(me); - - if (viscidus->IsAlive() && viscidus->GetHealthPct() < 5.0f) + void InitializeAI() override + { + me->SetInCombatWithZone(); + _scheduler.CancelAll(); + _scheduler.Schedule(2400ms, [this](TaskContext context) { - viscidus->SetVisible(true); - Unit::Kill(viscidus->GetVictim(), viscidus); - } - else - { - viscidus->SetHealth(viscidus->GetHealth() - viscidus->GetMaxHealth() / 20); - viscidus->CastSpell(viscidus, SPELL_VISCIDUS_SHRINKS); - } - } + me->GetMotionMaster()->MovePoint(ROOM_CENTER, roomCenter); + float topSpeed = me->GetSpeedRate(MOVE_RUN) + 0.2142855f * 4; + context.Schedule(1s, [this, topSpeed](TaskContext context) + { + float newSpeed = me->GetSpeedRate(MOVE_RUN) + 0.2142855f; // sniffed + me->SetSpeed(MOVE_RUN, newSpeed < topSpeed ? newSpeed : topSpeed); + context.Repeat(); + }); + }); } void MovementInform(uint32 /*type*/, uint32 id) override { if (id == ROOM_CENTER) { - DoCast(me, SPELL_REJOIN_VISCIDUS); - if (TempSummon* summon = me->ToTempSummon()) - summon->UnSummon(); + DoCastSelf(SPELL_REJOIN_VISCIDUS); + Unit::Kill(me, me); } } + + void UpdateAI(uint32 diff) override + { + _scheduler.Update(diff); + } + +protected: + TaskScheduler _scheduler; +}; + +struct npc_toxic_slime : public ScriptedAI +{ + npc_toxic_slime(Creature* creature) : ScriptedAI(creature) + { + me->SetReactState(REACT_PASSIVE); + } + + void InitializeAI() override + { + SetCombatMovement(false); + DoCastSelf(SPELL_TOXIN); + + InstanceScript* instance = me->GetInstanceScript(); + + if (Creature* viscidus = instance->GetCreature(DATA_VISCIDUS)) + if (viscidus->AI()) + viscidus->AI()->JustSummoned(me); + } +}; + +class spell_explode_trigger : public SpellScript +{ + PrepareSpellScript(spell_explode_trigger); + + void HandleOnHit() + { + Unit* caster = GetCaster(); + + uint8 globsToSpawn = std::floor(caster->GetHealthPct() / 5.f); + for (uint8 i = 0; i < globsToSpawn; i++) + caster->CastSpell((Unit*)nullptr, spawnGlobSpells[i], true); + } + + void Register() override + { + OnHit += SpellHitFn(spell_explode_trigger::HandleOnHit); + } +}; + +class spell_summon_toxin_slime : public SpellScript +{ + PrepareSpellScript(spell_summon_toxin_slime); + + void HandleOnHit() + { + if (Unit* target = GetHitUnit()) + target->CastSpell(target, SPELL_SUMMON_TOXIN_SLIME_2, true); + } + + void Register() override + { + OnHit += SpellHitFn(spell_summon_toxin_slime::HandleOnHit); + } }; void AddSC_boss_viscidus() { RegisterTempleOfAhnQirajCreatureAI(boss_viscidus); RegisterTempleOfAhnQirajCreatureAI(boss_glob_of_viscidus); + RegisterTempleOfAhnQirajCreatureAI(npc_toxic_slime); + RegisterSpellScript(spell_explode_trigger); + RegisterSpellScript(spell_summon_toxin_slime); } diff --git a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp index bf68a1a76..350c489d5 100644 --- a/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp +++ b/src/server/scripts/Kalimdor/TempleOfAhnQiraj/instance_temple_of_ahnqiraj.cpp @@ -28,7 +28,8 @@ ObjectData const creatureData[] = { NPC_OURO_SPAWNER, DATA_OURO_SPAWNER }, { NPC_MASTERS_EYE, DATA_MASTERS_EYE }, { NPC_VEKLOR, DATA_VEKLOR }, - { NPC_VEKNILASH, DATA_VEKNILASH } + { NPC_VEKNILASH, DATA_VEKNILASH }, + { NPC_VISCIDUS, DATA_VISCIDUS } }; DoorData const doorData[] = @@ -62,7 +63,6 @@ public: ObjectGuid VemGUID; ObjectGuid KriGUID; ObjectGuid YaujGUID; - ObjectGuid ViscidusGUID; ObjectGuid CThunGUID; GuidVector CThunGraspGUIDs; @@ -91,9 +91,6 @@ public: case NPC_YAUJ: YaujGUID = creature->GetGUID(); break; - case NPC_VISCIDUS: - ViscidusGUID = creature->GetGUID(); - break; case NPC_OURO_SPAWNER: if (GetBossState(DATA_OURO) != DONE) creature->Respawn(); @@ -168,8 +165,6 @@ public: return KriGUID; case DATA_YAUJ: return YaujGUID; - case DATA_VISCIDUS: - return ViscidusGUID; } return ObjectGuid::Empty; }