222 lines
No EOL
9.8 KiB
C#
222 lines
No EOL
9.8 KiB
C#
// WingsEmu
|
|
//
|
|
// Developed by NosWings Team
|
|
|
|
using System;
|
|
using System.Collections.Generic;
|
|
using System.Linq;
|
|
using System.Threading.Tasks;
|
|
using PhoenixLib.Caching;
|
|
using PhoenixLib.DAL.Redis.Locks;
|
|
using PhoenixLib.Logging;
|
|
using WingsAPI.Data.GameData;
|
|
using WingsEmu.Core.Extensions;
|
|
using WingsEmu.DTOs.Quests;
|
|
using WingsEmu.Game.Quests;
|
|
|
|
namespace Plugin.QuestImpl.Managers
|
|
{
|
|
public class QuestManager : IQuestManager
|
|
{
|
|
private readonly IExpirableLockService _lockService;
|
|
private readonly ILongKeyCachedRepository<QuestDto> _questCache;
|
|
private readonly Dictionary<int, List<int>> _questlines = new();
|
|
private readonly IKeyValueCache<List<QuestDto>> _questNameCache;
|
|
private readonly Dictionary<int, QuestNpcDto> _questNpcById = new();
|
|
private readonly IResourceLoader<QuestNpcDto> _questNpcResourceLoader;
|
|
|
|
private readonly IResourceLoader<QuestDto> _questResourceLoader;
|
|
private readonly Dictionary<int, TutorialDto> _tutorialDataById = new();
|
|
private readonly Dictionary<int, List<TutorialDto>> _tutorialDataByScriptId = new();
|
|
private readonly Dictionary<TutorialActionType, List<TutorialDto>> _tutorialDataByType = new();
|
|
private readonly Dictionary<int, TutorialDto> _tutorialQuestPayScriptByQuestId = new();
|
|
private readonly IResourceLoader<TutorialDto> _tutorialResourceLoader;
|
|
|
|
|
|
public QuestManager(ILongKeyCachedRepository<QuestDto> questCache, IResourceLoader<QuestDto> questResourceLoader, IResourceLoader<TutorialDto> tutorialResourceLoader,
|
|
IResourceLoader<QuestNpcDto> questNpcResourceLoader, IExpirableLockService lockService, IKeyValueCache<List<QuestDto>> questNameCache)
|
|
{
|
|
_questCache = questCache;
|
|
_questResourceLoader = questResourceLoader;
|
|
_questNpcResourceLoader = questNpcResourceLoader;
|
|
_tutorialResourceLoader = tutorialResourceLoader;
|
|
_lockService = lockService;
|
|
_questNameCache = questNameCache;
|
|
}
|
|
|
|
public IReadOnlyCollection<QuestNpcDto> GetNpcBlueAlertQuests() => _questNpcById.Values.Where(s => s.QuestId != null).ToList();
|
|
public QuestNpcDto GetNpcBlueAlertQuestByQuestId(int questId) => _questNpcById.Values.FirstOrDefault(s => s.QuestId == questId);
|
|
|
|
public async Task<bool> CanRefreshDailyQuests(long characterId) =>
|
|
await _lockService.TryAddTemporaryLockAsync($"game:locks:quest-daily-refresh:{characterId}", DateTime.UtcNow.Date.AddDays(1));
|
|
|
|
public async Task<bool> TryTakeDailyQuest(Guid masterAccId, int questPackId) =>
|
|
await _lockService.TryAddTemporaryLockAsync($"game:locks:quest-daily-master:{masterAccId}:{questPackId.ToString()}", DateTime.UtcNow.Date.AddDays(1));
|
|
|
|
public async Task<bool> TryTakeDailyQuest(long characterId, int questPackId) =>
|
|
await _lockService.TryAddTemporaryLockAsync($"game:locks:quest-daily-char:{characterId}:{questPackId.ToString()}", DateTime.UtcNow.Date.AddDays(1));
|
|
|
|
public bool IsNpcBlueAlertQuest(int questId) => _questNpcById.Values.Any(s => s.QuestId == questId);
|
|
|
|
public List<QuestDto> GetQuestByName(string name) => _questNameCache.Get(name);
|
|
|
|
public async Task InitializeAsync()
|
|
{
|
|
int questCounter = 0;
|
|
int scriptsCounter = 0;
|
|
int objectivesCounter = 0;
|
|
int prizesCounter = 0;
|
|
int questsNpcCounter = 0;
|
|
|
|
IEnumerable<QuestDto> quests = await _questResourceLoader.LoadAsync();
|
|
IEnumerable<TutorialDto> tutorialScripts = await _tutorialResourceLoader.LoadAsync();
|
|
IEnumerable<QuestNpcDto> questsNpc = await _questNpcResourceLoader.LoadAsync();
|
|
|
|
var nextQuests = new Dictionary<int, List<int>>();
|
|
foreach (QuestDto questDto in quests)
|
|
{
|
|
if (questDto.NextQuestId == -1)
|
|
{
|
|
continue;
|
|
}
|
|
|
|
if (!nextQuests.ContainsKey(questDto.Id))
|
|
{
|
|
nextQuests.Add(questDto.Id, new List<int>());
|
|
}
|
|
|
|
nextQuests[questDto.Id].Add(questDto.NextQuestId);
|
|
}
|
|
|
|
nextQuests = BuildQuestline(nextQuests);
|
|
foreach (QuestDto quest in quests)
|
|
{
|
|
_questlines.TryAdd(quest.Id, nextQuests.ContainsKey(quest.Id) ? nextQuests[quest.Id] : new List<int>());
|
|
|
|
_questCache.Set(quest.Id, quest);
|
|
_questNameCache.GetOrSet(quest.Name, () => new List<QuestDto>()).Add(quest);
|
|
if (quest.NextQuestId != -1)
|
|
{
|
|
if (!nextQuests.ContainsKey(quest.Id))
|
|
{
|
|
nextQuests.Add(quest.Id, new List<int>());
|
|
}
|
|
|
|
nextQuests[quest.Id].Add(quest.NextQuestId);
|
|
}
|
|
|
|
questCounter++;
|
|
objectivesCounter += quest.Objectives.Count;
|
|
prizesCounter += quest.Prizes.Count;
|
|
}
|
|
|
|
foreach (TutorialDto tutorialDto in tutorialScripts)
|
|
{
|
|
if (!_tutorialDataByType.ContainsKey(tutorialDto.Type))
|
|
{
|
|
_tutorialDataByType.Add(tutorialDto.Type, new List<TutorialDto>());
|
|
}
|
|
|
|
if (!_tutorialDataByScriptId.ContainsKey(tutorialDto.ScriptId))
|
|
{
|
|
_tutorialDataByScriptId.Add(tutorialDto.ScriptId, new List<TutorialDto>());
|
|
}
|
|
|
|
if (tutorialDto.Type == TutorialActionType.WAIT_FOR_REWARDS_CLAIM)
|
|
{
|
|
_tutorialQuestPayScriptByQuestId.Add(tutorialDto.Data, tutorialDto);
|
|
}
|
|
|
|
_tutorialDataById.TryAdd(tutorialDto.Id, tutorialDto);
|
|
_tutorialDataByType[tutorialDto.Type].Add(tutorialDto);
|
|
_tutorialDataByScriptId[tutorialDto.ScriptId].Add(tutorialDto);
|
|
;
|
|
scriptsCounter++;
|
|
}
|
|
|
|
foreach (QuestNpcDto questNpcDto in questsNpc)
|
|
{
|
|
_questNpcById.TryAdd(questNpcDto.Id, questNpcDto);
|
|
questsNpcCounter++;
|
|
}
|
|
|
|
Log.Info($"[RESOURCES] Loaded {questCounter} quests.");
|
|
Log.Info($"[RESOURCES] Loaded {objectivesCounter} quests objectives.");
|
|
Log.Info($"[RESOURCES] Loaded {prizesCounter} quests rewards.");
|
|
Log.Info($"[RESOURCES] Loaded {scriptsCounter} tutorial scripts.");
|
|
Log.Info($"[RESOURCES] Loaded {questsNpcCounter} NPC quests.");
|
|
}
|
|
|
|
public QuestDto GetQuestById(int questId) => _questCache.Get(questId);
|
|
|
|
public IReadOnlyCollection<TutorialDto> GetScriptsTutorialByType(TutorialActionType type) => _tutorialDataByType.ContainsKey(type) ? _tutorialDataByType[type] : Array.Empty<TutorialDto>();
|
|
|
|
public IReadOnlyCollection<TutorialDto> GetScriptsTutorialByScriptId(int scriptId) =>
|
|
_tutorialDataByScriptId.ContainsKey(scriptId) ? _tutorialDataByScriptId[scriptId] : Array.Empty<TutorialDto>();
|
|
|
|
public IReadOnlyCollection<TutorialDto> GetScriptsTutorial() => _tutorialDataById.Values;
|
|
|
|
public IReadOnlyCollection<TutorialDto> GetScriptsTutorialUntilIndex(int scriptId, int scriptIndex) => _tutorialDataById.Values
|
|
.Where(s => s.ScriptId < scriptId || s.ScriptId == scriptId && s.ScriptIndex <= scriptIndex)
|
|
.OrderBy(s => s.ScriptId).ThenBy(s => s.ScriptIndex).ToList();
|
|
|
|
public IReadOnlyCollection<int> GetQuestlines(int questId) => _questlines.ContainsKey(questId) ? _questlines[questId] : Array.Empty<int>();
|
|
|
|
public TutorialDto GetQuestPayScriptByQuestId(int questId) => _tutorialQuestPayScriptByQuestId.GetOrDefault(questId);
|
|
|
|
public TutorialDto GetScriptTutorialById(int scriptId) => _tutorialDataById.ContainsKey(scriptId) ? _tutorialDataById[scriptId] : null;
|
|
public TutorialDto GetScriptTutorialByIndex(int scriptId, int index) => _tutorialDataById.FirstOrDefault(s => s.Value.ScriptId == scriptId && s.Value.ScriptIndex == index).Value;
|
|
|
|
public TutorialDto GetFirstScriptFromIdByType(int scriptId, TutorialActionType type) =>
|
|
_tutorialDataByType.ContainsKey(type) ? _tutorialDataByType[type].FirstOrDefault(s => s.Id > scriptId) : null;
|
|
|
|
public QuestNpcDto GetQuestNpcByScriptId(int scriptId) => _questNpcById.FirstOrDefault(s => s.Value.StartingScript == scriptId).Value;
|
|
|
|
private static Dictionary<int, List<int>> BuildQuestline(IDictionary<int, List<int>> quests)
|
|
{
|
|
bool built = true;
|
|
foreach ((int questId, List<int> nextQuestIds) in quests.ToList())
|
|
{
|
|
if (!quests.ContainsKey(questId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
foreach (int nextQuestId in nextQuestIds.ToList())
|
|
{
|
|
if (!quests.ContainsKey(nextQuestId))
|
|
{
|
|
continue;
|
|
}
|
|
|
|
built = false;
|
|
quests[questId].AddRange(quests[nextQuestId]);
|
|
quests.Remove(nextQuestId);
|
|
}
|
|
}
|
|
|
|
if (!built)
|
|
{
|
|
return BuildQuestline(quests);
|
|
}
|
|
|
|
var invertedDictionary = new Dictionary<int, List<int>>();
|
|
foreach ((int questId, List<int> nextQuestIds) in quests)
|
|
{
|
|
foreach (int nextQuestId in nextQuestIds)
|
|
{
|
|
if (!invertedDictionary.ContainsKey(nextQuestId))
|
|
{
|
|
invertedDictionary.Add(nextQuestId, new List<int>());
|
|
}
|
|
|
|
invertedDictionary[nextQuestId].Add(questId);
|
|
}
|
|
|
|
invertedDictionary.Add(questId, new List<int> { questId });
|
|
}
|
|
|
|
return invertedDictionary;
|
|
}
|
|
}
|
|
} |