From 048025ceba086f71d1f78054caa6a38e11fca0ab Mon Sep 17 00:00:00 2001 From: UltraNix <80540499+UltraNix@users.noreply.github.com> Date: Tue, 21 Sep 2021 12:36:51 +0200 Subject: [PATCH] fix(Scripts/ZulFarrak): Moved Sergeant Bly's script from DB to C++ (#7855) --- .../rev_1631560059547242900.sql | 15 + .../Kalimdor/ZulFarrak/instance_zulfarrak.cpp | 406 ++++++++++- .../scripts/Kalimdor/ZulFarrak/zulfarrak.cpp | 674 ++++++++++++++++++ .../scripts/Kalimdor/ZulFarrak/zulfarrak.h | 54 +- .../Kalimdor/kalimdor_script_loader.cpp | 2 + 5 files changed, 1127 insertions(+), 24 deletions(-) create mode 100644 data/sql/updates/pending_db_world/rev_1631560059547242900.sql create mode 100644 src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp diff --git a/data/sql/updates/pending_db_world/rev_1631560059547242900.sql b/data/sql/updates/pending_db_world/rev_1631560059547242900.sql new file mode 100644 index 000000000..423c39ec7 --- /dev/null +++ b/data/sql/updates/pending_db_world/rev_1631560059547242900.sql @@ -0,0 +1,15 @@ +INSERT INTO `version_db_world` (`sql_rev`) VALUES ('1631560059547242900'); + +UPDATE `creature_template` SET `AIName`="", `ScriptName`="npc_sergeant_bly" WHERE `entry`=7604; +UPDATE `creature_template` SET `AIName`="", `ScriptName`="npc_weegli_blastfuse" WHERE `entry`=7607; +UPDATE `gameobject_template` SET `AIName`="", `ScriptName`="go_troll_cage" WHERE `entry` BETWEEN 141070 AND 141074; +UPDATE `creature_template` SET `AIName`="", `ScriptName`="npc_shadowpriest_sezziz" WHERE `entry`=7275; + +DELETE FROM `gossip_menu_option` WHERE `MenuId` IN (940,941); +DELETE FROM `conditions` WHERE `Sourcetypeorreferenceid` IN (14,15) AND `SourceGroup` IN (940,941); + +DELETE FROM `smart_scripts` WHERE `entryorguid` IN (7604,7607,7275); +DELETE FROM `smart_scripts` WHERE `entryorguid` BETWEEN 760400 AND 760403; +DELETE FROM `smart_scripts` WHERE `entryorguid` BETWEEN 141070 AND 141074; + +DELETE FROM `creature_summon_groups` WHERE `summonerId`=7604; diff --git a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp index 16393c72c..82ebca915 100644 --- a/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp +++ b/src/server/scripts/Kalimdor/ZulFarrak/instance_zulfarrak.cpp @@ -9,10 +9,87 @@ #include "TemporarySummon.h" #include "zulfarrak.h" +enum Misc +{ + // Paths + PATH_ADDS = 81553, + + SAY_BLY_FORWARD = 2 +}; + +struct PyramidEventData +{ + uint8 waveID = 0; + uint32 creatureID = 0; + Position pos; +}; + +uint32 const pyramidSpawnTotal = 54; + +/* list of wave spawns: 0 = wave ID, 1 = creature id, 2 = x, 3 = y +no z coordinat b/c they're all the same */ +static PyramidEventData pyramidSpawns[pyramidSpawnTotal] = +{ + { 1, NPC_SANDFURY_CRETIN, { 1894.64f, 1206.29f } }, + { 1, NPC_SANDFURY_SLAVE, { 1890.08f, 1218.68f } }, + { 1, NPC_SANDFURY_ACOLYTE, { 1883.76f, 1222.30f } }, + { 1, NPC_SANDFURY_CRETIN, { 1874.18f, 1221.24f } }, + { 1, NPC_SANDFURY_SLAVE, { 1892.28f, 1225.49f } }, + { 1, NPC_SANDFURY_DRUDGE, { 1889.94f, 1212.21f } }, + { 1, NPC_SANDFURY_SLAVE, { 1879.02f, 1223.06f } }, + { 1, NPC_SANDFURY_CRETIN, { 1874.45f, 1204.44f } }, + { 1, NPC_SANDFURY_ACOLYTE, { 1898.23f, 1217.97f } }, + { 1, NPC_SANDFURY_SLAVE, { 1882.07f, 1225.70f } }, + { 1, NPC_SANDFURY_ZEALOT, { 1896.46f, 1205.62f } }, + { 1, NPC_SANDFURY_SLAVE, { 1886.97f, 1225.86f } }, + { 1, NPC_SANDFURY_SLAVE, { 1894.72f, 1221.91f } }, + { 1, NPC_SANDFURY_SLAVE, { 1883.50f, 1218.25f } }, + { 1, NPC_SANDFURY_SLAVE, { 1886.93f, 1221.40f } }, + { 1, NPC_SANDFURY_ACOLYTE, { 1889.82f, 1222.51f } }, + { 1, NPC_SANDFURY_DRUDGE, { 1893.07f, 1215.26f } }, + { 1, NPC_SANDFURY_DRUDGE, { 1878.57f, 1214.16f } }, + { 1, NPC_SANDFURY_DRUDGE, { 1883.74f, 1212.35f } }, + { 1, NPC_SANDFURY_ZEALOT, { 1877.00f, 1207.27f } }, + { 1, NPC_SANDFURY_ZEALOT, { 1873.63f, 1204.65f } }, + { 1, NPC_SANDFURY_ACOLYTE, { 1877.40f, 1216.41f } }, + { 1, NPC_SANDFURY_ZEALOT, { 1899.63f, 1202.52f } }, + { 2, NPC_SANDFURY_CRETIN, { 1902.83f, 1223.41f } }, + { 2, NPC_SANDFURY_ACOLYTE, { 1889.82f, 1222.51f } }, + { 2, NPC_SANDFURY_SLAVE, { 1883.50f, 1218.25f } }, + { 2, NPC_SANDFURY_DRUDGE, { 1883.74f, 1212.35f } }, + { 2, NPC_SANDFURY_ZEALOT, { 1877.00f, 1207.27f } }, + { 2, NPC_SANDFURY_SLAVE, { 1890.08f, 1218.68f } }, + { 2, NPC_SANDFURY_CRETIN, { 1894.64f, 1206.29f } }, + { 2, NPC_SANDFURY_ACOLYTE, { 1877.40f, 1216.41f } }, + { 2, NPC_SANDFURY_SLAVE, { 1892.28f, 1225.49f } }, + { 2, NPC_SANDFURY_DRUDGE, { 1893.07f, 1215.26f } }, + { 2, NPC_SANDFURY_ZEALOT, { 1896.46f, 1205.62f } }, + { 2, NPC_SANDFURY_CRETIN, { 1874.45f, 1204.44f } }, + { 2, NPC_SANDFURY_CRETIN, { 1874.18f, 1221.24f } }, + { 2, NPC_SANDFURY_SLAVE, { 1879.02f, 1223.06f } }, + { 2, NPC_SANDFURY_ACOLYTE, { 1898.23f, 1217.97f } }, + { 2, NPC_SANDFURY_SLAVE, { 1882.07f, 1225.70f } }, + { 2, NPC_SANDFURY_ZEALOT, { 1873.63f, 1204.65f } }, + { 2, NPC_SANDFURY_SLAVE, { 1886.97f, 1225.86f } }, + { 2, NPC_SANDFURY_DRUDGE, { 1878.57f, 1214.16f } }, + { 2, NPC_SANDFURY_SLAVE, { 1894.72f, 1221.91f } }, + { 2, NPC_SANDFURY_SLAVE, { 1886.93f, 1221.40f } }, + { 2, NPC_SANDFURY_ACOLYTE, { 1883.76f, 1222.30f } }, + { 2, NPC_SANDFURY_DRUDGE, { 1889.94f, 1212.21f } }, + { 2, NPC_SANDFURY_ZEALOT, { 1899.63f, 1202.52f } }, + { 3, NPC_SANDFURY_DRUDGE, { 1878.57f, 1214.16f } }, + { 3, NPC_SANDFURY_SLAVE, { 1894.72f, 1221.91f } }, + { 3, NPC_SANDFURY_SLAVE, { 1886.93f, 1221.40f } }, + { 3, NPC_SANDFURY_ACOLYTE, { 1883.76f, 1222.30f } }, + { 3, NPC_SANDFURY_DRUDGE, { 1889.94f, 1212.21f } }, + { 3, NPC_SHADOWPRIEST_SEZZZIZ, { 1886.30f, 1199.65f } }, + { 3, NPC_NEKRUM_GUTCHEWER, { 1881.06f, 1199.70f } } +}; + class instance_zulfarrak : public InstanceMapScript { public: - instance_zulfarrak() : InstanceMapScript("instance_zulfarrak", 209) { } + instance_zulfarrak() : InstanceMapScript(ZFScriptName, 209) {} InstanceScript* GetInstanceScript(InstanceMap* map) const override { @@ -23,44 +100,337 @@ public: { instance_zulfarrak_InstanceMapScript(Map* map) : InstanceScript(map) { } + ObjectGuid EndDoorGUID; + ObjectGuid BlyGUID; + ObjectGuid WeegliGUID; + ObjectGuid OroGUID; + ObjectGuid RavenGUID; + ObjectGuid MurtaGUID; + ObjectGuid ShadowpriestGUID; + + GuidList addsAtBase; + GuidList movedadds; + + uint32 GahzrillaSummoned; + uint32 PyramidPhase; + uint32 major_wave_Timer; + uint32 minor_wave_Timer; + uint32 addGroupSize; + uint32 waypoint; + void Initialize() override { - _pyramidEventProgress = NOT_STARTED; - _gahzrillaSummoned = NOT_STARTED; + GahzrillaSummoned = NOT_STARTED; + + PyramidPhase = 0; + major_wave_Timer = 0; + minor_wave_Timer = 0; + addGroupSize = 0; + waypoint = 0; + } + + void OnCreatureCreate(Creature* creature) override + { + switch (creature->GetEntry()) + { + case NPC_BLY: + BlyGUID = creature->GetGUID(); + creature->SetReactState(REACT_PASSIVE); // starts out passive (in a cage) + break; + case NPC_RAVEN: + RavenGUID = creature->GetGUID(); + creature->SetReactState(REACT_PASSIVE);// starts out passive (in a cage) + break; + case NPC_ORO: + OroGUID = creature->GetGUID(); + creature->SetReactState(REACT_PASSIVE);// starts out passive (in a cage) + break; + case NPC_WEEGLI: + WeegliGUID = creature->GetGUID(); + creature->SetReactState(REACT_PASSIVE);// starts out passive (in a cage) + break; + case NPC_MURTA: + MurtaGUID = creature->GetGUID(); + creature->SetReactState(REACT_PASSIVE);// starts out passive (in a cage) + break; + case NPC_SHADOWPRIEST_SEZZZIZ: + ShadowpriestGUID = creature->GetGUID(); + break; + default: + break; + } } void OnGameObjectCreate(GameObject* gameobject) override { - if (gameobject->GetEntry() == GO_END_DOOR && _pyramidEventProgress == DONE) - gameobject->SetGoState(GO_STATE_ACTIVE_ALTERNATIVE); + switch (gameobject->GetEntry()) + { + case GO_END_DOOR: + EndDoorGUID = gameobject->GetGUID(); + if (PyramidPhase == PYRAMID_DONE) + { + HandleGameObject(gameobject->GetGUID(), true, gameobject); + } + break; + default: + break; + } } - uint32 GetData(uint32 type) const override + void OnUnitDeath(Unit* unit) override { - if (type == TYPE_PYRAMID_EVENT) - return _pyramidEventProgress; - return 0; + if (unit->GetEntry() == NPC_BLY) + { + PyramidPhase = PYRAMID_DONE; + SaveToDB(); + } } void SetData(uint32 type, uint32 data) override { switch (type) { - case TYPE_PYRAMID_EVENT: - _pyramidEventProgress = data; + case DATA_PYRAMID: + PyramidPhase = data; break; - case TYPE_GAHZRILLA: - _gahzrillaSummoned = data; + case DATA_GAHZRILLA: + GahzrillaSummoned = data; + break; + default: break; } SaveToDB(); } + uint32 GetData(uint32 type) const override + { + switch (type) + { + case DATA_PYRAMID: + return PyramidPhase; + case DATA_GAHZRILLA: + return GahzrillaSummoned; + } + + return 0; + } + + ObjectGuid GetGuidData(uint32 data) const override + { + switch (data) + { + case NPC_BLY: + return BlyGUID; + case NPC_RAVEN: + return RavenGUID; + case NPC_ORO: + return OroGUID; + case NPC_WEEGLI: + return WeegliGUID; + case NPC_MURTA: + return MurtaGUID; + case NPC_SHADOWPRIEST_SEZZZIZ: + return ShadowpriestGUID; + case GO_END_DOOR: + return EndDoorGUID; + } + + return ObjectGuid::Empty; + } + + void Update(uint32 diff) override + { + switch (PyramidPhase) + { + case PYRAMID_NOT_STARTED: + case PYRAMID_KILLED_ALL_TROLLS: + case PYRAMID_MOVED_DOWNSTAIRS: + case PYRAMID_DESTROY_GATES: + case PYRAMID_GATES_DESTROYED: + case PYRAMID_DONE: + break; + case PYRAMID_ARRIVED_AT_STAIR: + SpawnPyramidWave(1); + SetData(DATA_PYRAMID, PYRAMID_WAVE_1); + major_wave_Timer = 2 * MINUTE * IN_MILLISECONDS; + minor_wave_Timer = 0; + addGroupSize = 2; + break; + case PYRAMID_WAVE_1: + if (IsWaveAllDead()) + { + SetData(DATA_PYRAMID, PYRAMID_PRE_WAVE_2); + major_wave_Timer = 10 * IN_MILLISECONDS; //give players a few seconds before wave 2 starts to rebuff + } + else + { + if (minor_wave_Timer <= diff) + { + SendAddsUpStairs(addGroupSize++); + minor_wave_Timer = 10 * IN_MILLISECONDS; + } + else + { + minor_wave_Timer -= diff; + } + } + break; + case PYRAMID_PRE_WAVE_2: + if (major_wave_Timer <= diff) + { + // beginning 2nd wave! + SpawnPyramidWave(2); + SetData(DATA_PYRAMID, PYRAMID_WAVE_2); + minor_wave_Timer = 0; + addGroupSize = 2; + } + else + { + major_wave_Timer -= diff; + } + break; + case PYRAMID_WAVE_2: + if (IsWaveAllDead()) + { + SpawnPyramidWave(3); + SetData(DATA_PYRAMID, PYRAMID_PRE_WAVE_3); + major_wave_Timer = 5 * IN_MILLISECONDS; //give NPCs time to return to their home spots + } + else + { + if (minor_wave_Timer <= diff) + { + SendAddsUpStairs(addGroupSize++); + minor_wave_Timer = 10 * IN_MILLISECONDS; + } + else + { + minor_wave_Timer -= diff; + } + } + break; + case PYRAMID_PRE_WAVE_3: + if (major_wave_Timer <= diff) + { + // move NPCs to bottom of stair + MoveNPCIfAlive(NPC_BLY, 1887.92f, 1228.179f, 9.98f, 4.78f); + MoveNPCIfAlive(NPC_MURTA, 1891.57f, 1228.68f, 9.69f, 4.78f); + MoveNPCIfAlive(NPC_ORO, 1897.23f, 1228.34f, 9.43f, 4.78f); + MoveNPCIfAlive(NPC_RAVEN, 1883.68f, 1227.95f, 9.543f, 4.78f); + MoveNPCIfAlive(NPC_WEEGLI, 1878.02f, 1227.65f, 9.485f, 4.78f); + SetData(DATA_PYRAMID, PYRAMID_WAVE_3); + if (Creature* sergeantBlye = instance->GetCreature(BlyGUID)) + { + sergeantBlye->AI()->Talk(SAY_BLY_FORWARD); + } + } + else + { + major_wave_Timer -= diff; + } + break; + case PYRAMID_WAVE_3: + if (IsWaveAllDead()) // move NPCS to their final positions + { + SetData(DATA_PYRAMID, PYRAMID_KILLED_ALL_TROLLS); + MoveNPCIfAlive(NPC_BLY, 1883.82f, 1200.83f, 8.87f, 1.32f); + MoveNPCIfAlive(NPC_MURTA, 1891.83f, 1201.45f, 8.87f, 1.32f); + MoveNPCIfAlive(NPC_ORO, 1894.50f, 1204.40f, 8.87f, 1.32f); + MoveNPCIfAlive(NPC_RAVEN, 1874.11f, 1206.17f, 8.87f, 1.32f); + MoveNPCIfAlive(NPC_WEEGLI, 1877.52f, 1199.63f, 8.87f, 1.32f); + } + break; + default: + break; + }; + } + + void MoveNPCIfAlive(uint32 entry, float x, float y, float z, float o) + { + if (Creature* npc = instance->GetCreature(GetGuidData(entry))) + { + if (npc->IsAlive()) + { + npc->SetWalk(true); + npc->GetMotionMaster()->MovePoint(1, { x, y, z, o } ); + npc->SetHomePosition(x, y, z, o); + } + } + } + + void SpawnPyramidWave(uint32 wave) + { + for (uint32 i = 0; i < pyramidSpawnTotal; i++) + { + if (pyramidSpawns[i].waveID == wave) + { + float orientation = 4.78f; + if (pyramidSpawns[i].creatureID == NPC_SHADOWPRIEST_SEZZZIZ || pyramidSpawns[i].creatureID == NPC_NEKRUM_GUTCHEWER) + { + orientation = 1.32f; + } + + Position pos = { pyramidSpawns[i].pos.GetPositionX(), pyramidSpawns[i].pos.GetPositionY(), 8.87f, orientation }; + if (TempSummon* ts = instance->SummonCreature(pyramidSpawns[i].creatureID, pos)) + { + addsAtBase.push_back(ts->GetGUID()); + + if (pyramidSpawns[i].creatureID != NPC_SHADOWPRIEST_SEZZZIZ && pyramidSpawns[i].creatureID != NPC_NEKRUM_GUTCHEWER) + { + ts->GetMotionMaster()->MoveRandom(10); + } + } + } + } + } + + bool IsWaveAllDead() + { + for (GuidList::iterator itr = addsAtBase.begin(); itr != addsAtBase.end(); ++itr) + { + if (Creature* add = instance->GetCreature((*itr))) + { + if (add->IsAlive()) + { + return false; + } + } + } + + for (GuidList::iterator itr = movedadds.begin(); itr != movedadds.end(); ++itr) + { + if (Creature* add = instance->GetCreature(((*itr)))) + { + if (add->IsAlive()) + { + return false; + } + } + } + + return true; + } + + void SendAddsUpStairs(uint32 count) + { + //pop a add from list, send him up the stairs... + for (uint32 addCount = 0; addCount < count && !addsAtBase.empty(); addCount++) + { + if (Creature* add = instance->GetCreature(*addsAtBase.begin())) + { + add->GetMotionMaster()->MovePath(PATH_ADDS, false); + movedadds.push_back(add->GetGUID()); + } + + addsAtBase.erase(addsAtBase.begin()); + } + } + std::string GetSaveData() override { std::ostringstream saveStream; - saveStream << "Z F " << _pyramidEventProgress << ' ' << _gahzrillaSummoned; + saveStream << "Z F " << PyramidPhase << ' ' << GahzrillaSummoned; return saveStream.str(); } @@ -75,14 +445,10 @@ public: if (dataHead1 == 'Z' && dataHead2 == 'F') { - loadStream >> _pyramidEventProgress; - loadStream >> _gahzrillaSummoned; + loadStream >> PyramidPhase; + loadStream >> GahzrillaSummoned; } } - - private: - uint32 _pyramidEventProgress; - uint32 _gahzrillaSummoned; }; }; diff --git a/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp b/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp new file mode 100644 index 000000000..6ee73ff37 --- /dev/null +++ b/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.cpp @@ -0,0 +1,674 @@ +/* + * Copyright (C) 2016+ AzerothCore , released under GNU GPL v2 license, you may redistribute it and/or modify it under version 2 of the License, or (at your option), any later version. + * Copyright (C) 2008-2016 TrinityCore + * Copyright (C) 2005-2009 MaNGOS + */ + +/* ScriptData +SDName: Zulfarrak +SD%Complete: 50 +SDComment: Consider it temporary, no instance script made for this instance yet. +SDCategory: Zul'Farrak +EndScriptData */ + +/* ContentData +npc_sergeant_bly +npc_weegli_blastfuse +EndContentData */ + +#include "ScriptMgr.h" +#include "ScriptSystem.h" +#include "GameObject.h" +#include "GameObjectAI.h" +#include "InstanceScript.h" +#include "MotionMaster.h" +#include "ObjectAccessor.h" +#include "Player.h" +#include "ScriptedCreature.h" +#include "ScriptedGossip.h" +#include "zulfarrak.h" +#include "Cell.h" +#include "CellImpl.h" +#include "GridNotifiers.h" +#include "GridNotifiersImpl.h" + +/*###### +## npc_sergeant_bly +######*/ + +enum blySays +{ + SAY_1 = 0, + SAY_2 = 1 +}; + +enum blySpells +{ + SPELL_SHIELD_BASH = 11972, + SPELL_REVENGE = 12170 +}; + +#define GOSSIP_BLY "That's it! I'm tired of helping you out. It's time we settled things on the battlefield!" + +class npc_sergeant_bly : public CreatureScript +{ +public: + npc_sergeant_bly() : CreatureScript("npc_sergeant_bly") { } + + struct npc_sergeant_blyAI : public ScriptedAI + { + npc_sergeant_blyAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + } + + void InitializeAI() override + { + startedFight = false; + me->setFaction(35); + postGossipStep = 0; + Text_Timer = 0; + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + + InstanceScript* instance; + + bool startedFight; + uint32 postGossipStep; + uint32 Text_Timer; + uint32 ShieldBash_Timer; + uint32 Revenge_Timer; //this is wrong, spell should never be used unless me->GetVictim() dodge, parry or block attack. Trinity support required. + ObjectGuid PlayerGUID; + + void Reset() override + { + ShieldBash_Timer = 5000; + Revenge_Timer = 8000; + } + + void MovementInform(uint32 type, uint32 /*id*/) override + { + if (type != POINT_MOTION_TYPE) + { + return; + } + + if (instance->GetData(DATA_PYRAMID) == PYRAMID_WAVE_3) + { + if (Creature* shadowpriest = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_SHADOWPRIEST_SEZZZIZ))) + { + AttackStart(shadowpriest); + shadowpriest->CallAssistance(); + } + } + } + + void UpdateAI(uint32 diff) override + { + if (postGossipStep > 0 && postGossipStep < 4) + { + if (Text_Timer <= diff) + { + switch (postGossipStep) + { + case 1: + //weegli doesn't fight - he goes & blows up the door + if (Creature* pWeegli = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_WEEGLI))) + { + pWeegli->AI()->DoAction(0); + } + Talk(SAY_1); + Text_Timer = 5000; + break; + case 2: + Talk(SAY_2); + Text_Timer = 5000; + break; + case 3: + me->setFaction(14); + Player* target = ObjectAccessor::GetPlayer(*me, PlayerGUID); + + switchFactionIfAlive(NPC_RAVEN, target); + switchFactionIfAlive(NPC_ORO, target); + switchFactionIfAlive(NPC_MURTA, target); + + if (target) + { + AttackStart(target); + } + } + + postGossipStep++; + } + else + { + Text_Timer -= diff; + } + } + + if (!UpdateVictim()) + { + return; + } + + if (ShieldBash_Timer <= diff) + { + DoCastVictim(SPELL_SHIELD_BASH); + ShieldBash_Timer = 15000; + } + else + { + ShieldBash_Timer -= diff; + } + + if (Revenge_Timer <= diff) + { + DoCastVictim(SPELL_REVENGE); + Revenge_Timer = 10000; + } + else + { + Revenge_Timer -= diff; + } + + DoMeleeAttackIfReady(); + } + + void DoAction(int32 /*param*/) override + { + postGossipStep = 1; + Text_Timer = 0; + } + + void switchFactionIfAlive(uint32 entry, Player* target) + { + if (Creature* crew = ObjectAccessor::GetCreature(*me, instance->GetGuidData(entry))) + { + if (crew->IsAlive()) + { + crew->setFaction(14); + + if (target) + { + crew->AI()->AttackStart(target); + } + } + } + } + + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); + ClearGossipMenuFor(player); + + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + CloseGossipMenuFor(player); + PlayerGUID = player->GetGUID(); + DoAction(0); + } + } + + void sGossipHello(Player* player) override + { + if (instance->GetData(DATA_PYRAMID) >= PYRAMID_DESTROY_GATES && !startedFight) + { + startedFight = true; + AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_BLY, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + SendGossipMenuFor(player, 1517, me->GetGUID()); + } + else + { + if (instance->GetData(DATA_PYRAMID) == PYRAMID_NOT_STARTED) + { + SendGossipMenuFor(player, 1515, me->GetGUID()); + } + else + { + SendGossipMenuFor(player, 1516, me->GetGUID()); + } + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetZulFarrakAI(creature); + } +}; + +/*###### ++## go_troll_cage ++######*/ + +class go_troll_cage : public GameObjectScript +{ +public: + go_troll_cage() : GameObjectScript("go_troll_cage") { } + + struct go_troll_cageAI : public GameObjectAI + { + go_troll_cageAI(GameObject* go) : GameObjectAI(go), instance(go->GetInstanceScript()) { } + + InstanceScript* instance; + + bool GossipHello(Player* /*player*/, bool reportUse) override + { + if (reportUse) + { + return true; + } + + instance->SetData(DATA_PYRAMID, PYRAMID_CAGES_OPEN); + + //set bly & co to aggressive & start moving to top of stairs + initBlyCrewMember(NPC_BLY, 1884.99f, 1263, 41.52f); + initBlyCrewMember(NPC_RAVEN, 1882.5f, 1263, 41.52f); + initBlyCrewMember(NPC_ORO, 1886.47f, 1270.68f, 41.68f); + initBlyCrewMember(NPC_WEEGLI, 1890, 1263, 41.52f); + initBlyCrewMember(NPC_MURTA, 1891.19f, 1272.03f, 41.60f); + return false; + } + + private: + void initBlyCrewMember(uint32 entry, float x, float y, float z) + { + if (Creature* crew = ObjectAccessor::GetCreature(*go, instance->GetGuidData(entry))) + { + crew->SetReactState(REACT_AGGRESSIVE); + crew->SetWalk(true); + crew->SetHomePosition(x, y, z, 4.78f); + crew->GetMotionMaster()->MovePoint(1, { x, y, z, 4.78f }); + crew->setFaction(FACTION_ESCORT_N_NEUTRAL_ACTIVE); + + switch (entry) + { + case NPC_BLY: + case NPC_WEEGLI: + crew->RemoveFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + break; + default: + break; + } + } + } + }; + + GameObjectAI* GetAI(GameObject* go) const override + { + return GetZulFarrakAI(go); + } +}; + +/*###### +## npc_weegli_blastfuse +######*/ + +enum weegliSpells +{ + SPELL_BOMB = 8858, + SPELL_GOBLIN_LAND_MINE = 21688, + SPELL_SHOOT = 6660, + SPELL_WEEGLIS_BARREL = 10772 +}; + +enum weegliSays +{ + SAY_WEEGLI_OHNO = 0, + SAY_WEEGLI_OK_I_GO = 1, + SAY_WEEGLI_OUT_OF_HERE = 2 +}; + +#define GOSSIP_WEEGLI "Will you blow up that door now?" + +class npc_weegli_blastfuse : public CreatureScript +{ +public: + npc_weegli_blastfuse() : CreatureScript("npc_weegli_blastfuse") { } + + struct npc_weegli_blastfuseAI : public ScriptedAI + { + npc_weegli_blastfuseAI(Creature* creature) : ScriptedAI(creature) + { + instance = creature->GetInstanceScript(); + destroyingDoor = false; + outroTimer = 2000; + outroStage = 0; + } + + uint32 Bomb_Timer; + uint32 LandMine_Timer; + uint32 outroTimer; + uint8 outroStage; + bool destroyingDoor; + InstanceScript* instance; + + void InitializeAI() override + { + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + Reset(); + } + + void Reset() override + { + Bomb_Timer = 10000; + LandMine_Timer = 30000; + } + + void AttackStart(Unit* victim) override + { + AttackStartCaster(victim, 10);//keep back & toss bombs/shoot + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + if (destroyingDoor) + { + if (outroTimer <= diff) + { + switch (outroStage) + { + case 0: + DoCastSelf(SPELL_WEEGLIS_BARREL); + outroTimer = 2000; + ++outroStage; + break; + case 1: + me->GetMotionMaster()->MovePoint(2, 1871.18f, 1100.f, 8.88f); + Talk(SAY_WEEGLI_OUT_OF_HERE); + me->DespawnOrUnsummon(8000); + instance->SetData(DATA_PYRAMID, PYRAMID_GATES_DESTROYED); + destroyingDoor = false; + break; + } + } + else + { + outroTimer -= diff; + } + } + + return; + } + + if (Bomb_Timer <= diff) + { + DoCastVictim(SPELL_BOMB); + Bomb_Timer = 10000; + } + else + { + Bomb_Timer -= diff; + } + + if (LandMine_Timer <= diff) + { + DoCastSelf(SPELL_GOBLIN_LAND_MINE); + LandMine_Timer = 30000; + } + else + { + LandMine_Timer -= diff; + } + + if (me->isAttackReady() && !me->IsWithinMeleeRange(me->GetVictim())) + { + DoCastVictim(SPELL_SHOOT); + me->SetSheath(SHEATH_STATE_RANGED); + } + else + { + me->SetSheath(SHEATH_STATE_MELEE); + DoMeleeAttackIfReady(); + } + } + + void JustReachedHome() override + { + if (instance->GetData(DATA_PYRAMID) == PYRAMID_CAGES_OPEN) + { + instance->SetData(DATA_PYRAMID, PYRAMID_ARRIVED_AT_STAIR); + Talk(SAY_WEEGLI_OHNO); + } + else if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS) + { + instance->SetData(DATA_PYRAMID, PYRAMID_MOVED_DOWNSTAIRS); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + else if (instance->GetData(DATA_PYRAMID) == PYRAMID_DESTROY_GATES) + { + destroyingDoor = true; + } + } + + void MovementInform(uint32 type, uint32 /*id*/) override + { + if (type != POINT_MOTION_TYPE) + { + return; + } + + if (instance->GetData(DATA_PYRAMID) == PYRAMID_CAGES_OPEN) + { + instance->SetData(DATA_PYRAMID, PYRAMID_ARRIVED_AT_STAIR); + Talk(SAY_WEEGLI_OHNO); + } + else if (instance->GetData(DATA_PYRAMID) == PYRAMID_KILLED_ALL_TROLLS) + { + instance->SetData(DATA_PYRAMID, PYRAMID_MOVED_DOWNSTAIRS); + me->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + else if (instance->GetData(DATA_PYRAMID) == PYRAMID_DESTROY_GATES) + { + destroyingDoor = true; + } + } + + void DestroyDoor() + { + if (me->IsAlive()) + { + me->setFaction(35); + me->SetWalk(false); + me->GetMotionMaster()->MovePoint(0, { 1858.57f, 1146.35f, 14.745f, 3.85f }); + me->SetHomePosition(1858.57f, 1146.35f, 14.745f, 3.85f); + Talk(SAY_WEEGLI_OK_I_GO); + instance->SetData(DATA_PYRAMID, PYRAMID_DESTROY_GATES); + if (Creature* sergeantBly = ObjectAccessor::GetCreature(*me, instance->GetGuidData(NPC_BLY))) + { + sergeantBly->SetFlag(UNIT_NPC_FLAGS, UNIT_NPC_FLAG_GOSSIP); + } + } + } + + void sGossipSelect(Player* player, uint32 /*menuId*/, uint32 gossipListId) override + { + uint32 const action = player->PlayerTalkClass->GetGossipOptionAction(gossipListId); + ClearGossipMenuFor(player); + + if (action == GOSSIP_ACTION_INFO_DEF + 1) + { + CloseGossipMenuFor(player); + //here we make him run to door, set the charge and run away off to nowhere + DestroyDoor(); + } + } + + void sGossipHello(Player* player) override + { + switch (instance->GetData(DATA_PYRAMID)) + { + case PYRAMID_MOVED_DOWNSTAIRS: + AddGossipItemFor(player, GOSSIP_ICON_CHAT, GOSSIP_WEEGLI, GOSSIP_SENDER_MAIN, GOSSIP_ACTION_INFO_DEF + 1); + SendGossipMenuFor(player, 1514, me->GetGUID()); //if event can proceed to end + break; + case PYRAMID_NOT_STARTED: + SendGossipMenuFor(player, 1511, me->GetGUID()); //if event not started + break; + default: + SendGossipMenuFor(player, 1513, me->GetGUID()); //if event are in progress + } + } + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetZulFarrakAI(creature); + } +}; + +enum ShadowPriestSezzizEnum +{ + SPELL_SHADOW_BOLT = 15537, + SPELL_PSYCHIC_SCREEM = 13704, + SPELL_RENEW = 8362, + SPELL_HEAL = 12039 +}; + +std::array>, 4> shadowpriestSezzizAdds = +{ { + { { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1874.12f, 1198.90f, 8.87f } } }, + { { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.088f, 8.87f } } }, + { { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } } }, + { { NPC_SANDFURY_ZEALOT, { 895.26f, 1199.09f, 8.87f } }, { NPC_SANDFURY_ZEALOT, { 1874.12f, 1198.90f, 8.87f } }, { NPC_SANDFURY_ACOLYTE, { 1874.12f, 1198.90f } }, { NPC_SANDFURY_ACOLYTE, { 895.26f, 1199.09f, 8.87f } } } +} }; + +class npc_shadowpriest_sezziz : public CreatureScript +{ +public: + npc_shadowpriest_sezziz() : CreatureScript("npc_shadowpriest_sezziz") {} + + struct npc_shadowpriest_sezzizAI : public ScriptedAI + { + npc_shadowpriest_sezzizAI(Creature* creature) : ScriptedAI(creature) { } + + void Reset() override + { + _shadowBoltTimer = urand(0, 1 * IN_MILLISECONDS); + _physicScreemTimer = urand(1 * IN_MILLISECONDS, 16 * IN_MILLISECONDS); + _missingHPForRenewTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); + _missingHPForHealTimer = urand(7 * IN_MILLISECONDS, 10 * IN_MILLISECONDS); + _summonAddsTimer = 12 * IN_MILLISECONDS; + _summmonAddsCount = 0; + } + + void AttackStart(Unit* victim) override + { + ScriptedAI::AttackStartCaster(victim, 40.f); + } + + void UpdateAI(uint32 diff) override + { + if (!UpdateVictim()) + { + return; + } + + if (me->HasUnitState(UNIT_STATE_CASTING)) + { + return; + } + + if (_summonAddsTimer <= diff) + { + for (auto itr : shadowpriestSezzizAdds[_summmonAddsCount]) + { + if (Creature* add = me->SummonCreature(itr.first, itr.second, TEMPSUMMON_DEAD_DESPAWN, 10 * IN_MILLISECONDS)) + { + add->AI()->AttackStart(me->GetVictim()); + } + } + + if (++_summmonAddsCount >= 4) + { + _summmonAddsCount = 0; + } + + _summonAddsTimer = urand(10 * IN_MILLISECONDS, 14 * IN_MILLISECONDS); + } + else + { + _summonAddsTimer -= diff; + } + + if (_missingHPForHealTimer <= diff) + { + Unit* unit = nullptr; + Acore::MostHPMissingInRange u_check(me, 40.f, 1500); + Acore::UnitLastSearcher searcher(me, unit, u_check); + Cell::VisitGridObjects(me, searcher, 40.f); + if (unit) + { + DoCast(unit, SPELL_HEAL); + } + + _missingHPForHealTimer = urand(7 * IN_MILLISECONDS, 10 * IN_MILLISECONDS); + } + else + { + _missingHPForHealTimer -= diff; + } + + if (_missingHPForRenewTimer <= diff) + { + Unit* unit = nullptr; + Acore::MostHPMissingInRange u_check(me, 40.f, 700); + Acore::UnitLastSearcher searcher(me, unit, u_check); + Cell::VisitGridObjects(me, searcher, 40.f); + if (unit) + { + DoCast(unit, SPELL_RENEW); + } + + _missingHPForRenewTimer = urand(10 * IN_MILLISECONDS, 15 * IN_MILLISECONDS); + } + else + { + _missingHPForRenewTimer -= diff; + } + + if (_physicScreemTimer <= diff) + { + DoCastVictim(SPELL_PSYCHIC_SCREEM); + _physicScreemTimer = urand(22 * IN_MILLISECONDS, 30 * IN_MILLISECONDS); + } + else + { + _physicScreemTimer -= diff; + } + + if (_shadowBoltTimer <= diff) + { + DoCastVictim(SPELL_SHADOW_BOLT); + _shadowBoltTimer = urand(3 * IN_MILLISECONDS, 4 * IN_MILLISECONDS); + } + else + { + _shadowBoltTimer -= diff; + } + } + + private: + uint32 _shadowBoltTimer; + uint32 _physicScreemTimer; + uint32 _missingHPForRenewTimer; + uint32 _missingHPForHealTimer; + uint32 _summonAddsTimer; + uint8 _summmonAddsCount; + }; + + CreatureAI* GetAI(Creature* creature) const override + { + return GetZulFarrakAI(creature); + } +}; + +void AddSC_zulfarrak() +{ + new npc_sergeant_bly(); + new npc_weegli_blastfuse(); + new npc_shadowpriest_sezziz(); + new go_troll_cage(); +} diff --git a/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.h b/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.h index d63813273..91ae01f6b 100644 --- a/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.h +++ b/src/server/scripts/Kalimdor/ZulFarrak/zulfarrak.h @@ -5,15 +5,61 @@ #ifndef DEF_ZULFARRACK_H #define DEF_ZULFARRACK_H +#include "CreatureAIImpl.h" #include "CellImpl.h" #include "SpellScript.h" -enum ZulFarrakData -{ - TYPE_PYRAMID_EVENT = 0, - TYPE_GAHZRILLA = 1, +#define ZFScriptName "instance_zulfarrak" +enum ZulFarrakCreatures +{ + NPC_SANDFURY_CRETIN = 7789, + NPC_SANDFURY_SLAVE = 7787, + NPC_SANDFURY_ACOLYTE = 8876, + NPC_SANDFURY_DRUDGE = 7788, + NPC_SANDFURY_ZEALOT = 8877, + NPC_SHADOWPRIEST_SEZZZIZ = 7275, + NPC_NEKRUM_GUTCHEWER = 7796, + + NPC_BLY = 7604, + NPC_RAVEN = 7605, + NPC_ORO = 7606, + NPC_WEEGLI = 7607, + NPC_MURTA = 7608 +}; + +enum ZulFarrakGameobjects +{ GO_END_DOOR = 146084 }; +enum ZulFarrakData +{ + DATA_PYRAMID = 0, + DATA_GAHZRILLA = 1 +}; + +enum ZFPyramidPhases +{ + PYRAMID_NOT_STARTED, //default + PYRAMID_CAGES_OPEN, //happens in GO hello for cages + PYRAMID_ARRIVED_AT_STAIR, //happens in Weegli's movementinform + PYRAMID_WAVE_1, + PYRAMID_PRE_WAVE_2, + PYRAMID_WAVE_2, + PYRAMID_PRE_WAVE_3, + PYRAMID_WAVE_3, + PYRAMID_KILLED_ALL_TROLLS, + PYRAMID_MOVED_DOWNSTAIRS, + PYRAMID_DESTROY_GATES, + PYRAMID_GATES_DESTROYED, + PYRAMID_DONE +}; + +template +inline AI* GetZulFarrakAI(T* obj) +{ + return GetInstanceAI(obj, ZFScriptName); +} + #endif diff --git a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp index 10cf08875..2d3ccbcca 100644 --- a/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp +++ b/src/server/scripts/Kalimdor/kalimdor_script_loader.cpp @@ -57,6 +57,7 @@ void AddSC_boss_ouro(); void AddSC_npc_anubisath_sentinel(); void AddSC_instance_temple_of_ahnqiraj(); void AddSC_instance_wailing_caverns(); //Wailing caverns +void AddSC_zulfarrak(); void AddSC_instance_zulfarrak(); //Zul'Farrak instance script void AddSC_ashenvale(); void AddSC_azshara(); @@ -137,6 +138,7 @@ void AddKalimdorScripts() AddSC_npc_anubisath_sentinel(); AddSC_instance_temple_of_ahnqiraj(); AddSC_instance_wailing_caverns(); //Wailing caverns + AddSC_zulfarrak(); AddSC_instance_zulfarrak(); //Zul'Farrak instance script AddSC_ashenvale(); AddSC_azshara();