Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/OOZ/MegaOctus.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: MegaOctus Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectMegaOctus *MegaOctus;
11
12
void MegaOctus_Update(void)
13
{
14
RSDK_THIS(MegaOctus);
15
16
StateMachine_Run(self->state);
17
}
18
19
void MegaOctus_LateUpdate(void) {}
20
21
void MegaOctus_StaticUpdate(void)
22
{
23
foreach_active(MegaOctus, boss)
24
{
25
if (boss->type == MEGAOCTUS_ARM)
26
RSDK.AddDrawListRef(Zone->objectDrawGroup[0], RSDK.GetEntitySlot(boss));
27
}
28
}
29
30
void MegaOctus_Draw(void)
31
{
32
RSDK_THIS(MegaOctus);
33
34
StateMachine_Run(self->stateDraw);
35
}
36
37
void MegaOctus_Create(void *data)
38
{
39
RSDK_THIS(MegaOctus);
40
41
self->drawFX = FX_FLIP;
42
if (!SceneInfo->inEditor) {
43
if (globals->gameMode < MODE_TIMEATTACK) {
44
self->active = ACTIVE_BOUNDS;
45
if (data)
46
self->type = VOID_TO_INT(data);
47
48
switch (self->type) {
49
case MEGAOCTUS_BODY:
50
self->visible = false;
51
self->drawGroup = Zone->objectDrawGroup[0];
52
53
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 0, &self->animator, true, 0);
54
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 1, &MegaOctus->eggmanAnimator, true, 0);
55
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 1, &MegaOctus->noseAnimator, true, 5);
56
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 2, &MegaOctus->boltsAnimator, true, 5);
57
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 0, &MegaOctus->hatchBaseAnimator, true, 0);
58
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 1, &MegaOctus->hatchOpenAnimator, true, 0);
59
60
self->hitbox.left = -33;
61
self->hitbox.top = -37;
62
self->hitbox.right = 33;
63
self->hitbox.bottom = 6;
64
65
self->origin.x = self->position.x + 0x800000;
66
self->origin.y = self->position.y;
67
68
self->updateRange.x = 0x800000;
69
self->updateRange.y = 0x800000;
70
MegaOctus->bossEntity = self;
71
MegaOctus->turnPos = 0;
72
self->state = MegaOctus_State_SetupBounds;
73
self->stateDraw = MegaOctus_Draw_Body;
74
break;
75
76
case MEGAOCTUS_UNUSED1: break;
77
78
case MEGAOCTUS_HARPOON:
79
self->active = ACTIVE_NORMAL;
80
self->visible = true;
81
self->drawGroup = Zone->objectDrawGroup[0];
82
83
self->hitbox.left = -8;
84
self->hitbox.top = -8;
85
self->hitbox.right = 8;
86
self->hitbox.bottom = 8;
87
88
self->origin = self->position;
89
self->updateRange.x = 0x800000;
90
self->updateRange.y = 0x800000;
91
self->angle = 0;
92
self->rotation = 0;
93
94
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 3, &self->animator, true, 1);
95
break;
96
97
case MEGAOCTUS_CANNON:
98
self->active = ACTIVE_NORMAL;
99
self->visible = true;
100
self->updateRange.x = 0x800000;
101
self->updateRange.y = 0x800000;
102
self->drawGroup = Zone->objectDrawGroup[0] + 1;
103
104
self->hitbox.left = -8;
105
self->hitbox.top = -8;
106
self->hitbox.right = 8;
107
self->hitbox.bottom = 8;
108
109
self->targetPos = self->position.y - 0x700000;
110
self->origin = self->position;
111
self->lastAttackHeight = 0x100;
112
self->shotCount = 3;
113
114
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 3, &self->animator, true, 1);
115
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 4, &self->altAnimator, true, 3);
116
117
self->velocity.y = -0x10000;
118
self->state = MegaOctus_StateCannon_RiseUp;
119
self->stateDraw = MegaOctus_Draw_Cannon;
120
break;
121
122
case MEGAOCTUS_ORB:
123
self->active = ACTIVE_NORMAL;
124
self->visible = true;
125
self->updateRange.x = 0x800000;
126
self->updateRange.y = 0x800000;
127
self->drawGroup = Zone->objectDrawGroup[0] + 1;
128
129
self->hitbox.left = -16;
130
self->hitbox.top = -16;
131
self->hitbox.right = 16;
132
self->hitbox.bottom = 16;
133
134
self->targetPos = self->position.y - 0x1000000;
135
self->origin = self->position;
136
137
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 3, &self->animator, true, 1);
138
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 5, &self->altAnimator, true, 0);
139
140
self->velocity.y = -0x18000;
141
self->state = MegaOctus_StateOrb_Wait;
142
self->stateDraw = MegaOctus_Draw_Orb;
143
break;
144
145
case MEGAOCTUS_ARM:
146
self->active = ACTIVE_NORMAL;
147
self->visible = true;
148
self->updateRange.x = 0x800000;
149
self->drawGroup = Zone->objectDrawGroup[0] + 1;
150
151
self->hitbox.left = -8;
152
self->hitbox.top = -8;
153
self->hitbox.right = 8;
154
self->hitbox.bottom = 8;
155
156
self->origin = self->position;
157
self->updateRange.y = 0x800000;
158
self->angle = 0;
159
self->rotation = 0;
160
161
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 3, &self->animator, true, 1);
162
163
self->state = MegaOctus_StateArm_WrapAroundPlatform;
164
self->stateDraw = MegaOctus_Draw_Arm_WrapAroundPlatformBase;
165
break;
166
167
case MEGAOCTUS_LASER:
168
self->active = ACTIVE_NORMAL;
169
self->visible = true;
170
self->drawGroup = Zone->objectDrawGroup[0];
171
172
self->hitbox.left = -16;
173
self->hitbox.top = -1;
174
self->hitbox.right = 16;
175
self->hitbox.bottom = 1;
176
177
self->updateRange.x = 0x800000;
178
self->updateRange.y = 0x800000;
179
180
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 7, &self->animator, true, 0);
181
182
self->state = MegaOctus_State_Laser;
183
self->stateDraw = MegaOctus_Draw_Laser;
184
break;
185
186
case MEGAOCTUS_ORBSHOT:
187
self->active = ACTIVE_NORMAL;
188
self->visible = true;
189
self->drawGroup = Zone->objectDrawGroup[0];
190
191
self->hitbox.left = -3;
192
self->hitbox.top = -3;
193
self->hitbox.right = 3;
194
self->hitbox.bottom = 3;
195
196
self->updateRange.x = 0x400000;
197
self->updateRange.y = 0x400000;
198
199
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 6, &self->animator, true, 0);
200
201
self->state = MegaOctus_State_Shot;
202
self->stateDraw = MegaOctus_Draw_OrbShot;
203
break;
204
205
case MEGAOCTUS_UNUSED8: break;
206
207
case MEGAOCTUS_LASERFIRE:
208
self->active = ACTIVE_NORMAL;
209
self->visible = true;
210
self->drawGroup = Zone->objectDrawGroup[0];
211
212
self->hitbox.left = -8;
213
self->hitbox.top = -4;
214
self->hitbox.right = 8;
215
self->hitbox.bottom = 0;
216
217
self->updateRange.x = 0x800000;
218
self->updateRange.y = 0x800000;
219
220
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 8, &self->animator, true, 0);
221
222
self->state = MegaOctus_State_LaserFire;
223
self->stateDraw = MegaOctus_Draw_Laser;
224
break;
225
226
default: break;
227
}
228
}
229
else {
230
MegaOctus->bossEntity = NULL;
231
destroyEntity(self);
232
}
233
}
234
}
235
236
void MegaOctus_StageLoad(void)
237
{
238
MegaOctus->active = ACTIVE_ALWAYS;
239
MegaOctus->bossEntity = NULL;
240
241
MegaOctus->aniFrames = RSDK.LoadSpriteAnimation("OOZ/MegaOctus.bin", SCOPE_STAGE);
242
MegaOctus->eggmanFrames = RSDK.LoadSpriteAnimation("Eggman/EggmanOOZ.bin", SCOPE_STAGE);
243
MegaOctus->hatchFrames = RSDK.LoadSpriteAnimation("OOZ/Hatch.bin", SCOPE_STAGE);
244
245
MegaOctus->hitbox.left = -16;
246
MegaOctus->hitbox.top = 22;
247
MegaOctus->hitbox.right = 16;
248
MegaOctus->hitbox.bottom = 30;
249
250
MegaOctus->spawnHarpoon = false;
251
MegaOctus->orbHealth[0] = 4;
252
MegaOctus->orbHealth[1] = 4;
253
MegaOctus->defeated = false;
254
255
MegaOctus->sfxBossHit = RSDK.GetSfx("Stage/BossHit.wav");
256
MegaOctus->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
257
MegaOctus->sfxLaser = RSDK.GetSfx("OOZ/OOZLaser.wav");
258
MegaOctus->sfxBullet = RSDK.GetSfx("OOZ/OOZBullet.wav");
259
MegaOctus->sfxHarpoon = RSDK.GetSfx("OOZ/Harpoon.wav");
260
MegaOctus->sfxSurface = RSDK.GetSfx("OOZ/OOZSurface.wav");
261
MegaOctus->sfxLaserSplash = RSDK.GetSfx("OOZ/LaserSplash.wav");
262
}
263
264
void MegaOctus_CheckPlayerCollisions_Body(void)
265
{
266
RSDK_THIS(MegaOctus);
267
268
if (self->invincibilityTimer)
269
self->invincibilityTimer--;
270
271
foreach_active(Player, player)
272
{
273
int32 playerRadius = 0x100000;
274
275
EntityShield *shield = RSDK_GET_ENTITY(Player->playerCount + RSDK.GetEntitySlot(player), Shield);
276
if (shield->classID == Shield->classID && shield->state == Shield_State_Insta)
277
playerRadius = 0x160000;
278
279
if (RSDK.CheckObjectCollisionTouchCircle(self, 0x300000, player, playerRadius)) {
280
int32 angle = RSDK.ATan2(player->position.x - self->position.x, player->position.y - self->position.y);
281
282
player->velocity.x += 80 * RSDK.Cos256(angle);
283
if (self->invincibilityTimer || !Player_CheckBossHit(player, self)) {
284
player->velocity.y -= 80 * abs(RSDK.Sin256(angle));
285
}
286
else {
287
MegaOctus_Hit();
288
}
289
}
290
}
291
}
292
293
void MegaOctus_HandleEggmanAnim(void)
294
{
295
RSDK_THIS(MegaOctus);
296
297
switch (MegaOctus->eggmanAnimator.animationID) {
298
case 0:
299
case 1:
300
if (MegaOctus->eggmanAnimator.frameID >= MegaOctus->eggmanAnimator.frameCount - 1) {
301
if (self->invincibilityTimer) {
302
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 3, &MegaOctus->eggmanAnimator, true, 0);
303
}
304
else {
305
bool32 laughing = false;
306
foreach_active(Player, player)
307
{
308
if (player->state == Player_State_Hurt || player->state == Player_State_Death)
309
laughing = true;
310
}
311
312
if (laughing)
313
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 4, &MegaOctus->eggmanAnimator, true, 0);
314
}
315
}
316
break;
317
318
case 2:
319
case 4:
320
if (self->invincibilityTimer) {
321
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 3, &MegaOctus->eggmanAnimator, true, 0);
322
}
323
else {
324
bool32 laughing = false;
325
foreach_active(Player, player)
326
{
327
if (player->state == Player_State_Hurt || player->state == Player_State_Death)
328
laughing = true;
329
}
330
331
if (MegaOctus->eggmanAnimator.frameID >= MegaOctus->eggmanAnimator.frameCount - 1) {
332
if (!laughing) {
333
uint8 anim = self->state == MegaOctus_State_CloseHatch ? 2 : 0;
334
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, anim, &MegaOctus->eggmanAnimator, true, 0);
335
}
336
else {
337
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 4, &MegaOctus->eggmanAnimator, true, 8);
338
}
339
}
340
}
341
break;
342
343
case 3:
344
if (!self->invincibilityTimer) {
345
if (MegaOctus->eggmanAnimator.frameID >= MegaOctus->eggmanAnimator.frameCount - 1) {
346
uint8 anim = self->state == MegaOctus_State_CloseHatch ? 2 : 0;
347
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, anim, &MegaOctus->eggmanAnimator, true, 0);
348
}
349
}
350
break;
351
352
default: break;
353
}
354
}
355
356
void MegaOctus_Hit(void)
357
{
358
RSDK_THIS(MegaOctus);
359
360
if (self->health)
361
self->health--;
362
363
if (self->health) {
364
RSDK.PlaySfx(MegaOctus->sfxBossHit, false, 255);
365
self->invincibilityTimer = 30;
366
MegaOctus->spawnHarpoon = true;
367
}
368
else {
369
SceneInfo->timeEnabled = false;
370
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
371
RSDK.PlaySfx(MegaOctus->sfxExplosion, false, 255);
372
self->invincibilityTimer = 120;
373
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 5, &MegaOctus->eggmanAnimator, true, 0);
374
MegaOctus->defeated = true;
375
self->state = MegaOctus_State_Destroyed;
376
}
377
}
378
379
void MegaOctus_Explode(void)
380
{
381
RSDK_THIS(MegaOctus);
382
383
if (!(Zone->timer % 3)) {
384
RSDK.PlaySfx(MegaOctus->sfxExplosion, false, 255);
385
386
if (Zone->timer & 4) {
387
int32 x = self->position.x + (RSDK.Rand(-48, 48) << 16);
388
int32 y = self->position.y + (RSDK.Rand(-48, 48) << 16);
389
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);
390
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
391
}
392
}
393
}
394
395
void MegaOctus_HandleDirectionChange(void)
396
{
397
RSDK_THIS(MegaOctus);
398
399
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
400
401
if (abs(player1->position.x - self->position.x) < 0x200000) {
402
if (self->orbID > 10)
403
self->orbID--;
404
else if (self->orbID < 10)
405
self->orbID++;
406
}
407
else {
408
if (player1->position.x < self->position.x) {
409
if (self->orbID)
410
self->orbID--;
411
}
412
else {
413
if (self->orbID < 20)
414
self->orbID++;
415
}
416
}
417
418
self->direction = self->orbID < 10;
419
MegaOctus->noseAnimator.frameID = self->orbID >> 1;
420
MegaOctus->boltsAnimator.frameID = self->orbID >> 1;
421
MegaOctus->turnPos = MegaOctus->turnOffsets[MegaOctus->noseAnimator.frameID];
422
}
423
424
void MegaOctus_State_SetupBounds(void)
425
{
426
RSDK_THIS(MegaOctus);
427
428
if (++self->timer >= 2) {
429
self->timer = 0;
430
431
Zone->playerBoundActiveR[0] = true;
432
Zone->playerBoundActiveB[0] = false;
433
Zone->cameraBoundsR[0] = (self->position.x >> 16) + 448;
434
Zone->cameraBoundsB[0] = (self->position.y >> 16) + 96;
435
Zone->deathBoundary[0] = Zone->cameraBoundsB[0] << 16;
436
#if MANIA_USE_PLUS
437
Zone->cameraBoundsT[0] = Zone->cameraBoundsB[0] - 384;
438
#endif
439
440
self->position.y += 0xC00000;
441
self->active = ACTIVE_NORMAL;
442
self->visible = true;
443
self->state = MegaOctus_State_SetupArena;
444
}
445
}
446
447
void MegaOctus_State_SetupArena(void)
448
{
449
RSDK_THIS(MegaOctus);
450
451
Zone->playerBoundActiveL[0] = true;
452
Zone->cameraBoundsL[0] = ScreenInfo->position.x;
453
454
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->origin.x) {
455
RSDK.GetTileLayer(Zone->fgLayer[0])->drawGroup[0] = 2;
456
Zone->playerBoundActiveL[0] = true;
457
Zone->cameraBoundsL[0] = (self->position.x >> 16) - 192;
458
459
Music_TransitionTrack(TRACK_EGGMAN1, 0.0075);
460
461
OOZSetup->useSmogEffect = false;
462
self->velocity.y = -0x40000;
463
self->health = 8;
464
self->timer = 60;
465
466
EntityMegaOctus *arm = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ARM), self->position.x + 0x800000, self->origin.y + 0x400000);
467
arm->direction = self->direction;
468
arm->angle = 128;
469
470
arm = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ARM), self->position.x - 0x800000, self->origin.y + 0x400000);
471
arm->direction = self->direction;
472
arm->angle = 128;
473
474
self->state = MegaOctus_State_None;
475
}
476
}
477
478
void MegaOctus_State_None(void)
479
{
480
// :3
481
}
482
483
void MegaOctus_State_EnterMegaOctus(void)
484
{
485
RSDK_THIS(MegaOctus);
486
487
self->position.y += self->velocity.y;
488
self->velocity.y += MANIA_USE_PLUS ? 0x3800 : 0x6000;
489
490
if (self->velocity.y >= 0x10000) {
491
self->state = MegaOctus_State_OpenHatchAndLaugh;
492
self->velocity.y = 0;
493
}
494
495
MegaOctus_CheckPlayerCollisions_Body();
496
}
497
498
void MegaOctus_State_OpenHatchAndLaugh(void)
499
{
500
RSDK_THIS(MegaOctus);
501
502
RSDK.ProcessAnimation(&MegaOctus->hatchOpenAnimator);
503
RSDK.ProcessAnimation(&MegaOctus->eggmanAnimator);
504
505
MegaOctus_HandleEggmanAnim();
506
MegaOctus_HandleDirectionChange();
507
MegaOctus_CheckPlayerCollisions_Body();
508
509
if (--self->timer <= 0) {
510
self->timer = 60;
511
512
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 2, &MegaOctus->hatchOpenAnimator, true, 0);
513
if (MegaOctus->eggmanAnimator.animationID < 2)
514
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 2, &MegaOctus->eggmanAnimator, true, 0);
515
516
self->state = MegaOctus_State_CloseHatch;
517
}
518
}
519
520
void MegaOctus_State_CloseHatch(void)
521
{
522
RSDK_THIS(MegaOctus);
523
524
RSDK.ProcessAnimation(&MegaOctus->hatchOpenAnimator);
525
RSDK.ProcessAnimation(&MegaOctus->eggmanAnimator);
526
527
MegaOctus_HandleEggmanAnim();
528
529
MegaOctus->eggmanVelocity += 0x4800;
530
MegaOctus->eggmanOffset += MegaOctus->eggmanVelocity;
531
532
MegaOctus_HandleDirectionChange();
533
MegaOctus_CheckPlayerCollisions_Body();
534
535
if (--self->timer <= 0)
536
self->state = MegaOctus_State_DiveIntoOil;
537
}
538
539
void MegaOctus_State_DiveIntoOil(void)
540
{
541
RSDK_THIS(MegaOctus);
542
543
self->position.y += self->velocity.y;
544
self->velocity.y += MANIA_USE_PLUS ? 0x3800 : 0x6000;
545
546
if (self->position.y >= self->origin.y + 0xC00000) {
547
self->position.y = self->origin.y + 0xC00000;
548
549
self->state = MegaOctus_State_SpawnWeapons;
550
self->timer = MegaOctus->spawnHarpoon ? 480 : 240;
551
}
552
553
MegaOctus_CheckPlayerCollisions_Body();
554
}
555
556
void MegaOctus_State_SpawnWeapons(void)
557
{
558
RSDK_THIS(MegaOctus);
559
560
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x <= self->origin.x)
561
self->position.x = self->origin.x - 0x800000;
562
else
563
self->position.x = self->origin.x + 0x800000;
564
565
if (--self->timer == 240) {
566
MegaOctus->spawnHarpoon = false;
567
EntityMegaOctus *harpoon = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_HARPOON), self->position.x, self->origin.y + 0x300000);
568
harpoon->direction = self->direction;
569
RSDK.PlaySfx(MegaOctus->sfxHarpoon, false, 255);
570
571
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x <= self->position.x) {
572
harpoon->origin.x -= 0x800000;
573
harpoon->state = MegaOctus_State_HarpoonLeft;
574
harpoon->stateDraw = MegaOctus_Draw_HarpoonLeft;
575
}
576
else {
577
harpoon->origin.x += 0x800000;
578
harpoon->state = MegaOctus_State_HarpoonRight;
579
harpoon->stateDraw = MegaOctus_Draw_HarpoonRight;
580
}
581
}
582
else if (self->timer <= 0) {
583
self->timer = 480;
584
CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_CANNON), self->position.x, self->origin.y + 0x400000)->direction = self->direction;
585
self->state = MegaOctus_State_CannonThenSpawnOrbs;
586
}
587
588
MegaOctus_CheckPlayerCollisions_Body();
589
}
590
591
void MegaOctus_State_CannonThenSpawnOrbs(void)
592
{
593
RSDK_THIS(MegaOctus);
594
595
MegaOctus_HandleDirectionChange();
596
597
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
598
if (player1->position.x <= self->origin.x)
599
self->position.x = self->origin.x - 0x800000;
600
else
601
self->position.x = self->origin.x + 0x800000;
602
603
MegaOctus_CheckPlayerCollisions_Body();
604
605
if (--self->timer <= 0) {
606
EntityMegaOctus *arm = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ARM), self->position.x + 0x800000, self->origin.y + 0x400000);
607
arm->direction = self->direction;
608
arm->angle = 128;
609
610
arm = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ARM), self->position.x - 0x800000, self->origin.y + 0x400000);
611
arm->direction = self->direction;
612
arm->angle = 128;
613
614
if (MegaOctus->orbHealth[0] > 0) {
615
EntityMegaOctus *orb = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ORB), self->position.x + 0x380000, self->origin.y + 0x780000);
616
orb->direction = self->direction;
617
orb->health = MegaOctus->orbHealth[0];
618
orb->orbID = 0;
619
orb->velocity.x = self->position.x > self->origin.x ? 0 : 136;
620
}
621
622
if (MegaOctus->orbHealth[1] > 0) {
623
EntityMegaOctus *orb = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ORB), self->position.x - 0x380000, self->origin.y + 0x780000);
624
orb->direction = self->direction;
625
orb->health = MegaOctus->orbHealth[1];
626
orb->orbID = 1;
627
orb->angle = 0x100;
628
orb->velocity.x = self->position.x > self->origin.x ? -136 : 0;
629
}
630
631
self->timer = 120;
632
self->state = MegaOctus_State_None;
633
}
634
}
635
636
void MegaOctus_State_Destroyed(void)
637
{
638
RSDK_THIS(MegaOctus);
639
640
RSDK.ProcessAnimation(&MegaOctus->hatchOpenAnimator);
641
RSDK.ProcessAnimation(&MegaOctus->eggmanAnimator);
642
643
if (MegaOctus->eggmanVelocity > 0) {
644
MegaOctus->eggmanVelocity += 0x4800;
645
MegaOctus->eggmanOffset += MegaOctus->eggmanVelocity;
646
}
647
648
MegaOctus_Explode();
649
650
if (--self->invincibilityTimer <= 0) {
651
if (!MegaOctus->eggmanVelocity)
652
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 2, &MegaOctus->hatchOpenAnimator, true, 0);
653
654
Music_TransitionTrack(TRACK_STAGE, 0.0125);
655
self->timer = 0;
656
self->state = MegaOctus_State_Finish;
657
}
658
}
659
660
void MegaOctus_State_Finish(void)
661
{
662
RSDK_THIS(MegaOctus);
663
664
RSDK.ProcessAnimation(&MegaOctus->hatchOpenAnimator);
665
RSDK.ProcessAnimation(&MegaOctus->eggmanAnimator);
666
667
MegaOctus->eggmanVelocity += 0x4800;
668
MegaOctus->eggmanOffset += MegaOctus->eggmanVelocity;
669
670
self->position.y += 0x10000;
671
672
if (!(Zone->timer & 7)) {
673
int32 x = self->position.x + (RSDK.Rand(-48, 48) << 16);
674
int32 y = self->position.y + (RSDK.Rand(-48, 48) << 16);
675
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSSPUFF), x, y);
676
explosion->drawGroup = self->drawGroup;
677
}
678
679
if (++self->timer > 120) {
680
bool32 isFinished = true;
681
foreach_active(MegaOctus, boss)
682
{
683
if (boss->type == MEGAOCTUS_ARM) {
684
isFinished = false;
685
foreach_break;
686
}
687
}
688
689
if (isFinished) {
690
Zone->cameraBoundsR[0] += WIDE_SCR_XSIZE;
691
destroyEntity(self);
692
}
693
}
694
}
695
696
void MegaOctus_Draw_Body(void)
697
{
698
RSDK_THIS(MegaOctus);
699
700
int32 turnPos = abs(MegaOctus->turnPos) / 96;
701
if (self->invincibilityTimer & 1)
702
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
703
704
Vector2 drawPos = self->position;
705
drawPos.y -= 0x320000;
706
RSDK.DrawSprite(&MegaOctus->hatchOpenAnimator, &drawPos, false);
707
708
Vector2 eggmanPos = drawPos;
709
eggmanPos.y += MegaOctus->eggmanOffset;
710
RSDK.DrawSprite(&MegaOctus->eggmanAnimator, &eggmanPos, false);
711
712
self->animator.frameID = 0;
713
RSDK.DrawSprite(&self->animator, NULL, false);
714
RSDK.DrawSprite(&MegaOctus->hatchBaseAnimator, &drawPos, false);
715
716
if (MegaOctus->turnPos < 0) {
717
self->drawFX |= FX_SCALE;
718
self->scale.y = 0x200;
719
self->scale.x = 0x200 - (turnPos << 9 >> 15);
720
}
721
drawPos = self->position;
722
drawPos.x += turnPos - 0x10000 + MegaOctus->turnPos + 16 * (turnPos - 0x10000);
723
self->animator.frameID = 1;
724
RSDK.DrawSprite(&self->animator, &drawPos, false);
725
726
drawPos.x += turnPos * (MegaOctus->turnPos >> 18);
727
self->animator.frameID = 2;
728
RSDK.DrawSprite(&self->animator, &drawPos, false);
729
730
drawPos.x -= turnPos * (MegaOctus->turnPos >> 18);
731
self->drawFX &= ~FX_SCALE;
732
if (MegaOctus->turnPos > 0) {
733
self->drawFX |= FX_SCALE;
734
self->scale.y = 0x200;
735
self->scale.x = 0x200 - (turnPos << 9 >> 15);
736
}
737
738
drawPos.x += 34 * (0x10000 - turnPos);
739
self->animator.frameID = 1;
740
RSDK.DrawSprite(&self->animator, &drawPos, false);
741
742
drawPos.x += turnPos * (MegaOctus->turnPos >> 18);
743
self->animator.frameID = 2;
744
RSDK.DrawSprite(&self->animator, &drawPos, false);
745
746
drawPos.x -= turnPos * (MegaOctus->turnPos >> 18);
747
self->drawFX = FX_FLIP;
748
RSDK.DrawSprite(&MegaOctus->boltsAnimator, NULL, false);
749
RSDK.DrawSprite(&MegaOctus->noseAnimator, NULL, false);
750
751
self->animator.frameID = 6;
752
RSDK.DrawSprite(&self->animator, NULL, false);
753
RSDK.SetPaletteEntry(0, 128, 0x0000);
754
}
755
756
void MegaOctus_CheckPlayerCollisions_Harpoon(void)
757
{
758
RSDK_THIS(MegaOctus);
759
760
self->position.x = 0x3400 * RSDK.Sin512(self->angle) + self->origin.x;
761
self->position.y = 0x3400 * RSDK.Cos512(self->angle) + self->origin.y;
762
763
foreach_active(Player, player)
764
{
765
if (Player_CheckCollisionTouch(player, self, &self->hitbox)) {
766
#if MANIA_USE_PLUS
767
if (!Player_CheckMightyUnspin(player, 0x300, 2, &player->uncurlTimer))
768
#endif
769
Player_Hurt(player, self);
770
}
771
}
772
}
773
774
void MegaOctus_State_HarpoonLeft(void)
775
{
776
RSDK_THIS(MegaOctus);
777
778
self->angle = (self->angle + 3) & 0x1FF;
779
self->shotCount += 12;
780
781
MegaOctus_CheckPlayerCollisions_Harpoon();
782
783
if (self->angle == 1)
784
destroyEntity(self);
785
}
786
787
void MegaOctus_State_HarpoonRight(void)
788
{
789
RSDK_THIS(MegaOctus);
790
791
self->angle = (self->angle - 3) & 0x1FF;
792
self->shotCount -= 12;
793
794
MegaOctus_CheckPlayerCollisions_Harpoon();
795
796
if (self->angle == 2)
797
destroyEntity(self);
798
}
799
800
void MegaOctus_Draw_HarpoonLeft(void)
801
{
802
RSDK_THIS(MegaOctus);
803
804
self->animator.frameID = 1;
805
int32 angle = (self->angle - 108) & 0x1FF;
806
for (int32 i = 0; i < 9; ++i) {
807
self->position.x = 0x3400 * RSDK.Sin512(angle) + self->origin.x;
808
self->position.y = 0x3400 * RSDK.Cos512(angle) + self->origin.y;
809
RSDK.DrawSprite(&self->animator, NULL, false);
810
angle = (angle + 12) & 0x1FF;
811
}
812
813
self->drawFX |= FX_ROTATE;
814
self->direction = FLIP_NONE;
815
self->rotation = (-0x100 - self->angle) & 0x1FF;
816
self->animator.frameID = 0;
817
RSDK.DrawSprite(&self->animator, NULL, false);
818
819
self->drawFX &= ~FX_ROTATE;
820
}
821
822
void MegaOctus_Draw_HarpoonRight(void)
823
{
824
RSDK_THIS(MegaOctus);
825
826
self->animator.frameID = 1;
827
int32 angle = (self->angle + 108) & 0x1FF;
828
for (int32 i = 0; i < 9; ++i) {
829
self->position.x = 0x3400 * RSDK.Sin512(angle) + self->origin.x;
830
self->position.y = 0x3400 * RSDK.Cos512(angle) + self->origin.y;
831
RSDK.DrawSprite(&self->animator, NULL, false);
832
angle = (angle - 12) & 0x1FF;
833
}
834
835
self->drawFX |= FX_ROTATE;
836
self->direction = FLIP_X;
837
self->rotation = (-0x100 - self->angle) & 0x1FF;
838
self->animator.frameID = 0;
839
RSDK.DrawSprite(&self->animator, NULL, false);
840
841
self->drawFX &= ~FX_ROTATE;
842
}
843
844
void MegaOctus_CheckPlayerCollisions_Cannon(void)
845
{
846
RSDK_THIS(MegaOctus);
847
848
Vector2 storePos = self->position;
849
850
foreach_active(Player, player)
851
{
852
self->position.x = (RSDK.Cos512(self->angle) << 10) + storePos.x;
853
self->position.y = (RSDK.Sin512(self->angle) << 8) + storePos.y;
854
855
if (Player_CheckCollisionTouch(player, self, &self->hitbox)) {
856
self->position = storePos;
857
858
#if MANIA_USE_PLUS
859
if (!Player_CheckMightyUnspin(player, 0x300, 2, &player->uncurlTimer))
860
#endif
861
Player_Hurt(player, self);
862
}
863
}
864
865
self->position = storePos;
866
}
867
868
void MegaOctus_StateCannon_RiseUp(void)
869
{
870
RSDK_THIS(MegaOctus);
871
872
self->position.y += self->velocity.y;
873
self->angle = (self->angle + 6) & 0x1FF;
874
875
RSDK.ProcessAnimation(&self->altAnimator);
876
877
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
878
if (self->position.y <= self->targetPos) {
879
self->shotCount = MANIA_USE_PLUS ? 2 : 3;
880
self->timer = 128;
881
self->lastAttackHeight = 0x100;
882
self->state = MegaOctus_StateCannon_Idle;
883
}
884
885
MegaOctus_CheckPlayerCollisions_Cannon();
886
}
887
888
void MegaOctus_StateCannon_Idle(void)
889
{
890
RSDK_THIS(MegaOctus);
891
892
self->angle = (self->angle + 6) & 0x1FF;
893
RSDK.ProcessAnimation(&self->altAnimator);
894
895
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
896
897
if (--self->timer <= 0) {
898
if (self->shotCount <= 0) {
899
self->velocity.y = 0x10000;
900
self->state = MegaOctus_StateCannon_SinkDown;
901
}
902
else {
903
int32 attackHeight = self->lastAttackHeight;
904
while (attackHeight == self->lastAttackHeight) attackHeight = RSDK.Rand(0, 4);
905
906
self->lastAttackHeight = attackHeight;
907
self->targetPos = MegaOctus->cannonHeights[attackHeight] + self->origin.y;
908
self->state = MegaOctus_StateCannon_FireLaser;
909
self->velocity.y = self->targetPos < self->position.y ? -0x8000 : 0x8000;
910
}
911
}
912
913
MegaOctus_CheckPlayerCollisions_Cannon();
914
}
915
916
void MegaOctus_StateCannon_FireLaser(void)
917
{
918
RSDK_THIS(MegaOctus);
919
920
self->position.y += self->velocity.y;
921
self->angle = (self->angle + 6) & 0x1FF;
922
923
RSDK.ProcessAnimation(&self->altAnimator);
924
925
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
926
927
if ((self->velocity.y < 0 && self->position.y <= self->targetPos) || (self->velocity.y >= 0 && self->position.y >= self->targetPos)) {
928
self->position.y = self->targetPos;
929
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 4, &self->altAnimator, true, 0);
930
RSDK.PlaySfx(MegaOctus->sfxLaser, false, 255);
931
932
--self->shotCount;
933
self->timer = 40;
934
int32 x = (RSDK.Cos512(self->angle) << 10) + self->position.x;
935
int32 y = (RSDK.Sin512(self->angle) << 9) + self->position.y;
936
EntityMegaOctus *child = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_LASER), x, y);
937
child->direction = self->direction;
938
child->velocity.x = self->direction ? 0x40000 : -0x40000;
939
child->position.x += child->velocity.x;
940
child->parent = (Entity *)self;
941
child->timer = 11;
942
self->state = MegaOctus_StateCannon_Idle;
943
}
944
MegaOctus_CheckPlayerCollisions_Cannon();
945
}
946
947
void MegaOctus_StateCannon_SinkDown(void)
948
{
949
RSDK_THIS(MegaOctus);
950
951
self->angle = (self->angle + 6) & 0x1FF;
952
RSDK.ProcessAnimation(&self->altAnimator);
953
954
self->direction = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x >= self->position.x;
955
956
self->position.y += self->velocity.y;
957
958
MegaOctus_CheckPlayerCollisions_Cannon();
959
960
if (self->position.y > self->origin.y)
961
destroyEntity(self);
962
}
963
void MegaOctus_Draw_Cannon(void)
964
{
965
RSDK_THIS(MegaOctus);
966
967
int32 angle = self->angle;
968
int32 y = self->position.y + 0x780000;
969
970
Vector2 drawPos;
971
for (int32 i = 0; i < 8; ++i) {
972
drawPos.x = (RSDK.Cos512(angle) << 10) + self->position.x;
973
drawPos.y = (RSDK.Sin512(angle) << 8) + y;
974
RSDK.DrawSprite(&self->animator, &drawPos, false);
975
976
angle = (angle + 64) & 0x1FF;
977
y -= 0xF0000;
978
}
979
980
drawPos.x = (RSDK.Cos512(angle) << 10) + self->position.x;
981
drawPos.y = (RSDK.Sin512(angle) << 8) + y;
982
RSDK.DrawSprite(&self->altAnimator, &drawPos, false);
983
}
984
985
void MegaOctus_CheckPlayerCollisions_Orb(void)
986
{
987
RSDK_THIS(MegaOctus);
988
989
Vector2 storePos = self->position;
990
991
if (self->invincibilityTimer) {
992
self->invincibilityTimer--;
993
}
994
else {
995
foreach_active(Player, player)
996
{
997
self->position.x = (RSDK.Cos512(self->angle) << 10) + storePos.x;
998
self->position.y = (RSDK.Sin512(self->angle) << 8) + storePos.y;
999
if (Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self)) {
1000
self->position = storePos;
1001
1002
--self->health;
1003
--MegaOctus->orbHealth[self->orbID];
1004
1005
if (self->health) {
1006
RSDK.PlaySfx(MegaOctus->sfxBossHit, false, 255);
1007
self->invincibilityTimer = 30;
1008
}
1009
else {
1010
RSDK.PlaySfx(MegaOctus->sfxExplosion, false, 255);
1011
self->invincibilityTimer = 60;
1012
self->state = MegaOctus_StateOrb_Destroyed;
1013
}
1014
}
1015
}
1016
}
1017
1018
self->position = storePos;
1019
}
1020
1021
void MegaOctus_StateOrb_Wait(void)
1022
{
1023
RSDK_THIS(MegaOctus);
1024
1025
self->position.y += self->velocity.y;
1026
1027
self->angle = (self->angle + 6) & 0x1FF;
1028
RSDK.ProcessAnimation(&self->altAnimator);
1029
1030
if (MegaOctus->defeated) {
1031
self->invincibilityTimer = 60;
1032
self->state = MegaOctus_StateOrb_Destroyed;
1033
}
1034
1035
if (self->position.y <= self->targetPos) {
1036
self->timer = 60;
1037
self->state = MegaOctus_StateOrb_FireShot;
1038
}
1039
1040
MegaOctus_CheckPlayerCollisions_Orb();
1041
}
1042
1043
void MegaOctus_StateOrb_FireShot(void)
1044
{
1045
RSDK_THIS(MegaOctus);
1046
1047
self->angle = (self->angle + 6) & 0x1FF;
1048
RSDK.ProcessAnimation(&self->altAnimator);
1049
1050
if (MegaOctus->defeated) {
1051
self->invincibilityTimer = 60;
1052
self->state = MegaOctus_StateOrb_Destroyed;
1053
}
1054
1055
#if MANIA_USE_PLUS
1056
if (self->shotCount > 0)
1057
self->position.x += self->velocity.x * RSDK.Sin1024(++self->orbMoveAngle);
1058
#endif
1059
1060
if (--self->timer <= 0) {
1061
++self->shotCount;
1062
1063
int32 x = (RSDK.Cos512(self->angle) << 10) + self->position.x;
1064
int32 y = (RSDK.Sin512(self->angle) << 9) + self->position.y;
1065
1066
int32 angle = 0;
1067
#if MANIA_USE_PLUS
1068
if (MegaOctus->bossEntity->position.x <= x)
1069
angle = -3 * self->shotCount;
1070
else
1071
angle = 3 * self->shotCount;
1072
angle *= 4;
1073
1074
EntityMegaOctus *shot = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ORBSHOT), x, y);
1075
shot->velocity.x = 0x300 * RSDK.Sin256(angle);
1076
shot->velocity.y = 0x300 * RSDK.Cos256(angle);
1077
#else
1078
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1079
angle = RSDK.ATan2(player1->position.x - x, player1->position.y - y);
1080
1081
EntityMegaOctus *shot = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ORBSHOT), x, y);
1082
shot->velocity.x = 0x300 * RSDK.Cos256(angle);
1083
shot->velocity.y = 0x300 * RSDK.Sin256(angle);
1084
#endif
1085
1086
RSDK.PlaySfx(MegaOctus->sfxBullet, false, 255);
1087
if (self->shotCount >= 4) {
1088
self->velocity.y = 0x10000;
1089
self->state = MegaOctus_StateOrb_Idle;
1090
}
1091
else {
1092
self->timer = 60;
1093
}
1094
}
1095
1096
MegaOctus_CheckPlayerCollisions_Orb();
1097
}
1098
1099
void MegaOctus_StateOrb_Idle(void)
1100
{
1101
RSDK_THIS(MegaOctus);
1102
1103
self->angle = (self->angle + 6) & 0x1FF;
1104
RSDK.ProcessAnimation(&self->altAnimator);
1105
1106
if (MegaOctus->defeated) {
1107
self->invincibilityTimer = 60;
1108
self->state = MegaOctus_StateOrb_Destroyed;
1109
}
1110
1111
self->position.y += self->velocity.y;
1112
MegaOctus_CheckPlayerCollisions_Orb();
1113
1114
if (self->position.y > self->origin.y)
1115
destroyEntity(self);
1116
}
1117
1118
void MegaOctus_StateOrb_Destroyed(void)
1119
{
1120
RSDK_THIS(MegaOctus);
1121
1122
if (!(Zone->timer % 3)) {
1123
RSDK.PlaySfx(MegaOctus->sfxExplosion, false, 255);
1124
1125
if (Zone->timer & 4) {
1126
int32 x = self->position.x + (RSDK.Cos512(self->angle) << 10) + (RSDK.Rand(-16, 16) << 16);
1127
int32 y = self->position.y + (RSDK.Sin512(self->angle) << 9) + (RSDK.Rand(-16, 16) << 16);
1128
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + 2), x, y);
1129
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
1130
}
1131
}
1132
1133
if (--self->invincibilityTimer <= 0) {
1134
int32 angle = self->angle;
1135
int32 y = self->position.y + 0xF80000;
1136
1137
EntityDebris *debris = NULL;
1138
for (int32 i = 0; i < 16; ++i) {
1139
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x + (RSDK.Cos512(angle) << 10), y + (RSDK.Sin512(angle) << 8));
1140
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 3, &debris->animator, true, 1);
1141
debris->velocity.x = RSDK.Rand(-6, 6) << 15;
1142
debris->velocity.y = RSDK.Rand(-10, -6) << 15;
1143
debris->gravityStrength = 0x3800;
1144
debris->drawGroup = Zone->objectDrawGroup[0] + 1;
1145
debris->updateRange.x = 0x400000;
1146
debris->updateRange.y = 0x400000;
1147
1148
angle = (angle + 0x40) & 0x1FF;
1149
y -= 0xF0000;
1150
}
1151
1152
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x + (RSDK.Cos512(angle) << 10), y + (RSDK.Sin512(angle) << 8));
1153
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 9, &debris->animator, true, 0);
1154
debris->velocity.x = -0x20000;
1155
debris->velocity.y = -0x40000;
1156
debris->gravityStrength = 0x3800;
1157
debris->drawGroup = Zone->objectDrawGroup[0] + 1;
1158
debris->updateRange.x = 0x400000;
1159
debris->updateRange.y = 0x400000;
1160
1161
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x + (RSDK.Cos512(angle) << 10), y + (RSDK.Sin512(angle) << 8));
1162
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 9, &debris->animator, true, 1);
1163
debris->velocity.x = 0x20000;
1164
debris->velocity.y = -0x40000;
1165
debris->gravityStrength = 0x3800;
1166
debris->drawGroup = Zone->objectDrawGroup[0] + 1;
1167
debris->updateRange.x = 0x400000;
1168
debris->updateRange.y = 0x400000;
1169
1170
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x + (RSDK.Cos512(angle) << 10), y + (RSDK.Sin512(angle) << 8));
1171
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 9, &debris->animator, true, 2);
1172
debris->velocity.x = -0x10000;
1173
debris->velocity.y = -0x20000;
1174
debris->gravityStrength = 0x3800;
1175
debris->drawGroup = Zone->objectDrawGroup[0] + 1;
1176
debris->updateRange.x = 0x400000;
1177
debris->updateRange.y = 0x400000;
1178
1179
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x + (RSDK.Cos512(angle) << 10), y + (RSDK.Sin512(angle) << 8));
1180
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 9, &debris->animator, true, 3);
1181
debris->velocity.x = 0x10000;
1182
debris->velocity.y = -0x20000;
1183
debris->gravityStrength = 0x3800;
1184
debris->drawGroup = Zone->objectDrawGroup[0] + 1;
1185
debris->updateRange.x = 0x400000;
1186
debris->updateRange.y = 0x400000;
1187
1188
destroyEntity(self);
1189
}
1190
}
1191
1192
void MegaOctus_Draw_Orb(void)
1193
{
1194
RSDK_THIS(MegaOctus);
1195
1196
int32 angle = self->angle;
1197
int32 y = self->position.y + 0xF80000;
1198
1199
if (self->invincibilityTimer & 1)
1200
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
1201
1202
Vector2 drawPos;
1203
for (int32 i = 0; i < 16; ++i) {
1204
drawPos.x = (RSDK.Cos512(angle) << 10) + self->position.x;
1205
drawPos.y = (RSDK.Sin512(angle) << 8) + y;
1206
RSDK.DrawSprite(&self->animator, &drawPos, false);
1207
1208
angle = (angle + 0x40) & 0x1FF;
1209
y -= 0xF0000;
1210
}
1211
1212
drawPos.x = (RSDK.Cos512(angle) << 10) + self->position.x;
1213
drawPos.y = (RSDK.Sin512(angle) << 8) + y;
1214
RSDK.DrawSprite(&self->altAnimator, &drawPos, false);
1215
1216
RSDK.SetPaletteEntry(0, 128, 0x000000);
1217
}
1218
1219
void MegaOctus_StateArm_WrapAroundPlatform(void)
1220
{
1221
RSDK_THIS(MegaOctus);
1222
1223
self->angle += 4;
1224
1225
self->position.y -= 0xC000;
1226
self->shotCount += 0xC000;
1227
self->position.x = 0x1400 * RSDK.Cos256(self->angle) + self->origin.x;
1228
1229
if (self->shotCount >= 0x4B0000) {
1230
if (!self->targetPos) {
1231
// Create another arm to grab the other platform
1232
self->targetPos = 1;
1233
EntityMegaOctus *arm = CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_ARM), self->position.x, self->position.y + 0x40000);
1234
arm->direction = self->direction;
1235
arm->state = MegaOctus_StateArm_GrabPlatform;
1236
arm->stateDraw = MegaOctus_Draw_Arm_WrapAroundPlatformTop;
1237
}
1238
1239
if (self->shotCount >= 0x71A000) {
1240
self->timer = 30;
1241
self->state = MegaOctus_StateArm_GrabbedPlatform;
1242
}
1243
}
1244
}
1245
1246
void MegaOctus_StateArm_GrabPlatform(void)
1247
{
1248
RSDK_THIS(MegaOctus);
1249
1250
self->position.x += 0xC000;
1251
self->shotCount += 0xC000;
1252
1253
self->position.y = BadnikHelpers_Oscillate(self->origin.y, -4, 12);
1254
1255
if (self->shotCount >= 0x270000) {
1256
foreach_active(TilePlatform, platform)
1257
{
1258
if (RSDK.CheckObjectCollisionTouchBox(self, &self->hitbox, platform, &platform->hitbox)) {
1259
self->parent = (Entity *)platform;
1260
#if MANIA_USE_PLUS
1261
self->tilePlatY = platform->position.y;
1262
#endif
1263
RSDK.CopyTileLayer(Zone->fgLayer[0], (platform->position.x >> 20) - 4, (platform->position.y >> 20) - 2, Zone->moveLayer, 10, 1, 8,
1264
5);
1265
}
1266
}
1267
1268
int32 slot = RSDK.GetEntitySlot(MegaOctus->bossEntity) + 4;
1269
1270
EntityCollapsingPlatform *collapsingPlatform = RSDK_GET_ENTITY(slot, CollapsingPlatform);
1271
collapsingPlatform->collapseDelay = 24;
1272
collapsingPlatform->stoodPos.x = self->position.x;
1273
1274
collapsingPlatform = RSDK_GET_ENTITY(slot + 1, CollapsingPlatform);
1275
collapsingPlatform->collapseDelay = 24;
1276
collapsingPlatform->stoodPos.x = self->position.x;
1277
1278
self->timer = 30;
1279
self->state = MegaOctus_StateArm_GrabbedPlatform;
1280
}
1281
}
1282
1283
void MegaOctus_StateArm_GrabbedPlatform(void)
1284
{
1285
RSDK_THIS(MegaOctus);
1286
1287
if (--self->timer <= 0) {
1288
#if !MANIA_USE_PLUS
1289
foreach_active(Player, player)
1290
{
1291
if (abs(player->position.x - player->position.x) < 0x400000 && player->groundedStore) {
1292
player->state = Player_State_Air;
1293
player->velocity.y = -0x10000;
1294
player->onGround = false;
1295
}
1296
}
1297
#endif
1298
1299
self->state = MegaOctus_StateArm_PullPlatformDown;
1300
}
1301
}
1302
1303
void MegaOctus_StateArm_PullPlatformDown(void)
1304
{
1305
RSDK_THIS(MegaOctus);
1306
1307
EntityTilePlatform *parent = (EntityTilePlatform *)self->parent;
1308
1309
self->origin.y += 0x60000;
1310
self->position.y += 0x60000;
1311
1312
if (parent) {
1313
#if MANIA_USE_PLUS
1314
parent->state = MegaOctus_TilePlatformState_RiseOuttaOil;
1315
parent->velocity.y = 0x60000;
1316
if (parent->drawPos.y - self->tilePlatY > 0x480000)
1317
parent->stateCollide = Platform_Collision_None;
1318
#else
1319
parent->drawPos.y += 0x60000;
1320
parent->centerPos.y += 0x60000;
1321
#endif
1322
}
1323
1324
if (++self->timer >= 32) {
1325
EntityMegaOctus *boss = MegaOctus->bossEntity;
1326
boss->velocity.y = MANIA_USE_PLUS ? -0x8F400 : -0xB8000;
1327
boss->state = MegaOctus_State_EnterMegaOctus;
1328
1329
MegaOctus->eggmanOffset = 0;
1330
MegaOctus->eggmanVelocity = 0;
1331
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 1, &MegaOctus->eggmanAnimator, true, 0);
1332
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 1, &MegaOctus->hatchOpenAnimator, true, 0);
1333
RSDK.PlaySfx(MegaOctus->sfxSurface, false, 255);
1334
1335
if (parent) {
1336
self->timer = 384;
1337
self->state = MegaOctus_StateArm_RisePlatformUp;
1338
#if MANIA_USE_PLUS
1339
parent->state = StateMachine_None;
1340
parent->position.y = self->tilePlatY + (self->timer << 15);
1341
#endif
1342
}
1343
else {
1344
destroyEntity(self);
1345
}
1346
}
1347
}
1348
1349
void MegaOctus_StateArm_RisePlatformUp(void)
1350
{
1351
RSDK_THIS(MegaOctus);
1352
1353
EntityTilePlatform *parent = (EntityTilePlatform *)self->parent;
1354
1355
#if MANIA_USE_PLUS
1356
if (parent) {
1357
parent->state = MegaOctus_TilePlatformState_RiseOuttaOil;
1358
parent->timer = 4;
1359
parent->velocity.y = -0x8000;
1360
1361
if (parent->drawPos.y > self->tilePlatY)
1362
--self->timer;
1363
else
1364
self->timer = 0;
1365
1366
if (parent->drawPos.y - self->tilePlatY < 0x480000)
1367
parent->stateCollide = Platform_Collision_Tiles;
1368
}
1369
#else
1370
parent->drawPos.y -= 0x8000;
1371
parent->centerPos.y -= 0x8000;
1372
--self->timer;
1373
#endif
1374
1375
if (self->timer <= 0) {
1376
#if MANIA_USE_PLUS
1377
if (parent) {
1378
parent->state = StateMachine_None;
1379
parent->drawPos.y = self->tilePlatY;
1380
parent->velocity.y = 0;
1381
}
1382
#endif
1383
1384
RSDK.CopyTileLayer(Zone->fgLayer[0], (parent->position.x >> 20) - 4, (parent->position.y >> 20) - 2, Zone->moveLayer, 1, 1, 8, 5);
1385
destroyEntity(self);
1386
}
1387
}
1388
1389
void MegaOctus_Draw_Arm_WrapAroundPlatformBase(void)
1390
{
1391
RSDK_THIS(MegaOctus);
1392
1393
int32 pos = 0xA0000;
1394
Vector2 drawPos = self->position;
1395
uint8 angle = self->angle;
1396
1397
for (int32 i = self->shotCount; i > 0; i -= 0x6000) {
1398
pos += 0x6000;
1399
if (pos >= 0x60000) {
1400
if (i < 0x400000) {
1401
if ((SceneInfo->currentDrawGroup == Zone->objectDrawGroup[0] + 1 && angle < 0x80)
1402
|| (SceneInfo->currentDrawGroup == Zone->objectDrawGroup[0] && angle >= 0x80))
1403
RSDK.DrawSprite(&self->animator, &drawPos, false);
1404
}
1405
angle -= 0x20;
1406
drawPos.y += 0x60000;
1407
drawPos.x = 0x1400 * RSDK.Cos256(angle) + self->origin.x;
1408
pos -= 0x60000;
1409
}
1410
}
1411
}
1412
1413
void MegaOctus_Draw_Arm_WrapAroundPlatformTop(void)
1414
{
1415
RSDK_THIS(MegaOctus);
1416
1417
int32 pos = 0xA0000;
1418
Vector2 drawPos = self->position;
1419
int32 count = (self->shotCount - 1) / 0x6000 + 1;
1420
1421
int32 angle = self->angle;
1422
uint8 checkAngle = self->angle - 0x40;
1423
for (int32 i = 0; i < count; ++i) {
1424
pos += 0x6000;
1425
if (pos >= 0x60000) {
1426
if ((SceneInfo->currentDrawGroup == Zone->objectDrawGroup[0] + 1 && checkAngle < 0x80)
1427
|| (SceneInfo->currentDrawGroup == Zone->objectDrawGroup[0] && checkAngle >= 0x80))
1428
RSDK.DrawSprite(&self->animator, &drawPos, false);
1429
checkAngle += 0x20;
1430
angle += 0x20;
1431
drawPos.x -= 0x60000;
1432
drawPos.y = (RSDK.Sin256(angle) << 12) + self->origin.y;
1433
pos -= 0x60000;
1434
}
1435
}
1436
}
1437
1438
void MegaOctus_State_Laser(void)
1439
{
1440
RSDK_THIS(MegaOctus);
1441
1442
RSDK.ProcessAnimation(&self->animator);
1443
1444
self->position.x += self->velocity.x;
1445
1446
if (self->timer > 0) {
1447
self->timer--;
1448
self->position.y = (RSDK.Sin512(self->parent->angle) << 9) + self->parent->position.y;
1449
}
1450
1451
foreach_active(Player, player)
1452
{
1453
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
1454
Player_Hurt(player, self);
1455
}
1456
1457
if (self->onScreen == 1) {
1458
if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0, 8)) {
1459
if (self->shotCount != self->position.x >> 20) {
1460
self->shotCount = self->position.x >> 20;
1461
RSDK.PlaySfx(MegaOctus->sfxLaserSplash, false, 255);
1462
CREATE_ENTITY(MegaOctus, INT_TO_VOID(MEGAOCTUS_LASERFIRE), (self->position.x & 0xFFF00000) + 0x80000, self->position.y);
1463
}
1464
}
1465
}
1466
1467
if (!RSDK.CheckOnScreen(self, &self->updateRange))
1468
destroyEntity(self);
1469
}
1470
1471
void MegaOctus_State_LaserFire(void)
1472
{
1473
RSDK_THIS(MegaOctus);
1474
1475
RSDK.ProcessAnimation(&self->animator);
1476
1477
foreach_active(Player, player)
1478
{
1479
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
1480
Player_Hurt(player, self);
1481
}
1482
1483
if (self->animator.frameID == self->animator.frameCount - 1)
1484
destroyEntity(self);
1485
}
1486
1487
void MegaOctus_Draw_Laser(void)
1488
{
1489
RSDK_THIS(MegaOctus);
1490
1491
RSDK.DrawSprite(&self->animator, NULL, false);
1492
}
1493
1494
void MegaOctus_State_Shot(void)
1495
{
1496
RSDK_THIS(MegaOctus);
1497
1498
if (MegaOctus->defeated || !RSDK.CheckOnScreen(self, &self->updateRange)) {
1499
destroyEntity(self);
1500
}
1501
else {
1502
RSDK.ProcessAnimation(&self->animator);
1503
1504
self->position.x += self->velocity.x;
1505
self->position.y += self->velocity.y;
1506
1507
foreach_active(Player, player)
1508
{
1509
if (Player_CheckCollisionTouch(player, self, &self->hitbox))
1510
Player_ProjectileHurt(player, self);
1511
}
1512
}
1513
}
1514
1515
void MegaOctus_Draw_OrbShot(void)
1516
{
1517
RSDK_THIS(MegaOctus);
1518
1519
RSDK.DrawSprite(&self->animator, NULL, false);
1520
}
1521
1522
#if MANIA_USE_PLUS
1523
void MegaOctus_TilePlatformState_RiseOuttaOil(void)
1524
{
1525
EntityTilePlatform *self = RSDK_GET_ENTITY(SceneInfo->entitySlot, TilePlatform);
1526
1527
if (self->velocity.y <= 0x10000) {
1528
foreach_active(Player, player)
1529
{
1530
if ((1 << player->playerID) & self->stoodPlayers) {
1531
if (player->state == OOZSetup_PlayerState_OilPool) {
1532
player->state = Player_State_Ground;
1533
OOZSetup->activePlayers &= ~(1 << player->playerID);
1534
}
1535
}
1536
}
1537
}
1538
else {
1539
foreach_active(Player, player)
1540
{
1541
if ((1 << player->playerID) & self->stoodPlayers) {
1542
if (player->state != OOZSetup_PlayerState_OilPool)
1543
player->velocity.y = self->velocity.y >> 1;
1544
1545
player->onGround = false;
1546
self->stoodPlayers &= ~(1 << player->playerID);
1547
}
1548
}
1549
}
1550
1551
self->drawPos.y += self->velocity.y;
1552
}
1553
#endif
1554
1555
#if GAME_INCLUDE_EDITOR
1556
void MegaOctus_EditorDraw(void)
1557
{
1558
RSDK_THIS(MegaOctus);
1559
1560
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 0, &self->animator, false, 0);
1561
RSDK.SetSpriteAnimation(MegaOctus->eggmanFrames, 1, &MegaOctus->eggmanAnimator, false, 0);
1562
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 1, &MegaOctus->noseAnimator, false, 5);
1563
RSDK.SetSpriteAnimation(MegaOctus->aniFrames, 2, &MegaOctus->boltsAnimator, false, 5);
1564
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 0, &MegaOctus->hatchBaseAnimator, false, 0);
1565
RSDK.SetSpriteAnimation(MegaOctus->hatchFrames, 1, &MegaOctus->hatchOpenAnimator, false, 0);
1566
1567
MegaOctus_Draw_Body();
1568
1569
if (showGizmos()) {
1570
int32 slot = RSDK.GetEntitySlot(self);
1571
1572
RSDK_DRAWING_OVERLAY(true);
1573
1574
#if MANIA_USE_PLUS
1575
DrawHelpers_DrawArenaBounds(-192, -384, 448, 96, 1 | 2 | 4 | 8, 0x00C0F0);
1576
#else
1577
DrawHelpers_DrawArenaBounds(-192, -384, 448, 96, 1 | 0 | 4 | 8, 0x00C0F0);
1578
#endif
1579
1580
// Tile Platforms
1581
for (int32 p = 1; p < 4; ++p) {
1582
EntityTilePlatform *platform = RSDK_GET_ENTITY(slot + p, TilePlatform);
1583
if (!platform)
1584
continue;
1585
1586
DrawHelpers_DrawArrow(self->position.x, self->position.y, platform->position.x, platform->position.y, 0xFFFF00, INK_NONE, 0xFF);
1587
}
1588
1589
// Collapsing Platforms
1590
for (int32 p = 4; p < 6; ++p) {
1591
EntityCollapsingPlatform *platform = RSDK_GET_ENTITY(slot + p, CollapsingPlatform);
1592
if (!platform)
1593
continue;
1594
1595
DrawHelpers_DrawArrow(self->position.x, self->position.y, platform->position.x, platform->position.y, 0xFFFF00, INK_NONE, 0xFF);
1596
}
1597
RSDK_DRAWING_OVERLAY(false);
1598
}
1599
}
1600
1601
void MegaOctus_EditorLoad(void)
1602
{
1603
MegaOctus->aniFrames = RSDK.LoadSpriteAnimation("OOZ/MegaOctus.bin", SCOPE_STAGE);
1604
MegaOctus->eggmanFrames = RSDK.LoadSpriteAnimation("Eggman/EggmanOOZ.bin", SCOPE_STAGE);
1605
MegaOctus->hatchFrames = RSDK.LoadSpriteAnimation("OOZ/Hatch.bin", SCOPE_STAGE);
1606
}
1607
#endif
1608
1609
void MegaOctus_Serialize(void) {}
1610
1611