fr.jeanf.questsystem 0.1.9 β 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.
- package/.github/workflows/publish.yml +14 -12
- package/Runtime/Scripts/Core/QuestCatalogue.cs +63 -0
- package/Runtime/Scripts/Core/QuestCatalogue.cs.meta +3 -0
- package/Runtime/Scripts/Core/QuestItem.cs +25 -4
- package/Runtime/Scripts/Core/QuestManager.cs +170 -47
- package/Runtime/Scripts/Events/QuestEvents.cs +10 -10
- package/Runtime/Scripts/jeanf.QuestSystem.asmdef +3 -1
- package/package.json +5 -1
|
@@ -3,21 +3,23 @@ name: "π publish"
|
|
|
3
3
|
on:
|
|
4
4
|
push:
|
|
5
5
|
branches:
|
|
6
|
-
|
|
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
|
-
-
|
|
14
|
-
|
|
15
|
-
-
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
-
|
|
21
|
-
|
|
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
|
+
}
|
|
@@ -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)]
|
|
@@ -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 +=
|
|
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 -=
|
|
111
|
+
QuestInitialCheck.OnEventRaised -= OnQuestInitialCheck;
|
|
112
112
|
QuestProgress.OnEventRaised -= UpdateProgress;
|
|
113
113
|
GameEventsManager.instance.questEvents.onQuestStateChange -= QuestStateChange;
|
|
114
114
|
GameEventsManager.instance.inputEvents.onSubmitPressed -= UpdateState;
|
|
@@ -170,6 +170,27 @@ namespace jeanf.questsystem
|
|
|
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)
|
|
@@ -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
|
-
|
|
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 +=
|
|
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
|
-
|
|
85
|
+
|
|
86
|
+
private async Awaitable Start()
|
|
73
87
|
{
|
|
74
|
-
|
|
88
|
+
QuestCatalogue.BeginLoad();
|
|
89
|
+
try
|
|
75
90
|
{
|
|
76
|
-
|
|
77
|
-
|
|
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
|
-
|
|
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
|
-
|
|
105
|
-
|
|
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:
|
|
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
|
-
|
|
130
|
-
if (quest == null)
|
|
199
|
+
if (questMap == null || !questMap.TryGetValue(id, out var quest))
|
|
131
200
|
{
|
|
132
|
-
Debug.LogError("ID not found in the
|
|
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
|
-
|
|
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
|
-
|
|
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
|
-
|
|
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)
|
|
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.
|
|
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
|
-
|
|
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
|
|
|
@@ -225,7 +348,7 @@ namespace jeanf.questsystem
|
|
|
225
348
|
}
|
|
226
349
|
private Quest LoadQuest(QuestSO questSO)
|
|
227
350
|
{
|
|
228
|
-
|
|
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
|
-
|
|
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
|
-
|
|
367
|
+
if (isDebug) Debug.Log($"[QuestManager] loaded a fresh instance of quest with id: [{quest.questSO.id}]");
|
|
245
368
|
}
|
|
246
369
|
}
|
|
247
|
-
catch (
|
|
370
|
+
catch (Exception e)
|
|
248
371
|
{
|
|
249
|
-
|
|
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()
|
|
@@ -11,22 +11,22 @@ namespace jeanf.questsystem
|
|
|
11
11
|
|
|
12
12
|
public void StartQuest(string id)
|
|
13
13
|
{
|
|
14
|
-
if (onStartQuest
|
|
15
|
-
|
|
16
|
-
|
|
17
|
-
|
|
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
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
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.
|
|
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",
|