Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/MSZ/DBTower.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: DBTower Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
10
ObjectDBTower *DBTower;
11
12
void DBTower_Update(void)
13
{
14
RSDK_THIS(DBTower);
15
16
StateMachine_Run(self->state);
17
}
18
19
void DBTower_LateUpdate(void) {}
20
21
void DBTower_StaticUpdate(void) {}
22
23
void DBTower_Draw(void)
24
{
25
RSDK_THIS(DBTower);
26
27
if (self->connectedSegmentCount <= 0) {
28
self->bodyAnimator.frameID = self->rotation >> 5;
29
RSDK.DrawSprite(self->segmentAnimators[0], &self->bodyPositions[0], false);
30
}
31
else {
32
self->direction ^= FLIP_X;
33
for (int32 i = self->connectedSegmentCount; i > 0; --i) {
34
self->rotation = (2 * self->bodyAngles[i] - 15) & 0x1E;
35
self->bodyAnimator.frameID = self->bodyAngles[i] >> 4;
36
RSDK.DrawSprite(self->segmentAnimators[i], &self->bodyPositions[i], false);
37
}
38
39
self->direction ^= FLIP_X;
40
self->rotation = 0;
41
42
if (self->invincibilityTimer & 1)
43
RSDK.SetPaletteEntry(0, 160, 0xE0E0E0);
44
45
RSDK.DrawSprite(self->segmentAnimators[0], &self->bodyPositions[0], false);
46
RSDK.SetPaletteEntry(0, 160, 0x200000);
47
}
48
}
49
50
void DBTower_Create(void *data)
51
{
52
RSDK_THIS(DBTower);
53
54
if (!SceneInfo->inEditor) {
55
if (globals->gameMode < MODE_TIMEATTACK) {
56
self->drawFX = FX_ROTATE | FX_FLIP;
57
self->updateRange.x = 0x400000;
58
self->updateRange.y = 0x400000;
59
self->visible = true;
60
61
if (data) {
62
self->active = ACTIVE_NORMAL;
63
self->drawGroup = Zone->objectDrawGroup[0];
64
RSDK.SetSpriteAnimation(DBTower->aniFrames, 2, &self->bodyAnimator, true, 0);
65
self->segmentAnimators[0] = &self->bodyAnimator;
66
self->bodyPositions[0].x = self->position.x;
67
self->bodyPositions[0].y = self->position.y;
68
self->state = (Type_StateMachine)data;
69
}
70
else {
71
self->active = ACTIVE_BOUNDS;
72
self->drawGroup = Zone->objectDrawGroup[0] + 1;
73
self->timer = 0;
74
self->direction = FLIP_X;
75
76
for (int32 i = 0; i < DBTOWER_SEGMENT_COUNT; ++i) {
77
self->segmentAnimators[i] = &self->bodyAnimator;
78
self->segmentUnused3[i] = 0;
79
self->bodyAngles[i] = 0xC0;
80
}
81
self->segmentAnimators[0] = &self->headAnimator;
82
self->bodyPositions[0].x = self->position.x + ((ScreenInfo->center.x - 64) << 16);
83
self->bodyPositions[0].y = self->position.y + 0xA20000;
84
85
self->originPos = self->position;
86
self->health = 6;
87
self->wobbleAngleVel = 640;
88
self->xOffsetAngle = 64;
89
self->connectedSegmentCount = 0;
90
91
RSDK.SetSpriteAnimation(DBTower->aniFrames, 0, &self->headAnimator, true, 0);
92
RSDK.SetSpriteAnimation(DBTower->aniFrames, 2, &self->bodyAnimator, true, 0);
93
94
self->state = DBTower_State_SetupArena;
95
}
96
}
97
else {
98
destroyEntity(self);
99
}
100
}
101
}
102
103
void DBTower_StageLoad(void)
104
{
105
DBTower->aniFrames = RSDK.LoadSpriteAnimation("MSZ/Sandworm.bin", SCOPE_STAGE);
106
107
DBTower->hitboxSegment.left = -27;
108
DBTower->hitboxSegment.top = -27;
109
DBTower->hitboxSegment.right = 27;
110
DBTower->hitboxSegment.bottom = 27;
111
112
DBTower->defeated = false;
113
DBTower->active = ACTIVE_ALWAYS;
114
115
DBTower->sfxRocketJet = RSDK.GetSfx("Stage/RocketJet.wav");
116
DBTower->sfxHit = RSDK.GetSfx("Stage/BossHit.wav");
117
DBTower->sfxExplosion2 = RSDK.GetSfx("Stage/Explosion2.wav");
118
DBTower->sfxExplosion3 = RSDK.GetSfx("Stage/Explosion3.wav");
119
DBTower->sfxBumper3 = RSDK.GetSfx("Stage/Bumper3.wav");
120
DBTower->sfxAssemble = RSDK.GetSfx("Stage/Assemble.wav");
121
DBTower->sfxRocketJet2 = RSDK.GetSfx("Stage/RocketJet.wav");
122
DBTower->sfxRockemSockem = RSDK.GetSfx("Stage/RockemSockem.wav");
123
}
124
125
void DBTower_CheckPlayerCollisions_Head(void)
126
{
127
RSDK_THIS(DBTower);
128
129
if (self->invincibilityTimer > 0) {
130
self->invincibilityTimer--;
131
}
132
else {
133
foreach_active(Player, player)
134
{
135
int32 playerID = RSDK.GetEntitySlot(player);
136
137
if (self->playerTimers[playerID]) {
138
--self->playerTimers[playerID];
139
}
140
else {
141
self->position.x = self->bodyPositions[0].x;
142
self->position.y = self->bodyPositions[0].y;
143
144
if (Player_CheckBadnikTouch(player, self, &DBTower->hitboxSegment) && Player_CheckBossHit(player, self)) {
145
self->wobbleAngleVel = 2048;
146
147
if (--self->health <= 0) {
148
SceneInfo->timeEnabled = false;
149
Player_GiveScore(RSDK_GET_ENTITY(SLOT_PLAYER1, Player), 1000);
150
RSDK.PlaySfx(DBTower->sfxExplosion2, false, 255);
151
152
self->timer = 120;
153
self->state = DBTower_State_Destroyed;
154
}
155
else {
156
self->invincibilityTimer = 48;
157
RSDK.PlaySfx(DBTower->sfxHit, false, 255);
158
}
159
160
foreach_break;
161
}
162
else {
163
for (int32 i = 1; i <= self->connectedSegmentCount; ++i) {
164
self->position.x = self->bodyPositions[i].x;
165
self->position.y = self->bodyPositions[i].y;
166
167
uint8 angle = RSDK.ATan2(player->position.x - self->position.x, player->position.y - self->position.y) - self->bodyAngles[i];
168
if (Player_CheckBadnikTouch(player, self, &DBTower->hitboxSegment)) {
169
if (angle >= 0x80) {
170
#if MANIA_USE_PLUS
171
if (!Player_CheckMightyUnspin(player, 0x300, true, &player->uncurlTimer))
172
#endif
173
Player_Hurt(player, self);
174
}
175
else if (Player_CheckBossHit(player, self)) {
176
RSDK.PlaySfx(DBTower->sfxBumper3, false, 255);
177
self->playerTimers[playerID] = 30;
178
break;
179
}
180
}
181
}
182
}
183
}
184
}
185
}
186
187
self->position.x = self->bodyPositions[0].x;
188
self->position.y = self->bodyPositions[0].y;
189
}
190
191
void DBTower_Explode(void)
192
{
193
RSDK_THIS(DBTower);
194
195
if (!(Zone->timer & 3)) {
196
RSDK.PlaySfx(UberCaterkiller->sfxExplosion2, false, 255);
197
198
if (!(Zone->timer & 7)) {
199
int32 x = self->position.x + (RSDK.Rand(-19, 20) << 16);
200
int32 y = self->position.y + (RSDK.Rand(-24, 25) << 16);
201
EntityExplosion *explosion = CREATE_ENTITY(Explosion, INT_TO_VOID((RSDK.Rand(0, 256) > 192) + EXPLOSION_BOSS), x, y);
202
explosion->drawGroup = Zone->objectDrawGroup[1] + 2;
203
}
204
}
205
}
206
207
void DBTower_State_SetupArena(void)
208
{
209
RSDK_THIS(DBTower);
210
211
if (RSDK_GET_ENTITY(SLOT_PLAYER1, Player)->position.x > self->position.x) {
212
Zone->playerBoundActiveL[0] = true;
213
Zone->playerBoundActiveR[0] = true;
214
Zone->cameraBoundsL[0] = (self->position.x >> 16) - ScreenInfo->center.x;
215
Zone->cameraBoundsR[0] = (self->position.x >> 16) + ScreenInfo->center.x;
216
217
self->active = ACTIVE_NORMAL;
218
self->timer = 0;
219
self->position.x += (ScreenInfo->center.x - 64) << 16;
220
221
self->originPos.x = self->position.x;
222
self->originPos.y += 0xA20000;
223
224
self->bodyPositions[0].x = self->position.x;
225
self->bodyPositions[0].y = self->originPos.y;
226
for (int32 i = 1; i < DBTOWER_SEGMENT_COUNT; ++i) {
227
self->bodyPositions[i].x = self->originPos.x;
228
self->bodyPositions[i].y = 0x7FFF0000;
229
}
230
231
Music_TransitionTrack(TRACK_MINIBOSS, 0.0125);
232
233
#if MANIA_USE_PLUS
234
if (SceneInfo->filter == (FILTER_BOTH | FILTER_ENCORE)) {
235
self->timer = 60;
236
self->state = DBTower_State_Setup_Encore;
237
}
238
else {
239
#endif
240
RSDK.PlaySfx(DBTower->sfxAssemble, false, 255);
241
self->bodyPositions[++self->connectedSegmentCount].y = 0x7FFF0000;
242
self->segmentOffsetY += 0x360000;
243
self->timer = 0;
244
self->state = DBTower_State_HandleBoss;
245
#if MANIA_USE_PLUS
246
}
247
#endif
248
}
249
}
250
251
#if MANIA_USE_PLUS
252
void DBTower_State_Setup_Encore(void)
253
{
254
RSDK_THIS(DBTower);
255
256
if (--self->timer <= 0) {
257
RSDK.PlaySfx(DBTower->sfxAssemble, false, 255);
258
self->bodyPositions[++self->connectedSegmentCount].y = 0x7FFF0000;
259
self->segmentOffsetY += 0x360000;
260
self->timer = 0;
261
self->state = DBTower_State_HandleBoss;
262
}
263
}
264
#endif
265
266
void DBTower_State_HandleBoss(void)
267
{
268
RSDK_THIS(DBTower);
269
270
++self->xOffsetAngle;
271
self->angle = (RSDK.Sin256(self->wobbleAngle >> 8) >> 5) + 0xC0;
272
273
if (self->headAnimator.frameID != 0)
274
RSDK.ProcessAnimation(&self->headAnimator);
275
276
int32 x = 0x3600 * ((DBTOWER_SEGMENT_COUNT - 1) - self->connectedSegmentCount) * RSDK.Cos256(self->angle);
277
self->bodyPositions[self->connectedSegmentCount].x = self->originPos.x + x + (RSDK.Cos256(self->xOffsetAngle) << 12);
278
self->bodyPositions[self->connectedSegmentCount].y = self->originPos.y + self->segmentOffsetY;
279
280
for (int32 i = self->connectedSegmentCount - 1; i >= 0; --i) {
281
self->bodyPositions[i].x = 0x3600 * RSDK.Cos256(self->angle) + self->bodyPositions[i + 1].x;
282
self->bodyPositions[i].y = 0x3600 * RSDK.Sin256(self->angle) + self->bodyPositions[i + 1].y;
283
}
284
285
self->wobbleAngle += self->wobbleAngleVel;
286
287
if (self->wobbleAngleVel > 640) {
288
self->wobbleAngleVel -= 8;
289
RSDK.ProcessAnimation(&self->headAnimator);
290
}
291
292
if (self->segmentOffsetY < 0) {
293
self->segmentOffsetY += self->velocity.y;
294
if (self->segmentOffsetY < 0) {
295
self->velocity.y += 0x2800;
296
}
297
else {
298
self->segmentOffsetY = 0;
299
self->velocity.y = 0;
300
}
301
}
302
else if (self->segmentOffsetY <= 0) {
303
if (++self->timer >= 120 || !self->connectedSegmentCount) {
304
self->timer = 0;
305
if (self->connectedSegmentCount > 0) {
306
RSDK.PlaySfx(DBTower->sfxRocketJet2, false, 255);
307
308
EntityDBTower *child = NULL;
309
if (!(self->connectedSegmentCount & 1)) {
310
child = CREATE_ENTITY(DBTower, DBTower_State_BodyRolling, self->bodyPositions[self->connectedSegmentCount].x,
311
self->bodyPositions[self->connectedSegmentCount].y);
312
}
313
else {
314
child = CREATE_ENTITY(DBTower, DBTower_State_BodyBouncing, self->bodyPositions[self->connectedSegmentCount].x,
315
self->bodyPositions[self->connectedSegmentCount].y);
316
child->velocity.x = -0x20000;
317
child->velocity.y = -0x80000;
318
}
319
child->bodyAngles[0] = self->bodyAngles[self->connectedSegmentCount];
320
child->rotation = 2 * self->bodyAngles[self->connectedSegmentCount];
321
}
322
323
if (!(--self->connectedSegmentCount & 0x80)) {
324
self->segmentOffsetY -= 0x360000;
325
}
326
else {
327
RSDK.PlaySfx(DBTower->sfxAssemble, false, 255);
328
self->connectedSegmentCount += 2;
329
self->bodyPositions[self->connectedSegmentCount].y = 0x7FFF0000;
330
self->segmentOffsetY += 0x360000;
331
}
332
}
333
}
334
else {
335
self->segmentOffsetY -= 0x10000;
336
337
if (self->segmentOffsetY <= 0) {
338
if (self->connectedSegmentCount < DBTOWER_SEGMENT_COUNT - 1) {
339
self->connectedSegmentCount++;
340
self->bodyPositions[self->connectedSegmentCount].y = 0x7FFF0000;
341
self->segmentOffsetY += 0x360000;
342
}
343
}
344
}
345
DBTower_CheckPlayerCollisions_Head();
346
}
347
348
void DBTower_State_Destroyed(void)
349
{
350
RSDK_THIS(DBTower);
351
352
DBTower_Explode();
353
354
if (--self->timer <= 0) {
355
EntityFXFade *fxFade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
356
fxFade->speedIn = 256;
357
fxFade->speedOut = 32;
358
RSDK.PlaySfx(DBTower->sfxExplosion3, false, 255);
359
360
for (int32 i = 1; i < DBTOWER_SEGMENT_COUNT; ++i) {
361
EntityDebris *debris = CREATE_ENTITY(Debris, Debris_State_FallAndFlicker, self->bodyPositions[i].x, self->bodyPositions[i].y);
362
RSDK.SetSpriteAnimation(DBTower->aniFrames, self->segmentAnimators[i]->animationID, &debris->animator, true,
363
self->segmentAnimators[i]->frameID);
364
debris->velocity.x = 4 * RSDK.Rand(-0x20000, 0x20000);
365
debris->velocity.y = 4 * RSDK.Rand(-0x20000, -0x10000);
366
debris->gravityStrength = 0x4800;
367
debris->drawGroup = Zone->objectDrawGroup[1];
368
debris->updateRange.x = 0x400000;
369
debris->updateRange.y = 0x400000;
370
self->bodyPositions[i].x = -0x800000;
371
self->bodyPositions[i].y = -0x800000;
372
}
373
374
self->velocity.y = -0x40000;
375
self->state = DBTower_State_Finish;
376
}
377
}
378
379
void DBTower_State_Finish(void)
380
{
381
RSDK_THIS(DBTower);
382
383
DBTower_Explode();
384
385
self->position.y += self->velocity.y;
386
self->velocity.y += 0x3800;
387
388
self->bodyPositions[0].y = self->position.y;
389
390
if (!RSDK.CheckOnScreen(self, &self->updateRange)) {
391
DBTower->defeated = true;
392
Music_TransitionTrack(TRACK_STAGE, 0.0125);
393
394
#if MANIA_USE_PLUS
395
if (SceneInfo->filter == (FILTER_BOTH | FILTER_ENCORE)) {
396
self->timer = 0;
397
self->state = DBTower_State_SpawnSignPost;
398
}
399
else {
400
#endif
401
Zone->cameraBoundsR[0] += 848;
402
Zone->playerBoundActiveR[0] = false;
403
Zone->cameraBoundsB[0] = 304;
404
destroyEntity(self);
405
#if MANIA_USE_PLUS
406
}
407
#endif
408
}
409
}
410
411
#if MANIA_USE_PLUS
412
void DBTower_State_SpawnSignPost(void)
413
{
414
RSDK_THIS(DBTower);
415
416
if (++self->timer == 48) {
417
foreach_all(SignPost, signPost)
418
{
419
signPost->position.x = self->position.x;
420
signPost->state = SignPost_State_Falling;
421
RSDK.PlaySfx(SignPost->sfxTwinkle, false, 255);
422
}
423
destroyEntity(self);
424
}
425
}
426
#endif
427
428
void DBTower_CheckPlayerCollisions_Body(void)
429
{
430
RSDK_THIS(DBTower);
431
432
foreach_active(Player, player)
433
{
434
int32 playerID = RSDK.GetEntitySlot(player);
435
436
if (self->playerTimers[playerID]) {
437
self->playerTimers[playerID]--;
438
}
439
else {
440
uint8 angle = (RSDK.ATan2(player->position.x - self->position.x, player->position.y - self->position.y) - self->bodyAngles[0]);
441
if (Player_CheckBadnikTouch(player, self, &DBTower->hitboxSegment)) {
442
if (angle >= 0x80) {
443
#if MANIA_USE_PLUS
444
if (Player_CheckMightyUnspin(player, 0x300, 2, &player->uncurlTimer))
445
self->playerTimers[playerID] = 30;
446
else
447
#endif
448
Player_Hurt(player, self);
449
}
450
else if (
451
#if MANIA_USE_PLUS
452
(player->characterID == ID_MIGHTY && player->animator.animationID == ANI_CROUCH) ||
453
#endif
454
Player_CheckBossHit(player, self)) {
455
RSDK.PlaySfx(DBTower->sfxBumper3, false, 255);
456
self->playerTimers[playerID] = 30;
457
foreach_break;
458
}
459
}
460
}
461
}
462
}
463
464
void DBTower_State_BodyBouncing(void)
465
{
466
RSDK_THIS(DBTower);
467
468
self->position.x += self->velocity.x;
469
self->position.y += self->velocity.y;
470
self->velocity.y += 0x3800;
471
472
if (RSDK.ObjectTileCollision(self, Zone->collisionLayers, CMODE_FLOOR, 0, 0, 0x1B0000, true)) {
473
RSDK.PlaySfx(DBTower->sfxRockemSockem, false, 255);
474
self->velocity.y = -0x80000;
475
}
476
477
self->bodyPositions[0] = self->position;
478
self->bodyAngles[0] = (self->bodyAngles[0] - 8) & 0xFF;
479
self->rotation = self->bodyAngles[0] << 1;
480
481
if (RSDK.CheckOnScreen(self, &self->updateRange))
482
DBTower_CheckPlayerCollisions_Body();
483
else
484
destroyEntity(self);
485
}
486
487
void DBTower_State_BodyRolling(void)
488
{
489
RSDK_THIS(DBTower);
490
491
self->position.x += self->velocity.x;
492
self->velocity.x -= 0x2800;
493
494
self->bodyPositions[0] = self->position;
495
self->bodyPositions[0] = self->position;
496
self->bodyAngles[0] = (self->bodyAngles[0] - 8) & 0xFF;
497
self->rotation = self->bodyAngles[0] << 1;
498
499
if (RSDK.CheckOnScreen(self, &self->updateRange))
500
DBTower_CheckPlayerCollisions_Body();
501
else
502
destroyEntity(self);
503
}
504
505
#if GAME_INCLUDE_EDITOR
506
void DBTower_EditorDraw(void)
507
{
508
RSDK_THIS(DBTower);
509
510
RSDK.SetSpriteAnimation(DBTower->aniFrames, 0, &self->headAnimator, true, 0);
511
RSDK.DrawSprite(&self->headAnimator, NULL, false);
512
513
if (showGizmos()) {
514
RSDK_DRAWING_OVERLAY(true);
515
516
DrawHelpers_DrawArenaBounds(-WIDE_SCR_XCENTER, -SCREEN_YCENTER, WIDE_SCR_XCENTER, SCREEN_YCENTER, 1 | 0 | 4 | 0, 0x00C0F0);
517
518
RSDK_DRAWING_OVERLAY(false);
519
}
520
}
521
522
void DBTower_EditorLoad(void) { DBTower->aniFrames = RSDK.LoadSpriteAnimation("MSZ/Sandworm.bin", SCOPE_STAGE); }
523
#endif
524
525
void DBTower_Serialize(void) {}
526
527