Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/ERZ/ERZKing.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: ERZKing Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectERZKing *ERZKing;
11
12
void ERZKing_Update(void)
13
{
14
RSDK_THIS(ERZKing);
15
16
if (self->invincibilityTimer > 0)
17
self->invincibilityTimer--;
18
19
StateMachine_Run(self->state);
20
}
21
22
void ERZKing_LateUpdate(void) {}
23
24
void ERZKing_StaticUpdate(void) {}
25
26
void ERZKing_Draw(void)
27
{
28
RSDK_THIS(ERZKing);
29
30
if (self->stateDraw) {
31
StateMachine_Run(self->stateDraw);
32
}
33
else {
34
RSDK.DrawSprite(&self->basicAnimator, NULL, false);
35
}
36
}
37
38
void ERZKing_Create(void *data)
39
{
40
RSDK_THIS(ERZKing);
41
42
self->drawFX = FX_FLIP;
43
if (!SceneInfo->inEditor) {
44
self->active = ACTIVE_BOUNDS;
45
self->updateRange.x = 0x800000;
46
self->updateRange.y = 0x1000000;
47
self->drawGroup = Zone->objectDrawGroup[0];
48
self->drawFX = FX_ROTATE | FX_FLIP;
49
self->type = VOID_TO_INT(data);
50
51
switch (self->type) {
52
case ERZKING_KING:
53
self->hitbox.left = -24;
54
self->hitbox.top = -24;
55
self->hitbox.right = 24;
56
self->hitbox.bottom = 24;
57
58
self->visible = false;
59
self->direction = FLIP_X;
60
self->health = 8;
61
62
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 0, &self->headAnimator, true, 0);
63
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 1, &self->bodyAnimator, true, 0);
64
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 2, &self->beltAnimator, true, 0);
65
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 7, &self->particleAnimator, true, 0);
66
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 8, &self->rubyAnimator, true, 0);
67
68
self->originPos = self->position;
69
self->state = ERZKing_State_SetupArena;
70
break;
71
72
case ERZKING_ARM_L:
73
case ERZKING_ARM_R:
74
self->visible = true;
75
76
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 3, &self->armAnimator, true, 0);
77
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 4, &self->cuffAnimator, true, 0);
78
79
if (self->type == ERZKING_ARM_L) {
80
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 6, &self->handAnimator, true, 0);
81
}
82
else {
83
self->drawGroup = Zone->playerDrawGroup[0];
84
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 5, &self->handAnimator, true, 0);
85
}
86
87
self->stateDraw = ERZKing_Draw_Arm;
88
self->state = ERZKing_State_Arm;
89
break;
90
}
91
}
92
}
93
94
void ERZKing_StageLoad(void)
95
{
96
ERZKing->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomKing.bin", SCOPE_STAGE);
97
98
ERZKing->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
99
ERZKing->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");
100
}
101
102
void ERZKing_CheckPlayerCollisions(void)
103
{
104
RSDK_THIS(ERZKing);
105
106
foreach_active(Player, player)
107
{
108
if (!self->invincibilityTimer && Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self)) {
109
ERZKing_Hit();
110
}
111
}
112
}
113
114
void ERZKing_Hit(void)
115
{
116
RSDK_THIS(ERZKing);
117
118
if (--self->health <= 0) {
119
self->originPos.x = self->position.x;
120
self->originPos.y = self->position.y;
121
self->state = ERZKing_State_Explode;
122
self->velocity.y = -0x10000;
123
self->timer = 0;
124
SceneInfo->timeEnabled = false;
125
}
126
else {
127
self->invincibilityTimer = 48;
128
RSDK.PlaySfx(ERZKing->sfxHit, false, 255);
129
}
130
}
131
132
void ERZKing_Explode(void)
133
{
134
RSDK_THIS(ERZKing);
135
136
if (!(Zone->timer % 3)) {
137
RSDK.PlaySfx(ERZKing->sfxExplosion2, false, 255);
138
139
if (Zone->timer & 4) {
140
int32 x = self->position.x + (RSDK.Rand(self->hitbox.left, self->hitbox.right) << 16);
141
int32 y = self->position.y + (RSDK.Rand(self->hitbox.top, self->hitbox.bottom) << 16);
142
CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];
143
}
144
}
145
}
146
147
void ERZKing_HandleFrames(void)
148
{
149
RSDK_THIS(ERZKing);
150
151
RSDK.ProcessAnimation(&self->bodyAnimator);
152
153
self->rotation = RSDK.Sin512(2 * Zone->timer) >> 6;
154
int32 negAng = -self->rotation;
155
156
self->bodyAngle = (self->bodyAngle + 12) & 0x3FF;
157
158
int32 x = 0x1C00 * RSDK.Sin512(negAng) + self->position.x;
159
int32 y = 0x1C00 * RSDK.Cos512(negAng) + self->position.y;
160
161
int32 angle = self->bodyAngle;
162
163
for (int32 i = 0; i < 10; i += 2) {
164
self->armPositions[i].x = x + 2 * RSDK.Cos512(self->rotation) * RSDK.Cos1024(angle);
165
self->armPositions[i].y = y + 2 * RSDK.Sin512(self->rotation) * RSDK.Cos1024(angle);
166
self->armAngles[i] = angle & 0x3FF;
167
168
angle += 0x200;
169
170
self->armPositions[i + 1].x = x + 2 * RSDK.Cos512(self->rotation) * RSDK.Cos1024(angle);
171
self->armPositions[i + 1].y = y + 2 * RSDK.Sin512(self->rotation) * RSDK.Cos1024(angle);
172
self->armAngles[i + 1] = angle & 0x3FF;
173
174
x += RSDK.Sin512(negAng) << 10;
175
y += RSDK.Cos512(negAng) << 10;
176
angle += 0x240;
177
}
178
179
self->rubyPos.x = self->position.x - 0x1400 * RSDK.Sin512(negAng);
180
self->rubyPos.y = self->position.y - 0x1400 * RSDK.Cos512(negAng);
181
if (self->direction) {
182
self->rubyPos.x -= 0x180 * RSDK.Cos512(negAng);
183
self->rubyPos.y -= 0x180 * RSDK.Sin512(negAng);
184
}
185
else {
186
self->rubyPos.x += 0x180 * RSDK.Cos512(negAng);
187
self->rubyPos.y += 0x180 * RSDK.Sin512(negAng);
188
}
189
}
190
191
void ERZKing_Draw_Body(void)
192
{
193
RSDK_THIS(ERZKing);
194
195
if (self->typeChangeTimer <= 0) {
196
if (self->invincibilityTimer & 1)
197
RSDK.CopyPalette(2, 128, 0, 128, 128);
198
}
199
else {
200
RSDK.SetLimitedFade(0, 1, 4, self->typeChangeTimer, 0, 48);
201
RSDK.SetLimitedFade(0, 1, 4, self->typeChangeTimer, 128, 256);
202
}
203
204
RSDK.DrawSprite(&self->headAnimator, NULL, false);
205
RSDK.DrawSprite(&self->bodyAnimator, NULL, false);
206
207
for (int32 i = 0; i < 10; ++i) {
208
if (self->armAngles[i] < 0x200) {
209
self->particleAnimator.frameID = self->armAngles[i] / 42 % 6;
210
RSDK.DrawSprite(&self->particleAnimator, &self->armPositions[i], false);
211
}
212
}
213
214
self->drawFX = self->storeDrawFX | FX_ROTATE;
215
RSDK.DrawSprite(&self->beltAnimator, NULL, false);
216
217
self->drawFX = self->storeDrawFX | FX_ROTATE | FX_FLIP;
218
for (int32 i = 0; i < 10; ++i) {
219
if (self->armAngles[i] >= 0x200) {
220
self->particleAnimator.frameID = self->armAngles[i] / 42 % 6;
221
RSDK.DrawSprite(&self->particleAnimator, &self->armPositions[i], false);
222
}
223
}
224
225
RSDK.DrawSprite(&self->rubyAnimator, &self->rubyPos, false);
226
227
if (self->typeChangeTimer <= 0) {
228
if (self->invincibilityTimer & 1)
229
RSDK.CopyPalette(1, 128, 0, 128, 128);
230
}
231
else {
232
RSDK.CopyPalette(1, 0, 0, 0, 48);
233
RSDK.CopyPalette(1, 128, 0, 128, 128);
234
}
235
}
236
237
void ERZKing_Draw_Arm(void)
238
{
239
RSDK_THIS(ERZKing);
240
241
EntityERZKing *parent = self->parent;
242
243
if (parent->typeChangeTimer > 0) {
244
RSDK.SetLimitedFade(0, 1, 4, parent->typeChangeTimer, 0, 48);
245
RSDK.SetLimitedFade(0, 1, 4, parent->typeChangeTimer, 128, 256);
246
}
247
248
for (int32 i = 0; i < 6; ++i) {
249
RSDK.DrawSprite(&self->armAnimator, &self->armPositions[i], false);
250
}
251
252
RSDK.DrawSprite(&self->cuffAnimator, &self->armPositions[6], false);
253
RSDK.DrawSprite(&self->handAnimator, &self->armPositions[6], false);
254
255
if (parent->typeChangeTimer > 0) {
256
RSDK.CopyPalette(1, 0, 0, 0, 48);
257
RSDK.CopyPalette(1, 128, 0, 128, 128);
258
}
259
}
260
261
void ERZKing_State_SetupArena(void)
262
{
263
RSDK_THIS(ERZKing);
264
265
if (++self->timer >= 8) {
266
self->timer = 0;
267
268
Zone->playerBoundActiveL[0] = true;
269
Zone->playerBoundActiveR[0] = true;
270
Zone->cameraBoundsL[0] = (self->position.x >> 16) - 320;
271
Zone->cameraBoundsR[0] = (self->position.x >> 16) + 320;
272
Zone->cameraBoundsT[0] = Zone->cameraBoundsB[0] - ScreenInfo->size.y;
273
274
ERZKing->boundsL = (Zone->cameraBoundsL[0] + 64) << 16;
275
ERZKing->boundsR = (Zone->cameraBoundsR[0] - 64) << 16;
276
ERZKing->boundsM = self->position.x;
277
ERZKing->boundsT = (Zone->cameraBoundsT[0] + 48) << 16;
278
ERZKing->boundsB = (Zone->cameraBoundsB[0] - 96) << 16;
279
280
self->position.y += 0x1000000;
281
self->active = ACTIVE_NORMAL;
282
self->state = ERZKing_State_SetupBody;
283
}
284
}
285
286
void ERZKing_State_SetupBody(void)
287
{
288
RSDK_THIS(ERZKing);
289
290
if (self->timer) {
291
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x < self->position.x;
292
293
if (++self->timer == 30) {
294
EntityERZKing *leftArm = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, ERZKing);
295
RSDK.ResetEntity(leftArm, ERZKing->classID, INT_TO_VOID(ERZKING_ARM_L));
296
leftArm->position.x = self->position.x;
297
leftArm->position.y = self->position.y;
298
leftArm->parent = self;
299
300
EntityERZKing *rightArm = RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, ERZKing);
301
RSDK.ResetEntity(rightArm, ERZKing->classID, INT_TO_VOID(ERZKING_ARM_R));
302
rightArm->position.x = self->position.x;
303
rightArm->position.y = self->position.y;
304
rightArm->parent = self;
305
306
self->timer = 0;
307
self->visible = true;
308
self->stateDraw = ERZKing_Draw_Body;
309
self->state = ERZKing_State_EnterKing;
310
}
311
}
312
else {
313
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x)
314
++self->timer;
315
}
316
}
317
318
void ERZKing_State_EnterKing(void)
319
{
320
RSDK_THIS(ERZKing);
321
322
RSDK.ProcessAnimation(&self->beltAnimator);
323
324
self->velocity.y -= 0x1800;
325
326
if (self->position.y <= self->originPos.y - 0x200000) {
327
self->originPos = self->position;
328
self->state = ERZKing_State_FlyAround;
329
}
330
else {
331
self->position.y += self->velocity.y;
332
}
333
334
ERZKing_HandleFrames();
335
}
336
337
void ERZKing_State_FlyAround(void)
338
{
339
RSDK_THIS(ERZKing);
340
341
RSDK.ProcessAnimation(&self->beltAnimator);
342
343
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 3, 11);
344
345
ERZKing_CheckPlayerCollisions();
346
347
if (self->direction) {
348
if (self->velocity.x > -0x20000)
349
self->velocity.x -= 0x800;
350
351
if (self->position.x < ERZKing->boundsL)
352
self->direction = FLIP_NONE;
353
}
354
else {
355
if (self->velocity.x < 0x20000)
356
self->velocity.x += 0x800;
357
358
if (self->position.x > ERZKing->boundsR)
359
self->direction = FLIP_X;
360
}
361
self->position.x += self->velocity.x;
362
++self->timer;
363
364
if (self->timer > 240) {
365
if (abs(self->position.x - ERZKing->boundsM) < 0x200000) {
366
self->timer = 0;
367
self->scale.x = 0x200;
368
self->scale.y = 0x200;
369
self->storeDrawFX = FX_SCALE;
370
self->state = ERZKing_State_ChangeHBH;
371
372
CREATE_ENTITY(FXRuby, FXRuby_State_ShrinkAndDestroy, self->position.x, self->position.y)->radiusSpeed = 0x80000;
373
}
374
}
375
ERZKing_HandleFrames();
376
}
377
378
void ERZKing_State_ChangeHBH(void)
379
{
380
RSDK_THIS(ERZKing);
381
382
self->typeChangeTimer += 16;
383
384
self->scale.x -= self->scale.x >> 4;
385
self->scale.y = self->scale.x;
386
387
if (self->typeChangeTimer == 0x400) {
388
self->typeChangeTimer = 0;
389
390
foreach_all(ERZKing, king) { king->active = ACTIVE_NEVER; }
391
392
switch (self->nextType) {
393
case ERZKING_HEAVY_GUNNER:
394
CREATE_ENTITY(ERZGunner, NULL, self->position.x, self->position.y);
395
396
self->storeDrawFX = FX_NONE;
397
self->state = ERZKing_State_FlyAround;
398
self->nextType--;
399
self->nextType &= 1;
400
break;
401
402
case ERZKING_HEAVY_MYSTIC:
403
CREATE_ENTITY(ERZMystic, NULL, self->position.x, self->position.y);
404
self->storeDrawFX = FX_NONE;
405
self->state = ERZKing_State_FlyAround;
406
self->nextType--;
407
self->nextType &= 1;
408
break;
409
410
// Shinobi & Rider never got completed... RIP
411
}
412
}
413
}
414
415
void ERZKing_State_Arm(void)
416
{
417
RSDK_THIS(ERZKing);
418
419
EntityERZKing *parent = self->parent;
420
421
int32 moveX = 0;
422
int32 moveY = ((RSDK.Sin256(2 * (Zone->timer + (self->type << 6)) - 128) + 512) << 12) + parent->position.y;
423
424
self->direction = parent->direction;
425
int32 negAngle = -parent->rotation;
426
427
int32 x = 0, y = 0;
428
int32 x2 = 0, y2 = 0;
429
if (parent->direction) {
430
moveX = parent->position.x - 0x300000;
431
x = parent->position.x + 0xD00 * RSDK.Cos512(negAngle) + 0x300 * RSDK.Sin512(negAngle);
432
y = parent->position.y - 0xD00 * RSDK.Sin512(negAngle) + 0x300 * RSDK.Cos512(negAngle);
433
434
if (self->type == ERZKING_ARM_L) {
435
x += -0x1800 * RSDK.Cos512(parent->rotation);
436
y += 0x1800 * RSDK.Sin512(parent->rotation);
437
moveX -= 0x300000;
438
}
439
440
x2 = ((self->position.x + x) >> 1) + 0x200000;
441
y2 = ((self->position.y + y) >> 1) + 0x200000;
442
}
443
else {
444
moveX = parent->position.x + 0x300000;
445
x = 0x300 * RSDK.Sin512(negAngle) - 0xD00 * RSDK.Cos512(negAngle) + parent->position.x;
446
y = 0xD00 * RSDK.Sin512(negAngle) + 0x300 * RSDK.Cos512(negAngle) + parent->position.y;
447
448
if (self->type == ERZKING_ARM_L) {
449
x += 0x1800 * RSDK.Cos512(parent->rotation);
450
y += -0x1800 * RSDK.Sin512(parent->rotation);
451
moveX += 0x300000;
452
}
453
454
x2 = ((self->position.x + x) >> 1) - 0x100000;
455
y2 = ((self->position.y + y) >> 1) + 0x100000;
456
}
457
458
self->velocity.x += ((moveX - self->position.x) >> 5) - (self->velocity.x >> 3);
459
self->velocity.y += ((moveY - self->position.y) >> 5) - (self->velocity.y >> 3);
460
self->position.x += self->velocity.x;
461
self->position.y += self->velocity.y;
462
463
int32 percent = 0x1800;
464
for (int32 i = 0; i < 7; ++i) {
465
self->armPositions[i] = MathHelpers_GetBezierPoint(percent, x, y, x2, y2, x2, y2, self->position.x, self->position.y);
466
percent += 0x2000;
467
}
468
469
RSDK.ProcessAnimation(&self->cuffAnimator);
470
RSDK.ProcessAnimation(&self->handAnimator);
471
}
472
473
void ERZKing_State_Explode(void)
474
{
475
RSDK_THIS(ERZKing);
476
477
self->velocity.y += 0x2800;
478
self->position.y += self->velocity.y;
479
480
ERZKing_Explode();
481
482
if (!RSDK.CheckOnScreen(self, NULL)) {
483
// This boss made it far enough to get the player to the ending... neat!
484
GameProgress_GiveEnding(GAMEPROGRESS_ENDING_GOOD);
485
API_UnlockAchievement(&achievementList[ACH_GAME_CLEARED]);
486
487
// It is interesting that the boss doesn't show ending videos, it just takes you to the credits... perhaps they weren't finished yet?
488
RSDK.SetScene("Presentation", "Credits");
489
Zone_StartFadeOut(10, 0x000000);
490
Music_FadeOut(0.025);
491
492
destroyEntity(self);
493
}
494
}
495
496
#if GAME_INCLUDE_EDITOR
497
void ERZKing_EditorDraw(void)
498
{
499
RSDK_THIS(ERZKing);
500
501
self->originPos = self->position;
502
self->bodyAngle = 0;
503
ERZKing_HandleFrames();
504
505
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 0, &self->headAnimator, true, 0);
506
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 1, &self->bodyAnimator, true, 0);
507
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 2, &self->beltAnimator, true, 0);
508
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 7, &self->particleAnimator, true, 0);
509
RSDK.SetSpriteAnimation(ERZKing->aniFrames, 8, &self->rubyAnimator, true, 0);
510
511
ERZKing_Draw_Body();
512
513
if (showGizmos()) {
514
RSDK_DRAWING_OVERLAY(true);
515
516
DrawHelpers_DrawArenaBounds(-320, -SCREEN_YSIZE, 320, 0, 1 | 2 | 4 | 0, 0x00C0F0);
517
518
RSDK_DRAWING_OVERLAY(false);
519
}
520
}
521
522
void ERZKing_EditorLoad(void)
523
{
524
ERZKing->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomKing.bin", SCOPE_STAGE);
525
526
RSDK_ACTIVE_VAR(ERZKing, type);
527
RSDK_ENUM_VAR("King", ERZKING_KING);
528
}
529
#endif
530
531
void ERZKing_Serialize(void) { RSDK_EDITABLE_VAR(ERZKing, VAR_ENUM, type); }
532
533