Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/SSZ/MetalSonic.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: MetalSonic Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectMetalSonic *MetalSonic;
11
12
void MetalSonic_Update(void)
13
{
14
RSDK_THIS(MetalSonic);
15
16
if (self->invincibilityTimer)
17
self->invincibilityTimer--;
18
19
RSDK.ProcessAnimation(&self->metalSonicAnimator);
20
RSDK.ProcessAnimation(&self->boosterAnimator);
21
22
StateMachine_Run(self->state);
23
24
if (MetalSonic->invincibilityTimerPanel > 0)
25
MetalSonic->invincibilityTimerPanel--;
26
27
#if MANIA_USE_PLUS
28
foreach_active(StarPost, post) { post->bonusStageID = 0; }
29
#endif
30
}
31
32
void MetalSonic_LateUpdate(void) {}
33
34
void MetalSonic_StaticUpdate(void) {}
35
36
void MetalSonic_Draw(void)
37
{
38
RSDK_THIS(MetalSonic);
39
40
Vector2 drawPos;
41
drawPos.x = self->position.x;
42
drawPos.y = self->position.y;
43
if (self->position.x < 0x8000000)
44
drawPos.x += 0xE000000;
45
else
46
drawPos.x -= 0xE000000;
47
48
if (self->invincibilityTimer & 1) {
49
RSDK.CopyPalette(2, 240, 0, 240, 8);
50
51
RSDK.DrawSprite(&self->boosterAnimator, &drawPos, false);
52
RSDK.DrawSprite(&self->metalSonicAnimator, &drawPos, false);
53
RSDK.DrawSprite(&self->boosterAnimator, NULL, false);
54
RSDK.DrawSprite(&self->metalSonicAnimator, NULL, false);
55
56
RSDK.CopyPalette(1, 240, 0, 240, 8);
57
}
58
else {
59
RSDK.DrawSprite(&self->boosterAnimator, &drawPos, false);
60
RSDK.DrawSprite(&self->metalSonicAnimator, &drawPos, false);
61
RSDK.DrawSprite(&self->boosterAnimator, NULL, false);
62
RSDK.DrawSprite(&self->metalSonicAnimator, NULL, false);
63
}
64
}
65
66
void MetalSonic_Create(void *data)
67
{
68
RSDK_THIS(MetalSonic);
69
70
if (!SceneInfo->inEditor) {
71
if (globals->gameMode == MODE_TIMEATTACK) {
72
destroyEntity(self);
73
}
74
else {
75
Zone->autoScrollSpeed = 0;
76
self->active = ACTIVE_BOUNDS;
77
self->drawFX = FX_FLIP | FX_ROTATE;
78
self->visible = false;
79
self->updateRange.x = 0x800000;
80
self->updateRange.y = 0x800000;
81
self->tileCollisions = TILECOLLISION_DOWN;
82
self->collisionLayers = Zone->collisionLayers;
83
84
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, 0, &self->metalSonicAnimator, true, 0);
85
self->drawGroup = Zone->objectDrawGroup[0];
86
self->state = MetalSonic_State_SetupArena;
87
}
88
}
89
}
90
91
void MetalSonic_StageLoad(void)
92
{
93
MetalSonic->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/MetalSonic.bin", SCOPE_STAGE);
94
95
MetalSonic->hitboxHover.left = -8;
96
MetalSonic->hitboxHover.top = -8;
97
MetalSonic->hitboxHover.right = 8;
98
MetalSonic->hitboxHover.bottom = 8;
99
100
MetalSonic->hitboxDash.left = -16;
101
MetalSonic->hitboxDash.top = -8;
102
MetalSonic->hitboxDash.right = 16;
103
MetalSonic->hitboxDash.bottom = 8;
104
105
MetalSonic->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
106
MetalSonic->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");
107
MetalSonic->sfxExplosion3 = RSDK.GetSfx("Stage/Explosion3.wav");
108
MetalSonic->sfxRumble = RSDK.GetSfx("Stage/Rumble.wav");
109
MetalSonic->sfxJump2 = RSDK.GetSfx("Stage/Jump2.wav");
110
MetalSonic->sfxSpecialRing = RSDK.GetSfx("Global/SpecialRing.wav");
111
MetalSonic->sfxMSElecPulse = RSDK.GetSfx("SSZ2/MSElecPulse.wav");
112
MetalSonic->sfxMSBall = RSDK.GetSfx("SSZ2/MSBall.wav");
113
MetalSonic->sfxMSFireball = RSDK.GetSfx("SSZ2/MSFireball.wav");
114
MetalSonic->sfxBeep3 = RSDK.GetSfx("Stage/Beep3.wav");
115
MetalSonic->sfxBeep4 = RSDK.GetSfx("Stage/Beep4.wav");
116
MetalSonic->sfxRockemSockem = RSDK.GetSfx("Stage/RockemSockem.wav");
117
MetalSonic->sfxMSShoot = RSDK.GetSfx("SSZ2/MSShoot.wav");
118
MetalSonic->sfxMSChargeFire = RSDK.GetSfx("SSZ2/MSChargeFire.wav");
119
#if MANIA_USE_PLUS
120
MetalSonic->sfxMSTransform = RSDK.GetSfx("SSZ2/MSTransform.wav");
121
MetalSonic->sfxTransform2 = RSDK.GetSfx("Stage/Transform2.wav");
122
#endif
123
}
124
125
void MetalSonic_HandleStageWrap(void)
126
{
127
RSDK_THIS(MetalSonic);
128
129
EntityPlayer *player = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
130
EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);
131
132
#if !MANIA_USE_PLUS
133
EntityMetalSonic *marker = RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, MetalSonic);
134
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
135
if (player->position.y < marker->position.y && !player->collisionPlane) {
136
Zone->cameraBoundsT[0] = (marker->position.y >> 16) + 16 - ScreenInfo->size.y;
137
Zone->cameraBoundsB[0] = (marker->position.y >> 16) + 16;
138
Zone->deathBoundary[0] = (marker->position.y >> 16) + 16;
139
marker->position.y = -0x200000;
140
141
for (int32 i = 0; i < PLAYER_COUNT; ++i) {
142
Zone->cameraBoundsL[i] = (wall->position.x >> 16) - 95;
143
Zone->cameraBoundsR[i] = (wall->position.x >> 16) + 392;
144
Zone->playerBoundActiveL[i] = true;
145
Zone->playerBoundActiveR[i] = true;
146
}
147
148
self->timer = 59;
149
self->targetPos.x = 0x9100000;
150
self->targetPos.y = 0x23400000;
151
152
int32 anim = MS_ANI_IDLE;
153
if (self->position.x >= 0x9100000) {
154
if (self->direction != FLIP_X) {
155
self->direction = FLIP_X;
156
anim = MS_ANI_HOVERTURN;
157
}
158
else {
159
anim = MS_ANI_HOVER;
160
}
161
}
162
else if (self->direction) {
163
self->direction = FLIP_NONE;
164
anim = MS_ANI_HOVERTURN;
165
}
166
else {
167
anim = MS_ANI_HOVER;
168
}
169
170
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, anim, &self->metalSonicAnimator, true, 0);
171
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BOOSTER_WEAK, &self->boosterAnimator, false, 0);
172
self->state = MetalSonic_State_SetupSpikeWall;
173
}
174
#endif
175
176
if (camera->position.x < 0xF000000) {
177
if (camera->position.x <= 0x1000000) {
178
player->position.x += 0xE000000;
179
self->position.x += 0xE000000;
180
camera->position.x += 0xE000000;
181
camera->center.x += 0xE00;
182
MetalSonic_ProcessBGParallax(-0xE0000);
183
184
#if MANIA_USE_PLUS
185
if (self->classID == GigaMetal->classID) {
186
camera->boundsL += 0xE00;
187
camera->boundsR += 0xE00;
188
Zone->cameraBoundsL[0] += 0xE00;
189
Zone->cameraBoundsR[0] += 0xE00;
190
Zone->playerBoundsL[0] += 0xE000000;
191
Zone->playerBoundsR[0] += 0xE000000;
192
}
193
#endif
194
195
foreach_active(MSOrb, orb) { orb->position.x += 0xE000000; }
196
197
#if MANIA_USE_PLUS
198
foreach_active(MSBomb, bomb) { bomb->position.x += 0xE000000; }
199
#endif
200
201
foreach_active(Ring, ring) { ring->position.x += 0xE000000; }
202
foreach_active(Spring, spring) { spring->position.x += 0xE000000; }
203
foreach_active(Spikes, spikes) { spikes->position.x += 0xE000000; }
204
foreach_active(Firework, firework) { firework->position.x += 0xE000000; }
205
foreach_active(EggPrison, prison) { prison->position.x += 0xE000000; }
206
207
foreach_active(SSZSpotlight, spotlight)
208
{
209
spotlight->position.x += 0xE000000;
210
spotlight->originPos.x += 0xE000000;
211
}
212
213
foreach_active(ImageTrail, trail)
214
{
215
trail->position.x += 0xE000000;
216
trail->currentPos.x += 0xE000000;
217
for (int32 i = 0; i < IMAGETRAIL_TRACK_COUNT; ++i) trail->statePos[i].x += 0xE000000;
218
}
219
220
foreach_active(Platform, platform)
221
{
222
platform->position.x += 0xE000000;
223
platform->centerPos.x += 0xE000000;
224
platform->drawPos.x += 0xE000000;
225
}
226
227
for (int32 i = 1; i < Player->playerCount; ++i) {
228
RSDK_GET_ENTITY(i, Player)->position.x += 0xE000000;
229
}
230
}
231
}
232
else {
233
player->position.x -= 0xE000000;
234
self->position.x -= 0xE000000;
235
camera->position.x -= 0xE000000;
236
camera->center.x -= 0xE00;
237
MetalSonic_ProcessBGParallax(0xE0000);
238
239
#if MANIA_USE_PLUS
240
if (self->classID == GigaMetal->classID) {
241
camera->boundsL -= 0xE00;
242
camera->boundsR -= 0xE00;
243
Zone->cameraBoundsL[0] -= 0xE00;
244
Zone->cameraBoundsR[0] -= 0xE00;
245
Zone->playerBoundsL[0] -= 0xE000000;
246
Zone->playerBoundsR[0] -= 0xE000000;
247
}
248
#endif
249
250
foreach_active(MSOrb, orb) { orb->position.x -= 0xE000000; }
251
252
#if MANIA_USE_PLUS
253
foreach_active(MSBomb, bomb) { bomb->position.x -= 0xE000000; }
254
255
#endif
256
foreach_active(Ring, ring) { ring->position.x -= 0xE000000; }
257
foreach_active(Spring, spring) { spring->position.x -= 0xE000000; }
258
foreach_active(Spikes, spikes) { spikes->position.x -= 0xE000000; }
259
foreach_active(Firework, firework) { firework->position.x -= 0xE000000; }
260
foreach_active(EggPrison, prison) { prison->position.x -= 0xE000000; }
261
262
foreach_active(SSZSpotlight, spotlight)
263
{
264
spotlight->position.x -= 0xE000000;
265
spotlight->originPos.x -= 0xE000000;
266
}
267
268
foreach_active(ImageTrail, trail)
269
{
270
trail->position.x -= 0xE000000;
271
trail->currentPos.x -= 0xE000000;
272
for (int32 i = 0; i < IMAGETRAIL_TRACK_COUNT; ++i) trail->statePos[i].x -= 0xE000000;
273
}
274
275
foreach_active(Platform, platform)
276
{
277
platform->position.x -= 0xE000000;
278
platform->centerPos.x -= 0xE000000;
279
platform->drawPos.x -= 0xE000000;
280
}
281
282
for (int32 i = 1; i < Player->playerCount; ++i) {
283
RSDK_GET_ENTITY(i, Player)->position.x -= 0xE000000;
284
}
285
}
286
}
287
288
void MetalSonic_ProcessBGParallax(int32 mult)
289
{
290
for (int32 i = 0; i < 2; ++i) {
291
TileLayer *background = RSDK.GetTileLayer(i);
292
for (int32 s = 0; s < background->scrollInfoCount; ++s) {
293
background->scrollInfo[s].scrollPos += mult * background->scrollInfo[s].parallaxFactor;
294
}
295
}
296
297
TileLayer *tower = RSDK.GetTileLayer(2);
298
tower->scrollPos += mult * tower->parallaxFactor;
299
300
foreach_active(EggTower, eggTower) { eggTower->rotationX += (mult >> 8); }
301
}
302
303
void MetalSonic_HandleVelocity(void)
304
{
305
RSDK_THIS(MetalSonic);
306
307
int32 angle = RSDK.ATan2((self->targetPos.x - self->position.x) >> 16, (self->targetPos.y - self->position.y) >> 16);
308
int32 ang = 2 * angle - self->angle;
309
310
if (abs(ang) >= abs(ang - 0x200)) {
311
if (abs(ang - 0x200) < abs(ang + 0x200))
312
angle = ang - 0x200;
313
else
314
angle = ang + 0x200;
315
}
316
else {
317
if (abs(ang) < abs(ang + 0x200))
318
angle = ang;
319
else
320
angle = ang + 0x200;
321
}
322
323
self->angle = (angle / 18 + self->angle) & 0x1FF;
324
325
self->velocity.x = RSDK.Cos512(self->angle) << 9;
326
self->velocity.y = RSDK.Sin512(self->angle) << 9;
327
}
328
329
void MetalSonic_HandleAnimDir(void)
330
{
331
RSDK_THIS(MetalSonic);
332
333
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
334
335
if (self->metalSonicAnimator.animationID == MS_ANI_HOVERTURN) {
336
if (self->metalSonicAnimator.frameID == self->metalSonicAnimator.frameCount - 1)
337
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, true, 0);
338
}
339
else if (self->position.x <= player1->position.x) {
340
if (self->direction == FLIP_X) {
341
self->direction = FLIP_NONE;
342
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
343
}
344
}
345
else if (self->direction == FLIP_NONE) {
346
self->direction = FLIP_X;
347
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
348
}
349
}
350
351
#if !MANIA_USE_PLUS
352
void MetalSonic_CheckPlayerCollisions(void)
353
{
354
RSDK_THIS(MetalSonic);
355
356
foreach_active(Player, player)
357
{
358
if (!self->invincibilityTimer) {
359
Hitbox *hitbox = self->metalSonicAnimator.animationID == MS_ANI_FLY ? &MetalSonic->hitboxDash : &MetalSonic->hitboxHover;
360
361
if (Player_CheckBadnikTouch(player, self, hitbox) && Player_CheckBossHit(player, self)) {
362
if (player->velocity.x < 0)
363
player->velocity.x >>= 2;
364
365
player->velocity.x >>= 1;
366
player->velocity.y >>= 1;
367
player->velocity.x += self->velocity.x;
368
player->velocity.y += self->velocity.y;
369
player->groundVel = player->velocity.x;
370
371
MetalSonic_Hit();
372
}
373
}
374
}
375
}
376
377
void MetalSonic_Hit(void)
378
{
379
RSDK_THIS(MetalSonic);
380
381
if (!--self->health) {
382
self->timer = 180;
383
SceneInfo->timeEnabled = false;
384
self->velocity.y = -0x1800;
385
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
386
387
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_DEFEATED, &self->metalSonicAnimator, false, 0);
388
RSDK.SetSpriteAnimation(-1, MS_ANI_BOOSTER_INTRO, &self->boosterAnimator, false, 0);
389
self->drawFX |= FX_ROTATE;
390
self->state = MetalSonic_State_Explode;
391
}
392
else {
393
self->invincibilityTimer = 48;
394
RSDK.PlaySfx(MetalSonic->sfxHit, false, 0xFF);
395
}
396
}
397
398
void MetalSonic_Explode(void)
399
{
400
RSDK_THIS(MetalSonic);
401
402
if (!(Zone->timer & 7)) {
403
RSDK.PlaySfx(MetalSonic->sfxExplosion2, false, 0xFF);
404
405
if (!(Zone->timer & 0xF)) {
406
int32 x = self->position.x + (RSDK.Rand(-19, 20) << 16);
407
int32 y = self->position.y + (RSDK.Rand(-24, 25) << 16);
408
409
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);
410
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
411
}
412
}
413
}
414
#endif
415
416
void MetalSonic_State_SetupArena(void)
417
{
418
RSDK_THIS(MetalSonic);
419
420
if (++self->timer >= 8) {
421
self->timer = 0;
422
423
Zone->playerBoundActiveL[0] = true;
424
Zone->playerBoundActiveR[0] = true;
425
426
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x;
427
Zone->cameraBoundsR[0] = (self->position.x >> 16) + ScreenInfo->center.x;
428
Zone->cameraBoundsT[0] = (self->position.y >> 16) - ScreenInfo->size.y + 52;
429
Zone->cameraBoundsB[0] = (self->position.y >> 16) + 52;
430
431
self->state = MetalSonic_State_AwaitPlayer;
432
}
433
}
434
435
void MetalSonic_State_AwaitPlayer(void)
436
{
437
RSDK_THIS(MetalSonic);
438
439
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
440
441
if (player1->position.x > self->position.x) {
442
Music_FadeOut(0.0125);
443
self->state = MetalSonic_State_WaitForHologram;
444
}
445
}
446
447
void MetalSonic_State_WaitForHologram(void)
448
{
449
RSDK_THIS(MetalSonic);
450
451
foreach_active(MSHologram, hologram)
452
{
453
if (hologram->state == MSHologram_State_Destroyed && hologram->timer == 320) {
454
#if MANIA_USE_PLUS
455
Music_ClearMusicStack();
456
#endif
457
Music_PlayTrack(TRACK_METALSONIC);
458
RSDK.PlaySfx(MetalSonic->sfxMSFireball, false, 255);
459
460
self->position.y += 0x600000;
461
self->velocity.y = -0x80000;
462
self->visible = true;
463
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_ENTERPANEL, &self->metalSonicAnimator, false, 6);
464
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BOOSTER_INTRO, &self->boosterAnimator, false, 0);
465
466
self->direction = FLIP_X;
467
self->metalSonicAnimator.speed = 0;
468
self->active = ACTIVE_NORMAL;
469
self->state = MetalSonic_State_Appear;
470
Camera_ShakeScreen(0, 6, 6);
471
472
foreach_break;
473
}
474
}
475
}
476
477
void MetalSonic_State_Appear(void)
478
{
479
RSDK_THIS(MetalSonic);
480
481
self->velocity.y += 0x2000;
482
self->position.y += self->velocity.y;
483
484
if (self->velocity.y >= 0) {
485
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, false, 3);
486
RSDK.SetSpriteAnimation(-1, 0, &self->boosterAnimator, false, 0);
487
488
self->velocity.x = 0x18000;
489
self->onGround = false;
490
self->state = MetalSonic_State_Land;
491
}
492
}
493
494
void MetalSonic_State_Land(void)
495
{
496
RSDK_THIS(MetalSonic);
497
498
self->velocity.y += 0x2000;
499
self->outerBox = RSDK.GetHitbox(&self->metalSonicAnimator, 0);
500
self->innerBox = RSDK.GetHitbox(&self->metalSonicAnimator, 1);
501
502
RSDK.ProcessObjectMovement(self, self->outerBox, self->innerBox);
503
if (self->onGround) {
504
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_TAUNT, &self->metalSonicAnimator, false, 0);
505
self->state = MetalSonic_State_Taunt;
506
self->metalSonicAnimator.speed = 0;
507
self->direction = FLIP_NONE;
508
}
509
}
510
511
void MetalSonic_State_Taunt(void)
512
{
513
RSDK_THIS(MetalSonic);
514
515
if (++self->timer == 60) {
516
self->timer = 0;
517
self->metalSonicAnimator.speed = 1;
518
self->state = MetalSonic_State_GetReady;
519
}
520
}
521
522
void MetalSonic_State_GetReady(void)
523
{
524
RSDK_THIS(MetalSonic);
525
526
if (self->metalSonicAnimator.frameID == self->metalSonicAnimator.frameCount - 1) {
527
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_READY, &self->metalSonicAnimator, false, 0);
528
self->state = MetalSonic_State_Ready;
529
}
530
}
531
532
void MetalSonic_State_Ready(void)
533
{
534
RSDK_THIS(MetalSonic);
535
536
if (++self->timer == 60) {
537
self->timer = 0;
538
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BOOSTER_WEAK, &self->boosterAnimator, false, 0);
539
self->state = MetalSonic_State_Start;
540
541
Vector2 size;
542
RSDK.GetLayerSize(Zone->fgLayer[0], &size, true);
543
Zone->cameraBoundsR[0] = size.x;
544
Zone->cameraBoundsT[0] = 0;
545
}
546
}
547
548
void MetalSonic_State_Start(void)
549
{
550
RSDK_THIS(MetalSonic);
551
552
if (++self->timer == 90) {
553
self->timer = 0;
554
Zone->cameraBoundsL[0] = 0;
555
556
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BALLATTACK, &self->metalSonicAnimator, false, 0);
557
self->velocity.x = 0x18000;
558
self->velocity.y = -0x60000;
559
RSDK.PlaySfx(MetalSonic->sfxJump2, false, 255);
560
561
self->state = MetalSonic_State_EnterHoverMode;
562
}
563
}
564
565
void MetalSonic_State_EnterHoverMode(void)
566
{
567
RSDK_THIS(MetalSonic);
568
569
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
570
571
self->position.x += self->velocity.x;
572
self->position.y += self->velocity.y;
573
self->velocity.y += 0x3800;
574
575
if (self->velocity.y >= 0) {
576
RSDK.SetSpriteAnimation(-1, 0, &self->boosterAnimator, false, 0);
577
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BOOSTER_WEAK, &self->boosterAnimator, false, 0);
578
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, false, 6);
579
self->metalSonicAnimator.speed = 0;
580
581
self->targetPos.x = player1->position.x;
582
self->targetPos.y = player1->position.y;
583
self->velocity.x = 0;
584
self->velocity.y = 0;
585
self->attackTimer = 240;
586
self->state = MetalSonic_State_Hovering;
587
}
588
}
589
590
void MetalSonic_State_Hovering(void)
591
{
592
RSDK_THIS(MetalSonic);
593
594
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
595
596
if (--self->timer <= 0) {
597
self->timer = 60;
598
int32 angle = RSDK.Rand(0, 256);
599
int32 power = RSDK.Rand(64, 97) << 8;
600
601
self->targetPos.x = (power + (power >> 2)) * RSDK.Cos256(angle);
602
self->targetPos.y = power * RSDK.Sin256(angle);
603
}
604
605
self->targetPos.x += player1->position.x;
606
self->targetPos.y += player1->position.y;
607
608
MetalSonic_HandleVelocity();
609
MetalSonic_HandleAnimDir();
610
611
if (!self->onScreen) {
612
if (self->velocity.x <= 0) {
613
if (player1->velocity.x < self->targetVelocity.x)
614
self->targetVelocity.x = player1->velocity.x;
615
616
int32 dist = (self->targetPos.x - self->position.x) >> 5;
617
if (dist < self->targetVelocity.x)
618
self->targetVelocity.x = dist;
619
620
if (self->position.x < player1->position.x + 244) {
621
if (player1->velocity.x < 0) {
622
if (self->targetVelocity.x < 2 * player1->velocity.x) {
623
self->hoverVelocity.x = 2 * player1->velocity.x;
624
self->targetVelocity.x = 2 * player1->velocity.x;
625
}
626
}
627
}
628
}
629
else {
630
if (player1->velocity.x > self->targetVelocity.x)
631
self->targetVelocity.x = player1->velocity.x;
632
633
int32 dist = (self->targetPos.x - self->position.x) >> 5;
634
if (dist > self->targetVelocity.x)
635
self->targetVelocity.x = dist;
636
637
if (self->position.x > player1->position.x - 244) {
638
if (player1->velocity.x > 0) {
639
if (self->targetVelocity.x > 2 * player1->velocity.x) {
640
self->hoverVelocity.x = 2 * player1->velocity.x;
641
self->targetVelocity.x = 2 * player1->velocity.x;
642
}
643
}
644
}
645
}
646
647
if (self->velocity.y <= 0) {
648
if (player1->velocity.y < self->targetVelocity.y)
649
self->targetVelocity.y = player1->velocity.y;
650
651
int32 dist = (self->targetPos.y - self->position.y) >> 5;
652
if (dist < self->targetVelocity.y)
653
self->targetVelocity.y = dist;
654
655
if (self->position.y < player1->position.y + 152) {
656
if (player1->velocity.y < 0) {
657
if (self->targetVelocity.y < 2 * player1->velocity.y) {
658
self->hoverVelocity.y = 2 * player1->velocity.y;
659
self->targetVelocity.y = 2 * player1->velocity.y;
660
}
661
}
662
}
663
}
664
else {
665
if (player1->velocity.y > self->targetVelocity.y)
666
self->targetVelocity.y = player1->velocity.y;
667
668
int32 dist = (self->targetPos.y - self->position.y) >> 5;
669
if (dist > self->targetVelocity.y)
670
self->targetVelocity.y = dist;
671
672
if (self->position.y > player1->position.y - 152) {
673
if (player1->velocity.y > 0) {
674
if (self->targetVelocity.y > 2 * player1->velocity.y) {
675
self->hoverVelocity.y = 2 * player1->velocity.y;
676
self->targetVelocity.y = 2 * player1->velocity.y;
677
}
678
}
679
}
680
}
681
682
self->attackTimer--;
683
if (self->attackTimer < 4)
684
self->attackTimer = 4;
685
}
686
else {
687
self->targetVelocity.x = player1->velocity.x;
688
self->targetVelocity.y = player1->velocity.y;
689
self->attackTimer--;
690
}
691
692
if (self->attackTimer <= 0) {
693
self->attackType = RSDK.Rand(0, 6);
694
while ((1 << self->attackType) & MetalSonic->finishedAttacks) self->attackType = RSDK.Rand(0, 6);
695
696
MetalSonic->finishedAttacks |= 1 << self->attackType;
697
if (MetalSonic->finishedAttacks == 0x3F)
698
MetalSonic->finishedAttacks = 0;
699
700
self->attackType >>= 1;
701
702
switch (self->attackType) {
703
default: break;
704
705
case MS_ATTACK_DASH:
706
if (RSDK.Rand(0, 2) != 0)
707
self->targetPos.x = (ScreenInfo->size.x + 72) << 16;
708
else
709
self->targetPos.x = -0x480000;
710
self->targetPos.y = ScreenInfo->center.y << 16;
711
self->state = MetalSonic_State_PrepareAttack;
712
break;
713
714
case MS_ATTACK_ELECTRIC:
715
if (player1->velocity.x >= 0) {
716
self->targetPos.y = 0x200000;
717
self->targetPos.x = (ScreenInfo->size.x - 32) << 16;
718
}
719
else {
720
self->targetPos.x = 0x200000;
721
self->targetPos.y = 0x200000;
722
}
723
self->state = MetalSonic_State_PrepareAttack;
724
break;
725
726
case MS_ATTACK_BALL: {
727
int32 angle = (RSDK.Rand(0, 2) + 3) << 6;
728
self->targetPos.x = 0xA000 * RSDK.Cos256(angle) + player1->position.x - (ScreenInfo->position.x << 16);
729
self->targetPos.y = player1->position.y + 0x7000 * RSDK.Sin256(angle) - (ScreenInfo->position.y << 16);
730
self->state = MetalSonic_State_PrepareAttack;
731
break;
732
}
733
}
734
}
735
else {
736
self->targetPos.x -= player1->position.x;
737
self->targetPos.y -= player1->position.y;
738
739
if (self->hoverVelocity.x != self->targetVelocity.x) {
740
if (self->hoverVelocity.x <= self->targetVelocity.x) {
741
self->hoverVelocity.x += (0xC00 << (self->hoverVelocity.x < 0));
742
if (self->hoverVelocity.x > self->targetVelocity.x)
743
self->hoverVelocity.x = self->targetVelocity.x;
744
}
745
else {
746
self->hoverVelocity.x -= (0xC00 << (self->hoverVelocity.x > 0));
747
if (self->hoverVelocity.x < self->targetVelocity.x)
748
self->hoverVelocity.x = self->targetVelocity.x;
749
}
750
}
751
752
if (self->hoverVelocity.y != self->targetVelocity.y) {
753
if (self->hoverVelocity.y <= self->targetVelocity.y) {
754
self->hoverVelocity.y += (0xC00 << (self->hoverVelocity.y < 0));
755
if (self->hoverVelocity.y > self->targetVelocity.y)
756
self->hoverVelocity.y = self->targetVelocity.y;
757
}
758
else {
759
self->hoverVelocity.y -= (0xC00 << (self->hoverVelocity.y > 0));
760
761
if (self->hoverVelocity.y < self->targetVelocity.y)
762
self->hoverVelocity.y = self->targetVelocity.y;
763
}
764
}
765
766
self->position.x += self->hoverVelocity.x + (self->velocity.x >> 1) + (self->velocity.x >> 3);
767
self->position.y += self->hoverVelocity.y + (self->velocity.y >> 1) + (self->velocity.y >> 3);
768
}
769
770
MetalSonic_HandleStageWrap();
771
}
772
773
void MetalSonic_State_PrepareAttack(void)
774
{
775
RSDK_THIS(MetalSonic);
776
777
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
778
779
self->targetPos.x += ScreenInfo->position.x << 16;
780
self->targetPos.y += ScreenInfo->position.y << 16;
781
MetalSonic_HandleVelocity();
782
783
self->position.x += self->velocity.x;
784
self->position.y += self->velocity.y;
785
self->position.x += player1->velocity.x;
786
self->position.y += player1->velocity.y;
787
MetalSonic_HandleAnimDir();
788
789
int32 rx = self->position.x - self->targetPos.x;
790
int32 ry = self->position.y - self->targetPos.y;
791
self->targetPos.x = self->targetPos.x - (ScreenInfo->position.x << 16);
792
self->targetPos.y = self->targetPos.y - (ScreenInfo->position.y << 16);
793
794
if ((rx >> 16) * (rx >> 16) + (ry >> 16) * (ry >> 16) < 0x1000) {
795
self->attackTimer = 0;
796
self->position.x &= 0xFFFF0000;
797
self->position.y &= 0xFFFF0000;
798
self->targetPos.x &= 0xFFFF0000;
799
self->targetPos.y &= 0xFFFF0000;
800
self->state = MetalSonic_State_StartAttack;
801
}
802
803
MetalSonic_HandleStageWrap();
804
}
805
806
void MetalSonic_State_StartAttack(void)
807
{
808
RSDK_THIS(MetalSonic);
809
810
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
811
812
self->targetPos.x += ScreenInfo->position.x << 16;
813
self->targetPos.y += ScreenInfo->position.y << 16;
814
815
self->velocity.x = (self->targetPos.x - self->position.x) >> 4;
816
self->velocity.y = (self->targetPos.y - self->position.y) >> 4;
817
818
if (self->position.x >= self->targetPos.x)
819
self->velocity.x -= 0x10000;
820
else
821
self->velocity.x += 0x10000;
822
823
if (self->position.y >= self->targetPos.y)
824
self->velocity.y += 0x10000;
825
else
826
self->velocity.y += 0x10000;
827
828
self->position.x += self->velocity.x;
829
self->position.y += self->velocity.y;
830
self->position.x += player1->velocity.x;
831
self->position.y += player1->velocity.y;
832
833
uint8 axisReady = 0;
834
if (self->velocity.x > 0 && self->position.x > self->targetPos.x) {
835
axisReady = 1;
836
self->position.x = self->targetPos.x;
837
}
838
else if (self->velocity.x < 0 && self->position.x < self->targetPos.x) {
839
axisReady = 1;
840
self->position.x = self->targetPos.x;
841
}
842
843
if (self->velocity.y > 0 && self->position.y > self->targetPos.y) {
844
++axisReady;
845
self->position.y = self->targetPos.y;
846
}
847
else if (self->velocity.y < 0 && self->position.y < self->targetPos.y) {
848
++axisReady;
849
self->position.y = self->targetPos.y;
850
}
851
852
self->targetPos.x = self->targetPos.x - (ScreenInfo->position.x << 16);
853
self->targetPos.y = self->targetPos.y - (ScreenInfo->position.y << 16);
854
MetalSonic_HandleAnimDir();
855
856
if (axisReady == 2) {
857
switch (self->attackType) {
858
default: break;
859
860
case MS_ATTACK_DASH:
861
self->attackTimer = 60;
862
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_DASHATTACK, &self->metalSonicAnimator, true, 0);
863
RSDK.PlaySfx(MetalSonic->sfxMSChargeFire, false, 255);
864
self->state = MetalSonic_State_SetupDashAttack_Phase1;
865
break;
866
867
case MS_ATTACK_ELECTRIC:
868
self->attackTimer = 60;
869
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_ELECTRICATTACK, &self->metalSonicAnimator, true, 0);
870
RSDK.PlaySfx(MetalSonic->sfxMSElecPulse, false, 255);
871
self->state = MetalSonic_State_SetupElectricAttack_Phase1;
872
break;
873
874
case MS_ATTACK_BALL:
875
self->attackTimer = 60;
876
self->targetPos.x = self->position.x - player1->position.x;
877
self->targetPos.y = self->position.y - player1->position.y;
878
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BALLATTACK, &self->metalSonicAnimator, true, 0);
879
RSDK.PlaySfx(MetalSonic->sfxMSBall, false, 255);
880
self->state = MetalSonic_State_SetupBallAttack_Phase1;
881
break;
882
}
883
}
884
885
MetalSonic_HandleStageWrap();
886
}
887
888
void MetalSonic_State_SetupBallAttack_Phase1(void)
889
{
890
RSDK_THIS(MetalSonic);
891
892
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
893
894
self->position.x = player1->position.x + self->targetPos.x;
895
self->position.y = player1->position.y + self->targetPos.y;
896
897
int32 angle = RSDK.ATan2(player1->position.x - self->position.x, player1->position.y - self->position.y);
898
self->unusedVec1.x = self->position.x + (RSDK.Cos256(angle + 0xC0) << 12);
899
self->unusedVec1.y = self->position.y + (RSDK.Sin256(angle + 0xC0) << 12);
900
self->unusedVec2.x = self->position.x + (RSDK.Cos256(angle + 0x40) << 12);
901
self->unusedVec2.y = self->position.y + (RSDK.Sin256(angle + 0x40) << 12);
902
self->unusedVec3.x = self->position.x + (RSDK.Cos256(angle + 0x00) << 14);
903
self->unusedVec3.y = self->position.y + (RSDK.Sin256(angle + 0x00) << 14);
904
905
if (--self->attackTimer <= 0) {
906
self->unusedVec1.x = -1;
907
self->targetPos = player1->position;
908
self->velocity.x = 0xA00 * RSDK.Cos256(angle);
909
self->velocity.y = 0xA00 * RSDK.Sin256(angle);
910
RSDK.PlaySfx(Player->sfxPeelRelease, false, 255);
911
self->attackTimer = 15;
912
self->state = MetalSonic_State_BallAttack_Phase1;
913
}
914
915
MetalSonic_HandleStageWrap();
916
917
foreach_active(Player, player)
918
{
919
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
920
Player_Hurt(player, self);
921
}
922
}
923
924
void MetalSonic_State_BallAttack_Phase1(void)
925
{
926
RSDK_THIS(MetalSonic);
927
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
928
929
self->position.x += self->velocity.x;
930
self->position.y += self->velocity.y;
931
932
if (!self->onScreen && --self->attackTimer <= 0) {
933
self->targetPos = player1->position;
934
self->velocity.x = 0;
935
self->velocity.y = 0;
936
self->timer = 0;
937
self->attackTimer = 150;
938
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, true, 0);
939
self->state = MetalSonic_State_Hovering;
940
}
941
942
MetalSonic_HandleStageWrap();
943
944
foreach_active(Player, player)
945
{
946
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
947
Player_Hurt(player, self);
948
}
949
}
950
951
void MetalSonic_State_SetupElectricAttack_Phase1(void)
952
{
953
RSDK_THIS(MetalSonic);
954
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
955
956
self->position.x = self->targetPos.x + (ScreenInfo->position.x << 16);
957
self->position.y = self->targetPos.y + (ScreenInfo->position.y << 16);
958
959
if (--self->attackTimer <= 0) {
960
self->targetPos.x = player1->position.x;
961
self->targetPos.y = player1->position.y;
962
RSDK.ATan2(self->targetPos.x - self->position.x, self->targetPos.y - self->position.y);
963
964
self->velocity.x = self->position.x < player1->position.x ? 0x40000 : -0x40000;
965
self->velocity.y = 0x88000;
966
self->attackTimer = 15;
967
self->state = MetalSonic_State_ElectricAttack_Phase1;
968
}
969
970
MetalSonic_HandleStageWrap();
971
972
foreach_active(Player, player)
973
{
974
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
975
Player_Hurt(player, self);
976
}
977
}
978
979
void MetalSonic_State_ElectricAttack_Phase1(void)
980
{
981
RSDK_THIS(MetalSonic);
982
983
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
984
985
self->position.x += self->velocity.x;
986
self->position.y += self->velocity.y;
987
self->velocity.y -= 0x3800;
988
989
if (self->position.y < player1->position.y && !self->onScreen && --self->attackTimer <= 0) {
990
self->targetPos = player1->position;
991
self->velocity.x = 0;
992
self->velocity.y = 0;
993
self->timer = 0;
994
self->attackTimer = 150;
995
996
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, true, 0);
997
self->state = MetalSonic_State_Hovering;
998
}
999
1000
MetalSonic_HandleStageWrap();
1001
1002
foreach_active(Player, player)
1003
{
1004
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1005
Player_Hurt(player, self);
1006
}
1007
}
1008
1009
void MetalSonic_State_SetupDashAttack_Phase1(void)
1010
{
1011
RSDK_THIS(MetalSonic);
1012
1013
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1014
1015
self->position.x = self->targetPos.x + (ScreenInfo->position.x << 16);
1016
self->position.y = self->targetPos.y + (ScreenInfo->position.y << 16);
1017
1018
int32 angle = RSDK.ATan2(player1->position.x - self->position.x, player1->position.y - self->position.y);
1019
self->unusedVec1.x = self->position.x + (RSDK.Cos256(angle + 0xC0) << 12);
1020
self->unusedVec1.y = self->position.y + (RSDK.Sin256(angle + 0xC0) << 12);
1021
self->unusedVec2.x = self->position.x + (RSDK.Cos256(angle + 0x40) << 12);
1022
self->unusedVec2.y = self->position.y + (RSDK.Sin256(angle + 0x40) << 12);
1023
self->unusedVec3.x = self->position.x + (RSDK.Cos256(angle + 0x00) << 15);
1024
self->unusedVec3.y = self->position.y + (RSDK.Sin256(angle + 0x00) << 15);
1025
1026
if (--self->attackTimer <= 0) {
1027
self->unusedVec1.x = -1;
1028
self->targetPos = player1->position;
1029
self->velocity.x = 0xC00 * RSDK.Cos256(angle);
1030
self->velocity.y = 0xC00 * RSDK.Sin256(angle);
1031
RSDK.PlaySfx(MetalSonic->sfxMSFireball, false, 255);
1032
1033
self->attackTimer = 15;
1034
self->state = MetalSonic_State_DashAttack_Phase1;
1035
}
1036
1037
MetalSonic_HandleStageWrap();
1038
1039
foreach_active(Player, player)
1040
{
1041
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1042
Player_Hurt(player, self);
1043
}
1044
}
1045
1046
void MetalSonic_State_DashAttack_Phase1(void)
1047
{
1048
RSDK_THIS(MetalSonic);
1049
1050
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1051
1052
self->position.x += self->velocity.x;
1053
self->position.y += self->velocity.y;
1054
1055
bool32 finished = true;
1056
if (self->velocity.x <= 0) {
1057
if (self->velocity.x >= 0 || self->position.x >= player1->position.x)
1058
finished = false;
1059
}
1060
else if (self->position.x <= player1->position.x) {
1061
if (self->velocity.x >= 0 || self->position.x >= player1->position.x)
1062
finished = false;
1063
}
1064
1065
if (finished) {
1066
if (!self->onScreen && --self->attackTimer <= 0) {
1067
self->targetPos = player1->position;
1068
self->velocity.x = 0;
1069
self->velocity.y = 0;
1070
self->timer = 0;
1071
self->attackTimer = 150;
1072
1073
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, true, 0);
1074
self->state = MetalSonic_State_Hovering;
1075
}
1076
}
1077
1078
MetalSonic_HandleStageWrap();
1079
1080
foreach_active(Player, player)
1081
{
1082
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1083
Player_Hurt(player, self);
1084
}
1085
}
1086
1087
void MetalSonic_State_EnterPanel(void)
1088
{
1089
RSDK_THIS(MetalSonic);
1090
1091
int32 velX = self->velocity.x;
1092
1093
MetalSonic_HandleVelocity();
1094
1095
if (self->velocity.x < 0x20000 && velX >= 0x20000) {
1096
self->direction = FLIP_X;
1097
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
1098
}
1099
else if (self->velocity.x > -0x20000 && velX <= -0x20000) {
1100
self->direction = FLIP_NONE;
1101
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
1102
}
1103
1104
++self->timer;
1105
self->position.x += self->velocity.x;
1106
self->position.y += self->velocity.y;
1107
1108
int32 rx = self->position.x - self->targetPos.x;
1109
int32 ry = self->position.y - self->targetPos.y;
1110
1111
if ((rx >> 16) * (rx >> 16) + (ry >> 16) * (ry >> 16) < 4096 && self->timer > 96) {
1112
self->timer = 0;
1113
RSDK.SetSpriteAnimation(-1, 0, &self->boosterAnimator, false, 0);
1114
self->state = MetalSonic_State_StartPanelSequence;
1115
}
1116
}
1117
1118
void MetalSonic_State_StartPanelSequence(void)
1119
{
1120
RSDK_THIS(MetalSonic);
1121
1122
++self->timer;
1123
self->position.x += (self->targetPos.x - self->position.x) >> 4;
1124
self->position.y += (self->targetPos.y - self->position.y) >> 4;
1125
1126
if (self->timer == 32)
1127
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_ENTERPANEL, &self->metalSonicAnimator, true, 0);
1128
1129
if (self->timer == 59)
1130
RSDK.PlaySfx(MetalSonic->sfxSpecialRing, false, 255);
1131
1132
if (self->timer == 64) {
1133
int32 id = 0;
1134
#if MANIA_USE_PLUS
1135
for (int32 i = 48; i < 82; i += 2) {
1136
if (id > 0)
1137
RSDK.CopyTileLayer(Zone->fgLayer[0], 167, i, Zone->fgLayer[1], 222, 218, 2, 2);
1138
1139
RSDK.CopyTileLayer(Zone->fgLayer[0], 169, i, Zone->fgLayer[1], 222, 138, 2, 2);
1140
RSDK.CopyTileLayer(Zone->fgLayer[0], 171, i, Zone->fgLayer[1], 222, 138, 2, 2);
1141
RSDK.CopyTileLayer(Zone->fgLayer[0], 173, i, Zone->fgLayer[1], 222, 138, 2, 2);
1142
RSDK.CopyTileLayer(Zone->fgLayer[0], 175, i, Zone->fgLayer[1], 222, 138, 2, 2);
1143
RSDK.CopyTileLayer(Zone->fgLayer[0], 177, i, Zone->fgLayer[1], 222, 138, 2, 2);
1144
RSDK.CopyTileLayer(Zone->fgLayer[0], 179, i, Zone->fgLayer[1], 222, 138, 2, 2);
1145
++id;
1146
}
1147
#else
1148
for (int32 i = 128; i < 82; i += 2) {
1149
int32 y = i;
1150
if (id > 0) {
1151
y = 2 * id + 128;
1152
RSDK.CopyTileLayer(Zone->fgLayer[0], 167, i, Zone->fgLayer[1], 222, 218, 2, 2);
1153
}
1154
1155
RSDK.CopyTileLayer(Zone->fgLayer[0], 169, y, Zone->fgLayer[1], 222, 218, 2, 2);
1156
RSDK.CopyTileLayer(Zone->fgLayer[0], 171, y, Zone->fgLayer[1], 222, 218, 2, 2);
1157
RSDK.CopyTileLayer(Zone->fgLayer[0], 173, y, Zone->fgLayer[1], 222, 218, 2, 2);
1158
RSDK.CopyTileLayer(Zone->fgLayer[0], 175, y, Zone->fgLayer[1], 222, 218, 2, 2);
1159
RSDK.CopyTileLayer(Zone->fgLayer[0], 177, y, Zone->fgLayer[1], 222, 218, 2, 2);
1160
RSDK.CopyTileLayer(Zone->fgLayer[0], 179, y, Zone->fgLayer[1], 222, 218, 2, 2);
1161
++id;
1162
}
1163
#endif
1164
1165
EntityMSPanel *panel = self->panel;
1166
panel->state = MSPanel_State_Active;
1167
1168
self->position = self->targetPos;
1169
self->timer = 16;
1170
self->attackTimer = MANIA_USE_PLUS ? 240 : 180;
1171
self->health = MANIA_USE_PLUS ? 6 : 4;
1172
self->state = MetalSonic_State_OpenFactoryDoor;
1173
}
1174
}
1175
1176
void MetalSonic_HandlePanelAttack(void)
1177
{
1178
RSDK_THIS(MetalSonic);
1179
1180
#if MANIA_USE_PLUS
1181
if (!RSDK.GetEntityCount(MSBomb->classID, true))
1182
#endif
1183
--self->attackTimer;
1184
1185
if (self->attackTimer == 60) {
1186
RSDK.PlaySfx(MetalSonic->sfxMSElecPulse, false, 255);
1187
self->invincibilityTimer = 60;
1188
1189
#if MANIA_USE_PLUS
1190
EntityFXWaveRing *ring = CREATE_ENTITY(FXWaveRing, self, self->position.x, self->position.y);
1191
ring->radiusOffset = 24;
1192
ring->timer = 24;
1193
ring->r = 0xF0;
1194
ring->g = 0x80;
1195
ring->b = 0x00;
1196
ring->shrinkSpeed = 1;
1197
#endif
1198
}
1199
1200
if (self->attackTimer <= 0) {
1201
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1202
1203
self->attackTimer = 45 * self->health + (MANIA_USE_PLUS ? 195 : 135);
1204
int32 angle = RSDK.ATan2(player1->position.x - self->position.x, player1->position.y - self->position.y);
1205
1206
EntityMSOrb *orb = CREATE_ENTITY(MSOrb, NULL, self->position.x, self->position.y);
1207
orb->velocity.x = 0x280 * RSDK.Cos256(angle);
1208
orb->velocity.y = 0x280 * RSDK.Sin256(angle);
1209
RSDK.PlaySfx(MetalSonic->sfxMSShoot, false, 255);
1210
}
1211
}
1212
1213
void MetalSonic_State_OpenFactoryDoor(void)
1214
{
1215
RSDK_THIS(MetalSonic);
1216
1217
++self->timer;
1218
1219
MetalSonic_HandlePanelAttack();
1220
1221
if (self->timer == 60) {
1222
foreach_active(MSFactory, factory)
1223
{
1224
factory->visible = true;
1225
factory->state = MSFactory_State_OpeningDoor;
1226
}
1227
1228
self->timer = 0;
1229
self->state = MetalSonic_State_HandleSilverSonics;
1230
}
1231
}
1232
1233
void MetalSonic_State_HandleSilverSonics(void)
1234
{
1235
RSDK_THIS(MetalSonic);
1236
1237
++self->timer;
1238
MetalSonic_HandlePanelAttack();
1239
1240
#if MANIA_USE_PLUS
1241
if (!RSDK.GetEntityCount(SilverSonic->classID, true) && !RSDK.GetEntityCount(MSBomb->classID, true) && self->timer > 60) {
1242
#else
1243
if (!RSDK.GetEntityCount(SilverSonic->classID, true) && self->timer > 60) {
1244
#endif
1245
self->timer = 0;
1246
self->state = MetalSonic_State_OpenFactoryDoor;
1247
}
1248
}
1249
1250
void MetalSonic_State_PanelExplosion(void)
1251
{
1252
RSDK_THIS(MetalSonic);
1253
1254
EntityMSPanel *panel = self->panel;
1255
1256
self->position.y += 0x8000;
1257
panel->position.y += 0x8000;
1258
1259
if (!self->timer)
1260
panel->state = MSPanel_State_Explode;
1261
1262
if (++self->timer == 104) {
1263
self->timer = 0;
1264
panel->state = MSPanel_State_Rumbling;
1265
self->state = MetalSonic_State_ExitFactory;
1266
}
1267
}
1268
1269
void MetalSonic_State_ExitFactory(void)
1270
{
1271
RSDK_THIS(MetalSonic);
1272
1273
if (++self->timer == 60) {
1274
self->timer = 0;
1275
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, false, 3);
1276
1277
self->velocity.x = 0x18000;
1278
self->onGround = false;
1279
self->state = MetalSonic_State_PrepareFinalChase;
1280
}
1281
}
1282
1283
void MetalSonic_State_PrepareFinalChase(void)
1284
{
1285
RSDK_THIS(MetalSonic);
1286
1287
self->velocity.y += 0x2000;
1288
1289
self->outerBox = RSDK.GetHitbox(&self->metalSonicAnimator, 0);
1290
self->innerBox = RSDK.GetHitbox(&self->metalSonicAnimator, 1);
1291
RSDK.ProcessObjectMovement(self, self->outerBox, self->innerBox);
1292
1293
if (self->onGround) {
1294
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_READY, &self->metalSonicAnimator, false, 0);
1295
self->direction = FLIP_NONE;
1296
self->groundVel = 0;
1297
}
1298
1299
if (++self->timer == 120) {
1300
EntityFXFade *fade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
1301
fade->speedIn = 256;
1302
fade->wait = 32;
1303
fade->speedOut = 8;
1304
1305
self->panel->state = StateMachine_None;
1306
self->timer = 0;
1307
self->attackTimer = 240;
1308
self->state = MetalSonic_State_Hovering;
1309
1310
foreach_active(Player, player)
1311
{
1312
RSDK.PlaySfx(SpeedBooster->sfxSpeedBooster, false, 255);
1313
player->velocity.x = 0xE0000;
1314
player->groundVel = 0xE0000;
1315
player->controlLock = 60;
1316
player->direction = FLIP_NONE;
1317
RSDK.SetSpriteAnimation(player->aniFrames, ANI_RUN, &player->animator, false, 0);
1318
player->state = Player_State_Ground;
1319
}
1320
1321
Vector2 size;
1322
RSDK.GetLayerSize(Zone->fgLayer[0], &size, true);
1323
1324
Zone->cameraBoundsL[0] = 0;
1325
Zone->cameraBoundsR[0] = size.x;
1326
Zone->cameraBoundsT[0] = 0;
1327
1328
Zone->playerBoundActiveL[0] = false;
1329
Zone->playerBoundActiveR[0] = false;
1330
1331
RSDK.PlaySfx(MetalSonic->sfxExplosion3, false, 255);
1332
}
1333
}
1334
1335
#if MANIA_USE_PLUS
1336
void MetalSonic_State_WaitForRuby(void)
1337
{
1338
RSDK_THIS(MetalSonic);
1339
1340
int32 velX = self->velocity.x;
1341
MetalSonic_HandleVelocity();
1342
1343
if (self->velocity.x < 0x20000 && velX >= 0x20000) {
1344
self->direction = FLIP_X;
1345
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
1346
}
1347
else if (self->velocity.x > -0x20000 && velX <= -0x20000) {
1348
self->direction = FLIP_NONE;
1349
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
1350
}
1351
1352
++self->timer;
1353
self->position.x += self->velocity.x;
1354
self->position.y += self->velocity.y;
1355
int32 rx = self->position.x - self->targetPos.x;
1356
int32 ry = self->position.y - self->targetPos.y;
1357
1358
if ((rx >> 16) * (rx >> 16) + (ry >> 16) * (ry >> 16) < 4096 && self->timer > 96) {
1359
self->timer = 0;
1360
self->direction = FLIP_NONE;
1361
self->state = MetalSonic_State_ObtainRuby;
1362
}
1363
}
1364
1365
void MetalSonic_State_ObtainRuby(void)
1366
{
1367
RSDK_THIS(MetalSonic);
1368
1369
self->targetPos.y += RSDK.Sin256(4 * Zone->timer) << 8;
1370
1371
self->position.x += (self->targetPos.x - self->position.x) >> 4;
1372
self->position.y += (self->targetPos.y - self->position.y) >> 4;
1373
1374
foreach_active(PhantomRuby, ruby)
1375
{
1376
int32 rx = (self->position.x - ruby->position.x) >> 16;
1377
int32 ry = (self->position.y - ruby->position.y) >> 16;
1378
1379
if (rx * rx + ry * ry < 0x100) {
1380
ruby->startPos.x = ruby->position.x;
1381
ruby->startPos.y = ruby->position.y;
1382
ruby->state = PhantomRuby_State_Oscillate;
1383
self->state = MetalSonic_State_Transform;
1384
RSDK.PlaySfx(MetalSonic->sfxMSTransform, false, 255);
1385
}
1386
}
1387
}
1388
1389
void MetalSonic_State_Transform(void)
1390
{
1391
RSDK_THIS(MetalSonic);
1392
1393
self->position.y -= 0x2000;
1394
1395
foreach_active(PhantomRuby, ruby) { ruby->startPos.y -= 0x2000; }
1396
1397
if (++self->timer == 30) {
1398
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, -1, &self->boosterAnimator, true, 0);
1399
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_ELECTRICATTACK, &self->metalSonicAnimator, true, 0);
1400
}
1401
1402
int32 timer = MIN(self->timer >> 1, 96);
1403
1404
if ((self->timer & 0x1F) == 30) {
1405
EntityFXWaveRing *ring = CREATE_ENTITY(FXWaveRing, self, self->position.x, self->position.y);
1406
ring->radiusOffset = timer;
1407
ring->timer = 24;
1408
ring->r = 0xF0;
1409
ring->g = 0x00;
1410
ring->b = 0xF0;
1411
ring->shrinkSpeed = 2;
1412
}
1413
1414
if (timer >= 32 && (self->timer & 0xF) == 14) {
1415
EntityFXWaveRing *ring = CREATE_ENTITY(FXWaveRing, self, self->position.x, self->position.y);
1416
ring->radiusOffset = timer + 64;
1417
ring->timer = 24;
1418
ring->r = 0xF0;
1419
ring->g = 0xF0;
1420
ring->b = 0x60;
1421
ring->shrinkSpeed = 4;
1422
}
1423
1424
if (self->timer == 240) {
1425
self->timer = 0;
1426
self->active = ACTIVE_NEVER;
1427
1428
EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
1429
fxFade->speedIn = 256;
1430
fxFade->wait = 32;
1431
fxFade->speedOut = 8;
1432
1433
RSDK.PlaySfx(MetalSonic->sfxTransform2, false, 255);
1434
}
1435
}
1436
1437
void MetalSonic_State_Defeated(void)
1438
{
1439
RSDK_THIS(MetalSonic);
1440
1441
self->position.x += self->velocity.x;
1442
self->position.y += self->velocity.y;
1443
self->velocity.y += 0x3800;
1444
self->visible ^= true;
1445
1446
if (!RSDK.CheckOnScreen(self, NULL)) {
1447
Music_TransitionTrack(TRACK_STAGE, 0.0125);
1448
EntityEggPrison *prison = CREATE_ENTITY(EggPrison, INT_TO_VOID(EGGPRISON_FLYING), (ScreenInfo->position.x + ScreenInfo->center.x) << 16,
1449
(ScreenInfo->position.y - 48) << 16);
1450
prison->velocity.x = 0x10000;
1451
prison->active = ACTIVE_NORMAL;
1452
destroyEntity(self);
1453
}
1454
}
1455
#else
1456
void MetalSonic_State_SetupSpikeWall(void)
1457
{
1458
RSDK_THIS(MetalSonic);
1459
1460
MetalSonic_HandleVelocity();
1461
1462
self->position.x += self->velocity.x;
1463
self->position.y += self->velocity.y;
1464
1465
MetalSonic_HandleStageWrap();
1466
1467
self->timer++;
1468
1469
EntityPlatform *wall = NULL;
1470
EntityPlatform *startWall = NULL;
1471
1472
switch (self->timer) {
1473
case 60: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform); break;
1474
case 90: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 3, Platform); break;
1475
case 120: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 4, Platform); break;
1476
case 150: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 5, Platform); break;
1477
case 180: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 6, Platform); break;
1478
case 210: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 7, Platform); break;
1479
case 240: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 8, Platform); break;
1480
case 270: wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 9, Platform); break;
1481
case 300:
1482
startWall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1483
1484
self->targetPos.x = startWall->position.x + 0x800000;
1485
self->targetPos.y = (Zone->cameraBoundsB[0] << 16) - 0x340000;
1486
self->state = MetalSonic_State_FlyToSpikeWall;
1487
break;
1488
}
1489
1490
if (wall) {
1491
wall->active = ACTIVE_NORMAL;
1492
wall->state = MetalSonic_StateWall_Fall;
1493
}
1494
}
1495
void MetalSonic_State_FlyToSpikeWall(void)
1496
{
1497
RSDK_THIS(MetalSonic);
1498
1499
MetalSonic_HandleVelocity();
1500
1501
self->position.x += self->velocity.x;
1502
self->position.y += self->velocity.y;
1503
1504
MetalSonic_HandleAnimDir();
1505
1506
int32 rx = self->position.x - self->targetPos.x;
1507
int32 ry = self->position.y - self->targetPos.y;
1508
1509
if ((rx >> 16) * (rx >> 16) + (ry >> 16) * (ry >> 16) < 0x1000) {
1510
self->attackTimer = 0;
1511
self->position.x &= 0xFFFF0000;
1512
self->position.y &= 0xFFFF0000;
1513
self->targetPos.x &= 0xFFFF0000;
1514
self->targetPos.y &= 0xFFFF0000;
1515
self->state = MetalSonic_State_LandNearSpikeWall;
1516
}
1517
1518
MetalSonic_HandleStageWrap();
1519
}
1520
void MetalSonic_State_LandNearSpikeWall(void)
1521
{
1522
RSDK_THIS(MetalSonic);
1523
1524
self->velocity.x = (self->targetPos.x - self->position.x) >> 4;
1525
self->velocity.y = (self->targetPos.y - self->position.y) >> 4;
1526
1527
if (self->position.x >= self->targetPos.x)
1528
self->velocity.x -= 0x10000;
1529
else
1530
self->velocity.x += 0x10000;
1531
1532
if (self->position.y >= self->targetPos.y)
1533
self->velocity.y -= 0x10000;
1534
else
1535
self->velocity.y += 0x10000;
1536
1537
self->position.x += self->velocity.x;
1538
self->position.y += self->velocity.y;
1539
1540
uint8 axisFinished = 0;
1541
if (self->velocity.x > 0) {
1542
if (self->position.x > self->targetPos.x) {
1543
axisFinished = 1;
1544
self->position.x = self->targetPos.x;
1545
}
1546
}
1547
else if (self->position.x < self->targetPos.x) {
1548
axisFinished = 1;
1549
self->position.x = self->targetPos.x;
1550
}
1551
1552
if (self->velocity.y > 0) {
1553
if (self->position.y > self->targetPos.y) {
1554
++axisFinished;
1555
self->position.y = self->targetPos.y;
1556
}
1557
}
1558
else if (self->position.y < self->targetPos.y) {
1559
++axisFinished;
1560
self->position.y = self->targetPos.y;
1561
}
1562
1563
MetalSonic_HandleAnimDir();
1564
1565
if (axisFinished == 2) {
1566
self->velocity.x = 0;
1567
self->velocity.y = 0;
1568
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_TAUNT, &self->metalSonicAnimator, false, 0);
1569
RSDK.SetSpriteAnimation(-1, 0, &self->boosterAnimator, false, 0);
1570
1571
self->direction = FLIP_NONE;
1572
self->state = MetalSonic_State_Taunt_Phase2;
1573
}
1574
1575
MetalSonic_HandleStageWrap();
1576
}
1577
void MetalSonic_State_Taunt_Phase2(void)
1578
{
1579
RSDK_THIS(MetalSonic);
1580
1581
if (self->metalSonicAnimator.frameID == self->metalSonicAnimator.frameCount - 1) {
1582
self->timer = 0;
1583
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_READY, &self->metalSonicAnimator, false, 0);
1584
self->state = MetalSonic_State_Ready_Phase2;
1585
}
1586
MetalSonic_HandleStageWrap();
1587
}
1588
void MetalSonic_State_Ready_Phase2(void)
1589
{
1590
RSDK_THIS(MetalSonic);
1591
1592
self->timer++;
1593
if (self->timer == 60) {
1594
self->timer = 0;
1595
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BOOSTER_WEAK, &self->boosterAnimator, false, 0);
1596
self->state = MetalSonic_State_StartSpikeWallMovement;
1597
1598
Vector2 size;
1599
RSDK.GetLayerSize(Zone->fgLayer[0], &size, true);
1600
}
1601
1602
MetalSonic_HandleStageWrap();
1603
}
1604
void MetalSonic_State_StartSpikeWallMovement(void)
1605
{
1606
RSDK_THIS(MetalSonic);
1607
1608
self->timer++;
1609
if (self->timer == 90) {
1610
int32 slot = SceneInfo->entitySlot;
1611
RSDK_GET_ENTITY(slot + 2, Platform)->state = MetalSonic_StateWall_Move;
1612
RSDK_GET_ENTITY(slot + 3, Platform)->state = MetalSonic_StateWall_Move;
1613
RSDK_GET_ENTITY(slot + 4, Platform)->state = MetalSonic_StateWall_Move;
1614
RSDK_GET_ENTITY(slot + 5, Platform)->state = MetalSonic_StateWall_Move;
1615
RSDK_GET_ENTITY(slot + 6, Platform)->state = MetalSonic_StateWall_Move;
1616
RSDK_GET_ENTITY(slot + 7, Platform)->state = MetalSonic_StateWall_Move;
1617
RSDK_GET_ENTITY(slot + 8, Platform)->state = MetalSonic_StateWall_Move;
1618
RSDK_GET_ENTITY(slot + 9, Platform)->state = MetalSonic_StateWall_Move;
1619
1620
self->timer = 0;
1621
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, false, 0);
1622
self->state = MetalSonic_State_AccelerateSpikeWall;
1623
}
1624
1625
MetalSonic_HandleStageWrap();
1626
}
1627
void MetalSonic_State_AccelerateSpikeWall(void)
1628
{
1629
RSDK_THIS(MetalSonic);
1630
1631
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1632
1633
if (wall->velocity.x >= 0x1C000) {
1634
Vector2 size;
1635
RSDK.GetLayerSize(Zone->fgLayer[0], &size, true);
1636
1637
for (int32 i = 0; i < PLAYER_COUNT; ++i) {
1638
Zone->cameraBoundsL[i] = 0;
1639
Zone->cameraBoundsR[i] = size.x + 0x400;
1640
Zone->playerBoundActiveL[i] = false;
1641
Zone->playerBoundActiveL[i] = false;
1642
}
1643
1644
if (wall->velocity.x >= 0x54000) {
1645
self->health = 8;
1646
self->attackTimer = 120;
1647
self->state = MetalSonic_State_Hover_Phase2;
1648
}
1649
}
1650
1651
self->velocity.x = wall->velocity.x;
1652
self->position.x += self->velocity.x;
1653
self->position.y += self->velocity.y;
1654
1655
MetalSonic_HandleStageWrap();
1656
}
1657
void MetalSonic_State_Hover_Phase2(void)
1658
{
1659
RSDK_THIS(MetalSonic);
1660
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1661
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1662
1663
if (player1->position.x <= self->position.x) {
1664
if (self->velocity.x >= player1->velocity.x - 0x60000) {
1665
self->velocity.x -= 0xC00;
1666
}
1667
else
1668
self->velocity.x += 0xC00;
1669
}
1670
else if (self->velocity.x < 0x60000)
1671
self->velocity.x += 0xC00;
1672
1673
self->position.x += self->velocity.x;
1674
self->position.y += self->velocity.y;
1675
1676
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x140000, true))
1677
self->velocity.y = 0;
1678
1679
if (self->direction == FLIP_X) {
1680
self->direction = FLIP_NONE;
1681
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
1682
}
1683
1684
MetalSonic_HandleStageWrap();
1685
1686
if (self->position.x < wall->position.x + 0x800000)
1687
self->position.x = wall->position.x + 0x800000;
1688
1689
if (--self->attackTimer <= 0 && !player1->blinkTimer) {
1690
if (self->velocity.x < 0x60000)
1691
self->velocity.x = 0x60000;
1692
self->attackTimer = 120;
1693
1694
self->attackType = RSDK.Rand(0, 6);
1695
while ((1 << self->attackType) & MetalSonic->finishedAttacks) self->attackType = RSDK.Rand(0, 6);
1696
1697
MetalSonic->finishedAttacks |= 1 << self->attackType;
1698
if (MetalSonic->finishedAttacks == 0x3F)
1699
MetalSonic->finishedAttacks = 0;
1700
1701
self->attackType >>= 1;
1702
switch (self->attackType) {
1703
case MS_ATTACK_DASH:
1704
RSDK.PlaySfx(MetalSonic->sfxMSFireball, false, 0xFF);
1705
1706
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_DASHATTACK, &self->metalSonicAnimator, false, 0);
1707
RSDK.SetSpriteAnimation(-1, 0, &self->boosterAnimator, false, 0);
1708
self->attackTimer = 60;
1709
self->state = MetalSonic_State_DashAttack_Phase2;
1710
break;
1711
1712
case MS_ATTACK_ELECTRIC:
1713
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_ELECTRICATTACK, &self->metalSonicAnimator, true, 0);
1714
1715
RSDK.PlaySfx(MetalSonic->sfxMSElecPulse, false, 0xFF);
1716
self->state = MetalSonic_State_SetupElectricAttack_Phase2;
1717
break;
1718
1719
case MS_ATTACK_BALL:
1720
self->attackTimer = 60;
1721
self->targetPos.x = (RSDK.Rand(-3, 7) << 14) + 0x78000;
1722
self->targetPos.y = -0x80000;
1723
1724
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BALLATTACK, &self->metalSonicAnimator, true, 0);
1725
RSDK.PlaySfx(MetalSonic->sfxMSBall, false, 0xFF);
1726
self->state = MetalSonic_State_SetupBallAttack_Phase2;
1727
break;
1728
}
1729
}
1730
1731
MetalSonic_CheckPlayerCollisions();
1732
}
1733
void MetalSonic_State_SetupElectricAttack_Phase2(void)
1734
{
1735
RSDK_THIS(MetalSonic);
1736
1737
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1738
1739
if (self->velocity.x < 0xB0000)
1740
self->velocity.x += 0xC00;
1741
1742
self->position.x += self->velocity.x;
1743
self->position.y += self->velocity.y;
1744
1745
if (self->position.y <= (ScreenInfo->center.y + ScreenInfo->position.y) << 16) {
1746
if (self->velocity.y < 0x10000)
1747
self->velocity.y += 0x1000;
1748
}
1749
else {
1750
if (self->velocity.y >= -0x10000)
1751
self->velocity.y -= 0x1000;
1752
}
1753
1754
MetalSonic_HandleStageWrap();
1755
1756
if (self->position.x < wall->position.x + 0x410000)
1757
self->position.x = wall->position.x + 0x410000;
1758
1759
if (self->position.x > ((ScreenInfo->size.x + ScreenInfo->position.x) << 16) + 0x400000) {
1760
self->direction = FLIP_X;
1761
self->state = MetalSonic_State_StartElectricAttack_Phase2;
1762
}
1763
1764
foreach_active(Player, player)
1765
{
1766
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1767
Player_Hurt(player, self);
1768
}
1769
}
1770
void MetalSonic_State_StartElectricAttack_Phase2(void)
1771
{
1772
RSDK_THIS(MetalSonic);
1773
1774
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1775
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1776
1777
self->velocity.x = player1->velocity.x - 0x10000;
1778
self->position.x += self->velocity.x;
1779
self->position.y += self->velocity.y;
1780
1781
if (self->position.y <= (ScreenInfo->center.y + ScreenInfo->position.y) << 16) {
1782
if (self->velocity.y < 0x10000)
1783
self->velocity.y += 0x1000;
1784
}
1785
else {
1786
if (self->velocity.y >= -0x10000)
1787
self->velocity.y -= 0x1000;
1788
}
1789
1790
MetalSonic_HandleStageWrap();
1791
1792
if (self->position.x < wall->position.x + 0x410000)
1793
self->position.x = wall->position.x + 0x410000;
1794
1795
if (self->position.x < ((ScreenInfo->position.x + ScreenInfo->size.x) << 16) - 0x180000) {
1796
self->attackTimer = 120;
1797
self->position.x = ((ScreenInfo->position.x + ScreenInfo->size.x) << 16) - 0x180000;
1798
self->state = MetalSonic_State_ElectricAttack_Phase2;
1799
}
1800
1801
foreach_active(Player, player)
1802
{
1803
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1804
Player_Hurt(player, self);
1805
}
1806
}
1807
void MetalSonic_State_ElectricAttack_Phase2(void)
1808
{
1809
RSDK_THIS(MetalSonic);
1810
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1811
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1812
1813
self->velocity.x = player1->velocity.x;
1814
self->position.x = ((ScreenInfo->position.x + ScreenInfo->size.x) << 16) - 0x180000;
1815
self->position.y += self->velocity.y;
1816
1817
if (self->position.y <= (ScreenInfo->center.y + ScreenInfo->position.y) << 16) {
1818
if (self->velocity.y < 0x10000)
1819
self->velocity.y += 0x1000;
1820
}
1821
else {
1822
if (self->velocity.y >= -0x10000)
1823
self->velocity.y -= 0x1000;
1824
}
1825
1826
MetalSonic_HandleStageWrap();
1827
1828
if (self->position.x < wall->position.x + 0x410000)
1829
self->position.x = wall->position.x + 0x410000;
1830
1831
switch (--self->attackTimer) {
1832
case 0:
1833
self->attackTimer = 120;
1834
self->velocity.y = 0x10000;
1835
1836
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, false, 0);
1837
self->state = MetalSonic_State_FinishAttack_Phase2;
1838
break;
1839
1840
case 30:
1841
case 60:
1842
case 90: {
1843
EntityMSOrb *orb = CREATE_ENTITY(MSOrb, NULL, self->position.x, self->position.y);
1844
orb->velocity.x = player1->velocity.x - 0x40000;
1845
orb->velocity.y = (RSDK.Rand(-1, 2) + 2) << 16;
1846
RSDK.PlaySfx(MetalSonic->sfxMSShoot, false, 0xFF);
1847
break;
1848
}
1849
1850
default: break;
1851
}
1852
1853
foreach_active(Player, player)
1854
{
1855
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1856
Player_Hurt(player, self);
1857
}
1858
}
1859
void MetalSonic_State_SetupBallAttack_Phase2(void)
1860
{
1861
RSDK_THIS(MetalSonic);
1862
1863
int32 angle = RSDK.ATan2(self->targetPos.x, self->targetPos.y);
1864
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1865
1866
self->unusedVec1.x = self->position.x + (RSDK.Cos256(angle + 0xC0) << 12);
1867
self->unusedVec1.y = self->position.y + (RSDK.Sin256(angle - 0x40) << 12);
1868
self->unusedVec2.x = self->position.x + (RSDK.Cos256(angle + 0x40) << 12);
1869
self->unusedVec2.y = self->position.y + (RSDK.Sin256(angle + 0x40) << 12);
1870
self->unusedVec3.x = self->position.x + (RSDK.Cos256(angle + 0x00) << 14);
1871
self->unusedVec3.y = self->position.y + (RSDK.Sin256(angle + 0x00) << 14);
1872
1873
self->position.x += self->velocity.x;
1874
self->position.y += self->velocity.y;
1875
1876
MetalSonic_HandleStageWrap();
1877
1878
if (self->position.x < wall->position.x + 0x410000)
1879
self->position.x = wall->position.x + 0x410000;
1880
1881
self->attackTimer--;
1882
if (self->attackTimer <= 0) {
1883
self->unusedVec1.x = -1;
1884
self->attackTimer = 8;
1885
self->velocity = self->targetPos;
1886
RSDK.PlaySfx(Player->sfxPeelRelease, false, 0xFF);
1887
self->state = MetalSonic_State_BallAttack_Phase2;
1888
}
1889
1890
foreach_active(Player, player)
1891
{
1892
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1893
Player_Hurt(player, self);
1894
}
1895
}
1896
void MetalSonic_State_BallAttack_Phase2(void)
1897
{
1898
RSDK_THIS(MetalSonic);
1899
1900
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1901
1902
self->position.x += self->velocity.x;
1903
self->position.y += self->velocity.y;
1904
self->velocity.y += 0x3800;
1905
1906
if (self->position.y >= 0x1F00000) {
1907
if (self->position.x > ((ScreenInfo->size.x + ScreenInfo->position.x) << 16) + 0x400000 || !--self->attackTimer) {
1908
self->attackTimer = 120;
1909
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, false, 2);
1910
self->state = MetalSonic_State_FinishAttack_Phase2;
1911
}
1912
else {
1913
self->velocity.y = -0x80000;
1914
RSDK.PlaySfx(MetalSonic->sfxRockemSockem, false, 0xFF);
1915
}
1916
}
1917
1918
MetalSonic_HandleStageWrap();
1919
1920
if (self->position.x < wall->position.x + 0x410000)
1921
self->position.x = wall->position.x + 0x410000;
1922
1923
foreach_active(Player, player)
1924
{
1925
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1926
Player_Hurt(player, self);
1927
}
1928
}
1929
void MetalSonic_State_DashAttack_Phase2(void)
1930
{
1931
RSDK_THIS(MetalSonic);
1932
1933
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1934
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1935
1936
if (self->velocity.x < 0xD0000)
1937
self->velocity.x += 0x1000;
1938
1939
self->position.x += self->velocity.x;
1940
self->position.y += self->velocity.y;
1941
1942
MetalSonic_HandleStageWrap();
1943
1944
if (self->position.x < wall->position.x + 0x410000)
1945
self->position.x = wall->position.x + 0x410000;
1946
1947
if (self->attackTimer) {
1948
self->attackTimer--;
1949
}
1950
else if (self->position.x > player1->position.x + 0x400000) {
1951
self->attackTimer = 120;
1952
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_FLY, &self->metalSonicAnimator, false, 2);
1953
self->state = MetalSonic_State_FinishAttack_Phase2;
1954
}
1955
1956
foreach_active(Player, player)
1957
{
1958
if (Player_CheckCollisionTouch(player, self, &MetalSonic->hitboxHover))
1959
Player_Hurt(player, self);
1960
}
1961
}
1962
void MetalSonic_State_FinishAttack_Phase2(void)
1963
{
1964
RSDK_THIS(MetalSonic);
1965
1966
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
1967
1968
if (self->velocity.x > 0x20000)
1969
self->velocity.x -= 0x1800;
1970
1971
self->position.x += self->velocity.x;
1972
self->position.y += self->velocity.y;
1973
1974
if (self->position.y >= 0x1EC0000) {
1975
self->position.y = 0x1EC0000;
1976
self->velocity.y = 0;
1977
}
1978
1979
if (self->position.x < player1->position.x && self->direction == FLIP_X) {
1980
self->direction = FLIP_NONE;
1981
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVERTURN, &self->metalSonicAnimator, true, 0);
1982
}
1983
1984
MetalSonic_HandleStageWrap();
1985
1986
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
1987
1988
if (self->position.x < wall->position.x + 0x900000) {
1989
self->position.x = wall->position.x + 0x900000;
1990
self->attackTimer = 120;
1991
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_BOOSTER_WEAK, &self->boosterAnimator, false, 0);
1992
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_HOVER, &self->metalSonicAnimator, false, 0);
1993
self->state = MetalSonic_State_Hover_Phase2;
1994
}
1995
1996
MetalSonic_CheckPlayerCollisions();
1997
}
1998
void MetalSonic_State_Explode(void)
1999
{
2000
RSDK_THIS(MetalSonic);
2001
2002
EntityPlatform *wall1 = RSDK_GET_ENTITY(SceneInfo->entitySlot + 2, Platform);
2003
2004
self->position.x += self->velocity.x;
2005
self->position.y += self->velocity.y;
2006
self->velocity.x -= 0x1000;
2007
self->rotation += 6;
2008
2009
MetalSonic_Explode();
2010
MetalSonic_HandleStageWrap();
2011
2012
if (self->position.x < wall1->position.x + 0x20000) {
2013
RSDK.PlaySfx(MetalSonic->sfxExplosion3, false, 0xFF);
2014
2015
for (int32 i = 2; i < 10; ++i) {
2016
EntityPlatform *wall = RSDK_GET_ENTITY(SceneInfo->entitySlot + i, Platform);
2017
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, wall->position.x, wall->position.y);
2018
2019
RSDK.SetSpriteAnimation(Platform->aniFrames, 0, &debris->animator, true, 1);
2020
debris->velocity.x = RSDK.Rand(6, 11) << 16;
2021
debris->velocity.y = RSDK.Rand(-0x20000, -0x10000);
2022
debris->gravityStrength = 0x4800;
2023
debris->drawGroup = wall->drawGroup;
2024
debris->updateRange.x = 0x800000;
2025
debris->updateRange.x = 0x800000;
2026
2027
destroyEntity(wall);
2028
}
2029
2030
Zone->autoScrollSpeed = 0;
2031
self->velocity.x = 0x50000;
2032
self->velocity.y = -0x30000;
2033
self->rotation = 0;
2034
self->drawFX &= ~FX_ROTATE;
2035
self->state = MetalSonic_State_Defeated;
2036
}
2037
}
2038
void MetalSonic_State_Defeated(void)
2039
{
2040
RSDK_THIS(MetalSonic);
2041
2042
self->position.x += self->velocity.x;
2043
self->position.y += self->velocity.y;
2044
self->velocity.y += 0x3800;
2045
self->visible ^= true;
2046
2047
MetalSonic_HandleStageWrap();
2048
2049
if (!RSDK.CheckOnScreen(self, &self->updateRange)) {
2050
self->visible = true;
2051
self->timer = 90;
2052
self->state = MetalSonic_State_Finish;
2053
}
2054
}
2055
void MetalSonic_State_Finish(void)
2056
{
2057
RSDK_THIS(MetalSonic);
2058
2059
MetalSonic_HandleStageWrap();
2060
2061
if (!--self->timer) {
2062
Music_TransitionTrack(TRACK_STAGE, 0.0125);
2063
2064
Zone->cameraBoundsL[0] = ScreenInfo->position.x;
2065
Zone->cameraBoundsR[0] = ScreenInfo->size.x + ScreenInfo->position.x;
2066
Zone->playerBoundsR[0] = (ScreenInfo->size.x + ScreenInfo->position.x) << 16;
2067
Zone->playerBoundsL[0] = ScreenInfo->position.x << 16;
2068
2069
EntityEggPrison *prison = CREATE_ENTITY(EggPrison, INT_TO_VOID(EGGPRISON_FLYING), (ScreenInfo->position.x + ScreenInfo->center.x) << 16,
2070
(ScreenInfo->position.y - 48) << 16);
2071
2072
prison->velocity.x = 0x10000;
2073
prison->active = ACTIVE_NORMAL;
2074
self->state = MetalSonic_State_None;
2075
}
2076
}
2077
2078
void MetalSonic_State_None(void)
2079
{
2080
// common preplus L
2081
}
2082
2083
void MetalSonic_StateWall_Fall(void)
2084
{
2085
RSDK_THIS(Platform);
2086
2087
self->drawPos.y += self->velocity.y;
2088
self->centerPos.y = self->drawPos.y;
2089
self->position.y = self->drawPos.y;
2090
2091
self->velocity.y += 0x3800;
2092
2093
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, (self->speed << 21) + 0x100000, true)) {
2094
self->velocity.y = 0;
2095
self->drawPos.y = self->position.y;
2096
self->centerPos.y = self->position.y;
2097
self->state = Platform_State_Fixed;
2098
Camera_ShakeScreen(0, 0, 4);
2099
RSDK.PlaySfx(MetalSonic->sfxHit, false, 0xFF);
2100
}
2101
}
2102
void MetalSonic_StateWall_Move(void)
2103
{
2104
RSDK_THIS(Platform);
2105
2106
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
2107
2108
bool32 metalInvincibile = false;
2109
foreach_active(MetalSonic, metal)
2110
{
2111
if (metal->invincibilityTimer)
2112
metalInvincibile = true;
2113
}
2114
2115
if (metalInvincibile || player1->blinkTimer) {
2116
self->type = 120;
2117
self->type--;
2118
if (self->velocity.x > 0x40000)
2119
self->velocity.x -= 0x1000;
2120
}
2121
else {
2122
if (self->type <= 0) {
2123
if (self->velocity.x < 0x54000) {
2124
self->velocity.x += 0x400;
2125
}
2126
else {
2127
self->type--;
2128
if (self->velocity.x > 0x40000)
2129
self->velocity.x -= 0x1000;
2130
}
2131
}
2132
else {
2133
self->type--;
2134
if (self->velocity.x > 0x40000)
2135
self->velocity.x -= 0x1000;
2136
}
2137
}
2138
2139
Zone->autoScrollSpeed = self->velocity.x;
2140
self->drawPos.x += self->velocity.x;
2141
self->centerPos.x = self->drawPos.x;
2142
self->position.x = self->drawPos.x;
2143
2144
int32 x = player1->position.x - 0x2000000;
2145
if (x < self->position.x - 0x2000000 || x > self->position.x) {
2146
self->drawPos.x = x;
2147
self->centerPos.x = x;
2148
self->position.x = x;
2149
}
2150
2151
if (self->speed == 1) {
2152
EntityPlatform *belowPlat = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, Platform);
2153
belowPlat->drawPos.x = self->position.x;
2154
belowPlat->centerPos.x = self->position.x;
2155
belowPlat->position.x = self->position.x;
2156
}
2157
}
2158
#endif
2159
2160
#if GAME_INCLUDE_EDITOR
2161
void MetalSonic_EditorDraw(void)
2162
{
2163
RSDK_THIS(MetalSonic);
2164
2165
self->drawFX = FX_FLIP | FX_ROTATE;
2166
self->updateRange.x = 0x800000;
2167
self->updateRange.y = 0x800000;
2168
self->tileCollisions = TILECOLLISION_DOWN;
2169
RSDK.SetSpriteAnimation(MetalSonic->aniFrames, MS_ANI_IDLE, &self->metalSonicAnimator, false, 0);
2170
2171
RSDK.DrawSprite(&self->boosterAnimator, NULL, false);
2172
RSDK.DrawSprite(&self->metalSonicAnimator, NULL, false);
2173
2174
if (showGizmos()) {
2175
RSDK_DRAWING_OVERLAY(true);
2176
2177
DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER, -SCREEN_YSIZE + 52, WIDE_SCR_XCENTER, 52, 1 | 2 | 4 | 8, 0x00C0F0);
2178
2179
RSDK_DRAWING_OVERLAY(false);
2180
}
2181
}
2182
2183
void MetalSonic_EditorLoad(void) { MetalSonic->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/MetalSonic.bin", SCOPE_STAGE); }
2184
#endif
2185
2186
void MetalSonic_Serialize(void) {}
2187
2188