Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/ERZ/PhantomEgg.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: PhantomEgg Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectPhantomEgg *PhantomEgg;
11
12
void PhantomEgg_Update(void)
13
{
14
RSDK_THIS(PhantomEgg);
15
16
StateMachine_Run(self->state);
17
}
18
19
void PhantomEgg_LateUpdate(void) {}
20
21
void PhantomEgg_StaticUpdate(void) {}
22
23
void PhantomEgg_Draw(void)
24
{
25
RSDK_THIS(PhantomEgg);
26
27
if (self->stateDraw) {
28
StateMachine_Run(self->stateDraw);
29
}
30
else {
31
RSDK.DrawSprite(&self->crackAnimator, NULL, false);
32
}
33
}
34
35
void PhantomEgg_Create(void *data)
36
{
37
RSDK_THIS(PhantomEgg);
38
39
self->drawFX = FX_FLIP;
40
41
if (!SceneInfo->inEditor) {
42
self->active = ACTIVE_BOUNDS;
43
self->updateRange.x = 0x800000;
44
self->updateRange.y = 0x1000000;
45
self->attackStateTable = PhantomEgg->attackStateTable1;
46
self->type = VOID_TO_INT(data);
47
48
if (!data) {
49
self->visible = false;
50
self->drawGroup = Zone->objectDrawGroup[0];
51
52
self->hitbox.left = -24;
53
self->hitbox.top = -24;
54
self->hitbox.right = 24;
55
self->hitbox.bottom = 24;
56
57
self->drawFX = FX_FLIP;
58
self->active = ACTIVE_NORMAL;
59
self->health = 16;
60
61
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 0, &self->coreAnimator, true, 0);
62
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 17, &self->eggmanAnimator, true, 0);
63
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);
64
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 0);
65
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 13, &self->rubyAnimator, true, 0);
66
67
self->targetPos.x = self->position.x;
68
self->targetPos.y = self->position.y;
69
self->state = PhantomEgg_State_SetupArena;
70
}
71
}
72
}
73
74
void PhantomEgg_StageLoad(void)
75
{
76
PhantomEgg->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomEgg.bin", SCOPE_STAGE);
77
78
PhantomEgg->savedGameProgress = false;
79
80
#if MANIA_USE_PLUS
81
if (SceneInfo->filter & FILTER_ENCORE) {
82
RSDK.LoadPalette(0, "EncoreTMZ3.act", 0b0000000011111111);
83
RSDK.LoadPalette(1, "EncoreTMZ3.act", 0b0000000011111111);
84
}
85
#endif
86
87
if (!PhantomEgg->setupPalette) {
88
RSDK.CopyPalette(0, 0, 1, 0, 128);
89
PhantomEgg->setupPalette = true;
90
}
91
92
RSDK.CopyPalette(1, 128, 0, 128, 128);
93
RSDK.CopyPalette(0, 0, 4, 0, 128);
94
95
PhantomEgg->disableSuperForm = false;
96
97
PhantomEgg->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
98
PhantomEgg->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");
99
PhantomEgg->sfxRocketJet = RSDK.GetSfx("Stage/RocketJet.wav");
100
PhantomEgg->sfxExplosion3 = RSDK.GetSfx("Stage/Explosion3.wav");
101
PhantomEgg->sfxJump = RSDK.GetSfx("TMZ3/Jump.wav");
102
PhantomEgg->sfxLand = RSDK.GetSfx("TMZ3/Land.wav");
103
PhantomEgg->sfxRepel = RSDK.GetSfx("TMZ3/Repel.wav");
104
PhantomEgg->sfxShield = RSDK.GetSfx("TMZ3/Shield.wav");
105
PhantomEgg->sfxShock = RSDK.GetSfx("TMZ3/Shock.wav");
106
PhantomEgg->sfxSummon = RSDK.GetSfx("TMZ3/Summon.wav");
107
PhantomEgg->sfxMissile = RSDK.GetSfx("TMZ3/Missile.wav");
108
}
109
110
void PhantomEgg_HandleAnimations(void)
111
{
112
RSDK_THIS(PhantomEgg);
113
114
RSDK.ProcessAnimation(&self->eggmanAnimator);
115
116
if (self->rubyAnimator.animationID == 14) {
117
RSDK.ProcessAnimation(&self->rubyAnimator);
118
119
if (self->rubyAnimator.frameID == self->rubyAnimator.frameCount - 1)
120
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 13, &self->rubyAnimator, true, 0);
121
}
122
123
if (self->invincibilityTimer > 0)
124
self->invincibilityTimer--;
125
126
if (self->eggmanAnimator.animationID == 17) {
127
self->eggmanAnimator.speed = 0;
128
}
129
else {
130
if (self->eggmanAnimator.animationID == 18 || self->eggmanAnimator.animationID == 19) {
131
if (self->eggmanAnimator.frameID == self->eggmanAnimator.frameCount - 1)
132
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 17, &self->eggmanAnimator, true, 0);
133
}
134
}
135
}
136
137
void PhantomEgg_CheckPlayerCollisions(void)
138
{
139
RSDK_THIS(PhantomEgg);
140
141
if (RSDK.GetEntityCount(PhantomShield->classID, true) <= 0) {
142
foreach_active(Player, player)
143
{
144
if (!self->invincibilityTimer && Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self)) {
145
PhantomEgg_Hit();
146
}
147
}
148
}
149
}
150
151
void PhantomEgg_Hit(void)
152
{
153
RSDK_THIS(PhantomEgg);
154
155
--self->health;
156
if (!(self->health & 3)) {
157
int32 id = (-2 - RSDK.GetEntityCount(TMZCable->classID, true)) & 3;
158
159
foreach_active(TMZCable, cable)
160
{
161
if (cable->cableID == id || !self->health) {
162
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 9, &cable->animator, true, 0);
163
cable->state = TMZCable_State_Destroyed;
164
// Bug Details:
165
// uncomment to fix a minor visual bug where the start few cable nodes wont be destroyed properly
166
// cable->timer = 0;
167
}
168
}
169
170
if (id == 1)
171
self->attackStateTable = PhantomEgg->attackStateTable2;
172
else
173
self->attackStateTable = PhantomEgg->attackStateTable1;
174
}
175
176
if (self->health <= 0) {
177
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 20, &self->eggmanAnimator, true, 0);
178
self->targetPos.x = self->position.x;
179
self->targetPos.y = self->position.y;
180
self->state = PhantomEgg_State_Destroyed;
181
self->timer = 0;
182
PhantomEgg->disableSuperForm = true;
183
SceneInfo->timeEnabled = false;
184
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
185
}
186
else {
187
self->invincibilityTimer = 48;
188
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 19, &self->eggmanAnimator, true, 0);
189
RSDK.PlaySfx(PhantomEgg->sfxHit, false, 255);
190
}
191
}
192
193
void PhantomEgg_Explode(Hitbox *hitbox)
194
{
195
RSDK_THIS(PhantomEgg);
196
197
if (!(Zone->timer % 7)) {
198
RSDK.PlaySfx(PhantomEgg->sfxExplosion2, false, 255);
199
200
if (!(Zone->timer & 8)) {
201
int32 x = self->position.x + (RSDK.Rand(hitbox->left, hitbox->right) << 16);
202
int32 y = self->position.y + (RSDK.Rand(hitbox->top, hitbox->bottom) << 16);
203
CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];
204
}
205
}
206
}
207
208
void PhantomEgg_HandleNextAttack(void)
209
{
210
RSDK_THIS(PhantomEgg);
211
212
switch (self->attackStateTable[self->attackTimer]) {
213
case PHANTOMEGG_ATTACK_JUMP:
214
if (self->state != PhantomEgg_State_Attack_JumpLand) {
215
self->groundVel = 0;
216
self->velocity.x = 0;
217
self->remainingJumps = 3;
218
self->state = PhantomEgg_State_Attack_Jumped;
219
self->attackTimer++;
220
break;
221
}
222
break;
223
224
case PHANTOMEGG_ATTACK_SHOCK:
225
self->timer = 0;
226
self->state = PhantomEgg_State_Attack_CableShock;
227
self->attackTimer++;
228
break;
229
230
case PHANTOMEGG_ATTACK_MISSILES:
231
if (self->position.y >= PhantomEgg->boundsB - 0xA00000) {
232
foreach_active(PhantomMissile, missile) { missile->timer = 16 * missile->id + 8; }
233
234
++self->attackTimer;
235
}
236
break;
237
238
case PHANTOMEGG_ATTACK_WARP:
239
self->timer = 0;
240
self->state = PhantomEgg_State_Attack_PrepareWarp;
241
242
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 0);
243
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);
244
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armRAnimator, true, 0);
245
246
self->attackTimer++;
247
break;
248
249
default: self->attackTimer++; break;
250
}
251
252
self->attackTimer %= 32;
253
}
254
255
void PhantomEgg_SetupWarpFX(void)
256
{
257
foreach_all(PhantomEgg, phantomEgg)
258
{
259
PhantomEgg->startScanline = ScreenInfo->center.y;
260
PhantomEgg->endScanline = ScreenInfo->size.y;
261
262
RSDK.GetTileLayer(Zone->fgLayer[0])->scanlineCallback = PhantomEgg_Scanline_WarpFX;
263
RSDK.GetTileLayer(Zone->fgLayer[1])->scanlineCallback = PhantomEgg_Scanline_WarpFX;
264
265
PhantomRuby_PlaySfx(RUBYSFX_ATTACK1);
266
267
phantomEgg->timer = 0;
268
phantomEgg->state = PhantomEgg_State_Attack_HandleWarp;
269
foreach_break;
270
}
271
}
272
273
void PhantomEgg_HandlePhantomWarp(uint8 phantomID)
274
{
275
PhantomEgg->boundsStoreL1 = Zone->cameraBoundsL[0];
276
PhantomEgg->boundsStoreR1 = Zone->cameraBoundsR[0];
277
PhantomEgg->boundsStoreT1 = Zone->cameraBoundsT[0];
278
PhantomEgg->boundsStoreB1 = Zone->cameraBoundsB[0];
279
280
Entity *targetPhantom = NULL;
281
282
switch (phantomID & 3) {
283
case 0: {
284
foreach_all(PhantomGunner, gunner)
285
{
286
targetPhantom = (Entity *)gunner;
287
foreach_break;
288
}
289
break;
290
}
291
292
case 1: {
293
foreach_all(PhantomShinobi, shinobi)
294
{
295
targetPhantom = (Entity *)shinobi;
296
foreach_break;
297
}
298
break;
299
}
300
301
case 2: {
302
foreach_all(PhantomMystic, mystic)
303
{
304
targetPhantom = (Entity *)mystic;
305
foreach_break;
306
}
307
break;
308
}
309
310
case 3: {
311
foreach_all(PhantomRider, rider)
312
{
313
targetPhantom = (Entity *)rider;
314
foreach_break;
315
}
316
break;
317
}
318
}
319
320
if (targetPhantom) {
321
int32 phantomSlot = RSDK.GetEntitySlot(targetPhantom);
322
EntityPlatformNode *nodeTop = RSDK_GET_ENTITY(phantomSlot + 1, PlatformNode);
323
EntityPlatformNode *nodeBottom = RSDK_GET_ENTITY(phantomSlot + 2, PlatformNode);
324
EntityPlatformNode *nodeLeft = RSDK_GET_ENTITY(phantomSlot + 3, PlatformNode);
325
EntityPlatformNode *nodeRight = RSDK_GET_ENTITY(phantomSlot + 4, PlatformNode);
326
327
int32 offsetX = nodeBottom->position.x - PhantomEgg->boundsM;
328
int32 offsetY = nodeBottom->position.y - (PhantomEgg->boundsStoreB1 << 16);
329
330
for (int32 p = 0; p < Player->playerCount; ++p) {
331
Zone->cameraBoundsL[p] = nodeLeft->position.x >> 16;
332
Zone->cameraBoundsR[p] = nodeRight->position.x >> 16;
333
Zone->cameraBoundsT[p] = nodeTop->position.y >> 16;
334
Zone->cameraBoundsB[p] = nodeBottom->position.y >> 16;
335
336
Zone->playerBoundsL[p] = nodeLeft->position.x;
337
Zone->playerBoundsR[p] = nodeRight->position.x;
338
Zone->playerBoundsT[p] = nodeTop->position.y;
339
Zone->playerBoundsB[p] = nodeBottom->position.y;
340
Zone->deathBoundary[p] = nodeBottom->position.y + 0x800000;
341
}
342
343
targetPhantom->active = ACTIVE_NORMAL;
344
345
EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);
346
camera->boundsL = Zone->cameraBoundsL[0];
347
camera->boundsR = Zone->cameraBoundsR[0];
348
camera->boundsT = Zone->cameraBoundsT[0];
349
camera->boundsB = Zone->cameraBoundsB[0];
350
351
if ((phantomID & 3) == 3) {
352
EntityPlatformNode *nodeStart = RSDK_GET_ENTITY(phantomSlot + 5, PlatformNode);
353
camera->position.x = nodeStart->position.x;
354
camera->position.y = nodeStart->position.y;
355
356
foreach_active(Player, player)
357
{
358
player->position.x = camera->position.x;
359
player->position.y = camera->position.y;
360
}
361
362
foreach_all(PhantomHand, hand)
363
{
364
hand->position.x = camera->position.x;
365
hand->position.y = camera->position.y;
366
}
367
}
368
else {
369
camera->position.x += offsetX;
370
camera->position.y += offsetY;
371
foreach_active(Player, player)
372
{
373
player->position.x += offsetX;
374
player->position.y += offsetY;
375
}
376
377
foreach_all(PhantomHand, hand)
378
{
379
hand->position.x += offsetX;
380
hand->position.y += offsetY;
381
}
382
}
383
384
foreach_all(PhantomMissile, missile) { missile->active = ACTIVE_NEVER; }
385
foreach_all(TMZCable, cable) { cable->active = ACTIVE_NEVER; }
386
}
387
}
388
389
void PhantomEgg_HandleReturnWarp(void)
390
{
391
for (int32 p = 0; p < Player->playerCount; ++p) {
392
Zone->cameraBoundsL[p] = PhantomEgg->boundsStoreL1;
393
Zone->cameraBoundsR[p] = PhantomEgg->boundsStoreR1;
394
Zone->cameraBoundsT[p] = PhantomEgg->boundsStoreT1;
395
Zone->cameraBoundsB[p] = PhantomEgg->boundsStoreB1;
396
397
Zone->playerBoundsL[p] = PhantomEgg->boundsStoreL1 << 16;
398
Zone->playerBoundsR[p] = PhantomEgg->boundsStoreR1 << 16;
399
Zone->playerBoundsT[p] = PhantomEgg->boundsStoreT1 << 16;
400
Zone->playerBoundsB[p] = PhantomEgg->boundsStoreB1 << 16;
401
Zone->deathBoundary[p] = Zone->playerBoundsB[p];
402
}
403
404
EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);
405
camera->boundsL = Zone->cameraBoundsL[0];
406
camera->boundsR = Zone->cameraBoundsR[0];
407
camera->boundsT = Zone->cameraBoundsT[0];
408
camera->boundsB = Zone->cameraBoundsB[0];
409
camera->position.x = PhantomEgg->boundsM;
410
camera->position.y = PhantomEgg->boundsStoreB1 - (ScreenInfo->center.y << 16);
411
412
foreach_active(Player, player)
413
{
414
player->position.x = PhantomEgg->boundsM;
415
player->position.y = (PhantomEgg->boundsStoreB1 - 40) << 16;
416
}
417
418
foreach_all(PhantomMissile, missile) { missile->active = ACTIVE_NORMAL; }
419
foreach_all(TMZCable, cable) { cable->active = ACTIVE_NORMAL; }
420
}
421
422
void PhantomEgg_Scanline_WarpFX(ScanlineInfo *scanlines)
423
{
424
TileLayer *fgLow = RSDK.GetTileLayer(Zone->fgLayer[0]);
425
RSDK.ProcessParallax(fgLow);
426
427
int32 line = 0;
428
if (PhantomEgg->startScanline - PhantomEgg->endScanline >= 0)
429
line = PhantomEgg->startScanline - PhantomEgg->endScanline;
430
431
int32 lineY = scanlines[line].position.y;
432
for (int32 l = 0; l < line; ++l) scanlines[l].position.y = lineY;
433
434
line = PhantomEgg->startScanline + PhantomEgg->endScanline;
435
if (line > ScreenInfo->size.y)
436
line = ScreenInfo->size.y;
437
438
lineY = scanlines[line].position.y;
439
for (int32 l = line; l < ScreenInfo->size.y; ++l) scanlines[l].position.y = lineY;
440
}
441
442
void PhantomEgg_Draw_Normal(void)
443
{
444
RSDK_THIS(PhantomEgg);
445
446
if (self->invincibilityTimer & 1) {
447
RSDK.CopyPalette(3, 32, 0, 32, 10);
448
RSDK.CopyPalette(3, 128, 0, 128, 16);
449
RSDK.SetPaletteEntry(0, 128, 0xF0F0F0);
450
451
self->direction = FLIP_NONE;
452
self->coreAnimator.frameID = 1;
453
RSDK.DrawSprite(&self->coreAnimator, NULL, false);
454
RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);
455
456
self->coreAnimator.frameID = 0;
457
RSDK.DrawSprite(&self->coreAnimator, NULL, false);
458
RSDK.DrawSprite(&self->armLAnimator, NULL, false);
459
RSDK.DrawSprite(&self->legAnimator, NULL, false);
460
RSDK.DrawSprite(&self->rubyAnimator, NULL, false);
461
462
self->direction = FLIP_X;
463
RSDK.DrawSprite(&self->armLAnimator, NULL, false);
464
RSDK.DrawSprite(&self->legAnimator, NULL, false);
465
466
RSDK.CopyPalette(1, 32, 0, 32, 10);
467
RSDK.CopyPalette(1, 128, 0, 128, 16);
468
RSDK.SetPaletteEntry(0, 128, 0x000000);
469
}
470
else {
471
self->direction = FLIP_NONE;
472
self->coreAnimator.frameID = 1;
473
RSDK.DrawSprite(&self->coreAnimator, NULL, false);
474
RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);
475
476
self->coreAnimator.frameID = 0;
477
RSDK.DrawSprite(&self->coreAnimator, NULL, false);
478
RSDK.DrawSprite(&self->armLAnimator, NULL, false);
479
RSDK.DrawSprite(&self->legAnimator, NULL, false);
480
RSDK.DrawSprite(&self->rubyAnimator, NULL, false);
481
482
self->direction = FLIP_X;
483
RSDK.DrawSprite(&self->armLAnimator, NULL, false);
484
RSDK.DrawSprite(&self->legAnimator, NULL, false);
485
}
486
}
487
488
void PhantomEgg_Draw_Cracked(void)
489
{
490
RSDK_THIS(PhantomEgg);
491
492
self->direction = FLIP_NONE;
493
self->coreAnimator.frameID = 1;
494
RSDK.DrawSprite(&self->coreAnimator, NULL, false);
495
RSDK.DrawSprite(&self->eggmanAnimator, NULL, false);
496
497
self->coreAnimator.frameID = 0;
498
RSDK.DrawSprite(&self->coreAnimator, NULL, false);
499
RSDK.DrawSprite(&self->crackAnimator, NULL, false);
500
RSDK.DrawSprite(&self->armLAnimator, NULL, false);
501
RSDK.DrawSprite(&self->legAnimator, NULL, false);
502
503
self->direction = FLIP_X;
504
RSDK.DrawSprite(&self->armLAnimator, NULL, false);
505
RSDK.DrawSprite(&self->legAnimator, NULL, false);
506
}
507
508
void PhantomEgg_State_SetupArena(void)
509
{
510
RSDK_THIS(PhantomEgg);
511
512
if (++self->timer >= 8) {
513
self->timer = 0;
514
515
Zone->playerBoundActiveL[0] = true;
516
Zone->playerBoundActiveR[0] = true;
517
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x;
518
Zone->cameraBoundsR[0] = (self->position.x >> 16) + ScreenInfo->center.x;
519
Zone->cameraBoundsT[0] = Zone->cameraBoundsB[0] - ScreenInfo->size.y;
520
521
PhantomEgg->boundsL = (Zone->cameraBoundsL[0] + 64) << 16;
522
PhantomEgg->boundsR = (Zone->cameraBoundsR[0] - 64) << 16;
523
PhantomEgg->boundsM = self->position.x;
524
PhantomEgg->boundsT = (Zone->cameraBoundsT[0] + 48) << 16;
525
PhantomEgg->boundsB = (Zone->cameraBoundsB[0] - 96) << 16;
526
527
self->position.y -= 0x1000000;
528
self->active = ACTIVE_NORMAL;
529
self->state = PhantomEgg_State_DimArena;
530
}
531
}
532
533
void PhantomEgg_State_DimArena(void)
534
{
535
RSDK_THIS(PhantomEgg);
536
537
if (self->timer) {
538
self->timer += 4;
539
RSDK.SetLimitedFade(0, 1, 2, self->timer, 128, 256);
540
541
if (self->timer >= 384) {
542
self->timer = 0;
543
self->visible = true;
544
self->stateDraw = PhantomEgg_Draw_Normal;
545
self->state = PhantomEgg_State_EnterEggman;
546
547
foreach_active(TMZCable, cable)
548
{
549
cable->parentPos = &self->position;
550
cable->state = TMZCable_State_Idle;
551
}
552
553
int32 missileAngles[] = { -24, -10, 10, 24 };
554
int32 angle = 0;
555
int32 id = 0;
556
foreach_all(PhantomMissile, missile)
557
{
558
missile->angle = missileAngles[id];
559
missile->oscillateAngle = angle;
560
missile->active = ACTIVE_NORMAL;
561
missile->parent = self;
562
missile->id = id++;
563
angle += 64;
564
}
565
566
Music_TransitionTrack(TRACK_EGGMAN1, 0.0125);
567
}
568
}
569
else {
570
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x) {
571
SceneInfo->timeEnabled = false;
572
++self->timer;
573
}
574
}
575
}
576
577
void PhantomEgg_State_EnterEggman(void)
578
{
579
RSDK_THIS(PhantomEgg);
580
581
int32 startY = self->position.y;
582
self->velocity.y += self->position.y >= self->targetPos.y ? -0x3800 : 0x3800;
583
584
if (self->velocity.y > 0x50000)
585
self->velocity.y = 0x50000;
586
587
self->position.y += self->velocity.y;
588
if (startY >= self->targetPos.y) {
589
if (self->position.y < self->targetPos.y) {
590
++self->timer;
591
self->velocity.y = (4 * self->velocity.y) >> 3;
592
}
593
}
594
else {
595
if (self->position.y > self->targetPos.y) {
596
++self->timer;
597
self->velocity.y = (4 * self->velocity.y) >> 3;
598
}
599
}
600
601
if (self->timer > 4) {
602
self->timer = 0;
603
self->state = PhantomEgg_State_AdjustStartingPos;
604
}
605
}
606
607
void PhantomEgg_State_AdjustStartingPos(void)
608
{
609
RSDK_THIS(PhantomEgg);
610
611
self->position.y += (self->targetPos.y - self->position.y) >> 3;
612
613
if (++self->timer == 16) {
614
self->timer = 0;
615
foreach_active(TMZAlert, alert) { alert->state = TMZAlert_State_Activating; }
616
self->state = PhantomEgg_State_IntroHover;
617
}
618
}
619
620
void PhantomEgg_State_IntroHover(void)
621
{
622
RSDK_THIS(PhantomEgg);
623
624
PhantomEgg_HandleAnimations();
625
626
RSDK.SetLimitedFade(0, 1, 2, (RSDK.Cos256(self->timer >> 2) >> 1) + 128, 128, 256);
627
self->timer += 11;
628
629
self->angle = (self->angle + 3) & 0xFF;
630
self->position.y = (RSDK.Sin256(self->angle) << 11) + self->targetPos.y;
631
632
if (self->timer == 3960) {
633
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 14, &self->rubyAnimator, true, 0);
634
635
SceneInfo->milliseconds = 0;
636
SceneInfo->seconds = 0;
637
SceneInfo->minutes = 0;
638
639
PhantomRuby_PlaySfx(RUBYSFX_ATTACK1);
640
}
641
642
if (self->timer == 4092) {
643
EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
644
fxFade->speedIn = 32;
645
fxFade->speedOut = 32;
646
}
647
648
if (self->timer >= 4608) {
649
self->timer = 0;
650
self->state = PhantomEgg_State_BeginFight;
651
652
RSDK.CopyPalette(1, 128, 0, 128, 128);
653
}
654
}
655
656
void PhantomEgg_State_BeginFight(void)
657
{
658
RSDK_THIS(PhantomEgg);
659
660
PhantomEgg_HandleAnimations();
661
662
self->position.y = BadnikHelpers_Oscillate(self->targetPos.y, 3, 11);
663
664
if (++self->timer == 30) {
665
self->timer = 0;
666
SceneInfo->timeEnabled = true;
667
self->velocity.x = Player_GetNearestPlayerX()->position.x < self->position.x ? -0x60000 : 0x60000;
668
self->state = PhantomEgg_State_MoveAround;
669
CREATE_ENTITY(PhantomShield, self, self->position.x, self->position.y);
670
}
671
}
672
673
void PhantomEgg_State_MoveAround(void)
674
{
675
RSDK_THIS(PhantomEgg);
676
677
PhantomEgg_HandleAnimations();
678
679
int32 startX = self->position.x;
680
if (self->position.x >= self->targetPos.x - 0x100000) {
681
if (self->position.x > self->targetPos.x + 0x100000)
682
self->velocity.x -= 0x1800;
683
}
684
else {
685
self->velocity.x += 0x1800;
686
}
687
688
self->velocity.x = CLAMP(self->velocity.x, -0x50000, 0x50000);
689
self->position.x += self->velocity.x;
690
691
if (startX >= self->targetPos.x - 0x100000) {
692
if (startX > self->targetPos.x + 0x100000 && self->position.x < self->targetPos.x + 0x100000)
693
self->velocity.x = (7 * self->velocity.x) >> 3;
694
}
695
else {
696
if (self->position.x > self->targetPos.x - 0x100000)
697
self->velocity.x = (7 * self->velocity.x) >> 3;
698
}
699
700
if (self->timer <= 0) {
701
self->timer = RSDK.Rand(0x30, 0x60);
702
self->targetVelocity.x = self->targetPos.x + RSDK.Rand(-0x600000, 0x600000);
703
self->targetVelocity.y = self->targetPos.y + RSDK.Rand(-0x200000, 0x200000);
704
705
int32 angle = RSDK.ATan2((self->targetVelocity.x - self->targetPos.x) >> 16, (self->targetVelocity.y - self->targetPos.y) >> 16);
706
self->targetVelocity.x = RSDK.Cos256(angle) << 9;
707
self->targetVelocity.y = RSDK.Sin256(angle) << 9;
708
PhantomEgg_HandleNextAttack();
709
}
710
else {
711
self->timer--;
712
}
713
714
self->velocity.y += ((self->targetVelocity.y - self->velocity.y) >> 4);
715
self->position.y += self->velocity.y;
716
717
if (self->position.y < PhantomEgg->boundsT && self->targetVelocity.y < 0)
718
self->targetVelocity.y = -self->targetVelocity.y;
719
720
if (self->position.y > PhantomEgg->boundsB) {
721
if (self->targetVelocity.y > 0)
722
self->targetVelocity.y = -self->targetVelocity.y;
723
}
724
725
PhantomEgg_CheckPlayerCollisions();
726
}
727
728
void PhantomEgg_State_Attack_Jumped(void)
729
{
730
RSDK_THIS(PhantomEgg);
731
732
PhantomEgg_HandleAnimations();
733
734
self->velocity.y += 0x3800;
735
self->position.x += self->velocity.x;
736
self->position.y += self->velocity.y;
737
738
Hitbox *hitbox = RSDK.GetHitbox(&self->legAnimator, 0);
739
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, hitbox->bottom << 16, true)) {
740
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 4, &self->armLAnimator, true, 0);
741
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 4, &self->armRAnimator, true, 0);
742
743
self->state = PhantomEgg_State_Attack_JumpLand;
744
RSDK.PlaySfx(PhantomEgg->sfxLand, false, 255);
745
}
746
747
if (self->velocity.x < 0 && self->position.x < PhantomEgg->boundsL)
748
self->velocity.x = -self->velocity.x;
749
750
if (self->velocity.x > 0 && self->position.x > PhantomEgg->boundsR)
751
self->velocity.x = -self->velocity.x;
752
753
PhantomEgg_CheckPlayerCollisions();
754
}
755
756
void PhantomEgg_State_Attack_JumpLand(void)
757
{
758
RSDK_THIS(PhantomEgg);
759
760
RSDK.ProcessAnimation(&self->armLAnimator);
761
RSDK.ProcessAnimation(&self->armRAnimator);
762
RSDK.ProcessAnimation(&self->legAnimator);
763
764
PhantomEgg_HandleAnimations();
765
766
Hitbox *hitbox = RSDK.GetHitbox(&self->legAnimator, 0);
767
RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, hitbox->bottom << 16, 16);
768
769
if (self->legAnimator.frameID == 5)
770
PhantomEgg_HandleNextAttack();
771
772
if (self->legAnimator.frameID == 6) {
773
self->velocity.x = RSDK.Rand(0, 256) > 128 ? -0x20000 : 0x20000;
774
self->velocity.y = -0x80000;
775
self->state = PhantomEgg_State_Attack_JumpAttack;
776
777
RSDK.PlaySfx(PhantomEgg->sfxJump, false, 255);
778
}
779
780
PhantomEgg_CheckPlayerCollisions();
781
}
782
783
void PhantomEgg_State_Attack_JumpAttack(void)
784
{
785
RSDK_THIS(PhantomEgg);
786
787
RSDK.ProcessAnimation(&self->armLAnimator);
788
RSDK.ProcessAnimation(&self->armRAnimator);
789
RSDK.ProcessAnimation(&self->legAnimator);
790
791
PhantomEgg_HandleAnimations();
792
793
if (self->velocity.x < 0 && self->position.x < PhantomEgg->boundsL)
794
self->velocity.x = -self->velocity.x;
795
796
if (self->velocity.x > 0 && self->position.x > PhantomEgg->boundsR)
797
self->velocity.x = -self->velocity.x;
798
799
self->velocity.y += 0x3800;
800
self->position.x += self->velocity.x;
801
self->position.y += self->velocity.y;
802
803
if (self->legAnimator.frameID == self->legAnimator.frameCount - 1) {
804
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 0);
805
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);
806
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armRAnimator, true, 0);
807
808
--self->remainingJumps;
809
if (self->remainingJumps > 0)
810
self->state = PhantomEgg_State_Attack_Jumped;
811
else
812
self->state = PhantomEgg_State_MoveAround;
813
}
814
815
PhantomEgg_CheckPlayerCollisions();
816
}
817
818
void PhantomEgg_State_Attack_CableShock(void)
819
{
820
RSDK_THIS(PhantomEgg);
821
822
PhantomEgg_HandleAnimations();
823
824
if (self->timer < 160 && !(self->timer & 0xF))
825
RSDK.PlaySfx(PhantomEgg->sfxShock, false, 255);
826
827
if (++self->timer == 30) {
828
foreach_active(TMZCable, cable)
829
{
830
if (cable->state != TMZCable_State_Destroyed) {
831
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 10, &cable->animator, true, 0);
832
cable->state = TMZCable_State_Charge;
833
}
834
}
835
836
foreach_active(PhantomShield, shield)
837
{
838
RSDK.SetSpriteAnimation(PhantomShield->aniFrames, 2, &shield->animator, true, 0);
839
shield->state = PhantomShield_State_Disappear;
840
}
841
}
842
843
if (self->timer == 4 * self->health + 160)
844
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 14, &self->rubyAnimator, true, 0);
845
846
if ((self->rubyAnimator.animationID == 14 && self->rubyAnimator.frameID == self->rubyAnimator.frameCount - 2) || self->timer > 288) {
847
self->timer = 0;
848
849
CREATE_ENTITY(PhantomShield, self, self->position.x, self->position.y);
850
if (self->legAnimator.frameID > 0) {
851
self->legAnimator.frameID = 6;
852
self->legAnimator.timer = 0;
853
self->state = PhantomEgg_State_Attack_JumpLand;
854
}
855
else {
856
self->state = PhantomEgg_State_MoveAround;
857
}
858
}
859
860
PhantomEgg_CheckPlayerCollisions();
861
}
862
863
void PhantomEgg_State_Attack_PrepareWarp(void)
864
{
865
RSDK_THIS(PhantomEgg);
866
867
if (!self->timer) {
868
foreach_active(PhantomShield, shield)
869
{
870
RSDK.SetSpriteAnimation(PhantomShield->aniFrames, 2, &shield->animator, true, 0);
871
shield->state = PhantomShield_State_Disappear;
872
}
873
}
874
875
if (self->palBlendPercent < 112)
876
self->palBlendPercent += 4;
877
878
RSDK.SetLimitedFade(0, 1, 2, self->palBlendPercent, 128, 256);
879
self->position.x += (PhantomEgg->boundsM - self->position.x) >> 4;
880
self->position.y += ((PhantomEgg->boundsB - self->position.y) - 0x400000) >> 4;
881
882
if (++self->timer == 60) {
883
self->timer = 0;
884
self->position.x = PhantomEgg->boundsM;
885
self->position.y = PhantomEgg->boundsB - 0x400000;
886
887
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 5, &self->armLAnimator, true, 0);
888
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 6, &self->armRAnimator, true, 0);
889
self->state = PhantomEgg_State_Attack_GrabPlayers;
890
}
891
}
892
893
void PhantomEgg_State_Attack_GrabPlayers(void)
894
{
895
RSDK_THIS(PhantomEgg);
896
897
RSDK.ProcessAnimation(&self->armLAnimator);
898
RSDK.ProcessAnimation(&self->armRAnimator);
899
RSDK.SetLimitedFade(0, 1, 2, self->palBlendPercent, 128, 256);
900
901
if (!self->timer) {
902
CREATE_ENTITY(PhantomHand, self, self->position.x - 0x400000, self->position.y)->velocity.x = -0x8000;
903
904
EntityPhantomHand *hand = CREATE_ENTITY(PhantomHand, self, self->position.x + 0x400000, self->position.y);
905
hand->direction = FLIP_X;
906
hand->velocity.x = 0x8000;
907
}
908
909
++self->timer;
910
}
911
912
void PhantomEgg_State_Attack_HandleWarp(void)
913
{
914
RSDK_THIS(PhantomEgg);
915
916
if (PhantomEgg->endScanline == 96) {
917
EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
918
fxFade->speedIn = 16;
919
fxFade->speedOut = 16;
920
}
921
922
if (self->palBlendPercent > 0)
923
self->palBlendPercent -= 4;
924
925
RSDK.ProcessAnimation(&self->armLAnimator);
926
RSDK.ProcessAnimation(&self->armRAnimator);
927
RSDK.SetLimitedFade(0, 1, 2, self->palBlendPercent, 128, 256);
928
929
if (PhantomEgg->endScanline <= 0) {
930
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, true, 0);
931
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armRAnimator, true, 0);
932
933
if (self->visible) {
934
self->state = PhantomEgg_State_Attack_WarpAway;
935
PhantomEgg_HandlePhantomWarp(self->phantomID);
936
self->phantomID = (self->phantomID + 1) & 3;
937
}
938
else {
939
self->state = PhantomEgg_State_Attack_WarpReturn;
940
PhantomEgg_HandleReturnWarp();
941
self->visible = true;
942
}
943
}
944
else {
945
PhantomEgg->endScanline -= 4;
946
}
947
}
948
949
void PhantomEgg_State_Attack_WarpAway(void)
950
{
951
RSDK_THIS(PhantomEgg);
952
953
if (PhantomEgg->endScanline >= ScreenInfo->size.y) {
954
RSDK.GetTileLayer(Zone->fgLayer[0])->scanlineCallback = StateMachine_None;
955
RSDK.GetTileLayer(Zone->fgLayer[1])->scanlineCallback = StateMachine_None;
956
957
self->timer = 0;
958
self->visible = false;
959
self->state = StateMachine_None;
960
}
961
else {
962
if (self->timer > 8)
963
PhantomEgg->endScanline += 2;
964
965
if (self->timer == 60) {
966
foreach_active(PhantomHand, hand) { hand->state = PhantomHand_State_BreakApart; }
967
}
968
++self->timer;
969
}
970
}
971
972
void PhantomEgg_State_Attack_WarpReturn(void)
973
{
974
RSDK_THIS(PhantomEgg);
975
976
if (PhantomEgg->endScanline >= ScreenInfo->size.y) {
977
RSDK.GetTileLayer(Zone->fgLayer[0])->scanlineCallback = StateMachine_None;
978
RSDK.GetTileLayer(Zone->fgLayer[1])->scanlineCallback = StateMachine_None;
979
980
CREATE_ENTITY(PhantomShield, self, self->position.x, self->position.y);
981
self->timer = 0;
982
self->state = PhantomEgg_State_MoveAround;
983
}
984
else {
985
if (self->timer > 8)
986
PhantomEgg->endScanline += 2;
987
988
if (self->timer == 60) {
989
foreach_active(PhantomHand, hand) { hand->state = PhantomHand_State_BreakApart; }
990
}
991
++self->timer;
992
}
993
}
994
995
void PhantomEgg_State_Destroyed(void)
996
{
997
RSDK_THIS(PhantomEgg);
998
999
self->position.x = self->targetPos.x + RSDK.Rand(-0x20000, 0x20000);
1000
self->position.y = self->targetPos.y + RSDK.Rand(-0x20000, 0x20000);
1001
1002
if (!RSDK.GetEntityCount(TMZCable->classID, true)) {
1003
int32 id = 0;
1004
foreach_active(PhantomMissile, missile)
1005
{
1006
switch (id++) {
1007
case 0:
1008
missile->velocity.x = -0x20000;
1009
missile->velocity.y = -0x20000;
1010
missile->groundVel = 16;
1011
break;
1012
1013
case 1:
1014
missile->velocity.x = -0x10000;
1015
missile->velocity.y = -0x40000;
1016
missile->groundVel = 8;
1017
break;
1018
1019
case 2:
1020
missile->velocity.x = 0x10000;
1021
missile->velocity.y = -0x40000;
1022
missile->groundVel = -8;
1023
break;
1024
1025
case 3:
1026
missile->velocity.x = 0x20000;
1027
missile->velocity.y = -0x20000;
1028
missile->groundVel = -16;
1029
break;
1030
1031
default: break;
1032
}
1033
1034
missile->state = PhantomMissile_State_Destroyed;
1035
}
1036
1037
foreach_active(PhantomShield, shield)
1038
{
1039
RSDK.SetSpriteAnimation(PhantomShield->aniFrames, 2, &shield->animator, true, 0);
1040
shield->state = PhantomShield_State_Disappear;
1041
}
1042
1043
CREATE_ENTITY(TMZ2Outro, NULL, self->position.x, self->position.y);
1044
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, true, 1);
1045
1046
for (int32 i = 0; i < 0x100; ++i) RSDK.SetPaletteEntry(7, i, RSDK.GetPaletteEntry(1, i) & 0xFF0000);
1047
1048
self->state = PhantomEgg_State_Exploding;
1049
}
1050
}
1051
1052
void PhantomEgg_State_Exploding(void)
1053
{
1054
RSDK_THIS(PhantomEgg);
1055
1056
self->targetPos.x += ((PhantomEgg->boundsM - self->targetPos.x) >> 5);
1057
self->targetPos.y += ((PhantomEgg->boundsB - self->targetPos.y - 0x400000) >> 5);
1058
1059
self->position.x = self->targetPos.x + RSDK.Rand(-0x20000, 0x20000);
1060
self->position.y = self->targetPos.y + RSDK.Rand(-0x20000, 0x20000);
1061
1062
PhantomEgg_Explode(&self->hitbox);
1063
1064
if (++self->timer == 152) {
1065
self->timer = 0;
1066
self->position.x = PhantomEgg->boundsM;
1067
self->position.y = PhantomEgg->boundsB - 0x400000;
1068
1069
bool32 goodEnd = (CHECK_CHARACTER_ID(ID_SONIC, 1) || (CHECK_CHARACTER_ID(ID_KNUCKLES, 1) && CHECK_CHARACTER_ID(ID_KNUCKLES, 2)))
1070
&& SaveGame_AllChaosEmeralds();
1071
1072
#if MANIA_USE_PLUS
1073
if (SceneInfo->filter & FILTER_ENCORE)
1074
goodEnd = false; // no ERZ for encore modes
1075
#endif
1076
if (goodEnd) {
1077
self->state = PhantomEgg_State_StartGoodEnd;
1078
Music_FadeOut(0.0125);
1079
}
1080
else {
1081
self->state = PhantomEgg_State_StartBadEnd;
1082
}
1083
}
1084
}
1085
1086
void PhantomEgg_State_StartBadEnd(void)
1087
{
1088
RSDK_THIS(PhantomEgg);
1089
1090
if (++self->timer == 30) {
1091
self->timer = 0;
1092
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 1, &self->crackAnimator, false, 0);
1093
self->stateDraw = PhantomEgg_Draw_Cracked;
1094
1095
RSDK.PlaySfx(PhantomEgg->sfxRocketJet, false, 255);
1096
1097
EntityPhantomRuby *ruby = CREATE_ENTITY(PhantomRuby, NULL, self->position.x, self->position.y + 0x100000);
1098
ruby->state = PhantomRuby_State_MoveRotateGravity_CheckGround;
1099
ruby->velocity.x = -0x10000;
1100
ruby->velocity.y = -0x20000;
1101
self->state = PhantomEgg_State_CrackOpen;
1102
}
1103
}
1104
1105
void PhantomEgg_State_CrackOpen(void)
1106
{
1107
RSDK_THIS(PhantomEgg);
1108
1109
RSDK.ProcessAnimation(&self->crackAnimator);
1110
1111
if (++self->timer == 60) {
1112
EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
1113
fxFade->speedIn = 512;
1114
fxFade->wait = 16;
1115
fxFade->speedOut = 16;
1116
RSDK.PlaySfx(PhantomEgg->sfxExplosion3, false, 255);
1117
}
1118
1119
if (self->timer == 64) {
1120
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 2, &self->crackAnimator, false, 0);
1121
1122
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1123
self->stateDraw = StateMachine_None;
1124
1125
EntityEggman *eggman = CREATE_ENTITY(Eggman, NULL, self->position.x, self->position.y + 0x100000);
1126
RSDK.SetSpriteAnimation(Eggman->aniFrames, 7, &eggman->animator, true, 0);
1127
eggman->onGround = false;
1128
eggman->state = Eggman_State_FallAndCollide;
1129
}
1130
1131
foreach_active(Eggman, eggman)
1132
{
1133
eggman->position.x = self->position.x;
1134
eggman->position.y = self->position.y;
1135
eggman->velocity.y = 0;
1136
}
1137
1138
if (self->timer == 96) {
1139
self->timer = 0;
1140
self->state = PhantomEgg_State_CrackedExploding;
1141
}
1142
}
1143
1144
void PhantomEgg_State_CrackedExploding(void)
1145
{
1146
RSDK_THIS(PhantomEgg);
1147
1148
if (!(Zone->timer & 0xF)) {
1149
int32 x = self->position.x + RSDK.Rand(-0x380000, -0x180000);
1150
int32 y = self->position.y + RSDK.Rand(-0x300000, -0x100000);
1151
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSSPUFF), x, y);
1152
explosion->drawGroup = Zone->objectDrawGroup[1];
1153
}
1154
1155
if ((Zone->timer & 0xF) == 8) {
1156
int32 x = self->position.x + RSDK.Rand(0x180000, 0x380000);
1157
int32 y = self->position.y + RSDK.Rand(-0x300000, -0x100000);
1158
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSSPUFF), x, y);
1159
explosion->drawGroup = Zone->objectDrawGroup[1];
1160
}
1161
1162
if (++self->timer == 120)
1163
self->state = StateMachine_None;
1164
}
1165
1166
void PhantomEgg_State_StartGoodEnd(void)
1167
{
1168
RSDK_THIS(PhantomEgg);
1169
1170
if (self->timer < 256 && !(Zone->timer % 3)) {
1171
RSDK.PlaySfx(PhantomEgg->sfxExplosion2, false, 255);
1172
1173
if (Zone->timer & 8) {
1174
int32 x = self->position.x + RSDK.Rand(-0x800000, 0x800000);
1175
int32 y = self->position.y + (RSDK.Rand(self->hitbox.top, self->hitbox.bottom + 64) << 16);
1176
CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y)->drawGroup = Zone->objectDrawGroup[1];
1177
}
1178
}
1179
1180
if (++self->timer == 120) {
1181
CREATE_ENTITY(FXRuby, NULL, self->position.x, self->position.y + 0x100000)->radiusSpeed = 3;
1182
PhantomRuby_PlaySfx(RUBYSFX_REDCUBE);
1183
}
1184
1185
if (self->timer == 320) {
1186
EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
1187
fxFade->speedIn = 16;
1188
fxFade->wait = 32;
1189
fxFade->fadeOutBlack = 1;
1190
fxFade->speedOut = 16;
1191
PhantomRuby_PlaySfx(RUBYSFX_ATTACK1);
1192
}
1193
1194
if (self->timer >= 512) {
1195
if (globals->saveSlotID != NO_SAVE_SLOT) {
1196
if (self->timer == 512) {
1197
if (Zone_IsZoneLastAct())
1198
GameProgress_MarkZoneCompleted(Zone_GetZoneID());
1199
1200
SaveGame_SaveFile(PhantomEgg_SaveGameCB);
1201
UIWaitSpinner_StartWait();
1202
}
1203
1204
if (PhantomEgg->savedGameProgress)
1205
UIWaitSpinner_FinishWait();
1206
}
1207
1208
if (globals->saveSlotID == NO_SAVE_SLOT || PhantomEgg->savedGameProgress) {
1209
++SceneInfo->listPos;
1210
RSDK.LoadScene();
1211
self->state = 0;
1212
}
1213
}
1214
}
1215
1216
#if MANIA_USE_PLUS
1217
void PhantomEgg_SaveGameCB(bool32 success) { PhantomEgg->savedGameProgress = true; }
1218
#else
1219
void PhantomEgg_SaveGameCB(void) { PhantomEgg->savedGameProgress = true; }
1220
#endif
1221
1222
#if GAME_INCLUDE_EDITOR
1223
void PhantomEgg_EditorDraw(void)
1224
{
1225
RSDK_THIS(PhantomEgg);
1226
1227
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 0, &self->coreAnimator, false, 0);
1228
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 17, &self->eggmanAnimator, false, 0);
1229
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 3, &self->armLAnimator, false, 0);
1230
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 8, &self->legAnimator, false, 0);
1231
RSDK.SetSpriteAnimation(PhantomEgg->aniFrames, 13, &self->rubyAnimator, false, 0);
1232
1233
PhantomEgg_Draw_Normal();
1234
1235
if (showGizmos()) {
1236
RSDK_DRAWING_OVERLAY(true);
1237
1238
DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER, -SCREEN_YSIZE, WIDE_SCR_XCENTER, 0, 1 | 2 | 4 | 0, 0x00C0F0);
1239
1240
RSDK_DRAWING_OVERLAY(false);
1241
}
1242
}
1243
1244
void PhantomEgg_EditorLoad(void)
1245
{
1246
PhantomEgg->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomEgg.bin", SCOPE_STAGE);
1247
1248
RSDK_ACTIVE_VAR(PhantomEgg, type);
1249
RSDK_ENUM_VAR("Eggman", PHANTOMEGG_EGGMAN);
1250
}
1251
#endif
1252
1253
void PhantomEgg_Serialize(void) { RSDK_EDITABLE_VAR(PhantomEgg, VAR_ENUM, type); }
1254
1255