#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; } } SetWindowPosition(0, 0); 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; } double Started; static int internal_clock() { static const double Framerate = 1.0/60.0f; double OldTime = GetTime(); double DeltaTime = 0; double NewTime = 0; Started = GetTime(); struct timespec SleepFor = { 0, 99999 }; while(GameGoing == true) { nanosleep(&SleepFor, NULL); /* Reduces CPU usage */ NewTime = GetTime(); DeltaTime += (NewTime - OldTime)/Framerate; OldTime = NewTime; while(DeltaTime >= 1.0) { SDL_AtomicAdd(&Ticks, 1); DeltaTime--; } /* This will make sure no matter where you are in the game, it will close when it should. */ if (WindowShouldClose()) { GameGoing = false; } } 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 *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 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[]) { /* Credits acknowledgements */ fprintf(stdout, "Game Icon (resources/icon.svg) by 2xYz is licensed under Creative Commons. Check docs/credits.html for more info.\n"); /* Raylib Init */ SDL_Init(SDL_INIT_AUDIO); InitWindow(1280, 720, "Pong"); SDL_AtomicSet(&Ticks, 0); SDL_Thread *InternalClock = SDL_CreateThread(internal_clock, "Internal Clock", NULL); 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; /* Init Variables */ strncpy(VersionString, "Version 0.4 - APOLLO", sizeof(VersionString)); /* Populate Audio Queue */ unsigned int i = 0; for(i = 0; i < 20; i++) { AudioQueue[i] = -1; } /* Threading */ AudioQueueBeingModified = SDL_CreateMutex(); 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); }