using System; using System.Collections.Generic; namespace Jovian.EncounterSystem { /// /// Gated quest progression. Encounter E is gated iff some QuestKind encounter P has /// P.nextEncounter == E and P hasn't been resolved. Predecessor map is built once at /// construction; rolling and advancement are O(1). /// public class QuestProgress { private readonly HashSet resolvedIds; private readonly Dictionary predecessorOf; public event Action QuestStarted; public event Action QuestAdvanced; public event Action QuestCompleted; public IReadOnlyCollection ResolvedIds => resolvedIds; public QuestProgress(EncountersCollection encountersCollection) { var capacity = EstimateEncounterCount(encountersCollection); resolvedIds = new HashSet(capacity); predecessorOf = new Dictionary(capacity); IndexQuests(encountersCollection); } private static int EstimateEncounterCount(EncountersCollection collection) { if(collection?.encounterTables == null) { return 0; } var total = 0; for(int i = 0; i < collection.encounterTables.Length; i++) { var table = collection.encounterTables[i]; if(table?.encounters != null) { total += table.encounters.Count; } } return total; } public bool IsGated(IEncounter encounter) { var id = encounter?.EncounterDefinition?.internalId; if(string.IsNullOrEmpty(id)) { return false; } if(!predecessorOf.TryGetValue(id, out var predecessor)) { return false; } return !resolvedIds.Contains(predecessor.EncounterDefinition.internalId); } public void OnEncounterTriggered(IEncounter encounter) { if(encounter?.EncounterDefinition.Kind is not QuestKind questKind) { return; } var id = encounter.EncounterDefinition?.internalId; if(string.IsNullOrEmpty(id) || !resolvedIds.Add(id)) { return; } var next = questKind.nextEncounter.Resolve(); var hasPredecessor = predecessorOf.TryGetValue(id, out var predecessor); if(!hasPredecessor) { QuestStarted?.Invoke(encounter); } else { QuestAdvanced?.Invoke(predecessor, encounter); } if(next == null) { QuestCompleted?.Invoke(encounter); } } public void LoadResolvedIds(IEnumerable ids) { resolvedIds.Clear(); if(ids == null) { return; } foreach(var id in ids) { if(!string.IsNullOrEmpty(id)) { resolvedIds.Add(id); } } } private void IndexQuests(EncountersCollection collection) { if(collection?.encounterTables == null) { return; } for(int t = 0; t < collection.encounterTables.Length; t++) { var table = collection.encounterTables[t]; if(table?.encounters == null) { continue; } for(int i = 0; i < table.encounters.Count; i++) { var encounter = table.encounters[i]; if(encounter?.EncounterDefinition?.Kind is not QuestKind questKind) { continue; } var next = questKind.nextEncounter.Resolve(); if(next == null) { continue; } var nextId = next.EncounterDefinition?.internalId; if(!string.IsNullOrEmpty(nextId)) { predecessorOf[nextId] = encounter; } } } } } }