@gbl-uzh/platform 0.4.13 → 0.4.16
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 +1 -1
- package/dist/index.d.ts +7 -1473
- package/dist/index.d.ts.map +1 -0
- package/dist/index.js +11 -2012
- package/dist/index.js.map +1 -0
- package/dist/lib/SSELink.d.ts +9 -0
- package/dist/lib/SSELink.d.ts.map +1 -0
- package/dist/lib/SSELink.js +24 -0
- package/dist/lib/SSELink.js.map +1 -0
- package/dist/lib/apollo.d.ts +3 -4
- package/dist/lib/apollo.d.ts.map +1 -0
- package/dist/lib/apollo.js +50 -108
- package/dist/lib/apollo.js.map +1 -0
- package/dist/lib/constants.d.ts +8 -0
- package/dist/lib/constants.d.ts.map +1 -0
- package/dist/lib/logger.d.ts +4 -0
- package/dist/lib/logger.d.ts.map +1 -0
- package/dist/lib/logger.js +11 -0
- package/dist/lib/logger.js.map +1 -0
- package/dist/lib/pubsub.d.ts +5 -0
- package/dist/lib/pubsub.d.ts.map +1 -0
- package/dist/lib/pubsub.js +6 -0
- package/dist/lib/pubsub.js.map +1 -0
- package/dist/lib/util.d.ts +12 -13
- package/dist/lib/util.d.ts.map +1 -0
- package/dist/lib/util.js +63 -105
- package/dist/lib/util.js.map +1 -0
- package/dist/nexus.d.ts +11 -42
- package/dist/nexus.d.ts.map +1 -0
- package/dist/nexus.js +22 -2605
- package/dist/nexus.js.map +1 -0
- package/dist/ops/FGameData.graphql +1 -0
- package/dist/ops/FPeriodData.graphql +1 -0
- package/dist/ops/FPlayerData.graphql +0 -4
- package/dist/ops/FSegmentData.graphql +1 -0
- package/dist/ops/FStoryElementData.graphql +7 -0
- package/dist/ops/MActivateNextSegment.graphql +0 -3
- package/dist/ops/MAddGamePeriod.graphql +2 -2
- package/dist/ops/MUpdatePlayerData.graphql +1 -3
- package/dist/ops/QGame.graphql +2 -3
- package/dist/ops/QPastResults.graphql +0 -3
- package/dist/ops/QResult.graphql +4 -5
- package/dist/ops/QSpecificResults.graphql +11 -0
- package/dist/ops/QStoryElements.graphql +7 -0
- package/dist/schema.prisma +2 -3
- package/dist/services/AccountService.d.ts +65 -0
- package/dist/services/AccountService.d.ts.map +1 -0
- package/dist/services/AccountService.js +70 -0
- package/dist/services/AccountService.js.map +1 -0
- package/dist/services/EventService.d.ts +13 -0
- package/dist/services/EventService.d.ts.map +1 -0
- package/dist/services/EventService.js +180 -0
- package/dist/services/EventService.js.map +1 -0
- package/dist/services/GameService.d.ts +670 -0
- package/dist/services/GameService.d.ts.map +1 -0
- package/dist/services/GameService.js +1191 -0
- package/dist/services/GameService.js.map +1 -0
- package/dist/services/PlayService.d.ts +630 -0
- package/dist/services/PlayService.d.ts.map +1 -0
- package/dist/services/PlayService.js +534 -0
- package/dist/services/PlayService.js.map +1 -0
- package/dist/tsconfig.tsbuildinfo +1 -0
- package/dist/types/Achievement.d.ts +4 -0
- package/dist/types/Achievement.d.ts.map +1 -0
- package/dist/types/Achievement.js +35 -0
- package/dist/types/Achievement.js.map +1 -0
- package/dist/types/Game.d.ts +5 -0
- package/dist/types/Game.d.ts.map +1 -0
- package/dist/types/Game.js +85 -0
- package/dist/types/Game.js.map +1 -0
- package/dist/types/LearningElement.d.ts +5 -0
- package/dist/types/LearningElement.d.ts.map +1 -0
- package/dist/types/LearningElement.js +55 -0
- package/dist/types/LearningElement.js.map +1 -0
- package/dist/types/Mutation.d.ts +10 -0
- package/dist/types/Mutation.d.ts.map +1 -0
- package/dist/types/Mutation.js +194 -0
- package/dist/types/Mutation.js.map +1 -0
- package/dist/types/Player.d.ts +9 -0
- package/dist/types/Player.d.ts.map +1 -0
- package/dist/types/Player.js +139 -0
- package/dist/types/Player.js.map +1 -0
- package/dist/types/Query.d.ts +3 -0
- package/dist/types/Query.d.ts.map +1 -0
- package/dist/types/Query.js +91 -0
- package/dist/types/Query.js.map +1 -0
- package/dist/types/StoryElement.d.ts +3 -0
- package/dist/types/StoryElement.d.ts.map +1 -0
- package/dist/types/StoryElement.js +27 -0
- package/dist/types/StoryElement.js.map +1 -0
- package/dist/types/Subscription.d.ts +3 -0
- package/dist/types/Subscription.d.ts.map +1 -0
- package/dist/types/Subscription.js +35 -0
- package/dist/types/Subscription.js.map +1 -0
- package/dist/types.d.ts +139 -0
- package/dist/types.d.ts.map +1 -0
- package/dist/types.js +26 -0
- package/dist/types.js.map +1 -0
- package/package.json +42 -38
|
@@ -0,0 +1,1191 @@
|
|
|
1
|
+
import * as DB from '@prisma/client';
|
|
2
|
+
import { nanoid } from 'nanoid';
|
|
3
|
+
import { repeat, none } from 'ramda';
|
|
4
|
+
import logger from '../lib/logger.js';
|
|
5
|
+
import { receiveEvents } from './EventService.js';
|
|
6
|
+
|
|
7
|
+
async function createGame({ name, playerCount }, ctx, { roleAssigner }) {
|
|
8
|
+
return ctx.prisma.game.create({
|
|
9
|
+
data: {
|
|
10
|
+
name,
|
|
11
|
+
owner: {
|
|
12
|
+
connect: {
|
|
13
|
+
id: ctx.user.sub,
|
|
14
|
+
},
|
|
15
|
+
},
|
|
16
|
+
players: {
|
|
17
|
+
create: repeat(0, playerCount).map((_, ix) => {
|
|
18
|
+
return {
|
|
19
|
+
facts: {},
|
|
20
|
+
token: nanoid(),
|
|
21
|
+
role: roleAssigner ? roleAssigner(ix) : undefined,
|
|
22
|
+
number: playerCount - ix,
|
|
23
|
+
name: `Team ${playerCount - ix}`,
|
|
24
|
+
level: {
|
|
25
|
+
connect: {
|
|
26
|
+
index: 0,
|
|
27
|
+
},
|
|
28
|
+
},
|
|
29
|
+
};
|
|
30
|
+
}),
|
|
31
|
+
},
|
|
32
|
+
},
|
|
33
|
+
include: {
|
|
34
|
+
players: true,
|
|
35
|
+
periods: true,
|
|
36
|
+
},
|
|
37
|
+
});
|
|
38
|
+
}
|
|
39
|
+
async function addGamePeriod({ gameId, facts, segmentCount }, ctx, { schema, services }) {
|
|
40
|
+
const validatedFacts = schema.validateSync(facts);
|
|
41
|
+
const game = await ctx.prisma.game.findUnique({
|
|
42
|
+
where: {
|
|
43
|
+
id: gameId,
|
|
44
|
+
},
|
|
45
|
+
include: {
|
|
46
|
+
periods: {
|
|
47
|
+
orderBy: {
|
|
48
|
+
index: 'desc',
|
|
49
|
+
},
|
|
50
|
+
take: 1,
|
|
51
|
+
include: {
|
|
52
|
+
segments: {
|
|
53
|
+
orderBy: {
|
|
54
|
+
index: 'desc',
|
|
55
|
+
},
|
|
56
|
+
take: 1,
|
|
57
|
+
},
|
|
58
|
+
},
|
|
59
|
+
},
|
|
60
|
+
},
|
|
61
|
+
});
|
|
62
|
+
if (!game)
|
|
63
|
+
return null;
|
|
64
|
+
const index = (game.periods[0]?.index ?? -1) + 1;
|
|
65
|
+
// TODO(JJ): Why do we provide validatedFacts twice?
|
|
66
|
+
// - remove periodFacts from payload for initialize?
|
|
67
|
+
const { resultFacts: initializedFacts } = services.Period.initialize(validatedFacts, {
|
|
68
|
+
// TODO(JJ): replace with undefined
|
|
69
|
+
// At RS: If we replace validatedFacts with periodFacts the
|
|
70
|
+
// Derivative Game will be broken, as it computes the trend from
|
|
71
|
+
// periodFacts instead of the first arguement
|
|
72
|
+
periodFacts: validatedFacts,
|
|
73
|
+
previousPeriodFacts: game.periods[0]?.facts,
|
|
74
|
+
previousSegmentFacts: game.periods[0]?.segments[0]?.facts,
|
|
75
|
+
periodIx: index,
|
|
76
|
+
});
|
|
77
|
+
console.log(game.periods[0]?.facts, game.periods[0]?.segments[0]?.facts, initializedFacts);
|
|
78
|
+
// create or update the facts and settings of a game period
|
|
79
|
+
return ctx.prisma.period.upsert({
|
|
80
|
+
where: {
|
|
81
|
+
gameId_index: {
|
|
82
|
+
gameId,
|
|
83
|
+
index,
|
|
84
|
+
},
|
|
85
|
+
},
|
|
86
|
+
create: {
|
|
87
|
+
index,
|
|
88
|
+
facts: initializedFacts,
|
|
89
|
+
game: {
|
|
90
|
+
connect: {
|
|
91
|
+
id: gameId,
|
|
92
|
+
},
|
|
93
|
+
},
|
|
94
|
+
segmentCount,
|
|
95
|
+
previousPeriod: {
|
|
96
|
+
connect: index > 0
|
|
97
|
+
? {
|
|
98
|
+
gameId_index: {
|
|
99
|
+
gameId,
|
|
100
|
+
index: index - 1,
|
|
101
|
+
},
|
|
102
|
+
}
|
|
103
|
+
: [],
|
|
104
|
+
},
|
|
105
|
+
},
|
|
106
|
+
update: {
|
|
107
|
+
facts: initializedFacts,
|
|
108
|
+
},
|
|
109
|
+
include: {
|
|
110
|
+
segments: {
|
|
111
|
+
include: {
|
|
112
|
+
learningElements: true,
|
|
113
|
+
storyElements: true,
|
|
114
|
+
},
|
|
115
|
+
},
|
|
116
|
+
},
|
|
117
|
+
});
|
|
118
|
+
}
|
|
119
|
+
async function addPeriodSegment({ gameId, periodIx, facts, learningElements, storyElements, }, ctx, { schema, services }) {
|
|
120
|
+
const validatedFacts = schema.validateSync(facts);
|
|
121
|
+
const period = await ctx.prisma.period.findUnique({
|
|
122
|
+
where: {
|
|
123
|
+
gameId_index: {
|
|
124
|
+
gameId,
|
|
125
|
+
index: periodIx,
|
|
126
|
+
},
|
|
127
|
+
},
|
|
128
|
+
include: {
|
|
129
|
+
segments: {
|
|
130
|
+
orderBy: {
|
|
131
|
+
index: 'desc',
|
|
132
|
+
},
|
|
133
|
+
take: 1,
|
|
134
|
+
},
|
|
135
|
+
},
|
|
136
|
+
});
|
|
137
|
+
if (!period)
|
|
138
|
+
return null;
|
|
139
|
+
const index = (period.segments[0]?.index ?? -1) + 1;
|
|
140
|
+
const { resultFacts: initializedFacts } = services.Segment.initialize(validatedFacts, {
|
|
141
|
+
periodFacts: period.facts,
|
|
142
|
+
previousSegmentFacts: period.segments[0]?.facts,
|
|
143
|
+
segmentIx: index,
|
|
144
|
+
segmentCount: period.segmentCount,
|
|
145
|
+
periodIx,
|
|
146
|
+
});
|
|
147
|
+
// create or update the facts and settings of a period segment
|
|
148
|
+
return ctx.prisma.periodSegment.upsert({
|
|
149
|
+
where: {
|
|
150
|
+
gameId_periodIx_index: {
|
|
151
|
+
gameId,
|
|
152
|
+
periodIx,
|
|
153
|
+
index,
|
|
154
|
+
},
|
|
155
|
+
},
|
|
156
|
+
create: {
|
|
157
|
+
index,
|
|
158
|
+
facts: initializedFacts,
|
|
159
|
+
learningElements: {
|
|
160
|
+
connect: learningElements
|
|
161
|
+
? learningElements.map((item) => ({ id: item }))
|
|
162
|
+
: [],
|
|
163
|
+
},
|
|
164
|
+
storyElements: {
|
|
165
|
+
connect: storyElements
|
|
166
|
+
? storyElements.map((item) => ({ id: item }))
|
|
167
|
+
: [],
|
|
168
|
+
},
|
|
169
|
+
game: {
|
|
170
|
+
connect: {
|
|
171
|
+
id: gameId,
|
|
172
|
+
},
|
|
173
|
+
},
|
|
174
|
+
periodIx: periodIx,
|
|
175
|
+
period: {
|
|
176
|
+
connect: {
|
|
177
|
+
gameId_index: {
|
|
178
|
+
gameId,
|
|
179
|
+
index: periodIx,
|
|
180
|
+
},
|
|
181
|
+
},
|
|
182
|
+
},
|
|
183
|
+
previousSegment: {
|
|
184
|
+
connect: index > 0
|
|
185
|
+
? {
|
|
186
|
+
gameId_periodIx_index: {
|
|
187
|
+
gameId,
|
|
188
|
+
periodIx,
|
|
189
|
+
index: index - 1,
|
|
190
|
+
},
|
|
191
|
+
}
|
|
192
|
+
: [],
|
|
193
|
+
},
|
|
194
|
+
},
|
|
195
|
+
update: {
|
|
196
|
+
facts: initializedFacts,
|
|
197
|
+
learningElements: {
|
|
198
|
+
connect: learningElements
|
|
199
|
+
? learningElements.map((item) => ({ id: item }))
|
|
200
|
+
: [],
|
|
201
|
+
},
|
|
202
|
+
storyElements: {
|
|
203
|
+
connect: storyElements
|
|
204
|
+
? storyElements.map((item) => ({ id: item }))
|
|
205
|
+
: [],
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
include: {
|
|
209
|
+
learningElements: true,
|
|
210
|
+
storyElements: true,
|
|
211
|
+
},
|
|
212
|
+
});
|
|
213
|
+
}
|
|
214
|
+
async function activateNextPeriod({ gameId }, ctx, { services }) {
|
|
215
|
+
logger.info('activating next period');
|
|
216
|
+
// get the current game and as well as the results of the initially active period
|
|
217
|
+
// these will be used by the model to compute the starting situation of the next period
|
|
218
|
+
const game = await ctx.prisma.game.findUnique({
|
|
219
|
+
where: {
|
|
220
|
+
id: gameId,
|
|
221
|
+
},
|
|
222
|
+
include: {
|
|
223
|
+
players: true,
|
|
224
|
+
periods: true,
|
|
225
|
+
results: {
|
|
226
|
+
where: {
|
|
227
|
+
type: 'SEGMENT_END',
|
|
228
|
+
},
|
|
229
|
+
orderBy: [{ period: { index: 'asc' } }, { segment: { index: 'asc' } }],
|
|
230
|
+
},
|
|
231
|
+
activePeriod: {
|
|
232
|
+
include: {
|
|
233
|
+
results: {
|
|
234
|
+
include: {
|
|
235
|
+
player: true,
|
|
236
|
+
},
|
|
237
|
+
},
|
|
238
|
+
nextPeriod: true,
|
|
239
|
+
previousPeriod: {
|
|
240
|
+
include: {
|
|
241
|
+
results: {
|
|
242
|
+
include: {
|
|
243
|
+
player: true,
|
|
244
|
+
},
|
|
245
|
+
},
|
|
246
|
+
},
|
|
247
|
+
},
|
|
248
|
+
activeSegment: {
|
|
249
|
+
include: {
|
|
250
|
+
results: {
|
|
251
|
+
include: {
|
|
252
|
+
player: true,
|
|
253
|
+
},
|
|
254
|
+
},
|
|
255
|
+
},
|
|
256
|
+
},
|
|
257
|
+
decisions: {
|
|
258
|
+
include: {
|
|
259
|
+
player: true,
|
|
260
|
+
},
|
|
261
|
+
},
|
|
262
|
+
},
|
|
263
|
+
},
|
|
264
|
+
},
|
|
265
|
+
});
|
|
266
|
+
if (!game)
|
|
267
|
+
return null;
|
|
268
|
+
logger.info(`game found, status ${game.status}`);
|
|
269
|
+
const currentPeriodIx = game.activePeriodIx;
|
|
270
|
+
const currentSegmentIx = game.activePeriod?.activeSegmentIx;
|
|
271
|
+
const nextPeriodIx = currentPeriodIx + 1;
|
|
272
|
+
// NotificationService.publishGlobalNotification({
|
|
273
|
+
// type: GlobalNotificationType.PERIOD_ACTIVATED,
|
|
274
|
+
// })
|
|
275
|
+
switch (game.status) {
|
|
276
|
+
// SCHEDULED -> PREPARATION
|
|
277
|
+
// if the game is scheduled, initialize period results and move to PREPARATION
|
|
278
|
+
case DB.GameStatus.SCHEDULED: {
|
|
279
|
+
const { results, extras } = computePeriodStartResults({
|
|
280
|
+
results: undefined,
|
|
281
|
+
players: game.players,
|
|
282
|
+
activePeriodIx: currentPeriodIx,
|
|
283
|
+
gameId: game.id,
|
|
284
|
+
periodFacts: game.periods?.[0]?.facts,
|
|
285
|
+
}, ctx, { services });
|
|
286
|
+
// update the status and active period of the current game
|
|
287
|
+
// and prepare PERIOD_START results
|
|
288
|
+
const result = await ctx.prisma.$transaction([
|
|
289
|
+
ctx.prisma.game.update({
|
|
290
|
+
where: {
|
|
291
|
+
id: gameId,
|
|
292
|
+
},
|
|
293
|
+
include: {
|
|
294
|
+
periods: {
|
|
295
|
+
include: {
|
|
296
|
+
segments: true,
|
|
297
|
+
},
|
|
298
|
+
},
|
|
299
|
+
},
|
|
300
|
+
data: {
|
|
301
|
+
status: DB.GameStatus.PREPARATION,
|
|
302
|
+
activePeriodIx: nextPeriodIx,
|
|
303
|
+
activePeriod: {
|
|
304
|
+
connect: {
|
|
305
|
+
gameId_index: {
|
|
306
|
+
gameId,
|
|
307
|
+
index: nextPeriodIx,
|
|
308
|
+
},
|
|
309
|
+
},
|
|
310
|
+
},
|
|
311
|
+
},
|
|
312
|
+
}),
|
|
313
|
+
ctx.prisma.period.update({
|
|
314
|
+
where: {
|
|
315
|
+
gameId_index: {
|
|
316
|
+
gameId,
|
|
317
|
+
index: nextPeriodIx,
|
|
318
|
+
},
|
|
319
|
+
},
|
|
320
|
+
data: {
|
|
321
|
+
results: {
|
|
322
|
+
create: results,
|
|
323
|
+
},
|
|
324
|
+
},
|
|
325
|
+
}),
|
|
326
|
+
...extras,
|
|
327
|
+
]);
|
|
328
|
+
return result;
|
|
329
|
+
}
|
|
330
|
+
// RUNNING -> CONSOLIDATION
|
|
331
|
+
// if the final segment is running, go on with consolidation of the period
|
|
332
|
+
// compute the results of the last segment and update the game status
|
|
333
|
+
case DB.GameStatus.RUNNING: {
|
|
334
|
+
if (!game.activePeriod?.activeSegment || !currentSegmentIx)
|
|
335
|
+
return null;
|
|
336
|
+
const { results, extras } = computeSegmentEndResults(game, ctx, {
|
|
337
|
+
services,
|
|
338
|
+
});
|
|
339
|
+
// update period facts when starting consolidation
|
|
340
|
+
const { resultFacts: consolidatedFacts } = services.Period.consolidate(game.activePeriod.facts, {
|
|
341
|
+
previousSegmentFacts: game.activePeriod.activeSegment.facts,
|
|
342
|
+
periodIx: currentPeriodIx,
|
|
343
|
+
});
|
|
344
|
+
const result = await ctx.prisma.$transaction([
|
|
345
|
+
ctx.prisma.game.update({
|
|
346
|
+
data: {
|
|
347
|
+
status: DB.GameStatus.CONSOLIDATION,
|
|
348
|
+
},
|
|
349
|
+
include: {
|
|
350
|
+
periods: {
|
|
351
|
+
include: {
|
|
352
|
+
segments: true,
|
|
353
|
+
},
|
|
354
|
+
},
|
|
355
|
+
},
|
|
356
|
+
where: {
|
|
357
|
+
id: gameId,
|
|
358
|
+
},
|
|
359
|
+
}),
|
|
360
|
+
ctx.prisma.period.update({
|
|
361
|
+
where: {
|
|
362
|
+
gameId_index: {
|
|
363
|
+
gameId,
|
|
364
|
+
index: currentPeriodIx,
|
|
365
|
+
},
|
|
366
|
+
},
|
|
367
|
+
data: {
|
|
368
|
+
facts: consolidatedFacts,
|
|
369
|
+
},
|
|
370
|
+
}),
|
|
371
|
+
ctx.prisma.periodSegment.update({
|
|
372
|
+
where: {
|
|
373
|
+
gameId_periodIx_index: {
|
|
374
|
+
gameId,
|
|
375
|
+
periodIx: currentPeriodIx,
|
|
376
|
+
index: currentSegmentIx,
|
|
377
|
+
},
|
|
378
|
+
},
|
|
379
|
+
data: {
|
|
380
|
+
results: {
|
|
381
|
+
// compute SEGMENT_END results using model
|
|
382
|
+
update: results,
|
|
383
|
+
},
|
|
384
|
+
},
|
|
385
|
+
include: {
|
|
386
|
+
results: {
|
|
387
|
+
include: {
|
|
388
|
+
player: true,
|
|
389
|
+
},
|
|
390
|
+
},
|
|
391
|
+
},
|
|
392
|
+
}),
|
|
393
|
+
...extras,
|
|
394
|
+
]);
|
|
395
|
+
return result;
|
|
396
|
+
}
|
|
397
|
+
// CONSOLIDATION -> RESULTS
|
|
398
|
+
// compute period end results and move to the results phase
|
|
399
|
+
case DB.GameStatus.CONSOLIDATION: {
|
|
400
|
+
if (!game.activePeriod?.activeSegment)
|
|
401
|
+
return null;
|
|
402
|
+
const { results, extras, promises } = await computePeriodEndResults({
|
|
403
|
+
segmentEndResults: game.results,
|
|
404
|
+
players: game.players,
|
|
405
|
+
activeSegmentResults: game.activePeriod.activeSegment.results,
|
|
406
|
+
segmentFacts: game.activePeriod.activeSegment.facts,
|
|
407
|
+
periodFacts: game.activePeriod.facts,
|
|
408
|
+
periodDecisions: game.activePeriod.decisions,
|
|
409
|
+
activePeriodIx: currentPeriodIx,
|
|
410
|
+
activeSegmentIx: currentSegmentIx,
|
|
411
|
+
gameId: game.id,
|
|
412
|
+
}, ctx, { services });
|
|
413
|
+
await Promise.all(promises);
|
|
414
|
+
// TODO(JJ): The error happens here for the last consolidation
|
|
415
|
+
// - there are no more periods
|
|
416
|
+
// - we prob. need to change the nextPeriodIx if it the last period
|
|
417
|
+
// suggestion
|
|
418
|
+
// - maybe setting game.activePeriod to undefined is enough
|
|
419
|
+
// - In the DB.GameStatus.RESULTS case set the new status
|
|
420
|
+
// to completed when we are done
|
|
421
|
+
// - we prob. need to update the game to completed state
|
|
422
|
+
// - maybe we would like to add a button that finishes the game?
|
|
423
|
+
// => it's better not imo, but open for discussion
|
|
424
|
+
// -> Discuss with RS
|
|
425
|
+
// const lastPeriodIx = game.periods.length - 1
|
|
426
|
+
let periodIx = nextPeriodIx;
|
|
427
|
+
// let data: any = {
|
|
428
|
+
// status: DB.GameStatus.RESULTS,
|
|
429
|
+
// }
|
|
430
|
+
// if (nextPeriodIx <= lastPeriodIx) {
|
|
431
|
+
// // periodIx = lastPeriodIx
|
|
432
|
+
// data.activePeriodIx = periodIx
|
|
433
|
+
// data.activePeriod = {
|
|
434
|
+
// connect: {
|
|
435
|
+
// gameId_index: {
|
|
436
|
+
// gameId,
|
|
437
|
+
// index: periodIx,
|
|
438
|
+
// },
|
|
439
|
+
// },
|
|
440
|
+
// }
|
|
441
|
+
// }
|
|
442
|
+
// TODO(JJ): Check with RS
|
|
443
|
+
// - when updating the game with the nextPeriodIx it crashes
|
|
444
|
+
const result = await ctx.prisma.$transaction([
|
|
445
|
+
// update the status and active period of the current game
|
|
446
|
+
ctx.prisma.game.update({
|
|
447
|
+
where: {
|
|
448
|
+
id: gameId,
|
|
449
|
+
},
|
|
450
|
+
include: {
|
|
451
|
+
periods: {
|
|
452
|
+
include: {
|
|
453
|
+
segments: true,
|
|
454
|
+
},
|
|
455
|
+
},
|
|
456
|
+
},
|
|
457
|
+
data: {
|
|
458
|
+
status: DB.GameStatus.RESULTS,
|
|
459
|
+
activePeriodIx: periodIx,
|
|
460
|
+
activePeriod: {
|
|
461
|
+
connect: {
|
|
462
|
+
gameId_index: {
|
|
463
|
+
gameId,
|
|
464
|
+
index: periodIx,
|
|
465
|
+
},
|
|
466
|
+
},
|
|
467
|
+
},
|
|
468
|
+
},
|
|
469
|
+
}),
|
|
470
|
+
// create PERIOD_END results based on the previous SEGMENT_END results
|
|
471
|
+
ctx.prisma.period.update({
|
|
472
|
+
where: {
|
|
473
|
+
gameId_index: {
|
|
474
|
+
gameId,
|
|
475
|
+
index: currentPeriodIx,
|
|
476
|
+
},
|
|
477
|
+
},
|
|
478
|
+
data: {
|
|
479
|
+
results: {
|
|
480
|
+
create: results,
|
|
481
|
+
},
|
|
482
|
+
},
|
|
483
|
+
include: {
|
|
484
|
+
results: true,
|
|
485
|
+
},
|
|
486
|
+
}),
|
|
487
|
+
...extras,
|
|
488
|
+
]);
|
|
489
|
+
return result;
|
|
490
|
+
}
|
|
491
|
+
// RESULTS -> PREPARATION
|
|
492
|
+
// if the game is in the results phase (between periods)
|
|
493
|
+
// initialize the next period and move to PREPARATION
|
|
494
|
+
case DB.GameStatus.RESULTS: {
|
|
495
|
+
// if there is no next period, return
|
|
496
|
+
if (!game.activePeriod) {
|
|
497
|
+
logger.warn('no next period available');
|
|
498
|
+
return null;
|
|
499
|
+
}
|
|
500
|
+
// if (game.activePeriodIx >= game.periods.length) {
|
|
501
|
+
// const result = await ctx.prisma.$transaction([
|
|
502
|
+
// ctx.prisma.game.update({
|
|
503
|
+
// where: {
|
|
504
|
+
// id: gameId,
|
|
505
|
+
// },
|
|
506
|
+
// include: {
|
|
507
|
+
// periods: {
|
|
508
|
+
// include: {
|
|
509
|
+
// segments: true,
|
|
510
|
+
// },
|
|
511
|
+
// },
|
|
512
|
+
// },
|
|
513
|
+
// data: {
|
|
514
|
+
// // TODO(JJ): Double-check there is not else to update?
|
|
515
|
+
// status: DB.GameStatus.COMPLETED,
|
|
516
|
+
// },
|
|
517
|
+
// }),
|
|
518
|
+
// ])
|
|
519
|
+
// return result
|
|
520
|
+
// }
|
|
521
|
+
const { results, extras } = computePeriodStartResults({
|
|
522
|
+
results: game.activePeriod.previousPeriod[0]?.results,
|
|
523
|
+
players: game.players,
|
|
524
|
+
activePeriodIx: currentPeriodIx,
|
|
525
|
+
gameId: game.id,
|
|
526
|
+
periodFacts: game.activePeriod.facts,
|
|
527
|
+
}, ctx, { services });
|
|
528
|
+
const result = await ctx.prisma.$transaction([
|
|
529
|
+
// update the status and active period of the current game
|
|
530
|
+
ctx.prisma.game.update({
|
|
531
|
+
where: {
|
|
532
|
+
id: gameId,
|
|
533
|
+
},
|
|
534
|
+
include: {
|
|
535
|
+
periods: {
|
|
536
|
+
include: {
|
|
537
|
+
segments: true,
|
|
538
|
+
},
|
|
539
|
+
},
|
|
540
|
+
},
|
|
541
|
+
data: {
|
|
542
|
+
status: DB.GameStatus.PREPARATION,
|
|
543
|
+
},
|
|
544
|
+
}),
|
|
545
|
+
// create PERIOD_START results based on the previous PERIOD_END results
|
|
546
|
+
ctx.prisma.period.update({
|
|
547
|
+
where: {
|
|
548
|
+
gameId_index: {
|
|
549
|
+
gameId,
|
|
550
|
+
index: currentPeriodIx,
|
|
551
|
+
},
|
|
552
|
+
},
|
|
553
|
+
data: {
|
|
554
|
+
results: {
|
|
555
|
+
create: results,
|
|
556
|
+
},
|
|
557
|
+
},
|
|
558
|
+
}),
|
|
559
|
+
...extras,
|
|
560
|
+
]);
|
|
561
|
+
return result;
|
|
562
|
+
}
|
|
563
|
+
default:
|
|
564
|
+
// PREPARATION, PAUSED, COMPLETED, etc.
|
|
565
|
+
return null;
|
|
566
|
+
}
|
|
567
|
+
}
|
|
568
|
+
async function activateNextSegment({ gameId }, ctx, { services }) {
|
|
569
|
+
const game = await ctx.prisma.game.findUnique({
|
|
570
|
+
where: {
|
|
571
|
+
id: gameId,
|
|
572
|
+
},
|
|
573
|
+
include: {
|
|
574
|
+
players: true,
|
|
575
|
+
periods: true,
|
|
576
|
+
segments: true,
|
|
577
|
+
activePeriod: {
|
|
578
|
+
include: {
|
|
579
|
+
results: {
|
|
580
|
+
include: {
|
|
581
|
+
player: true,
|
|
582
|
+
},
|
|
583
|
+
},
|
|
584
|
+
activeSegment: {
|
|
585
|
+
include: {
|
|
586
|
+
nextSegment: true,
|
|
587
|
+
results: {
|
|
588
|
+
include: {
|
|
589
|
+
player: true,
|
|
590
|
+
},
|
|
591
|
+
},
|
|
592
|
+
},
|
|
593
|
+
},
|
|
594
|
+
},
|
|
595
|
+
},
|
|
596
|
+
},
|
|
597
|
+
});
|
|
598
|
+
if (!game?.activePeriod)
|
|
599
|
+
return null;
|
|
600
|
+
const currentPeriodIx = game.activePeriodIx;
|
|
601
|
+
const currentSegmentIx = game.activePeriod.activeSegmentIx;
|
|
602
|
+
const nextSegmentIx = currentSegmentIx + 1;
|
|
603
|
+
// NotificationService.publishGlobalNotification({
|
|
604
|
+
// type: GlobalNotificationType.SEGMENT_ACTIVATED,
|
|
605
|
+
// })
|
|
606
|
+
switch (game.status) {
|
|
607
|
+
// PREPARATION -> RUNNING
|
|
608
|
+
// PAUSED -> RUNNING
|
|
609
|
+
case DB.GameStatus.PREPARATION:
|
|
610
|
+
case DB.GameStatus.PAUSED: {
|
|
611
|
+
const { results, extras } = computeSegmentStartResults(game, ctx, {
|
|
612
|
+
services,
|
|
613
|
+
});
|
|
614
|
+
const result = await ctx.prisma.$transaction([
|
|
615
|
+
ctx.prisma.game.update({
|
|
616
|
+
where: {
|
|
617
|
+
id: gameId,
|
|
618
|
+
},
|
|
619
|
+
include: {
|
|
620
|
+
periods: {
|
|
621
|
+
include: {
|
|
622
|
+
segments: true,
|
|
623
|
+
},
|
|
624
|
+
},
|
|
625
|
+
players: true,
|
|
626
|
+
},
|
|
627
|
+
data: {
|
|
628
|
+
status: DB.GameStatus.RUNNING,
|
|
629
|
+
},
|
|
630
|
+
}),
|
|
631
|
+
// update the active segment of the current period
|
|
632
|
+
ctx.prisma.period.update({
|
|
633
|
+
where: {
|
|
634
|
+
gameId_index: {
|
|
635
|
+
gameId,
|
|
636
|
+
index: currentPeriodIx,
|
|
637
|
+
},
|
|
638
|
+
},
|
|
639
|
+
data: {
|
|
640
|
+
activeSegmentIx: nextSegmentIx,
|
|
641
|
+
activeSegment: {
|
|
642
|
+
connect: {
|
|
643
|
+
gameId_periodIx_index: {
|
|
644
|
+
gameId,
|
|
645
|
+
periodIx: currentPeriodIx,
|
|
646
|
+
index: nextSegmentIx,
|
|
647
|
+
},
|
|
648
|
+
},
|
|
649
|
+
},
|
|
650
|
+
},
|
|
651
|
+
}),
|
|
652
|
+
// SEGMENT INITIALIZATION
|
|
653
|
+
ctx.prisma.periodSegment.update({
|
|
654
|
+
where: {
|
|
655
|
+
gameId_periodIx_index: {
|
|
656
|
+
gameId,
|
|
657
|
+
periodIx: currentPeriodIx,
|
|
658
|
+
index: nextSegmentIx,
|
|
659
|
+
},
|
|
660
|
+
},
|
|
661
|
+
data: {
|
|
662
|
+
results: {
|
|
663
|
+
create: results,
|
|
664
|
+
},
|
|
665
|
+
},
|
|
666
|
+
}),
|
|
667
|
+
...extras,
|
|
668
|
+
]);
|
|
669
|
+
return result;
|
|
670
|
+
}
|
|
671
|
+
// RUNNING -> PAUSED
|
|
672
|
+
// compute the segment results of the current segment and set to paused
|
|
673
|
+
case DB.GameStatus.RUNNING: {
|
|
674
|
+
// return if there is no next segment available
|
|
675
|
+
if (!game.activePeriod?.activeSegment?.nextSegment) {
|
|
676
|
+
return null;
|
|
677
|
+
}
|
|
678
|
+
const { results, extras } = computeSegmentEndResults(game, ctx, {
|
|
679
|
+
services,
|
|
680
|
+
});
|
|
681
|
+
const result = await ctx.prisma.$transaction([
|
|
682
|
+
ctx.prisma.game.update({
|
|
683
|
+
where: { id: gameId },
|
|
684
|
+
include: {
|
|
685
|
+
periods: {
|
|
686
|
+
include: {
|
|
687
|
+
segments: true,
|
|
688
|
+
},
|
|
689
|
+
},
|
|
690
|
+
players: true,
|
|
691
|
+
},
|
|
692
|
+
data: {
|
|
693
|
+
status: DB.GameStatus.PAUSED,
|
|
694
|
+
},
|
|
695
|
+
}),
|
|
696
|
+
ctx.prisma.periodSegment.update({
|
|
697
|
+
where: {
|
|
698
|
+
gameId_periodIx_index: {
|
|
699
|
+
gameId,
|
|
700
|
+
periodIx: currentPeriodIx,
|
|
701
|
+
index: currentSegmentIx,
|
|
702
|
+
},
|
|
703
|
+
},
|
|
704
|
+
data: {
|
|
705
|
+
results: {
|
|
706
|
+
update: results,
|
|
707
|
+
},
|
|
708
|
+
},
|
|
709
|
+
include: {
|
|
710
|
+
results: true,
|
|
711
|
+
},
|
|
712
|
+
}),
|
|
713
|
+
// reset player readiness
|
|
714
|
+
ctx.prisma.player.updateMany({
|
|
715
|
+
where: {
|
|
716
|
+
game: {
|
|
717
|
+
id: gameId,
|
|
718
|
+
},
|
|
719
|
+
},
|
|
720
|
+
data: {
|
|
721
|
+
isReady: false,
|
|
722
|
+
},
|
|
723
|
+
}),
|
|
724
|
+
...extras,
|
|
725
|
+
]);
|
|
726
|
+
return result;
|
|
727
|
+
}
|
|
728
|
+
default:
|
|
729
|
+
return null;
|
|
730
|
+
}
|
|
731
|
+
}
|
|
732
|
+
async function updatePlayerData({ name, facts }, ctx, { schema }) {
|
|
733
|
+
// if none of the arguments have been provided, no update is performed
|
|
734
|
+
if (none(Boolean, [name, facts])) {
|
|
735
|
+
return null;
|
|
736
|
+
}
|
|
737
|
+
let data = {};
|
|
738
|
+
if (name) {
|
|
739
|
+
data['name'] = name;
|
|
740
|
+
}
|
|
741
|
+
if (facts) {
|
|
742
|
+
data['facts'] = schema.validateSync(facts, {
|
|
743
|
+
stripUnknown: true,
|
|
744
|
+
});
|
|
745
|
+
}
|
|
746
|
+
const player = await ctx.prisma.player.update({
|
|
747
|
+
where: {
|
|
748
|
+
id: ctx.user.sub,
|
|
749
|
+
},
|
|
750
|
+
data,
|
|
751
|
+
include: {
|
|
752
|
+
level: true,
|
|
753
|
+
achievements: {
|
|
754
|
+
include: {
|
|
755
|
+
achievement: true,
|
|
756
|
+
},
|
|
757
|
+
},
|
|
758
|
+
},
|
|
759
|
+
});
|
|
760
|
+
return player;
|
|
761
|
+
}
|
|
762
|
+
async function getGames(args, ctx) {
|
|
763
|
+
const result = await ctx.prisma.game.findMany({
|
|
764
|
+
orderBy: {
|
|
765
|
+
id: 'desc',
|
|
766
|
+
},
|
|
767
|
+
include: {
|
|
768
|
+
_count: {
|
|
769
|
+
select: { players: true },
|
|
770
|
+
},
|
|
771
|
+
activePeriod: true,
|
|
772
|
+
},
|
|
773
|
+
});
|
|
774
|
+
return result.map((game) => ({
|
|
775
|
+
...game,
|
|
776
|
+
activeSegmentIx: game.activePeriod?.activeSegmentIx,
|
|
777
|
+
}));
|
|
778
|
+
}
|
|
779
|
+
async function getGame(args, ctx) {
|
|
780
|
+
const gameId = args.id ?? ctx.user.gameId;
|
|
781
|
+
if (!gameId) {
|
|
782
|
+
return null;
|
|
783
|
+
}
|
|
784
|
+
return ctx.prisma.game.findUnique({
|
|
785
|
+
where: {
|
|
786
|
+
id: args.id,
|
|
787
|
+
},
|
|
788
|
+
include: {
|
|
789
|
+
players: {
|
|
790
|
+
include: {
|
|
791
|
+
level: true,
|
|
792
|
+
achievements: true,
|
|
793
|
+
},
|
|
794
|
+
orderBy: {
|
|
795
|
+
number: 'asc',
|
|
796
|
+
},
|
|
797
|
+
},
|
|
798
|
+
periods: {
|
|
799
|
+
orderBy: {
|
|
800
|
+
index: 'asc',
|
|
801
|
+
},
|
|
802
|
+
include: {
|
|
803
|
+
segments: {
|
|
804
|
+
orderBy: {
|
|
805
|
+
index: 'asc',
|
|
806
|
+
},
|
|
807
|
+
include: {
|
|
808
|
+
learningElements: true,
|
|
809
|
+
storyElements: true,
|
|
810
|
+
},
|
|
811
|
+
},
|
|
812
|
+
},
|
|
813
|
+
},
|
|
814
|
+
activePeriod: {
|
|
815
|
+
include: {
|
|
816
|
+
segments: {
|
|
817
|
+
orderBy: {
|
|
818
|
+
index: 'asc',
|
|
819
|
+
},
|
|
820
|
+
},
|
|
821
|
+
activeSegment: true,
|
|
822
|
+
},
|
|
823
|
+
},
|
|
824
|
+
},
|
|
825
|
+
});
|
|
826
|
+
}
|
|
827
|
+
async function getGameFromContext(ctx) {
|
|
828
|
+
return ctx.prisma.game.findUnique({
|
|
829
|
+
where: {
|
|
830
|
+
id: ctx.user.gameId,
|
|
831
|
+
},
|
|
832
|
+
include: {
|
|
833
|
+
activePeriod: true,
|
|
834
|
+
},
|
|
835
|
+
});
|
|
836
|
+
}
|
|
837
|
+
async function getLearningElements(args, ctx) {
|
|
838
|
+
return ctx.prisma.learningElement.findMany({
|
|
839
|
+
include: {
|
|
840
|
+
options: true,
|
|
841
|
+
},
|
|
842
|
+
});
|
|
843
|
+
}
|
|
844
|
+
async function getStoryElements(args, ctx) {
|
|
845
|
+
return ctx.prisma.storyElement.findMany();
|
|
846
|
+
}
|
|
847
|
+
function mapAction({ ctx, gameId, activePeriodIx, playerId }) {
|
|
848
|
+
return (action) => ctx.prisma.playerAction.create({
|
|
849
|
+
data: {
|
|
850
|
+
type: action.type,
|
|
851
|
+
facts: action.facts,
|
|
852
|
+
game: {
|
|
853
|
+
connect: { id: gameId },
|
|
854
|
+
},
|
|
855
|
+
player: {
|
|
856
|
+
connect: { id: playerId },
|
|
857
|
+
},
|
|
858
|
+
periodIx: activePeriodIx,
|
|
859
|
+
period: {
|
|
860
|
+
connect: {
|
|
861
|
+
gameId_index: {
|
|
862
|
+
gameId,
|
|
863
|
+
index: activePeriodIx,
|
|
864
|
+
},
|
|
865
|
+
},
|
|
866
|
+
},
|
|
867
|
+
segmentIx: typeof action.segment === 'number' ? action.segment : undefined,
|
|
868
|
+
segment: typeof action.segment === 'number'
|
|
869
|
+
? {
|
|
870
|
+
connect: {
|
|
871
|
+
gameId_periodIx_index: {
|
|
872
|
+
gameId,
|
|
873
|
+
periodIx: activePeriodIx,
|
|
874
|
+
index: action.segment,
|
|
875
|
+
},
|
|
876
|
+
},
|
|
877
|
+
}
|
|
878
|
+
: undefined,
|
|
879
|
+
},
|
|
880
|
+
});
|
|
881
|
+
}
|
|
882
|
+
function computePeriodStartResults({ results, players, activePeriodIx, gameId, periodFacts }, ctx, { services }) {
|
|
883
|
+
const currentPeriodIx = activePeriodIx;
|
|
884
|
+
const nextPeriodIx = currentPeriodIx + 1;
|
|
885
|
+
let extras = [];
|
|
886
|
+
// if the game is running, transform previous results to next
|
|
887
|
+
if (currentPeriodIx >= 0) {
|
|
888
|
+
const result = results
|
|
889
|
+
// ensure that we only work on PERIOD_END results of the preceding period
|
|
890
|
+
.filter((result) => result.type === DB.PlayerResultType.PERIOD_END)
|
|
891
|
+
.map((result, ix, allResults) => {
|
|
892
|
+
const { resultFacts: facts, actions } = services.PeriodResult.start(result.facts, {
|
|
893
|
+
playerRole: result.player?.role ?? result.player.connect?.role,
|
|
894
|
+
periodFacts,
|
|
895
|
+
});
|
|
896
|
+
if (actions && actions.length > 0) {
|
|
897
|
+
const mapper = mapAction({
|
|
898
|
+
ctx,
|
|
899
|
+
gameId,
|
|
900
|
+
activePeriodIx: currentPeriodIx,
|
|
901
|
+
playerId: result.player.id,
|
|
902
|
+
});
|
|
903
|
+
extras = [...extras, ...actions.map(mapper)];
|
|
904
|
+
}
|
|
905
|
+
return {
|
|
906
|
+
type: DB.PlayerResultType.PERIOD_START,
|
|
907
|
+
periodIx: currentPeriodIx,
|
|
908
|
+
facts,
|
|
909
|
+
player: {
|
|
910
|
+
connect: {
|
|
911
|
+
id: result.player.id ?? result.player.connect.id,
|
|
912
|
+
},
|
|
913
|
+
},
|
|
914
|
+
game: {
|
|
915
|
+
connect: {
|
|
916
|
+
id: gameId,
|
|
917
|
+
},
|
|
918
|
+
},
|
|
919
|
+
};
|
|
920
|
+
});
|
|
921
|
+
return {
|
|
922
|
+
results: result,
|
|
923
|
+
extras,
|
|
924
|
+
};
|
|
925
|
+
}
|
|
926
|
+
// if the game has not started yet, generate initial PERIOD_START results
|
|
927
|
+
const result = players.map((player, ix, allPlayers) => {
|
|
928
|
+
const { resultFacts: facts, actions } = services.PeriodResult.initialize({}, { playerRole: player.role, periodFacts });
|
|
929
|
+
if (actions && actions.length > 0) {
|
|
930
|
+
const mapper = mapAction({
|
|
931
|
+
ctx,
|
|
932
|
+
gameId,
|
|
933
|
+
activePeriodIx: nextPeriodIx,
|
|
934
|
+
playerId: player.id,
|
|
935
|
+
});
|
|
936
|
+
extras = [...extras, ...actions.map(mapper)];
|
|
937
|
+
}
|
|
938
|
+
return {
|
|
939
|
+
type: DB.PlayerResultType.PERIOD_START,
|
|
940
|
+
periodIx: nextPeriodIx,
|
|
941
|
+
facts,
|
|
942
|
+
player: {
|
|
943
|
+
connect: {
|
|
944
|
+
id: player.id,
|
|
945
|
+
},
|
|
946
|
+
},
|
|
947
|
+
game: {
|
|
948
|
+
connect: {
|
|
949
|
+
id: gameId,
|
|
950
|
+
},
|
|
951
|
+
},
|
|
952
|
+
};
|
|
953
|
+
});
|
|
954
|
+
return {
|
|
955
|
+
results: result,
|
|
956
|
+
extras,
|
|
957
|
+
};
|
|
958
|
+
}
|
|
959
|
+
async function computePeriodEndResults({ segmentEndResults, players, activeSegmentResults, periodFacts, periodDecisions, segmentFacts, activePeriodIx, activeSegmentIx, gameId, }, ctx, { services }) {
|
|
960
|
+
let extras = [];
|
|
961
|
+
let promises = [];
|
|
962
|
+
const perPlayer = {};
|
|
963
|
+
players.forEach((player) => {
|
|
964
|
+
perPlayer[player.id] = {
|
|
965
|
+
segmentEndResults: segmentEndResults.filter((res) => res.playerId === player.id),
|
|
966
|
+
consolidationDecisions: periodDecisions.find((decision) => decision.playerId === player.id),
|
|
967
|
+
};
|
|
968
|
+
});
|
|
969
|
+
const results = activeSegmentResults
|
|
970
|
+
.filter((result) => result.type === DB.PlayerResultType.SEGMENT_END)
|
|
971
|
+
.map((result, ix, allResults) => {
|
|
972
|
+
const segmentEndResults = perPlayer[result.playerId].segmentEndResults;
|
|
973
|
+
const consolidationDecisions = perPlayer[result.playerId].consolidationDecisions;
|
|
974
|
+
const { resultFacts: facts, actions, events, } = services.PeriodResult.end(result.facts, {
|
|
975
|
+
segmentEndResults,
|
|
976
|
+
periodFacts,
|
|
977
|
+
segmentFacts,
|
|
978
|
+
playerRole: result.player.role,
|
|
979
|
+
playerLevel: result.player.levelIx + 1,
|
|
980
|
+
playerExperience: result.player.experience,
|
|
981
|
+
consolidationDecisions,
|
|
982
|
+
periodIx: activePeriodIx,
|
|
983
|
+
segmentIx: activeSegmentIx,
|
|
984
|
+
});
|
|
985
|
+
logger.debug(actions);
|
|
986
|
+
if (actions && actions.length > 0) {
|
|
987
|
+
const mapper = mapAction({
|
|
988
|
+
ctx,
|
|
989
|
+
gameId,
|
|
990
|
+
activePeriodIx,
|
|
991
|
+
playerId: result.player.id,
|
|
992
|
+
});
|
|
993
|
+
extras = [...extras, ...actions.map(mapper)];
|
|
994
|
+
}
|
|
995
|
+
promises = [
|
|
996
|
+
...promises,
|
|
997
|
+
receiveEvents({
|
|
998
|
+
events,
|
|
999
|
+
ctx: {
|
|
1000
|
+
args: {
|
|
1001
|
+
playerId: result.player.id,
|
|
1002
|
+
periodIx: activePeriodIx,
|
|
1003
|
+
gameId,
|
|
1004
|
+
},
|
|
1005
|
+
user: ctx.user,
|
|
1006
|
+
achievements: result.player.achievementKeys,
|
|
1007
|
+
experience: result.player.experience,
|
|
1008
|
+
currentLevelIx: result.player.levelIx,
|
|
1009
|
+
},
|
|
1010
|
+
prisma: ctx.prisma,
|
|
1011
|
+
}),
|
|
1012
|
+
];
|
|
1013
|
+
return {
|
|
1014
|
+
type: DB.PlayerResultType.PERIOD_END,
|
|
1015
|
+
periodIx: activePeriodIx,
|
|
1016
|
+
facts,
|
|
1017
|
+
player: {
|
|
1018
|
+
connect: {
|
|
1019
|
+
id: result.playerId,
|
|
1020
|
+
},
|
|
1021
|
+
},
|
|
1022
|
+
game: {
|
|
1023
|
+
connect: {
|
|
1024
|
+
id: gameId,
|
|
1025
|
+
},
|
|
1026
|
+
},
|
|
1027
|
+
};
|
|
1028
|
+
});
|
|
1029
|
+
return {
|
|
1030
|
+
extras,
|
|
1031
|
+
results,
|
|
1032
|
+
promises,
|
|
1033
|
+
};
|
|
1034
|
+
}
|
|
1035
|
+
function computeSegmentStartResults(game, ctx, { services }) {
|
|
1036
|
+
const currentSegmentIx = game.activePeriod.activeSegmentIx;
|
|
1037
|
+
const nextSegmentIx = currentSegmentIx + 1;
|
|
1038
|
+
let extras = [];
|
|
1039
|
+
// if there was a previous segment, compute the change in results
|
|
1040
|
+
if (currentSegmentIx >= 0) {
|
|
1041
|
+
const results = game.activePeriod.activeSegment.results
|
|
1042
|
+
.filter((result) => result.type === DB.PlayerResultType.SEGMENT_END)
|
|
1043
|
+
.reduce((acc, result, ix, allResults) => {
|
|
1044
|
+
const { resultFacts: facts, actions } = services.SegmentResult.start(result.facts, {
|
|
1045
|
+
playerRole: result.player.role,
|
|
1046
|
+
periodFacts: game.activePeriod.facts,
|
|
1047
|
+
segmentFacts: game.activePeriod.activeSegment.facts,
|
|
1048
|
+
nextSegmentFacts: game.activePeriod.activeSegment.nextSegment?.facts,
|
|
1049
|
+
segmentIx: nextSegmentIx,
|
|
1050
|
+
});
|
|
1051
|
+
if (actions && actions.length > 0) {
|
|
1052
|
+
const mapper = mapAction({
|
|
1053
|
+
ctx,
|
|
1054
|
+
gameId: game.id,
|
|
1055
|
+
activePeriodIx: game.activePeriodIx,
|
|
1056
|
+
playerId: result.player.id,
|
|
1057
|
+
});
|
|
1058
|
+
extras = [...extras, ...actions.map(mapper)];
|
|
1059
|
+
}
|
|
1060
|
+
const common = {
|
|
1061
|
+
facts,
|
|
1062
|
+
periodIx: game.activePeriodIx,
|
|
1063
|
+
segmentIx: nextSegmentIx,
|
|
1064
|
+
player: {
|
|
1065
|
+
connect: {
|
|
1066
|
+
id: result.playerId,
|
|
1067
|
+
},
|
|
1068
|
+
},
|
|
1069
|
+
period: {
|
|
1070
|
+
connect: {
|
|
1071
|
+
id: game.activePeriodId,
|
|
1072
|
+
},
|
|
1073
|
+
},
|
|
1074
|
+
game: {
|
|
1075
|
+
connect: {
|
|
1076
|
+
id: game.id,
|
|
1077
|
+
},
|
|
1078
|
+
},
|
|
1079
|
+
};
|
|
1080
|
+
return [
|
|
1081
|
+
...acc,
|
|
1082
|
+
{
|
|
1083
|
+
...common,
|
|
1084
|
+
type: DB.PlayerResultType.SEGMENT_START,
|
|
1085
|
+
},
|
|
1086
|
+
{
|
|
1087
|
+
...common,
|
|
1088
|
+
type: DB.PlayerResultType.SEGMENT_END,
|
|
1089
|
+
},
|
|
1090
|
+
];
|
|
1091
|
+
}, []);
|
|
1092
|
+
return {
|
|
1093
|
+
results,
|
|
1094
|
+
extras,
|
|
1095
|
+
};
|
|
1096
|
+
}
|
|
1097
|
+
// if it is the first segment, transform PERIOD_START to SEGMENT_START
|
|
1098
|
+
const results = game.activePeriod.results
|
|
1099
|
+
.filter((result) => result.type === DB.PlayerResultType.PERIOD_START)
|
|
1100
|
+
.reduce((acc, result, ix, allResults) => {
|
|
1101
|
+
let { resultFacts: facts } = services.SegmentResult.initialize(result.facts, {
|
|
1102
|
+
playerRole: result.player.role,
|
|
1103
|
+
periodFacts: game.activePeriod.facts,
|
|
1104
|
+
segmentFacts: game.activePeriod.activeSegment?.facts,
|
|
1105
|
+
nextSegmentFacts: game.activePeriod.activeSegment?.nextSegment?.facts,
|
|
1106
|
+
segmentIx: nextSegmentIx,
|
|
1107
|
+
});
|
|
1108
|
+
const common = {
|
|
1109
|
+
facts,
|
|
1110
|
+
periodIx: game.activePeriodIx,
|
|
1111
|
+
segmentIx: nextSegmentIx,
|
|
1112
|
+
player: {
|
|
1113
|
+
connect: {
|
|
1114
|
+
id: result.playerId,
|
|
1115
|
+
},
|
|
1116
|
+
},
|
|
1117
|
+
period: {
|
|
1118
|
+
connect: {
|
|
1119
|
+
id: game.activePeriodId,
|
|
1120
|
+
},
|
|
1121
|
+
},
|
|
1122
|
+
game: {
|
|
1123
|
+
connect: {
|
|
1124
|
+
id: game.id,
|
|
1125
|
+
},
|
|
1126
|
+
},
|
|
1127
|
+
};
|
|
1128
|
+
return [
|
|
1129
|
+
...acc,
|
|
1130
|
+
{
|
|
1131
|
+
...common,
|
|
1132
|
+
type: DB.PlayerResultType.SEGMENT_START,
|
|
1133
|
+
},
|
|
1134
|
+
{
|
|
1135
|
+
...common,
|
|
1136
|
+
type: DB.PlayerResultType.SEGMENT_END,
|
|
1137
|
+
},
|
|
1138
|
+
];
|
|
1139
|
+
}, []);
|
|
1140
|
+
return {
|
|
1141
|
+
results,
|
|
1142
|
+
extras,
|
|
1143
|
+
};
|
|
1144
|
+
}
|
|
1145
|
+
function computeSegmentEndResults(game, ctx, { services }) {
|
|
1146
|
+
let extras = [];
|
|
1147
|
+
const results = game.activePeriod.activeSegment.results
|
|
1148
|
+
.filter((result) => result.type === DB.PlayerResultType.SEGMENT_END)
|
|
1149
|
+
.map((result, ix, allResults) => {
|
|
1150
|
+
const { resultFacts: facts, actions } = services.SegmentResult.end(result.facts, {
|
|
1151
|
+
playerRole: result.player.role,
|
|
1152
|
+
periodFacts: game.activePeriod.facts,
|
|
1153
|
+
segmentFacts: game.activePeriod.activeSegment.facts,
|
|
1154
|
+
segmentIx: game.activePeriod.activeSegmentIx,
|
|
1155
|
+
});
|
|
1156
|
+
if (actions && actions.length > 0) {
|
|
1157
|
+
const mapper = mapAction({
|
|
1158
|
+
ctx,
|
|
1159
|
+
gameId: game.id,
|
|
1160
|
+
activePeriodIx: game.activePeriodIx,
|
|
1161
|
+
playerId: result.player.id,
|
|
1162
|
+
});
|
|
1163
|
+
extras = [...extras, ...actions.map(mapper)];
|
|
1164
|
+
}
|
|
1165
|
+
return {
|
|
1166
|
+
where: {
|
|
1167
|
+
periodIx_segmentIx_playerId_type: {
|
|
1168
|
+
periodIx: game.activePeriodIx,
|
|
1169
|
+
segmentIx: game.activePeriod.activeSegmentIx,
|
|
1170
|
+
playerId: result.playerId,
|
|
1171
|
+
type: DB.PlayerResultType.SEGMENT_END,
|
|
1172
|
+
},
|
|
1173
|
+
},
|
|
1174
|
+
data: {
|
|
1175
|
+
facts,
|
|
1176
|
+
game: {
|
|
1177
|
+
connect: {
|
|
1178
|
+
id: game.id,
|
|
1179
|
+
},
|
|
1180
|
+
},
|
|
1181
|
+
},
|
|
1182
|
+
};
|
|
1183
|
+
});
|
|
1184
|
+
return {
|
|
1185
|
+
results,
|
|
1186
|
+
extras,
|
|
1187
|
+
};
|
|
1188
|
+
}
|
|
1189
|
+
|
|
1190
|
+
export { activateNextPeriod, activateNextSegment, addGamePeriod, addPeriodSegment, computePeriodEndResults, computePeriodStartResults, computeSegmentEndResults, computeSegmentStartResults, createGame, getGame, getGameFromContext, getGames, getLearningElements, getStoryElements, updatePlayerData };
|
|
1191
|
+
//# sourceMappingURL=GameService.js.map
|