Path: blob/master/SonicMania/Objects/Menu/LevelSelect.c
338 views
// ---------------------------------------------------------------------1// RSDK Project: Sonic Mania2// Object Description: LevelSelect Object3// Object Author: Christian Whitehead/Simon Thomley/Hunter Bridges4// Decompiled by: Rubberduckycooly & RMGRich5// ---------------------------------------------------------------------67#include "Game.h"89ObjectLevelSelect *LevelSelect;1011void LevelSelect_Update(void)12{13RSDK_THIS(LevelSelect);1415#if MANIA_USE_PLUS16EntityUIText *text = self->pinballLabel;17if (text)18text->visible = API.CheckDLC(DLC_PLUS);19#endif2021StateMachine_Run(self->state);2223ScreenInfo->position.x = 0x100 - ScreenInfo->center.x;24}2526void LevelSelect_LateUpdate(void) {}2728void LevelSelect_StaticUpdate(void)29{30if (--LevelSelect->bgAniDuration <= 0) {31++LevelSelect->bgAniFrame;3233LevelSelect->bgAniFrame &= 3;34LevelSelect->bgAniDuration = LevelSelect->bgAniDurationTable[LevelSelect->bgAniFrame];3536TileLayer *background = RSDK.GetTileLayer(0);37background->scrollPos = (background->scrollPos + 0x1000000) & 0x7FF0000;38}39}4041void LevelSelect_Draw(void)42{43RSDK_THIS(LevelSelect);4445StateMachine_Run(self->stateDraw);46}4748void LevelSelect_Create(void *data)49{50RSDK_THIS(LevelSelect);5152if (!SceneInfo->inEditor) {53self->active = ACTIVE_ALWAYS;54self->visible = true;55self->drawGroup = 12;56self->state = LevelSelect_State_Init;57self->stateDraw = LevelSelect_Draw_Fade;58self->timer = 640;59}60}6162void LevelSelect_StageLoad(void)63{64LevelSelect->sfxFail = RSDK.GetSfx("Stage/Fail.wav");65#if MANIA_USE_PLUS66LevelSelect->sfxRing = RSDK.GetSfx("Global/Ring.wav");67LevelSelect->sfxEmerald = RSDK.GetSfx("Special/Emerald.wav");68LevelSelect->sfxContinue = RSDK.GetSfx("Special/Continue.wav");69LevelSelect->sfxMedalGot = RSDK.GetSfx("Special/MedalCaught.wav");70#endif7172RSDK.ResetEntitySlot(0, LevelSelect->classID, NULL);7374UIPicture->aniFrames = RSDK.LoadSpriteAnimation("LSelect/Icons.bin", SCOPE_STAGE);75UIText->aniFrames = RSDK.LoadSpriteAnimation("LSelect/Text.bin", SCOPE_STAGE);7677LevelSelect->startMusicID = 0;78LevelSelect->soundTestMax = 0;79foreach_all(Music, music)80{81if (!LevelSelect->startMusicID) {82LevelSelect->startMusicID = RSDK.GetEntitySlot(music);83}84++LevelSelect->soundTestMax;85}86++LevelSelect->startMusicID;87--LevelSelect->soundTestMax;8889globals->medalMods |= MEDAL_DEBUGMODE;90#if MANIA_USE_PLUS91LevelSelect->cheatCodePtrs[0] = LevelSelect->cheat_RickyMode;92LevelSelect->cheatCodePtrs[1] = LevelSelect->cheat_AllEmeralds;93LevelSelect->cheatCodePtrs[2] = LevelSelect->cheat_MaxContinues;94LevelSelect->cheatCodePtrs[3] = LevelSelect->cheat_SwapGameMode;95LevelSelect->cheatCodePtrs[4] = LevelSelect->cheat_UnlockAllMedals;96LevelSelect->cheatCodePtrs[5] = LevelSelect->cheat_SuperDash;97LevelSelect->cheatCodePtrs[6] = LevelSelect->cheat_MaxControl;98LevelSelect->cheatCodePtrs[7] = LevelSelect->cheat_ToggleSuperMusic;99100LevelSelect->checkCheatActivated[0] = LevelSelect_Cheat_RickyMode;101LevelSelect->checkCheatActivated[1] = LevelSelect_Cheat_AllEmeralds;102LevelSelect->checkCheatActivated[2] = LevelSelect_Cheat_MaxContinues;103LevelSelect->checkCheatActivated[3] = LevelSelect_Cheat_SwapGameMode;104LevelSelect->checkCheatActivated[4] = LevelSelect_Cheat_UnlockAllMedals;105LevelSelect->checkCheatActivated[5] = LevelSelect_Cheat_SuperDash;106LevelSelect->checkCheatActivated[6] = LevelSelect_Cheat_MaxControl;107LevelSelect->checkCheatActivated[7] = LevelSelect_Cheat_ToggleSuperMusic;108109LevelSelect->cheatCodePos[0] = 0;110LevelSelect->cheatCodePos[1] = 0;111LevelSelect->cheatCodePos[2] = 0;112LevelSelect->cheatCodePos[3] = 0;113LevelSelect->cheatCodePos[4] = 0;114LevelSelect->cheatCodePos[5] = 0;115LevelSelect->cheatCodePos[6] = 0;116LevelSelect->cheatCodePos[7] = 0;117#endif118}119120#if MANIA_USE_PLUS121void LevelSelect_Cheat_AllEmeralds(void)122{123Music_FadeOut(0.125);124RSDK.PlaySfx(LevelSelect->sfxEmerald, false, 255);125126for (int32 e = 0; e < 7; ++e) SaveGame_SetEmerald(e);127}128129void LevelSelect_Cheat_ToggleSuperMusic(void)130{131RSDK.PlaySfx(LevelSelect->sfxRing, false, 255);132globals->superMusicEnabled ^= 1;133}134135void LevelSelect_Cheat_MaxContinues(void)136{137RSDK.PlaySfx(LevelSelect->sfxContinue, false, 255);138SaveGame_GetSaveRAM()->continues = 14;139}140141void LevelSelect_Cheat_MaxControl(void)142{143RSDK.PlaySfx(LevelSelect->sfxRing, false, 255);144globals->medalMods &= ~MEDAL_NODROPDASH;145globals->medalMods |= MEDAL_INSTASHIELD;146globals->medalMods |= MEDAL_PEELOUT;147}148149void LevelSelect_Cheat_RickyMode(void)150{151RSDK.PlaySfx(LevelSelect->sfxRing, false, 255);152globals->secrets ^= SECRET_RICKYMODE;153}154155void LevelSelect_Cheat_SuperDash(void)156{157RSDK.PlaySfx(LevelSelect->sfxRing, false, 255);158globals->secrets ^= SECRET_SUPERDASH;159}160161void LevelSelect_Cheat_SwapGameMode(void)162{163if (API.CheckDLC(DLC_PLUS)) {164RSDK.PlaySfx(LevelSelect->sfxRing, false, 255);165if (globals->gameMode == MODE_ENCORE) {166globals->gameMode = MODE_MANIA;167}168else {169globals->gameMode = MODE_ENCORE;170if (globals->medalMods & MEDAL_ANDKNUCKLES)171globals->medalMods &= ~MEDAL_ANDKNUCKLES;172}173}174}175176void LevelSelect_Cheat_UnlockAllMedals(void)177{178if (globals->superSecret && (globals->secrets & SECRET_RICKYMODE)) {179RSDK.PlaySfx(LevelSelect->sfxMedalGot, false, 255);180GameProgress_UnlockAll();181GameProgress_LockAllSpecialClear();182}183else {184RSDK.PlaySfx(LevelSelect->sfxRing, false, 255);185}186}187#endif188189void LevelSelect_Draw_Fade(void)190{191RSDK_THIS(LevelSelect);192RSDK.FillScreen(0x000000, self->timer, self->timer - 128, self->timer - 256);193}194195void LevelSelect_State_Init(void)196{197RSDK_THIS(LevelSelect);198199int32 labelPos[32];200int32 lastY = 0;201foreach_all(UIText, labelPosPtrL)202{203if (labelPosPtrL->position.x < 0x1000000 && labelPosPtrL->position.y > lastY) {204lastY = labelPosPtrL->position.y;205labelPos[self->labelCount++] = labelPosPtrL->position.y;206}207}208209foreach_all(UIText, labelL)210{211if (labelL->position.x < 0x1000000) {212for (int32 i = 0; i < self->labelCount; ++i) {213if (labelL->position.y == labelPos[i]) {214switch (labelL->align) {215case UITEXT_ALIGN_LEFT: self->zoneNameLabels[i] = labelL; break;216217default:218case UITEXT_ALIGN_CENTER: break;219220case UITEXT_ALIGN_RIGHT: self->stageIDLabels[i] = labelL; break;221}222}223}224}225}226227if (self->labelCount > 0)228memset(labelPos, 0, sizeof(int32) * self->labelCount);229230lastY = 0;231232foreach_all(UIText, labelPosPtrR)233{234if (labelPosPtrR->position.x > 0x1000000 && labelPosPtrR->position.y > lastY) {235lastY = labelPosPtrR->position.y;236labelPos[self->labelCount++] = labelPosPtrR->position.y;237}238}239240foreach_all(UIText, labelR)241{242if (labelR->position.x > 0x1000000 && self->labelCount > 0) {243for (int32 i = 0; i < self->labelCount; ++i) {244if (labelR->position.y == labelPos[i]) {245switch (labelR->align) {246case UITEXT_ALIGN_LEFT: self->zoneNameLabels[i] = labelR; break;247248default:249case UITEXT_ALIGN_CENTER: break;250251case UITEXT_ALIGN_RIGHT: self->stageIDLabels[i] = labelR;252#if MANIA_USE_PLUS253if (!labelR->data0 && labelR->data1 == 15)254self->pinballLabel = labelR;255#endif256break;257}258}259}260}261}262263for (int32 i = 0; i < self->labelCount; ++i) {264if (!self->zoneNameLabels[i]) {265for (int32 v = i; v >= 0; --v) {266if (self->zoneNameLabels[v]) {267self->zoneNameLabels[i] = self->zoneNameLabels[v];268break;269}270}271}272}273274LevelSelect_SetLabelHighlighted(true);275276foreach_all(UIText, soundTestLabel)277{278if (soundTestLabel->align == UITEXT_ALIGN_CENTER) {279self->soundTestLabel = soundTestLabel;280soundTestLabel->align = UITEXT_ALIGN_LEFT;281}282}283284self->leaderCharacterID = HUD_CharacterIndexFromID(GET_CHARACTER_ID(1)) + 1;285self->sidekickCharacterID = HUD_CharacterIndexFromID(GET_CHARACTER_ID(2)) + 1;286287foreach_all(UIPicture, picture)288{289if (picture->listID == 1) {290self->zoneIcon = picture;291292// Bug Details(?):293// frameID is equal to... playerID...?294// this feels like a slight oversight, though idk what it is meant to be295picture->animator.frameID = self->leaderCharacterID;296}297else if (picture->listID == 3) {298if (picture->frameID)299self->player2Icon = picture;300else301self->player1Icon = picture;302}303}304305self->stageIDLabels[self->labelCount - 1] = self->soundTestLabel;306307LevelSelect_ManagePlayerIcon();308309self->state = LevelSelect_State_FadeIn;310}311312void LevelSelect_State_FadeIn(void)313{314RSDK_THIS(LevelSelect);315316if (self->timer <= 0) {317self->timer = 0;318self->state = LevelSelect_State_Navigate;319self->stateDraw = StateMachine_None;320}321else {322self->timer -= 16;323}324}325326void LevelSelect_State_Navigate(void)327{328RSDK_THIS(LevelSelect);329330bool32 confirmPress = API_GetConfirmButtonFlip() ? ControllerInfo->keyB.press : ControllerInfo->keyA.press;331332if (ControllerInfo->keyUp.down || AnalogStickInfoL->keyUp.down) {333self->timer = (self->timer + 1) & 0xF;334335if (self->timer == 1) {336LevelSelect_SetLabelHighlighted(false);337#if MANIA_USE_PLUS338if (--self->labelID == 28 && !API.CheckDLC(DLC_PLUS))339#endif340--self->labelID;341342if (self->labelID < 0)343self->labelID = self->labelCount - 1;344345LevelSelect_SetLabelHighlighted(true);346}347}348else if (ControllerInfo->keyDown.down || AnalogStickInfoL->keyDown.down) {349self->timer = (self->timer + 1) & 0xF;350if (self->timer == 1) {351LevelSelect_SetLabelHighlighted(false);352353#if MANIA_USE_PLUS354if (++self->labelID == 28 && !API.CheckDLC(DLC_PLUS))355#endif356++self->labelID;357358if (self->labelID == self->labelCount)359self->labelID = 0;360361LevelSelect_SetLabelHighlighted(true);362}363}364else if (AnalogStickInfoL->keyLeft.press || ControllerInfo->keyLeft.press || ControllerInfo->keyRight.press || AnalogStickInfoL->keyRight.press) {365self->timer = 0;366367if (self->labelID >= self->labelCount - 1) {368if (AnalogStickInfoL->keyLeft.press || ControllerInfo->keyLeft.press) {369if (--self->soundTestID < 0)370self->soundTestID = LevelSelect->soundTestMax - 1;371}372else if (++self->soundTestID >= LevelSelect->soundTestMax) {373self->soundTestID = 0;374}375376EntityUIText *soundTest = self->soundTestLabel;377soundTest->text.chars[0] = self->soundTestID >> 4;378soundTest->text.chars[1] = self->soundTestID & 0xF;379}380else {381LevelSelect_HandleColumnChange();382}383}384else if (confirmPress || ControllerInfo->keyStart.press) {385if (self->labelID < self->labelCount - 1 || ControllerInfo->keyStart.press) {386#if MANIA_USE_PLUS387if (self->labelID != 28 || API.CheckDLC(DLC_PLUS))388LevelSelect_HandleNewStagePos();389else390RSDK.PlaySfx(LevelSelect->sfxFail, false, 255);391#else392LevelSelect_HandleNewStagePos();393#endif394}395else {396EntityMusic *track = RSDK_GET_ENTITY(self->soundTestID + LevelSelect->startMusicID, Music);397Music_PlayTrackPtr(track);398399#if MANIA_USE_PLUS400self->offsetUFO = self->soundTestID % 14;401self->offsetBSS = self->soundTestID & 0x1F;402for (int32 i = 0; i < 8; ++i) {403if (self->soundTestID != LevelSelect->cheatCodePtrs[i][LevelSelect->cheatCodePos[i]]) {404LevelSelect->cheatCodePos[i] = 0;405}406else {407LevelSelect->cheatCodePos[i]++;408if (LevelSelect->cheatCodePtrs[i][LevelSelect->cheatCodePos[i]] == 255) {409LevelSelect->checkCheatActivated[i]();410LevelSelect->cheatCodePos[i] = 0;411}412}413}414#endif415}416}417#if GAME_VERSION == VER_100418else if (TouchInfo->count) {419420self->timer = (self->timer + 1) & 0xF;421if (self->timer == 1) {422int32 selectedLabel = -1;423424int32 labelID = self->labelCount - 1;425for (int32 l = 0; l < self->labelCount; ++l, labelID--) {426EntityUIText *label = self->zoneNameLabels[labelID];427428if (label && labelID != self->labelID) {429int32 xOff = 5 * (label->text.length * 0.5);430431for (int32 f = 0; f < TouchInfo->count; ++f) {432float tx = TouchInfo->x[f] * ScreenInfo->size.x;433float ty = TouchInfo->y[f] * ScreenInfo->size.y;434435if (tx > ((label->position.x >> 16) - xOff) && tx < (xOff + (label->position.x >> 16))) {436if (ty < ((label->position.y >> 16) + 10) && ty > ((label->position.y >> 16) - 10))437selectedLabel = labelID;438}439}440}441}442443if (selectedLabel == -1) {444labelID = self->labelCount - 1;445for (int32 l = 0; l < self->labelCount; ++l) {446EntityUIText *label = self->stageIDLabels[labelID];447448if (label && labelID != self->labelID) {449int32 xOff = 5 * (label->text.length * 0.5);450451for (int32 f = 0; f < TouchInfo->count; ++f) {452float tx = TouchInfo->x[f] * ScreenInfo->size.x;453float ty = TouchInfo->y[f] * ScreenInfo->size.y;454455if (tx > ((label->position.x >> 16) - xOff) && tx < (xOff + (label->position.x >> 16))) {456if (ty < ((label->position.y >> 16) + 10) && ty > ((label->position.y >> 16) - 10))457selectedLabel = labelID;458}459}460}461labelID--;462}463}464465for (int32 f = 0; f < TouchInfo->count; ++f) {466float tx = TouchInfo->x[f] * ScreenInfo->size.x;467float ty = TouchInfo->y[f] * ScreenInfo->size.y;468469if (tx > 250.0 && ty > 170.0 && tx < 310.0 && ty < 230.0)470LevelSelect_HandleNewStagePos();471}472473if (selectedLabel != -1) {474LevelSelect_SetLabelHighlighted(false);475self->labelID = selectedLabel;476LevelSelect_SetLabelHighlighted(true);477}478}479}480#endif481else {482self->timer = 0;483}484485if (ControllerInfo->keyX.press) {486++self->leaderCharacterID;487LevelSelect_ManagePlayerIcon();488}489490if (ControllerInfo->keyY.press) {491++self->sidekickCharacterID;492LevelSelect_ManagePlayerIcon();493}494495EntityUIPicture *zoneIcon = self->zoneIcon;496if (self->labelID >= self->labelCount - 1)497RSDK.SetSpriteAnimation(UIPicture->aniFrames, 2, &zoneIcon->animator, true, self->leaderCharacterID);498else499RSDK.SetSpriteAnimation(UIPicture->aniFrames, 1, &zoneIcon->animator, true, self->stageIDLabels[self->labelID]->data1);500}501502void LevelSelect_State_FadeOut(void)503{504RSDK_THIS(LevelSelect);505506if (self->timer >= 1024)507RSDK.LoadScene();508else509self->timer += 16;510}511512void LevelSelect_ManagePlayerIcon(void)513{514RSDK_THIS(LevelSelect);515516EntityUIPicture *player1 = self->player1Icon;517EntityUIPicture *player2 = self->player2Icon;518519switch (self->leaderCharacterID) {520case LSELECT_PLAYER_SONIC:521case LSELECT_PLAYER_TAILS:522// Bug Details(?):523if (self->sidekickCharacterID == LSELECT_PLAYER_TAILS) // if leader is sonic or tails, and the sidekick is tails... change to knux...?524self->leaderCharacterID = LSELECT_PLAYER_KNUCKLES;525// playerID 3 may have been meant to be "S&T" before it was rearranged?526// v4 support this, with the player ids being: Sonic, Tails, Knux, Sonic & Tails527528player1->animator.frameID = self->leaderCharacterID;529break;530531case LSELECT_PLAYER_KNUCKLES: player1->animator.frameID = self->leaderCharacterID; break;532533#if MANIA_USE_PLUS534case LSELECT_PLAYER_MIGHTY:535case LSELECT_PLAYER_RAY:536if (!API.CheckDLC(DLC_PLUS))537self->leaderCharacterID = LSELECT_PLAYER_SONIC;538539player1->animator.frameID = self->leaderCharacterID;540break;541#endif542543default:544self->leaderCharacterID = LSELECT_PLAYER_SONIC;545player1->animator.frameID = LSELECT_PLAYER_SONIC;546break;547}548549switch (self->sidekickCharacterID) {550case LSELECT_PLAYER_TAILS:551player2->animator.frameID = self->sidekickCharacterID;552553// if leader is sonic & sidekick is tails, show ST icon. otherwise remove sidekick554if (self->leaderCharacterID != LSELECT_PLAYER_SONIC) {555self->sidekickCharacterID = LSELECT_PLAYER_NONE;556player2->animator.frameID = LSELECT_PLAYER_NONE;557}558break;559560default:561self->sidekickCharacterID = LSELECT_PLAYER_NONE;562player2->animator.frameID = LSELECT_PLAYER_NONE;563break;564565// if P2 is sonic, no he's not thats tails actually566case LSELECT_PLAYER_SONIC:567self->sidekickCharacterID = LSELECT_PLAYER_TAILS;568player2->animator.frameID = LSELECT_PLAYER_TAILS;569570// if leader is sonic & sidekick is tails, show ST icon. otherwise remove sidekick571if (self->leaderCharacterID != LSELECT_PLAYER_SONIC) {572self->sidekickCharacterID = LSELECT_PLAYER_NONE;573player2->animator.frameID = LSELECT_PLAYER_NONE;574}575break;576}577}578579void LevelSelect_SetLabelHighlighted(bool32 highlight)580{581RSDK_THIS(LevelSelect);582583EntityUIText *zoneName = self->zoneNameLabels[self->labelID];584if (zoneName)585zoneName->highlighted = highlight;586587EntityUIText *stageID = self->stageIDLabels[self->labelID];588if (stageID)589stageID->highlighted = highlight;590}591592void LevelSelect_HandleColumnChange(void)593{594RSDK_THIS(LevelSelect);595596EntityUIText *curLabel = self->stageIDLabels[self->labelID];597if (!curLabel)598curLabel = self->zoneNameLabels[self->labelID];599600int32 distance = 0x1000000;601EntityUIText *labelPtr = NULL;602if (curLabel->position.x < 0x1000000) {603foreach_active(UIText, label)604{605if (label->position.x > 0x1000000) {606int32 pos = abs(label->position.y - curLabel->position.y);607if (pos < distance) {608distance = pos;609labelPtr = label;610}611}612}613}614else {615foreach_active(UIText, label)616{617if (label->position.x < 0x1000000) {618int32 dist = abs(label->position.y - curLabel->position.y);619if (dist < distance) {620distance = dist;621labelPtr = label;622}623}624}625}626627int32 labelID = self->labelID;628for (int32 i = 0; i < self->labelCount; ++i) {629if (self->stageIDLabels[i] == labelPtr || self->zoneNameLabels[i] == labelPtr) {630labelID = i;631break;632}633}634635if (self->labelID != labelID) {636LevelSelect_SetLabelHighlighted(false);637638self->labelID = labelID;639LevelSelect_SetLabelHighlighted(true);640}641}642643void LevelSelect_HandleNewStagePos(void)644{645RSDK_THIS(LevelSelect);646647EntityUIText *curLabel = self->stageIDLabels[self->labelID];648if (!curLabel)649curLabel = self->zoneNameLabels[self->labelID];650651if (curLabel->selectable) {652char buffer[32];653RSDK.GetCString(buffer, &curLabel->tag);654RSDK.SetScene(buffer, "");655SceneInfo->listPos += curLabel->data0;656657#if MANIA_USE_PLUS658if (self->labelID == self->labelCount - 4)659SceneInfo->listPos += self->offsetUFO;660else if (self->labelID == self->labelCount - 3)661SceneInfo->listPos += self->offsetBSS;662else if (globals->gameMode == MODE_ENCORE)663SceneInfo->listPos = Zone_GetListPos_EncoreMode();664#endif665666int32 leaderID = 0;667if (self->leaderCharacterID > 0)668leaderID = 1 << (self->leaderCharacterID - 1);669670int32 sidekickID = 0;671if (self->sidekickCharacterID > 0)672sidekickID = 1 << (self->sidekickCharacterID - 1);673674globals->playerID = leaderID | (sidekickID << 8);675676// MSZ1K check677if (CHECK_CHARACTER_ID(ID_KNUCKLES, 1) && curLabel->data0 == 15)678++SceneInfo->listPos;679680self->timer = 0;681self->state = LevelSelect_State_FadeOut;682self->stateDraw = LevelSelect_Draw_Fade;683Music_FadeOut(0.1);684}685else {686RSDK.PlaySfx(LevelSelect->sfxFail, false, 255);687}688}689690#if GAME_INCLUDE_EDITOR691void LevelSelect_EditorDraw(void) {}692693void LevelSelect_EditorLoad(void)694{695if (UIPicture)696UIPicture->aniFrames = RSDK.LoadSpriteAnimation("LSelect/Icons.bin", SCOPE_STAGE);697698if (UIText)699UIText->aniFrames = RSDK.LoadSpriteAnimation("LSelect/Text.bin", SCOPE_STAGE);700}701#endif702703void LevelSelect_Serialize(void) {}704705706