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