fr.jeanf.questsystem 0.1.8 β†’ 1.0.0

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
@@ -3,21 +3,23 @@ name: "πŸš€ publish"
3
3
  on:
4
4
  push:
5
5
  branches:
6
- - main
6
+ - main
7
+
8
+ permissions:
9
+ id-token: write # Required for OIDC
10
+ contents: read
7
11
 
8
12
  jobs:
9
13
  release:
10
14
  name: πŸš€ publish
11
15
  runs-on: ubuntu-latest
12
16
  steps:
13
- - name: πŸ“š checkout
14
- uses: actions/checkout@v3
15
- - name: 🟒 node
16
- uses: actions/setup-node@v3
17
- with:
18
- node-version: 16
19
- registry-url: https://registry.npmjs.org
20
- - name: πŸš€ publish
21
- run: npm publish --access public
22
- env:
23
- NODE_AUTH_TOKEN: ${{secrets.NPM_AUTH_TOKEN}}
17
+ - name: πŸ“š checkout
18
+ uses: actions/checkout@v6
19
+ - name: 🟒 node
20
+ uses: actions/setup-node@v6
21
+ with:
22
+ node-version: 24
23
+ registry-url: https://registry.npmjs.org
24
+ - name: πŸš€ publish
25
+ run: npm publish --access public
@@ -0,0 +1,63 @@
1
+ using System;
2
+ using UnityEngine;
3
+
4
+ namespace jeanf.questsystem
5
+ {
6
+ /// <summary>
7
+ /// Tracks when <see cref="QuestManager"/> has finished loading addressable quest definitions.
8
+ /// Consumers should await <see cref="WhenReadyAsync"/> before driving quest lifecycle (start/finish/init).
9
+ /// </summary>
10
+ public static class QuestCatalogue
11
+ {
12
+ public static bool IsReady { get; private set; }
13
+ public static bool HasFailed { get; private set; }
14
+ public static Exception LoadFailure { get; private set; }
15
+
16
+ public static event Action Ready;
17
+ public static event Action<Exception> Failed;
18
+
19
+ public static async Awaitable WhenReadyAsync()
20
+ {
21
+ while (!IsReady && !HasFailed)
22
+ await Awaitable.NextFrameAsync();
23
+
24
+ if (HasFailed)
25
+ throw new InvalidOperationException(
26
+ "Quest catalog failed to load addressable quests.",
27
+ LoadFailure);
28
+ }
29
+
30
+ internal static void BeginLoad()
31
+ {
32
+ IsReady = false;
33
+ HasFailed = false;
34
+ LoadFailure = null;
35
+ }
36
+
37
+ internal static void MarkReady()
38
+ {
39
+ if (IsReady)
40
+ return;
41
+
42
+ IsReady = true;
43
+ Ready?.Invoke();
44
+ }
45
+
46
+ internal static void MarkFailed(Exception exception)
47
+ {
48
+ if (HasFailed)
49
+ return;
50
+
51
+ HasFailed = true;
52
+ LoadFailure = exception;
53
+ Failed?.Invoke(exception);
54
+ }
55
+
56
+ internal static void Reset()
57
+ {
58
+ IsReady = false;
59
+ HasFailed = false;
60
+ LoadFailure = null;
61
+ }
62
+ }
63
+ }
@@ -0,0 +1,3 @@
1
+ fileFormatVersion: 2
2
+ guid: 1c256a40528c4f68be69ed7c2fbd8dc1
3
+ timeCreated: 1780444664
@@ -41,6 +41,7 @@ namespace jeanf.questsystem
41
41
  [ReadOnly]
42
42
  private bool clearToStart = false;
43
43
  private string questId;
44
+ private int _initRequestVersion;
44
45
  private QuestState currentQuestState;
45
46
  [ReadOnly]
46
47
  [Range(0, 1)]
@@ -75,7 +76,7 @@ namespace jeanf.questsystem
75
76
  questId = questSO.id;
76
77
  for (int i = 0; i < questSO.rootSteps.Length; i++)
77
78
  {
78
- if (isDebug) Debug.Log($"id on awake {questSO.rootSteps[i].StepId}, added to {this.name}'s dictionary", this);
79
+ //if (isDebug) Debug.Log($"id on awake {questSO.rootSteps[i].StepId}, added to {this.name}'s dictionary", this);
79
80
  AddStepToStepMap(questSO.rootSteps[i]);
80
81
  rootSteps.Add(questSO.rootSteps[i]);
81
82
  }
@@ -87,15 +88,14 @@ namespace jeanf.questsystem
87
88
  private void OnEnable()
88
89
  {
89
90
  Subscribe();
90
-
91
- Init(questId);
91
+ RunInitWhenCatalogueReady(questId);
92
92
  }
93
93
  private void OnDisable() => Unsubscribe();
94
94
  private void OnDestroy() => Unsubscribe();
95
95
  private void Subscribe()
