Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/OOZ/Hatch.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: Hatch Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectHatch *Hatch;
11
12
void Hatch_Update(void)
13
{
14
RSDK_THIS(Hatch);
15
16
int32 storeX = self->position.x;
17
int32 storeY = self->position.y;
18
19
self->position.x = self->startPos.x;
20
self->position.y = self->startPos.y;
21
22
if (self->useMoveLayer) {
23
foreach_all(Player, player)
24
{
25
if (Player_CheckCollisionTouch(player, self, &self->hitbox)) {
26
TileLayer *moveLayer = RSDK.GetTileLayer(Zone->moveLayer);
27
if (!player->sidekick) {
28
moveLayer->scrollPos = -self->vScrollPos;
29
moveLayer->scrollInfo[0].scrollPos = -self->hScrollPos;
30
}
31
32
player->collisionLayers |= Zone->moveLayerMask;
33
player->moveLayerPosition.x = moveLayer->scrollInfo[0].scrollPos;
34
player->moveLayerPosition.y = moveLayer->scrollPos;
35
}
36
}
37
}
38
39
self->position.x = storeX;
40
self->position.y = storeY;
41
42
StateMachine_Run(self->state);
43
}
44
45
void Hatch_LateUpdate(void) {}
46
47
void Hatch_StaticUpdate(void)
48
{
49
foreach_all(Hatch, hatch) { RSDK.AddDrawListRef(Zone->objectDrawGroup[1], RSDK.GetEntitySlot(hatch)); }
50
}
51
52
void Hatch_Draw(void)
53
{
54
RSDK_THIS(Hatch);
55
56
if (SceneInfo->currentDrawGroup == self->drawGroup) {
57
RSDK.DrawSprite(&self->hatchAnimator, NULL, false);
58
}
59
else {
60
if (self->hatchAnimator.animationID == 2 && self->hatchAnimator.frameID >= 3)
61
RSDK.DrawSprite(&self->hatchAnimator, NULL, false);
62
63
RSDK.DrawSprite(&self->baseAnimator, NULL, false);
64
}
65
}
66
67
void Hatch_Create(void *data)
68
{
69
RSDK_THIS(Hatch);
70
71
self->drawFX = FX_FLIP;
72
if (!SceneInfo->inEditor) {
73
self->active = ACTIVE_BOUNDS;
74
self->drawGroup = Zone->objectDrawGroup[0] + 1;
75
76
self->updateRange.x = 16 * MAX(abs(self->subOff1.x), abs(self->subOff2.x));
77
self->updateRange.y = 16 * MAX(abs(self->subOff1.y), abs(self->subOff2.y));
78
79
RSDK.SetSpriteAnimation(Hatch->aniFrames, 0, &self->baseAnimator, true, 0);
80
RSDK.SetSpriteAnimation(Hatch->aniFrames, 2, &self->hatchAnimator, true, 4);
81
82
self->hitbox.left = (self->subOff1.x >> 12) - ((self->position.x >> 16) & 0xF) + 16;
83
self->hitbox.top = (self->subOff1.y >> 12) - ((self->position.y >> 16) & 0xF) + 16;
84
self->hitbox.right = (self->subOff2.x >> 12) - ((self->position.x >> 16) & 0xF) + 16;
85
self->hitbox.bottom = (self->subOff2.y >> 12) - ((self->position.y >> 16) & 0xF) + 16;
86
self->startPos = self->position;
87
88
EntityWarpDoor *warpDoor = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, WarpDoor);
89
if (warpDoor->classID == WarpDoor->classID) {
90
self->hitboxWarpDoor = warpDoor->hitbox;
91
warpDoor->hitbox.left = 0;
92
warpDoor->hitbox.top = 0x7FFF;
93
warpDoor->hitbox.right = 0;
94
warpDoor->hitbox.bottom = 0x7FFF;
95
}
96
97
switch (self->go) {
98
case HATCH_GO_SUBENTRYHATCH_SMOGONLY:
99
if (!OOZSetup->useSmogEffect) {
100
self->vScrollPos += self->depth << 16;
101
self->position.y += self->depth << 16;
102
destroyEntity(RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, WarpDoor));
103
self->useMoveLayer = true;
104
break;
105
}
106
// [[fallthrough]]
107
case HATCH_GO_SUBENTRYHATCH:
108
self->visible = true;
109
self->state = Hatch_State_SubEntryHatch;
110
self->useMoveLayer = true;
111
break;
112
113
case HATCH_GO_SUBEXITHATCH_COPYTILES:
114
// Copies the Hatch Tiles from the move layer
115
RSDK.CopyTileLayer(Zone->moveLayer, (self->position.x >> 20) - 2, (self->position.y >> 20) - 1, Zone->moveLayer, 1, 7, 4,
116
2); // Copy Hatch Tiles
117
RSDK.CopyTileLayer(Zone->fgLayer[1], (self->position.x >> 20) - 3, (self->position.y >> 20) + 1, Zone->moveLayer, 16, 1, 6,
118
2); // Copy [???] Tiles (There's nothing there in the scene)
119
// [[fallthrough]]
120
case HATCH_GO_SUBEXITHATCH_NOCOPY:
121
self->visible = false;
122
self->useMoveLayer = false;
123
self->state = Hatch_State_SubExitHatch;
124
break;
125
126
case HATCH_GO_SUBEXIT:
127
self->visible = false;
128
self->state = Hatch_State_SubExit;
129
self->useMoveLayer = true;
130
break;
131
132
default: break;
133
}
134
}
135
}
136
137
void Hatch_StageLoad(void)
138
{
139
// OOZ1 Check...? why? its never used in OOZ1...
140
if (RSDK.CheckSceneFolder("OOZ1") || RSDK.CheckSceneFolder("OOZ2"))
141
Hatch->aniFrames = RSDK.LoadSpriteAnimation("OOZ/Hatch.bin", SCOPE_STAGE);
142
143
Hatch->hitboxSubExit.left = -16;
144
Hatch->hitboxSubExit.top = -16;
145
Hatch->hitboxSubExit.right = 16;
146
Hatch->hitboxSubExit.bottom = 16;
147
148
Hatch->hitboxRange.left = -128;
149
Hatch->hitboxRange.top = -128;
150
Hatch->hitboxRange.right = 128;
151
Hatch->hitboxRange.bottom = 128;
152
153
Hatch->hitboxL.left = -24;
154
Hatch->hitboxL.top = 0;
155
Hatch->hitboxL.right = -12;
156
Hatch->hitboxL.bottom = 8;
157
158
Hatch->hitboxR.left = 12;
159
Hatch->hitboxR.top = 0;
160
Hatch->hitboxR.right = 24;
161
Hatch->hitboxR.bottom = 8;
162
163
Hatch->hitboxEntry.left = -12;
164
Hatch->hitboxEntry.top = 8;
165
Hatch->hitboxEntry.right = 12;
166
Hatch->hitboxEntry.bottom = 24;
167
168
Hatch->active = ACTIVE_ALWAYS;
169
170
Hatch->sfxHatchOpen = RSDK.GetSfx("OOZ/SubHatchOpen.wav");
171
Hatch->sfxHatchClose = RSDK.GetSfx("OOZ/SubHatchClose.wav");
172
Hatch->sfxDescend = RSDK.GetSfx("OOZ/SubDescend.wav");
173
Hatch->sfxSurface = RSDK.GetSfx("OOZ/SubSurface.wav");
174
Hatch->sfxGasPop = RSDK.GetSfx("OOZ/GasPop.wav");
175
}
176
177
void Hatch_State_SubEntryHatch(void)
178
{
179
RSDK_THIS(Hatch);
180
181
RSDK.ProcessAnimation(&self->hatchAnimator);
182
183
int32 entered = 0;
184
foreach_all(Player, player)
185
{
186
// Bug Details:
187
// this does foreach_all, instead of foreach_active
188
// meaning that even killed players are included
189
// Fix:
190
// this entire block of code should have a Player_CheckValidState call to make sure the player isn't dead or some other "invalid" state
191
192
// Extra notes: if you manage to die and fall into the hatch, you'll be brought back (sorta, this is because it changes your state from
193
// Player_State_Death to Player_State_Static) though the death state stuff will still be applied so you'll be on the highest layer (until its
194
// changed) and the player->active var will be set to ACTIVE_ALWAYS this means you can do really weird stuff such as move during the pause
195
// menu
196
197
if (Player_CheckCollisionBox(player, self, &Hatch->hitboxL) == C_TOP) {
198
entered = 1;
199
}
200
else if (Player_CheckCollisionBox(player, self, &Hatch->hitboxR) == C_TOP) {
201
entered = 1;
202
}
203
else if (Player_CheckCollisionBox(player, self, &Hatch->hitboxEntry) == C_TOP) {
204
if (player->onGround) {
205
if (!player->sidekick) {
206
self->active = ACTIVE_NORMAL;
207
self->playerPtr = player;
208
player->velocity.x = 0;
209
player->velocity.y = 0;
210
player->groundVel = 0;
211
player->nextAirState = StateMachine_None;
212
player->nextGroundState = StateMachine_None;
213
player->interaction = false;
214
215
if (player->animator.animationID != ANI_JUMP)
216
RSDK.PlaySfx(Player->sfxRoll, false, 0xFF);
217
218
RSDK.SetSpriteAnimation(player->aniFrames, ANI_JUMP, &player->animator, false, 0);
219
player->state = Player_State_Static;
220
RSDK.SetSpriteAnimation(Hatch->aniFrames, 2, &self->hatchAnimator, false, 0);
221
self->state = Hatch_State_PlayerEntered;
222
entered = 2;
223
foreach_break;
224
}
225
}
226
else {
227
entered = 1;
228
}
229
}
230
else {
231
if (Player_CheckCollisionTouch(player, self, &Hatch->hitboxRange))
232
entered = 1;
233
}
234
}
235
236
if (entered == 1) {
237
if (self->hatchAnimator.animationID == 2)
238
RSDK.PlaySfx(Hatch->sfxHatchOpen, false, 0xFF);
239
240
RSDK.SetSpriteAnimation(Hatch->aniFrames, 1, &self->hatchAnimator, false, 0);
241
}
242
else if (!entered) {
243
if (self->hatchAnimator.animationID == 1)
244
RSDK.PlaySfx(Hatch->sfxHatchClose, false, 0xFF);
245
246
RSDK.SetSpriteAnimation(Hatch->aniFrames, 2, &self->hatchAnimator, false, 0);
247
}
248
}
249
250
void Hatch_State_PlayerEntered(void)
251
{
252
RSDK_THIS(Hatch);
253
254
RSDK.ProcessAnimation(&self->hatchAnimator);
255
256
if (self->hatchAnimator.frameID == 4) {
257
EntityPlayer *player = self->playerPtr;
258
259
self->visible = false;
260
player->visible = false;
261
player->blinkTimer = 0;
262
player->tileCollisions = TILECOLLISION_NONE;
263
264
RSDK.CopyTileLayer(Zone->moveLayer, (self->position.x >> 20) - 2, (self->position.y >> 20) - 1, Zone->moveLayer, 1, 7, 4, 2);
265
RSDK.CopyTileLayer(Zone->fgLayer[1], (self->position.x >> 20) - 3, (self->position.y >> 20) + 1, Zone->moveLayer, 16, 1, 6, 2);
266
267
Zone->deathBoundary[0] = 0x7FFF0000;
268
Zone->deathBoundary[1] = 0x7FFF0000;
269
Zone->deathBoundary[2] = 0x7FFF0000;
270
Zone->deathBoundary[3] = 0x7FFF0000;
271
272
RSDK.PlaySfx(Hatch->sfxDescend, false, 0xFF);
273
self->state = Hatch_State_Descend;
274
275
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
276
zone->fadeColor = 0;
277
zone->timer = 0;
278
zone->drawGroup = 15;
279
zone->visible = true;
280
zone->stateDraw = Zone_Draw_Fade;
281
}
282
}
283
284
void Hatch_State_Descend(void)
285
{
286
RSDK_THIS(Hatch);
287
288
EntityPlayer *player = self->playerPtr;
289
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
290
291
++self->timer;
292
self->vScrollPos += 0x10000;
293
self->position.y += 0x10000;
294
player->position.y += 0x10000;
295
296
zone->timer += 2;
297
298
if (self->timer >= self->depth) {
299
if (self->dest) { // XOffset of where to surface
300
self->timer = 0;
301
self->state = Hatch_State_MoveToDestPos;
302
}
303
else { // Uses the WarpDoor obj for transport
304
player->state = Player_State_Air;
305
EntityWarpDoor *warpDoor = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, WarpDoor);
306
if (warpDoor->classID == WarpDoor->classID) {
307
Zone->cameraBoundsB[RSDK.GetEntitySlot(player)] = 0x7FFF;
308
warpDoor->hitbox = self->hitboxWarpDoor;
309
warpDoor->position.y = self->position.y;
310
player->tileCollisions = TILECOLLISION_DOWN;
311
player->interaction = true;
312
player->visible = true;
313
}
314
315
RSDK.GetTileLayer(Zone->moveLayer)->scrollPos = 0;
316
317
self->position.x = self->startPos.x;
318
self->position.y = self->startPos.y;
319
320
if (self->go != HATCH_GO_SUBENTRYHATCH_SMOGONLY)
321
self->useMoveLayer = false;
322
323
self->state = Hatch_State_FadeIn;
324
}
325
}
326
}
327
328
void Hatch_State_MoveToDestPos(void)
329
{
330
RSDK_THIS(Hatch);
331
332
EntityPlayer *player = self->playerPtr;
333
334
self->hScrollPos += self->dest << 16;
335
self->position.x += self->dest << 16;
336
337
player->position.x += self->dest << 16;
338
player->camera->position.x += self->dest << 16;
339
340
ScreenInfo[player->camera->screenID].position.x += self->dest;
341
342
self->timer = 0;
343
RSDK.PlaySfx(Hatch->sfxSurface, false, 0xFF);
344
345
self->state = Hatch_State_Surfacing;
346
}
347
348
void Hatch_State_Surfacing(void)
349
{
350
RSDK_THIS(Hatch);
351
352
EntityPlayer *player = self->playerPtr;
353
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
354
355
++self->timer;
356
357
self->vScrollPos -= 0x10000;
358
self->position.y -= 0x10000;
359
player->position.y -= 0x10000;
360
361
zone->timer -= 2;
362
363
if (self->timer >= self->depth) {
364
self->timer = 0;
365
self->visible = true;
366
self->position.x = player->position.x;
367
self->position.y = player->position.y + 0x80000;
368
369
RSDK.SetSpriteAnimation(Hatch->aniFrames, 1, &self->hatchAnimator, false, 0);
370
self->state = Hatch_State_OpenHatchReleasePlayer;
371
}
372
}
373
374
void Hatch_State_OpenHatchReleasePlayer(void)
375
{
376
RSDK_THIS(Hatch);
377
378
EntityPlayer *player = self->playerPtr;
379
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
380
381
RSDK.ProcessAnimation(&self->hatchAnimator);
382
zone->timer -= 2;
383
384
if (++self->timer == 30) {
385
player->state = Player_State_Air;
386
player->velocity.y = -0xA0000;
387
player->onGround = false;
388
player->tileCollisions = TILECOLLISION_DOWN;
389
player->interaction = true;
390
player->applyJumpCap = false;
391
player->visible = true;
392
RSDK.PlaySfx(Hatch->sfxGasPop, false, 255);
393
}
394
else if (self->timer == 38) {
395
RSDK.SetSpriteAnimation(Hatch->aniFrames, 2, &self->hatchAnimator, false, 0);
396
self->state = Hatch_State_CloseHatch;
397
}
398
}
399
400
void Hatch_State_CloseHatch(void)
401
{
402
RSDK_THIS(Hatch);
403
404
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
405
zone->timer -= 2;
406
407
RSDK.ProcessAnimation(&self->hatchAnimator);
408
409
if (self->hatchAnimator.frameID >= self->hatchAnimator.frameCount - 1) {
410
self->visible = false;
411
self->state = Hatch_State_FadeIn;
412
}
413
}
414
415
void Hatch_State_FadeIn(void)
416
{
417
RSDK_THIS(Hatch);
418
419
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
420
421
zone->timer -= 8;
422
if (zone->timer <= 0) {
423
self->active = ACTIVE_BOUNDS;
424
self->state = StateMachine_None;
425
zone->timer = 0;
426
}
427
}
428
429
void Hatch_State_SubExit(void)
430
{
431
RSDK_THIS(Hatch);
432
433
foreach_all(Player, player)
434
{
435
if (Player_CheckCollisionTouch(player, self, &Hatch->hitboxSubExit) && !player->sidekick) {
436
self->active = ACTIVE_NORMAL;
437
self->playerPtr = player;
438
self->stateStore = player->state;
439
self->interactionStore = player->interaction;
440
self->tileColStore = player->tileCollisions;
441
442
player->velocity.x = 0;
443
player->groundVel = 0;
444
player->nextAirState = StateMachine_None;
445
player->nextGroundState = StateMachine_None;
446
player->interaction = false;
447
player->tileCollisions = TILECOLLISION_NONE;
448
player->state = Player_State_Static;
449
450
self->state = Hatch_State_FadeOut;
451
foreach_break;
452
}
453
}
454
}
455
456
void Hatch_State_FadeOut(void)
457
{
458
RSDK_THIS(Hatch);
459
460
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
461
EntityPlayer *player = self->playerPtr;
462
463
zone->timer += 8;
464
if (zone->timer >= 512) {
465
EntityWarpDoor *warpDoor = RSDK_GET_ENTITY(SceneInfo->entitySlot - 1, WarpDoor);
466
467
if (warpDoor->classID == WarpDoor->classID) {
468
Zone->cameraBoundsB[RSDK.GetEntitySlot(player)] = 0x7FFF;
469
470
warpDoor->hitbox = self->hitboxWarpDoor;
471
warpDoor->position.y = player->position.y;
472
473
player->state = self->stateStore;
474
player->tileCollisions = self->tileColStore;
475
player->interaction = self->interactionStore;
476
player->visible = true;
477
}
478
479
if (self->depth) {
480
self->state = Hatch_State_FadeIn;
481
}
482
else {
483
self->active = ACTIVE_BOUNDS;
484
self->state = StateMachine_None;
485
}
486
}
487
}
488
489
void Hatch_State_SubExitHatch(void)
490
{
491
RSDK_THIS(Hatch);
492
493
EntityZone *zone = RSDK_GET_ENTITY(SLOT_ZONE, Zone);
494
495
if (zone->timer) {
496
foreach_all(Player, player)
497
{
498
if (Player_CheckCollisionTouch(player, self, &Hatch->hitboxSubExit) && !player->sidekick) {
499
self->hScrollPos += self->dest << 16;
500
self->vScrollPos += self->depth << 16;
501
self->useMoveLayer = true;
502
503
if (self->go == HATCH_GO_SUBEXITHATCH_NOCOPY) {
504
self->state = StateMachine_None;
505
}
506
else {
507
self->playerPtr = player;
508
self->active = ACTIVE_NORMAL;
509
self->stateStore = player->state;
510
self->interactionStore = player->interaction;
511
self->tileColStore = player->tileCollisions;
512
self->position.x += self->dest << 16;
513
self->velocity.y = player->velocity.y;
514
515
player->camera->position.x += self->dest << 16;
516
player->camera->position.y += self->depth << 16;
517
player->position.x += self->dest << 16;
518
player->position.y += self->depth << 16;
519
player->velocity.y = 0;
520
player->nextAirState = StateMachine_None;
521
player->nextGroundState = StateMachine_None;
522
player->interaction = false;
523
player->tileCollisions = TILECOLLISION_NONE;
524
player->visible = false;
525
player->state = Player_State_Static;
526
527
RSDK.PlaySfx(Hatch->sfxSurface, false, 255);
528
self->state = Hatch_State_Surfacing;
529
}
530
foreach_break;
531
}
532
}
533
}
534
}
535
536
#if GAME_INCLUDE_EDITOR
537
void Hatch_EditorDraw(void)
538
{
539
RSDK_THIS(Hatch);
540
541
self->drawGroup = Zone->objectDrawGroup[0] + 1;
542
543
self->updateRange.x = 16 * MAX(abs(self->subOff1.x), abs(self->subOff2.x));
544
self->updateRange.y = 16 * MAX(abs(self->subOff1.y), abs(self->subOff2.y));
545
546
RSDK.SetSpriteAnimation(Hatch->aniFrames, 0, &self->baseAnimator, false, 0);
547
RSDK.SetSpriteAnimation(Hatch->aniFrames, 2, &self->hatchAnimator, false, 4);
548
549
RSDK.DrawSprite(&self->hatchAnimator, NULL, false);
550
RSDK.DrawSprite(&self->baseAnimator, NULL, false);
551
552
if (showGizmos()) {
553
RSDK_DRAWING_OVERLAY(true);
554
555
DrawHelpers_DrawArrow(self->position.x, self->position.y, self->position.x, self->position.y + (self->depth << 16), 0x00FF00, INK_NONE, 0xFF);
556
557
self->hitbox.left = (self->subOff1.x >> 12) - ((self->position.x >> 16) & 0xF) + 16;
558
self->hitbox.top = (self->subOff1.y >> 12) - ((self->position.y >> 16) & 0xF) + 16;
559
self->hitbox.right = (self->subOff2.x >> 12) - ((self->position.x >> 16) & 0xF) + 16;
560
self->hitbox.bottom = (self->subOff2.y >> 12) - ((self->position.y >> 16) & 0xF) + 16;
561
562
DrawHelpers_DrawHitboxOutline(self->position.x, self->position.y, &self->hitbox, FLIP_NONE, 0xFF0000);
563
564
RSDK_DRAWING_OVERLAY(false);
565
}
566
}
567
568
void Hatch_EditorLoad(void)
569
{
570
Hatch->aniFrames = RSDK.LoadSpriteAnimation("OOZ/Hatch.bin", SCOPE_STAGE);
571
572
RSDK_ACTIVE_VAR(Hatch, go);
573
RSDK_ENUM_VAR("Hatch Entry", HATCH_GO_SUBENTRYHATCH);
574
RSDK_ENUM_VAR("Hatch Exit (Copy hatch tiles)", HATCH_GO_SUBEXITHATCH_COPYTILES);
575
RSDK_ENUM_VAR("Hatch Entry (Only When Smog is active)", HATCH_GO_SUBENTRYHATCH_SMOGONLY);
576
RSDK_ENUM_VAR("Submarine Exit", HATCH_GO_SUBEXIT);
577
RSDK_ENUM_VAR("Hatch Exit (Don't copy hatch tiles)", HATCH_GO_SUBEXITHATCH_NOCOPY);
578
}
579
#endif
580
581
void Hatch_Serialize(void)
582
{
583
RSDK_EDITABLE_VAR(Hatch, VAR_UINT8, go);
584
RSDK_EDITABLE_VAR(Hatch, VAR_VECTOR2, subOff1);
585
RSDK_EDITABLE_VAR(Hatch, VAR_VECTOR2, subOff2);
586
RSDK_EDITABLE_VAR(Hatch, VAR_ENUM, depth);
587
RSDK_EDITABLE_VAR(Hatch, VAR_ENUM, dest);
588
}
589
590