Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/PGZ/JuggleSaw.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: JuggleSaw Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectJuggleSaw *JuggleSaw;
11
12
// 🦀 crab in da code
13
14
void JuggleSaw_Update(void)
15
{
16
RSDK_THIS(JuggleSaw);
17
18
StateMachine_Run(self->state);
19
}
20
21
void JuggleSaw_LateUpdate(void) {}
22
23
void JuggleSaw_StaticUpdate(void) {}
24
25
void JuggleSaw_Draw(void)
26
{
27
RSDK_THIS(JuggleSaw);
28
29
RSDK.DrawSprite(&self->animator, NULL, false);
30
}
31
32
void JuggleSaw_Create(void *data)
33
{
34
RSDK_THIS(JuggleSaw);
35
36
self->visible = true;
37
self->drawFX |= FX_FLIP;
38
self->drawGroup = Zone->objectDrawGroup[0];
39
self->active = ACTIVE_BOUNDS;
40
self->updateRange.x = 0xC00000;
41
self->updateRange.y = 0xC00000;
42
43
if (!SceneInfo->inEditor) {
44
if (data) {
45
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 6, &self->animator, true, 0);
46
self->state = JuggleSaw_StateSaw_Handle;
47
}
48
else {
49
self->startPos = self->position;
50
self->startDir = self->direction;
51
52
switch (self->direction) {
53
case FLIP_NONE: RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->hasSaw == JUGGLESAW_HAS_SAW, &self->animator, true, 0); break;
54
55
case FLIP_X:
56
self->direction = FLIP_Y;
57
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->hasSaw == JUGGLESAW_HAS_SAW, &self->animator, true, 0);
58
break;
59
60
case FLIP_Y:
61
self->direction = FLIP_NONE;
62
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 4 - (self->hasSaw != JUGGLESAW_HAS_SAW), &self->animator, true, 0);
63
break;
64
65
case FLIP_XY:
66
self->direction = FLIP_X;
67
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 4 - (self->hasSaw != JUGGLESAW_HAS_SAW), &self->animator, true, 0);
68
break;
69
70
default: break;
71
}
72
73
if (!self->sawSpeed)
74
self->sawSpeed = 8;
75
76
self->sawSpeed <<= 7;
77
self->state = JuggleSaw_StateCrab_Setup;
78
}
79
}
80
}
81
82
void JuggleSaw_StageLoad(void)
83
{
84
if (RSDK.CheckSceneFolder("PSZ1"))
85
JuggleSaw->aniFrames = RSDK.LoadSpriteAnimation("PSZ1/JuggleSaw.bin", SCOPE_STAGE);
86
else if (RSDK.CheckSceneFolder("PSZ2"))
87
JuggleSaw->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/JuggleSaw.bin", SCOPE_STAGE);
88
89
JuggleSaw->hitboxBadnik.left = -12;
90
JuggleSaw->hitboxBadnik.top = -18;
91
JuggleSaw->hitboxBadnik.right = 12;
92
JuggleSaw->hitboxBadnik.bottom = 18;
93
94
JuggleSaw->hitboxFriendRange.left = -512;
95
JuggleSaw->hitboxFriendRange.top = -512;
96
JuggleSaw->hitboxFriendRange.right = 512;
97
JuggleSaw->hitboxFriendRange.bottom = 512;
98
99
JuggleSaw->hitboxSaw.left = -12;
100
JuggleSaw->hitboxSaw.top = -12;
101
JuggleSaw->hitboxSaw.right = 12;
102
JuggleSaw->hitboxSaw.bottom = 12;
103
104
JuggleSaw->hitboxGrabV.left = -24;
105
JuggleSaw->hitboxGrabV.top = -38;
106
JuggleSaw->hitboxGrabV.right = -16;
107
JuggleSaw->hitboxGrabV.bottom = -30;
108
109
JuggleSaw->hitboxGrabH.left = 38;
110
JuggleSaw->hitboxGrabH.top = -24;
111
JuggleSaw->hitboxGrabH.right = 30;
112
JuggleSaw->hitboxGrabH.bottom = -16;
113
114
DEBUGMODE_ADD_OBJ(JuggleSaw);
115
116
JuggleSaw->sfxExplode = RSDK.GetSfx("Stage/Explosion.wav"); // not actually used it seems
117
JuggleSaw->sfxJuggle = RSDK.GetSfx("PSZ/Juggle.wav");
118
JuggleSaw->sfxThrow = RSDK.GetSfx("PSZ/JuggleThrow.wav");
119
}
120
121
void JuggleSaw_DebugSpawn(void)
122
{
123
RSDK_THIS(DebugMode);
124
125
EntityJuggleSaw *badnik = CREATE_ENTITY(JuggleSaw, NULL, self->position.x, self->position.y);
126
badnik->sawDelay = 30;
127
}
128
129
void JuggleSaw_DebugDraw(void)
130
{
131
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 0, &DebugMode->animator, true, 0);
132
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
133
}
134
135
void JuggleSaw_CheckPlayerCollisions(void)
136
{
137
RSDK_THIS(JuggleSaw);
138
139
foreach_active(Player, player)
140
{
141
if (Player_CheckBadnikTouch(player, self, &JuggleSaw->hitboxBadnik) && Player_CheckBadnikBreak(player, self, false)) {
142
if (self->hasSaw == JUGGLESAW_HAS_SAW) {
143
int32 debrisX = self->position.x;
144
int32 debrisY = self->position.y;
145
if (self->startDir >= FLIP_Y)
146
debrisX += 0x200000 * ((self->direction & FLIP_X) ? -1 : 1);
147
else
148
debrisY += 0x200000 * ((self->direction & FLIP_Y) ? 1 : -1);
149
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, debrisX, debrisY);
150
151
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 6, &debris->animator, true, 0);
152
153
int32 minVelX = -4, maxVelX = 5, minVelY = -4, maxVelY = 5;
154
if (self->startDir >= FLIP_Y)
155
minVelY = (self->direction & FLIP_X) ? -1 : -4;
156
else
157
minVelY = (self->direction & FLIP_Y) ? -4 : -1;
158
159
debris->velocity.x = RSDK.Rand(minVelX, maxVelX) << 16;
160
debris->velocity.y = RSDK.Rand(minVelY, maxVelY) << 16;
161
debris->gravityStrength = 0x3800;
162
debris->drawGroup = self->drawGroup;
163
debris->updateRange.x = 0x400000;
164
debris->updateRange.y = 0x400000;
165
}
166
167
destroyEntity(self);
168
}
169
}
170
}
171
172
void JuggleSaw_CheckOffScreen(void)
173
{
174
RSDK_THIS(JuggleSaw);
175
176
if (self->hasSaw != JUGGLESAW_AWAITING_SAW && !RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
177
self->sawSpeed = self->sawSpeed >> 7;
178
self->position = self->startPos;
179
self->direction = self->startDir;
180
JuggleSaw_Create(NULL);
181
}
182
}
183
184
void JuggleSaw_StateCrab_Setup(void)
185
{
186
RSDK_THIS(JuggleSaw);
187
188
self->active = ACTIVE_NORMAL;
189
self->friends[0] = NULL;
190
self->angle = 0;
191
self->sawTimer = 0;
192
193
self->state = JuggleSaw_StateCrab_Handle;
194
JuggleSaw_StateCrab_Handle();
195
}
196
197
void JuggleSaw_StateCrab_Handle(void)
198
{
199
RSDK_THIS(JuggleSaw);
200
201
// this was the worst func to decompile so far. -rmg
202
RSDK.ProcessAnimation(&self->animator);
203
204
switch (self->startDir) {
205
case FLIP_NONE:
206
case FLIP_X: self->position.x = (RSDK.Cos256(self->angle) << 10) + self->startPos.x; break;
207
208
case FLIP_Y:
209
case FLIP_XY: self->position.y = (RSDK.Cos256(self->angle) << 10) + self->startPos.y; break;
210
211
default: break;
212
}
213
214
if (self->hasSaw != JUGGLESAW_HAS_SAW) {
215
self->angle += 2;
216
}
217
else {
218
self->angle += 4;
219
220
if (self->animator.frameID == self->animator.frameCount - 1) {
221
RSDK.PlaySfx(JuggleSaw->sfxJuggle, false, 0xFF);
222
if (self->startDir >= FLIP_Y)
223
self->direction ^= FLIP_Y;
224
else
225
self->direction ^= FLIP_X;
226
}
227
else {
228
if (self->sawTimer == self->sawDelay - 1)
229
--self->sawTimer;
230
}
231
232
if (++self->sawTimer >= self->sawDelay) {
233
bool32 throwSaw = true;
234
235
if (!self->friends[0] || self->friends[0]->classID != JuggleSaw->classID) {
236
self->friendCount = 0;
237
foreach_active(JuggleSaw, newFriend)
238
{
239
if (newFriend != self && newFriend->hasSaw == JUGGLESAW_NO_SAW && newFriend->setID == self->setID
240
&& self->friendCount < JUGGLESAW_MAX_FRIENDS) {
241
if (RSDK.CheckObjectCollisionTouchBox(newFriend, &JuggleSaw->hitboxBadnik, self, &JuggleSaw->hitboxFriendRange))
242
self->friends[self->friendCount++] = newFriend;
243
}
244
}
245
246
if (self->friendCount) {
247
self->friends[0] = self->friends[RSDK.Rand(0, self->friendCount)];
248
249
if (self->startDir >= FLIP_Y) {
250
if (self->position.y < self->friends[0]->position.y) {
251
if ((self->direction & FLIP_Y))
252
--self->sawTimer;
253
}
254
else {
255
if (!(self->direction & FLIP_Y))
256
--self->sawTimer;
257
}
258
}
259
else {
260
if (self->position.x < self->friends[0]->position.x) {
261
if ((self->direction & FLIP_X))
262
--self->sawTimer;
263
}
264
else {
265
if (!(self->direction & FLIP_X))
266
--self->sawTimer;
267
}
268
}
269
270
throwSaw = false;
271
if (self->sawTimer >= self->sawDelay)
272
throwSaw = true;
273
}
274
else {
275
throwSaw = false;
276
}
277
}
278
279
if (throwSaw) {
280
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->startDir >= FLIP_Y ? 5 : 2, &self->animator, true, 0);
281
self->direction ^= (self->startDir >= FLIP_Y) ? FLIP_Y : FLIP_X;
282
self->state = JuggleSaw_StateCrab_ThrowSaw;
283
}
284
}
285
}
286
287
JuggleSaw_CheckPlayerCollisions();
288
JuggleSaw_CheckOffScreen();
289
}
290
291
void JuggleSaw_StateCrab_ThrowSaw(void)
292
{
293
RSDK_THIS(JuggleSaw);
294
295
RSDK.ProcessAnimation(&self->animator);
296
297
if (self->animator.frameID == 3) {
298
EntityJuggleSaw *reciever = self->friends[0];
299
300
if (reciever->classID == JuggleSaw->classID) {
301
RSDK.PlaySfx(JuggleSaw->sfxThrow, false, 0xFF);
302
reciever->hasSaw = JUGGLESAW_AWAITING_SAW;
303
reciever->active = ACTIVE_NORMAL;
304
305
EntityJuggleSaw *saw = CREATE_ENTITY(JuggleSaw, INT_TO_VOID(true), self->position.x, self->position.y);
306
int32 sx = 0x2C0000, sy = -0xE0000;
307
if (self->startDir >= FLIP_Y) {
308
sx = 0xE0000;
309
sy = 0x2C0000;
310
}
311
312
saw->position.x += sx * ((self->direction & FLIP_X) ? -1 : 1);
313
saw->position.y += sy * ((self->direction & FLIP_Y) ? -1 : 1);
314
315
int32 targetX = 0;
316
int32 targetY = 0;
317
int32 sawDir = 0;
318
if (reciever->startDir >= FLIP_Y) {
319
targetX = reciever->position.x + 0x220000 * ((reciever->direction & FLIP_X) ? -1 : 1);
320
targetY = reciever->position.y + 0x140000 * (reciever->startPos.y >= self->startPos.y ? 1 : -1);
321
sawDir = (reciever->direction & FLIP_X) | (reciever->startPos.y >= self->startPos.y ? FLIP_Y : 0);
322
}
323
else {
324
targetX = reciever->position.x + 0x140000 * (reciever->startPos.x >= self->startPos.x ? 1 : -1);
325
targetY = reciever->position.y + 0x220000 * ((reciever->direction & FLIP_Y) ? 1 : -1);
326
sawDir = (reciever->direction & FLIP_Y) | (reciever->startPos.x >= self->startPos.x ? FLIP_X : FLIP_NONE);
327
}
328
329
saw->direction = sawDir;
330
int32 targetAngle = RSDK.ATan2(targetX - saw->position.x, targetY - saw->position.y);
331
saw->velocity.x = self->sawSpeed * RSDK.Cos256(targetAngle);
332
saw->velocity.y = self->sawSpeed * RSDK.Sin256(targetAngle);
333
saw->friends[0] = reciever;
334
saw->hasSaw = JUGGLESAW_HAS_SAW;
335
saw->active = ACTIVE_NORMAL;
336
self->sawTimer = 0;
337
self->hasSaw = JUGGLESAW_NO_SAW;
338
}
339
else {
340
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->startDir >= FLIP_Y ? 4 : 1, &self->animator, true, 0);
341
self->state = JuggleSaw_StateCrab_Handle;
342
}
343
344
self->friends[0] = NULL;
345
}
346
347
if (self->animator.frameID == self->animator.frameCount - 1) {
348
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->startDir >= FLIP_Y ? 3 : 0, &self->animator, true, 0);
349
self->state = JuggleSaw_StateCrab_Handle;
350
}
351
352
JuggleSaw_CheckPlayerCollisions();
353
JuggleSaw_CheckOffScreen();
354
}
355
356
void JuggleSaw_StateSaw_Handle(void)
357
{
358
RSDK_THIS(JuggleSaw);
359
360
self->position.x += self->velocity.x;
361
self->position.y += self->velocity.y;
362
363
EntityJuggleSaw *reciever = self->friends[0];
364
int32 oldDir = reciever->direction;
365
reciever->direction = self->direction;
366
367
Hitbox *hitboxGrab = &JuggleSaw->hitboxGrabV;
368
if (reciever->startDir >= FLIP_Y)
369
hitboxGrab = &JuggleSaw->hitboxGrabH;
370
371
// I have decided to imortalize this bit of code that the below code replaces because this cost RMG and I at least 4 hours of our lives
372
// collectively.
373
// reciever->direction = oldDir;
374
// if (RSDK.CheckObjectCollisionTouchBox(reciever, hitboxGrab, self, &JuggleSaw->hitboxSaw)) {
375
376
bool32 collided = RSDK.CheckObjectCollisionTouchBox(reciever, hitboxGrab, self, &JuggleSaw->hitboxSaw);
377
reciever->direction = oldDir;
378
379
if (collided) {
380
int32 newDir = 0;
381
if (reciever->startDir >= FLIP_Y) {
382
if (self->velocity.y > 0)
383
newDir = oldDir | FLIP_Y;
384
else if (self->velocity.y < 0)
385
newDir = oldDir & ~FLIP_Y;
386
}
387
else {
388
if (self->velocity.x > 0)
389
newDir = oldDir | FLIP_X;
390
else if (self->velocity.x < 0)
391
newDir = oldDir & ~FLIP_X;
392
}
393
394
reciever->direction = newDir;
395
396
reciever->hasSaw = JUGGLESAW_HAS_SAW;
397
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, reciever->startDir >= FLIP_Y ? 4 : 1, &reciever->animator, true, 0);
398
destroyEntity(self);
399
}
400
else if (RSDK.CheckOnScreen(self, &self->updateRange)) {
401
RSDK.ProcessAnimation(&self->animator);
402
403
foreach_active(Player, player)
404
{
405
if (Player_CheckCollisionTouch(player, self, &JuggleSaw->hitboxSaw)) {
406
#if MANIA_USE_PLUS
407
if (Player_CheckMightyShellHit(player, self, -0x400, -0x600)) {
408
self->interaction = false;
409
self->state = JuggleSaw_StateSaw_Debris;
410
}
411
else
412
#endif
413
Player_Hurt(player, self);
414
}
415
}
416
}
417
else if (!self->friends[0] || self->friends[0]->classID != JuggleSaw->classID)
418
destroyEntity(self);
419
}
420
421
void JuggleSaw_StateSaw_Debris(void)
422
{
423
RSDK_THIS(JuggleSaw);
424
425
self->position.x += self->velocity.x;
426
self->position.y += self->velocity.y;
427
self->velocity.y += 0x3800;
428
429
self->visible ^= true;
430
431
if (!RSDK.CheckOnScreen(self, &self->updateRange))
432
destroyEntity(self);
433
}
434
435
#if GAME_INCLUDE_EDITOR
436
void JuggleSaw_EditorDraw(void)
437
{
438
RSDK_THIS(JuggleSaw);
439
440
int32 dir = self->direction;
441
442
switch (self->direction) {
443
case FLIP_NONE: RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->hasSaw == JUGGLESAW_HAS_SAW, &self->animator, true, 0); break;
444
445
case FLIP_X:
446
self->direction = FLIP_Y;
447
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, self->hasSaw == JUGGLESAW_HAS_SAW, &self->animator, true, 0);
448
break;
449
450
case FLIP_Y:
451
self->direction = FLIP_NONE;
452
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 4 - (self->hasSaw != JUGGLESAW_HAS_SAW), &self->animator, true, 0);
453
break;
454
455
case FLIP_XY:
456
self->direction = FLIP_X;
457
RSDK.SetSpriteAnimation(JuggleSaw->aniFrames, 4 - (self->hasSaw != JUGGLESAW_HAS_SAW), &self->animator, true, 0);
458
break;
459
460
default: break;
461
}
462
463
JuggleSaw_Draw();
464
465
if (self->hasSaw == JUGGLESAW_HAS_SAW && showGizmos()) {
466
RSDK_DRAWING_OVERLAY(true);
467
468
DrawHelpers_DrawHitboxOutline(self->position.x, self->position.y, &JuggleSaw->hitboxFriendRange, self->direction, 0xFF0000);
469
470
RSDK_DRAWING_OVERLAY(false);
471
}
472
473
self->direction = dir;
474
}
475
476
void JuggleSaw_EditorLoad(void)
477
{
478
if (RSDK.CheckSceneFolder("PSZ1"))
479
JuggleSaw->aniFrames = RSDK.LoadSpriteAnimation("PSZ1/JuggleSaw.bin", SCOPE_STAGE);
480
else if (RSDK.CheckSceneFolder("PSZ2"))
481
JuggleSaw->aniFrames = RSDK.LoadSpriteAnimation("PSZ2/JuggleSaw.bin", SCOPE_STAGE);
482
483
JuggleSaw->hitboxFriendRange.left = -512;
484
JuggleSaw->hitboxFriendRange.top = -512;
485
JuggleSaw->hitboxFriendRange.right = 512;
486
JuggleSaw->hitboxFriendRange.bottom = 512;
487
488
RSDK_ACTIVE_VAR(JuggleSaw, direction);
489
RSDK_ENUM_VAR("No Flip (Vertical)", FLIP_NONE);
490
RSDK_ENUM_VAR("Flipped (Vertical)", FLIP_X);
491
RSDK_ENUM_VAR("No Flip (Horizontal)", FLIP_Y);
492
RSDK_ENUM_VAR("Flipped (Horizontal)", FLIP_XY);
493
}
494
#endif
495
496
void JuggleSaw_Serialize(void)
497
{
498
RSDK_EDITABLE_VAR(JuggleSaw, VAR_UINT8, direction);
499
RSDK_EDITABLE_VAR(JuggleSaw, VAR_UINT16, sawDelay);
500
RSDK_EDITABLE_VAR(JuggleSaw, VAR_ENUM, sawSpeed);
501
RSDK_EDITABLE_VAR(JuggleSaw, VAR_BOOL, hasSaw); // liars.
502
RSDK_EDITABLE_VAR(JuggleSaw, VAR_UINT16, setID);
503
}
504
505