#include #include #include #include #include #include "Config.h" #include "DPFLoader.h" #include "Game.h" #include "maps/GameMap.h" namespace rubyquill { namespace Z3 { //{ Static data & methods const char * Game::OPTIONSFILENAME = "options.json"; Game::Ptr Game::_instance = Ptr(); bool Game::_initialized = false; bool Game::_iswsmonitor = false; void Game::Init() { trace( "Game::Init\n" ); if (Game::_initialized ) { return; }; Config::_Init(); if ( CRM32Pro.Init( SDL_INIT_VIDEO | SDL_INIT_AUDIO | SDL_INIT_JOYSTICK | SDL_INIT_TIMER ) < 0 ) { throw SDL_Exception( std::string(SDL_GetError()) ); } ITimeSystem->Init(); ITimeSystem->SetRate(0,Config::FramesPerSecond); Config::_mainresourcehandle = IStuffDPF->Open( const_cast( Config::MainResourceFileName ) ); IMG_Init( IMG_INIT_JPG | IMG_INIT_PNG ); TTF_Init(); #ifdef DEBUG freopen("CON", "w", stdout); // redirects stdout back to console freopen("CON", "w", stderr); // redirects stderr back to console #endif atexit(Game::End); Game::_iswsmonitor = Game::MonitorIsWidescreen(); Game::_initialized = true; trace("Init okay \n"); } void Game::End(){ if (! Game::_initialized ) { return; }; IStuffDPF->Close( Config::_mainresourcehandle ); Game::_instance.reset(); TTF_Quit(); IMG_Quit(); CRM32Pro.Quit(); Game::_initialized = false; } /// Approximator; uses the current pixel mode to check for native modes, /// returning true if any are widescreen. bool Game::MonitorIsWidescreen() { SDL_Rect **modes; int i; float ar; const float threshold = 1.5555f; // average of 4:3 and 16:9 ratios; widescreen is AR greater than this. // Get available fullscreen/hardware modes. This must not be free()d. modes=SDL_ListModes(NULL, SDL_FULLSCREEN|SDL_HWSURFACE); // Check if there are any modes available if(modes == (SDL_Rect **)0){ return false; } // Look through the modes for a widescreen for(i=0; modes[i]; ++i) { ar = ((float)modes[i]->w) / ((float)modes[i]->h); if (ar > threshold) return true; } return false; } //} //{ Debug test harness #ifdef DEBUG void Game::testharness( SDL_Surface * blitto ) { } #endif //} //{ Constructors, Initializers, and Configuration Game::Game() { if (! Game::_initialized ) { Game::Init(); } _fsmode = Detect; _textfont = SDL::TrueTypeFont::Ptr(); _utilfont = SDL::TrueTypeFont::Ptr(); _gamepad = Gamepad::New( Gamepad::Single ); _lua = 0; } Game::~Game() { } void Game::ReadConfig() { using boost::property_tree::ptree; ptree pt; CRM32Pro.Config.VideoWidth = Game::SCREENWIDTH; CRM32Pro.Config.VideoHeight = Game::SCREENHEIGHT; CRM32Pro.Config.VideoRenderer = RENDER_DEFAULT; CRM32Pro.Config.VideoBPP = 0; // 0: autoselect best quality // Make sure the options file *exists*, even if empty. { FILE * f = fopen(OPTIONSFILENAME, "a+"); if (!f) { throw rubyquill::Exception( "Cannot access options file. Check the installation." ); }; fclose(f); } try { read_json( std::string(OPTIONSFILENAME), pt ); } catch (std::runtime_error e) { // Read failed? Leave it blank, which will get filled with defaults. } _fsmode = (FullscreenAR) pt.get("video.fullscreenshape", (int)Detect); CRM32Pro.Config.VideoWindow = pt.get("video.windowed", 1); // 0: fullscreen, 1: window, 2: undecorated window _gamepad->Load( pt, "main", Gamepad::Single ); } void Game::WriteConfig() const { using boost::property_tree::ptree; ptree pt; pt.put("video.fullscreenshape", (int)this->_fsmode); pt.put("video.windowed", (int)CRM32Pro.Config.VideoWindow); // TODO: Fix gamepad save. _gamepad->Save( pt, "main" ); write_json( std::string(OPTIONSFILENAME), pt ); } //} //{ Video Methods void Game::SetupVideoMode() { bool setwide = false; if (CRM32Pro.Config.VideoWindow == 0) { // 0: fullscreen if (_fsmode == Detect) { setwide = Game::_iswsmonitor; } else { setwide = (_fsmode == ForceWide); } if (setwide) { // TODO: Arrange for proper aspect ratios. For now, use 640x480 naive. CRM32Pro.Config.VideoAccel = ACCEL_HARDSMOOTH; if ( CRM32Pro.SetVideoMode() ) { return; } CRM32Pro.Config.VideoAccel = ACCEL_HARDSPEED; if ( CRM32Pro.SetVideoMode() ) { return; } CRM32Pro.Config.VideoAccel = ACCEL_SOFTWARE; if ( CRM32Pro.SetVideoMode() ) { return; } } else { CRM32Pro.Config.VideoAccel = ACCEL_HARDSMOOTH; if ( CRM32Pro.SetVideoMode() ) { return; } CRM32Pro.Config.VideoAccel = ACCEL_HARDSPEED; if ( CRM32Pro.SetVideoMode() ) { return; } CRM32Pro.Config.VideoAccel = ACCEL_SOFTWARE; if ( CRM32Pro.SetVideoMode() ) { return; } // Try several options, then fall back to windowed } } CRM32Pro.Config.VideoWindow = 1; // 1: Windowed normal CRM32Pro.Config.VideoAccel = ACCEL_HARDSMOOTH; if ( CRM32Pro.SetVideoMode() ) { return; } CRM32Pro.Config.VideoAccel = ACCEL_HARDSPEED; if ( CRM32Pro.SetVideoMode() ) { return; } CRM32Pro.Config.VideoAccel = ACCEL_SOFTWARE; if ( CRM32Pro.SetVideoMode() ) { return; } // At this point, must assume the user is on a VT-100 or something throw SDL_Exception("Cannot create video surface, even windowed. Check video drivers?"); } //} //{ Game Loops void Game::PreRun() { // Config. ReadConfig(); SetupVideoMode(); // Set up the lua environment. _lua = luaL_newstate(); luaL_openlibs(_lua); luaL_loadfile(_lua, "media/z3.lua"); lua_pcall(_lua, 0, LUA_MULTRET, 0); } void Game::PostRun( bool erroraborting ) { if( ! erroraborting ) { WriteConfig(); }; lua_close(_lua); _lua = NULL; } int Game::Run() { RunModeSelection nextmode = TitleMenu; PreRun(); _textfont = DPFLoader::LoadTTF( Config::_mainresourcehandle, const_cast(Config::TextFontName), Config::TextFontSize); try { do { switch(nextmode) { case Error: PostRun(true); return 1; case Quit: PostRun(false); return 0; case TitleMenu: nextmode = this->RunTitleMenu(); break; case Play: nextmode = this->RunPlay(); break; } } while(true); } catch (std::runtime_error e) { fprintf( stderr, "%s\n", e.what() ); PostRun(true); } return 1; } Game::RunModeSelection Game::RunTitleMenu() { SDL_Surface * screen = CRM32Pro.screen; // load an image SDL::SurfacePtr bkg = SDL::MakeShared( IImage->Load( Config::_mainresourcehandle, const_cast("titlescreen.png")) ); if (!bkg) { printf("Unable to load bitmap: %s\n", SDL_GetError()); return Error; } CRM32Pro.SetRenderCallback(0); CRM32Pro.CleanUp(); //CRM32Pro.RenderNeeded(UPDATEFRAME_NEVER); SDL_Color clrFg = {0xFF,0xFF,0xDD,0}; SDL::SurfacePtr sText = SDL::MakeShared( TTF_RenderText_Blended( _textfont->toSDL(), "Z3 v0.1", clrFg ) ); SDL_Rect textrect = {0,0,0,0}; // centre the bitmap on screen SDL_Rect dstrect = {0,0,0,0}; // -Main loop- SDL_PixelFormat * pf = screen->format; bool done = 0; SDL_Event event; #ifdef DEBUG testharness( screen ); #endif while(!done) { // 1.Logic frame stuff // ... SDL_FillRect( screen, 0, SDL_MapRGB(pf, 0, 0, 0x88) ); // draw bitmap SDL_BlitSurface(bkg.get(), 0, screen, &dstrect); SDL_BlitSurface(sText.get(), 0, screen, &textrect); // Event loop: update all systems: our graphics using render callback function,events,timing... while(CRM32Pro.Update(&event)) { switch(event.type) { case SDL_QUIT: return Quit; case SDL_KEYDOWN: break; case Gamepad::SDL_GAMEPADMOTION: if( _gamepad->isDown(Gamepad::GP_Down) ) { trace( "menu down\n" ); } else if (_gamepad->isDown(Gamepad::GP_Up) ) { trace( "menu up\n" ); } break; case Gamepad::SDL_GAMEPADBUTTON: if( _gamepad->isDown(Gamepad::GP_BtnA) || _gamepad->isDown(Gamepad::GP_BtnB) || _gamepad->isDown(Gamepad::GP_Start) ) { trace( "menu select\n" ); return Play; } if( _gamepad->isDown(Gamepad::GP_Back) || _gamepad->isDown(Gamepad::GP_Escape) ) { trace( "menu exit\n" ); return Quit; } break; default: break; } } } return Quit; }; Game::RunModeSelection Game::RunPlay() { SDL_Color clrFg = {0xFF,0xFF,0xDD,0}; _map = GameMap::Get(Config::NewGameFirstMap); trace( "Loaded map '%s', %d x %d.\n", Config::NewGameFirstMap, _map->GetWidth(), _map->GetHeight() ); _viewport.x = 0; _viewport.y = 0; _viewport.w = CRM32Pro.screen->w; _viewport.h = CRM32Pro.screen->h; CRM32Pro.SetRenderCallback(Game::RenderPlayHandle); CRM32Pro.CleanUp(); //CRM32Pro.RenderNeeded(UPDATEFRAME_ALWAYS); // -Main loop- bool done = 0; SDL_Event event; Gamepad::State padstate; while(!done) { // 1.Logic frame stuff // ... // Event loop: update all systems: our graphics using render callback function,events,timing... while(CRM32Pro.Update(&event)) { switch(event.type) { case SDL_QUIT: return Quit; case SDL_KEYDOWN: if(event.key.keysym.sym == SDLK_ESCAPE) return TitleMenu; break; case Gamepad::SDL_GAMEPADMOTION: break; case Gamepad::SDL_GAMEPADBUTTON: break; default: break; } } } CRM32Pro.SetRenderCallback(0); CRM32Pro.CleanUp(); //CRM32Pro.RenderNeeded(UPDATEFRAME_NEVER); return TitleMenu; }; void Game::RenderPlay(bool logicupdate) { if( /*!CRM32Pro.IsRenderNeeded() &&*/ (!logicupdate)) return; SDL_FillRect(CRM32Pro.screen, &(CRM32Pro.screen->clip_rect), 0); _map->BlitToViewport( CRM32Pro.screen, _viewport ); }; void Game::RenderPlayHandle(int bLogicUpdate) { Game::Get()->RenderPlay((bool) bLogicUpdate); }; //} } }