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