// Description: // High level infrastructure for game. // // Copyright (C) 2001 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 #include #include #include #include #include #include #include #include #include #include #include Game::Game( void) { XTRACE(); } Game::~Game() { XTRACE(); LOG_INFO << "Shutting down..." << endl; #if 1 // save config stuff ConfigS::instance()->saveToFile(); // save leaderboard ScoreKeeperS::instance()->save(); #endif StarfieldS::cleanup(); // misc cleanup MenuManagerS::cleanup(); StageManagerS::cleanup(); ModelManagerS::cleanup(); InputS::cleanup(); AudioS::cleanup(); VideoS::cleanup(); //Video calls SDL_Quit LPathManagerS::cleanup(); ParticleGroupManagerS::cleanup(); WeaponDepotS::cleanup(); ResourceManagerS::cleanup(); ScoreKeeperS::cleanup(); HeroS::cleanup(); //has to be after ParticleGroupManager } bool Game::init( void) { XTRACE(); bool result = true; ScoreKeeperS::instance()->load(); // init subsystems et al if( ! ParticleGroupManagerS::instance()->init()) return false; if( ! AudioS::instance()->init()) return false; if( ! VideoS::instance()->init()) return false; if( ! InputS::instance()->init()) return false; if( ! MenuManagerS::instance()->init()) return false; if( ! HeroS::instance()->init()) return false; StarfieldS::instance()->init( -150.0); ParticleGroupManager *pgm = ParticleGroupManagerS::instance(); if( ! pgm->init()) return false; //init all the paricle groups and links between them pgm->addGroup( HERO_GROUP, 1); pgm->addGroup( ENEMIES_GROUP, 300); pgm->addGroup( ENEMIES_BULLETS_GROUP, 300); //there are 3 effect groups to give very simple control over the order //of drawing which is important for alpha blending. pgm->addGroup( EFFECTS_GROUP1, 1000); pgm->addGroup( EFFECTS_GROUP2, 1000); pgm->addGroup( EFFECTS_GROUP3, 1000); pgm->addGroup( HERO_BULLETS_GROUP, 300); pgm->addGroup( BONUS_GROUP, 50); pgm->addGroup( SHOOTABLE_ENEMY_BULLETS_GROUP, 100); pgm->addGroup( SHOOTABLE_BONUS_GROUP, 50); //collision detect between the following groups pgm->addLink( HERO_BULLETS_GROUP, SHOOTABLE_ENEMY_BULLETS_GROUP); pgm->addLink( HERO_BULLETS_GROUP, ENEMIES_GROUP); pgm->addLink( HERO_BULLETS_GROUP, SHOOTABLE_BONUS_GROUP); pgm->addLink( HERO_GROUP, ENEMIES_GROUP); pgm->addLink( HERO_GROUP, ENEMIES_BULLETS_GROUP); pgm->addLink( HERO_GROUP, BONUS_GROUP); pgm->addLink( HERO_GROUP, SHOOTABLE_ENEMY_BULLETS_GROUP); pgm->addLink( HERO_GROUP, SHOOTABLE_BONUS_GROUP); //reset our stopwatch GameState::stopwatch.reset(); GameState::stopwatch.pause(); if( ! StageManagerS::instance()->init()) return false; ConfigS::instance()->getFloat( "horsePower", GameState::horsePower); //add our hero... ParticleGroupManagerS::instance()-> getParticleGroup( HERO_GROUP)->newParticle( string("Hero"), 0, 0, -100); LOG_INFO << "Initialization complete OK." << endl; return result; } void Game::reset( void) { //reset in order to start new game ParticleGroupManagerS::instance()->reset(); HeroS::instance()->reset(); ParticleGroupManagerS::instance()-> getParticleGroup( HERO_GROUP)->newParticle( string("Hero"), 0, 0, -100); ScoreKeeperS::instance()->resetCurrentScore(); GameState::stopwatch.reset(); GameState::startOfGameStep = GameState::stopwatch.getTime(); StageManagerS::instance()->reset(); } void Game::startNewGame( void) { GameS::instance()->reset(); GameState::context = Context::eInGame; InputS::instance()->disableInterceptor(); GameState::stopwatch.start(); AudioS::instance()->playSample( "sounds/voiceGo.wav"); bool allowVerticalMovement = false; ConfigS::instance()->getBoolean( "allowVerticalMovement", allowVerticalMovement); HeroS::instance()->allowVerticalMovement(allowVerticalMovement); } void Game::updateOtherLogic( void) { int stepCount = 0; float currentTime = Timer::getTime(); while( (currentTime - GameState::startOfStep) > GAME_STEP_SIZE) { StarfieldS::instance()->update(); //FIXME: shouldn't run all the time... MenuManagerS::instance()->update(); //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) { // adding new enemies, moving on to the next level, etc. StageManagerS::instance()->update(); // update all objects, particles, etc. ParticleGroupManagerS::instance()->update(); //FIXME: Currently the Critterboard is updated in the video system. Should be on its own. VideoS::instance()->updateLogic(); //advance to next start-of-game-step point in time GameState::startOfGameStep += GAME_STEP_SIZE; 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(); Video &video = *VideoS::instance(); Input &input = *InputS::instance(); //make sure we start of in menu mode MenuManagerS::instance()->turnMenuOn(); // Here it is: the main loop. LOG_INFO << "Entering Main loop." << endl; GameState::startOfStep = Timer::getTime(); GameState::startOfGameStep = GameState::stopwatch.getTime(); while( GameState::isAlive) { switch( GameState::context) { case Context::eInGame: //stuff that only needs updating when game is actually running updateInGameLogic(); break; default: break; } //stuff that should run all the time updateOtherLogic(); input.update(); audio.update(); video.update(); } }