!!! quest script, add before void AddSC_moonglade()
/*####
# The Nightmare Manifests (8736) quest-event
####*/
#define QUEST_NIGHTMARE_MANIFESTS 8736
#define NPC_REMULOS 11832
#define NPC_ERANIKUS 15491
#define NPC_TYRANDE 15633
#define NPC_NIGHTMARE_PHANTASM 15629
#define NPC_MOONGLADE_WARDEN 11822
#define WP_COUNT 17
#define NPC_NIGHTMARE_PHANTASMS_COUNT 25 // "There are many of them" (c) wowhead
#define NPC_MOONGLADE_WARDENS_COUNT 10 // All city guards should assist in battle
#define spell_Conjure_Dream_Rift 25813
#define spell_Starfire 21668
float remulos_WP[WP_COUNT][5] =
{
// x y z orien waitTime
{7829.66f, -2244.87f, 463.87f, 4.13f, 3000},
{7817.25f, -2306.20f, 456.00f, 6.19f, 0},
{7866.54f, -2312.20f, 463.32f, 6.21f, 0},
{7942.543457f, -2320.170654f, 476.770355f, 4.991899f, 0},
{7953.036133f, -2357.953613f, 486.379303f, 4.878017f, 0},
{7962.706055f, -2411.155518f, 488.955231f, 4.736650f, 0},
{7976.860352f, -2552.697998f, 490.081390f, 3.305654f, 0},
{7949.307617f, -2569.120361f, 489.716248f, 4.428772f, 0},
{7950.945801f, -2597.000000f, 489.765564f, 4.550511f, 0},
{7948.758301f, -2610.823486f, 492.368988f, 4.436631f, 0},
{7928.785156f, -2629.920654f, 492.524933f, 3.981101f, 0}, // 10, speaking with Eranikus
{7948.697754f, -2610.551758f, 492.363983f, 1.366361f, 0},
{7952.019531f, -2591.974609f, 490.081238f, 1.393850f, 0},
{7940.567871f, -2577.845703f, 488.946808f, 2.886107f, 0},
{7908.662109f, -2566.450439f, 488.634644f, 2.862545f, 0},
{7873.132324f, -2567.422363f, 486.946442f, 3.239536f, 0},
{7839.844238f, -2570.598877f, 489.286224f, 3.243463f, 0} // 16, fighting (with phantasms)
};
// real positions are unknown, somewhere outside building
float phantasms_spawnpos[6][4] =
{
// x y z orien
{7870.747070f, -2589.451904f, 486.918121f, 1.744260f},
{7859.779297f, -2548.876709f, 486.872559f, 5.368867f},
{7930.114746f, -2548.529541f, 486.591217f, 3.660625f},
{7906.033691f, -2592.193115f, 485.090118f, 2.282248f},
{7925.208984f, -2574.688477f, 489.643951f, 2.922349f},
{7892.436523f, -2526.877441f, 485.581818f, 4.512779f},
};
// most of abilities not implemented - see http://www.wowhead.com/npc=11832#abilities for a full list
class npc_remulos : public CreatureScript
{
public:
npc_remulos() : CreatureScript("npc_remulos") { }
bool OnQuestAccept(Player* pPlayer, Creature* pCreature, Quest const *quest)
{
if (quest->GetQuestId() == QUEST_NIGHTMARE_MANIFESTS)
CAST_AI(npc_remulos::npc_remulosAI, pCreature->AI())->StartEvent(pPlayer);
return true;
}
struct npc_remulosAI : public npc_escortAI
{
public:
npc_remulosAI(Creature *c) : npc_escortAI(c) {}
uint64 PlayerGUID;
uint32 uiEvent_Timer;
uint32 uiPhase;
uint32 uiCheckPlayer_Timer;
uint32 uiStarfire_timer;
bool bCanFight;
// I'm using remulos as a storage for global event variables
Creature* Eranikus; // it's easier to store it here than finding him every time when need
uint32 uiCountDeadNightmares;
void Reset()
{
bCanFight = false;
PlayerGUID = 0;
uiEvent_Timer = 0;
uiPhase = 0;
uiCheckPlayer_Timer = 5000;
uiStarfire_timer = urand(7*IN_MILLISECONDS, 10*IN_MILLISECONDS); // real timer is unknown
Eranikus = NULL;
uiCountDeadNightmares = 0;
me->SetRespawnTime(0); // doesn't work?
SetRun(true);
SetDespawnAtEnd(false);
SetDespawnAtFar(false); // on offy you should be within at least ~50 yards to Remulos
// some stuff for restarting event
ResetStatus();
// re-enable flags
me->SetUInt32Value(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP | UNIT_NPC_FLAG_QUESTGIVER);
}
void StartEvent(Player* pPlayer)
{
if (!pPlayer)
return;
PlayerGUID = pPlayer->GetGUID();
for (uint8 i = 0; i < WP_COUNT; ++i)
AddWaypoint(i, remulos_WP[i][0], remulos_WP[i][1], remulos_WP[i][2], (uint32)remulos_WP[i][4]);
// no easier way? :/
std::string sText =
("We will locate the origin of the Nightmare through the fragments you collected, " +
std::string(pPlayer->GetName()) +
". From there, we will pull Eranikus through a rift in the Dream. Steel yourself, " +
pPlayer->getClassName() + ". We are inviting the embodiment of the Nightmare into our world."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
Start(false, true, PlayerGUID); // run, no attack
return;
}
// loose - from Eranikus's death
void CheckPlayer(bool loose = false)
{
if (!PlayerGUID)
return;
Player* pPlayer = GetPlayerForEscort();
// Defend yourself
if (!pPlayer) // player exited game
{
Reset();
return;
}
else if (pPlayer->GetGroup()) // raid
{
if (!pPlayer->GetGroup()->GetAliveMembersCount())
{
pPlayer->FailQuestForRaid(QUEST_NIGHTMARE_MANIFESTS);
Reset();
return;
}
} else if (!pPlayer->isAlive()) // solo
{
pPlayer->FailQuest(QUEST_NIGHTMARE_MANIFESTS);
Reset();
return;
}
if (pPlayer && loose)
{
pPlayer->FailQuestForRaid(QUEST_NIGHTMARE_MANIFESTS);
Reset();
return;
}
return;
}
void WaypointReached(uint32 id)
{
CheckPlayer();
switch (id)
{
case 0:
me->MonsterSay("To Nighthaven! Keep your army close, champion.", LANG_UNIVERSAL, 0);
break;
// start phase with Eranikus (speaking)
case 10:
{
uiPhase = 2;
SetEscortPaused(true); // No move
Player* pPlayer = GetPlayerForEscort();
std::string sText =
("The rift will be opened there, above the Lake Elun'ara. Prepare yourself, " +
std::string(pPlayer->GetName()) +
". Eranikus's entry into our world will be wrought with chaos and strife."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiEvent_Timer = 5000; // start using it as a speech timer
break;
}
// fighting with shades, it's last waypoint. Remulos should follow player after this one.
case WP_COUNT-1:
{
//SetRun(true);
Player* pPlayer = GetPlayerForEscort();
me->SetFacingToObject(GetPlayerForEscort()); // or -180 degrees
std::string sText =
("Please, champion, protect our people."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
uiEvent_Timer = 3000;
SetEscortPaused(true); // No move on waypoints
break;
}
}
}
void EnterCombat(Unit * /*who*/)
{
}
void JustDied(Unit * /*killer*/)
{
if (!PlayerGUID)
return;
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->FailQuestForRaid(QUEST_NIGHTMARE_MANIFESTS);
npc_escortAI::JustDied(NULL);
Reset(); // will it be called when respawned?
//me->Respawn(true); // not offlike?
}
void JustRespawned()
{
if (!PlayerGUID)
return;
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->FailQuestForRaid(QUEST_NIGHTMARE_MANIFESTS);
npc_escortAI::JustRespawned();
}
void UpdateAI(const uint32 diff)
{
if (!PlayerGUID)
return;
npc_escortAI::UpdateAI(diff);
// uiEvent_Timer partly handled in WaypointReached
if (uiEvent_Timer <= diff)
{
Player* pPlayer = GetPlayerForEscort();
if (!pPlayer) // not in world
EnterEvadeMode();
switch (uiPhase)
{
// standing on floor of a 'house' and speaking a lot
case 2:
{
std::string sText =
("He will stop at nothing to get to Malfurion's physical manifistation. That must not happen... We must keep the beast occupied long enough for Tyrande to arrive."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
uiEvent_Timer = 4000;
break;
}
// still speaking
case 3:
{
std::string sText =
("Defend Nighthaven, hero..."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
DoCast(spell_Conjure_Dream_Rift); // visual
uiPhase++;
uiEvent_Timer = 10000; // casting time
break;
}
// summon Eranikus
case 4:
{
// not server-wide emote on 3.3.5+ !
std::string sText =
("Eranikus, Tyrant of the Dream, has entered our world."
);
me->MonsterYellToZone(sText.c_str(), LANG_UNIVERSAL, 0); // TODO: make it real 'Zone' (now radius is 500.0f)
// near right coords, looks better than on offy
Position pos;
me->GetPosition(&pos);
pos.m_positionX -= 20;
pos.m_positionY -= 20;
pos.m_positionZ += 20;
// we will need NPC_ERANIKUS until event finish
if (Creature* e = me->SummonCreature(NPC_ERANIKUS, pos, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60*IN_MILLISECONDS))
{
Eranikus = e;
Eranikus->SetFlying(true);
Eranikus->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
Eranikus->SetReactState(REACT_PASSIVE);
Eranikus->SetFacingToObject(me);
}
else Reset(); // Should never happens
uiPhase++;
uiEvent_Timer = 5000;
break;
}
// First Eranikus speach
case 5:
{
std::string sText =
("Pitful predictable mortals... You know not what you have done! The master's will fulfilled. The Moonglade shall be destroyed and Malfurion along with it!"
);
Eranikus->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
uiEvent_Timer = 5000;
break;
}
// Renaulos answering
case 6:
{
std::string sText =
("Fiend! Face the might of Cenarius!"
);
me->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
uiEvent_Timer = 2000;
break;
}
// Eranikus answering
case 7:
{
Eranikus->MonsterTextEmote(" lets loose a sinister laugh.", true);
std::string sText =
("You are certanly not your father, insect. Should it interest me, I would crush you with but a swipe of my claws. Turn Shan'do Stormrage over to me and your pitiful life will be spared along with the lives of your people."
);
Eranikus->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
uiEvent_Timer = 6500;
break;
}
// Renaulos answering 2
case 8:
{
std::string sText =
("Who is the predictable one, beast? Surely you did not think that we would summon you on top of Malfurion? Your redemption comes, Eranikus. You will be cleansed of this madness - this corruption."
);
me->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
uiEvent_Timer = 6000;
break;
}
// Eranikus answering 2
case 9:
{
std::string sText =
("My redemption? You are bold, little one. My redemption comes by the will of my god."
);
Eranikus->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
Eranikus->MonsterTextEmote(" lets loose a sinister laugh.", true);
Eranikus->GetMotionMaster()->MoveCharge(7908.616699f, -2568.102539f, 520.725739f, 30.0f);//(remulos_WP[15][1], remulos_WP[15][2], remulos_WP[15][3]+30.0f, 30.0f);
uiPhase++;
uiEvent_Timer = 3000;
break;
}
// Renaulos answering 3 and continue moving
case 10:
{
std::string sText =
("Hurry, " +
std::string(pPlayer->GetName()) +
"! We must find protective cover!"
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
//Eranikus->SetVisibility(VISIBILITY_OFF); invisible for a some time?
//SetRun(false); Looks like his speed is getting slower, but not as walking speed
SetEscortPaused(false); // Continue move
uiEvent_Timer = 600000; // wait until remulos goes to waypoint 16 and change phase, this case block shouldn't be called twice
break;
}
// First fighting
case 11:
{
std::string sText =
("Rise, servants of the Nightmare! Rise and destroy this world! Let there be no survivors..."
);
Eranikus->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
SetEscortPaused(true);
std::string sText2 = ("We will battle these fiends, together! Nighthaven's Defenders are also among us. They will fight to the death if asked. Now, quickly, we must drive these aberations back to the Nightmare. Destroy them all!");
me->MonsterSay(sText2.c_str(), LANG_UNIVERSAL, 0);
// summon phantasms and add Remalous as variable (because of uiCountDeadNightmares)
for (int i = 0; i < NPC_NIGHTMARE_PHANTASMS_COUNT; i++)
{
uint32 r = urand(0, 5);
float collision = (float)urand(4, 8); // 4-8 yards, just for visual
if (Creature* phantasm = Eranikus->SummonCreature(NPC_NIGHTMARE_PHANTASM, phantasms_spawnpos[r][0]+collision, phantasms_spawnpos[r][1]+collision, phantasms_spawnpos[r][2], phantasms_spawnpos[r][3], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60*IN_MILLISECONDS))
{
// store Remulos in phantasms script
phantasm->AI()->JustSummoned(me);
}
}
// he should attack phantasms if they are near
SetCanAttack(true); bCanFight = true;
uiPhase++;
uiCheckPlayer_Timer = 0;
uiEvent_Timer = 5000;
break;
}
// Eranikus phrase
case 12:
{
std::string sText =
("Where is your savior? How long can you hold out against my attacks?"
);
Eranikus->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
// Some guards running here for help (summoning or existed guards?)
for (int i = 0; i < NPC_MOONGLADE_WARDENS_COUNT; i++)
{
float collision = (float)urand(4, 8); // 4-8 yards, just for visual
uint32 r = urand(0, 1) ? 1 : 0; // seems like only left and right sides
if (Creature* warden = me->SummonCreature(NPC_MOONGLADE_WARDEN, phantasms_spawnpos[r][0]+collision, phantasms_spawnpos[r][1]+collision, phantasms_spawnpos[r][2], phantasms_spawnpos[r][3], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60*IN_MILLISECONDS))
{
if (pPlayer->isInCombat() && pPlayer->getAttackerForHelper())
{
warden->Attack(pPlayer->getAttackerForHelper(), true);
break;
} else
warden->GetMotionMaster()->MoveCharge(remulos_WP[15][1]+collision, remulos_WP[15][2]+collision, remulos_WP[15][3], 20.0f);
}
}
me->GetMotionMaster()->MoveFollow(pPlayer, 5.0f, PET_FOLLOW_ANGLE);
uiPhase++;
uiEvent_Timer = 1000;
break;
}
// still fighting
case 13:
{
// waiting until all phantasms got killed
if (uiCountDeadNightmares == NPC_NIGHTMARE_PHANTASMS_COUNT)
{
SetCanAttack(false); bCanFight = false;
SetEscortPaused(false); // Continue move
uiPhase++;
std::string sText =
("Defeated my minions? Then face me, mortals!"
);
Eranikus->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
// CAST_AI is impossible here, so use existed virtual functions
Eranikus->AI()->DoAction(1);
// A LOT OF guards helping here with their rifles (they should run from the whole city)
for (int i = 0; i < NPC_MOONGLADE_WARDENS_COUNT*2; i++)
{
float collision = (float)urand(5, 20);
uint32 r = urand(0, 5);
if (Creature* warden = me->SummonCreature(NPC_MOONGLADE_WARDEN, phantasms_spawnpos[r][0]+collision, phantasms_spawnpos[r][1]+collision, phantasms_spawnpos[r][2], phantasms_spawnpos[r][3], TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60*IN_MILLISECONDS))
warden->Attack(Eranikus, false);
}
}
uiEvent_Timer = 3000;
break;
}
// fighting with Eranikus
case 14:
uiEvent_Timer = 6000000; // nothing to do
break;
default: break;
}
} else uiEvent_Timer -= diff;
if (uiCheckPlayer_Timer <= diff)
{
CheckPlayer();
if (bCanFight && !me->isInCombat())
{
Player* pPlayer = GetPlayerForEscort();
if (pPlayer)
{
if (pPlayer->isInCombat() && pPlayer->getVictim())
{
if (pPlayer->getVictim()->IsHostileTo(me))
AttackStart(pPlayer->getVictim());
}
else
me->GetMotionMaster()->MoveFollow(pPlayer, 5.0f, PET_FOLLOW_ANGLE);
}
uiCheckPlayer_Timer = 3000;
} else
uiCheckPlayer_Timer = 5000;
} else uiCheckPlayer_Timer -= diff;
if (bCanFight)
{
if (uiStarfire_timer <= diff)
{
DoCast(me->getVictim(), spell_Starfire);
uiStarfire_timer = urand(7*IN_MILLISECONDS, 10*IN_MILLISECONDS);
} else uiStarfire_timer -= diff;
DoMeleeAttackIfReady();
}
}
// it will be good if we get no call of it
void EnterEvadeMode()
{
Player* pPlayer = GetPlayerForEscort();
if (pPlayer)
{
if (pPlayer->isInCombat() && pPlayer->getVictim())
{
if (pPlayer->getVictim()->IsHostileTo(me))
AttackStart(pPlayer->getVictim());
return;
} else
{
// follow player
me->GetMotionMaster()->MoveFollow(pPlayer, 10.0f, PET_FOLLOW_ANGLE);
return;
}
}
npc_escortAI::EnterEvadeMode();
}
void Increment()
{
sLog.outError("uiCountDeadNightmares %u", uiCountDeadNightmares);
uiCountDeadNightmares++;
}
// yeah, event ends successfully
void HappyEnd()
{
if (Player* pPlayer = GetPlayerForEscort())
pPlayer->GroupEventHappens(QUEST_NIGHTMARE_MANIFESTS, me);
}
};
CreatureAI* GetAI(Creature* pCreature) const
{
return new npc_remulosAI(pCreature);
}
};
// lazy to do it uppercase
#define spell_Shadow_Bolt_Volley 17228
#define spell_Swell_of_Souls 21307
#define spell_Aura_of_Fear 26641
// they also have some emotion - skipped
class npc_nightmare_phantasm : public CreatureScript
{
public:
npc_nightmare_phantasm() : CreatureScript("npc_nightmare_phantasm") { }
struct npc_nightmare_phantasmAI : public ScriptedAI
{
public:
npc_nightmare_phantasmAI(Creature *c) : ScriptedAI(c) {}
uint32 uiTimer_SBV; // spell_Shadow_Bolt_Volley timer
uint32 uiTimer_die; // if by some reason isn't dead (they like to run somewhere). Also on offy you shouldn't kill all of them
Creature* Remulos;
void Reset()
{
uiTimer_SBV = urand(7*IN_MILLISECONDS, 12*IN_MILLISECONDS); // suggested by quest video, real time is unknown
DoCast(me, spell_Aura_of_Fear);
me->ApplySpellImmune(0, IMMUNITY_MECHANIC, MECHANIC_FEAR, true); // without this they affected by aura of fear
Remulos = NULL;
uiTimer_die = 120*IN_MILLISECONDS;
}
void UpdateAI(const uint32 diff)
{
if (!UpdateVictim())
{
FindVictim();
// stuck
if (uiTimer_die <= diff)
{
me->Kill(me); // JustDied will be called
//uiTimer_die = 15*IN_MILLISECONDS;
} else uiTimer_die -= diff;
return;
}
if (uiTimer_SBV <= diff)
{
DoCast(me->getVictim(), spell_Shadow_Bolt_Volley);
uiTimer_SBV = urand(7*IN_MILLISECONDS, 12*IN_MILLISECONDS);
} else uiTimer_SBV -= diff;
DoMeleeAttackIfReady();
}
// only player?
void KilledUnit(Unit * /*victim*/)
{
DoCast(me, spell_Swell_of_Souls, true);
}
void JustDied(Unit * /*victim*/)
{
if (Remulos)
CAST_AI(npc_remulos::npc_remulosAI, Remulos->AI())->Increment();
// happens when Remulos is too far or when Reset() called from evade?
else
if (Creature* r = me->FindNearestCreature(NPC_REMULOS, 1000.0f))
CAST_AI(npc_remulos::npc_remulosAI, r->AI())->Increment();
}
void JustSummoned(Creature *pSummoner)
{
Remulos = pSummoner;
//me->GetMotionMaster()->MoveCharge(remulos_WP[15][1], remulos_WP[15][2], remulos_WP[15][3], 20.0f);
FindVictim();
// DoZoneInCombat(); - it's selects summoners enemy - our friend
}
void FindVictim()
{
if (Unit *pUnit = me->SelectNearestTarget(80.0f))
{
me->GetMotionMaster()->MoveChase(pUnit);
AttackStart(pUnit);
}
// Main target - Remulos
else if (Remulos)
{
me->GetMotionMaster()->MoveChase(Remulos);
AttackStart(Remulos);
}
}
};
CreatureAI* GetAI(Creature* pCreature) const
{
return new npc_nightmare_phantasmAI(pCreature);
}
};
#define spell_Acid_Breath 24839
#define spell_Noxious_Breath 24818
#define spell_Shadow_Bolt_Volley2 25586
class npc_eranikus : public CreatureScript
{
public:
npc_eranikus() : CreatureScript("npc_eranikus") { }
struct npc_eranikusAI : public ScriptedAI
{
public:
npc_eranikusAI(Creature *c) : ScriptedAI(c) {}
uint32 uiTimer_SBV; // spell_Shadow_Bolt_Volley timer
uint32 uiTimer_breath_acid;
uint32 uiTimer_breath_nox;
uint32 uiTimer_event;
uint32 uiPhase;
bool bFightTime;
void Reset()
{
// real timers are unknown
uiTimer_SBV = urand(5*IN_MILLISECONDS, 12*IN_MILLISECONDS);
uiTimer_breath_acid = urand(7*IN_MILLISECONDS, 14*IN_MILLISECONDS);
uiTimer_breath_nox = urand(20*IN_MILLISECONDS, 30*IN_MILLISECONDS);
uiTimer_event = 10000; // don't trigger at summon
uiPhase = 0;
bFightTime = false;
me->SetFlying(true);
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_PASSIVE);
}
void UpdateAI(const uint32 diff)
{
// observer phase
if (!bFightTime)
return;
if (!UpdateVictim() && uiPhase < 7)
{
FindVictim();
return;
}
if (uiTimer_SBV <= diff)
{
DoCast(me->getVictim(), spell_Shadow_Bolt_Volley2);
uiTimer_SBV = urand(7*IN_MILLISECONDS, 12*IN_MILLISECONDS);
} else uiTimer_SBV -= diff;
if (uiTimer_breath_acid <= diff)
{
DoCast(me->getVictim(), spell_Acid_Breath);
uiTimer_breath_acid = urand(7*IN_MILLISECONDS, 14*IN_MILLISECONDS);
} else uiTimer_breath_acid -= diff;
if (uiTimer_breath_nox <= diff)
{
DoCast(me->getVictim(), spell_Noxious_Breath);
uiTimer_breath_nox = urand(20*IN_MILLISECONDS, 30*IN_MILLISECONDS);
} else uiTimer_breath_nox -= diff;
if (uiTimer_event <= diff)
{
// seems like timer, it's near 80%
switch (uiPhase)
{
case 0:
if (HealthBelowPct(80))
{
std::string sText =
("Remulos, look how easy they fall before me? You can stop this, fool. Turn the druid over to me and it will all be over..."
);
me->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
// "She spawns in Moonglade as a part of the Ahn'Qiraj scepter chain. You have to hold Eranikus off for some time until she comes to help."
// so don't matter where she should be spawned
if (Creature* Remulos = me->FindNearestCreature(NPC_REMULOS, 1000.0f))
{
float x = 7945.326172f;
float y = -2579.838135f;
float z = 489.947205f;
if (Creature* tyrande = Remulos->SummonCreature(NPC_TYRANDE, x, y, z, 2.673891f, TEMPSUMMON_CORPSE_TIMED_DESPAWN, 60*IN_MILLISECONDS))
{
tyrande->SetVisibility(VISIBILITY_OFF);
std::string sText2 =
("Grant us serenity! Watch over our fallen..."
);
tyrande->MonsterYell(sText2.c_str(), LANG_UNIVERSAL, 0);
}
}
uiPhase++;
}
uiTimer_event = 3000;
break;
// next phrases - timer is unknown, using health %
case 1:
if (HealthBelowPct(70))
{
if (Creature* tyrande = me->FindNearestCreature(NPC_TYRANDE, 100.0f))
{
std::string sText =
("Seek absolution, Eranikus. All will be forgiven.."
);
tyrande->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
}
uiPhase++;
}
uiTimer_event = 3000;
break;
case 2:
if (HealthBelowPct(60))
{
if (Creature* tyrande = me->FindNearestCreature(NPC_TYRANDE, 100.0f))
{
std::string sText =
("You will be forgiven, Eranikus. Elune will always love you. Break free of the bonds that command you!"
);
tyrande->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
}
uiPhase++;
}
uiTimer_event = 3000;
break;
case 3:
if (HealthBelowPct(40))
{
if (Creature* tyrande = me->FindNearestCreature(NPC_TYRANDE, 100.0f))
{
std::string sText =
("The grasp of the Old Gods is unmoving. He is consumed by their dark thoughts... I... I... I cannot... cannot channel much longer... Elune aide me."
);
tyrande->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
}
uiPhase++;
}
uiTimer_event = 3000;
break;
case 4:
if (HealthBelowPct(25))
{
std::string sText =
("IT BURNS! THE PAIN.. SEARING..."
);
me->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
}
uiTimer_event = 3000;
break;
case 5:
if (HealthBelowPct(22))
{
std::string sText =
("WHY? Why did this happen to... to me? Where were you Tyrande? Where were you when I fell from the grace of Elune?"
);
me->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
uiPhase++;
}
uiTimer_event = 3000;
break;
case 6:
// there is should be timer condition - can't see it on videos (Eranikus fake death after 20%)
if (HealthBelowPct(20))
{
std::string sText =
("I... I feel... I feel the touch of Elune upon my being once more... She smiles upon me... Yes... I..."
);
me->MonsterYell(sText.c_str(), LANG_UNIVERSAL, 0);
for (std::set<Unit*>::const_iterator i = me->getAttackers().begin(); i != me->getAttackers().end(); ++i)
if ((*i))
{
(*i)->RemoveAura(spell_Acid_Breath);
(*i)->RemoveAura(spell_Noxious_Breath);
}
me->SetReactState(REACT_PASSIVE);
me->GetMotionMaster()->Clear();
me->RemoveAllAuras();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
me->DeleteThreatList();
me->InterruptNonMeleeSpells(true);
me->CombatStop(true);
me->setFaction(35);
// Tyrande and priestess should appear and moving to center
// priestess are not implemented
if (Creature* tyrande = me->FindNearestCreature(NPC_TYRANDE, 300.0f))
{
tyrande->SetVisibility(VISIBILITY_ON);
tyrande->GetMotionMaster()->MoveCharge(7873.132324f, -2567.422363f, 486.946442f, 20.0f);
}
uiTimer_event = 10000;
uiPhase++;
} else
uiTimer_event = 3000;
break;
case 7:
{
//me->setDeathState(JUST_DIED); don't use it
me->SetUInt64Value(UNIT_FIELD_TARGET, 0); // clear target
me->SetStandState(UNIT_STAND_STATE_DEAD);
std::string sText2 =
("Eranikus, Tyrant of the Dream is wholly consumed by the Light of Elune. Tranquility sets in over the Moonglade."
);
// not server-wide emote in 3.3.5
me->MonsterYellToZone(sText2.c_str(), LANG_UNIVERSAL, 0);
uiTimer_event = 3000;
uiPhase++;
break;
}
case 8:
{
// Tyrande and her group should be in center now
if (Creature* tyrande = me->FindNearestCreature(NPC_TYRANDE, 300.0f))
{
tyrande->SetFacingToObject(me);
std::string sText =
("Tyrande falls to one knee."
);
tyrande->MonsterTextEmote(sText.c_str(), 0);
std::string sText2 =
("Praise be to Elune... Eranikus is redeemed."
);
tyrande->MonsterYell(sText2.c_str(), LANG_UNIVERSAL, 0);
}
uiTimer_event = 5000;
uiPhase++;
break;
}
case 9:
{
me->SetStandState(UNIT_STAND_STATE_STAND);
me->UpdateEntry(15628); // Eranikus the Redeemed
// model changed in script, even on offy
me->SetDisplayId(16783); // wrong
std::string sText =
("For so long, I was lost... The Nightmare's corruption had consumed me... And now, you... all of you.. you have saved me. Released me from its grasp."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiTimer_event = 8000;
uiPhase++;
break;
}
case 10:
{
std::string sText =
("But... Malfurion, Cenarius, Ysera... They still fight. They need me. I must return to the Dream at once."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiTimer_event = 5000;
uiPhase++;
break;
}
case 11:
{
std::string sText =
("My lady, I am unworthy of your prayer. Truly, you are an angel of light. Please, assist me in returning to the barrow den so that I may return to the Dream. I like Malfurion, also have a love awaiting me... I must return to her... to protect her..."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiTimer_event = 10000;
uiPhase++;
break;
}
case 12:
{
std::string sText =
("And heroes... I hold that which you seek. May it once more see the evil dissolved. Remulos, see to it that our champion receives the shard of the Green Flight."
);
me->MonsterSay(sText.c_str(), LANG_UNIVERSAL, 0);
uiTimer_event = 8000;
uiPhase++;
break;
}
case 13:
{
// Eranikus and Tyrande should disappear in previos phase
if (Creature* Remulos = me->FindNearestCreature(NPC_REMULOS, 1000.0f))
{
std::string sText2 =
("It will be done, Eranikus. Be well, ancient one."
);
Remulos->MonsterSay(sText2.c_str(), LANG_UNIVERSAL, 0);
CAST_AI(npc_remulos::npc_remulosAI, Remulos->AI())->HappyEnd();
Remulos->DisappearAndDie(); // quest won't be failed because of check in FailQuestForRaid()
}
if (Creature* tyrande = me->FindNearestCreature(NPC_TYRANDE, 100.0f))
tyrande->DisappearAndDie();
me->DisappearAndDie();
// THE END!
uiTimer_event = 120000;
uiPhase++;
break;
}
default: uiTimer_event = 3000; break;
} // switch end
} else uiTimer_event -= diff;
DoMeleeAttackIfReady();
}
void FindVictim()
{
if (Unit *pUnit = me->SelectNearestTarget(50.0f))
AttackStart(pUnit);
else if (Creature* r = me->FindNearestCreature(NPC_REMULOS, 1000.0f))
AttackStart(r);
}
// not possible now, need more info from official server
void JustDied(Unit * /*victim*/)
{
// Death = quest fail
if (Creature* r = me->FindNearestCreature(NPC_REMULOS, 1000.0f))
CAST_AI(npc_remulos::npc_remulosAI, r->AI())->CheckPlayer(true);
}
void DoAction(const int32 param)
{
if (param && !bFightTime)
SetFightPhase();
}
void SetFightPhase()
{
float ground = me->GetMap()->GetHeight(me->GetPositionX(), me->GetPositionY(), MAX_HEIGHT, true);
me->GetMotionMaster()->MoveFall(ground+2.0f); // SetFlying(false); also here.
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE | UNIT_FLAG_NOT_SELECTABLE);
me->SetReactState(REACT_AGGRESSIVE);
bFightTime = true;
}
};
CreatureAI* GetAI(Creature* pCreature) const
{
return new npc_eranikusAI(pCreature);
}
};
!!! Add next things after new npc_clintar_spirit();
// custom
new npc_remulos; // update creature_template set scriptname = 'npc_remulos' where entry = 11832;
new npc_nightmare_phantasm; // update creature_template set scriptname = 'npc_nightmare_phantasm' where entry = 15629;
new npc_eranikus; // update creature_template set scriptname = 'npc_eranikus' where entry = 15491;