/* * 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 . */ /* ScriptData SDName: Bosses_Opera SD%Complete: 90 SDComment: Oz, Hood, and RAJ event implemented. RAJ event requires more testing. SDCategory: Karazhan EndScriptData */ #include "ScriptMgr.h" #include "InstanceScript.h" #include "karazhan.h" #include "Log.h" #include "MotionMaster.h" #include "ObjectAccessor.h" #include "Player.h" #include "ScriptedCreature.h" #include "ScriptedGossip.h" #include "SpellInfo.h" #include "TemporarySummon.h" /***********************************/ /*** OPERA WIZARD OF OZ EVENT *****/ /*********************************/ enum Says { SAY_DOROTHEE_DEATH = 0, SAY_DOROTHEE_SUMMON = 1, SAY_DOROTHEE_TITO_DEATH = 2, SAY_DOROTHEE_AGGRO = 3, SAY_ROAR_AGGRO = 0, SAY_ROAR_DEATH = 1, SAY_ROAR_SLAY = 2, SAY_STRAWMAN_AGGRO = 0, SAY_STRAWMAN_DEATH = 1, SAY_STRAWMAN_SLAY = 2, SAY_TINHEAD_AGGRO = 0, SAY_TINHEAD_DEATH = 1, SAY_TINHEAD_SLAY = 2, EMOTE_RUST = 3, SAY_CRONE_AGGRO = 0, SAY_CRONE_DEATH = 1, SAY_CRONE_SLAY = 2, }; enum Spells { // Dorothee SPELL_WATERBOLT = 31012, SPELL_SCREAM = 31013, SPELL_SUMMONTITO = 31014, // Tito SPELL_YIPPING = 31015, // Strawman SPELL_BRAIN_BASH = 31046, SPELL_BRAIN_WIPE = 31069, SPELL_BURNING_STRAW = 31075, // Tinhead SPELL_CLEAVE = 31043, SPELL_RUST = 31086, // Roar SPELL_MANGLE = 31041, SPELL_SHRED = 31042, SPELL_FRIGHTENED_SCREAM = 31013, // Crone SPELL_CHAIN_LIGHTNING = 32337, // Cyclone SPELL_KNOCKBACK = 32334, SPELL_CYCLONE_VISUAL = 32332, }; enum Creatures { CREATURE_TITO = 17548, CREATURE_CYCLONE = 18412, CREATURE_CRONE = 18168, }; void SummonCroneIfReady(InstanceScript* instance, Creature* creature) { instance->SetData(DATA_OPERA_OZ_DEATHCOUNT, SPECIAL); // Increment DeathCount if (instance->GetData(DATA_OPERA_OZ_DEATHCOUNT) == 4) { if (Creature* pCrone = creature->SummonCreature(CREATURE_CRONE, -10891.96f, -1755.95f, creature->GetPositionZ(), 4.64f, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 2h)) { if (creature->GetVictim()) pCrone->AI()->AttackStart(creature->GetVictim()); } } } class boss_dorothee : public CreatureScript { public: boss_dorothee() : CreatureScript("boss_dorothee") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_dorotheeAI : public ScriptedAI { boss_dorotheeAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { AggroTimer = 500; WaterBoltTimer = 5000; FearTimer = 15000; SummonTitoTimer = 47500; SummonedTito = false; TitoDied = false; } InstanceScript* instance; uint32 AggroTimer; uint32 WaterBoltTimer; uint32 FearTimer; uint32 SummonTitoTimer; bool SummonedTito; bool TitoDied; void Reset() override { Initialize(); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_DOROTHEE_AGGRO); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void SummonTito(); void JustDied(Unit* /*killer*/) override { Talk(SAY_DOROTHEE_DEATH); SummonCroneIfReady(instance, me); } void AttackStart(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::AttackStart(who); } void MoveInLineOfSight(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); } void UpdateAI(uint32 diff) override { if (AggroTimer) { if (AggroTimer <= diff) { me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); AggroTimer = 0; } else AggroTimer -= diff; } if (!UpdateVictim()) return; if (WaterBoltTimer <= diff) { DoCast(SelectTarget(SelectTargetMethod::Random, 0), SPELL_WATERBOLT); WaterBoltTimer = TitoDied ? 1500 : 5000; } else WaterBoltTimer -= diff; if (FearTimer <= diff) { DoCastVictim(SPELL_SCREAM); FearTimer = 30000; } else FearTimer -= diff; if (!SummonedTito) { if (SummonTitoTimer <= diff) SummonTito(); else SummonTitoTimer -= diff; } } }; }; class npc_tito : public CreatureScript { public: npc_tito() : CreatureScript("npc_tito") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct npc_titoAI : public ScriptedAI { npc_titoAI(Creature* creature) : ScriptedAI(creature) { Initialize(); } void Initialize() { DorotheeGUID.Clear(); YipTimer = 10000; } ObjectGuid DorotheeGUID; uint32 YipTimer; void Reset() override { Initialize(); } void JustEngagedWith(Unit* /*who*/) override { } void JustDied(Unit* /*killer*/) override { if (!DorotheeGUID.IsEmpty()) { Creature* Dorothee = (ObjectAccessor::GetCreature((*me), DorotheeGUID)); if (Dorothee && Dorothee->IsAlive()) { ENSURE_AI(boss_dorothee::boss_dorotheeAI, Dorothee->AI())->TitoDied = true; Talk(SAY_DOROTHEE_TITO_DEATH, Dorothee); } } } void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; if (YipTimer <= diff) { DoCastVictim(SPELL_YIPPING); YipTimer = 10000; } else YipTimer -= diff; } }; }; void boss_dorothee::boss_dorotheeAI::SummonTito() { if (Creature* pTito = me->SummonCreature(CREATURE_TITO, 0.0f, 0.0f, 0.0f, 0.0f, TEMPSUMMON_TIMED_DESPAWN_OUT_OF_COMBAT, 30s)) { Talk(SAY_DOROTHEE_SUMMON); ENSURE_AI(npc_tito::npc_titoAI, pTito->AI())->DorotheeGUID = me->GetGUID(); pTito->AI()->AttackStart(me->GetVictim()); SummonedTito = true; TitoDied = false; } } class boss_strawman : public CreatureScript { public: boss_strawman() : CreatureScript("boss_strawman") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_strawmanAI : public ScriptedAI { boss_strawmanAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { AggroTimer = 13000; BrainBashTimer = 5000; BrainWipeTimer = 7000; } InstanceScript* instance; uint32 AggroTimer; uint32 BrainBashTimer; uint32 BrainWipeTimer; void Reset() override { Initialize(); } void AttackStart(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::AttackStart(who); } void MoveInLineOfSight(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_STRAWMAN_AGGRO); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override { if ((spellInfo->SchoolMask == SPELL_SCHOOL_MASK_FIRE) && (!(rand32() % 10))) { /* if (not direct damage(aoe, dot)) return; */ DoCast(me, SPELL_BURNING_STRAW, true); } } void JustDied(Unit* /*killer*/) override { Talk(SAY_STRAWMAN_DEATH); SummonCroneIfReady(instance, me); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_STRAWMAN_SLAY); } void UpdateAI(uint32 diff) override { if (AggroTimer) { if (AggroTimer <= diff) { me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); AggroTimer = 0; } else AggroTimer -= diff; } if (!UpdateVictim()) return; if (BrainBashTimer <= diff) { DoCastVictim(SPELL_BRAIN_BASH); BrainBashTimer = 15000; } else BrainBashTimer -= diff; if (BrainWipeTimer <= diff) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) DoCast(target, SPELL_BRAIN_WIPE); BrainWipeTimer = 20000; } else BrainWipeTimer -= diff; } }; }; class boss_tinhead : public CreatureScript { public: boss_tinhead() : CreatureScript("boss_tinhead") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_tinheadAI : public ScriptedAI { boss_tinheadAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { AggroTimer = 15000; CleaveTimer = 5000; RustTimer = 30000; RustCount = 0; } InstanceScript* instance; uint32 AggroTimer; uint32 CleaveTimer; uint32 RustTimer; uint8 RustCount; void Reset() override { Initialize(); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_TINHEAD_AGGRO); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void AttackStart(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::AttackStart(who); } void MoveInLineOfSight(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); } void JustDied(Unit* /*killer*/) override { Talk(SAY_TINHEAD_DEATH); SummonCroneIfReady(instance, me); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_TINHEAD_SLAY); } void UpdateAI(uint32 diff) override { if (AggroTimer) { if (AggroTimer <= diff) { me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); AggroTimer = 0; } else AggroTimer -= diff; } if (!UpdateVictim()) return; if (CleaveTimer <= diff) { DoCastVictim(SPELL_CLEAVE); CleaveTimer = 5000; } else CleaveTimer -= diff; if (RustCount < 8) { if (RustTimer <= diff) { ++RustCount; Talk(EMOTE_RUST); DoCast(me, SPELL_RUST); RustTimer = 6000; } else RustTimer -= diff; } } }; }; class boss_roar : public CreatureScript { public: boss_roar() : CreatureScript("boss_roar") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_roarAI : public ScriptedAI { boss_roarAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { AggroTimer = 20000; MangleTimer = 5000; ShredTimer = 10000; ScreamTimer = 15000; } InstanceScript* instance; uint32 AggroTimer; uint32 MangleTimer; uint32 ShredTimer; uint32 ScreamTimer; void Reset() override { Initialize(); } void MoveInLineOfSight(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); } void AttackStart(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::AttackStart(who); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_ROAR_AGGRO); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void JustDied(Unit* /*killer*/) override { Talk(SAY_ROAR_DEATH); SummonCroneIfReady(instance, me); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_ROAR_SLAY); } void UpdateAI(uint32 diff) override { if (AggroTimer) { if (AggroTimer <= diff) { me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); AggroTimer = 0; } else AggroTimer -= diff; } if (!UpdateVictim()) return; if (MangleTimer <= diff) { DoCastVictim(SPELL_MANGLE); MangleTimer = urand(5000, 8000); } else MangleTimer -= diff; if (ShredTimer <= diff) { DoCastVictim(SPELL_SHRED); ShredTimer = urand(10000, 15000); } else ShredTimer -= diff; if (ScreamTimer <= diff) { DoCastVictim(SPELL_FRIGHTENED_SCREAM); ScreamTimer = urand(20000, 30000); } else ScreamTimer -= diff; } }; }; class boss_crone : public CreatureScript { public: boss_crone() : CreatureScript("boss_crone") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_croneAI : public ScriptedAI { boss_croneAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { // Hello, developer from the future! It's me again! // This time, you're fixing Karazhan scripts. Awesome. These are a mess of hacks. An amalgamation of hacks, so to speak. Maybe even a Patchwerk thereof. // Anyway, I digress. // @todo This line below is obviously a hack. Duh. I'm just coming in here to hackfix the encounter to actually be completable. // It needs a rewrite. Badly. Please, take good care of it. me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->SetImmuneToPC(false); CycloneTimer = 30000; ChainLightningTimer = 10000; } InstanceScript* instance; uint32 CycloneTimer; uint32 ChainLightningTimer; void Reset() override { Initialize(); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_CRONE_SLAY); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_CRONE_AGGRO); } void JustDied(Unit* /*killer*/) override { Talk(SAY_CRONE_DEATH); instance->SetBossState(DATA_OPERA_PERFORMANCE, DONE); } void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; if (CycloneTimer <= diff) { if (Creature* Cyclone = DoSpawnCreature(CREATURE_CYCLONE, float(urand(0, 9)), float(urand(0, 9)), 0, 0, TEMPSUMMON_TIMED_DESPAWN, 15s)) Cyclone->CastSpell(Cyclone, SPELL_CYCLONE_VISUAL, true); CycloneTimer = 30000; } else CycloneTimer -= diff; if (ChainLightningTimer <= diff) { DoCastVictim(SPELL_CHAIN_LIGHTNING); ChainLightningTimer = 15000; } else ChainLightningTimer -= diff; } }; }; class npc_cyclone : public CreatureScript { public: npc_cyclone() : CreatureScript("npc_cyclone") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct npc_cycloneAI : public ScriptedAI { npc_cycloneAI(Creature* creature) : ScriptedAI(creature) { Initialize(); } void Initialize() { MoveTimer = 1000; } uint32 MoveTimer; void Reset() override { Initialize(); } void JustEngagedWith(Unit* /*who*/) override { } void MoveInLineOfSight(Unit* /*who*/) override { } void UpdateAI(uint32 diff) override { if (!me->HasAura(SPELL_KNOCKBACK)) DoCast(me, SPELL_KNOCKBACK, true); if (MoveTimer <= diff) { Position pos = me->GetRandomNearPosition(10); me->GetMotionMaster()->MovePoint(0, pos); MoveTimer = urand(5000, 8000); } else MoveTimer -= diff; } }; }; /**************************************/ /**** Opera Red Riding Hood Event* ***/ /************************************/ enum RedRidingHood { SAY_WOLF_AGGRO = 0, SAY_WOLF_SLAY = 1, SAY_WOLF_HOOD = 2, OPTION_WHAT_PHAT_LEWTS_YOU_HAVE = 7443, SOUND_WOLF_DEATH = 9275, SPELL_LITTLE_RED_RIDING_HOOD = 30768, SPELL_TERRIFYING_HOWL = 30752, SPELL_WIDE_SWIPE = 30761, CREATURE_BIG_BAD_WOLF = 17521 }; class npc_grandmother : public CreatureScript { public: npc_grandmother() : CreatureScript("npc_grandmother") { } struct npc_grandmotherAI : public ScriptedAI { npc_grandmotherAI(Creature* creature) : ScriptedAI(creature) { } bool OnGossipSelect(Player* player, uint32 menuId, uint32 gossipListId) override { if (menuId == OPTION_WHAT_PHAT_LEWTS_YOU_HAVE && gossipListId == 0) { CloseGossipMenuFor(player); if (Creature* pBigBadWolf = me->SummonCreature(CREATURE_BIG_BAD_WOLF, me->GetPositionX(), me->GetPositionY(), me->GetPositionZ(), me->GetOrientation(), TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 2h)) pBigBadWolf->AI()->AttackStart(player); me->DespawnOrUnsummon(); } return false; } }; CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } }; class boss_bigbadwolf : public CreatureScript { public: boss_bigbadwolf() : CreatureScript("boss_bigbadwolf") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_bigbadwolfAI : public ScriptedAI { boss_bigbadwolfAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); } void Initialize() { ChaseTimer = 30000; FearTimer = urand(25000, 35000); SwipeTimer = 5000; HoodGUID.Clear(); TempThreat = 0; IsChasing = false; } InstanceScript* instance; uint32 ChaseTimer; uint32 FearTimer; uint32 SwipeTimer; ObjectGuid HoodGUID; float TempThreat; bool IsChasing; void Reset() override { Initialize(); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_WOLF_AGGRO); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_WOLF_SLAY); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void JustDied(Unit* /*killer*/) override { DoPlaySoundToSet(me, SOUND_WOLF_DEATH); instance->SetBossState(DATA_OPERA_PERFORMANCE, DONE); } void UpdateAI(uint32 diff) override { if (!UpdateVictim()) return; if (ChaseTimer <= diff) { if (!IsChasing) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) { Talk(SAY_WOLF_HOOD); DoCast(target, SPELL_LITTLE_RED_RIDING_HOOD, true); TempThreat = GetThreat(target); if (TempThreat) ModifyThreatByPercent(target, -100); HoodGUID = target->GetGUID(); AddThreat(target, 1000000.0f); ChaseTimer = 20000; IsChasing = true; } } else { IsChasing = false; if (Unit* target = ObjectAccessor::GetUnit(*me, HoodGUID)) { HoodGUID.Clear(); if (GetThreat(target)) ModifyThreatByPercent(target, -100); AddThreat(target, TempThreat); TempThreat = 0; } ChaseTimer = 40000; } } else ChaseTimer -= diff; if (IsChasing) return; if (FearTimer <= diff) { DoCastVictim(SPELL_TERRIFYING_HOWL); FearTimer = urand(25000, 35000); } else FearTimer -= diff; if (SwipeTimer <= diff) { DoCastVictim(SPELL_WIDE_SWIPE); SwipeTimer = urand(25000, 30000); } else SwipeTimer -= diff; } }; }; /**********************************************/ /******** Opera Romeo and Juliet Event* ******/ /********************************************/ enum JulianneRomulo { /**** Speech *****/ SAY_JULIANNE_AGGRO = 0, SAY_JULIANNE_ENTER = 1, SAY_JULIANNE_DEATH01 = 2, SAY_JULIANNE_DEATH02 = 3, SAY_JULIANNE_RESURRECT = 4, SAY_JULIANNE_SLAY = 5, SAY_ROMULO_AGGRO = 0, SAY_ROMULO_DEATH = 1, SAY_ROMULO_ENTER = 2, SAY_ROMULO_RESURRECT = 3, SAY_ROMULO_SLAY = 4, SPELL_BLINDING_PASSION = 30890, SPELL_DEVOTION = 30887, SPELL_ETERNAL_AFFECTION = 30878, SPELL_POWERFUL_ATTRACTION = 30889, SPELL_DRINK_POISON = 30907, SPELL_BACKWARD_LUNGE = 30815, SPELL_DARING = 30841, SPELL_DEADLY_SWATHE = 30817, SPELL_POISON_THRUST = 30822, SPELL_UNDYING_LOVE = 30951, SPELL_RES_VISUAL = 24171, CREATURE_ROMULO = 17533, ROMULO_X = -10900, ROMULO_Y = -1758, }; enum RAJPhase { PHASE_JULIANNE = 0, PHASE_ROMULO = 1, PHASE_BOTH = 2, }; void PretendToDie(Creature* creature) { creature->InterruptNonMeleeSpells(true); creature->RemoveAllAuras(); creature->SetHealth(0); creature->SetUninteractible(true); creature->GetMotionMaster()->Clear(); creature->GetMotionMaster()->MoveIdle(); creature->SetStandState(UNIT_STAND_STATE_DEAD); } void Resurrect(Creature* target) { target->SetUninteractible(false); target->SetFullHealth(); target->SetStandState(UNIT_STAND_STATE_STAND); target->CastSpell(target, SPELL_RES_VISUAL, true); if (target->GetVictim()) { target->GetMotionMaster()->MoveChase(target->GetVictim()); target->AI()->AttackStart(target->GetVictim()); } else target->GetMotionMaster()->Initialize(); } class boss_julianne : public CreatureScript { public: boss_julianne() : CreatureScript("boss_julianne") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_julianneAI : public ScriptedAI { boss_julianneAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); EntryYellTimer = 1000; AggroYellTimer = 10000; IsFakingDeath = false; ResurrectTimer = 0; } void Initialize() { RomuloGUID.Clear(); Phase = PHASE_JULIANNE; BlindingPassionTimer = 30000; DevotionTimer = 15000; EternalAffectionTimer = 25000; PowerfulAttractionTimer = 5000; SummonRomuloTimer = 10000; DrinkPoisonTimer = 0; ResurrectSelfTimer = 0; SummonedRomulo = false; RomuloDead = false; } InstanceScript* instance; uint32 EntryYellTimer; uint32 AggroYellTimer; ObjectGuid RomuloGUID; uint32 Phase; uint32 BlindingPassionTimer; uint32 DevotionTimer; uint32 EternalAffectionTimer; uint32 PowerfulAttractionTimer; uint32 SummonRomuloTimer; uint32 ResurrectTimer; uint32 DrinkPoisonTimer; uint32 ResurrectSelfTimer; bool IsFakingDeath; bool SummonedRomulo; bool RomuloDead; void Reset() override { Initialize(); if (IsFakingDeath) { Resurrect(me); IsFakingDeath = false; } } void JustEngagedWith(Unit* /*who*/) override { } void AttackStart(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::AttackStart(who); } void MoveInLineOfSight(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void SpellHit(WorldObject* /*caster*/, SpellInfo const* spellInfo) override { if (spellInfo->Id == SPELL_DRINK_POISON) { Talk(SAY_JULIANNE_DEATH01); DrinkPoisonTimer = 2500; } } void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override; void JustDied(Unit* /*killer*/) override { Talk(SAY_JULIANNE_DEATH02); instance->SetBossState(DATA_OPERA_PERFORMANCE, DONE); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_JULIANNE_SLAY); } void UpdateAI(uint32 diff) override; }; }; class boss_romulo : public CreatureScript { public: boss_romulo() : CreatureScript("boss_romulo") { } CreatureAI* GetAI(Creature* creature) const override { return GetKarazhanAI(creature); } struct boss_romuloAI : public ScriptedAI { boss_romuloAI(Creature* creature) : ScriptedAI(creature) { Initialize(); instance = creature->GetInstanceScript(); EntryYellTimer = 8000; AggroYellTimer = 15000; } void Initialize() { JulianneGUID.Clear(); Phase = PHASE_ROMULO; BackwardLungeTimer = 15000; DaringTimer = 20000; DeadlySwatheTimer = 25000; PoisonThrustTimer = 10000; ResurrectTimer = 10000; IsFakingDeath = false; JulianneDead = false; } InstanceScript* instance; ObjectGuid JulianneGUID; uint32 Phase; uint32 EntryYellTimer; uint32 AggroYellTimer; uint32 BackwardLungeTimer; uint32 DaringTimer; uint32 DeadlySwatheTimer; uint32 PoisonThrustTimer; uint32 ResurrectTimer; bool IsFakingDeath; bool JulianneDead; void Reset() override { Initialize(); } void JustReachedHome() override { me->DespawnOrUnsummon(); } void DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) override { if (damage < me->GetHealth()) return; //anything below only used if incoming damage will kill if (Phase == PHASE_ROMULO) { Talk(SAY_ROMULO_DEATH); PretendToDie(me); IsFakingDeath = true; Phase = PHASE_BOTH; if (Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID))) { ENSURE_AI(boss_julianne::boss_julianneAI, Julianne->AI())->RomuloDead = true; ENSURE_AI(boss_julianne::boss_julianneAI, Julianne->AI())->ResurrectSelfTimer = 10000; } damage = 0; return; } if (Phase == PHASE_BOTH) { if (JulianneDead) { if (Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID))) { Julianne->SetUninteractible(false); Julianne->GetMotionMaster()->Clear(); Julianne->setDeathState(JUST_DIED); Julianne->CombatStop(true); Julianne->ReplaceAllDynamicFlags(UNIT_DYNFLAG_LOOTABLE); } return; } if (Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID))) { PretendToDie(me); IsFakingDeath = true; ENSURE_AI(boss_julianne::boss_julianneAI, Julianne->AI())->ResurrectTimer = 10000; ENSURE_AI(boss_julianne::boss_julianneAI, Julianne->AI())->RomuloDead = true; damage = 0; return; } } TC_LOG_ERROR("scripts", "boss_romuloAI: DamageTaken reach end of code, that should not happen."); } void JustEngagedWith(Unit* /*who*/) override { Talk(SAY_ROMULO_AGGRO); if (!JulianneGUID.IsEmpty()) { Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID)); if (Julianne && Julianne->GetVictim()) { AddThreat(Julianne->GetVictim(), 1.0f); AttackStart(Julianne->GetVictim()); } } } void MoveInLineOfSight(Unit* who) override { if (me->HasUnitFlag(UNIT_FLAG_NON_ATTACKABLE)) return; ScriptedAI::MoveInLineOfSight(who); } void JustDied(Unit* /*killer*/) override { Talk(SAY_ROMULO_DEATH); instance->SetBossState(DATA_OPERA_PERFORMANCE, DONE); } void KilledUnit(Unit* /*victim*/) override { Talk(SAY_ROMULO_SLAY); } void UpdateAI(uint32 diff) override { if (!UpdateVictim() || IsFakingDeath) return; if (JulianneDead) { if (ResurrectTimer <= diff) { Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID)); if (Julianne && ENSURE_AI(boss_julianne::boss_julianneAI, Julianne->AI())->IsFakingDeath) { Talk(SAY_ROMULO_RESURRECT); Resurrect(Julianne); ENSURE_AI(boss_julianne::boss_julianneAI, Julianne->AI())->IsFakingDeath = false; JulianneDead = false; ResurrectTimer = 10000; } } else ResurrectTimer -= diff; } if (BackwardLungeTimer <= diff) { Unit* target = SelectTarget(SelectTargetMethod::Random, 1, 100, true); if (target && !me->HasInArc(float(M_PI), target)) { DoCast(target, SPELL_BACKWARD_LUNGE); BackwardLungeTimer = urand(15000, 30000); } } else BackwardLungeTimer -= diff; if (DaringTimer <= diff) { DoCast(me, SPELL_DARING); DaringTimer = urand(20000, 40000); } else DaringTimer -= diff; if (DeadlySwatheTimer <= diff) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) DoCast(target, SPELL_DEADLY_SWATHE); DeadlySwatheTimer = urand(15000, 25000); } else DeadlySwatheTimer -= diff; if (PoisonThrustTimer <= diff) { DoCastVictim(SPELL_POISON_THRUST); PoisonThrustTimer = urand(10000, 20000); } else PoisonThrustTimer -= diff; } }; }; void boss_julianne::boss_julianneAI::UpdateAI(uint32 diff) { if (EntryYellTimer) { if (EntryYellTimer <= diff) { Talk(SAY_JULIANNE_ENTER); EntryYellTimer = 0; } else EntryYellTimer -= diff; } if (AggroYellTimer) { if (AggroYellTimer <= diff) { Talk(SAY_JULIANNE_AGGRO); me->RemoveUnitFlag(UNIT_FLAG_NON_ATTACKABLE); me->SetFaction(FACTION_MONSTER_2); AggroYellTimer = 0; } else AggroYellTimer -= diff; } if (DrinkPoisonTimer) { //will do this 2secs after spell hit. this is time to display visual as expected if (DrinkPoisonTimer <= diff) { PretendToDie(me); Phase = PHASE_ROMULO; SummonRomuloTimer = 10000; DrinkPoisonTimer = 0; } else DrinkPoisonTimer -= diff; } if (Phase == PHASE_ROMULO && !SummonedRomulo) { if (SummonRomuloTimer <= diff) { if (Creature* pRomulo = me->SummonCreature(CREATURE_ROMULO, ROMULO_X, ROMULO_Y, me->GetPositionZ(), 0, TEMPSUMMON_TIMED_OR_DEAD_DESPAWN, 2h)) { RomuloGUID = pRomulo->GetGUID(); ENSURE_AI(boss_romulo::boss_romuloAI, pRomulo->AI())->JulianneGUID = me->GetGUID(); ENSURE_AI(boss_romulo::boss_romuloAI, pRomulo->AI())->Phase = PHASE_ROMULO; DoZoneInCombat(pRomulo); pRomulo->SetFaction(FACTION_MONSTER_2); } SummonedRomulo = true; } else SummonRomuloTimer -= diff; } if (ResurrectSelfTimer) { if (ResurrectSelfTimer <= diff) { Resurrect(me); Phase = PHASE_BOTH; IsFakingDeath = false; if (me->GetVictim()) AttackStart(me->GetVictim()); ResurrectSelfTimer = 0; ResurrectTimer = 1000; } else ResurrectSelfTimer -= diff; } if (!UpdateVictim() || IsFakingDeath) return; if (RomuloDead) { if (ResurrectTimer <= diff) { Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID)); if (Romulo && ENSURE_AI(boss_romulo::boss_romuloAI, Romulo->AI())->IsFakingDeath) { Talk(SAY_JULIANNE_RESURRECT); Resurrect(Romulo); ENSURE_AI(boss_romulo::boss_romuloAI, Romulo->AI())->IsFakingDeath = false; RomuloDead = false; ResurrectTimer = 10000; } } else ResurrectTimer -= diff; } if (BlindingPassionTimer <= diff) { if (Unit* target = SelectTarget(SelectTargetMethod::Random, 0, 100, true)) DoCast(target, SPELL_BLINDING_PASSION); BlindingPassionTimer = urand(30000, 45000); } else BlindingPassionTimer -= diff; if (DevotionTimer <= diff) { DoCast(me, SPELL_DEVOTION); DevotionTimer = urand(15000, 45000); } else DevotionTimer -= diff; if (PowerfulAttractionTimer <= diff) { DoCast(SelectTarget(SelectTargetMethod::Random, 0), SPELL_POWERFUL_ATTRACTION); PowerfulAttractionTimer = urand(5000, 30000); } else PowerfulAttractionTimer -= diff; if (EternalAffectionTimer <= diff) { if (urand(0, 1) && SummonedRomulo) { Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID)); if (Romulo && Romulo->IsAlive() && !RomuloDead) DoCast(Romulo, SPELL_ETERNAL_AFFECTION); } else DoCast(me, SPELL_ETERNAL_AFFECTION); EternalAffectionTimer = urand(45000, 60000); } else EternalAffectionTimer -= diff; } void boss_julianne::boss_julianneAI::DamageTaken(Unit* /*done_by*/, uint32& damage, DamageEffectType /*damageType*/, SpellInfo const* /*spellInfo = nullptr*/) { if (damage < me->GetHealth()) return; //anything below only used if incoming damage will kill if (Phase == PHASE_JULIANNE) { damage = 0; //this means already drinking, so return if (IsFakingDeath) return; me->InterruptNonMeleeSpells(true); DoCast(me, SPELL_DRINK_POISON); IsFakingDeath = true; //IS THIS USEFULL? Creature* Julianne = (ObjectAccessor::GetCreature((*me), JulianneGUID)); return; } if (Phase == PHASE_ROMULO) { TC_LOG_ERROR("scripts", "boss_julianneAI: cannot take damage in PHASE_ROMULO, why was i here?"); damage = 0; return; } if (Phase == PHASE_BOTH) { //if this is true then we have to kill romulo too if (RomuloDead) { if (Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID))) { Romulo->SetUninteractible(false); Romulo->GetMotionMaster()->Clear(); Romulo->setDeathState(JUST_DIED); Romulo->CombatStop(true); Romulo->ReplaceAllDynamicFlags(UNIT_DYNFLAG_LOOTABLE); } return; } //if not already returned, then romulo is alive and we can pretend die if (Creature* Romulo = (ObjectAccessor::GetCreature((*me), RomuloGUID))) { PretendToDie(me); IsFakingDeath = true; ENSURE_AI(boss_romulo::boss_romuloAI, Romulo->AI())->ResurrectTimer = 10000; ENSURE_AI(boss_romulo::boss_romuloAI, Romulo->AI())->JulianneDead = true; damage = 0; return; } } TC_LOG_ERROR("scripts", "boss_julianneAI: DamageTaken reach end of code, that should not happen."); } void AddSC_bosses_opera() { new boss_dorothee(); new boss_strawman(); new boss_tinhead(); new boss_roar(); new boss_crone(); new npc_tito(); new npc_cyclone(); new npc_grandmother(); new boss_bigbadwolf(); new boss_julianne(); new boss_romulo(); }