Book a Demo!
CoCalc Logo Icon
StoreFeaturesDocsShareSupportNewsAboutPoliciesSign UpSign In
rubberduckycooly
GitHub Repository: rubberduckycooly/Sonic-Mania-Decompilation
Path: blob/master/SonicMania/Objects/BSS/BSS_Setup.c
338 views
1
// ---------------------------------------------------------------------
2
// RSDK Project: Sonic Mania
3
// Object Description: BSS_Setup Object
4
// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges
5
// Decompiled by: Rubberduckycooly & RMGRich
6
// ---------------------------------------------------------------------
7
8
#include "Game.h"
9
#include <time.h>
10
11
ObjectBSS_Setup *BSS_Setup;
12
13
void BSS_Setup_Update(void)
14
{
15
RSDK_THIS(BSS_Setup);
16
17
StateMachine_Run(self->state);
18
19
ScreenInfo->position.x = 0x100 - ScreenInfo->center.x;
20
21
if (self->palettePage) {
22
RSDK.CopyPalette(2, 16 * self->paletteLine, 0, 128, 16);
23
RSDK.CopyPalette(1, 16 * self->paletteLine, 0, 144, 16);
24
}
25
else {
26
RSDK.CopyPalette(1, 16 * self->paletteLine, 0, 128, 16);
27
RSDK.CopyPalette(2, 16 * self->paletteLine, 0, 144, 16);
28
}
29
30
#if MANIA_USE_PLUS
31
EntityMenuParam *param = MenuParam_GetParam();
32
if (param->bssSelection == BSS_SELECTION_NONE && !self->stopMovement && globals->gameMode < MODE_TIMEATTACK)
33
++SaveGame_GetSaveRAM()->zoneTimes[28];
34
#endif
35
}
36
37
void BSS_Setup_LateUpdate(void) {}
38
39
void BSS_Setup_StaticUpdate(void) {}
40
41
void BSS_Setup_Draw(void)
42
{
43
RSDK_THIS(BSS_Setup);
44
45
TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);
46
self->inkEffect = INK_NONE;
47
if (globe->drawGroup[0] == DRAWGROUP_COUNT)
48
RSDK.DrawSprite(&self->globeSpinAnimator, NULL, false);
49
50
Vector2 drawPos;
51
self->inkEffect = INK_BLEND;
52
drawPos.x = self->position.x;
53
drawPos.y = TO_FIXED(158);
54
RSDK.DrawSprite(&self->shadowAnimator, &drawPos, false);
55
}
56
57
void BSS_Setup_Create(void *data)
58
{
59
RSDK_THIS(BSS_Setup);
60
61
if (!SceneInfo->inEditor) {
62
self->active = ACTIVE_BOUNDS;
63
self->visible = true;
64
self->drawGroup = 2;
65
self->drawFX = FX_FLIP;
66
self->position.x = TO_FIXED(256);
67
self->updateRange.x = TO_FIXED(128);
68
self->updateRange.y = TO_FIXED(128);
69
self->speedupInterval = 30 * 60; // speed up every 30 seconds
70
self->stopMovement = false;
71
72
RSDK.SetSpriteAnimation(BSS_Setup->globeFrames, 0, &self->globeSpinAnimator, true, 0);
73
RSDK.SetSpriteAnimation(BSS_Setup->globeFrames, 1, &self->shadowAnimator, true, 0);
74
75
BSS_Setup_GetStartupInfo();
76
77
self->state = BSS_Setup_State_GlobeMoveZ;
78
}
79
}
80
81
void BSS_Setup_StageLoad(void)
82
{
83
BSS_Setup->globeFrames = RSDK.LoadSpriteAnimation("SpecialBS/Globe.bin", SCOPE_STAGE);
84
85
BSS_Setup->bgLayer = RSDK.GetTileLayerID("Background");
86
BSS_Setup->globeLayer = RSDK.GetTileLayerID("Globe");
87
BSS_Setup->frustum1Layer = RSDK.GetTileLayerID("Frustum 1");
88
BSS_Setup->frustum2Layer = RSDK.GetTileLayerID("Frustum 2");
89
BSS_Setup->playFieldLayer = RSDK.GetTileLayerID("Playfield");
90
BSS_Setup->ringCountLayer = RSDK.GetTileLayerID("Ring Count");
91
92
BSS_Setup_SetupFrustum();
93
BSS_Setup->ringCount = 0;
94
95
TileLayer *playField = RSDK.GetTileLayer(BSS_Setup->playFieldLayer);
96
97
memset(BSS_Setup->playField, 0, sizeof(BSS_Setup->playField));
98
memset(BSS_Setup->sphereChainTable, 0, sizeof(BSS_Setup->sphereChainTable));
99
memset(BSS_Setup->sphereCollectedTable, 0, sizeof(BSS_Setup->sphereCollectedTable));
100
101
if (playField->width <= BSS_PLAYFIELD_W) {
102
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
103
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
104
uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, x, y);
105
106
int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;
107
BSS_Setup->playField[playFieldPos] = tile & 0x3FF;
108
if (BSS_Setup->playField[playFieldPos] > 24)
109
BSS_Setup->playField[playFieldPos] = BSS_NONE;
110
111
if ((RSDK.GetTile(BSS_Setup->ringCountLayer, x, y) & 0x3FF) == BSS_RING)
112
++BSS_Setup->ringCount;
113
}
114
}
115
}
116
else {
117
int32 max = (playField->width >> 4) * ((playField->height - 32) >> 4);
118
int32 key = (int32)time(0);
119
120
// Randomly generate a random number (same format as the random numbers from the S3 blue spheres game)
121
BSS_Setup->randomNumbers[0] = RSDK.RandSeeded(0, max, &key);
122
BSS_Setup->randomNumbers[1] = RSDK.RandSeeded(0, max, &key);
123
BSS_Setup->randomNumbers[2] = RSDK.RandSeeded(0, max, &key);
124
BSS_Setup->randomNumbers[3] = RSDK.RandSeeded(0, max, &key);
125
126
BSS_Palette->startColorID = 16 * (BSS_Setup->randomNumbers[1] & 0x0F);
127
128
// Top Left Quadrant
129
for (int32 y = 0, py = 0; y < BSS_PLAYFIELD_H / 2; ++y, ++py) {
130
for (int32 x = 0, px = 0; x < BSS_PLAYFIELD_W / 2; ++x, ++px) {
131
int32 tx = px + (0x10 * (BSS_Setup->randomNumbers[0] & 0x0F));
132
int32 ty = py + BSS_PLAYFIELD_H + (BSS_Setup->randomNumbers[0] & 0xF0);
133
134
uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);
135
136
int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;
137
BSS_Setup->playField[playFieldPos] = tile & 0x3FF;
138
if (BSS_Setup->playField[playFieldPos] > 24)
139
BSS_Setup->playField[playFieldPos] = BSS_NONE;
140
141
if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)
142
++BSS_Setup->ringCount;
143
}
144
}
145
146
// Top Right Quadrant
147
for (int32 y = 0, py = 0; y < BSS_PLAYFIELD_H / 2; ++y, ++py) {
148
for (int32 x = BSS_PLAYFIELD_W / 2, px = 0; x < BSS_PLAYFIELD_W; ++x, ++px) {
149
int32 tx = 0x10 * (BSS_Setup->randomNumbers[1] & 0x0F) - px + 0x0F;
150
int32 ty = py + BSS_PLAYFIELD_H + (BSS_Setup->randomNumbers[1] & 0xF0);
151
152
uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);
153
154
int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;
155
BSS_Setup->playField[playFieldPos] = tile & 0x3FF;
156
if (BSS_Setup->playField[playFieldPos] > 24)
157
BSS_Setup->playField[playFieldPos] = BSS_NONE;
158
159
if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)
160
++BSS_Setup->ringCount;
161
}
162
}
163
164
// Bottom Left Quadrant
165
for (int32 y = BSS_PLAYFIELD_H / 2, py = 0; y < BSS_PLAYFIELD_H; ++y, ++py) {
166
for (int32 x = 0, px = 0; x < BSS_PLAYFIELD_W / 2; ++x, ++px) {
167
int32 tx = px + (0x10 * (BSS_Setup->randomNumbers[2] & 0x0F));
168
int32 ty = (BSS_Setup->randomNumbers[2] & 0xF0) - py + 0x2F;
169
170
uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);
171
172
int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;
173
BSS_Setup->playField[playFieldPos] = tile & 0x3FF;
174
if (BSS_Setup->playField[playFieldPos] > 24)
175
BSS_Setup->playField[playFieldPos] = BSS_NONE;
176
177
if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)
178
++BSS_Setup->ringCount;
179
}
180
}
181
182
// Bottom Right Quadrant
183
for (int32 y = BSS_PLAYFIELD_H / 2, py = 0; y < BSS_PLAYFIELD_H; ++y, ++py) {
184
for (int32 x = BSS_PLAYFIELD_W / 2, px = 0; x < BSS_PLAYFIELD_W; ++x, ++px) {
185
int32 tx = 0x10 * (BSS_Setup->randomNumbers[3] & 0x0F) - px + 0x0F;
186
int32 ty = (BSS_Setup->randomNumbers[3] & 0xF0) - py + 0x2F;
187
188
uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);
189
190
int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;
191
BSS_Setup->playField[playFieldPos] = tile & 0x3FF;
192
if (BSS_Setup->playField[playFieldPos] > 24)
193
BSS_Setup->playField[playFieldPos] = BSS_NONE;
194
195
if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)
196
++BSS_Setup->ringCount;
197
}
198
}
199
200
BSS_Setup->playField[(16 * BSS_PLAYFIELD_H) + 3] = BSS_SPAWN_RIGHT;
201
}
202
203
RSDK.ResetEntitySlot(SLOT_BSS_SETUP, BSS_Setup->classID, NULL);
204
205
BSS_Setup_SetupPalette();
206
207
globals->specialCleared = false;
208
209
BSS_Setup->sfxBlueSphere = RSDK.GetSfx("Special/BlueSphere.wav");
210
BSS_Setup->sfxSSExit = RSDK.GetSfx("Special/SSExit.wav");
211
BSS_Setup->sfxSSJettison = RSDK.GetSfx("Special/SSJettison.wav");
212
BSS_Setup->sfxEmerald = RSDK.GetSfx("Special/Emerald.wav");
213
BSS_Setup->sfxEvent = RSDK.GetSfx("Special/Event.wav");
214
BSS_Setup->sfxBumper = RSDK.GetSfx("Stage/Bumper.wav");
215
BSS_Setup->sfxSpring = RSDK.GetSfx("Global/Spring.wav");
216
BSS_Setup->sfxRing = RSDK.GetSfx("Global/Ring.wav");
217
BSS_Setup->sfxLoseRings = RSDK.GetSfx("Global/LoseRings.wav");
218
BSS_Setup->sfxMedal = RSDK.GetSfx("Special/Medal.wav");
219
BSS_Setup->sfxMedalCaught = RSDK.GetSfx("Special/MedalCaught.wav");
220
BSS_Setup->sfxTeleport = RSDK.GetSfx("Global/Teleport.wav");
221
222
EntityMenuParam *param = MenuParam_GetParam();
223
if (param->bssSelection == BSS_SELECTION_EXTRAS) {
224
String string;
225
Localization_GetString(&string, STR_RPC_PLAYING);
226
API_SetRichPresence(PRESENCE_GENERIC, &string);
227
}
228
}
229
230
int32 BSS_Setup_GetStageID(void)
231
{
232
int32 pos = SceneInfo->listPos;
233
RSDK.SetScene("Blue Spheres", "");
234
int32 id = (pos - SceneInfo->listPos) % 32;
235
236
SceneInfo->listPos = pos;
237
return id;
238
}
239
240
void BSS_Setup_SetupPalette(void)
241
{
242
// Globe Colour 1 & 2
243
for (int32 i = 0; i < 0x10; ++i) RSDK.SetPaletteEntry(0, 0x80 + i, RSDK.GetPaletteEntry(1, BSS_Palette->startColorID));
244
for (int32 i = 0; i < 0x10; ++i) RSDK.SetPaletteEntry(0, 0x90 + i, RSDK.GetPaletteEntry(1, BSS_Palette->startColorID + 1));
245
246
// Sky Colours
247
for (int32 i = 0; i < 3; ++i) RSDK.SetPaletteEntry(0, 0xA0 + i, RSDK.GetPaletteEntry(1, i + BSS_Palette->startColorID + 2));
248
249
// Emerald Colours (Unused in mania)
250
for (int32 i = 0; i < 4; ++i) RSDK.SetPaletteEntry(0, 0xD0 + i, RSDK.GetPaletteEntry(1, i + BSS_Palette->startColorID + 8));
251
252
// Alt Globe Palettes
253
for (int32 i = 0; i < 0x100; i += 0x10) {
254
RSDK.CopyPalette(0, 0x80, 1, i, 0x10);
255
RSDK.RotatePalette(0, 0x80, 0x9F, true);
256
}
257
258
for (int32 i = 0; i < 0x100; i += 0x10) {
259
RSDK.CopyPalette(0, 0x80, 2, i, 0x10);
260
RSDK.RotatePalette(0, 0x80, 0x9F, true);
261
}
262
}
263
264
void BSS_Setup_SetupFrustum(void)
265
{
266
int32 offset = 0;
267
int32 count = 0;
268
269
for (int32 f = 0; f < 2; ++f) {
270
int32 frustumID = f ? BSS_Setup->frustum2Layer : BSS_Setup->frustum1Layer;
271
TileLayer *frustum = RSDK.GetTileLayer(frustumID);
272
273
count = offset;
274
int32 lastX = 0;
275
int32 lastY = 0;
276
277
for (int32 y = 0; y < frustum->height; ++y) {
278
for (int32 x = 0; x < frustum->width; ++x) {
279
uint16 id = (RSDK.GetTile(frustumID, x, y) & 0x3FF);
280
if (id == BSS_SPHERE_BLUE || id == BSS_SPAWN_UP) {
281
BSS_Setup->offsetTable[count].x = x;
282
BSS_Setup->offsetTable[count].y = y;
283
count++;
284
285
if (id == BSS_SPAWN_UP) {
286
lastX = x;
287
lastY = y;
288
}
289
}
290
}
291
}
292
293
BSS_Setup->frustumCount[f] = count - offset;
294
BSS_Setup->frustumOffset[f] = offset;
295
Vector2 *offsetTable = &BSS_Setup->offsetTable[BSS_Setup->frustumOffset[f]];
296
int32 *offsetRadiusTable = &BSS_Setup->offsetRadiusTable[BSS_Setup->frustumOffset[f]];
297
298
for (int32 i = 0; i < BSS_Setup->frustumCount[f]; ++i) {
299
offsetTable[i].x -= lastX;
300
offsetTable[i].y -= lastY;
301
offsetRadiusTable[i] = offsetTable[i].x * offsetTable[i].x + offsetTable[i].y * offsetTable[i].y;
302
}
303
304
for (int32 o = 0; o < BSS_Setup->frustumCount[f]; ++o) {
305
for (int32 i = BSS_Setup->frustumCount[f] - 1; i > o; --i) {
306
int32 ox = offsetTable[i - 1].x;
307
int32 oy = offsetTable[i - 1].y;
308
int32 id = offsetRadiusTable[i - 1];
309
310
if (offsetRadiusTable[i] > offsetRadiusTable[i - 1]) {
311
offsetTable[i - 1].x = offsetTable[i].x;
312
offsetTable[i - 1].y = offsetTable[i].y;
313
offsetRadiusTable[i - 1] = offsetRadiusTable[i];
314
315
offsetTable[i].y = oy;
316
offsetTable[i].x = ox;
317
offsetRadiusTable[i] = id;
318
}
319
}
320
}
321
322
offset += BSS_Setup->frustumCount[f];
323
}
324
325
for (int32 i = RESERVE_ENTITY_COUNT; i < RESERVE_ENTITY_COUNT + 0x60; ++i) {
326
RSDK.ResetEntitySlot(i, BSS_Collectable->classID, NULL);
327
}
328
}
329
330
void BSS_Setup_CollectRing(void)
331
{
332
RSDK_THIS(BSS_Setup);
333
334
++BSS_Setup->rings;
335
336
if (BSS_Setup->ringCount > 0) {
337
BSS_Setup->ringCount--;
338
339
if (!BSS_Setup->ringCount) {
340
CREATE_ENTITY(BSS_Message, INT_TO_VOID(BSS_MESSAGE_PERFECT), self->position.x, self->position.y);
341
RSDK.PlaySfx(BSS_Setup->sfxEvent, false, 0xFF);
342
}
343
}
344
345
if (BSS_Setup->ringPan) {
346
int32 channel = RSDK.PlaySfx(BSS_Setup->sfxRing, false, 0xFF);
347
RSDK.SetChannelAttributes(channel, 1.0, -1.0, 1.0);
348
BSS_Setup->ringPan = 0;
349
}
350
else {
351
int32 channel = RSDK.PlaySfx(BSS_Setup->sfxRing, false, 0xFF);
352
RSDK.SetChannelAttributes(channel, 1.0, 1.0, 1.0);
353
BSS_Setup->ringPan = 1;
354
}
355
}
356
357
void BSS_Setup_GetStartupInfo(void)
358
{
359
RSDK_THIS(BSS_Setup);
360
361
BSS_Setup->sphereCount = 0;
362
BSS_Setup->pinkSphereCount = 0;
363
364
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
365
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
366
int32 playFieldPos = (x * BSS_PLAYFIELD_W) + y;
367
switch (BSS_Setup->playField[playFieldPos]) {
368
case BSS_SPHERE_BLUE:
369
case BSS_SPHERE_GREEN: ++BSS_Setup->sphereCount; break;
370
371
case BSS_SPHERE_PINK: ++BSS_Setup->pinkSphereCount; break;
372
373
case BSS_SPAWN_UP:
374
self->angle = 0x00;
375
self->playerPos.x = x;
376
self->playerPos.y = y;
377
BSS_Setup->playField[playFieldPos] = BSS_NONE;
378
break;
379
380
case BSS_SPAWN_RIGHT:
381
self->angle = 0xC0;
382
self->playerPos.x = x;
383
self->playerPos.y = y;
384
BSS_Setup->playField[playFieldPos] = BSS_NONE;
385
break;
386
387
case BSS_SPAWN_DOWN:
388
self->angle = 0x80;
389
self->playerPos.x = x;
390
self->playerPos.y = y;
391
BSS_Setup->playField[playFieldPos] = BSS_NONE;
392
break;
393
394
case BSS_SPAWN_LEFT:
395
self->angle = 0x40;
396
self->playerPos.x = x;
397
self->playerPos.y = y;
398
BSS_Setup->playField[playFieldPos] = BSS_NONE;
399
break;
400
401
default: break;
402
}
403
}
404
}
405
406
RSDK.GetTileLayer(BSS_Setup->bgLayer)->scrollInfo[0].scrollPos = self->angle << 18;
407
}
408
409
void BSS_Setup_State_GlobeJettison(void)
410
{
411
RSDK_THIS(BSS_Setup);
412
413
RSDK.GetTileLayer(BSS_Setup->globeLayer)->drawGroup[0] = 1;
414
415
self->globeTimer += self->globeSpeed;
416
if (self->globeSpeed <= 0 && self->globeTimer < 0) {
417
self->palettePage ^= 1;
418
self->globeTimer += 0x100;
419
self->playerPos.x -= RSDK.Sin256(self->angle) >> 8;
420
self->playerPos.y += RSDK.Cos256(self->angle) >> 8;
421
}
422
else if (self->globeTimer >= 0x100) {
423
self->palettePage ^= 1;
424
self->globeTimer -= 0x100;
425
self->playerPos.x += RSDK.Sin256(self->angle) >> 8;
426
self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;
427
}
428
429
self->playerPos.x &= 0x1F;
430
self->playerPos.y &= 0x1F;
431
432
self->paletteLine = (self->globeTimer >> 4) & 0xF;
433
434
TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);
435
background->scrollPos += self->globeSpeed << 14;
436
437
self->stopMovement = true;
438
BSS_Setup_HandleCollectableMovement();
439
BSS_Setup_LaunchSpheres();
440
441
if (++self->spinTimer == 128) {
442
self->spinTimer = 0;
443
self->speedupLevel = 8;
444
self->globeSpeed = 8;
445
BSS_Setup_SetupFinishSequence();
446
447
EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
448
player->stateInput = StateMachine_None;
449
player->jumpPress = false;
450
451
self->state = BSS_Setup_State_GlobeEmerald;
452
}
453
}
454
455
void BSS_Setup_HandleSteppedObjects(void)
456
{
457
RSDK_THIS(BSS_Setup);
458
459
if (self->globeTimer < 32)
460
self->disableBumpers = false;
461
462
if (self->globeTimer > 224)
463
self->disableBumpers = false;
464
465
int32 fieldPos = self->playerPos.y + (BSS_PLAYFIELD_H * self->playerPos.x);
466
switch (BSS_Setup->playField[fieldPos]) {
467
case BSS_SPHERE_BLUE:
468
if (self->globeTimer < 128) {
469
self->lastSpherePos.x = self->playerPos.x;
470
self->lastSpherePos.y = self->playerPos.y;
471
472
BSS_Setup_ProcessChain();
473
--BSS_Setup->sphereCount;
474
475
if (!self->completedRingLoop) {
476
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_BLUE), self->playerPos.x, self->playerPos.y);
477
BSS_Setup->playField[fieldPos] = BSS_BLUE_STOOD;
478
}
479
480
if (BSS_Setup->sphereCount <= 0) {
481
BSS_Setup->sphereCount = 0;
482
self->state = BSS_Setup_State_GlobeJettison;
483
RSDK.PlaySfx(BSS_Setup->sfxSSJettison, false, 255);
484
Music_FadeOut(0.0125);
485
}
486
else {
487
RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);
488
}
489
}
490
break;
491
492
case BSS_SPHERE_RED:
493
if (self->state != BSS_Setup_State_GlobeExit && self->globeTimer < 32) {
494
self->state = BSS_Setup_State_GlobeExit;
495
self->stopMovement = true;
496
self->spinTimer = 0;
497
self->globeTimer = 0;
498
RSDK.PlaySfx(BSS_Setup->sfxSSExit, false, 255);
499
Music_FadeOut(0.0125);
500
}
501
break;
502
503
case BSS_SPHERE_BUMPER:
504
if (!self->disableBumpers && self->globeTimer < 112) {
505
if (self->globeTimer > 16) {
506
if (self->globeSpeed < 0) {
507
self->disableBumpers = true;
508
self->globeSpeed = -self->globeSpeed;
509
self->playerWasBumped = false;
510
RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);
511
}
512
}
513
else if (!self->spinState) {
514
if (self->globeSpeed < 0) {
515
self->globeTimer = 16;
516
self->disableBumpers = true;
517
self->globeSpeed = -self->globeSpeed;
518
self->playerWasBumped = false;
519
RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);
520
}
521
}
522
}
523
break;
524
525
case BSS_SPHERE_YELLOW:
526
if (self->globeTimer < 128) {
527
EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
528
player->velocity.y = -TO_FIXED(24);
529
player->onGround = 0;
530
RSDK.SetSpriteAnimation(player->aniFrames, 3, &player->animator, false, 0);
531
BSS_Player->unused1 = 4;
532
533
self->globeSpeed *= 2;
534
self->spinState = 0;
535
self->globeSpeedInc = 4;
536
RSDK.PlaySfx(BSS_Setup->sfxSpring, false, 255);
537
}
538
break;
539
540
case BSS_SPHERE_GREEN:
541
if (self->globeTimer > 128) {
542
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_GREEN), self->playerPos.x, self->playerPos.y);
543
BSS_Setup->playField[fieldPos] = BSS_SPHERE_GREEN_STOOD;
544
RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);
545
}
546
break;
547
548
case BSS_SPHERE_PINK:
549
if (self->state != BSS_Setup_State_StartGlobeTeleport && self->globeTimer < 64) {
550
self->state = BSS_Setup_State_StartGlobeTeleport;
551
self->spinTimer = 0;
552
self->globeTimer = 0;
553
RSDK.PlaySfx(BSS_Setup->sfxTeleport, false, 255);
554
555
EntityFXFade *fade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);
556
fade->speedIn = 32;
557
fade->speedOut = 32;
558
fade->wait = 48;
559
}
560
break;
561
562
case BSS_RING:
563
if (self->globeTimer < 128) {
564
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_RING), self->playerPos.x, self->playerPos.y);
565
BSS_Setup->playField[fieldPos] = BSS_RING_SPARKLE;
566
BSS_Setup_CollectRing();
567
}
568
break;
569
570
default: break;
571
}
572
573
int32 posX = (self->playerPos.x + (RSDK.Sin256(self->angle) >> 8)) & 0x1F;
574
int32 posY = (self->playerPos.y - (RSDK.Cos256(self->angle) >> 8)) & 0x1F;
575
fieldPos = posY + (BSS_PLAYFIELD_H * posX);
576
577
switch (BSS_Setup->playField[fieldPos]) {
578
case BSS_SPHERE_BLUE:
579
if (self->globeTimer > 128) {
580
self->lastSpherePos.x = posX;
581
self->lastSpherePos.y = posY;
582
583
BSS_Setup_ProcessChain();
584
--BSS_Setup->sphereCount;
585
586
if (!self->completedRingLoop) {
587
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_BLUE), posX, posY);
588
BSS_Setup->playField[fieldPos] = BSS_BLUE_STOOD;
589
}
590
591
if (BSS_Setup->sphereCount <= 0) {
592
BSS_Setup->sphereCount = 0;
593
RSDK.PlaySfx(BSS_Setup->sfxMedal, false, 255);
594
self->state = BSS_Setup_State_GlobeJettison;
595
RSDK.PlaySfx(BSS_Setup->sfxSSJettison, false, 255);
596
Music_FadeOut(0.0125);
597
}
598
else {
599
RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);
600
}
601
}
602
break;
603
604
case BSS_SPHERE_RED:
605
if (self->state != BSS_Setup_State_GlobeExit && self->globeTimer > 224) {
606
self->palettePage ^= 1;
607
self->state = BSS_Setup_State_GlobeExit;
608
self->spinTimer = 0;
609
self->globeTimer = 0;
610
611
self->playerPos.x += RSDK.Sin256(self->angle) >> 8;
612
self->playerPos.x &= 0x1F;
613
self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;
614
self->playerPos.y &= 0x1F;
615
RSDK.PlaySfx(BSS_Setup->sfxSSExit, false, 255);
616
617
Music_FadeOut(0.0125);
618
}
619
break;
620
621
case BSS_SPHERE_BUMPER:
622
if (!self->disableBumpers && self->globeTimer > 144) {
623
if (self->globeTimer >= 240) {
624
if (!self->spinState) {
625
if (self->globeSpeed > 0) {
626
self->globeTimer = 240;
627
self->disableBumpers = true;
628
self->globeSpeed = -self->globeSpeed;
629
self->playerWasBumped = true;
630
RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);
631
}
632
}
633
}
634
else {
635
if (self->globeSpeed > 0) {
636
self->disableBumpers = true;
637
self->globeSpeed = -self->globeSpeed;
638
self->playerWasBumped = true;
639
RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);
640
}
641
}
642
}
643
break;
644
645
case BSS_SPHERE_YELLOW:
646
if (self->globeTimer > 128) {
647
EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
648
player->velocity.y = -TO_FIXED(24);
649
player->onGround = 0;
650
RSDK.SetSpriteAnimation(player->aniFrames, 3, &player->animator, 0, 0);
651
BSS_Player->unused1 = 4;
652
653
self->globeSpeed *= 2;
654
self->spinState = 0;
655
self->globeSpeedInc = 4;
656
RSDK.PlaySfx(BSS_Setup->sfxSpring, false, 255);
657
}
658
break;
659
660
case BSS_SPHERE_GREEN:
661
if (self->globeTimer > 128) {
662
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_GREEN), posX, posY);
663
BSS_Setup->playField[fieldPos] = BSS_SPHERE_GREEN_STOOD;
664
RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);
665
}
666
break;
667
668
case BSS_RING:
669
if (self->globeTimer > 128) {
670
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_RING), posX, posY);
671
BSS_Setup->playField[fieldPos] = BSS_RING_SPARKLE;
672
BSS_Setup_CollectRing();
673
}
674
break;
675
676
case BSS_EMERALD_CHAOS:
677
case BSS_EMERALD_SUPER:
678
case BSS_MEDAL_SILVER:
679
case BSS_MEDAL_GOLD:
680
if (self->globeTimer > 240) {
681
EntityMenuParam *param = MenuParam_GetParam();
682
if (param->bssSelection == BSS_SELECTION_NONE && globals->gameMode < MODE_TIMEATTACK) {
683
int32 pos = BSS_Setup_GetStageID();
684
if (pos >= 0) {
685
ProgressRAM *progress = GameProgress_GetProgressRAM();
686
if (progress) {
687
uint8 medal = BSS_Setup->playField[fieldPos] == BSS_MEDAL_SILVER ? 1
688
: BSS_Setup->playField[fieldPos] == BSS_MEDAL_GOLD ? 2
689
: 0;
690
691
if (medal)
692
GameProgress_GiveMedal(pos, medal);
693
694
if (progress->allGoldMedals && progress->goldMedalCount == 31) {
695
API_UnlockAchievement(&achievementList[ACH_GOLD_MEDAL]);
696
}
697
698
if (progress->allSilverMedals && progress->silverMedalCount == 31) {
699
API_UnlockAchievement(&achievementList[ACH_SILVER_MEDAL]);
700
}
701
}
702
}
703
}
704
705
self->palettePage ^= 1;
706
self->state = BSS_Setup_State_GlobeExit;
707
self->spinTimer = 0;
708
self->globeTimer = 0;
709
710
self->playerPos.x += RSDK.Sin256(self->angle) >> 8;
711
self->playerPos.x &= 0x1F;
712
self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;
713
self->playerPos.y &= 0x1F;
714
globals->specialCleared = true;
715
716
RSDK.PlaySfx(BSS_Setup->sfxSSExit, false, 255);
717
}
718
break;
719
720
default: break;
721
}
722
}
723
724
void BSS_Setup_HandleCollectableMovement(void)
725
{
726
RSDK_THIS(BSS_Setup);
727
728
self->offsetDir = self->angle >> 6;
729
730
int32 off = BSS_Setup->frustumOffset[(self->angle & 0x3F) != 0];
731
int32 id = BSS_Setup->frustumCount[(self->angle & 0x3F) != 0];
732
Vector2 *offset = &BSS_Setup->offsetTable[off];
733
734
int32 slot = RESERVE_ENTITY_COUNT;
735
while (id > 0) {
736
switch (self->offsetDir) {
737
case FLIP_NONE:
738
self->offset.x = offset->x;
739
self->offset.y = offset->y;
740
break;
741
742
case FLIP_X:
743
self->offset.x = -offset->y;
744
self->offset.y = offset->x;
745
break;
746
747
case FLIP_Y:
748
self->offset.x = offset->x;
749
self->offset.y = -offset->y;
750
break;
751
752
case FLIP_XY:
753
self->offset.x = offset->y;
754
self->offset.y = offset->x;
755
break;
756
757
default: break;
758
}
759
760
uint16 tile =
761
BSS_Setup->playField[((self->offset.y + self->playerPos.y) & 0x1F) + (BSS_PLAYFIELD_H * ((self->offset.x + self->playerPos.x) & 0x1F))];
762
if (tile) {
763
EntityBSS_Collectable *collectable = RSDK_GET_ENTITY(slot, BSS_Collectable);
764
int32 x = (self->offset.x * RSDK.Cos256(self->angle) + self->offset.y * RSDK.Sin256(self->angle)) >> 4;
765
int32 y = (self->offset.y * RSDK.Cos256(self->angle) - self->offset.x * RSDK.Sin256(self->angle)) >> 4;
766
y = -(y + self->paletteLine - 16);
767
768
if (y < 0) {
769
collectable->classID = TYPE_BLANK;
770
}
771
else {
772
collectable->classID = BSS_Collectable->classID;
773
collectable->type = tile & 0x3FF;
774
if (y < 112) {
775
self->xMultiplier = BSS_Setup->xMultiplierTable[y];
776
self->divisor = BSS_Setup->divisorTable[y];
777
collectable->animator.frameID = BSS_Setup->frameTable[y];
778
collectable->animator.frameID -= abs(x) >> 5;
779
if (collectable->animator.frameID < 0)
780
collectable->animator.frameID = 0;
781
782
int32 finalX = self->xMultiplier * x;
783
int32 distX = finalX * finalX >> 16;
784
785
int32 worldX = (finalX <= 0 ? (finalX + distX) : (finalX - distX)) >> 4;
786
787
collectable->position.x = (worldX + ScreenInfo->center.x) << 16;
788
collectable->position.y = (BSS_Setup->screenYTable[y] + worldX * worldX / self->divisor) << 16;
789
790
++slot;
791
}
792
}
793
}
794
795
--id;
796
++offset;
797
}
798
799
while (slot < RESERVE_ENTITY_COUNT + 0x80) {
800
RSDK_GET_ENTITY(slot++, BSS_Collectable)->classID = TYPE_BLANK;
801
}
802
}
803
804
void BSS_Setup_State_GlobeEmerald(void)
805
{
806
RSDK_THIS(BSS_Setup);
807
808
RSDK.GetTileLayer(BSS_Setup->globeLayer)->drawGroup[0] = 1;
809
810
self->globeTimer += self->globeSpeed;
811
if (++self->spinTimer == 120)
812
RSDK.PlaySfx(BSS_Setup->sfxMedalCaught, false, 255);
813
814
BSS_Setup_HandleSteppedObjects();
815
816
if (self->globeSpeed <= 0 && self->globeTimer < 0) {
817
self->palettePage ^= 1;
818
self->globeTimer += 256;
819
self->playerPos.x -= RSDK.Sin256(self->angle) >> 8;
820
self->playerPos.y += RSDK.Cos256(self->angle) >> 8;
821
}
822
else if (self->globeTimer >= 0x100) {
823
self->palettePage ^= 1;
824
self->globeTimer -= 256;
825
self->playerPos.x += RSDK.Sin256(self->angle) >> 8;
826
self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;
827
}
828
829
self->playerPos.x &= 0x1F;
830
self->playerPos.y &= 0x1F;
831
self->paletteLine = (self->globeTimer >> 4) & 0xF;
832
833
TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);
834
background->scrollPos += self->globeSpeed << 14;
835
836
BSS_Setup_HandleCollectableMovement();
837
}
838
839
void BSS_Setup_State_StartGlobeTeleport(void)
840
{
841
RSDK_THIS(BSS_Setup);
842
843
self->alpha += 8;
844
if (self->alpha == 320) {
845
EntityBSS_Player *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
846
EntityBSS_Player *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, BSS_Player);
847
848
RSDK.SetSpriteAnimation(player1->aniFrames, 0, &player1->animator, true, 0);
849
RSDK.SetSpriteAnimation(player2->aniFrames, 0, &player2->animator, true, 0);
850
851
int32 count = BSS_Setup->pinkSphereCount;
852
bool32 foundValidPlayerPos = false;
853
854
if (count > 1) {
855
int32 dir = RSDK.Rand(0, count - 1);
856
for (; (count && dir >= 0) && !foundValidPlayerPos; --count) {
857
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
858
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
859
uint16 tile = BSS_Setup->playField[y + (BSS_PLAYFIELD_H * x)];
860
if ((tile & 0x7F) == BSS_SPHERE_PINK && (x != self->playerPos.x || y != self->playerPos.y) && --dir < 0) {
861
self->playerPos.x = x;
862
self->playerPos.y = y;
863
864
x = 0x20;
865
y = 0x20;
866
foundValidPlayerPos = true;
867
}
868
}
869
}
870
}
871
}
872
873
int32 dir = RSDK.Rand(0, 4);
874
bool32 foundValidSpawnDir = false;
875
for (int32 i = 0; i < 4; ++i) {
876
int32 x = self->playerPos.x;
877
int32 y = self->playerPos.y;
878
switch (dir) {
879
case 0: y = (y - 1) & 0x1F; break;
880
case 1: x = (x + 1) & 0x1F; break;
881
case 2: y = (y + 1) & 0x1F; break;
882
case 3: x = (x - 1) & 0x1F; break;
883
default: break;
884
}
885
886
uint16 tile = BSS_Setup->playField[y + (BSS_PLAYFIELD_H * x)];
887
if (tile < BSS_SPHERE_RED || (tile > BSS_SPHERE_BUMPER && tile != BSS_SPHERE_PINK)) {
888
foundValidSpawnDir = true;
889
break;
890
}
891
892
dir = (dir + 1) & 3;
893
}
894
895
if (!foundValidSpawnDir) {
896
for (int32 i = 0; i < 4; ++i) {
897
int32 x = self->playerPos.x;
898
int32 y = self->playerPos.y;
899
switch (dir) {
900
case 0: y = (y - 2) & 0x1F; break;
901
case 1: x = (x + 2) & 0x1F; break;
902
case 2: y = (y + 2) & 0x1F; break;
903
case 3: x = (x - 2) & 0x1F; break;
904
default: break;
905
}
906
907
uint16 tile = BSS_Setup->playField[y + (BSS_PLAYFIELD_H * x)];
908
if (tile < BSS_SPHERE_RED || (tile > BSS_SPHERE_BUMPER && tile != BSS_SPHERE_PINK)) {
909
foundValidSpawnDir = true;
910
break;
911
}
912
913
dir = (dir + 1) & 3;
914
}
915
}
916
917
self->angle = dir << 6;
918
919
CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_PINK), self->playerPos.x, self->playerPos.y);
920
BSS_Setup->playField[self->playerPos.y + (BSS_PLAYFIELD_H * self->playerPos.x)] = BSS_SPHERE_PINK_STOOD;
921
922
self->timer = 100;
923
self->state = BSS_Setup_State_FinishGlobeTeleport;
924
}
925
BSS_Setup_HandleCollectableMovement();
926
}
927
928
void BSS_Setup_State_GlobeExit(void)
929
{
930
RSDK_THIS(BSS_Setup);
931
932
PauseMenu->disableEvents = true;
933
self->speedupLevel = 0;
934
935
if (self->spinTimer <= 0) {
936
CREATE_ENTITY(BSS_Message, INT_TO_VOID(BSS_MESSAGE_FINISHED), self->position.x, self->position.y);
937
foreach_active(BSS_Player, player) { player->stateInput = StateMachine_None; }
938
}
939
else {
940
TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);
941
background->scrollInfo[0].scrollPos -= TO_FIXED(32);
942
943
self->angle -= 8;
944
self->angle &= 0xFF;
945
}
946
947
TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);
948
if (self->spinTimer & 0xF) {
949
globe->drawGroup[0] = DRAWGROUP_COUNT;
950
951
int32 timer = self->spinTimer & 0xF;
952
self->globeSpinAnimator.frameID = BSS_Setup->globeFrameTable[timer - 1];
953
self->direction = BSS_Setup->globeDirTableL[timer - 1];
954
}
955
else {
956
if (self->spinTimer > 0) {
957
self->palettePage ^= 1;
958
globe->drawGroup[0] = 1;
959
}
960
}
961
962
self->spinTimer += 2;
963
BSS_Setup_HandleCollectableMovement();
964
}
965
966
void BSS_Setup_State_GlobeMoveZ(void)
967
{
968
RSDK_THIS(BSS_Setup);
969
970
EntityBSS_Player *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
971
972
if (self->speedupLevel < 32 && ++self->speedupTimer >= self->speedupInterval) {
973
self->speedupTimer = 0;
974
self->speedupLevel += 4;
975
}
976
977
RSDK.GetTileLayer(BSS_Setup->globeLayer)->drawGroup[0] = 1;
978
979
if (self->playerWasBumped) {
980
if (!self->disableBumpers && player1->up)
981
self->playerWasBumped = false;
982
}
983
else {
984
if (self->globeSpeed < self->speedupLevel)
985
self->globeSpeed += self->globeSpeedInc;
986
}
987
988
if (player1->onGround) {
989
if (self->globeTimer > 0 && self->globeTimer < 256) {
990
if (player1->left)
991
self->spinState = 1;
992
993
if (player1->right)
994
self->spinState = 2;
995
}
996
997
self->globeTimer += self->globeSpeed;
998
BSS_Setup_HandleSteppedObjects();
999
}
1000
else {
1001
self->globeTimer += self->globeSpeed;
1002
self->spinState = 0;
1003
}
1004
1005
if (self->state == BSS_Setup_State_GlobeMoveZ) {
1006
if (self->globeSpeed > 0) {
1007
if (self->globeTimer >= 0x100) {
1008
switch (self->spinState) {
1009
case 0: self->globeTimer -= 256; break;
1010
1011
case 1:
1012
self->state = BSS_Setup_State_GlobeTurnLeft;
1013
self->globeTimer = 0;
1014
break;
1015
1016
case 2:
1017
self->state = BSS_Setup_State_GlobeTurnRight;
1018
self->globeTimer = 0;
1019
break;
1020
}
1021
1022
self->palettePage ^= 1;
1023
self->spinState = 0;
1024
1025
self->playerPos.x += RSDK.Sin256(self->angle) >> 8;
1026
self->playerPos.x &= 0x1F;
1027
self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;
1028
self->playerPos.y &= 0x1F;
1029
}
1030
}
1031
else if (self->globeTimer < 0) {
1032
switch (self->spinState) {
1033
case 0:
1034
self->globeTimer += 256;
1035
self->palettePage ^= 1;
1036
1037
self->playerPos.x -= RSDK.Sin256(self->angle) >> 8;
1038
self->playerPos.x &= 0x1F;
1039
self->playerPos.y += RSDK.Cos256(self->angle) >> 8;
1040
self->playerPos.y &= 0x1F;
1041
1042
break;
1043
1044
case 1:
1045
self->state = BSS_Setup_State_GlobeTurnLeft;
1046
self->globeTimer = 0;
1047
break;
1048
1049
case 2:
1050
self->state = BSS_Setup_State_GlobeTurnRight;
1051
self->globeTimer = 0;
1052
break;
1053
}
1054
1055
self->spinState = 0;
1056
}
1057
1058
TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);
1059
background->scrollPos += self->globeSpeed << 14;
1060
}
1061
1062
self->paletteLine = (self->globeTimer >> 4) & 0xF;
1063
BSS_Setup_HandleCollectableMovement();
1064
}
1065
1066
void BSS_Setup_State_GlobeTurnLeft(void)
1067
{
1068
RSDK_THIS(BSS_Setup);
1069
1070
if (self->speedupLevel < 32 && ++self->speedupTimer >= self->speedupInterval) {
1071
self->speedupTimer = 0;
1072
self->speedupLevel += 4;
1073
}
1074
1075
TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);
1076
background->scrollInfo[0].scrollPos -= TO_FIXED(16);
1077
1078
self->angle -= 4;
1079
self->angle &= 0xFF;
1080
1081
TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);
1082
if (self->spinTimer == 15) {
1083
globe->drawGroup[0] = 1;
1084
1085
self->spinTimer = 0;
1086
self->palettePage ^= 1;
1087
if (!self->timer)
1088
self->state = BSS_Setup_State_GlobeMoveZ;
1089
else
1090
self->state = BSS_Setup_State_FinishGlobeTeleport;
1091
1092
BSS_Setup_HandleCollectableMovement();
1093
}
1094
else {
1095
globe->drawGroup[0] = DRAWGROUP_COUNT;
1096
1097
self->globeSpinAnimator.frameID = BSS_Setup->globeFrameTable[self->spinTimer];
1098
self->direction = BSS_Setup->globeDirTableL[self->spinTimer];
1099
1100
self->spinTimer++;
1101
if (self->timer > 1)
1102
self->timer--;
1103
1104
BSS_Setup_HandleCollectableMovement();
1105
}
1106
}
1107
1108
void BSS_Setup_State_GlobeTurnRight(void)
1109
{
1110
RSDK_THIS(BSS_Setup);
1111
1112
if (self->speedupLevel < 32 && ++self->speedupTimer >= self->speedupInterval) {
1113
self->speedupTimer = 0;
1114
self->speedupLevel += 4;
1115
}
1116
TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);
1117
background->scrollInfo[0].scrollPos += TO_FIXED(16);
1118
1119
self->angle += 4;
1120
self->angle &= 0xFF;
1121
1122
TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);
1123
if (self->spinTimer == 15) {
1124
globe->drawGroup[0] = 1;
1125
1126
self->spinTimer = 0;
1127
if (!self->timer)
1128
self->state = BSS_Setup_State_GlobeMoveZ;
1129
else
1130
self->state = BSS_Setup_State_FinishGlobeTeleport;
1131
1132
BSS_Setup_HandleCollectableMovement();
1133
}
1134
else {
1135
globe->drawGroup[0] = DRAWGROUP_COUNT;
1136
1137
if (!self->spinTimer)
1138
self->palettePage ^= 1;
1139
1140
self->globeSpinAnimator.frameID = BSS_Setup->globeFrameTable[self->spinTimer];
1141
self->direction = BSS_Setup->globeDirTableR[self->spinTimer];
1142
1143
self->spinTimer++;
1144
if (self->timer > 1)
1145
self->timer--;
1146
1147
BSS_Setup_HandleCollectableMovement();
1148
}
1149
}
1150
1151
void BSS_Setup_State_FinishGlobeTeleport(void)
1152
{
1153
RSDK_THIS(BSS_Setup);
1154
1155
EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
1156
1157
if (self->alpha <= 0) {
1158
if (player->up)
1159
self->timer = 1;
1160
else if (player->left)
1161
self->state = BSS_Setup_State_GlobeTurnLeft;
1162
else if (player->right)
1163
self->state = BSS_Setup_State_GlobeTurnRight;
1164
}
1165
else {
1166
self->alpha -= 8;
1167
}
1168
1169
if (!--self->timer) {
1170
self->state = BSS_Setup_State_GlobeMoveZ;
1171
1172
EntityBSS_Player *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);
1173
if (player1->onGround)
1174
RSDK.SetSpriteAnimation(player1->aniFrames, 1, &player1->animator, false, 0);
1175
1176
EntityBSS_Player *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, BSS_Player);
1177
if (player2->onGround)
1178
RSDK.SetSpriteAnimation(player2->aniFrames, 1, &player2->animator, false, 0);
1179
}
1180
1181
BSS_Setup_HandleCollectableMovement();
1182
}
1183
1184
bool32 BSS_Setup_CheckSphereValid(int32 x, int32 y)
1185
{
1186
int32 x1 = BSS_PLAYFIELD_H * (((uint8)x - 1) & 0x1F);
1187
int32 y1 = ((uint8)y - 1) & 0x1F;
1188
int32 x2 = BSS_PLAYFIELD_H * (((uint8)x + 1) & 0x1F);
1189
int32 y2 = ((uint8)y + 1) & 0x1F;
1190
1191
if ((BSS_Setup->playField[x1 + y1] & 0x7F) == BSS_SPHERE_BLUE)
1192
return true;
1193
1194
if ((BSS_Setup->playField[x2 + y1] & 0x7F) == BSS_SPHERE_BLUE || (BSS_Setup->playField[x1 + y] & 0x7F) == BSS_SPHERE_BLUE
1195
|| (BSS_Setup->playField[x2 + y] & 0x7F) == BSS_SPHERE_BLUE) {
1196
return true;
1197
}
1198
1199
if ((BSS_Setup->playField[x1 + y2] & 0x7F) != BSS_SPHERE_BLUE && (BSS_Setup->playField[x2 + y2] & 0x7F) != BSS_SPHERE_BLUE
1200
&& (BSS_Setup->playField[(BSS_PLAYFIELD_H * x) + y1] & 0x7F) != BSS_SPHERE_BLUE
1201
&& (BSS_Setup->playField[(BSS_PLAYFIELD_H * x) + y2] & 0x7F) != BSS_SPHERE_BLUE) {
1202
return false;
1203
}
1204
1205
return true;
1206
}
1207
1208
void BSS_Setup_LaunchSpheres(void)
1209
{
1210
RSDK_THIS(BSS_Setup);
1211
1212
int32 x = self->spinTimer + 0x100;
1213
int32 y = self->spinTimer << 17;
1214
RSDKScreenInfo *screen = ScreenInfo;
1215
1216
int32 slot = RESERVE_ENTITY_COUNT;
1217
EntityBSS_Collectable *collectable = NULL;
1218
1219
collectable = RSDK_GET_ENTITY(slot++, BSS_Collectable);
1220
while (collectable->classID != TYPE_BLANK) {
1221
int32 ix = (collectable->position.x >> 16);
1222
collectable->position.x = ((x * (ix - screen->center.x) >> 8) + screen->center.x) << 16;
1223
collectable->position.y -= y;
1224
collectable = RSDK_GET_ENTITY(slot++, BSS_Collectable);
1225
}
1226
}
1227
1228
void BSS_Setup_SetupFinishSequence(void)
1229
{
1230
RSDK_THIS(BSS_Setup);
1231
1232
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
1233
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) BSS_Setup->playField[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;
1234
}
1235
1236
int32 fx = (RSDK.Sin256(self->angle) >> 5) + self->playerPos.x;
1237
int32 fy = (((uint8)self->playerPos.y - (uint8)(RSDK.Cos256(self->angle) >> 5)) & 0x1F);
1238
int32 fieldPos = fy + (BSS_PLAYFIELD_H * (fx & 0x1F));
1239
1240
if (BSS_Setup->ringCount > 0)
1241
BSS_Setup->playField[fieldPos] = BSS_MEDAL_SILVER;
1242
else
1243
BSS_Setup->playField[fieldPos] = BSS_MEDAL_GOLD;
1244
1245
RSDK_GET_ENTITY(RESERVE_ENTITY_COUNT, BSS_Collectable)->drawGroup = 3;
1246
RSDK_GET_ENTITY(RESERVE_ENTITY_COUNT + 1, BSS_Collectable)->drawGroup = 3;
1247
}
1248
1249
bool32 BSS_Setup_ScanSphereChain_Up(uint8 x, uint8 y)
1250
{
1251
RSDK_THIS(BSS_Setup);
1252
1253
if (self->completedRingLoop)
1254
return true;
1255
1256
int32 px = BSS_PLAYFIELD_H * x;
1257
int32 id = 0;
1258
while (true) {
1259
y = (y - 1) & 0x1F;
1260
1261
if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)
1262
break;
1263
1264
if ((BSS_Setup->sphereChainTable[px + y] & 0x7F) == BSS_SPHERE_BLUE)
1265
break;
1266
1267
if (!BSS_Setup_CheckSphereValid(x, y))
1268
break;
1269
1270
BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;
1271
BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;
1272
1273
if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {
1274
self->completedRingLoop = true;
1275
return true;
1276
}
1277
1278
bool32 foundLoop = false;
1279
1280
int32 fieldPosRight = y + (BSS_PLAYFIELD_H * ((x + 1) & 0x1F));
1281
if ((BSS_Setup->playField[fieldPosRight] & 0x7F) == BSS_SPHERE_RED)
1282
foundLoop |= BSS_Setup_ScanSphereChain_Right(x, y);
1283
1284
int32 fieldPosLeft = y + (BSS_PLAYFIELD_H * ((x - 1) & 0x1F));
1285
if ((BSS_Setup->playField[fieldPosLeft] & 0x7F) == BSS_SPHERE_RED)
1286
foundLoop |= BSS_Setup_ScanSphereChain_Left(x, y);
1287
1288
if (!foundLoop)
1289
id++;
1290
else
1291
id = 0;
1292
1293
if (self->completedRingLoop)
1294
return true;
1295
}
1296
1297
for (int32 i = id; i > 0; --i) {
1298
y = (y + 1) & 0x1F;
1299
BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;
1300
}
1301
1302
return false;
1303
}
1304
bool32 BSS_Setup_ScanSphereChain_Down(uint8 x, uint8 y)
1305
{
1306
RSDK_THIS(BSS_Setup);
1307
1308
if (self->completedRingLoop)
1309
return true;
1310
1311
int32 px = BSS_PLAYFIELD_H * x;
1312
int32 id = 0;
1313
while (true) {
1314
y = (y + 1) & 0x1F;
1315
1316
if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)
1317
break;
1318
1319
if (BSS_Setup->sphereChainTable[px + y] == BSS_SPHERE_BLUE)
1320
break;
1321
1322
if (!BSS_Setup_CheckSphereValid(x, y))
1323
break;
1324
1325
BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;
1326
BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;
1327
if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {
1328
self->completedRingLoop = true;
1329
return true;
1330
}
1331
1332
bool32 foundLoop = false;
1333
1334
int32 fieldPosLeft = y + (BSS_PLAYFIELD_H * ((x - 1) & 0x1F));
1335
if ((BSS_Setup->playField[fieldPosLeft] & 0x7F) == BSS_SPHERE_RED)
1336
foundLoop |= BSS_Setup_ScanSphereChain_Left(x, y);
1337
1338
int32 fieldPosRight = y + (BSS_PLAYFIELD_H * ((x + 1) & 0x1F));
1339
if ((BSS_Setup->playField[fieldPosRight] & 0x7F) == BSS_SPHERE_RED)
1340
foundLoop |= BSS_Setup_ScanSphereChain_Right(x, y);
1341
1342
if (!foundLoop)
1343
id++;
1344
else
1345
id = 0;
1346
1347
if (self->completedRingLoop)
1348
return true;
1349
}
1350
1351
for (int32 i = id; i > 0; --i) {
1352
y = (y - 1) & 0x1F;
1353
BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;
1354
}
1355
1356
return false;
1357
}
1358
bool32 BSS_Setup_ScanSphereChain_Left(uint8 x, uint8 y)
1359
{
1360
RSDK_THIS(BSS_Setup);
1361
1362
if (self->completedRingLoop)
1363
return true;
1364
1365
int32 id = 0;
1366
while (true) {
1367
x = (x - 1) & 0x1F;
1368
int32 px = (BSS_PLAYFIELD_H * x);
1369
1370
if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)
1371
break;
1372
1373
if ((BSS_Setup->sphereChainTable[px + y] & 0x7F) == BSS_SPHERE_BLUE)
1374
break;
1375
1376
if (!BSS_Setup_CheckSphereValid(x, y))
1377
break;
1378
1379
BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;
1380
BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;
1381
if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {
1382
self->completedRingLoop = true;
1383
return true;
1384
}
1385
1386
bool32 foundLoop = false;
1387
1388
int32 fieldPosUp = px + ((y - 1) & 0x1F);
1389
if ((BSS_Setup->playField[fieldPosUp] & 0x7F) == BSS_SPHERE_RED)
1390
foundLoop |= BSS_Setup_ScanSphereChain_Up(x, y);
1391
1392
int32 fieldPosDown = px + ((y + 1) & 0x1F);
1393
if ((BSS_Setup->playField[fieldPosDown] & 0x7F) == BSS_SPHERE_RED)
1394
foundLoop |= BSS_Setup_ScanSphereChain_Down(x, y);
1395
1396
if (!foundLoop)
1397
id++;
1398
else
1399
id = 0;
1400
1401
if (self->completedRingLoop)
1402
return true;
1403
}
1404
1405
for (int32 i = id; i > 0; --i) {
1406
x = (x + 1) & 0x1F;
1407
int32 px = (BSS_PLAYFIELD_H * x);
1408
BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;
1409
}
1410
1411
return false;
1412
}
1413
bool32 BSS_Setup_ScanSphereChain_Right(uint8 x, uint8 y)
1414
{
1415
RSDK_THIS(BSS_Setup);
1416
1417
if (self->completedRingLoop)
1418
return true;
1419
1420
int32 id = 0;
1421
while (true) {
1422
x = (x + 1) & 0x1F;
1423
int32 px = (BSS_PLAYFIELD_H * x);
1424
1425
if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)
1426
break;
1427
1428
if ((BSS_Setup->sphereChainTable[px + y] & 0x7F) == BSS_SPHERE_BLUE)
1429
break;
1430
1431
if (!BSS_Setup_CheckSphereValid(x, y))
1432
break;
1433
1434
BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;
1435
BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;
1436
if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {
1437
self->completedRingLoop = true;
1438
return true;
1439
}
1440
1441
bool32 foundLoop = false;
1442
1443
int32 fieldPosDown = px + ((y + 1) & 0x1F);
1444
if ((BSS_Setup->playField[fieldPosDown] & 0x7F) == BSS_SPHERE_RED)
1445
foundLoop |= BSS_Setup_ScanSphereChain_Down(x, y);
1446
1447
int32 fieldPosUp = px + ((y - 1) & 0x1F);
1448
if ((BSS_Setup->playField[fieldPosUp] & 0x7F) == BSS_SPHERE_RED)
1449
foundLoop |= BSS_Setup_ScanSphereChain_Up(x, y);
1450
1451
if (!foundLoop)
1452
id++;
1453
else
1454
id = 0;
1455
1456
if (self->completedRingLoop)
1457
return true;
1458
}
1459
1460
for (int32 i = id; i > 0; --i) {
1461
x = (x - 1) & 0x1F;
1462
int32 px = (BSS_PLAYFIELD_H * x);
1463
BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;
1464
}
1465
1466
return false;
1467
}
1468
bool32 BSS_Setup_GetChainedSphereCount(uint8 x, uint8 y)
1469
{
1470
int32 px = BSS_PLAYFIELD_H * x;
1471
1472
uint8 y1 = (y - 1) & 0x1F;
1473
for (int32 i = 0; i < BSS_PLAYFIELD_H; ++i) {
1474
if (BSS_Setup->sphereCollectedTable[px + y1] == BSS_SPHERE_BLUE) {
1475
break;
1476
}
1477
else {
1478
int32 id = BSS_Setup->playField[px + y1] & 0x7F;
1479
if (id == BSS_NONE || id == BSS_SPHERE_RED)
1480
return false;
1481
y1 = (y1 - 1) & 0x1F;
1482
}
1483
}
1484
1485
y1 = (y + 1) & 0x1F;
1486
for (int32 i = 0; i < BSS_PLAYFIELD_H; ++i) {
1487
if (BSS_Setup->sphereCollectedTable[px + y1] == BSS_SPHERE_BLUE) {
1488
break;
1489
}
1490
else {
1491
int32 id = BSS_Setup->playField[px + y1] & 0x7F;
1492
if (id == BSS_NONE || id == BSS_SPHERE_RED)
1493
return false;
1494
y1 = (y1 + 1) & 0x1F;
1495
}
1496
}
1497
1498
uint8 x1 = (x - 1) & 0x1F;
1499
for (int32 i = 0; i < BSS_PLAYFIELD_W; ++i) {
1500
if (BSS_Setup->sphereCollectedTable[y + (BSS_PLAYFIELD_W * x1)] == BSS_SPHERE_BLUE) {
1501
break;
1502
}
1503
else {
1504
int32 id = BSS_Setup->playField[y + (BSS_PLAYFIELD_W * x1)] & 0x7F;
1505
if (id == BSS_NONE || id == BSS_SPHERE_RED)
1506
return false;
1507
x1 = (x1 - 1) & 0x1F;
1508
}
1509
}
1510
1511
x1 = (x + 1) & 0x1F;
1512
for (int32 i = 0; i < BSS_PLAYFIELD_W; ++i) {
1513
if (BSS_Setup->sphereCollectedTable[y + (BSS_PLAYFIELD_W * x1)] == BSS_SPHERE_BLUE) {
1514
break;
1515
}
1516
else {
1517
int32 id = BSS_Setup->playField[y + (BSS_PLAYFIELD_W * x1)] & 0x7F;
1518
if (id == BSS_NONE || id == BSS_SPHERE_RED)
1519
return false;
1520
x1 = (x1 + 1) & 0x1F;
1521
}
1522
}
1523
1524
BSS_Setup->sphereCollectedTable[px + y] = BSS_SPHERE_BLUE;
1525
return true;
1526
}
1527
1528
void BSS_Setup_ProcessChain(void)
1529
{
1530
RSDK_THIS(BSS_Setup);
1531
1532
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
1533
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
1534
BSS_Setup->sphereChainTable[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;
1535
BSS_Setup->sphereCollectedTable[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;
1536
}
1537
}
1538
1539
BSS_Setup->playField[self->lastSpherePos.y + (BSS_PLAYFIELD_H * self->lastSpherePos.x)] = BSS_SPHERE_RED;
1540
BSS_Setup->sphereCollectedTable[self->lastSpherePos.y + (BSS_PLAYFIELD_H * self->lastSpherePos.x)] = BSS_SPHERE_BLUE;
1541
1542
self->completedRingLoop = false;
1543
BSS_Setup_ScanSphereChain_Up(self->lastSpherePos.x, self->lastSpherePos.y);
1544
BSS_Setup_ScanSphereChain_Down(self->lastSpherePos.x, self->lastSpherePos.y);
1545
BSS_Setup_ScanSphereChain_Left(self->lastSpherePos.x, self->lastSpherePos.y);
1546
BSS_Setup_ScanSphereChain_Right(self->lastSpherePos.x, self->lastSpherePos.y);
1547
1548
BSS_Setup->playField[self->lastSpherePos.y + (BSS_PLAYFIELD_H * self->lastSpherePos.x)] = BSS_SPHERE_BLUE;
1549
1550
if (self->completedRingLoop) {
1551
int32 spheresCollected = 0;
1552
1553
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
1554
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
1555
if ((BSS_Setup->playField[(x * BSS_PLAYFIELD_H) + y] & 0x7F) == BSS_SPHERE_BLUE)
1556
spheresCollected += BSS_Setup_GetChainedSphereCount(x, y);
1557
}
1558
}
1559
1560
if (spheresCollected <= 0) {
1561
self->completedRingLoop = false;
1562
}
1563
else {
1564
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
1565
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
1566
int32 p = x * BSS_PLAYFIELD_H;
1567
1568
// The hell pit
1569
uint32 y1 = (y - 1) & 0x1F;
1570
uint32 y2 = (y + 1) & 0x1F;
1571
uint32 x1 = BSS_PLAYFIELD_H * ((x - 1) & 0x1F);
1572
uint32 x2 = BSS_PLAYFIELD_H * ((x + 1) & 0x1F);
1573
1574
if (BSS_Setup->sphereCollectedTable[p + y] == BSS_SPHERE_BLUE) {
1575
if ((BSS_Setup->playField[p + y1] & 0x7F) != BSS_SPHERE_BLUE) {
1576
if ((BSS_Setup->playField[p + y2] & 0x7F) != BSS_SPHERE_BLUE) {
1577
if ((BSS_Setup->playField[x1 + y] & 0x7F) != BSS_SPHERE_BLUE) {
1578
if ((BSS_Setup->playField[x2 + y] & 0x7F) != BSS_SPHERE_BLUE
1579
&& (BSS_Setup->playField[x1 + y1] & 0x7F) != BSS_SPHERE_BLUE
1580
&& (BSS_Setup->playField[x2 + y1] & 0x7F) != BSS_SPHERE_BLUE
1581
&& (BSS_Setup->playField[x1 + y2] & 0x7F) != BSS_SPHERE_BLUE) {
1582
if ((BSS_Setup->playField[x2 + y2] & 0x7F) != BSS_SPHERE_BLUE)
1583
BSS_Setup->sphereCollectedTable[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;
1584
}
1585
}
1586
}
1587
}
1588
}
1589
}
1590
}
1591
1592
for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {
1593
for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {
1594
if (BSS_Setup->sphereCollectedTable[(x * BSS_PLAYFIELD_H) + y])
1595
BSS_Setup->playField[(x * BSS_PLAYFIELD_H) + y] = BSS_RING;
1596
}
1597
}
1598
1599
BSS_Setup->sphereCount -= spheresCollected;
1600
RSDK.PlaySfx(BSS_Setup->sfxLoseRings, false, 0xFF);
1601
}
1602
}
1603
}
1604
1605
#if GAME_INCLUDE_EDITOR
1606
void BSS_Setup_EditorDraw(void) {}
1607
1608
void BSS_Setup_EditorLoad(void)
1609
{
1610
1611
// functionality was likely replaced by BSS_Palette
1612
RSDK_ACTIVE_VAR(BSS_Setup, paletteID);
1613
RSDK_ENUM_VAR("(Unused)", 0);
1614
}
1615
#endif
1616
1617
void BSS_Setup_Serialize(void) { RSDK_EDITABLE_VAR(BSS_Setup, VAR_UINT8, paletteID); }
1618
1619