Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/ERZ/ERZOutro.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: ERZOutro Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectERZOutro *ERZOutro;
11
12
void ERZOutro_Update(void)
13
{
14
RSDK_THIS(ERZOutro);
15
16
if (!self->activated) {
17
CutsceneSeq_StartSequence(self, ERZOutro_Cutscene_AttackEggman, ERZOutro_Cutscene_AttackRecoil, ERZOutro_Cutscene_LoseEmeralds,
18
ERZOutro_Cutscene_OpenPortal, ERZOutro_Cutscene_EnterPortal, ERZOutro_Cutscene_FadeOut,
19
ERZOutro_Cutscene_ShowEnding, StateMachine_None);
20
21
#if MANIA_USE_PLUS
22
CutsceneSeq_SetSkipType(SKIPTYPE_DISABLED);
23
#endif
24
25
self->activated = true;
26
}
27
}
28
29
void ERZOutro_LateUpdate(void) {}
30
31
void ERZOutro_StaticUpdate(void) {}
32
33
void ERZOutro_Draw(void) {}
34
35
void ERZOutro_Create(void *data)
36
{
37
RSDK_THIS(ERZOutro);
38
39
INIT_ENTITY(self);
40
CutsceneRules_SetupEntity(self, &self->size, &self->hitbox);
41
self->active = ACTIVE_NEVER;
42
}
43
44
void ERZOutro_StageLoad(void)
45
{
46
foreach_all(RubyPortal, portal)
47
{
48
ERZOutro->rubyPortal = portal;
49
foreach_break;
50
}
51
52
foreach_all(PhantomRuby, ruby)
53
{
54
ERZOutro->ruby = ruby;
55
foreach_break;
56
}
57
58
foreach_all(FXRuby, fxRuby)
59
{
60
ERZOutro->fxRuby = fxRuby;
61
foreach_break;
62
}
63
64
foreach_all(PhantomKing, king)
65
{
66
if (!king->type)
67
ERZOutro->king = king;
68
}
69
70
foreach_all(ChaosEmerald, emerald) { ERZStart->emeralds[emerald->type] = emerald; }
71
72
foreach_all(KleptoMobile, eggman)
73
{
74
if (!eggman->type)
75
ERZOutro->eggman = eggman;
76
}
77
78
ERZOutro->savedGame = false;
79
}
80
81
void ERZOutro_SetEmeraldStates(void)
82
{
83
EntityPhantomRuby *ruby = ERZOutro->ruby;
84
85
for (int32 e = 0; e < 7; ++e) {
86
EntityChaosEmerald *emerald = ERZStart->emeralds[e];
87
emerald->originPos.x = ruby->startPos.x;
88
emerald->originPos.y = ruby->startPos.y;
89
emerald->radius += 0x400;
90
emerald->state = ChaosEmerald_State_Rotate;
91
emerald->visible = true;
92
}
93
}
94
95
void ERZOutro_HandleRubyHover(void)
96
{
97
EntityPhantomRuby *ruby = ERZOutro->ruby;
98
EntityFXRuby *fxRuby = ERZOutro->fxRuby;
99
100
ruby->angle += 2;
101
int32 amplitude = abs(RSDK.Sin256(Zone->timer)) >> 6;
102
int32 offset = amplitude == 0 ? 0 : RSDK.Rand(-amplitude, amplitude);
103
104
ruby->position.x = (offset << 16) + ruby->startPos.x;
105
ruby->position.y = (offset << 16) + ruby->startPos.y + (RSDK.Sin256(ruby->angle) << 8);
106
107
fxRuby->position.x = ruby->position.x;
108
fxRuby->position.y = ruby->position.y;
109
}
110
111
bool32 ERZOutro_Cutscene_AttackEggman(EntityCutsceneSeq *host)
112
{
113
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
114
EntityKleptoMobile *eggman = ERZOutro->eggman;
115
EntityFXRuby *fxRuby = ERZOutro->fxRuby;
116
EntityERZOutro *entity = (EntityERZOutro *)host->activeEntity;
117
118
uint16 eggmanSlot = RSDK.GetEntitySlot(eggman);
119
EntityKleptoMobile *eggmanHand = RSDK_GET_ENTITY(eggmanSlot - 2, KleptoMobile);
120
EntityKleptoMobile *eggmanArmL = RSDK_GET_ENTITY(eggmanSlot - 1, KleptoMobile);
121
EntityKleptoMobile *eggmanArmR = RSDK_GET_ENTITY(eggmanSlot + 1, KleptoMobile);
122
123
if (!host->timer) {
124
host->storedValue = player1->rings;
125
eggman->state = StateMachine_None;
126
if (player1->characterID == ID_KNUCKLES)
127
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_GLIDE, &player1->animator, false, 6);
128
else
129
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_RUN, &player1->animator, false, 0);
130
131
player1->state = ERZStart_State_PlayerSuperFly;
132
player1->nextAirState = StateMachine_None;
133
player1->nextGroundState = StateMachine_None;
134
player1->onGround = false;
135
player1->stateInput = StateMachine_None;
136
137
PhantomRuby_PlaySfx(RUBYSFX_ATTACK4);
138
fxRuby->fadeWhite = 768;
139
140
Zone->playerBoundActiveT[0] = false;
141
CutsceneSeq_LockPlayerControl(player1);
142
SceneInfo->timeEnabled = false;
143
144
foreach_all(RingField, field) { field->running = false; }
145
foreach_all(Ring, ring) { destroyEntity(ring); }
146
147
ERZOutro->playerPos.x = player1->position.x;
148
ERZOutro->playerPos.y = player1->position.y;
149
}
150
151
if (fxRuby->fadeWhite > 0)
152
fxRuby->fadeWhite -= 8;
153
154
int32 posX = entity->position.x - entity->size.x;
155
int32 posY = entity->position.y;
156
157
if (host->timer <= 0) {
158
player1->rings = host->storedValue;
159
}
160
else if (host->timer < 120) {
161
if (player1->characterID == ID_KNUCKLES)
162
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_GLIDE, &player1->animator, false, 6);
163
else
164
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_RUN, &player1->animator, false, 0);
165
166
int32 x = player1->position.x;
167
int32 y = player1->position.y;
168
player1->position = MathHelpers_GetBezierPoint((host->timer << 16) / 120, ERZOutro->playerPos.x, ERZOutro->playerPos.y, ERZOutro->playerPos.x,
169
ERZOutro->playerPos.y - 0x2000000, posX - 0x2000000, posY, posX, posY);
170
171
player1->velocity.y = player1->position.y - y;
172
player1->velocity.x = player1->position.x - x;
173
player1->direction = x >= player1->position.x;
174
player1->rings = host->storedValue;
175
}
176
else {
177
player1->position.x = posX;
178
player1->position.y = posY;
179
player1->direction = FLIP_NONE;
180
player1->right = true;
181
ERZStart_Player_HandleSuperDash(player1);
182
player1->velocity.y >>= 1;
183
player1->right = false;
184
185
eggman->originPos = entity->position;
186
eggman->originPos.x += entity->size.x;
187
eggman->position = eggman->originPos;
188
eggman->velocity.x = 0;
189
eggman->velocity.y = 0;
190
eggman->direction = FLIP_NONE;
191
RSDK.SetSpriteAnimation(KleptoMobile->aniFrames, 13, &eggman->eggmanAnimator, true, 0);
192
eggman->state = KleptoMobile_State_CutsceneControlled;
193
eggman->holdingRuby = true;
194
195
eggmanHand->state = KleptoMobile_StateHand_Cutscene;
196
eggmanHand->originPos.x = eggman->position.x;
197
eggmanHand->originPos.y = eggman->position.y;
198
eggmanHand->position.x = eggman->position.x;
199
eggmanHand->position.y = eggman->position.y;
200
201
eggmanArmL->state = KleptoMobile_StateArm_Cutscene;
202
eggmanArmL->originPos.x = eggman->position.x;
203
eggmanArmL->originPos.y = eggman->position.y;
204
eggmanArmL->position.x = eggman->position.x;
205
eggmanArmL->position.y = eggman->position.y;
206
207
eggmanArmR->state = KleptoMobile_StateArm_Cutscene;
208
eggmanArmR->originPos.x = eggman->position.x;
209
eggmanArmR->originPos.y = eggman->position.y;
210
eggmanArmR->position.x = eggman->position.x;
211
eggmanArmR->position.y = eggman->position.y;
212
return true;
213
}
214
215
return false;
216
}
217
218
bool32 ERZOutro_Cutscene_AttackRecoil(EntityCutsceneSeq *host)
219
{
220
MANIA_GET_PLAYER(player1, player2, camera);
221
UNUSED(player2);
222
223
EntityKleptoMobile *eggman = ERZOutro->eggman;
224
EntityFXRuby *fxRuby = ERZOutro->fxRuby;
225
EntityRubyPortal *portal = ERZOutro->rubyPortal;
226
EntityPhantomRuby *ruby = ERZOutro->ruby;
227
228
fxRuby->position = eggman->rubyPos;
229
int32 x = eggman->position.x - 0x400000;
230
int32 y = eggman->originPos.y - 0x200000;
231
232
if (!host->values[0]) {
233
if (player1->position.x >= eggman->position.x - 0x200000) {
234
host->storedTimer = player1->position.y;
235
236
player1->position.x = eggman->position.x - 0x200000;
237
player1->velocity.x = -0x40000;
238
player1->velocity.y = -0x40000;
239
player1->state = Player_State_Static;
240
player1->superState = SUPERSTATE_FADEOUT;
241
player1->shield = SHIELD_NONE;
242
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_HURT, &player1->animator, true, 0);
243
244
host->values[0] = true;
245
fxRuby->state = FXRuby_State_Expanding;
246
fxRuby->drawGroup = Zone->objectDrawGroup[0] - 1;
247
player1->camera = 0;
248
camera->target = 0;
249
Camera_SetupLerp(CAMERA_LERP_NORMAL, 0, x, y, 3);
250
251
portal->position.x = x;
252
portal->position.y = y;
253
Music_TransitionTrack(TRACK_EGGMAN1, 0.2);
254
255
RSDK.SetSpriteAnimation(KleptoMobile->aniFrames, 16, &eggman->eggmanAnimator, true, 0);
256
eggman->holdingRuby = false;
257
eggman->state = KleptoMobile_State_CutsceneExplode;
258
eggman->timer = 0;
259
eggman->invincibilityTimer = 48;
260
eggman->velocity.x = 0x60000;
261
RSDK.PlaySfx(KleptoMobile->sfxHit, false, 255);
262
263
ruby->visible = true;
264
ruby->startPos = eggman->rubyPos;
265
ruby->position = eggman->rubyPos;
266
}
267
}
268
else {
269
if (!host->values[1]) {
270
ruby->startPos.x += 0x20000;
271
if (eggman->velocity.x <= 0x10000)
272
host->values[1] = true;
273
else
274
eggman->velocity.x -= 0x2000;
275
}
276
277
if (!host->values[2]) {
278
player1->velocity.y += 0x3800;
279
if (player1->velocity.y > 0 && player1->position.y >= host->storedTimer) {
280
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_FAN, &player1->animator, true, 0);
281
player1->position.y = host->storedTimer;
282
player1->velocity.x = 0;
283
player1->velocity.y = 0;
284
host->values[2] = true;
285
}
286
}
287
288
ERZOutro_HandleRubyHover();
289
290
if (host->values[1] && host->values[2])
291
return true;
292
}
293
294
player1->rings = host->storedValue;
295
return false;
296
}
297
298
bool32 ERZOutro_Cutscene_LoseEmeralds(EntityCutsceneSeq *host)
299
{
300
EntityPhantomRuby *ruby = ERZOutro->ruby;
301
302
ERZOutro_HandleRubyHover();
303
304
if (!host->timer) {
305
int32 angle = 0;
306
for (int32 e = 0; e < 7; ++e) {
307
EntityChaosEmerald *emerald = ERZStart->emeralds[e];
308
emerald->angle = angle;
309
emerald->radius = 0;
310
emerald->scale.x = 0x200;
311
emerald->scale.y = 0x200;
312
emerald->groundVel = 0x200;
313
emerald->state = ChaosEmerald_State_Rotate;
314
emerald->visible = true;
315
316
angle += 0x2492;
317
}
318
}
319
320
if (host->timer >= 30) {
321
for (int32 e = 0; e < 7; ++e) ERZStart->emeralds[e]->radius = 0x2000;
322
}
323
else {
324
for (int32 e = 0; e < 7; ++e) ERZStart->emeralds[e]->radius = (host->timer << 13) / 30;
325
}
326
327
for (int32 e = 0; e < 7; ++e) ERZStart->emeralds[e]->originPos = ruby->startPos;
328
329
return host->timer == 90;
330
}
331
332
bool32 ERZOutro_Cutscene_OpenPortal(EntityCutsceneSeq *host)
333
{
334
EntityRubyPortal *portal = ERZOutro->rubyPortal;
335
336
ERZOutro_HandleRubyHover();
337
338
if (host->timer == 90) {
339
portal->state = RubyPortal_State_Opened;
340
portal->drawGroup = Zone->objectDrawGroup[0];
341
portal->visible = true;
342
PhantomRuby_PlaySfx(RUBYSFX_REDCUBE);
343
}
344
345
if (host->timer >= 90) {
346
ERZOutro_SetEmeraldStates();
347
return portal->state == StateMachine_None;
348
}
349
350
return false;
351
}
352
353
bool32 ERZOutro_Cutscene_EnterPortal(EntityCutsceneSeq *host)
354
{
355
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
356
EntityPhantomRuby *ruby = ERZOutro->ruby;
357
EntityFXRuby *fxRuby = ERZOutro->fxRuby;
358
EntityRubyPortal *portal = ERZOutro->rubyPortal;
359
360
ERZOutro_SetEmeraldStates();
361
362
if (!host->timer) {
363
ERZOutro->rubyPortalAcceleration = 0;
364
365
host->storedTimer = RSDK.ATan2(player1->position.x - portal->position.x, player1->position.y - portal->position.y) << 16;
366
host->storedValue = MathHelpers_Distance(player1->position.x, player1->position.y, portal->position.x, portal->position.y);
367
368
player1->drawFX |= FX_SCALE;
369
player1->scale.x = 0x200;
370
player1->scale.y = 0x200;
371
}
372
373
if (host->timer == 60)
374
PhantomRuby_SetupFlash(ruby);
375
376
if (host->timer < 108) {
377
ERZOutro_HandleRubyHover();
378
}
379
else {
380
if (host->timer == 108) {
381
ERZOutro_HandleRubyHover();
382
383
ruby->startPos.x = ruby->position.x;
384
ruby->startPos.y = ruby->position.y;
385
386
ERZOutro->rubyPortalAngle = RSDK.ATan2(ruby->position.x - portal->position.x, ruby->position.y - portal->position.y) << 16;
387
ERZOutro->rubyPortalRadius = MathHelpers_Distance(ruby->position.x, ruby->position.y, portal->position.x, portal->position.y);
388
389
ruby->drawFX |= FX_SCALE;
390
ruby->scale.x = 0x200;
391
ruby->scale.y = 0x200;
392
PhantomRuby_PlaySfx(RUBYSFX_ATTACK4);
393
394
Music_FadeOut(0.2);
395
}
396
397
ERZOutro->rubyPortalAcceleration += 0x800;
398
399
host->storedTimer += ERZOutro->rubyPortalAcceleration;
400
host->storedTimer &= 0xFFFFFF00;
401
player1->position.x = portal->position.x;
402
player1->position.y = portal->position.y;
403
player1->position.x += ((host->storedValue >> 9) & 0xFFFFFF80) * RSDK.Cos512(host->storedTimer >> 15);
404
player1->position.y += ((host->storedValue >> 9) & 0xFFFFFF80) * RSDK.Sin512(host->storedTimer >> 15);
405
406
ERZOutro->rubyPortalAngle += ERZOutro->rubyPortalAcceleration;
407
ERZOutro->rubyPortalAngle &= 0xFFFFFF00;
408
ruby->position.x = portal->position.x;
409
ruby->position.y = portal->position.y;
410
ruby->position.x += ((ERZOutro->rubyPortalRadius >> 9) & 0xFFFFFF80) * RSDK.Cos512(ERZOutro->rubyPortalAngle >> 15);
411
ruby->position.y += ((ERZOutro->rubyPortalRadius >> 9) & 0xFFFFFF80) * RSDK.Sin512(ERZOutro->rubyPortalAngle >> 15);
412
413
if (fxRuby->fadeWhite >= 0x200)
414
host->values[0] = true;
415
else
416
fxRuby->fadeWhite += 2;
417
418
int32 rx = abs(portal->position.x - player1->position.x) >> 16;
419
int32 ry = abs(portal->position.y - player1->position.y) >> 16;
420
int32 dist = MathHelpers_SquareRoot(rx * rx + ry * ry);
421
422
player1->scale.x = 8 * MIN(dist, 0x40);
423
player1->scale.y = 8 * MIN(dist, 0x40);
424
425
rx = abs(portal->position.x - ruby->position.x) >> 16;
426
ry = abs(portal->position.y - ruby->position.y) >> 16;
427
dist = MathHelpers_SquareRoot(rx * rx + ry * ry);
428
ruby->scale.x = 8 * MIN(dist, 0x40);
429
ruby->scale.y = 8 * MIN(dist, 0x40);
430
431
if (ERZOutro->rubyPortalRadius > 0)
432
ERZOutro->rubyPortalRadius -= 0x8000;
433
434
if (host->storedValue <= 0)
435
host->values[1] = true;
436
else
437
host->storedValue -= 0x8000;
438
439
return host->values[0] && host->values[1];
440
}
441
442
return false;
443
}
444
445
bool32 ERZOutro_Cutscene_FadeOut(EntityCutsceneSeq *host)
446
{
447
EntityFXRuby *fxRuby = (EntityFXRuby *)ERZOutro->fxRuby;
448
ERZOutro_HandleRubyHover();
449
450
if (host->timer >= 90) {
451
if (fxRuby->fadeBlack < 512)
452
fxRuby->fadeBlack += 8;
453
454
return host->timer == 150;
455
}
456
457
return false;
458
}
459
460
bool32 ERZOutro_Cutscene_ShowEnding(EntityCutsceneSeq *host)
461
{
462
if (globals->saveSlotID != NO_SAVE_SLOT) {
463
if (!host->timer) {
464
ERZOutro->savedGame = false;
465
SaveGame_SaveProgress();
466
GameProgress_GiveEnding(GAMEPROGRESS_ENDING_GOOD);
467
SaveGame_SaveFile(ERZOutro_SaveFileCB);
468
UIWaitSpinner_StartWait();
469
}
470
471
if (!ERZOutro->savedGame)
472
return false;
473
474
UIWaitSpinner_FinishWait();
475
}
476
477
API_UnlockAchievement(&achievementList[ACH_GAME_CLEARED]);
478
479
if (CHECK_CHARACTER_ID(ID_KNUCKLES, 1) && CHECK_CHARACTER_ID(ID_KNUCKLES, 2))
480
RSDK.SetScene("Videos", "True End?");
481
else
482
RSDK.SetScene("Videos", "Good End");
483
484
RSDK.LoadScene();
485
return true;
486
}
487
488
#if MANIA_USE_PLUS
489
void ERZOutro_SaveFileCB(bool32 success) { ERZOutro->savedGame = true; }
490
#else
491
void ERZOutro_SaveFileCB(void) { ERZOutro->savedGame = true; }
492
#endif
493
494
#if GAME_INCLUDE_EDITOR
495
void ERZOutro_EditorDraw(void)
496
{
497
RSDK_THIS(ERZOutro);
498
499
CutsceneRules_DrawCutsceneBounds(self, &self->size);
500
}
501
502
void ERZOutro_EditorLoad(void) {}
503
#endif
504
505
void ERZOutro_Serialize(void) { RSDK_EDITABLE_VAR(ERZOutro, VAR_VECTOR2, size); }
506
507