Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/ERZ/PhantomMystic.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: PhantomMystic Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectPhantomMystic *PhantomMystic;
11
12
void PhantomMystic_Update(void)
13
{
14
RSDK_THIS(PhantomMystic);
15
16
if (self->invincibilityTimer > 0)
17
self->invincibilityTimer--;
18
19
StateMachine_Run(self->state);
20
21
RSDK.ProcessAnimation(&self->mysticAnimator);
22
}
23
24
void PhantomMystic_LateUpdate(void) {}
25
26
void PhantomMystic_StaticUpdate(void) {}
27
28
void PhantomMystic_Draw(void)
29
{
30
RSDK_THIS(PhantomMystic);
31
32
RSDK.SetActivePalette(4, 0, ScreenInfo[SceneInfo->currentScreenID].size.y);
33
34
if (self->stateDraw) {
35
StateMachine_Run(self->stateDraw);
36
}
37
else {
38
RSDK.DrawSprite(&self->mysticAnimator, NULL, false);
39
}
40
41
RSDK.SetActivePalette(0, 0, ScreenInfo[SceneInfo->currentScreenID].size.y);
42
}
43
44
void PhantomMystic_Create(void *data)
45
{
46
RSDK_THIS(PhantomMystic);
47
48
self->drawFX = FX_FLIP;
49
if (!SceneInfo->inEditor) {
50
self->visible = true;
51
self->drawGroup = Zone->objectDrawGroup[0];
52
self->active = ACTIVE_NEVER;
53
self->updateRange.x = 0x800000;
54
self->updateRange.y = 0x800000;
55
56
self->unused1[0] = 0;
57
self->unused1[1] = 1;
58
self->unused1[2] = 2;
59
60
self->cupAlpha[0] = 0x60;
61
self->cupAlpha[1] = 0;
62
self->cupAlpha[2] = 0x60;
63
64
self->hitbox.left = -12;
65
self->hitbox.top = -12;
66
self->hitbox.right = 12;
67
self->hitbox.bottom = 12;
68
69
self->correctCup = 1;
70
self->state = PhantomMystic_State_Init;
71
self->stateDraw = PhantomMystic_Draw_CupSetup;
72
73
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 2, &self->mysticAnimator, true, 0);
74
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 0, &self->cupAnimator, true, 0);
75
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 0, &self->cupSilhouetteAnimator, true, 1);
76
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 1, &self->cupSpikeAnimator, true, 0);
77
}
78
}
79
80
void PhantomMystic_StageLoad(void)
81
{
82
PhantomMystic->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomMystic.bin", SCOPE_STAGE);
83
84
PhantomMystic->sfxCupSwap = RSDK.GetSfx("TMZ3/CupSwap.wav");
85
PhantomMystic->sfxBigLaser = RSDK.GetSfx("TMZ3/BigLaser.wav");
86
PhantomMystic->sfxImpact = RSDK.GetSfx("Stage/Impact2.wav");
87
}
88
89
void PhantomMystic_CheckPlayerCollisions(void)
90
{
91
RSDK_THIS(PhantomMystic);
92
93
int32 storeX = self->position.x;
94
int32 storeY = self->position.y;
95
96
self->position.x = self->mysticPos.x;
97
self->position.y = self->mysticPos.y;
98
99
foreach_active(Player, player)
100
{
101
if (!self->invincibilityTimer && Player_CheckBadnikTouch(player, self, &self->hitbox) && Player_CheckBossHit(player, self)) {
102
PhantomMystic_Hit();
103
}
104
105
if (self->cupBlastAnimator.frameID > 8 && self->cupBlastAnimator.frameID < 26) {
106
for (int32 i = 0; i < 3; ++i) {
107
if (i != self->correctCup) {
108
self->position.x = storeX + self->cupPos[i];
109
if (abs(self->position.x - player->position.x) < 0x400000 && player->position.y > self->position.y)
110
Player_Hurt(player, self);
111
}
112
}
113
}
114
115
self->position.x = self->mysticPos.x;
116
}
117
118
self->position.x = storeX;
119
self->position.y = storeY;
120
}
121
122
void PhantomMystic_Hit(void)
123
{
124
RSDK_THIS(PhantomMystic);
125
126
self->invincibilityTimer = 48;
127
RSDK.PlaySfx(PhantomEgg->sfxHit, false, 255);
128
}
129
130
void PhantomMystic_SetupNewCupSwap(void)
131
{
132
RSDK_THIS(PhantomMystic);
133
134
self->swapCup1 = RSDK.Rand(0, 3);
135
136
switch (self->swapCup1) {
137
case 0: self->swapCup2 = (RSDK.Rand(0, 256) > 128) + 1; break;
138
case 1: self->swapCup2 = RSDK.Rand(0, 256) <= 128 ? 2 : 0; break;
139
case 2: self->swapCup2 = RSDK.Rand(0, 256) <= 128; break;
140
}
141
142
self->swapCup1Pos = self->cupPos[self->swapCup1];
143
self->swapCup1Alpha = self->cupAlpha[self->swapCup1];
144
145
self->swapCup2Pos = self->cupPos[self->swapCup2];
146
self->swapCup2Alpha = self->cupAlpha[self->swapCup2];
147
}
148
149
void PhantomMystic_Draw_CupSetup(void)
150
{
151
RSDK_THIS(PhantomMystic);
152
153
RSDK.DrawSprite(&self->mysticAnimator, &self->mysticPos, false);
154
155
for (int32 i = 0; i < 3; ++i) {
156
Vector2 drawPos;
157
drawPos.x = self->position.x + self->cupPos[i];
158
if (i == 1) {
159
drawPos.y = self->middleCupY;
160
self->drawFX = FX_ROTATE | FX_FLIP;
161
}
162
else {
163
drawPos.y = self->position.y;
164
}
165
166
self->direction = FLIP_X;
167
RSDK.DrawSprite(&self->cupAnimator, &drawPos, false);
168
169
self->direction = FLIP_NONE;
170
RSDK.DrawSprite(&self->cupAnimator, &drawPos, false);
171
172
self->inkEffect = INK_ALPHA;
173
self->drawFX = FX_FLIP;
174
self->alpha = self->cupAlpha[i];
175
self->direction = FLIP_X;
176
RSDK.DrawSprite(&self->cupSilhouetteAnimator, &drawPos, false);
177
178
self->direction = FLIP_NONE;
179
RSDK.DrawSprite(&self->cupSilhouetteAnimator, &drawPos, false);
180
181
self->inkEffect = INK_NONE;
182
}
183
}
184
185
void PhantomMystic_Draw_CupSwap(void)
186
{
187
RSDK_THIS(PhantomMystic);
188
189
Vector2 drawPos = self->position;
190
if (self->invincibilityTimer & 1) {
191
RSDK.CopyPalette(6, 128, 4, 128, 128);
192
193
RSDK.DrawSprite(&self->mysticAnimator, &self->mysticPos, false);
194
195
RSDK.CopyPalette(5, 128, 4, 128, 128);
196
}
197
else {
198
RSDK.DrawSprite(&self->mysticAnimator, &self->mysticPos, false);
199
}
200
201
for (int32 i = 0; i < 3; ++i) {
202
drawPos.x = self->position.x + self->cupPos[i];
203
self->direction = FLIP_X;
204
RSDK.DrawSprite(&self->cupAnimator, &drawPos, false);
205
206
self->direction = FLIP_NONE;
207
RSDK.DrawSprite(&self->cupAnimator, &drawPos, false);
208
209
self->inkEffect = INK_ALPHA;
210
self->alpha = self->cupAlpha[i];
211
self->direction = FLIP_X;
212
RSDK.DrawSprite(&self->cupSilhouetteAnimator, &drawPos, false);
213
214
self->direction = FLIP_NONE;
215
RSDK.DrawSprite(&self->cupSilhouetteAnimator, &drawPos, false);
216
217
self->inkEffect = INK_NONE;
218
if (i != self->correctCup)
219
RSDK.DrawSprite(&self->cupBlastAnimator, &drawPos, false);
220
}
221
}
222
223
void PhantomMystic_State_Init(void)
224
{
225
RSDK_THIS(PhantomMystic);
226
227
self->mysticPos.x = self->position.x;
228
self->velocity.y = 0x40000;
229
self->mysticVelY = -0x80000;
230
self->mysticPos.y = self->position.y;
231
self->middleCupY = self->position.y;
232
233
self->state = PhantomMystic_State_SetupInitialCupPos;
234
}
235
236
void PhantomMystic_State_SetupInitialCupPos(void)
237
{
238
RSDK_THIS(PhantomMystic);
239
240
self->mysticVelY += 0x3800;
241
self->mysticPos.y += self->mysticVelY;
242
243
self->velocity.y -= 0x2800;
244
self->middleCupY += self->velocity.y;
245
246
if (self->middleCupY <= self->position.y) {
247
self->velocity.y = 0;
248
self->middleCupY = self->position.y;
249
}
250
251
self->cupPos[0] += (-0x800000 - self->cupPos[0]) >> 4;
252
self->cupPos[2] += ((0x800000 - self->cupPos[2]) >> 4);
253
254
if (self->rotation < 0x100) {
255
self->rotation += 0x10;
256
}
257
258
if (++self->timer == 60) {
259
self->cupPos[0] = -0x800000;
260
self->cupPos[2] = 0x800000;
261
self->timer = 0;
262
263
self->state = PhantomMystic_State_MoveCupsDownwards;
264
}
265
}
266
267
void PhantomMystic_State_MoveCupsDownwards(void)
268
{
269
RSDK_THIS(PhantomMystic);
270
271
self->mysticVelY += 0x3800;
272
self->mysticPos.y += self->mysticVelY;
273
274
self->velocity.y += 0x3800;
275
self->position.y += self->velocity.y;
276
277
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x380000, true))
278
self->velocity.y = 0;
279
280
if (self->mysticPos.y >= self->position.y - 0x180000) {
281
self->mysticPos.y = 0;
282
self->mysticVelY = 0;
283
self->onGround = false;
284
285
self->state = PhantomMystic_State_RotateMiddleCup;
286
}
287
}
288
289
void PhantomMystic_State_RotateMiddleCup(void)
290
{
291
RSDK_THIS(PhantomMystic);
292
293
// This does continue to move the side cups downwards too if they're not on the ground yet
294
self->velocity.y += 0x4000;
295
self->position.y += self->velocity.y;
296
297
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x380000, true)) {
298
if (!self->onGround)
299
Camera_ShakeScreen(0, 0, 4);
300
301
self->velocity.y = 0;
302
self->onGround = true;
303
}
304
305
if (self->rotation >= 0x200) {
306
self->rotation = 0;
307
self->state = PhantomMystic_State_MoveMiddleCupToFloor;
308
}
309
else {
310
self->rotation += 0x10;
311
}
312
}
313
314
void PhantomMystic_State_MoveMiddleCupToFloor(void)
315
{
316
RSDK_THIS(PhantomMystic);
317
318
self->velocity.y += 0x4000;
319
self->middleCupY += self->velocity.y;
320
321
if (self->middleCupY >= self->position.y) {
322
Camera_ShakeScreen(0, 0, 4);
323
RSDK.PlaySfx(PhantomMystic->sfxImpact, false, 255);
324
325
self->velocity.y = 0;
326
self->middleCupY = self->position.y;
327
self->stateDraw = PhantomMystic_Draw_CupSwap;
328
self->state = PhantomMystic_State_PrepareCupSwap;
329
}
330
}
331
332
void PhantomMystic_State_PrepareCupSwap(void)
333
{
334
RSDK_THIS(PhantomMystic);
335
336
++self->timer;
337
if (self->cupAlpha[0] < 0x90) {
338
self->cupAlpha[0] += 4;
339
self->cupAlpha[2] += 4;
340
}
341
342
if (self->timer == 30) {
343
self->timer = 0;
344
self->cupSwapCount = 6;
345
PhantomMystic_SetupNewCupSwap();
346
RSDK.PlaySfx(PhantomMystic->sfxCupSwap, false, 255);
347
self->state = PhantomMystic_State_CupSwapping;
348
}
349
}
350
351
void PhantomMystic_State_CupSwapping(void)
352
{
353
RSDK_THIS(PhantomMystic);
354
355
if (abs(self->swapCup2Pos - self->swapCup1Pos) <= 0x800000)
356
self->timer += 16;
357
else
358
self->timer += 8;
359
360
int32 cup1 = self->swapCup1;
361
int32 cup2 = self->swapCup2;
362
363
// Use Lerp Math to move each cup to the other's initial position
364
int32 cup1Pos = self->swapCup1Pos;
365
if (self->timer > 0) {
366
if (self->timer < 256)
367
cup1Pos += ((self->swapCup2Pos - cup1Pos) >> 8) * ((RSDK.Sin512(self->timer + 0x180) >> 2) + 0x80);
368
else
369
cup1Pos = self->swapCup2Pos;
370
}
371
self->cupPos[cup1] = cup1Pos;
372
373
int32 cup2Pos = self->swapCup2Pos;
374
if (self->timer > 0) {
375
if (self->timer < 256)
376
cup2Pos += ((self->swapCup1Pos - cup2Pos) >> 8) * ((RSDK.Sin512(self->timer + 0x180) >> 2) + 0x80);
377
else
378
cup2Pos = self->swapCup1Pos;
379
}
380
self->cupPos[cup2] = cup2Pos;
381
382
self->cupAlpha[cup1] = abs(self->cupPos[cup1]) / 0xE38E;
383
self->cupAlpha[cup2] = abs(self->cupPos[cup2]) / 0xE38E;
384
385
if (self->timer == 0x100) {
386
self->timer = 0;
387
388
if (self->cupSwapCount <= 1) {
389
self->middleCupY -= 0x600000;
390
self->mysticPos.x = self->position.x + self->cupPos[self->correctCup];
391
self->mysticPos.y = self->position.y;
392
self->originPos.x = self->mysticPos.x;
393
self->originPos.y = self->mysticPos.y;
394
self->velocity.y = -0x10000;
395
self->state = PhantomMystic_State_RevealMystic;
396
}
397
else {
398
self->cupSwapCount--;
399
RSDK.PlaySfx(PhantomMystic->sfxCupSwap, false, 255);
400
PhantomMystic_SetupNewCupSwap();
401
}
402
}
403
}
404
405
void PhantomMystic_State_RevealMystic(void)
406
{
407
RSDK_THIS(PhantomMystic);
408
409
self->velocity.y -= 0x3800;
410
self->position.y += self->velocity.y;
411
412
self->cupAlpha[0] -= self->cupAlpha[0] >> 4;
413
self->cupAlpha[1] -= self->cupAlpha[1] >> 4;
414
self->cupAlpha[2] -= self->cupAlpha[2] >> 4;
415
416
if (self->position.y <= self->middleCupY) {
417
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 3, &self->cupBlastAnimator, true, 0);
418
self->velocity.y = 0;
419
self->position.y = self->middleCupY;
420
self->state = PhantomMystic_State_CupBlast;
421
}
422
423
self->mysticPos.y = BadnikHelpers_Oscillate(self->originPos.y, 4, 11);
424
425
if (self->position.y - self->middleCupY < 0x200000)
426
PhantomMystic_CheckPlayerCollisions();
427
}
428
429
void PhantomMystic_State_CupBlast(void)
430
{
431
RSDK_THIS(PhantomMystic);
432
433
RSDK.ProcessAnimation(&self->cupBlastAnimator);
434
435
self->cupAlpha[0] -= self->cupAlpha[0] >> 4;
436
self->cupAlpha[1] -= self->cupAlpha[1] >> 4;
437
self->cupAlpha[2] -= self->cupAlpha[2] >> 4;
438
439
if (self->cupBlastAnimator.frameID == self->cupBlastAnimator.frameCount - 1) {
440
self->mysticVelY = 0;
441
RSDK.PlaySfx(PhantomMystic->sfxCupSwap, false, 255);
442
self->state = PhantomMystic_State_MoveCupsToMystic;
443
}
444
445
self->mysticPos.y = BadnikHelpers_Oscillate(self->originPos.y, 4, 11);
446
447
PhantomMystic_CheckPlayerCollisions();
448
}
449
450
void PhantomMystic_State_MoveCupsToMystic(void)
451
{
452
RSDK_THIS(PhantomMystic);
453
454
self->cupPos[0] += (self->cupPos[self->correctCup] - self->cupPos[0]) >> 3;
455
self->cupPos[1] += (self->cupPos[self->correctCup] - self->cupPos[1]) >> 3;
456
self->cupPos[2] += (self->cupPos[self->correctCup] - self->cupPos[2]) >> 3;
457
458
self->mysticVelY -= 0x3000;
459
self->mysticPos.y += self->mysticVelY;
460
461
if (self->mysticPos.y < self->position.y)
462
self->mysticPos.y = self->position.y;
463
464
PhantomMystic_CheckPlayerCollisions();
465
466
if (++self->timer == 60)
467
PhantomEgg_SetupWarpFX();
468
469
if (self->timer == 120) {
470
int32 x = self->position.x;
471
int32 y = self->position.y;
472
RSDK.ResetEntity(self, PhantomMystic->classID, NULL);
473
474
self->position.x = x;
475
self->position.y = y;
476
}
477
}
478
479
#if GAME_INCLUDE_EDITOR
480
void PhantomMystic_EditorDraw(void)
481
{
482
RSDK_THIS(PhantomMystic);
483
484
self->mysticPos = self->position;
485
self->middleCupY = self->position.y;
486
487
self->cupAlpha[0] = 0x60;
488
self->cupAlpha[1] = 0;
489
self->cupAlpha[2] = 0x60;
490
491
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 2, &self->mysticAnimator, true, 0);
492
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 0, &self->cupAnimator, true, 0);
493
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 0, &self->cupSilhouetteAnimator, true, 1);
494
RSDK.SetSpriteAnimation(PhantomMystic->aniFrames, 1, &self->cupSpikeAnimator, true, 0);
495
496
PhantomMystic_Draw_CupSetup();
497
}
498
499
void PhantomMystic_EditorLoad(void) { PhantomMystic->aniFrames = RSDK.LoadSpriteAnimation("Phantom/PhantomMystic.bin", SCOPE_STAGE); }
500
#endif
501
502
void PhantomMystic_Serialize(void) {}
503
504