// Description: // High level infrastructure for game. // // Copyright (C) 2003 Frank Becker // // This program is free software; you can redistribute it and/or modify it under // the terms of the GNU General Public License as published by the Free Software // Foundation; either version 2 of the License, or (at your option) any later // version. // // This program is distributed in the hope that it will be useful, but WITHOUT // ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS // FOR A PARTICULAR PURPOSE. See the GNU General Public License for more details // #include #include #include #include #include #include #include #include #include Game::Game( void): _model(0), _controller(0), _view(0), _zo(0), _oStream(0), _zi(0), _iStream(0) { XTRACE(); } Game::~Game() { XTRACE(); LOG_INFO << "Shutting down..." << endl; if( _view) _view->close(); AudioS::cleanup(); delete _model; delete _controller; delete _view; delete _zo; delete _oStream; delete _zi; delete _iStream; // save config stuff ConfigS::instance()->saveToFile(); } bool Game::init( void) { XTRACE(); bool result = true; if( ! AudioS::instance()->init()) return false; EventInjector *ei = 0; string play; if( ConfigS::instance()->getString( "play", play)) { _iStream = new ifstream( play.c_str(), ios::in | ios::binary); _zi = new ziStream( *_iStream); string line; getline( *_zi, line); getline( *_zi, line); Tokenizer token(line); unsigned int version = strtoul(token.next().c_str(), 0, 10); unsigned int seed = strtoul(token.next().c_str(), 0, 10); LOG_INFO << "Line [" << line << "] version=" << version << " seed=" << seed << endl; GameState::r250.reset(seed); ei = new EventInjector(*_zi); } EventWatcher *ew = 0; string record; if( ConfigS::instance()->getString( "record", record)) { _oStream = new ofstream( record.c_str(), ios::out | ios::binary); ostream *os = _oStream; bool compress = false; ConfigS::instance()->getBoolean( "compress", compress); if( compress) { _zo = new zoStream( *_oStream); os = _zo; } char rTime[128]; time_t t; time(&t); strftime( rTime, 127, "%a %d-%b-%Y at %H:%M", localtime(&t)); (*os) << "# Game recorded on " << rTime << "\n"; (*os) << "1 " << GameState::r250.getSeed() << "\n"; ew = new EventWatcher(*os); } int dimx = 5; int dimy = 5; int dimz = 12; int startLevel = 1; ConfigS::instance()->getInteger( "shaftWidth", dimx); ConfigS::instance()->getInteger( "shaftHeight", dimy); ConfigS::instance()->getInteger( "shaftDepth", dimz); ConfigS::instance()->getInteger( "startLevel", startLevel); _model = new BlockModel( dimx, dimy, dimz, startLevel, GameState::r250); if( ! _model->init()) { return false; } _view = new BlockViewGLSmooth( *_model); _model->registerView( _view); _controller = new BlockController( *_model, *_view); if( ew) _controller->setEventWatcher( ew); if( ei) _controller->setEventInjector( ei); if( ! _view->init()) return false; //reset our stopwatch GameState::stopwatch.reset(); GameState::stopwatch.pause(); LOG_INFO << "Initialization complete OK." << endl; return result; } void Game::reset( void) { //reset in order to start new game GameState::stopwatch.reset(); GameState::startOfGameStep = GameState::stopwatch.getTime(); GameState::gameTick = 0; } void Game::updateOtherLogic( void) { int stepCount = 0; float currentTime = Timer::getTime(); while( (currentTime - GameState::startOfStep) > GAME_STEP_SIZE) { //advance to next start-of-game-step point in time GameState::startOfStep += GAME_STEP_SIZE; currentTime = Timer::getTime(); stepCount++; if( stepCount > MAX_GAME_STEPS) break; } GameState::frameFractionOther = (currentTime - GameState::startOfStep) / GAME_STEP_SIZE; if( stepCount > 1) { LOG_WARNING << "Skipped " << stepCount << " frames." << endl; if( GameState::frameFractionOther > 1.0) { //Our logic is still way behind where it should be at this //point in time. If we get here we already ran through //MAX_GAME_STEPS logic runs trying to catch up. //We clamp the value to 1.0, higher values would try //to predict were we are visually. Maybe not a bad idead //to allow values up to let's say 1.3... GameState::frameFractionOther = 1.0; } } } void Game::updateInGameLogic( void) { int stepCount = 0; float currentGameTime = GameState::stopwatch.getTime(); while( (currentGameTime - GameState::startOfGameStep) > GAME_STEP_SIZE) { if( ! _controller->update()) GameState::requestExit = true; if( GameState::isAlive) { if( ! _model->update()) GameState::isAlive = false; } _view->update(); //XXX: keep seperate view state? //advance to next start-of-game-step point in time GameState::startOfGameStep += GAME_STEP_SIZE; GameState::gameTick++; currentGameTime = GameState::stopwatch.getTime(); stepCount++; if( stepCount > MAX_GAME_STEPS) break; } GameState::frameFraction = (currentGameTime - GameState::startOfGameStep) / GAME_STEP_SIZE; if( stepCount > 1) { if( GameState::frameFraction > 1.0) { //Our logic is still way behind where it should be at this //point in time. If we get here we already ran through //MAX_GAME_STEPS logic runs trying to catch up. //We clamp the value to 1.0, higher values would try //to predict were we are visually. Maybe not a bad idead //to allow values up to let's say 1.3... GameState::frameFraction = 1.0; } } } void Game::run( void) { XTRACE(); Audio &audio = *AudioS::instance(); // Here it is: the main loop. LOG_INFO << "Entering Main loop." << endl; reset(); GameState::startOfStep = Timer::getTime(); GameState::startOfGameStep = GameState::stopwatch.getTime(); while( ! GameState::requestExit) { #if 0 switch( GameState::context) { case Context::eInGame: //stuff that only needs updating when game is actually running updateInGameLogic(); break; default: break; } #else updateInGameLogic(); #endif //stuff that should run all the time updateOtherLogic(); audio.update(); _view->draw(); } LOG_INFO << "Game Over!\n"; LOG_INFO << "Score: " << _model->getScore() << "\n"; LOG_INFO << "Blocks: " << _model->getElementCount() << "\n"; }