Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/TMZ/CrashTest.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: CrashTest Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectCrashTest *CrashTest;
11
12
void CrashTest_Update(void)
13
{
14
RSDK_THIS(CrashTest);
15
16
StateMachine_Run(self->state);
17
18
CrashTest_HandleLightAnims();
19
20
RSDK.ProcessAnimation(&self->driverAnimator);
21
RSDK.ProcessAnimation(&self->carAnimator);
22
}
23
24
void CrashTest_LateUpdate(void) {}
25
26
void CrashTest_StaticUpdate(void)
27
{
28
foreach_active(CrashTest, crashtest) { RSDK.AddDrawListRef(Zone->playerDrawGroup[0] + 1, RSDK.GetEntitySlot(crashtest)); }
29
}
30
31
void CrashTest_Draw(void)
32
{
33
RSDK_THIS(CrashTest);
34
35
Vector2 lightPos = self->startPos;
36
lightPos.x += 0x300000 * (2 * (self->direction != FLIP_NONE) - 1);
37
38
Vector2 carPos = self->startPos;
39
carPos.x += self->travelDistance * (2 * (self->direction != FLIP_NONE) - 1);
40
41
Vector2 stopperPos;
42
stopperPos.x =
43
(2 * (self->direction != FLIP_NONE) - 1) * (self->length << 16) + self->startPos.x + 0x340000 * (2 * (self->direction != FLIP_NONE) - 1);
44
stopperPos.y = self->startPos.y;
45
46
if (SceneInfo->currentDrawGroup == Zone->playerDrawGroup[0] + 1) {
47
if (self->state != CrashTest_State_Crashed)
48
RSDK.DrawSprite(&self->carAnimator, &carPos, false);
49
}
50
else {
51
RSDK.DrawSprite(&self->lightAnimator, &lightPos, false);
52
RSDK.DrawSprite(&self->stopperAnimator, &stopperPos, false);
53
54
if (self->state != CrashTest_State_Crashed)
55
RSDK.DrawSprite(&self->driverAnimator, &carPos, false);
56
}
57
}
58
59
void CrashTest_Create(void *data)
60
{
61
RSDK_THIS(CrashTest);
62
63
self->active = ACTIVE_BOUNDS;
64
self->drawGroup = Zone->objectDrawGroup[0];
65
self->startPos = self->position;
66
self->visible = true;
67
self->drawFX = FX_FLIP;
68
self->updateRange.x = (self->length + 128) << 16;
69
self->updateRange.y = 0x800000;
70
71
CrashTest_SetupHitboxes();
72
73
Vector2 positions[] = { { -0x100000, -0x180000 }, { 0x80000, -0x1A0000 }, { -0x100000, -0x180000 }, { 0x00000, -0x100000 },
74
{ -0x200000, -0x80000 }, { 0x100000, -0x100000 }, { -0x40000, -0x180000 }, { -0x40000, -0x80000 } };
75
76
for (int32 i = 0; i < 8; ++i) {
77
self->debrisOffsets[i].x = positions[i].x;
78
self->debrisOffsets[i].y = positions[i].y;
79
}
80
81
self->state = CrashTest_State_Init;
82
}
83
84
void CrashTest_StageLoad(void)
85
{
86
CrashTest->active = ACTIVE_ALWAYS;
87
88
CrashTest->aniFrames = RSDK.LoadSpriteAnimation("TMZ1/CrashTest.bin", SCOPE_STAGE);
89
90
CrashTest->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
91
CrashTest->sfxCrash = RSDK.GetSfx("TMZ1/Crash.wav");
92
CrashTest->sfxTrafficLight = RSDK.GetSfx("TMZ1/TrafficLight.wav");
93
CrashTest->sfxCarRev = RSDK.GetSfx("TMZ1/CarRev.wav");
94
CrashTest->sfxSpeedBooster = RSDK.GetSfx("Stage/SpeedBooster.wav");
95
}
96
97
void CrashTest_SetupHitboxes(void)
98
{
99
RSDK_THIS(CrashTest);
100
101
self->hitboxSeat.left = -22;
102
self->hitboxSeat.top = -16;
103
self->hitboxSeat.right = 14;
104
self->hitboxSeat.bottom = 4;
105
106
self->hitboxFront.left = -40;
107
self->hitboxFront.top = -16;
108
self->hitboxFront.right = -22;
109
self->hitboxFront.bottom = 0;
110
111
self->hitboxFloor.left = -22;
112
self->hitboxFloor.top = -4;
113
self->hitboxFloor.right = 14;
114
self->hitboxFloor.bottom = 0;
115
116
self->hitboxBack.left = 14;
117
self->hitboxBack.top = -36;
118
self->hitboxBack.right = 25;
119
self->hitboxBack.bottom = 0;
120
121
self->hitboxStopper.left = -12;
122
self->hitboxStopper.top = -24;
123
self->hitboxStopper.right = 12;
124
self->hitboxStopper.bottom = 0;
125
126
self->hitboxBooster.left = 28;
127
self->hitboxBooster.top = -22;
128
self->hitboxBooster.right = 51;
129
self->hitboxBooster.bottom = -2;
130
}
131
132
void CrashTest_CheckOffScreen(void)
133
{
134
RSDK_THIS(CrashTest);
135
136
if (!RSDK.CheckOnScreen(self, NULL) && !RSDK.CheckPosOnScreen(&self->startPos, &self->updateRange)) {
137
self->position = self->startPos;
138
self->active = ACTIVE_BOUNDS;
139
self->state = CrashTest_State_Init;
140
}
141
}
142
143
void CrashTest_HandlePlayerCrash(void)
144
{
145
RSDK_THIS(CrashTest);
146
147
foreach_active(Player, player)
148
{
149
int32 playerID = RSDK.GetEntitySlot(player);
150
if ((1 << playerID) & self->activePlayers) {
151
self->activePlayers &= ~(1 << playerID);
152
bool32 isSidekick = player->sidekick;
153
154
player->sidekick = true;
155
Player_Hit(player);
156
157
player->sidekick = isSidekick;
158
}
159
}
160
}
161
162
void CrashTest_CheckPlayerCollisionsSolid(void)
163
{
164
RSDK_THIS(CrashTest);
165
166
int32 storeDir = self->direction;
167
168
int32 storeX = self->position.x;
169
int32 storeY = self->position.y;
170
171
int32 stopperX = (2 * (storeDir != FLIP_NONE) - 1) * (self->length << 16) + self->startPos.x + 0x340000 * (2 * (storeDir != FLIP_NONE) - 1);
172
int32 stopperY = self->startPos.y;
173
174
int32 carX = (2 * (storeDir != FLIP_NONE) - 1) + self->startPos.x;
175
int32 carY = self->startPos.y;
176
177
foreach_active(Player, player)
178
{
179
RSDK.GetEntitySlot(player);
180
if (self->state != CrashTest_State_Crashed) {
181
self->position.y = carY;
182
self->position.x = carX;
183
self->direction = storeDir;
184
185
Player_CheckCollisionBox(player, self, &self->hitboxFront);
186
Player_CheckCollisionBox(player, self, &self->hitboxFloor);
187
Player_CheckCollisionBox(player, self, &self->hitboxBack);
188
189
if (self->boosterActive) {
190
if (Player_CheckCollisionTouch(player, self, &self->hitboxBooster)) {
191
Player_ElementHurt(player, self, SHIELD_FIRE);
192
}
193
}
194
}
195
196
self->position.x = stopperX;
197
self->position.y = stopperY;
198
self->direction = FLIP_NONE;
199
Player_CheckCollisionBox(player, self, &self->hitboxStopper);
200
}
201
202
self->position.x = storeX;
203
self->position.y = storeY;
204
self->direction = storeDir;
205
}
206
207
void CrashTest_CheckPlayerRide(void)
208
{
209
RSDK_THIS(CrashTest);
210
211
int32 storeX = self->position.x;
212
int32 storeY = self->position.y;
213
214
self->position.x = self->travelDistance * (2 * (self->direction != FLIP_NONE) - 1) + self->startPos.x;
215
self->position.y = self->startPos.y;
216
217
foreach_active(Player, player)
218
{
219
int32 playerID = RSDK.GetEntitySlot(player);
220
221
if (!((1 << playerID) & self->activePlayers) && !self->playerTimers[playerID]) {
222
if (Player_CheckCollisionTouch(player, self, &self->hitboxSeat)) {
223
self->activePlayers |= 1 << playerID;
224
player->groundVel = 0;
225
226
RSDK.PlaySfx(Player->sfxGrab, false, 0xFF);
227
RSDK.SetSpriteAnimation(player->aniFrames, ANI_HURT, &player->animator, false, 4);
228
229
player->nextGroundState = StateMachine_None;
230
player->nextAirState = StateMachine_None;
231
player->state = Player_State_Static;
232
}
233
}
234
235
if (self->playerTimers[playerID] > 0)
236
self->playerTimers[playerID]--;
237
}
238
239
self->position.x = storeX;
240
self->position.y = storeY;
241
}
242
243
void CrashTest_CheckPlayerJump(void)
244
{
245
RSDK_THIS(CrashTest);
246
247
int32 x = self->startPos.x + (self->travelDistance * (2 * (self->direction != FLIP_NONE) - 1));
248
int32 y = self->startPos.y;
249
250
foreach_active(Player, player)
251
{
252
int32 playerID = RSDK.GetEntitySlot(player);
253
254
if ((1 << playerID) & self->activePlayers) {
255
player->velocity.x = 0;
256
player->velocity.y = 0;
257
258
player->velocity.x = self->direction == FLIP_NONE ? -self->velocity.x : self->velocity.x;
259
player->direction = self->direction == FLIP_NONE;
260
261
player->position.x = x;
262
player->position.y = y;
263
player->position.y -= 0x100000;
264
265
if (player->jumpPress) {
266
self->activePlayers &= ~(1 << playerID);
267
Player_Action_Jump(player);
268
player->velocity.x = self->direction == FLIP_NONE ? -self->velocity.x : self->velocity.x;
269
self->playerTimers[playerID] = 10;
270
}
271
}
272
}
273
}
274
275
void CrashTest_HandleLightAnims(void)
276
{
277
RSDK_THIS(CrashTest);
278
279
if (self->turningGreen != self->prevTurningGreen) {
280
if (self->turningGreen)
281
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 7, &self->lightAnimator, true, 0);
282
else
283
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 9, &self->lightAnimator, true, 0);
284
}
285
286
// finished "Turning [clr]" Animation
287
if (self->lightAnimator.speed == 1 && self->lightAnimator.frameID == self->lightAnimator.frameCount - 1) {
288
if (self->turningGreen)
289
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 8, &self->lightAnimator, false, 0);
290
else
291
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 6, &self->lightAnimator, false, 0);
292
}
293
294
self->prevTurningGreen = self->turningGreen;
295
296
RSDK.ProcessAnimation(&self->lightAnimator);
297
}
298
299
void CrashTest_State_Init(void)
300
{
301
RSDK_THIS(CrashTest);
302
303
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 0, &self->carAnimator, true, 0);
304
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 2, &self->driverAnimator, true, 0);
305
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 6, &self->lightAnimator, true, 0);
306
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 4, &self->stopperAnimator, true, 0);
307
308
self->travelDistance = 0;
309
self->timer = 0;
310
self->activePlayers = 0;
311
self->velocity.x = 0;
312
self->boosterActive = false;
313
self->turningGreen = false;
314
self->prevTurningGreen = false;
315
316
self->state = CrashTest_State_AwaitPlayer;
317
}
318
319
void CrashTest_State_AwaitPlayer(void)
320
{
321
RSDK_THIS(CrashTest);
322
323
CrashTest_CheckPlayerCollisionsSolid();
324
CrashTest_CheckPlayerRide();
325
CrashTest_CheckPlayerJump();
326
327
if (self->activePlayers & 1) {
328
self->active = ACTIVE_NORMAL;
329
self->timer = 40;
330
self->state = CrashTest_State_Rev;
331
RSDK.PlaySfx(CrashTest->sfxTrafficLight, false, 255);
332
}
333
}
334
335
void CrashTest_State_Rev(void)
336
{
337
RSDK_THIS(CrashTest);
338
339
if (self->timer <= 0) {
340
self->boosterActive = true;
341
342
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 1, &self->carAnimator, true, 0);
343
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 3, &self->driverAnimator, true, 0);
344
345
self->active = ACTIVE_NORMAL;
346
self->turningGreen = true;
347
self->state = CrashTest_State_Move;
348
RSDK.PlaySfx(CrashTest->sfxCarRev, false, 255);
349
}
350
else {
351
self->timer--;
352
}
353
}
354
355
void CrashTest_State_Move(void)
356
{
357
RSDK_THIS(CrashTest);
358
359
if (self->velocity.x < 0x100000) {
360
self->velocity.x += 0x2000;
361
362
if (self->velocity.x > 0x100000)
363
self->velocity.x = 0x100000;
364
}
365
366
self->travelDistance += self->velocity.x;
367
368
bool32 crashed = false;
369
if (self->travelDistance >= self->length << 16) {
370
self->travelDistance = self->length << 16;
371
crashed = true;
372
}
373
374
CrashTest_CheckPlayerCollisionsSolid();
375
CrashTest_CheckPlayerRide();
376
CrashTest_CheckPlayerJump();
377
CrashTest_CheckOffScreen();
378
379
if (crashed) {
380
int32 x = self->travelDistance * (2 * (self->direction != FLIP_NONE) - 1) + self->startPos.x;
381
382
Camera_ShakeScreen(0, 5, 5);
383
CrashTest_HandlePlayerCrash();
384
385
self->timer = 0;
386
RSDK.PlaySfx(CrashTest->sfxCrash, false, 255);
387
388
int32 storeX = self->position.x;
389
int32 storeY = self->position.y;
390
391
self->state = CrashTest_State_Crashed;
392
self->turningGreen = false;
393
394
for (int32 i = 0; i < 8; ++i) {
395
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, x + self->debrisOffsets[i].x * (2 * (self->direction == FLIP_NONE) - 1),
396
self->startPos.y + self->debrisOffsets[i].y);
397
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 5, &debris->animator, true, i);
398
debris->drawFX |= FX_ROTATE;
399
400
debris->rotSpeed = self->direction == FLIP_NONE ? -8 : 8;
401
debris->drawGroup = Zone->objectDrawGroup[1];
402
debris->gravityStrength = 0x3800;
403
debris->direction = self->direction;
404
debris->velocity.x = (RSDK.Rand(-32, 32) << 12) + (self->velocity.x >> 1) * (2 * (self->direction != FLIP_NONE) - 1);
405
debris->velocity.y = -0x1000 * RSDK.Rand(32, 128);
406
}
407
408
self->position.x = storeX;
409
self->position.y = storeY;
410
}
411
}
412
413
void CrashTest_State_Crashed(void)
414
{
415
RSDK_THIS(CrashTest);
416
417
CrashTest_CheckOffScreen();
418
CrashTest_CheckPlayerCollisionsSolid();
419
420
if (self->timer >= 30) {
421
Vector2 range = { 0x200000, 0x200000 };
422
423
if (!RSDK.CheckPosOnScreen(&self->startPos, &range))
424
self->state = CrashTest_State_Init;
425
}
426
else {
427
int32 startX = self->travelDistance * (2 * (self->direction != FLIP_NONE) - 1) + self->startPos.x;
428
429
if (!(Zone->timer % 3)) {
430
RSDK.PlaySfx(CrashTest->sfxExplosion, false, 255);
431
432
if (Zone->timer & 4) {
433
int32 x = startX + (RSDK.Rand(-16, 16) << 16);
434
int32 y = self->startPos.y + (RSDK.Rand(-22, 0) << 16);
435
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + 2), x, y);
436
explosion->drawGroup = Zone->objectDrawGroup[1];
437
}
438
}
439
440
++self->timer;
441
}
442
}
443
444
#if GAME_INCLUDE_EDITOR
445
void CrashTest_EditorDraw(void)
446
{
447
RSDK_THIS(CrashTest);
448
449
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 0, &self->carAnimator, false, 0);
450
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 2, &self->driverAnimator, false, 0);
451
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 6, &self->lightAnimator, false, 0);
452
RSDK.SetSpriteAnimation(CrashTest->aniFrames, 4, &self->stopperAnimator, false, 0);
453
454
Vector2 lightPos = self->startPos;
455
lightPos.x += 0x300000 * (2 * (self->direction != FLIP_NONE) - 1);
456
457
Vector2 carPos = self->startPos;
458
carPos.x += self->travelDistance * (2 * (self->direction != FLIP_NONE) - 1);
459
460
Vector2 stopperPos;
461
stopperPos.x =
462
(2 * (self->direction != FLIP_NONE) - 1) * (self->length << 16) + self->startPos.x + 0x340000 * (2 * (self->direction != FLIP_NONE) - 1);
463
stopperPos.y = self->startPos.y;
464
465
RSDK.DrawSprite(&self->lightAnimator, &lightPos, false);
466
RSDK.DrawSprite(&self->stopperAnimator, &stopperPos, false);
467
RSDK.DrawSprite(&self->driverAnimator, &carPos, false);
468
RSDK.DrawSprite(&self->carAnimator, &carPos, false);
469
}
470
471
void CrashTest_EditorLoad(void)
472
{
473
CrashTest->aniFrames = RSDK.LoadSpriteAnimation("TMZ1/CrashTest.bin", SCOPE_STAGE);
474
475
RSDK_ACTIVE_VAR(CrashTest, direction);
476
RSDK_ENUM_VAR("Left", FLIP_NONE);
477
RSDK_ENUM_VAR("Right", FLIP_X);
478
479
// dunno what "debugParticles" is, maybe a way for the devs to test the "particles" of this object in editor?
480
}
481
#endif
482
483
void CrashTest_Serialize(void)
484
{
485
RSDK_EDITABLE_VAR(CrashTest, VAR_UINT8, direction);
486
RSDK_EDITABLE_VAR(CrashTest, VAR_INT32, length);
487
RSDK_EDITABLE_VAR(CrashTest, VAR_BOOL, debugParticles);
488
}
489
490