Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/HCZ/DiveEggman.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: DiveEggman Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectDiveEggman *DiveEggman;
11
12
void DiveEggman_Update(void)
13
{
14
RSDK_THIS(DiveEggman);
15
16
StateMachine_Run(self->state);
17
}
18
19
void DiveEggman_LateUpdate(void) {}
20
21
void DiveEggman_StaticUpdate(void) {}
22
23
void DiveEggman_Draw(void)
24
{
25
RSDK_THIS(DiveEggman);
26
27
if (self->invincibilityTimer & 1)
28
RSDK.SetPaletteEntry(0, 130, 0xE0E0E0);
29
30
RSDK.DrawSprite(&self->animator, NULL, false);
31
32
RSDK.SetPaletteEntry(0, 130, 0x001840);
33
}
34
35
void DiveEggman_Create(void *data)
36
{
37
RSDK_THIS(DiveEggman);
38
39
if (globals->gameMode == MODE_TIMEATTACK) {
40
destroyEntity(self);
41
}
42
else {
43
self->drawFX = FX_FLIP;
44
if (!SceneInfo->inEditor) {
45
self->visible = true;
46
if (data) {
47
switch (VOID_TO_INT(data)) {
48
default: break;
49
50
case DIVEEGGMAN_BOMB:
51
self->active = ACTIVE_NORMAL;
52
self->drawGroup = Zone->playerDrawGroup[0] + 1;
53
self->timer = 480;
54
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 5, &self->animator, true, 0);
55
self->state = DiveEggman_StateBomb_Idle;
56
break;
57
}
58
}
59
else {
60
self->active = ACTIVE_BOUNDS;
61
self->drawGroup = Zone->playerDrawGroup[0] + 2;
62
self->updateRange.x = 0x800000;
63
self->startY = self->position.y;
64
self->updateRange.y = 0x800000;
65
self->health = 4;
66
DiveEggman->screwMobile = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, ScrewMobile);
67
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 0, &self->animator, true, 0);
68
self->state = DiveEggman_StateEggman_AwaitPlayer;
69
}
70
}
71
}
72
}
73
74
void DiveEggman_StageLoad(void)
75
{
76
if (RSDK.CheckSceneFolder("HCZ")) {
77
DiveEggman->diveFrames = RSDK.LoadSpriteAnimation("HCZ/DiveEggman.bin", SCOPE_STAGE);
78
DiveEggman->aniFrames = RSDK.LoadSpriteAnimation("Eggman/EggmanHCZ1.bin", SCOPE_STAGE);
79
}
80
81
DiveEggman->unusedHitbox1.left = -8;
82
DiveEggman->unusedHitbox1.top = -8;
83
DiveEggman->unusedHitbox1.right = 8;
84
DiveEggman->unusedHitbox1.bottom = 8;
85
86
DiveEggman->hitboxBomb.left = -8;
87
DiveEggman->hitboxBomb.top = -8;
88
DiveEggman->hitboxBomb.right = 8;
89
DiveEggman->hitboxBomb.bottom = 8;
90
91
DiveEggman->hitboxEggman.left = -26;
92
DiveEggman->hitboxEggman.top = -22;
93
DiveEggman->hitboxEggman.right = 7;
94
DiveEggman->hitboxEggman.bottom = 11;
95
96
DiveEggman->sfxBigFan = RSDK.GetSfx("HCZ/BigFan.wav");
97
DiveEggman->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
98
DiveEggman->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
99
DiveEggman->sfxRockemSockem = RSDK.GetSfx("Stage/RockemSockem.wav");
100
}
101
102
void DiveEggman_Hit(void)
103
{
104
RSDK_THIS(DiveEggman);
105
106
RSDK.PlaySfx(DiveEggman->sfxHit, false, 255);
107
108
self->invincibilityTimer = 30;
109
if (self->health)
110
self->health--;
111
112
if (!self->health) {
113
self->timer = 120;
114
self->drawGroup = Zone->hudDrawGroup - 1;
115
self->state = DiveEggman_StateEggman_Destroyed;
116
SceneInfo->timeEnabled = false;
117
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
118
}
119
}
120
121
void DiveEggman_Explode(void)
122
{
123
RSDK_THIS(DiveEggman);
124
125
if (!(Zone->timer & 7)) {
126
RSDK.PlaySfx(DiveEggman->sfxExplosion, false, 255);
127
128
if (!(Zone->timer & 0xF)) {
129
int32 x = self->position.x + (RSDK.Rand(-19, 20) << 16);
130
int32 y = self->position.y + (RSDK.Rand(-24, 25) << 16);
131
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);
132
133
if (self->timer <= 40)
134
explosion->drawGroup = self->drawGroup;
135
else
136
explosion->drawGroup = self->drawGroup - 1;
137
}
138
}
139
}
140
141
void DiveEggman_StateEggman_AwaitPlayer(void)
142
{
143
RSDK_THIS(DiveEggman);
144
145
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
146
147
RSDK.ProcessAnimation(&self->animator);
148
149
if (self->invincibilityTimer > 0)
150
self->invincibilityTimer--;
151
152
if (screwMobile->state != ScrewMobile_State_CheckPlayerEnter) {
153
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 1, &self->animator, true, 0);
154
self->velocity.x = -0x10000;
155
self->active = ACTIVE_NORMAL;
156
self->state = DiveEggman_StateEggman_Swimming;
157
}
158
159
if (!RSDK.CheckOnScreen(self, &self->updateRange))
160
destroyEntity(self);
161
}
162
163
void DiveEggman_StateEggman_Swimming(void)
164
{
165
RSDK_THIS(DiveEggman);
166
167
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
168
169
RSDK.ProcessAnimation(&self->animator);
170
if (self->invincibilityTimer > 0)
171
self->invincibilityTimer--;
172
173
self->position.x += self->velocity.x;
174
175
if (self->velocity.y > 0) {
176
self->position.y += self->velocity.y;
177
self->velocity.y -= 0x400;
178
}
179
180
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, 0x200000, 0, true)
181
|| RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -0x200000, 0, true)) {
182
self->direction ^= FLIP_X;
183
self->velocity.x = -self->velocity.x;
184
}
185
186
if (screwMobile->propellerAnimator.speed >= 0xFF) {
187
if (self->position.x <= screwMobile->position.x) {
188
self->direction = FLIP_NONE;
189
self->velocity.x = 0x8000;
190
}
191
else {
192
self->direction = FLIP_X;
193
self->velocity.x = -0x8000;
194
}
195
196
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 2, &self->animator, false, 0);
197
self->state = DiveEggman_StateEggman_InWhirlpool;
198
}
199
200
if (--self->timer <= 0) {
201
self->timer = 180;
202
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 4, &self->animator, false, 0);
203
self->state = DiveEggman_StateEggman_PlaceBomb;
204
}
205
}
206
207
void DiveEggman_StateEggman_InWhirlpool(void)
208
{
209
RSDK_THIS(DiveEggman);
210
211
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
212
213
RSDK.ProcessAnimation(&self->animator);
214
215
if (self->invincibilityTimer > 0)
216
self->invincibilityTimer--;
217
218
self->position.x += self->velocity.x;
219
220
if (self->position.y >= Water->waterLevel + 0x180000)
221
self->position.y -= 0x2000;
222
223
if (screwMobile->propellerAnimator.speed >= 0x100) {
224
if (abs(self->position.x - screwMobile->position.x) < 0x100000) {
225
int32 x = self->position.x - screwMobile->position.x;
226
int32 y = MathHelpers_SquareRoot(0x100 - (x >> 16) * (x >> 16)) << 16;
227
228
self->angle = RSDK.ATan2(x, y);
229
if (self->angle < 0x80)
230
self->drawGroup = Zone->playerDrawGroup[0] + 2;
231
else
232
self->drawGroup = Zone->hudDrawGroup - 1;
233
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 3, &self->animator, false, 0);
234
self->state = DiveEggman_StateEggman_WhirlpoolRise;
235
}
236
}
237
else {
238
if (self->direction == FLIP_NONE)
239
self->velocity.x = -0x10000;
240
else
241
self->velocity.x = 0x10000;
242
243
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 1, &self->animator, false, 0);
244
self->state = DiveEggman_StateEggman_Swimming;
245
}
246
}
247
248
void DiveEggman_StateEggman_WhirlpoolRise(void)
249
{
250
RSDK_THIS(DiveEggman);
251
252
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
253
254
RSDK.ProcessAnimation(&self->animator);
255
256
if (self->invincibilityTimer > 0)
257
self->invincibilityTimer--;
258
259
self->position.x = (RSDK.Cos256(self->angle) << 12) + screwMobile->position.x;
260
self->position.y -= 0x10000;
261
262
if ((self->angle & 0xFF) < 0x80)
263
self->drawGroup = Zone->playerDrawGroup[0] + 2;
264
else
265
self->drawGroup = Zone->hudDrawGroup - 1;
266
self->angle += 4;
267
268
if (screwMobile->propellerAnimator.speed >= 0x100) {
269
if (self->position.y < screwMobile->position.y + 0x280000) {
270
DiveEggman_Hit();
271
screwMobile->whirlPoolTimer = 60;
272
}
273
}
274
else {
275
if (self->position.y < Water->waterLevel - (screwMobile->whirlpoolHeight << 16)) {
276
self->velocity.y = -0x10000;
277
self->state = DiveEggman_StateEggman_Falling;
278
self->velocity.x = RSDK.Cos256(self->angle) << 8;
279
}
280
}
281
}
282
283
void DiveEggman_StateEggman_Falling(void)
284
{
285
RSDK_THIS(DiveEggman);
286
287
RSDK.ProcessAnimation(&self->animator);
288
289
if (self->invincibilityTimer > 0)
290
self->invincibilityTimer--;
291
292
self->position.x += self->velocity.x;
293
self->position.y += self->velocity.y;
294
self->velocity.y += 0x2800;
295
296
if (self->position.y >= Water->waterLevel) {
297
self->velocity.y >>= 2;
298
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);
299
300
RSDK.PlaySfx(Water->sfxSplash, false, 255);
301
self->velocity.x = self->direction == FLIP_NONE ? -0x10000 : 0x10000;
302
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 1, &self->animator, false, 0);
303
self->state = DiveEggman_StateEggman_Swimming;
304
}
305
}
306
307
void DiveEggman_StateEggman_PlaceBomb(void)
308
{
309
RSDK_THIS(DiveEggman);
310
311
RSDK.ProcessAnimation(&self->animator);
312
313
if (self->invincibilityTimer > 0)
314
self->invincibilityTimer--;
315
316
if (self->animator.frameID >= self->animator.frameCount - 1) {
317
EntityDiveEggman *bomb = CREATE_ENTITY(DiveEggman, INT_TO_VOID(DIVEEGGMAN_BOMB), self->position.x, self->position.y + 0x20000);
318
bomb->position.x += self->direction ? 0x1A0000 : -0x1A0000;
319
320
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 1, &self->animator, false, 0);
321
self->state = DiveEggman_StateEggman_Swimming;
322
}
323
}
324
325
void DiveEggman_StateEggman_Destroyed(void)
326
{
327
RSDK_THIS(DiveEggman);
328
329
RSDK.ProcessAnimation(&self->animator);
330
331
if (self->invincibilityTimer > 0)
332
self->invincibilityTimer--;
333
334
DiveEggman_Explode();
335
336
if (--self->timer <= 0) {
337
Music_TransitionTrack(TRACK_STAGE, 0.0125);
338
RSDK.SetSpriteAnimation(DiveEggman->aniFrames, 0, &self->animator, true, 0);
339
self->velocity.y = -0x20000;
340
self->timer = 0x2000;
341
self->state = DiveEggman_StateEggman_Finish;
342
}
343
}
344
345
void DiveEggman_StateEggman_Finish(void)
346
{
347
RSDK_THIS(DiveEggman);
348
349
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
350
351
RSDK.ProcessAnimation(&self->animator);
352
353
if (self->invincibilityTimer > 0)
354
self->invincibilityTimer--;
355
356
self->position.x += self->velocity.x;
357
358
self->position.y += self->velocity.y;
359
self->velocity.y += self->timer;
360
361
if (self->timer == 0x2000 && self->position.y >= Water->waterLevel) {
362
self->velocity.y >>= 2;
363
self->timer = 0x1000;
364
365
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);
366
RSDK.PlaySfx(Water->sfxSplash, false, 255);
367
}
368
369
if (!RSDK.CheckOnScreen(self, NULL)) {
370
Zone->cameraBoundsT[0] = 0;
371
screwMobile->state = ScrewMobile_State_BossFinished;
372
destroyEntity(self);
373
}
374
}
375
376
bool32 DiveEggman_CheckNoBombExplode(void)
377
{
378
RSDK_THIS(DiveEggman);
379
380
RSDK.ProcessAnimation(&self->animator);
381
382
if (!--self->timer) {
383
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
384
RSDK.PlaySfx(DiveEggman->sfxExplosion, false, 255);
385
386
EntityWater *water = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y);
387
water->velocity.y = -0x8800;
388
water->angle = 2 * RSDK.Rand(0, 256);
389
water->bubbleX = water->position.x;
390
water->childPtr = 0;
391
RSDK.SetSpriteAnimation(Water->aniFrames, 3, &water->animator, true, 0);
392
393
destroyEntity(self);
394
return false;
395
}
396
397
return true;
398
}
399
400
void DiveEggman_StateBomb_Idle(void)
401
{
402
RSDK_THIS(DiveEggman);
403
404
if (self->velocity.x) {
405
if (self->velocity.x <= 0)
406
self->velocity.x += 0x2000;
407
else
408
self->velocity.x -= 0x2000;
409
}
410
411
if (self->velocity.y > 0) {
412
self->position.y += self->velocity.y;
413
self->velocity.y -= 0x400;
414
}
415
416
if (DiveEggman_CheckNoBombExplode()) {
417
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
418
419
if (screwMobile->propellerAnimator.speed >= 0xFF) {
420
self->state = DiveEggman_StateBomb_InWhirlpool;
421
self->velocity.x = self->position.x > screwMobile->position.x ? -0x10000 : 0x10000;
422
}
423
}
424
}
425
426
void DiveEggman_StateBomb_InWhirlpool(void)
427
{
428
RSDK_THIS(DiveEggman);
429
430
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
431
432
if (DiveEggman_CheckNoBombExplode()) {
433
self->position.x += self->velocity.x;
434
435
if (self->position.y >= Water->waterLevel + 0x100000)
436
self->position.y -= 0x2000;
437
438
if (screwMobile->propellerAnimator.speed >= 0x100) {
439
if (abs(self->position.x - screwMobile->position.x) < 0x100000) {
440
int32 x = self->position.x - screwMobile->position.x;
441
int32 y = MathHelpers_SquareRoot(0x100 - (x >> 16) * (x >> 16)) << 16;
442
443
self->angle = RSDK.ATan2(x, y);
444
if (self->angle < 0x80)
445
self->drawGroup = Zone->playerDrawGroup[0] + 2;
446
else
447
self->drawGroup = Zone->hudDrawGroup - 1;
448
449
self->state = DiveEggman_StateBomb_WhirlpoolRise;
450
}
451
}
452
else {
453
self->state = DiveEggman_StateBomb_Idle;
454
}
455
}
456
}
457
458
void DiveEggman_StateBomb_WhirlpoolRise(void)
459
{
460
RSDK_THIS(DiveEggman);
461
462
EntityScrewMobile *screwMobile = DiveEggman->screwMobile;
463
464
if (DiveEggman_CheckNoBombExplode()) {
465
self->position.x = (RSDK.Cos256(self->angle) << 12) + screwMobile->position.x;
466
self->position.y -= 0x10000;
467
468
if ((self->angle & 0xFF) < 0x80)
469
self->drawGroup = Zone->playerDrawGroup[0] + 2;
470
else
471
self->drawGroup = Zone->hudDrawGroup - 1;
472
self->angle += 4;
473
474
if (screwMobile->propellerAnimator.speed >= 0x100) {
475
if (self->position.y < screwMobile->position.y + 0x180000) {
476
self->timer = 1;
477
--screwMobile->health; // ??? what the...
478
screwMobile->invincibilityTimer = 30;
479
screwMobile->whirlPoolTimer = 30;
480
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
481
482
// Hit that player!
483
if (!player1->blinkTimer && !player1->invincibleTimer) {
484
if (player1->shield) {
485
EntityShield *shield = RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player1), Shield);
486
if (shield->classID == Shield->classID)
487
destroyEntity(shield);
488
489
player1->shield = SHIELD_NONE;
490
player1->blinkTimer = 120;
491
RSDK.PlaySfx(Player->sfxHurt, false, 255);
492
}
493
else {
494
if (player1->rings <= 0) {
495
player1->deathType = PLAYER_DEATH_DIE_USESFX;
496
RSDK.PlaySfx(Player->sfxHurt, false, 255);
497
ScrewMobile->fanSfxTimer = 0;
498
}
499
else {
500
player1->blinkTimer = 120;
501
if (!player1->hyperRing)
502
Ring_LoseRings(player1, player1->rings, player1->collisionPlane);
503
else
504
Ring_LoseHyperRings(player1, player1->rings, player1->collisionPlane);
505
506
player1->hyperRing = false;
507
player1->rings = 0;
508
player1->ringExtraLife = 100;
509
RSDK.PlaySfx(Player->sfxLoseRings, false, 255);
510
}
511
}
512
DiveEggman_CheckNoBombExplode();
513
}
514
}
515
}
516
else {
517
if (self->position.y < Water->waterLevel - (screwMobile->whirlpoolHeight << 16)) {
518
self->velocity.y = -0x10000;
519
self->state = DiveEggman_StateBomb_Falling;
520
self->velocity.x = (RSDK.Cos256(self->angle) << 8);
521
}
522
}
523
}
524
}
525
526
void DiveEggman_StateBomb_Falling(void)
527
{
528
RSDK_THIS(DiveEggman);
529
530
if (DiveEggman_CheckNoBombExplode()) {
531
self->position.x += self->velocity.x;
532
self->position.y += self->velocity.y;
533
self->velocity.y += 0x2000;
534
535
if (self->position.y >= Water->waterLevel) {
536
self->velocity.y >>= 2;
537
538
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, Water->waterLevel);
539
RSDK.PlaySfx(Water->sfxSplash, false, 255);
540
541
self->state = DiveEggman_StateBomb_Idle;
542
}
543
}
544
}
545
546
#if GAME_INCLUDE_EDITOR
547
void DiveEggman_EditorDraw(void)
548
{
549
RSDK_THIS(DiveEggman);
550
551
RSDK.SetSpriteAnimation(DiveEggman->diveFrames, 0, &self->animator, true, 0);
552
553
DiveEggman_Draw();
554
555
if (showGizmos()) {
556
RSDK_DRAWING_OVERLAY(true);
557
558
EntityCollapsingPlatform *screwMobile = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, CollapsingPlatform);
559
if (ScrewMobile && screwMobile->classID == ScrewMobile->classID)
560
DrawHelpers_DrawArrow(self->position.x, self->position.y, screwMobile->position.x, screwMobile->position.y, 0xFFFF00, INK_NONE, 0xFF);
561
562
RSDK_DRAWING_OVERLAY(false);
563
}
564
}
565
566
void DiveEggman_EditorLoad(void) { DiveEggman->diveFrames = RSDK.LoadSpriteAnimation("HCZ/DiveEggman.bin", SCOPE_STAGE); }
567
#endif
568
569
void DiveEggman_Serialize(void) {}
570
571