Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/MMZ/MatryoshkaBom.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: MatryoshkaBom Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectMatryoshkaBom *MatryoshkaBom;
11
12
void MatryoshkaBom_Update(void)
13
{
14
RSDK_THIS(MatryoshkaBom);
15
16
StateMachine_Run(self->state);
17
}
18
19
void MatryoshkaBom_LateUpdate(void) {}
20
21
void MatryoshkaBom_StaticUpdate(void) {}
22
23
void MatryoshkaBom_Draw(void)
24
{
25
RSDK_THIS(MatryoshkaBom);
26
27
if (self->state == MatryoshkaBom_State_FuseLit) {
28
Vector2 drawPos;
29
drawPos.x = self->position.x;
30
drawPos.y = self->position.y + self->fusePos;
31
RSDK.DrawSprite(&self->fuseAnimator, &drawPos, false);
32
}
33
34
RSDK.DrawSprite(&self->bodyAnimator, NULL, false);
35
RSDK.DrawSprite(&self->legsAnimator, NULL, false);
36
}
37
38
void MatryoshkaBom_Create(void *data)
39
{
40
RSDK_THIS(MatryoshkaBom);
41
42
self->visible = true;
43
44
if (self->planeFilter > 0 && ((uint8)(self->planeFilter - 1) & 2))
45
self->drawGroup = Zone->objectDrawGroup[1] + 2;
46
else
47
self->drawGroup = Zone->objectDrawGroup[0] + 2;
48
49
self->updateRange.x = 0x800000;
50
self->updateRange.y = 0x800000;
51
52
int32 size = VOID_TO_INT(data);
53
if (size < MATRYOSHKA_SIZE_SHRAPNEL) {
54
self->active = ACTIVE_BOUNDS;
55
if (size)
56
self->size = size;
57
58
self->startDir = self->direction;
59
self->startPos = self->position;
60
61
self->velocity.x = size ? 0xC000 : -0xC000;
62
self->drawFX |= FX_FLIP;
63
self->timer = 0x600;
64
65
switch (self->size) {
66
default: break;
67
case MATRYOSHKA_SIZE_BIG:
68
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 0, &self->bodyAnimator, true, 0);
69
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 13, &self->fuseAnimator, true, 0);
70
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 2, &self->legsAnimator, true, 0);
71
72
self->offsetX = 0x140000;
73
self->offsetY = 0x1B0000;
74
self->canExplode = true;
75
break;
76
77
case MATRYOSHKA_SIZE_MED:
78
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 4, &self->bodyAnimator, true, 0);
79
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 14, &self->fuseAnimator, true, 0);
80
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 6, &self->legsAnimator, true, 0);
81
82
self->offsetX = 0xD0000;
83
self->offsetY = 0x140000;
84
self->canExplode = true;
85
break;
86
87
case MATRYOSHKA_SIZE_SMALL:
88
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 9, &self->bodyAnimator, true, 0);
89
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 15, &self->fuseAnimator, true, 0);
90
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 10, &self->legsAnimator, true, 0);
91
92
self->offsetX = 0x90000;
93
self->offsetY = 0xE0000;
94
self->canExplode = false;
95
break;
96
}
97
98
self->state = MatryoshkaBom_State_Init;
99
}
100
else {
101
self->active = ACTIVE_NORMAL;
102
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 16, &self->bodyAnimator, true, 0);
103
self->state = MatryoshkaBom_State_Shrapnel;
104
}
105
}
106
107
void MatryoshkaBom_StageLoad(void)
108
{
109
if (RSDK.CheckSceneFolder("MMZ"))
110
MatryoshkaBom->aniFrames = RSDK.LoadSpriteAnimation("MMZ/MatryoshkaBom.bin", SCOPE_STAGE);
111
112
MatryoshkaBom->hitboxHurt.left = -12;
113
MatryoshkaBom->hitboxHurt.top = -18;
114
MatryoshkaBom->hitboxHurt.right = 12;
115
MatryoshkaBom->hitboxHurt.bottom = 18;
116
117
MatryoshkaBom->hitboxExplode.left = -96;
118
MatryoshkaBom->hitboxExplode.top = -96;
119
MatryoshkaBom->hitboxExplode.right = 96;
120
MatryoshkaBom->hitboxExplode.bottom = 96;
121
122
MatryoshkaBom->hitboxShrapnel.left = -6;
123
MatryoshkaBom->hitboxShrapnel.top = -6;
124
MatryoshkaBom->hitboxShrapnel.right = 6;
125
MatryoshkaBom->hitboxShrapnel.bottom = 6;
126
127
DEBUGMODE_ADD_OBJ(MatryoshkaBom);
128
129
MatryoshkaBom->sfxExplosion = RSDK.GetSfx("Stage/Explosion.wav");
130
MatryoshkaBom->sfxButton = RSDK.GetSfx("Stage/Button2.wav");
131
MatryoshkaBom->sfxPon = RSDK.GetSfx("Stage/Pon.wav");
132
}
133
134
void MatryoshkaBom_DebugSpawn(void)
135
{
136
RSDK_THIS(DebugMode);
137
138
CREATE_ENTITY(MatryoshkaBom, NULL, self->position.x, self->position.y);
139
}
140
141
void MatryoshkaBom_DebugDraw(void)
142
{
143
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 0, &DebugMode->animator, true, 0);
144
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
145
}
146
147
void MatryoshkaBom_CheckPlayerCollisions(void)
148
{
149
RSDK_THIS(MatryoshkaBom);
150
151
foreach_active(Player, player)
152
{
153
if (self->planeFilter <= 0 || player->collisionPlane == ((uint8)(self->planeFilter - 1) & 1)) {
154
if (self->state != MatryoshkaBom_State_Hatched) {
155
if (self->canExplode) {
156
if (self->state != MatryoshkaBom_State_ReleaseSmallerBuddy) {
157
if (Player_CheckCollisionTouch(player, self, &MatryoshkaBom->hitboxExplode)) {
158
RSDK.PlaySfx(MatryoshkaBom->sfxButton, false, 255);
159
160
switch (self->size) {
161
default: break;
162
case MATRYOSHKA_SIZE_BIG: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 1, &self->bodyAnimator, true, 0); break;
163
case MATRYOSHKA_SIZE_MED: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 5, &self->bodyAnimator, true, 0); break;
164
case MATRYOSHKA_SIZE_SMALL: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 9, &self->bodyAnimator, true, 0); break;
165
}
166
167
self->timer = 21;
168
self->state = MatryoshkaBom_State_ReleaseSmallerBuddy;
169
}
170
}
171
}
172
else if (self->state != MatryoshkaBom_State_FuseLit) {
173
if (Player_CheckCollisionTouch(player, self, &MatryoshkaBom->hitboxExplode)) {
174
self->timer = 144;
175
self->state = MatryoshkaBom_State_FuseLit;
176
}
177
}
178
}
179
180
if (Player_CheckCollisionTouch(player, self, &MatryoshkaBom->hitboxHurt)) {
181
#if MANIA_USE_PLUS
182
if (!Player_CheckMightyUnspin(player, 0x300, 2, &player->uncurlTimer))
183
#endif
184
Player_Hurt(player, self);
185
}
186
}
187
}
188
}
189
190
void MatryoshkaBom_CheckOffScreen(void)
191
{
192
RSDK_THIS(MatryoshkaBom);
193
194
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
195
if (self->destroyOffscreen) {
196
destroyEntity(self);
197
}
198
else {
199
self->position = self->startPos;
200
self->direction = self->startDir;
201
MatryoshkaBom_Create(NULL);
202
}
203
}
204
}
205
206
void MatryoshkaBom_State_Init(void)
207
{
208
RSDK_THIS(MatryoshkaBom);
209
210
self->active = ACTIVE_NORMAL;
211
212
self->state = MatryoshkaBom_State_Walk;
213
MatryoshkaBom_State_Walk();
214
}
215
216
void MatryoshkaBom_State_Walk(void)
217
{
218
RSDK_THIS(MatryoshkaBom);
219
220
self->position.x += self->velocity.x;
221
222
if (!--self->timer) {
223
self->timer = 60;
224
self->state = MatryoshkaBom_State_Stopped;
225
}
226
else {
227
bool32 shouldTurn = false;
228
if (self->velocity.x <= 0)
229
shouldTurn = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -self->offsetX, 0, true);
230
else
231
shouldTurn = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, self->offsetX, 0, true);
232
233
if (!shouldTurn) {
234
if (self->direction & FLIP_Y)
235
shouldTurn = RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, -self->offsetY, 8) == false;
236
else
237
shouldTurn = RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, self->offsetY, 8) == false;
238
}
239
240
if (shouldTurn) {
241
self->timer = 60;
242
self->state = MatryoshkaBom_State_Stopped;
243
244
switch (self->size) {
245
default: break;
246
case MATRYOSHKA_SIZE_BIG: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 0, &self->bodyAnimator, true, 0); break;
247
case MATRYOSHKA_SIZE_MED: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 4, &self->bodyAnimator, true, 0); break;
248
case MATRYOSHKA_SIZE_SMALL: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 9, &self->bodyAnimator, true, 0); break;
249
}
250
}
251
}
252
253
RSDK.ProcessAnimation(&self->bodyAnimator);
254
RSDK.ProcessAnimation(&self->legsAnimator);
255
256
MatryoshkaBom_CheckPlayerCollisions();
257
MatryoshkaBom_CheckOffScreen();
258
}
259
260
void MatryoshkaBom_State_Stopped(void)
261
{
262
RSDK_THIS(MatryoshkaBom);
263
264
if (!--self->timer) {
265
switch (self->size) {
266
default: break;
267
case MATRYOSHKA_SIZE_BIG: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 3, &self->bodyAnimator, true, 0); break;
268
case MATRYOSHKA_SIZE_MED: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 7, &self->bodyAnimator, true, 0); break;
269
case MATRYOSHKA_SIZE_SMALL: RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 11, &self->bodyAnimator, true, 0); break;
270
}
271
272
RSDK.SetSpriteAnimation(-1, 0, &self->legsAnimator, true, 0);
273
self->state = MatryoshkaBom_State_Turn;
274
}
275
276
RSDK.ProcessAnimation(&self->bodyAnimator);
277
278
MatryoshkaBom_CheckPlayerCollisions();
279
MatryoshkaBom_CheckOffScreen();
280
}
281
282
void MatryoshkaBom_State_Turn(void)
283
{
284
RSDK_THIS(MatryoshkaBom);
285
286
RSDK.ProcessAnimation(&self->bodyAnimator);
287
288
if (self->bodyAnimator.frameID == self->bodyAnimator.frameCount - 1) {
289
self->direction ^= FLIP_X;
290
self->velocity.x = -self->velocity.x;
291
self->timer = 0x600;
292
self->state = MatryoshkaBom_State_Walk;
293
294
switch (self->size) {
295
default: break;
296
case MATRYOSHKA_SIZE_BIG:
297
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 0, &self->bodyAnimator, true, 0);
298
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 2, &self->legsAnimator, true, 0);
299
break;
300
301
case MATRYOSHKA_SIZE_MED:
302
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 4, &self->bodyAnimator, true, 0);
303
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 6, &self->legsAnimator, true, 0);
304
break;
305
306
case MATRYOSHKA_SIZE_SMALL:
307
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 9, &self->bodyAnimator, true, 0);
308
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 10, &self->legsAnimator, true, 0);
309
break;
310
}
311
}
312
313
MatryoshkaBom_CheckPlayerCollisions();
314
MatryoshkaBom_CheckOffScreen();
315
}
316
317
void MatryoshkaBom_State_FuseLit(void)
318
{
319
RSDK_THIS(MatryoshkaBom);
320
321
self->fusePos += (self->direction & FLIP_Y) ? -0x1000 : 0x1000;
322
323
if (--self->timer > 0) {
324
RSDK.ProcessAnimation(&self->bodyAnimator);
325
RSDK.ProcessAnimation(&self->fuseAnimator);
326
327
MatryoshkaBom_CheckPlayerCollisions();
328
MatryoshkaBom_CheckOffScreen();
329
}
330
else {
331
RSDK.PlaySfx(MatryoshkaBom->sfxExplosion, false, 255);
332
333
EntityMatryoshkaBom *shrapnel = CREATE_ENTITY(MatryoshkaBom, INT_TO_VOID(MATRYOSHKA_SIZE_SHRAPNEL), self->position.x, self->position.y);
334
shrapnel->velocity.x = -0x20000;
335
shrapnel->velocity.y = -0x30000;
336
shrapnel->planeFilter = self->planeFilter;
337
shrapnel->drawGroup = self->drawGroup;
338
339
shrapnel = CREATE_ENTITY(MatryoshkaBom, INT_TO_VOID(MATRYOSHKA_SIZE_SHRAPNEL), self->position.x, self->position.y);
340
shrapnel->velocity.x = -0x10000;
341
shrapnel->velocity.y = -0x20000;
342
shrapnel->planeFilter = self->planeFilter;
343
shrapnel->drawGroup = self->drawGroup;
344
345
shrapnel = CREATE_ENTITY(MatryoshkaBom, INT_TO_VOID(MATRYOSHKA_SIZE_SHRAPNEL), self->position.x, self->position.y);
346
shrapnel->velocity.x = 0x20000;
347
shrapnel->velocity.y = -0x30000;
348
shrapnel->planeFilter = self->planeFilter;
349
shrapnel->drawGroup = self->drawGroup;
350
351
shrapnel = CREATE_ENTITY(MatryoshkaBom, INT_TO_VOID(MATRYOSHKA_SIZE_SHRAPNEL), self->position.x, self->position.y);
352
shrapnel->velocity.x = 0x10000;
353
shrapnel->velocity.y = -0x20000;
354
shrapnel->planeFilter = self->planeFilter;
355
shrapnel->drawGroup = self->drawGroup;
356
357
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_ENEMY), self->position.x, self->position.y);
358
explosion->drawGroup = self->drawGroup + 1;
359
explosion->planeFilter = self->planeFilter;
360
361
destroyEntity(self);
362
}
363
}
364
365
void MatryoshkaBom_State_ReleaseSmallerBuddy(void)
366
{
367
RSDK_THIS(MatryoshkaBom);
368
369
RSDK.ProcessAnimation(&self->bodyAnimator);
370
371
if (!--self->timer) {
372
RSDK.PlaySfx(MatryoshkaBom->sfxPon, false, 255);
373
374
EntityMatryoshkaBom *child = CREATE_ENTITY(MatryoshkaBom, INT_TO_VOID(self->size + 1), self->position.x, self->position.y);
375
376
child->velocity.x = self->direction == FLIP_NONE ? -0x18000 : 0x18000;
377
child->velocity.y = -0x40000;
378
379
child->planeFilter = self->planeFilter;
380
child->drawGroup = self->drawGroup - 1;
381
child->active = ACTIVE_NORMAL;
382
child->direction = self->direction;
383
child->destroyOffscreen = true;
384
child->state = MatryoshkaBom_State_Hatched;
385
self->canExplode = false;
386
}
387
388
if (self->bodyAnimator.frameID >= self->bodyAnimator.frameDuration - 1)
389
self->state = MatryoshkaBom_State_Walk;
390
391
MatryoshkaBom_CheckPlayerCollisions();
392
MatryoshkaBom_CheckOffScreen();
393
}
394
395
void MatryoshkaBom_State_Hatched(void)
396
{
397
RSDK_THIS(MatryoshkaBom);
398
399
self->position.x += self->velocity.x;
400
self->position.y += self->velocity.y;
401
self->velocity.y += 0x3800;
402
403
if (self->velocity.x) {
404
bool32 collided = false;
405
if (self->velocity.x <= 0)
406
collided = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -self->offsetX, 0, true);
407
else
408
collided = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, self->offsetX, 0, true);
409
410
if (collided)
411
self->velocity.x = 0;
412
}
413
414
bool32 collided = false;
415
if (self->direction & FLIP_Y)
416
collided = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, -self->offsetY, true);
417
else
418
collided = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, self->offsetY, true);
419
420
if (collided) {
421
self->timer = 0x600;
422
self->velocity.x = self->direction == FLIP_NONE ? -0xC000 : 0xC000;
423
self->state = MatryoshkaBom_State_Walk;
424
}
425
426
RSDK.ProcessAnimation(&self->bodyAnimator);
427
428
MatryoshkaBom_CheckPlayerCollisions();
429
MatryoshkaBom_CheckOffScreen();
430
}
431
432
void MatryoshkaBom_State_Shrapnel(void)
433
{
434
RSDK_THIS(MatryoshkaBom);
435
436
self->position.x += self->velocity.x;
437
self->position.y += self->velocity.y;
438
self->velocity.y += 0x1800;
439
440
if (RSDK.CheckOnScreen(self, &self->updateRange)) {
441
RSDK.ProcessAnimation(&self->bodyAnimator);
442
443
foreach_active(Player, player)
444
{
445
if (self->planeFilter <= 0 || player->collisionPlane == ((uint8)(self->planeFilter - 1) & 1)) {
446
if (Player_CheckCollisionTouch(player, self, &MatryoshkaBom->hitboxShrapnel)) {
447
Player_Hurt(player, self);
448
}
449
}
450
}
451
}
452
else {
453
destroyEntity(self);
454
}
455
}
456
457
#if GAME_INCLUDE_EDITOR
458
void MatryoshkaBom_EditorDraw(void)
459
{
460
RSDK_THIS(MatryoshkaBom);
461
462
self->drawFX |= FX_FLIP;
463
464
switch (self->size) {
465
default: break;
466
case MATRYOSHKA_SIZE_BIG:
467
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 0, &self->bodyAnimator, false, 0);
468
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 13, &self->fuseAnimator, false, 0);
469
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 2, &self->legsAnimator, false, 0);
470
break;
471
472
case MATRYOSHKA_SIZE_MED:
473
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 4, &self->bodyAnimator, false, 0);
474
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 14, &self->fuseAnimator, false, 0);
475
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 6, &self->legsAnimator, false, 0);
476
break;
477
478
case MATRYOSHKA_SIZE_SMALL:
479
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 9, &self->bodyAnimator, false, 0);
480
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 15, &self->fuseAnimator, false, 0);
481
RSDK.SetSpriteAnimation(MatryoshkaBom->aniFrames, 10, &self->legsAnimator, false, 0);
482
break;
483
}
484
485
MatryoshkaBom_Draw();
486
}
487
488
void MatryoshkaBom_EditorLoad(void)
489
{
490
MatryoshkaBom->aniFrames = RSDK.LoadSpriteAnimation("MMZ/MatryoshkaBom.bin", SCOPE_STAGE);
491
492
RSDK_ACTIVE_VAR(MatryoshkaBom, direction);
493
RSDK_ENUM_VAR("Left (Rightside Up)", FLIP_NONE);
494
RSDK_ENUM_VAR("Right (Rightside Up)", FLIP_X);
495
RSDK_ENUM_VAR("Left (Upside down)", FLIP_Y);
496
RSDK_ENUM_VAR("Right (Upside down)", FLIP_XY);
497
498
RSDK_ACTIVE_VAR(MatryoshkaBom, planeFilter);
499
RSDK_ENUM_VAR("None", PLANEFILTER_NONE);
500
RSDK_ENUM_VAR("AL", PLANEFILTER_AL);
501
RSDK_ENUM_VAR("BL", PLANEFILTER_BL);
502
RSDK_ENUM_VAR("AH", PLANEFILTER_AH);
503
RSDK_ENUM_VAR("BH", PLANEFILTER_BH);
504
505
RSDK_ACTIVE_VAR(MatryoshkaBom, size);
506
RSDK_ENUM_VAR("Big", MATRYOSHKA_SIZE_BIG);
507
RSDK_ENUM_VAR("Medium", MATRYOSHKA_SIZE_MED);
508
RSDK_ENUM_VAR("Small", MATRYOSHKA_SIZE_SMALL);
509
}
510
#endif
511
512
void MatryoshkaBom_Serialize(void)
513
{
514
RSDK_EDITABLE_VAR(MatryoshkaBom, VAR_UINT8, direction);
515
RSDK_EDITABLE_VAR(MatryoshkaBom, VAR_ENUM, planeFilter);
516
RSDK_EDITABLE_VAR(MatryoshkaBom, VAR_ENUM, size);
517
}
518
519