diff options
author | Duarte Duarte <dnpd.dd@gmail.com> | 2014-07-19 22:03:58 +0100 |
---|---|---|
committer | Duarte Duarte <dnpd.dd@gmail.com> | 2014-07-19 22:03:58 +0100 |
commit | 40b4bd2eda333e90dd0ee1a1b6a6e939c10a6985 (patch) | |
tree | 3145b84599d730d45a69d002150ed1ec3b222dfe /src | |
parent | b153e1a47f7327f3002e26da265c3b656a969e76 (diff) | |
parent | 6fdcefee31a2511cc5a21b42c53bd680e0fdc743 (diff) |
Merge pull request #12500 from MitchesD/misc_3
Scripts/MagistersTerrace: reworked Selin Fireheart
Diffstat (limited to 'src')
3 files changed, 183 insertions, 274 deletions
diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp index d6c0f95f967..d34123c58b2 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/boss_selin_fireheart.cpp @@ -1,6 +1,5 @@ /* * Copyright (C) 2008-2014 TrinityCore <http://www.trinitycore.org/> - * Copyright (C) 2006-2009 ScriptDev2 <https://scriptdev2.svn.sourceforge.net/> * * This program is free software; you can redistribute it and/or modify it * under the terms of the GNU General Public License as published by the @@ -16,13 +15,6 @@ * with this program. If not, see <http://www.gnu.org/licenses/>. */ -/* ScriptData -SDName: Boss_Selin_Fireheart -SD%Complete: 90 -SDComment: Heroic and Normal Support. Needs further testing. -SDCategory: Magister's Terrace -EndScriptData */ - #include "ScriptMgr.h" #include "ScriptedCreature.h" #include "magisters_terrace.h" @@ -39,321 +31,264 @@ enum Says enum Spells { - //Crystal effect spells - SPELL_FEL_CRYSTAL_COSMETIC = 44374, + // Crystal effect spells SPELL_FEL_CRYSTAL_DUMMY = 44329, - SPELL_FEL_CRYSTAL_VISUAL = 44355, SPELL_MANA_RAGE = 44320, // This spell triggers 44321, which changes scale and regens mana Requires an entry in spell_script_target - //Selin's spells + // Selin's spells SPELL_DRAIN_LIFE = 44294, SPELL_FEL_EXPLOSION = 44314, SPELL_DRAIN_MANA = 46153 // Heroic only }; +enum Phases +{ + PHASE_NORMAL = 1, + PHASE_DRAIN = 2 +}; + +enum Events +{ + EVENT_FEL_EXPLOSION = 1, + EVENT_DRAIN_CRYSTAL, + EVENT_DRAIN_MANA, + EVENT_DRAIN_LIFE, + EVENT_EMPOWER +}; + enum Misc { - CRYSTALS_NUMBER = 5, - DATA_CRYSTALS = 6, - CREATURE_FEL_CRYSTAL = 24722 + ACTION_SWITCH_PHASE = 1 }; class boss_selin_fireheart : public CreatureScript { -public: - boss_selin_fireheart() : CreatureScript("boss_selin_fireheart") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return GetInstanceAI<boss_selin_fireheartAI>(creature); - }; + public: + boss_selin_fireheart() : CreatureScript("boss_selin_fireheart") { } - struct boss_selin_fireheartAI : public ScriptedAI - { - boss_selin_fireheartAI(Creature* creature) : ScriptedAI(creature) + struct boss_selin_fireheartAI : public BossAI { - instance = creature->GetInstanceScript(); - - Crystals.clear(); - //GUIDs per instance is static, so we only need to load them once. - uint32 size = instance->GetData(DATA_FEL_CRYSTAL_SIZE); - for (uint8 i = 0; i < size; ++i) + boss_selin_fireheartAI(Creature* creature) : BossAI(creature, DATA_SELIN) { - instance->SetData64(DATA_FEL_CRYSTAL, i); - uint64 guid = instance->GetData64(DATA_FEL_CRYSTAL); - TC_LOG_DEBUG("scripts", "Selin: Adding Fel Crystal " UI64FMTD " to list", guid); - Crystals.push_back(guid); + CrystalGUID = 0; + _scheduledEvents = false; } - } - - InstanceScript* instance; - - std::list<uint64> Crystals; - uint32 DrainLifeTimer; - uint32 DrainManaTimer; - uint32 FelExplosionTimer; - uint32 DrainCrystalTimer; - uint32 EmpowerTimer; - - bool IsDraining; - bool DrainingCrystal; - - uint64 CrystalGUID; // This will help us create a pointer to the crystal we are draining. We store GUIDs, never units in case unit is deleted/offline (offline if player of course). - - void Reset() override - { - //for (uint8 i = 0; i < CRYSTALS_NUMBER; ++i) - for (std::list<uint64>::const_iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr) + void Reset() override { - //Unit* unit = ObjectAccessor::GetUnit(*me, FelCrystals[i]); - if (Creature* creature = ObjectAccessor::GetCreature(*me, *itr)) + Crystals.clear(); + me->GetCreatureListWithEntryInGrid(Crystals, NPC_FEL_CRYSTAL, 250.0f); + + for (Creature* creature : Crystals) { if (!creature->IsAlive()) - creature->Respawn(); // Let the core handle setting death state, etc. + creature->Respawn(); - // Only need to set unselectable flag. You can't attack unselectable units so non_attackable flag is not necessary here. creature->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); } - } - // Set Inst data for encounter - instance->SetBossState(DATA_SELIN, NOT_STARTED); - - DrainLifeTimer = urand(3000, 7000); - DrainManaTimer = DrainLifeTimer + 5000; - FelExplosionTimer = 2100; - if (IsHeroic()) - DrainCrystalTimer = urand(10000, 15000); - else - DrainCrystalTimer = urand(20000, 25000); - EmpowerTimer = 10000; - - IsDraining = false; - DrainingCrystal = false; - CrystalGUID = 0; - } + _Reset(); + CrystalGUID = 0; + _scheduledEvents = false; + } - void SelectNearestCrystal() - { - if (Crystals.empty()) - return; - - //float ShortestDistance = 0; - CrystalGUID = 0; - Unit* pCrystal = NULL; - Unit* CrystalChosen = NULL; - //for (uint8 i = 0; i < CRYSTALS_NUMBER; ++i) - for (std::list<uint64>::const_iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr) + void DoAction(int32 action) override { - pCrystal = NULL; - //pCrystal = ObjectAccessor::GetUnit(*me, FelCrystals[i]); - pCrystal = ObjectAccessor::GetUnit(*me, *itr); - if (pCrystal && pCrystal->IsAlive()) + switch (action) { - // select nearest - if (!CrystalChosen || me->GetDistanceOrder(pCrystal, CrystalChosen, false)) - { - CrystalGUID = pCrystal->GetGUID(); - CrystalChosen = pCrystal; // Store a copy of pCrystal so we don't need to recreate a pointer to closest crystal for the movement and yell. - } + case ACTION_SWITCH_PHASE: + events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_FEL_EXPLOSION, 2000, 0, PHASE_NORMAL); + AttackStart(me->GetVictim()); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + break; + default: + break; } } - if (CrystalChosen) + + void SelectNearestCrystal() { - Talk(SAY_ENERGY); - Talk(EMOTE_CRYSTAL); + if (Crystals.empty()) + return; - CrystalChosen->CastSpell(CrystalChosen, SPELL_FEL_CRYSTAL_COSMETIC, true); + Crystals.sort(Trinity::ObjectDistanceOrderPred(me)); + if (Creature* CrystalChosen = Crystals.front()) + { + Talk(SAY_ENERGY); + Talk(EMOTE_CRYSTAL); - float x, y, z; // coords that we move to, close to the crystal. - CrystalChosen->GetClosePoint(x, y, z, me->GetObjectSize(), CONTACT_DISTANCE); + DoCast(CrystalChosen, SPELL_FEL_CRYSTAL_DUMMY); + CrystalGUID = CrystalChosen->GetGUID(); + Crystals.remove(CrystalChosen); - me->SetWalk(false); - me->GetMotionMaster()->MovePoint(1, x, y, z); - DrainingCrystal = true; - } - } + float x, y, z; + CrystalChosen->GetClosePoint(x, y, z, me->GetObjectSize(), CONTACT_DISTANCE); - void ShatterRemainingCrystals() - { - if (Crystals.empty()) - return; + events.SetPhase(PHASE_DRAIN); + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(1, x, y, z); + } + } - //for (uint8 i = 0; i < CRYSTALS_NUMBER; ++i) - for (std::list<uint64>::const_iterator itr = Crystals.begin(); itr != Crystals.end(); ++itr) + void ShatterRemainingCrystals() { - //Creature* pCrystal = (ObjectAccessor::GetCreature(*me, FelCrystals[i])); - Creature* pCrystal = ObjectAccessor::GetCreature(*me, *itr); - if (pCrystal && pCrystal->IsAlive()) - pCrystal->Kill(pCrystal); + if (Crystals.empty()) + return; + + for (Creature* crystal : Crystals) + { + if (crystal && crystal->IsAlive()) + crystal->Kill(crystal); + } } - } - void EnterCombat(Unit* /*who*/) override - { - Talk(SAY_AGGRO); - instance->SetBossState(DATA_SELIN, IN_PROGRESS); - } + void EnterCombat(Unit* /*who*/) override + { + Talk(SAY_AGGRO); + _EnterCombat(); - void KilledUnit(Unit* /*victim*/) override - { - Talk(SAY_KILL); - } + events.SetPhase(PHASE_NORMAL); + events.ScheduleEvent(EVENT_FEL_EXPLOSION, 2100, 0, PHASE_NORMAL); + } - void MovementInform(uint32 type, uint32 id) override - { - if (type == POINT_MOTION_TYPE && id == 1) + void KilledUnit(Unit* victim) override { - Unit* CrystalChosen = ObjectAccessor::GetUnit(*me, CrystalGUID); - if (CrystalChosen && CrystalChosen->IsAlive()) - { - // Make the crystal attackable - // We also remove NON_ATTACKABLE in case the database has it set. - CrystalChosen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE + UNIT_FLAG_NOT_SELECTABLE); - CrystalChosen->CastSpell(me, SPELL_MANA_RAGE, true); - IsDraining = true; - } - else + if (victim->GetTypeId() == TYPEID_PLAYER) + Talk(SAY_KILL); + } + + void MovementInform(uint32 type, uint32 id) override + { + if (type == POINT_MOTION_TYPE && id == 1) { - // Make an error message in case something weird happened here - TC_LOG_ERROR("scripts", "Selin Fireheart unable to drain crystal as the crystal is either dead or despawned"); - DrainingCrystal = false; + Unit* CrystalChosen = ObjectAccessor::GetUnit(*me, CrystalGUID); + if (CrystalChosen && CrystalChosen->IsAlive()) + { + CrystalChosen->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NOT_SELECTABLE); + CrystalChosen->CastSpell(me, SPELL_MANA_RAGE, true); + events.ScheduleEvent(EVENT_EMPOWER, 10000, PHASE_DRAIN); + } } } - } - - void JustDied(Unit* /*killer*/) override - { - Talk(SAY_DEATH); - instance->SetBossState(DATA_SELIN, DONE); // Encounter complete! - ShatterRemainingCrystals(); - } + void JustDied(Unit* /*killer*/) override + { + Talk(SAY_DEATH); + _JustDied(); - void UpdateAI(uint32 diff) override - { - if (!UpdateVictim()) - return; + ShatterRemainingCrystals(); + } - if (!DrainingCrystal) + void UpdateAI(uint32 diff) override { - uint32 maxPowerMana = me->GetMaxPower(POWER_MANA); - if (maxPowerMana && ((me->GetPower(POWER_MANA)*100 / maxPowerMana) < 10)) - { - if (DrainLifeTimer <= diff) - { - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 0), SPELL_DRAIN_LIFE); - DrainLifeTimer = 10000; - } else DrainLifeTimer -= diff; + if (!UpdateVictim()) + return; + + events.Update(diff); - // Heroic only - if (IsHeroic()) + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) { - if (DrainManaTimer <= diff) + case EVENT_FEL_EXPLOSION: + DoCastAOE(SPELL_FEL_EXPLOSION); + events.ScheduleEvent(EVENT_FEL_EXPLOSION, 2000, 0, PHASE_NORMAL); + break; + case EVENT_DRAIN_CRYSTAL: + SelectNearestCrystal(); + _scheduledEvents = false; + break; + case EVENT_DRAIN_MANA: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 45.0f, true)) + DoCast(target, SPELL_DRAIN_MANA); + events.ScheduleEvent(EVENT_DRAIN_MANA, 10000, 0, PHASE_NORMAL); + break; + case EVENT_DRAIN_LIFE: + if (Unit* target = SelectTarget(SELECT_TARGET_RANDOM, 0, 20.0f, true)) + DoCast(target, SPELL_DRAIN_LIFE); + events.ScheduleEvent(EVENT_DRAIN_LIFE, 10000, 0, PHASE_NORMAL); + break; + case EVENT_EMPOWER: { - DoCast(SelectTarget(SELECT_TARGET_RANDOM, 1), SPELL_DRAIN_MANA); - DrainManaTimer = 10000; - } else DrainManaTimer -= diff; + Talk(SAY_EMPOWERED); + + Creature* CrystalChosen = ObjectAccessor::GetCreature(*me, CrystalGUID); + if (CrystalChosen && CrystalChosen->IsAlive()) + CrystalChosen->Kill(CrystalChosen); + + CrystalGUID = 0; + + me->GetMotionMaster()->Clear(); + me->GetMotionMaster()->MoveChase(me->GetVictim()); + break; + } + default: + break; } } - if (FelExplosionTimer <= diff) + if (me->GetPower(POWER_MANA) * 100 / me->GetMaxPower(POWER_MANA) < 10) { - if (!me->IsNonMeleeSpellCast(false)) + if (events.IsInPhase(PHASE_NORMAL) && !_scheduledEvents) { - DoCast(me, SPELL_FEL_EXPLOSION); - FelExplosionTimer = 2000; - } - } else FelExplosionTimer -= diff; + _scheduledEvents = true; + uint32 timer = urand(3000, 7000); + events.ScheduleEvent(EVENT_DRAIN_LIFE, timer, 0, PHASE_NORMAL); - // If below 10% mana, start recharging - maxPowerMana = me->GetMaxPower(POWER_MANA); - if (maxPowerMana && ((me->GetPower(POWER_MANA)*100 / maxPowerMana) < 10)) - { - if (DrainCrystalTimer <= diff) - { - SelectNearestCrystal(); if (IsHeroic()) - DrainCrystalTimer = urand(10000, 15000); + { + events.ScheduleEvent(EVENT_DRAIN_CRYSTAL, urand(10000, 15000), 0, PHASE_NORMAL); + events.ScheduleEvent(EVENT_DRAIN_MANA, timer + 5000, 0, PHASE_NORMAL); + } else - DrainCrystalTimer = urand(20000, 25000); - } else DrainCrystalTimer -= diff; + events.ScheduleEvent(EVENT_DRAIN_CRYSTAL, urand(20000, 25000), 0, PHASE_NORMAL); + } } - }else - { - if (IsDraining) - { - if (EmpowerTimer <= diff) - { - IsDraining = false; - DrainingCrystal = false; - - Talk(SAY_EMPOWERED); - - Unit* CrystalChosen = ObjectAccessor::GetUnit(*me, CrystalGUID); - if (CrystalChosen && CrystalChosen->IsAlive()) - // Use Deal Damage to kill it, not setDeathState. - CrystalChosen->Kill(CrystalChosen); - - CrystalGUID = 0; - me->GetMotionMaster()->Clear(); - me->GetMotionMaster()->MoveChase(me->GetVictim()); - } else EmpowerTimer -= diff; - } + DoMeleeAttackIfReady(); } - DoMeleeAttackIfReady(); // No need to check if we are draining crystal here, as the spell has a stun. - } - }; + private: + std::list<Creature*> Crystals; + uint64 CrystalGUID; + bool _scheduledEvents; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<boss_selin_fireheartAI>(creature); + }; }; class npc_fel_crystal : public CreatureScript { -public: - npc_fel_crystal() : CreatureScript("npc_fel_crystal") { } - - CreatureAI* GetAI(Creature* creature) const override - { - return new npc_fel_crystalAI(creature); - }; + public: + npc_fel_crystal() : CreatureScript("npc_fel_crystal") { } - struct npc_fel_crystalAI : public ScriptedAI - { - npc_fel_crystalAI(Creature* creature) : ScriptedAI(creature) { } - - void Reset() override { } - void EnterCombat(Unit* /*who*/) override { } - void AttackStart(Unit* /*who*/) override { } - void MoveInLineOfSight(Unit* /*who*/) override { } - - void UpdateAI(uint32 /*diff*/) override { } - - void JustDied(Unit* /*killer*/) override + struct npc_fel_crystalAI : public ScriptedAI { - if (InstanceScript* instance = me->GetInstanceScript()) + npc_fel_crystalAI(Creature* creature) : ScriptedAI(creature) { } + + void JustDied(Unit* /*killer*/) override { - Creature* Selin = (ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SELIN))); - if (Selin && Selin->IsAlive()) + if (InstanceScript* instance = me->GetInstanceScript()) { - if (CAST_AI(boss_selin_fireheart::boss_selin_fireheartAI, Selin->AI())->CrystalGUID == me->GetGUID()) - { - // Set this to false if we are the Creature that Selin is draining so his AI flows properly - CAST_AI(boss_selin_fireheart::boss_selin_fireheartAI, Selin->AI())->DrainingCrystal = false; - CAST_AI(boss_selin_fireheart::boss_selin_fireheartAI, Selin->AI())->IsDraining = false; - CAST_AI(boss_selin_fireheart::boss_selin_fireheartAI, Selin->AI())->EmpowerTimer = 10000; - if (Selin->GetVictim()) - { - Selin->AI()->AttackStart(Selin->GetVictim()); - Selin->GetMotionMaster()->MoveChase(Selin->GetVictim()); - } - } + Creature* Selin = ObjectAccessor::GetCreature(*me, instance->GetData64(DATA_SELIN)); + if (Selin && Selin->IsAlive()) + Selin->AI()->DoAction(ACTION_SWITCH_PHASE); } } - } - }; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetInstanceAI<npc_fel_crystalAI>(creature); + }; }; void AddSC_boss_selin_fireheart() diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp index 950d3c3d8f6..65b3553fd87 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/instance_magisters_terrace.cpp @@ -48,13 +48,11 @@ class instance_magisters_terrace : public InstanceMapScript SetBossNumber(EncounterCount); LoadDoorData(doorData); - FelCrystals.clear(); DelrissaDeathCount = 0; SelinGUID = 0; DelrissaGUID = 0; EscapeOrbGUID = 0; - FelCristalIndex = 0; memset(KaelStatue, 0, 2 * sizeof(uint64)); } @@ -65,8 +63,6 @@ class instance_magisters_terrace : public InstanceMapScript { case DATA_DELRISSA_DEATH_COUNT: return DelrissaDeathCount; - case DATA_FEL_CRYSTAL_SIZE: - return uint32(FelCrystals.size()); default: break; } @@ -102,9 +98,6 @@ class instance_magisters_terrace : public InstanceMapScript case NPC_DELRISSA: DelrissaGUID = creature->GetGUID(); break; - case NPC_FELCRYSTALS: - FelCrystals.push_back(creature->GetGUID()); - break; default: break; } @@ -224,35 +217,18 @@ class instance_magisters_terrace : public InstanceMapScript return KaelStatue[1]; case DATA_ESCAPE_ORB: return EscapeOrbGUID; - case DATA_FEL_CRYSTAL: - if (FelCrystals.size() < FelCristalIndex) - { - TC_LOG_ERROR("scripts", "Magisters Terrace: No Fel Crystals loaded in Inst Data"); - return 0; - } - - return FelCrystals.at(FelCristalIndex); default: break; } return 0; } - void SetData64(uint32 type, uint64 value) override - { - if (type == DATA_FEL_CRYSTAL) - FelCristalIndex = value; - } - protected: - std::vector<uint64> FelCrystals; - uint64 SelinGUID; uint64 DelrissaGUID; uint64 KaelStatue[2]; uint64 EscapeOrbGUID; uint32 DelrissaDeathCount; - uint8 FelCristalIndex; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h index d3517dfccf6..8b8d7d5b875 100644 --- a/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h +++ b/src/server/scripts/EasternKingdoms/MagistersTerrace/magisters_terrace.h @@ -28,8 +28,6 @@ enum DataTypes DATA_DELRISSA, DATA_KAELTHAS, - DATA_FEL_CRYSTAL, - DATA_FEL_CRYSTAL_SIZE, DATA_KAEL_STATUE_LEFT, DATA_KAEL_STATUE_RIGHT, @@ -42,7 +40,7 @@ enum CreatureIds { NPC_SELIN = 24723, NPC_DELRISSA = 24560, - NPC_FELCRYSTALS = 24722 + NPC_FEL_CRYSTAL = 24722 }; enum GameObjectIds |