Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/CPZ/CPZ1Intro.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: CPZ1Intro Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectCPZ1Intro *CPZ1Intro;
11
12
void CPZ1Intro_Update(void)
13
{
14
RSDK_THIS(CPZ1Intro);
15
16
if (!self->activated) {
17
if (!isMainGameMode() || !globals->enableIntro || CutsceneRules_CheckStageReload()) {
18
destroyEntity(CPZ1Intro->fxRuby);
19
self->active = ACTIVE_NEVER;
20
}
21
else {
22
self->activated = true;
23
CPZ1Intro_SetupCutscene();
24
}
25
}
26
}
27
28
void CPZ1Intro_LateUpdate(void) {}
29
30
void CPZ1Intro_StaticUpdate(void) {}
31
32
void CPZ1Intro_Draw(void) {}
33
34
void CPZ1Intro_Create(void *data)
35
{
36
RSDK_THIS(CPZ1Intro);
37
38
INIT_ENTITY(self);
39
CutsceneRules_SetupEntity(self, &self->size, &self->hitbox);
40
self->active = ACTIVE_NORMAL;
41
}
42
43
void CPZ1Intro_StageLoad(void)
44
{
45
CPZ1Intro->playerFrames = RSDK.LoadSpriteAnimation("Players/CutsceneCPZ.bin", SCOPE_STAGE);
46
CPZ1Intro->particleFrames = RSDK.LoadSpriteAnimation("CPZ/Particles.bin", SCOPE_STAGE);
47
48
foreach_all(FXRuby, fxRuby)
49
{
50
CPZ1Intro->fxRuby = fxRuby;
51
foreach_break;
52
}
53
54
CPZ1Intro->sfxChemDrop = RSDK.GetSfx("CPZ/ChemDrop.wav");
55
CPZ1Intro->sfxDNABurst = RSDK.GetSfx("CPZ/DNABurst.wav");
56
}
57
58
void CPZ1Intro_SetupCutscene(void)
59
{
60
RSDK_THIS(CPZ1Intro);
61
62
CutsceneSeq_StartSequence(self, CPZ1Intro_Cutscene_RubyWarp, CPZ1Intro_Cutscene_PostWarpDrop, CPZ1Intro_Cutscene_Waiting,
63
CPZ1Intro_Cutscene_ChemicalDrop, CPZ1Intro_Cutscene_PlayerChemicalReact, CPZ1Intro_Cutscene_ReadyStage,
64
StateMachine_None);
65
66
#if MANIA_USE_PLUS
67
CutsceneSeq_SetSkipType(SKIPTYPE_RELOADSCN);
68
#endif
69
}
70
71
void CPZ1Intro_Particle_ChemDrop(EntityDebris *debris)
72
{
73
debris->velocity.y += RSDK.Rand(-0x28000, -0x20000);
74
RSDK.SetSpriteAnimation(CPZ1Intro->particleFrames, 1, &debris->animator, true, 0);
75
}
76
77
void CPZ1Intro_HandleRubyHover(EntityCutsceneSeq *cutsceneSequence, EntityPlayer *player1, EntityPlayer *player2, int32 targetY)
78
{
79
EntityPlayer *players[2];
80
players[0] = player1;
81
players[1] = player2;
82
83
int32 id = 0;
84
for (int32 angle = 0; angle < 0x80; angle += 0x40) {
85
EntityPlayer *player = players[id++];
86
if (!player)
87
break;
88
89
int32 velX = (player->position.x - player->position.x) >> 3;
90
int32 velY = (targetY + 0xA00 * RSDK.Sin256(2 * (angle + cutsceneSequence->timer - cutsceneSequence->storedTimer)) - player->position.y) >> 3;
91
player->position.x += velX;
92
player->position.y += velY;
93
RSDK.SetSpriteAnimation(player->aniFrames, ANI_FAN, &player->animator, false, 0);
94
player->state = Player_State_Static;
95
}
96
}
97
98
bool32 CPZ1Intro_CheckSonicAnimFinish(void)
99
{
100
MANIA_GET_PLAYER(player1, player2, camera);
101
UNUSED(player2);
102
UNUSED(camera);
103
104
return player1->animator.frameID == player1->animator.frameCount - 1 && player1->animator.timer == 30;
105
}
106
107
bool32 CPZ1Intro_CheckTailsAnimFinish(int32 timer)
108
{
109
MANIA_GET_PLAYER(player1, player2, camera);
110
UNUSED(player2);
111
UNUSED(camera);
112
113
#if MANIA_USE_PLUS
114
if (player1->animator.frameID == player1->animator.frameCount - 1 && player1->animator.timer == 4) {
115
RSDK.SetSpriteAnimation(player1->tailFrames, ANI_IDLE, &player1->tailAnimator, true, 0);
116
player1->animator.animationID = 0;
117
player1->animator.prevAnimationID = 0;
118
return true;
119
}
120
#else
121
// Same Code as 2P AI, it's unknown why tails' anim was cut in pre-plus, but it's in plus so hooray
122
if (timer == 60) {
123
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);
124
player1->up = false;
125
return true;
126
}
127
#endif
128
return false;
129
}
130
131
bool32 CPZ1Intro_CheckKnuxAnimFinish(void)
132
{
133
MANIA_GET_PLAYER(player1, player2, camera);
134
UNUSED(player2);
135
UNUSED(camera);
136
137
return player1->animator.frameID == player1->animator.frameCount - 1 && player1->animator.timer == 4;
138
}
139
140
#if MANIA_USE_PLUS
141
bool32 CPZ1Intro_CheckMightyAnimFinish(void)
142
{
143
MANIA_GET_PLAYER(player1, player2, camera);
144
UNUSED(player2);
145
UNUSED(camera);
146
147
return player1->animator.frameID == player1->animator.frameCount - 1 && player1->animator.timer == 4;
148
}
149
150
bool32 CPZ1Intro_CheckRayAnimFinish(void)
151
{
152
MANIA_GET_PLAYER(player1, player2, camera);
153
UNUSED(player2);
154
UNUSED(camera);
155
156
return player1->animator.frameID == player1->animator.frameCount - 1 && player1->animator.timer == 4;
157
}
158
#endif
159
160
bool32 CPZ1Intro_Cutscene_RubyWarp(EntityCutsceneSeq *host)
161
{
162
MANIA_GET_PLAYER(player1, player2, camera);
163
164
EntityFXRuby *fxRuby = CPZ1Intro->fxRuby;
165
if (!host->timer) {
166
player1->camera = NULL;
167
camera->position.y -= 0x40000;
168
camera->target = 0;
169
player1->stateInput = StateMachine_None;
170
CutsceneSeq_LockAllPlayerControl();
171
player1->velocity.x = 0;
172
player1->velocity.y = 0;
173
player1->onGround = false;
174
if (player2->classID == Player->classID) {
175
player2->position.x -= 0x80000;
176
player2->velocity.x = 0;
177
player2->velocity.y = 0;
178
player2->onGround = false;
179
player2->stateInput = StateMachine_None;
180
}
181
}
182
183
camera->state = StateMachine_None;
184
if (fxRuby->fadeBlack > 0) {
185
fxRuby->fadeBlack -= 16;
186
}
187
else {
188
if (fxRuby->fadeWhite > 0) {
189
fxRuby->fadeWhite -= 16;
190
}
191
else {
192
if (!host->values[0]) {
193
PhantomRuby_PlaySfx(RUBYSFX_ATTACK4);
194
host->values[0] = 1;
195
fxRuby->state = FXRuby_State_Shrinking;
196
}
197
198
if (!fxRuby->outerRadius) {
199
player1->state = Player_State_Air;
200
if (player2->classID == Player->classID)
201
player2->state = Player_State_Air;
202
fxRuby->active = ACTIVE_NEVER;
203
return true;
204
}
205
}
206
}
207
208
CPZ1Intro_HandleRubyHover(host, player1, player2, host->activeEntity->position.y - 0x200000);
209
210
return false;
211
}
212
213
bool32 CPZ1Intro_Cutscene_PostWarpDrop(EntityCutsceneSeq *host)
214
{
215
MANIA_GET_PLAYER(player1, player2, camera);
216
UNUSED(host);
217
UNUSED(camera);
218
219
if (player2->classID == Player->classID) {
220
if (player1->onGround && player2->onGround)
221
return true;
222
}
223
else {
224
if (player1->onGround)
225
return true;
226
}
227
228
return false;
229
}
230
231
bool32 CPZ1Intro_Cutscene_Waiting(EntityCutsceneSeq *host)
232
{
233
MANIA_GET_PLAYER(player1, player2, camera);
234
UNUSED(camera);
235
236
if (!host->timer) {
237
player1->state = Player_State_Static;
238
if (player2->classID == Player->classID) {
239
player2->state = Player_State_Static;
240
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);
241
}
242
}
243
244
if (host->timer == 30) {
245
if (CHECK_CHARACTER_ID(ID_SONIC, 1))
246
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_BORED_2, &player1->animator, false, 0);
247
else
248
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_BORED_1, &player1->animator, false, 0);
249
}
250
251
return host->timer == 60;
252
}
253
254
bool32 CPZ1Intro_Cutscene_ChemicalDrop(EntityCutsceneSeq *host)
255
{
256
MANIA_GET_PLAYER(player1, player2, camera);
257
UNUSED(camera);
258
259
if (!host->timer) {
260
RSDK.PlaySfx(CPZ1Intro->sfxChemDrop, false, 255);
261
EntityDebris *debris = CREATE_ENTITY(Debris, NULL, player1->position.x + 0x20000, (ScreenInfo->position.y - 8) << 16);
262
debris->updateRange.x = 0x800000;
263
debris->updateRange.y = 0x800000;
264
debris->active = ACTIVE_NORMAL;
265
debris->drawGroup = Zone->playerDrawGroup[1];
266
debris->state = Debris_State_Fall;
267
debris->gravityStrength = 0x3800;
268
RSDK.SetSpriteAnimation(CPZ1Intro->particleFrames, 0, &debris->animator, true, 0);
269
CPZ1Intro->debris = debris;
270
}
271
272
EntityDebris *debris = CPZ1Intro->debris;
273
Hitbox *playerHitbox = Player_GetHitbox(player1);
274
275
int32 playerY = player1->position.y + ((playerHitbox->top + 2) << 16);
276
if (debris->position.y >= playerY) {
277
RSDK.PlaySfx(CPZ1Intro->sfxDNABurst, false, 255);
278
ParticleHelpers_SetupFallingParticles(debris->position.x, playerY, CPZ1Intro_Particle_ChemDrop);
279
destroyEntity(debris);
280
281
if (CHECK_CHARACTER_ID(ID_TAILS, 2))
282
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_SKID, &player2->animator, true, 0);
283
284
return true;
285
}
286
287
return false;
288
}
289
290
bool32 CPZ1Intro_Cutscene_PlayerChemicalReact(EntityCutsceneSeq *host)
291
{
292
MANIA_GET_PLAYER(player1, player2, camera);
293
UNUSED(camera);
294
295
if (!host->timer) {
296
switch (GET_CHARACTER_ID(1)) {
297
case ID_SONIC:
298
CPZ1Intro->playerAnimID = 0;
299
player1->state = Player_State_Static;
300
player1->tileCollisions = TILECOLLISION_NONE;
301
RSDK.SetSpriteAnimation(CPZ1Intro->playerFrames, CPZ1Intro->playerAnimID, &player1->animator, true, 0);
302
break;
303
304
case ID_TAILS: CPZ1Intro->playerAnimID = 1;
305
#if MANIA_USE_PLUS
306
player1->state = Player_State_Static;
307
player1->tileCollisions = TILECOLLISION_NONE;
308
RSDK.SetSpriteAnimation(CPZ1Intro->playerFrames, CPZ1Intro->playerAnimID, &player1->animator, true, 0);
309
RSDK.SetSpriteAnimation(-1, 0, &player1->tailAnimator, true, 0);
310
player1->animator.animationID = 0xFF;
311
player1->animator.prevAnimationID = 0xFF;
312
#else
313
// Same Code as 2P AI, it's unknown why tails' anim was cut in pre-plus, but it's in plus so hooray
314
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_LOOK_UP, &player1->animator, true, 0);
315
player1->up = true;
316
player1->state = Player_State_LookUp;
317
#endif
318
319
break;
320
case ID_KNUCKLES:
321
CPZ1Intro->playerAnimID = 2;
322
player1->state = Player_State_Static;
323
player1->tileCollisions = TILECOLLISION_NONE;
324
RSDK.SetSpriteAnimation(CPZ1Intro->playerFrames, CPZ1Intro->playerAnimID, &player1->animator, true, 0);
325
break;
326
327
#if MANIA_USE_PLUS
328
case ID_MIGHTY:
329
CPZ1Intro->playerAnimID = 3;
330
player1->state = Player_State_Static;
331
player1->tileCollisions = TILECOLLISION_NONE;
332
RSDK.SetSpriteAnimation(CPZ1Intro->playerFrames, CPZ1Intro->playerAnimID, &player1->animator, true, 0);
333
break;
334
335
case ID_RAY:
336
CPZ1Intro->playerAnimID = 4;
337
RSDK.SetSpriteAnimation(CPZ1Intro->playerFrames, CPZ1Intro->playerAnimID, &player1->animator, true, 0);
338
player1->state = Player_State_Static;
339
player1->tileCollisions = TILECOLLISION_NONE;
340
break;
341
#endif
342
}
343
}
344
345
if (host->timer == 60 && player2->classID == Player->classID) {
346
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_LOOK_UP, &player2->animator, true, 0);
347
player2->up = true;
348
player2->state = Player_State_LookUp;
349
}
350
351
switch (CPZ1Intro->playerAnimID) {
352
case 0: return CPZ1Intro_CheckSonicAnimFinish();
353
case 1: return CPZ1Intro_CheckTailsAnimFinish(host->timer);
354
case 2: return CPZ1Intro_CheckKnuxAnimFinish();
355
#if MANIA_USE_PLUS
356
case 3: return CPZ1Intro_CheckMightyAnimFinish();
357
case 4: return CPZ1Intro_CheckRayAnimFinish();
358
#endif
359
default: break;
360
}
361
362
return false;
363
}
364
365
bool32 CPZ1Intro_Cutscene_ReadyStage(EntityCutsceneSeq *host)
366
{
367
MANIA_GET_PLAYER(player1, player2, camera);
368
369
if (!host->timer) {
370
RSDK.SetSpriteAnimation(player1->aniFrames, ANI_IDLE, &player1->animator, true, 0);
371
372
if (player2->classID == Player->classID) {
373
RSDK.SetSpriteAnimation(player2->aniFrames, ANI_IDLE, &player2->animator, true, 0);
374
player2->up = false;
375
}
376
}
377
378
if (host->timer == 30) {
379
player1->stateInput = Player_Input_P1;
380
player1->tileCollisions = TILECOLLISION_DOWN;
381
player1->onGround = true;
382
player1->state = Player_State_Ground;
383
player1->camera = camera;
384
camera->target = (Entity *)player1;
385
camera->state = Camera_State_FollowXY;
386
if (player2->classID == Player->classID) {
387
player2->stateInput = Player_Input_P2_AI;
388
player2->tileCollisions = TILECOLLISION_DOWN;
389
player2->onGround = true;
390
player2->state = Player_State_Ground;
391
}
392
393
foreach_all(TitleCard, titlecard)
394
{
395
titlecard->active = ACTIVE_NORMAL;
396
titlecard->state = TitleCard_State_SetupBGElements;
397
titlecard->stateDraw = TitleCard_Draw_SlideIn;
398
Music_PlayTrack(TRACK_STAGE);
399
foreach_break;
400
}
401
402
return true;
403
}
404
405
return false;
406
}
407
408
#if GAME_INCLUDE_EDITOR
409
void CPZ1Intro_EditorDraw(void)
410
{
411
RSDK_THIS(CPZ1Intro);
412
CutsceneRules_DrawCutsceneBounds(self, &self->size);
413
}
414
415
void CPZ1Intro_EditorLoad(void) {}
416
#endif
417
418
void CPZ1Intro_Serialize(void) { RSDK_EDITABLE_VAR(CPZ1Intro, VAR_VECTOR2, size); }
419
420