Scripts/Icecrown Citadel

* Implemented heroic limited attempts for wing end bosses
* Added script for Blood Quickening weekly quest
This commit is contained in:
Shauren
2011-03-13 21:51:42 +01:00
parent b986dfb57a
commit e97d6e3a60
10 changed files with 302 additions and 25 deletions
@@ -8942,6 +8942,7 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
uint16 NumberOfFields = 0;
uint32 mapid = GetMapId();
OutdoorPvP * pvp = sOutdoorPvPMgr->GetOutdoorPvPToZoneId(zoneid);
InstanceScript* instance = GetInstanceScript();
sLog->outDebug(LOG_FILTER_NETWORKIO, "Sending SMSG_INIT_WORLD_STATES to Map: %u, Zone: %u", mapid, zoneid);
@@ -9012,6 +9013,9 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
case 4710:
NumberOfFields = 28;
break;
case 4812:
NumberOfFields = 13;
break;
default:
NumberOfFields = 12;
break;
@@ -9530,6 +9534,19 @@ void Player::SendInitWorldStates(uint32 zoneid, uint32 areaid)
data << uint32(4345) << uint32(1); // 24 unknown
}
break;
// Icecrown Citadel
case 4812:
if (instance && mapid == 631)
instance->FillInitialWorldStates(data);
else
{
data << uint32(4903) << uint32(0); // 9 WORLDSTATE_SHOW_TIMER (Blood Quickening weekly)
data << uint32(4904) << uint32(30); // 10 WORLDSTATE_EXECUTION_TIME
data << uint32(4940) << uint32(0); // 11 WORLDSTATE_SHOW_ATTEMPTS
data << uint32(4941) << uint32(50); // 12 WORLDSTATE_ATTEMPTS_REMAINING
data << uint32(4942) << uint32(50); // 13 WORLDSTATE_ATTEMPTS_MAX
}
break;
default:
data << uint32(0x914) << uint32(0x0); // 7
data << uint32(0x913) << uint32(0x0); // 8
@@ -60,6 +60,7 @@ enum Spells
SPELL_TWILIGHT_BLOODBOLT = 71446,
SPELL_INCITE_TERROR = 73070,
SPELL_BLOODBOLT_WHIRL = 71772,
SPELL_ANNIHILATE = 71322,
};
enum Shadowmourne
@@ -82,7 +83,7 @@ static const uint32 vampireAuras[3][MAX_DIFFICULTY] =
#define ESSENCE_OF_BLOOD_QUEEN_PLR RAID_MODE<uint32>(70879,71525,71530,71531)
#define FRENZIED_BLOODTHIRST RAID_MODE<uint32>(70877,71474,70877,71474)
enum eEvents
enum Events
{
EVENT_BERSERK = 1,
EVENT_VAMPIRIC_BITE = 2,
@@ -101,15 +102,17 @@ enum eEvents
#define GUID_VAMPIRE 1
enum ePoints
enum Points
{
POINT_CENTER = 1,
POINT_AIR = 2,
POINT_GROUND = 3,
POINT_MINCHAR = 4,
};
static const Position centerPos = {4595.7090f, 2769.4190f, 400.6368f, 0.0000f};
static const Position airPos = {4595.7090f, 2769.4190f, 422.3893f, 0.0000f};
static const Position centerPos = {4595.7090f, 2769.4190f, 400.6368f, 0.000000f};
static const Position airPos = {4595.7090f, 2769.4190f, 422.3893f, 0.000000f};
static const Position mincharPos = {4629.3711f, 2782.6089f, 424.6390f, 0.000000f};
bool IsVampire(Unit const* unit)
{
@@ -152,6 +155,7 @@ class boss_blood_queen_lana_thel : public CreatureScript
me->SetSpeed(MOVE_FLIGHT, 0.642857f, true);
offtank = NULL;
vampires.clear();
creditBloodQuickening = false;
}
void EnterCombat(Unit* who)
@@ -170,9 +174,10 @@ class boss_blood_queen_lana_thel : public CreatureScript
DoCast(me, SPELL_SHROUD_OF_SORROW, true);
DoCast(me, SPELL_FRENZIED_BLOODTHIRST_VISUAL, true);
creditBloodQuickening = instance->GetData(DATA_BLOOD_QUICKENING_STATE) == IN_PROGRESS;
}
void JustDied(Unit* /*killer*/)
void JustDied(Unit* killer)
{
_JustDied();
Talk(SAY_DEATH);
@@ -185,10 +190,62 @@ class boss_blood_queen_lana_thel : public CreatureScript
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_BLOOD_MIRROR_DUMMY);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_DELIRIOUS_SLASH);
instance->DoRemoveAurasDueToSpellOnPlayers(SPELL_PACT_OF_THE_DARKFALLEN);
// Blah, credit the quest
if (creditBloodQuickening)
{
if (Player* plr = killer->ToPlayer())
plr->RewardPlayerAndGroupAtEvent(NPC_INFILTRATOR_MINCHAR_BQ, plr);
if (Creature* minchar = me->FindNearestCreature(NPC_INFILTRATOR_MINCHAR_BQ, 200.0f))
{
minchar->SetUInt32Value(UNIT_NPC_EMOTESTATE, 0);
minchar->SetFlying(false);
minchar->SendMovementFlagUpdate();
minchar->RemoveAllAuras();
minchar->GetMotionMaster()->MoveCharge(4629.3711f, 2782.6089f, 401.5301f, SPEED_CHARGE/2.0f);
}
}
}
void DoAction(const int32 action)
{
if (action != ACTION_KILL_MINCHAR)
return;
if (instance->GetBossState(DATA_BLOOD_QUEEN_LANA_THEL) == IN_PROGRESS)
killMinchar = true;
else
{
me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x01);
me->SetFlying(true);
me->SendMovementFlagUpdate();
me->GetMotionMaster()->MovePoint(POINT_MINCHAR, mincharPos);
}
}
void EnterEvadeMode()
{
_EnterEvadeMode();
if (killMinchar)
{
me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x01);
me->SetFlying(true);
me->GetMotionMaster()->MovePoint(POINT_MINCHAR, mincharPos);
}
else
{
me->GetMotionMaster()->MoveTargetedHome();
Reset();
}
}
void JustReachedHome()
{
me->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
me->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, 0x01);
me->SetFlying(false);
me->SetReactState(REACT_AGGRESSIVE);
_JustReachedHome();
Talk(SAY_WIPE);
instance->SetBossState(DATA_BLOOD_QUEEN_LANA_THEL, FAIL);
@@ -229,11 +286,17 @@ class boss_blood_queen_lana_thel : public CreatureScript
me->RemoveUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
me->RemoveByteFlag(UNIT_FIELD_BYTES_1, 3, 0x01);
me->SetFlying(false);
me->SendMovementFlagUpdate();
me->SetReactState(REACT_AGGRESSIVE);
if (Unit *victim = me->SelectVictim())
AttackStart(victim);
events.ScheduleEvent(EVENT_BLOOD_MIRROR, 2500, EVENT_GROUP_CANCELLABLE);
break;
case POINT_MINCHAR:
DoCast(me, SPELL_ANNIHILATE, true);
// already in evade mode
me->GetMotionMaster()->MoveTargetedHome();
Reset();
default:
break;
}
@@ -346,6 +409,7 @@ class boss_blood_queen_lana_thel : public CreatureScript
me->AddUnitMovementFlag(MOVEMENTFLAG_LEVITATING);
me->SetByteFlag(UNIT_FIELD_BYTES_1, 3, 0x01);
me->SetFlying(true);
me->SendMovementFlagUpdate();
me->GetMotionMaster()->MovePoint(POINT_AIR, airPos);
break;
case EVENT_AIR_FLY_DOWN:
@@ -401,6 +465,8 @@ class boss_blood_queen_lana_thel : public CreatureScript
Player* offtank;
std::set<uint64> vampires;
bool creditBloodQuickening;
bool killMinchar;
};
CreatureAI* GetAI(Creature* creature) const
@@ -168,9 +168,6 @@ enum Phases
enum DeprogrammingData
{
QUEST_DEPROGRAMMING_10 = 24869,
QUEST_DEPROGRAMMING_25 = 24875,
NPC_DARNAVAN_10 = 38472,
NPC_DARNAVAN_25 = 38485,
NPC_DARNAVAN_CREDIT_10 = 39091,
@@ -1471,6 +1471,9 @@ class at_sindragosa_lair : public AreaTriggerScript
if (!instance->GetData(DATA_SINDRAGOSA_FROSTWYRMS) && instance->GetBossState(DATA_SINDRAGOSA) != DONE)
{
if (player->GetMap()->IsHeroic() && !instance->GetData(DATA_HEROIC_ATTEMPTS))
return true;
player->GetMap()->LoadGrid(SindragosaSpawnPos.GetPositionX(), SindragosaSpawnPos.GetPositionY());
if (Creature* sindragosa = player->GetMap()->SummonCreature(NPC_SINDRAGOSA, SindragosaSpawnPos))
{
@@ -644,6 +644,20 @@ class at_icc_shutdown_traps : public AreaTriggerScript
}
};
class at_icc_start_blood_quickening : public AreaTriggerScript
{
public:
at_icc_start_blood_quickening() : AreaTriggerScript("at_icc_start_blood_quickening") { }
bool OnTrigger(Player* player, AreaTriggerEntry const* /*areaTrigger*/)
{
if (InstanceScript* instance = player->GetInstanceScript())
if (instance->GetData(DATA_BLOOD_QUICKENING_STATE) == NOT_STARTED)
instance->SetData(DATA_BLOOD_QUICKENING_STATE, IN_PROGRESS);
return true;
}
};
void AddSC_icecrown_citadel()
{
new npc_highlord_tirion_fordring_lh();
@@ -654,4 +668,5 @@ void AddSC_icecrown_citadel()
new spell_icc_harvest_blight_specimen();
new at_icc_saurfang_portal();
new at_icc_shutdown_traps();
new at_icc_start_blood_quickening();
}
@@ -74,9 +74,12 @@ enum DataTypes
DATA_RIMEFANG = 24,
DATA_COLDFLAME_JETS = 25,
DATA_TEAM_IN_INSTANCE = 26,
DATA_BLOOD_QUICKENING_STATE = 27,
DATA_HEROIC_ATTEMPTS = 28,
};
#define MAX_ENCOUNTER 12
#define WEEKLY_NPCS 7
enum CreaturesIds
{
@@ -101,12 +104,14 @@ enum CreaturesIds
NPC_GARROSH_HELLSCREAM = 39372,
NPC_KING_VARIAN_WRYNN = 39371,
// Weekly questgivers
// Weekly quests
NPC_INFILTRATOR_MINCHAR = 38471,
NPC_KOR_KRON_LIEUTENANT = 38491,
NPC_SKYBREAKER_LIEUTENANT = 38492,
NPC_ALCHEMIST_ADRIANNA = 38501,
NPC_ALRIN_THE_AGILE = 38551,
NPC_INFILTRATOR_MINCHAR_BQ = 38558,
NPC_MINCHAR_BEAM_STALKER = 38557,
NPC_VALITHRIA_DREAMWALKER_QUEST = 38589,
// Lord Marrowgar
@@ -288,11 +293,37 @@ enum SharedActions
ACTION_ROTFACE_DEATH = -366272,
ACTION_CHANGE_PHASE = -366780,
// Blood-Queen Lana'thel
ACTION_KILL_MINCHAR = -379550,
// Sindragosa
ACTION_START_FROSTWYRM = -368530,
ACTION_TRIGGER_ASPHYXIATION = -368531,
};
enum WeekliesICC
{
QUEST_DEPROGRAMMING_10 = 24869,
QUEST_DEPROGRAMMING_25 = 24875,
QUEST_SECURING_THE_RAMPARTS_10 = 24870,
QUEST_SECURING_THE_RAMPARTS_25 = 24877,
QUEST_RESIDUE_RENDEZVOUS_10 = 24873,
QUEST_RESIDUE_RENDEZVOUS_25 = 24878,
QUEST_BLOOD_QUICKENING_10 = 24874,
QUEST_BLOOD_QUICKENING_25 = 24879,
QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_10 = 24872,
QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_25 = 24880,
};
enum WorldStatesICC
{
WORLDSTATE_SHOW_TIMER = 4903,
WORLDSTATE_EXECUTION_TIME = 4904,
WORLDSTATE_SHOW_ATTEMPTS = 4940,
WORLDSTATE_ATTEMPTS_REMAINING = 4941,
WORLDSTATE_ATTEMPTS_MAX = 4942,
};
// Declaration
extern Position const SindragosaSpawnPos;
@@ -48,17 +48,20 @@ static const DoorData doorData[] =
{0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE} // END
};
// this doesnt have to only store questgivers, also can be used for related quest spawns
struct WeeklyQuest
{
uint32 npcStart;
uint32 creatureEntry;
uint32 questId[2]; // 10 and 25 man versions
} WeeklyQuestData[5] =
} WeeklyQuestData[WEEKLY_NPCS] =
{
{NPC_INFILTRATOR_MINCHAR, {24869, 24875}}, // Deprogramming
{NPC_KOR_KRON_LIEUTENANT, {24870, 24877}}, // Securing the Ramparts
{NPC_ALCHEMIST_ADRIANNA, {24873, 24878}}, // Residue Rendezvous
{NPC_ALRIN_THE_AGILE, {24874, 24879}}, // Blood Quickening
{NPC_VALITHRIA_DREAMWALKER_QUEST, {24872, 24880}}, // Respite for a Tormented Soul
{NPC_INFILTRATOR_MINCHAR, {QUEST_DEPROGRAMMING_10, QUEST_DEPROGRAMMING_25 }}, // Deprogramming
{NPC_KOR_KRON_LIEUTENANT, {QUEST_SECURING_THE_RAMPARTS_10, QUEST_SECURING_THE_RAMPARTS_25 }}, // Securing the Ramparts
{NPC_ALCHEMIST_ADRIANNA, {QUEST_RESIDUE_RENDEZVOUS_10, QUEST_RESIDUE_RENDEZVOUS_25 }}, // Residue Rendezvous
{NPC_ALRIN_THE_AGILE, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening
{NPC_INFILTRATOR_MINCHAR_BQ, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening
{NPC_MINCHAR_BEAM_STALKER, {QUEST_BLOOD_QUICKENING_10, QUEST_BLOOD_QUICKENING_25 }}, // Blood Quickening
{NPC_VALITHRIA_DREAMWALKER_QUEST, {QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_10, QUEST_RESPITE_FOR_A_TORNMENTED_SOUL_25}}, // Respite for a Tormented Soul
};
class instance_icecrown_citadel : public InstanceMapScript
@@ -73,6 +76,7 @@ class instance_icecrown_citadel : public InstanceMapScript
SetBossNumber(MAX_ENCOUNTER);
LoadDoorData(doorData);
teamInInstance = 0;
heroicAttempts = 50;
ladyDeathwisperElevator = 0;
deathbringerSaurfang = 0;
saurfangDoor = 0;
@@ -103,6 +107,18 @@ class instance_icecrown_citadel : public InstanceMapScript
isNauseaEligible = true;
isOrbWhispererEligible = true;
coldflameJetsState = NOT_STARTED;
bloodQuickeningState = NOT_STARTED;
bloodQuickeningTimer = 0;
bloodQuickeningMinutes = 0;
}
void FillInitialWorldStates(WorldPacket& data)
{
data << uint32(WORLDSTATE_SHOW_TIMER) << uint32(bloodQuickeningState == IN_PROGRESS);
data << uint32(WORLDSTATE_EXECUTION_TIME) << uint32(bloodQuickeningMinutes);
data << uint32(WORLDSTATE_SHOW_ATTEMPTS) << uint32(instance->IsHeroic());
data << uint32(WORLDSTATE_ATTEMPTS_REMAINING) << uint32(heroicAttempts);
data << uint32(WORLDSTATE_ATTEMPTS_MAX) << uint32(50);
}
void OnPlayerEnter(Player* player)
@@ -228,11 +244,13 @@ class instance_icecrown_citadel : public InstanceMapScript
case NPC_KOR_KRON_LIEUTENANT:
case NPC_ALCHEMIST_ADRIANNA:
case NPC_ALRIN_THE_AGILE:
case NPC_INFILTRATOR_MINCHAR_BQ:
case NPC_MINCHAR_BEAM_STALKER:
case NPC_VALITHRIA_DREAMWALKER_QUEST:
{
for (uint8 questIndex = 0; questIndex < 5; ++questIndex)
for (uint8 questIndex = 0; questIndex < WEEKLY_NPCS; ++questIndex)
{
if (WeeklyQuestData[questIndex].npcStart == entry)
if (WeeklyQuestData[questIndex].creatureEntry == entry)
{
uint8 diffIndex = instance->GetSpawnMode() & 1;
if (!sPoolMgr->IsSpawnedObject<Quest>(WeeklyQuestData[questIndex].questId[diffIndex]))
@@ -396,6 +414,8 @@ class instance_icecrown_citadel : public InstanceMapScript
return coldflameJetsState;
case DATA_TEAM_IN_INSTANCE:
return teamInInstance;
case DATA_HEROIC_ATTEMPTS:
return heroicAttempts;
default:
break;
}
@@ -512,14 +532,47 @@ class instance_icecrown_citadel : public InstanceMapScript
break;
case DATA_PROFESSOR_PUTRICIDE:
HandleGameObject(plagueSigil, state != DONE);
if (instance->IsHeroic())
{
if (state == FAIL && heroicAttempts)
{
--heroicAttempts;
DoUpdateWorldState(WORLDSTATE_ATTEMPTS_REMAINING, heroicAttempts);
if (!heroicAttempts)
if (Creature* putricide = instance->GetCreature(professorPutricide))
putricide->DespawnOrUnsummon();
}
}
break;
case DATA_BLOOD_QUEEN_LANA_THEL:
HandleGameObject(bloodwingSigil, state != DONE);
if (instance->IsHeroic())
{
if (state == FAIL && heroicAttempts)
{
--heroicAttempts;
DoUpdateWorldState(WORLDSTATE_ATTEMPTS_REMAINING, heroicAttempts);
if (!heroicAttempts)
if (Creature* bq = instance->GetCreature(bloodQueenLanaThel))
bq->DespawnOrUnsummon();
}
}
break;
case DATA_VALITHRIA_DREAMWALKER:
break;
case DATA_SINDRAGOSA:
HandleGameObject(frostwingSigil, state != DONE);
if (instance->IsHeroic())
{
if (state == FAIL && heroicAttempts)
{
--heroicAttempts;
DoUpdateWorldState(WORLDSTATE_ATTEMPTS_REMAINING, heroicAttempts);
if (!heroicAttempts)
if (Creature* sindra = instance->GetCreature(sindragosa))
sindra->DespawnOrUnsummon();
}
}
break;
case DATA_THE_LICH_KING:
break;
@@ -547,6 +600,8 @@ class instance_icecrown_citadel : public InstanceMapScript
isOrbWhispererEligible = data ? true : false;
break;
case DATA_SINDRAGOSA_FROSTWYRMS:
if (instance->IsHeroic() && !heroicAttempts)
return;
if (data > 1)
frostwyrms = data;
else if (data == 1)
@@ -589,6 +644,31 @@ class instance_icecrown_citadel : public InstanceMapScript
trap->AI()->DoAction(ACTION_STOP_TRAPS);
}
break;
case DATA_BLOOD_QUICKENING_STATE:
{
// 3 is the index of Blood Quickening
if (!sPoolMgr->IsSpawnedObject<Quest>(WeeklyQuestData[3].questId[instance->GetSpawnMode() & 1]))
break;
if (bloodQuickeningState == data)
break;
switch (data)
{
case IN_PROGRESS:
bloodQuickeningTimer = 60000;
bloodQuickeningMinutes = 30;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, bloodQuickeningMinutes);
break;
case DONE:
bloodQuickeningTimer = 0;
bloodQuickeningMinutes = 0;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
break;
}
bloodQuickeningState = data;
break;
}
default:
break;
}
@@ -772,7 +852,8 @@ class instance_icecrown_citadel : public InstanceMapScript
OUT_SAVE_INST_DATA;
std::ostringstream saveStream;
saveStream << "I C " << GetBossSaveData() << uint32(coldflameJetsState);
saveStream << "I C " << GetBossSaveData() << uint32(coldflameJetsState)
<< " " << uint32(bloodQuickeningState) << " " << bloodQuickeningMinutes;
OUT_SAVE_INST_DATA_COMPLETE;
return saveStream.str();
@@ -804,14 +885,47 @@ class instance_icecrown_citadel : public InstanceMapScript
SetBossState(i, EncounterState(tmpState));
}
uint32 jets = 0;
loadStream >> jets;
coldflameJetsState = jets ? DONE : NOT_STARTED;
} else OUT_LOAD_INST_DATA_FAIL;
uint32 temp = 0;
loadStream >> temp;
coldflameJetsState = temp ? DONE : NOT_STARTED;
temp = 0;
loadStream >> temp;
bloodQuickeningState = temp ? DONE : NOT_STARTED; // DONE means finished (not success/fail)
loadStream >> bloodQuickeningMinutes;
}
else
OUT_LOAD_INST_DATA_FAIL;
OUT_LOAD_INST_DATA_COMPLETE;
}
void Update(uint32 diff)
{
if (bloodQuickeningMinutes)
{
if (bloodQuickeningTimer <= diff)
{
--bloodQuickeningMinutes;
bloodQuickeningTimer = 60000;
if (bloodQuickeningMinutes)
{
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 1);
DoUpdateWorldState(WORLDSTATE_EXECUTION_TIME, bloodQuickeningMinutes);
}
else
{
bloodQuickeningState = DONE;
DoUpdateWorldState(WORLDSTATE_SHOW_TIMER, 0);
if (Creature* bq = instance->GetCreature(bloodQueenLanaThel))
bq->AI()->DoAction(ACTION_KILL_MINCHAR);
}
SaveToDB();
}
else
bloodQuickeningTimer -= diff;
}
}
private:
uint64 ladyDeathwisperElevator;
uint64 deathbringerSaurfang;
@@ -837,10 +951,14 @@ class instance_icecrown_citadel : public InstanceMapScript
uint64 rimefang;
std::set<uint64> coldflameJets;
uint32 teamInInstance;
uint32 bloodQuickeningTimer;
uint16 heroicAttempts;
uint16 coldflameJetsState;
uint16 frostwyrms;
uint16 spinestalkerTrash;
uint16 rimefangTrash;
uint16 bloodQuickeningState;
uint16 bloodQuickeningMinutes;
bool isBonedEligible;
bool isOozeDanceEligible;
bool isNauseaEligible;