@bscotch/gcdata 0.14.2
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/LICENSE.md +29 -0
- package/README.md +36 -0
- package/dist/GameChanger.d.ts +209 -0
- package/dist/GameChanger.d.ts.map +1 -0
- package/dist/GameChanger.events.d.ts +13 -0
- package/dist/GameChanger.events.d.ts.map +1 -0
- package/dist/GameChanger.events.js +3 -0
- package/dist/GameChanger.events.js.map +1 -0
- package/dist/GameChanger.js +478 -0
- package/dist/GameChanger.js.map +1 -0
- package/dist/SpellChecker.d.ts +31 -0
- package/dist/SpellChecker.d.ts.map +1 -0
- package/dist/SpellChecker.js +92 -0
- package/dist/SpellChecker.js.map +1 -0
- package/dist/assert.d.ts +6 -0
- package/dist/assert.d.ts.map +1 -0
- package/dist/assert.js +22 -0
- package/dist/assert.js.map +1 -0
- package/dist/cl2.loc.d.ts +1 -0
- package/dist/cl2.loc.d.ts.map +1 -0
- package/dist/cl2.loc.js +2 -0
- package/dist/cl2.loc.js.map +1 -0
- package/dist/cl2.quest.d.ts +4 -0
- package/dist/cl2.quest.d.ts.map +1 -0
- package/dist/cl2.quest.js +4 -0
- package/dist/cl2.quest.js.map +1 -0
- package/dist/cl2.quest.parse.d.ts +7 -0
- package/dist/cl2.quest.parse.d.ts.map +1 -0
- package/dist/cl2.quest.parse.js +825 -0
- package/dist/cl2.quest.parse.js.map +1 -0
- package/dist/cl2.quest.pointers.d.ts +3 -0
- package/dist/cl2.quest.pointers.d.ts.map +1 -0
- package/dist/cl2.quest.pointers.js +2 -0
- package/dist/cl2.quest.pointers.js.map +1 -0
- package/dist/cl2.quest.stringify.d.ts +5 -0
- package/dist/cl2.quest.stringify.d.ts.map +1 -0
- package/dist/cl2.quest.stringify.js +148 -0
- package/dist/cl2.quest.stringify.js.map +1 -0
- package/dist/cl2.quest.types.d.ts +168 -0
- package/dist/cl2.quest.types.d.ts.map +1 -0
- package/dist/cl2.quest.types.js +116 -0
- package/dist/cl2.quest.types.js.map +1 -0
- package/dist/cl2.quest.utils.d.ts +16 -0
- package/dist/cl2.quest.utils.d.ts.map +1 -0
- package/dist/cl2.quest.utils.js +105 -0
- package/dist/cl2.quest.utils.js.map +1 -0
- package/dist/cl2.storyline.d.ts +4 -0
- package/dist/cl2.storyline.d.ts.map +1 -0
- package/dist/cl2.storyline.js +4 -0
- package/dist/cl2.storyline.js.map +1 -0
- package/dist/cl2.storyline.parse.d.ts +7 -0
- package/dist/cl2.storyline.parse.d.ts.map +1 -0
- package/dist/cl2.storyline.parse.js +208 -0
- package/dist/cl2.storyline.parse.js.map +1 -0
- package/dist/cl2.storyline.pointers.d.ts +3 -0
- package/dist/cl2.storyline.pointers.d.ts.map +1 -0
- package/dist/cl2.storyline.pointers.js +2 -0
- package/dist/cl2.storyline.pointers.js.map +1 -0
- package/dist/cl2.storyline.stringify.d.ts +4 -0
- package/dist/cl2.storyline.stringify.d.ts.map +1 -0
- package/dist/cl2.storyline.stringify.js +18 -0
- package/dist/cl2.storyline.stringify.js.map +1 -0
- package/dist/cl2.storyline.types.d.ts +24 -0
- package/dist/cl2.storyline.types.d.ts.map +1 -0
- package/dist/cl2.storyline.types.js +87 -0
- package/dist/cl2.storyline.types.js.map +1 -0
- package/dist/cl2.storyline.utils.d.ts +1 -0
- package/dist/cl2.storyline.utils.d.ts.map +1 -0
- package/dist/cl2.storyline.utils.js +2 -0
- package/dist/cl2.storyline.utils.js.map +1 -0
- package/dist/cl2.types.auto.d.ts +22682 -0
- package/dist/cl2.types.auto.d.ts.map +1 -0
- package/dist/cl2.types.auto.js +2 -0
- package/dist/cl2.types.auto.js.map +1 -0
- package/dist/cl2.types.editor.d.ts +31 -0
- package/dist/cl2.types.editor.d.ts.map +1 -0
- package/dist/cl2.types.editor.js +2 -0
- package/dist/cl2.types.editor.js.map +1 -0
- package/dist/dict.d.ts +3 -0
- package/dist/dict.d.ts.map +1 -0
- package/dist/dict.js +49777 -0
- package/dist/dict.js.map +1 -0
- package/dist/helpers.d.ts +44 -0
- package/dist/helpers.d.ts.map +1 -0
- package/dist/helpers.js +149 -0
- package/dist/helpers.js.map +1 -0
- package/dist/index.d.ts +9 -0
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +9 -0
- package/dist/index.js.map +1 -0
- package/dist/types.cl2.rumpus.d.ts +190 -0
- package/dist/types.cl2.rumpus.d.ts.map +1 -0
- package/dist/types.cl2.rumpus.js +30 -0
- package/dist/types.cl2.rumpus.js.map +1 -0
- package/dist/types.d.ts +586 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.editor.d.ts +25 -0
- package/dist/types.editor.d.ts.map +1 -0
- package/dist/types.editor.js +2 -0
- package/dist/types.editor.js.map +1 -0
- package/dist/types.js +98 -0
- package/dist/types.js.map +1 -0
- package/dist/util.d.ts +42 -0
- package/dist/util.d.ts.map +1 -0
- package/dist/util.js +272 -0
- package/dist/util.js.map +1 -0
- package/package.json +32 -0
|
@@ -0,0 +1,825 @@
|
|
|
1
|
+
import { assert } from './assert.js';
|
|
2
|
+
import { arrayTagPattern, lineIsArrayItem, linePatterns, parseIfMatch, } from './cl2.quest.types.js';
|
|
3
|
+
import { getMomentStyleNames, getMoteLists, getRequirementStyleNames, getReuirementQuestStatuses, isEmoteMoment, } from './cl2.quest.utils.js';
|
|
4
|
+
import { bsArrayToArray, changedPosition, createBsArrayKey, updateBsArrayOrder, } from './helpers.js';
|
|
5
|
+
import { parsedItemToWords } from './util.js';
|
|
6
|
+
export function parseStringifiedQuest(text, packed, options = {}) {
|
|
7
|
+
const motes = getMoteLists(packed.working);
|
|
8
|
+
const momentStyles = getMomentStyleNames(packed.working);
|
|
9
|
+
// Remove 'Dialogue' and 'Emote' from the list of moment styles
|
|
10
|
+
for (const style of ['Dialogue', 'Emote']) {
|
|
11
|
+
momentStyles.splice(momentStyles.indexOf(style), 1);
|
|
12
|
+
}
|
|
13
|
+
const requirementStyles = getRequirementStyleNames(packed.working);
|
|
14
|
+
const requirementQuestStatuses = getReuirementQuestStatuses(packed.working);
|
|
15
|
+
const requirementCompletions = [...requirementStyles];
|
|
16
|
+
requirementCompletions.splice(requirementCompletions.indexOf('Quest'), 1);
|
|
17
|
+
requirementCompletions.push(...requirementQuestStatuses.map((s) => `Quest ${s}`));
|
|
18
|
+
/**
|
|
19
|
+
* Shared list of keywords that can be used at the start of any line,
|
|
20
|
+
* with required-unique entries removed when found.
|
|
21
|
+
*/
|
|
22
|
+
const nonUniqueGlobalLabels = new Set(['Clue']);
|
|
23
|
+
const availableGlobalLabels = new Set([
|
|
24
|
+
'Draft',
|
|
25
|
+
'Name',
|
|
26
|
+
'Storyline',
|
|
27
|
+
'Giver',
|
|
28
|
+
'Receiver',
|
|
29
|
+
'Clue',
|
|
30
|
+
'Start Requirements',
|
|
31
|
+
'Start Moments',
|
|
32
|
+
'End Requirements',
|
|
33
|
+
'End Moments',
|
|
34
|
+
'Log',
|
|
35
|
+
]);
|
|
36
|
+
const result = {
|
|
37
|
+
diagnostics: [],
|
|
38
|
+
hovers: [],
|
|
39
|
+
edits: [],
|
|
40
|
+
completions: [],
|
|
41
|
+
words: [],
|
|
42
|
+
parsed: {
|
|
43
|
+
clues: [],
|
|
44
|
+
quest_end_moments: [],
|
|
45
|
+
quest_start_moments: [],
|
|
46
|
+
quest_start_requirements: [],
|
|
47
|
+
quest_end_requirements: [],
|
|
48
|
+
comments: [],
|
|
49
|
+
},
|
|
50
|
+
};
|
|
51
|
+
const lines = text.split(/(\r?\n)/g);
|
|
52
|
+
let index = 0;
|
|
53
|
+
let lineNumber = 0;
|
|
54
|
+
const emojiIdFromName = (name) => {
|
|
55
|
+
if (!name) {
|
|
56
|
+
return undefined;
|
|
57
|
+
}
|
|
58
|
+
const emoji = motes.emojis.find((e) => packed.working.getMoteName(e)?.toLowerCase() ===
|
|
59
|
+
name?.trim().toLowerCase() || e.id === name?.trim());
|
|
60
|
+
return emoji?.id;
|
|
61
|
+
};
|
|
62
|
+
const checkSpelling = (item) => {
|
|
63
|
+
if (!item || !options.checkSpelling)
|
|
64
|
+
return;
|
|
65
|
+
// Parse out the word positions so they can be used as ranges to check cursor position
|
|
66
|
+
const words = parsedItemToWords(item);
|
|
67
|
+
for (const word of words) {
|
|
68
|
+
result.words.push(packed.spellChecker.checkWord(word));
|
|
69
|
+
}
|
|
70
|
+
};
|
|
71
|
+
/** The MoteId for the last speaker we saw. Used to figure out who to assign stuff to */
|
|
72
|
+
let lastSpeaker;
|
|
73
|
+
let lastClue;
|
|
74
|
+
let lastSectionGroup;
|
|
75
|
+
let lastEmojiGroup;
|
|
76
|
+
for (const line of lines) {
|
|
77
|
+
const trace = [];
|
|
78
|
+
try {
|
|
79
|
+
// Is this just a newline?
|
|
80
|
+
if (line.match(/\r?\n/)) {
|
|
81
|
+
// Then we just need to increment the index
|
|
82
|
+
index += line.length;
|
|
83
|
+
lineNumber++;
|
|
84
|
+
continue;
|
|
85
|
+
}
|
|
86
|
+
const lineRange = {
|
|
87
|
+
start: {
|
|
88
|
+
index,
|
|
89
|
+
line: lineNumber,
|
|
90
|
+
character: 0,
|
|
91
|
+
},
|
|
92
|
+
end: {
|
|
93
|
+
index: index + line.length,
|
|
94
|
+
line: lineNumber,
|
|
95
|
+
character: line.length,
|
|
96
|
+
},
|
|
97
|
+
};
|
|
98
|
+
// Is this just a blank line?
|
|
99
|
+
if (!line) {
|
|
100
|
+
// Add global autocompletes
|
|
101
|
+
result.completions.push({
|
|
102
|
+
type: 'labels',
|
|
103
|
+
start: lineRange.start,
|
|
104
|
+
end: lineRange.end,
|
|
105
|
+
options: availableGlobalLabels,
|
|
106
|
+
});
|
|
107
|
+
if (isQuestMomentLabel(lastSectionGroup)) {
|
|
108
|
+
result.completions.push({
|
|
109
|
+
type: 'momentStyles',
|
|
110
|
+
options: momentStyles,
|
|
111
|
+
start: lineRange.start,
|
|
112
|
+
end: lineRange.end,
|
|
113
|
+
});
|
|
114
|
+
}
|
|
115
|
+
else if (isQuestRequirementLabel(lastSectionGroup)) {
|
|
116
|
+
result.completions.push({
|
|
117
|
+
type: 'requirementStyles',
|
|
118
|
+
options: requirementCompletions,
|
|
119
|
+
start: lineRange.start,
|
|
120
|
+
end: lineRange.end,
|
|
121
|
+
});
|
|
122
|
+
}
|
|
123
|
+
continue;
|
|
124
|
+
}
|
|
125
|
+
// Find the first matching pattern and pull the values from it.
|
|
126
|
+
let parsedLine = null;
|
|
127
|
+
for (const pattern of linePatterns) {
|
|
128
|
+
parsedLine = parseIfMatch(pattern, line, lineRange.start);
|
|
129
|
+
if (parsedLine)
|
|
130
|
+
break;
|
|
131
|
+
}
|
|
132
|
+
if (!parsedLine) {
|
|
133
|
+
// Then this is likely the result of uncommenting something
|
|
134
|
+
// that was commented out, resulting in a line that starts with
|
|
135
|
+
// the comment's array tag. Provide a deletion edit!
|
|
136
|
+
parsedLine = parseIfMatch(`^${arrayTagPattern} +(?<text>.*)$`, line, lineRange.start);
|
|
137
|
+
if (parsedLine) {
|
|
138
|
+
result.edits.push({
|
|
139
|
+
start: lineRange.start,
|
|
140
|
+
end: lineRange.end,
|
|
141
|
+
newText: parsedLine.text.value,
|
|
142
|
+
});
|
|
143
|
+
}
|
|
144
|
+
else {
|
|
145
|
+
result.diagnostics.push({
|
|
146
|
+
message: `Unfamiliar syntax: ${line}`,
|
|
147
|
+
...lineRange,
|
|
148
|
+
});
|
|
149
|
+
}
|
|
150
|
+
index += line.length;
|
|
151
|
+
continue;
|
|
152
|
+
}
|
|
153
|
+
// Ensure the array tag. It goes right after the label or indicator.
|
|
154
|
+
if (!parsedLine.arrayTag?.value && lineIsArrayItem(line)) {
|
|
155
|
+
const arrayTag = createBsArrayKey();
|
|
156
|
+
const start = parsedLine.indicator?.end || parsedLine.label?.end;
|
|
157
|
+
result.edits.push({
|
|
158
|
+
start,
|
|
159
|
+
end: start,
|
|
160
|
+
newText: `#${arrayTag}`,
|
|
161
|
+
});
|
|
162
|
+
parsedLine.arrayTag = {
|
|
163
|
+
start,
|
|
164
|
+
end: start,
|
|
165
|
+
value: arrayTag,
|
|
166
|
+
};
|
|
167
|
+
}
|
|
168
|
+
// If this has a label, remove it from the list of available labels
|
|
169
|
+
if (parsedLine.label?.value &&
|
|
170
|
+
availableGlobalLabels.has(parsedLine.label.value) &&
|
|
171
|
+
!nonUniqueGlobalLabels.has(parsedLine.label.value)) {
|
|
172
|
+
availableGlobalLabels.delete(parsedLine.label.value);
|
|
173
|
+
}
|
|
174
|
+
// Track common problems so that we don't need to repeat logic
|
|
175
|
+
/** The character where a mote should exist. */
|
|
176
|
+
let requiresMote;
|
|
177
|
+
let requiresEmoji;
|
|
178
|
+
// Work through each line type to add diagnostics and completions
|
|
179
|
+
const labelLower = parsedLine.label?.value?.toLowerCase();
|
|
180
|
+
const indicator = parsedLine.indicator?.value;
|
|
181
|
+
// Resets
|
|
182
|
+
if (indicator !== '>') {
|
|
183
|
+
// Then we need to reset the speaker
|
|
184
|
+
lastSpeaker = undefined;
|
|
185
|
+
lastClue = undefined;
|
|
186
|
+
}
|
|
187
|
+
if (indicator !== '!') {
|
|
188
|
+
lastEmojiGroup = undefined;
|
|
189
|
+
}
|
|
190
|
+
// Parsing
|
|
191
|
+
if (labelLower === 'start moments') {
|
|
192
|
+
lastSectionGroup = 'quest_start_moments';
|
|
193
|
+
}
|
|
194
|
+
else if (labelLower === 'end moments') {
|
|
195
|
+
lastSectionGroup = 'quest_end_moments';
|
|
196
|
+
}
|
|
197
|
+
else if (labelLower === 'start requirements') {
|
|
198
|
+
lastSectionGroup = 'quest_start_requirements';
|
|
199
|
+
}
|
|
200
|
+
else if (labelLower === 'end requirements') {
|
|
201
|
+
lastSectionGroup = 'quest_end_requirements';
|
|
202
|
+
}
|
|
203
|
+
if (indicator === '\t') {
|
|
204
|
+
// No data gets stored here, this is just a convenience marker
|
|
205
|
+
// to set the speaker for the next set of dialog lines.
|
|
206
|
+
requiresMote = {
|
|
207
|
+
at: parsedLine.indicator.end,
|
|
208
|
+
options: motes.allowedSpeakers,
|
|
209
|
+
};
|
|
210
|
+
lastSpeaker = parsedLine.moteTag?.value;
|
|
211
|
+
}
|
|
212
|
+
else if (labelLower === 'clue') {
|
|
213
|
+
requiresMote = {
|
|
214
|
+
at: parsedLine.labelGroup.end,
|
|
215
|
+
options: motes.allowedSpeakers,
|
|
216
|
+
};
|
|
217
|
+
lastClue = {
|
|
218
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
219
|
+
speaker: parsedLine.moteTag?.value?.trim(),
|
|
220
|
+
phrases: [],
|
|
221
|
+
};
|
|
222
|
+
result.parsed.clues ||= [];
|
|
223
|
+
result.parsed.clues.push(lastClue);
|
|
224
|
+
}
|
|
225
|
+
else if (indicator === '>') {
|
|
226
|
+
// Then this is a dialog line, either within a Clue or a Dialog Moment
|
|
227
|
+
const emoji = emojiIdFromName(parsedLine.emojiName?.value);
|
|
228
|
+
if (parsedLine.emojiGroup) {
|
|
229
|
+
// Emojis are optional. If we see a "group" (parentheses) then
|
|
230
|
+
// that changes to a requirement.
|
|
231
|
+
requiresEmoji = {
|
|
232
|
+
at: changedPosition(parsedLine.emojiGroup.start, { characters: 1 }),
|
|
233
|
+
options: motes.emojis,
|
|
234
|
+
};
|
|
235
|
+
}
|
|
236
|
+
checkSpelling(parsedLine.text);
|
|
237
|
+
const moment = {
|
|
238
|
+
kind: 'dialogue',
|
|
239
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
240
|
+
speaker: lastSpeaker,
|
|
241
|
+
emoji,
|
|
242
|
+
text: parsedLine.text?.value?.trim() || '',
|
|
243
|
+
};
|
|
244
|
+
if (lastClue) {
|
|
245
|
+
lastClue.phrases.push(moment);
|
|
246
|
+
}
|
|
247
|
+
else if (isQuestMomentLabel(lastSectionGroup)) {
|
|
248
|
+
result.parsed[lastSectionGroup].push(moment);
|
|
249
|
+
}
|
|
250
|
+
else {
|
|
251
|
+
// Then this is an error!
|
|
252
|
+
result.diagnostics.push({
|
|
253
|
+
message: `Dialog line without a Clue or Moment!`,
|
|
254
|
+
...lineRange,
|
|
255
|
+
});
|
|
256
|
+
}
|
|
257
|
+
}
|
|
258
|
+
else if (labelLower === 'name') {
|
|
259
|
+
result.parsed.name = parsedLine.text?.value?.trim();
|
|
260
|
+
if (!result.parsed.name) {
|
|
261
|
+
result.diagnostics.push({
|
|
262
|
+
message: `Quest name required!`,
|
|
263
|
+
...lineRange,
|
|
264
|
+
});
|
|
265
|
+
}
|
|
266
|
+
}
|
|
267
|
+
else if (labelLower === 'draft') {
|
|
268
|
+
result.parsed.draft = parsedLine.text?.value?.trim() === 'true';
|
|
269
|
+
}
|
|
270
|
+
else if (labelLower === 'log') {
|
|
271
|
+
result.parsed.quest_start_log = parsedLine.text?.value?.trim();
|
|
272
|
+
checkSpelling(parsedLine.text);
|
|
273
|
+
}
|
|
274
|
+
else if (labelLower === 'storyline') {
|
|
275
|
+
requiresMote = {
|
|
276
|
+
at: parsedLine.labelGroup.end,
|
|
277
|
+
options: motes.storylines,
|
|
278
|
+
};
|
|
279
|
+
result.parsed.storyline = parsedLine.moteTag?.value?.trim();
|
|
280
|
+
}
|
|
281
|
+
else if (labelLower === 'giver') {
|
|
282
|
+
requiresMote = {
|
|
283
|
+
at: parsedLine.labelGroup.end,
|
|
284
|
+
options: motes.allowedGivers,
|
|
285
|
+
};
|
|
286
|
+
result.parsed.quest_giver = parsedLine.moteTag?.value?.trim();
|
|
287
|
+
}
|
|
288
|
+
else if (labelLower === 'receiver') {
|
|
289
|
+
requiresMote = {
|
|
290
|
+
at: parsedLine.labelGroup.end,
|
|
291
|
+
options: motes.allowedGivers,
|
|
292
|
+
};
|
|
293
|
+
result.parsed.quest_receiver = parsedLine.moteTag?.value?.trim();
|
|
294
|
+
}
|
|
295
|
+
else if (indicator === ':)') {
|
|
296
|
+
if (isQuestMomentLabel(lastSectionGroup)) {
|
|
297
|
+
// Then this is a declaration line for an Emote moment
|
|
298
|
+
// Create the new emoji group
|
|
299
|
+
lastEmojiGroup = {
|
|
300
|
+
kind: 'emote',
|
|
301
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
302
|
+
emotes: [],
|
|
303
|
+
};
|
|
304
|
+
result.parsed[lastSectionGroup].push(lastEmojiGroup);
|
|
305
|
+
}
|
|
306
|
+
else {
|
|
307
|
+
result.diagnostics.push({
|
|
308
|
+
message: `Must be defined in a Start/End Moments section!`,
|
|
309
|
+
...lineRange,
|
|
310
|
+
});
|
|
311
|
+
}
|
|
312
|
+
}
|
|
313
|
+
else if (indicator === '!') {
|
|
314
|
+
// Then this is an emote within a Emote moment
|
|
315
|
+
if (lastEmojiGroup) {
|
|
316
|
+
// Add speaker autocompletes
|
|
317
|
+
if (parsedLine.sep) {
|
|
318
|
+
requiresMote = {
|
|
319
|
+
at: parsedLine.sep.end,
|
|
320
|
+
options: motes.allowedSpeakers,
|
|
321
|
+
};
|
|
322
|
+
}
|
|
323
|
+
else {
|
|
324
|
+
result.diagnostics.push({
|
|
325
|
+
message: `Invalid syntax. Space required after the prefix.`,
|
|
326
|
+
...lineRange,
|
|
327
|
+
});
|
|
328
|
+
}
|
|
329
|
+
// Add emoji autocompletes
|
|
330
|
+
if (parsedLine.emojiGroup) {
|
|
331
|
+
requiresEmoji = {
|
|
332
|
+
at: changedPosition(parsedLine.emojiGroup.start, {
|
|
333
|
+
characters: 1,
|
|
334
|
+
}),
|
|
335
|
+
options: motes.emojis,
|
|
336
|
+
};
|
|
337
|
+
}
|
|
338
|
+
else {
|
|
339
|
+
result.diagnostics.push({
|
|
340
|
+
message: `Invalid syntax. Emoji required after the speaker.`,
|
|
341
|
+
...lineRange,
|
|
342
|
+
});
|
|
343
|
+
}
|
|
344
|
+
lastEmojiGroup.emotes.push({
|
|
345
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
346
|
+
speaker: parsedLine.moteTag?.value?.trim(),
|
|
347
|
+
emoji: emojiIdFromName(parsedLine.emojiName?.value),
|
|
348
|
+
});
|
|
349
|
+
}
|
|
350
|
+
else {
|
|
351
|
+
result.diagnostics.push({
|
|
352
|
+
message: `Missing an Emote declaration line!`,
|
|
353
|
+
...lineRange,
|
|
354
|
+
});
|
|
355
|
+
}
|
|
356
|
+
}
|
|
357
|
+
else if (indicator === '?') {
|
|
358
|
+
// Then this is a non-dialog quest moment. These are not implemented, but should be available as placeholders.
|
|
359
|
+
if (isQuestMomentLabel(lastSectionGroup)) {
|
|
360
|
+
// Put an autocomplete after the "?#meh " prefix
|
|
361
|
+
if (parsedLine.sep) {
|
|
362
|
+
result.completions.push({
|
|
363
|
+
type: 'momentStyles',
|
|
364
|
+
options: momentStyles,
|
|
365
|
+
start: parsedLine.sep.end,
|
|
366
|
+
end: parsedLine.sep.end,
|
|
367
|
+
});
|
|
368
|
+
}
|
|
369
|
+
else {
|
|
370
|
+
result.diagnostics.push({
|
|
371
|
+
message: `Invalid syntax. Space required after the prefix.`,
|
|
372
|
+
...lineRange,
|
|
373
|
+
});
|
|
374
|
+
}
|
|
375
|
+
// Add to data if this momentStyle actually exists
|
|
376
|
+
if (momentStyles.includes(parsedLine.style?.value)) {
|
|
377
|
+
result.parsed[lastSectionGroup].push({
|
|
378
|
+
kind: 'other',
|
|
379
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
380
|
+
style: parsedLine.style?.value,
|
|
381
|
+
});
|
|
382
|
+
}
|
|
383
|
+
else {
|
|
384
|
+
result.diagnostics.push({
|
|
385
|
+
message: `Unknown moment style "${parsedLine.style?.value}"`,
|
|
386
|
+
...lineRange,
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
}
|
|
390
|
+
else if (isQuestRequirementLabel(lastSectionGroup)) {
|
|
391
|
+
// Put an autocomplete after the "?#meh " prefix
|
|
392
|
+
if (parsedLine.sep) {
|
|
393
|
+
result.completions.push({
|
|
394
|
+
type: 'requirementStyles',
|
|
395
|
+
options: requirementCompletions,
|
|
396
|
+
start: parsedLine.sep.end,
|
|
397
|
+
end: parsedLine.sep.end,
|
|
398
|
+
});
|
|
399
|
+
}
|
|
400
|
+
else {
|
|
401
|
+
result.diagnostics.push({
|
|
402
|
+
message: `Invalid syntax. Space required after the prefix.`,
|
|
403
|
+
...lineRange,
|
|
404
|
+
});
|
|
405
|
+
}
|
|
406
|
+
const style = parsedLine.style?.value;
|
|
407
|
+
const isValidStyle = requirementStyles.includes(style);
|
|
408
|
+
const isQuest = style === 'Quest';
|
|
409
|
+
const hasValidQuestStatus = requirementQuestStatuses.includes(parsedLine.status?.value);
|
|
410
|
+
// Autocomplete Quests
|
|
411
|
+
if (isQuest) {
|
|
412
|
+
const canProvideAutocomplete = !!parsedLine.sep;
|
|
413
|
+
if (canProvideAutocomplete) {
|
|
414
|
+
requiresMote = {
|
|
415
|
+
at: parsedLine.sep.end,
|
|
416
|
+
options: motes.quests,
|
|
417
|
+
};
|
|
418
|
+
}
|
|
419
|
+
else {
|
|
420
|
+
result.diagnostics.push({
|
|
421
|
+
message: `Invalid syntax. Space required after the prefix.`,
|
|
422
|
+
...lineRange,
|
|
423
|
+
});
|
|
424
|
+
}
|
|
425
|
+
}
|
|
426
|
+
// Add to parsed data
|
|
427
|
+
if (isQuest) {
|
|
428
|
+
if (hasValidQuestStatus) {
|
|
429
|
+
result.parsed[lastSectionGroup].push({
|
|
430
|
+
kind: 'quest',
|
|
431
|
+
style: 'Quest',
|
|
432
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
433
|
+
quest: parsedLine.moteTag?.value?.trim(),
|
|
434
|
+
status: parsedLine.status?.value,
|
|
435
|
+
});
|
|
436
|
+
}
|
|
437
|
+
else {
|
|
438
|
+
result.diagnostics.push({
|
|
439
|
+
message: `Unknown quest status "${parsedLine.status?.value}"`,
|
|
440
|
+
...lineRange,
|
|
441
|
+
});
|
|
442
|
+
}
|
|
443
|
+
}
|
|
444
|
+
else if (isValidStyle) {
|
|
445
|
+
result.parsed[lastSectionGroup].push({
|
|
446
|
+
kind: 'other',
|
|
447
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
448
|
+
style: style,
|
|
449
|
+
});
|
|
450
|
+
}
|
|
451
|
+
else {
|
|
452
|
+
result.diagnostics.push({
|
|
453
|
+
message: `Unknown requirement style "${style}"`,
|
|
454
|
+
...lineRange,
|
|
455
|
+
});
|
|
456
|
+
}
|
|
457
|
+
}
|
|
458
|
+
else {
|
|
459
|
+
result.diagnostics.push({
|
|
460
|
+
message: `Must be defined in a Start/End Moments/Requirements section!`,
|
|
461
|
+
...lineRange,
|
|
462
|
+
});
|
|
463
|
+
}
|
|
464
|
+
}
|
|
465
|
+
else if (indicator === '//') {
|
|
466
|
+
// Then this is a comment/note
|
|
467
|
+
result.parsed.comments.push({
|
|
468
|
+
id: parsedLine.arrayTag?.value?.trim(),
|
|
469
|
+
text: parsedLine.text?.value?.trim(),
|
|
470
|
+
});
|
|
471
|
+
checkSpelling(parsedLine.text);
|
|
472
|
+
}
|
|
473
|
+
if (requiresEmoji) {
|
|
474
|
+
const where = {
|
|
475
|
+
start: requiresEmoji.at,
|
|
476
|
+
end: parsedLine.emojiGroup.end,
|
|
477
|
+
};
|
|
478
|
+
if (!parsedLine.emojiName?.value) {
|
|
479
|
+
result.completions.push({
|
|
480
|
+
type: 'motes',
|
|
481
|
+
options: requiresEmoji.options,
|
|
482
|
+
...where,
|
|
483
|
+
});
|
|
484
|
+
}
|
|
485
|
+
else if (!emojiIdFromName(parsedLine.emojiName?.value)) {
|
|
486
|
+
result.diagnostics.push({
|
|
487
|
+
message: `Emoji "${parsedLine.emojiName?.value}" not found!`,
|
|
488
|
+
...where,
|
|
489
|
+
});
|
|
490
|
+
}
|
|
491
|
+
}
|
|
492
|
+
if (requiresMote) {
|
|
493
|
+
if (!parsedLine.moteName || !parsedLine.moteTag) {
|
|
494
|
+
const where = {
|
|
495
|
+
start: requiresMote.at,
|
|
496
|
+
end: parsedLine.emojiGroup?.start || lineRange.end,
|
|
497
|
+
};
|
|
498
|
+
result.completions.push({
|
|
499
|
+
type: 'motes',
|
|
500
|
+
options: requiresMote.options,
|
|
501
|
+
...where,
|
|
502
|
+
});
|
|
503
|
+
if (!parsedLine.moteTag) {
|
|
504
|
+
result.diagnostics.push({
|
|
505
|
+
message: `Mote required!`,
|
|
506
|
+
...where,
|
|
507
|
+
});
|
|
508
|
+
}
|
|
509
|
+
else if (!packed.working.getMote(parsedLine.moteTag.value)) {
|
|
510
|
+
result.diagnostics.push({
|
|
511
|
+
message: `Mote not found!`,
|
|
512
|
+
...where,
|
|
513
|
+
});
|
|
514
|
+
}
|
|
515
|
+
}
|
|
516
|
+
}
|
|
517
|
+
}
|
|
518
|
+
catch (err) {
|
|
519
|
+
if (err instanceof Error) {
|
|
520
|
+
err.cause = trace;
|
|
521
|
+
}
|
|
522
|
+
throw err;
|
|
523
|
+
}
|
|
524
|
+
index += line.length;
|
|
525
|
+
}
|
|
526
|
+
return result;
|
|
527
|
+
}
|
|
528
|
+
export async function updateChangesFromParsedQuest(parsed, moteId, packed) {
|
|
529
|
+
const _traceLogs = [];
|
|
530
|
+
const trace = (log) => _traceLogs.push(log);
|
|
531
|
+
trace(`Updating mote ${moteId}`);
|
|
532
|
+
try {
|
|
533
|
+
// We're always going to be computing ALL changes, so clear whatever
|
|
534
|
+
// we previously had.
|
|
535
|
+
packed.clearMoteChanges(moteId);
|
|
536
|
+
const questMoteBase = packed.base.getMote(moteId);
|
|
537
|
+
const questMoteWorking = packed.working.getMote(moteId);
|
|
538
|
+
const schema = packed.working.getSchema('cl2_quest');
|
|
539
|
+
assert(schema.name, 'Quest mote must have a name pointer');
|
|
540
|
+
assert(schema, 'cl2_quest schema not found in working copy');
|
|
541
|
+
const updateMote = (path, value) => {
|
|
542
|
+
packed.updateMoteData(moteId, path, value);
|
|
543
|
+
};
|
|
544
|
+
updateMote('data/name', parsed.name);
|
|
545
|
+
updateMote('data/quest_giver/item', parsed.quest_giver);
|
|
546
|
+
updateMote('data/quest_receiver/item', parsed.quest_receiver);
|
|
547
|
+
updateMote('data/quest_start_log/text', parsed.quest_start_log);
|
|
548
|
+
updateMote('data/wip/draft', parsed.draft);
|
|
549
|
+
updateMote('data/storyline', parsed.storyline);
|
|
550
|
+
const parsedComments = parsed.comments.filter((c) => !!c.text);
|
|
551
|
+
const parsedClues = parsed.clues.filter((c) => !!c.id && !!c.speaker);
|
|
552
|
+
//#region COMMENTS
|
|
553
|
+
// Add/Update COMMENTS
|
|
554
|
+
trace(`Updating comments`);
|
|
555
|
+
for (const comment of parsedComments) {
|
|
556
|
+
trace(`Updating comment ${comment.id} with text "${comment.text}"`);
|
|
557
|
+
updateMote(`data/wip/comments/${comment.id}/element`, comment.text);
|
|
558
|
+
}
|
|
559
|
+
// Remove deleted comments
|
|
560
|
+
for (const existingComment of bsArrayToArray(questMoteBase?.data.wip?.comments || {})) {
|
|
561
|
+
if (!parsedComments.find((c) => c.id === existingComment.id)) {
|
|
562
|
+
trace(`Deleting comment ${existingComment.id}`);
|
|
563
|
+
updateMote(`data/wip/comments/${existingComment.id}`, null);
|
|
564
|
+
}
|
|
565
|
+
}
|
|
566
|
+
// Get the BASE order of the comments (if any) and use those
|
|
567
|
+
// as the starting point for an up to date order.
|
|
568
|
+
const comments = parsedComments.map((c) => {
|
|
569
|
+
// Look up the base comment
|
|
570
|
+
let comment = questMoteBase?.data.wip?.comments?.[c.id];
|
|
571
|
+
if (!comment) {
|
|
572
|
+
comment = questMoteWorking?.data.wip?.comments?.[c.id];
|
|
573
|
+
// @ts-expect-error - order is a required field, but it'll be re-added
|
|
574
|
+
delete comment?.order;
|
|
575
|
+
}
|
|
576
|
+
assert(comment, `Comment ${c.id} not found in base or working mote`);
|
|
577
|
+
return { ...comment, id: c.id };
|
|
578
|
+
});
|
|
579
|
+
trace('Updating comment order');
|
|
580
|
+
updateBsArrayOrder(comments);
|
|
581
|
+
comments.forEach((comment) => {
|
|
582
|
+
trace(`Updating comment ${comment.id} order to ${comment.order}`);
|
|
583
|
+
updateMote(`data/wip/comments/${comment.id}/order`, comment.order);
|
|
584
|
+
});
|
|
585
|
+
//#endregion
|
|
586
|
+
//#region CLUES
|
|
587
|
+
// Add/update clues
|
|
588
|
+
trace(`Updating clues`);
|
|
589
|
+
for (const clue of parsedClues) {
|
|
590
|
+
trace(`Setting clue ${clue.id} speaker to "${clue.speaker}"`);
|
|
591
|
+
updateMote(`data/clues/${clue.id}/element/speaker`, clue.speaker);
|
|
592
|
+
for (const phrase of clue.phrases) {
|
|
593
|
+
trace(`Setting phrase ${phrase.id} text to "${phrase.text}"`);
|
|
594
|
+
updateMote(`data/clues/${clue.id}/element/phrases/${phrase.id}/element/phrase/text/text`, phrase.text || '');
|
|
595
|
+
if (phrase.emoji) {
|
|
596
|
+
trace(`Setting phrase ${phrase.id} emoji to "${phrase.emoji}"`);
|
|
597
|
+
updateMote(`data/clues/${clue.id}/element/phrases/${phrase.id}/element/phrase/emoji`, phrase.emoji);
|
|
598
|
+
}
|
|
599
|
+
}
|
|
600
|
+
}
|
|
601
|
+
// Delete clues that were removed
|
|
602
|
+
trace(`Deleting removed clues`);
|
|
603
|
+
for (const existingClue of bsArrayToArray(questMoteBase?.data.clues || {})) {
|
|
604
|
+
const parsedClue = parsedClues.find((c) => c.id === existingClue.id);
|
|
605
|
+
if (!parsedClue) {
|
|
606
|
+
trace(`Deleting clue ${existingClue.id}`);
|
|
607
|
+
updateMote(`data/clues/${existingClue.id}`, null);
|
|
608
|
+
}
|
|
609
|
+
else {
|
|
610
|
+
// Delete phrases that were removed
|
|
611
|
+
for (const existingPhrase of bsArrayToArray(existingClue.element.phrases)) {
|
|
612
|
+
if (!parsedClue.phrases.find((p) => p.id === existingPhrase.id)) {
|
|
613
|
+
trace(`Deleting phrase ${existingPhrase.id} from clue ${existingClue.id}`);
|
|
614
|
+
updateMote(`data/clues/${existingClue.id}/element/phrases/${existingPhrase.id}`, null);
|
|
615
|
+
}
|
|
616
|
+
}
|
|
617
|
+
}
|
|
618
|
+
}
|
|
619
|
+
// Update the order of the clues and phrases
|
|
620
|
+
trace(`Updating clue order`);
|
|
621
|
+
const clues = parsedClues.map((c) => {
|
|
622
|
+
// Look up the base clue
|
|
623
|
+
let clue = questMoteBase?.data.clues?.[c.id];
|
|
624
|
+
if (!clue) {
|
|
625
|
+
clue = questMoteWorking?.data.clues?.[c.id];
|
|
626
|
+
// @ts-expect-error - order is a required field, but it'll be re-added
|
|
627
|
+
delete clue?.order;
|
|
628
|
+
}
|
|
629
|
+
assert(clue, `Clue ${c.id} not found in base or working mote`);
|
|
630
|
+
const phrases = c.phrases.map((p) => {
|
|
631
|
+
let phrase = questMoteBase?.data.clues?.[c.id]?.element?.phrases?.[p.id];
|
|
632
|
+
if (!phrase) {
|
|
633
|
+
phrase =
|
|
634
|
+
questMoteWorking?.data.clues?.[c.id]?.element?.phrases?.[p.id];
|
|
635
|
+
// @ts-expect-error - order is a required field, but it'll be re-added
|
|
636
|
+
delete phrase?.order;
|
|
637
|
+
}
|
|
638
|
+
assert(phrase, `Phrase ${p.id} not found in base or working mote`);
|
|
639
|
+
return { ...phrase, id: p.id };
|
|
640
|
+
});
|
|
641
|
+
trace(`Updating phrase order for clue ${c.id}`);
|
|
642
|
+
updateBsArrayOrder(phrases);
|
|
643
|
+
return { ...clue, phrases, id: c.id };
|
|
644
|
+
});
|
|
645
|
+
updateBsArrayOrder(clues);
|
|
646
|
+
clues.forEach((clue) => {
|
|
647
|
+
trace(`Updating clue ${clue.id} order to ${clue.order}`);
|
|
648
|
+
updateMote(`data/clues/${clue.id}/order`, clue.order);
|
|
649
|
+
clue.phrases.forEach((phrase) => {
|
|
650
|
+
trace(`Updating phrase ${phrase.id} order to ${phrase.order}`);
|
|
651
|
+
updateMote(`data/clues/${clue.id}/element/phrases/${phrase.id}/order`, phrase.order);
|
|
652
|
+
});
|
|
653
|
+
});
|
|
654
|
+
//#endregion
|
|
655
|
+
for (const requirementGroup of [
|
|
656
|
+
'quest_start_requirements',
|
|
657
|
+
'quest_end_requirements',
|
|
658
|
+
]) {
|
|
659
|
+
trace(`Updating Requirement Group ${requirementGroup}`);
|
|
660
|
+
const parsedRequirements = parsed[requirementGroup];
|
|
661
|
+
// Add/Update requirements
|
|
662
|
+
trace('Adding/updating requirements');
|
|
663
|
+
for (const requirement of parsedRequirements) {
|
|
664
|
+
trace(`Updating requirement ${requirement.id}`);
|
|
665
|
+
updateMote(`data/${requirementGroup}/${requirement.id}/element/style`, requirement.style);
|
|
666
|
+
if (requirement.kind === 'quest') {
|
|
667
|
+
updateMote(`data/${requirementGroup}/${requirement.id}/element/quest`, requirement.quest);
|
|
668
|
+
updateMote(`data/${requirementGroup}/${requirement.id}/element/quest_status`, requirement.status);
|
|
669
|
+
}
|
|
670
|
+
}
|
|
671
|
+
// Delete requirements that were removed
|
|
672
|
+
trace('Deleting removed requirements');
|
|
673
|
+
for (const existingRequirement of bsArrayToArray(questMoteBase?.data[requirementGroup] || {})) {
|
|
674
|
+
const parsedRequirement = parsedRequirements.find((r) => r.id === existingRequirement.id);
|
|
675
|
+
if (!parsedRequirement) {
|
|
676
|
+
trace(`Deleting removed requirement ${existingRequirement.id}`);
|
|
677
|
+
updateMote(`data/${requirementGroup}/${existingRequirement.id}`, null);
|
|
678
|
+
}
|
|
679
|
+
}
|
|
680
|
+
// Update the requirement order
|
|
681
|
+
trace('Updating requirement order');
|
|
682
|
+
const requirements = parsedRequirements.map((r) => {
|
|
683
|
+
let requirement = questMoteBase?.data[requirementGroup]?.[r.id];
|
|
684
|
+
if (!requirement) {
|
|
685
|
+
requirement = questMoteWorking?.data[requirementGroup]?.[r.id];
|
|
686
|
+
// @ts-expect-error - order is a required field, but it'll be re-added
|
|
687
|
+
delete requirement?.order;
|
|
688
|
+
}
|
|
689
|
+
assert(requirement, `Requirement ${r.id} not found in base or working mote`);
|
|
690
|
+
return { ...requirement, id: r.id };
|
|
691
|
+
});
|
|
692
|
+
updateBsArrayOrder(requirements);
|
|
693
|
+
requirements.forEach((r) => {
|
|
694
|
+
trace(`Updating requirement ${r.id} order to ${r.order}`);
|
|
695
|
+
updateMote(`data/${requirementGroup}/${r.id}/order`, r.order);
|
|
696
|
+
});
|
|
697
|
+
}
|
|
698
|
+
//#region QUEST MOMENTS
|
|
699
|
+
for (const momentGroup of [
|
|
700
|
+
'quest_start_moments',
|
|
701
|
+
'quest_end_moments',
|
|
702
|
+
]) {
|
|
703
|
+
trace(`Updating Moment Group ${momentGroup}`);
|
|
704
|
+
const parsedMoments = parsed[momentGroup];
|
|
705
|
+
// Add/Update moments
|
|
706
|
+
trace('Adding/updating moments');
|
|
707
|
+
for (const moment of parsedMoments) {
|
|
708
|
+
trace(`Updating moment ${moment.id} of kind ${moment.kind}`);
|
|
709
|
+
const setStyle = (style) => updateMote(`data/${momentGroup}/${moment.id}/element/style`, style);
|
|
710
|
+
if (moment.kind === 'other') {
|
|
711
|
+
// Note: we're only tracking style for moment types that
|
|
712
|
+
// are not fully implemented.
|
|
713
|
+
setStyle(moment.style);
|
|
714
|
+
}
|
|
715
|
+
else if (moment.kind === 'dialogue') {
|
|
716
|
+
trace('Updating speaker');
|
|
717
|
+
setStyle('Dialogue');
|
|
718
|
+
updateMote(`data/${momentGroup}/${moment.id}/element/speech/speaker`, moment.speaker);
|
|
719
|
+
trace('Updating emoji');
|
|
720
|
+
updateMote(`data/${momentGroup}/${moment.id}/element/speech/emotion`, moment.emoji);
|
|
721
|
+
trace('Updating text');
|
|
722
|
+
updateMote(`data/${momentGroup}/${moment.id}/element/speech/text/text`, moment.text);
|
|
723
|
+
}
|
|
724
|
+
else if (moment.kind === 'emote') {
|
|
725
|
+
setStyle('Emote');
|
|
726
|
+
for (const emote of moment.emotes) {
|
|
727
|
+
updateMote(`data/${momentGroup}/${moment.id}/element/emotes/${emote.id}/element/key`, emote.speaker);
|
|
728
|
+
updateMote(`data/${momentGroup}/${moment.id}/element/emotes/${emote.id}/element/value`, emote.emoji);
|
|
729
|
+
}
|
|
730
|
+
}
|
|
731
|
+
trace(`Updated moment ${moment.id}`);
|
|
732
|
+
}
|
|
733
|
+
// Delete moments that were removed
|
|
734
|
+
trace('Deleting removed moments');
|
|
735
|
+
for (const existingMoment of bsArrayToArray(questMoteBase?.data[momentGroup] || {})) {
|
|
736
|
+
const parsedMoment = parsedMoments.find((m) => m.id === existingMoment.id);
|
|
737
|
+
const existingElement = existingMoment.element;
|
|
738
|
+
if (!parsedMoment) {
|
|
739
|
+
trace(`Deleting removed moment ${existingMoment.id}`);
|
|
740
|
+
updateMote(`data/${momentGroup}/${existingMoment.id}`, null);
|
|
741
|
+
}
|
|
742
|
+
else if (existingElement.style === 'Emote') {
|
|
743
|
+
// Delete emotes that were removed
|
|
744
|
+
trace(`Deleting removed emotes from moment ${existingMoment.id}`);
|
|
745
|
+
assert(parsedMoment.kind === 'emote', `Expected moment ${existingMoment.id} to be an emote`);
|
|
746
|
+
for (const existingEmote of bsArrayToArray(existingElement.emotes)) {
|
|
747
|
+
if (!parsedMoment.emotes.find((e) => e.id === existingEmote.id)) {
|
|
748
|
+
trace(`Deleting removed emote ${existingEmote.id} from moment ${existingMoment.id}`);
|
|
749
|
+
updateMote(`data/${momentGroup}/${existingMoment.id}/element/emotes/${existingEmote.id}`, null);
|
|
750
|
+
}
|
|
751
|
+
}
|
|
752
|
+
}
|
|
753
|
+
}
|
|
754
|
+
// Update the order of the moments
|
|
755
|
+
trace('Updating moment order');
|
|
756
|
+
const moments = parsedMoments.map((m) => {
|
|
757
|
+
// Look up the base moment
|
|
758
|
+
let moment = questMoteBase?.data[momentGroup]?.[m.id];
|
|
759
|
+
if (!moment) {
|
|
760
|
+
moment = questMoteWorking?.data[momentGroup]?.[m.id];
|
|
761
|
+
// @ts-expect-error - order is a required field, but it'll be re-added
|
|
762
|
+
delete moment?.order;
|
|
763
|
+
}
|
|
764
|
+
assert(moment, `Moment ${m.id} not found in base or working mote`);
|
|
765
|
+
moment.element.style;
|
|
766
|
+
const element = moment.element;
|
|
767
|
+
if (element.style === 'Emote') {
|
|
768
|
+
assert(m.kind === 'emote', `Expected moment ${m.id} to be an emote`);
|
|
769
|
+
// Then make sure the emotes are in the right order
|
|
770
|
+
const emotes = m.emotes.map((e) => {
|
|
771
|
+
let emoteElement = questMoteBase?.data[momentGroup]?.[m.id]?.element;
|
|
772
|
+
let emote;
|
|
773
|
+
if (emoteElement && isEmoteMoment(emoteElement)) {
|
|
774
|
+
emote = emoteElement.emotes[e.id];
|
|
775
|
+
}
|
|
776
|
+
if (!emote) {
|
|
777
|
+
emoteElement =
|
|
778
|
+
questMoteWorking?.data[momentGroup]?.[m.id]?.element;
|
|
779
|
+
if (emoteElement && isEmoteMoment(emoteElement)) {
|
|
780
|
+
emote = emoteElement.emotes[e.id];
|
|
781
|
+
}
|
|
782
|
+
// Then we don't need to try to keep a prior order value
|
|
783
|
+
delete emote?.order;
|
|
784
|
+
}
|
|
785
|
+
assert(emote, `Emote ${e.id} not found in base or working mote`);
|
|
786
|
+
return { ...emote, id: e.id };
|
|
787
|
+
});
|
|
788
|
+
trace(`Updating emote order for moment ${m.id}`);
|
|
789
|
+
updateBsArrayOrder(emotes);
|
|
790
|
+
return { ...moment, emotes, id: m.id };
|
|
791
|
+
}
|
|
792
|
+
return { ...moment, id: m.id };
|
|
793
|
+
});
|
|
794
|
+
updateBsArrayOrder(moments);
|
|
795
|
+
moments.forEach((m) => {
|
|
796
|
+
trace(`Updating moment ${m.id} order to ${m.order}`);
|
|
797
|
+
updateMote(`data/${momentGroup}/${m.id}/order`, m.order);
|
|
798
|
+
if ('emotes' in m) {
|
|
799
|
+
m.emotes.forEach((e) => {
|
|
800
|
+
trace(`Updating emote ${e.id} order to ${e.order}`);
|
|
801
|
+
updateMote(`data/${momentGroup}/${m.id}/element/emotes/${e.id}/order`, e.order);
|
|
802
|
+
});
|
|
803
|
+
}
|
|
804
|
+
});
|
|
805
|
+
}
|
|
806
|
+
//#endregion
|
|
807
|
+
trace(`Writing changes`);
|
|
808
|
+
await packed.writeChanges();
|
|
809
|
+
}
|
|
810
|
+
catch (err) {
|
|
811
|
+
console.error(err);
|
|
812
|
+
console.error(_traceLogs);
|
|
813
|
+
if (err instanceof Error) {
|
|
814
|
+
err.cause = _traceLogs;
|
|
815
|
+
}
|
|
816
|
+
throw err;
|
|
817
|
+
}
|
|
818
|
+
}
|
|
819
|
+
function isQuestRequirementLabel(label) {
|
|
820
|
+
return !!(label?.startsWith('quest_') && label.endsWith('_requirements'));
|
|
821
|
+
}
|
|
822
|
+
function isQuestMomentLabel(label) {
|
|
823
|
+
return !!(label?.startsWith('quest_') && label.endsWith('_moments'));
|
|
824
|
+
}
|
|
825
|
+
//# sourceMappingURL=cl2.quest.parse.js.map
|