Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/CPZ/Bubbler.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Bubbler Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectBubbler *Bubbler = NULL;
11
12
// NOTE: this object is basically a port of the Bubbler & Bubbler's mother debug objects in S2 '13, though it's unused
13
// It's not in any stage lists, and the sprites for it aren't aligned correctly, though they are at different positions from S2 '13s
14
15
void Bubbler_Update(void)
16
{
17
RSDK_THIS(Bubbler);
18
19
StateMachine_Run(self->state);
20
}
21
22
void Bubbler_LateUpdate(void) {}
23
24
void Bubbler_StaticUpdate(void) {}
25
26
void Bubbler_Draw(void)
27
{
28
RSDK_THIS(Bubbler);
29
30
RSDK.DrawSprite(&self->bodyHitbox, NULL, false);
31
if (self->startPos.x)
32
RSDK.DrawSprite(&self->flameAnimator, NULL, false);
33
}
34
35
void Bubbler_Create(void *data)
36
{
37
RSDK_THIS(Bubbler);
38
39
self->visible = true;
40
self->drawGroup = Zone->objectDrawGroup[0];
41
self->drawFX |= FX_FLIP;
42
self->active = ACTIVE_BOUNDS;
43
self->updateRange.x = 0x800000;
44
self->updateRange.y = 0x800000;
45
46
if (data) {
47
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 3, &self->bodyHitbox, true, 0);
48
self->state = Bubbler_StateProjectile_Seed;
49
}
50
else {
51
self->startPos = self->position;
52
self->startDir = self->direction;
53
self->velocity.x = !self->direction ? -0x4000 : 0x4000;
54
self->velocity.y = 0;
55
self->timer = 0;
56
self->spawnTimer = 32;
57
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 0, &self->bodyHitbox, true, 0);
58
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 1, &self->flameAnimator, true, 0);
59
self->state = Bubbler_State_Init;
60
}
61
}
62
63
void Bubbler_StageLoad(void)
64
{
65
if (RSDK.CheckSceneFolder("CPZ"))
66
Bubbler->aniFrames = RSDK.LoadSpriteAnimation("CPZ/Bubbler.bin", SCOPE_STAGE);
67
68
Bubbler->hitboxBadnik.left = -16;
69
Bubbler->hitboxBadnik.top = -12;
70
Bubbler->hitboxBadnik.right = 16;
71
Bubbler->hitboxBadnik.bottom = 12;
72
73
Bubbler->hitboxRange.left = -160;
74
Bubbler->hitboxRange.top = -12;
75
Bubbler->hitboxRange.right = 16;
76
Bubbler->hitboxRange.bottom = 96;
77
78
Bubbler->hitboxProjectile.left = -2;
79
Bubbler->hitboxProjectile.top = -2;
80
Bubbler->hitboxProjectile.right = 2;
81
Bubbler->hitboxProjectile.bottom = 2;
82
83
DEBUGMODE_ADD_OBJ(Bubbler);
84
}
85
86
void Bubbler_DebugSpawn(void)
87
{
88
RSDK_THIS(Bubbler);
89
90
CREATE_ENTITY(Bubbler, NULL, self->position.x, self->position.y);
91
}
92
93
void Bubbler_DebugDraw(void)
94
{
95
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 0, &DebugMode->animator, true, 0);
96
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
97
}
98
99
void Bubbler_HandleInteractions(void)
100
{
101
RSDK_THIS(Bubbler);
102
103
foreach_active(Player, player)
104
{
105
if (Player_CheckBadnikTouch(player, self, &Bubbler->hitboxBadnik))
106
Player_CheckBadnikBreak(player, self, true);
107
}
108
}
109
110
void Bubbler_HandleProjectileInteractions(void)
111
{
112
RSDK_THIS(Bubbler);
113
114
int32 distance = 0x7FFFFFFF;
115
foreach_active(Player, player)
116
{
117
if (abs(player->position.x - self->position.x) < distance) {
118
distance = abs(player->position.x - self->position.x);
119
self->direction = player->position.x >= self->position.x;
120
}
121
122
if (Player_CheckCollisionTouch(player, self, &Bubbler->hitboxProjectile))
123
Player_Hurt(player, self);
124
}
125
}
126
127
void Bubbler_CheckOffScreen(void)
128
{
129
RSDK_THIS(Bubbler);
130
131
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
132
self->position = self->startPos;
133
self->direction = self->startDir;
134
Bubbler_Create(NULL);
135
}
136
}
137
138
void Bubbler_State_Init(void)
139
{
140
RSDK_THIS(Bubbler);
141
142
self->active = ACTIVE_NORMAL;
143
self->velocity.x = -0x10000;
144
self->state = Bubbler_State_MotherPatrol;
145
Bubbler_State_MotherPatrol();
146
}
147
148
void Bubbler_State_MotherPatrol(void)
149
{
150
RSDK_THIS(Bubbler);
151
152
self->position.x += self->velocity.x;
153
RSDK.ProcessAnimation(&self->flameAnimator);
154
155
if (!--self->timer) {
156
self->direction ^= FLIP_X;
157
self->velocity.x = -self->velocity.x;
158
self->timer = 0x200;
159
}
160
161
foreach_active(Player, player)
162
{
163
if (Player_CheckCollisionTouch(player, self, &Bubbler->hitboxRange)) {
164
self->timer = 16;
165
self->state = Bubbler_State_FoundPlayer;
166
if (self->direction == FLIP_NONE)
167
self->velocity.x = -0x28000;
168
else
169
self->velocity.x = 0x28000;
170
}
171
}
172
173
Bubbler_HandleInteractions();
174
Bubbler_CheckOffScreen();
175
}
176
177
void Bubbler_State_FoundPlayer(void)
178
{
179
RSDK_THIS(Bubbler);
180
181
RSDK.ProcessAnimation(&self->flameAnimator);
182
183
if (--self->timer < 0) {
184
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 2, &self->flameAnimator, true, 0);
185
self->state = Bubbler_State_AttackPlayer;
186
}
187
188
Bubbler_HandleInteractions();
189
Bubbler_CheckOffScreen();
190
}
191
192
void Bubbler_State_AttackPlayer(void)
193
{
194
RSDK_THIS(Bubbler);
195
196
RSDK.ProcessAnimation(&self->flameAnimator);
197
198
self->position.x += self->velocity.x;
199
self->position.y += self->velocity.y;
200
201
if (++self->spawnTimer >= 30) {
202
self->spawnTimer = 0;
203
int32 spawnX = self->position.x + 0x60000;
204
if (self->direction)
205
spawnX = self->position.x - 0x60000;
206
CREATE_ENTITY(Bubbler, INT_TO_VOID(true), spawnX, self->position.y + 0xA0000)->active = ACTIVE_NORMAL;
207
}
208
209
Bubbler_HandleInteractions();
210
Bubbler_CheckOffScreen();
211
}
212
213
void Bubbler_StateProjectile_Seed(void)
214
{
215
RSDK_THIS(Bubbler);
216
217
RSDK.ProcessAnimation(&self->bodyHitbox);
218
self->position.y += 0x10000;
219
220
Bubbler_HandleProjectileInteractions();
221
222
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0, false)) {
223
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 4, &self->bodyHitbox, true, 0);
224
self->state = Bubbler_StateProjectile_Bubbler;
225
}
226
227
if (!RSDK.CheckOnScreen(self, &self->updateRange))
228
destroyEntity(self);
229
}
230
231
void Bubbler_StateProjectile_Bubbler(void)
232
{
233
RSDK_THIS(Bubbler);
234
235
RSDK.ProcessAnimation(&self->bodyHitbox);
236
237
if (self->bodyHitbox.frameID >= 7)
238
destroyEntity(self);
239
else if (self->bodyHitbox.frameID < 6)
240
Bubbler_HandleProjectileInteractions();
241
}
242
243
#if GAME_INCLUDE_EDITOR
244
void Bubbler_EditorDraw(void)
245
{
246
RSDK_THIS(Bubbler);
247
248
self->startPos = self->position;
249
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 0, &self->bodyHitbox, true, 0);
250
RSDK.SetSpriteAnimation(Bubbler->aniFrames, 1, &self->flameAnimator, true, 0);
251
252
Bubbler_Draw();
253
}
254
255
void Bubbler_EditorLoad(void)
256
{
257
Bubbler->aniFrames = RSDK.LoadSpriteAnimation("CPZ/Bubbler.bin", SCOPE_STAGE);
258
259
RSDK_ACTIVE_VAR(Bubbler, direction);
260
RSDK_ENUM_VAR("Left", FLIP_NONE);
261
RSDK_ENUM_VAR("Right", FLIP_X);
262
}
263
#endif
264
265
void Bubbler_Serialize(void) { RSDK_EDITABLE_VAR(Bubbler, VAR_UINT8, direction); }
266
267