Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/MMZ/SpikeCorridor.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: SpikeCorridor Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectSpikeCorridor *SpikeCorridor;
11
12
void SpikeCorridor_Update(void)
13
{
14
RSDK_THIS(SpikeCorridor);
15
16
StateMachine_Run(self->state);
17
}
18
19
void SpikeCorridor_LateUpdate(void) {}
20
21
void SpikeCorridor_StaticUpdate(void) {}
22
23
void SpikeCorridor_Draw(void)
24
{
25
RSDK_THIS(SpikeCorridor);
26
27
StateMachine_Run(self->stateDraw);
28
}
29
30
void SpikeCorridor_Create(void *data)
31
{
32
RSDK_THIS(SpikeCorridor);
33
34
self->visible = true;
35
self->drawGroup = Zone->objectDrawGroup[0];
36
self->active = ACTIVE_BOUNDS;
37
38
if (SceneInfo->inEditor) {
39
if (self->colWidth < 20)
40
self->colWidth = 20;
41
42
if (self->rowHeight < 1)
43
self->rowHeight = 1;
44
45
if (self->yOffset < 64)
46
self->yOffset = 64;
47
}
48
49
self->updateRange.x = 0x2000000;
50
self->updateRange.y = 0x2000000;
51
self->state = SpikeCorridor_StateDropper_Setup;
52
}
53
54
void SpikeCorridor_StageLoad(void)
55
{
56
SpikeCorridor->aniFrames = RSDK.LoadSpriteAnimation("MMZ/SpikeCorridor.bin", SCOPE_STAGE);
57
58
SpikeCorridor->sfxDrop = RSDK.GetSfx("Stage/Drop.wav");
59
SpikeCorridor->sfxImpact = RSDK.GetSfx("Stage/Impact2.wav");
60
SpikeCorridor->sfxIndicator = RSDK.GetSfx("MMZ/Indicator.wav");
61
}
62
63
void SpikeCorridor_SetupHitboxes(void)
64
{
65
RSDK_THIS(SpikeCorridor);
66
67
int32 size = (-8 * self->colWidth) >> 1;
68
69
self->hitboxes[0].left = size;
70
self->hitboxes[0].top = 0;
71
self->hitboxes[0].right = size + 8 * self->size;
72
self->hitboxes[0].bottom = 24;
73
74
self->hitboxes[1].left = size + 8 * (self->size + 6);
75
self->hitboxes[1].top = 0;
76
self->hitboxes[1].bottom = 24;
77
self->hitboxes[1].right = size + 8 * self->colWidth;
78
}
79
80
void SpikeCorridor_HandleDrawing(Animator *animator, int32 offsetY, int8 size, bool32 useWarnAnim)
81
{
82
RSDK_THIS(SpikeCorridor);
83
84
int32 startX = self->position.x - (self->colWidth << 18);
85
86
Vector2 drawPos;
87
drawPos.x = 0;
88
drawPos.y = self->position.y + offsetY;
89
90
int32 anim = useWarnAnim ? 5 : 0;
91
92
for (int32 x = 0; x < self->colWidth;) {
93
drawPos.x = startX;
94
95
int32 w = 0;
96
bool32 useOpeningAnim = false;
97
if (x >= size) {
98
if (x < size + 6)
99
useOpeningAnim = true;
100
else
101
w = self->colWidth;
102
}
103
else {
104
w = size;
105
}
106
107
if (useOpeningAnim) {
108
RSDK.SetSpriteAnimation(SpikeCorridor->aniFrames, anim, animator, true, 0);
109
x += 2;
110
startX += 0x100000;
111
}
112
else {
113
switch (w - x) {
114
case 0:
115
RSDK.SetSpriteAnimation(SpikeCorridor->aniFrames, anim, animator, true, 0);
116
x += 2;
117
startX += 0x100000;
118
break;
119
120
case 1: RSDK.SetSpriteAnimation(SpikeCorridor->aniFrames, anim + 1, animator, true, 0); break;
121
122
case 2:
123
RSDK.SetSpriteAnimation(SpikeCorridor->aniFrames, anim + 2, animator, true, 0);
124
++x;
125
startX += 0x80000;
126
break;
127
128
case 3:
129
RSDK.SetSpriteAnimation(SpikeCorridor->aniFrames, anim + 3, animator, true, 0);
130
x += 2;
131
startX += 0x100000;
132
break;
133
134
default:
135
case 4:
136
RSDK.SetSpriteAnimation(SpikeCorridor->aniFrames, anim + 4, animator, true, 0);
137
x += 3;
138
startX += 0x180000;
139
break;
140
}
141
}
142
143
++x;
144
RSDK.DrawSprite(animator, &drawPos, false);
145
startX += 0x80000;
146
}
147
}
148
149
void SpikeCorridor_SetupNextSpikeRow(void)
150
{
151
RSDK_THIS(SpikeCorridor);
152
153
int32 max = (int8)(self->colWidth - 9);
154
int32 size = RSDK.Rand(3, max);
155
156
if (self->lastSize <= -1) {
157
self->size = size;
158
self->lastSize = size;
159
}
160
else {
161
while (true) {
162
if (abs(size - self->lastSize) >= 6 || abs(size - self->lastSize) == 3)
163
break;
164
165
size = RSDK.Rand(3, max);
166
}
167
168
self->size = size;
169
self->lastSize = size;
170
}
171
}
172
173
void SpikeCorridor_CheckPlayerCollisions(void)
174
{
175
RSDK_THIS(SpikeCorridor);
176
177
foreach_active(Player, player)
178
{
179
for (int32 i = 0; i < 2; ++i) {
180
int32 side = Player_CheckCollisionBox(player, self, &self->hitboxes[i]);
181
182
if (side == C_BOTTOM) {
183
Player_Hurt(player, self);
184
player->collisionFlagV |= 2;
185
}
186
else if (side == C_TOP) {
187
player->collisionFlagV |= 1;
188
}
189
}
190
}
191
}
192
193
void SpikeCorridor_StateDropper_Setup(void)
194
{
195
RSDK_THIS(SpikeCorridor);
196
197
self->hitbox.left = -((8 * self->colWidth) >> 1);
198
self->hitbox.top = self->yOffset;
199
self->hitbox.right = (8 * self->colWidth) >> 1;
200
self->hitbox.bottom = self->yOffset + 24 * self->rowHeight;
201
202
self->startPos = self->position;
203
self->active = ACTIVE_BOUNDS;
204
self->visible = true;
205
self->rowID = 0;
206
self->lastSize = -1;
207
self->timer = 0;
208
209
SpikeCorridor_StateDropper_CheckForPlayer();
210
self->state = SpikeCorridor_StateDropper_CheckForPlayer;
211
212
self->stateDraw = StateMachine_None;
213
}
214
215
void SpikeCorridor_StateDropper_CheckForPlayer(void)
216
{
217
RSDK_THIS(SpikeCorridor);
218
219
foreach_active(Player, player)
220
{
221
if (player->sidekick)
222
continue;
223
224
if (Player_CheckCollisionTouch(player, self, &self->hitbox)) {
225
Hitbox *playerHitbox = Player_GetHitbox(player);
226
227
if (player->position.x <= self->position.x) {
228
if (player->position.x + (playerHitbox->left << 16) >= self->position.x + (self->hitbox.left << 16)) {
229
SpikeCorridor_SetupNextSpikeRow();
230
231
self->state = SpikeCorridor_StateDropper_DropWarn;
232
self->stateDraw = SpikeCorridor_Draw_DropWarn;
233
}
234
}
235
else if (player->position.x + (playerHitbox->right << 16) <= self->position.x + (self->hitbox.right << 16)) {
236
SpikeCorridor_SetupNextSpikeRow();
237
238
self->state = SpikeCorridor_StateDropper_DropWarn;
239
self->stateDraw = SpikeCorridor_Draw_DropWarn;
240
}
241
}
242
}
243
}
244
245
void SpikeCorridor_StateDropper_DropWarn(void)
246
{
247
RSDK_THIS(SpikeCorridor);
248
249
if (self->timer >= 120) {
250
self->state = SpikeCorridor_StateDropper_SpawnSpikes;
251
self->stateDraw = 0;
252
}
253
else {
254
if (!(self->timer % 40))
255
RSDK.PlaySfx(SpikeCorridor->sfxIndicator, false, 255);
256
257
++self->timer;
258
}
259
}
260
261
void SpikeCorridor_StateDropper_SpawnSpikes(void)
262
{
263
RSDK_THIS(SpikeCorridor);
264
265
RSDK.PlaySfx(SpikeCorridor->sfxDrop, false, 255);
266
267
EntitySpikeCorridor *fallRow = CREATE_ENTITY(SpikeCorridor, self, self->startPos.x, self->startPos.y);
268
fallRow->isPermanent = true;
269
fallRow->state = SpikeCorridor_StateSpikes_Setup;
270
fallRow->active = ACTIVE_NORMAL;
271
fallRow->storedRowID = self->rowID;
272
fallRow->size = self->size;
273
fallRow->parent = self;
274
fallRow->drawGroup = self->drawGroup;
275
fallRow->colWidth = self->colWidth;
276
fallRow->yOffset = 0;
277
fallRow->fallOffset = (self->yOffset + 24 * (self->rowHeight - self->rowID++ - 1)) << 16;
278
279
self->timer = 0;
280
281
if (self->rowID >= self->rowHeight) {
282
self->interaction = false;
283
self->state = StateMachine_None;
284
}
285
else {
286
self->state = SpikeCorridor_StateDropper_DropWait;
287
}
288
}
289
290
void SpikeCorridor_StateDropper_DropWait(void)
291
{
292
RSDK_THIS(SpikeCorridor);
293
294
if (self->timer >= 60) {
295
SpikeCorridor_SetupNextSpikeRow();
296
self->timer = 0;
297
self->state = SpikeCorridor_StateDropper_DropWarn;
298
self->stateDraw = SpikeCorridor_Draw_DropWarn;
299
}
300
else {
301
self->timer++;
302
}
303
}
304
305
void SpikeCorridor_StateSpikes_Setup(void)
306
{
307
RSDK_THIS(SpikeCorridor);
308
309
SpikeCorridor_SetupHitboxes();
310
311
self->active = ACTIVE_NORMAL;
312
self->visible = true;
313
self->stateDraw = SpikeCorridor_Draw_Spikes;
314
315
self->state = SpikeCorridor_StateSpikes_Fall;
316
SpikeCorridor_StateSpikes_Fall();
317
}
318
319
void SpikeCorridor_StateSpikes_Fall(void)
320
{
321
RSDK_THIS(SpikeCorridor);
322
323
if (self->yOffset >= self->fallOffset) {
324
RSDK.StopSfx(SpikeCorridor->sfxDrop);
325
RSDK.PlaySfx(SpikeCorridor->sfxImpact, false, 255);
326
327
Camera_ShakeScreen(0, 0, 5);
328
self->active = ACTIVE_ALWAYS;
329
self->state = SpikeCorridor_StateSpikes_Land;
330
}
331
else {
332
self->velocity.y += 0x3800;
333
self->yOffset += self->velocity.y;
334
335
if (self->yOffset > self->fallOffset)
336
self->yOffset = self->fallOffset;
337
338
self->position.y = self->yOffset + self->parent->startPos.y;
339
}
340
341
SpikeCorridor_CheckPlayerCollisions();
342
}
343
344
void SpikeCorridor_StateSpikes_Land(void) { SpikeCorridor_CheckPlayerCollisions(); }
345
346
void SpikeCorridor_Draw_DropWarn(void)
347
{
348
RSDK_THIS(SpikeCorridor);
349
350
Animator animator;
351
memset(&animator, 0, sizeof(Animator));
352
353
int32 yOff = 0;
354
if ((ScreenInfo->position.y << 16) - self->startPos.y > 0)
355
yOff = (ScreenInfo->position.y << 16) - self->startPos.y;
356
357
SpikeCorridor_HandleDrawing(&animator, yOff, self->size, true);
358
}
359
360
void SpikeCorridor_Draw_Spikes(void)
361
{
362
RSDK_THIS(SpikeCorridor);
363
364
Animator animator;
365
memset(&animator, 0, sizeof(Animator));
366
367
SpikeCorridor_HandleDrawing(&animator, 0, self->size, false);
368
}
369
370
#if GAME_INCLUDE_EDITOR
371
void SpikeCorridor_EditorDraw(void)
372
{
373
RSDK_THIS(SpikeCorridor);
374
375
Animator animator;
376
memset(&animator, 0, sizeof(Animator));
377
378
self->inkEffect = INK_NONE;
379
SpikeCorridor_HandleDrawing(&animator, 0, self->colWidth, true);
380
381
if (showGizmos()) {
382
RSDK_DRAWING_OVERLAY(true);
383
384
int32 yOffset = 0;
385
self->fallOffset = (self->yOffset + 24 * (self->rowHeight - 1)) << 16;
386
387
while (yOffset < self->fallOffset) {
388
self->velocity.y += 0x3800;
389
yOffset += self->velocity.y;
390
if (yOffset > self->fallOffset)
391
yOffset = self->fallOffset;
392
}
393
394
self->inkEffect = INK_BLEND;
395
SpikeCorridor_HandleDrawing(&animator, yOffset, self->colWidth, false);
396
397
RSDK_DRAWING_OVERLAY(false);
398
}
399
}
400
401
void SpikeCorridor_EditorLoad(void) { SpikeCorridor->aniFrames = RSDK.LoadSpriteAnimation("MMZ/SpikeCorridor.bin", SCOPE_STAGE); }
402
#endif
403
404
void SpikeCorridor_Serialize(void)
405
{
406
RSDK_EDITABLE_VAR(SpikeCorridor, VAR_INT8, colWidth);
407
RSDK_EDITABLE_VAR(SpikeCorridor, VAR_INT8, rowHeight);
408
RSDK_EDITABLE_VAR(SpikeCorridor, VAR_INT32, yOffset);
409
}
410
411