Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/HCZ/DCEvent.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: DCEvent Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectDCEvent *DCEvent;
11
12
void DCEvent_Update(void)
13
{
14
RSDK_THIS(DCEvent);
15
16
StateMachine_Run(self->state);
17
}
18
19
void DCEvent_LateUpdate(void) {}
20
21
void DCEvent_StaticUpdate(void) {}
22
23
void DCEvent_Draw(void)
24
{
25
RSDK_THIS(DCEvent);
26
27
RSDK.DrawSprite(&self->animator, NULL, false);
28
}
29
30
void DCEvent_Create(void *data)
31
{
32
RSDK_THIS(DCEvent);
33
34
self->drawFX = FX_FLIP;
35
if (!SceneInfo->inEditor) {
36
self->visible = true;
37
38
int32 type = VOID_TO_INT(data);
39
if (self->type == DCEVENT_BUBBLE)
40
type = DCEVENT_BUBBLE;
41
42
switch (type) {
43
case DCEVENT_EGGMAN:
44
self->visible = false;
45
self->drawGroup = Zone->playerDrawGroup[0] + 2;
46
self->updateRange.x = 0x800000;
47
self->updateRange.y = 0x800000;
48
self->startY = self->position.y;
49
self->remainingBombs = 2;
50
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 0, &self->animator, true, 0);
51
52
if (self->type == DCEVENT_EGGMAN_SWIMMING) {
53
self->active = ACTIVE_BOUNDS;
54
self->state = DCEvent_StateEggmanSwim_AwaitPlayer;
55
}
56
else {
57
self->active = ACTIVE_XBOUNDS;
58
self->state = DCEvent_StateEggmanBomber_AwaitPlayer;
59
}
60
break;
61
62
case DCEVENT_BOMB:
63
self->active = ACTIVE_NORMAL;
64
self->drawGroup = Zone->playerDrawGroup[0] + 1;
65
self->timer = 480;
66
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 5, &self->animator, true, 0);
67
self->state = DCEvent_State_Bomb;
68
break;
69
70
case DCEVENT_BUBBLE: {
71
EntityWater *water = (EntityWater *)self;
72
int32 x = self->position.x;
73
int32 y = self->position.y;
74
RSDK.ResetEntity(water, Water->classID, INT_TO_VOID(WATER_BUBBLE));
75
76
water->position.x = x;
77
water->bubbleX = x;
78
water->position.y = y;
79
water->childPtr = 0;
80
water->speed = -1;
81
RSDK.SetSpriteAnimation(Water->aniFrames, 3, &water->animator, true, 0);
82
break;
83
}
84
}
85
}
86
}
87
88
void DCEvent_StageLoad(void)
89
{
90
if (RSDK.CheckSceneFolder("HCZ")) {
91
DCEvent->aniFrames = RSDK.LoadSpriteAnimation("HCZ/DiveEggman.bin", SCOPE_STAGE);
92
// Never actually used
93
DCEvent->eggmanFrames = RSDK.LoadSpriteAnimation("Eggman/EggmanHCZ1.bin", SCOPE_STAGE);
94
}
95
96
DCEvent->canExplodeBombs = false;
97
98
// Both unused, who knows what they could've been used for tbh
99
// Maybe bomb hitboxes???
100
DCEvent->unusedHitbox1.left = -8;
101
DCEvent->unusedHitbox1.top = -8;
102
DCEvent->unusedHitbox1.right = 8;
103
DCEvent->unusedHitbox1.bottom = 8;
104
105
// This is one's educated guess based on the fact that HCZ/DiveEggman is very similary laid out to this object
106
// Still unused tho
107
DCEvent->hitboxBomb.left = -8;
108
DCEvent->hitboxBomb.top = -8;
109
DCEvent->hitboxBomb.right = 8;
110
DCEvent->hitboxBomb.bottom = 8;
111
112
DCEvent->sfxExplosion = RSDK.GetSfx("Stage/Explosion2.wav");
113
DCEvent->sfxRumble = RSDK.GetSfx("Stage/Rumble.wav");
114
DCEvent->sfxImpact6 = RSDK.GetSfx("Stage/Impact6.wav");
115
DCEvent->sfxImpact4 = RSDK.GetSfx("Stage/Impact4.wav");
116
}
117
118
void DCEvent_State_Collapse(void)
119
{
120
RSDK_THIS(DCEvent);
121
122
TileLayer *move = RSDK.GetTileLayer(Zone->moveLayer);
123
move->scrollPos -= 0x8000;
124
125
foreach_active(Player, player)
126
{
127
player->collisionLayers |= Zone->moveLayerMask;
128
player->moveLayerPosition.x = move->scrollInfo[0].scrollPos;
129
player->moveLayerPosition.y = move->scrollPos;
130
}
131
132
int32 slot = SceneInfo->entitySlot + 1;
133
for (int32 i = 0; i < self->numChildren; ++i) {
134
Entity *child = RSDK_GET_ENTITY_GEN(slot + i);
135
child->position.y += 0x8000;
136
}
137
138
if (!(Zone->timer & 3))
139
Camera_ShakeScreen(0, 0, 2);
140
141
if (!(Zone->timer & 7))
142
RSDK.PlaySfx(DCEvent->sfxRumble, false, 255);
143
144
if (++self->timer >= 2176 || DCEvent->canExplodeBombs) {
145
RSDK.PlaySfx(DCEvent->sfxImpact4, false, 255);
146
destroyEntity(self);
147
}
148
}
149
150
void DCEvent_StateEggmanBomber_AwaitPlayer(void)
151
{
152
RSDK_THIS(DCEvent);
153
154
RSDK.ProcessAnimation(&self->animator);
155
156
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
157
158
if (player1->position.x > self->position.x - 0x100000)
159
player1->stateInput = DCEvent_Input_MoveRight;
160
161
if (player1->position.x > self->position.x) {
162
player1->velocity.x = 0;
163
player1->velocity.y = 0;
164
player1->groundVel = 0;
165
player1->nextAirState = StateMachine_None;
166
player1->nextGroundState = StateMachine_None;
167
player1->stateInput = DCEvent_Input_LookDown;
168
Music_TransitionTrack(TRACK_MINIBOSS, 0.0125);
169
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 1, &self->animator, true, 0);
170
171
TileLayer *moveLayer = RSDK.GetTileLayer(Zone->moveLayer);
172
moveLayer->drawGroup[0] = 6;
173
174
self->position.x -= 0x1000000;
175
self->timer = 172;
176
self->direction = FLIP_X;
177
self->velocity.x = 0x10000;
178
self->active = ACTIVE_NORMAL;
179
self->visible = true;
180
self->state = DCEvent_StateEggmanBomber_WaitForLookDown;
181
}
182
}
183
184
void DCEvent_Input_MoveRight(void)
185
{
186
RSDK_THIS(Player);
187
188
Player_Input_P1();
189
self->up = false;
190
self->down = false;
191
self->left = false;
192
self->right = true;
193
self->jumpPress = false;
194
self->jumpHold = false;
195
}
196
197
void DCEvent_Input_LookDown(void)
198
{
199
RSDK_THIS(Player);
200
201
Player_Input_P1();
202
self->drownTimer = 0;
203
self->up = false;
204
self->down = true;
205
self->left = false;
206
self->right = false;
207
self->jumpPress = false;
208
self->jumpHold = false;
209
210
if (self->onGround)
211
RSDK.SetSpriteAnimation(self->aniFrames, ANI_CROUCH, &self->animator, false, 1);
212
}
213
214
void DCEvent_StateEggmanBomber_WaitForLookDown(void)
215
{
216
RSDK_THIS(DCEvent);
217
218
EntityCamera *camera = RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->camera;
219
if (camera->lookPos.y >= 96)
220
self->state = DCEvent_StateEggmanBomber_Swimming;
221
}
222
223
void DCEvent_StateEggmanBomber_Swimming(void)
224
{
225
RSDK_THIS(DCEvent);
226
227
RSDK.ProcessAnimation(&self->animator);
228
229
self->position.x += self->velocity.x;
230
231
if (self->velocity.y > 0) {
232
self->position.y += self->velocity.y;
233
self->velocity.y -= 0x400;
234
}
235
236
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_LWALL, 0, 0x200000, 0, true)
237
|| RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_RWALL, 0, -0x200000, 0, true)) {
238
self->direction ^= FLIP_X;
239
self->velocity.x = -self->velocity.x;
240
}
241
242
if (--self->timer <= 0) {
243
switch (self->remainingBombs) {
244
case 0:
245
DCEvent->canExplodeBombs = true;
246
self->state = DCEvent_StateEggmanBomber_PlacedAllBombs;
247
break;
248
249
case 1:
250
self->remainingBombs--;
251
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 4, &self->animator, false, 0);
252
self->state = DCEvent_StateEggmanBomber_PlaceBomb;
253
break;
254
255
case 2:
256
self->direction = FLIP_NONE;
257
self->remainingBombs--;
258
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 4, &self->animator, false, 0);
259
self->state = DCEvent_StateEggmanBomber_PlaceBomb;
260
break;
261
}
262
}
263
}
264
265
void DCEvent_StateEggmanBomber_PlaceBomb(void)
266
{
267
RSDK_THIS(DCEvent);
268
269
RSDK.ProcessAnimation(&self->animator);
270
271
if (self->animator.frameID >= self->animator.frameCount - 1) {
272
EntityDCEvent *bomb = CREATE_ENTITY(DCEvent, INT_TO_VOID(DCEVENT_BOMB), self->position.x, self->position.y + 0x20000);
273
if (self->direction)
274
bomb->position.x += 0x1A0000;
275
else
276
bomb->position.x -= 0x1A0000;
277
278
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 1, &self->animator, false, 0);
279
280
if (self->remainingBombs == 1)
281
self->direction = FLIP_X;
282
283
self->timer = 160;
284
self->state = DCEvent_StateEggmanBomber_Swimming;
285
}
286
}
287
288
void DCEvent_StateEggmanBomber_PlacedAllBombs(void)
289
{
290
RSDK_THIS(DCEvent);
291
292
if (!DCEvent->canExplodeBombs) {
293
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
294
player1->stateInput = Player_Input_P1;
295
296
Water->targetWaterLevel = 0x8DC0000;
297
Water->waterMoveSpeed = 0x8000;
298
Music_TransitionTrack(TRACK_STAGE, 0.0125);
299
self->state = DCEvent_State_Collapse;
300
}
301
}
302
303
void DCEvent_StateEggmanSwim_AwaitPlayer(void)
304
{
305
RSDK_THIS(DCEvent);
306
307
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
308
309
RSDK.ProcessAnimation(&self->animator);
310
311
if (player1->position.y < self->position.y) {
312
RSDK.SetSpriteAnimation(DCEvent->aniFrames, 1, &self->animator, true, 0);
313
self->timer = 900;
314
self->direction = FLIP_X;
315
self->velocity.y = -0x8000;
316
self->active = ACTIVE_NORMAL;
317
self->visible = true;
318
self->state = DCEvent_StateEggmanSwim_Swimming;
319
}
320
}
321
322
void DCEvent_StateEggmanSwim_Swimming(void)
323
{
324
RSDK_THIS(DCEvent);
325
326
RSDK.ProcessAnimation(&self->animator);
327
328
self->position.y += self->velocity.y;
329
if (--self->timer <= 0)
330
destroyEntity(self);
331
}
332
333
void DCEvent_State_Bomb(void)
334
{
335
RSDK_THIS(DCEvent);
336
337
if (self->velocity.x) {
338
if (self->velocity.x <= 0)
339
self->velocity.x += 0x2000;
340
else
341
self->velocity.x -= 0x2000;
342
}
343
344
if (self->velocity.y > 0) {
345
self->position.y += self->velocity.y;
346
self->velocity.y -= 0x400;
347
}
348
349
RSDK.ProcessAnimation(&self->animator);
350
351
if (DCEvent->canExplodeBombs) {
352
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), self->position.x, self->position.y)->drawGroup = Zone->objectDrawGroup[1];
353
RSDK.PlaySfx(DCEvent->sfxImpact6, false, 255);
354
RSDK.PlaySfx(DCEvent->sfxExplosion, false, 255);
355
Camera_ShakeScreen(0, 4, 0);
356
357
EntityWater *water = CREATE_ENTITY(Water, INT_TO_VOID(WATER_BUBBLE), self->position.x, self->position.y);
358
water->velocity.y = -0x8800;
359
water->childPtr = 0;
360
water->angle = 2 * RSDK.Rand(0, 256);
361
water->bubbleX = water->position.x;
362
RSDK.SetSpriteAnimation(Water->aniFrames, 3, &water->animator, true, 0);
363
364
EntityPlayer *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, Player);
365
if (self->position.x >= player1->position.x)
366
self->position.x += 0x260000;
367
else
368
self->position.x -= 0x260000;
369
370
// Spawn Bricks
371
for (int32 i = 0; i < 8; ++i) {
372
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_Fall, self->position.x, self->position.y);
373
374
RSDK.SetSpriteAnimation(WaterGush->aniFrames, 4, &debris->animator, true, 0);
375
debris->position.x += RSDK.Rand(-8, 8) << 16;
376
debris->position.y += RSDK.Rand(-8, 8) << 16;
377
debris->velocity.x = RSDK.Rand(-8, 8) << 16;
378
debris->velocity.y = RSDK.Rand(-8, 8) << 16;
379
if (self->position.x >= player1->position.x)
380
debris->velocity.x = RSDK.Rand(-8, 0) << 15;
381
else
382
debris->velocity.x = RSDK.Rand(1, 9) << 15;
383
debris->velocity.y = RSDK.Rand(-8, 5) << 16;
384
debris->direction = RSDK.Rand(0, 4);
385
debris->drawFX = FX_FLIP;
386
debris->drawGroup = Zone->objectDrawGroup[1];
387
debris->gravityStrength = 0x3800;
388
389
RSDK.CopyTileLayer(Zone->moveLayer, 1038, 146, Zone->moveLayer, 1068, 8, 20, 6);
390
}
391
392
self->state = DCEvent_State_BombExplode;
393
}
394
}
395
396
void DCEvent_State_BombExplode(void)
397
{
398
RSDK_THIS(DCEvent);
399
400
if (!(Zone->timer & 3)) {
401
self->position.y -= 0x100000;
402
CREATE_ENTITY(Explosion, INT_TO_VOID(EXPLOSION_BOSS), (RSDK.Rand(-16, 17) << 17) + self->position.x, self->position.y)->drawGroup =
403
Zone->objectDrawGroup[1];
404
405
RSDK.PlaySfx(DCEvent->sfxExplosion, false, 255);
406
407
// a bit hacky but sure
408
if (++self->remainingBombs == 16) {
409
DCEvent->canExplodeBombs = false;
410
destroyEntity(self);
411
}
412
}
413
}
414
415
#if GAME_INCLUDE_EDITOR
416
void DCEvent_EditorDraw(void)
417
{
418
RSDK_THIS(DCEvent);
419
420
switch (self->type) {
421
case DCEVENT_EGGMAN_PLACEBOMB:
422
case DCEVENT_EGGMAN_SWIMMING: RSDK.SetSpriteAnimation(DCEvent->aniFrames, 0, &self->animator, true, 0); break;
423
424
case DCEVENT_BUBBLE:
425
if (Water)
426
RSDK.SetSpriteAnimation(Water->aniFrames, 3, &self->animator, true, 0);
427
break;
428
}
429
430
DCEvent_Draw();
431
432
if (showGizmos() && self->type == DCEVENT_EGGMAN_PLACEBOMB) {
433
RSDK_DRAWING_OVERLAY(true);
434
435
int32 slot = SceneInfo->entitySlot + 1;
436
for (int32 i = 0; i < self->numChildren; ++i) {
437
Entity *child = RSDK_GET_ENTITY_GEN(slot + i);
438
if (!child)
439
continue;
440
441
DrawHelpers_DrawArrow(self->position.x, self->position.y, child->position.x, child->position.y, 0xFFFF00, INK_NONE, 0xFF);
442
}
443
444
RSDK_DRAWING_OVERLAY(false);
445
}
446
}
447
448
void DCEvent_EditorLoad(void)
449
{
450
DCEvent->aniFrames = RSDK.LoadSpriteAnimation("HCZ/DiveEggman.bin", SCOPE_STAGE);
451
452
RSDK_ACTIVE_VAR(DCEvent, type);
453
RSDK_ENUM_VAR("Placing Bombs", DCEVENT_EGGMAN_PLACEBOMB);
454
RSDK_ENUM_VAR("Swimming Upwards", DCEVENT_EGGMAN_SWIMMING);
455
RSDK_ENUM_VAR("Bubble", DCEVENT_BUBBLE);
456
}
457
#endif
458
459
void DCEvent_Serialize(void)
460
{
461
RSDK_EDITABLE_VAR(DCEvent, VAR_UINT8, numChildren);
462
RSDK_EDITABLE_VAR(DCEvent, VAR_UINT8, type);
463
}
464
465