about summary refs log blame commit diff stats
path: root/src/main.c
blob: 51942dd757777810df032f59e212b02d726e9edf (plain) (tree)
1
2
3
4
5
6
7
8
9
                 

                   
                   
                      
                        
 

                               
                                   
                               


                   
                        



                                                  
                                           
                                                                                   
                                                                                                                   
                                                                                                 






                                                                                                                                     
                                                        








                                                                                                                   
                                         






                                                                                                                            
                             
                                       
                           


                                  

                                              
         
                  

 
                    
              


                                                          
                                                                
                                                                      



                                                                           



                                       
                                             


                                                
                                                                           

                                                                    
                                                                               

                                                                
                                                                               

                                                          
                                                                                
                                                      


                                                                                    
                                                              
                                                                                    

                                                             
                                                                                   
                                                      
                                                                  
                                                                    



                                                      
                                                                       
                                                   
                                                                         

                         
                                              
         
                  


                                  
                       
                                               






                                                        
                                                                 



                                     
                                                 


                    
                                  


                                      
                             



                                                     
                                    
 




                                                             


                                 
                        
                                                                              
        
                              
                                              
                                   
         
 

                                                    


                                                                                             
                                             

                       
                                                                   



                                                                                                   
                                                   











                                                                             
                                                 
         
        
                                   











                                                                
                             

                          
 
                                                 







                                                            






                                                            



                                                            

                                      
                 
         
        
                                                                 


                                            


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