From f583cf39a8819939de501cb15243ef3e535d540b Mon Sep 17 00:00:00 2001 From: "Martin Felis (berta)" Date: Sun, 28 Nov 2010 00:20:58 +0100 Subject: [PATCH] previous functionality of the view restored with a few new benefits ("non-linear" menus) --- asteroids/AsteroidsEvents.h | 1 + asteroids/Model.cc | 36 +++++++++++++------------- asteroids/Model.h | 2 +- asteroids/View.cc | 50 +++++++++++++++++++++++++++++-------- asteroids/View.h | 32 +++++++++++++++++++----- asteroids/main.cc | 4 +++ engine/Engine.h | 1 + engine/ModelBase.cc | 20 ++++++++++++++- engine/ModelBase.h | 7 ++++++ engine/ModelBaseGlobal.h | 5 ++++ engine/doc/Mainpage.h | 5 +++- 11 files changed, 125 insertions(+), 38 deletions(-) diff --git a/asteroids/AsteroidsEvents.h b/asteroids/AsteroidsEvents.h index 9335fc8..550ab2f 100644 --- a/asteroids/AsteroidsEvents.h +++ b/asteroids/AsteroidsEvents.h @@ -13,6 +13,7 @@ BEGIN_ENUM(Event) DECL_ENUM_ELEMENT(EventChangeGameState), DECL_ENUM_ELEMENT(EventGameOver), DECL_ENUM_ELEMENT(EventShipExplode), + DECL_ENUM_ELEMENT(EventPlayerDied), DECL_ENUM_LAST (Event) } END_ENUM(Event) diff --git a/asteroids/Model.cc b/asteroids/Model.cc index 0fef6e8..a4d5e36 100644 --- a/asteroids/Model.cc +++ b/asteroids/Model.cc @@ -60,7 +60,6 @@ int Model::OnInit (int argc, char* argv[]) { mNewestHighscoreEntryIndex = 99999; // initialize event handlers and register them - Engine::RegisterListener (this, EventLevelComplete); Engine::RegisterListener (this, EventGameOver); Engine::RegisterListener (this, EventShipExplode); @@ -82,9 +81,6 @@ bool Model::OnReceiveEvent (const Engine::EventBasePtr &event) { case EventAccelerateStop: Engine::HaltSoundLoop("./data/sounds/thrust.wav"); break; - case EventLevelComplete: - return OnLevelComplete(); - break; case EventShipExplode: OnShipExplode(); break; @@ -354,6 +350,15 @@ int Model::DoSaveLevel (const char* filename) { 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++; @@ -377,24 +382,13 @@ void Model::SetGameState (const unsigned int &state) { mGameState = state; } -bool Model::OnLevelComplete() { - Engine::LogMessage ("Level complete!"); - - ProceedToNextLevel(); - - 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(GameStatePaused); return false; }; @@ -403,7 +397,7 @@ void Model::OnNewGame() { ClearEntities(); mNewestHighscoreEntryIndex = 99999; - mPlayerLives = 1; + mPlayerLives = 2; mCurrentLevelIndex = 0; mPoints = 0; @@ -414,9 +408,13 @@ void Model::OnShipExplode () { mPlayerLives --; if (mPlayerLives == 0) { - Engine::EventBasePtr gameover_event (new Engine::EventBase()); + 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); } } @@ -436,7 +434,9 @@ void Model::OnKillEntity (const Engine::EntityBase *entity) { unsigned int i; const AsteroidEntity *asteroid = static_cast(entity); - mPoints += 150 + asteroid->mSubAsteroidsCount * 75; + + if (GetPlayerEntityId() != Engine::NullEntityId) + mPoints += 150 + asteroid->mSubAsteroidsCount * 75; for (i = 0; i < mAsteroids.size(); i++) { if (mAsteroids.at(i) == entity->mId) { diff --git a/asteroids/Model.h b/asteroids/Model.h index 77d1b9a..54ce104 100644 --- a/asteroids/Model.h +++ b/asteroids/Model.h @@ -15,7 +15,6 @@ class Model : public Engine::ModelBase { float GetWorldHeight (); /* Event handler */ - bool OnLevelComplete(); bool OnGameOver(); /** \brief Resets values from a previous game */ void OnNewGame (); @@ -25,6 +24,7 @@ class Model : public Engine::ModelBase { int DoLoadLevel (const char* filename); int DoSaveLevel (const char* filename); + void ReloadLevel(); void ProceedToNextLevel (); int GetPlayerLives () { return mPlayerLives; }; unsigned int GetPoints () { return mPoints; }; diff --git a/asteroids/View.cc b/asteroids/View.cc index d095169..b9ffc32 100644 --- a/asteroids/View.cc +++ b/asteroids/View.cc @@ -71,7 +71,9 @@ int View::OnInit (int argc, char* argv[]) { Engine::RegisterListener (this, EventAccelerateStart); Engine::RegisterListener (this, EventAccelerateStop); Engine::RegisterListener (this, EventShipExplode); + Engine::RegisterListener (this, EventPlayerDied); Engine::RegisterListener (this, EventGameOver); + Engine::RegisterListener (this, EventLevelComplete); PushViewState (ViewStateMainMenu); @@ -96,19 +98,32 @@ bool View::OnReceiveEvent (const Engine::EventBasePtr &event) { return true; break; + case EventLevelComplete: + PushViewState(ViewStateLevelComplete); + GetModel()->SetPlayerEntityId(Engine::NullEntityId); + break; + case EventGameOver: + mFadeTimerSecValue = 2.; + PopViewState(); PushViewState(ViewStateGameOver); - GetModel()->SetGameState(GameStatePaused); + + GetModel()->SetPlayerEntityId(Engine::NullEntityId); + + break; + case EventPlayerDied: + mFadeTimerSecValue = 2.; + PushViewState(ViewStatePlayerDied); + + GetModel()->SetPlayerEntityId(Engine::NullEntityId); break; case EventShipExplode: { - Engine::LogDebug ("Received Ship Explode Event: %d", event->mEventType); + // This event is fired when the ship explodes. Note: this event is not + // sufficient for EventPlayerDied! + Engine::LogDebug ("Received PlayerDied Event: %d", event->mEventType); - PopViewState(); - PushViewState(ViewStatePlayerDied); - GetModel()->SetGameState(GameStatePaused); - // insert sprits that contains parts of the ship Engine::EntityBase *ship_entity = Engine::GetEntity (event->mEventUnsignedInt); vector3d position = ship_entity->mPhysicState->mPosition; @@ -206,6 +221,8 @@ void View::DrawStars() { void View::Draw() { PreDraw(); + mFadeTimerSecValue -= GetModel()->GetFrameDuration(); + // Actual Drawing UpdateCamera (); @@ -215,12 +232,11 @@ void View::Draw() { /* if (mDrawAxis) DrawAxis (); - */ + */ DrawWorld (); DrawUi (); -// mOverlayManager.Draw(); // Perform post-Draw actions PostDraw(); @@ -499,9 +515,15 @@ void View::DrawUiGameOver() { screen_bottom * 0.5 + 32); } + if (mFadeTimerSecValue > 0.) + return; + if (Engine::GUI::Button (2, "Continue...", (screen_right - button_width) * 0.5, screen_bottom * 0.5 + 80, button_width, button_height)) { + // We do not want the background to show the remaining bits of the game + GetModel()->SetGameState(GameStatePaused); + PopViewState(); PushViewState(ViewStateShowHighscore); } @@ -514,6 +536,7 @@ void View::DrawUiLevelComplete() { if(Engine::GUI::Button (1, "Next level ...", (screen_right - button_width) * 0.5, screen_bottom * 0.5 + 60, button_width, button_height)) { PopViewState(); + GetModel()->ProceedToNextLevel(); } } @@ -531,8 +554,7 @@ void View::DrawUiGamePaused() { } if (Engine::GUI::Button (3, "Abort Game", screen_right * 0.5 - 100, 300, button_width, button_height)) { - while (mViewStateStack.size()) - PopViewState(); + ResetViewState(); PushViewState(ViewStateMainMenu); GetModel()->SetGameState(GameStatePaused); @@ -547,8 +569,13 @@ void View::DrawUiPlayerDied() { DrawPageTitle ("You died!"); SelectFont ("console.ttf size=23"); - if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 200, button_width, button_height)) { + if (mFadeTimerSecValue > 0.) + return; + + if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 380, button_width, button_height)) { PopViewState(); + GetModel()->ReloadLevel(); + GetModel()->SetGameState(GameStateRunning); } } @@ -593,6 +620,7 @@ void View::DrawUiHighscore() { DrawPageTitle ("Highscores"); SelectFont ("console.ttf size=23"); + unsigned int entry_length = 32; float char_width, height; DrawGLStringMeasure ("M", &char_width, &height); diff --git a/asteroids/View.h b/asteroids/View.h index 3949df2..a8809f4 100644 --- a/asteroids/View.h +++ b/asteroids/View.h @@ -20,6 +20,12 @@ struct BackgroundStar { }; /** \brief Performs the actual drawing based on Camera and Model + * + * This View also takes care of the drawn user interface. There are different + * view states that are drawn by the member functions with signatures + * DrawUi{ViewStateName}(). Different view states can be pushed onto and + * popped from the mViewStateStack which allows non-linear arrangement + * of different ViewStates in menus. */ class View : public Engine::ViewBase { public: @@ -59,28 +65,39 @@ class View : public Engine::ViewBase { void DrawRocket (RocketEntity *asteroid); void DrawShipPart (Engine::EntityBase *entity); - // boost::shared_ptr mMenuOverlay; - // boost::shared_ptr mConsoleOverlay; - std::vector mBackgroundStars; - std::vector mShipPartsEntityIds; + /** \brief The ViewState stack that is used for non-linear menus */ std::stack mViewStateStack; + /** \brief Pushes the given state onto mViewStateStack */ void PushViewState (const ViewState state) { Engine::LogDebug ("Pushing ViewState %s", GetStringViewState(state)); mViewStateStack.push(state); } + /** \brief Returns the current ViewState */ ViewState GetViewState () { return mViewStateStack.top(); } + /** \brief Removes the top element of the current ViewState stack */ void PopViewState () { // Warning: you must not query for an invalid enum with // GetStringENUM_NAME! - Engine::LogDebug("Popping ViewState: %s remainging: %u", GetStringViewState(mViewStateStack.top()), mViewStateStack.size()); - + std::string popped_name = GetStringViewState(mViewStateStack.top()); mViewStateStack.pop(); + + std::string current_name ("None"); + if (mViewStateStack.size() > 0) + current_name = GetStringViewState(mViewStateStack.top()); + + Engine::LogDebug("Popped ViewState: %s current %s remaining: %u", popped_name.c_str(), current_name.c_str(), mViewStateStack.size()); + } + /** \brief Removes all elements of the ViewState stack */ + void ResetViewState() { + Engine::LogDebug ("Resetting ViewState stack"); + while (mViewStateStack.size()) + mViewStateStack.pop(); } // \todo [high] add Resource Manager! @@ -98,6 +115,9 @@ class View : public Engine::ViewBase { int button_width; int button_height; + /// \brief can be used to perform some fading, etc. + float mFadeTimerSecValue; + virtual bool OnReceiveEvent (const Engine::EventBasePtr &event); }; diff --git a/asteroids/main.cc b/asteroids/main.cc index 3515097..7320f52 100644 --- a/asteroids/main.cc +++ b/asteroids/main.cc @@ -46,6 +46,10 @@ int main (int argc, char* argv[]) { Engine::RunCommand ("exec asteroids.rc"); + Engine::LogMessage("Warning: muting sound!"); + Engine::SetEffectsVolume(0.); + Engine::SetMusicVolume(0.); + engine.MainLoop (); SDL_WM_SetIcon(NULL,NULL); diff --git a/engine/Engine.h b/engine/Engine.h index 2e8e72d..15e3932 100644 --- a/engine/Engine.h +++ b/engine/Engine.h @@ -15,6 +15,7 @@ #include #include #include +#include #include "Module.h" #include "EngineEnums.h" diff --git a/engine/ModelBase.cc b/engine/ModelBase.cc index e637654..edc0b66 100644 --- a/engine/ModelBase.cc +++ b/engine/ModelBase.cc @@ -163,6 +163,9 @@ void ModelBase::UnregisterEntity (const unsigned int id) { } EntityBase* ModelBase::GetEntity (const unsigned int id) { + if (id == NullEntityId) + return NULL; + std::map::iterator iter = mEntities.find (id); if (iter != mEntities.end ()) { @@ -173,6 +176,9 @@ EntityBase* ModelBase::GetEntity (const unsigned int id) { } unsigned int ModelBase::CreateEntityId () { + if (mEntityIdCounter == NullEntityId - 1) + LogError ("Could not create valid entity id, reached maximum value of %u", mEntityIdCounter); + return ++mEntityIdCounter; } @@ -201,6 +207,10 @@ unsigned int ModelBase::GetPlayerEntityId () { return mPlayerEntityId; } +void ModelBase::SetPlayerEntityId(unsigned int entity_id) { + mPlayerEntityId = entity_id; +} + /** * \param collision_time The time when the collision occured relative to the start of the simulation frame */ @@ -249,12 +259,20 @@ void ModelBase::SendEntityCollisionEvent (const unsigned int reference_entity_id */ unsigned int GetPlayerEntityId () { if (!ModelInstance) { - LogError ("Couldn't create Entity: Model not initialized!"); + LogError ("Couldn't get Player id: Model not initialized!"); } return ModelInstance->GetPlayerEntityId (); } +void SetPlayerEntityId(unsigned int entity_id) { + if (!ModelInstance) { + LogError ("Couldn't set Player id: Model not initialized!"); + } + + ModelInstance->SetPlayerEntityId (entity_id); +} + float GetFrameDuration () { if (!ModelInstance) { LogError ("Couldn't create Entity: Model not initialized!"); diff --git a/engine/ModelBase.h b/engine/ModelBase.h index 4ca6dff..d2f2a82 100644 --- a/engine/ModelBase.h +++ b/engine/ModelBase.h @@ -13,6 +13,8 @@ class Events; class EntityFactoryBase; class OverlayManager; +const unsigned int NullEntityId = std::numeric_limits::max() - 1; + struct EntityBase; /** \brief Represents the current state of the Engine @@ -69,6 +71,11 @@ class ModelBase : public Module { /** Returns the id of the entity the player is currently controlling */ unsigned int GetPlayerEntityId (); + /** \brief Assigns the player to an Entity. All controls will be redirected to that player + * + * This can be used to disable the controls of of the player by assinging + * the special entity id of NullEntityId */ + void SetPlayerEntityId(unsigned int entity_id); /** Notifies the gamelogic of a collision event */ void SendEntityCollisionEvent (const unsigned int reference_entity_id, diff --git a/engine/ModelBaseGlobal.h b/engine/ModelBaseGlobal.h index 3e2ca0a..ae62cbc 100644 --- a/engine/ModelBaseGlobal.h +++ b/engine/ModelBaseGlobal.h @@ -5,6 +5,11 @@ namespace Engine { /** \brief Adds the function callback as command for the given name*/ unsigned int GetPlayerEntityId (); +/** \brief Assigns the player to an Entity. All controls will be redirected to that player + * + * This can be used to disable the controls of of the player by assinging + * the special entity id of NullEntityId */ +void SetPlayerEntityId(unsigned int entity_id); /** \brief Returns the duration of the frame in seconds */ float GetFrameDuration (); diff --git a/engine/doc/Mainpage.h b/engine/doc/Mainpage.h index 412420c..29ee4a3 100644 --- a/engine/doc/Mainpage.h +++ b/engine/doc/Mainpage.h @@ -72,6 +72,8 @@ * todos within the code have a look at the \ref todo. * * \todo [high] Create a simple racing or asteroids game + * \todo [high] Enable saving of music and effects volume + * \todo [med] Use shared_ptr instead of raw pointers for the Entities * \todo [med] Clear all references of EntityVisualState and EntityGameState * \todo [med] Clarify functionalities of CreateEntity, KillEntity, RegisterEntity, UnregisterEntity (which frees memory, which does only change the state of the Model?) * \todo [med] Add basic networking @@ -86,7 +88,8 @@ * Engine::Module::Init() * * Done: - * \todo [high] (11-09-2010) Since introduction of the IMGUI pressing 'space' in the menu crashes the game + * \todo [high] (28-11-2010) Allow transitions when player dies +* \todo [high] (11-09-2010) Since introduction of the IMGUI pressing 'space' in the menu crashes the game * - [high] In Physics remove dependancy on the Model to pass on collision * events * - [high] Fix Physics bug when two actors collide actively (i.e. velocity