#include "View.h" #include "CameraBase.h" #include "SimpleConsoleOverlay.h" #include "IMGUIControls.h" #include "Engine.h" #include "Physics.h" #include "Model.h" #include "Controller.h" #include "EventBase.h" #include "Game.h" #include "ShipEntity.h" #include "AsteroidEntity.h" #include "AsteroidsEvents.h" #include "RocketEntity.h" #include #include // #define DRAW_BOUNDARIES #ifdef DRAW_BOUNDARIES #include "coll2d.h" #include "DrawingsGL.h" #endif using namespace std; namespace asteroids { int View::OnInit (int argc, char* argv[]) { ViewBase::OnInit (argc, argv); /* // We want the console mConsoleOverlay = boost::shared_ptr (new Engine::SimpleConsoleOverlay); // We also want to display the log bar mConsoleOverlay->SetDrawLogBar (true); mOverlayManager.Register (mConsoleOverlay, GameStateMainMenu); */ LoadFont("AldotheApache.ttf", 20); // This is a simple star field that makes the game so spacy int i; for (i = 0; i < 200; i++) { BackgroundStar star; star.position[0] = rand() / float(RAND_MAX); star.position[1] = rand() / float(RAND_MAX); star.position[2] = rand() / float(RAND_MAX); mBackgroundStars.push_back (star); } mGUIShipSprite.LoadFromPNG("./data/textures/ship.png"); mGUIShipSprite.SetScale (0.1); mAsteroidSprite.LoadFromPNG ("./data/textures/asteroid.png"); mShipSprite.LoadFromPNG ("./data/textures/ship.png"); mShipThrustSprite.LoadFromPNG ("./data/textures/ship_thrust.png"); mShipThrustSprite.SetAnimation (4, 8); mShipPartsSprite.LoadFromPNG ("./data/textures/ship_parts.png"); mShipPartsSprite.SetSubSpriteCount (10); Engine::RegisterListener (this, EventAccelerateStart); Engine::RegisterListener (this, EventAccelerateStop); Engine::RegisterListener (this, EventShipExplode); return 0; } void View::OnDestroy() { mBackgroundStars.clear(); mShipPartsEntityIds.clear(); Engine::ViewBase::OnDestroy(); } bool View::OnReceiveEvent (const Engine::EventBasePtr &event) { switch (event->mEventType) { case EventAccelerateStart: case EventAccelerateStop: if (event->mEventType == EventAccelerateStart) mShipThrustSprite.ResetAnimation(); Engine::LogDebug ("Received Acceleration Event: %d", event->mEventType); return true; break; case EventShipExplode: if (event->mEventType == EventShipExplode) { Engine::EntityBase *ship_entity = Engine::GetEntity (event->mEventUnsignedInt); vector3d position = ship_entity->mPhysicState->mPosition; vector3d orientation = ship_entity->mPhysicState->mOrientation; vector3d velocity = ship_entity->mPhysicState->mVelocity; unsigned int i; mShipPartsEntityIds.clear(); for (i = 0; i < mShipPartsSprite.GetSubSpriteCount(); i++) { Engine::EntityBase* part_sprite_particle = Engine::CreateEntity (GameEntityTypeShipPart); part_sprite_particle->mPhysicState->mPosition = position; part_sprite_particle->mPhysicState->mOrientation = orientation; part_sprite_particle->mPhysicState->mVelocity = velocity; part_sprite_particle->mPhysicState->mVelocity = vector3d (velocity[0] * (rand()/float(RAND_MAX)) * 1.7, 0., velocity[2] * (rand()/float(RAND_MAX)) * 1.5); part_sprite_particle->mPhysicState->mAngleVelocity = (rand()/float(RAND_MAX) - 0.5 ) * 100.; mShipPartsEntityIds.push_back(part_sprite_particle->mId); } } Engine::LogDebug ("Received Ship Explode Event: %d", event->mEventType); return true; 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 View::UpdateCamera () { mCamera->SetEye ( 0., 9.5, 0. ); mCamera->SetPointOfIntrest ( 0., 0., 0. ); mCamera->SetUp ( 0., 0., -1. ); mCamera->Update (); } void View::DrawStars() { unsigned int i; float world_width, world_height; world_width = static_cast(mModel)->GetWorldWidth(); world_height = static_cast(mModel)->GetWorldHeight(); vector3d velocity (1., 0., 0.); glPushMatrix(); glTranslatef(-world_width * 0.5, 0, -world_height * 0.5); glColor3f (1., 1., 1.); glPointSize(2.); glBegin(GL_POINTS); float z_value; for (i = 0; i < mBackgroundStars.size(); i++) { // glPointSize (2. + 300. *mBackgroundStars.at(i).position[1]); z_value = mBackgroundStars.at(i).position[1] + 0.1; glColor3f (z_value, z_value, z_value); glVertex3f (mBackgroundStars.at(i).position[0] * world_width, -1., mBackgroundStars.at(i).position[2] * world_height); mBackgroundStars.at(i).position -= vector3d(Engine::GetFrameDuration() * 0.7 * mBackgroundStars.at(i).position[1] / world_width, 0., 0.); if (mBackgroundStars.at(i).position[0] < 0.) mBackgroundStars.at(i).position[0] += 1.; if (mBackgroundStars.at(i).position[0] >= 1.) mBackgroundStars.at(i).position[0] -= 1.; } glEnd(); glPopMatrix(); } void View::Draw() { PreDraw(); // Actual Drawing UpdateCamera (); if (mDrawGrid) DrawGrid (); /* if (mDrawAxis) DrawAxis (); */ DrawWorld (); DrawUi (); // mOverlayManager.Draw(); // Perform post-Draw actions PostDraw(); } void View::DrawWorld() { std::map::iterator entity_iterator; Model *game_model = static_cast (mModel); DrawStars (); if ( game_model->GetGameState() != GameStateRunning) { return; } ViewBase::DrawWorld(); for (entity_iterator = game_model->mEntities.begin (); entity_iterator != game_model->mEntities.end(); entity_iterator++) { Engine::EntityBase* entity = entity_iterator->second; // Perform multiple drawing if the entity is at the border Physics* game_physics = (Physics*) game_model->mPhysics; float world_width = game_physics->GetWorldWidth(); float world_height = game_physics->GetWorldHeight(); // Drawing at the original position: glPushMatrix (); glTranslatef (entity->mPhysicState->mPosition[0], entity->mPhysicState->mPosition[1], entity->mPhysicState->mPosition[2]); glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); glColor3f (1., 1., 1.); DrawEntity (entity); glPopMatrix (); // If we move out the right side if (entity->mPhysicState->mPosition[0] + entity->mPhysicState->mRadius * 2 >= world_width * 0.5) { glPushMatrix (); glTranslatef (entity->mPhysicState->mPosition[0] - world_width, entity->mPhysicState->mPosition[1], entity->mPhysicState->mPosition[2]); glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); glColor3f (1., 1., 1.); DrawEntity (entity); glPopMatrix (); } // if we move out the left side if (entity->mPhysicState->mPosition[0] - entity->mPhysicState->mRadius * 2 < - world_width * 0.5) { glPushMatrix (); glTranslatef (entity->mPhysicState->mPosition[0] + world_width, entity->mPhysicState->mPosition[1], entity->mPhysicState->mPosition[2]); glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); glColor3f (1., 1., 1.); DrawEntity (entity); glPopMatrix (); } // If we move out the bottom side if (entity->mPhysicState->mPosition[2] + entity->mPhysicState->mRadius * 2 >= world_height * 0.5) { glPushMatrix (); glTranslatef (entity->mPhysicState->mPosition[0], entity->mPhysicState->mPosition[1], entity->mPhysicState->mPosition[2] - world_height); glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); glColor3f (1., 1., 1.); DrawEntity (entity); glPopMatrix (); } // if we move out the left side if (entity->mPhysicState->mPosition[2] - entity->mPhysicState->mRadius * 2 < - world_height* 0.5) { glPushMatrix (); glTranslatef (entity->mPhysicState->mPosition[0], entity->mPhysicState->mPosition[1], entity->mPhysicState->mPosition[2] + world_height); glRotatef(entity->mPhysicState->mOrientation[0], 0., 0., 1.); glRotatef(entity->mPhysicState->mOrientation[1], 0., 1., 0.); glRotatef(entity->mPhysicState->mOrientation[2], 1., 0., 0.); glColor3f (1., 1., 1.); DrawEntity (entity); glPopMatrix (); } } } /* * Userinterface */ void View::DrawUi () { glClearColor (0.1, 0.1, 0.1, 1.); screen_right = static_cast (Engine::GetWindowWidth()); screen_bottom = static_cast (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 (); unsigned int game_state = GetModel()->GetGameState(); SelectFont("console.ttf"); SetFontJustification (Engine::FontJustificationLeft); GetController()->EnableTextinput(true); switch (game_state) { case GameStateMainMenu: DrawUiMainMenu(); break; case GameStateRunning: DrawUiGameRunning(); break; case GameStatePaused: DrawUiGamePaused(); break; case GameStatePlayerDied: DrawUiPlayerDied(); break; case GameStateLevelComplete: DrawUiLevelComplete(); break; case GameStateShowHighscore: DrawUiHighscore(); break; case GameStateEnterPlayername: DrawUiEnterPlayername(); break; case GameStateGameOver: DrawUiGameOver(); break; default: Engine::LogWarning ("Trying to draw unknown GameState: %s (%d)", GetStringGameState (game_state), game_state); break; } glPopMatrix (); glMatrixMode (GL_PROJECTION); glPopMatrix (); glMatrixMode (GL_MODELVIEW); } void View::DrawUiMainMenu() { Engine::GUI::Label (4, "A s t e r o i d s", screen_right * 0.5 - 100, 180); if (Engine::GUI::Button (1, "New Game", screen_right * 0.5 - 100, 200, 250, 40)) { GetModel()->SetGameState(GameStateEnterPlayername); } if (Engine::GUI::Button (2, "Highscores", screen_right * 0.5 - 100, 250, 250, 40)) { GetModel()->SetGameState(GameStateShowHighscore); } if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, 250, 40)) { Engine::RunCommand("quit"); } } void View::DrawUiGameRunning() { // We choose a different font and also draw it aligned to the right as this // looks nicer with the points SelectFont ("AldotheApache.ttf"); SetFontJustification (Engine::FontJustificationRight); std::ostringstream out_stream; out_stream << GetModel()->GetPlayerLives() << " x "; DrawGLString (screen_right - 64, screen_bottom - 20, out_stream.str().c_str()); mGUIShipSprite.DrawAt2D (screen_right - 32 - 10, screen_bottom - 16); out_stream.str(""); out_stream << GetModel()->GetPoints(); DrawGLString (screen_right - 30, 40, out_stream.str().c_str()); // revert the font justification SetFontJustification (Engine::FontJustificationLeft); if (Engine::GUI::CheckKeyPress(SDLK_ESCAPE)) GetModel()->SetGameState(GameStatePaused); } void View::DrawUiGameOver() { Engine::GUI::Label (4, "G a m e O v e r", screen_right * 0.5 - 100, 180); if (GetModel()->GetPlayerLives() == 0) { Engine::GUI::Label(5, "That was pathetic!", screen_right * 0.5 - 80, screen_bottom * 0.5 - 8); } else { Engine::GUI::Label (6, "You earned yourself the", screen_right * 0.5 - 80, screen_bottom * 0.5 - 32); Engine::GUI::Label (7, "You Rock(TM) award.", screen_right * 0.5 - 60, screen_bottom * 0.5 + 0); Engine::GUI::Label (8, "Go tell your friends.", screen_right * 0.5 - 70, screen_bottom * 0.5 + 32); } if (Engine::GUI::Button (2, "Continue...", screen_right * 0.5 - 100, screen_bottom * 0.5 + 60, 250, 40)) { GetModel()->SetGameState(GameStateShowHighscore); } } void View::DrawUiLevelComplete() { Engine::GUI::Label (4, "Level Complete!", screen_right * 0.5 - 100, 180); Engine::GUI::Label (6, "Congratulations - You rock!", screen_right * 0.5 - 80, screen_bottom * 0.5 -16); if(Engine::GUI::Button (1, "Next level ...", screen_right * 0.5 - 80, screen_bottom * 0.5 -16, 250, 40)) { GetModel()->SetGameState(GameStateRunning); } } void View::DrawUiGamePaused() { Engine::GUI::Label (4, "- P a u s e d -", screen_right * 0.5 - 100, 180); if (Engine::GUI::Button (1, "Resume Game", screen_right * 0.5 - 100, 200, 250, 40)) { GetModel()->SetGameState(GameStateRunning); } if (Engine::GUI::Button (2, "Abort Game", screen_right * 0.5 - 100, 250, 250, 40)) { GetModel()->SetGameState(GameStateMainMenu); } if (Engine::GUI::Button (3, "Quit", screen_right * 0.5 - 100, 330, 250, 40)) { Engine::RunCommand("quit"); } } void View::DrawUiPlayerDied() { Engine::GUI::Label (4, "You died ...", screen_right * 0.5 - 40, screen_bottom * 0.5 - 8 - 32); if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 200, 250, 40)) { GetModel()->SetGameState(GameStateRunning); } } void View::DrawUiHighscore() { float x = screen_right * 0.5 - 100; 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; 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) { SetFontColor (224./255., 200/255., 0.); Engine::DrawGLString ( x, y, out_stream.str().c_str()); SetFontColor (1., 1., 1.); } else { Engine::DrawGLString ( x, y, out_stream.str().c_str()); } // go down one line y += 16; i++; highscore_iter++; } if (Engine::GUI::Button (1, "Back to MainMenu", x + 16, y + 16, 250, 40)) { 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); // Enter your name std::string player_name = GetModel()->GetPlayerName(); Engine::GUI::Label (1, "Enter your name: ", screen_right * 0.5 - 100, 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)) { GetModel()->SetGameState(GameStateRunning); } if (Engine::GUI::Button (5, "Back", 20, 500, 150, 40)) { GetModel()->SetGameState(GameStateMainMenu); } } /* * Entities */ void View::DrawEntity (Engine::EntityBase *entity) { if (entity->mType == GameEntityTypeAsteroid) DrawAsteroid ((AsteroidEntity*) entity); else if (entity->mType == GameEntityTypeShip) DrawShip ((ShipEntity*) entity); else if (entity->mType == GameEntityTypeRocket) DrawRocket ((RocketEntity*) entity); else if (entity->mType == GameEntityTypeShipPart) DrawShipPart (entity); else { Engine::LogError ("Cannot draw entity: unknown type '%d'", entity->mType); } } /// \todo: Update of the animation ?? void View::DrawShip (ShipEntity *ship) { if (!ship->mAlive) return; mShipSprite.SetScale (2. * ship->mPhysicState->mRadius / mShipSprite.GetHeight()); mShipThrustSprite.SetScale (2. * ship->mPhysicState->mRadius / mShipSprite.GetHeight()); if (ship->mState == ShipEntity::Accelerating) { mShipThrustSprite.UpdateAnimation (Engine::GetFrameDuration()); mShipThrustSprite.DrawAt(-0.5, 0., 0.); } mShipSprite.DrawAt(0., 0., 0.); #ifdef DRAW_BOUNDARIES glColor3f (1., 1., 1.); DrawCircle (ship->mPhysicState->mRadius, 20); #endif } void View::DrawAsteroid (AsteroidEntity *asteroid) { mAsteroidSprite.SetScale (2. * asteroid->mPhysicState->mRadius / mAsteroidSprite.GetWidth()); mAsteroidSprite.DrawAt(0., 0., 0.); #ifdef DRAW_BOUNDARIES glColor3f (1., 1., 1.); DrawCircle (asteroid->mPhysicState->mRadius, 20); #endif } void View::DrawRocket (RocketEntity *rocket) { glColor3f (1., 1., 1.); glBegin (GL_QUADS); glVertex3f (-0.25, 0., 0.05); glVertex3f (0.05, 0., 0.05); glVertex3f (0.05, 0., -0.05); glVertex3f (-0.25, 0., -0.05); glEnd (); } void View::DrawShipPart (Engine::EntityBase *entity) { unsigned int i; mShipPartsSprite.SetScale (1. / mShipSprite.GetHeight()); for (i = 0; i < mShipPartsEntityIds.size(); i++) { if (mShipPartsEntityIds.at(i) == entity->mId) { mShipPartsSprite.DrawSubAt (i, 0., 0., 0.); } } #ifdef DRAW_BOUNDARIES glColor3f (1., 1., 1.); DrawCircle (entity->mPhysicState->mRadius, 20); #endif } }