diff --git a/asteroids/AsteroidsEnums.h b/asteroids/AsteroidsEnums.h index 281f8a1..d490d36 100644 --- a/asteroids/AsteroidsEnums.h +++ b/asteroids/AsteroidsEnums.h @@ -19,16 +19,24 @@ BEGIN_ENUM(GameEntityType) } END_ENUM(GameEntityType) +BEGIN_ENUM(ViewState) +{ + DECL_ENUM_ELEMENT(ViewStateMainMenu), + DECL_ENUM_ELEMENT(ViewStateGameRunning), + DECL_ENUM_ELEMENT(ViewStatePaused), + DECL_ENUM_ELEMENT(ViewStatePlayerDied), + DECL_ENUM_ELEMENT(ViewStateLevelComplete), + DECL_ENUM_ELEMENT(ViewStateShowHighscore), + DECL_ENUM_ELEMENT(ViewStateEnterPlayername), + DECL_ENUM_ELEMENT(ViewStateOptions), + DECL_ENUM_ELEMENT(ViewStateGameOver) +} +END_ENUM(ViewState) + BEGIN_ENUM(GameState) { - DECL_ENUM_ELEMENT(GameStateMainMenu), DECL_ENUM_ELEMENT(GameStateRunning), DECL_ENUM_ELEMENT(GameStatePaused), - DECL_ENUM_ELEMENT(GameStatePlayerDied), - DECL_ENUM_ELEMENT(GameStateLevelComplete), - DECL_ENUM_ELEMENT(GameStateShowHighscore), - DECL_ENUM_ELEMENT(GameStateEnterPlayername), - DECL_ENUM_ELEMENT(GameStateGameOver) } END_ENUM(GameState) diff --git a/asteroids/Model.cc b/asteroids/Model.cc index c805fb0..7a7edc0 100644 --- a/asteroids/Model.cc +++ b/asteroids/Model.cc @@ -27,8 +27,8 @@ int Model::OnInit (int argc, char* argv[]) { ModelInstance = this; - mGameState = GameStateMainMenu; - mLastGameState = GameStateMainMenu; + mGameState = GameStatePaused; + mLastGameState = GameStatePaused; /// \TODO use or similar for initialization of mCurrentLevelIndex mCurrentLevelIndex = 99999; @@ -62,6 +62,7 @@ int Model::OnInit (int argc, char* argv[]) { // initialize event handlers and register them Engine::RegisterListener (this, EventLevelComplete); Engine::RegisterListener (this, EventGameOver); + Engine::RegisterListener (this, EventShipExplode); mPlayerName = "Player"; @@ -84,6 +85,9 @@ bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) { case EventLevelComplete: return OnLevelComplete(); break; + case EventShipExplode: + OnShipExplode(); + break; case EventGameOver: return OnGameOver(); break; @@ -110,36 +114,6 @@ void Model::Process () { // some action. Engine::LogDebug ("Switching from %s->%s", GetStringGameState(mLastGameState), GetStringGameState(mGameState)); - if ( (mLastGameState == GameStateMainMenu && mGameState == GameStateRunning) - || (mLastGameState == GameStateEnterPlayername && mGameState == GameStateRunning)){ - OnNewGame(); - } - else if (mLastGameState == GameStateRunning && mGameState == GameStatePlayerDied) { - mPlayerLives --; - - ClearEntities(); - - if (mPlayerLives == 0) { - Engine::EventBasePtr gameover_event (new Engine::EventBase()); - gameover_event->mEventType = EventGameOver; - QueueEvent (gameover_event); - } - } - else if (mLastGameState == GameStateLevelComplete && mGameState == GameStateRunning) { - 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()); - } - } - else if (mLastGameState == GameStatePlayerDied && mGameState == GameStateRunning) - DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str()); - else if (mLastGameState == GameStateRunning && mGameState == GameStateGameOver) - ClearEntities(); - // ... 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. @@ -391,24 +365,31 @@ void Model::SetGameState (const unsigned int &state) { bool Model::OnLevelComplete() { Engine::LogMessage ("Level complete!"); + mCurrentLevelIndex++; + if (mCurrentLevelIndex + 1 == mLevelList.size()) { Engine::EventBasePtr gameover_event (new Engine::EventBase()); gameover_event->mEventType = EventGameOver; QueueEvent (gameover_event); } else { - SetGameState (GameStateLevelComplete); + DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str()); } + SetGameState(GameStatePaused); + return true; } bool Model::OnGameOver() { + ClearEntities(); + Engine::LogMessage ("Points = %d lowest = %d", mPoints, mHighscoreList.back().points ); + if (mPoints > mHighscoreList.back().points) { Engine::LogMessage ("New Highscore!"); AddHighscoreEntry (mPlayerName, mPoints); } - SetGameState(GameStateGameOver); + SetGameState(GameStatePaused); return false; }; @@ -421,6 +402,22 @@ void Model::OnNewGame() { DoLoadLevel (mLevelList[mCurrentLevelIndex].c_str()); } +void Model::OnShipExplode () { + mPlayerLives --; + + ClearEntities(); + + if (mPlayerLives == 0) { + Engine::EventBasePtr gameover_event (new Engine::EventBase()); + gameover_event->mEventType = EventGameOver; + QueueEvent (gameover_event); + } else { + DoLoadLevel(mLevelList[mCurrentLevelIndex].c_str()); + } + + SetGameState(GameStatePaused); +} + void Model::OnCreateEntity (const int type, const unsigned int id) { GameEntityType entity_type = (GameEntityType) type; diff --git a/asteroids/Model.h b/asteroids/Model.h index b77a557..ac93587 100644 --- a/asteroids/Model.h +++ b/asteroids/Model.h @@ -16,6 +16,7 @@ class Model : public Engine::ModelBase { bool OnGameOver(); /** \brief Resets values from a previous game */ void OnNewGame (); + void OnShipExplode (); virtual void SetGameState (const unsigned int &state); diff --git a/asteroids/ShipEntity.cc b/asteroids/ShipEntity.cc index d5009ab..0163960 100644 --- a/asteroids/ShipEntity.cc +++ b/asteroids/ShipEntity.cc @@ -26,7 +26,7 @@ void ShipEntity::Update (float delta_sec) { if (mFadeTimer <= 0.) { Model *model = (Model*) Engine::EngineGetModel(); - model->SetGameState (GameStatePlayerDied); + model->SetGameState (GameStatePaused); } return; } diff --git a/asteroids/View.cc b/asteroids/View.cc index 8736690..d7f39bf 100644 --- a/asteroids/View.cc +++ b/asteroids/View.cc @@ -72,6 +72,8 @@ int View::OnInit (int argc, char* argv[]) { Engine::RegisterListener (this, EventAccelerateStop); Engine::RegisterListener (this, EventShipExplode); + PushViewState (ViewStateMainMenu); + return 0; } @@ -355,34 +357,42 @@ void View::DrawUi () { SetFontJustification (Engine::FontJustificationLeft); GetController()->EnableTextinput(true); - switch (game_state) { - case GameStateMainMenu: + ViewState current_view_state = GetViewState(); + + SelectFont ("console.ttf size=12"); + Engine::GUI::Label (99999, GetStringViewState(current_view_state), 8, 16); + + switch (current_view_state) { + case ViewStateMainMenu: DrawUiMainMenu(); break; - case GameStateRunning: + case ViewStateGameRunning: DrawUiGameRunning(); break; - case GameStatePaused: + case ViewStatePaused: DrawUiGamePaused(); break; - case GameStatePlayerDied: + case ViewStatePlayerDied: DrawUiPlayerDied(); break; - case GameStateLevelComplete: + case ViewStateLevelComplete: DrawUiLevelComplete(); break; - case GameStateShowHighscore: + case ViewStateShowHighscore: DrawUiHighscore(); break; - case GameStateEnterPlayername: + case ViewStateOptions: + DrawUiOptions(); + break; + case ViewStateEnterPlayername: DrawUiEnterPlayername(); break; - case GameStateGameOver: + case ViewStateGameOver: DrawUiGameOver(); break; default: - Engine::LogWarning ("Trying to draw unknown GameState: %s (%d)", - GetStringGameState (game_state), game_state); + Engine::LogWarning ("Trying to draw unknown ViewState: %s (%d)", + GetStringViewState (game_state), game_state); break; } @@ -412,14 +422,18 @@ void View::DrawUiMainMenu() { 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); + PushViewState(ViewStateEnterPlayername); } - if (Engine::GUI::Button (2, "Highscores", screen_right * 0.5 - 100, 250, button_width, button_height)) { - GetModel()->SetGameState(GameStateShowHighscore); + if (Engine::GUI::Button (2, "Options", screen_right * 0.5 - 100, 250, button_width, button_height)) { + PushViewState (ViewStateOptions); } - if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, button_width, button_height)) { + if (Engine::GUI::Button (3, "Highscores", screen_right * 0.5 - 100, 300, button_width, button_height)) { + PushViewState(ViewStateShowHighscore); + } + + if (Engine::GUI::Button (4, "Quit", screen_right * 0.5 - 100, 380, button_width, button_height)) { Engine::RunCommand("quit"); } } @@ -443,8 +457,9 @@ void View::DrawUiGameRunning() { // revert the font justification SetFontJustification (Engine::FontJustificationLeft); - if (Engine::GUI::CheckKeyPress(SDLK_ESCAPE)) - GetModel()->SetGameState(GameStatePaused); + if (Engine::GUI::CheckKeyPress(SDLK_ESCAPE)) { + PushViewState(ViewStatePaused); + } } void View::DrawUiGameOver() { @@ -472,7 +487,8 @@ void View::DrawUiGameOver() { if (Engine::GUI::Button (2, "Continue...", (screen_right - button_width) * 0.5, screen_bottom * 0.5 + 80, button_width, button_height)) { - GetModel()->SetGameState(GameStateShowHighscore); + PopViewState(); + PushViewState(ViewStateShowHighscore); } } @@ -482,7 +498,7 @@ void View::DrawUiLevelComplete() { SelectFont ("console.ttf size=23"); 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); + PopViewState(); } } @@ -491,11 +507,15 @@ void View::DrawUiGamePaused() { SelectFont ("console.ttf size=23"); if (Engine::GUI::Button (1, "Resume Game", screen_right * 0.5 - 100, 200, button_width, button_height)) { - GetModel()->SetGameState(GameStateRunning); + PopViewState(); } if (Engine::GUI::Button (2, "Abort Game", screen_right * 0.5 - 100, 250, button_width, button_height)) { - GetModel()->SetGameState(GameStateMainMenu); + while (mViewStateStack.size()) + PopViewState(); + + PushViewState(ViewStateGameRunning); + GetModel()->SetGameState(GameStateRunning); } if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, button_width, button_height)) { @@ -508,7 +528,7 @@ void View::DrawUiPlayerDied() { SelectFont ("console.ttf size=23"); if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 200, button_width, button_height)) { - GetModel()->SetGameState(GameStateRunning); + PopViewState(); } } @@ -588,7 +608,36 @@ void View::DrawUiHighscore() { } if (Engine::GUI::Button (1, "Back to Menu", screen_right * 0.5 - 250 * 0.5, y + 16, button_width, button_height)) { - GetModel()->SetGameState(GameStateMainMenu); + PopViewState(); + } +} + +void View::DrawUiOptions() { + DrawPageTitle ("Options"); + SelectFont ("console.ttf size=23"); + + // Enter your name + std::string player_name = GetModel()->GetPlayerName(); + + Engine::GUI::Label (1, "Effects Volume: ", screen_right * 0.5 - 150, 240); + + float effects_volume = Engine::GetEffectsVolume(); + if (Engine::GUI::VerticalSlider (2, screen_right * 0.5 - 100, 250, 250, 16, 0., 1., effects_volume)) { + Engine::LogDebug ("Setting effects volume to: %f", effects_volume); + Engine::SetEffectsVolume(effects_volume); + } + + Engine::GUI::Label (3, "Music Volume: ", screen_right * 0.5 - 150, 300); + + float music_volume = Engine::GetMusicVolume(); + if (Engine::GUI::VerticalSlider (4, screen_right * 0.5 - 100, 310, 250, 16, 0., 1., music_volume)) { + Engine::LogDebug ("Setting music volume to: %f", music_volume); + Engine::SetMusicVolume(music_volume); + } + + + if (Engine::GUI::Button (5, "Back", screen_right * 0.5 - 100, 380, button_width, button_height)) { + PopViewState(); } } @@ -607,11 +656,14 @@ void View::DrawUiEnterPlayername() { } if (Engine::GUI::Button (3, "Start Game", screen_right - 180 - 20, 500, 180, 40)) { + PopViewState(); + PushViewState(ViewStateGameRunning); + GetModel()->OnNewGame(); GetModel()->SetGameState(GameStateRunning); } if (Engine::GUI::Button (5, "Back", 20, 500, 180, 40)) { - GetModel()->SetGameState(GameStateMainMenu); + PopViewState(); } } diff --git a/asteroids/View.h b/asteroids/View.h index bf7573b..12f1a40 100644 --- a/asteroids/View.h +++ b/asteroids/View.h @@ -7,6 +7,7 @@ #include "Sprite.h" #include "EntityBase.h" #include "SimpleConsoleOverlay.h" +#include "AsteroidsEnums.h" namespace asteroids { @@ -45,6 +46,7 @@ class View : public Engine::ViewBase { void DrawUiPlayerDied(); void DrawHighscoreEntry(float x, float y, float entry_width, const std::string &name, unsigned int points); void DrawUiHighscore(); + void DrawUiOptions(); void DrawUiEnterPlayername(); virtual void Draw (); @@ -64,6 +66,20 @@ class View : public Engine::ViewBase { std::vector mShipPartsEntityIds; + std::stack mViewStateStack; + + void PushViewState (const ViewState state) { + Engine::LogDebug ("Pushing ViewState %s", GetStringViewState(state)); + mViewStateStack.push(state); + } + ViewState GetViewState () { + return mViewStateStack.top(); + } + void PopViewState () { + mViewStateStack.pop(); + Engine::LogDebug ("Popped ViewState top is now:%s", GetStringViewState(mViewStateStack.top())); + } + // \todo [high] add Resource Manager! Engine::Sprite mGUIShipSprite; Engine::Sprite mAsteroidSprite; diff --git a/engine/Engine.h b/engine/Engine.h index 8e27047..bb01f52 100644 --- a/engine/Engine.h +++ b/engine/Engine.h @@ -13,6 +13,7 @@ #include #include #include +#include #include #include "Module.h" diff --git a/engine/IMGUIControls.cc b/engine/IMGUIControls.cc index 0385fe6..e643ea0 100644 --- a/engine/IMGUIControls.cc +++ b/engine/IMGUIControls.cc @@ -31,13 +31,9 @@ bool regionhit (int x, int y, int w, int h) { } void DrawBlock (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); - float color[4]; glGetFloatv (GL_CURRENT_COLOR, color); @@ -413,6 +409,103 @@ bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxleng return false; } +float VerticalSlider (int id, int x, int y, int w, int h, float min_value, float max_value, float &value) { + const int knob_width = 16; + const int knob_height = h * 2; + + int slider_pos = (w * value) / max_value - knob_width / 2; + + // Check for hotness + if (regionhit (x, y + h * 0.5 - knob_height * 0.5, w, knob_height)) { + controller->uistate.hotitem = id; + if (controller->uistate.activeitem == 0 + && controller->GetButtonState(MouseButtonLeft)) { + controller->uistate.activeitem = id; + } + } + + // If nothing is selected + if (controller->uistate.hotitem != 0) { + controller->uistate.kbditem = controller->uistate.hotitem; + } + + if (controller->uistate.kbditem == 0) { + controller->uistate.hotitem = id; + controller->uistate.kbditem = id; + } + + // If we have keyboard focus, we highlight the widget + if ( controller->uistate.kbditem == id) { + if (controller->uistate.activeitem == id) { + glColor3f (0.6, 0.6, 0.6); + DrawBlock (x, y, w, h); + glColor3f (0.8, 0.8, 0.8); + DrawBlock (x + slider_pos, y + h * 0.5 - knob_height * 0.5, knob_width, knob_height); + } else { + glColor3f (0.6, 0.6, 0.6); + DrawBlock (x, y, w, h); + glColor3f (0.7, 0.7, 0.7); + DrawBlock (x + slider_pos, y + h * 0.5 - knob_height * 0.5, knob_width, knob_height); + } + } else { + glColor3f (0.4, 0.4, 0.4); + DrawBlock (x, y, w, h); + glColor3f (0.5, 0.5, 0.5); + DrawBlock (x + slider_pos, y + h * 0.5 - knob_height * 0.5, knob_width, knob_height); + } + + if (controller->uistate.kbditem == id) { + switch (controller->uistate.last_keysym) { + case SDLK_DOWN: + controller->uistate.kbditem = 0; + controller->uistate.hotitem = 0; + controller->uistate.last_keysym = SDLK_CLEAR; + break; + case SDLK_UP: + controller->uistate.kbditem = controller->uistate.lastwidget; + controller->uistate.hotitem = controller->uistate.lastwidget; + controller->uistate.last_keysym = SDLK_CLEAR; + break; + case SDLK_LEFT: + value -= (max_value - min_value) * 0.1; + if ( value < min_value) + value = min_value; + controller->uistate.last_keysym = SDLK_CLEAR; + return true; + break; + case SDLK_RIGHT: + value += (max_value - min_value) * 0.1; + if ( value > max_value) + value = max_value; + controller->uistate.last_keysym = SDLK_CLEAR; + return true; + break; + } + } + + controller->uistate.lastwidget = id; + + if (controller->uistate.activeitem == id) { + int mouse_pos[2]; + controller->GetMouseScreenPosition(mouse_pos); + int rel_mouse_pos = mouse_pos[0] - x; + if (rel_mouse_pos < 0) + rel_mouse_pos = 0; + if (rel_mouse_pos > w) + rel_mouse_pos = w; + + float mouse_value = rel_mouse_pos * max_value / static_cast(w); + + if (mouse_value != value) { + value = mouse_value; + return true; + } + + } + + return false; +} + bool CheckKeyPress (int keycode) { if (controller->uistate.last_keysym == keycode) return true; diff --git a/engine/IMGUIControls.h b/engine/IMGUIControls.h index 0815925..029edcb 100644 --- a/engine/IMGUIControls.h +++ b/engine/IMGUIControls.h @@ -34,6 +34,8 @@ bool Button (int id, const char* caption, int x, int y, int w, int h); bool LineEdit (int id, int x, int y, std::string &text_value, const int &maxlength); +float VerticalSlider (int id, int x, int y, int w, int h, float min_value, float max_value, float &value); + /** \brief Checks whether a given key was pressed * * This function can be used to check whether a single key (e.g. ESC) was diff --git a/engine/SoundBase.cc b/engine/SoundBase.cc index 2790ee2..cc5b5dc 100644 --- a/engine/SoundBase.cc +++ b/engine/SoundBase.cc @@ -233,5 +233,36 @@ void HaltSoundLoop (const std::string &sound_name) { SoundInstance->HaltSoundLoop(sound_name); } +void SetMusicVolume (const float &music_volume) { + if (!SoundInstance) { + LogError("Could set music volume: sound system not initialized!"); + } + + SoundInstance->SetMusicVolume(music_volume); +} + +float GetMusicVolume () { + if (!SoundInstance) { + LogError("Could get music volume: sound system not initialized!"); + } + + return SoundInstance->GetMusicVolume(); +} + +void SetEffectsVolume (const float &effects_volume) { + if (!SoundInstance) { + LogError("Could set effects volume: sound system not initialized!"); + } + + SoundInstance->SetEffectsVolume(effects_volume); +} + +float GetEffectsVolume () { + if (!SoundInstance) { + LogError("Could get effects volume: sound system not initialized!"); + } + + return SoundInstance->GetEffectsVolume(); +} }; diff --git a/engine/SoundBaseGlobal.h b/engine/SoundBaseGlobal.h index 6eea6c7..08f0f54 100644 --- a/engine/SoundBaseGlobal.h +++ b/engine/SoundBaseGlobal.h @@ -8,6 +8,21 @@ void PlaySoundLoop (const std::string &sound_name, int count); void HaltSoundLoop (const std::string &sound_name); void PlayMusic (const std::string &music_name); +/** \brief Sets the volume of the music channel to the given value + * + * \param music_value is a value from 0. (no music) to 1. (maximum volume) + */ +void SetMusicVolume (const float &music_volume); +/** \brief Returns the value of the music (0. - 1.) */ +float GetMusicVolume (); +/** \brief Sets the volume of the music channel to the given value + * + * \param music_value is a value from 0. (no music) to 1. (maximum volume) + */ +void SetEffectsVolume (const float &effects_volume); +/** \brief Returns the value of the effects (0. - 1.) */ +float GetEffectsVolume (); + } #endif /* _SOUNDGLOBAL_H */