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