started to work on the highscore

main
Martin Felis (berta) 2010-06-10 23:30:29 +02:00
parent a9ffd12b43
commit 38096a4502
9 changed files with 330 additions and 57 deletions

View File

@ -26,6 +26,7 @@ BEGIN_ENUM(GameState)
DECL_ENUM_ELEMENT(GameStatePaused), DECL_ENUM_ELEMENT(GameStatePaused),
DECL_ENUM_ELEMENT(GameStatePlayerDied), DECL_ENUM_ELEMENT(GameStatePlayerDied),
DECL_ENUM_ELEMENT(GameStateLevelComplete), DECL_ENUM_ELEMENT(GameStateLevelComplete),
DECL_ENUM_ELEMENT(GameStateShowHighscore),
DECL_ENUM_ELEMENT(GameStateGameOver) DECL_ENUM_ELEMENT(GameStateGameOver)
} }
END_ENUM(GameState) END_ENUM(GameState)

View File

@ -1,3 +1,6 @@
#ifndef ASTEROIDSEVENTS_H
#define ASTEROIDSEVENTS_H
#include "EnumToString.h" #include "EnumToString.h"
namespace asteroids { namespace asteroids {
@ -7,9 +10,12 @@ BEGIN_ENUM(Event)
DECL_ENUM_ELEMENT(EventAccelerateStart), DECL_ENUM_ELEMENT(EventAccelerateStart),
DECL_ENUM_ELEMENT(EventAccelerateStop), DECL_ENUM_ELEMENT(EventAccelerateStop),
DECL_ENUM_ELEMENT(EventLevelComplete), DECL_ENUM_ELEMENT(EventLevelComplete),
DECL_ENUM_ELEMENT(EventGameOver),
DECL_ENUM_ELEMENT(EventShipExplode) DECL_ENUM_ELEMENT(EventShipExplode)
} }
END_ENUM(Event) END_ENUM(Event)
} }
#endif /* ASTEROIDSEVENTS_H */

View File

@ -1,44 +0,0 @@
#ifndef MENUOVERLAY
#define MENUOVERLAY
namespace Engine {
class OverlayBase;
}
#include "OverlayBase.h"
#include "Sprite.h"
namespace asteroids {
class Model;
class View;
class MenuOverlay : public Engine::OverlayBase {
public:
MenuOverlay () {
};
virtual void Init ();
virtual ~MenuOverlay() {};
virtual bool OnKeyDown (const SDL_keysym &keysym);
virtual void Draw ();
void DrawGameOverScreen ();
void DrawGameMenu();
void DrawGameLevelComplete ();
void DrawGamePaused ();
void DrawGameRunning ();
void DrawPlayerDied ();
void SetModel (Model *model) { mModel = model; };
void SetView (View *view) { mView = view; };
private:
Model *mModel;
View *mView;
Engine::Sprite mShipSprite;
};
}
#endif /* MENUOVERLAY */

View File

