summary refs log tree commit diff stats
path: root/compiler/injectdestructors.nim
Commit message (Expand)AuthorAgeFilesLines
* cursor and mutation tracking fixes (#15113)Andreas Rumpf2020-07-301-2/+2
* fixes #15076 (#15095)Andreas Rumpf2020-07-271-13/+20
* fixes a closure iterator memory leaks, progress on #15076Araq2020-07-271-1/+1
* fixes #15038 [backport:1.2]Andreas Rumpf2020-07-261-46/+1
* fixes #15036Andreas Rumpf2020-07-231-2/+0
* arc: cursors for simple for loop variables (#15008)Andreas Rumpf2020-07-171-1/+1
* Show that a variable is cursor in --expandArc (#15002)Clyybber2020-07-171-6/+5
* An optimizer for ARC (#14962)Andreas Rumpf2020-07-151-26/+28
* Fix #14985 (#14988)Clyybber2020-07-151-2/+2
* injectdestructors fixes and refactor (#14964)Clyybber2020-07-141-192/+109
* fixes a minor regressionAraq2020-07-101-1/+1
* fixes the tcontrolflow regression, clen idea of an escaping expressionAraq2020-07-101-20/+44
* no wasMoved() calls after destructors necessaryAraq2020-07-101-6/+6
* optimize sinks even when in a loopAraq2020-07-101-23/+12
* Move `wasMoved` out of `=destroy`Clyybber2020-07-101-5/+5
* CosmeticsClyybber2020-07-101-52/+51
* Make arc compile laser againClyybber2020-07-081-1/+1
* fixes #14900, this time for real, maybe (#14934)Andreas Rumpf2020-07-081-2/+8
* fixes #14900Araq2020-07-081-38/+42
* fixes #14899Araq2020-07-081-4/+37
* progressAraq2020-07-081-17/+33
* injectdestructors: refactoring, added more cases explicitly (#14929)Andreas Rumpf2020-07-071-7/+68
* scoped memory management (#14790)Andreas Rumpf2020-07-041-416/+301
* DFA and injectdestructors cleanup (#14824)Clyybber2020-06-281-22/+26
* Fix #14568 (#14583)Clyybber2020-06-071-23/+23
* fixes #14118 (#14595)Andreas Rumpf2020-06-071-1/+4
* more checking for --gc:arc, no need for valgrind (#14467)Andreas Rumpf2020-05-291-0/+2
* New "ping-pong" DFA (#14322)Clyybber2020-05-151-32/+38
* fix detecting closure env for nested asts (#14326)b3liever2020-05-121-3/+3
* fix #14219 (#14225)cooldome2020-05-111-1/+1
* small refactoring (#14303)b3liever2020-05-111-3/+4
* better error messageAntonis2020-05-111-1/+1
* fix closure env checkAntonis2020-05-111-1/+2
* Fix the DFA for "unstructured controlflow" (#14263)Clyybber2020-05-071-6/+2
* destructors: don't produce stupid code for 'cast' (#14208) [backport:1.2]Andreas Rumpf2020-05-041-2/+6
* Step2: fixes #13781, fixes #13805 (#13897)cooldome2020-04-161-4/+9
* Implements RFCs #209 (#13995)cooldome2020-04-161-16/+57
* fixes #13722 (#13729)Andreas Rumpf2020-03-231-1/+1
* disable even more of scope based destruction handling; fixes #13709Araq2020-03-231-7/+98
* fixes #13691 (#13694)Andreas Rumpf2020-03-191-2/+5
* arc optimizations (#13325)Andreas Rumpf2020-03-181-81/+240
* fixes #13622 (#13679)Andreas Rumpf2020-03-181-2/+4
* Fixes #13659 (#13674)cooldome2020-03-171-2/+2
* fixes #13436 (#13615)Andreas Rumpf2020-03-101-1/+2
* sink parameter inference for types that have destructors (#13544)Andreas Rumpf2020-03-041-6/+6
* fixes #13368 (#13397)cooldome2020-02-141-7/+8
* ARC: optimize complete object constructors to use nimNewObjUninitAraq2020-01-261-2/+4
* make sink operator optional (#13068)cooldome2020-01-171-37/+23
* Cleanup DFA (#13173)Clyybber2020-01-161-2/+2
* ARC: misc bugfixes (#13156)Andreas Rumpf2020-01-151-1/+2
ddd } /* Generic.Deleted */ .highlight .ge { font-style: italic } /* Generic.Emph */ .highlight .ges { font-weight: bold; font-style: italic } /* Generic.EmphStrong */ .highlight .gr { color: #aa0000 } /* Generic.Error */ .highlight .gh { color: #333333 } /* Generic.Heading */ .highlight .gi { color: #000000; background-color: #ddffdd } /* Generic.Inserted */ .highlight .go { color: #888888 } /* Generic.Output */ .highlight .gp { color: #555555 } /* Generic.Prompt */ .highlight .gs { font-weight: bold } /* Generic.Strong */ .highlight .gu { color: #666666 } /* Generic.Subheading */ .highlight .gt { color: #aa0000 } /* Generic.Traceback */ .highlight .kc { color: #008800; font-weight: bold } /* Keyword.Constant */ .highlight .kd { color: #008800; font-weight: bold } /* Keyword.Declaration */ .highlight .kn { color: #008800; font-weight: bold } /* Keyword.Namespace */ .highlight .kp { color: #008800 } /* Keyword.Pseudo */ .highlight .kr { color: #008800; font-weight: bold } /* Keyword.Reserved */ .highlight .kt { color: #888888; font-weight: bold } /* Keyword.Type */ .highlight .m { color: #0000DD; font-weight: bold } /* Literal.Number */ .highlight .s { color: #dd2200; background-color: #fff0f0 } /* Literal.String */ .highlight .na { color: #336699 } /* Name.Attribute */ .highlight .nb { color: #003388 } /* Name.Builtin */ .highlight .nc { color: #bb0066; font-weight: bold } /* Name.Class */ .highlight .no { color: #003366; font-weight: bold } /* Name.Constant */ .highlight .nd { color: #555555 } /* Name.Decorator */ .highlight .ne { color: #bb0066; font-weight: bold } /* Name.Exception */ .highlight .nf { color: #0066bb; font-weight: bold } /* Name.Function */ .highlight .nl { color: #336699; font-style: italic } /* Name.Label */ .highlight .nn { color: #bb0066; font-weight: bold } /* Name.Namespace */ .highlight .py { color: #336699; font-weight: bold } /* Name.Property */ .highlight .nt { color: #bb0066; font-weight: bold } /* Name.Tag */ .highlight .nv { color: #336699 } /* Name.Variable */ .highlight .ow { color: #008800 } /* Operator.Word */ .highlight .w { color: #bbbbbb } /* Text.Whitespace */ .highlight .mb { color: #0000DD; font-weight: bold } /* Literal.Number.Bin */ .highlight .mf { color: #0000DD; font-weight: bold } /* Literal.Number.Float */ .highlight .mh { color: #0000DD; font-weight: bold } /* Literal.Number.Hex */ .highlight .mi { color: #0000DD; font-weight: bold } /* Literal.Number.Integer */ .highlight .mo { color: #0000DD; font-weight: bold } /* Literal.Number.Oct */ .highlight .sa { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Affix */ .highlight .sb { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Backtick */ .highlight .sc { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Char */ .highlight .dl { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Delimiter */ .highlight .sd { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Doc */ .highlight .s2 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Double */ .highlight .se { color: #0044dd; background-color: #fff0f0 } /* Literal.String.Escape */ .highlight .sh { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Heredoc */ .highlight .si { color: #3333bb; background-color: #fff0f0 } /* Literal.String.Interpol */ .highlight .sx { color: #22bb22; background-color: #f0fff0 } /* Literal.String.Other */ .highlight .sr { color: #008800; background-color: #fff0ff } /* Literal.String.Regex */ .highlight .s1 { color: #dd2200; background-color: #fff0f0 } /* Literal.String.Single */ .highlight .ss { color: #aa6600; background-color: #fff0f0 } /* Literal.String.Symbol */ .highlight .bp { color: #003388 } /* Name.Builtin.Pseudo */ .highlight .fm { color: #0066bb; font-weight: bold } /* Name.Function.Magic */ .highlight .vc { color: #336699 } /* Name.Variable.Class */ .highlight .vg { color: #dd7700 } /* Name.Variable.Global */ .highlight .vi { color: #3333bb } /* Name.Variable.Instance */ .highlight .vm { color: #336699 } /* Name.Variable.Magic */ .highlight .il { color: #0000DD; font-weight: bold } /* Literal.Number.Integer.Long */
#include "pong.h"
#include <raylib.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];
    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] = 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 */
    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] = 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;
    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);
			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);
	    	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, DARKGRAY);
            DrawText("<", 0,0,128,WHITE);
			i = 0;
            for(i = 0; i < 10; i++) {
				printf("%d\n", 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;
    DisableCursor();

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

		/* Gamepad & Keyboard */
		if(IsGamepadButtonPressed(0, GAMEPAD_BUTTON_LEFT_FACE_DOWN) || IsKeyPressed(KEY_S)) {
			Choice++;
		} else if(IsGamepadButtonPressed(0, GAMEPAD_BUTTON_LEFT_FACE_UP) || IsKeyPressed(KEY_W)) {
			Choice--;
		}
		if(Choice > 4) {
			Choice = 4;
		} else if(Choice < 0) {
			Choice = 0;
		}
        if (IsKeyPressed(KEY_ESCAPE)) {
            EnableCursor();
            MouseCursorIn = false;
        } else if (IsCursorOnScreen() && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) {
            DisableCursor();
            MouseCursorIn = true;
        }
		/* Mouse */
		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;
		}

		/* Select 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(((CheckCollisionRecs(Mouse, *Selected) && IsMouseButtonPressed(MOUSE_BUTTON_LEFT)) || IsKeyPressed(KEY_SPACE) || IsGamepadButtonPressed(0, GAMEPAD_BUTTON_RIGHT_FACE_DOWN))) {
			switch(Choice) {
				case 2:
					settings(&MainCamera, TitleMusic);
					break;
				case 3:
					OpenURL("docs/index.html");
					EnableCursor();
					break;
				case 4:
					TitleScreenGoing = false;
					GameGoing = false;
				default:
					TitleScreenGoing = false;
					break;
			}
		}

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