#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;
}