From 3e6fed71dc549d70e27abe82162809dd956711c1 Mon Sep 17 00:00:00 2001 From: Cala Date: Fri, 20 May 2016 23:30:27 +0200 Subject: [PATCH] Add Scholomance entrance room reset on Rattlegore's death The entrance room of Scholomance (below the bridge) is now reset when Rattlegore is killed by players. Many of the mobs there are removed, some are replaced and a new patrol is added. Data come from retail. Many thanks to @evil-at-wow for these. See https://github.com/classicdb/database/issues/832 for more details --- .../scholomance/instance_scholomance.cpp | 79 +++++++++++++++++-- .../scholomance/scholomance.h | 45 +++++++++++ 2 files changed, 118 insertions(+), 6 deletions(-) mode change 100644 => 100755 src/scriptdev2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp mode change 100644 => 100755 src/scriptdev2/scripts/eastern_kingdoms/scholomance/scholomance.h diff --git a/src/scriptdev2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp b/src/scriptdev2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp old mode 100644 new mode 100755 index 8478b56496..ad49a034a1 --- a/src/scriptdev2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp +++ b/src/scriptdev2/scripts/eastern_kingdoms/scholomance/instance_scholomance.cpp @@ -25,7 +25,8 @@ EndScriptData */ #include "scholomance.h" instance_scholomance::instance_scholomance(Map* pMap) : ScriptedInstance(pMap), - m_uiGandlingEvent(0) + m_uiGandlingEvent(0), + m_bIsRoomReset(false) { Initialize(); } @@ -42,12 +43,24 @@ void instance_scholomance::OnPlayerEnter(Player* /*pPlayer*/) { // Summon Gandling if can DoSpawnGandlingIfCan(true); + + if (GetData(TYPE_RATTLEGORE) == DONE) + DoRespawnEntranceRoom(); } void instance_scholomance::OnCreatureCreate(Creature* pCreature) { switch (pCreature->GetEntry()) { + // Store the Guids of the NPCs in the entrance room to remove them + // and spawn their replacement or Rattlegore's death + // (only store those located in the room volume) + case NPC_REANIMATED_CORPSE: + case NPC_DISEASED_GHOUL: + case NPC_RISEN_ABERRATION: + if (GetData(TYPE_RATTLEGORE) != DONE && (pCreature->GetPositionZ() > aEntranceRoom->m_fCenterZ) && (pCreature->GetPositionX() - aEntranceRoom->m_fCornerX < aEntranceRoom->m_uiLength) && (pCreature->GetPositionY() - aEntranceRoom->m_fCornerY < aEntranceRoom->m_uiWidth)) + m_sEntranceRoomGuids.insert(pCreature->GetObjectGuid()); + break; case NPC_DARKMASTER_GANDLING: m_mNpcEntryGuidStore[NPC_DARKMASTER_GANDLING] = pCreature->GetObjectGuid(); break; @@ -63,6 +76,10 @@ void instance_scholomance::OnObjectCreate(GameObject* pGo) { switch (pGo->GetEntry()) { + case GO_VIEWING_ROOM_DOOR: + // In normal flow of the instance, this door is opened by a dropped key + if (m_auiEncounter[TYPE_RATTLEGORE] == DONE) + pGo->SetGoState(GO_STATE_ACTIVE); case GO_GATE_KIRTONOS: case GO_GATE_RAS: case GO_GATE_GANDLING: @@ -75,13 +92,61 @@ void instance_scholomance::OnObjectCreate(GameObject* pGo) case GO_GATE_RAVENIAN: m_mGandlingData[EVENT_ID_RAVENIAN].m_doorGuid = pGo->GetObjectGuid(); break; case GO_GATE_BAROV: m_mGandlingData[EVENT_ID_BAROV].m_doorGuid = pGo->GetObjectGuid(); break; case GO_GATE_ILLUCIA: m_mGandlingData[EVENT_ID_ILLUCIA].m_doorGuid = pGo->GetObjectGuid(); break; + } +} - case GO_VIEWING_ROOM_DOOR: - // In normal flow of the instance, this door is opened by a dropped key - if (m_auiEncounter[TYPE_RATTLEGORE] == DONE) - pGo->SetGoState(GO_STATE_ACTIVE); - break; +void instance_scholomance::DoRespawnEntranceRoom() +{ + // safety check to avoid the room being reset for each OnPlayerEnter() call if Rattlegore is dead + if (m_bIsRoomReset) + return; + + // Despawn the mobs already in the room with the exception of the necrofiend (not stored, so not despawned) + for (GuidSet::const_iterator itr = m_sEntranceRoomGuids.begin(); itr != m_sEntranceRoomGuids.end(); ++itr) + { + if (Creature* pMob = instance->GetCreature(*itr)) + pMob->ForcedDespawn(); } + // Spawn the new and less numerous groups instead + // Four groups, one in each corner + // The creatures in each point are random + // but follow the generic rule for each group of 4 NPCs: + // 2 risen aberrations, 1 diseased ghoul, 1 diseased ghoul/reanimated corpse + for (uint8 i = 0; i < MAX_GROUPS; ++i) + { + std::vector uiMobList; // Vector holding the 4 creatures entries for each spawned group + uiMobList.push_back(NPC_RISEN_ABERRATION); // 3 static NPC entries + uiMobList.push_back(NPC_RISEN_ABERRATION); + uiMobList.push_back(NPC_DISEASED_GHOUL); + + uint32 uiMobEntry; // will hold the last random creature entry + + // Pick the fourth NPC in the group and randomize the four possible spawns + switch (urand(0, 1)) + { + case 0: uiMobEntry = NPC_REANIMATED_CORPSE; break; + case 1: uiMobEntry = NPC_DISEASED_GHOUL; break; + } + + uiMobList.push_back(uiMobEntry); + std::random_shuffle(uiMobList.begin(), uiMobList.end()); + + for (uint8 j = 0; j < MAX_NPC_PER_GROUP; ++j) + // We use the Viewing Room door as the summoning object because we are sure it is here + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_VIEWING_ROOM_DOOR)) + pGo->SummonCreature(uiMobList[j], aEntranceRoomSpawnLocs[4*i+j].m_fX, aEntranceRoomSpawnLocs[4*i+j].m_fY, aEntranceRoomSpawnLocs[4*i+j].m_fZ, aEntranceRoomSpawnLocs[4*i+j].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0); + } + // spawn also a patrolling necrofiend + // the waypoints are handled in DB creature_movement_template table (shared with the other necrofiend in the room) + // the two other necrofiends in the instance are using DB creature_movement table + if (GameObject* pGo = GetSingleGameObjectFromStorage(GO_VIEWING_ROOM_DOOR)) + if (Creature* pNecrofiend = pGo->SummonCreature(NPC_NECROFIEND, aEntranceRoomSpawnLocs[16].m_fX, aEntranceRoomSpawnLocs[16].m_fY, aEntranceRoomSpawnLocs[16].m_fZ, aEntranceRoomSpawnLocs[16].m_fO, TEMPSUMMON_DEAD_DESPAWN, 0)) + pNecrofiend->GetMotionMaster()->MoveWaypoint(); + + m_bIsRoomReset = true; + + debug_log("SD2: Entrance room in Scholomance reset after Rattlegore's death"); + return; } void instance_scholomance::SetData(uint32 uiType, uint32 uiData) @@ -97,6 +162,8 @@ void instance_scholomance::SetData(uint32 uiType, uint32 uiData) break; case TYPE_RATTLEGORE: m_auiEncounter[uiType] = uiData; + if (uiData == DONE) + DoRespawnEntranceRoom(); break; case TYPE_RAS_FROSTWHISPER: m_auiEncounter[uiType] = uiData; diff --git a/src/scriptdev2/scripts/eastern_kingdoms/scholomance/scholomance.h b/src/scriptdev2/scripts/eastern_kingdoms/scholomance/scholomance.h old mode 100644 new mode 100755 index 4637dd02b1..225cc81d0d --- a/src/scriptdev2/scripts/eastern_kingdoms/scholomance/scholomance.h +++ b/src/scriptdev2/scripts/eastern_kingdoms/scholomance/scholomance.h @@ -9,6 +9,8 @@ enum { MAX_ENCOUNTER = 10, MAX_EVENTS = 6, + MAX_GROUPS = 4, + MAX_NPC_PER_GROUP = 4, TYPE_KIRTONOS = 0, TYPE_RATTLEGORE = 1, @@ -21,6 +23,11 @@ enum TYPE_ILLUCIA_BAROV = 8, TYPE_GANDLING = 9, + NPC_NECROFIEND = 11551, + NPC_RISEN_ABERRATION = 10485, + NPC_DISEASED_GHOUL = 10495, + NPC_REANIMATED_CORPSE = 10481, + NPC_KIRTONOS = 10506, NPC_RATTLEGORE = 11622, NPC_RAS_FROSTWHISPER = 10508, @@ -66,6 +73,29 @@ static const SpawnLocation aGandlingSpawnLocs[1] = {180.771f, -5.4286f, 75.5702f, 1.29154f} }; +// Coordinates used to respawn the NPCs in the entrance room +// (right before the Viewing Room) on Rattlegore's death +static const SpawnLocation aEntranceRoomSpawnLocs[17] = +{ + {186.036f, 94.5f, 104.72f, 1.29154f}, // First corner + {179.117f, 95.5166f, 104.81f, 1.29154f}, + {180.612f, 100.176f, 104.80f, 1.29154f}, + {185.926f, 100.079f, 104.80f, 1.29154f}, + {178.999f, 75.2952f, 104.72f, 1.29154f}, // Second corner + {185.558f, 77.276f, 104.72f, 1.29154f}, + {187.556f, 70.4334f, 104.72f, 1.29154f}, + {180.51f, 82.3917f, 104.72f, 1.29154f}, + {212.915f, 70.6005f, 104.80f, 1.29154f}, // Third corner + {221.199f, 77.0037f, 104.72f, 1.29154f}, + {214.381f, 76.233f, 104.80f, 1.29154f}, + {218.64f, 71.5957f, 104.72f, 1.29154f}, + {221.249f, 94.9361f, 104.72f, 1.29154f}, // Fourth corner + {214.406f, 101.903f, 104.72f, 1.29154f}, + {217.521f, 95.4237f, 104.72f, 1.29154f}, + {223.296f, 105.101f, 104.72f, 1.29154f}, + {209.233f, 73.2819f, 104.80f, 1.29154f} // patrolling necrofiend +}; + struct GandlingEventData { GandlingEventData() : m_bIsActive(false) {} @@ -74,6 +104,17 @@ struct GandlingEventData std::set m_sAddGuids; }; +struct BoxVolume +{ + float m_fCornerX; + float m_fCornerY; + float m_fCenterZ; + uint32 m_uiLength; + uint32 m_uiWidth; +}; + +static const BoxVolume aEntranceRoom[] = {174.13f, 63.84f, 104.0f, 54, 44}; + static const uint32 aGandlingEvents[MAX_EVENTS] = {EVENT_ID_POLKELT, EVENT_ID_THEOLEN, EVENT_ID_MALICIA, EVENT_ID_ILLUCIA, EVENT_ID_BAROV, EVENT_ID_RAVENIAN}; typedef std::map GandlingEventMap; @@ -104,12 +145,16 @@ class instance_scholomance : public ScriptedInstance private: void DoSpawnGandlingIfCan(bool bByPlayerEnter); + void DoRespawnEntranceRoom(); uint32 m_auiEncounter[MAX_ENCOUNTER]; std::string m_strInstData; uint32 m_uiGandlingEvent; GandlingEventMap m_mGandlingData; + + bool m_bIsRoomReset; + GuidSet m_sEntranceRoomGuids; }; #endif