@ -23,6 +23,8 @@ static Model* ModelInstance = NULL;
int Model::OnInit (int argc, char* argv[]) { int Model::OnInit (int argc, char* argv[]) {
int result = Engine::ModelBase::OnInit (argc, argv); int result = Engine::ModelBase::OnInit (argc, argv);
Engine::LogMessage ("Model Initialization!");
ModelInstance = this; ModelInstance = this;
mGameState = GameStateMainMenu; mGameState = GameStateMainMenu;
@ -30,14 +32,38 @@ int Model::OnInit (int argc, char* argv[]) {
/// \TODO use <optional> or similar for initialization of mCurrentLevelIndex /// \TODO use <optional> or similar for initialization of mCurrentLevelIndex
mCurrentLevelIndex = 99999; mCurrentLevelIndex = 99999;
mNewestHighscoreEntryIndex = 99999;
if (InitLevelList() == 0) if (InitLevelList() == 0)
Engine::LogError ("No levels found!"); 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);
}
// initialize event handlers and register them
mLevelCompleteEventHandler = new LevelCompleteEventHandler (this); mLevelCompleteEventHandler = new LevelCompleteEventHandler (this);
Engine::RegisterListener (mLevelCompleteEventHandler, EventLevelComplete); Engine::RegisterListener (mLevelCompleteEventHandler, EventLevelComplete);
Engine::LogMessage ("Model Initialization!"); mGameOverEventHandler = new GameOverEventHandler (this);
Engine::RegisterListener (mGameOverEventHandler, EventGameOver);
mPlayerName = "Player";
return result; return result;
} }
@ -55,18 +81,19 @@ void Model::Process () {
Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState)); Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState));
if (mLastGameState == GameStateMainMenu && mGameState == GameStateRunning) { if (mLastGameState == GameStateMainMenu && mGameState == GameStateRunning) {
mPlayerLives = 3; OnNewGame();
mCurrentLevelIndex = 0;
mPoints = 0;
DoLoadLevel (mLevelList[mCurrentLevelIndex].c_str());
} }
else if (mLastGameState == GameStateRunning && mGameState == GameStatePlayerDied) { else if (mLastGameState == GameStateRunning && mGameState == GameStatePlayerDied) {
mPlayerLives --; mPlayerLives --;
ClearEntities(); ClearEntities();
if (mPlayerLives == 0) if (mPlayerLives == 0) {
mGameState = GameStateGameOver; Engine::EventBasePtr gameover_event (new Engine::EventBase());
gameover_event->mEventType = EventGameOver;
QueueEvent (gameover_event);
//mGameState = GameStateGameOver;
}
} }
else if (mLastGameState == GameStateLevelComplete && mGameState == GameStateRunning) { else if (mLastGameState == GameStateLevelComplete && mGameState == GameStateRunning) {
mCurrentLevelIndex++; mCurrentLevelIndex++;
@ -82,14 +109,11 @@ void Model::Process () {
ClearEntities(); ClearEntities();
// ... and we have to set the last game state to the current gamestate // ... and we have to set the last game state to the current gamestate
// otherwise we end up in an infinit loop of performing the switching // otherwise we end up in an infinite loop of performing the switching
// action. // action.
mLastGameState = mGameState; mLastGameState = mGameState;
} }
void DoLoadLevel (unsigned int level_index) {
}
unsigned int Model::InitLevelList () { unsigned int Model::InitLevelList () {
const char* level_dir_name = "./data/levels/"; const char* level_dir_name = "./data/levels/";
Engine::LogDebug ("Searching for levels in %s", level_dir_name); Engine::LogDebug ("Searching for levels in %s", level_dir_name);
@ -123,6 +147,97 @@ unsigned int Model::InitLevelList () {
return mLevelList.size(); 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;
mNewestHighscoreEntryIndex = 99999;
return 99999;
}
int Model::DoLoadLevel (const char* filename) { int Model::DoLoadLevel (const char* filename) {
Engine::LogMessage ("Loading level from %s", filename); Engine::LogMessage ("Loading level from %s", filename);
std::fstream level_file (filename, std::ios::in); std::fstream level_file (filename, std::ios::in);
@ -235,6 +350,27 @@ bool Model::OnLevelComplete() {
} else { } else {
SetGameState (GameStateLevelComplete); SetGameState (GameStateLevelComplete);
} }
return true;
}
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);
}
SetGameState(GameStateGameOver);
return false;
};
void Model::OnNewGame() {
mNewestHighscoreEntryIndex = 99999;
mPlayerLives = 1;
mCurrentLevelIndex = 0;
mPoints = 0;
DoLoadLevel (mLevelList[mCurrentLevelIndex].c_str());
} }
void Model::OnCreateEntity (const int type, const unsigned int id) { void Model::OnCreateEntity (const int type, const unsigned int id) {
@ -265,7 +401,6 @@ void Model::OnKillEntity (const Engine::EntityBase *entity) {
Engine::EventBasePtr level_complete_event (new Engine::EventBase()); Engine::EventBasePtr level_complete_event (new Engine::EventBase());
level_complete_event->mEventType = EventLevelComplete; level_complete_event->mEventType = EventLevelComplete;
TriggerEvent (level_complete_event); TriggerEvent (level_complete_event);
// SetGameState (GameStateLevelComplete);
} }
} }
} }

View File

