Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/CPZ/Grabber.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Grabber Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectGrabber *Grabber;
11
12
void Grabber_Update(void)
13
{
14
RSDK_THIS(Grabber);
15
16
StateMachine_Run(self->state);
17
}
18
19
void Grabber_LateUpdate(void) {}
20
21
void Grabber_StaticUpdate(void) {}
22
23
void Grabber_Draw(void)
24
{
25
RSDK_THIS(Grabber);
26
27
if (SceneInfo->currentDrawGroup == self->drawGroup) {
28
RSDK.DrawLine(self->position.x, self->startPos.y - 0x100000, self->position.x, self->position.y, 0x202020, 0x00, INK_NONE, false);
29
RSDK.DrawLine(self->position.x - 0x10000, self->startPos.y - 0x100000, self->position.x - 0x10000, self->position.y, 0xE0E0E0, 0x00, INK_NONE,
30
false);
31
32
Vector2 drawPos;
33
int32 storeDir = self->direction;
34
drawPos.x = self->position.x;
35
drawPos.y = self->startPos.y;
36
self->direction = 0;
37
RSDK.DrawSprite(&self->wheelAnimator, &drawPos, false);
38
39
self->direction = storeDir;
40
RSDK.DrawSprite(&self->bodyAnimator, NULL, false);
41
42
if (!SceneInfo->currentScreenID)
43
RSDK.AddDrawListRef(Zone->objectDrawGroup[1], SceneInfo->entitySlot);
44
}
45
else {
46
RSDK.DrawSprite(&self->clampAnimator, NULL, false);
47
}
48
}
49
50
void Grabber_Create(void *data)
51
{
52
RSDK_THIS(Grabber);
53
54
self->visible = true;
55
self->drawGroup = Zone->objectDrawGroup[0];
56
self->startPos = self->position;
57
self->startDir = self->direction;
58
self->active = ACTIVE_BOUNDS;
59
60
self->velocity.x = !self->direction ? -0x4000 : 0x4000;
61
self->updateRange.x = 0x800000;
62
self->updateRange.y = 0x800000;
63
self->drawFX = FX_FLIP;
64
RSDK.SetSpriteAnimation(Grabber->aniFrames, 0, &self->bodyAnimator, true, 0);
65
RSDK.SetSpriteAnimation(Grabber->aniFrames, 1, &self->clampAnimator, true, 0);
66
RSDK.SetSpriteAnimation(Grabber->aniFrames, 4, &self->wheelAnimator, true, 0);
67
self->state = Grabber_State_Init;
68
}
69
70
void Grabber_StageLoad(void)
71
{
72
Grabber->aniFrames = RSDK.LoadSpriteAnimation("CPZ/Grabber.bin", SCOPE_STAGE);
73
74
Grabber->hitboxBadnik.left = -8;
75
Grabber->hitboxBadnik.top = -8;
76
Grabber->hitboxBadnik.right = 8;
77
Grabber->hitboxBadnik.bottom = 8;
78
79
Grabber->hitboxRange.left = -64;
80
Grabber->hitboxRange.top = 0;
81
Grabber->hitboxRange.right = 64;
82
Grabber->hitboxRange.bottom = 128;
83
84
Grabber->hitboxGrab.left = -8;
85
Grabber->hitboxGrab.top = 0;
86
Grabber->hitboxGrab.right = 8;
87
Grabber->hitboxGrab.bottom = 16;
88
89
DEBUGMODE_ADD_OBJ(Grabber);
90
91
Grabber->sfxGrab = RSDK.GetSfx("Global/Grab.wav");
92
Grabber->sfxDestroy = RSDK.GetSfx("Global/Destroy.wav");
93
}
94
95
void Grabber_DebugDraw(void)
96
{
97
RSDK.SetSpriteAnimation(Grabber->aniFrames, 0, &DebugMode->animator, true, 0);
98
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
99
}
100
101
void Grabber_DebugSpawn(void)
102
{
103
RSDK_THIS(Grabber);
104
EntityGrabber *grabber = CREATE_ENTITY(Grabber, NULL, self->position.x, self->position.y);
105
grabber->direction = self->direction;
106
grabber->startDir = self->direction;
107
grabber->velocity.x = !self->direction ? -0x4000 : 0x4000;
108
}
109
110
void Grabber_CheckPlayerCollisions(void)
111
{
112
RSDK_THIS(Grabber);
113
114
foreach_active(Player, player)
115
{
116
if (player != self->grabbedPlayer) {
117
if (self->state == Grabber_State_TryToGrab) {
118
if (Player_CheckCollisionTouch(player, self, &Grabber->hitboxGrab)) {
119
RSDK.PlaySfx(Grabber->sfxGrab, false, 255);
120
self->state = Grabber_State_GrabbedPlayer;
121
self->isPermanent = true;
122
self->grabbedPlayer = player;
123
player->velocity.x = 0;
124
player->velocity.y = 0;
125
player->groundVel = 0;
126
player->state = Player_State_Static;
127
player->nextAirState = StateMachine_None;
128
player->nextGroundState = StateMachine_None;
129
player->onGround = false;
130
player->direction = self->direction;
131
RSDK.SetSpriteAnimation(player->aniFrames, ANI_FAN, &player->animator, true, 0);
132
player->animator.speed = 0;
133
player->direction = self->direction ^ FLIP_X;
134
}
135
}
136
137
if (player != self->grabbedPlayer && !self->grabDelay && Player_CheckBadnikTouch(player, self, &Grabber->hitboxBadnik)
138
&& Player_CheckBadnikBreak(player, self, false)) {
139
EntityPlayer *playerPtr = self->grabbedPlayer;
140
if (playerPtr)
141
playerPtr->state = Player_State_Air;
142
destroyEntity(self);
143
}
144
}
145
}
146
}
147
148
void Grabber_CheckOffScreen(void)
149
{
150
RSDK_THIS(Grabber);
151
152
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
153
self->position = self->startPos;
154
self->struggleDelay = 0;
155
self->struggleTimer = 0;
156
self->struggleFlags = 0;
157
self->direction = self->startDir;
158
self->grabbedPlayer = NULL;
159
self->turnTimer = 0;
160
self->timer = 0;
161
Grabber_Create(NULL);
162
}
163
}
164
165
void Grabber_HandleExplode(void)
166
{
167
RSDK_THIS(Grabber);
168
169
if (!--self->turnTimer) {
170
self->bodyAnimator.frameID ^= 1;
171
self->turnTimer = self->timer;
172
173
if (!--self->timer) {
174
EntityPlayer *player = self->grabbedPlayer;
175
if (player && player->state == Player_State_Static) {
176
Player_Hurt(player, self);
177
if (player->state != Player_State_Hurt && Player_CheckValidState(player))
178
player->state = Player_State_Air;
179
self->grabbedPlayer = NULL;
180
}
181
182
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_ENEMY), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
183
RSDK.PlaySfx(Explosion->sfxDestroy, false, 255);
184
destroyEntity(self);
185
}
186
}
187
}
188
189
void Grabber_State_Init(void)
190
{
191
RSDK_THIS(Grabber);
192
193
self->active = ACTIVE_NORMAL;
194
self->state = Grabber_State_CheckForGrab;
195
Grabber_State_CheckForGrab();
196
}
197
198
void Grabber_State_CheckForGrab(void)
199
{
200
RSDK_THIS(Grabber);
201
202
RSDK.ProcessAnimation(&self->wheelAnimator);
203
204
if (++self->turnTimer >= 256) {
205
self->turnTimer = 0;
206
RSDK.SetSpriteAnimation(Grabber->aniFrames, 3, &self->bodyAnimator, true, 0);
207
RSDK.SetSpriteAnimation(-1, 0, &self->clampAnimator, true, 0);
208
self->state = Grabber_State_HandleTurn;
209
}
210
self->position.x += self->velocity.x;
211
212
foreach_active(Player, player)
213
{
214
if (Player_CheckCollisionTouch(player, self, &Grabber->hitboxRange)) {
215
self->timer = 16;
216
self->state = Grabber_State_GrabDelay;
217
}
218
}
219
220
Grabber_CheckPlayerCollisions();
221
Grabber_CheckOffScreen();
222
}
223
224
void Grabber_State_GrabDelay(void)
225
{
226
RSDK_THIS(Grabber);
227
228
if (--self->timer < 0) {
229
self->timer = 32;
230
self->state = Grabber_State_TryToGrab;
231
}
232
233
Grabber_CheckPlayerCollisions();
234
Grabber_CheckOffScreen();
235
}
236
237
void Grabber_State_HandleTurn(void)
238
{
239
RSDK_THIS(Grabber);
240
241
RSDK.ProcessAnimation(&self->bodyAnimator);
242
243
if (self->bodyAnimator.frameID == self->bodyAnimator.frameCount - 1) {
244
RSDK.SetSpriteAnimation(Grabber->aniFrames, 0, &self->bodyAnimator, true, 0);
245
RSDK.SetSpriteAnimation(Grabber->aniFrames, 1, &self->clampAnimator, true, 0);
246
if (self->direction == FLIP_X)
247
self->position.x -= 0x10000;
248
else
249
self->position.x += 0x10000;
250
251
self->state = Grabber_State_CheckForGrab;
252
self->velocity.x = -self->velocity.x;
253
self->direction ^= FLIP_X;
254
Grabber_State_CheckForGrab();
255
}
256
else {
257
Grabber_CheckPlayerCollisions();
258
Grabber_CheckOffScreen();
259
}
260
}
261
262
void Grabber_State_TryToGrab(void)
263
{
264
RSDK_THIS(Grabber);
265
266
if (--self->timer < 0)
267
self->state = Grabber_State_RiseUp;
268
269
self->position.y += 0x20000;
270
271
Grabber_CheckPlayerCollisions();
272
Grabber_CheckOffScreen();
273
}
274
275
void Grabber_State_RiseUp(void)
276
{
277
RSDK_THIS(Grabber);
278
279
if (++self->timer >= 32) {
280
self->timer = 0;
281
self->state = Grabber_State_CheckForGrab;
282
}
283
284
self->position.y -= 0x20000;
285
Grabber_CheckPlayerCollisions();
286
Grabber_CheckOffScreen();
287
}
288
289
void Grabber_State_GrabbedPlayer(void)
290
{
291
RSDK_THIS(Grabber);
292
293
RSDK.ProcessAnimation(&self->bodyAnimator);
294
RSDK.ProcessAnimation(&self->clampAnimator);
295
296
if (++self->timer < 32) {
297
self->position.y -= 0x20000;
298
299
EntityPlayer *player = self->grabbedPlayer;
300
if (player) {
301
player->animator.speed = 0;
302
player->position.x = self->position.x;
303
player->position.y = self->position.y + 0x100000;
304
player->velocity.x = 0;
305
player->velocity.y = 0;
306
player->direction = self->direction ^ FLIP_X;
307
}
308
Grabber_CheckPlayerCollisions();
309
}
310
else if (self->bodyAnimator.frameID != self->bodyAnimator.frameCount - 1) {
311
Grabber_CheckPlayerCollisions();
312
}
313
else {
314
self->turnTimer = 1;
315
self->timer = 16;
316
RSDK.SetSpriteAnimation(Grabber->aniFrames, 2, &self->bodyAnimator, true, 0);
317
self->state = Grabber_State_Struggle;
318
Grabber_CheckPlayerCollisions();
319
}
320
321
Grabber_CheckOffScreen();
322
}
323
324
void Grabber_State_Struggle(void)
325
{
326
RSDK_THIS(Grabber);
327
328
EntityPlayer *player = self->grabbedPlayer;
329
if (player) {
330
player->animator.speed = 0;
331
if (self->struggleFlags) {
332
player->velocity.x = 0;
333
player->velocity.y = 0;
334
if (self->struggleDelay) {
335
self->struggleDelay--;
336
337
uint8 struggleFlags = 0;
338
if (player->left)
339
struggleFlags = 1;
340
341
if (player->right)
342
struggleFlags |= 2;
343
344
if (struggleFlags) {
345
if (struggleFlags != 3 && struggleFlags != self->struggleFlags) {
346
self->struggleFlags = struggleFlags;
347
if (++self->struggleTimer >= 4) {
348
player->state = Player_State_Air;
349
self->grabbedPlayer = 0;
350
self->grabDelay = 16;
351
self->state = Grabber_State_PlayerEscaped;
352
}
353
}
354
}
355
}
356
else {
357
self->struggleTimer = 0;
358
self->struggleFlags = 0;
359
}
360
}
361
else {
362
if (player->left) {
363
self->struggleFlags = 1;
364
self->struggleDelay = 64;
365
}
366
else if (player->right) {
367
self->struggleFlags = 2;
368
self->struggleDelay = 64;
369
}
370
}
371
372
player->position.x = self->position.x;
373
player->position.y = self->position.y + 0x100000;
374
player->direction = self->direction ^ FLIP_X;
375
}
376
377
Grabber_CheckPlayerCollisions();
378
Grabber_HandleExplode();
379
Grabber_CheckOffScreen();
380
}
381
382
void Grabber_State_PlayerEscaped(void)
383
{
384
RSDK_THIS(Grabber);
385
386
if (self->grabDelay)
387
self->grabDelay--;
388
389
Grabber_CheckPlayerCollisions();
390
Grabber_HandleExplode();
391
Grabber_CheckOffScreen();
392
}
393
394
#if GAME_INCLUDE_EDITOR
395
void Grabber_EditorDraw(void)
396
{
397
RSDK_THIS(Grabber);
398
399
self->startPos = self->position;
400
401
RSDK.DrawLine(self->position.x, self->startPos.y - 0x100000, self->position.x, self->position.y, 0x202020, 0, INK_NONE, false);
402
RSDK.DrawLine(self->position.x - 0x10000, self->startPos.y - 0x100000, self->position.x - 0x10000, self->position.y, 0xE0E0E0, 0, INK_NONE,
403
false);
404
405
Vector2 drawPos;
406
int32 dir = self->direction;
407
drawPos.x = self->position.x;
408
drawPos.y = self->startPos.y;
409
self->direction = FLIP_NONE;
410
RSDK.DrawSprite(&self->wheelAnimator, &drawPos, false);
411
412
self->direction = dir;
413
RSDK.DrawSprite(&self->bodyAnimator, NULL, false);
414
RSDK.DrawSprite(&self->clampAnimator, NULL, false);
415
}
416
417
void Grabber_EditorLoad(void)
418
{
419
Grabber->aniFrames = RSDK.LoadSpriteAnimation("CPZ/Grabber.bin", SCOPE_STAGE);
420
421
RSDK_ACTIVE_VAR(Grabber, direction);
422
RSDK_ENUM_VAR("Left", FLIP_NONE);
423
RSDK_ENUM_VAR("Right", FLIP_X);
424
}
425
#endif
426
427
void Grabber_Serialize(void) { RSDK_EDITABLE_VAR(Grabber, VAR_UINT8, direction); }
428
429