diff --git a/sql/updates/world/4.3.4/2022_04_26_00_world.sql b/sql/updates/world/4.3.4/2022_04_26_00_world.sql new file mode 100644 index 00000000000..41c330300a2 --- /dev/null +++ b/sql/updates/world/4.3.4/2022_04_26_00_world.sql @@ -0,0 +1,80 @@ +UPDATE `gameobject_template` SET `ScriptName`= 'go_end_time_time_transit_device' WHERE `entry` IN (209441, 209442); +UPDATE `gameobject_template` SET `ScriptName`= 'go_end_time_fragment_of_jainas_staff' WHERE `entry`= 209318; + +UPDATE `gossip_menu_option` SET `OptionType`= 1 WHERE `MenuId`= 13321; + +UPDATE `creature_template` SET `unit_flags`= 33587968, `unit_flags2`= 2099200, `AIName`= 'NullCreatureAI' WHERE `entry`= 54641; +UPDATE `creature_template` SET `unit_flags`= 33554432, `flags_extra`= `flags_extra` | 128, `AIName`= 'NullCreatureAI' WHERE `entry`= 54639; +UPDATE `creature_template` SET `unit_flags`= 33554432, `flags_extra`= `flags_extra` | 128, `ScriptName`= 'npc_echo_of_jaina_blink_target' WHERE `entry`= 54542; +UPDATE `creature_template` SET `unit_flags`= 33554432, `unit_flags2`= 34816, `flags_extra`= `flags_extra` | 128, `AIName`= 'NullCreatureAI' WHERE `entry`= 54446; + +DELETE FROM `spawn_group_template` WHERE `groupId`= 460; +INSERT INTO `spawn_group_template` (`groupId`, `groupName`, `groupFlags`) VALUES +(460, 'End Time - Echo of Jaina - Jaina', 4); + +DELETE FROM `spawn_group` WHERE `groupId`= 460; +INSERT INTO `spawn_group` (`groupId`, `spawnType`, `spawnId`) VALUES +(460, 0, 341769); + +UPDATE `creature_template` SET `unit_flags`= 33088, `unit_flags2`= 2099200, `ScriptName`= 'boss_echo_of_jaina' WHERE `entry`= 54445; +UPDATE `creature_template` SET `flags_extra`= `flags_extra` | 128, `AIName`= 'NullCreatureAI' WHERE `entry`= 54494; + +DELETE FROM `creature_text` WHERE `CreatureID`= 54445; +INSERT INTO `creature_text` (`CreatureID`, `GroupID`, `ID`, `Text`, `Type`, `Language`, `Probability`, `Emote`, `Duration`, `Sound`, `BroadcastTextId`, `comment`) VALUES +(54445, 0, 0, 'I don\'t know who you are, but I\'ll defend this shrine with my life. Leave, now, before we come to blows.', 12, 0, 100, 0, 0, 25920, 56573, 'Echo of Jaina - Intro'), +(54445, 1, 0, 'You asked for it.', 14, 0, 100, 0, 0, 25917, 53040, 'Echo of Jaina - Aggro'), +(54445, 2, 0, 'Why won\'t you give up?!', 14, 0, 100, 0, 0, 25926, 56580, 'Echo of Jaina - Blink 1'), +(54445, 2, 1, 'Perhaps this will cool your heads...', 14, 0, 100, 0, 0, 25924, 56578, 'Echo of Jaina - Blink 2'), +(54445, 2, 2, 'A little ice ought to quench the fire in your hearts...', 14, 0, 100, 0, 0, 25925, 56579, 'Echo of Jaina - Blink 3'), +(54445, 3, 0, 'I understand, now. Farewell, and good luck.', 12, 0, 100, 0, 0, 25919, 56574, 'Echo of Jaina - Death'), +(54445, 4, 0, 'You forced my hand.', 14, 0, 100, 0, 0, 25921, 56575, 'Echo of Jaina - Slay 1'), +(54445, 4, 1, 'I didn\'t want to do that.', 14, 0, 100, 0, 0, 25922, 56576, 'Echo of Jaina - Slay 2'), +(54445, 4, 2, 'I wish you\'d surrendered.', 14, 0, 100, 0, 0, 25923, 56577, 'Echo of Jaina - Slay 3'); + +DELETE FROM `spell_script_names` WHERE `ScriptName` IN +('spell_echo_of_jaina_face_highest_threat_target', +'spell_echo_of_jaina_frost_blade', +'spell_echo_of_jaina_disable_stalker_search', +'spell_echo_of_jaina_blink', +'spell_echo_of_jaina_flarecore', +'spell_echo_of_jaina_flarecore_triggered', +'spell_echo_of_jaina_flarecore_periodic'); + +INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES +(107897, 'spell_echo_of_jaina_face_highest_threat_target'), +(101337, 'spell_echo_of_jaina_frost_blade'), +(101540, 'spell_echo_of_jaina_disable_stalker_search'), +(101812, 'spell_echo_of_jaina_blink'), +(101944, 'spell_echo_of_jaina_flarecore'), +(101616, 'spell_echo_of_jaina_flarecore_triggered'), +(101588, 'spell_echo_of_jaina_flarecore_periodic'); + +DELETE FROM `creature_template_movement` WHERE `CreatureId` IN (54494, 54446); +INSERT INTO `creature_template_movement` (`CreatureId`, `Ground`, `Swim`, `Flight`, `Rooted`) VALUES +(54494, 0, 0, 2, 0), +(54446, 0, 0, 2, 1); + +DELETE FROM `conditions` WHERE `SourceEntry` IN (101812, 101540) AND `SourceTypeOrReferenceId`= 13; +INSERT INTO `conditions` (`SourceTypeOrReferenceId`, `SourceGroup`, `SourceEntry`, `SourceId`, `ElseGroup`, `ConditionTypeOrReference`, `ConditionTarget`, `ConditionValue1`, `ConditionValue2`, `ConditionValue3`, `NegativeCondition`, `ErrorType`, `ScriptName`, `Comment`) VALUES +(13, 1, 101812, 0, 0, 31, 0, 3, 54542, 0, 0, 0, '', 'Blink - Target Blink Target'), +(13, 2, 101540, 0, 0, 31, 0, 3, 54445, 0, 0, 0, '', 'Disable Stalker Search - Target Echo of Jaina'); + +DELETE FROM `creature_onkill_reward` WHERE `creature_id`= 54445; +INSERT INTO `creature_onkill_reward` (`creature_id`, `RewOnKillRepFaction1`, `MaxStanding1`, `IsTeamAward1`, `RewOnKillRepValue1`, `TeamDependent`, `CurrencyId1`, `CurrencyCount1`) VALUES +(54445, 1162, 7, 0, 250, 0, 395, 7000); + +UPDATE `creature_template`SET `mingold`= 17000, `maxgold`= 23000 WHERE `entry`= 54445; +DELETE FROM `creature_loot_template` WHERE `Entry`= 54445; +INSERT INTO `creature_loot_template` (`Entry`, `Reference`, `Item`, `Chance`, `GroupId`, `MinCount`, `MaxCount`, `LootMode`) VALUES +(54445, 0, 72808, 20, 1, 1, 1, 1), +(54445, 0, 72809, 15, 1, 1, 1, 1), +(54445, 0, 72805, 5, 1, 1, 1, 1), +(54445, 0, 72801, 5, 1, 1, 1, 1), +(54445, 0, 72804, 5, 1, 1, 1, 1), +(54445, 0, 72802, 5, 1, 1, 1, 1), +(54445, 0, 72799, 5, 1, 1, 1, 1), +(54445, 0, 72803, 5, 1, 1, 1, 1), +(54445, 0, 72806, 5, 1, 1, 1, 1), +(54445, 0, 72798, 5, 1, 1, 1, 1), +(54445, 0, 72800, 5, 1, 1, 1, 1), +(54445, 0, 72807, 5, 1, 1, 1, 1); diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/boss_echo_of_jaina.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/boss_echo_of_jaina.cpp new file mode 100644 index 00000000000..b0c507f8843 --- /dev/null +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/boss_echo_of_jaina.cpp @@ -0,0 +1,480 @@ +/* + * This file is part of the TrinityCore Project. See AUTHORS file for Copyright information + * + * 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 + * Free Software Foundation; either version 2 of the License, or (at your + * option) any later version. + * + * This program is distributed in the hope that it will be useful, but WITHOUT + * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or + * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for + * more details. + * + * You should have received a copy of the GNU General Public License along + * with this program. If not, see . + */ + +#include "end_time.h" +#include "InstanceScript.h" +#include "MotionMaster.h" +#include "PassiveAI.h" +#include "ScriptedCreature.h" +#include "ScriptMgr.h" +#include "Spell.h" +#include "SpellAuras.h" +#include "SpellAuraEffects.h" +#include "SpellScript.h" + +enum Spells +{ + // Echo of Jaina + SPELL_PYROBLAST = 101809, + SPELL_FROSTBOLT_VOLLEY = 101810, + SPELL_BLINK = 101812, + SPELL_FACE_HIGHEST_THREAT_TARGET = 107897, + SPELL_FROST_BLADES = 101339, + SPELL_FLARECORE = 101944, + + // Frost Blades + SPELL_FROST_BLADES_PERIODIC = 101338, + + // Blink Target + SPELL_DISABLE_STALKER_SEARCH = 101540, + + // Flarecore + SPELL_FLARECORE_PERIODIC = 101588, + SPELL_FLARE_UP = 101589, + SPELL_UNSTABLE_FLARE = 101980, + SPELL_FLARE = 101587, +}; + +enum Events +{ + // Echo of Jaina + EVENT_MAKE_ATTACKABLE = 1, + EVENT_PYROBLAST, + EVENT_FROSTBOLT_VOLLEY, + EVENT_BLINK, + EVENT_FACE_HIGHEST_THREAT_TARGET, + EVENT_FLARECORE, + + // Blink Target + EVENT_DISABLE_STALKER_SEARCH +}; + +enum Actions +{ + // Blink Target + ACTION_DISABLE_STALKER = 0 +}; + +enum Phases +{ + PHASE_INTRO = 1, + PHASE_COMBAT = 2 +}; + +enum Texts +{ + SAY_INTRO = 0, + SAY_AGGRO = 1, + SAY_BLINK = 2, + SAY_DEATH = 3, + SAY_SLAY = 4 +}; + +enum Data +{ + DATA_STALKER_DISABLED = 0 +}; + +struct boss_echo_of_jaina : public BossAI +{ + boss_echo_of_jaina(Creature* creature) : BossAI(creature, DATA_ECHO_OF_JAINA), _firstSpawn(false), _frostBladeCount(0), _frostboltVolleyCount(0) { } + + void InitializeAI() override + { + _firstSpawn = instance->GetBossState(DATA_ECHO_OF_JAINA) == NOT_STARTED; + } + + void JustAppeared() override + { + if (_firstSpawn) + { + Talk(SAY_INTRO); + events.SetPhase(PHASE_INTRO); + events.ScheduleEvent(EVENT_MAKE_ATTACKABLE, 9s, 0, PHASE_INTRO); + } + else + me->SetImmuneToPC(false); + } + + void JustEngagedWith(Unit* who) override + { + BossAI::JustEngagedWith(who); + Talk(SAY_AGGRO, who); + instance->SendEncounterUnit(ENCOUNTER_FRAME_ENGAGE, me, 1); + events.SetPhase(PHASE_COMBAT); + events.ScheduleEvent(EVENT_PYROBLAST, 1ms); + events.ScheduleEvent(EVENT_FLARECORE, 15s); + events.ScheduleEvent(EVENT_BLINK, 19s); + } + + void EnterEvadeMode(EvadeReason /*why*/) override + { + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + summons.DespawnAll(); + _DespawnAtEvade(); + } + + void JustDied(Unit* killer) override + { + BossAI::JustDied(killer); + Talk(SAY_DEATH, killer); + instance->SendEncounterUnit(ENCOUNTER_FRAME_DISENGAGE, me); + } + + void KilledUnit(Unit* victim) override + { + if (victim->IsPlayer()) + Talk(SAY_SLAY, victim); + } + + void JustSummoned(Creature* summon) override + { + summons.Summon(summon); + + switch (summon->GetEntry()) + { + case NPC_FROST_BLADE: + { + summon->CastSpell(nullptr, SPELL_FROST_BLADES_PERIODIC); + + float stepSize = float(M_PI_2 / 3); + float angle = me->GetOrientation() - stepSize; + angle = Position::NormalizeOrientation(angle + stepSize * _frostBladeCount); + ++_frostBladeCount; + float x = me->GetPositionX() + std::cos(angle) * 50.f; + float y = me->GetPositionY() + std::sin(angle) * 50.f; + summon->GetMotionMaster()->MovePoint(0, x, y, me->GetPositionZ(), false); + summon->DespawnOrUnsummon(6s + 500ms); + break; + } + case NPC_FLARECORE_EMBER: + summon->CastSpell(summon, SPELL_FLARECORE_PERIODIC); + break; + default: + break; + } + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim() && !events.IsInPhase(PHASE_INTRO)) + return; + + events.Update(diff); + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + + while (uint32 eventId = events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_MAKE_ATTACKABLE: + me->SetImmuneToPC(false); + break; + case EVENT_PYROBLAST: + DoCastVictim(SPELL_PYROBLAST); + events.Repeat(2s); + break; + case EVENT_FROSTBOLT_VOLLEY: + DoCastAOE(SPELL_FROSTBOLT_VOLLEY); + // Blink and Frost Blades have been pulled off, time to return to regular combat routine + me->SetReactState(REACT_AGGRESSIVE); + + ++_frostboltVolleyCount; + if (_frostboltVolleyCount < 3) + events.Repeat(1ms); + else + events.ScheduleEvent(EVENT_PYROBLAST, 1ms); + break; + case EVENT_BLINK: + me->AttackStop(); + me->SetReactState(REACT_PASSIVE); + Talk(SAY_BLINK); + DoCastSelf(SPELL_BLINK); + _frostBladeCount = 0; + _frostboltVolleyCount = 0; + events.ScheduleEvent(EVENT_FACE_HIGHEST_THREAT_TARGET, 1ms); + events.ScheduleEvent(EVENT_FROSTBOLT_VOLLEY, 2s); + events.CancelEvent(EVENT_PYROBLAST); + events.Repeat(24s); + break; + case EVENT_FACE_HIGHEST_THREAT_TARGET: + DoCast(me->GetThreatManager().GetCurrentVictim(), SPELL_FACE_HIGHEST_THREAT_TARGET); + break; + case EVENT_FLARECORE: + DoCastAOE(SPELL_FLARECORE, CastSpellExtraArgs().AddSpellMod(SPELLVALUE_MAX_TARGETS, 1)); + break; + default: + break; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + return; + } + + DoMeleeAttackIfReady(); + } + +private: + bool _firstSpawn; + uint8 _frostBladeCount; + uint8 _frostboltVolleyCount; +}; + +struct npc_echo_of_jaina_blink_target : public NullCreatureAI +{ + npc_echo_of_jaina_blink_target(Creature* creature) : NullCreatureAI(creature), _disabled(false) { } + + void JustAppeared() override + { + _events.ScheduleEvent(EVENT_DISABLE_STALKER_SEARCH, 2s + 500ms); + } + + void DoAction(int32 action) override + { + switch (action) + { + case ACTION_DISABLE_STALKER: + _disabled = true; + break; + default: + break; + } + } + + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_STALKER_DISABLED: + return _disabled ? 1 : 0; + default: + return 0; + } + } + + void UpdateAI(uint32 diff) override + { + _events.Update(diff); + + while (uint32 eventId = _events.ExecuteEvent()) + { + switch (eventId) + { + case EVENT_DISABLE_STALKER_SEARCH: + _disabled = false; + DoCastAOE(SPELL_DISABLE_STALKER_SEARCH); + _events.Repeat(2s + 500ms); + break; + default: + break; + } + } + } + +private: + EventMap _events; + bool _disabled; +}; + +class spell_echo_of_jaina_face_highest_threat_target : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FROST_BLADES }); + } + + void HandleDummyEffect(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (!caster) + return; + + caster->SetFacingToObject(GetHitUnit()); + caster->SetOrientationTowards(GetHitUnit()); // we need an updated orientation right now. No time to wait for the spline to update + + for (uint8 i = 0; i < 3; ++i) + caster->CastSpell(caster, SPELL_FROST_BLADES); + } + + void Register() override + { + OnEffectHitTarget.Register(&spell_echo_of_jaina_face_highest_threat_target::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_echo_of_jaina_frost_blade : public SpellScript +{ + void FilterTargets(std::list& targets) + { + targets.remove_if([&](WorldObject const* target) + { + return target->GetExactDist2d(GetCaster()) > GetCaster()->GetCombatReach(); + }); + } + + void Register() override + { + OnObjectAreaTargetSelect.Register(&spell_echo_of_jaina_frost_blade::FilterTargets, EFFECT_0, TARGET_UNIT_SRC_AREA_ENEMY); + } +}; + +class spell_echo_of_jaina_disable_stalker_search : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FROST_BLADES }); + } + + void HandleDummyEffect(SpellEffIndex /*effIndex*/) + { + Unit* caster = GetCaster(); + if (!caster || !caster->IsCreature()) + return; + + if (CreatureAI* ai = caster->ToCreature()->AI()) + ai->DoAction(ACTION_DISABLE_STALKER); + } + + void Register() override + { + OnEffectHitTarget.Register(&spell_echo_of_jaina_disable_stalker_search::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + OnEffectHitTarget.Register(&spell_echo_of_jaina_disable_stalker_search::HandleDummyEffect, EFFECT_1, SPELL_EFFECT_DUMMY); + } +}; + +class spell_echo_of_jaina_blink : public SpellScript +{ + void SetTarget(SpellDestination& target) + { + std::list blinkTargets; + GetCaster()->GetCreatureListWithEntryInGrid(blinkTargets, NPC_BLINK_TARGET, GetSpellInfo()->GetMaxRange()); + if (blinkTargets.empty()) + return; + + blinkTargets.remove_if([](Creature const* creature) + { + if (CreatureAI const* ai = creature->AI()) + if (ai->GetData(DATA_STALKER_DISABLED)) + return true; + + return false; + }); + + if (blinkTargets.size() > 1) + { + blinkTargets.sort(Trinity::ObjectDistanceOrderPred(GetCaster())); + blinkTargets.resize(1); + target = *blinkTargets.front(); + } + } + + void Register() override + { + OnDestinationTargetSelect.Register(&spell_echo_of_jaina_blink::SetTarget, EFFECT_0, TARGET_DEST_NEARBY_ENTRY); + } +}; + +class spell_echo_of_jaina_flarecore : public SpellScript +{ + void HandleDummyEffect(SpellEffIndex /*effIndex*/) + { + if (Unit* caster = GetCaster()) + caster->CastSpell(GetHitUnit(), GetEffectValue(), true); + } + + void Register() override + { + OnEffectHitTarget.Register(&spell_echo_of_jaina_flarecore::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } +}; + +class spell_echo_of_jaina_flarecore_triggered : public SpellScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_UNSTABLE_FLARE, SPELL_FLARECORE_PERIODIC }); + } + + void HandleDummyEffect(SpellEffIndex /*effIndex*/) + { + if (_detonated) + return; + + Unit* caster = GetCaster(); + if (!caster) + return; + + caster->CastSpell(nullptr, SPELL_UNSTABLE_FLARE); + caster->RemoveAurasDueToSpell(SPELL_FLARECORE_PERIODIC); + + _detonated = true; + } + + void Register() override + { + OnEffectHitTarget.Register(&spell_echo_of_jaina_flarecore_triggered::HandleDummyEffect, EFFECT_0, SPELL_EFFECT_DUMMY); + } +private: + bool _detonated = false; +}; + +class spell_echo_of_jaina_flarecore_periodic : public AuraScript +{ + bool Validate(SpellInfo const* /*spellInfo*/) override + { + return ValidateSpellInfo({ SPELL_FLARE_UP, SPELL_FLARE, SPELL_FLARECORE_PERIODIC }); + } + + void HandlePeriodic(AuraEffect const* aurEff) + { + if (!((aurEff->GetTickNumber() - 1) % 4)) + GetTarget()->CastSpell(nullptr, SPELL_FLARE_UP); + } + + void AfterRemove(AuraEffect const* /*aurEff*/, AuraEffectHandleModes /*mode*/) + { + if (GetTargetApplication()->GetRemoveMode().HasFlag(AuraRemoveFlags::Expired)) + { + GetTarget()->CastSpell(nullptr, SPELL_FLARE); + GetTarget()->RemoveAurasDueToSpell(SPELL_FLARECORE_PERIODIC); + } + + if (Creature* creature = GetTarget()->ToCreature()) + creature->DespawnOrUnsummon(5s + 500ms); + } + + void Register() override + { + OnEffectPeriodic.Register(&spell_echo_of_jaina_flarecore_periodic::HandlePeriodic, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL); + AfterEffectRemove.Register(&spell_echo_of_jaina_flarecore_periodic::AfterRemove, EFFECT_0, SPELL_AURA_PERIODIC_TRIGGER_SPELL, AURA_EFFECT_HANDLE_REAL); + } +}; + +void AddSC_boss_echo_of_jaina() +{ + RegisterEndTimeCreatureAI(boss_echo_of_jaina); + RegisterEndTimeCreatureAI(npc_echo_of_jaina_blink_target); + RegisterSpellScript(spell_echo_of_jaina_face_highest_threat_target); + RegisterSpellScript(spell_echo_of_jaina_frost_blade); + RegisterSpellScript(spell_echo_of_jaina_disable_stalker_search); + RegisterSpellScript(spell_echo_of_jaina_blink); + RegisterSpellScript(spell_echo_of_jaina_flarecore); + RegisterSpellScript(spell_echo_of_jaina_flarecore_triggered); + RegisterSpellScript(spell_echo_of_jaina_flarecore_periodic); +} diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.cpp index 504c5762456..ff536fb4ef5 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.cpp @@ -16,6 +16,8 @@ */ #include "end_time.h" +#include "GameObject.h" +#include "GameObjectAI.h" #include "InstanceScript.h" #include "PassiveAI.h" #include "Player.h" @@ -50,7 +52,7 @@ enum Texts SAY_ENCOUNTER_OUTRO_4 = 4 }; -enum GossipMenus +enum GossipMenuIds { GOSSIP_MENU_ID_NOZDORMU = 13360, GOSSIP_MENU_OPTION_ID_WELL_OF_ETERNITY = 0 @@ -152,7 +154,101 @@ private: bool _introDone; }; +enum TimeTransitDeviceAreaIds +{ + AREA_ID_ENTRYWAY_OF_TIME = 5796 +}; + +enum TimeTransitDeviceSpells +{ + SPELL_TELEPORT_TO_ENTRANCE = 102564, + SPELL_TELEPORT_TO_BLUE_DRAGONSHRINE = 102126 +}; + +enum TimeTransitGossipMenuIds +{ + GOSSIP_MENU_ID_SELECT_YOUR_DESTINATION = 13321 +}; + +enum TimeTransitGossipIndexes +{ + GOSSIP_INDEX_TELEPORT_TO_ENTRYWAY_OF_TIME = 0, + GOSSIP_INDEX_TELEPORT_TO_BLUE_DRAGONSHRINE_FIRST_ECHO = 3, + GOSSIP_INDEX_TELEPORT_TO_BLUE_DRAGONSHRINE_SECOND_ECHO = 7 +}; + +static std::unordered_map TransitDeviceTeleportSpells = +{ + { GOSSIP_INDEX_TELEPORT_TO_ENTRYWAY_OF_TIME, SPELL_TELEPORT_TO_ENTRANCE }, + { GOSSIP_INDEX_TELEPORT_TO_BLUE_DRAGONSHRINE_FIRST_ECHO, SPELL_TELEPORT_TO_BLUE_DRAGONSHRINE }, + { GOSSIP_INDEX_TELEPORT_TO_BLUE_DRAGONSHRINE_SECOND_ECHO, SPELL_TELEPORT_TO_BLUE_DRAGONSHRINE } +}; + +struct go_end_time_time_transit_device : public GameObjectAI +{ + go_end_time_time_transit_device(GameObject* gameObject) : GameObjectAI(gameObject), _instance(nullptr) { } + + void InitializeAI() override + { + _instance = me->GetInstanceScript(); + } + + bool GossipHello(Player* player) override + { + if (!_instance) + return false; + + if (player->GetAreaId() != AREA_ID_ENTRYWAY_OF_TIME) + AddGossipItemFor(player, GOSSIP_MENU_ID_SELECT_YOUR_DESTINATION, GOSSIP_INDEX_TELEPORT_TO_ENTRYWAY_OF_TIME, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + GOSSIP_INDEX_TELEPORT_TO_ENTRYWAY_OF_TIME); + + // @todo: world state based menu generation + AddGossipItemFor(player, GOSSIP_MENU_ID_SELECT_YOUR_DESTINATION, GOSSIP_INDEX_TELEPORT_TO_BLUE_DRAGONSHRINE_FIRST_ECHO, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + GOSSIP_INDEX_TELEPORT_TO_BLUE_DRAGONSHRINE_FIRST_ECHO); + + SendGossipMenuFor(player, player->GetGossipTextId(GOSSIP_MENU_ID_SELECT_YOUR_DESTINATION, me), me->GetGUID()); + + return true; + } + + bool GossipSelect(Player* player, uint32 /*gossipMenuId*/, uint32 action) override + { + uint32 index = player->PlayerTalkClass->GetGossipOptionAction(action) - GOSSIP_ACTION_INFO_DEF; + player->CastSpell(player, TransitDeviceTeleportSpells[index]); + ClearGossipMenuFor(player); + + return true; + } + +private: + InstanceScript* _instance; +}; + +struct go_end_time_fragment_of_jainas_staff : public GameObjectAI +{ + go_end_time_fragment_of_jainas_staff(GameObject* gameObject) : GameObjectAI(gameObject), _instance(nullptr) { } + + void InitializeAI() override + { + _instance = me->GetInstanceScript(); + } + + bool GossipHello(Player* /*player*/) override + { + if (!_instance) + return false; + + _instance->SetData(DATA_COLLECTED_FRAGMENT_OF_JAINAS_STAFF, 0); + me->DespawnOrUnsummon(); + + return true; + } + +private: + InstanceScript* _instance; +}; + void AddSC_end_time() { RegisterEndTimeCreatureAI(npc_end_time_nozdormu); + RegisterGameObjectAI(go_end_time_time_transit_device); + RegisterGameObjectAI(go_end_time_fragment_of_jainas_staff); } diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.h b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.h index 6e135aa3c0d..8c14385b73d 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.h +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/end_time.h @@ -32,7 +32,7 @@ enum ETDataTypes { // Bosses DATA_ECHO_OF_BAINE = 0, - DATA_ECHO_OF_JANA = 1, + DATA_ECHO_OF_JAINA = 1, DATA_ECHO_OF_SYLVANAS = 2, DATA_ECHO_OF_TYRANDE = 3, DATA_MUROZOND = 4, @@ -40,19 +40,27 @@ enum ETDataTypes // Additional Data DATA_HOURGLASS_OF_TIME, DATA_MUROZOND_INTRO, - DATA_NOZDORMU_BRONZE_DRAGON_SHRINE + DATA_NOZDORMU_BRONZE_DRAGON_SHRINE, + DATA_ARCANE_CIRCLE, + DATA_COLLECTED_FRAGMENT_OF_JAINAS_STAFF, }; enum ETCreatures { // Bosses - BOSS_MUROZOND = 54432, + BOSS_MUROZOND = 54432, // Encounter Related Creatures /*Murozond*/ - NPC_INFINITE_WARDEN = 54923, - NPC_INFINITE_SUPRESSOR = 54920, - NPC_NOZDORMU_DRAGON_SHRINES = 54751 + NPC_INFINITE_WARDEN = 54923, + NPC_INFINITE_SUPRESSOR = 54920, + NPC_NOZDORMU_DRAGON_SHRINES = 54751, + + /*Echo of Jaina*/ + NPC_ARCANE_CIRCLE = 54639, + NPC_FROST_BLADE = 54494, + NPC_BLINK_TARGET = 54542, + NPC_FLARECORE_EMBER = 54446 }; enum ETGameObjectIds diff --git a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/instance_end_time.cpp b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/instance_end_time.cpp index 9c78cf05d19..bd0cb3d4d34 100644 --- a/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/instance_end_time.cpp +++ b/src/server/scripts/Kalimdor/CavernsOfTime/EndTime/instance_end_time.cpp @@ -27,8 +27,9 @@ ObjectData const creatureData[] = { - { BOSS_MUROZOND, DATA_MUROZOND }, - { 0, 0 } // END + { BOSS_MUROZOND, DATA_MUROZOND }, + { NPC_ARCANE_CIRCLE, DATA_ARCANE_CIRCLE }, + { 0, 0 } // END }; ObjectData const gameobjectData[] = @@ -48,7 +49,8 @@ enum Events enum SpawnGroups { - SPAWN_GROUP_ID_MUROZOND_CHEST = 437 + SPAWN_GROUP_ID_MUROZOND_CHEST = 437, + SPAWN_GROUP_ID_ECHO_OF_JAINA = 460 }; enum AreaIds @@ -56,6 +58,22 @@ enum AreaIds AREA_ID_BRONZE_DRAGON_SHRINE = 5795 }; +enum WorldStates +{ + WORLD_STATE_ID_SHOW_COLLECTED_STAVE_FRAGMENTS = 6046, + WORLD_STATE_ID_COLLECTED_STAVE_FRAGMENTS = 6025 +}; + +enum MapEvents +{ + MAP_EVENT_AZURE_DRAGONSHRINE_ENTERED = 29225 +}; + +enum Spells +{ + SPELL_SUMMON_PHANTOM = 102200 +}; + std::array MurozondSpawnPositions = { Position(4288.125f, -456.40277f, 160.4989f, 2.98451f), // Initial spawn position @@ -69,7 +87,8 @@ public: struct instance_end_time_InstanceMapScript : public InstanceScript { - instance_end_time_InstanceMapScript(InstanceMap* map) : InstanceScript(map), _killedInfiniteDragonkins(0) + instance_end_time_InstanceMapScript(InstanceMap* map) : InstanceScript(map), + _killedInfiniteDragonkins(0), _collectedStaffFragments(0) { SetHeaders(DataHeader); SetBossNumber(EncounterCount); @@ -77,6 +96,25 @@ public: LoadObjectData(creatureData, gameobjectData); } + void ProcessEvent(WorldObject* /*obj*/, uint32 eventId) override + { + if (_executedMapEvents.find(eventId) != _executedMapEvents.end()) + return; + + switch (eventId) + { + case MAP_EVENT_AZURE_DRAGONSHRINE_ENTERED: + if (GetBossState(DATA_ECHO_OF_JAINA) == DONE) + break; + instance->SetWorldState(WORLD_STATE_ID_SHOW_COLLECTED_STAVE_FRAGMENTS, 1); + break; + default: + break; + } + + _executedMapEvents.insert(eventId); + } + void Create() override { InstanceScript::Create(); @@ -157,6 +195,14 @@ public: else if (state == DONE) instance->SpawnGroupSpawn(SPAWN_GROUP_ID_MUROZOND_CHEST); break; + case DATA_ECHO_OF_JAINA: + if (state == IN_PROGRESS) + { + instance->SetWorldState(WORLD_STATE_ID_SHOW_COLLECTED_STAVE_FRAGMENTS, 0); + if (Creature* circle = GetCreature(DATA_ARCANE_CIRCLE)) + circle->DespawnOrUnsummon(); + } + break; default: break; } @@ -164,6 +210,30 @@ public: return true; } + void SetData(uint32 type, uint32 /*value*/) override + { + switch (type) + { + case DATA_COLLECTED_FRAGMENT_OF_JAINAS_STAFF: + if (GetBossState(DATA_ECHO_OF_JAINA) == DONE) + break; + + ++_collectedStaffFragments; + instance->SetWorldState(WORLD_STATE_ID_COLLECTED_STAVE_FRAGMENTS, _collectedStaffFragments); + + if (_collectedStaffFragments < 16) + { + if (Creature* circle = GetCreature(DATA_ARCANE_CIRCLE)) + circle->CastSpell(nullptr, SPELL_SUMMON_PHANTOM); + } + else + instance->SpawnGroupSpawn(SPAWN_GROUP_ID_ECHO_OF_JAINA); + break; + default: + break; + } + } + void Update(uint32 diff) override { _events.Update(diff); @@ -184,6 +254,9 @@ public: private: EventMap _events; uint8 _killedInfiniteDragonkins; + uint8 _collectedStaffFragments; + + std::unordered_set _executedMapEvents; }; InstanceScript* GetInstanceScript(InstanceMap* map) const override diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp index 46b1560b6f5..da2adb0f56f 100644 --- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp +++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp @@ -49,6 +49,7 @@ void AddSC_culling_of_stratholme(); void AddSC_instance_culling_of_stratholme(); void AddSC_end_time(); //CoT End Time void AddSC_boss_murozond(); +void AddSC_boss_echo_of_jaina(); void AddSC_instance_end_time(); void AddSC_instance_well_of_eternity(); //CoT Well of Eternity void AddSC_instance_hour_of_twilight(); //CoT Hour of Twilight @@ -187,6 +188,7 @@ void AddKalimdorScripts() AddSC_instance_culling_of_stratholme(); AddSC_end_time(); //CoT End Time AddSC_boss_murozond(); + AddSC_boss_echo_of_jaina(); AddSC_instance_end_time(); AddSC_instance_well_of_eternity(); //CoT Well of Eternity AddSC_instance_hour_of_twilight(); //CoT Hour of Twilight