2e856158fa
Port From (https://github.com/TrinityCore/TrinityCore/commit/49b3ea96f5450a9649983267d34622e5f5cec876)
414 lines
14 KiB
C#
414 lines
14 KiB
C#
// Copyright (c) CypherCore <http://github.com/CypherCore> All rights reserved.
|
|
// Licensed under the GNU GENERAL PUBLIC LICENSE. See LICENSE file in the project root for full license information.
|
|
|
|
using Framework.Constants;
|
|
using Game.Achievements;
|
|
using Game.DataStorage;
|
|
using Game.Entities;
|
|
using Game.Maps;
|
|
using Game.Networking;
|
|
using Game.Networking.Packets;
|
|
using System;
|
|
using System.Collections.Generic;
|
|
|
|
namespace Game.Scenarios
|
|
{
|
|
public class Scenario : CriteriaHandler
|
|
{
|
|
public Scenario(Map map, ScenarioData scenarioData)
|
|
{
|
|
_map = map;
|
|
_data = scenarioData;
|
|
_guid = ObjectGuid.Create(HighGuid.Scenario, map.GetId(), scenarioData.Entry.Id, map.GenerateLowGuid(HighGuid.Scenario));
|
|
_currentstep = null;
|
|
|
|
//ASSERT(_data);
|
|
|
|
foreach (var scenarioStep in _data.Steps.Values)
|
|
SetStepState(scenarioStep, ScenarioStepState.NotStarted);
|
|
|
|
ScenarioStepRecord firstStep = GetFirstStep();
|
|
if (firstStep != null)
|
|
SetStep(firstStep);
|
|
else
|
|
Log.outError(LogFilter.Scenario, "Scenario.Scenario: Could not launch Scenario (id: {0}), found no valid scenario step", _data.Entry.Id);
|
|
}
|
|
|
|
~Scenario()
|
|
{
|
|
foreach (ObjectGuid guid in _players)
|
|
{
|
|
Player player = Global.ObjAccessor.GetPlayer(_map, guid);
|
|
if (player != null)
|
|
SendBootPlayer(player);
|
|
}
|
|
|
|
_players.Clear();
|
|
}
|
|
|
|
public override void Reset()
|
|
{
|
|
base.Reset();
|
|
SetStep(GetFirstStep());
|
|
}
|
|
|
|
public virtual void CompleteStep(ScenarioStepRecord step)
|
|
{
|
|
Quest quest = Global.ObjectMgr.GetQuestTemplate(step.RewardQuestID);
|
|
if (quest != null)
|
|
{
|
|
foreach (ObjectGuid guid in _players)
|
|
{
|
|
Player player = Global.ObjAccessor.GetPlayer(_map, guid);
|
|
if (player != null)
|
|
player.RewardQuest(quest, LootItemType.Item, 0, null, false);
|
|
}
|
|
}
|
|
|
|
if (step.IsBonusObjective())
|
|
return;
|
|
|
|
ScenarioStepRecord newStep = null;
|
|
foreach (var scenarioStep in _data.Steps.Values)
|
|
{
|
|
if (scenarioStep.IsBonusObjective())
|
|
continue;
|
|
|
|
if (GetStepState(scenarioStep) == ScenarioStepState.Done)
|
|
continue;
|
|
|
|
if (newStep == null || scenarioStep.OrderIndex < newStep.OrderIndex)
|
|
newStep = scenarioStep;
|
|
}
|
|
|
|
SetStep(newStep);
|
|
if (IsComplete())
|
|
CompleteScenario();
|
|
else
|
|
Log.outError(LogFilter.Scenario, "Scenario.CompleteStep: Scenario (id: {0}, step: {1}) was completed, but could not determine new step, or validate scenario completion.", step.ScenarioID, step.Id);
|
|
}
|
|
|
|
public virtual void CompleteScenario()
|
|
{
|
|
SendPacket(new ScenarioCompleted(_data.Entry.Id));
|
|
|
|
DoForAllPlayers(player =>
|
|
{
|
|
player.UpdateCriteria(CriteriaType.CompleteAnyScenario, 1);
|
|
player.UpdateCriteria(CriteriaType.CompleteScenario, _data.Entry.Id);
|
|
});
|
|
}
|
|
|
|
void SetStep(ScenarioStepRecord step)
|
|
{
|
|
_currentstep = step;
|
|
if (step != null)
|
|
{
|
|
SetStepState(step, ScenarioStepState.InProgress);
|
|
foreach (ObjectGuid guid in _players)
|
|
{
|
|
Player player = Global.ObjAccessor.GetPlayer(_map, guid);
|
|
if (player != null)
|
|
player.StartCriteria(CriteriaStartEvent.BeginScenarioStep, step.Id);
|
|
}
|
|
}
|
|
|
|
DoForAllPlayers(receiver =>
|
|
{
|
|
ScenarioState scenarioState = new();
|
|
BuildScenarioStateFor(receiver, scenarioState);
|
|
receiver.SendPacket(scenarioState);
|
|
});
|
|
}
|
|
|
|
public virtual void OnPlayerEnter(Player player)
|
|
{
|
|
_players.Add(player.GetGUID());
|
|
SendScenarioState(player);
|
|
}
|
|
|
|
public virtual void OnPlayerExit(Player player)
|
|
{
|
|
_players.Remove(player.GetGUID());
|
|
SendBootPlayer(player);
|
|
}
|
|
|
|
bool IsComplete()
|
|
{
|
|
foreach (var scenarioStep in _data.Steps.Values)
|
|
{
|
|
if (scenarioStep.IsBonusObjective())
|
|
continue;
|
|
|
|
if (GetStepState(scenarioStep) != ScenarioStepState.Done)
|
|
return false;
|
|
}
|
|
|
|
return true;
|
|
}
|
|
|
|
public ScenarioRecord GetEntry()
|
|
{
|
|
return _data.Entry;
|
|
}
|
|
|
|
ScenarioStepState GetStepState(ScenarioStepRecord step)
|
|
{
|
|
if (!_stepStates.ContainsKey(step))
|
|
return ScenarioStepState.Invalid;
|
|
|
|
return _stepStates[step];
|
|
}
|
|
|
|
public override void SendCriteriaUpdate(Criteria criteria, CriteriaProgress progress, TimeSpan timeElapsed, bool timedCompleted)
|
|
{
|
|
DoForAllPlayers(receiver =>
|
|
{
|
|
ScenarioProgressUpdate progressUpdate = new();
|
|
progressUpdate.CriteriaProgress.Id = criteria.Id;
|
|
progressUpdate.CriteriaProgress.Quantity = progress.Counter;
|
|
progressUpdate.CriteriaProgress.Player = _guid;
|
|
progressUpdate.CriteriaProgress.Date.SetUtcTimeFromUnixTime(progress.Date);
|
|
progressUpdate.CriteriaProgress.Date += receiver.GetSession().GetTimezoneOffset();
|
|
if (criteria.Entry.StartTimer != 0)
|
|
progressUpdate.CriteriaProgress.Flags = timedCompleted ? 1 : 0u;
|
|
|
|
progressUpdate.CriteriaProgress.TimeFromStart = (uint)timeElapsed.TotalSeconds;
|
|
progressUpdate.CriteriaProgress.TimeFromCreate = 0;
|
|
|
|
receiver.SendPacket(progressUpdate);
|
|
});
|
|
}
|
|
|
|
public override bool CanUpdateCriteriaTree(Criteria criteria, CriteriaTree tree, Player referencePlayer)
|
|
{
|
|
ScenarioStepRecord step = tree.ScenarioStep;
|
|
if (step == null)
|
|
return false;
|
|
|
|
if (step.ScenarioID != _data.Entry.Id)
|
|
return false;
|
|
|
|
ScenarioStepRecord currentStep = GetStep();
|
|
if (currentStep == null)
|
|
return false;
|
|
|
|
if (step.IsBonusObjective())
|
|
return true;
|
|
|
|
return currentStep == step;
|
|
}
|
|
|
|
public override bool CanCompleteCriteriaTree(CriteriaTree tree)
|
|
{
|
|
ScenarioStepRecord step = tree.ScenarioStep;
|
|
if (step == null)
|
|
return false;
|
|
|
|
ScenarioStepState state = GetStepState(step);
|
|
if (state == ScenarioStepState.Done)
|
|
return false;
|
|
|
|
ScenarioStepRecord currentStep = GetStep();
|
|
if (currentStep == null)
|
|
return false;
|
|
|
|
if (step.IsBonusObjective())
|
|
if (step != currentStep)
|
|
return false;
|
|
|
|
return base.CanCompleteCriteriaTree(tree);
|
|
}
|
|
|
|
public override void CompletedCriteriaTree(CriteriaTree tree, Player referencePlayer)
|
|
{
|
|
ScenarioStepRecord step = tree.ScenarioStep;
|
|
if (!IsCompletedStep(step))
|
|
return;
|
|
|
|
SetStepState(step, ScenarioStepState.Done);
|
|
CompleteStep(step);
|
|
}
|
|
|
|
bool IsCompletedStep(ScenarioStepRecord step)
|
|
{
|
|
CriteriaTree tree = Global.CriteriaMgr.GetCriteriaTree(step.CriteriaTreeId);
|
|
if (tree == null)
|
|
return false;
|
|
|
|
return IsCompletedCriteriaTree(tree);
|
|
}
|
|
|
|
public void DoForAllPlayers(Action<Player> worker)
|
|
{
|
|
foreach (ObjectGuid guid in _players)
|
|
{
|
|
Player player = Global.ObjAccessor.GetPlayer(_map, guid);
|
|
if (player != null)
|
|
worker(player);
|
|
}
|
|
}
|
|
|
|
public override void SendPacket(ServerPacket data)
|
|
{
|
|
DoForAllPlayers(player => player.SendPacket(data));
|
|
}
|
|
|
|
void BuildScenarioStateFor(Player player, ScenarioState scenarioState)
|
|
{
|
|
scenarioState.ScenarioGUID = _guid;
|
|
scenarioState.ScenarioID = (int)_data.Entry.Id;
|
|
ScenarioStepRecord step = GetStep();
|
|
if (step != null)
|
|
scenarioState.CurrentStep = (int)step.Id;
|
|
scenarioState.CriteriaProgress = GetCriteriasProgressFor(player);
|
|
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
|
|
foreach (var state in _stepStates)
|
|
{
|
|
if (state.Key.IsBonusObjective())
|
|
continue;
|
|
|
|
switch (state.Value)
|
|
{
|
|
case ScenarioStepState.InProgress:
|
|
case ScenarioStepState.Done:
|
|
break;
|
|
case ScenarioStepState.NotStarted:
|
|
default:
|
|
continue;
|
|
}
|
|
|
|
scenarioState.PickedSteps.Add(state.Key.Id);
|
|
}
|
|
scenarioState.ScenarioComplete = IsComplete();
|
|
}
|
|
|
|
ScenarioStepRecord GetFirstStep()
|
|
{
|
|
// Do it like this because we don't know what order they're in inside the container.
|
|
ScenarioStepRecord firstStep = null;
|
|
foreach (var scenarioStep in _data.Steps.Values)
|
|
{
|
|
if (scenarioStep.IsBonusObjective())
|
|
continue;
|
|
|
|
if (firstStep == null || scenarioStep.OrderIndex < firstStep.OrderIndex)
|
|
firstStep = scenarioStep;
|
|
}
|
|
|
|
return firstStep;
|
|
}
|
|
|
|
public ScenarioStepRecord GetLastStep()
|
|
{
|
|
// Do it like this because we don't know what order they're in inside the container.
|
|
ScenarioStepRecord lastStep = null;
|
|
foreach (var scenarioStep in _data.Steps.Values)
|
|
{
|
|
if (scenarioStep.IsBonusObjective())
|
|
continue;
|
|
|
|
if (lastStep == null || scenarioStep.OrderIndex > lastStep.OrderIndex)
|
|
lastStep = scenarioStep;
|
|
}
|
|
|
|
return lastStep;
|
|
}
|
|
|
|
public void SendScenarioState(Player player)
|
|
{
|
|
ScenarioState scenarioState = new();
|
|
BuildScenarioStateFor(player, scenarioState);
|
|
player.SendPacket(scenarioState);
|
|
}
|
|
|
|
List<BonusObjectiveData> GetBonusObjectivesData()
|
|
{
|
|
List<BonusObjectiveData> bonusObjectivesData = new();
|
|
foreach (var scenarioStep in _data.Steps.Values)
|
|
{
|
|
if (!scenarioStep.IsBonusObjective())
|
|
continue;
|
|
|
|
if (Global.CriteriaMgr.GetCriteriaTree(scenarioStep.CriteriaTreeId) != null)
|
|
{
|
|
BonusObjectiveData bonusObjectiveData;
|
|
bonusObjectiveData.BonusObjectiveID = (int)scenarioStep.Id;
|
|
bonusObjectiveData.ObjectiveComplete = GetStepState(scenarioStep) == ScenarioStepState.Done;
|
|
bonusObjectivesData.Add(bonusObjectiveData);
|
|
}
|
|
}
|
|
|
|
return bonusObjectivesData;
|
|
}
|
|
|
|
List<CriteriaProgressPkt> GetCriteriasProgressFor(Player player)
|
|
{
|
|
List<CriteriaProgressPkt> criteriasProgress = new();
|
|
|
|
foreach (var (criteriaId, progress) in _criteriaProgress)
|
|
{
|
|
CriteriaProgressPkt criteriaProgress = new();
|
|
criteriaProgress.Id = criteriaId;
|
|
criteriaProgress.Quantity = progress.Counter;
|
|
criteriaProgress.Date.SetUtcTimeFromUnixTime(progress.Date);
|
|
criteriaProgress.Date += player.GetSession().GetTimezoneOffset();
|
|
criteriaProgress.Player = _guid;
|
|
criteriasProgress.Add(criteriaProgress);
|
|
}
|
|
|
|
return criteriasProgress;
|
|
}
|
|
|
|
public override List<Criteria> GetCriteriaByType(CriteriaType type, uint asset)
|
|
{
|
|
return Global.CriteriaMgr.GetScenarioCriteriaByTypeAndScenario(type, _data.Entry.Id);
|
|
}
|
|
|
|
void SendBootPlayer(Player player)
|
|
{
|
|
ScenarioVacate scenarioBoot = new();
|
|
scenarioBoot.ScenarioGUID = _guid;
|
|
scenarioBoot.ScenarioID = (int)_data.Entry.Id;
|
|
player.SendPacket(scenarioBoot);
|
|
}
|
|
|
|
public virtual void Update(uint diff) { }
|
|
|
|
public void SetStepState(ScenarioStepRecord step, ScenarioStepState state) { _stepStates[step] = state; }
|
|
public ScenarioStepRecord GetStep()
|
|
{
|
|
return _currentstep;
|
|
}
|
|
|
|
public override void SendCriteriaProgressRemoved(uint criteriaId) { }
|
|
public override void AfterCriteriaTreeUpdate(CriteriaTree tree, Player referencePlayer) { }
|
|
public override void SendAllData(Player receiver) { }
|
|
|
|
protected Map _map;
|
|
List<ObjectGuid> _players = new();
|
|
protected ScenarioData _data;
|
|
ObjectGuid _guid;
|
|
ScenarioStepRecord _currentstep;
|
|
Dictionary<ScenarioStepRecord, ScenarioStepState> _stepStates = new();
|
|
}
|
|
|
|
public enum ScenarioStepState
|
|
{
|
|
Invalid = 0,
|
|
NotStarted = 1,
|
|
InProgress = 2,
|
|
Done = 3
|
|
}
|
|
|
|
enum ScenarioType
|
|
{
|
|
Scenario = 0,
|
|
ChallengeMode = 1,
|
|
Solo = 2,
|
|
Dungeon = 10,
|
|
}
|
|
|
|
}
|