Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/TMZ/CrimsonEye.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: CrimsonEye Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectCrimsonEye *CrimsonEye;
11
12
void CrimsonEye_Update(void)
13
{
14
RSDK_THIS(CrimsonEye);
15
16
StateMachine_Run(self->state);
17
}
18
19
void CrimsonEye_LateUpdate(void) {}
20
21
void CrimsonEye_StaticUpdate(void) {}
22
23
void CrimsonEye_Draw(void)
24
{
25
RSDK_THIS(CrimsonEye);
26
27
StateMachine_Run(self->stateDraw);
28
}
29
30
void CrimsonEye_Create(void *data)
31
{
32
RSDK_THIS(CrimsonEye);
33
34
if (!SceneInfo->inEditor) {
35
if (globals->gameMode < MODE_TIMEATTACK) {
36
if (data)
37
self->type = VOID_TO_INT(data);
38
39
switch (self->type) {
40
case CRIMSONEYE_CONTAINER:
41
self->active = ACTIVE_BOUNDS;
42
self->visible = true;
43
self->drawGroup = Zone->objectDrawGroup[0] + 1;
44
45
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 0, &self->animator, true, 0);
46
47
self->updateRange.x = 0x800000;
48
self->updateRange.y = 0x800000;
49
CrimsonEye->containerPos.x = self->position.x;
50
CrimsonEye->containerPos.y = self->position.y - 0xA00000;
51
52
self->state = CrimsonEye_StateContainer_SetupArena;
53
self->stateDraw = CrimsonEye_Draw_Container;
54
break;
55
56
case CRIMSONEYE_CORE:
57
self->active = ACTIVE_NEVER;
58
self->visible = true;
59
self->drawGroup = Zone->objectDrawGroup[0] + 1;
60
61
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &self->animator, true, 0);
62
self->originPos = self->position;
63
64
self->state = CrimsonEye_StateCore_ContainerActive;
65
self->stateDraw = CrimsonEye_Draw_Core;
66
67
self->updateRange.x = 0x800000;
68
self->updateRange.y = 0x800000;
69
break;
70
71
case CRIMSONEYE_BALL:
72
self->drawFX = FX_SCALE;
73
self->active = ACTIVE_NEVER;
74
self->visible = true;
75
self->drawGroup = Zone->objectDrawGroup[0] + 1;
76
77
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 2, &self->animator, true, 0);
78
self->scale.x = 0x200;
79
self->scale.y = 0x200;
80
81
self->state = CrimsonEye_StateBall_Spinning;
82
self->stateDraw = CrimsonEye_Draw_Simple;
83
84
self->updateRange.x = 0x800000;
85
self->updateRange.y = 0x800000;
86
break;
87
88
case CRIMSONEYE_ARROW:
89
self->active = ACTIVE_BOUNDS;
90
self->visible = true;
91
self->drawGroup = Zone->objectDrawGroup[0];
92
93
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 3, &self->animator, true, 0);
94
self->scale.x = 0x200;
95
self->scale.y = 0x200;
96
97
self->state = CrimsonEye_State_Arrow;
98
self->stateDraw = CrimsonEye_Draw_Arrow;
99
100
self->updateRange.x = 0x800000;
101
self->updateRange.y = 0x800000;
102
break;
103
104
case CRIMSONEYE_SPIKE:
105
self->active = ACTIVE_NORMAL;
106
self->visible = true;
107
self->drawGroup = Zone->objectDrawGroup[0] + 1;
108
109
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 2, &self->animator, true, 0);
110
111
self->state = CrimsonEye_StateSpike_Harmful;
112
self->stateDraw = CrimsonEye_Draw_Simple;
113
114
self->updateRange.x = 0x800000;
115
self->updateRange.y = 0x800000;
116
break;
117
118
case CRIMSONEYE_SHOT:
119
self->active = ACTIVE_NORMAL;
120
self->visible = true;
121
self->drawGroup = Zone->objectDrawGroup[0] + 1;
122
123
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 4, &self->animator, true, 0);
124
125
self->state = CrimsonEye_State_Shot;
126
self->stateDraw = CrimsonEye_Draw_Simple;
127
128
self->updateRange.x = 0x800000;
129
self->updateRange.y = 0x800000;
130
break;
131
132
default: break;
133
}
134
}
135
else {
136
destroyEntity(self);
137
}
138
}
139
}
140
141
void CrimsonEye_StageLoad(void)
142
{
143
CrimsonEye->aniFrames = RSDK.LoadSpriteAnimation("TMZ1/CrimsonEye.bin", SCOPE_STAGE);
144
145
CrimsonEye->hitboxEye.left = -12;
146
CrimsonEye->hitboxEye.top = -12;
147
CrimsonEye->hitboxEye.right = 12;
148
CrimsonEye->hitboxEye.bottom = 12;
149
150
CrimsonEye->hitboxBall.left = -16;
151
CrimsonEye->hitboxBall.top = -16;
152
CrimsonEye->hitboxBall.right = 16;
153
CrimsonEye->hitboxBall.bottom = 16;
154
155
CrimsonEye->hitboxOrb.left = -3;
156
CrimsonEye->hitboxOrb.top = -3;
157
CrimsonEye->hitboxOrb.right = 3;
158
CrimsonEye->hitboxOrb.bottom = 3;
159
160
CrimsonEye->hitboxBlock.left = -21;
161
CrimsonEye->hitboxBlock.top = -23;
162
CrimsonEye->hitboxBlock.right = 21;
163
CrimsonEye->hitboxBlock.bottom = 23;
164
165
CrimsonEye->hitboxElecOrb.left = -6;
166
CrimsonEye->hitboxElecOrb.top = -6;
167
CrimsonEye->hitboxElecOrb.right = 6;
168
CrimsonEye->hitboxElecOrb.bottom = 6;
169
170
CrimsonEye->hitboxUnused1.left = -14;
171
CrimsonEye->hitboxUnused1.top = -32;
172
CrimsonEye->hitboxUnused1.right = 14;
173
CrimsonEye->hitboxUnused1.bottom = -23;
174
175
CrimsonEye->hitboxUnused2.left = -14;
176
CrimsonEye->hitboxUnused2.top = 23;
177
CrimsonEye->hitboxUnused2.right = 14;
178
CrimsonEye->hitboxUnused2.bottom = 32;
179
180
CrimsonEye->health = 17;
181
CrimsonEye->invincibilityTimer = 0;
182
CrimsonEye->ballSpinAngleX = 0;
183
CrimsonEye->ballSpinRadius = 0x4800;
184
CrimsonEye->ballOffsetY = 0;
185
CrimsonEye->elevatorSpeed = 0;
186
CrimsonEye->targetElevatorSpeed = -0x8000;
187
CrimsonEye->elevatorGravity = 0x800;
188
CrimsonEye->destroyedBallCount = 0;
189
CrimsonEye->shotsRemaining = 0;
190
191
CrimsonEye->liftBackground = RSDK.GetTileLayer(RSDK.GetTileLayerID("Lift Background"));
192
193
CrimsonEye->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
194
CrimsonEye->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
195
CrimsonEye->sfxHover = RSDK.GetSfx("TMZ1/Hover.wav");
196
CrimsonEye->sfxHover2 = RSDK.GetSfx("TMZ1/Hover2.wav");
197
CrimsonEye->sfxElevator = RSDK.GetSfx("TMZ1/Elevator.wav");
198
CrimsonEye->sfxShot = RSDK.GetSfx("Stage/Shot.wav");
199
CrimsonEye->sfxBeep = RSDK.GetSfx("Stage/Beep4.wav");
200
CrimsonEye->sfxHullClose = RSDK.GetSfx("Stage/HullClose.wav");
201
CrimsonEye->sfxButton = RSDK.GetSfx("Stage/Button.wav");
202
CrimsonEye->sfxImpact = RSDK.GetSfx("Stage/Impact5.wav");
203
}
204
205
void CrimsonEye_Explode(void)
206
{
207
RSDK_THIS(CrimsonEye);
208
209
if (!(Zone->timer & 3)) {
210
RSDK.PlaySfx(CrimsonEye->sfxExplosion, false, 255);
211
212
int32 x = self->position.x;
213
int32 y = self->position.y;
214
215
if (CrimsonEye->health) {
216
x += RSDK.Rand(-32, 33) << 16;
217
y += RSDK.Rand(-32, 33) << 16;
218
}
219
else {
220
x += RSDK.Rand(-19, 20) << 16;
221
y += RSDK.Rand(-24, 25) << 16;
222
}
223
224
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS)), x, y);
225
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
226
}
227
}
228
229
void CrimsonEye_Hit(void)
230
{
231
RSDK_THIS(CrimsonEye);
232
233
if (CrimsonEye->health)
234
CrimsonEye->health--;
235
236
if (CrimsonEye->health) {
237
if (CrimsonEye->health == 9) {
238
CrimsonEye_DestroyBall();
239
RSDK.PlaySfx(CrimsonEye->sfxExplosion, false, 0xFF);
240
self->timer = 120;
241
self->state = CrimsonEye_StateCore_BreakOut;
242
}
243
else {
244
CrimsonEye_DestroyBall();
245
RSDK.PlaySfx(CrimsonEye->sfxHit, false, 0xFF);
246
CrimsonEye->invincibilityTimer = 48;
247
}
248
}
249
else {
250
RSDK.PlaySfx(CrimsonEye->sfxExplosion, false, 0xFF);
251
self->timer = 120;
252
253
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
254
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 14);
255
debris->velocity.x = -0x30000;
256
debris->velocity.y = -0x40000;
257
debris->gravityStrength = 0x3800;
258
debris->drawGroup = Zone->objectDrawGroup[1];
259
debris->updateRange.x = 0x400000;
260
debris->updateRange.y = 0x400000;
261
debris->delay = 120;
262
263
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
264
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 15);
265
debris->velocity.x = 0x30000;
266
debris->velocity.y = -0x40000;
267
debris->gravityStrength = 0x3800;
268
debris->drawGroup = Zone->objectDrawGroup[1];
269
debris->updateRange.x = 0x400000;
270
debris->updateRange.y = 0x400000;
271
debris->delay = 120;
272
273
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
274
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 16);
275
debris->velocity.x = -0x20000;
276
debris->velocity.y = -0x20000;
277
debris->gravityStrength = 0x3800;
278
debris->drawGroup = Zone->objectDrawGroup[1];
279
debris->updateRange.x = 0x400000;
280
debris->updateRange.y = 0x400000;
281
debris->delay = 120;
282
283
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
284
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 17);
285
debris->velocity.x = 0x20000;
286
debris->velocity.y = -0x20000;
287
debris->gravityStrength = 0x3800;
288
debris->drawGroup = Zone->objectDrawGroup[1];
289
debris->updateRange.x = 0x400000;
290
debris->updateRange.y = 0x400000;
291
debris->delay = 120;
292
293
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
294
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 12);
295
debris->velocity.x = 0;
296
debris->velocity.y = -0x30000;
297
debris->gravityStrength = 0x3800;
298
debris->drawGroup = Zone->objectDrawGroup[1];
299
debris->updateRange.x = 0x400000;
300
debris->updateRange.y = 0x400000;
301
302
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
303
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 13);
304
debris->velocity.x = 0;
305
debris->velocity.y = 0;
306
debris->gravityStrength = 0x3800;
307
debris->drawGroup = Zone->objectDrawGroup[1];
308
debris->updateRange.x = 0x400000;
309
debris->updateRange.y = 0x400000;
310
311
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x - 0x160000, self->position.y);
312
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 4);
313
debris->drawFX = FX_ROTATE;
314
debris->rotation = self->rotation;
315
debris->velocity.x = -0x20000;
316
debris->velocity.y = -0x20000;
317
debris->gravityStrength = 0x3800;
318
debris->drawGroup = Zone->objectDrawGroup[1];
319
debris->updateRange.x = 0x400000;
320
debris->updateRange.y = 0x400000;
321
322
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x + 0x160000, self->position.y);
323
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 5);
324
debris->drawFX = FX_ROTATE;
325
debris->rotation = 0x1FF - self->rotation;
326
debris->velocity.x = 0x20000;
327
debris->velocity.y = -0x20000;
328
debris->gravityStrength = 0x3800;
329
debris->drawGroup = Zone->objectDrawGroup[1];
330
debris->updateRange.x = 0x400000;
331
debris->updateRange.y = 0x400000;
332
333
int32 velX = RSDK.Rand(-6, 6) << 15;
334
int32 velY = RSDK.Rand(-10, -6) << 15;
335
336
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, CrimsonEye->eyePositions[0].x, CrimsonEye->eyePositions[0].y);
337
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 1);
338
debris->velocity.x = velX;
339
debris->velocity.y = velY;
340
debris->gravityStrength = 0x3800;
341
debris->drawGroup = Zone->objectDrawGroup[1];
342
debris->updateRange.x = 0x400000;
343
debris->updateRange.y = 0x400000;
344
debris->delay = 60;
345
346
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, CrimsonEye->eyePositions[1].x, CrimsonEye->eyePositions[1].y);
347
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 2);
348
debris->velocity.x = velX;
349
debris->velocity.y = velY;
350
debris->gravityStrength = 0x3800;
351
debris->drawGroup = Zone->objectDrawGroup[1];
352
debris->updateRange.x = 0x400000;
353
debris->updateRange.y = 0x400000;
354
debris->delay = 60;
355
356
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, CrimsonEye->eyePositions[2].x, CrimsonEye->eyePositions[2].y);
357
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &debris->animator, true, 3);
358
debris->velocity.x = velX;
359
debris->velocity.y = velY;
360
debris->gravityStrength = 0x3800;
361
debris->drawGroup = Zone->objectDrawGroup[1];
362
debris->updateRange.x = 0x400000;
363
debris->updateRange.y = 0x400000;
364
debris->delay = 60;
365
366
SceneInfo->timeEnabled = false;
367
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
368
self->visible = false;
369
self->state = CrimsonEye_StateCore_Explode;
370
}
371
}
372
373
void CrimsonEye_SetupBG2Layer(void)
374
{
375
for (int32 y = 0; y < 48; ++y) {
376
uint16 tile = RSDK.GetTile(1, 0, y + 24 * (2 - y / 24));
377
378
RSDK.SetTile(1, 0, y, tile);
379
RSDK.SetTile(1, 31, y, tile);
380
381
for (int32 x = 1; x < 31; ++x) RSDK.SetTile(1, x, y, -1);
382
}
383
384
TileLayer *bg2Layer = RSDK.GetTileLayer(1);
385
for (int32 i = 0; i < bg2Layer->scrollInfoCount; ++i) {
386
bg2Layer->scrollInfo[i].parallaxFactor = 0x200;
387
bg2Layer->scrollInfo[i].scrollPos = -0x1C0000;
388
}
389
390
bg2Layer->drawGroup[0] = Zone->hudDrawGroup - 1;
391
bg2Layer->parallaxFactor = 0x200;
392
CrimsonEye->bg2Layer = bg2Layer;
393
394
RSDK.SetDrawGroupProperties(Zone->hudDrawGroup - 1, false, CrimsonEye_DrawHook_EnableFGSilhouette);
395
RSDK.SetDrawGroupProperties(Zone->hudDrawGroup, false, CrimsonEye_DrawHook_DisableFGSilhouette);
396
}
397
398
// Manages the black strucures that move in the foreground
399
void CrimsonEye_DrawHook_DisableFGSilhouette(void) { RSDK.SetActivePalette(0, 0, ScreenInfo->size.y); }
400
void CrimsonEye_DrawHook_EnableFGSilhouette(void) { RSDK.SetActivePalette(5, 0, ScreenInfo->size.y); }
401
402
void CrimsonEye_SetArrowDir(int32 type)
403
{
404
foreach_active(CrimsonEye, boss)
405
{
406
if (boss->stateDraw == CrimsonEye_Draw_Arrow)
407
boss->type = type;
408
}
409
}
410
411
void CrimsonEye_StateContainer_SetupArena(void)
412
{
413
RSDK_THIS(CrimsonEye);
414
415
if (++self->timer >= 2) {
416
self->timer = 0;
417
418
Zone->playerBoundActiveR[0] = true;
419
Zone->playerBoundActiveB[0] = true;
420
Zone->cameraBoundsR[0] = (self->position.x >> 16) + ScreenInfo->center.x + 80;
421
Zone->cameraBoundsB[0] = (self->position.y >> 16) + 124;
422
Zone->cameraBoundsT[0] = Zone->cameraBoundsB[0] - ScreenInfo->size.y;
423
424
self->active = ACTIVE_NORMAL;
425
CREATE_ENTITY(TMZ1Setup, NULL, 0, 0);
426
427
RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, CrimsonEye)->active = ACTIVE_NORMAL;
428
for (int32 i = 2; i < 14; ++i) {
429
EntityCrimsonEye *child = RSDK_GET_ENTITY(SceneInfo->entitySlot + i, CrimsonEye);
430
child->active = ACTIVE_NORMAL;
431
if (i < 10)
432
child->angle = 32 * (9 - i);
433
}
434
435
self->state = CrimsonEye_StateContainer_AwaitPlayer;
436
}
437
}
438
439
void CrimsonEye_StateContainer_AwaitPlayer(void)
440
{
441
RSDK_THIS(CrimsonEye);
442
443
CrimsonEye->ballSpinAngleX += 4;
444
445
Zone->playerBoundActiveL[0] = true;
446
Zone->cameraBoundsL[0] = ScreenInfo->position.x;
447
448
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
449
450
if (!self->timer && ScreenInfo->position.x + ScreenInfo->center.x > (self->position.x >> 16) - 256) {
451
CrimsonEye_SetupBG2Layer();
452
self->timer = 1;
453
}
454
455
if (player1->position.x > self->position.x - 0x500000) {
456
Zone->playerBoundActiveL[0] = true;
457
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x - 80;
458
459
Music_TransitionTrack(TRACK_MINIBOSS, 0.0125);
460
RSDK.PlaySfx(CrimsonEye->sfxElevator, false, 255);
461
462
EntityCamera *camera = RSDK_GET_ENTITY(SLOT_CAMERA1, Camera);
463
if (!camera->shakePos.y)
464
camera->shakePos.y = 4;
465
466
self->timer = 15;
467
self->state = CrimsonEye_StateContainer_StartFight;
468
}
469
}
470
471
void CrimsonEye_StateContainer_StartFight(void)
472
{
473
RSDK_THIS(CrimsonEye);
474
475
CrimsonEye->ballSpinAngleX += 4;
476
477
if (!--self->timer) {
478
self->stateEye = CrimsonEye_StateEye_EnterBalls;
479
self->state = CrimsonEye_StateContainer_CoreActive;
480
}
481
}
482
483
void CrimsonEye_StateContainer_CoreActive(void)
484
{
485
RSDK_THIS(CrimsonEye);
486
487
StateMachine_Run(self->stateEye);
488
489
if (CrimsonEye->elevatorSpeed == CrimsonEye->targetElevatorSpeed && CrimsonEye->elevatorGravity != 0x800 && CrimsonEye->health) {
490
if (--self->timer == 30) {
491
RSDK.PlaySfx(CrimsonEye->sfxBeep, false, 255);
492
493
if (CrimsonEye->targetElevatorSpeed) {
494
CrimsonEye->nextTargetElevatorSpeed = 0;
495
CrimsonEye_SetArrowDir(CE_ARROW_OFF);
496
}
497
else if (RSDK.Rand(0, 3)) {
498
CrimsonEye->nextTargetElevatorSpeed = -0x80000;
499
CrimsonEye_SetArrowDir(CE_ARROW_UP);
500
}
501
else {
502
CrimsonEye->nextTargetElevatorSpeed = 0x80000;
503
CrimsonEye_SetArrowDir(CE_ARROW_DOWN);
504
}
505
}
506
507
if (self->timer <= 0) {
508
RSDK.PlaySfx(CrimsonEye->sfxElevator, false, 255);
509
510
Camera_ShakeScreen(0, 4, 8);
511
512
RSDK.PlaySfx(CrimsonEye->sfxHullClose, false, 255);
513
514
self->timer = RSDK.Rand(120, 180);
515
CrimsonEye->targetElevatorSpeed = CrimsonEye->nextTargetElevatorSpeed;
516
517
EntityCrimsonEye *child = RSDK_GET_ENTITY(SceneInfo->entitySlot + 1, CrimsonEye);
518
519
if (CrimsonEye->targetElevatorSpeed != -0x80000) {
520
if (!CrimsonEye->targetElevatorSpeed) {
521
child->velocity.y = -CrimsonEye->elevatorSpeed;
522
CrimsonEye->elevatorSpeed = 0;
523
}
524
else {
525
if (CrimsonEye->targetElevatorSpeed == 0x80000)
526
child->velocity.y = CrimsonEye->elevatorSpeed >> 2;
527
}
528
}
529
else
530
child->velocity.y = CrimsonEye->elevatorSpeed >> 2;
531
}
532
}
533
534
// Same code as CrimsonEye_StateContainer_HandleElevator() but using CrimsonEye->elevatorGravity instead of 0x800
535
if (CrimsonEye->elevatorSpeed != CrimsonEye->targetElevatorSpeed) {
536
if (CrimsonEye->elevatorSpeed >= CrimsonEye->targetElevatorSpeed) {
537
CrimsonEye->elevatorSpeed -= CrimsonEye->elevatorGravity;
538
539
if (CrimsonEye->elevatorSpeed < CrimsonEye->targetElevatorSpeed)
540
CrimsonEye->elevatorSpeed = CrimsonEye->targetElevatorSpeed;
541
}
542
else {
543
CrimsonEye->elevatorSpeed += CrimsonEye->elevatorGravity;
544
545
if (CrimsonEye->elevatorSpeed > CrimsonEye->targetElevatorSpeed)
546
CrimsonEye->elevatorSpeed = CrimsonEye->targetElevatorSpeed;
547
}
548
}
549
550
for (int32 i = 0; i < PLAYER_COUNT; ++i) {
551
EntityPlayer *player = RSDK_GET_ENTITY(i, Player);
552
// NOTE:
553
// according to IDA, the original code is
554
// if (player->classID)
555
// but this crashes the game when super & without a sidekick so this fix will stay
556
if (player->classID == Player->classID)
557
player->gravityStrength = 0x3800 - CrimsonEye->elevatorSpeed / 0x30;
558
}
559
560
TileLayer *elevatorLayer = CrimsonEye->liftBackground;
561
elevatorLayer->scrollPos = -0x800000;
562
for (int32 i = 0; i < elevatorLayer->scrollInfoCount; ++i) {
563
elevatorLayer->scrollInfo[i].scrollSpeed = elevatorLayer->scrollInfo[i].parallaxFactor * (CrimsonEye->elevatorSpeed / 24);
564
}
565
566
CrimsonEye->bg2Layer->scrollSpeed = 6 * CrimsonEye->elevatorSpeed;
567
//
568
569
if (CrimsonEye->health == 9)
570
self->stateEye = StateMachine_None;
571
572
if (CrimsonEye->health == 8 && !self->animator.frameID) {
573
self->animator.frameID = 1;
574
self->drawGroup = Zone->objectDrawGroup[0];
575
576
// Destroy Container
577
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
578
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 0, &debris->animator, true, 2);
579
debris->velocity.x = -0x20000;
580
debris->velocity.y = -0x20000;
581
debris->gravityStrength = 0x3800;
582
debris->drawGroup = Zone->objectDrawGroup[1];
583
debris->updateRange.x = 0x400000;
584
debris->updateRange.y = 0x400000;
585
debris->delay = 60;
586
587
debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->position.x, self->position.y);
588
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 0, &debris->animator, true, 3);
589
debris->velocity.x = 0x20000;
590
debris->velocity.y = -0x20000;
591
debris->gravityStrength = 0x3800;
592
debris->drawGroup = Zone->objectDrawGroup[1];
593
debris->updateRange.x = 0x400000;
594
debris->updateRange.y = 0x400000;
595
debris->delay = 60;
596
}
597
598
if (self->drawGroup > Zone->objectDrawGroup[0]) {
599
foreach_active(Ring, ring)
600
{
601
if (ring->drawGroup == Zone->objectDrawGroup[0])
602
ring->drawGroup++;
603
}
604
}
605
606
if (!CrimsonEye->health) {
607
CrimsonEye->targetElevatorSpeed = 0;
608
self->timer = 0;
609
self->state = CrimsonEye_StateContainer_HandleElevator;
610
611
foreach_active(CrimsonEye, part)
612
{
613
if (part->stateDraw == CrimsonEye_Draw_Arrow)
614
part->type = CE_ARROW_OFF;
615
}
616
}
617
}
618
619
void CrimsonEye_StateContainer_HandleElevator(void)
620
{
621
if (CrimsonEye->elevatorSpeed != CrimsonEye->targetElevatorSpeed) {
622
if (CrimsonEye->elevatorSpeed >= CrimsonEye->targetElevatorSpeed) {
623
CrimsonEye->elevatorSpeed -= 0x800;
624
625
if (CrimsonEye->elevatorSpeed < CrimsonEye->targetElevatorSpeed)
626
CrimsonEye->elevatorSpeed = CrimsonEye->targetElevatorSpeed;
627
}
628
else {
629
CrimsonEye->elevatorSpeed += 0x800;
630
631
if (CrimsonEye->elevatorSpeed > CrimsonEye->targetElevatorSpeed)
632
CrimsonEye->elevatorSpeed = CrimsonEye->targetElevatorSpeed;
633
}
634
}
635
636
for (int32 i = 0; i < PLAYER_COUNT; ++i) {
637
EntityPlayer *player = RSDK_GET_ENTITY(i, Player);
638
// NOTE:
639
// according to IDA, the original code is
640
// if (player->classID)
641
// but this crashes the game when super & without a sidekick so this fix will stay
642
if (player->classID == Player->classID)
643
player->gravityStrength = 0x3800 - CrimsonEye->elevatorSpeed / 0x30;
644
}
645
646
TileLayer *elevatorLayer = CrimsonEye->liftBackground;
647
elevatorLayer->scrollPos = -0x800000;
648
for (int32 i = 0; i < elevatorLayer->scrollInfoCount; ++i) {
649
elevatorLayer->scrollInfo[i].scrollSpeed = elevatorLayer->scrollInfo[i].parallaxFactor * (CrimsonEye->elevatorSpeed / 24);
650
}
651
652
CrimsonEye->bg2Layer->scrollSpeed = 6 * CrimsonEye->elevatorSpeed;
653
}
654
655
void CrimsonEye_StateContainer_Explode(void)
656
{
657
RSDK_THIS(CrimsonEye);
658
659
if (self->timer < 48) {
660
if (!(Zone->timer & 3)) {
661
RSDK.PlaySfx(ItemBox->sfxDestroy, false, 255);
662
663
int32 x = self->position.x + RSDK.Rand(-0x200000, 0x200000);
664
int32 y = self->position.y + RSDK.Rand(-0x800000, 0x800000);
665
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_ENEMY), x, y);
666
667
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
668
}
669
}
670
671
if (++self->timer == 48) {
672
Debris_CreateFromEntries(CrimsonEye->aniFrames, CrimsonEye->debrisInfo, 5);
673
self->visible = false;
674
Camera_ShakeScreen(0, 0, 4);
675
self->state = CrimsonEye_StateContainer_MoveElevatorToTMZ2Entry;
676
}
677
}
678
679
void CrimsonEye_StateContainer_MoveElevatorToTMZ2Entry(void)
680
{
681
if (CrimsonEye->elevatorSpeed != CrimsonEye->targetElevatorSpeed) {
682
if (CrimsonEye->elevatorSpeed >= CrimsonEye->targetElevatorSpeed) {
683
CrimsonEye->elevatorSpeed -= 0x800;
684
685
if (CrimsonEye->elevatorSpeed < CrimsonEye->targetElevatorSpeed)
686
CrimsonEye->elevatorSpeed = CrimsonEye->targetElevatorSpeed;
687
}
688
else {
689
CrimsonEye->elevatorSpeed += 0x800;
690
691
if (CrimsonEye->elevatorSpeed > CrimsonEye->targetElevatorSpeed)
692
CrimsonEye->elevatorSpeed = CrimsonEye->targetElevatorSpeed;
693
}
694
}
695
696
TileLayer *liftLayer = CrimsonEye->liftBackground;
697
liftLayer->scrollPos = -0x800000;
698
for (int32 i = 0; i < liftLayer->scrollInfoCount; ++i) {
699
liftLayer->scrollInfo[i].scrollSpeed = liftLayer->scrollInfo[i].parallaxFactor * (CrimsonEye->elevatorSpeed / 24);
700
}
701
702
CrimsonEye->bg2Layer->scrollSpeed = 6 * CrimsonEye->elevatorSpeed;
703
704
TileLayer *moveLayer = RSDK.GetTileLayer(Zone->moveLayer);
705
moveLayer->scrollSpeed = (CrimsonEye->elevatorSpeed / 24) << 6;
706
}
707
708
void CrimsonEye_Draw_Container(void)
709
{
710
RSDK_THIS(CrimsonEye);
711
712
if (!self->animator.frameID && (CrimsonEye->invincibilityTimer & 1))
713
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
714
715
RSDK.DrawSprite(&self->animator, NULL, false);
716
717
RSDK.SetPaletteEntry(0, 128, 0x000000);
718
}
719
720
void CrimsonEye_StateEye_EnterBalls(void)
721
{
722
RSDK_THIS(CrimsonEye);
723
724
CrimsonEye->ballSpinAngleX += 4;
725
CrimsonEye->ballOffsetY += 0x40000;
726
727
if (CrimsonEye->ballOffsetY >= 0x800000) {
728
CrimsonEye->ballOffsetY = 0x800000;
729
self->stateEye = CrimsonEye_StateEye_SpinningBalls;
730
}
731
732
if (!(Zone->timer % 36))
733
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
734
}
735
736
void CrimsonEye_StateEye_SpinningBalls(void)
737
{
738
RSDK_THIS(CrimsonEye);
739
740
CrimsonEye->ballSpinAngleX += 4;
741
742
if (++self->ballTimer == 120) {
743
self->ballTimer = 0;
744
self->stateEye = CrimsonEye_StateEye_LowerBalls;
745
}
746
747
if (!(Zone->timer % 36))
748
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
749
}
750
751
void CrimsonEye_StateEye_LowerBalls(void)
752
{
753
RSDK_THIS(CrimsonEye);
754
755
CrimsonEye->ballSpinAngleX += 4;
756
CrimsonEye->ballOffsetY += self->velocity.y;
757
758
self->velocity.y += 0x3800;
759
760
if (CrimsonEye->ballOffsetY >= 0xEC0000) {
761
CrimsonEye->ballOffsetY = 0xEC0000;
762
763
self->velocity.y = (self->velocity.y >> 3) - (self->velocity.y >> 1);
764
765
if (self->velocity.y > -0x10000)
766
self->stateEye = CrimsonEye_StateEye_LoweredBalls;
767
}
768
769
if (!(Zone->timer % 36))
770
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
771
}
772
773
void CrimsonEye_StateEye_LoweredBalls(void)
774
{
775
RSDK_THIS(CrimsonEye);
776
777
CrimsonEye->ballSpinAngleX += 4;
778
779
if (++self->ballTimer == 120) {
780
self->ballTimer = 0;
781
self->stateEye = CrimsonEye_StateEye_ExtendBallAttack;
782
}
783
784
if (!(Zone->timer % 36))
785
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
786
}
787
788
void CrimsonEye_StateEye_ExtendBallAttack(void)
789
{
790
RSDK_THIS(CrimsonEye);
791
792
CrimsonEye->ballSpinAngleX += 4;
793
self->ballRadiusTimer = (self->ballRadiusTimer + 1) & 0x7F;
794
795
CrimsonEye->ballSpinRadius = 80 * RSDK.Sin256(self->ballRadiusTimer) + 0x4800;
796
797
if (!self->ballRadiusTimer)
798
self->stateEye = CrimsonEye_StateEye_FinishExtendAttack;
799
800
if (!(Zone->timer % 36))
801
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
802
}
803
804
void CrimsonEye_StateEye_FinishExtendAttack(void)
805
{
806
RSDK_THIS(CrimsonEye);
807
808
CrimsonEye->ballSpinAngleX += 4;
809
810
if (++self->ballTimer == 120) {
811
self->ballTimer = 0;
812
self->stateEye = CrimsonEye_StateEye_RaiseBalls;
813
}
814
815
if (!(Zone->timer % 36))
816
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
817
}
818
819
void CrimsonEye_StateEye_RaiseBalls(void)
820
{
821
RSDK_THIS(CrimsonEye);
822
823
CrimsonEye->ballSpinAngleX += 4;
824
CrimsonEye->ballOffsetY -= 0x40000;
825
826
if (CrimsonEye->ballOffsetY <= 0x800000) {
827
CrimsonEye->ballOffsetY = 0x800000;
828
self->stateEye = CrimsonEye_StateEye_SpinningBalls;
829
}
830
831
if (!(Zone->timer % 36))
832
RSDK.PlaySfx(CrimsonEye->sfxHover, false, 255);
833
}
834
835
void CrimsonEye_ShootShot(void)
836
{
837
RSDK_THIS(CrimsonEye);
838
839
EntityCrimsonEye *child = CREATE_ENTITY(CrimsonEye, INT_TO_VOID(CRIMSONEYE_SHOT), self->position.x + 0x260000, self->position.y);
840
child->velocity.x = 0x280 * RSDK.Cos512(self->rotation);
841
child->velocity.y = -0x280 * RSDK.Sin512(self->rotation);
842
child->position.y += self->rotation < 0x100 ? -0x110000 : 0x110000;
843
844
child = CREATE_ENTITY(CrimsonEye, INT_TO_VOID(CRIMSONEYE_SHOT), self->position.x - 0x260000, self->position.y);
845
child->velocity.x = -0x280 * RSDK.Cos512(self->rotation);
846
child->velocity.y = -0x280 * RSDK.Sin512(self->rotation);
847
child->position.y += self->rotation < 0x100 ? -0x110000 : 0x110000;
848
RSDK.PlaySfx(CrimsonEye->sfxShot, false, 255);
849
}
850
851
void CrimsonEye_SetupEyePositions(void)
852
{
853
RSDK_THIS(CrimsonEye);
854
855
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
856
857
// Eyeball
858
CrimsonEye->eyePositions[0].x = self->position.x;
859
CrimsonEye->eyePositions[0].y = self->position.y;
860
861
CrimsonEye->eyePositions[0].x += CLAMP(10 * ((player1->position.x - self->position.x) >> 8), -0xC0000, 0xC0000);
862
CrimsonEye->eyePositions[0].x &= 0xFFFF0000;
863
864
// Eye shine
865
CrimsonEye->eyePositions[2] = CrimsonEye->eyePositions[0];
866
867
// Iris thingy
868
CrimsonEye->eyePositions[1] = CrimsonEye->eyePositions[0];
869
CrimsonEye->eyePositions[1].x += CLAMP(3 * ((player1->position.x - self->position.x) >> 8), -0x30000, 0x30000);
870
CrimsonEye->eyePositions[1].x &= 0xFFFF0000;
871
}
872
873
void CrimsonEye_DestroyBall(void)
874
{
875
if (CrimsonEye->destroyedBallCount < 8) {
876
EntityCrimsonEye *child = RSDK_GET_ENTITY(SceneInfo->entitySlot + CrimsonEye->ballDestroyOrder[CrimsonEye->destroyedBallCount], CrimsonEye);
877
child->state = CrimsonEye_StateBall_Destroyed;
878
child->timer = 30;
879
child->drawGroup = Zone->objectDrawGroup[0];
880
child->scale.y = 0x200;
881
child->scale.x = 0x200;
882
883
++CrimsonEye->destroyedBallCount;
884
}
885
}
886
887
void CrimsonEye_CheckPlayerCollisions(void)
888
{
889
RSDK_THIS(CrimsonEye);
890
891
if (CrimsonEye->invincibilityTimer) {
892
CrimsonEye->invincibilityTimer--;
893
}
894
else {
895
Vector2 storePos = self->position;
896
foreach_active(Player, player)
897
{
898
self->position = CrimsonEye->eyePositions[0];
899
if (Player_CheckBadnikTouch(player, self, &CrimsonEye->hitboxEye) && Player_CheckBossHit(player, self)) {
900
self->position = storePos;
901
CrimsonEye_Hit();
902
foreach_break;
903
}
904
}
905
self->position = storePos;
906
}
907
}
908
909
void CrimsonEye_StateCore_ContainerActive(void)
910
{
911
CrimsonEye_SetupEyePositions();
912
CrimsonEye_CheckPlayerCollisions();
913
}
914
915
void CrimsonEye_StateCore_BreakOut(void)
916
{
917
RSDK_THIS(CrimsonEye);
918
919
CrimsonEye_SetupEyePositions();
920
CrimsonEye_Explode();
921
922
if (--self->timer <= 0) {
923
self->timer = 90;
924
--CrimsonEye->health;
925
self->drawGroup = Zone->objectDrawGroup[0];
926
self->state = CrimsonEye_StateCore_BrokenOut;
927
}
928
}
929
930
void CrimsonEye_StateCore_BrokenOut(void)
931
{
932
RSDK_THIS(CrimsonEye);
933
934
CrimsonEye_SetupEyePositions();
935
CrimsonEye_Explode();
936
937
if (--self->timer <= 0) {
938
CrimsonEye->elevatorGravity = 0x4000;
939
940
self->velocity.x = -0x10000;
941
self->timer = 90;
942
self->drawGroup = Zone->objectDrawGroup[0] + 1;
943
self->state = CrimsonEye_StateCore_Hovering;
944
}
945
}
946
947
void CrimsonEye_StateCore_Hovering(void)
948
{
949
RSDK_THIS(CrimsonEye);
950
951
self->position.x += self->velocity.x;
952
953
if (abs(self->velocity.y) <= 0x10000) {
954
if (self->originPos.y <= (Zone->cameraBoundsB[0] - 144) << 16) {
955
self->originPos.y += 0x10000;
956
957
if (self->originPos.y > (Zone->cameraBoundsB[0] - 144) << 16)
958
self->originPos.y = (Zone->cameraBoundsB[0] - 144) << 16;
959
}
960
else {
961
self->originPos.y -= 0x10000;
962
963
if (self->originPos.y < (Zone->cameraBoundsB[0] - 144) << 16)
964
self->originPos.y = (Zone->cameraBoundsB[0] - 144) << 16;
965
}
966
}
967
968
self->originPos.y -= self->velocity.y;
969
970
self->position.y = BadnikHelpers_Oscillate(self->originPos.y, 2, 10);
971
if (self->position.x <= (Zone->cameraBoundsL[0] + 64) << 16 || self->position.x >= (Zone->cameraBoundsR[0] - 64) << 16) {
972
self->velocity.x = -self->velocity.x;
973
}
974
975
int32 storeY = self->position.y;
976
self->position.y = self->originPos.y;
977
978
foreach_active(InvisibleBlock, block)
979
{
980
int32 side = RSDK.CheckObjectCollisionBox(block, &block->hitbox, self, &CrimsonEye->hitboxBlock, true);
981
if (side == C_TOP) {
982
if (self->velocity.y < 0)
983
self->velocity.y = 0;
984
985
self->rotation = 0x40;
986
CrimsonEye->shotsRemaining = 8;
987
CrimsonEye->shotTimer = 15;
988
989
self->state = CrimsonEye_StateCore_ImpactLift;
990
991
RSDK.PlaySfx(CrimsonEye->sfxButton, false, 0xFF);
992
RSDK.PlaySfx(CrimsonEye->sfxImpact, false, 0xFF);
993
}
994
else if (side == C_BOTTOM) {
995
if (self->velocity.y > 0)
996
self->velocity.y = 0;
997
998
self->rotation = 0x1C0;
999
CrimsonEye->shotsRemaining = 8;
1000
CrimsonEye->shotTimer = 15;
1001
1002
self->state = CrimsonEye_StateCore_ImpactLift;
1003
1004
RSDK.PlaySfx(CrimsonEye->sfxButton, false, 0xFF);
1005
RSDK.PlaySfx(CrimsonEye->sfxImpact, false, 0xFF);
1006
}
1007
}
1008
1009
self->originPos.y = self->position.y;
1010
self->position.y = storeY;
1011
1012
CrimsonEye_SetupEyePositions();
1013
CrimsonEye_CheckPlayerCollisions();
1014
1015
if (!(Zone->timer & 0x3F))
1016
RSDK.PlaySfx(CrimsonEye->sfxHover2, false, 255);
1017
}
1018
1019
void CrimsonEye_StateCore_ImpactLift(void)
1020
{
1021
RSDK_THIS(CrimsonEye);
1022
1023
if (--CrimsonEye->shotTimer <= 0) {
1024
CrimsonEye->shotTimer = 15;
1025
1026
if (--CrimsonEye->shotsRemaining != 0xFF)
1027
CrimsonEye_ShootShot();
1028
else
1029
self->state = CrimsonEye_StateCore_Hovering;
1030
}
1031
1032
CrimsonEye_SetupEyePositions();
1033
CrimsonEye_CheckPlayerCollisions();
1034
}
1035
1036
void CrimsonEye_StateCore_Explode(void)
1037
{
1038
RSDK_THIS(CrimsonEye);
1039
1040
CrimsonEye_Explode();
1041
1042
if (--self->timer <= 0) {
1043
Music_TransitionTrack(TRACK_STAGE, 0.0125);
1044
self->state = CrimsonEye_StateCore_SpawnSignPost;
1045
}
1046
}
1047
1048
void CrimsonEye_StateCore_SpawnSignPost(void)
1049
{
1050
RSDK_THIS(CrimsonEye);
1051
1052
if (++self->timer == 48) {
1053
foreach_all(SignPost, signPost)
1054
{
1055
signPost->position.x = self->position.x;
1056
signPost->position.y = (ScreenInfo->position.y - 64) << 16;
1057
signPost->state = SignPost_State_Falling;
1058
RSDK.PlaySfx(SignPost->sfxTwinkle, false, 255);
1059
}
1060
1061
destroyEntity(self);
1062
}
1063
}
1064
1065
void CrimsonEye_Draw_Core(void)
1066
{
1067
RSDK_THIS(CrimsonEye);
1068
1069
if (CrimsonEye->invincibilityTimer & 1)
1070
RSDK.SetPaletteEntry(0, 128, 0xE0E0E0);
1071
1072
if (self->state != CrimsonEye_StateCore_ContainerActive && self->state != CrimsonEye_StateCore_BreakOut) {
1073
self->animator.frameID = 12;
1074
RSDK.DrawSprite(&self->animator, NULL, false);
1075
1076
self->animator.frameID = 13;
1077
RSDK.DrawSprite(&self->animator, NULL, false);
1078
1079
self->drawFX |= FX_ROTATE;
1080
Vector2 drawPos = self->position;
1081
drawPos.x = self->position.x - 0x160000;
1082
self->animator.frameID = 4;
1083
RSDK.DrawSprite(&self->animator, &drawPos, false);
1084
1085
self->rotation = 0x1FF - self->rotation;
1086
drawPos.x += 0x2C0000;
1087
self->animator.frameID = 5;
1088
RSDK.DrawSprite(&self->animator, &drawPos, false);
1089
1090
self->drawFX &= ~FX_ROTATE;
1091
self->rotation = 0x1FF - self->rotation;
1092
self->animator.frameID = 0;
1093
RSDK.DrawSprite(&self->animator, NULL, false);
1094
}
1095
1096
self->animator.frameID = 1;
1097
RSDK.DrawSprite(&self->animator, &CrimsonEye->eyePositions[0], false);
1098
1099
self->animator.frameID = 2;
1100
RSDK.DrawSprite(&self->animator, &CrimsonEye->eyePositions[1], false);
1101
1102
self->inkEffect = INK_ALPHA;
1103
self->alpha = 0xC0;
1104
self->animator.frameID = 3;
1105
RSDK.DrawSprite(&self->animator, &CrimsonEye->eyePositions[2], false);
1106
1107
self->inkEffect = INK_NONE;
1108
RSDK.SetPaletteEntry(0, 128, 0x000000);
1109
}
1110
1111
void CrimsonEye_CheckPlayerCollisions_Ball(void)
1112
{
1113
RSDK_THIS(CrimsonEye);
1114
1115
foreach_active(Player, player)
1116
{
1117
if (Player_CheckCollisionTouch(player, self, &CrimsonEye->hitboxBall)) {
1118
#if MANIA_USE_PLUS
1119
if (!Player_CheckMightyUnspin(player, 0x600, 2, &player->uncurlTimer))
1120
#endif
1121
Player_Hurt(player, self);
1122
}
1123
}
1124
}
1125
1126
void CrimsonEye_StateBall_Spinning(void)
1127
{
1128
RSDK_THIS(CrimsonEye);
1129
1130
uint8 angle = ((self->angle & 0xFF) + CrimsonEye->ballSpinAngleX);
1131
1132
self->position.x = CrimsonEye->ballSpinRadius * RSDK.Cos256(angle) + CrimsonEye->containerPos.x;
1133
self->position.y = (RSDK.Sin256(CrimsonEye->ballSpinAngleY) << 13) + CrimsonEye->containerPos.y + CrimsonEye->ballOffsetY;
1134
1135
if (angle <= 0x80) {
1136
self->drawGroup = Zone->objectDrawGroup[0];
1137
self->animator.frameID = 0;
1138
self->scale.x = 2 * abs(angle - 0x40) + 0x182;
1139
self->scale.y = 2 * abs(angle - 0x40) + 0x182;
1140
}
1141
else {
1142
self->animator.frameID = 1;
1143
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1144
self->scale.x = 2 * (0x13F - abs(angle - 0xC0));
1145
self->scale.y = 2 * (0x13F - abs(angle - 0xC0));
1146
1147
CrimsonEye_CheckPlayerCollisions_Ball();
1148
}
1149
}
1150
1151
void CrimsonEye_StateBall_Destroyed(void)
1152
{
1153
RSDK_THIS(CrimsonEye);
1154
1155
self->position.y -= 0x18000;
1156
1157
CrimsonEye_CheckPlayerCollisions_Ball();
1158
1159
if (--self->timer <= 0) {
1160
RSDK.PlaySfx(CrimsonEye->sfxExplosion, false, 255);
1161
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1] + 2;
1162
1163
int32 angle = 0;
1164
for (int32 i = 0; i < 8; ++i) {
1165
EntityCrimsonEye *spike = CREATE_ENTITY(CrimsonEye, INT_TO_VOID(CRIMSONEYE_SPIKE), self->position.x, self->position.y);
1166
spike->animator.frameID = i + 2;
1167
spike->velocity.x = RSDK.Cos256(angle) << 9;
1168
spike->velocity.y = RSDK.Sin256(angle) << 9;
1169
angle += 32;
1170
}
1171
1172
destroyEntity(self);
1173
}
1174
}
1175
1176
void CrimsonEye_Draw_Simple(void)
1177
{
1178
RSDK_THIS(CrimsonEye);
1179
1180
RSDK.DrawSprite(&self->animator, NULL, false);
1181
}
1182
1183
void CrimsonEye_StateSpike_Harmful(void)
1184
{
1185
RSDK_THIS(CrimsonEye);
1186
1187
self->position.x += self->velocity.x;
1188
self->position.y += self->velocity.y;
1189
1190
foreach_active(Player, player)
1191
{
1192
if (Player_CheckCollisionTouch(player, self, &CrimsonEye->hitboxOrb)) {
1193
#if MANIA_USE_PLUS
1194
if (!Player_CheckMightyUnspin(player, 0x600, 2, &player->uncurlTimer))
1195
#endif
1196
Player_Hurt(player, self);
1197
}
1198
}
1199
1200
if (!RSDK.CheckOnScreen(self, &self->updateRange))
1201
destroyEntity(self);
1202
}
1203
1204
void CrimsonEye_StateSpike_Debris(void)
1205
{
1206
RSDK_THIS(CrimsonEye);
1207
1208
self->position.x += self->velocity.x;
1209
self->position.y += self->velocity.y;
1210
self->velocity.y += 0x3800;
1211
1212
self->visible ^= true;
1213
1214
if (!RSDK.CheckOnScreen(self, &self->updateRange))
1215
destroyEntity(self);
1216
}
1217
1218
void CrimsonEye_State_Shot(void)
1219
{
1220
RSDK_THIS(CrimsonEye);
1221
1222
RSDK.ProcessAnimation(&self->animator);
1223
1224
self->position.x += self->velocity.x;
1225
self->position.y += self->velocity.y;
1226
1227
foreach_active(Player, player)
1228
{
1229
if (Player_CheckCollisionTouch(player, self, &CrimsonEye->hitboxElecOrb)) {
1230
Player_ElementHurt(player, self, SHIELD_LIGHTNING);
1231
}
1232
}
1233
1234
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, true)
1235
|| RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, -0x80000, true)) {
1236
self->velocity.y = -self->velocity.y;
1237
}
1238
1239
if (!RSDK.CheckOnScreen(self, &self->updateRange))
1240
destroyEntity(self);
1241
}
1242
1243
void CrimsonEye_State_Arrow(void)
1244
{
1245
// hi poe
1246
}
1247
1248
void CrimsonEye_Draw_Arrow(void)
1249
{
1250
RSDK_THIS(CrimsonEye);
1251
1252
self->animator.frameID = 0;
1253
RSDK.DrawSprite(&self->animator, NULL, false);
1254
1255
self->animator.frameID = self->type;
1256
if (self->type != CE_ARROW_OFF)
1257
RSDK.DrawSprite(&self->animator, NULL, false);
1258
}
1259
1260
#if GAME_INCLUDE_EDITOR
1261
void CrimsonEye_EditorDraw(void)
1262
{
1263
RSDK_THIS(CrimsonEye);
1264
1265
switch (self->type) {
1266
case CRIMSONEYE_CONTAINER:
1267
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1268
1269
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 0, &self->animator, true, 0);
1270
1271
self->updateRange.x = 0x800000;
1272
self->updateRange.y = 0x800000;
1273
CrimsonEye->containerPos.x = self->position.x;
1274
CrimsonEye->containerPos.y = self->position.y - 0xA00000;
1275
1276
self->stateDraw = CrimsonEye_Draw_Container;
1277
break;
1278
1279
case CRIMSONEYE_CORE:
1280
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1281
1282
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 1, &self->animator, true, 0);
1283
1284
self->originPos = self->position;
1285
self->stateDraw = CrimsonEye_Draw_Core;
1286
self->updateRange.x = 0x800000;
1287
self->updateRange.y = 0x800000;
1288
1289
// Eyeball
1290
CrimsonEye->eyePositions[0].x = self->position.x & 0xFFFF0000;
1291
CrimsonEye->eyePositions[0].y = self->position.y;
1292
1293
// Iris thingy
1294
CrimsonEye->eyePositions[1] = CrimsonEye->eyePositions[0];
1295
1296
// Eye shine
1297
CrimsonEye->eyePositions[2] = CrimsonEye->eyePositions[0];
1298
break;
1299
1300
case CRIMSONEYE_BALL:
1301
self->drawFX = FX_SCALE;
1302
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1303
1304
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 2, &self->animator, true, 0);
1305
self->scale.x = 0x200;
1306
self->scale.y = 0x200;
1307
1308
self->stateDraw = CrimsonEye_Draw_Simple;
1309
self->updateRange.x = 0x800000;
1310
self->updateRange.y = 0x800000;
1311
break;
1312
1313
case CRIMSONEYE_ARROW:
1314
self->drawGroup = Zone->objectDrawGroup[0];
1315
1316
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 3, &self->animator, true, 0);
1317
self->scale.x = 0x200;
1318
self->scale.y = 0x200;
1319
1320
self->stateDraw = CrimsonEye_Draw_Arrow;
1321
self->updateRange.x = 0x800000;
1322
self->updateRange.y = 0x800000;
1323
break;
1324
1325
case CRIMSONEYE_SPIKE:
1326
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1327
1328
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 2, &self->animator, true, 0);
1329
1330
self->stateDraw = CrimsonEye_Draw_Simple;
1331
self->updateRange.x = 0x800000;
1332
self->updateRange.y = 0x800000;
1333
break;
1334
1335
case CRIMSONEYE_SHOT:
1336
self->drawGroup = Zone->objectDrawGroup[0] + 1;
1337
1338
RSDK.SetSpriteAnimation(CrimsonEye->aniFrames, 4, &self->animator, true, 0);
1339
1340
self->stateDraw = CrimsonEye_Draw_Simple;
1341
self->updateRange.x = 0x800000;
1342
self->updateRange.y = 0x800000;
1343
break;
1344
1345
default: break;
1346
}
1347
1348
StateMachine_Run(self->stateDraw);
1349
1350
if (showGizmos() && self->type == CRIMSONEYE_CONTAINER) {
1351
RSDK_DRAWING_OVERLAY(true);
1352
1353
DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER - 80, -SCREEN_YSIZE, WIDE_SCR_XCENTER + 80, 124, 1 | 2 | 4 | 8, 0x00C0F0);
1354
1355
for (int32 s = SceneInfo->entitySlot + 1, i = 0; i < 9; ++i) {
1356
Entity *child = RSDK_GET_ENTITY_GEN(s + i);
1357
if (!child)
1358
continue;
1359
1360
DrawHelpers_DrawArrow(self->position.x, self->position.y, child->position.x, child->position.y, 0xE0E0E0, INK_NONE, 0xFF);
1361
}
1362
1363
RSDK_DRAWING_OVERLAY(false);
1364
}
1365
}
1366
1367
void CrimsonEye_EditorLoad(void)
1368
{
1369
CrimsonEye->aniFrames = RSDK.LoadSpriteAnimation("TMZ1/CrimsonEye.bin", SCOPE_STAGE);
1370
1371
RSDK_ACTIVE_VAR(CrimsonEye, type);
1372
RSDK_ENUM_VAR("Container", CRIMSONEYE_CONTAINER);
1373
RSDK_ENUM_VAR("Core", CRIMSONEYE_CORE);
1374
RSDK_ENUM_VAR("Ball", CRIMSONEYE_BALL);
1375
RSDK_ENUM_VAR("Arrow", CRIMSONEYE_ARROW);
1376
}
1377
#endif
1378
1379
void CrimsonEye_Serialize(void) { RSDK_EDITABLE_VAR(CrimsonEye, VAR_ENUM, type); }
1380
1381