Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/LRZ/BuckwildBall.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: BuckwildBall Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectBuckwildBall *BuckwildBall;
11
12
void BuckwildBall_Update(void)
13
{
14
RSDK_THIS(BuckwildBall);
15
16
StateMachine_Run(self->state);
17
18
if (self->state != BuckwildBall_State_AwaitDetection && self->state != BuckwildBall_State_Debris && self->state != BuckwildBall_State_Init) {
19
if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x1C0000, 2)) {
20
if (self->particleDelay-- <= 0) {
21
BuckwildBall_SpawnDebris();
22
BuckwildBall_HandleTimerSfx();
23
self->particleDelay = RSDK.Rand(8, 15);
24
}
25
}
26
27
BuckwildBall_CheckPlayerCollisions();
28
BuckwildBall_HandleRollCrush();
29
30
RSDK.ProcessAnimation(&self->animator);
31
32
if (self->timerSfx > 0)
33
self->timerSfx--;
34
}
35
}
36
37
void BuckwildBall_LateUpdate(void) {}
38
39
void BuckwildBall_StaticUpdate(void) {}
40
41
void BuckwildBall_Draw(void)
42
{
43
RSDK_THIS(BuckwildBall);
44
45
RSDK.DrawSprite(&self->animator, NULL, false);
46
}
47
48
void BuckwildBall_Create(void *data)
49
{
50
RSDK_THIS(BuckwildBall);
51
52
if (!SceneInfo->inEditor) {
53
self->active = ACTIVE_BOUNDS;
54
self->drawGroup = Zone->objectDrawGroup[0];
55
self->startPos = self->position;
56
self->visible = true;
57
self->drawFX = FX_FLIP;
58
self->updateRange.x = 0x800000;
59
self->updateRange.y = 0x800000;
60
self->bossBallSlot = -1;
61
62
if (self->mode != BUCKWILDBALL_ROLLING)
63
self->updateRange.x = (self->amplitude + 0x80) << 16;
64
65
self->state = BuckwildBall_State_Init;
66
67
if (!self->speed)
68
self->speed = 2;
69
}
70
}
71
72
void BuckwildBall_StageLoad(void)
73
{
74
BuckwildBall->aniFrames = RSDK.LoadSpriteAnimation("LRZ1/BuckwildBall.bin", SCOPE_STAGE);
75
BuckwildBall->particleFrames = RSDK.LoadSpriteAnimation("LRZ1/Particles.bin", SCOPE_STAGE);
76
77
BuckwildBall->sfxImpact = RSDK.GetSfx("Stage/Impact2.wav");
78
BuckwildBall->sfxSharp = RSDK.GetSfx("Stage/Sharp.wav");
79
80
BuckwildBall->hitbox.left = -28;
81
BuckwildBall->hitbox.top = -28;
82
BuckwildBall->hitbox.right = 28;
83
BuckwildBall->hitbox.bottom = 28;
84
}
85
86
void BuckwildBall_HandleTimerSfx(void)
87
{
88
RSDK_THIS(BuckwildBall);
89
90
if (self->timerSfx > 0) {
91
LogHelpers_Print("timerSfx = %d", self->timerSfx);
92
}
93
else {
94
RSDK.PlaySfx(BuckwildBall->sfxImpact, false, 255);
95
self->timerSfx = 8;
96
}
97
}
98
99
void BuckwildBall_CheckOffScreen(void)
100
{
101
RSDK_THIS(BuckwildBall);
102
103
if (!RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange) && !RSDK.CheckPosOnScreen(&self->position, &self->updateRange)) {
104
if (self->respawn) {
105
self->position = self->startPos;
106
self->state = BuckwildBall_State_Init;
107
self->active = ACTIVE_BOUNDS;
108
self->visible = false;
109
}
110
else {
111
destroyEntity(self);
112
}
113
}
114
}
115
116
void BuckwildBall_SpawnDebris(void)
117
{
118
RSDK_THIS(BuckwildBall);
119
120
int32 x = self->position.x;
121
int32 y = self->position.y + 0x1C0000;
122
123
int32 sizeX = (BuckwildBall->hitbox.right - BuckwildBall->hitbox.left) >> 1;
124
int32 spawnX = x + ((RSDK.Rand(0, 64) - 32) << 16);
125
int32 spawnY = y + ((RSDK.Rand(0, 8) - 4) << 15);
126
127
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, spawnX, spawnY);
128
RSDK.SetSpriteAnimation(BuckwildBall->particleFrames, 1, &debris->animator, true, 0);
129
debris->drawGroup = Zone->objectDrawGroup[1];
130
debris->gravityStrength = 0x3800;
131
debris->velocity.x = 0x180 * (abs(spawnX - x) >> 8) / sizeX;
132
133
if (debris->position.x < self->position.x) {
134
debris->direction = FLIP_X;
135
debris->velocity.x = -debris->velocity.x;
136
}
137
138
debris->velocity.y = -0x1000 * RSDK.Rand(32, 54);
139
}
140
141
void BuckwildBall_CheckPlayerCollisions(void)
142
{
143
RSDK_THIS(BuckwildBall);
144
145
foreach_active(Player, player)
146
{
147
if (Player_CheckCollisionTouch(player, self, &BuckwildBall->hitbox)) {
148
#if MANIA_USE_PLUS
149
if (!Player_CheckMightyUnspin(player, 0x600, false, &player->uncurlTimer))
150
#endif
151
Player_Hurt(player, self);
152
}
153
}
154
}
155
156
void BuckwildBall_HandleRollCrush(void)
157
{
158
RSDK_THIS(BuckwildBall);
159
160
Hitbox crushHitbox;
161
crushHitbox.left = -8;
162
crushHitbox.top = -8;
163
crushHitbox.right = 8;
164
crushHitbox.bottom = 8;
165
166
foreach_active(Iwamodoki, iwamodoki)
167
{
168
if (RSDK.CheckObjectCollisionTouchBox(self, &BuckwildBall->hitbox, iwamodoki, &crushHitbox)) {
169
BadnikHelpers_BadnikBreak(iwamodoki, true, true);
170
}
171
}
172
173
foreach_active(Fireworm, fireworm)
174
{
175
if (RSDK.CheckObjectCollisionTouchBox(self, &BuckwildBall->hitbox, fireworm, &crushHitbox)) {
176
BadnikHelpers_BadnikBreak(fireworm, true, true);
177
}
178
}
179
180
foreach_active(Toxomister, toxomister)
181
{
182
if (RSDK.CheckObjectCollisionTouchBox(self, &BuckwildBall->hitbox, toxomister, &crushHitbox)) {
183
if (toxomister->state == Toxomister_State_CreateClouds) {
184
BadnikHelpers_BadnikBreak(toxomister, true, true);
185
}
186
else if (!toxomister->grabbedPlayer) {
187
destroyEntity(toxomister);
188
}
189
}
190
}
191
192
Hitbox spikeHitbox;
193
spikeHitbox.left = -16;
194
spikeHitbox.top = -16;
195
spikeHitbox.right = 16;
196
spikeHitbox.bottom = 16;
197
198
foreach_active(Spikes, spikes)
199
{
200
if (RSDK.CheckObjectCollisionTouchBox(self, &BuckwildBall->hitbox, spikes, &spikeHitbox)) {
201
for (int32 i = 0; i < 2; ++i) {
202
int32 x = spikes->position.x + (((2 * (i != 0) - 1) * (spikes->type == SPIKES_UP)) << 19);
203
int32 y = spikes->position.y + (((2 * (i != 0) - 1) * (spikes->type != SPIKES_UP)) << 19);
204
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, x, y);
205
206
RSDK.SetSpriteAnimation(BuckwildBall->particleFrames, 4, &debris->animator, true, spikes->type >> 1);
207
debris->drawGroup = Zone->objectDrawGroup[1];
208
debris->direction = spikes->direction;
209
debris->drawFX |= FX_ROTATE;
210
debris->gravityStrength = 0x3800;
211
debris->rotSpeed = RSDK.Rand(-32, 32);
212
debris->velocity.x = RSDK.Rand(-0x28000, 0x28000);
213
debris->velocity.y = -0x1000 * RSDK.Rand(32, 96);
214
}
215
216
destroyEntity(spikes);
217
218
RSDK.PlaySfx(BuckwildBall->sfxSharp, false, 255);
219
RSDK.PlaySfx(BuckwildBall->sfxImpact, false, 255);
220
self->timerSfx = 8;
221
}
222
}
223
}
224
225
void BuckwildBall_State_Init(void)
226
{
227
RSDK_THIS(BuckwildBall);
228
229
RSDK.SetSpriteAnimation(BuckwildBall->aniFrames, 0, &self->animator, true, 0);
230
231
self->timerSfx = 0;
232
233
switch (self->mode) {
234
case BUCKWILDBALL_PATROLLING: self->state = BuckwildBall_State_Patrolling; break;
235
236
case BUCKWILDBALL_ROLLING:
237
self->visible = false;
238
self->state = BuckwildBall_State_AwaitDetection;
239
self->drawGroup = Zone->objectDrawGroup[0];
240
241
self->detectHitbox.left = -(self->detectSize.x >> 17);
242
self->detectHitbox.top = -(self->detectSize.y >> 17);
243
self->detectHitbox.right = self->detectSize.x >> 17;
244
self->detectHitbox.bottom = self->detectSize.y >> 17;
245
246
self->velocity.x = 0;
247
self->ballPos.x = self->startPos.x + self->detectOffset.x;
248
self->ballPos.y = self->startPos.y + self->detectOffset.y;
249
self->velocity.y = 0;
250
break;
251
}
252
}
253
254
void BuckwildBall_State_Patrolling(void)
255
{
256
RSDK_THIS(BuckwildBall);
257
258
int32 angle = ((self->speed & 0xFFFF) * (Zone->timer & 0xFFFF)) & 0x3FF;
259
self->position.x = (self->amplitude << 6) * RSDK.Sin1024(angle) + self->startPos.x;
260
self->direction = (angle - 0x100) > 0x200;
261
}
262
263
void BuckwildBall_State_AwaitDetection(void)
264
{
265
RSDK_THIS(BuckwildBall);
266
267
self->position.x = self->ballPos.x;
268
self->position.y = self->ballPos.y;
269
270
foreach_active(Player, player)
271
{
272
if (!player->sidekick) {
273
if (Player_CheckCollisionTouch(player, self, &self->detectHitbox)) {
274
self->visible = true;
275
self->active = ACTIVE_NORMAL;
276
self->state = BuckwildBall_State_Falling;
277
}
278
}
279
}
280
281
self->position.x = self->startPos.x;
282
self->position.y = self->startPos.y;
283
}
284
285
void BuckwildBall_State_Falling(void)
286
{
287
RSDK_THIS(BuckwildBall);
288
289
self->velocity.y += 0x3800;
290
self->position.x += self->velocity.x;
291
self->position.y += self->velocity.y;
292
293
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x1C0000, true)) {
294
if (!RSDK.GetEntityCount(Drillerdroid->classID, true) || (Drillerdroid->platformActive[self->bossBallSlot] == true)) {
295
self->velocity.y = 0;
296
self->state = BuckwildBall_State_Rolling;
297
self->velocity.x = abs(self->speed << 15) * (2 * (self->direction != FLIP_NONE) - 1);
298
}
299
else if (Drillerdroid->platformActive[self->bossBallSlot]) {
300
self->state = BuckwildBall_State_Debris;
301
self->velocity.x = 0;
302
self->velocity.y = -0x40000;
303
self->animator.speed = 0;
304
self->drawGroup = Zone->objectDrawGroup[1];
305
}
306
else {
307
int32 slot = RSDK.GetEntitySlot(Drillerdroid->boss);
308
RSDK_GET_ENTITY(slot + 6 + self->bossBallSlot, CollapsingPlatform)->collapseDelay = 1;
309
Drillerdroid->platformActive[self->bossBallSlot] = -1;
310
self->velocity.y = -0x40000;
311
}
312
313
BuckwildBall_HandleTimerSfx();
314
Camera_ShakeScreen(0, 0, 5);
315
}
316
317
if (self->bossBallSlot > -1)
318
BuckwildBall_CheckOffScreen();
319
}
320
321
void BuckwildBall_State_Rolling(void)
322
{
323
RSDK_THIS(BuckwildBall);
324
325
self->velocity.x += abs(self->speed << 10) * (2 * !(self->direction == FLIP_NONE) - 1);
326
self->velocity.y += 0x3800;
327
328
self->position.x += self->velocity.x;
329
self->position.y += self->velocity.y;
330
331
if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x1C0000, 2)) {
332
self->velocity.y = 0;
333
if (abs(self->velocity.x) > 0x20000 && RSDK.Rand(0, 100) > 0x50) {
334
BuckwildBall_SpawnDebris();
335
BuckwildBall_HandleTimerSfx();
336
self->velocity.y = -0x18000;
337
}
338
}
339
340
bool32 collidedWall = false;
341
if (self->direction == FLIP_X)
342
collidedWall = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, 0x1C0000, 0, true);
343
else if (self->direction == FLIP_NONE)
344
collidedWall = RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -0x1C0000, 0, true);
345
346
if (collidedWall) {
347
self->state = BuckwildBall_State_Debris;
348
BuckwildBall_HandleTimerSfx();
349
Camera_ShakeScreen(0, 0, 5);
350
self->velocity.x = 0;
351
self->velocity.y = -0x40000;
352
self->animator.speed = 0;
353
self->drawGroup = Zone->objectDrawGroup[1];
354
}
355
356
if (self->bossBallSlot > -1)
357
BuckwildBall_CheckOffScreen();
358
}
359
360
void BuckwildBall_State_Debris(void)
361
{
362
RSDK_THIS(BuckwildBall);
363
364
self->velocity.y += 0x3800;
365
self->position.x += self->velocity.x;
366
self->position.y += self->velocity.y;
367
368
self->visible = (Zone->timer % 4) < 2;
369
370
BuckwildBall_CheckOffScreen();
371
}
372
373
#if GAME_INCLUDE_EDITOR
374
void BuckwildBall_EditorDraw(void)
375
{
376
RSDK_THIS(BuckwildBall);
377
378
RSDK.SetSpriteAnimation(BuckwildBall->aniFrames, 0, &self->animator, true, 0);
379
380
self->updateRange.x = 0x800000;
381
self->updateRange.y = 0x800000;
382
self->startPos = self->position;
383
384
BuckwildBall_Draw();
385
386
if (showGizmos()) {
387
388
RSDK_DRAWING_OVERLAY(true);
389
switch (self->mode) {
390
case BUCKWILDBALL_PATROLLING:
391
self->updateRange.x = (self->amplitude + 0x80) << 16;
392
393
self->inkEffect = INK_BLEND;
394
395
self->position.x = (self->amplitude << 6) * RSDK.Sin1024(0x100) + self->startPos.x;
396
int32 x1 = self->position.x;
397
BuckwildBall_Draw();
398
399
self->position.x = (self->amplitude << 6) * RSDK.Sin1024(0x300) + self->startPos.x;
400
BuckwildBall_Draw();
401
402
RSDK.DrawLine(x1, self->position.y, self->position.x, self->position.y, 0x00FF00, 0xFF, INK_BLEND, false);
403
404
self->inkEffect = INK_NONE;
405
break;
406
407
case BUCKWILDBALL_ROLLING:
408
self->detectHitbox.left = -(self->detectSize.x >> 17);
409
self->detectHitbox.top = -(self->detectSize.y >> 17);
410
self->detectHitbox.right = self->detectSize.x >> 17;
411
self->detectHitbox.bottom = self->detectSize.y >> 17;
412
self->ballPos = self->position;
413
self->ballPos.x = self->position.x + self->detectOffset.x;
414
self->ballPos.y = self->position.y + self->detectOffset.y;
415
416
DrawHelpers_DrawHitboxOutline(self->ballPos.x, self->ballPos.y, &self->detectHitbox, FLIP_NONE, 0xFF0000);
417
418
self->velocity.x = abs(self->speed << 15) * (2 * (self->direction != FLIP_NONE) - 1);
419
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x + (self->velocity.x << 3), self->position.y, 0xFFFF00,
420
INK_NONE, 0xFF);
421
break;
422
}
423
424
RSDK_DRAWING_OVERLAY(false);
425
}
426
}
427
428
void BuckwildBall_EditorLoad(void)
429
{
430
BuckwildBall->aniFrames = RSDK.LoadSpriteAnimation("LRZ1/BuckwildBall.bin", SCOPE_STAGE);
431
432
RSDK_ACTIVE_VAR(BuckwildBall, direction);
433
RSDK_ENUM_VAR("Left", FLIP_NONE);
434
RSDK_ENUM_VAR("Right", FLIP_X);
435
436
RSDK_ACTIVE_VAR(BuckwildBall, mode);
437
RSDK_ENUM_VAR("Patrolling", BUCKWILDBALL_PATROLLING);
438
RSDK_ENUM_VAR("Rolling", BUCKWILDBALL_ROLLING);
439
}
440
#endif
441
442
void BuckwildBall_Serialize(void)
443
{
444
RSDK_EDITABLE_VAR(BuckwildBall, VAR_UINT32, amplitude);
445
RSDK_EDITABLE_VAR(BuckwildBall, VAR_UINT8, direction);
446
RSDK_EDITABLE_VAR(BuckwildBall, VAR_UINT8, mode);
447
RSDK_EDITABLE_VAR(BuckwildBall, VAR_ENUM, speed);
448
RSDK_EDITABLE_VAR(BuckwildBall, VAR_VECTOR2, detectOffset);
449
RSDK_EDITABLE_VAR(BuckwildBall, VAR_VECTOR2, detectSize);
450
RSDK_EDITABLE_VAR(BuckwildBall, VAR_BOOL, respawn);
451
}
452
453