Example #1
0
int main(int argc, char *argv[])
{
	Conversation conversation;
	bool debugMode = false;
	for(const char *const *it = argv + 1; *it; ++it)
	{
		string arg = *it;
		if(arg == "-h" || arg == "--help")
		{
			PrintHelp();
			return 0;
		}
		else if(arg == "-v" || arg == "--version")
		{
			PrintVersion();
			return 0;
		}
		else if(arg == "-t" || arg == "--talk")
			conversation = LoadConversation();
		else if(arg == "-d" || arg == "--debug")
			debugMode = true;
	}
	PlayerInfo player;
	
	try {
		SDL_Init(SDL_INIT_VIDEO);
		
		// Begin loading the game data.
		GameData::BeginLoad(argv);
		Audio::Init(GameData::Sources());
		
		// On Windows, make sure that the sleep timer has at least 1 ms resolution
		// to avoid irregular frame rates.
#ifdef _WIN32
		timeBeginPeriod(1);
#endif
		
		player.LoadRecent();
		player.ApplyChanges();
		
		// Check how big the window can be.
		SDL_DisplayMode mode;
		if(SDL_GetCurrentDisplayMode(0, &mode))
			return DoError("Unable to query monitor resolution!");
		
		Preferences::Load();
		Uint32 flags = SDL_WINDOW_OPENGL | SDL_WINDOW_RESIZABLE | SDL_WINDOW_SHOWN | SDL_WINDOW_ALLOW_HIGHDPI;
		if(Preferences::Has("fullscreen"))
			flags |= SDL_WINDOW_FULLSCREEN_DESKTOP;
		
		// Make the window just slightly smaller than the monitor resolution.
		int maxWidth = mode.w;
		int maxHeight = mode.h;
		// Restore this after toggling fullscreen.
		int restoreWidth = 0;
		int restoreHeight = 0;
		if(maxWidth < 640 || maxHeight < 480)
			return DoError("Monitor resolution is too small!");
		
		if(Screen::RawWidth() && Screen::RawHeight())
		{
			// Never allow the saved screen width to be leaving less than 100
			// pixels free around the window. This avoids the problem where you
			// maximize without going full-screen, and next time the window pops
			// up you can't access the resize control because it is offscreen.
			Screen::SetRaw(
				min(Screen::RawWidth(), (maxWidth - 100)),
				min(Screen::RawHeight(), (maxHeight - 100)));
			if(flags & SDL_WINDOW_FULLSCREEN_DESKTOP)
			{
				restoreWidth = Screen::RawWidth();
				restoreHeight = Screen::RawHeight();
				Screen::SetRaw(maxWidth, maxHeight);
			}
		}
		else
			Screen::SetRaw(maxWidth - 100, maxHeight - 100);
		// Make sure the zoom factor is not set too high for the full UI to fit.
		if(Screen::Height() < 700)
			Screen::SetZoom(100);
		
		// Create the window.
		SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
#ifdef _WIN32
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MAJOR_VERSION, 3);
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_MINOR_VERSION, 0);
#endif
		SDL_GL_SetAttribute(SDL_GL_CONTEXT_PROFILE_MASK, SDL_GL_CONTEXT_PROFILE_CORE);
		
		SDL_Window *window = SDL_CreateWindow("Endless Sky",
			SDL_WINDOWPOS_UNDEFINED, SDL_WINDOWPOS_UNDEFINED,
			Screen::RawWidth(), Screen::RawHeight(), flags);
		if(!window)
			return DoError("Unable to create window!");
		
		SDL_GLContext context = SDL_GL_CreateContext(window);
		if(!context)
			return DoError("Unable to create OpenGL context! Check if your system supports OpenGL 3.0.", window);
		
		if(SDL_GL_MakeCurrent(window, context))
			return DoError("Unable to set the current OpenGL context!", window, context);
		
		SDL_GL_SetSwapInterval(1);
		
		// Initialize GLEW.
#ifndef __APPLE__
		glewExperimental = GL_TRUE;
		if(glewInit() != GLEW_OK)
			return DoError("Unable to initialize GLEW!", window, context);
#endif
		
		// Check that the OpenGL version is high enough.
		const char *glVersion = reinterpret_cast<const char *>(glGetString(GL_VERSION));
		if(!glVersion || !*glVersion)
			return DoError("Unable to query the OpenGL version!", window, context);
		
		const char *glslVersion = reinterpret_cast<const char *>(glGetString(GL_SHADING_LANGUAGE_VERSION));
		if(!glslVersion || !*glslVersion)
		{
			ostringstream out;
			out << "Unable to query the GLSL version. OpenGL version is " << glVersion << ".";
			return DoError(out.str(), window, context);
		}
		
		if(*glVersion < '3')
		{
			ostringstream out;
			out << "Endless Sky requires OpenGL version 3.0 or higher." << endl;
			out << "Your OpenGL version is " << glVersion << ", GLSL version " << glslVersion << "." << endl;
			out << "Please update your graphics drivers.";
			return DoError(out.str(), window, context);
		}
		
		glClearColor(0.f, 0.f, 0.0f, 1.f);
		glEnable(GL_BLEND);
		glDisable(GL_DEPTH_TEST);
		glBlendFunc(GL_ONE, GL_ONE_MINUS_SRC_ALPHA);
		
		GameData::LoadShaders();
		
		{
			// Check whether this is a high-DPI window.
			int width = 0;
			int height = 0;
			SDL_GL_GetDrawableSize(window, &width, &height);
			Screen::SetHighDPI(width > Screen::RawWidth() && height > Screen::RawHeight());
			
			// Fix a possible race condition leading to the wrong window dimensions.
			glViewport(0, 0, width, height);
		}
		
		
		UI gamePanels;
		UI menuPanels;
		menuPanels.Push(new MenuPanel(player, gamePanels));
		if(!conversation.IsEmpty())
			menuPanels.Push(new ConversationPanel(player, conversation));
		
		string swizzleName = "_texture_swizzle";
