diff --git a/src/ToSHelpers.h b/src/ToSHelpers.h deleted file mode 100644 index d9a7981..0000000 --- a/src/ToSHelpers.h +++ /dev/null @@ -1,19 +0,0 @@ -#ifndef MODULE_TRIAL_OF_STRENGTH_HELPER_H -#define MODULE_TRIAL_OF_STRENGTH_HELPER_H - -Creature* SpawnNPC(uint32 entry, Map* map, Position* position) -{ - if (!map || !position) - { - return nullptr; - } - - if (!sObjectMgr->GetCreatureTemplate(entry)) - { - return nullptr; - } - - return map->SummonCreature(entry, *position); -} - -#endif // MODULE_TRIAL_OF_STRENGTH_HELPER_H diff --git a/src/ToSMapMgr.cpp b/src/ToSMapMgr.cpp new file mode 100644 index 0000000..2ad6058 --- /dev/null +++ b/src/ToSMapMgr.cpp @@ -0,0 +1,122 @@ +#include "ToSMapMgr.h" + +std::string ToSMapManager::GetHexColorFromClass(uint8 classId) +{ + switch (classId) + { + case CLASS_DEATH_KNIGHT: + return "|cffFC2A43"; + case CLASS_HUNTER: + return "|cffAAD174"; + case CLASS_PALADIN: + return "|cffF28CBC"; + case CLASS_ROGUE: + return "|cffFEF262"; + case CLASS_WARLOCK: + return "|cff9A81C2"; + case CLASS_DRUID: + return "|cffF67404"; + case CLASS_MAGE: + return "|cff70C9F1"; + case CLASS_PRIEST: + return "|cffF5F3F6"; + case CLASS_SHAMAN: + return "|cff05D7BA"; + case CLASS_WARRIOR: + return "|cffC9A074"; + } + + return "|cffFFFFFF"; +} + +ToSCurseTemplate* ToSMapManager::GetCurseById(uint32 curseId) +{ + auto it = CurseTemplates.find(curseId); + if (it == CurseTemplates.end()) + { + return nullptr; + } + + return &it->second; +} + +ToSWaveTemplate* ToSMapManager::GetWaveTemplateForWave(uint32 wave) +{ + auto it = WaveTemplates.find(wave); + if (it == WaveTemplates.end()) + { + return nullptr; + } + + return &it->second; +} + +uint32 ToSMapManager::GetTotalWaves() +{ + return WaveTemplates.size(); +} + +std::vector ToSMapManager::GetEnemiesFromGroup(uint32 groupId, uint32 subGroup) +{ + std::vector groups; + + for (auto it = EnemyGroups.begin(); it != EnemyGroups.end(); ++it) + { + if (it->second.group == groupId && + it->second.subGroup == subGroup) + { + groups.push_back(&it->second); + } + } + + return groups; +} + +std::vector ToSMapManager::GetSubGroups(uint32 groupId) +{ + std::vector subgroups; + + for (auto it = EnemyGroups.begin(); it != EnemyGroups.end(); ++it) + { + if (it->second.group == groupId) + { + uint32 subgroup = it->second.subGroup; + + auto it = std::find(subgroups.begin(), subgroups.end(), subgroup); + if (it != subgroups.end()) + { + continue; + } + + subgroups.push_back(subgroup); + } + } + + return subgroups; +} + +std::vector* ToSMapManager::GetRewardTemplates(uint32 rewardId) +{ + auto it = RewardTemplates.find(rewardId); + if (it == RewardTemplates.end()) + { + return nullptr; + } + + return &it->second; +} + +Creature* ToSMapManager::SpawnNPC(uint32 entry, Map* map, Position* position) +{ + if (!map || !position) + { + return nullptr; + } + + if (!sObjectMgr->GetCreatureTemplate(entry)) + { + return nullptr; + } + + return map->SummonCreature(entry, *position); +} diff --git a/src/ToSMapMgr.h b/src/ToSMapMgr.h new file mode 100644 index 0000000..0630397 --- /dev/null +++ b/src/ToSMapMgr.h @@ -0,0 +1,42 @@ +#ifndef MODULE_TRIAL_OF_STRENGTH_MAP_MGR_H +#define MODULE_TRIAL_OF_STRENGTH_MAP_MGR_H + +#include "TrialOfStrength.h" + +#include + +class ToSMapManager +{ +private: + ToSMapManager() { } +public: + static ToSMapManager* GetInstance() + { + if (!instance) + { + instance = new ToSMapManager(); + } + + return instance; + } + + std::string GetHexColorFromClass(uint8 classId); + ToSCurseTemplate* GetCurseById(uint32 curseId); + ToSWaveTemplate* GetWaveTemplateForWave(uint32 wave); + uint32 GetTotalWaves(); + std::vector GetEnemiesFromGroup(uint32 groupId, uint32 subGroup); + std::vector* GetRewardTemplates(uint32 rewardId); + std::vector GetSubGroups(uint32 groupId); + Creature* SpawnNPC(uint32 entry, Map* map, Position* position); +public: + std::unordered_map WaveTemplates; + std::unordered_map EnemyGroups; + std::unordered_map> RewardTemplates; + std::unordered_map CurseTemplates; +private: + inline static ToSMapManager* instance; +}; + +#define sToSMapMgr ToSMapManager::GetInstance() + +#endif // MODULE_TRIAL_OF_STRENGTH_MAP_MGR_H diff --git a/src/TrialOfStrength.cpp b/src/TrialOfStrength.cpp index 0cba9a7..89d1960 100644 --- a/src/TrialOfStrength.cpp +++ b/src/TrialOfStrength.cpp @@ -1,12 +1,13 @@ #include "TrialOfStrength.h" - -#include "ToSHelpers.h" +#include "ToSMapMgr.h" #include "scripts/ToSEnemyCombatantScript.h" #include "scripts/ToSEnemyCombatantBossScript.h" #include "scripts/ToSArenaMasterScript.h" + #include "scripts/ToSInstanceScript.h" +#include "scripts/ToSInstanceMapScript.h" void LoadWaveTemplates() { @@ -31,7 +32,7 @@ void LoadWaveTemplates() waveTemplate.hasReward = fields[2].Get(); waveTemplate.rewardTemplate = fields[3].Get(); - waveTemplates.emplace(waveTemplate.wave, waveTemplate); + sToSMapMgr->WaveTemplates.emplace(waveTemplate.wave, waveTemplate); count++; } while (qResult->NextRow()); @@ -63,7 +64,7 @@ void LoadEnemyGroups() enemyGroup.subGroup = fields[2].Get(); enemyGroup.creatureEntry = fields[3].Get(); - enemyGroups.emplace(enemyGroup.id, enemyGroup); + sToSMapMgr->EnemyGroups.emplace(enemyGroup.id, enemyGroup); count++; } while (qResult->NextRow()); @@ -96,12 +97,12 @@ void LoadRewardTemplates() rewardTemplate.countMax = fields[3].Get(); rewardTemplate.chance = fields[4].Get(); - auto templates = GetRewardTemplates(rewardId); + auto templates = sToSMapMgr->GetRewardTemplates(rewardId); if (!templates) { std::vector newTemplates; newTemplates.push_back(rewardTemplate); - auto it = rewardTemplates.emplace(rewardId, newTemplates); + auto it = sToSMapMgr->RewardTemplates.emplace(rewardId, newTemplates); } else { @@ -140,7 +141,7 @@ void LoadCurseTemplates() curseTemplate.name = fields[4].Get(); curseTemplate.description = fields[5].Get(); - curseTemplates.emplace(curseId, curseTemplate); + sToSMapMgr->CurseTemplates.emplace(curseId, curseTemplate); count++; } while (qResult->NextRow()); @@ -148,120 +149,14 @@ void LoadCurseTemplates() LOG_INFO("module", "Loaded '{}' trial of strength curse templates.", count); } -std::string GetHexColorFromClass(uint8 classId) -{ - switch (classId) - { - case CLASS_DEATH_KNIGHT: - return "|cffFC2A43"; - case CLASS_HUNTER: - return "|cffAAD174"; - case CLASS_PALADIN: - return "|cffF28CBC"; - case CLASS_ROGUE: - return "|cffFEF262"; - case CLASS_WARLOCK: - return "|cff9A81C2"; - case CLASS_DRUID: - return "|cffF67404"; - case CLASS_MAGE: - return "|cff70C9F1"; - case CLASS_PRIEST: - return "|cffF5F3F6"; - case CLASS_SHAMAN: - return "|cff05D7BA"; - case CLASS_WARRIOR: - return "|cffC9A074"; - } - - return "|cffFFFFFF"; -} - -ToSCurseTemplate* GetCurseById(uint32 curseId) -{ - auto it = curseTemplates.find(curseId); - if (it == curseTemplates.end()) - { - return nullptr; - } - - return &it->second; -} - -ToSWaveTemplate* GetWaveTemplateForWave(uint32 wave) -{ - auto it = waveTemplates.find(wave); - if (it == waveTemplates.end()) - { - return nullptr; - } - - return &it->second; -} - -uint32 GetTotalWaves() -{ - return waveTemplates.size(); -} - -std::vector GetEnemiesFromGroup(uint32 groupId, uint32 subGroup) -{ - std::vector groups; - - for (auto it = enemyGroups.begin(); it != enemyGroups.end(); ++it) - { - if (it->second.group == groupId && - it->second.subGroup == subGroup) - { - groups.push_back(&it->second); - } - } - - return groups; -} - -std::vector GetSubGroups(uint32 groupId) -{ - std::vector subgroups; - - for (auto it = enemyGroups.begin(); it != enemyGroups.end(); ++it) - { - if (it->second.group == groupId) - { - uint32 subgroup = it->second.subGroup; - - auto it = std::find(subgroups.begin(), subgroups.end(), subgroup); - if (it != subgroups.end()) - { - continue; - } - - subgroups.push_back(subgroup); - } - } - - return subgroups; -} - -std::vector* GetRewardTemplates(uint32 rewardId) -{ - auto it = rewardTemplates.find(rewardId); - if (it == rewardTemplates.end()) - { - return nullptr; - } - - return &it->second; -} - void ToSWorldScript::OnAfterConfigLoad(bool reload) { if (reload) { - waveTemplates.clear(); - enemyGroups.clear(); - rewardTemplates.clear(); - curseTemplates.clear(); + sToSMapMgr->WaveTemplates.clear(); + sToSMapMgr->EnemyGroups.clear(); + sToSMapMgr->RewardTemplates.clear(); + sToSMapMgr->CurseTemplates.clear(); } LoadWaveTemplates(); @@ -278,5 +173,5 @@ void SC_AddTrialOfStrengthScripts() new ToSEnemyCombatantScript(); new ToSEnemyCombatantBossScript(); - new instance_trial_of_strength(); + new ToSInstanceMapScript(); } diff --git a/src/TrialOfStrength.h b/src/TrialOfStrength.h index 2ca6457..77b5c88 100644 --- a/src/TrialOfStrength.h +++ b/src/TrialOfStrength.h @@ -65,19 +65,6 @@ struct ToSCurseTemplate { std::string description; }; -std::unordered_map waveTemplates; -std::unordered_map enemyGroups; -std::unordered_map> rewardTemplates; -std::unordered_map curseTemplates; - -std::string GetHexColorFromClass(uint8 classId); -ToSCurseTemplate* GetCurseById(uint32 curseId); -ToSWaveTemplate* GetWaveTemplateForWave(uint32 wave); -uint32 GetTotalWaves(); -std::vector GetEnemiesFromGroup(uint32 groupId, uint32 subGroup); -std::vector* GetRewardTemplates(uint32 rewardId); -std::vector GetSubGroups(uint32 groupId); - class ToSWorldScript : public WorldScript { public: diff --git a/src/scripts/ToSInstanceMapScript.cpp b/src/scripts/ToSInstanceMapScript.cpp new file mode 100644 index 0000000..cc33f8f --- /dev/null +++ b/src/scripts/ToSInstanceMapScript.cpp @@ -0,0 +1,7 @@ +#include "ToSInstanceMapScript.h" +#include "ToSInstanceScript.h" + +InstanceScript* ToSInstanceMapScript::GetInstanceScript(InstanceMap* map) const +{ + return new ToSInstanceScript(map); +} diff --git a/src/scripts/ToSInstanceMapScript.h b/src/scripts/ToSInstanceMapScript.h new file mode 100644 index 0000000..f36a462 --- /dev/null +++ b/src/scripts/ToSInstanceMapScript.h @@ -0,0 +1,14 @@ +#ifndef MODULE_TRIAL_OF_STRENGTH_INSTANCE_MAP_SCRIPT_H +#define MODULE_TRIAL_OF_STRENGTH_INSTANCE_MAP_SCRIPT_H + +#include "ScriptMgr.h" + +class ToSInstanceMapScript : public InstanceMapScript +{ +public: + ToSInstanceMapScript() : InstanceMapScript("instance_trial_of_strength", 44) { } + + InstanceScript* GetInstanceScript(InstanceMap* map) const override; +}; + +#endif // MODULE_TRIAL_OF_STRENGTH_INSTANCE_MAP_SCRIPT_H diff --git a/src/scripts/ToSInstanceScript.cpp b/src/scripts/ToSInstanceScript.cpp new file mode 100644 index 0000000..5217987 --- /dev/null +++ b/src/scripts/ToSInstanceScript.cpp @@ -0,0 +1,674 @@ +#include "ToSInstanceScript.h" + +void ToSInstanceScript::AddCurse(uint32 curseId) +{ + auto it = sToSMapMgr->CurseTemplates.find(curseId); + if (it == sToSMapMgr->CurseTemplates.end()) + { + LOG_WARN("module", "Tried to add curse {} to creature curses but it does not exist.", curseId); + return; + } + + auto curse = sToSMapMgr->GetCurseById(curseId); + if (!curse) + { + return; + } + + curses.push_back(curse); +} + +void ToSInstanceScript::OnCreatureCreate(Creature* creature) +{ + if (creature && + creature->GetEntry() == TOS_NPC_ARENA_MASTER) + { + arenaMaster = creature; + } +} + +bool ToSInstanceScript::IsSubWaveCleared() const +{ + return waveInProgress && GetRemainingAlive() == 0; +} + +bool ToSInstanceScript::IsWaveCleared() const +{ + return waveCleared; +} + +bool ToSInstanceScript::HasMoreWaves() const +{ + return currentWave < totalWaves ? true : false; +} + +bool ToSInstanceScript::IsWaveInProgress() const +{ + return waveInProgress; +} + +void ToSInstanceScript::SpawnNextWave(ToSWaveTemplate* waveTemplate = nullptr) +{ + if (!waveTemplate) + { + waveTemplate = sToSMapMgr->GetWaveTemplateForWave(currentWave); + } + + if (!waveTemplate) + { + LOG_WARN("module", "Wave template is nullptr."); + return; + } + + auto enemies = sToSMapMgr->GetEnemiesFromGroup(waveTemplate->enemyGroup, currentSubGroup); + if (enemies.empty()) + { + LOG_WARN("module", "No enemies found in wave template."); + return; + } + + waveCleared = false; + CleanupCreatures(); + ApplyCursesToPlayers(); + + for (auto it = enemies.begin(); it != enemies.end(); ++it) + { + auto enemy = (*it); + auto summon = sToSMapMgr->SpawnNPC(enemy->creatureEntry, instance, combatantPosStart); + + ApplyCurses(summon); + + waveCreatures.push_back(summon); + + if (currentSubGroup == 1) + summon->SetFaction(FACTION_FRIENDLY); + + MakeEntrance(summon); + } + + events.ScheduleEvent(TOS_DATA_ENCOUNTER_COMBATANTS_HOSTILE, 5s); + events.ScheduleEvent(TOS_DATA_ENCOUNTER_CHECK_WAVE_COMPLETE, 10s); + events.ScheduleEvent(TOS_DATA_ENCOUNTER_CHECK_FAILURE, 5s); +} + +void ToSInstanceScript::MakeEntrance(Creature* creature) +{ + creature->GetMotionMaster()->MovePoint(0, *combatantPosEnd); + creature->SetHomePosition(*combatantPosEnd); +} + +void ToSInstanceScript::ApplyCurses(Unit* unit) +{ + if (!unit || + curses.empty()) + { + return; + } + + for (auto const& curse : curses) + { + if (unit->ToPlayer() && + curse->type == TOS_CURSE_TYPE_PLAYER && + !unit->HasAura(curse->aura)) + { + unit->AddAura(curse->aura, unit); + continue; + } + + if (!unit->ToPlayer() && + curse->type == TOS_CURSE_TYPE_ENEMY && + !unit->HasAura(curse->aura)) + { + unit->AddAura(curse->aura, unit); + continue; + } + } +} + +void ToSInstanceScript::ApplyCursesToPlayers() +{ + Map::PlayerList const& players = instance->GetPlayers(); + + for (const auto& it : players) + { + Player* player = it.GetSource(); + + if (!player) + continue; + + ApplyCurses(player); + } +} + +void ToSInstanceScript::ClearCurses(Unit* unit) +{ + if (!unit) + { + return; + } + + for (auto const& curse : curses) + { + if (!curse) + { + continue; + } + + if (unit->HasAura(curse->aura)) + { + unit->RemoveAura(curse->aura); + } + } +} + +void ToSInstanceScript::ClearCursesFromPlayers() +{ + Map::PlayerList const& players = instance->GetPlayers(); + + for (const auto& it : players) + { + Player* player = it.GetSource(); + + if (!player) + { + continue; + } + + ClearCurses(player); + } +} + +void ToSInstanceScript::SetCombatantsHostile() +{ + for (auto it = waveCreatures.begin(); it != waveCreatures.end(); ++it) + { + auto creature = *it; + + creature->SetFaction(FACTION_MONSTER); + creature->SetInCombatWithZone(); + + if (auto player = creature->SelectNearestPlayer(100.0f)) + { + if (creature->Attack(player, true)) + creature->GetMotionMaster()->MoveChase(player); + } + } +} + +void ToSInstanceScript::Update(uint32 diff) +{ + events.Update(diff); + + switch (events.ExecuteEvent()) + { + case TOS_DATA_ENCOUNTER_START: + SetupEncounter(); + break; + + case TOS_DATA_ENCOUNTER_COMBATANTS_HOSTILE: + SetCombatantsHostile(); + break; + + case TOS_DATA_ENCOUNTER_CHECK_WAVE_COMPLETE: + CheckWaveCompletion(); + break; + + case TOS_DATA_ENCOUNTER_START_NEXT_WAVE: + SpawnNextWave(); + break; + + case TOS_DATA_ENCOUNTER_CHECK_FAILURE: + if (!CheckFailure()) + { + events.RescheduleEvent(TOS_DATA_ENCOUNTER_CHECK_FAILURE, 3s); + } + break; + case TOS_DATA_ENCOUNTER_CHECK_ARENA_MASTER_RELOCATE: + CheckArenaMasterRelocate(); + events.RescheduleEvent(TOS_DATA_ENCOUNTER_CHECK_ARENA_MASTER_RELOCATE, 3s); + break; + } +} + +void ToSInstanceScript::CheckArenaMasterRelocate() +{ + if (!arenaMaster) + { + return; + } + + if (!IsEncounterInProgress()) + { + return; + } + + if (IsWaveInProgress() && !arenaMasterLeft) + { + RelocateArenaMaster(false); + arenaMasterLeft = true; + return; + } + + if (!IsWaveInProgress() && arenaMasterLeft) + { + RelocateArenaMaster(true); + arenaMasterLeft = false; + return; + } +} + +void ToSInstanceScript::RelocateArenaMaster(bool returning) +{ + if (!arenaMaster || !arenaMaster->IsInWorld()) + { + return; + } + + arenaMaster->CastSpell(arenaMaster, 64446); + + if (returning) + { + arenaMaster->NearTeleportTo(244.114, -99.9485, 23.7741, 3.16135); + } + else + { + arenaMaster->NearTeleportTo(211.368, -91.809, 18.677, 4.730); + } +} + +bool ToSInstanceScript::AnyPlayerAlive() +{ + Map::PlayerList const& players = instance->GetPlayers(); + + for (const auto& it : players) + { + Player* player = it.GetSource(); + + if (!player) + continue; + + if (!player->isDead()) + { + return true; + } + } + + return false; +} + +bool ToSInstanceScript::CheckFailure() +{ + // Check if all players are dead. + if (AnyPlayerAlive()) + { + return false; + } + + NotifyFailure(); + ResetEncounter(); + + return true; +} + +void ToSInstanceScript::NotifyFailure() +{ + std::string message = Acore::StringFormatFmt("|cffFF9900Wave failed!|r", currentWave); + Map::PlayerList const& players = instance->GetPlayers(); + + for (const auto& it : players) + { + Player* player = it.GetSource(); + + if (!player) + continue; + + player->SendSystemMessage(message); + player->PlayDirectSound(847 /* Quest Failed Sound */); + + { + WorldPacket data(SMSG_NOTIFICATION, (message.size() + 1)); + data << message; + + player->SendDirectMessage(&data); + } + } +} + +void ToSInstanceScript::SetupEncounter() +{ + encounterInProgress = true; + waveInProgress = true; + waveCleared = false; + + auto waveTemplate = sToSMapMgr->GetWaveTemplateForWave(currentWave); + if (!waveTemplate) + { + LOG_WARN("module", "Wave template is nullptr."); + return; + } + + totalWaves = sToSMapMgr->GetTotalWaves(); + + currentSubGroup = 1; + auto subGroups = sToSMapMgr->GetSubGroups(waveTemplate->enemyGroup); + totalSubGroups = subGroups.size(); + + if (totalSubGroups < 1) + { + LOG_WARN("module", "There were no subgroups found for wave {}.", currentWave); + return; + } + + events.ScheduleEvent(TOS_DATA_ENCOUNTER_START_NEXT_WAVE, 5s); +} + +void ToSInstanceScript::CheckWaveCompletion() +{ + if (!IsSubWaveCleared()) + { + events.RescheduleEvent(TOS_DATA_ENCOUNTER_CHECK_WAVE_COMPLETE, 2s); + return; + } + + LOG_INFO("module", "Sub group {} complete.", currentSubGroup); + + if (currentSubGroup < totalSubGroups) + { + LOG_INFO("module", "Spawning next subgroup.."); + currentSubGroup++; + + events.ScheduleEvent(TOS_DATA_ENCOUNTER_START_NEXT_WAVE, 5s); + } + else + { + NotifyPlayers(); + PopulateRewardChest(); + CleanupCreatures(); + + waveInProgress = false; + waveCleared = true; + + if (currentWave == totalWaves) + { + trialCompleted = true; + AnnounceCompletion(); + } + } +} + +void ToSInstanceScript::NotifyPlayers() +{ + Map::PlayerList const& players = instance->GetPlayers(); + + for (const auto& it : players) + { + Player* player = it.GetSource(); + + if (!player) + continue; + + std::string message = Acore::StringFormatFmt("|cffFFFFFFWave |cff00FF00{}|r |cffFFFFFFcleared!|r", currentWave); + + player->SendSystemMessage(message); + player->PlayDirectSound(17316 /* RDF Reward Sound */); + + { + WorldPacket data(SMSG_NOTIFICATION, (message.size() + 1)); + data << message; + + player->SendDirectMessage(&data); + } + } +} + +void ToSInstanceScript::PopulateRewardChest() +{ + auto waveTemplate = sToSMapMgr->GetWaveTemplateForWave(currentWave); + if (!waveTemplate) + { + return; + } + + if (!waveTemplate->hasReward) + { + return; + } + + auto rewardId = waveTemplate->rewardTemplate; + + if (!rewardId) + { + return; + } + + Position* tempPos = new Position(269.173, -100.046, 18.679, 3.180); + rewardChest = instance->SummonGameObject(441250, *tempPos); + if (!rewardChest) + { + rewardChest->DespawnOrUnsummon(); + return; + } + + rewardBeam = instance->SummonGameObject(177705, *tempPos); + if (!rewardBeam) + { + rewardBeam->DespawnOrUnsummon(); + } + + rewardChest->loot.clear(); + rewardChest->SetLootRecipient(instance); + + rewardChest->loot.items.reserve(MAX_NR_LOOT_ITEMS); + + auto rewardTemplates = sToSMapMgr->GetRewardTemplates(rewardId); + + for (auto rewardTemplate = rewardTemplates->begin(); rewardTemplate != rewardTemplates->end(); ++rewardTemplate) + { + LootStoreItem* lootStoreItem = new LootStoreItem(rewardTemplate->itemEntry, 0, 0, false, 1, 0, 1, 1); + + LootItem lootItem(*lootStoreItem); + lootItem.itemIndex = rewardChest->loot.items.size(); + lootItem.itemid = rewardTemplate->itemEntry; + lootItem.count = urand(rewardTemplate->countMin, rewardTemplate->countMax); + + rewardChest->loot.items.push_back(lootItem); + } + + rewardChest->loot.generateMoneyLoot(500, 5000); + + rewardChest->SetLootGenerationTime(); + rewardChest->SetLootState(GO_ACTIVATED); +} + +void ToSInstanceScript::SetData(uint32 dataId, uint32 value) +{ + switch (dataId) + { + case TOS_DATA_ENCOUNTER_START: + events.ScheduleEvent(TOS_DATA_ENCOUNTER_START, 0s); + break; + + case TOS_DATA_ENCOUNTER_CURRENT_WAVE: + currentWave = value; + break; + + case TOS_DATA_ENCOUNTER_RESET: + ResetEncounter(); + break; + } +} + +uint32 ToSInstanceScript::GetData(uint32 dataId) const +{ + switch (dataId) + { + case TOS_DATA_ENCOUNTER_START: + return encounterInProgress; + + case TOS_DATA_ENCOUNTER_CURRENT_WAVE: + return currentWave; + + case TOS_DATA_ENCOUNTER_CURRENT_WAVE_CLEARED: + return IsWaveCleared(); + + case TOS_DATA_ENCOUNTER_HAS_MORE_WAVES: + return HasMoreWaves(); + + case TOS_DATA_ENCOUNTER_CURRENT_WAVE_REMAINING: + return GetRemainingAlive(); + + case TOS_DATA_ENCOUNTER_CURRENT_SUBWAVE: + return currentSubGroup; + + case TOS_DATA_ENCOUNTER_TOTAL_SUBWAVE: + return totalSubGroups; + + case TOS_DATA_ENCOUNTER_TRIAL_COMPLETED: + return trialCompleted; + + case TOS_DATA_ENCOUNTER_WAVE_IN_PROGRESS: + return waveInProgress; + } + + return 0; +} + +bool ToSInstanceScript::IsEncounterInProgress() const +{ + return encounterInProgress; +} + +uint32 ToSInstanceScript::GetRemainingAlive() const +{ + if (waveCreatures.empty()) + { + return 0; + } + + uint32 count = 0; + for (auto it = waveCreatures.begin(); it != waveCreatures.end(); ++it) + { + auto creature = *it; + + if (!creature) + { + continue; + } + + if (creature->IsInWorld() && + creature->IsAlive() && + creature->GetMapId() == TOS_MAP_ID /*When creatures are cleaned up they end up on another map, wtf?*/) + { + count++; + } + } + + return count; +} + +void ToSInstanceScript::CleanupCreatures() +{ + if (waveCreatures.empty()) + { + return; + } + + for (auto it = waveCreatures.begin(); it != waveCreatures.end(); ++it) + { + auto creature = *it; + + if (!creature || !creature->IsInWorld()) + { + continue; + } + + creature->DespawnOrUnsummon(); + } + + waveCreatures.clear(); +} + +void ToSInstanceScript::CleanupGameObjects() +{ + if (rewardBeam && rewardBeam->IsInWorld()) + { + rewardBeam->DespawnOrUnsummon(); + } + + if (rewardChest && rewardChest->IsInWorld()) + { + rewardChest->DespawnOrUnsummon(); + } +} + +void ToSInstanceScript::ResetEncounter() +{ + encounterInProgress = false; + waveInProgress = false; + + currentWave = 1; + totalWaves = 0; + currentSubGroup = 1; + totalSubGroups = 0; + + waveCleared = false; + trialCompleted = false; + + events.Reset(); + events.ScheduleEvent(TOS_DATA_ENCOUNTER_CHECK_ARENA_MASTER_RELOCATE, 3s); + + CleanupCreatures(); + CleanupGameObjects(); + ClearCursesFromPlayers(); + curses.clear(); + + if (arenaMasterLeft) + { + RelocateArenaMaster(true); + arenaMasterLeft = false; + } +} + +void ToSInstanceScript::AnnounceCompletion() +{ + bool hasCurses = curses.size() > 0; + + std::stringstream ss; + ss << "|TInterface\\WorldMap\\Skull_64Red:16|t |cffFFFFFFCongratulations to player(s) "; + + Map::PlayerList const& players = instance->GetPlayers(); + auto playerCount = players.getSize(); + + uint32 i = 0; + for (const auto& it : players) + { + i++; + + Player* player = it.GetSource(); + + if (!player) + { + continue; + } + + ss << Acore::StringFormatFmt("{}{}", sToSMapMgr->GetHexColorFromClass(player->getClass()), player->GetName()); + + if (i != playerCount) + { + ss << "|cffFFFFFF, "; + } + } + + ss << Acore::StringFormatFmt(" |cffFFFFFFfor defeating all waves ({}) in the |cffFF2651Trial of Strength", sToSMapMgr->GetTotalWaves()); + + if (hasCurses) + { + ss << Acore::StringFormatFmt(" with |cffC436C1{}|cffFFFFFF curses!|r", curses.size()); + } + else + { + ss << "!|r"; + } + + sWorld->SendServerMessage(SERVER_MSG_STRING, ss.str()); +} diff --git a/src/scripts/ToSInstanceScript.h b/src/scripts/ToSInstanceScript.h index 31c91bb..0a5cd5a 100644 --- a/src/scripts/ToSInstanceScript.h +++ b/src/scripts/ToSInstanceScript.h @@ -1,730 +1,98 @@ #ifndef MODULE_TRIAL_OF_STRENGTH_INSTANCE_SCRIPT_H #define MODULE_TRIAL_OF_STRENGTH_INSTANCE_SCRIPT_H -class instance_trial_of_strength : public InstanceMapScript +#include "ScriptMgr.h" + +#include "TrialOfStrength.h" +#include "ToSMapMgr.h" + +class ToSInstanceScript : public InstanceScript { public: - instance_trial_of_strength() : InstanceMapScript("instance_trial_of_strength", 44) { } - - struct trial_of_strength_InstanceScript : public InstanceScript + ToSInstanceScript(Map* map) : InstanceScript(map) { - EventMap events; - - bool encounterInProgress; - bool waveInProgress; - - uint32 currentWave; - uint32 totalWaves; - uint32 currentSubGroup; - uint32 totalSubGroups; - - bool waveCleared; - bool trialCompleted; - - std::vector waveCreatures; - std::vector curses; - - Creature* arenaMaster; - bool arenaMasterLeft; - - Position* combatantPosStart; - Position* combatantPosEnd; - - GameObject* rewardChest; - GameObject* rewardBeam; - - trial_of_strength_InstanceScript(Map* map) : InstanceScript(map) - { - arenaMaster = nullptr; - arenaMasterLeft = false; - - rewardChest = nullptr; - rewardBeam = nullptr; - - combatantPosStart = new Position(228.324, -99.921, 18.007, 6.282); - combatantPosEnd = new Position(265.175, -100.163, 18.677, 3.121); - - ResetEncounter(); - } - - void AddCurse(uint32 curseId) - { - auto it = curseTemplates.find(curseId); - if (it == curseTemplates.end()) - { - LOG_WARN("module", "Tried to add curse {} to creature curses but it does not exist.", curseId); - return; - } - - auto curse = GetCurseById(curseId); - if (!curse) - { - return; - } - - curses.push_back(curse); - } - - void OnCreatureCreate(Creature* creature) override - { - if (creature && - creature->GetEntry() == TOS_NPC_ARENA_MASTER) - { - arenaMaster = creature; - } - } - - bool IsSubWaveCleared() const - { - return waveInProgress && GetRemainingAlive() == 0; - } - - bool IsWaveCleared() const - { - return waveCleared; - } - - bool HasMoreWaves() const - { - return currentWave < totalWaves ? true : false; - } - - bool IsWaveInProgress() const - { - return waveInProgress; - } - - void SpawnNextWave(ToSWaveTemplate* waveTemplate = nullptr) - { - if (!waveTemplate) - { - waveTemplate = GetWaveTemplateForWave(currentWave); - } - - if (!waveTemplate) - { - LOG_WARN("module", "Wave template is nullptr."); - return; - } - - auto enemies = GetEnemiesFromGroup(waveTemplate->enemyGroup, currentSubGroup); - if (enemies.empty()) - { - LOG_WARN("module", "No enemies found in wave template."); - return; - } - - waveCleared = false; - CleanupCreatures(); - ApplyCursesToPlayers(); - - for (auto it = enemies.begin(); it != enemies.end(); ++it) - { - auto enemy = (*it); - auto summon = SpawnNPC(enemy->creatureEntry, instance, combatantPosStart); - - ApplyCurses(summon); - - waveCreatures.push_back(summon); - - if(currentSubGroup == 1) - summon->SetFaction(FACTION_FRIENDLY); - - MakeEntrance(summon); - } - - events.ScheduleEvent(TOS_DATA_ENCOUNTER_COMBATANTS_HOSTILE, 5s); - events.ScheduleEvent(TOS_DATA_ENCOUNTER_CHECK_WAVE_COMPLETE, 10s); - events.ScheduleEvent(TOS_DATA_ENCOUNTER_CHECK_FAILURE, 5s); - } - - void MakeEntrance(Creature* creature) - { - creature->GetMotionMaster()->MovePoint(0, *combatantPosEnd); - creature->SetHomePosition(*combatantPosEnd); - } - - void ApplyCurses(Unit* unit) - { - if (!unit || - curses.empty()) - { - return; - } - - for (auto const& curse : curses) - { - if (unit->ToPlayer() && - curse->type == TOS_CURSE_TYPE_PLAYER && - !unit->HasAura(curse->aura)) - { - unit->AddAura(curse->aura, unit); - continue; - } - - if(!unit->ToPlayer() && - curse->type == TOS_CURSE_TYPE_ENEMY && - !unit->HasAura(curse->aura)) - { - unit->AddAura(curse->aura, unit); - continue; - } - } - } - - void ApplyCursesToPlayers() - { - Map::PlayerList const& players = instance->GetPlayers(); - - for (const auto& it : players) - { - Player* player = it.GetSource(); - - if (!player) - continue; - - ApplyCurses(player); - } - } - - void ClearCurses(Unit* unit) - { - if (!unit) - { - return; - } - - for (auto const& curse : curses) - { - if (!curse) - { - continue; - } - - if (unit->HasAura(curse->aura)) - { - unit->RemoveAura(curse->aura); - } - } - } - - void ClearCursesFromPlayers() - { - Map::PlayerList const& players = instance->GetPlayers(); - - for (const auto& it : players) - { - Player* player = it.GetSource(); - - if (!player) - { - continue; - } - - ClearCurses(player); - } - } - - void SetCombatantsHostile() - { - for (auto it = waveCreatures.begin(); it != waveCreatures.end(); ++it) - { - auto creature = *it; - - creature->SetFaction(FACTION_MONSTER); - creature->SetInCombatWithZone(); - - if (auto player = creature->SelectNearestPlayer(100.0f)) - { - if(creature->Attack(player, true)) - creature->GetMotionMaster()->MoveChase(player); - } - } - } - - void Update(uint32 diff) override - { - events.Update(diff); - - switch (events.ExecuteEvent()) - { - case TOS_DATA_ENCOUNTER_START: - SetupEncounter(); - break; - - case TOS_DATA_ENCOUNTER_COMBATANTS_HOSTILE: - SetCombatantsHostile(); - break; - - case TOS_DATA_ENCOUNTER_CHECK_WAVE_COMPLETE: - CheckWaveCompletion(); - break; - - case TOS_DATA_ENCOUNTER_START_NEXT_WAVE: - SpawnNextWave(); - break; - - case TOS_DATA_ENCOUNTER_CHECK_FAILURE: - if (!CheckFailure()) - { - events.RescheduleEvent(TOS_DATA_ENCOUNTER_CHECK_FAILURE, 3s); - } - break; - case TOS_DATA_ENCOUNTER_CHECK_ARENA_MASTER_RELOCATE: - CheckArenaMasterRelocate(); - events.RescheduleEvent(TOS_DATA_ENCOUNTER_CHECK_ARENA_MASTER_RELOCATE, 3s); - break; - } - } - - void CheckArenaMasterRelocate() - { - if (!arenaMaster) - { - return; - } - - if (!IsEncounterInProgress()) - { - return; - } - - if (IsWaveInProgress() && !arenaMasterLeft) - { - RelocateArenaMaster(false); - arenaMasterLeft = true; - return; - } - - if (!IsWaveInProgress() && arenaMasterLeft) - { - RelocateArenaMaster(true); - arenaMasterLeft = false; - return; - } - } - - void RelocateArenaMaster(bool returning) - { - if (!arenaMaster || !arenaMaster->IsInWorld()) - { - return; - } - - arenaMaster->CastSpell(arenaMaster, 64446); - - if (returning) - { - arenaMaster->NearTeleportTo(244.114, -99.9485, 23.7741, 3.16135); - } - else - { - arenaMaster->NearTeleportTo(211.368, -91.809, 18.677, 4.730); - } - } - - bool AnyPlayerAlive() - { - Map::PlayerList const& players = instance->GetPlayers(); - - for (const auto& it : players) - { - Player* player = it.GetSource(); - - if (!player) - continue; + arenaMaster = nullptr; + arenaMasterLeft = false; - if (!player->isDead()) - { - return true; - } - } + rewardChest = nullptr; + rewardBeam = nullptr; - return false; - } + combatantPosStart = new Position(228.324, -99.921, 18.007, 6.282); + combatantPosEnd = new Position(265.175, -100.163, 18.677, 3.121); - bool CheckFailure() - { - // Check if all players are dead. - if (AnyPlayerAlive()) - { - return false; - } - - NotifyFailure(); - ResetEncounter(); - - return true; - } - - void NotifyFailure() - { - std::string message = Acore::StringFormatFmt("|cffFF9900Wave failed!|r", currentWave); - Map::PlayerList const& players = instance->GetPlayers(); - - for (const auto& it : players) - { - Player* player = it.GetSource(); - - if (!player) - continue; - - player->SendSystemMessage(message); - player->PlayDirectSound(847 /* Quest Failed Sound */); - - { - WorldPacket data(SMSG_NOTIFICATION, (message.size() + 1)); - data << message; - - player->SendDirectMessage(&data); - } - } - } - - void SetupEncounter() - { - encounterInProgress = true; - waveInProgress = true; - waveCleared = false; - - auto waveTemplate = GetWaveTemplateForWave(currentWave); - if (!waveTemplate) - { - LOG_WARN("module", "Wave template is nullptr."); - return; - } - - totalWaves = GetTotalWaves(); - - currentSubGroup = 1; - auto subGroups = GetSubGroups(waveTemplate->enemyGroup); - totalSubGroups = subGroups.size(); - - if (totalSubGroups < 1) - { - LOG_WARN("module", "There were no subgroups found for wave {}.", currentWave); - return; - } - - events.ScheduleEvent(TOS_DATA_ENCOUNTER_START_NEXT_WAVE, 5s); - } - - void CheckWaveCompletion() - { - if (!IsSubWaveCleared()) - { - events.RescheduleEvent(TOS_DATA_ENCOUNTER_CHECK_WAVE_COMPLETE, 2s); - return; - } - - LOG_INFO("module", "Sub group {} complete.", currentSubGroup); - - if (currentSubGroup < totalSubGroups) - { - LOG_INFO("module", "Spawning next subgroup.."); - currentSubGroup++; - - events.ScheduleEvent(TOS_DATA_ENCOUNTER_START_NEXT_WAVE, 5s); - } - else - { - NotifyPlayers(); - PopulateRewardChest(); - CleanupCreatures(); - - waveInProgress = false; - waveCleared = true; - - if (currentWave == totalWaves) - { - trialCompleted = true; - AnnounceCompletion(); - } - } - } - - void NotifyPlayers() - { - Map::PlayerList const& players = instance->GetPlayers(); - - for (const auto& it : players) - { - Player* player = it.GetSource(); - - if (!player) - continue; - - std::string message = Acore::StringFormatFmt("|cffFFFFFFWave |cff00FF00{}|r |cffFFFFFFcleared!|r", currentWave); - - player->SendSystemMessage(message); - player->PlayDirectSound(17316 /* RDF Reward Sound */); - - { - WorldPacket data(SMSG_NOTIFICATION, (message.size() + 1)); - data << message; - - player->SendDirectMessage(&data); - } - } - } - - void PopulateRewardChest() - { - auto waveTemplate = GetWaveTemplateForWave(currentWave); - if (!waveTemplate) - { - return; - } - - if (!waveTemplate->hasReward) - { - return; - } - - auto rewardId = waveTemplate->rewardTemplate; - - if (!rewardId) - { - return; - } - - Position* tempPos = new Position(269.173, -100.046, 18.679, 3.180); - rewardChest = instance->SummonGameObject(441250, *tempPos); - if (!rewardChest) - { - rewardChest->DespawnOrUnsummon(); - return; - } - - rewardBeam = instance->SummonGameObject(177705, *tempPos); - if (!rewardBeam) - { - rewardBeam->DespawnOrUnsummon(); - } - - rewardChest->loot.clear(); - rewardChest->SetLootRecipient(instance); - - rewardChest->loot.items.reserve(MAX_NR_LOOT_ITEMS); - - auto rewardTemplates = GetRewardTemplates(rewardId); - - for (auto rewardTemplate = rewardTemplates->begin(); rewardTemplate != rewardTemplates->end(); ++rewardTemplate) - { - LootStoreItem* lootStoreItem = new LootStoreItem(rewardTemplate->itemEntry, 0, 0, false, 1, 0, 1, 1); - - LootItem lootItem(*lootStoreItem); - lootItem.itemIndex = rewardChest->loot.items.size(); - lootItem.itemid = rewardTemplate->itemEntry; - lootItem.count = urand(rewardTemplate->countMin, rewardTemplate->countMax); - - rewardChest->loot.items.push_back(lootItem); - } - - rewardChest->loot.generateMoneyLoot(500, 5000); - - rewardChest->SetLootGenerationTime(); - rewardChest->SetLootState(GO_ACTIVATED); - } - - void SetData(uint32 dataId, uint32 value) override - { - switch (dataId) - { - case TOS_DATA_ENCOUNTER_START: - events.ScheduleEvent(TOS_DATA_ENCOUNTER_START, 0s); - break; - - case TOS_DATA_ENCOUNTER_CURRENT_WAVE: - currentWave = value; - break; - - case TOS_DATA_ENCOUNTER_RESET: - ResetEncounter(); - break; - } - } - - uint32 GetData(uint32 dataId) const override - { - switch (dataId) - { - case TOS_DATA_ENCOUNTER_START: - return encounterInProgress; - - case TOS_DATA_ENCOUNTER_CURRENT_WAVE: - return currentWave; - - case TOS_DATA_ENCOUNTER_CURRENT_WAVE_CLEARED: - return IsWaveCleared(); - - case TOS_DATA_ENCOUNTER_HAS_MORE_WAVES: - return HasMoreWaves(); - - case TOS_DATA_ENCOUNTER_CURRENT_WAVE_REMAINING: - return GetRemainingAlive(); - - case TOS_DATA_ENCOUNTER_CURRENT_SUBWAVE: - return currentSubGroup; - - case TOS_DATA_ENCOUNTER_TOTAL_SUBWAVE: - return totalSubGroups; - - case TOS_DATA_ENCOUNTER_TRIAL_COMPLETED: - return trialCompleted; - - case TOS_DATA_ENCOUNTER_WAVE_IN_PROGRESS: - return waveInProgress; - } - - return 0; - } - - bool IsEncounterInProgress() const override - { - return encounterInProgress; - } - - uint32 GetRemainingAlive() const - { - if (waveCreatures.empty()) - { - return 0; - } - - uint32 count = 0; - for (auto it = waveCreatures.begin(); it != waveCreatures.end(); ++it) - { - auto creature = *it; - - if (!creature) - { - continue; - } - - if (creature->IsInWorld() && - creature->IsAlive() && - creature->GetMapId() == TOS_MAP_ID /*When creatures are cleaned up they end up on another map, wtf?*/) - { - count++; - } - } - - return count; - } - - void CleanupCreatures() - { - if (waveCreatures.empty()) - { - return; - } - - for (auto it = waveCreatures.begin(); it != waveCreatures.end(); ++it) - { - auto creature = *it; - - if (!creature || !creature->IsInWorld()) - { - continue; - } + ResetEncounter(); + } + void OnCreatureCreate(Creature* creature) override; - creature->DespawnOrUnsummon(); - } + bool IsSubWaveCleared() const; + bool IsWaveCleared() const; + bool HasMoreWaves() const; + bool IsWaveInProgress() const; - waveCreatures.clear(); - } + void SpawnNextWave(ToSWaveTemplate* /*waveTemplate = nullptr*/); - void CleanupGameObjects() - { - if (rewardBeam && rewardBeam->IsInWorld()) - { - rewardBeam->DespawnOrUnsummon(); - } + void MakeEntrance(Creature* creature); + void SetCombatantsHostile(); + uint32 GetRemainingAlive() const; - if (rewardChest && rewardChest->IsInWorld()) - { - rewardChest->DespawnOrUnsummon(); - } - } + void AddCurse(uint32 curseId); + void ApplyCurses(Unit* unit); + void ApplyCursesToPlayers(); + void ClearCurses(Unit* unit); + void ClearCursesFromPlayers(); - void ResetEncounter() - { - encounterInProgress = false; - waveInProgress = false; + void Update(uint32 diff) override; - currentWave = 1; - totalWaves = 0; - currentSubGroup = 1; - totalSubGroups = 0; + void CheckArenaMasterRelocate(); + void RelocateArenaMaster(bool returning); - waveCleared = false; - trialCompleted = false; + bool AnyPlayerAlive(); - events.Reset(); - events.ScheduleEvent(TOS_DATA_ENCOUNTER_CHECK_ARENA_MASTER_RELOCATE, 3s); + bool CheckFailure(); - CleanupCreatures(); - CleanupGameObjects(); - ClearCursesFromPlayers(); - curses.clear(); + void NotifyFailure(); + void NotifyPlayers(); - if (arenaMasterLeft) - { - RelocateArenaMaster(true); - arenaMasterLeft = false; - } - } + void SetupEncounter(); - void AnnounceCompletion() - { - bool hasCurses = curses.size() > 0; + void CheckWaveCompletion(); + void PopulateRewardChest(); + void AnnounceCompletion(); - std::stringstream ss; - ss << "|TInterface\\WorldMap\\Skull_64Red:16|t |cffFFFFFFCongratulations to player(s) "; + void SetData(uint32 dataId, uint32 value) override; + uint32 GetData(uint32 dataId) const override; - Map::PlayerList const& players = instance->GetPlayers(); - auto playerCount = players.getSize(); + bool IsEncounterInProgress() const override; - uint32 i = 0; - for (const auto& it : players) - { - i++; + void CleanupCreatures(); + void CleanupGameObjects(); + void ResetEncounter(); - Player* player = it.GetSource(); +private: + EventMap events; - if (!player) - { - continue; - } + bool encounterInProgress; + bool waveInProgress; - ss << Acore::StringFormatFmt("{}{}", GetHexColorFromClass(player->getClass()), player->GetName()); + uint32 currentWave; + uint32 totalWaves; + uint32 currentSubGroup; + uint32 totalSubGroups; - if (i != playerCount) - { - ss << "|cffFFFFFF, "; - } - } + bool waveCleared; + bool trialCompleted; - ss << Acore::StringFormatFmt(" |cffFFFFFFfor defeating all waves ({}) in the |cffFF2651Trial of Strength", GetTotalWaves()); + std::vector waveCreatures; + std::vector curses; - if (hasCurses) - { - ss << Acore::StringFormatFmt(" with |cffC436C1{}|cffFFFFFF curses!|r", curses.size()); - } - else - { - ss << "!|r"; - } + Creature* arenaMaster; + bool arenaMasterLeft; - sWorld->SendServerMessage(SERVER_MSG_STRING, ss.str()); - } - }; + Position* combatantPosStart; + Position* combatantPosEnd; - InstanceScript* GetInstanceScript(InstanceMap* map) const override - { - return new trial_of_strength_InstanceScript(map); - } + GameObject* rewardChest; + GameObject* rewardBeam; }; #endif // MODULE_TRIAL_OF_STRENGTH_INSTANCE_SCRIPT_H