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) {