Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/Global/ActClear.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: ActClear Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectActClear *ActClear;
11
12
void ActClear_Update(void) {}
13
14
void ActClear_LateUpdate(void)
15
{
16
RSDK_THIS(ActClear);
17
18
StateMachine_Run(self->state);
19
}
20
21
void ActClear_StaticUpdate(void) {}
22
23
void ActClear_Draw(void)
24
{
25
RSDK_THIS(ActClear);
26
27
Vector2 drawPos;
28
Vector2 verts[4];
29
#if MANIA_USE_PLUS
30
int32 milliseconds = 0;
31
int32 seconds = 0;
32
int32 minutes = 0;
33
#endif
34
int32 offset = 0;
35
36
drawPos.x = self->gotThroughPos.x;
37
drawPos.y = self->gotThroughPos.y;
38
verts[0].y = drawPos.y - TO_FIXED(20);
39
verts[1].y = drawPos.y - TO_FIXED(20);
40
verts[2].y = drawPos.y - TO_FIXED(4);
41
verts[3].y = drawPos.y - TO_FIXED(4);
42
if ((GET_CHARACTER_ID(1)) == ID_KNUCKLES) {
43
int32 center = TO_FIXED(ScreenInfo->center.x + 16);
44
45
drawPos.x = 2 * self->gotThroughPos.x + center;
46
verts[0].x = drawPos.x - TO_FIXED(145);
47
verts[1].x = drawPos.x + TO_FIXED(52);
48
verts[2].x = drawPos.x + TO_FIXED(68);
49
verts[3].x = drawPos.x - TO_FIXED(129);
50
RSDK.DrawFace(verts, 4, 0x00, 0x00, 0x00, 0xFF, INK_NONE);
51
52
drawPos.x = 2 * self->playerNamePos.x + center;
53
drawPos.y = self->playerNamePos.y;
54
verts[0].x = drawPos.x - TO_FIXED(124);
55
verts[0].y = drawPos.y + TO_FIXED(28);
56
verts[1].x = drawPos.x + TO_FIXED(80);
57
verts[2].x = drawPos.x + TO_FIXED(96);
58
verts[3].x = drawPos.x - TO_FIXED(108);
59
verts[1].y = drawPos.y + TO_FIXED(28);
60
verts[2].y = drawPos.y + TO_FIXED(44);
61
verts[3].y = drawPos.y + TO_FIXED(44);
62
RSDK.DrawFace(verts, 4, 0x00, 0x00, 0x00, 0xFF, INK_NONE);
63
64
drawPos.x = self->playerNamePos.x + center;
65
drawPos.y = self->playerNamePos.y;
66
RSDK.DrawSprite(&self->playerNameAnimator, &drawPos, true);
67
68
self->gotThroughAnimator.frameID = 2;
69
RSDK.DrawSprite(&self->gotThroughAnimator, &drawPos, true);
70
71
drawPos.x = self->gotThroughPos.x + center;
72
drawPos.y = self->gotThroughPos.y;
73
self->gotThroughAnimator.frameID = 3;
74
RSDK.DrawSprite(&self->gotThroughAnimator, &drawPos, true);
75
RSDK.DrawSprite(&self->actNumAnimator, &drawPos, true);
76
77
offset = center - TO_FIXED(10);
78
}
79
else {
80
int32 center = TO_FIXED(ScreenInfo->center.x);
81
82
drawPos.x = 2 * self->gotThroughPos.x + center;
83
verts[0].x = drawPos.x - TO_FIXED(109);
84
verts[1].x = drawPos.x + TO_FIXED(52);
85
verts[2].x = drawPos.x + TO_FIXED(68);
86
verts[3].x = drawPos.x - TO_FIXED(93);
87
RSDK.DrawFace(verts, 4, 0x00, 0x00, 0x00, 0xFF, INK_NONE);
88
89
drawPos.x = 2 * self->playerNamePos.x + center;
90
drawPos.y = self->playerNamePos.y;
91
verts[0].x = drawPos.x - TO_FIXED(88);
92
verts[0].y = drawPos.y + TO_FIXED(28);
93
verts[1].x = drawPos.x + TO_FIXED(80);
94
verts[2].x = drawPos.x + TO_FIXED(96);
95
verts[3].x = drawPos.x - TO_FIXED(72);
96
verts[1].y = drawPos.y + TO_FIXED(28);
97
verts[2].y = drawPos.y + TO_FIXED(44);
98
verts[3].y = drawPos.y + TO_FIXED(44);
99
RSDK.DrawFace(verts, 4, 0x00, 0x00, 0x00, 0xFF, INK_NONE);
100
101
drawPos.x = self->playerNamePos.x + center;
102
drawPos.y = self->playerNamePos.y;
103
RSDK.DrawSprite(&self->playerNameAnimator, &drawPos, true);
104
105
#if MANIA_USE_PLUS
106
if ((GET_CHARACTER_ID(1)) == ID_MIGHTY)
107
self->gotThroughAnimator.frameID = 2;
108
else
109
#endif
110
self->gotThroughAnimator.frameID = 0;
111
RSDK.DrawSprite(&self->gotThroughAnimator, &drawPos, true);
112
113
drawPos.x = self->gotThroughPos.x + center;
114
drawPos.y = self->gotThroughPos.y;
115
self->gotThroughAnimator.frameID = 1;
116
RSDK.DrawSprite(&self->gotThroughAnimator, &drawPos, true);
117
RSDK.DrawSprite(&self->actNumAnimator, &drawPos, true);
118
119
offset = center + TO_FIXED(6);
120
}
121
122
// Draw "Time" Bonus Sprite
123
drawPos.x = offset + self->timeBonusPos.x - TO_FIXED(92);
124
drawPos.y = self->timeBonusPos.y;
125
self->hudElementsAnimator.frameID = 1;
126
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
127
128
// Draw "Bonus" Sprite
129
drawPos.x += TO_FIXED(50);
130
#if MANIA_USE_PLUS
131
if (globals->gameMode != MODE_TIMEATTACK) {
132
#endif
133
self->hudElementsAnimator.frameID = 8; // "Bonus"
134
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
135
#if MANIA_USE_PLUS
136
}
137
#endif
138
139
// Draw Time Bonus BG thingy
140
self->hudElementsAnimator.frameID = 10;
141
drawPos.x += TO_FIXED(52);
142
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
143
144
drawPos.x += TO_FIXED(67);
145
drawPos.y += TO_FIXED(14);
146
147
#if MANIA_USE_PLUS
148
if (globals->gameMode == MODE_TIMEATTACK) {
149
drawPos.x -= TO_FIXED(98);
150
drawPos.y -= TO_FIXED(14);
151
// Draw Current Time
152
ActClear_DrawTime(&drawPos, SceneInfo->minutes, SceneInfo->seconds, SceneInfo->milliseconds);
153
}
154
else {
155
#endif
156
ActClear_DrawNumbers(&drawPos, self->timeBonus, 0); // Draw Time Bonus
157
#if MANIA_USE_PLUS
158
}
159
#endif
160
161
// Draw Ring Bonus
162
drawPos.x = offset + self->ringBonusPos.x - TO_FIXED(92);
163
drawPos.y = self->ringBonusPos.y;
164
#if MANIA_USE_PLUS
165
if (globals->gameMode == MODE_TIMEATTACK)
166
self->hudElementsAnimator.frameID = 17; // "Best"
167
else
168
#endif
169
self->hudElementsAnimator.frameID = 5; // "Ring"
170
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
171
172
drawPos.x += TO_FIXED(50);
173
#if MANIA_USE_PLUS
174
if (globals->gameMode == MODE_TIMEATTACK) {
175
drawPos.x -= TO_FIXED(10);
176
self->hudElementsAnimator.frameID = 1; // "Time"
177
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
178
drawPos.x += TO_FIXED(10);
179
}
180
else {
181
#endif
182
self->hudElementsAnimator.frameID = 8; // "Bonus"
183
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
184
#if MANIA_USE_PLUS
185
}
186
#endif
187
188
// Draw Ring Bonus BG thingy
189
self->hudElementsAnimator.frameID = 10;
190
drawPos.x += TO_FIXED(52);
191
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
192
193
drawPos.x += TO_FIXED(67);
194
drawPos.y += TO_FIXED(14);
195
#if MANIA_USE_PLUS
196
if (globals->gameMode == MODE_TIMEATTACK) { // Draw Best Time
197
TimeAttackData_GetUnpackedTime(self->time, &minutes, &seconds, &milliseconds);
198
drawPos.x -= TO_FIXED(98);
199
drawPos.y -= TO_FIXED(14);
200
if (!self->isNewRecord || (self->isNewRecord && (Zone->timer & 8)))
201
ActClear_DrawTime(&drawPos, minutes, seconds, milliseconds);
202
}
203
else {
204
#endif
205
ActClear_DrawNumbers(&drawPos, self->ringBonus, 0);
206
#if MANIA_USE_PLUS
207
}
208
#endif
209
210
if (self->showCoolBonus) { // Draw Cool Bonus
211
drawPos.x = offset + self->coolBonusPos.x - TO_FIXED(92);
212
drawPos.y = self->coolBonusPos.y;
213
#if MANIA_USE_PLUS
214
if (globals->gameMode == MODE_TIMEATTACK)
215
self->hudElementsAnimator.frameID = 18; // "Rank"
216
else
217
#endif
218
self->hudElementsAnimator.frameID = 15; // "Cool"
219
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
220
221
drawPos.x += TO_FIXED(50);
222
#if MANIA_USE_PLUS
223
if (globals->gameMode != MODE_TIMEATTACK) {
224
#endif
225
self->hudElementsAnimator.frameID = 8; // "Bonus"
226
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
227
#if MANIA_USE_PLUS
228
}
229
#endif
230
231
// Draw Cool Bonus BG thingy
232
self->hudElementsAnimator.frameID = 10;
233
drawPos.x += TO_FIXED(52);
234
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
235
236
drawPos.x += TO_FIXED(67);
237
drawPos.y += TO_FIXED(14);
238
#if MANIA_USE_PLUS
239
if (globals->gameMode != MODE_TIMEATTACK) {
240
#endif
241
ActClear_DrawNumbers(&drawPos, self->coolBonus, 0); // Draw Cool bonus
242
#if MANIA_USE_PLUS
243
}
244
else {
245
// Draw Rank
246
if (!TimeAttackData->personalRank) {
247
self->numbersAnimator.frameID = 16;
248
RSDK.DrawSprite(&self->numbersAnimator, &drawPos, true);
249
250
drawPos.x -= TO_FIXED(9);
251
}
252
else if (!self->achievedRank || (self->achievedRank && (Zone->timer & 8)))
253
ActClear_DrawNumbers(&drawPos, TimeAttackData->personalRank, 0);
254
}
255
#endif
256
}
257
258
drawPos.x = self->totalScorePos.x;
259
drawPos.y = self->totalScorePos.y;
260
261
#if MANIA_USE_PLUS
262
if (globals->gameMode == MODE_TIMEATTACK) {
263
// Draw World Rank
264
drawPos.x = offset + self->totalScorePos.x - TO_FIXED(92);
265
self->hudElementsAnimator.frameID = 19; // "World"
266
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
267
268
drawPos.x += TO_FIXED(50);
269
self->hudElementsAnimator.frameID = 18; // "Rank"
270
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
271
272
// Draw World Rank BG thingy
273
self->hudElementsAnimator.frameID = 10;
274
drawPos.x += TO_FIXED(52);
275
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
276
277
drawPos.x += TO_FIXED(67);
278
drawPos.y += TO_FIXED(14);
279
if (!TimeAttackData->leaderboardRank) {
280
self->numbersAnimator.frameID = 16; // "-" (no rank)
281
RSDK.DrawSprite(&self->numbersAnimator, &drawPos, true);
282
}
283
else {
284
// Draw Rank
285
ActClear_DrawNumbers(&drawPos, TimeAttackData->leaderboardRank, 0);
286
}
287
}
288
else {
289
#endif
290
// Draw Total Score
291
drawPos.x = offset + self->totalScorePos.x - TO_FIXED(68);
292
self->hudElementsAnimator.frameID = 9; // "Total"
293
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
294
295
// Draw Total Score BG thingy
296
self->hudElementsAnimator.frameID = 10;
297
drawPos.x += TO_FIXED(52);
298
RSDK.DrawSprite(&self->hudElementsAnimator, &drawPos, true);
299
300
// Draw Total Score
301
drawPos.x += TO_FIXED(67);
302
drawPos.y += TO_FIXED(14);
303
ActClear_DrawNumbers(&drawPos, self->totalScore, 0);
304
#if MANIA_USE_PLUS
305
}
306
#endif
307
}
308
309
void ActClear_Create(void *data)
310
{
311
RSDK_THIS(ActClear);
312
if (!SceneInfo->inEditor) {
313
#if MANIA_USE_PLUS
314
ActClear->actClearActive = true;
315
#endif
316
self->active = ACTIVE_NORMAL;
317
self->visible = true;
318
self->drawGroup = Zone->hudDrawGroup;
319
self->state = ActClear_State_EnterText;
320
self->stageFinishTimer = 0;
321
self->newRecordTimer = 0;
322
323
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
324
self->targetPlayer = player1;
325
326
if (Zone_GetZoneID() > ZONE_INVALID) {
327
uint16 time = TimeAttackData_GetPackedTime(SceneInfo->minutes, SceneInfo->seconds, SceneInfo->milliseconds);
328
329
#if MANIA_USE_PLUS
330
StatInfo stat;
331
switch (GET_CHARACTER_ID(1)) {
332
case ID_SONIC: TimeAttackData_TrackActClear(&stat, Zone_GetZoneID(), Zone->actID, 1, time, player1->rings, player1->score); break;
333
case ID_TAILS: TimeAttackData_TrackActClear(&stat, Zone_GetZoneID(), Zone->actID, 2, time, player1->rings, player1->score); break;
334
case ID_KNUCKLES: TimeAttackData_TrackActClear(&stat, Zone_GetZoneID(), Zone->actID, 3, time, player1->rings, player1->score); break;
335
case ID_MIGHTY: TimeAttackData_TrackActClear(&stat, Zone_GetZoneID(), Zone->actID, 4, time, player1->rings, player1->score); break;
336
default:
337
case ID_RAY:
338
if ((GET_CHARACTER_ID(1)) == ID_RAY)
339
TimeAttackData_TrackActClear(&stat, Zone_GetZoneID(), Zone->actID, 5, time, player1->rings, player1->score);
340
else
341
TimeAttackData_TrackActClear(&stat, Zone_GetZoneID(), Zone->actID, 1, time, player1->rings, player1->score);
342
break;
343
}
344
API.TryTrackStat(&stat);
345
#else
346
switch (GET_CHARACTER_ID(1)) {
347
case ID_SONIC: APICallback_TrackActClear(Zone_GetZoneID(), Zone->actID, 1, time, player1->rings, player1->score); break;
348
case ID_TAILS: APICallback_TrackActClear(Zone_GetZoneID(), Zone->actID, 2, time, player1->rings, player1->score); break;
349
case ID_KNUCKLES: APICallback_TrackActClear(Zone_GetZoneID(), Zone->actID, 3, time, player1->rings, player1->score); break;
350
default: break;
351
}
352
#endif
353
}
354
355
#if MANIA_USE_PLUS
356
if (!ActClear->disableTimeBonus) {
357
#endif
358
switch (SceneInfo->minutes) {
359
case 0:
360
if (SceneInfo->seconds >= 30)
361
self->timeBonus = SceneInfo->seconds < 45 ? 10000 : 5000;
362
else
363
self->timeBonus = 50000;
364
break;
365
366
case 1: self->timeBonus = SceneInfo->seconds < 30 ? 4000 : 3000; break;
367
case 2: self->timeBonus = 2000; break;
368
case 3: self->timeBonus = 1000; break;
369
case 4: self->timeBonus = 500; break;
370
case 5: self->timeBonus = 100; break;
371
372
case 9:
373
if (!SceneInfo->debugMode && globals->gameMode < MODE_TIMEATTACK && SceneInfo->seconds == 59) {
374
#if MANIA_USE_PLUS
375
if (globals->gameMode != MODE_ENCORE && !(globals->medalMods & MEDAL_NOTIMEOVER))
376
#endif
377
self->timeBonus = 100000;
378
}
379
break;
380
381
default: break;
382
}
383
#if MANIA_USE_PLUS
384
}
385
#endif
386
387
self->ringBonus = 100 * player1->rings;
388
self->coolBonus = globals->coolBonus[0];
389
globals->initCoolBonus = false;
390
#if MANIA_USE_PLUS
391
if (globals->gameMode == MODE_TIMEATTACK) {
392
EntityMenuParam *param = MenuParam_GetParam();
393
self->time =
394
TimeAttackData_GetScore(param->zoneID, param->actID, param->characterID, SceneInfo->filter == (FILTER_BOTH | FILTER_ENCORE), 1);
395
self->achievedRank = false;
396
self->isNewRecord = false;
397
}
398
#endif
399
self->showCoolBonus = true;
400
self->playerNamePos.x = TO_FIXED(224);
401
self->playerNamePos.y = TO_FIXED(88);
402
self->gotThroughPos.x = -TO_FIXED(224);
403
self->gotThroughPos.y = TO_FIXED(112);
404
self->timeBonusPos.x = TO_FIXED(488);
405
self->timeBonusPos.y = TO_FIXED(120);
406
self->ringBonusPos.x = TO_FIXED(776);
407
self->ringBonusPos.y = TO_FIXED(136);
408
self->coolBonusPos.x = TO_FIXED(1064);
409
self->coolBonusPos.y = TO_FIXED(152);
410
self->totalScorePos.x = -TO_FIXED(1352);
411
self->totalScorePos.y = TO_FIXED(192);
412
413
RSDK.SetSpriteAnimation(ActClear->aniFrames, 0, &self->hudElementsAnimator, true, 0);
414
RSDK.SetSpriteAnimation(ActClear->aniFrames, 1, &self->numbersAnimator, true, 0);
415
#if MANIA_USE_PLUS
416
RSDK.SetSpriteAnimation(ActClear->aniFrames, 0, &self->timeElementsAnimator, true, 12);
417
#endif
418
419
switch (GET_CHARACTER_ID(1)) {
420
default:
421
case ID_SONIC: RSDK.SetSpriteAnimation(ActClear->aniFrames, 3, &self->playerNameAnimator, true, 0); break;
422
case ID_TAILS: RSDK.SetSpriteAnimation(ActClear->aniFrames, 3, &self->playerNameAnimator, true, 1); break;
423
case ID_KNUCKLES: RSDK.SetSpriteAnimation(ActClear->aniFrames, 3, &self->playerNameAnimator, true, 2); break;
424
#if MANIA_USE_PLUS
425
case ID_MIGHTY: RSDK.SetSpriteAnimation(ActClear->aniFrames, 3, &self->playerNameAnimator, true, 3); break;
426
case ID_RAY: RSDK.SetSpriteAnimation(ActClear->aniFrames, 3, &self->playerNameAnimator, true, 4); break;
427
#endif
428
}
429
RSDK.SetSpriteAnimation(ActClear->aniFrames, 4, &self->gotThroughAnimator, true, 0);
430
431
// Used in cases like OOZ1 outro where the act clear actually happens in OOZ2
432
if (ActClear->displayedActID <= 0)
433
RSDK.SetSpriteAnimation(ActClear->aniFrames, 5, &self->actNumAnimator, true, Zone->actID);
434
else
435
RSDK.SetSpriteAnimation(ActClear->aniFrames, 5, &self->actNumAnimator, true, ActClear->displayedActID - 1);
436
}
437
}
438
439
void ActClear_StageLoad(void)
440
{
441
ActClear->aniFrames = RSDK.LoadSpriteAnimation("Global/HUD.bin", SCOPE_STAGE);
442
443
ActClear->sfxScoreAdd = RSDK.GetSfx("Global/ScoreAdd.wav");
444
ActClear->sfxScoreTotal = RSDK.GetSfx("Global/ScoreTotal.wav");
445
#if MANIA_USE_PLUS
446
ActClear->sfxEvent = RSDK.GetSfx("Special/Event.wav");
447
448
ActClear->actClearActive = false;
449
ActClear->forceNoSave = false;
450
#endif
451
}
452
453
#if MANIA_USE_PLUS
454
void ActClear_DrawTime(Vector2 *drawPosPtr, int32 mins, int32 secs, int32 millisecs)
455
{
456
RSDK_THIS(ActClear);
457
458
Vector2 drawPos;
459
if (!mins && !secs && !millisecs) {
460
millisecs = -1;
461
secs = -1;
462
mins = -1;
463
}
464
465
// The ":" thing
466
drawPos.x = drawPosPtr->x + TO_FIXED(50);
467
drawPos.y = drawPosPtr->y - TO_FIXED(2);
468
RSDK.DrawSprite(&self->timeElementsAnimator, &drawPos, true);
469
470
// Miliseconds
471
drawPos.x = drawPosPtr->x + TO_FIXED(97);
472
drawPos.y = drawPosPtr->y + TO_FIXED(14);
473
ActClear_DrawNumbers(&drawPos, millisecs, 2);
474
475
// Seconds
476
drawPos.x -= TO_FIXED(9);
477
if (!mins && !secs && !millisecs)
478
secs = -1;
479
ActClear_DrawNumbers(&drawPos, secs, 2);
480
481
// Minutes
482
drawPos.x -= TO_FIXED(9);
483
if (!mins && !secs && !millisecs)
484
mins = -1;
485
ActClear_DrawNumbers(&drawPos, mins, 1);
486
}
487
#endif
488
489
void ActClear_DrawNumbers(Vector2 *drawPos, int32 value, int32 digitCount)
490
{
491
RSDK_THIS(ActClear);
492
493
if (value >= 0) {
494
if (!digitCount && value > 0) {
495
int32 v = value;
496
while (v > 0) {
497
++digitCount;
498
v /= 10;
499
}
500
}
501
else {
502
if (!digitCount && !value)
503
digitCount = 1;
504
}
505
506
if (digitCount > 0) {
507
int32 digit = 1;
508
while (digitCount--) {
509
self->numbersAnimator.frameID = value / digit % 10;
510
RSDK.DrawSprite(&self->numbersAnimator, drawPos, true);
511
512
drawPos->x -= TO_FIXED(9);
513
digit *= 10;
514
}
515
}
516
}
517
else {
518
while (digitCount--) {
519
self->numbersAnimator.frameID = 16;
520
RSDK.DrawSprite(&self->numbersAnimator, drawPos, true);
521
522
drawPos->x -= TO_FIXED(9);
523
}
524
}
525
}
526
void ActClear_CheckPlayerVictory(void)
527
{
528
foreach_active(Player, player)
529
{
530
if (player->state != Player_State_FlyToPlayer && player->state != Player_State_ReturnToPlayer && player->state != Player_State_Victory
531
&& player->onGround) {
532
player->state = Player_State_Victory;
533
player->nextAirState = StateMachine_None;
534
player->nextGroundState = StateMachine_None;
535
if (Zone->shouldRecoverPlayers)
536
player->stateInput = StateMachine_None;
537
538
RSDK.SetSpriteAnimation(player->aniFrames, ANI_VICTORY, &player->animator, true, 0);
539
}
540
}
541
}
542
543
#if MANIA_USE_PLUS
544
void ActClear_SaveGameCallback(bool32 success)
545
{
546
UIWaitSpinner_FinishWait();
547
ActClear->isSavingGame = false;
548
}
549
#else
550
void ActClear_SaveGameCallback(void)
551
{
552
UIWaitSpinner_FinishWait();
553
ActClear->isSavingGame = false;
554
}
555
#endif
556
557
void ActClear_SetupRecoverPlayers(void)
558
{
559
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
560
EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);
561
562
if (player2 && player2->sidekick) {
563
if (player2->state != Player_State_FlyToPlayer && player2->state != Player_State_ReturnToPlayer) {
564
if (player2->position.x <= TO_FIXED(ScreenInfo->size.x + ScreenInfo->position.x)
565
|| abs(player2->position.y - player1->position.y) > TO_FIXED(16)) {
566
Player->respawnTimer = 240;
567
568
Entity *entStore = SceneInfo->entity;
569
SceneInfo->entity = (Entity *)player2;
570
Player_HandleSidekickRespawn();
571
SceneInfo->entity = entStore;
572
573
if (player2->state == Player_State_FlyToPlayer || player2->state == Player_State_ReturnToPlayer
574
|| player2->state == Player_State_HoldRespawn) {
575
player2->active = ACTIVE_NORMAL;
576
player2->position.y = TO_FIXED(ScreenInfo->position.y - 16);
577
}
578
}
579
else {
580
Player->respawnTimer = -3600;
581
}
582
}
583
}
584
}
585
586
void ActClear_State_EnterText(void)
587
{
588
RSDK_THIS(ActClear);
589
590
if (self->playerNamePos.x > 0)
591
self->playerNamePos.x -= TO_FIXED(16);
592
593
if (self->gotThroughPos.x < 0)
594
self->gotThroughPos.x += TO_FIXED(16);
595
596
if (!self->timer && Zone->shouldRecoverPlayers)
597
ActClear_SetupRecoverPlayers();
598
599
if (++self->timer == 48) {
600
self->timer = 0;
601
self->state = ActClear_State_AdjustText;
602
}
603
604
ActClear_CheckPlayerVictory();
605
}
606
607
void ActClear_State_AdjustText(void)
608
{
609
RSDK_THIS(ActClear);
610
611
self->playerNamePos.y -= 0x8000;
612
self->gotThroughPos.y -= 0x8000;
613
614
if (++self->timer == 48) {
615
self->timer = 0;
616
self->state = ActClear_State_EnterResults;
617
}
618
619
ActClear_CheckPlayerVictory();
620
}
621
622
void ActClear_State_EnterResults(void)
623
{
624
RSDK_THIS(ActClear);
625
626
if (self->timeBonusPos.x > 0)
627
self->timeBonusPos.x -= TO_FIXED(16);
628
629
if (self->ringBonusPos.x > 0)
630
self->ringBonusPos.x -= TO_FIXED(16);
631
632
if (self->coolBonusPos.x > 0)
633
self->coolBonusPos.x -= TO_FIXED(16);
634
635
if (self->totalScorePos.x < -TO_FIXED(8)) {
636
self->totalScorePos.x += TO_FIXED(16);
637
}
638
else {
639
#if MANIA_USE_PLUS
640
if (globals->gameMode == MODE_TIMEATTACK) {
641
if (ActClear->bufferMoveEnabled) {
642
StateMachine_Run(ActClear->bufferMove_CB);
643
}
644
645
HUD->showTAPrompt = true;
646
ActClear->hasSavedReplay = false;
647
self->newRecordTimer = 240;
648
self->state = ActClear_State_ShowResultsTA;
649
RSDK.SetScene("Presentation", "Menu");
650
}
651
else {
652
self->state = ActClear_State_ScoreShownDelay;
653
}
654
#else
655
self->state = ActClear_State_ScoreShownDelay;
656
#endif
657
}
658
659
ActClear_CheckPlayerVictory();
660
}
661
662
void ActClear_State_ScoreShownDelay(void)
663
{
664
RSDK_THIS(ActClear);
665
666
if (++self->timer == 120) {
667
self->timer = 0;
668
self->state = ActClear_State_TallyScore;
669
}
670
671
ActClear_CheckPlayerVictory();
672
}
673
674
void ActClear_State_TallyScore(void)
675
{
676
RSDK_THIS(ActClear);
677
678
EntityPlayer *player = self->targetPlayer;
679
680
if (self->timeBonus > 0) {
681
self->totalScore += 100;
682
self->timeBonus -= 100;
683
Player_GiveScore(player, 100);
684
}
685
686
if (self->ringBonus > 0) {
687
self->totalScore += 100;
688
self->ringBonus -= 100;
689
Player_GiveScore(player, 100);
690
}
691
692
if (self->coolBonus > 0) {
693
self->totalScore += 100;
694
self->coolBonus -= 100;
695
Player_GiveScore(player, 100);
696
}
697
698
if (ControllerInfo[player->controllerID].keyA.press || ControllerInfo[player->controllerID].keyStart.press) {
699
Player_GiveScore(player, self->timeBonus + self->ringBonus + self->coolBonus);
700
self->totalScore += self->timeBonus + self->ringBonus + self->coolBonus;
701
self->timeBonus = 0;
702
self->ringBonus = 0;
703
self->coolBonus = 0;
704
}
705
706
if (self->timeBonus + self->ringBonus + self->coolBonus <= 0) {
707
self->timer = 0;
708
self->state = ActClear_State_SaveGameProgress;
709
RSDK.PlaySfx(ActClear->sfxScoreTotal, false, 255);
710
}
711
else if (++self->timer == 2) {
712
self->timer = 0;
713
RSDK.PlaySfx(ActClear->sfxScoreAdd, false, 255);
714
}
715
716
Music->nextTrack = TRACK_NONE;
717
ActClear_CheckPlayerVictory();
718
}
719
720
void ActClear_State_SaveGameProgress(void)
721
{
722
RSDK_THIS(ActClear);
723
724
if (++self->timer == 120) {
725
self->timer = 0;
726
globals->specialRingID = 0;
727
if (ActClear->displayedActID <= 0) {
728
if (globals->gameMode == MODE_COMPETITION) {
729
RSDK.SetScene("Presentation", "Menu");
730
}
731
#if !MANIA_USE_PLUS
732
else if (globals->gameMode == MODE_TIMEATTACK) {
733
EntityMenuParam *param = MenuParam_GetParam();
734
ActClear->isSavingGame = true;
735
uint8 characterID = param->characterID;
736
uint8 zoneID = param->zoneID;
737
uint8 act = param->actID;
738
739
int32 time = 6000 * SceneInfo->minutes + 100 * SceneInfo->seconds + SceneInfo->milliseconds;
740
741
uint16 *record = TimeAttackData_GetRecordedTime(zoneID, act, characterID, 1);
742
int32 rank = 0;
743
for (; rank < 3; ++rank) {
744
if (!record[rank] || time < record[rank])
745
break;
746
}
747
748
if (rank < 3) {
749
rank++;
750
TimeAttackData_AddRecord(zoneID, act, characterID, rank, time);
751
APICallback_TrackTAClear(zoneID, act, characterID, time);
752
param->timeScore = rank;
753
SaveGame_SaveFile(ActClear_SaveGameCallback);
754
}
755
else {
756
ActClear->isSavingGame = false;
757
}
758
759
RSDK.SetScene("Presentation", "Menu");
760
}
761
#endif
762
else {
763
globals->enableIntro = true;
764
Player_SaveValues();
765
SaveGame_ClearRestartData();
766
StarPost_ResetStarPosts();
767
if (Zone->actID > 0)
768
SaveGame_ClearCollectedSpecialRings();
769
SaveGame_SaveProgress();
770
#if MANIA_USE_PLUS
771
if (globals->saveSlotID != NO_SAVE_SLOT && !ActClear->forceNoSave) {
772
#else
773
if (globals->gameMode == MODE_MANIA) {
774
#endif
775
if (Zone_IsZoneLastAct())
776
GameProgress_MarkZoneCompleted(Zone_GetZoneID());
777
778
ActClear->isSavingGame = true;
779
SaveGame_SaveFile(ActClear_SaveGameCallback);
780
}
781
782
++SceneInfo->listPos;
783
if (!RSDK.CheckValidScene())
784
RSDK.SetScene("Presentation", "Title Screen");
785
}
786
}
787
else {
788
Player_SaveValues();
789
SaveGame_ClearRestartData();
790
StarPost_ResetStarPosts();
791
SaveGame_SaveProgress();
792
793
#if MANIA_USE_PLUS
794
if (globals->saveSlotID != NO_SAVE_SLOT && !ActClear->forceNoSave) {
795
#else
796
if (globals->gameMode == MODE_MANIA) {
797
#endif
798
ActClear->isSavingGame = true;
799
SaveGame_SaveFile(ActClear_SaveGameCallback);
800
}
801
}
802
803
if (ActClear->isSavingGame)
804
UIWaitSpinner_StartWait();
805
806
self->state = ActClear_State_WaitForSave;
807
ActClear_State_WaitForSave();
808
}
809
}
810
811
#if MANIA_USE_PLUS
812
void ActClear_State_ShowResultsTA(void)
813
{
814
RSDK_THIS(ActClear);
815
816
if (self->newRecordTimer > 0) {
817
if (TimeAttackData->personalRank > 0 && !ReplayRecorder->hasSetupGhostView) {
818
if (self->newRecordTimer == 120) {
819
if (TimeAttackData->personalRank == 1)
820
self->isNewRecord = true;
821
822
self->achievedRank = true;
823
RSDK.PlaySfx(ActClear->sfxEvent, false, 255);
824
}
825
826
if (self->newRecordTimer == 30) {
827
if (TimeAttackData->personalRank == 1)
828
RSDK.PlaySfx(Announcer->sfxNewRecordTop, false, 255);
829
else if (TimeAttackData->personalRank <= 3)
830
RSDK.PlaySfx(Announcer->sfxNewRecordMid, false, 255);
831
}
832
}
833
--self->newRecordTimer;
834
}
835
836
if (!ActClear->isSavingGame && !ActClear->disableResultsInput) {
837
if (ControllerInfo->keyY.press) {
838
if (!ActClear->hasSavedReplay) {
839
if (HUD->replaySaveEnabled) {
840
if (!UIDialog->activeDialog) {
841
if (API.CheckDLC(DLC_PLUS)) {
842
StateMachine_Run(ActClear->saveReplay_CB);
843
ActClear->hasSavedReplay = true;
844
ActClear->disableResultsInput = true;
845
return;
846
}
847
}
848
}
849
}
850
}
851
852
if (ControllerInfo->keyStart.press) {
853
RSDK.PlaySfx(UIWidgets->sfxAccept, false, 255);
854
855
ActClear_State_WaitForSave();
856
}
857
}
858
}
859
#endif
860
861
void ActClear_State_WaitForSave(void)
862
{
863
RSDK_THIS(ActClear);
864
865
if (!ActClear->isSavingGame) {
866
if (ActClear->displayedActID > 0 || Zone->stageFinishCallback) {
867
self->state = ActClear_State_ExitActClear;
868
}
869
else {
870
self->state = StateMachine_None;
871
Zone_StartFadeOut(10, 0x000000);
872
}
873
}
874
}
875
876
void ActClear_State_ExitActClear(void)
877
{
878
RSDK_THIS(ActClear);
879
880
self->playerNamePos.x += TO_FIXED(32);
881
self->gotThroughPos.x -= TO_FIXED(32);
882
883
if (self->playerNamePos.x > TO_FIXED(64))
884
self->timeBonusPos.x += TO_FIXED(32);
885
886
if (self->timeBonusPos.x > TO_FIXED(64))
887
self->ringBonusPos.x += TO_FIXED(32);
888
889
if (self->ringBonusPos.x > TO_FIXED(64))
890
self->coolBonusPos.x += TO_FIXED(32);
891
892
if (self->coolBonusPos.x > TO_FIXED(64))
893
self->totalScorePos.x -= TO_FIXED(32);
894
895
if (self->totalScorePos.x < -TO_FIXED(512)) {
896
if (ActClear->displayedActID <= 0) {
897
if (Zone->stageFinishCallback) {
898
if (Zone->shouldRecoverPlayers) {
899
self->timer = 0;
900
self->state = ActClear_State_RecoverPlayers;
901
}
902
else {
903
foreach_active(Animals, animal)
904
{
905
if (animal->behaviour == ANIMAL_BEHAVE_FOLLOW)
906
animal->behaviour = ANIMAL_BEHAVE_FREE;
907
}
908
#if RETRO_USE_MOD_LOADER
909
StateMachine_Run(Zone->stageFinishCallback);
910
#else
911
Zone->stageFinishCallback();
912
#endif
913
Zone->stageFinishCallback = StateMachine_None;
914
}
915
}
916
}
917
else {
918
ActClear->finished = true;
919
ActClear->displayedActID = 0;
920
SceneInfo->milliseconds = 0;
921
SceneInfo->seconds = 0;
922
SceneInfo->minutes = 0;
923
foreach_active(Player, player)
924
{
925
player->ringExtraLife = 100;
926
player->rings = 0;
927
}
928
}
929
930
if (self->state != ActClear_State_RecoverPlayers)
931
destroyEntity(self);
932
}
933
}
934
935
void ActClear_State_RecoverPlayers(void)
936
{
937
RSDK_THIS(ActClear);
938
939
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
940
EntityPlayer *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, Player);
941
942
bool32 finishedP2 = false;
943
bool32 finishedP1 = false;
944
int32 screenOffX = TO_FIXED(ScreenInfo->position.x + ScreenInfo->size.x - 16);
945
player1->up = false;
946
player1->down = false;
947
player1->jumpPress = false;
948
player1->jumpHold = false;
949
950
if (player1->position.x >= screenOffX) {
951
player1->stateInput = StateMachine_None;
952
player1->state = Player_State_Ground;
953
player1->groundVel = -0x40000;
954
player1->left = true;
955
player1->right = false;
956
}
957
else {
958
if ((!player1->onGround || player1->groundVel) && player1->position.x < screenOffX - (ScreenInfo->center.x << 15)) {
959
player1->right = true;
960
if (!player1->skidding) {
961
if (!player1->left)
962
player1->right = false;
963
else
964
player1->left = false;
965
}
966
}
967
968
if (player1->onGround && !player1->groundVel)
969
finishedP1 = true;
970
}
971
972
if (player2->classID != Player->classID) {
973
finishedP2 = true;
974
}
975
else {
976
player2->up = false;
977
player2->down = false;
978
player2->jumpPress = false;
979
player2->jumpHold = false;
980
981
if (player2->state == Player_State_FlyToPlayer || player2->state == Player_State_ReturnToPlayer) {
982
if (player2->position.x < screenOffX) {
983
if (player2->onGround && !player2->groundVel) {
984
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, false, 0);
985
player2->direction = FLIP_NONE;
986
finishedP2 = true;
987
}
988
}
989
}
990
else if (player2->position.x >= screenOffX) {
991
player2->stateInput = Player_Input_P2_AI;
992
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_RUN, &player2->animator, false, 0);
993
player2->state = Player_State_Ground;
994
player2->groundVel = -TO_FIXED(4);
995
player2->left = true;
996
player2->right = false;
997
998
if (player2->position.x < screenOffX) {
999
if (player2->onGround && !player2->groundVel) {
1000
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, false, 0);
1001
player2->direction = FLIP_NONE;
1002
finishedP2 = true;
1003
}
1004
}
1005
}
1006
else {
1007
if (player2->onGround && !player2->groundVel) {
1008
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, false, 0);
1009
player2->direction = FLIP_NONE;
1010
finishedP2 = true;
1011
}
1012
}
1013
}
1014
1015
if (finishedP1) {
1016
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, false, 0);
1017
player1->direction = FLIP_NONE;
1018
}
1019
1020
++self->stageFinishTimer;
1021
if ((finishedP1 && finishedP2) || self->stageFinishTimer >= 900) {
1022
if (self->timer >= 10) {
1023
Player->respawnTimer = 0;
1024
StateMachine_Run(Zone->stageFinishCallback);
1025
Zone->stageFinishCallback = StateMachine_None;
1026
destroyEntity(self);
1027
}
1028
else {
1029
self->timer++;
1030
}
1031
}
1032
}
1033
1034
#if GAME_INCLUDE_EDITOR
1035
void ActClear_EditorDraw(void) {}
1036
1037
void ActClear_EditorLoad(void) {}
1038
#endif
1039
1040
void ActClear_Serialize(void) {}
1041
1042