Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/SSZ/Firework.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Firework Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectFirework *Firework;
11
12
void Firework_Update(void)
13
{
14
RSDK_THIS(Firework);
15
16
if (self->mainAnimator.animationID < 3) {
17
RSDK.ProcessAnimation(&self->mainAnimator);
18
}
19
else if (self->timer == 10) {
20
if (self->mainAnimator.frameID < self->mainAnimator.frameCount - 1)
21
RSDK.ProcessAnimation(&self->mainAnimator);
22
23
if (self->sparkAnimator.frameID < self->sparkAnimator.frameCount - 1)
24
RSDK.ProcessAnimation(&self->sparkAnimator);
25
}
26
27
StateMachine_Run(self->state);
28
Firework_HandlePlayerTimers();
29
}
30
31
void Firework_LateUpdate(void) {}
32
33
void Firework_StaticUpdate(void)
34
{
35
bool32 fireworkActive = false;
36
37
if (RSDK_GET_ENTITY(SLOT_PAUSEMENU, PauseMenu)->classID != PauseMenu->classID) {
38
foreach_active(Firework, firework)
39
{
40
if (firework->rideActive) {
41
if (!Firework->playingFlameSfx) {
42
RSDK.PlaySfx(Firework->sfxFlame, true, 255);
43
Firework->playingFlameSfx = true;
44
}
45
46
fireworkActive = true;
47
foreach_break;
48
}
49
}
50
}
51
52
if (!fireworkActive) {
53
if (Firework->playingFlameSfx) {
54
RSDK.StopSfx(Firework->sfxFlame);
55
Firework->playingFlameSfx = false;
56
}
57
}
58
}
59
60
void Firework_Draw(void)
61
{
62
RSDK_THIS(Firework);
63
64
self->rotation = ((self->angle >> 1) + 0x80) & 0x1FF;
65
if (self->state == Firework_State_Explode) {
66
if (self->timer > 0)
67
RSDK.FillScreen(0xF0F0F0, self->timer - 128, self->timer - 256, self->timer);
68
}
69
else {
70
RSDK.DrawSprite(&self->mainAnimator, &self->position, false);
71
}
72
73
if (self->mainAnimator.animationID >= 3) {
74
self->inkEffect = INK_ADD;
75
RSDK.DrawSprite(&self->sparkAnimator, NULL, false);
76
self->inkEffect = INK_NONE;
77
}
78
}
79
80
void Firework_Create(void *data)
81
{
82
RSDK_THIS(Firework);
83
84
self->active = ACTIVE_BOUNDS;
85
self->visible = true;
86
self->drawFX = FX_ROTATE | FX_FLIP;
87
self->drawGroup = Zone->objectDrawGroup[0];
88
self->updateRange.x = 0x800000;
89
self->updateRange.y = 0x800000;
90
91
if (!self->innerRadius)
92
self->innerRadius = 64;
93
94
if (!self->outerRadius)
95
self->outerRadius = 128;
96
97
if (!self->distance)
98
self->distance = 512;
99
100
self->startPos = self->position;
101
102
self->state = Firework_State_Init;
103
}
104
105
void Firework_StageLoad(void)
106
{
107
Firework->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/Firework.bin", SCOPE_STAGE);
108
109
Firework->hitboxFireworkGrab.left = -11;
110
Firework->hitboxFireworkGrab.top = -32;
111
Firework->hitboxFireworkGrab.right = 10;
112
Firework->hitboxFireworkGrab.bottom = 15;
113
114
Firework->hitboxTileCheck.left = -10;
115
Firework->hitboxTileCheck.top = -10;
116
Firework->hitboxTileCheck.right = 10;
117
Firework->hitboxTileCheck.bottom = 10;
118
119
Firework->sfxRocketJet = RSDK.GetSfx("Stage/RocketJet.wav");
120
Firework->sfxFlame = RSDK.GetSfx("Stage/Flame2.wav");
121
Firework->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");
122
Firework->sfxExplosion = RSDK.GetSfx("Stage/Explosion.wav");
123
124
Firework->active = ACTIVE_ALWAYS;
125
}
126
127
void Firework_CheckPlayerCollisions(void)
128
{
129
RSDK_THIS(Firework);
130
131
foreach_active(Player, player)
132
{
133
int32 playerID = RSDK.GetEntitySlot(player);
134
135
if (!((1 << playerID) & self->activePlayers)) {
136
if (player->state != Player_State_Static && !self->playerTimers[playerID]
137
&& Player_CheckCollisionTouch(player, self, &Firework->hitboxFireworkGrab)) {
138
player->tileCollisions = TILECOLLISION_NONE;
139
RSDK.SetSpriteAnimation(player->aniFrames, ANI_PULLEY_HOLD, &player->animator, false, 0);
140
141
player->state = Player_State_Static;
142
self->activePlayers |= 1 << playerID;
143
RSDK.PlaySfx(Player->sfxGrab, false, 255);
144
145
player->direction = player->position.x > self->position.x;
146
if (player->sidekick == false)
147
self->activated = true;
148
}
149
}
150
151
if ((1 << playerID) & self->activePlayers) {
152
if (player->state == Player_State_Static) {
153
player->velocity.x = 0;
154
player->velocity.y = 0;
155
player->groundVel = 0;
156
player->angle = self->angle >> 1;
157
158
if (player->direction == FLIP_NONE)
159
player->rotation = ((self->angle >> 1) + 0xC0) % 511;
160
else
161
player->rotation = ((self->angle >> 1) + 0x444) % 511;
162
163
player->position.x = self->position.x;
164
player->position.y = self->position.y;
165
166
int32 power = player->direction == FLIP_NONE ? 16 : -16;
167
168
player->position.x += power * (RSDK.Sin1024(self->angle) << 6);
169
player->position.y -= power * (RSDK.Cos1024(self->angle) << 6);
170
player->position.x += -0x100 * RSDK.Cos1024(self->angle);
171
player->position.y -= -0x100 * RSDK.Sin1024(self->angle);
172
}
173
else {
174
self->activePlayers &= ~(1 << playerID);
175
}
176
}
177
}
178
}
179
180
void Firework_HandlePlayerTimers(void)
181
{
182
RSDK_THIS(Firework);
183
184
foreach_active(Player, player)
185
{
186
int32 playerID = RSDK.GetEntitySlot(player);
187
188
if (self->playerTimers[playerID])
189
self->playerTimers[playerID]--;
190
}
191
}
192
193
void Firework_RemovePlayers(bool32 crashed)
194
{
195
foreach_active(Player, player)
196
{
197
RSDK.GetEntitySlot(player); // so true tbh
198
Firework_HandlePlayerRemoval(player, crashed);
199
}
200
}
201
202
void Firework_HandlePlayerRemoval(EntityPlayer *player, bool32 crashed)
203
{
204
RSDK_THIS(Firework);
205
206
int32 playerID = RSDK.GetEntitySlot(player);
207
208
if ((1 << playerID) & self->activePlayers) {
209
player->state = Player_State_Air;
210
player->onGround = false;
211
player->tileCollisions = TILECOLLISION_DOWN;
212
player->velocity = self->velocity;
213
214
self->playerTimers[playerID] = 30;
215
if (crashed) {
216
bool32 isSidekick = player->sidekick;
217
player->sidekick = true;
218
Player_Hit(player);
219
player->sidekick = isSidekick;
220
}
221
else {
222
RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, false, 0);
223
}
224
225
self->activePlayers &= ~(1 << playerID);
226
}
227
}
228
229
void Firework_HandlePlayerJump(void)
230
{
231
RSDK_THIS(Firework);
232
233
foreach_active(Player, player)
234
{
235
if ((1 << RSDK.GetEntitySlot(player)) & self->activePlayers && player->jumpPress) {
236
int32 playerID = RSDK.GetEntitySlot(player);
237
238
if ((1 << playerID) & self->activePlayers) {
239
player->state = Player_State_Air;
240
player->onGround = false;
241
player->tileCollisions = TILECOLLISION_DOWN;
242
player->velocity.x = self->velocity.x;
243
player->velocity.y = self->velocity.y;
244
self->playerTimers[playerID] = 30;
245
RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, false, 0);
246
247
self->activePlayers &= ~(1 << playerID);
248
}
249
}
250
}
251
}
252
253
void Firework_HandlePlayerControl(void)
254
{
255
RSDK_THIS(Firework);
256
257
foreach_active(Player, player)
258
{
259
if (!player->sidekick && (1 << RSDK.GetEntitySlot(player)) & self->activePlayers) {
260
if (player->left)
261
--self->angle;
262
else if (player->right)
263
++self->angle;
264
}
265
}
266
}
267
268
void Firework_HandleMoveDir(void)
269
{
270
RSDK_THIS(Firework);
271
272
if (self->moveVelocity < 0x80000)
273
self->moveVelocity += 0x2000;
274
275
if (self->moveVelocity > 0x80000)
276
self->moveVelocity = 0x80000;
277
278
self->velocity.x = (self->moveVelocity >> 10) * RSDK.Cos1024(self->angle);
279
self->velocity.y = (self->moveVelocity >> 10) * RSDK.Sin1024(self->angle);
280
self->position.x += self->velocity.x;
281
self->position.y += self->velocity.y;
282
283
int32 rx = abs(self->lastPosition.x - self->position.x) >> 16;
284
int32 ry = abs(self->lastPosition.y - self->position.y) >> 16;
285
self->ridePos += MathHelpers_SquareRoot(rx * rx + ry * ry);
286
287
self->lastPosition.x = self->position.x;
288
self->lastPosition.y = self->position.y;
289
}
290
291
void Firework_HandleDebrisSpawn(void)
292
{
293
RSDK_THIS(Firework);
294
295
if (!(self->timer % 5)) {
296
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Move, self->position.x, self->position.y);
297
298
RSDK.SetSpriteAnimation(Explosion->aniFrames, 3, &debris->animator, true, 0);
299
debris->position.x -= 0x600 * RSDK.Cos1024(self->angle);
300
debris->position.y -= 0x600 * RSDK.Sin1024(self->angle);
301
debris->drawGroup = Zone->objectDrawGroup[0];
302
debris->timer = 52;
303
}
304
}
305
306
void Firework_HandleTileCollisions(void)
307
{
308
RSDK_THIS(Firework);
309
310
bool32 collided = RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_LWALL, 0, Firework->hitboxTileCheck.right << 13, 0, 4);
311
collided |= RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, Firework->hitboxTileCheck.top << 13, 4);
312
collided |= RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_RWALL, 0, Firework->hitboxTileCheck.left << 13, 0, 4);
313
314
if (collided) {
315
Camera_ShakeScreen(0, 0, 4);
316
Firework_HandleRideEnd(true);
317
}
318
}
319
320
void Firework_HandleRideEnd(bool32 crashed)
321
{
322
RSDK_THIS(Firework);
323
324
self->rideActive = false;
325
326
if (crashed) {
327
// Watch out! you're gonna crash! ah!
328
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSSPUFF), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
329
RSDK.PlaySfx(Firework->sfxExplosion2, false, 255);
330
331
Firework_RemovePlayers(true);
332
self->state = Firework_State_Explode;
333
}
334
else {
335
int32 angle = 0;
336
337
for (int32 i = 0; i < 8; ++i) {
338
int32 x = (RSDK.Cos1024(angle) << 6) * self->innerRadius + self->position.x;
339
int32 y = (RSDK.Sin1024(angle) << 6) * self->innerRadius + self->position.y;
340
341
EntityFirework *firework = CREATE_ENTITY(Firework, NULL, x, y);
342
firework->velocity.x = 0x30 * RSDK.Cos1024(angle);
343
firework->velocity.y = 0x30 * RSDK.Sin1024(angle);
344
firework->state = Firework_State_SparkSetup;
345
firework->angle = angle;
346
firework->sparkID = 0;
347
348
x = (RSDK.Cos1024(angle) << 6) * self->outerRadius + self->position.x;
349
y = (RSDK.Sin1024(angle) << 6) * self->outerRadius + self->position.y;
350
firework = CREATE_ENTITY(Firework, NULL, x, y);
351
firework->velocity.x = 0x20 * RSDK.Cos1024(angle);
352
firework->velocity.y = 0x20 * RSDK.Sin1024(angle);
353
firework->angle = angle;
354
firework->state = Firework_State_SparkSetup;
355
firework->sparkID = 1;
356
357
angle += 0x80;
358
}
359
360
self->timer = 512;
361
self->drawGroup = Zone->hudDrawGroup;
362
RSDK.PlaySfx(Firework->sfxExplosion, false, 255);
363
364
Firework_RemovePlayers(false);
365
self->state = Firework_State_Explode;
366
}
367
}
368
369
void Firework_CheckOffScreen(void)
370
{
371
RSDK_THIS(Firework);
372
373
if (self->state == Firework_State_Explode) {
374
if (self->timer <= 0) {
375
if (!RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
376
self->position = self->startPos;
377
self->state = Firework_State_ResetOffScreen;
378
self->visible = false;
379
self->direction = FLIP_NONE;
380
}
381
}
382
}
383
else {
384
if (!RSDK.CheckOnScreen(self, NULL)) {
385
if (!RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
386
self->position = self->startPos;
387
self->state = Firework_State_ResetOffScreen;
388
self->visible = false;
389
self->direction = FLIP_NONE;
390
}
391
}
392
}
393
}
394
395
void Firework_HandleSparkAnimations(int32 sparkID, int16 angle, Animator *fireworkAnimator, Animator *sparkAnimator, uint8 *direction)
396
{
397
uint16 ang = angle & 0x3FF;
398
int32 anim = 4 * (sparkID != 0) + 3;
399
400
if (ang <= 0x40 || ang > 0x3C0) {
401
if (direction)
402
*direction = FLIP_NONE;
403
404
if (fireworkAnimator)
405
RSDK.SetSpriteAnimation(Firework->aniFrames, anim, fireworkAnimator, true, 0);
406
}
407
else if (ang <= 0xC0) {
408
if (direction)
409
*direction = FLIP_Y;
410
411
if (fireworkAnimator)
412
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 2, fireworkAnimator, true, 0);
413
}
414
else if (ang <= 0x140) {
415
if (direction)
416
*direction = FLIP_Y;
417
418
if (fireworkAnimator)
419
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 1, fireworkAnimator, true, 0);
420
}
421
else if (ang <= 0x1C0) {
422
if (direction)
423
*direction = FLIP_XY;
424
425
if (fireworkAnimator)
426
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 2, fireworkAnimator, true, 0);
427
}
428
else if (ang <= 0x240) {
429
if (direction)
430
*direction = FLIP_X;
431
432
if (fireworkAnimator)
433
RSDK.SetSpriteAnimation(Firework->aniFrames, anim, fireworkAnimator, true, 0);
434
}
435
else if (ang <= 0x2C0) {
436
if (direction)
437
*direction = FLIP_X;
438
439
if (fireworkAnimator)
440
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 2, fireworkAnimator, true, 0);
441
}
442
else if (ang > 0x340) {
443
if (direction)
444
*direction = FLIP_NONE;
445
446
if (fireworkAnimator)
447
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 2, fireworkAnimator, true, 0);
448
}
449
else {
450
if (direction)
451
*direction = FLIP_NONE;
452
else if (fireworkAnimator)
453
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 1, fireworkAnimator, true, 0);
454
}
455
456
if (sparkAnimator)
457
RSDK.SetSpriteAnimation(Firework->aniFrames, anim + 3, sparkAnimator, true, 0);
458
}
459
460
void Firework_State_Init(void)
461
{
462
RSDK_THIS(Firework);
463
464
self->position = self->startPos;
465
self->lastPosition = self->startPos;
466
self->visible = true;
467
self->activePlayers = 0;
468
self->rotation = 0;
469
self->angle = 0x300;
470
self->timer = 0;
471
self->activated = false;
472
self->moveVelocity = 0;
473
self->ridePos = 0;
474
self->drawGroup = Zone->objectDrawGroup[0];
475
476
RSDK.SetSpriteAnimation(Firework->aniFrames, 0, &self->mainAnimator, true, 0);
477
self->state = Firework_State_AwaitPlayerRide;
478
}
479
480
void Firework_State_AwaitPlayerRide(void)
481
{
482
RSDK_THIS(Firework);
483
484
Firework_CheckPlayerCollisions();
485
486
if (self->activated) {
487
RSDK.PlaySfx(Firework->sfxRocketJet, false, 255);
488
RSDK.SetSpriteAnimation(Firework->aniFrames, 1, &self->mainAnimator, true, 0);
489
self->timer = 30;
490
self->state = Firework_State_PlayerGrabDelay;
491
}
492
}
493
494
void Firework_State_PlayerGrabDelay(void)
495
{
496
RSDK_THIS(Firework);
497
498
if (self->timer <= 0) {
499
self->rideActive = true;
500
RSDK.SetSpriteAnimation(Firework->aniFrames, 2, &self->mainAnimator, true, 0);
501
502
self->lastPosition = self->position;
503
self->timer = 0;
504
self->state = Firework_State_Riding;
505
}
506
else {
507
self->timer--;
508
}
509
510
Firework_CheckPlayerCollisions();
511
Firework_HandlePlayerJump();
512
}
513
514
void Firework_State_Riding(void)
515
{
516
RSDK_THIS(Firework);
517
518
Firework_HandleMoveDir();
519
Firework_HandleDebrisSpawn();
520
Firework_CheckPlayerCollisions();
521
Firework_HandlePlayerJump();
522
Firework_HandlePlayerControl();
523
524
if (self->ridePos > self->distance)
525
Firework_HandleRideEnd(false);
526
527
Firework_HandleTileCollisions();
528
Firework_CheckOffScreen();
529
530
++self->timer;
531
}
532
533
void Firework_State_Explode(void)
534
{
535
RSDK_THIS(Firework);
536
537
if (self->timer <= 0) {
538
self->activePlayers = 0;
539
Firework_CheckOffScreen();
540
self->visible = false;
541
}
542
else {
543
self->timer -= 0x40;
544
}
545
}
546
547
void Firework_State_ResetOffScreen(void)
548
{
549
RSDK_THIS(Firework);
550
551
if (RSDK.CheckOnScreen(self, &self->updateRange)) {
552
self->state = Firework_State_Init;
553
Firework_Create(NULL);
554
}
555
}
556
557
void Firework_State_SparkSetup(void)
558
{
559
RSDK_THIS(Firework);
560
561
self->visible = true;
562
self->rotation = 0;
563
self->timer = 0;
564
self->state = Firework_State_Spark;
565
}
566
567
void Firework_State_Spark(void)
568
{
569
RSDK_THIS(Firework);
570
571
if (self->timer >= 10) {
572
if (self->timer == 10) {
573
self->position.x += self->velocity.x;
574
self->position.y += self->velocity.y;
575
576
if (self->mainAnimator.frameID == self->mainAnimator.frameCount - 1)
577
destroyEntity(self);
578
}
579
}
580
else {
581
Firework_HandleSparkAnimations(self->sparkID, self->angle, &self->mainAnimator, &self->sparkAnimator, &self->direction);
582
++self->timer;
583
}
584
}
585
586
#if GAME_INCLUDE_EDITOR
587
void Firework_EditorDraw(void)
588
{
589
RSDK_THIS(Firework);
590
591
RSDK.SetSpriteAnimation(Firework->aniFrames, 0, &self->mainAnimator, false, 0);
592
593
Firework_Draw();
594
595
if (showGizmos()) {
596
RSDK_DRAWING_OVERLAY(true);
597
598
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x, self->position.y - (self->distance << 16), 0xFFFF00, INK_NONE,
599
0xFF);
600
601
RSDK_DRAWING_OVERLAY(false);
602
}
603
}
604
605
void Firework_EditorLoad(void) { Firework->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/Firework.bin", SCOPE_STAGE); }
606
#endif
607
608
void Firework_Serialize(void)
609
{
610
RSDK_EDITABLE_VAR(Firework, VAR_INT32, distance);
611
RSDK_EDITABLE_VAR(Firework, VAR_INT32, innerRadius);
612
RSDK_EDITABLE_VAR(Firework, VAR_INT32, outerRadius);
613
}
614
615