diff --git a/asteroids/ControllerCommands.cc b/asteroids/ControllerCommands.cc index 7398a3b..87446ec 100644 --- a/asteroids/ControllerCommands.cc +++ b/asteroids/ControllerCommands.cc @@ -1,5 +1,6 @@ // #include "Engine.h" +#include "Game.h" #include "Controller.h" #include "Model.h" #include "EntityBase.h" @@ -18,6 +19,10 @@ static Controller *ControllerInstance = NULL; bool Cmd_ControllerForwardDown (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); if (player_entity) { player_entity->SetControllerKeyState (EntityKeyStateForward); @@ -36,6 +41,10 @@ bool Cmd_ControllerForwardDown (std::vector args) { bool Cmd_ControllerForwardUp (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); if (player_entity) { player_entity->UnsetControllerKeyState (EntityKeyStateForward); @@ -54,6 +63,10 @@ bool Cmd_ControllerForwardUp (std::vector args) { bool Cmd_ControllerTurnLeftDown (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); if (player_entity) { player_entity->SetControllerKeyState (EntityKeyStateTurnLeft); @@ -67,6 +80,10 @@ bool Cmd_ControllerTurnLeftDown (std::vector args) { bool Cmd_ControllerTurnLeftUp (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); if (player_entity) { player_entity->UnsetControllerKeyState (EntityKeyStateTurnLeft); @@ -80,6 +97,10 @@ bool Cmd_ControllerTurnLeftUp (std::vector args) { bool Cmd_ControllerTurnRightDown (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); if (player_entity) { player_entity->SetControllerKeyState (EntityKeyStateTurnRight); @@ -93,6 +114,10 @@ bool Cmd_ControllerTurnRightDown (std::vector args) { bool Cmd_ControllerTurnRightUp (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + Engine::EntityBase* player_entity = Engine::GetEntity (Engine::GetPlayerEntityId()); if (player_entity) { player_entity->UnsetControllerKeyState (EntityKeyStateTurnRight); @@ -106,6 +131,10 @@ bool Cmd_ControllerTurnRightUp (std::vector args) { bool Cmd_ControllerAttack (std::vector args) { assert (ControllerInstance); + // only continue if the game is running + if (GetModel()->GetGameState() != GameStateRunning) + return false; + ShipEntity* player_entity = (ShipEntity*) Engine::GetEntity (Engine::GetPlayerEntityId ()); if (player_entity) { player_entity->Attack (); diff --git a/asteroids/Model.cc b/asteroids/Model.cc index b90d751..c805fb0 100644 --- a/asteroids/Model.cc +++ b/asteroids/Model.cc @@ -32,7 +32,6 @@ int Model::OnInit (int argc, char* argv[]) { /// \TODO use or similar for initialization of mCurrentLevelIndex mCurrentLevelIndex = 99999; - mNewestHighscoreEntryIndex = 99999; if (InitLevelList() == 0) Engine::LogError ("No levels found!"); @@ -56,6 +55,10 @@ int Model::OnInit (int argc, char* argv[]) { 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, EventLevelComplete); Engine::RegisterListener (this, EventGameOver); diff --git a/asteroids/View.cc b/asteroids/View.cc index 752855f..d146067 100644 --- a/asteroids/View.cc +++ b/asteroids/View.cc @@ -42,6 +42,9 @@ int View::OnInit (int argc, char* argv[]) { LoadFont("AldotheApache.ttf size=20 color=#000000"); + button_width = 250; + button_height = 40; + // This is a simple star field that makes the game so spacy int i; for (i = 0; i < 200; i++) { @@ -389,21 +392,34 @@ void View::DrawUi () { glPopMatrix (); glMatrixMode (GL_MODELVIEW); +} +void View::DrawPageTitle (const std::string& title) { + SelectFont("console.ttf size=46 color=#808080"); + float width, height; + DrawGLStringMeasure(title.c_str(), &width, &height); + + float xpos = (screen_right - width) * 0.5 - 40; + float ypos = 180; + Engine::GUI::Label (4, title.c_str(), xpos - 2, ypos + 2); + + SelectFont("console.ttf size=46 color=#ffffff"); + Engine::GUI::Label (4, title.c_str(), xpos, ypos); } void View::DrawUiMainMenu() { - Engine::GUI::Label (4, "A s t e r o i d s", screen_right * 0.5 - 100, 180); + DrawPageTitle ("Asteroids"); - if (Engine::GUI::Button (1, "New Game", screen_right * 0.5 - 100, 200, 250, 40)) { + SelectFont("console.ttf size=23"); + if (Engine::GUI::Button (1, "New Game", screen_right * 0.5 - 100, 200, button_width, button_height)) { GetModel()->SetGameState(GameStateEnterPlayername); } - if (Engine::GUI::Button (2, "Highscores", screen_right * 0.5 - 100, 250, 250, 40)) { + if (Engine::GUI::Button (2, "Highscores", screen_right * 0.5 - 100, 250, button_width, button_height)) { GetModel()->SetGameState(GameStateShowHighscore); } - if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, 250, 40)) { + if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, button_width, button_height)) { Engine::RunCommand("quit"); } } @@ -432,141 +448,168 @@ void View::DrawUiGameRunning() { } void View::DrawUiGameOver() { - Engine::GUI::Label (4, "G a m e O v e r", screen_right * 0.5 - 100, 180); + DrawPageTitle ("Game Over"); + + SelectFont("console.ttf size=23 color=#ffffff"); if (GetModel()->GetPlayerLives() == 0) { - Engine::GUI::Label(5, "That was pathetic!", screen_right * 0.5 - 80, screen_bottom * 0.5 - 8); + Engine::GUI::LabelCentered(5, + "That was pathetic!", + screen_right * 0.5, + screen_bottom * 0.5 - 8); } else { - Engine::GUI::Label (6, "You earned yourself the", - screen_right * 0.5 - 80, + Engine::GUI::LabelCentered (6, "You earned yourself the", + screen_right * 0.5, screen_bottom * 0.5 - 32); - Engine::GUI::Label (7, "You Rock(TM) award.", - screen_right * 0.5 - 60, + Engine::GUI::LabelCentered (7, "You Rock(TM) award.", + screen_right * 0.5, screen_bottom * 0.5 + 0); - Engine::GUI::Label (8, "Go tell your friends.", - screen_right * 0.5 - 70, + Engine::GUI::LabelCentered (8, "Go tell your friends.", + screen_right * 0.5, screen_bottom * 0.5 + 32); } if (Engine::GUI::Button (2, "Continue...", - screen_right * 0.5 - 100, - screen_bottom * 0.5 + 60, 250, 40)) { + (screen_right - button_width) * 0.5, + screen_bottom * 0.5 + 80, button_width, button_height)) { GetModel()->SetGameState(GameStateShowHighscore); } } void View::DrawUiLevelComplete() { - Engine::GUI::Label (4, "Level Complete!", screen_right * 0.5 - 100, 180); + DrawPageTitle ("Level Complete!"); - Engine::GUI::Label (6, "Congratulations - You rock!", screen_right * 0.5 - 80, screen_bottom * 0.5 -16); + SelectFont ("console.ttf size=23"); - if(Engine::GUI::Button (1, "Next level ...", screen_right * 0.5 - 80, screen_bottom * 0.5 - 48, 250, 40)) { + if(Engine::GUI::Button (1, "Next level ...", (screen_right - button_width) * 0.5, screen_bottom * 0.5 + 60, button_width, button_height)) { GetModel()->SetGameState(GameStateRunning); } } void View::DrawUiGamePaused() { - Engine::GUI::Label (4, "- P a u s e d -", screen_right * 0.5 - 100, 180); + DrawPageTitle ("Game Paused"); + SelectFont ("console.ttf size=23"); - if (Engine::GUI::Button (1, "Resume Game", screen_right * 0.5 - 100, 200, 250, 40)) { + if (Engine::GUI::Button (1, "Resume Game", screen_right * 0.5 - 100, 200, button_width, button_height)) { GetModel()->SetGameState(GameStateRunning); } - if (Engine::GUI::Button (2, "Abort Game", screen_right * 0.5 - 100, 250, 250, 40)) { + if (Engine::GUI::Button (2, "Abort Game", screen_right * 0.5 - 100, 250, button_width, button_height)) { GetModel()->SetGameState(GameStateMainMenu); } - if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, 250, 40)) { + if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, button_width, button_height)) { Engine::RunCommand("quit"); } } void View::DrawUiPlayerDied() { - Engine::GUI::Label (4, "You died ...", screen_right * 0.5 - 40, screen_bottom * 0.5 - 8 - 32); + DrawPageTitle ("You died!"); + SelectFont ("console.ttf size=23"); - if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 200, 250, 40)) { + if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 200, button_width, button_height)) { GetModel()->SetGameState(GameStateRunning); } } +/** \brief Draws an entry in the form .... + * + * The amount of dots is also computed within this function. Not necessarily + * fast, however it works for now. + */ +void View::DrawHighscoreEntry (float x, float y, float entry_width, const std::string &name, unsigned int points) { + const float width_buffer = 30.; + float width, height; + float name_width, points_value_width; + + // Measure and draw the name + DrawGLStringMeasure (name.c_str(), &name_width, &height); + name_width = fabsf(name_width); + Engine::DrawGLString ( x, y, name.c_str()); + + // Compute the width of the points value when it is printed out + std::ostringstream points_value_stream; + points_value_stream << points; + DrawGLStringMeasure (points_value_stream.str().c_str(), &points_value_width, &height); + points_value_width = fabsf(points_value_width); + + // Compute the amount of dots. This is done by iteratively adding a dot '.' + // to a stream and check whether it has the desired width. + float dots_width = 0.; + std::ostringstream dots_stream; + + while (ceil(name_width) + dots_width + points_value_width + width_buffer < entry_width) { + dots_stream << "."; + DrawGLStringMeasure (dots_stream.str().c_str(), &dots_width, &height); + dots_width = fabsf(dots_width); + } + + // Drawing of the dots and the points value + Engine::DrawGLString ( x + entry_width - points_value_width - dots_width - 10, y, dots_stream.str().c_str()); + Engine::DrawGLString ( x + entry_width - points_value_width, y, points_value_stream.str().c_str()); +} + void View::DrawUiHighscore() { - float x = screen_right * 0.5 - 100; + DrawPageTitle ("Highscores"); + SelectFont ("console.ttf size=23"); + + unsigned int entry_length = 32; + float char_width, height; + DrawGLStringMeasure ("M", &char_width, &height); + float entry_width = fabsf(char_width * entry_length); + float x = screen_right * 0.5 - entry_width * 0.5; float y = screen_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; + y += 60; std::list::iterator highscore_iter = GetModel()->mHighscoreList.begin(); 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) { - SelectFont("console.ttf color=#e8d500"); - Engine::DrawGLString ( x, y, out_stream.str().c_str()); - SelectFont("console.ttf color=#ffffff"); + // we highlight the newest entry + SelectFont("console.ttf color=#e8d500 size=23"); } else { - Engine::DrawGLString ( x, y, out_stream.str().c_str()); + SelectFont("console.ttf color=#ffffff size=23"); } + DrawHighscoreEntry ( x, y, entry_width, highscore_iter->name, highscore_iter->points); // go down one line - y += 16; + y += 20; i++; highscore_iter++; } - if (Engine::GUI::Button (1, "Back to MainMenu", x + 16, y + 16, 250, 40)) { + if (Engine::GUI::Button (1, "Back to Menu", screen_right * 0.5 - 250 * 0.5, y + 16, button_width, button_height)) { GetModel()->SetGameState(GameStateMainMenu); } } void View::DrawUiEnterPlayername() { - Engine::GUI::Label (4, "A s t e r o i d s", screen_right * 0.5 - 100, 180); - + DrawPageTitle ("Asteroids"); + SelectFont ("console.ttf size=23"); + // Enter your name std::string player_name = GetModel()->GetPlayerName(); - SelectFont("console.ttf"); - Engine::GUI::Label (1, "Enter your name: ", screen_right * 0.5 - 100, 250); + SelectFont("console.ttf size=23"); + Engine::GUI::Label (1, "Enter your name: ", screen_right * 0.5 - 220, 250); if (Engine::GUI::LineEdit (2, screen_right * 0.5 + 20, 238, player_name, 16)) { GetModel()->SetPlayerName(player_name); } - if (Engine::GUI::Button (3, "Start Game", screen_right - 150 - 20, 500, 150, 40)) { + if (Engine::GUI::Button (3, "Start Game", screen_right - 180 - 20, 500, 180, 40)) { GetModel()->SetGameState(GameStateRunning); } - if (Engine::GUI::Button (5, "Back", 20, 500, 150, 40)) { + if (Engine::GUI::Button (5, "Back", 20, 500, 180, 40)) { GetModel()->SetGameState(GameStateMainMenu); } } diff --git a/asteroids/View.h b/asteroids/View.h index 2a9ea61..bf7573b 100644 --- a/asteroids/View.h +++ b/asteroids/View.h @@ -35,12 +35,15 @@ class View : public Engine::ViewBase { private: void DrawUi(); + void DrawPageTitle(const std::string &title); + void DrawUiMainMenu(); void DrawUiGameRunning(); void DrawUiGameOver(); void DrawUiLevelComplete(); void DrawUiGamePaused(); void DrawUiPlayerDied(); + void DrawHighscoreEntry(float x, float y, float entry_width, const std::string &name, unsigned int points); void DrawUiHighscore(); void DrawUiEnterPlayername(); @@ -73,6 +76,9 @@ class View : public Engine::ViewBase { float screen_top; float screen_bottom; + int button_width; + int button_height; + virtual bool OnReceiveEvent (const Engine::EventBasePtr &event); }; diff --git a/data/sounds/laser.wav b/data/sounds/laser.wav index 2db525c..08b676f 100644 Binary files a/data/sounds/laser.wav and b/data/sounds/laser.wav differ diff --git a/engine/ControllerBase.cc b/engine/ControllerBase.cc index 855d73a..54cb273 100644 --- a/engine/ControllerBase.cc +++ b/engine/ControllerBase.cc @@ -159,6 +159,9 @@ void ControllerBase::IMGUIFinish () { if (uistate.activeitem == 0) uistate.activeitem = -1; } + + // also reset the last keysym such that old values will not be reported + uistate.last_keysym = SDLK_FIRST; } /** \brief Keyboard processing */ diff --git a/engine/IMGUIControls.cc b/engine/IMGUIControls.cc index 27128b5..0385fe6 100644 --- a/engine/IMGUIControls.cc +++ b/engine/IMGUIControls.cc @@ -38,6 +38,40 @@ void DrawBlock (int x, int y, int w, int h) { assert (h > d); assert (w > d); + float color[4]; + glGetFloatv (GL_CURRENT_COLOR, color); + + glBegin(GL_QUADS); + + // middle part + glVertex3f (x, y, 0.); + glVertex3f (x, y + h, 0.); + glVertex3f (x + w, y + h, 0.); + glVertex3f (x + w, y, 0.); + + glEnd(); + + glColor4fv (color); + + // "Shading" + glLineWidth(3); + glColor3f (color[0] * shading_dark, color[1] * shading_dark, color[2] * shading_dark); + + glBegin(GL_LINE_STRIP); + glVertex3f (x,y + 2, 0.); + glVertex3f (x,y +h, 0.); + glVertex3f (x + w - 2, y + h, 0.); + glEnd(); +} + +void DrawRoundedBlock (int x, int y, int w, int h) { + const int d = 16; + const float shading_dark = 0.5; + const float shading_light = 1.3; + + assert (h > d); + assert (w > d); + glBegin(GL_QUADS); // lower part glVertex3f (x, y, 0.); @@ -103,6 +137,26 @@ void Label (int id, const char* caption, int x, int y) { } } +/** \brief Draws a label with a given caption centered at the position (vertically centered) + * + * This function draws the label at the horizontal x position and centers the + * label with regard to the height of the rendered caption. + * + * \TODO [med] The vertical alignment around the center of the vertical height + * of the string is rather unfortunate as different contents will be rendered + * at different vertical positions (e.g. "aaa" and "ggg") + */ +void LabelCentered (int id, const char* caption, int x, int y) { + if (caption != NULL) { + float width, height; + view = EngineGetView (); + assert (view); + + view->DrawGLStringMeasure(caption, &width, &height); + view->DrawGLString(x - 0.5 * width, y + height * 0.5, caption); + } +} + /** \brief Draws a button at the given position * * The area defined by the parameters defines the clickable area. However @@ -137,20 +191,20 @@ bool Button (int id, const char* caption, int x, int y, int w, int h) { // Render glColor3f (0.2, 0.2, 0.2); -// DrawBlock (x + 4, y + 4, w, h); +// DrawRoundedBlock (x + 4, y + 4, w, h); if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { glColor3f (0.8, 0.8, 0.8); - DrawBlock (x, y, w, h); + DrawRoundedBlock (x, y, w, h); } else { glColor3f (0.7, 0.7, 0.7); - DrawBlock (x, y, w, h); + DrawRoundedBlock (x, y, w, h); } } else { glColor3f (0.4, 0.4, 0.4); - DrawBlock (x, y, w, h); + DrawRoundedBlock (x, y, w, h); } // Caption @@ -159,9 +213,25 @@ bool Button (int id, const char* caption, int x, int y, int w, int h) { view = EngineGetView (); assert (view); - glColor3f (1., 1., 1.); + // we have to load the font prior to measuring it + SelectFont("console.ttf size=23"); view->DrawGLStringMeasure(caption, &width, &height); - view->DrawGLString(x + w * 0.5 - width * 0.5, y + h * 0.5 - height * 0.5, caption); + + float xpos = x + w * 0.5 - width * 0.5; + float ypos = y + h * 0.5 - height * 0.5; + // LogDebug ("measure '%s' width = %f height = %f", caption, width, height); + + if (controller->uistate.hotitem == id || controller->uistate.kbditem == id) { + SelectFont("console.ttf size=23 color=#666666"); + view->DrawGLString( xpos - 2., ypos + 2., caption); + SelectFont("console.ttf size=23 color=#ffffff"); + view->DrawGLString( xpos, ypos, caption); + } else { + SelectFont("console.ttf size=23 color=#4d4d4d"); + view->DrawGLString( xpos - 2., ypos + 2., caption); + SelectFont("console.ttf size=23 color=#b3b3b3"); + view->DrawGLString( xpos, ypos, caption); + } } // Mouse Logic @@ -217,8 +287,12 @@ bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxleng controller = EngineGetController(); assert (controller); + int textpos_x = x + 8; + int textpos_y = y + 5; + + y -= 16; int w = maxlength * 16; - int h = 20; + int h = 30; // LogMessage ("id = %d hotitem = %d activeitem = %d kbditem = %d key = %s", id, controller->uistate.hotitem, controller->uistate.activeitem, controller->uistate.kbditem, convert_keycode (controller->uistate.last_keysym)); @@ -238,10 +312,6 @@ bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxleng return false; } - // Render - glColor3f (0.2, 0.2, 0.2); - DrawBlock (x + 4, y + 4, w, h); - // If we have keyboard focus, we highlight the widget if ( controller->uistate.kbditem == id) { if (controller->uistate.activeitem == id) { @@ -269,7 +339,7 @@ bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxleng text_output += "_"; view->DrawGLStringMeasure(text_value.c_str(), &width, &height); - view->DrawGLString(x + 16, y + 12, text_output.c_str()); + view->DrawGLString(textpos_x, textpos_y, text_output.c_str()); // Keyboard Logic if (controller->uistate.kbditem == id) { diff --git a/engine/IMGUIControls.h b/engine/IMGUIControls.h index ffb821e..0815925 100644 --- a/engine/IMGUIControls.h +++ b/engine/IMGUIControls.h @@ -19,6 +19,9 @@ bool regionhit (int x, int y, int w, int h); /** \brief Draws a label at the given position with vertical center at y */ void Label (int id, const char* caption, int x, int y); +/** \brief Draws a label centered at the given position with vertical center at y */ +void LabelCentered (int id, const char* caption, int x, int y); + /** \brief Draws a button at the given position * diff --git a/engine/SoundBase.cc b/engine/SoundBase.cc index e32c1ed..68aae26 100644 --- a/engine/SoundBase.cc +++ b/engine/SoundBase.cc @@ -46,7 +46,7 @@ int SoundBase::OnInit(int argc, char* argv[]) { int audio_rate = 22050; Uint16 audio_format = AUDIO_S16; int audio_channels = 2; - int audio_buffers = 4096; + int audio_buffers = 1024; LogDebug("Sound Init"); if (Mix_OpenAudio (audio_rate, audio_format, audio_channels, audio_buffers)) { diff --git a/engine/ViewBase.cc b/engine/ViewBase.cc index 2f7e5f0..e820b4c 100644 --- a/engine/ViewBase.cc +++ b/engine/ViewBase.cc @@ -339,6 +339,7 @@ void ViewBase::DrawGLStringMeasure (const char* str, float *width, float *height OGLFT::BBox bbox = mCurrentFont->measure (str); *width = bbox.x_max_ - bbox.x_min_; *height = bbox.y_max_ - bbox.y_min_; +// LogDebug ("measure bbox '%s' = %f,%f %f,%f",str, bbox.x_min_, bbox.y_min_, bbox.x_max_, bbox.y_max_); } void ViewBase::GetCamereEye (float *eye_out) {