Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/HCZ/MegaChopper.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: MegaChopper Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectMegaChopper *MegaChopper;
11
12
void MegaChopper_Update(void)
13
{
14
RSDK_THIS(MegaChopper);
15
16
StateMachine_Run(self->state);
17
}
18
19
void MegaChopper_LateUpdate(void) {}
20
21
void MegaChopper_StaticUpdate(void) {}
22
23
void MegaChopper_Draw(void)
24
{
25
RSDK_THIS(MegaChopper);
26
27
RSDK.DrawSprite(&self->animator, NULL, false);
28
}
29
30
void MegaChopper_Create(void *data)
31
{
32
RSDK_THIS(MegaChopper);
33
34
self->visible = true;
35
self->drawFX |= FX_FLIP;
36
self->drawGroup = Zone->playerDrawGroup[0] + 1;
37
self->startPos = self->position;
38
self->active = ACTIVE_BOUNDS;
39
self->updateRange.x = 0x800000;
40
self->updateRange.y = 0x800000;
41
42
RSDK.SetSpriteAnimation(MegaChopper->aniFrames, 0, &self->animator, true, 0);
43
self->state = MegaChopper_State_Init;
44
}
45
46
void MegaChopper_StageLoad(void)
47
{
48
if (RSDK.CheckSceneFolder("HCZ"))
49
MegaChopper->aniFrames = RSDK.LoadSpriteAnimation("HCZ/MegaChopper.bin", SCOPE_STAGE);
50
51
MegaChopper->hitboxBadnik.left = -8;
52
MegaChopper->hitboxBadnik.top = -12;
53
MegaChopper->hitboxBadnik.right = 2;
54
MegaChopper->hitboxBadnik.bottom = 12;
55
56
MegaChopper->hitboxChop.left = -1;
57
MegaChopper->hitboxChop.top = -1;
58
MegaChopper->hitboxChop.right = 1;
59
MegaChopper->hitboxChop.bottom = 1;
60
61
DEBUGMODE_ADD_OBJ(MegaChopper);
62
}
63
64
void MegaChopper_DebugSpawn(void)
65
{
66
RSDK_THIS(DebugMode);
67
68
CREATE_ENTITY(MegaChopper, NULL, self->position.x, self->position.y);
69
}
70
71
void MegaChopper_DebugDraw(void)
72
{
73
RSDK.SetSpriteAnimation(MegaChopper->aniFrames, 0, &DebugMode->animator, true, 0);
74
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
75
}
76
77
void MegaChopper_CheckPlayerCollisions(void)
78
{
79
RSDK_THIS(MegaChopper);
80
81
foreach_active(Player, player)
82
{
83
if (Player_CheckBadnikTouch(player, self, &MegaChopper->hitboxBadnik)) {
84
int32 blink = player->blinkTimer;
85
86
player->blinkTimer = 1;
87
if (!Player_CheckBadnikBreak(player, self, true) && Player_CheckCollisionTouch(player, self, &MegaChopper->hitboxChop)) {
88
self->grabbedPlayer = player;
89
self->playerPos.x = (self->position.x - player->position.x) & 0xFFFF0000;
90
self->playerPos.y = (self->position.y - player->position.y) & 0xFFFF0000;
91
self->playerDir = player->direction;
92
93
if (player->stateInput == Player_Input_P1)
94
player->stateInput = MegaChopper_Input_GrabbedP1;
95
else if (player->stateInput == Player_Input_P2_Player)
96
player->stateInput = MegaChopper_Input_GrabbedP2;
97
else if (player->stateInput == Player_Input_P2_AI)
98
player->stateInput = MegaChopper_Input_GrabbedP2_AI;
99
100
self->drawGroup = player->drawGroup + 1;
101
self->isPermanent = true;
102
self->state = MegaChopper_State_Chopping;
103
}
104
player->blinkTimer = blink;
105
foreach_break;
106
}
107
}
108
}
109
110
void MegaChopper_CheckOffScreen(void)
111
{
112
RSDK_THIS(MegaChopper);
113
114
if (!RSDK.CheckOnScreen(self, NULL) && RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange) == false) {
115
self->position = self->startPos;
116
117
EntityPlayer *player = self->grabbedPlayer;
118
if (player) {
119
if (player->stateInput == MegaChopper_Input_GrabbedP1)
120
player->stateInput = Player_Input_P1;
121
else if (player->stateInput == MegaChopper_Input_GrabbedP2)
122
player->stateInput = Player_Input_P2_Player;
123
else if (player->stateInput == MegaChopper_Input_GrabbedP2_AI)
124
player->stateInput = Player_Input_P2_AI;
125
}
126
127
self->nibbleTimer = 0;
128
self->isPermanent = false;
129
130
MegaChopper_Create(NULL);
131
}
132
}
133
134
void MegaChopper_Input_GrabbedP1(void)
135
{
136
RSDK_THIS(Player);
137
138
Player_Input_P1();
139
140
if (self->state != Player_State_Static) {
141
self->up = false;
142
self->down = false;
143
self->jumpPress = false;
144
self->jumpHold = false;
145
}
146
}
147
148
void MegaChopper_Input_GrabbedP2(void)
149
{
150
RSDK_THIS(Player);
151
152
Player_Input_P2_Player();
153
154
self->up = false;
155
self->down = false;
156
self->jumpPress = false;
157
self->jumpHold = false;
158
159
if (self->stateInput == Player_Input_P2_AI)
160
self->stateInput = MegaChopper_Input_GrabbedP2_AI;
161
}
162
163
void MegaChopper_Input_GrabbedP2_AI(void)
164
{
165
RSDK_THIS(Player);
166
167
Player_Input_P2_AI();
168
169
self->up = false;
170
self->down = false;
171
self->jumpPress = false;
172
self->jumpHold = false;
173
174
if (self->stateInput == Player_Input_P2_Player)
175
self->stateInput = MegaChopper_Input_GrabbedP2;
176
}
177
178
void MegaChopper_State_Init(void)
179
{
180
RSDK_THIS(MegaChopper);
181
182
if (self->position.y >= Water->waterLevel) {
183
self->active = ACTIVE_NORMAL;
184
self->velocity.x = -0x10000;
185
186
self->state = MegaChopper_State_InWater;
187
MegaChopper_State_InWater();
188
}
189
else {
190
destroyEntity(self);
191
}
192
}
193
194
void MegaChopper_State_InWater(void)
195
{
196
RSDK_THIS(MegaChopper);
197
198
if (++self->animator.frameID == 6)
199
self->animator.frameID = 0;
200
201
if (self->animator.frameID == 12)
202
self->animator.frameID = 6;
203
204
EntityPlayer *player = Player_GetNearestPlayer();
205
if (self->position.x >= player->position.x) {
206
self->velocity.x -= 0x800;
207
208
if (self->velocity.x < -0x20000)
209
self->velocity.x = -0x20000;
210
}
211
else {
212
self->velocity.x += 0x800;
213
214
if (self->velocity.x > 0x20000)
215
self->velocity.x = 0x20000;
216
}
217
218
self->position.y += self->position.y < player->position.y ? 0x2000 : -0x2000;
219
220
self->direction = self->velocity.x > 0;
221
if (self->velocity.y > 0) {
222
self->velocity.y -= 0x3800;
223
224
if (self->velocity.y < 0)
225
self->velocity.y = 0;
226
}
227
228
self->position.x += self->velocity.x;
229
self->position.y += self->velocity.y;
230
231
if (self->position.y < Water->waterLevel) {
232
bool32 inWater = false;
233
foreach_active(Water, water)
234
{
235
if (water->type == WATER_POOL && RSDK.CheckObjectCollisionTouchBox(water, &water->hitbox, self, &Water->hitboxPoint)) {
236
inWater = true;
237
}
238
}
239
240
if (!inWater) {
241
self->velocity.x = self->direction == FLIP_NONE ? -0x20000 : 0x20000;
242
self->velocity.y = -0x40000;
243
self->state = MegaChopper_State_OutOfWater;
244
}
245
}
246
247
MegaChopper_CheckPlayerCollisions();
248
MegaChopper_CheckOffScreen();
249
}
250
251
void MegaChopper_State_OutOfWater(void)
252
{
253
RSDK_THIS(MegaChopper);
254
255
if (++self->animator.frameID == 6)
256
self->animator.frameID = 0;
257
258
if (self->animator.frameID == 12)
259
self->animator.frameID = 6;
260
261
self->position.x += self->velocity.x;
262
self->position.y += self->velocity.y;
263
self->velocity.y += 0x1800;
264
265
if (self->position.y >= Water->waterLevel) {
266
self->state = MegaChopper_State_InWater;
267
}
268
else {
269
foreach_active(Water, water)
270
{
271
if (water->type == WATER_POOL && RSDK.CheckObjectCollisionTouchBox(water, &water->hitbox, self, &Water->hitboxPoint)) {
272
self->state = MegaChopper_State_InWater;
273
}
274
}
275
}
276
277
MegaChopper_CheckPlayerCollisions();
278
MegaChopper_CheckOffScreen();
279
}
280
281
void MegaChopper_State_Chopping(void)
282
{
283
RSDK_THIS(MegaChopper);
284
285
if (++self->animator.timer == 3) {
286
self->animator.timer = 0;
287
self->animator.frameID = (self->animator.frameID + 6) % 12;
288
}
289
290
EntityPlayer *player = self->grabbedPlayer;
291
292
if (!player) {
293
self->velocity.x = self->direction == FLIP_NONE ? 0x20000 : -0x20000;
294
self->velocity.y = -0x40000;
295
self->state = MegaChopper_State_ShakenOff;
296
}
297
else {
298
if (player->animator.animationID == ANI_JUMP) {
299
self->grabbedPlayer = NULL;
300
301
if (player->stateInput == MegaChopper_Input_GrabbedP1)
302
player->stateInput = Player_Input_P1;
303
else if (player->stateInput == MegaChopper_Input_GrabbedP2)
304
player->stateInput = Player_Input_P2_Player;
305
else if (player->stateInput == MegaChopper_Input_GrabbedP2_AI)
306
player->stateInput = Player_Input_P2_AI;
307
308
self->velocity.x = self->direction == FLIP_NONE ? 0x20000 : -0x20000;
309
self->velocity.y = -0x40000;
310
self->state = MegaChopper_State_ShakenOff;
311
}
312
else {
313
if (++self->nibbleTimer >= 60) {
314
self->nibbleTimer = 0;
315
316
if (!player->rings || player->sidekick) {
317
Player_Hit(player);
318
player->velocity.x = player->position.x > self->position.x ? 0x20000 : -0x20000;
319
self->grabbedPlayer = NULL;
320
}
321
else {
322
player->rings--;
323
if (Ring->pan) {
324
int32 channel = RSDK.PlaySfx(Ring->sfxRing, false, 255);
325
RSDK.SetChannelAttributes(channel, 1.0, -1.0, 1.0);
326
Ring->pan = 0;
327
}
328
else {
329
int32 channel = RSDK.PlaySfx(Ring->sfxRing, false, 255);
330
RSDK.SetChannelAttributes(channel, 1.0, 1.0, 1.0);
331
Ring->pan = 1;
332
}
333
}
334
}
335
336
if (self->grabbedPlayer) {
337
self->position.x = player->position.x + self->playerPos.x;
338
self->position.y = player->position.y + self->playerPos.y;
339
340
if (player->direction != self->playerDir) {
341
self->direction ^= FLIP_X;
342
self->playerDir = player->direction;
343
}
344
345
self->playerDir = player->direction;
346
if (self->lastShakeFlags) {
347
if (!self->shakeTimer) {
348
self->shakeCount = 0;
349
self->lastShakeFlags = 0;
350
}
351
else {
352
self->shakeTimer--;
353
354
uint8 shakeFlags = 0;
355
if (player->left)
356
shakeFlags = 1;
357
if (player->right)
358
shakeFlags |= 2;
359
360
if (shakeFlags) {
361
if (shakeFlags != 3 && shakeFlags != self->lastShakeFlags) {
362
self->lastShakeFlags = shakeFlags;
363
if (++self->shakeCount >= 6) {
364
self->grabbedPlayer = NULL;
365
366
if (player->stateInput == MegaChopper_Input_GrabbedP1)
367
player->stateInput = Player_Input_P1;
368
else if (player->stateInput == MegaChopper_Input_GrabbedP2)
369
player->stateInput = Player_Input_P2_Player;
370
else if (player->stateInput == MegaChopper_Input_GrabbedP2_AI)
371
player->stateInput = Player_Input_P2_AI;
372
373
self->velocity.x = self->direction == FLIP_NONE ? 0x20000 : -0x20000;
374
self->velocity.y = -0x40000;
375
self->state = MegaChopper_State_ShakenOff;
376
}
377
}
378
}
379
}
380
}
381
else if (player->left) {
382
self->lastShakeFlags = 1;
383
self->shakeTimer = 64;
384
}
385
else if (player->right) {
386
self->lastShakeFlags = 2;
387
self->shakeTimer = 64;
388
}
389
}
390
else {
391
if (player->stateInput == MegaChopper_Input_GrabbedP1)
392
player->stateInput = Player_Input_P1;
393
else if (player->stateInput == MegaChopper_Input_GrabbedP2)
394
player->stateInput = Player_Input_P2_Player;
395
else if (player->stateInput == MegaChopper_Input_GrabbedP2_AI)
396
player->stateInput = Player_Input_P2_AI;
397
398
self->velocity.x = self->direction == FLIP_NONE ? 0x20000 : -0x20000;
399
self->velocity.y = -0x40000;
400
self->state = MegaChopper_State_ShakenOff;
401
}
402
}
403
}
404
405
MegaChopper_CheckOffScreen();
406
}
407
408
void MegaChopper_State_ShakenOff(void)
409
{
410
RSDK_THIS(MegaChopper);
411
412
self->position.x += self->velocity.x;
413
self->position.y += self->velocity.y;
414
self->velocity.y += 0x3800;
415
416
MegaChopper_CheckOffScreen();
417
}
418
419
#if GAME_INCLUDE_EDITOR
420
void MegaChopper_EditorDraw(void) { MegaChopper_Draw(); }
421
422
void MegaChopper_EditorLoad(void) { MegaChopper->aniFrames = RSDK.LoadSpriteAnimation("HCZ/MegaChopper.bin", SCOPE_STAGE); }
423
#endif
424
425
void MegaChopper_Serialize(void) {}
426
427