#include #include #include #include #include "Model.h" #include "Physics.h" #include "PhysicsBase.h" #include "EntityFactory.h" #include "AsteroidsEvents.h" #include "AsteroidEntity.h" #include namespace asteroids { static Model* ModelInstance = NULL; /* * Inherited Module functions */ int Model::OnInit (int argc, char* argv[]) { int result = Engine::ModelBase::OnInit (argc, argv); Engine::LogMessage ("Model Initialization!"); ModelInstance = this; mGameState = GameStatePaused; mLastGameState = GameStatePaused; /// \TODO use or similar for initialization of mCurrentLevelIndex mCurrentLevelIndex = 99999; mLevelName = ""; if (InitLevelList() == 0) Engine::LogError ("No levels found!"); // First we reset the highscore list mHighscoreList.clear(); // then we try to load values from the file LoadHighscoreList(); // if we have less than the usual number of entries we add default values if (mHighscoreList.size() < 10) { AddHighscoreEntry ("Imperator", 1000000); AddHighscoreEntry ("Darth Vader", 800000); AddHighscoreEntry ("Luke Skywalker", 600000); AddHighscoreEntry ("Han Solo", 400000); AddHighscoreEntry ("Princess Leia", 200000); AddHighscoreEntry ("C3PO", 100000); AddHighscoreEntry ("R2-D2", 50000); AddHighscoreEntry ("Chewy", 10000); AddHighscoreEntry ("Mr. Ewok", 5000); AddHighscoreEntry ("Jabba the Hutt", 1000); } // Reset the newest highscore entry index which may be used for highlighting // the newest entry. mNewestHighscoreEntryIndex = std::numeric_limits::max(); // initialize event handlers and register them Engine::RegisterListener (this, EventGameOver); Engine::RegisterListener (this, EventShipExplode); mPlayerName = "Player"; mLevelAuthor = ""; mLevelTitle = ""; Engine::RegisterListener (this, EventAccelerateStart); Engine::RegisterListener (this, EventAccelerateStop); return result; } bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) { switch (event->mEventType) { case EventAccelerateStart: Engine::PlaySoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav"), -1); break; case EventAccelerateStop: Engine::HaltSoundLoop(Engine::GetResourceFullPath("/data/sounds/thrust.wav")); break; case EventShipExplode: OnShipExplode(); break; case EventGameOver: return OnGameOver(); break; default: Engine::LogWarning ("Received Event with type %d but don't know what to do with it!", event->mEventType); break; } return false; } /* * Module specific functions */ void Model::Process () { if (mLastGameState == mGameState) { if (mGameState == GameStateRunning) { Engine::ModelBase::Process(); } return; } // when we are here we know that something has changed so we need to take // some action. Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState)); // ... and we have to set the last game state to the current gamestate // otherwise we end up in an infinite loop of performing the switching // action. mLastGameState = mGameState; } unsigned int Model::InitLevelList () { std::string level_dir_name = Engine::GetResourceFullPath("/data/levels/"); Engine::LogDebug ("Searching for levels in %s", level_dir_name.c_str()); mLevelList.clear(); boost::filesystem::path level_dir(level_dir_name); if (!boost::filesystem::exists(level_dir)) { Engine::LogError ("Could not init level list: \todo %s does not exist!"); } if (!boost::filesystem::is_directory(level_dir)) { Engine::LogError ("Could not init level list: \todo %s is not a directory!"); } boost::filesystem::directory_iterator end_iter; for (boost::filesystem::directory_iterator dir_iter(level_dir); dir_iter != end_iter; ++dir_iter) { if (boost::filesystem::is_regular_file (dir_iter->status())) { std::string level_relative_path (level_dir_name); level_relative_path += dir_iter->path().filename(); mLevelList.push_back (level_relative_path); Engine::LogDebug ("Found level %s", mLevelList[mLevelList.size()-1].c_str()); } } std::sort(mLevelList.begin(), mLevelList.end()); return mLevelList.size(); } void Model::LoadHighscoreList () { Engine::LogDebug ("Loading highscore file"); boost::filesystem::path highscore_file(Engine::GetUserDirFullPath("/highscore.dat")); // if the file does not exist, we create it and write standard values into // it. if (!boost::filesystem::exists(highscore_file)) return; if (!boost::filesystem::is_regular_file(highscore_file)) { Engine::LogError ("Could not load highscore file: %s is not a regular file!", highscore_file.filename().c_str()); } std::ifstream score_stream (highscore_file.filename().c_str()); while (!score_stream.eof()) { std::string name; unsigned int points; std::string line; getline (score_stream, line); std::string::size_type delimiter = line.find ('\t'); if (delimiter == std::string::npos) break; name = line.substr(0, delimiter); std::istringstream points_stream(line.substr(delimiter + 1, line.size())); points_stream >> points; Engine::LogDebug ("Read Highscore Entry Name: %s Points: %d", name.c_str(), points); AddHighscoreEntry (name, points); } } void Model::SaveHighscoreList () { std::ofstream highscore_file (Engine::GetUserDirFullPath("/highscore.dat").c_str()); std::list::iterator iter = mHighscoreList.begin(); while (iter != mHighscoreList.end()) { highscore_file << iter->name << "\t" << iter->points << std::endl; iter++; } highscore_file.close(); } bool highscore_cmp (Model::HighscoreEntry a, Model::HighscoreEntry b) { return a.points > b.points; } /** \brief Addes an entry to the highscore list and takes care that the list stays valid * * \TODO Re-think usage of mNewestHighscoreEntryIndex variable in this function */ unsigned int Model::AddHighscoreEntry(const std::string &name, const unsigned int points) { HighscoreEntry entry; entry.name = name; entry.points = points; unsigned int counter = 0; std::list::iterator iter = mHighscoreList.begin(); while (iter != mHighscoreList.end()) { if (points >= iter->points) { mHighscoreList.insert (iter, entry); mNewestHighscoreEntryIndex = counter; break; } iter++; counter ++; } if (mHighscoreList.size() < 10) { mHighscoreList.push_back(entry); mNewestHighscoreEntryIndex = mHighscoreList.size(); return mHighscoreList.size(); } while (mHighscoreList.size() > 10) { mHighscoreList.pop_back(); } if (counter < 10) return counter; // if we have all 10 entries then we can save // the highscore SaveHighscoreList(); mNewestHighscoreEntryIndex = 99999; return 99999; } int Model::DoLoadLevel (const char* filename) { Engine::LogMessage ("Loading level from %s", filename); std::fstream level_file (filename, std::ios::in); if (!level_file) { Engine::LogError ("Unable to open file %s for reading!", filename); exit (-1); } ClearEntities(); mAsteroids.clear(); std::string entity_type_str; int entity_count = 0; SetLevelTitle (""); SetLevelAuthor (""); while (level_file >> entity_type_str) { if (entity_type_str[0] == '#') { getline (level_file, entity_type_str); Engine::LogDebug ("Read Comment: %s", entity_type_str.c_str()); continue; } else if (entity_type_str == "Title") { std::string level_title; getline (level_file, level_title); level_title = strip_whitespaces (level_title); SetLevelTitle (level_title); continue; } else if (entity_type_str == "Author") { std::string level_author; getline (level_file, level_author); level_author = strip_whitespaces (level_author); SetLevelAuthor (level_author); continue; } GameEntityType entity_type = GameEntityTypeUnknown; if (entity_type_str == "GameEntityTypeShip") entity_type = GameEntityTypeShip; else if (entity_type_str == "GameEntityTypeAsteroid") entity_type = GameEntityTypeAsteroid; else { Engine::LogError ("Unknown Entity type: %s", entity_type_str.c_str()); exit (-1); } Engine::EntityBase* entity = CreateEntity (entity_type); bool is_player; level_file >> is_player; if (is_player) { mPlayerEntityId = entity->mId; Engine::LogDebug ("Entity with id '%d' is player", entity->mId); } level_file >> entity->mPhysicState->mPosition[0]; level_file >> entity->mPhysicState->mPosition[1]; level_file >> entity->mPhysicState->mPosition[2]; level_file >> entity->mPhysicState->mOrientation[0]; level_file >> entity->mPhysicState->mOrientation[1]; level_file >> entity->mPhysicState->mOrientation[2]; level_file >> entity->mPhysicState->mVelocity[0]; level_file >> entity->mPhysicState->mVelocity[1]; level_file >> entity->mPhysicState->mVelocity[2]; level_file >> entity->mPhysicState->mAngleVelocity; entity_count ++; } level_file.close(); Engine::LogDebug ("%d Entities loaded!", mEntities.size()); return 0; } int Model::DoSaveLevel (const char* filename) { Engine::LogMessage ("Saving level to %s", filename); std::fstream level_file (filename, std::ios::out); if (!level_file) { Engine::LogError ("Unable to open file %s for writing!", filename); exit (-1); } level_file << "# Format" << std::endl; level_file << "# " << std::endl; if (GetLevelTitle() != "") { level_file << "Title " << mLevelTitle << std::endl; } if (GetLevelAuthor() != "") { level_file << "Author " << mLevelAuthor << std::endl; } std::map::iterator iter = mEntities.begin(); unsigned int player_id = GetPlayerEntityId(); for (iter = mEntities.begin(); iter != mEntities.end(); iter++) { Engine::EntityBase* game_entity = iter->second; level_file << GetStringGameEntityType((GameEntityType)game_entity->mType) << "\t" // this stores the player id << (game_entity->mId == player_id) << "\t" << game_entity->mPhysicState->mPosition[0] << "\t" << game_entity->mPhysicState->mPosition[1] << "\t" << game_entity->mPhysicState->mPosition[2] << "\t" << game_entity->mPhysicState->mOrientation[0] << "\t" << game_entity->mPhysicState->mOrientation[1] << "\t" << game_entity->mPhysicState->mOrientation[2] << "\t" << game_entity->mPhysicState->mVelocity[0] << "\t" << game_entity->mPhysicState->mVelocity[1] << "\t" << game_entity->mPhysicState->mVelocity[2] << "\t" << game_entity->mPhysicState->mAngleVelocity << "\t" << std::endl; } level_file.close(); return 0; } void Model::ReloadLevel () { Engine::LogDebug ("Reloading level %d", mCurrentLevelIndex + 1); if (mCurrentLevelIndex < 0 || mCurrentLevelIndex >= mLevelList.size()) Engine::LogError ("Invalid level index: %u", mCurrentLevelIndex); DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str()); } void Model::ProceedToNextLevel () { Engine::LogDebug ("Proceeding to next level %d", mCurrentLevelIndex + 1); mCurrentLevelIndex++; if (mCurrentLevelIndex == mLevelList.size()) { Engine::EventBasePtr gameover_event (new Engine::EventBase()); gameover_event->mEventType = EventGameOver; QueueEvent (gameover_event); } else { DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str()); } } void Model::SetGameState (const unsigned int &state) { mLastGameState = mGameState; mGameState = state; } bool Model::OnGameOver() { Engine::LogMessage ("Points = %d lowest = %d", mPoints, mHighscoreList.back().points ); if (mPoints > mHighscoreList.back().points) { Engine::LogMessage ("New Highscore!"); AddHighscoreEntry (mPlayerName, mPoints); } return false; }; void Model::OnNewGame() { ClearEntities(); mNewestHighscoreEntryIndex = std::numeric_limits::max(); mPlayerLives = 3; mCurrentLevelIndex = 0; mPoints = 0; DoLoadLevel (mLevelList[mCurrentLevelIndex].c_str()); } void Model::OnShipExplode () { Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav")); mPlayerLives --; if (mPlayerLives == 0) { Engine::EventBasePtr gameover_event (new Engine::EventBase()); gameover_event->mEventType = EventGameOver; QueueEvent (gameover_event); } else { Engine::EventBasePtr playerdied_event (new Engine::EventBase()); playerdied_event->mEventType = EventPlayerDied; QueueEvent (playerdied_event); } } void Model::OnCreateEntity (const int type, const unsigned int id) { GameEntityType entity_type = (GameEntityType) type; if (entity_type == GameEntityTypeAsteroid) { mAsteroids.push_back (id); } } void Model::OnKillEntity (const Engine::EntityBase *entity) { GameEntityType entity_type = (GameEntityType) entity->mType; if (entity_type == GameEntityTypeAsteroid) { Engine::PlaySound(Engine::GetResourceFullPath("/data/sounds/rock_destroyed.wav")); unsigned int i; const AsteroidEntity *asteroid = static_cast(entity); if (GetPlayerEntityId() != Engine::NullEntityId) mPoints += 150 + asteroid->mSubAsteroidsCount * 75; for (i = 0; i < mAsteroids.size(); i++) { if (mAsteroids.at(i) == entity->mId) { std::vector::iterator entity_iter = mAsteroids.begin() + i; mAsteroids.erase (entity_iter); break; } } if (mAsteroids.size() == 0) { Engine::EventBasePtr level_complete_event (new Engine::EventBase()); level_complete_event->mEventType = EventLevelComplete; QueueEvent (level_complete_event); } } } float Model::GetWorldWidth () { return static_cast(mPhysics)->GetWorldWidth(); } float Model::GetWorldHeight () { return static_cast(mPhysics)->GetWorldHeight(); } }