@ -13,13 +13,35 @@ class Model : public Engine::ModelBase {
int DoLoadLevel (const char* filename); int DoLoadLevel (const char* filename);
int DoSaveLevel (const char* filename); int DoSaveLevel (const char* filename);
bool OnLevelComplete(); bool OnLevelComplete();
bool OnGameOver();
/** \brief Resets values from a previous game */
void OnNewGame ();
int GetPlayerLives () { return mPlayerLives; }; int GetPlayerLives () { return mPlayerLives; };
unsigned int GetPoints () { return mPoints; }; unsigned int GetPoints () { return mPoints; };
std::string GetPlayerName() { return mPlayerName; };
void SetPlayerName(const std::string &name) { mPlayerName = name; };
float GetWorldWidth (); float GetWorldWidth ();
float GetWorldHeight (); float GetWorldHeight ();
struct HighscoreEntry {
HighscoreEntry(): name ("unknown"), points (0) { };
HighscoreEntry (const char *e_name, const unsigned int e_points):
name (e_name), points (e_points) { };
std::string name;
unsigned int points;
};
/* Highscore */
void LoadHighscoreList ();
void SaveHighscoreList ();
unsigned int AddHighscoreEntry(const std::string &name, const unsigned int points);
bool HighscoreCmp (HighscoreEntry a, HighscoreEntry b);
std::list<HighscoreEntry> mHighscoreList;
unsigned int mNewestHighscoreEntryIndex;
protected: protected:
/** \brief Initializes the system */ /** \brief Initializes the system */
virtual int OnInit (int argc, char* argv[]); virtual int OnInit (int argc, char* argv[]);
@ -27,7 +49,9 @@ class Model : public Engine::ModelBase {
Engine::ModelBase::OnDestroy(); Engine::ModelBase::OnDestroy();
mAsteroids.clear(); mAsteroids.clear();
mLevelList.clear(); mLevelList.clear();
SaveHighscoreList();
delete mLevelCompleteEventHandler; delete mLevelCompleteEventHandler;
delete mGameOverEventHandler;
}; };
virtual void OnRegisterCommands (); virtual void OnRegisterCommands ();
@ -42,8 +66,9 @@ class Model : public Engine::ModelBase {
int mPlayerLives; int mPlayerLives;
unsigned int mPoints; unsigned int mPoints;
std::vector<std::string> mLevelList;
unsigned int mCurrentLevelIndex; unsigned int mCurrentLevelIndex;
std::string mPlayerName;
std::vector<std::string> mLevelList;
/* event handler class definitions */ /* event handler class definitions */
class LevelCompleteEventHandler : public Engine::EventListenerBase { class LevelCompleteEventHandler : public Engine::EventListenerBase {
@ -57,8 +82,20 @@ class Model : public Engine::ModelBase {
Model *mModel; Model *mModel;
}; };
class GameOverEventHandler : public Engine::EventListenerBase {
public:
explicit GameOverEventHandler (Model *view) : mModel (view) {};
virtual bool HandleEvent (const Engine::EventBasePtr &event) const {
return mModel->OnGameOver();
}
private:
GameOverEventHandler() {};
Model *mModel;
};
/* event handler member variables */ /* event handler member variables */
LevelCompleteEventHandler *mLevelCompleteEventHandler; LevelCompleteEventHandler *mLevelCompleteEventHandler;
GameOverEventHandler *mGameOverEventHandler;
friend class View; friend class View;
}; };

View File

