Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/LRZ/Flamethrower.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Flamethrower Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectFlamethrower *Flamethrower;
11
12
void Flamethrower_Update(void)
13
{
14
RSDK_THIS(Flamethrower);
15
16
RSDK.ProcessAnimation(&self->animator);
17
18
StateMachine_Run(self->state);
19
}
20
21
void Flamethrower_LateUpdate(void) {}
22
23
void Flamethrower_StaticUpdate(void)
24
{
25
int32 activeFlamethrowerCount = 0;
26
27
if (RSDK_GET_ENTITY(SLOT_PAUSEMENU, PauseMenu)->classID != PauseMenu->classID) {
28
foreach_active(Flamethrower, flamethrower)
29
{
30
if (flamethrower->state == Flamethrower_State_EmittingFlames)
31
activeFlamethrowerCount++;
32
}
33
34
if (activeFlamethrowerCount) {
35
if (!Flamethrower->playingFlameSfx) {
36
RSDK.PlaySfx(Flamethrower->sfxFlame, true, 255);
37
Flamethrower->playingFlameSfx = true;
38
}
39
}
40
}
41
42
if (!activeFlamethrowerCount && Flamethrower->playingFlameSfx) {
43
RSDK.StopSfx(Flamethrower->sfxFlame);
44
Flamethrower->playingFlameSfx = false;
45
}
46
}
47
48
void Flamethrower_Draw(void)
49
{
50
RSDK_THIS(Flamethrower);
51
52
RSDK.DrawSprite(&self->animator, NULL, false);
53
}
54
55
void Flamethrower_Create(void *data)
56
{
57
RSDK_THIS(Flamethrower);
58
59
self->active = ACTIVE_BOUNDS;
60
61
if (!RSDK.CheckSceneFolder("LRZ3")) {
62
if (Flamethrower->hitboxMouthH.left)
63
self->drawGroup = Zone->playerDrawGroup[0];
64
else
65
self->drawGroup = Zone->objectDrawGroup[0];
66
}
67
else {
68
self->drawGroup = Zone->playerDrawGroup[0];
69
}
70
71
self->origin = self->position;
72
self->visible = true;
73
self->drawFX = FX_ROTATE | FX_FLIP;
74
self->updateRange.x = 0x800000;
75
self->updateRange.y = 0x800000;
76
77
if (SceneInfo->inEditor) {
78
if (!self->maxDist)
79
self->maxDist = 96;
80
81
if (!self->spread)
82
self->spread = 42;
83
84
if (!self->duration)
85
self->duration = 120;
86
87
if (!self->interval)
88
self->interval = 60;
89
}
90
91
self->state = Flamethrower_State_Init;
92
}
93
94
void Flamethrower_StageLoad(void)
95
{
96
Flamethrower->hitboxMouthH.left = -4;
97
Flamethrower->hitboxMouthH.top = -17;
98
Flamethrower->hitboxMouthH.right = 12;
99
Flamethrower->hitboxMouthH.bottom = 17;
100
101
Flamethrower->hitboxMouthV.left = -16;
102
Flamethrower->hitboxMouthV.top = 0;
103
Flamethrower->hitboxMouthV.right = 16;
104
Flamethrower->hitboxMouthV.bottom = 12;
105
106
Flamethrower->hitboxSmallFireball.left = -4;
107
Flamethrower->hitboxSmallFireball.top = -4;
108
Flamethrower->hitboxSmallFireball.right = 4;
109
Flamethrower->hitboxSmallFireball.bottom = 4;
110
111
Flamethrower->hitboxMediumFireball.left = -6;
112
Flamethrower->hitboxMediumFireball.top = -6;
113
Flamethrower->hitboxMediumFireball.right = 6;
114
Flamethrower->hitboxMediumFireball.bottom = 6;
115
116
Flamethrower->hitboxLargeFireball.left = -9;
117
Flamethrower->hitboxLargeFireball.top = -9;
118
Flamethrower->hitboxLargeFireball.right = 9;
119
Flamethrower->hitboxLargeFireball.bottom = 9;
120
121
Flamethrower->sfxFlame = RSDK.GetSfx("Stage/Flame2.wav");
122
123
if (RSDK.CheckSceneFolder("LRZ2")) {
124
Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ2/Flamethrower.bin", SCOPE_STAGE);
125
}
126
else if (RSDK.CheckSceneFolder("LRZ3")) {
127
Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ3/Flamethrower.bin", SCOPE_STAGE);
128
129
Flamethrower->hitboxMouthH.left = 0;
130
}
131
132
Flamethrower->active = ACTIVE_ALWAYS;
133
}
134
135
void Flamethrower_SetupOrientation(uint8 orientation)
136
{
137
RSDK_THIS(Flamethrower);
138
139
if (orientation == FLAMETHROWER_ORIENTATION_DOWN || orientation == FLAMETHROWER_ORIENTATION_UP)
140
RSDK.SetSpriteAnimation(Flamethrower->aniFrames, 1, &self->animator, true, 0);
141
else
142
RSDK.SetSpriteAnimation(Flamethrower->aniFrames, 0, &self->animator, true, 0);
143
}
144
145
Hitbox Flamethrower_GetHitbox(void)
146
{
147
RSDK_THIS(Flamethrower);
148
149
switch (self->animator.animationID) {
150
case 2:
151
case 5: return Flamethrower->hitboxSmallFireball;
152
153
case 3:
154
case 6: return Flamethrower->hitboxMediumFireball;
155
156
default:
157
case 4:
158
case 7: return Flamethrower->hitboxLargeFireball;
159
}
160
}
161
162
void Flamethrower_CheckOffScreen(void)
163
{
164
RSDK_THIS(Flamethrower);
165
166
if (!RSDK.CheckOnScreen(self, NULL)) {
167
self->active = ACTIVE_BOUNDS;
168
self->state = Flamethrower_State_AwaitInterval;
169
}
170
}
171
172
void Flamethrower_HandleAnimations(void)
173
{
174
RSDK_THIS(Flamethrower);
175
176
int32 dist = 0;
177
if (self->mode == FLAMETHROWER_MODE_SPRINKLER) {
178
dist = self->currentDist;
179
}
180
else {
181
if (self->orientation == FLAMETHROWER_ORIENTATION_DOWN || self->orientation == FLAMETHROWER_ORIENTATION_UP)
182
dist = abs(self->position.y - self->origin.y) >> 16;
183
else
184
dist = abs(self->position.x - self->origin.x) >> 16;
185
}
186
187
int32 anim = dist > 0x20 ? (dist > 0x40 ? 2 : 1) : 0;
188
189
if (self->orientation == FLAMETHROWER_ORIENTATION_DOWN || self->orientation == FLAMETHROWER_ORIENTATION_UP)
190
RSDK.SetSpriteAnimation(Flamethrower->aniFrames, anim + 5, &self->animator, false, 0);
191
else
192
RSDK.SetSpriteAnimation(Flamethrower->aniFrames, anim + 2, &self->animator, false, 0);
193
}
194
195
void Flamethrower_HandleAngles(void)
196
{
197
RSDK_THIS(Flamethrower);
198
199
if (self->mode != FLAMETHROWER_MODE_EMITTER) {
200
if (self->lastPos.x || self->lastPos.y) {
201
int32 angle = RSDK.ATan2(self->position.x - self->lastPos.x, self->position.y - self->lastPos.y);
202
int32 rot = 2 * angle;
203
204
switch (self->orientation) {
205
default:
206
case FLAMETHROWER_ORIENTATION_RIGHT: self->rotation = rot; break;
207
case FLAMETHROWER_ORIENTATION_DOWN: self->rotation = rot + 0x180; break;
208
case FLAMETHROWER_ORIENTATION_LEFT: self->rotation = rot + 0x100; break;
209
case FLAMETHROWER_ORIENTATION_UP: self->rotation = rot + 0x180; break;
210
}
211
self->rotation &= 0x1FF;
212
}
213
else {
214
self->rotation = 0;
215
}
216
}
217
else {
218
self->rotation = self->orientation == FLAMETHROWER_ORIENTATION_UP ? 0x100 : 0x00;
219
}
220
}
221
222
void Flamethrower_HandleTileCollisions(void)
223
{
224
RSDK_THIS(Flamethrower);
225
226
if (self->timer >= 15) {
227
Hitbox hitbox = Flamethrower_GetHitbox();
228
229
if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, hitbox.bottom << 13, 4)
230
|| RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_LWALL, 0, hitbox.right << 13, 0, 4)
231
|| RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_ROOF, 0, 0, hitbox.top << 13, 4)
232
|| RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_RWALL, 0, hitbox.left << 13, 0, 4)) {
233
destroyEntity(self);
234
}
235
}
236
}
237
238
void Flamethrower_CheckOutOfBounds(uint8 orientation)
239
{
240
RSDK_THIS(Flamethrower);
241
242
int32 dist = 0;
243
if (self->mode == FLAMETHROWER_MODE_SPRINKLER) {
244
dist = self->currentDist;
245
}
246
else {
247
if (orientation == FLAMETHROWER_ORIENTATION_DOWN || orientation == FLAMETHROWER_ORIENTATION_UP)
248
dist = abs(self->position.y - self->origin.y) >> 16;
249
else
250
dist = abs(self->position.x - self->origin.x) >> 16;
251
}
252
253
if (dist >= self->maxDist)
254
destroyEntity(self);
255
}
256
257
void Flamethrower_CheckFlameCollisions(void)
258
{
259
RSDK_THIS(Flamethrower);
260
261
Hitbox hitbox = Flamethrower_GetHitbox();
262
263
foreach_active(Player, player)
264
{
265
if (Player_CheckCollisionTouch(player, self, &hitbox)) {
266
Player_ElementHurt(player, self, SHIELD_FIRE);
267
}
268
}
269
}
270
271
void Flamethrower_CheckMouthCollisions(void)
272
{
273
RSDK_THIS(Flamethrower);
274
275
if (Flamethrower->hitboxMouthH.left) {
276
Hitbox *hitbox;
277
if (self->orientation == FLAMETHROWER_ORIENTATION_DOWN || self->orientation == FLAMETHROWER_ORIENTATION_UP)
278
hitbox = &Flamethrower->hitboxMouthV;
279
else
280
hitbox = &Flamethrower->hitboxMouthH;
281
282
foreach_active(Player, player) { Player_CheckCollisionBox(player, self, hitbox); }
283
}
284
}
285
286
void Flamethrower_State_Init(void)
287
{
288
RSDK_THIS(Flamethrower);
289
290
self->timer = 0;
291
self->active = ACTIVE_BOUNDS;
292
293
switch (self->orientation) {
294
default:
295
case FLAMETHROWER_ORIENTATION_RIGHT: self->direction = FLIP_NONE; break;
296
case FLAMETHROWER_ORIENTATION_DOWN: self->direction = MANIA_USE_PLUS ? FLIP_NONE : FLIP_Y; break;
297
case FLAMETHROWER_ORIENTATION_LEFT: self->direction = FLIP_X; break;
298
case FLAMETHROWER_ORIENTATION_UP: self->direction = MANIA_USE_PLUS ? FLIP_Y : FLIP_NONE; break;
299
}
300
301
Flamethrower_SetupOrientation(self->orientation);
302
if (!RSDK.CheckSceneFolder("LRZ3")) {
303
if (Flamethrower->hitboxMouthH.left)
304
self->drawGroup = Zone->playerDrawGroup[0];
305
else
306
self->drawGroup = Zone->objectDrawGroup[0];
307
}
308
else {
309
self->drawGroup = Zone->playerDrawGroup[0];
310
}
311
312
self->state = Flamethrower_State_AwaitInterval;
313
Flamethrower_State_AwaitInterval();
314
}
315
316
void Flamethrower_State_AwaitInterval(void)
317
{
318
RSDK_THIS(Flamethrower);
319
320
self->active = ACTIVE_BOUNDS;
321
if (!self->interval || !((Zone->timer + self->intervalOffset) % (self->interval + self->duration))) {
322
self->active = ACTIVE_NORMAL;
323
self->timer = 0;
324
self->state = Flamethrower_State_EmittingFlames;
325
}
326
327
Flamethrower_CheckMouthCollisions();
328
}
329
330
void Flamethrower_State_EmittingFlames(void)
331
{
332
RSDK_THIS(Flamethrower);
333
334
int32 angle = 0;
335
switch (self->orientation) {
336
default:
337
case FLAMETHROWER_ORIENTATION_RIGHT: angle = 0x00; break;
338
case FLAMETHROWER_ORIENTATION_DOWN: angle = 0x80; break;
339
case FLAMETHROWER_ORIENTATION_LEFT: angle = 0x100; break;
340
case FLAMETHROWER_ORIENTATION_UP: angle = 0x180; break;
341
}
342
343
self->angle = (angle + (((self->spread >> 1) * RSDK.Sin512(8 * self->flameAngle)) >> 9)) & 0x1FF;
344
if (!(self->timer & 3)) {
345
EntityFlamethrower *flame = CREATE_ENTITY(Flamethrower, self, self->position.x, self->position.y);
346
flame->active = ACTIVE_NORMAL;
347
flame->visible = true;
348
if (RSDK.CheckSceneFolder("LRZ3"))
349
flame->drawGroup = Zone->objectDrawGroup[0] - 1;
350
else
351
flame->drawGroup = Zone->objectDrawGroup[0];
352
flame->maxDist = self->maxDist;
353
flame->state = Flamethrower_State_SetupFireball;
354
flame->angle = self->angle;
355
flame->orientation = self->orientation;
356
flame->mode = self->mode;
357
flame->direction = self->direction;
358
flame->parent = self;
359
flame->velocity.x = RSDK.Cos512(self->angle) << 17 >> 8;
360
flame->velocity.y = RSDK.Sin512(self->angle) << 17 >> 8;
361
}
362
363
if (self->interval && self->timer >= self->duration) {
364
self->active = ACTIVE_BOUNDS;
365
self->timer = 0;
366
self->state = Flamethrower_State_AwaitInterval;
367
}
368
369
Flamethrower_CheckOffScreen();
370
371
++self->timer;
372
++self->flameAngle;
373
374
Flamethrower_CheckMouthCollisions();
375
}
376
377
void Flamethrower_State_SetupFireball(void)
378
{
379
RSDK_THIS(Flamethrower);
380
381
self->timer = 0;
382
self->active = ACTIVE_NORMAL;
383
self->drawFX = FX_ROTATE | FX_FLIP;
384
385
Flamethrower_HandleAnimations();
386
387
switch (self->orientation) {
388
default:
389
case FLAMETHROWER_ORIENTATION_RIGHT: self->direction = FLIP_NONE; break;
390
case FLAMETHROWER_ORIENTATION_DOWN: self->direction = MANIA_USE_PLUS ? FLIP_NONE : FLIP_Y; break;
391
case FLAMETHROWER_ORIENTATION_LEFT: self->direction = FLIP_X; break;
392
case FLAMETHROWER_ORIENTATION_UP: self->direction = MANIA_USE_PLUS ? FLIP_Y : FLIP_NONE; break;
393
}
394
395
if (RSDK.CheckSceneFolder("LRZ3"))
396
self->drawGroup = Zone->objectDrawGroup[0] - 1;
397
else
398
self->drawGroup = Zone->objectDrawGroup[0];
399
400
self->state = Flamethrower_State_Fireball;
401
self->lastPos.x = 0;
402
self->lastPos.y = 0;
403
self->lastYVelocity = 0;
404
405
Flamethrower_State_Fireball();
406
}
407
408
void Flamethrower_State_Fireball(void)
409
{
410
RSDK_THIS(Flamethrower);
411
412
if (self->mode == FLAMETHROWER_MODE_SPRINKLER)
413
self->velocity.y += 0x3800;
414
415
if (self->velocity.y < self->lastYVelocity)
416
self->lastYVelocity = self->velocity.y;
417
418
// Check for flipping downwards after going upwards
419
if (self->currentDist > 12 && self->lastYVelocity < 0 && self->velocity.y > 0 && self->drawGroup == Zone->objectDrawGroup[0])
420
self->drawGroup = Zone->objectDrawGroup[1];
421
422
self->position.x += self->velocity.x;
423
self->position.y += self->velocity.y;
424
425
if (HeavyRider && self->velocity.y > 0)
426
self->drawGroup = Zone->objectDrawGroup[1];
427
428
if (self->lastPos.x && self->lastPos.y) {
429
int32 rx = (self->position.x - self->lastPos.x) >> 8;
430
int32 ry = (self->position.y - self->lastPos.y) >> 8;
431
self->currentDist += MathHelpers_SquareRoot((rx * rx + ry * ry) >> 16);
432
}
433
434
#if MANIA_USE_PLUS
435
if (self->orientation == FLAMETHROWER_ORIENTATION_UP && self->velocity.y > 0) {
436
self->orientation = FLAMETHROWER_ORIENTATION_DOWN;
437
self->direction = FLIP_NONE;
438
}
439
#endif
440
441
Flamethrower_HandleAnimations();
442
Flamethrower_HandleTileCollisions();
443
Flamethrower_HandleAngles();
444
445
if (self->mode == FLAMETHROWER_MODE_EMITTER)
446
Flamethrower_CheckOutOfBounds(self->orientation);
447
448
Flamethrower_CheckFlameCollisions();
449
450
++self->timer;
451
self->lastPos = self->position;
452
}
453
454
#if GAME_INCLUDE_EDITOR
455
void Flamethrower_EditorDraw(void)
456
{
457
RSDK_THIS(Flamethrower);
458
459
Flamethrower_Create(NULL);
460
Flamethrower_SetupOrientation(self->orientation);
461
462
switch (self->orientation) {
463
default:
464
case FLAMETHROWER_ORIENTATION_RIGHT: self->direction = FLIP_NONE; break;
465
case FLAMETHROWER_ORIENTATION_DOWN: self->direction = MANIA_USE_PLUS ? FLIP_NONE : FLIP_Y; break;
466
case FLAMETHROWER_ORIENTATION_LEFT: self->direction = FLIP_X; break;
467
case FLAMETHROWER_ORIENTATION_UP: self->direction = MANIA_USE_PLUS ? FLIP_Y : FLIP_NONE; break;
468
}
469
470
Flamethrower_Draw();
471
}
472
473
void Flamethrower_EditorLoad(void)
474
{
475
if (RSDK.CheckSceneFolder("LRZ2"))
476
Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ2/Flamethrower.bin", SCOPE_STAGE);
477
else if (RSDK.CheckSceneFolder("LRZ3"))
478
Flamethrower->aniFrames = RSDK.LoadSpriteAnimation("LRZ3/Flamethrower.bin", SCOPE_STAGE);
479
480
RSDK_ACTIVE_VAR(Flamethrower, orientation);
481
RSDK_ENUM_VAR("Right", FLAMETHROWER_ORIENTATION_RIGHT);
482
RSDK_ENUM_VAR("Down", FLAMETHROWER_ORIENTATION_DOWN);
483
RSDK_ENUM_VAR("Left", FLAMETHROWER_ORIENTATION_LEFT);
484
RSDK_ENUM_VAR("Up", FLAMETHROWER_ORIENTATION_UP);
485
486
RSDK_ACTIVE_VAR(Flamethrower, mode);
487
RSDK_ENUM_VAR("Emitter", FLAMETHROWER_MODE_EMITTER);
488
RSDK_ENUM_VAR("Sprinkler", FLAMETHROWER_MODE_SPRINKLER);
489
}
490
#endif
491
492
void Flamethrower_Serialize(void)
493
{
494
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT8, orientation);
495
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT8, mode);
496
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT32, maxDist);
497
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, spread);
498
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, interval);
499
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, intervalOffset);
500
RSDK_EDITABLE_VAR(Flamethrower, VAR_UINT16, duration);
501
}
502
503