#ifndef __APPLE__
		const char *extensions = reinterpret_cast<const char *>(glGetString(GL_EXTENSIONS));
		if(!strstr(extensions, swizzleName.c_str()))
#else
		bool hasSwizzle = false;
		GLint extensionCount;
		glGetIntegerv(GL_NUM_EXTENSIONS, &extensionCount);
		for(GLint i = 0; i < extensionCount && !hasSwizzle; ++i)
		{
			const char *extension = reinterpret_cast<const char *>(glGetStringi(GL_EXTENSIONS, i));
			hasSwizzle = (extension && strstr(extension, swizzleName.c_str()));
		}
		if(!hasSwizzle)
#endif
			menuPanels.Push(new Dialog(
				"Note: your computer does not support the \"texture swizzling\" OpenGL feature, "
				"which Endless Sky uses to draw ships in different colors depending on which "
				"government they belong to. So, all human ships will be the same color, which "
				"may be confusing. Consider upgrading your graphics driver (or your OS)."));
		
		FrameTimer timer(60);
		bool isPaused = false;
		while(!menuPanels.IsDone())
		{
			// Handle any events that occurred in this frame.
			SDL_Event event;
			while(SDL_PollEvent(&event))
			{
				UI &activeUI = (menuPanels.IsEmpty() ? gamePanels : menuPanels);
				
				// The caps lock key slows the game down (to make it easier to
				// see and debug things that are happening quickly).
				if(debugMode && (event.type == SDL_KEYDOWN || event.type == SDL_KEYUP)
						&& event.key.keysym.sym == SDLK_CAPSLOCK)
				{
					timer.SetFrameRate((event.key.keysym.mod & KMOD_CAPS) ? 10 : 60);
				}
				else if(debugMode && event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_BACKQUOTE)
				{
					isPaused = !isPaused;
				}
				else if(event.type == SDL_KEYDOWN && menuPanels.IsEmpty()
						&& Command(event.key.keysym.sym).Has(Command::MENU)
						&& !gamePanels.IsEmpty() && gamePanels.Top()->IsInterruptible())
				{
					menuPanels.Push(shared_ptr<Panel>(
						new MenuPanel(player, gamePanels)));
				}
				else if(event.type == SDL_QUIT)
				{
					menuPanels.Quit();
				}
				else if(event.type == SDL_WINDOWEVENT && event.window.event == SDL_WINDOWEVENT_SIZE_CHANGED)
				{
					int width = event.window.data1 & ~1;
					int height = event.window.data2 & ~1;
					if(width != Screen::RawWidth() || height != Screen::RawHeight())
					{
						Screen::SetRaw(width, height);
						if((event.window.data1 | event.window.data2) & 1)
							SDL_SetWindowSize(window, Screen::RawWidth(), Screen::RawHeight());
						SDL_GL_GetDrawableSize(window, &width, &height);
						glViewport(0, 0, width, height);
					}
				}
				else if(event.type == SDL_KEYDOWN
						&& (Command(event.key.keysym.sym).Has(Command::FULLSCREEN)
						|| (event.key.keysym.sym == SDLK_RETURN && event.key.keysym.mod & KMOD_ALT)))
				{
					if(restoreWidth)
					{
						SDL_SetWindowFullscreen(window, 0);
						Screen::SetRaw(restoreWidth, restoreHeight);
						SDL_SetWindowSize(window, Screen::RawWidth(), Screen::RawHeight());
						restoreWidth = 0;
						restoreHeight = 0;
					}
					else
					{
						restoreWidth = Screen::RawWidth();
						restoreHeight = Screen::RawHeight();
						Screen::SetRaw(maxWidth, maxHeight);
						SDL_SetWindowFullscreen(window, SDL_WINDOW_FULLSCREEN_DESKTOP);
					}
					int width, height;
					SDL_GL_GetDrawableSize(window, &width, &height);
					glViewport(0, 0, width, height);
				}
				else if(activeUI.Handle(event))
				{
					// No need to do anything more!
				}
			}
			Font::ShowUnderlines(SDL_GetModState() & KMOD_ALT);
			
			// Tell all the panels to step forward, then draw them.
			((!isPaused && menuPanels.IsEmpty()) ? gamePanels : menuPanels).StepAll();
			Audio::Step();
			// That may have cleared out the menu, in which case we should draw
			// the game panels instead:
			(menuPanels.IsEmpty() ? gamePanels : menuPanels).DrawAll();
			
			SDL_GL_SwapWindow(window);
			timer.Wait();
		}
		
		// If you quit while landed on a planet, save the game.
		if(player.GetPlanet())
			player.Save();
		
		// The Preferences class reads the screen dimensions, so update them if
		// the window is full screen:
		bool isFullscreen = (restoreWidth != 0);
		Preferences::Set("fullscreen", isFullscreen);
		if(isFullscreen)
			Screen::SetRaw(restoreWidth, restoreHeight);
		Preferences::Save();
		
		Cleanup(window, context);
	}
	catch(const runtime_error &error)
	{
		DoError(error.what());
	}
	
	return 0;
}