Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/SSZ/Hotaru.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Hotaru Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectHotaru *Hotaru;
11
12
void Hotaru_Update(void)
13
{
14
RSDK_THIS(Hotaru);
15
StateMachine_Run(self->state);
16
}
17
18
void Hotaru_LateUpdate(void) {}
19
20
void Hotaru_StaticUpdate(void) {}
21
22
void Hotaru_Draw(void)
23
{
24
RSDK_THIS(Hotaru);
25
Vector2 drawPos;
26
27
self->direction = self->hotaruDir;
28
if (self->attackState == HOTARU_ATTACK_ATTACKING) {
29
self->inkEffect = INK_ADD;
30
self->alpha = 0x80;
31
drawPos.x = self->offset2.x + self->position.x;
32
drawPos.y = self->offset2.y + self->position.y + 0xC0000;
33
34
drawPos.x += self->electricityOffset.x;
35
drawPos.y += self->electricityOffset.y;
36
RSDK.DrawSprite(&self->electricityAnimator, &drawPos, false);
37
38
drawPos.x += self->electricityOffset.x;
39
drawPos.y += self->electricityOffset.y;
40
RSDK.DrawSprite(&self->electricityAnimator, &drawPos, false);
41
42
drawPos.x += self->electricityOffset.x;
43
drawPos.y += self->electricityOffset.y;
44
RSDK.DrawSprite(&self->electricityAnimator, &drawPos, false);
45
46
self->alpha = 0x100;
47
self->inkEffect = INK_NONE;
48
}
49
50
if (!(self->destroyedHotarus & 1)) {
51
drawPos.x = self->position.x + self->offset1.x;
52
drawPos.y = self->position.y + self->offset1.y;
53
54
if (self->alpha > 0 && (((Zone->timer & 1) << (self->state == Hotaru_State_FinishedAttacking)) & 0x7FFFFFFF)) {
55
self->inkEffect = INK_ADD;
56
RSDK.DrawSprite(&self->bulbAnimator, &drawPos, false);
57
58
self->inkEffect = INK_NONE;
59
}
60
61
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
62
}
63
64
if (!(self->destroyedHotarus & 2)) {
65
self->direction ^= FLIP_X;
66
67
drawPos.x = self->position.x + self->offset2.x;
68
drawPos.y = self->position.y + self->offset2.y;
69
70
if (self->alpha > 0 && (((Zone->timer & 1) << (self->state == Hotaru_State_FinishedAttacking)) & 0x7FFFFFFF)) {
71
self->inkEffect = INK_ADD;
72
RSDK.DrawSprite(&self->bulbAnimator, &drawPos, false);
73
74
self->inkEffect = INK_NONE;
75
}
76
77
RSDK.DrawSprite(&self->mainAnimator, &drawPos, false);
78
}
79
}
80
81
void Hotaru_Create(void *data)
82
{
83
RSDK_THIS(Hotaru);
84
85
if (!SceneInfo->inEditor) {
86
self->distX1 <<= 16;
87
self->distX2 <<= 16;
88
self->speed <<= 15;
89
90
self->offset2.x = -self->dist.x;
91
self->offset2.y = -self->dist.y;
92
self->offset1 = self->dist;
93
self->visible = true;
94
self->drawGroup = Zone->objectDrawGroup[0];
95
self->active = ACTIVE_BOUNDS;
96
self->updateRange.x = MAX(self->distX1, self->distX2) + 0x400000;
97
self->updateRange.y = self->dist.y + 0x400000;
98
99
self->drawFX = FX_ROTATE | FX_FLIP;
100
self->startPos.x = self->position.x;
101
self->startPos.y = self->position.y;
102
self->startDir = self->direction;
103
RSDK.SetSpriteAnimation(Hotaru->aniFrames, self->quality, &self->mainAnimator, true, 0);
104
RSDK.SetSpriteAnimation(Hotaru->aniFrames, 2, &self->bulbAnimator, true, 0);
105
RSDK.SetSpriteAnimation(Hotaru->aniFrames, 3, &self->electricityAnimator, true, 0);
106
107
self->state = Hotaru_State_Init;
108
}
109
}
110
111
void Hotaru_StageLoad(void)
112
{
113
if (RSDK.CheckSceneFolder("SSZ1"))
114
Hotaru->aniFrames = RSDK.LoadSpriteAnimation("SSZ1/Hotaru.bin", SCOPE_STAGE);
115
else if (RSDK.CheckSceneFolder("SSZ2"))
116
Hotaru->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/Hotaru.bin", SCOPE_STAGE);
117
118
Hotaru->hitboxBadnik.top = -6;
119
Hotaru->hitboxBadnik.left = -6;
120
Hotaru->hitboxBadnik.right = 6;
121
Hotaru->hitboxBadnik.bottom = 6;
122
123
Hotaru->hitboxTrigger.top = -32;
124
Hotaru->hitboxTrigger.left = -32;
125
Hotaru->hitboxTrigger.right = 32;
126
Hotaru->hitboxTrigger.bottom = 32;
127
128
Hotaru->hitboxElectricity.top = -8;
129
Hotaru->hitboxElectricity.left = -8;
130
Hotaru->hitboxElectricity.right = 8;
131
Hotaru->hitboxElectricity.bottom = 8;
132
133
DEBUGMODE_ADD_OBJ(Hotaru);
134
}
135
136
void Hotaru_DebugSpawn(void)
137
{
138
RSDK_THIS(DebugMode);
139
140
CREATE_ENTITY(Hotaru, NULL, self->position.x, self->position.y);
141
}
142
143
void Hotaru_DebugDraw(void)
144
{
145
RSDK.SetSpriteAnimation(Hotaru->aniFrames, 0, &DebugMode->animator, true, 0);
146
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
147
}
148
149
void Hotaru_CheckPlayerCollisions(void)
150
{
151
RSDK_THIS(Hotaru);
152
153
int32 storeX = self->position.x;
154
int32 storeY = self->position.y;
155
156
foreach_active(Player, player)
157
{
158
if (!(self->destroyedHotarus & 1)) {
159
self->position.x += self->offset1.x;
160
self->position.y += self->offset1.y;
161
if (Player_CheckBadnikTouch(player, self, &Hotaru->hitboxBadnik) && Player_CheckBadnikBreak(player, self, false)) {
162
self->destroyedHotarus |= 1;
163
self->attackState = HOTARU_ATTACK_IDLE;
164
}
165
166
self->position.x = storeX;
167
self->position.y = storeY;
168
}
169
170
if (!(self->destroyedHotarus & 2)) {
171
self->position.x += self->offset2.x;
172
self->position.y += self->offset2.y;
173
if (Player_CheckBadnikTouch(player, self, &Hotaru->hitboxBadnik) && Player_CheckBadnikBreak(player, &self, false)) {
174
self->destroyedHotarus |= 2;
175
self->attackState = HOTARU_ATTACK_IDLE;
176
}
177
178
self->position.x = storeX;
179
self->position.y = storeY;
180
}
181
}
182
}
183
184
void Hotaru_CheckOffScreen(void)
185
{
186
RSDK_THIS(Hotaru);
187
188
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
189
self->speed >>= 15;
190
self->position = self->startPos;
191
self->direction = self->startDir;
192
self->distX1 = self->distX1 >> 16;
193
self->distX2 = self->distX2 >> 16;
194
Hotaru_Create(NULL);
195
}
196
}
197
198
void Hotaru_State_Init(void)
199
{
200
RSDK_THIS(Hotaru);
201
202
self->active = ACTIVE_NORMAL;
203
self->hotaruDir = FLIP_NONE;
204
self->alpha = 0x00;
205
self->attackState = HOTARU_ATTACK_IDLE;
206
207
self->state = Hotaru_State_CheckPlayerInRange;
208
Hotaru_State_CheckPlayerInRange();
209
}
210
211
void Hotaru_State_CheckPlayerInRange(void)
212
{
213
RSDK_THIS(Hotaru);
214
215
if (self->hotaruDir) {
216
self->offset2.x -= self->speed;
217
self->offset1.x += self->speed;
218
if (self->offset1.x >= self->distX2) {
219
self->offset2.x = -self->distX2;
220
self->offset1.x = self->distX2;
221
self->hotaruDir = FLIP_NONE;
222
}
223
}
224
else {
225
self->offset2.x += self->speed;
226
self->offset1.x -= self->speed;
227
if (self->offset2.x >= self->distX1) {
228
self->offset2.x = self->distX1;
229
self->offset1.x = -self->distX1;
230
self->hotaruDir = FLIP_X;
231
}
232
}
233
234
Hotaru_CheckPlayerCollisions();
235
236
int32 storeX = self->position.x;
237
int32 storeY = self->position.y;
238
foreach_active(Player, player)
239
{
240
bool32 inRange = false;
241
242
if (!(self->destroyedHotarus & 1)) {
243
self->position.x += self->offset1.x;
244
self->position.y += self->offset1.y;
245
246
if (Player_CheckCollisionTouch(player, self, &Hotaru->hitboxTrigger))
247
inRange = true;
248
}
249
250
self->position.x = storeX;
251
self->position.y = storeY;
252
253
if (!(self->destroyedHotarus & 2) && !inRange) {
254
self->position.x += self->offset2.x;
255
self->position.y += self->offset2.y;
256
257
if (Player_CheckCollisionTouch(player, self, &Hotaru->hitboxTrigger))
258
inRange = true;
259
}
260
261
if (inRange) {
262
self->playerPtr = player;
263
self->screenID = player->sidekick ? 0 : player->camera->screenID;
264
265
self->position.x = storeX;
266
self->position.y = storeY;
267
self->state = Hotaru_State_FoundPlayer;
268
foreach_break;
269
}
270
271
self->position.x = storeX;
272
self->position.y = storeY;
273
}
274
}
275
276
void Hotaru_State_FoundPlayer(void)
277
{
278
RSDK_THIS(Hotaru);
279
280
self->state = Hotaru_State_FlyOnScreen;
281
Hotaru_State_FlyOnScreen();
282
}
283
284
void Hotaru_State_FlyOnScreen(void)
285
{
286
RSDK_THIS(Hotaru);
287
288
EntityPlayer *player = self->playerPtr;
289
RSDKScreenInfo *screen = &ScreenInfo[self->screenID];
290
291
int32 screenX = (screen->position.x + screen->center.x) << 16;
292
int32 screenY = (screen->position.y + screen->center.y) << 16;
293
294
if (self->position.x <= screenX) {
295
self->position.x += player->velocity.x + 0x20000;
296
if (self->position.x > screenX)
297
self->position.x = screenX;
298
}
299
else {
300
self->position.x -= player->velocity.x - 0x20000;
301
if (self->position.x < screenX)
302
self->position.x = screenX;
303
}
304
305
if (self->position.y <= screenY) {
306
self->position.y += player->velocity.y + 0x20000;
307
if (self->position.y > screenY)
308
self->position.y = screenY;
309
}
310
else {
311
self->position.y -= player->velocity.y - 0x20000;
312
if (self->position.y < screenY)
313
self->position.y = screenY;
314
}
315
316
if (self->position.x == screenX && self->position.y == screenY) {
317
self->timer = 100;
318
self->state = Hotaru_State_AttachedToScreen;
319
}
320
321
Hotaru_CheckPlayerCollisions();
322
Hotaru_CheckOffScreen();
323
}
324
325
void Hotaru_State_AttachedToScreen(void)
326
{
327
RSDK_THIS(Hotaru);
328
329
RSDKScreenInfo *screen = &ScreenInfo[self->screenID];
330
331
self->position.x = (screen->position.x + screen->center.x) << 16;
332
self->position.y = (screen->position.y + screen->center.y) << 16;
333
334
if (++self->rotation == 128) {
335
self->rotation = 0;
336
self->mainAnimator.frameID = 1;
337
self->state = Hotaru_State_Charging;
338
}
339
340
Hotaru_CheckPlayerCollisions();
341
Hotaru_CheckOffScreen();
342
}
343
344
void Hotaru_State_Charging(void)
345
{
346
RSDK_THIS(Hotaru);
347
348
if (self->alpha >= 0x80) {
349
Hitbox hitboxCharge;
350
if (self->offset2.x >= self->offset1.x) {
351
hitboxCharge.left = self->offset1.x >> 16;
352
hitboxCharge.right = self->offset2.x >> 16;
353
}
354
else {
355
hitboxCharge.left = self->offset2.x >> 16;
356
hitboxCharge.right = self->offset1.x >> 16;
357
}
358
359
if (self->offset2.y >= self->offset1.y) {
360
hitboxCharge.top = self->offset1.y >> 16;
361
hitboxCharge.bottom = self->offset2.y >> 16;
362
}
363
else {
364
hitboxCharge.top = self->offset2.y >> 16;
365
hitboxCharge.bottom = self->offset1.y >> 16;
366
}
367
368
foreach_active(Player, player)
369
{
370
if (Player_CheckCollisionTouch(player, self, &hitboxCharge)) {
371
self->attackState = HOTARU_ATTACK_CHARGING;
372
}
373
}
374
}
375
376
self->alpha += 2;
377
if (self->alpha == 0x100) {
378
self->timer = 128;
379
380
if (!self->destroyedHotarus && !self->quality) {
381
++self->attackState;
382
383
self->electricityOffset.x = (self->offset1.x - self->offset2.x) >> 2;
384
self->electricityOffset.y = (self->offset1.y - self->offset2.y) >> 2;
385
}
386
387
self->state = Hotaru_State_Attacking;
388
}
389
390
Hotaru_CheckPlayerCollisions();
391
Hotaru_CheckOffScreen();
392
}
393
394
void Hotaru_State_Attacking(void)
395
{
396
RSDK_THIS(Hotaru);
397
398
RSDK.ProcessAnimation(&self->electricityAnimator);
399
if (--self->timer <= 0) {
400
self->attackState = HOTARU_ATTACK_IDLE;
401
self->alpha = 0xC0;
402
self->state = Hotaru_State_FinishedAttacking;
403
}
404
405
Hotaru_CheckPlayerCollisions();
406
407
if (self->attackState == HOTARU_ATTACK_ATTACKING) {
408
int32 storeX = self->position.x;
409
int32 storeY = self->position.y;
410
411
foreach_active(Player, player)
412
{
413
self->position.x += self->offset2.x;
414
self->position.y += self->offset2.y;
415
416
for (int32 i = 0; i < 3; ++i) {
417
self->position.x += self->electricityOffset.x;
418
self->position.y += self->electricityOffset.y;
419
420
if (Player_CheckCollisionTouch(player, self, &Hotaru->hitboxElectricity))
421
Player_ElementHurt(player, self, SHIELD_LIGHTNING);
422
}
423
424
self->position.x = storeX;
425
self->position.y = storeY;
426
}
427
}
428
429
Hotaru_CheckOffScreen();
430
}
431
432
void Hotaru_State_FinishedAttacking(void)
433
{
434
RSDK_THIS(Hotaru);
435
436
self->position.y -= 0x20000;
437
438
Hotaru_CheckPlayerCollisions();
439
Hotaru_CheckOffScreen();
440
}
441
442
#if GAME_INCLUDE_EDITOR
443
void Hotaru_EditorDraw(void)
444
{
445
RSDK_THIS(Hotaru);
446
447
self->drawGroup = Zone->objectDrawGroup[0];
448
self->active = ACTIVE_BOUNDS;
449
self->updateRange.x = MAX(self->distX1 << 16, self->distX2 << 16) + 0x400000;
450
self->updateRange.y = self->dist.y + 0x400000;
451
452
self->drawFX = FX_ROTATE | FX_FLIP;
453
self->offset2.x = -self->dist.x;
454
self->offset2.y = -self->dist.y;
455
self->offset1 = self->dist;
456
457
RSDK.SetSpriteAnimation(Hotaru->aniFrames, self->quality, &self->mainAnimator, true, 0);
458
RSDK.SetSpriteAnimation(Hotaru->aniFrames, 2, &self->bulbAnimator, true, 0);
459
RSDK.SetSpriteAnimation(Hotaru->aniFrames, 3, &self->electricityAnimator, true, 0);
460
461
Hotaru_Draw();
462
463
if (showGizmos()) {
464
RSDK_DRAWING_OVERLAY(true);
465
466
// Left Hotaru
467
self->offset1.x = -(self->distX1 << 16);
468
self->offset2.x = (self->distX1 << 16);
469
self->hotaruDir = FLIP_X;
470
self->inkEffect = INK_BLEND;
471
Hotaru_Draw();
472
473
// Right Hotaru
474
self->offset1.x = (self->distX2 << 16);
475
self->offset2.x = -(self->distX2 << 16);
476
self->hotaruDir = FLIP_NONE;
477
self->inkEffect = INK_BLEND;
478
Hotaru_Draw();
479
480
self->inkEffect = INK_NONE;
481
482
if (self->distX1 && self->distX2) {
483
// Left Hotaru
484
DrawHelpers_DrawArrow(self->position.x - (self->distX1 << 16), self->position.y, self->position.x - (self->distX2 << 16),
485
self->position.y, 0x00FF00, INK_NONE, 0xFF);
486
DrawHelpers_DrawArrow(self->position.x - (self->distX2 << 16), self->position.y, self->position.x - (self->distX1 << 16),
487
self->position.y, 0x00FF00, INK_NONE, 0xFF);
488
489
// Right Hotaru
490
DrawHelpers_DrawArrow(self->position.x + (self->distX1 << 16), self->position.y, self->position.x + (self->distX2 << 16),
491
self->position.y, 0x00FF00, INK_NONE, 0xFF);
492
DrawHelpers_DrawArrow(self->position.x + (self->distX2 << 16), self->position.y, self->position.x + (self->distX1 << 16),
493
self->position.y, 0x00FF00, INK_NONE, 0xFF);
494
}
495
496
RSDK_DRAWING_OVERLAY(false);
497
}
498
}
499
500
void Hotaru_EditorLoad(void)
501
{
502
if (RSDK.CheckSceneFolder("SSZ1"))
503
Hotaru->aniFrames = RSDK.LoadSpriteAnimation("SSZ1/Hotaru.bin", SCOPE_STAGE);
504
else if (RSDK.CheckSceneFolder("SSZ2"))
505
Hotaru->aniFrames = RSDK.LoadSpriteAnimation("SSZ2/Hotaru.bin", SCOPE_STAGE);
506
507
RSDK_ACTIVE_VAR(Hotaru, quality);
508
RSDK_ENUM_VAR("Good", HOTARU_GOOD);
509
RSDK_ENUM_VAR("Bad", HOTARU_BAD);
510
511
RSDK_ACTIVE_VAR(Hotaru, preset);
512
RSDK_ENUM_VAR("(Unused)", 0);
513
}
514
#endif
515
516
void Hotaru_Serialize(void)
517
{
518
RSDK_EDITABLE_VAR(Hotaru, VAR_UINT8, quality);
519
RSDK_EDITABLE_VAR(Hotaru, VAR_VECTOR2, dist);
520
RSDK_EDITABLE_VAR(Hotaru, VAR_ENUM, distX1);
521
RSDK_EDITABLE_VAR(Hotaru, VAR_ENUM, distX2);
522
RSDK_EDITABLE_VAR(Hotaru, VAR_ENUM, speed);
523
RSDK_EDITABLE_VAR(Hotaru, VAR_UINT8, preset);
524
}
525
526