Merge pull request #14157 from ariel-/shathalls

Shattered Halls: Revamped instance script
This commit is contained in:
MitchesD
2015-04-24 20:59:12 +02:00
6 changed files with 517 additions and 32 deletions

View File

@@ -0,0 +1,42 @@
-- Manual Spawn Randy Whizzlesprocket
SET @GUID := 49056;
DELETE FROM `creature` WHERE `guid` = @GUID;
INSERT INTO `creature` (`guid`, `id`, `map`, `zoneId`, `areaId`, `spawnMask`, `phaseMask`, `modelid`, `equipment_id`, `position_x`, `position_y`, `position_z`, `orientation`, `spawntimesecs`, `spawndist`, `currentwaypoint`, `curhealth`, `curmana`, `MovementType`, `npcflag`, `unit_flags`, `dynamicflags`) VALUES
(@GUID, 17288, 540, 0, 0, 2, 1, 0, 0, 131.106, 254.520, -45.236, 3.951, 7200, 0, 0, 6104, 0, 0, 0, 0, 0);
-- Missing 2xEmblem of Justice from quest 'Imprisoned in the citadel'
UPDATE `quest_template` SET `RewardItemId1` = 29434, `RewardItemCount1` = 2 WHERE `Id` IN (9524,9525);
-- Update Shattered Hand Executioner loot Table
UPDATE `creature_loot_template` SET `LootMode` = 8, `GroupId` = 1, `Chance` = 0 WHERE `Entry` = 20585;
DELETE FROM `creature_loot_template` WHERE `Entry` = 20585 AND `Item` IN (31716,29434,22829,22832,24726);
INSERT INTO `creature_loot_template` (`Entry`, `Item`, `Reference`, `Chance`, `QuestRequired`, `LootMode`, `GroupId`, `MinCount`, `MaxCount`, `Comment`) VALUES
(20585, 31716, 0, 100, 1, 2|4|8, 0, 1, 1, NULL), -- Unused Axe
(20585, 29434, 0, 100, 0, 8, 0, 1, 1, NULL), -- 1x Emblem of Justice (3 Prisoners Rescued)
(20585, 22829, 0, 0, 0, 2|4|8, 2, 2, 8, NULL), -- Greater Potions
(20585, 22832, 0, 0, 0, 2|4|8, 2, 2, 8, NULL),
(20585, 24726, 24726, 100, 0, 4|8, 0, 1, 1, NULL); -- Scroll of XXXX VI
DELETE FROM `smart_scripts` WHERE `entryorguid` = 17301 AND `source_type` = 0;
UPDATE `creature_template` SET `AIName` = '', ScriptName = 'boss_shattered_executioner', `flags_extra` = 0 WHERE `entry` = 17301;
UPDATE `creature_template` SET `unit_flags` = 33088 WHERE `entry` IN (17301,20585);
UPDATE `creature_template` SET `unit_flags` = 33555200 WHERE `entry` IN (19523,19524,20572,20573);
UPDATE `creature_template` SET `flags_extra` = 0 WHERE `entry` = 20585;
DELETE FROM `areatrigger_scripts` WHERE `entry` = 4524;
INSERT INTO `areatrigger_scripts` (`entry`, `ScriptName`) VALUES
(4524, 'at_nethekurse_exit');
DELETE FROM `spell_script_names` WHERE `spell_id` IN (39288,39289,39290);
INSERT INTO `spell_script_names` (`spell_id`, `ScriptName`) VALUES
(39288, 'spell_kargath_executioner'),
(39289, 'spell_kargath_executioner'),
(39290, 'spell_kargath_executioner');
UPDATE `gameobject_template` SET `flags` = 34 WHERE `entry` IN (182539,182540); -- Sniffed value
-- Missing texts
SET @KARGATH := 16808;
SET @BROADCAST := 13721;
DELETE FROM `creature_text` WHERE `entry` = @KARGATH AND `groupid` IN (3,4);
INSERT INTO `creature_text` (`entry`, `groupid`, `id`, `text`, `type`, `language`, `probability`, `emote`, `duration`, `sound`, `BroadcastTextId`, `TextRange`, `comment`) VALUES
(@KARGATH, 3, 0, 'The Alliance dares to intrude this far into my fortress? Bring out the Honor Hold prisoners and call for the executioner! They''ll pay with their lives for this trespass!', 14, 0, 100, 0, 0, 0, @BROADCAST+0, 3, 'kargath SAY_CALL_EXECUTIONER_A'),
(@KARGATH, 4, 0, 'Thrall''s false Horde dares to intrude this far into my fortress? Bring out the Thrallmar prisoners and call for the executioner! They''ll pay with their lives for this trespass!', 14, 0, 100, 0, 0, 0, @BROADCAST+1, 3, 'kargath SAY_CALL_EXECUTIONER_H');

