#include #include "pong.h" #include "sounds.h" struct LeaderboardEntries { int Score; char Name[16]; }; struct LeaderboardEntries Top10[10]; /* Fun fact, I don't know what this does for the most part! * Okay, well I kind of do. It sorts the leaderboard from highest score to lowest. * But uh, I don't know how it does, but it works! */ 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]; int size = 0; for (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] = GC_malloc(sizeof(struct LeaderboardEntries)); if (*TmpStore == NULL) { /* Check if memory got assigned correctly. */ exit(1); } /* Load Scores and Names */ size = 0; int a = 0; for (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); int i = 0; for (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 */ GC_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]; int size = 0; for (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] = GC_malloc(sizeof(struct LeaderboardEntries)); if (*Scores == NULL) { /* Check if memory got assigned correctly. */ exit(1); } int i = 0; for (i = 0; i < (UINT16_MAX * 2); i++) { strcpy(Scores[i]->Name, " "); } /* Load Scores and Names. */ size = 0; int a = 0; for (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; bool BackButtonSelected = 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; int i = 0; for (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); BackButtonSelected = false; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { if (Page > 0) { Page--; } } } else if (CheckCollisionRecs(MouseCursor, NextPage) && EndOfPages == false) { DrawRectangleRec(NextPage, RED); BackButtonSelected = false; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { if (EndOfPages == false) { Page++; } } } switch (player_controls_pressed()) { case CONTROLLER_RIGHT: if (EndOfPages == false) { ++Page; } break; case CONTROLLER_LEFT: if (Page > 0) { --Page; } break; case CONTROLLER_UP: BackButtonSelected = true; break; case CONTROLLER_ACTIVATE: if (BackButtonSelected == true) { LookingAtScores = false; } break; default: break; } DrawText("<--", 5, 720 - 50, 48, WHITE); DrawText("-->", 1280 - 70, 720 - 50, 48, WHITE); /* Exit Button */ if (CheckCollisionRecs(MouseCursor, (Rectangle){0, 0, 42, 120}) || BackButtonSelected == true) { DrawRectangle(0, 0, 42, 120, RED); BackButtonSelected = true; if (IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) { LookingAtScores = false; } } DrawText("<", 0, 0, 128, WHITE); /* Cursor */ DrawTexture(MouseCursorSprite, MouseCursor.x, MouseCursor.y, WHITE); EndMode2D(); EndDrawing(); } clear_input_buffer(); 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; bool OnBackButton = true; int Choice = 0; 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; } } switch (player_controls_pressed()) { case CONTROLLER_ACTIVATE: if (Choice == 0) { SettingsGoing = false; } break; case CONTROLLER_UP: --Choice; break; case CONTROLLER_DOWN: ++Choice; break; case CONTROLLER_LEFT: switch (Choice) { case 1: GlobalSettings.MusicVolume -= 10; break; case 2: GlobalSettings.SoundVolume -= 10; play_audio(SOUND_BOUNCE); break; default: break; } break; case CONTROLLER_RIGHT: switch (Choice) { case 1: GlobalSettings.MusicVolume += 10; break; case 2: GlobalSettings.SoundVolume += 10; play_audio(SOUND_BOUNCE); break; default: break; } break; } /* Prevent out-of-bounds volume settings. */ if (GlobalSettings.MusicVolume > 100) { GlobalSettings.MusicVolume = 100; } else if (GlobalSettings.MusicVolume < 0) { GlobalSettings.MusicVolume = 0; } if (GlobalSettings.SoundVolume > 100) { GlobalSettings.SoundVolume = 100; } else if (GlobalSettings.SoundVolume < 0) { GlobalSettings.SoundVolume = 0; } 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); /* Draw Selection */ OnBackButton = false; Color MusicVolumeBarColor = DARKGRAY; Color SoundVolumeBarColor = DARKGRAY; switch (Choice) { case 0: OnBackButton = true; break; case 1: MusicVolumeBarColor = YELLOW; break; case 2: SoundVolumeBarColor = YELLOW; break; default: break; } DrawRectangle(0, 0, 1280, 720, (Color){20, 20, 20, 255}); /* Back Button */ if (CheckCollisionRecs(MouseCursor, BackButton) || OnBackButton == true) { 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, MusicVolumeBarColor); int i = 0; for (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, SoundVolumeBarColor); i = 0; for (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 (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); DrawRectangleRec(MouseCursor, 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; } 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; bool LeaderboardButtonSelected = false; DisableCursor(); bool OutOfScoreScreen = false; while (TitleScreenGoing == true && GameGoing == true) { 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; } } /* Check which button the mouse is hovering over. */ if (CheckCollisionRecs(Mouse, Versus)) { Choice = 0; } else if (CheckCollisionRecs(Mouse, Marathon)) { Choice = 1; } else if (CheckCollisionRecs(Mouse, Settings)) { Choice = 2; } else if (CheckCollisionRecs(Mouse, Help)) { Choice = 3; } else if (CheckCollisionRecs(Mouse, Exit)) { Choice = 4; } else if (CheckCollisionRecs(Mouse, AllScores)) { LeaderboardButtonSelected = true; } /* Gamepad & Keyboard */ switch (player_controls_pressed()) { case CONTROLLER_UP: --Choice; break; case CONTROLLER_DOWN: ++Choice; break; case CONTROLLER_RIGHT: if (Choice == 1) { LeaderboardButtonSelected = true; break; } break; case CONTROLLER_LEFT: if (LeaderboardButtonSelected == true) { LeaderboardButtonSelected = false; } break; default: break; } /* Unselect the Leaderboard button if Marathon is no longer selected. */ if (Choice != 1) { LeaderboardButtonSelected = false; } /* Unlock, and locking cursor */ if (IsKeyPressed(KEY_ESCAPE)) { EnableCursor(); MouseCursorIn = false; } else if (IsCursorOnScreen() && IsMouseButtonPressed(MOUSE_BUTTON_LEFT) && MouseCursorIn != true) { DisableCursor(); BeginDrawing(); EndDrawing(); MouseCursorIn = true; } /* Highlight selected Menu Item */ switch (Choice) { case 0: Selected = &Versus; break; case 1: Selected = &Marathon; break; case 2: Selected = &Settings; break; case 3: Selected = &Help; break; case 4: Selected = &Exit; break; default: break; } /* Activate menu item */ if (player_controls_pressed() == CONTROLLER_ACTIVATE) { switch (Choice) { case 1: if (OutOfScoreScreen == false && LeaderboardButtonSelected == false) { return (Choice); } break; case 2: clear_input_buffer(); settings(&MainCamera, TitleMusic); break; case 3: clear_input_buffer(); OpenURL("docs/index.html"); EnableCursor(); break; case 4: TitleScreenGoing = false; GameGoing = false; break; default: TitleScreenGoing = false; break; } } /* Make sure choice selector doesn't go off screen */ if (Choice > 4) { Choice = 4; } else if (Choice < 0) { Choice = 0; } OutOfScoreScreen = false; 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); DrawRectangleRec(Mouse, WHITE); if (Choice == 1) { DrawText("Leaderboard:", 600, 0, 48, WHITE); if (NoScores == 1) { goto skip; } char LeaderboardText[1024]; int i = 1; for (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 (LeaderboardButtonSelected == true) { DrawRectangleRec(AllScores, RED); if (player_controls_pressed() == CONTROLLER_ACTIVATE) { score_screen(&MainCamera); OutOfScoreScreen = true; } } 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(); if (OutOfScoreScreen == true) { clear_input_buffer(); } } printf("%d\n", Choice); Mix_HaltMusic(); Mix_FreeMusic(TitleMusic); return Choice; }