Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/SSZ/HotaruMKII.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: HotaruMKII Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectHotaruMKII *HotaruMKII;
11
12
void HotaruMKII_Update(void)
13
{
14
RSDK_THIS(HotaruMKII);
15
StateMachine_Run(self->state);
16
}
17
18
void HotaruMKII_LateUpdate(void) {}
19
20
void HotaruMKII_StaticUpdate(void) {}
21
22
void HotaruMKII_Draw(void)
23
{
24
RSDK_THIS(HotaruMKII);
25
26
if (self->state != HotaruMKII_State_CheckPlayerInRange) {
27
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
28
29
if (self->state != HotaruMKII_State_FlyOnScreen && ((Zone->timer & 1) || self->state == HotaruMKII_State_LaserAttack)) {
30
int32 ink = self->inkEffect;
31
self->inkEffect = INK_ALPHA;
32
RSDK.DrawSprite(&self->flashAnimator, NULL, false);
33
34
self->inkEffect = ink;
35
}
36
}
37
}
38
39
void HotaruMKII_Create(void *data)
40
{
41
RSDK_THIS(HotaruMKII);
42
43
if (!SceneInfo->inEditor) {
44
self->drawGroup = Zone->objectDrawGroup[1];
45
self->startPos = self->position;
46
self->startDir = self->direction;
47
self->updateRange.x = 0x1000000;
48
self->updateRange.y = 0x1000000;
49
self->drawFX = FX_FLIP;
50
51
if (!self->triggerSize.x) {
52
self->triggerSize.x = 0x1000000;
53
self->triggerSize.y = 0x1000000;
54
}
55
56
self->hitboxTrigger.right = self->triggerSize.x >> 17;
57
self->hitboxTrigger.left = -self->hitboxTrigger.right;
58
self->hitboxTrigger.bottom = self->triggerSize.y >> 17;
59
self->hitboxTrigger.top = -self->hitboxTrigger.bottom;
60
61
self->type = VOID_TO_INT(data);
62
switch (self->type) {
63
default: break;
64
case HOTARUMKII_MAIN:
65
self->active = ACTIVE_BOUNDS;
66
self->inkEffect = INK_NONE;
67
self->visible = true;
68
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 1, &self->flashAnimator, true, 0);
69
self->state = HotaruMKII_State_Init;
70
break;
71
72
case HOTARUMKII_FLASH:
73
self->active = ACTIVE_NORMAL;
74
self->inkEffect = INK_ALPHA;
75
self->visible = true;
76
self->alpha = 0x80;
77
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 1, &self->mainAnimator, true, 0);
78
self->state = HotaruMKII_State_Flash;
79
break;
80
81
case HOTARUMKII_LASER:
82
--self->drawGroup;
83
self->active = ACTIVE_NORMAL;
84
self->inkEffect = INK_ALPHA;
85
self->visible = true;
86
self->alpha = 0x80;
87
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 2, &self->mainAnimator, true, 0);
88
self->state = HotaruMKII_State_Laser;
89
break;
90
}
91
}
92
}
93
94
void HotaruMKII_StageLoad(void)
95
{
96
if (RSDK.CheckSceneFolder("SSZ1"))
97
HotaruMKII->aniFrames = RSDK.LoadSpriteAnimation("SSZ1/HotaruMKII.bin", SCOPE_STAGE);
98
else if (RSDK.CheckSceneFolder("SSZ2"))
99
HotaruMKII->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/HotaruMKII.bin", SCOPE_STAGE);
100
101
HotaruMKII->hitboxBadnik.top = -6;
102
HotaruMKII->hitboxBadnik.left = -6;
103
HotaruMKII->hitboxBadnik.right = 6;
104
HotaruMKII->hitboxBadnik.bottom = 6;
105
106
HotaruMKII->hitboxLaser.top = -8;
107
HotaruMKII->hitboxLaser.left = -8;
108
HotaruMKII->hitboxLaser.right = 8;
109
HotaruMKII->hitboxLaser.bottom = 8;
110
111
HotaruMKII->sfxLaser = RSDK.GetSfx("SSZ1/HotaruLaser.wav");
112
HotaruMKII->sfxAppear = RSDK.GetSfx("SSZ1/HotaruAppear.wav");
113
HotaruMKII->sfxFly = RSDK.GetSfx("SSZ1/HotaruFly.wav");
114
HotaruMKII->sfxCharge = RSDK.GetSfx("SSZ1/HotaruCharge.wav");
115
116
DEBUGMODE_ADD_OBJ(HotaruMKII);
117
}
118
119
void HotaruMKII_DebugSpawn(void)
120
{
121
RSDK_THIS(DebugMode);
122
123
EntityHotaruMKII *hotaruMKII = CREATE_ENTITY(HotaruMKII, NULL, self->position.x, self->position.y);
124
hotaruMKII->origin = RSDK.Rand(0, 0x100);
125
126
hotaruMKII->offset1.x = -0x10000 * RSDK.Rand(0, 2) * RSDK.Rand(0x20, 0x100);
127
hotaruMKII->offset1.y = -0x10000 * RSDK.Rand(0, 2) * RSDK.Rand(0x20, 0xC0);
128
hotaruMKII->offset2.x = -0x10000 * RSDK.Rand(0, 2) * RSDK.Rand(0x20, 0xC0);
129
hotaruMKII->offset2.y = -0x10000 * RSDK.Rand(0, 2) * RSDK.Rand(0x20, 0xC0);
130
hotaruMKII->offset3.x = -0x10000 * RSDK.Rand(0, 2) * RSDK.Rand(0x20, 0xC0);
131
hotaruMKII->offset3.y = -0x10000 * RSDK.Rand(0, 2) * RSDK.Rand(0x20, 0xC0);
132
}
133
134
void HotaruMKII_DebugDraw(void)
135
{
136
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 0, &DebugMode->animator, true, 0);
137
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
138
}
139
140
void HotaruMKII_CheckPlayerCollisions(void)
141
{
142
RSDK_THIS(HotaruMKII);
143
144
foreach_active(Player, player)
145
{
146
if (Player_CheckBadnikTouch(player, self, &HotaruMKII->hitboxBadnik))
147
Player_CheckBadnikBreak(player, self, true);
148
}
149
}
150
151
void HotaruMKII_CheckOffScreen(void)
152
{
153
RSDK_THIS(HotaruMKII);
154
155
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
156
self->position = self->startPos;
157
self->direction = self->startDir;
158
self->offsetID = 0;
159
HotaruMKII_Create(NULL);
160
}
161
}
162
163
void HotaruMKII_HandleDistances(EntityPlayer *player)
164
{
165
RSDK_THIS(HotaruMKII);
166
167
if (Player_CheckValidState(player)) {
168
int32 distX = self->curOffset.x;
169
switch (self->offsetID) {
170
case 0:
171
self->curOffset.x = self->offset1.x;
172
self->curOffset.y = self->offset1.y;
173
break;
174
175
case 1:
176
self->curOffset.x = self->offset2.x;
177
self->curOffset.y = self->offset2.y;
178
break;
179
180
case 2:
181
self->curOffset.x = self->offset3.x;
182
self->curOffset.y = self->offset3.y;
183
break;
184
185
case 3:
186
self->curOffset.x = 0;
187
self->curOffset.y = 0;
188
break;
189
190
default: break;
191
}
192
193
self->curOffset.x &= 0xFFFF0000;
194
self->curOffset.y &= 0xFFFF0000;
195
if (!self->curOffset.x && !self->curOffset.y) {
196
self->curOffset.x = distX;
197
self->curOffset.y = 0xB0010000;
198
}
199
200
int32 angle =
201
RSDK.ATan2(self->curOffset.x + player->position.x - self->position.x, self->curOffset.y + player->position.y - self->position.y);
202
self->moveAcceleration.x = 0x300 * RSDK.Cos256(angle);
203
self->moveAcceleration.y = 0x300 * RSDK.Sin256(angle);
204
205
self->velocity.x = player->velocity.x + self->moveAcceleration.x;
206
self->velocity.y = player->velocity.y + self->moveAcceleration.y;
207
208
++self->offsetID;
209
}
210
else {
211
self->playerPtr = NULL;
212
self->offsetID = 4;
213
self->velocity.x = 0;
214
self->velocity.y = -0x30000;
215
self->state = HotaruMKII_State_FlyAway;
216
}
217
}
218
219
void HotaruMKII_State_Init(void)
220
{
221
RSDK_THIS(HotaruMKII);
222
223
self->active = ACTIVE_NORMAL;
224
self->childCount = 0;
225
226
self->state = HotaruMKII_State_CheckPlayerInRange;
227
HotaruMKII_State_CheckPlayerInRange();
228
}
229
230
void HotaruMKII_State_CheckPlayerInRange(void)
231
{
232
RSDK_THIS(HotaruMKII);
233
234
bool32 foundTargetPlayer = false;
235
foreach_active(Player, player)
236
{
237
if (Player_CheckCollisionTouch(player, self, &self->hitboxTrigger)) {
238
self->playerPtr = player;
239
if (!player->sidekick) {
240
foundTargetPlayer = true;
241
242
int32 screenID = 0;
243
if (player->camera)
244
screenID = player->camera->screenID;
245
246
self->position.x = RSDK.Cos256(self->origin) << 17;
247
self->position.y = RSDK.Sin256(self->origin) << 17;
248
RSDKScreenInfo *screen = &ScreenInfo[screenID];
249
250
if (self->position.x > (int32)(screen->size.x & 0xFFFFFFFE) << 15)
251
self->position.x = (int32)(screen->size.x & 0xFFFFFFFE) << 15;
252
253
if (self->position.x < -((int32)(screen->size.x & 0xFFFFFFFE) << 15))
254
self->position.x = -((int32)(screen->size.x & 0xFFFFFFFE) << 15);
255
256
if (self->position.y > (int32)(screen->size.y & 0xFFFFFFFE) << 15)
257
self->position.y = (int32)(screen->size.y & 0xFFFFFFFE) << 15;
258
259
if (self->position.y < -((int32)(screen->size.y & 0xFFFFFFFE) << 15))
260
self->position.y = -((int32)(screen->size.y & 0xFFFFFFFE) << 15);
261
262
if (self->position.x >= 0)
263
self->position.x += 0x100000;
264
else
265
self->position.x -= 0x100000;
266
267
if (self->position.y >= 0)
268
self->position.y += 0x100000;
269
else
270
self->position.y -= 0x100000;
271
272
self->position.x += ((screen->size.x & 0xFFFFFFFE) + 2 * screen->position.x) << 15;
273
self->position.y += ((screen->size.y & 0xFFFFFFFE) + 2 * screen->position.y) << 15;
274
HotaruMKII_HandleDistances(player);
275
276
self->inkEffect = INK_ALPHA;
277
self->visible = true;
278
self->alpha = 0x80;
279
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 1, &self->mainAnimator, true, 0);
280
281
RSDK.PlaySfx(HotaruMKII->sfxFly, false, 255);
282
self->state = HotaruMKII_State_FlyOnScreen;
283
284
foreach_break;
285
}
286
}
287
}
288
289
if (!foundTargetPlayer)
290
HotaruMKII_CheckOffScreen();
291
}
292
293
void HotaruMKII_State_FlyAway(void)
294
{
295
RSDK_THIS(HotaruMKII);
296
297
self->position.x += self->velocity.x;
298
self->position.y += self->velocity.y;
299
300
HotaruMKII_CheckOffScreen();
301
}
302
303
void HotaruMKII_State_FlyOnScreen(void)
304
{
305
RSDK_THIS(HotaruMKII);
306
EntityPlayer *player = self->playerPtr;
307
308
self->position.x += self->velocity.x;
309
self->position.y += self->velocity.y;
310
311
if (player && !Player_CheckValidState(player)) {
312
HotaruMKII_HandleDistances(player);
313
}
314
else {
315
if (!(Zone->timer & 7)) {
316
EntityHotaruMKII *flash = CREATE_ENTITY(HotaruMKII, INT_TO_VOID(HOTARUMKII_FLASH), self->position.x, self->position.y);
317
flash->playerPtr = self->playerPtr;
318
flash->curOffset.x = self->position.x - player->position.x;
319
flash->curOffset.y = self->position.y - player->position.y;
320
}
321
322
int32 moveFinished = 0;
323
if (self->moveAcceleration.x < 0) {
324
int32 x = player->position.x + self->curOffset.x;
325
if (self->position.x <= x) {
326
moveFinished = 1;
327
self->position.x = x;
328
}
329
} else if (self->moveAcceleration.x >= 1) {
330
int32 x = player->position.x + self->curOffset.x;
331
if (self->position.x >= x) {
332
moveFinished = 1;
333
self->position.x = x;
334
}
335
} else {
336
moveFinished = 1;
337
}
338
339
if (self->moveAcceleration.y < 0) {
340
int32 y = player->position.y + self->curOffset.y;
341
if (self->position.y <= y) {
342
moveFinished++;
343
self->position.y = y;
344
}
345
} else if (self->moveAcceleration.y > 0) {
346
int32 y = player->position.y + self->curOffset.y;
347
if (self->position.y >= y) {
348
moveFinished++;
349
self->position.y = y;
350
}
351
} else {
352
moveFinished++;
353
}
354
355
if (moveFinished == 2) {
356
self->inkEffect = INK_NONE;
357
self->alpha = 0x00;
358
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 0, &self->mainAnimator, true, 0);
359
360
self->timer = 60;
361
RSDK.PlaySfx(HotaruMKII->sfxAppear, false, 255);
362
self->state = HotaruMKII_State_AttackDelay;
363
}
364
else {
365
RSDK.ProcessAnimation(&self->mainAnimator);
366
367
int32 angle =
368
RSDK.ATan2(player->position.x + self->curOffset.x - self->position.x, player->position.y + self->curOffset.y - self->position.y);
369
self->moveAcceleration.x = 0x300 * RSDK.Cos256(angle);
370
self->moveAcceleration.y = 0x300 * RSDK.Sin256(angle);
371
372
self->velocity.x = player->velocity.x + self->moveAcceleration.x;
373
self->velocity.y = player->velocity.y + self->moveAcceleration.y;
374
}
375
376
HotaruMKII_CheckOffScreen();
377
}
378
}
379
380
void HotaruMKII_State_AttackDelay(void)
381
{
382
RSDK_THIS(HotaruMKII);
383
EntityPlayer *player = self->playerPtr;
384
385
RSDK.ProcessAnimation(&self->unusedAnimator);
386
387
if (Player_CheckValidState(player)) {
388
self->position.x = player->position.x + self->curOffset.x;
389
self->position.y = player->position.y + self->curOffset.y;
390
if (--self->timer <= 0) {
391
RSDK.PlaySfx(HotaruMKII->sfxCharge, false, 255);
392
self->state = HotaruMKII_State_Charging;
393
}
394
HotaruMKII_CheckOffScreen();
395
}
396
else {
397
HotaruMKII_HandleDistances(player);
398
}
399
}
400
401
void HotaruMKII_State_Charging(void)
402
{
403
RSDK_THIS(HotaruMKII);
404
EntityPlayer *player = self->playerPtr;
405
406
RSDK.ProcessAnimation(&self->flashAnimator);
407
408
if (Player_CheckValidState(player)) {
409
self->position.x = player->position.x + self->curOffset.x;
410
self->position.y = player->position.y + self->curOffset.y;
411
412
self->alpha += 2;
413
if (self->alpha == 0x100) {
414
self->timer = 90;
415
self->alpha = 0x100;
416
self->inkEffect = INK_ALPHA;
417
self->state = HotaruMKII_State_LaserAttack;
418
}
419
HotaruMKII_CheckPlayerCollisions();
420
HotaruMKII_CheckOffScreen();
421
}
422
else {
423
HotaruMKII_HandleDistances(player);
424
}
425
}
426
427
void HotaruMKII_State_LaserAttack(void)
428
{
429
RSDK_THIS(HotaruMKII);
430
EntityPlayer *player = self->playerPtr;
431
432
RSDK.ProcessAnimation(&self->mainAnimator);
433
if (Player_CheckValidState(player)) {
434
if (--self->timer > 0) {
435
if (self->timer <= 60) {
436
if (self->timer == 60) {
437
RSDK.PlaySfx(HotaruMKII->sfxLaser, false, 255);
438
RSDK.StopSfx(HotaruMKII->sfxCharge);
439
}
440
441
self->alpha -= 3;
442
if (!(self->timer & 3)) {
443
EntityHotaruMKII *laser = CREATE_ENTITY(HotaruMKII, INT_TO_VOID(HOTARUMKII_LASER), self->position.x, self->position.y + 0xE0000);
444
445
if (!self->childCount) {
446
self->childCount = 1;
447
laser->mainAnimator.frameID = 1;
448
}
449
450
if (self->childCount == 1)
451
laser->childCount = 1;
452
453
if (++self->childCount == 3)
454
self->childCount = 1;
455
}
456
}
457
458
RSDK.ProcessAnimation(&self->flashAnimator);
459
HotaruMKII_CheckPlayerCollisions();
460
HotaruMKII_CheckOffScreen();
461
}
462
else {
463
HotaruMKII_HandleDistances(player);
464
self->childCount = 0;
465
self->alpha = 0x80;
466
self->inkEffect = INK_ALPHA;
467
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 1, &self->mainAnimator, true, 0);
468
self->state = HotaruMKII_State_FlyOnScreen;
469
}
470
}
471
else {
472
HotaruMKII_HandleDistances(player);
473
}
474
}
475
476
void HotaruMKII_State_Flash(void)
477
{
478
RSDK_THIS(HotaruMKII);
479
EntityPlayer *player = self->playerPtr;
480
481
self->position.x = player->position.x + self->curOffset.x;
482
self->position.y = player->position.y + self->curOffset.y;
483
484
RSDK.ProcessAnimation(&self->mainAnimator);
485
486
self->alpha -= 4;
487
if (self->alpha <= 0)
488
destroyEntity(self);
489
}
490
491
void HotaruMKII_State_Laser(void)
492
{
493
RSDK_THIS(HotaruMKII);
494
495
self->position.y += 0x40000;
496
foreach_active(Player, player)
497
{
498
if (Player_CheckCollisionTouch(player, self, &HotaruMKII->hitboxLaser)) {
499
Player_ElementHurt(player, self, SHIELD_LIGHTNING);
500
}
501
}
502
503
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, true)) {
504
if (self->childCount == 1) {
505
++self->drawGroup;
506
self->position.y += 0x80000;
507
self->inkEffect = INK_NONE;
508
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 3, &self->mainAnimator, true, 0);
509
self->state = HotaruMKII_State_LaserStrike;
510
}
511
else {
512
destroyEntity(self);
513
}
514
}
515
}
516
517
void HotaruMKII_State_LaserStrike(void)
518
{
519
RSDK_THIS(HotaruMKII);
520
521
RSDK.ProcessAnimation(&self->mainAnimator);
522
if (self->mainAnimator.frameID >= self->mainAnimator.frameCount - 1)
523
destroyEntity(self);
524
}
525
526
#if GAME_INCLUDE_EDITOR
527
void HotaruMKII_EditorDraw(void)
528
{
529
RSDK_THIS(HotaruMKII);
530
self->drawGroup = Zone->objectDrawGroup[1];
531
self->updateRange.x = 0x1000000;
532
self->updateRange.y = 0x1000000;
533
self->drawFX = FX_FLIP;
534
self->active = ACTIVE_BOUNDS;
535
self->inkEffect = INK_NONE;
536
self->visible = true;
537
RSDK.SetSpriteAnimation(HotaruMKII->aniFrames, 0, &self->mainAnimator, false, 0);
538
539
RSDK.DrawSprite(&self->mainAnimator, NULL, false);
540
541
if (showGizmos()) {
542
Vector2 size = self->triggerSize;
543
if (!self->triggerSize.x) {
544
self->triggerSize.x = 0x1000000;
545
self->triggerSize.y = 0x1000000;
546
}
547
548
RSDK_DRAWING_OVERLAY(true);
549
550
DrawHelpers_DrawRectOutline(self->position.x, self->position.y, self->triggerSize.x, self->triggerSize.y, 0xFF0000);
551
552
RSDK_DRAWING_OVERLAY(false);
553
554
self->triggerSize = size;
555
}
556
}
557
558
void HotaruMKII_EditorLoad(void)
559
{
560
if (RSDK.CheckSceneFolder("SSZ1"))
561
HotaruMKII->aniFrames = RSDK.LoadSpriteAnimation("SSZ1/HotaruMKII.bin", SCOPE_STAGE);
562
else if (RSDK.CheckSceneFolder("SSZ2"))
563
HotaruMKII->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/HotaruMKII.bin", SCOPE_STAGE);
564
}
565
#endif
566
567
void HotaruMKII_Serialize(void)
568
{
569
RSDK_EDITABLE_VAR(HotaruMKII, VAR_UINT8, origin);
570
RSDK_EDITABLE_VAR(HotaruMKII, VAR_VECTOR2, offset1);
571
RSDK_EDITABLE_VAR(HotaruMKII, VAR_VECTOR2, offset2);
572
RSDK_EDITABLE_VAR(HotaruMKII, VAR_VECTOR2, offset3);
573
RSDK_EDITABLE_VAR(HotaruMKII, VAR_VECTOR2, triggerSize);
574
}
575
576