#include "pong.h" struct LeaderboardEntries { int Score; char Name[16]; }; struct LeaderboardEntries Top10[10]; static int compare_int(const void *Score1, const void *Score2) { const struct LeaderboardEntries *Entry1 = *(struct LeaderboardEntries**) Score1; const struct LeaderboardEntries *Entry2 = *(struct LeaderboardEntries**) Score2; if (Entry1->Score > Entry2->Score) { return -1; } return +1; } static int order_leaderboard() { // Find leaderboard file, and open it. char *LeaderboardDirectory = SDL_GetPrefPath("iotib", "Pong"); char LeaderboardFilePath[8192]; snprintf(LeaderboardFilePath, sizeof(LeaderboardFilePath), "%s/leaderboard.txt", LeaderboardDirectory); FILE *LeaderboardFile; if((LeaderboardFile = fopen(LeaderboardFilePath, "r")) == NULL) { return(1); //If it doesn't exist yet, we don't need to care. } // Check if file is empty. fscanf(LeaderboardFile, "\n"); if(feof(LeaderboardFile)) { return(1); } rewind(LeaderboardFile); //If not , rewind the stream. // Init a temporary pointer to store scores and names into. This will be organized into Top10. struct LeaderboardEntries *TmpStore[UINT16_MAX*2]; for (int size = 0; size < (UINT16_MAX*2); size++) //If there's 131,000 entries, I dunno what to tell you. You really like pong... TmpStore[size] = malloc(sizeof(struct LeaderboardEntries)); if(*TmpStore == NULL) { //Check if memory got assigned correctly. exit(1); } // Load Scores and Names int size = 0; for (int a = 0; !feof(LeaderboardFile); a++, size++) { //Load data from file into array. fscanf(LeaderboardFile, "%s %d\n", TmpStore[a]->Name, &TmpStore[a]->Score); } qsort(TmpStore, size, sizeof(struct LeaderboardEntries *), compare_int); for(int i = 0; i < size; i++) { //Copy first 10 elements into top10 strcpy(Top10[i].Name, TmpStore[i]->Name); Top10[i].Score = TmpStore[i]->Score; } // Cleanup // Free TmpStore free(*TmpStore); //Truth be told, I dunno if this even works, since the amount of memory used is kilobytes... // Close leaderboard file. if (LeaderboardFile != NULL) { fclose(LeaderboardFile); } return(0); } static void score_screen(Camera2D *MainCamera) { // Find leaderboard file, and open it. char *LeaderboardDirectory = SDL_GetPrefPath("iotib", "Pong"); char LeaderboardFilePath[8192]; snprintf(LeaderboardFilePath, sizeof(LeaderboardFilePath), "%s/leaderboard.txt", LeaderboardDirectory); FILE *LeaderboardFile; if((LeaderboardFile = fopen(LeaderboardFilePath, "r")) == NULL) { return; //If it doesn't exist yet, we don't need to care. } // Init a temporary pointer to store scores and names into. This will be organized into Top10. struct LeaderboardEntries *Scores[UINT16_MAX*2]; for (int size = 0; size < (UINT16_MAX*2); size++) //If there's 131,000 entries, I dunno what to tell you. You really like pong... Scores[size] = malloc(sizeof(struct LeaderboardEntries)); if(*Scores == NULL) { //Check if memory got assigned correctly. exit(1); } for (int i = 0; i < (UINT16_MAX*2); i++) { strcpy(Scores[i]->Name, " "); } // Load Scores and Names int size = 0; for (int a = 0; !feof(LeaderboardFile); a++, size++) { //Load data from file into array. fscanf(LeaderboardFile, "%s %d", Scores[a]->Name, &Scores[a]->Score); if(feof(LeaderboardFile)) { break; } } qsort(Scores, size, sizeof(struct LeaderboardEntries *), compare_int); //Begin drawing scores. //Mouse bool LookingAtScores = true; bool MouseCursorIn = true; SetMousePosition(1280/2, 720/2); Vector2 OldPosition = GetMousePosition(); Vector2 NewPosition = GetMousePosition(); Rectangle MouseCursor = {1280/2.0f,720/2.0f,1,1}; Texture2D MouseCursorSprite = LoadTexture("resources/cursor.png"); //Page Buttons Rectangles Rectangle PrevPage = {5, 720-50, 70, 45}; Rectangle NextPage = {1280-70, 720-50, 70, 45}; int Page = 0; bool EndOfPages = false; while(LookingAtScores == true && GameGoing == true) { // Update Camera MainCamera->zoom = GetScreenHeight()/720.0f; MainCamera->offset = (Vector2){GetScreenWidth()/2.0f, GetScreenHeight()/2.0f}; MainCamera->target = (Vector2){1280/2.0f, 720/2.0f}; //Mouse if (MouseCursorIn == true) { OldPosition = NewPosition; NewPosition = GetMousePosition(); MouseCursor.y -= OldPosition.y-NewPosition.y; MouseCursor.x -= OldPosition.x-NewPosition.x; if (MouseCursor.y >= 720 || MouseCursor.y <= 0) { MouseCursor.y += OldPosition.y-NewPosition.y; } if (MouseCursor.x >= 1280 || MouseCursor.x <= 0) { MouseCursor.x += OldPosition.x-NewPosition.x; } } if (IsKeyPressed(KEY_ESCAPE)) { EnableCursor(); MouseCursorIn = false; } else if (IsCursorOnScreen() && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { DisableCursor(); MouseCursorIn = true; } EndOfPages = false; BeginDrawing(); ClearBackground(BLACK); BeginMode2D(*MainCamera); DrawRectangle(0, 0, 1280, 720, (Color){20, 20, 20, 255}); // Scores int a = 0; for (int i = 1+(10*Page); i <= 10+(10*Page); i++, a++) { if(Scores[i-1]->Name[0] != ' ') { DrawText(TextFormat("%d. %s: %d", i, Scores[i-1]->Name, Scores[i-1]->Score), 460, 60+(50*a), 48, WHITE); } else { EndOfPages = true; } } // Page Buttons if(CheckCollisionRecs(MouseCursor, PrevPage) && Page > 0) { DrawRectangleRec(PrevPage, RED); if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { if(Page > 0) { Page--; } } } else if(CheckCollisionRecs(MouseCursor, NextPage) && EndOfPages == false) { DrawRectangleRec(NextPage, RED); if(IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { if(EndOfPages == false) { Page++; } } } DrawText("<--", 5, 720-50, 48, WHITE); DrawText("-->", 1280-70, 720-50, 48, WHITE); //Exit Button if(CheckCollisionRecs(MouseCursor, (Rectangle){0,0,42,120})) { DrawRectangle(0, 0, 42, 120, RED); if(IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { LookingAtScores = false; } } DrawText("<", 0, 0, 128, WHITE); // Cursor DrawTexture(MouseCursorSprite, MouseCursor.x, MouseCursor.y, WHITE); EndMode2D(); EndDrawing(); } return; } static void settings(Camera2D *MainCamera, Mix_Music *TitleScreenMusic) { bool SettingsGoing = true; int MusicBarY = 50; int SoundBarY = 150; Rectangle MusicBar[10] = { {50,MusicBarY,50,50}, {105,MusicBarY,50,50}, {160,MusicBarY,50,50}, {215,MusicBarY,50,50}, {270,MusicBarY,50,50}, {325,MusicBarY,50,50}, {380,MusicBarY,50,50}, {435,MusicBarY,50,50}, {490,MusicBarY,50,50}, {545,MusicBarY,50,50}, }; Rectangle SoundBar[10] = { {50,SoundBarY,50,50}, {105,SoundBarY,50,50}, {160,SoundBarY,50,50}, {215,SoundBarY,50,50}, {270,SoundBarY,50,50}, {325,SoundBarY,50,50}, {380,SoundBarY,50,50}, {435,SoundBarY,50,50}, {490,SoundBarY,50,50}, {545,SoundBarY,50,50}, }; // Back Selection Rectangle BackButton = { 0,0,42,120 }; //Screen Buttons int ScreenButtonLength = 250; Rectangle ScreenButtons[3] = { {95, 242, ScreenButtonLength, 42}, {95, 284, ScreenButtonLength, 42}, {95, 326, ScreenButtonLength, 42} }; SetMousePosition(GetScreenWidth()/2, GetScreenHeight()/2); // Mouse Rectangle MouseCursor = { 1280/2.0f,720/2.0f,1,1 }; Vector2 OldPosition = GetMousePosition(); Vector2 NewPosition = GetMousePosition(); Texture2D MouseCursorSprite = LoadTexture("resources/cursor.png"); bool MouseCursorIn = true; while(SettingsGoing == true && GameGoing == true) { MainCamera->zoom = GetScreenHeight()/720.0f; MainCamera->offset = (Vector2){GetScreenWidth()/2.0f, GetScreenHeight()/2.0f}; MainCamera->target = (Vector2){1280/2.0f, 720/2.0f}; //Mouse if (MouseCursorIn == true) { OldPosition = NewPosition; NewPosition = GetMousePosition(); MouseCursor.y -= OldPosition.y-NewPosition.y; MouseCursor.x -= OldPosition.x-NewPosition.x; if (MouseCursor.y >= 720 || MouseCursor.y <= 0) { MouseCursor.y += OldPosition.y-NewPosition.y; } if (MouseCursor.x >= 1280 || MouseCursor.x <= 0) { MouseCursor.x += OldPosition.x-NewPosition.x; } } if (IsKeyPressed(KEY_ESCAPE)) { EnableCursor(); MouseCursorIn = false; } else if (IsCursorOnScreen() && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { DisableCursor(); MouseCursorIn = true; } Mix_VolumeMusic(GlobalSettings.MusicVolume); BeginDrawing(); ClearBackground(BLACK); BeginMode2D(*MainCamera); DrawRectangle(0, 0, 1280, 720, (Color){20, 20, 20, 255}); // Back Button if (CheckCollisionRecs(MouseCursor, BackButton)) { DrawRectangleRec(BackButton, RED); if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { SettingsGoing = false; } } // Music DrawText("Music Volume:", 50, 10, 42, WHITE); DrawText("<", 0,0,128,WHITE); DrawRectangle(45, MusicBarY-5, 555, 60, DARKGRAY); for(int i = 0; i < 10; i++) { if (CheckCollisionRecs(MouseCursor, MusicBar[i]) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { GlobalSettings.MusicVolume = i*10; } if(i <= (GlobalSettings.MusicVolume/10)) { DrawRectangleRec(MusicBar[i], RED); } } // Sound DrawText("Sound Volume:", 50, 108, 42, WHITE); DrawRectangle(45, SoundBarY-5, 555, 60, DARKGRAY); DrawText("<", 0,0,128,WHITE); for(int i = 0; i < 10; i++) { if (CheckCollisionRecs(MouseCursor, SoundBar[i]) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { play_audio(SOUND_BOUNCE); GlobalSettings.SoundVolume = i*10; } if(i <= (GlobalSettings.SoundVolume/10)) { DrawRectangleRec(SoundBar[i], RED); } } // Fullscreen DrawRectangle(95, 243, 250, 125, DARKGRAY); bool MouseHovering = false; for(int i = 0; i < 3; i++) { if(CheckCollisionRecs(MouseCursor, ScreenButtons[i])) { DrawRectangleRec(ScreenButtons[i], RED); MouseHovering = true; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { GlobalSettings.Fullscreen = i; set_screen_mode(); } } } if (MouseHovering == false) { DrawRectangleRec(ScreenButtons[GlobalSettings.Fullscreen], GREEN); } DrawText("Screen Mode:", 50, 205, 42, WHITE); DrawText("Windowed", 100, 242, 42, WHITE); DrawText("Fullscreen", 100, 284, 42, WHITE); DrawText("Borderless", 100, 326, 42, WHITE); DrawTexture(MouseCursorSprite, MouseCursor.x, MouseCursor.y, WHITE); EndMode2D(); EndDrawing(); } char *SettingsDirectory = SDL_GetPrefPath("iotib", "Pong"); char SettingsFilePath[8192]; snprintf(SettingsFilePath, sizeof(SettingsFilePath), "%s/settings.txt", SettingsDirectory); FILE *SettingsFile; // Put save to file here. if ((SettingsFile = fopen(SettingsFilePath, "w")) == NULL) { fprintf(stderr, "Unable to create settings file.\n"); exit(1); } fprintf(SettingsFile, "sound_volume %d\n", GlobalSettings.SoundVolume); fprintf(SettingsFile, "music_volume %d\n", GlobalSettings.MusicVolume); fprintf(SettingsFile, "fullscreen %d", GlobalSettings.Fullscreen); fclose(SettingsFile); return; } static void help_text() { } int title_screen() { // Init Camera Camera2D MainCamera; MainCamera.offset = (Vector2){0,0}; MainCamera.target = (Vector2){0,0}; MainCamera.rotation = 0.0f; // Load leaderboard int NoScores = order_leaderboard(); bool TitleScreenGoing = true; int Choice = 0; // Selection Rectangle Versus = { 20, 150, 230, 48 }; Rectangle Marathon = { 20, 200, 230, 48 }; Rectangle Settings = { 20, 250, 230, 48 }; Rectangle Help = { 20, 300, 230, 48 }; Rectangle Exit = { 20, 350, 230, 48 }; Rectangle Mouse = { 1280.0f/2, 720.0f/2, 10, 10 }; Rectangle AllScores = { 595, 720-155, 400, 55 }; Rectangle *Selected; Selected = &Versus; //Music Mix_Music *TitleMusic = Mix_LoadMUS("resources/title.wav"); Mix_PlayMusic(TitleMusic, -1); Mix_VolumeMusic(GlobalSettings.MusicVolume); // Mouse Vector2 OldPosition = GetMousePosition(); Vector2 NewPosition = GetMousePosition(); Texture2D MouseCursor = LoadTexture("resources/cursor.png"); bool MouseCursorIn = true; DisableCursor(); while(TitleScreenGoing == true && GameGoing == true) { printf("%lf, %d\n", GetTime(), SDL_AtomicGet(&Ticks)); if (WindowShouldClose()) { //Quit Game if the window is closed. GameGoing = false; TitleScreenGoing = false; } // Update Camera MainCamera.zoom = GetScreenHeight()/720.0f; MainCamera.offset = (Vector2){GetScreenWidth()/2.0f, GetScreenHeight()/2.0f}; MainCamera.target = (Vector2){1280/2.0f, 720/2.0f}; //Mouse if (MouseCursorIn == true) { OldPosition = NewPosition; NewPosition = GetMousePosition(); Mouse.y -= OldPosition.y-NewPosition.y; Mouse.x -= OldPosition.x-NewPosition.x; if (Mouse.y >= 720 || Mouse.y <= 0) { Mouse.y += OldPosition.y-NewPosition.y; } if (Mouse.x >= 1280 || Mouse.x <= 0) { Mouse.x += OldPosition.x-NewPosition.x; } } if (IsKeyPressed(KEY_ESCAPE)) { EnableCursor(); MouseCursorIn = false; } else if (IsCursorOnScreen() && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { DisableCursor(); MouseCursorIn = true; } if (CheckCollisionRecs(Mouse, Versus)) { Selected = &Versus; Choice = 0; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { TitleScreenGoing = false; } } else if (CheckCollisionRecs(Mouse, Marathon)) { Selected = &Marathon; Choice = 1; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { TitleScreenGoing = false; } } else if (CheckCollisionRecs(Mouse, Settings)) { Selected = &Settings; Choice = 0; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { settings(&MainCamera, TitleMusic); } } else if (CheckCollisionRecs(Mouse, Help)) { Selected = &Help; } else if (CheckCollisionRecs(Mouse, Exit)) { Selected = &Exit; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { GameGoing = false; Choice = -1; } } BeginDrawing(); ClearBackground(BLACK); BeginMode2D(MainCamera); DrawRectangleRec(Mouse, YELLOW); DrawRectangle(0, 0, 1280, 720, (Color){20, 20, 20, 255}); DrawRectangleRec(*Selected, RED); DrawText("PONG", 0, 0, 128, WHITE); DrawText("Versus", 20, 150, 48, WHITE); DrawText("Marathon", 20, 200, 48, WHITE); DrawText("Settings", 20, 250, 48, WHITE); DrawText("Help", 20, 300, 48, WHITE); DrawText("Exit", 20, 350, 48, WHITE); if(Choice == 1) { DrawText("Leaderboard:", 600, 0, 48, WHITE); if(NoScores == 1) { goto skip; } printf("%c\n", Top10[0].Name[0]); char LeaderboardText[1024]; for (int i = 1; i <= 10; i++) { if(Top10[i-1].Name[0] != '\0') { //If name is blank, that means we're at the end of the list. snprintf(LeaderboardText, sizeof(LeaderboardText), "%d: %s : %d", i, Top10[i-1].Name, Top10[i-1].Score); DrawText(LeaderboardText, 600, 50*i, 48, WHITE); } } } skip: if (Choice == 1) { if(CheckCollisionRecs(Mouse, AllScores)) { DrawRectangleRec(AllScores, RED); if(IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { score_screen(&MainCamera); } } DrawText("See All Scores...", 600, 720-150, 48, WHITE); } DrawTexture(MouseCursor, Mouse.x, Mouse.y, WHITE); EndMode2D(); DrawText(VersionString, GetScreenWidth()-400, GetScreenHeight()-32, 32, GREEN); EndDrawing(); } Mix_HaltMusic(); Mix_FreeMusic(TitleMusic); return Choice; }