View File

@@ -108,6 +108,7 @@ class boss_grand_warlock_nethekurse : public CreatureScript
void Reset() override
{
_Reset();
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_NON_ATTACKABLE);
Initialize();
@@ -115,9 +116,8 @@ class boss_grand_warlock_nethekurse : public CreatureScript
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DIE);
instance->SetBossState(DATA_NETHEKURSE, DONE);
}
void SetData(uint32 data, uint32 value) override

View File

@@ -160,6 +160,7 @@ class boss_warbringer_omrogg : public CreatureScript
void Reset() override
{
_Reset();
if (Unit* LeftHead = ObjectAccessor::GetUnit(*me, LeftHeadGUID))
{
LeftHead->setDeathState(JUST_DIED);
@@ -257,14 +258,14 @@ class boss_warbringer_omrogg : public CreatureScript
Creature* LeftHead = ObjectAccessor::GetCreature(*me, LeftHeadGUID);
Creature* RightHead = ObjectAccessor::GetCreature(*me, RightHeadGUID);
_JustDied();
if (!LeftHead || !RightHead)
return;
LeftHead->AI()->Talk(YELL_DIE_L);
RightHead->AI()->SetData(SETDATA_DATA, SETDATA_YELL);
instance->SetBossState(DATA_OMROGG, DONE);
}
void UpdateAI(uint32 diff) override

View File

@@ -35,7 +35,10 @@ enum Says
{
SAY_AGGRO = 0,
SAY_SLAY = 1,
SAY_DEATH = 2
SAY_DEATH = 2,
SAY_CALL_EXECUTIONER_A = 3,
SAY_CALL_EXECUTIONER_H = 4
};
enum Spells
@@ -84,10 +87,28 @@ class boss_warchief_kargath_bladefist : public CreatureScript
resetcheck_timer = 5000;
}
void SetData(uint32 type, uint32 data) override
{
if (type == 1)
{
switch (data)
{
case ALLIANCE:
Talk(SAY_CALL_EXECUTIONER_A);
break;
case HORDE:
Talk(SAY_CALL_EXECUTIONER_H);
break;
default:
break;
}
}
}
void Reset() override
{
removeAdds();
_Reset();
me->SetSpeed(MOVE_RUN, 2);
me->SetWalk(false);
@@ -96,10 +117,9 @@ class boss_warchief_kargath_bladefist : public CreatureScript
void JustDied(Unit* /*killer*/) override
{
_JustDied();
Talk(SAY_DEATH);
removeAdds();
instance->SetBossState(DATA_KARGATH, DONE);
}
void EnterCombat(Unit* /*who*/) override

View File

@@ -27,6 +27,22 @@ EndScriptData */
#include "InstanceScript.h"
#include "shattered_halls.h"
enum Spells
{
SPELL_CLEAVE = 15284,
SPELL_EXECUTE_1 = 39288,
SPELL_EXECUTE_2,
SPELL_EXECUTE_3
};
DoorData const doorData[] =
{
{GO_GRAND_WARLOCK_CHAMBER_DOOR_1, DATA_NETHEKURSE, DOOR_TYPE_PASSAGE, BOUNDARY_S },
{GO_GRAND_WARLOCK_CHAMBER_DOOR_2, DATA_NETHEKURSE, DOOR_TYPE_PASSAGE, BOUNDARY_N },
{0, 0, DOOR_TYPE_ROOM, BOUNDARY_NONE}
};
class instance_shattered_halls : public InstanceMapScript
{
public:
@@ -43,6 +59,43 @@ class instance_shattered_halls : public InstanceMapScript
{
SetHeaders(DataHeader);
SetBossNumber(EncounterCount);
LoadDoorData(doorData);
executionTimer = 0;
executed = 0;
_team = 0;
}
void OnPlayerEnter(Player* player) override
{
Aura* ex = nullptr;
if (!_team)
_team = player->GetTeam();
player->RemoveAurasDueToSpell(SPELL_EXECUTE_1);
player->RemoveAurasDueToSpell(SPELL_EXECUTE_2);
player->RemoveAurasDueToSpell(SPELL_EXECUTE_3);
if (!executionTimer || executionerGUID.IsEmpty())
return;
switch (executed)
{
case 0:
ex = player->AddAura(SPELL_EXECUTE_1, player);
break;
case 1:
ex = player->AddAura(SPELL_EXECUTE_2, player);
break;
case 2:
ex = player->AddAura(SPELL_EXECUTE_3, player);
break;
default:
break;
}
if (ex)
ex->SetDuration(executionTimer);
}
void OnGameObjectCreate(GameObject* go) override
@@ -50,21 +103,53 @@ class instance_shattered_halls : public InstanceMapScript
switch (go->GetEntry())
{
case GO_GRAND_WARLOCK_CHAMBER_DOOR_1:
nethekurseDoor1GUID = go->GetGUID();
break;
case GO_GRAND_WARLOCK_CHAMBER_DOOR_2:
nethekurseDoor2GUID = go->GetGUID();
AddDoor(go, true);
default:
break;
}
}
void OnGameObjectRemove(GameObject* go) override
{
switch (go->GetEntry())
{
case GO_GRAND_WARLOCK_CHAMBER_DOOR_1:
case GO_GRAND_WARLOCK_CHAMBER_DOOR_2:
AddDoor(go, false);
default:
break;
}
}
void OnCreatureCreate(Creature* creature) override
{
if (!_team)
{
Map::PlayerList const &players = instance->GetPlayers();
if (!players.isEmpty())
if (Player* player = players.begin()->GetSource())
_team = player->GetTeam();
}
switch (creature->GetEntry())
{
case NPC_GRAND_WARLOCK_NETHEKURSE:
nethekurseGUID = creature->GetGUID();
break;
case NPC_KARGATH_BLADEFIST:
kargathGUID = creature->GetGUID();
break;
case NPC_RANDY_WHIZZLESPROCKET:
if (_team == HORDE)
creature->UpdateEntry(NPC_DRISELLA);
break;
case NPC_SHATTERED_EXECUTIONER:
executionTimer = 55 * MINUTE * IN_MILLISECONDS;
DoCastSpellOnPlayers(SPELL_EXECUTE_1);
executionerGUID = creature->GetGUID();
SaveToDB();
break;
}
}
@@ -75,18 +160,20 @@ class instance_shattered_halls : public InstanceMapScript
switch (type)
{
case DATA_NETHEKURSE:
if (state == IN_PROGRESS)
case DATA_SHATTERED_EXECUTIONER:
if (state == DONE)
{
HandleGameObject(nethekurseDoor1GUID, false);
HandleGameObject(nethekurseDoor2GUID, false);
}
else
{
HandleGameObject(nethekurseDoor1GUID, true);
HandleGameObject(nethekurseDoor2GUID, true);
DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_1);
DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_2);
DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_3);
executionTimer = 0;
SaveToDB();
}
break;
case DATA_KARGATH:
if (Creature* executioner = instance->GetCreature(executionerGUID))
executioner->AI()->Reset();
break;
case DATA_OMROGG:
break;
}
@@ -100,24 +187,307 @@ class instance_shattered_halls : public InstanceMapScript
case NPC_GRAND_WARLOCK_NETHEKURSE:
return nethekurseGUID;
break;
case GO_GRAND_WARLOCK_CHAMBER_DOOR_1:
return nethekurseDoor1GUID;
case NPC_KARGATH_BLADEFIST:
return kargathGUID;
break;
case GO_GRAND_WARLOCK_CHAMBER_DOOR_2:
return nethekurseDoor2GUID;
case NPC_SHATTERED_EXECUTIONER:
return executionerGUID;
break;
default:
return ObjectGuid::Empty;
break;
}
return ObjectGuid::Empty;
}
void WriteSaveDataMore(std::ostringstream& data) override
{
if (!instance->IsHeroic())
return;
data << uint32(executed) << ' '
<< executionTimer << ' ';
}
void ReadSaveDataMore(std::istringstream& data) override
{
if (!instance->IsHeroic())
return;
uint32 readbuff;
data >> readbuff;
executed = uint8(readbuff);
data >> readbuff;
if (executed > VictimCount)
{
executed = VictimCount;
executionTimer = 0;
return;
}
if (!readbuff)
return;
Creature* executioner = nullptr;
instance->LoadGrid(Executioner.GetPositionX(), Executioner.GetPositionY());
if (Creature* kargath = instance->GetCreature(kargathGUID))
if (executionerGUID.IsEmpty())
executioner = kargath->SummonCreature(NPC_SHATTERED_EXECUTIONER, Executioner);
if (executioner)
for (uint8 i = executed; i < VictimCount; ++i)
executioner->SummonCreature(executionerVictims[i](GetData(DATA_TEAM_IN_INSTANCE)), executionerVictims[i].GetPos());
executionTimer = readbuff;
}
uint32 GetData(uint32 type) const override
{
switch (type)
{
case DATA_PRISONERS_EXECUTED:
return executed;
break;
case DATA_TEAM_IN_INSTANCE:
return _team;
break;
default:
return 0;
break;
}
}
void Update(uint32 diff) override
{
if (!executionTimer)
return;
if (executionTimer <= diff)
{
switch (++executed)
{
case 1:
DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_1);
DoCastSpellOnPlayers(SPELL_EXECUTE_2);
executionTimer = 10 * MINUTE * IN_MILLISECONDS;
break;
case 2:
DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_2);
DoCastSpellOnPlayers(SPELL_EXECUTE_3);
executionTimer = 15 * MINUTE * IN_MILLISECONDS;
break;
default:
DoRemoveAurasDueToSpellOnPlayers(SPELL_EXECUTE_3);
executionTimer = 0;
break;
}
if (Creature* executioner = instance->GetCreature(executionerGUID))
executioner->AI()->SetData(1, executed);
SaveToDB();
}
else
executionTimer -= diff;
}
protected:
ObjectGuid nethekurseGUID;
ObjectGuid nethekurseDoor1GUID;
ObjectGuid nethekurseDoor2GUID;
ObjectGuid kargathGUID;
ObjectGuid executionerGUID;
uint8 executed;
uint32 executionTimer;
uint32 _team;
};
};
class at_nethekurse_exit : public AreaTriggerScript
{
public:
at_nethekurse_exit() : AreaTriggerScript("at_nethekurse_exit") { };
bool OnTrigger(Player* player, AreaTriggerEntry const*) override
{
if (InstanceScript* is = player->GetInstanceScript())
{
if (is->instance->IsHeroic())
{
Creature* executioner = nullptr;
is->instance->LoadGrid(Executioner.GetPositionX(), Executioner.GetPositionY());
if (Creature* kargath = ObjectAccessor::GetCreature(*player, is->GetGuidData(NPC_KARGATH_BLADEFIST)))
{
if (is->GetGuidData(NPC_SHATTERED_EXECUTIONER).IsEmpty())
{
executioner = kargath->SummonCreature(NPC_SHATTERED_EXECUTIONER, Executioner);
kargath->AI()->SetData(1, is->GetData(DATA_TEAM_IN_INSTANCE));
}
}
if (executioner)
for (uint8 i = 0; i < VictimCount; ++i)
executioner->SummonCreature(executionerVictims[i](is->GetData(DATA_TEAM_IN_INSTANCE)), executionerVictims[i].GetPos());
}
}
return false;
}
};
class boss_shattered_executioner : public CreatureScript
{
public:
boss_shattered_executioner() : CreatureScript("boss_shattered_executioner") { }
struct boss_shattered_executionerAI : public BossAI
{
boss_shattered_executionerAI(Creature* creature) : BossAI(creature, DATA_SHATTERED_EXECUTIONER)
{
Initialize();
};
void Initialize()
{
cleaveTimer = 500;
me->ResetLootMode();
switch (instance->GetData(DATA_PRISONERS_EXECUTED))
{
case 0:
me->AddLootMode(LOOT_MODE_HARD_MODE_3);
case 1:
me->AddLootMode(LOOT_MODE_HARD_MODE_2);
case 2:
me->AddLootMode(LOOT_MODE_HARD_MODE_1);
default:
break;
}
}
void Reset() override
{
_Reset();
if (instance->GetBossState(DATA_KARGATH) == DONE)
me->RemoveFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
else
me->SetFlag(UNIT_FIELD_FLAGS, UNIT_FLAG_IMMUNE_TO_PC);
Initialize();
}
void JustSummoned(Creature*) override { }
void JustDied(Unit*) override
{
_JustDied();
Map::PlayerList const &players = instance->instance->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
Player* pl = itr->GetSource();
uint32 qId = pl->GetTeam() == ALLIANCE ? QUEST_IMPRISONED_A : QUEST_IMPRISONED_H;
if (pl->GetQuestStatus(qId) != QUEST_STATUS_NONE && pl->GetQuestStatus(qId) != QUEST_STATUS_FAILED)
pl->CompleteQuest(qId);
}
}
void SetData(uint32 type, uint32 data) override
{
if (type == 1)
{
uint32 entry = executionerVictims[data - 1](instance->GetData(DATA_TEAM_IN_INSTANCE));
if (Creature* victim = me->FindNearestCreature(entry, 30.0f, true))
me->Kill(victim);
if (data == 1)
{
Map::PlayerList const &players = instance->instance->GetPlayers();
for (Map::PlayerList::const_iterator itr = players.begin(); itr != players.end(); ++itr)
{
Player* pl = itr->GetSource();
uint32 qId = pl->GetTeam() == ALLIANCE ? QUEST_IMPRISONED_A : QUEST_IMPRISONED_H;
if (pl->GetQuestStatus(qId) == QUEST_STATUS_INCOMPLETE)
pl->FailQuest(qId);
}
}
switch (data)
{
case 3:
me->RemoveLootMode(LOOT_MODE_HARD_MODE_1);
case 2:
me->RemoveLootMode(LOOT_MODE_HARD_MODE_2);
case 1:
me->RemoveLootMode(LOOT_MODE_HARD_MODE_3);
default:
break;
}
}
}
void UpdateAI(uint32 diff) override
{
if (!UpdateVictim())
return;
if (cleaveTimer <= diff)
{
DoCast(SPELL_CLEAVE);
cleaveTimer = urand(5000, 7000);
}
else
cleaveTimer -= diff;
DoMeleeAttackIfReady();
}
private:
uint32 cleaveTimer;
};
CreatureAI* GetAI(Creature* creature) const override
{
return GetInstanceAI<boss_shattered_executionerAI>(creature);
}
};
class spell_kargath_executioner : public SpellScriptLoader
{
public:
spell_kargath_executioner() : SpellScriptLoader("spell_kargath_executioner") { }
class spell_kargath_executioner_AuraScript : public AuraScript
{
PrepareAuraScript(spell_kargath_executioner_AuraScript);
bool AreaCheck(Unit* target)
{
if (target->GetMap()->GetId() != 540)
return false;
return true;
}
bool Load() override
{
return GetCaster()->GetTypeId() == TYPEID_PLAYER;
}
void Register() override
{
DoCheckAreaTarget += AuraCheckAreaTargetFn(spell_kargath_executioner_AuraScript::AreaCheck);
}
};
AuraScript* GetAuraScript() const override
{
return new spell_kargath_executioner_AuraScript();
}
};
void AddSC_instance_shattered_halls()
{
new instance_shattered_halls();
new at_nethekurse_exit();
new boss_shattered_executioner();
new spell_kargath_executioner();
}

