/*
* This file is part of the TrinityCore Project. See AUTHORS file for Copyright information
*
* This program is free software; you can redistribute it and/or modify it
* under the terms of the GNU General Public License as published by the
* Free Software Foundation; either version 2 of the License, or (at your
* option) any later version.
*
* This program is distributed in the hope that it will be useful, but WITHOUT
* ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
* FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
* more details.
*
* You should have received a copy of the GNU General Public License along
* with this program. If not, see .
*/
#include "Scenario.h"
#include "Log.h"
#include "ObjectAccessor.h"
#include "ObjectMgr.h"
#include "Player.h"
#include "ScenarioMgr.h"
#include "ScenarioPackets.h"
Scenario::Scenario(ScenarioData const* scenarioData) : _data(scenarioData), _currentstep(nullptr)
{
ASSERT(_data);
for (std::pair const& scenarioStep : _data->Steps)
SetStepState(scenarioStep.second, SCENARIO_STEP_NOT_STARTED);
if (ScenarioStepEntry const* step = GetFirstStep())
SetStep(step);
else
TC_LOG_ERROR("scenario", "Scenario::Scenario: Could not launch Scenario (id: %u), found no valid scenario step", _data->Entry->ID);
}
Scenario::~Scenario()
{
for (ObjectGuid guid : _players)
if (Player* player = ObjectAccessor::FindPlayer(guid))
SendBootPlayer(player);
_players.clear();
}
void Scenario::Reset()
{
CriteriaHandler::Reset();
SetStep(GetFirstStep());
}
void Scenario::CompleteStep(ScenarioStepEntry const* step)
{
if (Quest const* quest = sObjectMgr->GetQuestTemplate(step->RewardQuestID))
for (ObjectGuid guid : _players)
if (Player* player = ObjectAccessor::FindPlayer(guid))
player->RewardQuest(quest, LootItemType::Item, 0, nullptr, false);
if (step->IsBonusObjective())
return;
ScenarioStepEntry const* newStep = nullptr;
for (auto const& _step : _data->Steps)
{
if (_step.second->IsBonusObjective())
continue;
if (GetStepState(_step.second) == SCENARIO_STEP_DONE)
continue;
if (!newStep || _step.second->OrderIndex < newStep->OrderIndex)
newStep = _step.second;
}
SetStep(newStep);
if (IsComplete())
CompleteScenario();
else
TC_LOG_ERROR("scenario", "Scenario::CompleteStep: Scenario (id: %u, step: %u) was completed, but could not determine new step, or validate scenario completion.", step->ScenarioID, step->ID);
}
void Scenario::CompleteScenario()
{
return SendPacket(WorldPackets::Scenario::ScenarioCompleted(_data->Entry->ID).Write());
}
void Scenario::SetStep(ScenarioStepEntry const* step)
{
_currentstep = step;
if (step)
SetStepState(step, SCENARIO_STEP_IN_PROGRESS);
WorldPackets::Scenario::ScenarioState scenarioState;
BuildScenarioState(&scenarioState);
SendPacket(scenarioState.Write());
}
void Scenario::OnPlayerEnter(Player* player)
{
_players.insert(player->GetGUID());
SendScenarioState(player);
}
void Scenario::OnPlayerExit(Player* player)
{
_players.erase(player->GetGUID());
SendBootPlayer(player);
}
bool Scenario::IsComplete()
{
for (std::pair const& scenarioStep : _data->Steps)
{
if (scenarioStep.second->IsBonusObjective())
continue;
if (GetStepState(scenarioStep.second) != SCENARIO_STEP_DONE)
return false;
}
return true;
}
ScenarioEntry const* Scenario::GetEntry() const
{
return _data->Entry;
}
ScenarioStepState Scenario::GetStepState(ScenarioStepEntry const* step)
{
std::map::const_iterator itr = _stepStates.find(step);
if (itr == _stepStates.end())
return SCENARIO_STEP_INVALID;
return itr->second;
}
void Scenario::SendCriteriaUpdate(Criteria const * criteria, CriteriaProgress const * progress, Seconds timeElapsed, bool timedCompleted) const
{
WorldPackets::Scenario::ScenarioProgressUpdate progressUpdate;
progressUpdate.CriteriaProgress.Id = criteria->ID;
progressUpdate.CriteriaProgress.Quantity = progress->Counter;
progressUpdate.CriteriaProgress.Player = progress->PlayerGUID;
progressUpdate.CriteriaProgress.Date = progress->Date;
if (criteria->Entry->StartTimer)
progressUpdate.CriteriaProgress.Flags = timedCompleted ? 1 : 0;
progressUpdate.CriteriaProgress.TimeFromStart = timeElapsed;
progressUpdate.CriteriaProgress.TimeFromCreate = Seconds::zero();
SendPacket(progressUpdate.Write());
}
bool Scenario::CanUpdateCriteriaTree(Criteria const * /*criteria*/, CriteriaTree const * tree, Player * /*referencePlayer*/) const
{
ScenarioStepEntry const* step = tree->ScenarioStep;
if (!step)
return false;
if (step->ScenarioID != _data->Entry->ID)
return false;
ScenarioStepEntry const* currentStep = GetStep();
if (!currentStep)
return false;
if (step->IsBonusObjective())
return true;
return currentStep == step;
}
bool Scenario::CanCompleteCriteriaTree(CriteriaTree const* tree)
{
ScenarioStepEntry const* step = ASSERT_NOTNULL(tree->ScenarioStep);
ScenarioStepState const state = GetStepState(step);
if (state == SCENARIO_STEP_DONE)
return false;
ScenarioStepEntry const* currentStep = GetStep();
if (!currentStep)
return false;
if (!step->IsBonusObjective())
if (step != currentStep)
return false;
return CriteriaHandler::CanCompleteCriteriaTree(tree);
}
void Scenario::CompletedCriteriaTree(CriteriaTree const* tree, Player* /*referencePlayer*/)
{
ScenarioStepEntry const* step = ASSERT_NOTNULL(tree->ScenarioStep);
if (!IsCompletedStep(step))
return;
SetStepState(step, SCENARIO_STEP_DONE);
CompleteStep(step);
}
bool Scenario::IsCompletedStep(ScenarioStepEntry const* step)
{
CriteriaTree const* tree = sCriteriaMgr->GetCriteriaTree(step->Criteriatreeid);
if (!tree)
return false;
return IsCompletedCriteriaTree(tree);
}
void Scenario::SendPacket(WorldPacket const* data) const
{
for (ObjectGuid guid : _players)
if (Player* player = ObjectAccessor::FindPlayer(guid))
player->SendDirectMessage(data);
}
void Scenario::BuildScenarioState(WorldPackets::Scenario::ScenarioState* scenarioState)
{
scenarioState->ScenarioID = _data->Entry->ID;
if (ScenarioStepEntry const* step = GetStep())
scenarioState->CurrentStep = step->ID;
scenarioState->CriteriaProgress = GetCriteriasProgress();
scenarioState->BonusObjectives = GetBonusObjectivesData();
// Don't know exactly what this is for, but seems to contain list of scenario steps that we're either on or that are completed
for (std::pair const& state : _stepStates)
{
if (state.first->IsBonusObjective())
continue;
switch (state.second)
{
case SCENARIO_STEP_IN_PROGRESS:
case SCENARIO_STEP_DONE:
break;
case SCENARIO_STEP_NOT_STARTED:
default:
continue;
}
scenarioState->PickedSteps.push_back(state.first->ID);
}
scenarioState->ScenarioComplete = IsComplete();
}
ScenarioStepEntry const* Scenario::GetFirstStep() const
{
// Do it like this because we don't know what order they're in inside the container.
ScenarioStepEntry const* firstStep = nullptr;
for (std::pair const& scenarioStep : _data->Steps)
{
if (scenarioStep.second->IsBonusObjective())
continue;
if (!firstStep || scenarioStep.second->OrderIndex < firstStep->OrderIndex)
firstStep = scenarioStep.second;
}
return firstStep;
}
ScenarioStepEntry const* Scenario::GetLastStep() const
{
// Do it like this because we don't know what order they're in inside the container.
ScenarioStepEntry const* lastStep = nullptr;
for (std::pair const& scenarioStep : _data->Steps)
{
if (scenarioStep.second->IsBonusObjective())
continue;
if (!lastStep || scenarioStep.second->OrderIndex > lastStep->OrderIndex)
lastStep = scenarioStep.second;
}
return lastStep;
}
void Scenario::SendScenarioState(Player* player)
{
WorldPackets::Scenario::ScenarioState scenarioState;
BuildScenarioState(&scenarioState);
player->SendDirectMessage(scenarioState.Write());
}
std::vector Scenario::GetBonusObjectivesData()
{
std::vector bonusObjectivesData;
for (std::pair const& scenarioStep : _data->Steps)
{
if (!scenarioStep.second->IsBonusObjective())
continue;
if (sCriteriaMgr->GetCriteriaTree(scenarioStep.second->Criteriatreeid))
{
WorldPackets::Scenario::BonusObjectiveData bonusObjectiveData;
bonusObjectiveData.BonusObjectiveID = scenarioStep.second->ID;
bonusObjectiveData.ObjectiveComplete = GetStepState(scenarioStep.second) == SCENARIO_STEP_DONE;
bonusObjectivesData.push_back(bonusObjectiveData);
}
}
return bonusObjectivesData;
}
std::vector Scenario::GetCriteriasProgress()
{
std::vector criteriasProgress;
if (!_criteriaProgress.empty())
{
for (std::pair const& progressPair : _criteriaProgress)
{
WorldPackets::Achievement::CriteriaProgress criteriaProgress;
criteriaProgress.Id = progressPair.first;
criteriaProgress.Quantity = progressPair.second.Counter;
criteriaProgress.Date = progressPair.second.Date;
criteriaProgress.Player = progressPair.second.PlayerGUID;
criteriasProgress.push_back(criteriaProgress);
}
}
return criteriasProgress;
}
CriteriaList const& Scenario::GetCriteriaByType(CriteriaType type, uint32 /*asset*/) const
{
return sCriteriaMgr->GetScenarioCriteriaByTypeAndScenario(type, _data->Entry->ID);
}
void Scenario::SendBootPlayer(Player* player)
{
WorldPackets::Scenario::ScenarioVacate scenarioBoot;
scenarioBoot.ScenarioID = _data->Entry->ID;
player->SendDirectMessage(scenarioBoot.Write());
}