Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/HCZ/TurboSpiker.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: TurboSpiker Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectTurboSpiker *TurboSpiker;
11
12
// back in 3mixed, i was assigned this object
13
// i was never able to get it properly working
14
// here it is. :smirk2: -rmg
15
// hello rmg, this is rdc. there were MULTIPLE easy to find/fix bugs here so don't u dare smirk
16
17
void TurboSpiker_Update(void)
18
{
19
RSDK_THIS(TurboSpiker);
20
21
StateMachine_Run(self->state);
22
}
23
24
void TurboSpiker_LateUpdate(void) {}
25
26
void TurboSpiker_StaticUpdate(void) {}
27
28
void TurboSpiker_Draw(void)
29
{
30
RSDK_THIS(TurboSpiker);
31
32
RSDK.DrawSprite(&self->shellAnimator, NULL, false);
33
RSDK.DrawSprite(&self->animator, NULL, false);
34
}
35
36
void TurboSpiker_Create(void *data)
37
{
38
RSDK_THIS(TurboSpiker);
39
40
self->playedLaunchSfx = false;
41
self->visible = true;
42
self->drawFX |= FX_FLIP;
43
self->drawGroup = Zone->objectDrawGroup[0] + 1;
44
self->startPos = self->position;
45
self->active = ACTIVE_BOUNDS;
46
self->updateRange.x = 0x800000;
47
self->updateRange.y = 0x800000;
48
self->startDir = self->direction;
49
50
if (data) {
51
self->active = ACTIVE_NORMAL;
52
}
53
else {
54
self->unused = 16;
55
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 1, &self->shellAnimator, true, 0);
56
self->state = TurboSpiker_State_Init;
57
}
58
}
59
60
void TurboSpiker_StageLoad(void)
61
{
62
if (RSDK.CheckSceneFolder("HCZ"))
63
TurboSpiker->aniFrames = RSDK.LoadSpriteAnimation("HCZ/TurboSpiker.bin", SCOPE_STAGE);
64
65
TurboSpiker->hitboxBadnik.left = -14;
66
TurboSpiker->hitboxBadnik.top = -14;
67
TurboSpiker->hitboxBadnik.right = 14;
68
TurboSpiker->hitboxBadnik.bottom = 14;
69
70
TurboSpiker->hitboxSpike.left = -5;
71
TurboSpiker->hitboxSpike.top = -31;
72
TurboSpiker->hitboxSpike.right = 9;
73
TurboSpiker->hitboxSpike.bottom = 2;
74
75
TurboSpiker->hitboxRange.left = -128;
76
TurboSpiker->hitboxRange.top = -512;
77
TurboSpiker->hitboxRange.right = 128;
78
TurboSpiker->hitboxRange.bottom = 512;
79
80
TurboSpiker->sfxLaunch = RSDK.GetSfx("Stage/Launch.wav");
81
TurboSpiker->sfxSplash = RSDK.GetSfx("Stage/Splash.wav");
82
83
DEBUGMODE_ADD_OBJ(TurboSpiker);
84
}
85
86
void TurboSpiker_DebugDraw(void)
87
{
88
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 0, &DebugMode->animator, true, 0);
89
RSDK.DrawSprite(&DebugMode->animator, NULL, false);
90
}
91
92
void TurboSpiker_DebugSpawn(void)
93
{
94
RSDK_THIS(TurboSpiker);
95
CREATE_ENTITY(TurboSpiker, NULL, self->position.x, self->position.y);
96
}
97
98
void TurboSpiker_Hermit_Collide(void)
99
{
100
RSDK_THIS(TurboSpiker);
101
foreach_active(Player, player)
102
{
103
if (Player_CheckBadnikTouch(player, self, &TurboSpiker->hitboxBadnik) && Player_CheckBadnikBreak(player, self, false)) {
104
if (self->spike)
105
destroyEntity(self->spike);
106
107
destroyEntity(self);
108
}
109
}
110
}
111
112
void TurboSpiker_Hermit_CheckOffScreen(void)
113
{
114
RSDK_THIS(TurboSpiker);
115
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
116
self->position.x = self->startPos.x;
117
self->position.y = self->startPos.y;
118
if (self->spike)
119
destroyEntity(self->spike);
120
121
self->direction = self->startDir;
122
TurboSpiker_Create(NULL);
123
}
124
}
125
126
void TurboSpiker_HandleMovement(void)
127
{
128
RSDK_THIS(TurboSpiker);
129
130
self->position.x += self->velocity.x;
131
132
if (!RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, 8)) {
133
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 1, &self->shellAnimator, true, 0);
134
self->timer = 0;
135
if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0x20000 * (self->direction ? -1 : 1), 0x80000, 8))
136
self->state = TurboSpiker_State_Turning;
137
else
138
self->state = TurboSpiker_State_Falling;
139
}
140
}
141
142
void TurboSpiker_State_Init(void)
143
{
144
RSDK_THIS(TurboSpiker);
145
146
self->active = ACTIVE_NORMAL;
147
148
if (self->type == TURBOSPIKER_HIDDEN) {
149
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 1, &self->animator, true, 0);
150
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 3, &self->shellAnimator, true, 0);
151
self->drawGroup = Zone->fgDrawGroup[0] + 1;
152
if (Player_GetNearestPlayer())
153
self->direction = Player_GetNearestPlayer()->position.x >= self->position.x;
154
}
155
156
EntityTurboSpiker *spike = CREATE_ENTITY(TurboSpiker, INT_TO_VOID(true), self->position.x, self->position.y);
157
spike->isPermanent = true;
158
spike->direction = self->direction;
159
spike->drawGroup = self->drawGroup - 1;
160
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 3, &spike->shellAnimator, true, 0);
161
spike->state = TurboSpiker_HandleSpikeCollisions;
162
self->spike = spike;
163
164
if (self->type == TURBOSPIKER_VISIBLE) {
165
self->timer = 64;
166
self->state = TurboSpiker_State_Idle;
167
self->velocity.x = 0x10000 * (self->direction ? 1 : -1);
168
TurboSpiker_State_Idle();
169
self->velocity.y = 0;
170
}
171
else {
172
self->state = TurboSpiker_State_Hidden;
173
TurboSpiker_State_Hidden();
174
self->velocity.y = 0;
175
}
176
}
177
void TurboSpiker_State_Idle(void)
178
{
179
RSDK_THIS(TurboSpiker);
180
181
foreach_active(Player, player)
182
{
183
if (Player_CheckCollisionTouch(player, self, &TurboSpiker->hitboxRange)) {
184
self->timer = 1;
185
self->state = TurboSpiker_State_FireShell;
186
TurboSpiker_Hermit_Collide();
187
TurboSpiker_Hermit_CheckOffScreen();
188
foreach_return;
189
}
190
}
191
192
TurboSpiker_HandleMovement();
193
194
if (self->spike) {
195
self->spike->position.x = self->position.x;
196
self->spike->position.y = self->position.y;
197
}
198
199
if (self->state == TurboSpiker_State_Idle && !--self->timer) {
200
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 1, &self->shellAnimator, true, 0);
201
self->timer = 0;
202
self->state = TurboSpiker_State_Turning;
203
}
204
205
RSDK.ProcessAnimation(&self->shellAnimator);
206
TurboSpiker_Hermit_Collide();
207
TurboSpiker_Hermit_CheckOffScreen();
208
}
209
210
void TurboSpiker_State_Hidden(void)
211
{
212
RSDK_THIS(TurboSpiker);
213
214
foreach_active(Player, player)
215
{
216
if (Player_CheckCollisionTouch(player, self, &TurboSpiker->hitboxRange)) {
217
CREATE_ENTITY(Water, INT_TO_VOID(WATER_SPLASH), self->position.x, self->position.y + 0x80000)->childPtr = INT_TO_VOID(true);
218
RSDK.PlaySfx(TurboSpiker->sfxSplash, false, 0xFF);
219
RSDK.SetSpriteAnimation(-1, 0, &self->animator, true, 0);
220
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 1, &self->shellAnimator, true, 0);
221
self->drawGroup = Zone->objectDrawGroup[0] + 1;
222
if (self->spike)
223
self->spike->drawGroup = Zone->objectDrawGroup[0];
224
225
EntityTurboSpiker *ember = CREATE_ENTITY(TurboSpiker, INT_TO_VOID(true), self->position.x, self->position.y);
226
ember->direction = self->direction;
227
ember->drawGroup = self->drawGroup + 1;
228
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 6, &ember->shellAnimator, true, 0);
229
ember->state = TurboSpiker_State_Ember;
230
self->timer = 60;
231
self->state = TurboSpiker_State_FireShell;
232
TurboSpiker_State_FireShell();
233
foreach_return;
234
}
235
}
236
237
RSDK.ProcessAnimation(&self->animator);
238
TurboSpiker_Hermit_Collide();
239
TurboSpiker_Hermit_CheckOffScreen();
240
}
241
242
void TurboSpiker_State_Moving(void)
243
{
244
RSDK_THIS(TurboSpiker);
245
246
TurboSpiker_HandleMovement();
247
248
if (self->spike) {
249
self->spike->position.x = self->position.x;
250
self->spike->position.y = self->position.y;
251
}
252
253
RSDK.ProcessAnimation(&self->shellAnimator);
254
TurboSpiker_Hermit_Collide();
255
TurboSpiker_Hermit_CheckOffScreen();
256
}
257
258
void TurboSpiker_State_Turning(void)
259
{
260
RSDK_THIS(TurboSpiker);
261
RSDK.ProcessAnimation(&self->shellAnimator);
262
263
if (++self->timer == 15) {
264
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 2, &self->shellAnimator, true, 0);
265
self->state = TurboSpiker_State_FinishTurning;
266
}
267
268
TurboSpiker_Hermit_Collide();
269
TurboSpiker_Hermit_CheckOffScreen();
270
}
271
272
void TurboSpiker_State_FinishTurning(void)
273
{
274
RSDK_THIS(TurboSpiker);
275
276
RSDK.ProcessAnimation(&self->shellAnimator);
277
278
if (self->shellAnimator.frameID == self->shellAnimator.frameCount - 1) {
279
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 0, &self->shellAnimator, true, 0);
280
self->direction ^= FLIP_X;
281
self->velocity.x = -self->velocity.x;
282
283
if (self->spike) {
284
self->spike->direction = self->direction;
285
self->timer = 128;
286
self->state = TurboSpiker_State_Idle;
287
TurboSpiker_State_Idle();
288
}
289
else {
290
self->state = TurboSpiker_State_Moving;
291
TurboSpiker_State_Moving();
292
}
293
}
294
else {
295
TurboSpiker_Hermit_Collide();
296
TurboSpiker_Hermit_CheckOffScreen();
297
}
298
}
299
300
void TurboSpiker_State_Falling(void)
301
{
302
RSDK_THIS(TurboSpiker);
303
304
RSDK.ProcessAnimation(&self->shellAnimator);
305
306
self->position.x += self->velocity.x;
307
self->position.y += self->velocity.y;
308
self->velocity.y += 0x3800;
309
310
if (self->spike) {
311
self->spike->position.x = self->position.x;
312
self->spike->position.y = self->position.y;
313
}
314
315
if (RSDK.ObjectTileGrip(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x80000, 8)) {
316
self->velocity.y = 0;
317
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 0, &self->shellAnimator, true, 0);
318
self->state = TurboSpiker_State_Moving;
319
TurboSpiker_State_Moving();
320
}
321
else {
322
TurboSpiker_Hermit_Collide();
323
TurboSpiker_Hermit_CheckOffScreen();
324
}
325
}
326
327
void TurboSpiker_State_FireShell(void)
328
{
329
RSDK_THIS(TurboSpiker);
330
331
if (!--self->timer) {
332
if (Player_GetNearestPlayer())
333
self->direction = Player_GetNearestPlayer()->position.x < self->position.x;
334
335
if (self->spike) {
336
RSDK.PlaySfx(TurboSpiker->sfxLaunch, false, 0xFF);
337
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 4, &self->spike->animator, true, 0);
338
self->spike->direction = self->direction;
339
self->spike->velocity.x = 0x14000 * (self->direction ? -1 : 1);
340
self->spike->velocity.y = -0x30000;
341
self->spike->state = TurboSpiker_State_Spike;
342
self->spike = NULL;
343
}
344
345
self->velocity.x = 0x20000 * (self->direction ? 1 : -1);
346
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 0, &self->shellAnimator, true, 0);
347
self->state = TurboSpiker_State_Moving;
348
self->shellAnimator.speed = 2;
349
TurboSpiker_HandleMovement();
350
}
351
352
RSDK.ProcessAnimation(&self->shellAnimator);
353
354
TurboSpiker_Hermit_Collide();
355
TurboSpiker_Hermit_CheckOffScreen();
356
}
357
358
void TurboSpiker_HandleSpikeCollisions(void)
359
{
360
RSDK_THIS(TurboSpiker);
361
362
foreach_active(Player, player)
363
{
364
if (Player_CheckCollisionTouch(player, self, &TurboSpiker->hitboxSpike)) {
365
#if MANIA_USE_PLUS
366
if (!Player_CheckMightyUnspin(player, 0x400, 2, &player->uncurlTimer))
367
#endif
368
Player_Hurt(player, self);
369
}
370
}
371
}
372
373
void TurboSpiker_State_Spike(void)
374
{
375
RSDK_THIS(TurboSpiker);
376
377
if (!self->playedLaunchSfx) {
378
RSDK.PlaySfx(TurboSpiker->sfxLaunch, false, 0xFF);
379
self->playedLaunchSfx = true;
380
}
381
382
if (!self->onScreen) {
383
RSDK.StopSfx(TurboSpiker->sfxLaunch);
384
self->playedLaunchSfx = false;
385
}
386
387
self->position.x += self->velocity.x;
388
self->position.y += self->velocity.y;
389
TurboSpiker_HandleSpikeCollisions();
390
391
if (!(Zone->timer & 3)) {
392
int32 x = self->position.x + (RSDK.Rand(-4, 4) << 16);
393
int32 y = self->position.y + (RSDK.Rand(-2, 3) << 16);
394
EntityTurboSpiker *ember = CREATE_ENTITY(TurboSpiker, INT_TO_VOID(true), x, y);
395
ember->direction = self->direction;
396
ember->drawGroup = self->drawGroup - 1;
397
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 5, &ember->shellAnimator, true, 0);
398
ember->state = TurboSpiker_State_Ember;
399
}
400
401
RSDK.ProcessAnimation(&self->animator);
402
if (!RSDK.CheckOnScreen(self, NULL))
403
destroyEntity(self);
404
}
405
406
void TurboSpiker_State_Ember(void)
407
{
408
RSDK_THIS(TurboSpiker);
409
410
RSDK.ProcessAnimation(&self->shellAnimator);
411
if (self->shellAnimator.frameID == self->shellAnimator.frameCount - 1)
412
destroyEntity(self);
413
}
414
415
#if GAME_INCLUDE_EDITOR
416
void TurboSpiker_EditorDraw(void)
417
{
418
RSDK_THIS(TurboSpiker);
419
420
RSDK.SetSpriteAnimation(TurboSpiker->aniFrames, 1, &self->animator, true, 0);
421
RSDK.SetSpriteAnimation(self->type == TURBOSPIKER_VISIBLE ? TurboSpiker->aniFrames : -1, 3, &self->shellAnimator, true, 0);
422
423
RSDK.DrawSprite(&self->shellAnimator, NULL, false);
424
RSDK.DrawSprite(&self->animator, NULL, false);
425
}
426
427
void TurboSpiker_EditorLoad(void)
428
{
429
TurboSpiker->aniFrames = RSDK.LoadSpriteAnimation("HCZ/TurboSpiker.bin", SCOPE_STAGE);
430
431
RSDK_ACTIVE_VAR(TurboSpiker, type);
432
RSDK_ENUM_VAR("Always Visible", TURBOSPIKER_VISIBLE);
433
RSDK_ENUM_VAR("Hidden Behind Waterfall", TURBOSPIKER_HIDDEN);
434
435
RSDK_ACTIVE_VAR(TurboSpiker, direction);
436
RSDK_ENUM_VAR("Left", FLIP_NONE);
437
RSDK_ENUM_VAR("Right", FLIP_X);
438
}
439
#endif
440
441
void TurboSpiker_Serialize(void)
442
{
443
RSDK_EDITABLE_VAR(TurboSpiker, VAR_ENUM, type);
444
RSDK_EDITABLE_VAR(TurboSpiker, VAR_UINT8, direction);
445
}
446