View File

@@ -21,18 +21,41 @@
#define DataHeader "SH"
uint32 const EncounterCount = 3;
uint32 const EncounterCount = 4;
uint32 const VictimCount = 3;
enum DataTypes
{
DATA_NETHEKURSE = 1,
DATA_OMROGG = 2,
DATA_KARGATH = 3
DATA_NETHEKURSE = 0,
DATA_OMROGG = 1,
DATA_KARGATH = 2,
DATA_SHATTERED_EXECUTIONER = 3,
DATA_PRISONERS_EXECUTED = 4,
DATA_TEAM_IN_INSTANCE = 5
};
enum CreatureIds
{
NPC_GRAND_WARLOCK_NETHEKURSE = 16807
NPC_GRAND_WARLOCK_NETHEKURSE = 16807,
NPC_KARGATH_BLADEFIST = 16808,
NPC_SHATTERED_EXECUTIONER = 17301,
// Alliance Ids
NPC_RANDY_WHIZZLESPROCKET = 17288,
NPC_CAPTAIN_ALINA = 17290,
NPC_ALLIANCE_VICTIM_1 = 17289,
NPC_ALLIANCE_VICTIM_2 = 17292,
// Horde Ids
NPC_DRISELLA = 17294,
NPC_CAPTAIN_BONESHATTER = 17296,
NPC_HORDE_VICTIM_1 = 17295,
NPC_HORDE_VICTIM_2 = 17297
};
enum GameobjectIds
@@ -41,4 +64,33 @@ enum GameobjectIds
GO_GRAND_WARLOCK_CHAMBER_DOOR_2 = 182540
};
enum QuestIds
{
QUEST_IMPRISONED_A = 9524,
QUEST_IMPRISONED_H = 9525
};
const Position Executioner = { 152.8524f, -83.63912f, 2.021005f, 0.06981317f };
struct FactionSpawnerHelper
{
FactionSpawnerHelper(uint32 allianceEntry, uint32 hordeEntry, const Position& pos) : _allianceNPC(allianceEntry), _hordeNPC(hordeEntry), _spawnPos(pos) { }
inline uint32 operator()(uint32 teamID) const { return teamID == ALLIANCE ? _allianceNPC : _hordeNPC; }
inline const Position GetPos() const { return _spawnPos; }
private:
const uint32 _allianceNPC;
const uint32 _hordeNPC;
const Position _spawnPos;
};
const FactionSpawnerHelper executionerVictims[VictimCount] =
{
{ NPC_CAPTAIN_ALINA, NPC_CAPTAIN_BONESHATTER, { 138.8807f, -84.22707f, 1.992269f, 0.06981317f } },
{ NPC_ALLIANCE_VICTIM_1, NPC_HORDE_VICTIM_1, { 151.2411f, -91.02930f, 2.019741f, 1.57079600f } },
{ NPC_ALLIANCE_VICTIM_2, NPC_HORDE_VICTIM_2, { 151.0459f, -77.51981f, 2.021008f, 4.74729500f } }
};
#endif