Path: blob/master/SonicMania/Objects/BSS/BSS_Setup.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: BSS_Setup Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"8#include <time.h>910ObjectBSS_Setup *BSS_Setup;1112void BSS_Setup_Update(void)13{14RSDK_THIS(BSS_Setup);1516StateMachine_Run(self->state);1718ScreenInfo->position.x = 0x100 - ScreenInfo->center.x;1920if (self->palettePage) {21RSDK.CopyPalette(2, 16 * self->paletteLine, 0, 128, 16);22RSDK.CopyPalette(1, 16 * self->paletteLine, 0, 144, 16);23}24else {25RSDK.CopyPalette(1, 16 * self->paletteLine, 0, 128, 16);26RSDK.CopyPalette(2, 16 * self->paletteLine, 0, 144, 16);27}2829#if MANIA_USE_PLUS30EntityMenuParam *param = MenuParam_GetParam();31if (param->bssSelection == BSS_SELECTION_NONE && !self->stopMovement && globals->gameMode < MODE_TIMEATTACK)32++SaveGame_GetSaveRAM()->zoneTimes[28];33#endif34}3536void BSS_Setup_LateUpdate(void) {}3738void BSS_Setup_StaticUpdate(void) {}3940void BSS_Setup_Draw(void)41{42RSDK_THIS(BSS_Setup);4344TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);45self->inkEffect = INK_NONE;46if (globe->drawGroup[0] == DRAWGROUP_COUNT)47RSDK.DrawSprite(&self->globeSpinAnimator, NULL, false);4849Vector2 drawPos;50self->inkEffect = INK_BLEND;51drawPos.x = self->position.x;52drawPos.y = TO_FIXED(158);53RSDK.DrawSprite(&self->shadowAnimator, &drawPos, false);54}5556void BSS_Setup_Create(void *data)57{58RSDK_THIS(BSS_Setup);5960if (!SceneInfo->inEditor) {61self->active = ACTIVE_BOUNDS;62self->visible = true;63self->drawGroup = 2;64self->drawFX = FX_FLIP;65self->position.x = TO_FIXED(256);66self->updateRange.x = TO_FIXED(128);67self->updateRange.y = TO_FIXED(128);68self->speedupInterval = 30 * 60; // speed up every 30 seconds69self->stopMovement = false;7071RSDK.SetSpriteAnimation(BSS_Setup->globeFrames, 0, &self->globeSpinAnimator, true, 0);72RSDK.SetSpriteAnimation(BSS_Setup->globeFrames, 1, &self->shadowAnimator, true, 0);7374BSS_Setup_GetStartupInfo();7576self->state = BSS_Setup_State_GlobeMoveZ;77}78}7980void BSS_Setup_StageLoad(void)81{82BSS_Setup->globeFrames = RSDK.LoadSpriteAnimation("SpecialBS/Globe.bin", SCOPE_STAGE);8384BSS_Setup->bgLayer = RSDK.GetTileLayerID("Background");85BSS_Setup->globeLayer = RSDK.GetTileLayerID("Globe");86BSS_Setup->frustum1Layer = RSDK.GetTileLayerID("Frustum 1");87BSS_Setup->frustum2Layer = RSDK.GetTileLayerID("Frustum 2");88BSS_Setup->playFieldLayer = RSDK.GetTileLayerID("Playfield");89BSS_Setup->ringCountLayer = RSDK.GetTileLayerID("Ring Count");9091BSS_Setup_SetupFrustum();92BSS_Setup->ringCount = 0;9394TileLayer *playField = RSDK.GetTileLayer(BSS_Setup->playFieldLayer);9596memset(BSS_Setup->playField, 0, sizeof(BSS_Setup->playField));97memset(BSS_Setup->sphereChainTable, 0, sizeof(BSS_Setup->sphereChainTable));98memset(BSS_Setup->sphereCollectedTable, 0, sizeof(BSS_Setup->sphereCollectedTable));99100if (playField->width <= BSS_PLAYFIELD_W) {101for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {102for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {103uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, x, y);104105int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;106BSS_Setup->playField[playFieldPos] = tile & 0x3FF;107if (BSS_Setup->playField[playFieldPos] > 24)108BSS_Setup->playField[playFieldPos] = BSS_NONE;109110if ((RSDK.GetTile(BSS_Setup->ringCountLayer, x, y) & 0x3FF) == BSS_RING)111++BSS_Setup->ringCount;112}113}114}115else {116int32 max = (playField->width >> 4) * ((playField->height - 32) >> 4);117int32 key = (int32)time(0);118119// Randomly generate a random number (same format as the random numbers from the S3 blue spheres game)120BSS_Setup->randomNumbers[0] = RSDK.RandSeeded(0, max, &key);121BSS_Setup->randomNumbers[1] = RSDK.RandSeeded(0, max, &key);122BSS_Setup->randomNumbers[2] = RSDK.RandSeeded(0, max, &key);123BSS_Setup->randomNumbers[3] = RSDK.RandSeeded(0, max, &key);124125BSS_Palette->startColorID = 16 * (BSS_Setup->randomNumbers[1] & 0x0F);126127// Top Left Quadrant128for (int32 y = 0, py = 0; y < BSS_PLAYFIELD_H / 2; ++y, ++py) {129for (int32 x = 0, px = 0; x < BSS_PLAYFIELD_W / 2; ++x, ++px) {130int32 tx = px + (0x10 * (BSS_Setup->randomNumbers[0] & 0x0F));131int32 ty = py + BSS_PLAYFIELD_H + (BSS_Setup->randomNumbers[0] & 0xF0);132133uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);134135int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;136BSS_Setup->playField[playFieldPos] = tile & 0x3FF;137if (BSS_Setup->playField[playFieldPos] > 24)138BSS_Setup->playField[playFieldPos] = BSS_NONE;139140if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)141++BSS_Setup->ringCount;142}143}144145// Top Right Quadrant146for (int32 y = 0, py = 0; y < BSS_PLAYFIELD_H / 2; ++y, ++py) {147for (int32 x = BSS_PLAYFIELD_W / 2, px = 0; x < BSS_PLAYFIELD_W; ++x, ++px) {148int32 tx = 0x10 * (BSS_Setup->randomNumbers[1] & 0x0F) - px + 0x0F;149int32 ty = py + BSS_PLAYFIELD_H + (BSS_Setup->randomNumbers[1] & 0xF0);150151uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);152153int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;154BSS_Setup->playField[playFieldPos] = tile & 0x3FF;155if (BSS_Setup->playField[playFieldPos] > 24)156BSS_Setup->playField[playFieldPos] = BSS_NONE;157158if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)159++BSS_Setup->ringCount;160}161}162163// Bottom Left Quadrant164for (int32 y = BSS_PLAYFIELD_H / 2, py = 0; y < BSS_PLAYFIELD_H; ++y, ++py) {165for (int32 x = 0, px = 0; x < BSS_PLAYFIELD_W / 2; ++x, ++px) {166int32 tx = px + (0x10 * (BSS_Setup->randomNumbers[2] & 0x0F));167int32 ty = (BSS_Setup->randomNumbers[2] & 0xF0) - py + 0x2F;168169uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);170171int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;172BSS_Setup->playField[playFieldPos] = tile & 0x3FF;173if (BSS_Setup->playField[playFieldPos] > 24)174BSS_Setup->playField[playFieldPos] = BSS_NONE;175176if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)177++BSS_Setup->ringCount;178}179}180181// Bottom Right Quadrant182for (int32 y = BSS_PLAYFIELD_H / 2, py = 0; y < BSS_PLAYFIELD_H; ++y, ++py) {183for (int32 x = BSS_PLAYFIELD_W / 2, px = 0; x < BSS_PLAYFIELD_W; ++x, ++px) {184int32 tx = 0x10 * (BSS_Setup->randomNumbers[3] & 0x0F) - px + 0x0F;185int32 ty = (BSS_Setup->randomNumbers[3] & 0xF0) - py + 0x2F;186187uint16 tile = RSDK.GetTile(BSS_Setup->playFieldLayer, tx, ty);188189int32 playFieldPos = (x * BSS_PLAYFIELD_H) + y;190BSS_Setup->playField[playFieldPos] = tile & 0x3FF;191if (BSS_Setup->playField[playFieldPos] > 24)192BSS_Setup->playField[playFieldPos] = BSS_NONE;193194if ((RSDK.GetTile(BSS_Setup->ringCountLayer, tx, ty) & 0x3FF) == BSS_RING)195++BSS_Setup->ringCount;196}197}198199BSS_Setup->playField[(16 * BSS_PLAYFIELD_H) + 3] = BSS_SPAWN_RIGHT;200}201202RSDK.ResetEntitySlot(SLOT_BSS_SETUP, BSS_Setup->classID, NULL);203204BSS_Setup_SetupPalette();205206globals->specialCleared = false;207208BSS_Setup->sfxBlueSphere = RSDK.GetSfx("Special/BlueSphere.wav");209BSS_Setup->sfxSSExit = RSDK.GetSfx("Special/SSExit.wav");210BSS_Setup->sfxSSJettison = RSDK.GetSfx("Special/SSJettison.wav");211BSS_Setup->sfxEmerald = RSDK.GetSfx("Special/Emerald.wav");212BSS_Setup->sfxEvent = RSDK.GetSfx("Special/Event.wav");213BSS_Setup->sfxBumper = RSDK.GetSfx("Stage/Bumper.wav");214BSS_Setup->sfxSpring = RSDK.GetSfx("Global/Spring.wav");215BSS_Setup->sfxRing = RSDK.GetSfx("Global/Ring.wav");216BSS_Setup->sfxLoseRings = RSDK.GetSfx("Global/LoseRings.wav");217BSS_Setup->sfxMedal = RSDK.GetSfx("Special/Medal.wav");218BSS_Setup->sfxMedalCaught = RSDK.GetSfx("Special/MedalCaught.wav");219BSS_Setup->sfxTeleport = RSDK.GetSfx("Global/Teleport.wav");220221EntityMenuParam *param = MenuParam_GetParam();222if (param->bssSelection == BSS_SELECTION_EXTRAS) {223String string;224Localization_GetString(&string, STR_RPC_PLAYING);225API_SetRichPresence(PRESENCE_GENERIC, &string);226}227}228229int32 BSS_Setup_GetStageID(void)230{231int32 pos = SceneInfo->listPos;232RSDK.SetScene("Blue Spheres", "");233int32 id = (pos - SceneInfo->listPos) % 32;234235SceneInfo->listPos = pos;236return id;237}238239void BSS_Setup_SetupPalette(void)240{241// Globe Colour 1 & 2242for (int32 i = 0; i < 0x10; ++i) RSDK.SetPaletteEntry(0, 0x80 + i, RSDK.GetPaletteEntry(1, BSS_Palette->startColorID));243for (int32 i = 0; i < 0x10; ++i) RSDK.SetPaletteEntry(0, 0x90 + i, RSDK.GetPaletteEntry(1, BSS_Palette->startColorID + 1));244245// Sky Colours246for (int32 i = 0; i < 3; ++i) RSDK.SetPaletteEntry(0, 0xA0 + i, RSDK.GetPaletteEntry(1, i + BSS_Palette->startColorID + 2));247248// Emerald Colours (Unused in mania)249for (int32 i = 0; i < 4; ++i) RSDK.SetPaletteEntry(0, 0xD0 + i, RSDK.GetPaletteEntry(1, i + BSS_Palette->startColorID + 8));250251// Alt Globe Palettes252for (int32 i = 0; i < 0x100; i += 0x10) {253RSDK.CopyPalette(0, 0x80, 1, i, 0x10);254RSDK.RotatePalette(0, 0x80, 0x9F, true);255}256257for (int32 i = 0; i < 0x100; i += 0x10) {258RSDK.CopyPalette(0, 0x80, 2, i, 0x10);259RSDK.RotatePalette(0, 0x80, 0x9F, true);260}261}262263void BSS_Setup_SetupFrustum(void)264{265int32 offset = 0;266int32 count = 0;267268for (int32 f = 0; f < 2; ++f) {269int32 frustumID = f ? BSS_Setup->frustum2Layer : BSS_Setup->frustum1Layer;270TileLayer *frustum = RSDK.GetTileLayer(frustumID);271272count = offset;273int32 lastX = 0;274int32 lastY = 0;275276for (int32 y = 0; y < frustum->height; ++y) {277for (int32 x = 0; x < frustum->width; ++x) {278uint16 id = (RSDK.GetTile(frustumID, x, y) & 0x3FF);279if (id == BSS_SPHERE_BLUE || id == BSS_SPAWN_UP) {280BSS_Setup->offsetTable[count].x = x;281BSS_Setup->offsetTable[count].y = y;282count++;283284if (id == BSS_SPAWN_UP) {285lastX = x;286lastY = y;287}288}289}290}291292BSS_Setup->frustumCount[f] = count - offset;293BSS_Setup->frustumOffset[f] = offset;294Vector2 *offsetTable = &BSS_Setup->offsetTable[BSS_Setup->frustumOffset[f]];295int32 *offsetRadiusTable = &BSS_Setup->offsetRadiusTable[BSS_Setup->frustumOffset[f]];296297for (int32 i = 0; i < BSS_Setup->frustumCount[f]; ++i) {298offsetTable[i].x -= lastX;299offsetTable[i].y -= lastY;300offsetRadiusTable[i] = offsetTable[i].x * offsetTable[i].x + offsetTable[i].y * offsetTable[i].y;301}302303for (int32 o = 0; o < BSS_Setup->frustumCount[f]; ++o) {304for (int32 i = BSS_Setup->frustumCount[f] - 1; i > o; --i) {305int32 ox = offsetTable[i - 1].x;306int32 oy = offsetTable[i - 1].y;307int32 id = offsetRadiusTable[i - 1];308309if (offsetRadiusTable[i] > offsetRadiusTable[i - 1]) {310offsetTable[i - 1].x = offsetTable[i].x;311offsetTable[i - 1].y = offsetTable[i].y;312offsetRadiusTable[i - 1] = offsetRadiusTable[i];313314offsetTable[i].y = oy;315offsetTable[i].x = ox;316offsetRadiusTable[i] = id;317}318}319}320321offset += BSS_Setup->frustumCount[f];322}323324for (int32 i = RESERVE_ENTITY_COUNT; i < RESERVE_ENTITY_COUNT + 0x60; ++i) {325RSDK.ResetEntitySlot(i, BSS_Collectable->classID, NULL);326}327}328329void BSS_Setup_CollectRing(void)330{331RSDK_THIS(BSS_Setup);332333++BSS_Setup->rings;334335if (BSS_Setup->ringCount > 0) {336BSS_Setup->ringCount--;337338if (!BSS_Setup->ringCount) {339CREATE_ENTITY(BSS_Message, INT_TO_VOID(BSS_MESSAGE_PERFECT), self->position.x, self->position.y);340RSDK.PlaySfx(BSS_Setup->sfxEvent, false, 0xFF);341}342}343344if (BSS_Setup->ringPan) {345int32 channel = RSDK.PlaySfx(BSS_Setup->sfxRing, false, 0xFF);346RSDK.SetChannelAttributes(channel, 1.0, -1.0, 1.0);347BSS_Setup->ringPan = 0;348}349else {350int32 channel = RSDK.PlaySfx(BSS_Setup->sfxRing, false, 0xFF);351RSDK.SetChannelAttributes(channel, 1.0, 1.0, 1.0);352BSS_Setup->ringPan = 1;353}354}355356void BSS_Setup_GetStartupInfo(void)357{358RSDK_THIS(BSS_Setup);359360BSS_Setup->sphereCount = 0;361BSS_Setup->pinkSphereCount = 0;362363for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {364for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {365int32 playFieldPos = (x * BSS_PLAYFIELD_W) + y;366switch (BSS_Setup->playField[playFieldPos]) {367case BSS_SPHERE_BLUE:368case BSS_SPHERE_GREEN: ++BSS_Setup->sphereCount; break;369370case BSS_SPHERE_PINK: ++BSS_Setup->pinkSphereCount; break;371372case BSS_SPAWN_UP:373self->angle = 0x00;374self->playerPos.x = x;375self->playerPos.y = y;376BSS_Setup->playField[playFieldPos] = BSS_NONE;377break;378379case BSS_SPAWN_RIGHT:380self->angle = 0xC0;381self->playerPos.x = x;382self->playerPos.y = y;383BSS_Setup->playField[playFieldPos] = BSS_NONE;384break;385386case BSS_SPAWN_DOWN:387self->angle = 0x80;388self->playerPos.x = x;389self->playerPos.y = y;390BSS_Setup->playField[playFieldPos] = BSS_NONE;391break;392393case BSS_SPAWN_LEFT:394self->angle = 0x40;395self->playerPos.x = x;396self->playerPos.y = y;397BSS_Setup->playField[playFieldPos] = BSS_NONE;398break;399400default: break;401}402}403}404405RSDK.GetTileLayer(BSS_Setup->bgLayer)->scrollInfo[0].scrollPos = self->angle << 18;406}407408void BSS_Setup_State_GlobeJettison(void)409{410RSDK_THIS(BSS_Setup);411412RSDK.GetTileLayer(BSS_Setup->globeLayer)->drawGroup[0] = 1;413414self->globeTimer += self->globeSpeed;415if (self->globeSpeed <= 0 && self->globeTimer < 0) {416self->palettePage ^= 1;417self->globeTimer += 0x100;418self->playerPos.x -= RSDK.Sin256(self->angle) >> 8;419self->playerPos.y += RSDK.Cos256(self->angle) >> 8;420}421else if (self->globeTimer >= 0x100) {422self->palettePage ^= 1;423self->globeTimer -= 0x100;424self->playerPos.x += RSDK.Sin256(self->angle) >> 8;425self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;426}427428self->playerPos.x &= 0x1F;429self->playerPos.y &= 0x1F;430431self->paletteLine = (self->globeTimer >> 4) & 0xF;432433TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);434background->scrollPos += self->globeSpeed << 14;435436self->stopMovement = true;437BSS_Setup_HandleCollectableMovement();438BSS_Setup_LaunchSpheres();439440if (++self->spinTimer == 128) {441self->spinTimer = 0;442self->speedupLevel = 8;443self->globeSpeed = 8;444BSS_Setup_SetupFinishSequence();445446EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);447player->stateInput = StateMachine_None;448player->jumpPress = false;449450self->state = BSS_Setup_State_GlobeEmerald;451}452}453454void BSS_Setup_HandleSteppedObjects(void)455{456RSDK_THIS(BSS_Setup);457458if (self->globeTimer < 32)459self->disableBumpers = false;460461if (self->globeTimer > 224)462self->disableBumpers = false;463464int32 fieldPos = self->playerPos.y + (BSS_PLAYFIELD_H * self->playerPos.x);465switch (BSS_Setup->playField[fieldPos]) {466case BSS_SPHERE_BLUE:467if (self->globeTimer < 128) {468self->lastSpherePos.x = self->playerPos.x;469self->lastSpherePos.y = self->playerPos.y;470471BSS_Setup_ProcessChain();472--BSS_Setup->sphereCount;473474if (!self->completedRingLoop) {475CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_BLUE), self->playerPos.x, self->playerPos.y);476BSS_Setup->playField[fieldPos] = BSS_BLUE_STOOD;477}478479if (BSS_Setup->sphereCount <= 0) {480BSS_Setup->sphereCount = 0;481self->state = BSS_Setup_State_GlobeJettison;482RSDK.PlaySfx(BSS_Setup->sfxSSJettison, false, 255);483Music_FadeOut(0.0125);484}485else {486RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);487}488}489break;490491case BSS_SPHERE_RED:492if (self->state != BSS_Setup_State_GlobeExit && self->globeTimer < 32) {493self->state = BSS_Setup_State_GlobeExit;494self->stopMovement = true;495self->spinTimer = 0;496self->globeTimer = 0;497RSDK.PlaySfx(BSS_Setup->sfxSSExit, false, 255);498Music_FadeOut(0.0125);499}500break;501502case BSS_SPHERE_BUMPER:503if (!self->disableBumpers && self->globeTimer < 112) {504if (self->globeTimer > 16) {505if (self->globeSpeed < 0) {506self->disableBumpers = true;507self->globeSpeed = -self->globeSpeed;508self->playerWasBumped = false;509RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);510}511}512else if (!self->spinState) {513if (self->globeSpeed < 0) {514self->globeTimer = 16;515self->disableBumpers = true;516self->globeSpeed = -self->globeSpeed;517self->playerWasBumped = false;518RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);519}520}521}522break;523524case BSS_SPHERE_YELLOW:525if (self->globeTimer < 128) {526EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);527player->velocity.y = -TO_FIXED(24);528player->onGround = 0;529RSDK.SetSpriteAnimation(player->aniFrames, 3, &player->animator, false, 0);530BSS_Player->unused1 = 4;531532self->globeSpeed *= 2;533self->spinState = 0;534self->globeSpeedInc = 4;535RSDK.PlaySfx(BSS_Setup->sfxSpring, false, 255);536}537break;538539case BSS_SPHERE_GREEN:540if (self->globeTimer > 128) {541CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_GREEN), self->playerPos.x, self->playerPos.y);542BSS_Setup->playField[fieldPos] = BSS_SPHERE_GREEN_STOOD;543RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);544}545break;546547case BSS_SPHERE_PINK:548if (self->state != BSS_Setup_State_StartGlobeTeleport && self->globeTimer < 64) {549self->state = BSS_Setup_State_StartGlobeTeleport;550self->spinTimer = 0;551self->globeTimer = 0;552RSDK.PlaySfx(BSS_Setup->sfxTeleport, false, 255);553554EntityFXFade *fade = CREATE_ENTITY(FXFade, INT_TO_VOID(0xF0F0F0), self->position.x, self->position.y);555fade->speedIn = 32;556fade->speedOut = 32;557fade->wait = 48;558}559break;560561case BSS_RING:562if (self->globeTimer < 128) {563CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_RING), self->playerPos.x, self->playerPos.y);564BSS_Setup->playField[fieldPos] = BSS_RING_SPARKLE;565BSS_Setup_CollectRing();566}567break;568569default: break;570}571572int32 posX = (self->playerPos.x + (RSDK.Sin256(self->angle) >> 8)) & 0x1F;573int32 posY = (self->playerPos.y - (RSDK.Cos256(self->angle) >> 8)) & 0x1F;574fieldPos = posY + (BSS_PLAYFIELD_H * posX);575576switch (BSS_Setup->playField[fieldPos]) {577case BSS_SPHERE_BLUE:578if (self->globeTimer > 128) {579self->lastSpherePos.x = posX;580self->lastSpherePos.y = posY;581582BSS_Setup_ProcessChain();583--BSS_Setup->sphereCount;584585if (!self->completedRingLoop) {586CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_BLUE), posX, posY);587BSS_Setup->playField[fieldPos] = BSS_BLUE_STOOD;588}589590if (BSS_Setup->sphereCount <= 0) {591BSS_Setup->sphereCount = 0;592RSDK.PlaySfx(BSS_Setup->sfxMedal, false, 255);593self->state = BSS_Setup_State_GlobeJettison;594RSDK.PlaySfx(BSS_Setup->sfxSSJettison, false, 255);595Music_FadeOut(0.0125);596}597else {598RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);599}600}601break;602603case BSS_SPHERE_RED:604if (self->state != BSS_Setup_State_GlobeExit && self->globeTimer > 224) {605self->palettePage ^= 1;606self->state = BSS_Setup_State_GlobeExit;607self->spinTimer = 0;608self->globeTimer = 0;609610self->playerPos.x += RSDK.Sin256(self->angle) >> 8;611self->playerPos.x &= 0x1F;612self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;613self->playerPos.y &= 0x1F;614RSDK.PlaySfx(BSS_Setup->sfxSSExit, false, 255);615616Music_FadeOut(0.0125);617}618break;619620case BSS_SPHERE_BUMPER:621if (!self->disableBumpers && self->globeTimer > 144) {622if (self->globeTimer >= 240) {623if (!self->spinState) {624if (self->globeSpeed > 0) {625self->globeTimer = 240;626self->disableBumpers = true;627self->globeSpeed = -self->globeSpeed;628self->playerWasBumped = true;629RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);630}631}632}633else {634if (self->globeSpeed > 0) {635self->disableBumpers = true;636self->globeSpeed = -self->globeSpeed;637self->playerWasBumped = true;638RSDK.PlaySfx(BSS_Setup->sfxBumper, false, 255);639}640}641}642break;643644case BSS_SPHERE_YELLOW:645if (self->globeTimer > 128) {646EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);647player->velocity.y = -TO_FIXED(24);648player->onGround = 0;649RSDK.SetSpriteAnimation(player->aniFrames, 3, &player->animator, 0, 0);650BSS_Player->unused1 = 4;651652self->globeSpeed *= 2;653self->spinState = 0;654self->globeSpeedInc = 4;655RSDK.PlaySfx(BSS_Setup->sfxSpring, false, 255);656}657break;658659case BSS_SPHERE_GREEN:660if (self->globeTimer > 128) {661CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_GREEN), posX, posY);662BSS_Setup->playField[fieldPos] = BSS_SPHERE_GREEN_STOOD;663RSDK.PlaySfx(BSS_Setup->sfxBlueSphere, false, 255);664}665break;666667case BSS_RING:668if (self->globeTimer > 128) {669CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_RING), posX, posY);670BSS_Setup->playField[fieldPos] = BSS_RING_SPARKLE;671BSS_Setup_CollectRing();672}673break;674675case BSS_EMERALD_CHAOS:676case BSS_EMERALD_SUPER:677case BSS_MEDAL_SILVER:678case BSS_MEDAL_GOLD:679if (self->globeTimer > 240) {680EntityMenuParam *param = MenuParam_GetParam();681if (param->bssSelection == BSS_SELECTION_NONE && globals->gameMode < MODE_TIMEATTACK) {682int32 pos = BSS_Setup_GetStageID();683if (pos >= 0) {684ProgressRAM *progress = GameProgress_GetProgressRAM();685if (progress) {686uint8 medal = BSS_Setup->playField[fieldPos] == BSS_MEDAL_SILVER ? 1687: BSS_Setup->playField[fieldPos] == BSS_MEDAL_GOLD ? 2688: 0;689690if (medal)691GameProgress_GiveMedal(pos, medal);692693if (progress->allGoldMedals && progress->goldMedalCount == 31) {694API_UnlockAchievement(&achievementList[ACH_GOLD_MEDAL]);695}696697if (progress->allSilverMedals && progress->silverMedalCount == 31) {698API_UnlockAchievement(&achievementList[ACH_SILVER_MEDAL]);699}700}701}702}703704self->palettePage ^= 1;705self->state = BSS_Setup_State_GlobeExit;706self->spinTimer = 0;707self->globeTimer = 0;708709self->playerPos.x += RSDK.Sin256(self->angle) >> 8;710self->playerPos.x &= 0x1F;711self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;712self->playerPos.y &= 0x1F;713globals->specialCleared = true;714715RSDK.PlaySfx(BSS_Setup->sfxSSExit, false, 255);716}717break;718719default: break;720}721}722723void BSS_Setup_HandleCollectableMovement(void)724{725RSDK_THIS(BSS_Setup);726727self->offsetDir = self->angle >> 6;728729int32 off = BSS_Setup->frustumOffset[(self->angle & 0x3F) != 0];730int32 id = BSS_Setup->frustumCount[(self->angle & 0x3F) != 0];731Vector2 *offset = &BSS_Setup->offsetTable[off];732733int32 slot = RESERVE_ENTITY_COUNT;734while (id > 0) {735switch (self->offsetDir) {736case FLIP_NONE:737self->offset.x = offset->x;738self->offset.y = offset->y;739break;740741case FLIP_X:742self->offset.x = -offset->y;743self->offset.y = offset->x;744break;745746case FLIP_Y:747self->offset.x = offset->x;748self->offset.y = -offset->y;749break;750751case FLIP_XY:752self->offset.x = offset->y;753self->offset.y = offset->x;754break;755756default: break;757}758759uint16 tile =760BSS_Setup->playField[((self->offset.y + self->playerPos.y) & 0x1F) + (BSS_PLAYFIELD_H * ((self->offset.x + self->playerPos.x) & 0x1F))];761if (tile) {762EntityBSS_Collectable *collectable = RSDK_GET_ENTITY(slot, BSS_Collectable);763int32 x = (self->offset.x * RSDK.Cos256(self->angle) + self->offset.y * RSDK.Sin256(self->angle)) >> 4;764int32 y = (self->offset.y * RSDK.Cos256(self->angle) - self->offset.x * RSDK.Sin256(self->angle)) >> 4;765y = -(y + self->paletteLine - 16);766767if (y < 0) {768collectable->classID = TYPE_BLANK;769}770else {771collectable->classID = BSS_Collectable->classID;772collectable->type = tile & 0x3FF;773if (y < 112) {774self->xMultiplier = BSS_Setup->xMultiplierTable[y];775self->divisor = BSS_Setup->divisorTable[y];776collectable->animator.frameID = BSS_Setup->frameTable[y];777collectable->animator.frameID -= abs(x) >> 5;778if (collectable->animator.frameID < 0)779collectable->animator.frameID = 0;780781int32 finalX = self->xMultiplier * x;782int32 distX = finalX * finalX >> 16;783784int32 worldX = (finalX <= 0 ? (finalX + distX) : (finalX - distX)) >> 4;785786collectable->position.x = (worldX + ScreenInfo->center.x) << 16;787collectable->position.y = (BSS_Setup->screenYTable[y] + worldX * worldX / self->divisor) << 16;788789++slot;790}791}792}793794--id;795++offset;796}797798while (slot < RESERVE_ENTITY_COUNT + 0x80) {799RSDK_GET_ENTITY(slot++, BSS_Collectable)->classID = TYPE_BLANK;800}801}802803void BSS_Setup_State_GlobeEmerald(void)804{805RSDK_THIS(BSS_Setup);806807RSDK.GetTileLayer(BSS_Setup->globeLayer)->drawGroup[0] = 1;808809self->globeTimer += self->globeSpeed;810if (++self->spinTimer == 120)811RSDK.PlaySfx(BSS_Setup->sfxMedalCaught, false, 255);812813BSS_Setup_HandleSteppedObjects();814815if (self->globeSpeed <= 0 && self->globeTimer < 0) {816self->palettePage ^= 1;817self->globeTimer += 256;818self->playerPos.x -= RSDK.Sin256(self->angle) >> 8;819self->playerPos.y += RSDK.Cos256(self->angle) >> 8;820}821else if (self->globeTimer >= 0x100) {822self->palettePage ^= 1;823self->globeTimer -= 256;824self->playerPos.x += RSDK.Sin256(self->angle) >> 8;825self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;826}827828self->playerPos.x &= 0x1F;829self->playerPos.y &= 0x1F;830self->paletteLine = (self->globeTimer >> 4) & 0xF;831832TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);833background->scrollPos += self->globeSpeed << 14;834835BSS_Setup_HandleCollectableMovement();836}837838void BSS_Setup_State_StartGlobeTeleport(void)839{840RSDK_THIS(BSS_Setup);841842self->alpha += 8;843if (self->alpha == 320) {844EntityBSS_Player *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);845EntityBSS_Player *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, BSS_Player);846847RSDK.SetSpriteAnimation(player1->aniFrames, 0, &player1->animator, true, 0);848RSDK.SetSpriteAnimation(player2->aniFrames, 0, &player2->animator, true, 0);849850int32 count = BSS_Setup->pinkSphereCount;851bool32 foundValidPlayerPos = false;852853if (count > 1) {854int32 dir = RSDK.Rand(0, count - 1);855for (; (count && dir >= 0) && !foundValidPlayerPos; --count) {856for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {857for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {858uint16 tile = BSS_Setup->playField[y + (BSS_PLAYFIELD_H * x)];859if ((tile & 0x7F) == BSS_SPHERE_PINK && (x != self->playerPos.x || y != self->playerPos.y) && --dir < 0) {860self->playerPos.x = x;861self->playerPos.y = y;862863x = 0x20;864y = 0x20;865foundValidPlayerPos = true;866}867}868}869}870}871872int32 dir = RSDK.Rand(0, 4);873bool32 foundValidSpawnDir = false;874for (int32 i = 0; i < 4; ++i) {875int32 x = self->playerPos.x;876int32 y = self->playerPos.y;877switch (dir) {878case 0: y = (y - 1) & 0x1F; break;879case 1: x = (x + 1) & 0x1F; break;880case 2: y = (y + 1) & 0x1F; break;881case 3: x = (x - 1) & 0x1F; break;882default: break;883}884885uint16 tile = BSS_Setup->playField[y + (BSS_PLAYFIELD_H * x)];886if (tile < BSS_SPHERE_RED || (tile > BSS_SPHERE_BUMPER && tile != BSS_SPHERE_PINK)) {887foundValidSpawnDir = true;888break;889}890891dir = (dir + 1) & 3;892}893894if (!foundValidSpawnDir) {895for (int32 i = 0; i < 4; ++i) {896int32 x = self->playerPos.x;897int32 y = self->playerPos.y;898switch (dir) {899case 0: y = (y - 2) & 0x1F; break;900case 1: x = (x + 2) & 0x1F; break;901case 2: y = (y + 2) & 0x1F; break;902case 3: x = (x - 2) & 0x1F; break;903default: break;904}905906uint16 tile = BSS_Setup->playField[y + (BSS_PLAYFIELD_H * x)];907if (tile < BSS_SPHERE_RED || (tile > BSS_SPHERE_BUMPER && tile != BSS_SPHERE_PINK)) {908foundValidSpawnDir = true;909break;910}911912dir = (dir + 1) & 3;913}914}915916self->angle = dir << 6;917918CREATE_ENTITY(BSS_Collected, INT_TO_VOID(BSS_COLLECTED_PINK), self->playerPos.x, self->playerPos.y);919BSS_Setup->playField[self->playerPos.y + (BSS_PLAYFIELD_H * self->playerPos.x)] = BSS_SPHERE_PINK_STOOD;920921self->timer = 100;922self->state = BSS_Setup_State_FinishGlobeTeleport;923}924BSS_Setup_HandleCollectableMovement();925}926927void BSS_Setup_State_GlobeExit(void)928{929RSDK_THIS(BSS_Setup);930931PauseMenu->disableEvents = true;932self->speedupLevel = 0;933934if (self->spinTimer <= 0) {935CREATE_ENTITY(BSS_Message, INT_TO_VOID(BSS_MESSAGE_FINISHED), self->position.x, self->position.y);936foreach_active(BSS_Player, player) { player->stateInput = StateMachine_None; }937}938else {939TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);940background->scrollInfo[0].scrollPos -= TO_FIXED(32);941942self->angle -= 8;943self->angle &= 0xFF;944}945946TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);947if (self->spinTimer & 0xF) {948globe->drawGroup[0] = DRAWGROUP_COUNT;949950int32 timer = self->spinTimer & 0xF;951self->globeSpinAnimator.frameID = BSS_Setup->globeFrameTable[timer - 1];952self->direction = BSS_Setup->globeDirTableL[timer - 1];953}954else {955if (self->spinTimer > 0) {956self->palettePage ^= 1;957globe->drawGroup[0] = 1;958}959}960961self->spinTimer += 2;962BSS_Setup_HandleCollectableMovement();963}964965void BSS_Setup_State_GlobeMoveZ(void)966{967RSDK_THIS(BSS_Setup);968969EntityBSS_Player *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);970971if (self->speedupLevel < 32 && ++self->speedupTimer >= self->speedupInterval) {972self->speedupTimer = 0;973self->speedupLevel += 4;974}975976RSDK.GetTileLayer(BSS_Setup->globeLayer)->drawGroup[0] = 1;977978if (self->playerWasBumped) {979if (!self->disableBumpers && player1->up)980self->playerWasBumped = false;981}982else {983if (self->globeSpeed < self->speedupLevel)984self->globeSpeed += self->globeSpeedInc;985}986987if (player1->onGround) {988if (self->globeTimer > 0 && self->globeTimer < 256) {989if (player1->left)990self->spinState = 1;991992if (player1->right)993self->spinState = 2;994}995996self->globeTimer += self->globeSpeed;997BSS_Setup_HandleSteppedObjects();998}999else {1000self->globeTimer += self->globeSpeed;1001self->spinState = 0;1002}10031004if (self->state == BSS_Setup_State_GlobeMoveZ) {1005if (self->globeSpeed > 0) {1006if (self->globeTimer >= 0x100) {1007switch (self->spinState) {1008case 0: self->globeTimer -= 256; break;10091010case 1:1011self->state = BSS_Setup_State_GlobeTurnLeft;1012self->globeTimer = 0;1013break;10141015case 2:1016self->state = BSS_Setup_State_GlobeTurnRight;1017self->globeTimer = 0;1018break;1019}10201021self->palettePage ^= 1;1022self->spinState = 0;10231024self->playerPos.x += RSDK.Sin256(self->angle) >> 8;1025self->playerPos.x &= 0x1F;1026self->playerPos.y -= RSDK.Cos256(self->angle) >> 8;1027self->playerPos.y &= 0x1F;1028}1029}1030else if (self->globeTimer < 0) {1031switch (self->spinState) {1032case 0:1033self->globeTimer += 256;1034self->palettePage ^= 1;10351036self->playerPos.x -= RSDK.Sin256(self->angle) >> 8;1037self->playerPos.x &= 0x1F;1038self->playerPos.y += RSDK.Cos256(self->angle) >> 8;1039self->playerPos.y &= 0x1F;10401041break;10421043case 1:1044self->state = BSS_Setup_State_GlobeTurnLeft;1045self->globeTimer = 0;1046break;10471048case 2:1049self->state = BSS_Setup_State_GlobeTurnRight;1050self->globeTimer = 0;1051break;1052}10531054self->spinState = 0;1055}10561057TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);1058background->scrollPos += self->globeSpeed << 14;1059}10601061self->paletteLine = (self->globeTimer >> 4) & 0xF;1062BSS_Setup_HandleCollectableMovement();1063}10641065void BSS_Setup_State_GlobeTurnLeft(void)1066{1067RSDK_THIS(BSS_Setup);10681069if (self->speedupLevel < 32 && ++self->speedupTimer >= self->speedupInterval) {1070self->speedupTimer = 0;1071self->speedupLevel += 4;1072}10731074TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);1075background->scrollInfo[0].scrollPos -= TO_FIXED(16);10761077self->angle -= 4;1078self->angle &= 0xFF;10791080TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);1081if (self->spinTimer == 15) {1082globe->drawGroup[0] = 1;10831084self->spinTimer = 0;1085self->palettePage ^= 1;1086if (!self->timer)1087self->state = BSS_Setup_State_GlobeMoveZ;1088else1089self->state = BSS_Setup_State_FinishGlobeTeleport;10901091BSS_Setup_HandleCollectableMovement();1092}1093else {1094globe->drawGroup[0] = DRAWGROUP_COUNT;10951096self->globeSpinAnimator.frameID = BSS_Setup->globeFrameTable[self->spinTimer];1097self->direction = BSS_Setup->globeDirTableL[self->spinTimer];10981099self->spinTimer++;1100if (self->timer > 1)1101self->timer--;11021103BSS_Setup_HandleCollectableMovement();1104}1105}11061107void BSS_Setup_State_GlobeTurnRight(void)1108{1109RSDK_THIS(BSS_Setup);11101111if (self->speedupLevel < 32 && ++self->speedupTimer >= self->speedupInterval) {1112self->speedupTimer = 0;1113self->speedupLevel += 4;1114}1115TileLayer *background = RSDK.GetTileLayer(BSS_Setup->bgLayer);1116background->scrollInfo[0].scrollPos += TO_FIXED(16);11171118self->angle += 4;1119self->angle &= 0xFF;11201121TileLayer *globe = RSDK.GetTileLayer(BSS_Setup->globeLayer);1122if (self->spinTimer == 15) {1123globe->drawGroup[0] = 1;11241125self->spinTimer = 0;1126if (!self->timer)1127self->state = BSS_Setup_State_GlobeMoveZ;1128else1129self->state = BSS_Setup_State_FinishGlobeTeleport;11301131BSS_Setup_HandleCollectableMovement();1132}1133else {1134globe->drawGroup[0] = DRAWGROUP_COUNT;11351136if (!self->spinTimer)1137self->palettePage ^= 1;11381139self->globeSpinAnimator.frameID = BSS_Setup->globeFrameTable[self->spinTimer];1140self->direction = BSS_Setup->globeDirTableR[self->spinTimer];11411142self->spinTimer++;1143if (self->timer > 1)1144self->timer--;11451146BSS_Setup_HandleCollectableMovement();1147}1148}11491150void BSS_Setup_State_FinishGlobeTeleport(void)1151{1152RSDK_THIS(BSS_Setup);11531154EntityBSS_Player *player = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);11551156if (self->alpha <= 0) {1157if (player->up)1158self->timer = 1;1159else if (player->left)1160self->state = BSS_Setup_State_GlobeTurnLeft;1161else if (player->right)1162self->state = BSS_Setup_State_GlobeTurnRight;1163}1164else {1165self->alpha -= 8;1166}11671168if (!--self->timer) {1169self->state = BSS_Setup_State_GlobeMoveZ;11701171EntityBSS_Player *player1 = RSDK_GET_ENTITY(SLOT_PLAYER1, BSS_Player);1172if (player1->onGround)1173RSDK.SetSpriteAnimation(player1->aniFrames, 1, &player1->animator, false, 0);11741175EntityBSS_Player *player2 = RSDK_GET_ENTITY(SLOT_PLAYER2, BSS_Player);1176if (player2->onGround)1177RSDK.SetSpriteAnimation(player2->aniFrames, 1, &player2->animator, false, 0);1178}11791180BSS_Setup_HandleCollectableMovement();1181}11821183bool32 BSS_Setup_CheckSphereValid(int32 x, int32 y)1184{1185int32 x1 = BSS_PLAYFIELD_H * (((uint8)x - 1) & 0x1F);1186int32 y1 = ((uint8)y - 1) & 0x1F;1187int32 x2 = BSS_PLAYFIELD_H * (((uint8)x + 1) & 0x1F);1188int32 y2 = ((uint8)y + 1) & 0x1F;11891190if ((BSS_Setup->playField[x1 + y1] & 0x7F) == BSS_SPHERE_BLUE)1191return true;11921193if ((BSS_Setup->playField[x2 + y1] & 0x7F) == BSS_SPHERE_BLUE || (BSS_Setup->playField[x1 + y] & 0x7F) == BSS_SPHERE_BLUE1194|| (BSS_Setup->playField[x2 + y] & 0x7F) == BSS_SPHERE_BLUE) {1195return true;1196}11971198if ((BSS_Setup->playField[x1 + y2] & 0x7F) != BSS_SPHERE_BLUE && (BSS_Setup->playField[x2 + y2] & 0x7F) != BSS_SPHERE_BLUE1199&& (BSS_Setup->playField[(BSS_PLAYFIELD_H * x) + y1] & 0x7F) != BSS_SPHERE_BLUE1200&& (BSS_Setup->playField[(BSS_PLAYFIELD_H * x) + y2] & 0x7F) != BSS_SPHERE_BLUE) {1201return false;1202}12031204return true;1205}12061207void BSS_Setup_LaunchSpheres(void)1208{1209RSDK_THIS(BSS_Setup);12101211int32 x = self->spinTimer + 0x100;1212int32 y = self->spinTimer << 17;1213RSDKScreenInfo *screen = ScreenInfo;12141215int32 slot = RESERVE_ENTITY_COUNT;1216EntityBSS_Collectable *collectable = NULL;12171218collectable = RSDK_GET_ENTITY(slot++, BSS_Collectable);1219while (collectable->classID != TYPE_BLANK) {1220int32 ix = (collectable->position.x >> 16);1221collectable->position.x = ((x * (ix - screen->center.x) >> 8) + screen->center.x) << 16;1222collectable->position.y -= y;1223collectable = RSDK_GET_ENTITY(slot++, BSS_Collectable);1224}1225}12261227void BSS_Setup_SetupFinishSequence(void)1228{1229RSDK_THIS(BSS_Setup);12301231for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {1232for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) BSS_Setup->playField[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;1233}12341235int32 fx = (RSDK.Sin256(self->angle) >> 5) + self->playerPos.x;1236int32 fy = (((uint8)self->playerPos.y - (uint8)(RSDK.Cos256(self->angle) >> 5)) & 0x1F);1237int32 fieldPos = fy + (BSS_PLAYFIELD_H * (fx & 0x1F));12381239if (BSS_Setup->ringCount > 0)1240BSS_Setup->playField[fieldPos] = BSS_MEDAL_SILVER;1241else1242BSS_Setup->playField[fieldPos] = BSS_MEDAL_GOLD;12431244RSDK_GET_ENTITY(RESERVE_ENTITY_COUNT, BSS_Collectable)->drawGroup = 3;1245RSDK_GET_ENTITY(RESERVE_ENTITY_COUNT + 1, BSS_Collectable)->drawGroup = 3;1246}12471248bool32 BSS_Setup_ScanSphereChain_Up(uint8 x, uint8 y)1249{1250RSDK_THIS(BSS_Setup);12511252if (self->completedRingLoop)1253return true;12541255int32 px = BSS_PLAYFIELD_H * x;1256int32 id = 0;1257while (true) {1258y = (y - 1) & 0x1F;12591260if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)1261break;12621263if ((BSS_Setup->sphereChainTable[px + y] & 0x7F) == BSS_SPHERE_BLUE)1264break;12651266if (!BSS_Setup_CheckSphereValid(x, y))1267break;12681269BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;1270BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;12711272if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {1273self->completedRingLoop = true;1274return true;1275}12761277bool32 foundLoop = false;12781279int32 fieldPosRight = y + (BSS_PLAYFIELD_H * ((x + 1) & 0x1F));1280if ((BSS_Setup->playField[fieldPosRight] & 0x7F) == BSS_SPHERE_RED)1281foundLoop |= BSS_Setup_ScanSphereChain_Right(x, y);12821283int32 fieldPosLeft = y + (BSS_PLAYFIELD_H * ((x - 1) & 0x1F));1284if ((BSS_Setup->playField[fieldPosLeft] & 0x7F) == BSS_SPHERE_RED)1285foundLoop |= BSS_Setup_ScanSphereChain_Left(x, y);12861287if (!foundLoop)1288id++;1289else1290id = 0;12911292if (self->completedRingLoop)1293return true;1294}12951296for (int32 i = id; i > 0; --i) {1297y = (y + 1) & 0x1F;1298BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;1299}13001301return false;1302}1303bool32 BSS_Setup_ScanSphereChain_Down(uint8 x, uint8 y)1304{1305RSDK_THIS(BSS_Setup);13061307if (self->completedRingLoop)1308return true;13091310int32 px = BSS_PLAYFIELD_H * x;1311int32 id = 0;1312while (true) {1313y = (y + 1) & 0x1F;13141315if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)1316break;13171318if (BSS_Setup->sphereChainTable[px + y] == BSS_SPHERE_BLUE)1319break;13201321if (!BSS_Setup_CheckSphereValid(x, y))1322break;13231324BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;1325BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;1326if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {1327self->completedRingLoop = true;1328return true;1329}13301331bool32 foundLoop = false;13321333int32 fieldPosLeft = y + (BSS_PLAYFIELD_H * ((x - 1) & 0x1F));1334if ((BSS_Setup->playField[fieldPosLeft] & 0x7F) == BSS_SPHERE_RED)1335foundLoop |= BSS_Setup_ScanSphereChain_Left(x, y);13361337int32 fieldPosRight = y + (BSS_PLAYFIELD_H * ((x + 1) & 0x1F));1338if ((BSS_Setup->playField[fieldPosRight] & 0x7F) == BSS_SPHERE_RED)1339foundLoop |= BSS_Setup_ScanSphereChain_Right(x, y);13401341if (!foundLoop)1342id++;1343else1344id = 0;13451346if (self->completedRingLoop)1347return true;1348}13491350for (int32 i = id; i > 0; --i) {1351y = (y - 1) & 0x1F;1352BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;1353}13541355return false;1356}1357bool32 BSS_Setup_ScanSphereChain_Left(uint8 x, uint8 y)1358{1359RSDK_THIS(BSS_Setup);13601361if (self->completedRingLoop)1362return true;13631364int32 id = 0;1365while (true) {1366x = (x - 1) & 0x1F;1367int32 px = (BSS_PLAYFIELD_H * x);13681369if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)1370break;13711372if ((BSS_Setup->sphereChainTable[px + y] & 0x7F) == BSS_SPHERE_BLUE)1373break;13741375if (!BSS_Setup_CheckSphereValid(x, y))1376break;13771378BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;1379BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;1380if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {1381self->completedRingLoop = true;1382return true;1383}13841385bool32 foundLoop = false;13861387int32 fieldPosUp = px + ((y - 1) & 0x1F);1388if ((BSS_Setup->playField[fieldPosUp] & 0x7F) == BSS_SPHERE_RED)1389foundLoop |= BSS_Setup_ScanSphereChain_Up(x, y);13901391int32 fieldPosDown = px + ((y + 1) & 0x1F);1392if ((BSS_Setup->playField[fieldPosDown] & 0x7F) == BSS_SPHERE_RED)1393foundLoop |= BSS_Setup_ScanSphereChain_Down(x, y);13941395if (!foundLoop)1396id++;1397else1398id = 0;13991400if (self->completedRingLoop)1401return true;1402}14031404for (int32 i = id; i > 0; --i) {1405x = (x + 1) & 0x1F;1406int32 px = (BSS_PLAYFIELD_H * x);1407BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;1408}14091410return false;1411}1412bool32 BSS_Setup_ScanSphereChain_Right(uint8 x, uint8 y)1413{1414RSDK_THIS(BSS_Setup);14151416if (self->completedRingLoop)1417return true;14181419int32 id = 0;1420while (true) {1421x = (x + 1) & 0x1F;1422int32 px = (BSS_PLAYFIELD_H * x);14231424if ((BSS_Setup->playField[px + y] & 0x7F) != BSS_SPHERE_RED)1425break;14261427if ((BSS_Setup->sphereChainTable[px + y] & 0x7F) == BSS_SPHERE_BLUE)1428break;14291430if (!BSS_Setup_CheckSphereValid(x, y))1431break;14321433BSS_Setup->sphereChainTable[y + px] = BSS_SPHERE_BLUE;1434BSS_Setup->sphereCollectedTable[y + px] = BSS_SPHERE_BLUE;1435if (x == self->lastSpherePos.x && y == self->lastSpherePos.y) {1436self->completedRingLoop = true;1437return true;1438}14391440bool32 foundLoop = false;14411442int32 fieldPosDown = px + ((y + 1) & 0x1F);1443if ((BSS_Setup->playField[fieldPosDown] & 0x7F) == BSS_SPHERE_RED)1444foundLoop |= BSS_Setup_ScanSphereChain_Down(x, y);14451446int32 fieldPosUp = px + ((y - 1) & 0x1F);1447if ((BSS_Setup->playField[fieldPosUp] & 0x7F) == BSS_SPHERE_RED)1448foundLoop |= BSS_Setup_ScanSphereChain_Up(x, y);14491450if (!foundLoop)1451id++;1452else1453id = 0;14541455if (self->completedRingLoop)1456return true;1457}14581459for (int32 i = id; i > 0; --i) {1460x = (x - 1) & 0x1F;1461int32 px = (BSS_PLAYFIELD_H * x);1462BSS_Setup->sphereCollectedTable[px + y] = BSS_NONE;1463}14641465return false;1466}1467bool32 BSS_Setup_GetChainedSphereCount(uint8 x, uint8 y)1468{1469int32 px = BSS_PLAYFIELD_H * x;14701471uint8 y1 = (y - 1) & 0x1F;1472for (int32 i = 0; i < BSS_PLAYFIELD_H; ++i) {1473if (BSS_Setup->sphereCollectedTable[px + y1] == BSS_SPHERE_BLUE) {1474break;1475}1476else {1477int32 id = BSS_Setup->playField[px + y1] & 0x7F;1478if (id == BSS_NONE || id == BSS_SPHERE_RED)1479return false;1480y1 = (y1 - 1) & 0x1F;1481}1482}14831484y1 = (y + 1) & 0x1F;1485for (int32 i = 0; i < BSS_PLAYFIELD_H; ++i) {1486if (BSS_Setup->sphereCollectedTable[px + y1] == BSS_SPHERE_BLUE) {1487break;1488}1489else {1490int32 id = BSS_Setup->playField[px + y1] & 0x7F;1491if (id == BSS_NONE || id == BSS_SPHERE_RED)1492return false;1493y1 = (y1 + 1) & 0x1F;1494}1495}14961497uint8 x1 = (x - 1) & 0x1F;1498for (int32 i = 0; i < BSS_PLAYFIELD_W; ++i) {1499if (BSS_Setup->sphereCollectedTable[y + (BSS_PLAYFIELD_W * x1)] == BSS_SPHERE_BLUE) {1500break;1501}1502else {1503int32 id = BSS_Setup->playField[y + (BSS_PLAYFIELD_W * x1)] & 0x7F;1504if (id == BSS_NONE || id == BSS_SPHERE_RED)1505return false;1506x1 = (x1 - 1) & 0x1F;1507}1508}15091510x1 = (x + 1) & 0x1F;1511for (int32 i = 0; i < BSS_PLAYFIELD_W; ++i) {1512if (BSS_Setup->sphereCollectedTable[y + (BSS_PLAYFIELD_W * x1)] == BSS_SPHERE_BLUE) {1513break;1514}1515else {1516int32 id = BSS_Setup->playField[y + (BSS_PLAYFIELD_W * x1)] & 0x7F;1517if (id == BSS_NONE || id == BSS_SPHERE_RED)1518return false;1519x1 = (x1 + 1) & 0x1F;1520}1521}15221523BSS_Setup->sphereCollectedTable[px + y] = BSS_SPHERE_BLUE;1524return true;1525}15261527void BSS_Setup_ProcessChain(void)1528{1529RSDK_THIS(BSS_Setup);15301531for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {1532for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {1533BSS_Setup->sphereChainTable[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;1534BSS_Setup->sphereCollectedTable[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;1535}1536}15371538BSS_Setup->playField[self->lastSpherePos.y + (BSS_PLAYFIELD_H * self->lastSpherePos.x)] = BSS_SPHERE_RED;1539BSS_Setup->sphereCollectedTable[self->lastSpherePos.y + (BSS_PLAYFIELD_H * self->lastSpherePos.x)] = BSS_SPHERE_BLUE;15401541self->completedRingLoop = false;1542BSS_Setup_ScanSphereChain_Up(self->lastSpherePos.x, self->lastSpherePos.y);1543BSS_Setup_ScanSphereChain_Down(self->lastSpherePos.x, self->lastSpherePos.y);1544BSS_Setup_ScanSphereChain_Left(self->lastSpherePos.x, self->lastSpherePos.y);1545BSS_Setup_ScanSphereChain_Right(self->lastSpherePos.x, self->lastSpherePos.y);15461547BSS_Setup->playField[self->lastSpherePos.y + (BSS_PLAYFIELD_H * self->lastSpherePos.x)] = BSS_SPHERE_BLUE;15481549if (self->completedRingLoop) {1550int32 spheresCollected = 0;15511552for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {1553for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {1554if ((BSS_Setup->playField[(x * BSS_PLAYFIELD_H) + y] & 0x7F) == BSS_SPHERE_BLUE)1555spheresCollected += BSS_Setup_GetChainedSphereCount(x, y);1556}1557}15581559if (spheresCollected <= 0) {1560self->completedRingLoop = false;1561}1562else {1563for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {1564for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {1565int32 p = x * BSS_PLAYFIELD_H;15661567// The hell pit1568uint32 y1 = (y - 1) & 0x1F;1569uint32 y2 = (y + 1) & 0x1F;1570uint32 x1 = BSS_PLAYFIELD_H * ((x - 1) & 0x1F);1571uint32 x2 = BSS_PLAYFIELD_H * ((x + 1) & 0x1F);15721573if (BSS_Setup->sphereCollectedTable[p + y] == BSS_SPHERE_BLUE) {1574if ((BSS_Setup->playField[p + y1] & 0x7F) != BSS_SPHERE_BLUE) {1575if ((BSS_Setup->playField[p + y2] & 0x7F) != BSS_SPHERE_BLUE) {1576if ((BSS_Setup->playField[x1 + y] & 0x7F) != BSS_SPHERE_BLUE) {1577if ((BSS_Setup->playField[x2 + y] & 0x7F) != BSS_SPHERE_BLUE1578&& (BSS_Setup->playField[x1 + y1] & 0x7F) != BSS_SPHERE_BLUE1579&& (BSS_Setup->playField[x2 + y1] & 0x7F) != BSS_SPHERE_BLUE1580&& (BSS_Setup->playField[x1 + y2] & 0x7F) != BSS_SPHERE_BLUE) {1581if ((BSS_Setup->playField[x2 + y2] & 0x7F) != BSS_SPHERE_BLUE)1582BSS_Setup->sphereCollectedTable[(x * BSS_PLAYFIELD_H) + y] = BSS_NONE;1583}1584}1585}1586}1587}1588}1589}15901591for (int32 y = 0; y < BSS_PLAYFIELD_H; ++y) {1592for (int32 x = 0; x < BSS_PLAYFIELD_W; ++x) {1593if (BSS_Setup->sphereCollectedTable[(x * BSS_PLAYFIELD_H) + y])1594BSS_Setup->playField[(x * BSS_PLAYFIELD_H) + y] = BSS_RING;1595}1596}15971598BSS_Setup->sphereCount -= spheresCollected;1599RSDK.PlaySfx(BSS_Setup->sfxLoseRings, false, 0xFF);1600}1601}1602}16031604#if GAME_INCLUDE_EDITOR1605void BSS_Setup_EditorDraw(void) {}16061607void BSS_Setup_EditorLoad(void)1608{16091610// functionality was likely replaced by BSS_Palette1611RSDK_ACTIVE_VAR(BSS_Setup, paletteID);1612RSDK_ENUM_VAR("(Unused)", 0);1613}1614#endif16151616void BSS_Setup_Serialize(void) { RSDK_EDITABLE_VAR(BSS_Setup, VAR_UINT8, paletteID); }161716181619