Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Helpers/GameProgress.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: GameProgress Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
// clang-format off
11
AchievementID achievementList[] = {
12
{ .idPS4 = 0, .idUnknown = 19, .id = "ACH_GOLD_MEDAL" },
13
{ .idPS4 = 1, .idUnknown = 20, .id = "ACH_SILVER_MEDAL" },
14
{ .idPS4 = 2, .idUnknown = 21, .id = "ACH_EMERALDS" },
15
{ .idPS4 = 3, .idUnknown = 22, .id = "ACH_GAME_CLEARED" },
16
{ .idPS4 = 4, .idUnknown = 23, .id = "ACH_STARPOST" },
17
{ .idPS4 = 5, .idUnknown = 24, .id = "ACH_SIGNPOST" },
18
{ .idPS4 = 6, .idUnknown = 25, .id = "ACH_GHZ" },
19
{ .idPS4 = 7, .idUnknown = 26, .id = "ACH_CPZ" },
20
{ .idPS4 = 8, .idUnknown = 27, .id = "ACH_SPZ" },
21
{ .idPS4 = 9, .idUnknown = 28, .id = "ACH_FBZ" },
22
{ .idPS4 = 10, .idUnknown = 29, .id = "ACH_PGZ" },
23
{ .idPS4 = 11, .idUnknown = 30, .id = "ACH_SSZ" },
24
{ .idPS4 = 12, .idUnknown = 31, .id = "ACH_HCZ" },
25
{ .idPS4 = 13, .idUnknown = 32, .id = "ACH_MSZ" },
26
{ .idPS4 = 14, .idUnknown = 33, .id = "ACH_OOZ" },
27
{ .idPS4 = 15, .idUnknown = 34, .id = "ACH_LRZ" },
28
{ .idPS4 = 16, .idUnknown = 35, .id = "ACH_MMZ" },
29
{ .idPS4 = 17, .idUnknown = 36, .id = "ACH_TMZ" },
30
};
31
// clang-format on
32
33
ObjectGameProgress *GameProgress;
34
35
void GameProgress_Update(void) {}
36
37
void GameProgress_LateUpdate(void) {}
38
39
void GameProgress_StaticUpdate(void) {}
40
41
void GameProgress_Draw(void) {}
42
43
void GameProgress_Create(void *data) {}
44
45
void GameProgress_StageLoad(void) {}
46
47
ProgressRAM *GameProgress_GetProgressRAM(void) { return (ProgressRAM *)&globals->saveRAM[0x900]; }
48
49
int32 GameProgress_GetNotifStringID(int32 type)
50
{
51
switch (type) {
52
case GAMEPROGRESS_UNLOCK_TIMEATTACK: return STR_TAUNLOCKED;
53
54
case GAMEPROGRESS_UNLOCK_COMPETITION: return STR_COMPUNLOCKED;
55
56
case GAMEPROGRESS_UNLOCK_PEELOUT: return STR_PEELOUTUNLOCKED;
57
58
case GAMEPROGRESS_UNLOCK_INSTASHIELD: return STR_INSTASHIELDUNLOCKED;
59
60
case GAMEPROGRESS_UNLOCK_ANDKNUX: return STR_ANDKNUXUNLOCKED;
61
62
case GAMEPROGRESS_UNLOCK_DEBUGMODE: return STR_DEBUGMODEUNLOCKED;
63
64
case GAMEPROGRESS_UNLOCK_MEANBEAN: return STR_MBMUNLOCKED;
65
66
case GAMEPROGRESS_UNLOCK_DAGARDEN: return STR_DAGARDENUNLOCKED;
67
68
case GAMEPROGRESS_UNLOCK_BLUESPHERES: return STR_BLUESPHERESUNLOCKED;
69
70
default: return STR_FEATUREUNIMPLIMENTED;
71
}
72
}
73
74
void GameProgress_ShuffleBSSID(void)
75
{
76
ProgressRAM *progress = GameProgress_GetProgressRAM();
77
78
int32 startID = globals->blueSpheresID;
79
if (progress) {
80
while (true) {
81
if (globals->blueSpheresInit) {
82
++globals->blueSpheresID;
83
globals->blueSpheresID %= GAMEPROGRESS_MEDAL_COUNT;
84
}
85
else {
86
globals->blueSpheresID = 0;
87
globals->blueSpheresInit = true;
88
}
89
90
if (progress->goldMedalCount >= GAMEPROGRESS_MEDAL_COUNT)
91
break;
92
93
bool32 rotatedBSS = false;
94
if (progress->silverMedalCount < GAMEPROGRESS_MEDAL_COUNT)
95
rotatedBSS = progress->medals[globals->blueSpheresID] == 0;
96
else
97
rotatedBSS = progress->medals[globals->blueSpheresID] < 2;
98
99
if (rotatedBSS) {
100
LogHelpers_Print("Blue Spheres ID rotated by %d to %d", globals->blueSpheresID - startID, globals->blueSpheresID);
101
break;
102
}
103
}
104
}
105
else {
106
if (globals->blueSpheresInit) {
107
globals->blueSpheresID++;
108
globals->blueSpheresID %= GAMEPROGRESS_MEDAL_COUNT;
109
}
110
else {
111
globals->blueSpheresID = 0;
112
globals->blueSpheresInit = true;
113
}
114
115
LogHelpers_Print("WARNING GameProgress Attempted to get BS ID before loading SaveGame file");
116
LogHelpers_Print("Blue Spheres ID rotated by %d to %d", globals->blueSpheresID - startID, globals->blueSpheresID);
117
}
118
}
119
120
bool32 GameProgress_GetZoneUnlocked(int32 zoneID)
121
{
122
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
123
LogHelpers_Print("WARNING GameProgress Attempted to check zone clear before loading SaveGame file");
124
return false;
125
}
126
else {
127
ProgressRAM *progress = GameProgress_GetProgressRAM();
128
return progress->zoneCleared[zoneID];
129
}
130
}
131
132
float GameProgress_GetCompletionPercent(ProgressRAM *progress)
133
{
134
int32 completeZones = 0;
135
int32 medalsGotten = 0;
136
int32 emeraldsGotten = 0;
137
138
for (int32 i = 0; i < GAMEPROGRESS_MEDAL_COUNT; ++i) {
139
if (i < GAMEPROGRESS_EMERALD_COUNT)
140
emeraldsGotten += progress->emeraldObtained[i] == 1;
141
142
if (i < ZONE_ERZ)
143
completeZones += progress->zoneCleared[i] == 1;
144
145
medalsGotten += progress->medals[i];
146
}
147
148
// get the count of the unlock
149
// then multiply by its completion weight (in this case zones are worth 55% of completion percent)
150
// then finally divide by the maximum count to normalize it
151
152
float zonePercent = ((MIN(completeZones, GAMEPROGRESS_ZONE_COUNT) * 0.55) / (float)GAMEPROGRESS_ZONE_COUNT);
153
float medalPercent = ((MIN(medalsGotten, GAMEPROGRESS_MEDAL_COUNT * 2) * 0.35) / (float)(GAMEPROGRESS_MEDAL_COUNT * 2));
154
float specialPercent = ((MIN(emeraldsGotten, GAMEPROGRESS_EMERALD_COUNT) * 0.05) / (float)GAMEPROGRESS_EMERALD_COUNT);
155
float endingPercent = ((MIN(progress->unlockedEndingID, GAMEPROGRESS_ENDING_GOOD) * 0.05) / (float)GAMEPROGRESS_ENDING_GOOD);
156
return zonePercent + medalPercent + specialPercent + endingPercent;
157
}
158
159
#if MANIA_USE_PLUS
160
void GameProgress_TrackGameProgress(void (*callback)(bool32 success))
161
#else
162
void GameProgress_TrackGameProgress(void (*callback)(void))
163
#endif
164
{
165
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
166
LogHelpers_Print("WARNING GameProgress Attempted to track progress before loading SaveGame file");
167
}
168
else {
169
ProgressRAM *progress = GameProgress_GetProgressRAM();
170
if (!progress->allSpecialCleared) {
171
float percent = GameProgress_GetCompletionPercent(progress);
172
#if MANIA_USE_PLUS
173
StatInfo stat;
174
memset(&stat, 0, sizeof(StatInfo));
175
stat.statID = 3;
176
stat.name = "GAME_PROGRESS";
177
stat.data[0] = FLOAT_TO_VOID(percent);
178
API.TryTrackStat(&stat);
179
#else
180
APICallback_TrackGameProgress(percent);
181
#endif
182
SaveGame_SaveFile(callback);
183
return;
184
}
185
}
186
187
#if MANIA_USE_PLUS
188
if (callback)
189
callback(false);
190
#else
191
if (callback)
192
callback();
193
#endif
194
}
195
void GameProgress_ClearBSSSave(void)
196
{
197
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
198
LogHelpers_Print("WARNING GameProgress Attempted to clear BSS before loading SaveGame file");
199
return;
200
}
201
202
ProgressRAM *progress = GameProgress_GetProgressRAM();
203
progress->allGoldMedals = false;
204
progress->allSilverMedals = false;
205
progress->goldMedalCount = 0;
206
progress->silverMedalCount = 0;
207
memset(progress->medals, 0, sizeof(progress->medals));
208
}
209
void GameProgress_UnlockAll(void)
210
{
211
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
212
LogHelpers_Print("WARNING GameProgress Attempted to unlock all before loading SaveGame file");
213
return;
214
}
215
216
ProgressRAM *progress = GameProgress_GetProgressRAM();
217
218
progress->allSpecialCleared = true;
219
progress->allEmeraldsObtained = true;
220
progress->unlockedEndingID = GAMEPROGRESS_ENDING_GOOD;
221
progress->silverMedalCount = GAMEPROGRESS_MEDAL_COUNT;
222
progress->goldMedalCount = GAMEPROGRESS_MEDAL_COUNT;
223
progress->allGoldMedals = true;
224
progress->allSilverMedals = true;
225
226
for (int32 m = 0; m < GAMEPROGRESS_MEDAL_COUNT; ++m) {
227
if (m < GAMEPROGRESS_EMERALD_COUNT)
228
progress->emeraldObtained[m] = true;
229
230
if (m < GAMEPROGRESS_ZONE_COUNT)
231
progress->zoneCleared[m] = true;
232
233
progress->medals[m] = GAMEPROGRESS_MEDAL_GOLD;
234
}
235
}
236
237
void GameProgress_LockAllSpecialClear(void)
238
{
239
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
240
LogHelpers_Print("WARNING GameProgress Attempted to lock special clear before loading SaveGame file");
241
}
242
else {
243
ProgressRAM *progress = GameProgress_GetProgressRAM();
244
progress->allSpecialCleared = false;
245
}
246
}
247
248
void GameProgress_ClearProgress(void)
249
{
250
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
251
LogHelpers_Print("WARNING GameProgress Attempted to clear all before loading SaveGame file");
252
return;
253
}
254
255
ProgressRAM *progress = GameProgress_GetProgressRAM();
256
257
progress->allSpecialCleared = false;
258
progress->allEmeraldsObtained = false;
259
progress->unlockedEndingID = GAMEPROGRESS_ENDING_NONE;
260
progress->silverMedalCount = 0;
261
progress->goldMedalCount = 0;
262
progress->allGoldMedals = false;
263
progress->allSilverMedals = false;
264
265
for (int32 m = 0; m < GAMEPROGRESS_MEDAL_COUNT; ++m) {
266
if (m < GAMEPROGRESS_EMERALD_COUNT)
267
progress->emeraldObtained[m] = false;
268
269
if (m < GAMEPROGRESS_ZONE_COUNT)
270
progress->zoneCleared[m] = false;
271
272
if (m < GAMEPROGRESS_EMERALD_COUNT)
273
progress->specialCleared[m] = false;
274
275
if (m < GAMEPROGRESS_UNLOCK_COUNT)
276
progress->unreadNotifs[m] = false;
277
278
progress->medals[m] = GAMEPROGRESS_MEDAL_NONE;
279
}
280
}
281
282
void GameProgress_MarkZoneCompleted(int32 zoneID)
283
{
284
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
285
LogHelpers_Print("WARNING GameProgress Attempted to mark completed zone before loading SaveGame file");
286
return;
287
}
288
289
if (zoneID > ZONE_INVALID) {
290
ProgressRAM *progress = GameProgress_GetProgressRAM();
291
for (int32 z = 0; z <= zoneID; ++z) {
292
if (!progress->zoneCleared[z]) {
293
LogHelpers_Print("PROGRESS Cleared zone %d", z);
294
progress->zoneCleared[z] = true;
295
}
296
}
297
}
298
}
299
300
bool32 GameProgress_CheckZoneClear(void)
301
{
302
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
303
LogHelpers_Print("WARNING GameProgress Attempted to check zone clear before loading SaveGame file");
304
return false;
305
}
306
307
ProgressRAM *progress = GameProgress_GetProgressRAM();
308
309
for (int32 z = 0; z < GAMEPROGRESS_ZONE_COUNT; ++z) {
310
if (!progress->zoneCleared[z]) {
311
GameProgress_MarkZoneCompleted(z);
312
return true;
313
}
314
}
315
316
return false;
317
}
318
319
void GameProgress_GiveEmerald(int32 emeraldID)
320
{
321
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
322
LogHelpers_Print("WARNING GameProgress Attempted to get emerald before loading SaveGame file");
323
return;
324
}
325
326
ProgressRAM *progress = GameProgress_GetProgressRAM();
327
328
progress->emeraldObtained[emeraldID] = true;
329
bool32 allEmeralds = true;
330
for (int32 i = 0; i < GAMEPROGRESS_EMERALD_COUNT; ++i) {
331
allEmeralds = allEmeralds && progress->emeraldObtained[i];
332
}
333
334
if (allEmeralds)
335
progress->allEmeraldsObtained = true;
336
}
337
338
void GameProgress_GiveMedal(uint8 medalID, uint8 type)
339
{
340
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
341
LogHelpers_Print("WARNING GameProgress Attempted to get medallion before loading SaveGame file");
342
return;
343
}
344
345
ProgressRAM *progress = GameProgress_GetProgressRAM();
346
int32 goldCount = 0;
347
int32 silverCount = 0;
348
for (int32 m = 0; m < GAMEPROGRESS_MEDAL_COUNT; ++m) {
349
if (m == medalID && type > progress->medals[m])
350
progress->medals[m] = type;
351
352
if (progress->medals[m] >= GAMEPROGRESS_MEDAL_GOLD)
353
++goldCount;
354
355
if (progress->medals[m] >= GAMEPROGRESS_MEDAL_SILVER)
356
++silverCount;
357
}
358
359
progress->goldMedalCount = goldCount;
360
progress->silverMedalCount = silverCount;
361
362
LogHelpers_Print("Get %d medallion #%d", type, medalID);
363
LogHelpers_Print("Gold: %d %d, Silver: %d %d", goldCount, goldCount >= GAMEPROGRESS_MEDAL_COUNT, silverCount,
364
silverCount >= GAMEPROGRESS_MEDAL_COUNT);
365
366
if (goldCount >= GAMEPROGRESS_MEDAL_COUNT)
367
progress->allGoldMedals = true;
368
369
if (silverCount >= GAMEPROGRESS_MEDAL_COUNT)
370
progress->allSilverMedals = true;
371
}
372
373
void GameProgress_GiveEnding(uint8 ending)
374
{
375
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
376
LogHelpers_Print("WARNING GameProgress Attempted to get game ending before loading SaveGame file");
377
}
378
379
ProgressRAM *progress = GameProgress_GetProgressRAM();
380
if (ending > progress->unlockedEndingID)
381
progress->unlockedEndingID = ending;
382
}
383
384
void GameProgress_PrintSaveProgress(void)
385
{
386
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
387
LogHelpers_Print("WARNING GameProgress Attempted to dump before loading SaveGame file");
388
return;
389
}
390
391
ProgressRAM *progress = GameProgress_GetProgressRAM();
392
393
LogHelpers_Print("=========================");
394
LogHelpers_Print("Game Progress:\n");
395
396
for (int32 e = 0; e < GAMEPROGRESS_EMERALD_COUNT; ++e) {
397
if (progress->emeraldObtained[e])
398
LogHelpers_Print("Emerald %d => TRUE", e);
399
else
400
LogHelpers_Print("Emerald %d => FALSE", e);
401
}
402
403
if (progress->allEmeraldsObtained)
404
LogHelpers_Print("ALL EMERALDS!\n");
405
else
406
LogHelpers_Print("YOU'VE NOT ENOUGH EMERALDS!\n");
407
408
for (int32 z = 0; z < GAMEPROGRESS_ZONE_COUNT; ++z) {
409
if (progress->zoneCleared[z])
410
LogHelpers_Print("Zone %d clear => TRUE", z);
411
else
412
LogHelpers_Print("Zone %d clear => FALSE", z);
413
}
414
415
switch (progress->unlockedEndingID) {
416
default:
417
case GAMEPROGRESS_ENDING_NONE: LogHelpers_Print("NO ENDING!\n"); break;
418
419
case GAMEPROGRESS_ENDING_BAD: LogHelpers_Print("BAD ENDING!\n"); break;
420
421
case GAMEPROGRESS_ENDING_GOOD: LogHelpers_Print("GOOD ENDING!\n"); break;
422
}
423
424
for (int32 m = 0; m < GAMEPROGRESS_MEDAL_COUNT; ++m) {
425
switch (progress->medals[m]) {
426
default:
427
case GAMEPROGRESS_MEDAL_GOLD: LogHelpers_Print("Medallion %d => GOLD", m); break;
428
429
case GAMEPROGRESS_MEDAL_SILVER: LogHelpers_Print("Medallion %d => SILVER", m); break;
430
431
case GAMEPROGRESS_MEDAL_NONE: LogHelpers_Print("Medallion %d => NULL", m); break;
432
}
433
}
434
435
LogHelpers_Print("GOLD COUNT: %d", progress->goldMedalCount);
436
if (progress->allGoldMedals)
437
LogHelpers_Print("ALL GOLD MEDALLIONS!");
438
439
LogHelpers_Print("SILVER COUNT: %d", progress->silverMedalCount);
440
if (progress->silverMedalCount)
441
LogHelpers_Print("ALL SILVER MEDALLIONS!");
442
443
LogHelpers_Print("\n=========================");
444
}
445
int32 GameProgress_CountUnreadNotifs(void)
446
{
447
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
448
LogHelpers_Print("WARNING GameProgress Attempted to count unread notifs before loading SaveGame file");
449
return 0;
450
}
451
else {
452
int32 unreadCount = 0;
453
ProgressRAM *progress = GameProgress_GetProgressRAM();
454
for (int32 i = 0; i < GAMEPROGRESS_UNLOCK_COUNT; ++i) {
455
bool32 unlocked = progress->unreadNotifs[i];
456
bool32 notif = GameProgress_CheckUnlock(i);
457
458
if (!unlocked && notif)
459
unreadCount++;
460
}
461
462
return unreadCount;
463
}
464
}
465
int32 GameProgress_GetNextNotif(void)
466
{
467
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
468
LogHelpers_Print("WARNING GameProgress Attempted to get next unread notif before loading SaveGame file");
469
return -1;
470
}
471
else {
472
ProgressRAM *progress = GameProgress_GetProgressRAM();
473
for (int32 i = 0; i < GAMEPROGRESS_UNLOCK_COUNT; ++i) {
474
bool32 unlocked = progress->unreadNotifs[i];
475
bool32 notif = GameProgress_CheckUnlock(i);
476
477
if (!unlocked && notif)
478
return i;
479
}
480
}
481
482
return -1;
483
}
484
bool32 GameProgress_CheckUnlock(uint8 id)
485
{
486
if (SceneInfo->inEditor || API_GetNoSave() || globals->saveLoaded != STATUS_OK) {
487
LogHelpers_Print("WARNING GameProgress Attempted to check unlock before loading SaveGame file");
488
return false;
489
}
490
else {
491
ProgressRAM *progress = GameProgress_GetProgressRAM();
492
switch (id) {
493
case GAMEPROGRESS_UNLOCK_TIMEATTACK:
494
case GAMEPROGRESS_UNLOCK_COMPETITION: return progress->zoneCleared[0];
495
496
case GAMEPROGRESS_UNLOCK_PEELOUT: return progress->silverMedalCount >= 1;
497
498
case GAMEPROGRESS_UNLOCK_INSTASHIELD: return progress->silverMedalCount >= 6;
499
500
case GAMEPROGRESS_UNLOCK_ANDKNUX: return progress->silverMedalCount >= 11;
501
502
case GAMEPROGRESS_UNLOCK_DEBUGMODE: return progress->silverMedalCount >= 16;
503
504
case GAMEPROGRESS_UNLOCK_MEANBEAN: return progress->silverMedalCount >= 21;
505
506
case GAMEPROGRESS_UNLOCK_DAGARDEN: return progress->silverMedalCount >= 26;
507
508
case GAMEPROGRESS_UNLOCK_BLUESPHERES: return progress->silverMedalCount >= GAMEPROGRESS_MEDAL_COUNT;
509
510
default: return false;
511
}
512
}
513
}
514
515
#if GAME_INCLUDE_EDITOR
516
void GameProgress_EditorDraw(void) {}
517
518
void GameProgress_EditorLoad(void) {}
519
#endif
520
void GameProgress_Serialize(void) {}
521
522