#include "pong.h" int Difficulty = 1; SDL_atomic_t Ticks; bool GameGoing = true; char VersionString[256]; struct Settings GlobalSettings; SDL_mutex *AudioQueueBeingModified; SDL_atomic_t AudioInitializing; int AudioQueue[20]; void set_screen_mode() { ClearWindowState(FLAG_VSYNC_HINT); ClearWindowState(FLAG_WINDOW_TOPMOST); ClearWindowState(FLAG_WINDOW_UNDECORATED); ClearWindowState(FLAG_FULLSCREEN_MODE); switch(GlobalSettings.Fullscreen) { case 1: //Real Fullscreen is fickle as fuck. So it needs a timeout. SetWindowSize(GetMonitorWidth(GetCurrentMonitor()), GetMonitorHeight(GetCurrentMonitor())); int Timeout = SDL_AtomicGet(&Ticks)+300; //Set the timeout for 5 seconds. while(GetScreenHeight() != GetMonitorHeight(GetCurrentMonitor())) { BeginDrawing(); EndDrawing(); if(SDL_AtomicGet(&Ticks) >= Timeout) { //Default to windowed if fullscreen fails for whatever reason. goto FullScreenFailed; } } SetWindowState(FLAG_VSYNC_HINT); SetWindowState(FLAG_FULLSCREEN_MODE); break; case 2: SetWindowState(FLAG_WINDOW_TOPMOST); SetWindowState(FLAG_WINDOW_UNDECORATED); SetWindowPosition(0, 0); SetWindowSize(GetMonitorWidth(GetCurrentMonitor()), GetMonitorHeight(GetCurrentMonitor())); break; default: FullScreenFailed: SetWindowSize(1280, 720); SetWindowPosition(GetMonitorHeight(GetCurrentMonitor())/5, GetMonitorHeight(GetCurrentMonitor())/5); break; } return; } static int internal_clock() { const struct timespec Delay = { 0, 16666666 }; struct timespec Remaining; while(GameGoing == true) { SDL_AtomicAdd(&Ticks, 1); nanosleep(&Delay, &Remaining); } return(0); } static int audio() { int i; Mix_Init(0); Mix_OpenAudio(48000, MIX_DEFAULT_FORMAT, 2, 1024); Mix_AllocateChannels(64); Mix_Chunk *Bounce = Mix_LoadWAV("resources/bounce.wav"); //Mix_Chunk *TitleScreen = Mix_LoadWAV("resources/title.wav"); Mix_Chunk *Victory = Mix_LoadWAV("resources/victory.wav"); Mix_Chunk *Defeat = Mix_LoadWAV("resources/defeat.wav"); Mix_Chunk *PlayerScore = Mix_LoadWAV("resources/score_player.wav"); Mix_Chunk *EnemyScore = Mix_LoadWAV("resources/score_enemy.wav"); const struct timespec Delay = { 0, 20000000 }; struct timespec Remaining; SDL_AtomicSet(&AudioInitializing, 1); while(GameGoing == true) { for(i = 0; i < 20; i++) { if(AudioQueue[i] != -1){ Mix_Volume(-1, GlobalSettings.SoundVolume); switch(AudioQueue[i]) { case 0: //Play bounce sound. Mix_PlayChannel(-1, Bounce, 0); break; case 1: //Play game over Mix_PlayChannel(-1, Defeat, 0); break; case 2: //Play win Mix_PlayChannel(-1, Victory, 0); break; //case 3: //Title Screen // Mix_PlayChannel(-1, TitleScreen, 0); // break; case 4: //Player Score Mix_PlayChannel(-1, PlayerScore, 0); break; case 5: //Enemy Score Mix_PlayChannel(-1, EnemyScore, 0); break; case 99: //Stop All Sounds Mix_HaltChannel(-1); break; default: break; } SDL_LockMutex(AudioQueueBeingModified); AudioQueue[i] = -1; SDL_UnlockMutex(AudioQueueBeingModified); } } nanosleep(&Delay, &Remaining); } return(0); } bool play_audio(int SoundEffect) { unsigned int i; SDL_LockMutex(AudioQueueBeingModified); for(i = 1; i != 20; i++) { if (AudioQueue[i-1] == -1) { AudioQueue[i-1] = AudioQueue[i]; } } for(i = 0; AudioQueue[i] != -1; i++) { if(i > sizeof(AudioQueue)) { SDL_UnlockMutex(AudioQueueBeingModified); return false; } } AudioQueue[i] = SoundEffect; SDL_UnlockMutex(AudioQueueBeingModified); return true; } int main(int argc, char *argv[]) { //Raylib Init InitWindow(1280, 720, "Pong"); SetTargetFPS(60); SetExitKey(KEY_NULL); Image Icon = LoadImage("resources/ball.png"); SetWindowIcon(Icon); SetWindowState(FLAG_VSYNC_HINT); SetWindowState(FLAG_WINDOW_RESIZABLE); SetWindowMinSize(1280, 720); //Settings declaration to prevent undefined behavior. GlobalSettings.Fullscreen = 0; GlobalSettings.SoundVolume = 0; GlobalSettings.MusicVolume = 0; //SDL Init SDL_Init(SDL_INIT_AUDIO); //Init Variables strncpy(VersionString, "Version 0.3 - Charon", sizeof(VersionString)); //Populate Audio Queue for(unsigned int i = 0; i < 20; i++) { AudioQueue[i] = -1; } //Threading AudioQueueBeingModified = SDL_CreateMutex(); SDL_AtomicSet(&Ticks, 0); SDL_Thread *InternalClock = SDL_CreateThread(internal_clock, "Internal Clock", NULL); SDL_Thread *AudioThread = SDL_CreateThread(audio, "Audio", NULL); SDL_AtomicSet(&AudioInitializing, 0); //Load Settings char *SettingsDirectory = SDL_GetPrefPath("iotib", "Pong"); char SettingsFilePath[8192]; snprintf(SettingsFilePath, sizeof(SettingsFilePath), "%s/settings.txt", SettingsDirectory); FILE *SettingsFile; reopen: //Create settings file if it doesn't exist. if ((SettingsFile = fopen(SettingsFilePath, "r")) == NULL) { if(SettingsFile != NULL) { fclose(SettingsFile); } if ((SettingsFile = fopen(SettingsFilePath, "w")) == NULL) { fprintf(stderr, "Unable to create settings file.\n"); exit(1); } fprintf(SettingsFile, "sound_volume 100\n"); fprintf(SettingsFile, "music_volume 100\n"); fprintf(SettingsFile, "fullscreen 0"); fclose(SettingsFile); goto reopen; //Try opening again. } // Parse the settings file. char Option[2048]; int Value; while(!feof(SettingsFile)) { fscanf(SettingsFile, "%s %i", Option, &Value); if(strcmp(Option, "sound_volume") == 0) { GlobalSettings.SoundVolume = Value; } else if(strcmp(Option, "music_volume") == 0) { GlobalSettings.MusicVolume = Value; } else if(strcmp(Option, "fullscreen") == 0) { GlobalSettings.Fullscreen = Value; } } fclose(SettingsFile); set_screen_mode(); // Wait for audio to finish initializing. struct timespec a = { 0, 5000000 }; struct timespec b; while(SDL_AtomicGet(&AudioInitializing) == 0) { nanosleep(&a, &b); //Prevent heavy cpu usage } // Launch Game while (GameGoing == true) { switch(title_screen()) { case 0: //Versus play_audio(STOP_ALL_SOUNDS); versus_main(); break; case 1: //Marathon play_audio(STOP_ALL_SOUNDS); marathon_main(); break; default: break; } } GameGoing = false; // Make sure the game is going to end. SDL_WaitThread(InternalClock, NULL); SDL_WaitThread(AudioThread, NULL); SDL_Quit(); CloseWindow(); return(0); }