diff --git a/Runtime/EncounterContext.cs b/Runtime/EncounterContext.cs index 55bbea9..a438185 100644 --- a/Runtime/EncounterContext.cs +++ b/Runtime/EncounterContext.cs @@ -2,6 +2,10 @@ namespace Jovian.EncounterSystem { /// Per-resolution scratch object passed to every event handler. Extend with fields as handlers need them. public class EncounterContext { public IEncounter CurrentEncounter { get; } + public EncounterRegistry Registry { get; set; } + + /// Set to true to halt processing of remaining events. + public bool ShouldStopEvents { get; set; } public EncounterContext(IEncounter currentEncounter) { CurrentEncounter = currentEncounter; diff --git a/Runtime/EncounterRegistry.cs b/Runtime/EncounterRegistry.cs index 5d93a71..e861ded 100644 --- a/Runtime/EncounterRegistry.cs +++ b/Runtime/EncounterRegistry.cs @@ -11,7 +11,9 @@ namespace Jovian.EncounterSystem { private readonly Dictionary encounters = new(); - public Dictionary GetEncounters() => encounters; + public Dictionary GetEncounters() { + return encounters; + } public void RegisterEncounter(IEncounter encounter) { var id = encounter?.EncounterDefinition?.internalId; @@ -38,19 +40,19 @@ namespace Jovian.EncounterSystem { return; } - for(int c = 0; c < encounterCollections.Length; c++) { + for(var c = 0; c < encounterCollections.Length; c++) { var collection = encounterCollections[c]; if(collection?.encounterTables == null) { continue; } - for(int t = 0; t < collection.encounterTables.Length; t++) { + for(var 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++) { + for(var i = 0; i < table.encounters.Count; i++) { RegisterEncounter(table.encounters[i]); } } @@ -67,12 +69,12 @@ namespace Jovian.EncounterSystem { return null; } - public IEncounter GetRandomEncounter(string encounterTableId) { - var table = GetEncounterTable(encounterTableId); - if(!table) { - return null; - } - var ec = table.encounters; + public IEncounter GetRandomEncounter(string encounterTableId) { + var table = GetEncounterTable(encounterTableId); + if(!table) { + return null; + } + var ec = table.encounters; return ec[UnityEngine.Random.Range(0, ec.Count)]; } @@ -86,7 +88,7 @@ namespace Jovian.EncounterSystem { IEncounter selected = null; var seen = 0; - for(var i = 0; i < encounters.Count; i++) { + for(var i = 0; i < table.encounters.Count; i++) { var encounter = table.encounters[i]; if(encounter == null || encounter.GetType() != encounterKind.GetType()) { continue; diff --git a/Runtime/EncounterResolver.cs b/Runtime/EncounterResolver.cs index c523ca4..f6c3370 100644 --- a/Runtime/EncounterResolver.cs +++ b/Runtime/EncounterResolver.cs @@ -2,13 +2,12 @@ using System; using System.Collections.Generic; namespace Jovian.EncounterSystem { - /// Dispatches instances to per-type handlers. Unknown types are skipped. + /// Dispatches instances to per-type handlers. + /// Handlers return true to stop processing remaining events. public class EncounterResolver { - private readonly Dictionary> handlers = new(); + private readonly Dictionary> handlers = new(); - public void Register(Action handler) where T : IEncounterEvent { - // Wrap the typed delegate so the dictionary can hold handlers for any event type uniformly. - // Cast is safe because the wrapper is only invoked via lookup under typeof(T). + public void Register(Func handler) where T : IEncounterEvent { handlers[typeof(T)] = (evt, ctx) => handler((T)evt, ctx); } @@ -17,22 +16,26 @@ namespace Jovian.EncounterSystem { } /// Indexed iteration over — avoids the boxed enumerator - /// that an IEnumerable<T> parameter would force. - /// is a List, which implements IReadOnlyList, so call sites just pass it directly. + /// that an IEnumerable<T> parameter would force. Stops when a handler + /// returns true or is set. public void Resolve(IReadOnlyList events, EncounterContext context) { if(events == null) { return; } var count = events.Count; - for(int i = 0; i < count; i++) { + for(var i = 0; i < count; i++) { var evt = events[i]; - if(evt == null) { + if(evt == null || context.ShouldStopEvents) { continue; } - if(handlers.TryGetValue(evt.GetType(), out var handler)) { - handler(evt, context); + if(!handlers.TryGetValue(evt.GetType(), out var handler)) { + continue; + } + + if(handler(evt, context)) { + context.ShouldStopEvents = true; } } } diff --git a/Runtime/IEncounter.cs b/Runtime/IEncounter.cs index 8f296e1..20d0e09 100644 --- a/Runtime/IEncounter.cs +++ b/Runtime/IEncounter.cs @@ -46,8 +46,9 @@ namespace Jovian.EncounterSystem { public Sprite encounterArt; } - [Serializable] + [Serializable] public class EncounterProperties { public int difficulty; + public bool isRepeatable = true; } } diff --git a/Runtime/IEncounterEvent.cs b/Runtime/IEncounterEvent.cs index 482b91f..2f05acf 100644 --- a/Runtime/IEncounterEvent.cs +++ b/Runtime/IEncounterEvent.cs @@ -24,4 +24,8 @@ namespace Jovian.EncounterSystem { public class GiveRewardEvent : IEncounterEvent { public Reward reward; } + + [Serializable] + public class AbandonEncounterEvent : IEncounterEvent { + } } diff --git a/Runtime/QuestProgress.cs b/Runtime/QuestProgress.cs index 2e6551a..0f549e5 100644 --- a/Runtime/QuestProgress.cs +++ b/Runtime/QuestProgress.cs @@ -14,6 +14,7 @@ namespace Jovian.EncounterSystem { public event Action QuestStarted; public event Action QuestAdvanced; public event Action QuestCompleted; + public event Action QuestAbandoned; public IReadOnlyCollection ResolvedIds => resolvedIds; @@ -76,6 +77,19 @@ namespace Jovian.EncounterSystem { } } + public void OnEncounterAbandoned(IEncounter encounter) { + if(encounter?.EncounterDefinition.Kind is not QuestKind questKind) { + return; + } + + var id = encounter.EncounterDefinition?.internalId; + if(string.IsNullOrEmpty(id) || !resolvedIds.Remove(id)) { + return; + } + + QuestAbandoned?.Invoke(encounter); + } + public void LoadResolvedIds(IEnumerable ids) { resolvedIds.Clear(); if(ids == null) {