96
96
  {
97
97
  resetChannel.OnEventRaised += Reset;
98
- QuestInitialCheck.OnEventRaised += Init;
98
+ QuestInitialCheck.OnEventRaised += OnQuestInitialCheck;
99
99
  QuestProgress.OnEventRaised += UpdateProgress;
100
100
  GameEventsManager.instance.questEvents.onQuestStateChange += QuestStateChange;
101
101
  GameEventsManager.instance.inputEvents.onSubmitPressed += UpdateState;
@@ -108,7 +108,7 @@ namespace jeanf.questsystem
108
108
  private void Unsubscribe()
109
109
  {
110
110
  resetChannel.OnEventRaised -= Reset;
111
- QuestInitialCheck.OnEventRaised -= Init;
111
+ QuestInitialCheck.OnEventRaised -= OnQuestInitialCheck;
112
112
  QuestProgress.OnEventRaised -= UpdateProgress;
113
113
  GameEventsManager.instance.questEvents.onQuestStateChange -= QuestStateChange;
114
114
  GameEventsManager.instance.inputEvents.onSubmitPressed -= UpdateState;
@@ -155,21 +155,42 @@ namespace jeanf.questsystem
155
155
  }
156
156
  private void AddStepToStepMap(QuestStep step)
157
157
  {
158
- if (isDebug) Debug.Log($"--- Received request to add step {step.name} with id: {step.StepId} to stepMap.");
158
+ //if (isDebug) Debug.Log($"--- Received request to add step {step.name} with id: {step.StepId} to stepMap.");
159
159
  if (!stepMap.ContainsKey(step.StepId))
160
160
  {
161
- if (isDebug) Debug.Log($"--- Step [{step.StepId}] not found in stepMap, adding it.");
161
+ //if (isDebug) Debug.Log($"--- Step [{step.StepId}] not found in stepMap, adding it.");
162
162
  stepMap.Add(step.StepId, step);
163
163
  }
164
164
  if (completedSteps.ContainsKey(step.StepId))
165
165
  {
166
- if (isDebug) Debug.Log($"--- Step [{step.StepId}] already completed removing it from completeSteps so that we can go through it again.");
166
+ //if (isDebug) Debug.Log($"--- Step [{step.StepId}] already completed removing it from completeSteps so that we can go through it again.");
167
167
  completedSteps.Remove(step.StepId);
168
168
  }
169
169
  }
170
170
  #endregion
171
171
 
172
172
  #region quest process
173
+ private void OnQuestInitialCheck(string id) => RunInitWhenCatalogueReady(id);
174
+
175
+ private async void RunInitWhenCatalogueReady(string id)
176
+ {
177
+ var requestVersion = ++_initRequestVersion;
178
+ try
179
+ {
180
+ await QuestCatalogue.WhenReadyAsync();
181
+ }
182
+ catch (InvalidOperationException e)
183
+ {
184
+ Debug.LogError($"[QuestItem] Cannot initialize quest '{id}': {e.Message}", this);
185
+ return;
186
+ }
187
+
188
+ if (requestVersion != _initRequestVersion || !isActiveAndEnabled)
189
+ return;
190
+
191
+ Init(id);
192
+ }
193
+
173
194
  private void Init(string id)
174
195
  {
175
196
  if (transform.childCount > 0)
@@ -185,7 +206,7 @@ namespace jeanf.questsystem
185
206
  completedSteps.Clear();
186
207
  completedSteps.TrimExcess();
187
208
 
188
- if (isDebug) Debug.Log($"Quest [{id}]: _startQuestOnEnable value is: [{_startQuestOnEnable}]");
209
+ //if (isDebug) Debug.Log($"Quest [{id}]: _startQuestOnEnable value is: [{_startQuestOnEnable}]");
189
210
  if (!_startQuestOnEnable || id != questId) return;
190
211
  clearToStart = true;
191
212
  currentQuestState = QuestState.CAN_START;
@@ -221,28 +242,28 @@ namespace jeanf.questsystem
221
242
  }
222
243
  private void UpdateState()
223
244
  {
224
- if (isDebug) Debug.Log($"Updating State...");
245
+ //if (isDebug) Debug.Log($"Updating State...");
225
246
  if (!clearToStart) return;
226
- if (isDebug) Debug.Log($"All is clear, continuing ...");
247
+ //if (isDebug) Debug.Log($"All is clear, continuing ...");
227
248
 
228
249
  switch (currentQuestState)
229
250
  {
230
251
  case QuestState.CAN_START:
231
252
  {
232
- if (isDebug) Debug.Log($"Starting quest: {questId}");
253
+ //if (isDebug) Debug.Log($"Starting quest: {questId}");
233
254
  GameEventsManager.instance.questEvents.StartQuest(questId);
234
255
  break;
235
256
  }
236
257
  case QuestState.CAN_FINISH:
237
258
  {
238
- if (isDebug) Debug.Log($"Finishing quest: {questId}");
259
+ //if (isDebug) Debug.Log($"Finishing quest: {questId}");
239
260
  GameEventsManager.instance.questEvents.FinishQuest(questId);
240
261
  break;
241
262
  }
242
263
  case QuestState.REQUIREMENTS_NOT_MET:
243
264
  if (_startQuestOnEnable)
244
265
  {
245
- if (isDebug) Debug.Log($"forcing start of quest: {questId}");
266
+ //if (isDebug) Debug.Log($"forcing start of quest: {questId}");
246
267
  GameEventsManager.instance.questEvents.StartQuest(questId);
247
268
  }
248
269
  break;
@@ -258,7 +279,7 @@ namespace jeanf.questsystem
258
279
  {
259
280
  if (id != questId) return;
260
281
  this.progress = progress;
261
- if (isDebug) Debug.Log($"questid [{id}] progress = {progress * 100}%");
282
+ //if (isDebug) Debug.Log($"questid [{id}] progress = {progress * 100}%");
262
283
  }
263
284
  private void QuestStateChange(Quest quest)
264
285
  {
@@ -294,10 +315,10 @@ namespace jeanf.questsystem
294
315
  }
295
316
  public void LogActiveSteps()
296
317
  {
297
- Debug.Log($"There is {activeSteps.Count} active steps at the moment.");
318
+ //Debug.Log($"There is {activeSteps.Count} active steps at the moment.");
298
319
  foreach (var step in activeSteps.Keys)
299
320
  {
300
- Debug.Log($"active step: {step}");
321
+ //Debug.Log($"active step: {step}");
301
322
  }
302
323
  }
303
324
  #endif
@@ -316,7 +337,7 @@ namespace jeanf.questsystem
316
337
 
317
338
  if (QuestInitialCheck == null)
318
339
  {
319
- if (isDebug) Debug.Log($"{searching} {_}/QuestInitialCheck in {searchLocation} ", this);
340
+ //if (isDebug) Debug.Log($"{searching} {_}/QuestInitialCheck in {searchLocation} ", this);
320
341
  QuestInitialCheck = Resources.Load<StringEventChannelSO>($"{_}/QuestInitialCheck");
321
342
  if (QuestInitialCheck == null)
322
343
  {
@@ -329,7 +350,7 @@ namespace jeanf.questsystem
329
350
 
330
351
  if (questSO == null)
331
352
  {
332
- if (isDebug) Debug.Log($"There is no questSO in the questItem");
353
+ //if (isDebug) Debug.Log($"There is no questSO in the questItem");
333
354
  validityCheck = false;
334
355
  invalidObjects.Add(questSO);
335
356
  }
@@ -337,7 +358,7 @@ namespace jeanf.questsystem
337
358
 
338
359
  if (QuestProgress == null)
339
360
  {
340
- if (isDebug) Debug.Log($"{searching} {_}/QuestsProgressChannel in {searchLocation} ", this);
361
+ //if (isDebug) Debug.Log($"{searching} {_}/QuestsProgressChannel in {searchLocation} ", this);
341
362
  QuestProgress = Resources.Load<StringFloatEventChannelSO>($"{_}/QuestsProgressChannel");
342
363
  if (QuestProgress == null)
343
364
  {
@@ -350,7 +371,7 @@ namespace jeanf.questsystem
350
371
 
351
372
  if (loadRequiredScenesEventChannel == null)
352
373
  {
353
- if (isDebug) Debug.Log($"{searching} {_}/loadRequiredScenesEventChannel in {searchLocation} ", this);
374
+ //if (isDebug) Debug.Log($"{searching} {_}/loadRequiredScenesEventChannel in {searchLocation} ", this);
354
375
  loadRequiredScenesEventChannel = Resources.Load<StringListEventChannelSO>($"{_}/LoadRequiredScenesEventChannel");
355
376
  if (loadRequiredScenesEventChannel == null)
356
377
  {
@@ -363,7 +384,7 @@ namespace jeanf.questsystem
363
384
 
364
385
  if (requirementCheck == null)
365
386
  {
366
- if (isDebug) Debug.Log($"{searching} {_}/QuestRequirementCheck in {searchLocation}", this);
387
+ //if (isDebug) Debug.Log($"{searching} {_}/QuestRequirementCheck in {searchLocation}", this);
367
388
  requirementCheck = Resources.Load<StringEventChannelSO>($"{_}/QuestRequirementCheck");
368
389
  if (requirementCheck == null)
369
390
  {
@@ -4,6 +4,8 @@ using System.Collections.Generic;
4
4
  using jeanf.EventSystem;
5
5
  using jeanf.validationTools;
6
6
  using UnityEngine;
7
+ using UnityEngine.AddressableAssets;
8
+ using UnityEngine.ResourceManagement.AsyncOperations;
7
9
  using UnityEngine.Serialization;
8
10
 
9
11
  namespace jeanf.questsystem
@@ -34,28 +36,29 @@ namespace jeanf.questsystem
34
36
  [FormerlySerializedAs("loadQuestState")] [Header("Config")] [SerializeField]
35
37
  private bool loadSavedQuestState = true;
36
38
  private Dictionary<string, Quest> questMap;
39
+ private readonly Queue<string> _pendingStartQuestIds = new Queue<string>();
40
+ private readonly Queue<string> _pendingFinishQuestIds = new Queue<string>();
37
41
  private int currentPlayerLevel;
42
+
43
+ // Label strings to load for scriptable objects
44
+ [Header("Addressables group to load:")]
45
+ [Tooltip("This group should contain all the scriptable objects that define your quests.")]
46
+ public List<string> _keys = new List<string>() { "Quests" };
47
+ private AsyncOperationHandle<IList<QuestSO>> _questAssetsHandle;
48
+
38
49
  #endregion
39
50
  #endregion
40
51
 
41
52
  #region Methods
42
53
  #region Standard Unity Methods
43
- private void Awake()
44
- {
45
- questMap = CreateQuestMap();
46
-
47
- foreach (var quest in questMap)
48
- {
49
- CheckIfQuestIsAlreadyLoaded(quest.Key);
50
- }
51
- }
54
+
52
55
  private void OnEnable()
53
56
  {
54
57
  GameEventsManager.instance.questEvents.onStartQuest += StartQuest;
55
58
  GameEventsManager.instance.questEvents.onFinishQuest += FinishQuest;
56
- //GameEventsManager.instance.questEvents.onQuestStepStateChange += QuestStepStateChange;
57
59
  GameEventsManager.instance.playerEvents.onPlayerLevelChange += PlayerLevelChange;
58
- questStatusUpdateRequested.OnEventRaised += ctx => CheckRequirementsMet(questMap[ctx]);
60
+ questStatusUpdateRequested.OnEventRaised += OnQuestStatusUpdateRequested;
61
+ //GameEventsManager.instance.questEvents.onQuestStepStateChange += QuestStepStateChange;
59
62
  }
60
63
  private void OnDisable() => Unsubscribe();
61
64
  private void OnDestroy() => Unsubscribe();
@@ -63,22 +66,50 @@ namespace jeanf.questsystem
63
66
  {
64
67
  GameEventsManager.instance.questEvents.onStartQuest -= StartQuest;
65
68
  GameEventsManager.instance.questEvents.onFinishQuest -= FinishQuest;
66
-
67
- //GameEventsManager.instance.questEvents.onQuestStepStateChange -= QuestStepStateChange;
68
-
69
69
  GameEventsManager.instance.playerEvents.onPlayerLevelChange -= PlayerLevelChange;
70
+ questStatusUpdateRequested.OnEventRaised -= OnQuestStatusUpdateRequested;
71
+ if (_questAssetsHandle.IsValid())
72
+ Addressables.Release(_questAssetsHandle);
73
+ QuestCatalogue.Reset();
74
+ _pendingStartQuestIds.Clear();
75
+ _pendingFinishQuestIds.Clear();
76
+ //GameEventsManager.instance.questEvents.onQuestStepStateChange -= QuestStepStateChange;
77
+ }
70
78
 
79
+ private void OnQuestStatusUpdateRequested(string questId)
80
+ {
81
+ if (questMap == null || !questMap.TryGetValue(questId, out var quest))
82
+ return;
83
+ CheckRequirementsMet(quest);
71
84
  }
72
- private void Start()
85
+
86
+ private async Awaitable Start()
73
87
  {
74
- foreach (Quest quest in questMap.Values)
88
+ QuestCatalogue.BeginLoad();
89
+ try
75
90
  {
76
- // broadcast the initial state of all quests on startup
77
- GameEventsManager.instance.questEvents.QuestStateChange(quest);
91
+ questMap = await CreateQuestMap();
92
+ QuestCatalogue.MarkReady();
93
+ FlushPendingQuestOperations();
94
+
95
+ foreach (var quest in questMap)
96
+ {
97
+ CheckIfQuestIsAlreadyLoaded(quest.Key);
98
+ GameEventsManager.instance.questEvents.QuestStateChange(quest.Value);
99
+ }
100
+ }
101
+ catch (Exception e)
102
+ {
103
+ QuestCatalogue.MarkFailed(e);
104
+ Debug.LogError($"[QuestManager] Error loading quest catalogue: {e.Message}");
78
105
  }
79
106
  }
107
+
80
108
  private void Update()
81
109
  {
110
+ if (!QuestCatalogue.IsReady || questMap == null)
111
+ return;
112
+
82
113
  // loop through ALL quests
83
114
  foreach (Quest quest in questMap.Values)
84
115
  {
@@ -91,6 +122,8 @@ namespace jeanf.questsystem
91
122
  }
92
123
  private void OnApplicationQuit()
93
124
  {
125
+ if (questMap == null)
126
+ return;
94
127
  foreach (Quest quest in questMap.Values)
95
128
  {
96
129
  SaveQuest(quest);
@@ -99,10 +132,47 @@ namespace jeanf.questsystem
99
132
  #endregion
100
133
 
101
134
  #region Quest Checks and getters
102
- private Dictionary<string, Quest> CreateQuestMap()
135
+
136
+ private static async Awaitable AwaitAsyncOperation<T>(AsyncOperationHandle<T> handle)
137
+ {
138
+ while (!handle.IsDone)
139
+ await Awaitable.NextFrameAsync();
140
+
141
+ if (handle.Status != AsyncOperationStatus.Succeeded)
142
+ throw new Exception("QuestManager failed to load Addressable assets.");
143
+ }
144
+
145
+ private async Awaitable<IList<QuestSO>> LoadQuestAssets(List<string> labels)
103
146
  {
104
- // loads all QuestInfoSO Scriptable Objects under the Assets/Resources/Quests folder
105
- QuestSO[] allQuests = Resources.LoadAll<QuestSO>("Quests");
147
+ _questAssetsHandle = Addressables.LoadAssetsAsync<QuestSO>(
148
+ labels,
149
+ addressable =>
150
+ {
151
+ if (isDebug) Debug.Log($"[QuestManager] Loaded quest SO: {addressable.name}");
152
+ },
153
+ Addressables.MergeMode.Union,
154
+ true);
155
+
156
+ await AwaitAsyncOperation(_questAssetsHandle);
157
+ return _questAssetsHandle.Result;
158
+ }
159
+
160
+ private async Awaitable<Dictionary<string, Quest>> CreateQuestMap()
161
+ {
162
+ // loads all QuestInfoSO Scriptable Objects under the Assets/Quests folder
163
+ Debug.Log("[QuestManager] Creating quest map");
164
+ IList<QuestSO> allQuests = new List<QuestSO>();
165
+ try
166
+ {
167
+ allQuests = await LoadQuestAssets(_keys);
168
+ if (isDebug) Debug.Log($"[QuestManager] Successfully loaded {allQuests.Count} assets!");
169
+ }
170
+ catch (Exception e)
171
+ {
172
+ Debug.LogError($"[QuestManager] Error loading assets: {e.Message}");
173
+ throw;
174
+ }
175
+
106
176
  // Create the quest map
107
177
  Dictionary<string, Quest> questMap = new Dictionary<string, Quest>();
108
178
  foreach (QuestSO questSO in allQuests)
@@ -110,13 +180,13 @@ namespace jeanf.questsystem
110
180
  var id = questSO.id;
111
181
  if (questMap.ContainsKey(id))
112
182
  {
113
- Debug.LogWarning("Duplicate ID found when creating quest map: " + questSO.id);
183
+ Debug.LogWarning($"[QuestManager] Duplicate ID found when creating quest map: {questSO.id}");
114
184
  }
115
185
  else
116
186
  {
117
187
  questMap.Add(id, LoadQuest(questSO));
118
188
  }
119
- if (isDebug) Debug.Log($"Adding {questSO.name} to the questmap, its id is: {questSO.id}");
189
+ if (isDebug) Debug.Log($"[QuestManager] Adding {questSO.name} to the questmap, its id is: {questSO.id}");
120
190
  }
121
191
  return questMap;
122
192
  }
@@ -126,14 +196,35 @@ namespace jeanf.questsystem
126
196
  }
127
197
  public Quest GetQuestById(string id)
128
198
  {
129
- Quest quest = questMap[id];
130
- if (quest == null)
199
+ if (questMap == null || !questMap.TryGetValue(id, out var quest))
131
200
  {
132
- Debug.LogError("ID not found in the Quest Map: " + id);
201
+ Debug.LogError($"[QuestManager] ID not found in the quest map: {id}");
202
+ return null;
133
203
  }
134
204
 
135
205
  return quest;
136
206
  }
207
+
208
+ private void FlushPendingQuestOperations()
209
+ {
210
+ while (_pendingStartQuestIds.Count > 0)
211
+ StartQuestCore(_pendingStartQuestIds.Dequeue());
212
+
213
+ while (_pendingFinishQuestIds.Count > 0)
214
+ FinishQuestCore(_pendingFinishQuestIds.Dequeue());
215
+ }
216
+
217
+ private static bool EnqueueUnique(Queue<string> queue, string id)
218
+ {
219
+ foreach (var pending in queue)
220
+ {
221
+ if (pending == id)
222
+ return false;
223
+ }
224
+
225
+ queue.Enqueue(id);
226
+ return true;
227
+ }
137
228
  private bool CheckRequirementsMet(Quest quest)
138
229
  {
139
230
  // check player level requirements
@@ -142,19 +233,21 @@ namespace jeanf.questsystem
142
233
  // check quest prerequisites for completion
143
234
  foreach (QuestSO prerequisiteQuestInfo in quest.questSO.questPrerequisites)
144
235
  {
145
- if (GetQuestById(prerequisiteQuestInfo.id).state != QuestState.FINISHED)
146
- {
236
+ var prerequisite = GetQuestById(prerequisiteQuestInfo.id);
237
+ if (prerequisite == null || prerequisite.state != QuestState.FINISHED)
147
238
  meetsRequirements = false;
148
- }
149
239
  }
150
240
 
151
- if (isDebug) Debug.Log($"checking requirements for quest: {quest.questSO.name}, [{quest.questSO.id}], meetsRequirements: {meetsRequirements}");
241
+ if (isDebug) Debug.Log($"[QuestManager] checking requirements for quest: {quest.questSO.name}, [{quest.questSO.id}], meetsRequirements: {meetsRequirements}");
152
242
 
153
243
  return meetsRequirements;
154
244
  }
155
245
  private void ChangeQuestState(string id, QuestState state)
156
246
  {
157
- Quest quest = GetQuestById(id);
247
+ var quest = GetQuestById(id);
248
+ if (quest == null)
249
+ return;
250
+
158
251
  quest.state = state;
159
252
  GameEventsManager.instance.questEvents.QuestStateChange(quest);
160
253
  }
@@ -163,30 +256,60 @@ namespace jeanf.questsystem
163
256
  #region main process
164
257
  private void StartQuest(string id)
165
258
  {
166
- Quest quest = GetQuestById(id);
259
+ if (!QuestCatalogue.IsReady || questMap == null)
260
+ {
261
+ if (EnqueueUnique(_pendingStartQuestIds, id) && isDebug)
262
+ Debug.Log($"[QuestManager] StartQuest deferred until catalogue is ready: {id}");
263
+ return;
264
+ }
265
+
266
+ StartQuestCore(id);
267
+ }
268
+
269
+ private void StartQuestCore(string id)
270
+ {
271
+ var quest = GetQuestById(id);
272
+ if (quest == null)
273
+ return;
274
+
167
275
  ChangeQuestState(quest.questSO.id, QuestState.IN_PROGRESS);
168
276
  SaveQuest(quest);
169
277
  if (!quest.sendMessageOnInitialization) return;
170
278
  quest.messageChannel.RaiseEvent(quest.messageToSendOnInit);
171
- if(isDebug) Debug.Log($"quest id:{id} started, a message was attatched to the initialization: {quest.messageToSendOnInit}");
279
+ if (isDebug)
280
+ Debug.Log($"[QuestManager] quest id:{id} started, message on init: {quest.messageToSendOnInit}");
172
281
  }
173
282
  private void UpdateProgress(Quest quest)
174
283
  {
175
284
  var progress = 0;
176
- if (quest.questSO.id == null) Debug.Log("C'est null");;
177
- if (isDebug) Debug.Log($"[{quest.questSO.id}] progress: {progress * 100}%", this);
285
+ if (quest.questSO.id == null) Debug.LogError("[QuestManager] quest.questSO.id is null");
286
+ if (isDebug) Debug.Log($"[QuestManager] [{quest.questSO.id}] progress: {progress * 100}%", this);
178
287
  questProgress.RaiseEvent(quest.questSO.id, progress);
179
288
  }
180
289
  private void FinishQuest(string id)
181
290
  {
182
- Quest quest = GetQuestById(id);
291
+ if (!QuestCatalogue.IsReady || questMap == null)
292
+ {
293
+ if (EnqueueUnique(_pendingFinishQuestIds, id) && isDebug)
294
+ Debug.Log($"[QuestManager] FinishQuest deferred until catalogue is ready: {id}");
295
+ return;
296
+ }
297
+
298
+ FinishQuestCore(id);
299
+ }
300
+
301
+ private void FinishQuestCore(string id)
302
+ {
303
+ var quest = GetQuestById(id);
304
+ if (quest == null)
305
+ return;
306
+
183
307
  UpdateProgress(quest);
184
308
  ClaimRewards(quest);
185
309
  ChangeQuestState(quest.questSO.id, QuestState.FINISHED);
186
310
  questStatusUpdateChannel.RaiseEvent(quest.questSO.id);
187
311
  questProgress.RaiseEvent(quest.questSO.id, 1);
188
312
  SaveQuest(quest);
189
- if (!quest.sendMessageOnFinish) return;
190
313
  }
191
314
  #endregion
192
315
 
@@ -212,7 +335,7 @@ namespace jeanf.questsystem
212
335
  //quest.GetQuestData();
213
336
  // serialize using JsonUtility, but use whatever you want here (like JSON.NET)
214
337
  string serializedData = JsonUtility.ToJson(questData);
215
- if(isDebug) Debug.Log($"saved data {serializedData}");
338
+ //if(isDebug) Debug.Log($"saved data {serializedData}");
216
339
  // saving to PlayerPrefs is just a quick example for this tutorial video,
217
340
  // you probably don't want to save this info there long-term.
218
341
  // instead, use an actual Save & Load system and write to a file, the cloud, etc..
@@ -220,12 +343,12 @@ namespace jeanf.questsystem
220
343
  }
221
344
  catch (System.Exception e)
222
345
  {
223
- Debug.LogError("Failed to save quest with id " + quest.questSO.id + ": " + e);
346
+ //Debug.LogError("Failed to save quest with id " + quest.questSO.id + ": " + e);
224
347
  }
225
348
  }
226
349
  private Quest LoadQuest(QuestSO questSO)
227
350
  {
228
- Debug.Log($"attempting to load quest with id: [{questSO.id}]");
351
+ if (isDebug) Debug.Log($"[QuestManager] attempting to load quest with id: [{questSO.id}]");
229
352
  var quest = new Quest(questSO);
230
353
  try
231
354
  {
@@ -235,18 +358,18 @@ namespace jeanf.questsystem
235
358
  var serializedData = PlayerPrefs.GetString(questSO.id);
236
359
  var questData = JsonUtility.FromJson<QuestData>(serializedData);
237
360
  quest = new Quest(questSO, questData.state, questData.questStepIndex, questData.questStepStates);
238
- Debug.Log($"loaded previously saved quest with id: [{quest.questSO.id}]");
361
+ if (isDebug) Debug.Log($"[QuestManager] loaded previously saved quest with id: [{quest.questSO.id}]");
239
362
  }
240
363
  // otherwise, initialize a new quest
241
364
  else
242
365
  {
243
366
  quest = new Quest(questSO);
244
- Debug.Log($"loaded a fresh instance of quest with id: [{quest.questSO.id}]");
367
+ if (isDebug) Debug.Log($"[QuestManager] loaded a fresh instance of quest with id: [{quest.questSO.id}]");
245
368
  }
246
369
  }
247
- catch (System.Exception e)
370
+ catch (Exception e)
248
371
  {
249
- Debug.LogError($"Failed to load quest with id: [{quest.questSO.id}] - exception: {e}");
372
+ Debug.LogError($"[QuestManager] Failed to load quest with id: [{quest.questSO.id}] - exception: {e}");
250
373
  }
251
374
 
252
375
  return quest;
@@ -268,7 +391,7 @@ namespace jeanf.questsystem
268
391
 
269
392
  if (QuestInitialCheck == null)
270
393
  {
271
- if (isDebug) Debug.Log($"{searching} {_}/QuestInitialCheck in {searchLocation}", this);
394
+ if (isDebug) Debug.Log($"[QuestManager] {searching} {_}/QuestInitialCheck in {searchLocation}", this);
272
395
  QuestInitialCheck = Resources.Load<StringEventChannelSO>($"{_}/QuestInitialCheck");
273
396
  if (QuestInitialCheck == null)
274
397
  {
@@ -280,7 +403,7 @@ namespace jeanf.questsystem
280
403
 
281
404
  if (questStatusUpdateChannel == null)
282
405
  {
283
- if (isDebug) Debug.Log($"{searching} {_}/QuestStatusUpdate in {searchLocation}", this);
406
+ if (isDebug) Debug.Log($"[QuestManager] {searching} {_}/QuestStatusUpdate in {searchLocation}", this);
284
407
  questStatusUpdateChannel = Resources.Load<StringEventChannelSO>($"{_}/QuestStatusUpdate");
285
408
  if (questStatusUpdateChannel == null)
286
409
  {
@@ -292,7 +415,7 @@ namespace jeanf.questsystem
292
415
 
293
416
  if (questProgress == null)
294
417
  {
295
- if (isDebug) Debug.Log($"{searching} {_}/QuestsProgressChannel in {searchLocation}", this);
418
+ if (isDebug) Debug.Log($"[QuestManager] {searching} {_}/QuestsProgressChannel in {searchLocation}", this);
296
419
  questProgress = Resources.Load<StringFloatEventChannelSO>($"{_}/QuestsProgressChannel");
297
420
  if (questProgress == null)
298
421
  {
@@ -304,7 +427,7 @@ namespace jeanf.questsystem
304
427
 
305
428
  if (questStatusUpdateRequested == null)
306
429
  {
307
- if (isDebug) Debug.Log($"{searching} {_}/QuestRequirementCheck in {searchLocation}", this);
430
+ if (isDebug) Debug.Log($"[QuestManager] {searching} {_}/QuestRequirementCheck in {searchLocation}", this);
308
431
  questStatusUpdateRequested = Resources.Load<StringEventChannelSO>($"{_}/QuestRequirementCheck");
309
432
  if (questStatusUpdateRequested == null)
310
433
  {
@@ -320,7 +443,7 @@ namespace jeanf.questsystem
320
443
  if (IsValid && !Application.isPlaying) return;
321
444
  for(var i = 0 ; i < invalidObjects.Count ; i++)
322
445
  {
323
- Debug.LogError($"Error: {errorMessages[i]} " , this.gameObject);
446
+ Debug.LogError($"[QuestManager] Error: {errorMessages[i]} " , this.gameObject);
324
447
  }
325
448
  }
326
449
  public void OnValidate()
@@ -103,7 +103,7 @@ namespace jeanf.questsystem
103
103
  // failsafe to avoid lauching the same step more than once at a time.
104
104
  if (stepStatus == QuestStepStatus.Active) return;
105
105
 
106
- if(isDebug) Debug.Log($"Initializing questStep [{stepId}] with for quest with questId: [{questId}]");
106
+ //if(isDebug) Debug.Log($"Initializing questStep [{stepId}] with for quest with questId: [{questId}]");
107
107
 
108
108
  stepStatus = QuestStepStatus.Active;
109
109
  stepActive?.Invoke(stepId, stepStatus);
@@ -115,15 +115,15 @@ namespace jeanf.questsystem
115
115
  }
116
116
  if (isUsingIntroTimeline && timeline)
117
117
  {
118
- if(isDebug) Debug.Log($"sending trigger to timeline: {timeline.name}, triggerValue: true");
118
+ //if(isDebug) Debug.Log($"sending trigger to timeline: {timeline.name}, triggerValue: true");
119
119
  _timelineTriggerEventChannelSo.RaiseEvent(timeline, true);
120
120
 
121
121
  }
122
122
 
123
- if(isDebug) Debug.Log($"Step with id [{stepId}] has {questStepsToTrigger.Count} childSteps");
123
+ //if(isDebug) Debug.Log($"Step with id [{stepId}] has {questStepsToTrigger.Count} childSteps");
124
124
  foreach(QuestStep questStep in questStepsToTrigger)
125
125
  {
126
- if(isDebug) Debug.Log($"sending childstep to initialization: {questStep.name}, stepId: [{questStep.stepId}]");
126
+ //if(isDebug) Debug.Log($"sending childstep to initialization: {questStep.name}, stepId: [{questStep.stepId}]");
127
127
  childStep?.Invoke(questStep);
128
128
  }
129
129
  }
@@ -145,7 +145,7 @@ namespace jeanf.questsystem
145
145
  endStepChannel.RaiseEvent(stepId);
146
146
  foreach (QuestStep questStep in questStepsTriggeredOnFailure)
147
147
  {
148
- if (isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Requesting to start next step: {questStep.stepId}", this);
148
+ //if (isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Requesting to start next step: {questStep.stepId}", this);
149
149
 
150
150
  //Si questStep.prerequisitesStep are in QuestItem.stepsCompleted
151
151
  sendNextStepId?.Invoke(questStep.stepId);
@@ -153,31 +153,31 @@ namespace jeanf.questsystem
153
153
  }
154
154
  public void FinishQuestStep()
155
155
  {
156
- if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Changing status to completed", this);
156
+ //if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Changing status to completed", this);
157
157
  stepStatus = QuestStepStatus.Completed;
158
158
 
159
159
  if (sendQuestStepTooltip != null)
160
160
  {
161
- if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Sending tooltip", this);
161
+ //if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Sending tooltip", this);
162
162
  sendQuestStepTooltip.RaiseEvent(string.Empty);
163
163
  }
164
164
 
165
165
 
166
- if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Sending stepCompleted Event (delegate) with argument: {stepId}", this);
166
+ //if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Sending stepCompleted Event (delegate) with argument: {stepId}", this);
167
167
  stepCompleted?.Invoke(stepId);
168
- if (isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Sending stepActive Event (delegate) with arguments: {stepId}, {stepStatus} ", this);
168
+ //if (isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Sending stepActive Event (delegate) with arguments: {stepId}, {stepStatus} ", this);
169
169
  stepActive?.Invoke(stepId, stepStatus);
170
170
 
171
171
  foreach (QuestStep questStep in questStepsToTrigger)
172
172
  {
173
- if (isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Requesting to start next step: {questStep.stepId}", this);
173
+ //if (isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Requesting to start next step: {questStep.stepId}", this);
174
174
 
175
175
  //Si questStep.prerequisitesStep are in QuestItem.stepsCompleted
176
176
  sendNextStepId?.Invoke(questStep.stepId);
177
177
  }
178
178
 
179
179
 
180
- if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Destroying the gameobject with name {this.name}", this);
180
+ //if(isDebug) Debug.Log($" ---- Step with id: {stepId} finished. Destroying the gameobject with name {this.name}", this);
181
181
  Destroy(this.gameObject);
182
182
  }
183
183
  #endregion
@@ -11,22 +11,22 @@ namespace jeanf.questsystem
11
11
 
12
12
  public void StartQuest(string id)
13
13
  {
14
- if (onStartQuest != null)
15
- {
16
- Debug.Log($"starting quest: {id}");
17
- onStartQuest(id);
18
- }
14
+ if (onStartQuest == null)
15
+ return;
16
+
17
+ Debug.Log($"starting quest: {id}");
18
+ onStartQuest(id);
19
19
  }
20
20
 
21
21
  public event Action<string> onFinishQuest;
22
22
 
23
23
  public void FinishQuest(string id)
24
24
  {
25
- if (onFinishQuest != null)
26
- {
27
- Debug.Log($"finishing quest: {id}");
28
- onFinishQuest(id);
29
- }
25
+ if (onFinishQuest == null)
26
+ return;
27
+
28
+ Debug.Log($"finishing quest: {id}");
29
+ onFinishQuest(id);
30
30
  }
31
31
 
32
32
  public event Action<Quest> onQuestStateChange;
@@ -5,7 +5,9 @@
5
5
  "GUID:75469ad4d38634e559750d17036d5f7c",
6
6
  "GUID:e134609276952144282613c65d798616",
7
7
  "GUID:e8958e6939af7314a97769de4be4ce25",
8
- "GUID:db1b0a3157155ad47996061dbcc07bbb"
8
+ "GUID:db1b0a3157155ad47996061dbcc07bbb",
9
+ "GUID:84651a3751eca9349aac36a66bba901b",
10
+ "GUID:9e24947de15b9834991c9d8411ea37cf"
9
11
  ],
10
12
  "includePlatforms": [],
11
13
  "excludePlatforms": [],
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "fr.jeanf.questsystem",
3
- "version": "0.1.8",
3
+ "version": "1.0.0",
4
4
  "displayName": "Quest system",
5
5
  "description": "This package uses Scriptable Objects to define quests.",
6
6
  "unity": "2021.3",
@@ -8,6 +8,10 @@
8
8
  "jeanf",
9
9
  "quest system"
10
10
  ],
11
+ "repository": {
12
+ "type": "git",
13
+ "url": "https://github.com/lejeanf/QuestSystem"
14
+ },
11
15
  "author": {
12
16
  "name": "Jean-FranΓ§ois Robin",
13
17
  "email": "robin.jeanfrancois@gmail.com",