diff --git a/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp b/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp index 2cb398200..5a4114649 100644 --- a/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp +++ b/scripts/eastern_kingdoms/zulaman/boss_malacrass.cpp @@ -16,8 +16,8 @@ /* ScriptData SDName: Boss_Malacrass -SD%Complete: 10 -SDComment: Contain adds and adds selection +SD%Complete: 70 +SDComment: Contain adds and adds selection; Stolen abilities timers need improvement SDCategory: Zul'Aman EndScriptData */ @@ -38,15 +38,22 @@ enum SAY_ADD_DIED3 = -1568054, SAY_DEATH = -1568055, + /* Notes about the event: + * The boss casts siphon soul right after he finishes the spirit bolts channel, which takes 10 sec + * The siphon soul is a channeled spell for 30 sec during which the boss uses some class abilities of the target + * Basically the boss casts a dummy spell which chooses a random target on which it casts the actuall channel spell + * The drain power spell acts as a enrage timer. It's cast each 30 seconds after the boss' health is below 80% + */ SPELL_SPIRIT_BOLTS = 43383, + SPELL_SIPHON_SOUL_DUMMY = 43498, SPELL_SIPHON_SOUL = 43501, SPELL_DRAIN_POWER = 44131, //for various powers he uses after using soul drain //Death Knight SPELL_DK_DEATH_AND_DECAY = 61603, - SPELL_DK_PLAGUE_STRIKE = 61606, - SPELL_DK_MARK_OF_BLOOD = 61600, + SPELL_DK_PLAGUE_STRIKE = 61600, + SPELL_DK_MARK_OF_BLOOD = 61606, //Druid SPELL_DR_THORNS = 43420, @@ -98,7 +105,11 @@ enum SPELL_WR_SPELL_REFLECT = 43443, //misc - //WEAPON_ID = 33494, //weapon equip id, must be set by database. + TARGET_TYPE_RANDOM = 0, + TARGET_TYPE_VICTIM = 1, + TARGET_TYPE_SELF = 2, + TARGET_TYPE_FRIENDLY = 3, + MAX_ACTIVE_ADDS = 4 }; @@ -123,6 +134,76 @@ SpawnGroup m_auiSpawnEntry[] = {24246, 24247}, //Darkheart / Koragg }; +struct PlayerAbilityStruct +{ + uint32 m_uiSpellId; + uint8 m_uiTargetType; + uint32 m_uiInitialTimer, m_uiCooldown; +}; + +// Classes are in the same order as they are in DBC +static PlayerAbilityStruct m_aMalacrassStolenAbility[][4] = +{ + { // 0* shadow priest - exception: it seems that the priest has two specs. We use this slot for the shadow priest + {SPELL_PR_MIND_CONTROL, TARGET_TYPE_RANDOM, 15000, 30000}, + {SPELL_PR_MIND_BLAST, TARGET_TYPE_RANDOM, 23000, 30000}, + {SPELL_PR_SW_DEATH, TARGET_TYPE_RANDOM, 5000, 16000} + }, + { // 1 warrior + {SPELL_WR_SPELL_REFLECT, TARGET_TYPE_SELF, 2000, 30000}, + {SPELL_WR_WHIRLWIND, TARGET_TYPE_SELF, 10000, 30000}, + {SPELL_WR_MORTAL_STRIKE, TARGET_TYPE_VICTIM, 6000, 15000} + }, + { // 2 paladin + {SPELL_PA_CONSECRATION, TARGET_TYPE_SELF, 10000, 30000}, + {SPELL_PA_HOLY_LIGHT, TARGET_TYPE_FRIENDLY, 17000, 30000}, + {SPELL_PA_AVENGING_WRATH, TARGET_TYPE_SELF, 0, 30000} + }, + { // 3 hunter + {SPELL_HU_EXPLOSIVE_TRAP, TARGET_TYPE_SELF, 12000, 30000}, + {SPELL_HU_FREEZING_TRAP, TARGET_TYPE_SELF, 3000, 30000}, + {SPELL_HU_SNAKE_TRAP, TARGET_TYPE_SELF, 21000, 30000} + }, + { // 4 rogue + {SPELL_RO_WOUND_POISON, TARGET_TYPE_VICTIM, 3000, 17000}, + {SPELL_RO_SLICE_DICE, TARGET_TYPE_SELF, 17000, 30000}, + {SPELL_RO_BLIND, TARGET_TYPE_RANDOM, 12000, 30000} + }, + { // 5 priest + {SPELL_PR_PAIN_SUPP, TARGET_TYPE_FRIENDLY, 24000, 30000}, + {SPELL_PR_HEAL, TARGET_TYPE_FRIENDLY, 16000, 30000}, + {SPELL_PR_PSYCHIC_SCREAM, TARGET_TYPE_RANDOM, 8000, 30000} + }, + { // 6 death knight + {SPELL_DK_DEATH_AND_DECAY, TARGET_TYPE_RANDOM, 25000, 30000}, + {SPELL_DK_PLAGUE_STRIKE, TARGET_TYPE_VICTIM, 5000, 17000}, + {SPELL_DK_MARK_OF_BLOOD, TARGET_TYPE_RANDOM, 14000, 30000} + }, + { // 7 shaman + {SPELL_SH_FIRE_NOVA, TARGET_TYPE_SELF, 25000, 30000}, + {SPELL_SH_HEALING_WAVE, TARGET_TYPE_FRIENDLY, 15000, 30000}, + {SPELL_SH_CHAIN_LIGHT, TARGET_TYPE_RANDOM, 4000, 16000} + }, + { // 8 mage + {SPELL_MG_FIREBALL, TARGET_TYPE_RANDOM, 8000, 30000}, + {SPELL_MG_FROSTBOLT, TARGET_TYPE_RANDOM, 25000, 30000}, + {SPELL_MG_ICE_LANCE, TARGET_TYPE_RANDOM, 2000, 18000}, + {SPELL_MG_FROST_NOVA, TARGET_TYPE_SELF, 17000, 30000} + }, + { // 9 warlock + {SPELL_WL_CURSE_OF_DOOM, TARGET_TYPE_RANDOM, 0, 30000}, + {SPELL_WL_RAIN_OF_FIRE, TARGET_TYPE_RANDOM, 16000, 30000}, + {SPELL_WL_UNSTABLE_AFFL, TARGET_TYPE_RANDOM, 8000, 13000} + }, + { // 10 unused - no class in DBC here + }, + { // 11 druid + {SPELL_DR_LIFEBLOOM, TARGET_TYPE_FRIENDLY, 15000, 30000}, + {SPELL_DR_THORNS, TARGET_TYPE_SELF, 0, 30000}, + {SPELL_DR_MOONFIRE, TARGET_TYPE_RANDOM, 8000, 13000} + } +}; + struct MANGOS_DLL_DECL boss_malacrassAI : public ScriptedAI { boss_malacrassAI(Creature* pCreature) : ScriptedAI(pCreature) @@ -134,17 +215,29 @@ struct MANGOS_DLL_DECL boss_malacrassAI : public ScriptedAI ScriptedInstance* m_pInstance; + uint32 m_uiSpiritBoltsTimer; + uint32 m_uiDrainPowerTimer; + uint32 m_uiSiphonSoulTimer; + uint32 m_uiPlayerAbilityTimer; + uint8 m_uiPlayerClass; + + bool m_bCanUsePlayerSpell; + + std::vector m_vPlayerSpellTimer; std::list m_lAddsEntryList; ObjectGuid m_aAddGuid[MAX_ACTIVE_ADDS]; void Reset() { - InitializeAdds(); + m_uiSpiritBoltsTimer = 30000; + m_uiDrainPowerTimer = 0; + m_uiSiphonSoulTimer = 40000; + m_uiPlayerAbilityTimer = 10000; + m_uiPlayerClass = 0; - if (!m_pInstance) - return; + m_bCanUsePlayerSpell = false; - m_pInstance->SetData(TYPE_MALACRASS, NOT_STARTED); + InitializeAdds(); } void JustReachedHome() @@ -242,6 +335,28 @@ struct MANGOS_DLL_DECL boss_malacrassAI : public ScriptedAI m_pInstance->SetData(TYPE_MALACRASS, DONE); } + void SpellHitTarget(Unit* pTarget, const SpellEntry* pSpell) + { + // Set the player's class when hit with soul siphon + if (pTarget->GetTypeId() == TYPEID_PLAYER && pSpell->Id == SPELL_SIPHON_SOUL) + { + m_uiPlayerClass = ((Player*)pTarget)->getClass(); + m_bCanUsePlayerSpell = true; + + // In case the player it's priest we can choose either a holy priest or a shadow priest + if (m_uiPlayerClass == CLASS_PRIEST) + m_uiPlayerClass = urand(0, 1) ? CLASS_PRIEST : 0; + + // Init the spell timers + uint8 m_uiMaxSpells = m_uiPlayerClass == CLASS_MAGE ? 4 : 3; + + m_vPlayerSpellTimer.clear(); + m_vPlayerSpellTimer.reserve(m_uiMaxSpells); + for (uint8 i = 0; i < m_uiMaxSpells; ++i) + m_vPlayerSpellTimer.push_back(m_aMalacrassStolenAbility[m_uiPlayerClass][i].m_uiInitialTimer); + } + } + void CleanAdds() { for(uint8 i = 0; i < MAX_ACTIVE_ADDS; ++i) @@ -259,11 +374,94 @@ struct MANGOS_DLL_DECL boss_malacrassAI : public ScriptedAI m_lAddsEntryList.clear(); } + bool CanUseSpecialAbility(uint32 uiSpellIndex) + { + Unit* pTarget = NULL; + + switch(m_aMalacrassStolenAbility[m_uiPlayerClass][uiSpellIndex].m_uiTargetType) + { + case TARGET_TYPE_SELF: + pTarget = m_creature; + break; + case TARGET_TYPE_VICTIM: + pTarget = m_creature->getVictim(); + break; + case TARGET_TYPE_RANDOM: + pTarget = m_creature->SelectAttackingTarget(ATTACKING_TARGET_RANDOM, 0); + break; + case TARGET_TYPE_FRIENDLY: + pTarget = DoSelectLowestHpFriendly(50.0f); + break; + } + + if (pTarget) + { + if (DoCastSpellIfCan(pTarget, m_aMalacrassStolenAbility[m_uiPlayerClass][uiSpellIndex].m_uiSpellId, CAST_TRIGGERED) == CAST_OK) + return true; + } + + return false; + } + void UpdateAI(const uint32 uiDiff) { if (!m_creature->SelectHostileTarget() || !m_creature->getVictim()) return; + // Acts as an enrage timer + if (m_creature->GetHealthPercent() < 80.0f) + { + if (m_uiDrainPowerTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_DRAIN_POWER) == CAST_OK) + { + DoScriptText(SAY_DRAIN_POWER, m_creature); + m_uiDrainPowerTimer = 30000; + } + } + else + m_uiDrainPowerTimer -= uiDiff; + } + + if (m_uiSpiritBoltsTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SPIRIT_BOLTS) == CAST_OK) + { + DoScriptText(SAY_SPIRIT_BOLTS, m_creature); + m_bCanUsePlayerSpell = false; + m_uiSpiritBoltsTimer = 40000; + } + } + else + m_uiSpiritBoltsTimer -= uiDiff; + + if (m_uiSiphonSoulTimer < uiDiff) + { + if (DoCastSpellIfCan(m_creature, SPELL_SIPHON_SOUL_DUMMY) == CAST_OK) + { + DoScriptText(SAY_SOUL_SIPHON, m_creature); + m_uiSiphonSoulTimer = 40000; + } + } + else + m_uiSiphonSoulTimer -= uiDiff; + + // Use abilities only during the siphon soul phases + if (m_bCanUsePlayerSpell) + { + // Loop through all abilities + for (uint8 i = 0; i < m_vPlayerSpellTimer.size(); ++i) + { + if (m_vPlayerSpellTimer[i] < uiDiff) + { + if (CanUseSpecialAbility(i)) + m_vPlayerSpellTimer[i] = m_aMalacrassStolenAbility[m_uiPlayerClass][i].m_uiCooldown; + } + else + m_vPlayerSpellTimer[i] -= uiDiff; + } + } + DoMeleeAttackIfReady(); } }; @@ -817,50 +1015,50 @@ CreatureAI* GetAI_mob_koragg(Creature* pCreature) void AddSC_boss_malacrass() { - Script* newscript; - - newscript = new Script; - newscript->Name = "boss_malacrass"; - newscript->GetAI = &GetAI_boss_malacrass; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_thurg"; - newscript->GetAI = &GetAI_mob_thurg; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_gazakroth"; - newscript->GetAI = &GetAI_mob_gazakroth; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_lord_raadan"; - newscript->GetAI = &GetAI_mob_lord_raadan; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_darkheart"; - newscript->GetAI = &GetAI_mob_darkheart; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_slither"; - newscript->GetAI = &GetAI_mob_slither; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_fenstalker"; - newscript->GetAI = &GetAI_mob_fenstalker; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_koragg"; - newscript->GetAI = &GetAI_mob_koragg; - newscript->RegisterSelf(); - - newscript = new Script; - newscript->Name = "mob_alyson_antille"; - newscript->GetAI = &GetAI_mob_alyson_antille; - newscript->RegisterSelf(); + Script* pNewScript; + + pNewScript = new Script; + pNewScript->Name = "boss_malacrass"; + pNewScript->GetAI = &GetAI_boss_malacrass; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_thurg"; + pNewScript->GetAI = &GetAI_mob_thurg; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_gazakroth"; + pNewScript->GetAI = &GetAI_mob_gazakroth; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_lord_raadan"; + pNewScript->GetAI = &GetAI_mob_lord_raadan; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_darkheart"; + pNewScript->GetAI = &GetAI_mob_darkheart; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_slither"; + pNewScript->GetAI = &GetAI_mob_slither; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_fenstalker"; + pNewScript->GetAI = &GetAI_mob_fenstalker; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_koragg"; + pNewScript->GetAI = &GetAI_mob_koragg; + pNewScript->RegisterSelf(); + + pNewScript = new Script; + pNewScript->Name = "mob_alyson_antille"; + pNewScript->GetAI = &GetAI_mob_alyson_antille; + pNewScript->RegisterSelf(); } diff --git a/sd2_revision_nr.h b/sd2_revision_nr.h index 43bff2bf9..e945a9a6b 100644 --- a/sd2_revision_nr.h +++ b/sd2_revision_nr.h @@ -1,4 +1,4 @@ #ifndef __SD2_REVISION_NR_H__ #define __SD2_REVISION_NR_H__ - #define SD2_REVISION_NR "2287" + #define SD2_REVISION_NR "2288" #endif // __SD2_REVISION_NR_H__