Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/CPZ/Ball.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Ball Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectBall *Ball = NULL;
11
12
void Ball_Update(void)
13
{
14
RSDK_THIS(Ball);
15
16
StateMachine_Run(self->state);
17
}
18
19
void Ball_LateUpdate(void) {}
20
21
void Ball_StaticUpdate(void) {}
22
23
void Ball_Draw(void)
24
{
25
RSDK_THIS(Ball);
26
27
RSDK.DrawSprite(&self->animator, NULL, false);
28
}
29
30
void Ball_Create(void *data)
31
{
32
RSDK_THIS(Ball);
33
34
self->visible = true;
35
self->drawGroup = Zone->objectDrawGroup[0];
36
self->active = ACTIVE_BOUNDS;
37
self->updateRange.x = 0x800000;
38
self->updateRange.y = 0x800000;
39
40
if (data) {
41
RSDK.SetSpriteAnimation(Ball->aniFrames, 2, &self->animator, true, 0);
42
self->state = Ball_State_Splash;
43
}
44
else {
45
self->startPos = self->position;
46
if (!self->type) {
47
RSDK.SetSpriteAnimation(Ball->aniFrames, 0, &self->animator, true, 0);
48
self->state = Ball_State_Init;
49
}
50
else {
51
self->state = Ball_State_Spawner;
52
}
53
}
54
}
55
56
void Ball_StageLoad(void)
57
{
58
if (RSDK.CheckSceneFolder("CPZ"))
59
Ball->aniFrames = RSDK.LoadSpriteAnimation("CPZ/Ball.bin", SCOPE_STAGE);
60
61
Ball->hitboxBall.left = -10;
62
Ball->hitboxBall.top = -10;
63
Ball->hitboxBall.right = 10;
64
Ball->hitboxBall.bottom = 10;
65
66
Ball->hitboxRange.left = -128;
67
Ball->hitboxRange.top = -128;
68
Ball->hitboxRange.right = 128;
69
Ball->hitboxRange.bottom = 128;
70
71
Ball->sfxSplash = RSDK.GetSfx("Stage/Splash2.wav");
72
73
DEBUGMODE_ADD_OBJ(Ball);
74
}
75
76
void Ball_DebugSpawn(void)
77
{
78
RSDK_THIS(Ball);
79
80
CREATE_ENTITY(Ball, NULL, self->position.x, self->position.y);
81
}
82
83
void Ball_DebugDraw(void)
84
{
85
RSDK.SetSpriteAnimation(Ball->aniFrames, 0, &DebugMode->animator, true, 0);
86
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
87
}
88
89
void Ball_HandleInteractions(void)
90
{
91
RSDK_THIS(Ball);
92
93
foreach_active(Player, player)
94
{
95
if (Player_CheckCollisionTouch(player, self, &Ball->hitboxBall)) {
96
Player_Hurt(player, self);
97
98
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_ENEMY), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
99
RSDK.PlaySfx(Explosion->sfxDestroy, false, 255);
100
101
self->velocity.y = 0;
102
RSDK.SetSpriteAnimation(Ball->aniFrames, 1, &self->animator, true, 0);
103
self->state = Ball_State_ChemicalDrop;
104
}
105
}
106
}
107
108
void Ball_CheckOffScreen(void)
109
{
110
RSDK_THIS(Ball);
111
112
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
113
self->position = self->startPos;
114
self->direction = FLIP_NONE;
115
self->velocity.x = 0;
116
self->velocity.y = 0;
117
Ball_Create(NULL);
118
}
119
}
120
121
void Ball_SpawnSplashes(void)
122
{
123
RSDK_THIS(Ball);
124
125
RSDK.PlaySfx(Ball->sfxSplash, false, 255);
126
127
for (int32 i = 0; i < 5; ++i) {
128
EntityBall *ball = CREATE_ENTITY(Ball, INT_TO_VOID(true), self->position.x, self->position.y);
129
ball->drawGroup = Zone->objectDrawGroup[1];
130
ball->velocity.x = RSDK.Rand(-0x100, 0x100) << 10;
131
if (ball->velocity.x < 0)
132
ball->velocity.x += 0x20000;
133
134
ball->velocity.x -= 0x10000;
135
if (i > 0)
136
ball->velocity.y = RSDK.Rand(-0x400, 0x400) << 8;
137
ball->velocity.y -= self->velocity.y >> 1;
138
}
139
140
destroyEntity(self);
141
}
142
143
void Ball_State_Init(void)
144
{
145
RSDK_THIS(Ball);
146
147
self->active = ACTIVE_NORMAL;
148
149
self->state = Ball_State_AwaitPlayer;
150
Ball_State_AwaitPlayer();
151
}
152
153
void Ball_State_AwaitPlayer(void)
154
{
155
RSDK_THIS(Ball);
156
157
if (self->direction == FLIP_X) {
158
self->velocity.y += 0x800;
159
if (self->velocity.y >= 0xC000)
160
self->direction = FLIP_NONE;
161
}
162
else {
163
self->velocity.y -= 0x800;
164
if (self->velocity.y <= -0xC000)
165
self->direction = FLIP_X;
166
}
167
self->position.y += self->velocity.y;
168
169
RSDK.ProcessAnimation(&self->animator);
170
171
foreach_active(Player, player)
172
{
173
if (Player_CheckCollisionTouch(player, self, &Ball->hitboxRange)) {
174
self->targetPlayer = player;
175
self->state = Ball_State_TargetingPlayer;
176
}
177
}
178
179
Ball_HandleInteractions();
180
Ball_CheckOffScreen();
181
}
182
183
void Ball_State_TargetingPlayer(void)
184
{
185
RSDK_THIS(Ball);
186
187
EntityPlayer *targetPlayer = self->targetPlayer;
188
if (self->position.x <= targetPlayer->position.x) {
189
self->velocity.x += 0x1000;
190
if (self->velocity.x > 0x20000)
191
self->velocity.x = 0x20000;
192
}
193
else {
194
self->velocity.x -= 0x1000;
195
if (self->velocity.x < -0x20000)
196
self->velocity.x = -0x20000;
197
}
198
199
if (self->position.y <= targetPlayer->position.y - 0x400000) {
200
self->velocity.y += 0x1000;
201
if (self->velocity.y > 0x20000)
202
self->velocity.y = 0x20000;
203
}
204
else {
205
self->velocity.y -= 0x1000;
206
if (self->velocity.y < -0x20000)
207
self->velocity.y = -0x20000;
208
}
209
210
self->position.x += self->velocity.x;
211
self->position.y += self->velocity.y;
212
213
if (abs(self->position.x - targetPlayer->position.x) < 0x100000 && abs(0x500000 + self->position.y - targetPlayer->position.y) < 0x100000) {
214
if (RSDK.CheckOnScreen(self, &self->updateRange)) {
215
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_ENEMY), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
216
RSDK.PlaySfx(Explosion->sfxDestroy, false, 255);
217
218
self->velocity.y = 0;
219
RSDK.SetSpriteAnimation(Ball->aniFrames, 1, &self->animator, true, 0);
220
self->state = Ball_State_ChemicalDrop;
221
}
222
}
223
224
RSDK.ProcessAnimation(&self->animator);
225
226
Ball_HandleInteractions();
227
Ball_CheckOffScreen();
228
}
229
230
void Ball_State_ChemicalDrop(void)
231
{
232
RSDK_THIS(Ball);
233
234
RSDK.ProcessAnimation(&self->animator);
235
236
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0xB0000, false)) {
237
Ball_SpawnSplashes();
238
}
239
else {
240
self->position.y += self->velocity.y;
241
self->velocity.y += 0x3800;
242
243
foreach_active(Player, player)
244
{
245
if (Player_CheckCollisionTouch(player, self, &Ball->hitboxBall)) {
246
Player_ElementHurt(player, self, SHIELD_BUBBLE);
247
248
Ball_SpawnSplashes();
249
}
250
}
251
252
if (!RSDK.CheckOnScreen(self, &self->updateRange))
253
destroyEntity(self);
254
}
255
}
256
257
void Ball_State_Splash(void)
258
{
259
RSDK_THIS(Ball);
260
261
self->position.x += self->velocity.x;
262
self->position.y += self->velocity.y;
263
self->velocity.y += 0x3800;
264
265
if (!RSDK.CheckOnScreen(self, &self->updateRange))
266
destroyEntity(self);
267
}
268
269
void Ball_State_StraightMovement(void)
270
{
271
RSDK_THIS(Ball);
272
273
RSDK.ProcessAnimation(&self->animator);
274
275
self->position.x += self->velocity.x;
276
self->position.y += self->velocity.y;
277
278
if (self->velocity.x < 0)
279
self->velocity.x += 0x400;
280
if (self->velocity.x > 0)
281
self->velocity.x -= 0x400;
282
283
if (self->velocity.y < 0)
284
self->velocity.y += 0x400;
285
if (self->velocity.y > 0)
286
self->velocity.y -= 0x400;
287
288
if (!self->velocity.x && !self->velocity.y) {
289
self->startPos.x = self->position.x;
290
self->startPos.y = self->position.y;
291
self->state = Ball_State_AwaitPlayer;
292
}
293
294
Ball_HandleInteractions();
295
}
296
297
void Ball_State_Spawner(void)
298
{
299
RSDK_THIS(Ball);
300
301
EntityBall *child = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, Ball);
302
if (child->classID != Ball->classID) {
303
RSDK.ResetEntity(child, Ball->classID, NULL);
304
child->active = ACTIVE_NORMAL;
305
child->position.x = self->position.x;
306
child->position.y = self->position.y;
307
child->state = Ball_State_StraightMovement;
308
switch (self->type) {
309
case BALL_SPAWN_LEFT: child->velocity.x = -0x20000; break;
310
case BALL_SPAWN_UP: child->velocity.y = -0x20000; break;
311
case BALL_SPAWN_RIGHT: child->velocity.x = 0x20000; break;
312
case BALL_SPAWN_DOWN: child->velocity.y = 0x20000; break;
313
default: break;
314
}
315
}
316
}
317
318
#if GAME_INCLUDE_EDITOR
319
void Ball_EditorDraw(void)
320
{
321
RSDK_THIS(Ball);
322
323
self->startPos = self->position;
324
RSDK.SetSpriteAnimation(Ball->aniFrames, 0, &self->animator, true, 0);
325
326
Ball_Draw();
327
328
if (showGizmos()) {
329
switch (self->type) {
330
case BALL_SPAWN_LEFT:
331
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x - 0x200000, self->position.y, 0xFFFF00, INK_NONE, 0xFF);
332
break;
333
334
case BALL_SPAWN_UP:
335
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x, self->position.y - 0x200000, 0xFFFF00, INK_NONE, 0xFF);
336
break;
337
338
case BALL_SPAWN_RIGHT:
339
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x + 0x200000, self->position.y, 0xFFFF00, INK_NONE, 0xFF);
340
break;
341
342
case BALL_SPAWN_DOWN:
343
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x, self->position.y + 0x200000, 0xFFFF00, INK_NONE, 0xFF);
344
break;
345
346
default: break;
347
}
348
}
349
}
350
351
void Ball_EditorLoad(void)
352
{
353
Ball->aniFrames = RSDK.LoadSpriteAnimation("CPZ/Ball.bin", SCOPE_STAGE);
354
355
RSDK_ACTIVE_VAR(Ball, type);
356
RSDK_ENUM_VAR("Single Ball", BALL_SINGLE);
357
RSDK_ENUM_VAR("Spawner (Left)", BALL_SPAWN_LEFT);
358
RSDK_ENUM_VAR("Spawner (Up)", BALL_SPAWN_UP);
359
RSDK_ENUM_VAR("Spawner (Right)", BALL_SPAWN_RIGHT);
360
RSDK_ENUM_VAR("Spawner (Down)", BALL_SPAWN_DOWN);
361
}
362
#endif
363
364
void Ball_Serialize(void) { RSDK_EDITABLE_VAR(Ball, VAR_UINT8, type); }
365
366