fysxasteroids/asteroids/Model.cc

463 lines
12 KiB
C++

#include <iostream>
#include <fstream>
#include <algorithm>
#include <boost/filesystem.hpp>
#include "Model.h"
#include "Physics.h"
#include "PhysicsBase.h"
#include "EntityFactory.h"
#include "AsteroidsEvents.h"
#include "AsteroidEntity.h"
#include <assert.h>
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 <optional> or similar for initialization of mCurrentLevelIndex
mCurrentLevelIndex = 99999;
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 = 99999;
// initialize event handlers and register them
Engine::RegisterListener (this, EventGameOver);
Engine::RegisterListener (this, EventShipExplode);
mPlayerName = "Player";
Engine::PlayMusic ("./data/sounds/intro_music.ogg");
Engine::RegisterListener (this, EventAccelerateStart);
Engine::RegisterListener (this, EventAccelerateStop);
return result;
}
bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) {
switch (event->mEventType) {
case EventAccelerateStart:
Engine::PlaySoundLoop("./data/sounds/thrust.wav", -1);
break;
case EventAccelerateStop:
Engine::HaltSoundLoop("./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 () {
const char* level_dir_name = "./data/levels/";
Engine::LogDebug ("Searching for levels in %s", level_dir_name);
mLevelList.clear();
boost::filesystem::path level_dir(level_dir_name);
if (!boost::filesystem::exists(level_dir)) {
Engine::LogError ("Could not init level list: %s does not exist!");
}
if (!boost::filesystem::is_directory(level_dir)) {
Engine::LogError ("Could not init level list: %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("./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 ("./highscore.dat");
std::list<HighscoreEntry>::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<HighscoreEntry>::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 writing!", filename);
exit (-1);
}
ClearEntities();
mAsteroids.clear();
std::string entity_type_str;
int entity_count = 0;
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;
}
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 << "# <Type> <player?> <xpos> <ypos> <zpos> <zrot> <yrot> <xrot> <xvel> <yvel> <zvel> <rotvel>" << std::endl;
std::map<unsigned int, Engine::EntityBase*>::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<unsigned int>::max();
mPlayerLives = 3;
mCurrentLevelIndex = 0;
mPoints = 0;
DoLoadLevel (mLevelList[mCurrentLevelIndex].c_str());
}
void Model::OnShipExplode () {
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("./data/sounds/rock_destroyed.wav");
unsigned int i;
const AsteroidEntity *asteroid = static_cast<const AsteroidEntity*>(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<unsigned int>::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<Physics*>(mPhysics)->GetWorldWidth();
}
float Model::GetWorldHeight () {
return static_cast<Physics*>(mPhysics)->GetWorldHeight();
}
}