@ -37,6 +37,9 @@ bool MainMenuOverlay::OnKeyDown (const SDL_keysym &keysym) {
case SDLK_RETURN: case SDLK_RETURN:
GetModel()->SetGameState(GameStateRunning); GetModel()->SetGameState(GameStateRunning);
return true; return true;
case SDLK_h:
GetModel()->SetGameState(GameStateShowHighscore);
return true;
default: default:
return true; return true;
} }
@ -408,4 +411,124 @@ void PlayerDiedOverlay::Draw () {
}; };
/**********************
*
* Highscore
*
**********************/
void HighscoreOverlay::Init () {
}
bool HighscoreOverlay::OnKeyDown (const SDL_keysym &keysym) {
switch (keysym.sym) {
case SDLK_ESCAPE:
case SDLK_RETURN:
// If we just entered a new entry we simply show the highscore table,
// otherwise we switch back to the main menu
if (GetModel()->mNewestHighscoreEntryIndex < GetModel()->mHighscoreList.size()) {
GetModel()->mNewestHighscoreEntryIndex = GetModel()->mHighscoreList.size();
SDL_EnableUNICODE(-1);
SDL_EnableKeyRepeat(0,100);
return true;
}
GetModel()->SetGameState(GameStateMainMenu);
return true;
default:
break;
}
return true;
}
void HighscoreOverlay::Draw () {
glClearColor (0., 0., 0., 1.);
right = static_cast<float> (Engine::GetWindowWidth());
bottom = static_cast<float> (Engine::GetWindowHeight());
// we switch to orthographic projection and draw the contents of the 2d
// overlay on top of the previous drawings
glMatrixMode (GL_PROJECTION);
glPushMatrix ();
glLoadIdentity ();
// first we have to get the size of the current viewport to set up the
// orthographic projection correctly
GLint viewport[4];
glGetIntegerv(GL_VIEWPORT, viewport);
gluOrtho2D (viewport[0], viewport[2], viewport[3], viewport[1]);
glMatrixMode (GL_MODELVIEW);
glPushMatrix ();
glLoadIdentity ();
GetView()->SelectFont("console.ttf");
float x = right * 0.5 - 100;
float y = bottom * 0.5 - 8 - 128;
// then we do the drawings
Engine::DrawGLString ( x, y, "A s t e r o i d s");
y += 30;
Engine::DrawGLString ( x, y, "Highscore");
y += 30;
std::list<Model::HighscoreEntry>::iterator highscore_iter = GetModel()->mHighscoreList.begin();
GetView()->SetFontJustification(Engine::FontJustificationLeft);
unsigned int entry_length = 32;
unsigned int i = 0;
while (highscore_iter != GetModel()->mHighscoreList.end()) {
// compute the number of digits
unsigned int digits = 1;
unsigned int temp_val = highscore_iter->points;
while (temp_val > 0) {
temp_val /= 10;
digits ++;
}
// output of the name
std::ostringstream out_stream;
out_stream << highscore_iter->name;
// add dots to fill the line
temp_val = entry_length - out_stream.str().size() - digits;
for (temp_val; temp_val > 0; temp_val--) {
out_stream << ".";
}
out_stream << highscore_iter->points;
// Check whether we have to highlight an entry (such as when entering
// the name)
if (GetModel()->mNewestHighscoreEntryIndex < GetModel()->mHighscoreList.size()
&& GetModel()->mNewestHighscoreEntryIndex == i) {
GetView()->SetFontColor (224./255., 200/255., 0.);
Engine::DrawGLString ( x, y, out_stream.str().c_str());
GetView()->SetFontColor (1., 1., 1.);
} else {
Engine::DrawGLString ( x, y, out_stream.str().c_str());
}
// go down one line
y += 16;
i++;
highscore_iter++;
}
Engine::DrawGLString ( x + 16, y + 16, "Press [Return] to continue.");
glPopMatrix ();
glMatrixMode (GL_PROJECTION);
glPopMatrix ();
glMatrixMode (GL_MODELVIEW);
};
} }

View File

@ -91,6 +91,18 @@ class PlayerDiedOverlay : public Engine::OverlayBase {
virtual void Draw (); virtual void Draw ();
}; };
class HighscoreOverlay : public Engine::OverlayBase {
public:
HighscoreOverlay () {
};
virtual ~HighscoreOverlay() {};
virtual void Init ();
virtual bool OnKeyDown (const SDL_keysym &keysym);
virtual void Draw ();
};
} }
#endif /* USERINTERFACE */ #endif /* USERINTERFACE */

View File

@ -36,6 +36,7 @@ int View::OnInit (int argc, char* argv[]) {
Engine::OverlayBasePtr level_complete_overlay (new LevelCompleteOverlay); Engine::OverlayBasePtr level_complete_overlay (new LevelCompleteOverlay);
Engine::OverlayBasePtr game_paused_overlay (new GamePausedOverlay); Engine::OverlayBasePtr game_paused_overlay (new GamePausedOverlay);
Engine::OverlayBasePtr player_died_overlay (new PlayerDiedOverlay); Engine::OverlayBasePtr player_died_overlay (new PlayerDiedOverlay);
Engine::OverlayBasePtr highscore_overlay (new HighscoreOverlay);
mOverlayManager.Register (menu_overlay, GameStateMainMenu); mOverlayManager.Register (menu_overlay, GameStateMainMenu);
mOverlayManager.Register (game_running_overlay, GameStateRunning); mOverlayManager.Register (game_running_overlay, GameStateRunning);
@ -43,6 +44,7 @@ int View::OnInit (int argc, char* argv[]) {
mOverlayManager.Register (game_over_overlay, GameStateGameOver); mOverlayManager.Register (game_over_overlay, GameStateGameOver);
mOverlayManager.Register (player_died_overlay, GameStatePlayerDied); mOverlayManager.Register (player_died_overlay, GameStatePlayerDied);
mOverlayManager.Register (game_paused_overlay, GameStatePaused); mOverlayManager.Register (game_paused_overlay, GameStatePaused);
mOverlayManager.Register (highscore_overlay, GameStateShowHighscore);
mOverlayManager.InitOverlays(); mOverlayManager.InitOverlays();

View File

@ -10,6 +10,7 @@
#include <sstream> #include <sstream>
#include <string> #include <string>
#include <vector> #include <vector>
#include <list>
#include <map> #include <map>
#include <queue> #include <queue>
#include <bitset> #include <bitset>