fysxasteroids/asteroids/View.cc

1257 lines
35 KiB
C++

#include "View.h"
#include "CameraBase.h"
#include "SimpleConsoleOverlay.h"
#include "IMGUIControls.h"
#include "keytable.h"
#include "DrawingsGL.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 <GL/gl.h>
#include <GL/glu.h>
// #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<Engine::SimpleConsoleOverlay> (new Engine::SimpleConsoleOverlay);
// We also want to display the log bar
mConsoleOverlay->SetDrawLogBar (true);
mOverlayManager.Register (mConsoleOverlay, GameStateMainMenu);
*/
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++) {
BackgroundStar star;
star.position[0] = rand() / float(RAND_MAX) * 1.2;
star.position[1] = rand() / float(RAND_MAX) * 1.2;
star.position[2] = rand() / float(RAND_MAX) * 1.2;
mBackgroundStars.push_back (star);
}
// Editor default variables
mEditorEntityId = 0;
mEditorState = EditorStateUnknown;
mGUIShipSprite.LoadFromPNG(Engine::GetResourceFullPath("/data/textures/ship.png"));
mGUIShipSprite.SetScale (0.1);
mAsteroidSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/asteroid.png"));
mShipSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship.png"));
mShipThrustSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship_thrust.png"));
mShipThrustSprite.SetAnimation (4, 8);
mShipPartsSprite.LoadFromPNG (Engine::GetResourceFullPath("/data/textures/ship_parts.png"));
mShipPartsSprite.SetSubSpriteCount (10);
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);
mCreditsPageIndex = 0;
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 EventLevelComplete:
if (GetModel()->GetGameState() == GameStateRunning) {
PushViewState(ViewStateLevelComplete);
GetController()->ResetPlayerEntity();
}
break;
case EventGameOver:
mFadeTimerSecValue = 2.;
PopViewState();
PushViewState(ViewStateGameOver);
GetController()->ResetPlayerEntity();
break;
case EventPlayerDied:
mFadeTimerSecValue = 2.;
PushViewState(ViewStatePlayerDied);
GetController()->ResetPlayerEntity();
break;
case EventShipExplode: {
// This event is fired when the ship explodes. Note: this event is not
// sufficient for EventPlayerDied!
Engine::LogDebug ("Received PlayerDied Event: %d", event->mEventType);
// insert sprits that contains parts of the ship
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;
vector3d rel_velocity = event->mVector3d;
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;
vector3d part_velocity (
rel_velocity[0] * (rand()/float(RAND_MAX)) * 1.7,
0.,
rel_velocity[2] * (rand()/float(RAND_MAX)) * 1.5
);
part_sprite_particle->mPhysicState->mVelocity = part_velocity * -1.;
part_sprite_particle->mPhysicState->mAngleVelocity = (rand()/float(RAND_MAX) - 0.5 ) * 100.;
mShipPartsEntityIds.push_back(part_sprite_particle->mId);
}
// We do not need the entity anymore
Engine::KillEntity(event->mEventUnsignedInt);
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<Model*>(mModel)->GetWorldWidth();
world_height = static_cast<Model*>(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.);
// we move the stars a little more such that they are not blende out too early
// \todo compute proper bounds from Physics world size!
if (mBackgroundStars.at(i).position[0] < -0.1)
mBackgroundStars.at(i).position[0] += 1.2;
if (mBackgroundStars.at(i).position[0] >= 1.1)
mBackgroundStars.at(i).position[0] -= 1.2;
}
glEnd();
glPopMatrix();
}
void View::Draw() {
PreDraw();
mFadeTimerSecValue -= GetModel()->GetFrameDuration();
// Actual Drawing
UpdateCamera ();
if (mDrawGrid)
DrawGrid ();
/*
if (mDrawAxis)
DrawAxis ();
*/
std::map<unsigned int, Engine::EntityBase*>::iterator entity_iterator;
Model *game_model = static_cast<Model*> (mModel);
unsigned int game_state = game_model->GetGameState();
DrawStars ();
if ( game_state == GameStateRunning || GetViewState() == ViewStateEditor) {
DrawWorld ();
}
DrawUi ();
// Perform post-Draw actions
PostDraw();
}
void View::DrawWorld() {
std::map<unsigned int, Engine::EntityBase*>::iterator entity_iterator;
Model *game_model = static_cast<Model*> (mModel);
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();
// if we are in Edit mode and are currently editing the velocity we must
// make sure that we display it
if (GetViewState() == ViewStateEditor) {
if (mEditorState == EditorStateEntityVelocity) {
DrawVector (entity->mPhysicState->GetPosition(),
entity->mPhysicState->GetPosition() + entity->mPhysicState->GetVelocity());
}
}
// 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<float> (Engine::GetWindowWidth());
screen_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 ();
unsigned int game_state = GetModel()->GetGameState();
SelectFont("console.ttf");
SetFontJustification (Engine::FontJustificationLeft);
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 ViewStateGameRunning:
DrawUiGameRunning();
break;
case ViewStatePaused:
DrawUiGamePaused();
break;
case ViewStatePlayerDied:
DrawUiPlayerDied();
break;
case ViewStateLevelComplete:
DrawUiLevelComplete();
break;
case ViewStateShowHighscore:
DrawUiHighscore();
break;
case ViewStateOptions:
DrawUiOptions();
break;
case ViewStateEnterPlayername:
DrawUiEnterPlayername();
break;
case ViewStateGameOver:
DrawUiGameOver();
break;
case ViewStateEditor:
DrawUiEditor();
break;
case ViewStateCredits:
DrawUiCredits();
break;
default:
Engine::LogWarning ("Trying to draw unknown ViewState: %s (%d)",
GetStringViewState (game_state), game_state);
break;
}
glPopMatrix ();
glMatrixMode (GL_PROJECTION);
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() {
DrawPageTitle ("Asteroids");
SelectFont("console.ttf size=23 color=#444444");
DrawGLString( screen_right - 272, screen_bottom - 16, "http://www.fysx.org");
SelectFont("console.ttf size=23 color=#cccccc");
DrawGLString( screen_right - 270, screen_bottom - 18, "http://www.fysx.org");
SelectFont("console.ttf size=23");
if (Engine::GUI::Button (1, "New Game", screen_right * 0.5 - 100, 200, button_width, button_height)) {
PushViewState(ViewStateEnterPlayername);
}
if (Engine::GUI::Button (2, "Options", screen_right * 0.5 - 100, 250, button_width, button_height)) {
PushViewState (ViewStateOptions);
}
if (Engine::GUI::Button (3, "Highscores", screen_right * 0.5 - 100, 300, button_width, button_height)) {
PushViewState(ViewStateShowHighscore);
}
if (Engine::GUI::Button (4, "Credits", screen_right * 0.5 - 100, 350, button_width, button_height)) {
mCreditsPageIndex = 0;
PushViewState(ViewStateCredits);
}
/*
if (Engine::GUI::Button (6, "E", screen_right - 48, 20, 32, button_height)) {
PushViewState(ViewStateEditor);
GetController()->ResetLevel();
}
*/
if (Engine::GUI::Button (5, "Quit", screen_right * 0.5 - 100, 430, button_width, button_height)) {
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 size=20 color=#ffffff");
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)) {
PushViewState(ViewStatePaused);
GetModel()->SetGameState(GameStatePaused);
}
}
void View::DrawUiGameOver() {
DrawPageTitle ("Game Over");
SelectFont("console.ttf size=23 color=#ffffff");
if (GetModel()->GetPlayerLives() == 0) {
Engine::GUI::LabelCentered(5,
"That was pathetic!",
screen_right * 0.5,
screen_bottom * 0.5 - 8);
} else {
Engine::GUI::LabelCentered (6, "You earned yourself the",
screen_right * 0.5,
screen_bottom * 0.5 - 32);
Engine::GUI::LabelCentered (7, "You Rock(TM) award.",
screen_right * 0.5,
screen_bottom * 0.5 + 0);
Engine::GUI::LabelCentered (8, "Go tell your friends.",
screen_right * 0.5,
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);
}
}
void View::DrawUiLevelComplete() {
DrawPageTitle ("Level Complete!");
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)) {
PopViewState();
// we have to take care when we are testing the level to not proceed to
// the next level...
if (GetViewState() == ViewStateEditor) {
GetModel()->SetGameState(GameStatePaused);
if (mEditorState == EditorStateTest) {
GetModel()->DoLoadLevel("level_edit_temp.map");
mEditorState = EditorStateUnknown;
}
} else {
GetModel()->ProceedToNextLevel();
}
}
}
void View::DrawUiGamePaused() {
DrawPageTitle ("Game Paused");
SelectFont ("console.ttf size=23");
if (Engine::GUI::Button (1, "Resume Game", screen_right * 0.5 - 100, 200, button_width, button_height)) {
PopViewState();
GetModel()->SetGameState(GameStateRunning);
}
if (Engine::GUI::Button (2, "Options", screen_right * 0.5 - 100, 250, button_width, button_height)) {
PushViewState (ViewStateOptions);
}
if (Engine::GUI::Button (3, "Abort Game", screen_right * 0.5 - 100, 300, button_width, button_height)) {
ResetViewState();
PushViewState(ViewStateMainMenu);
GetModel()->SetGameState(GameStatePaused);
}
if (Engine::GUI::Button (4, "Quit", screen_right * 0.5 - 100, 380, button_width, button_height)) {
Engine::RunCommand("quit");
}
}
void View::DrawUiPlayerDied() {
DrawPageTitle ("You died!");
SelectFont ("console.ttf size=23");
if (mFadeTimerSecValue > 0.)
return;
if (Engine::GUI::Button (1, "Continue", screen_right * 0.5 - 100, 380, button_width, button_height)) {
PopViewState();
// we have to take care when we are testing the level to not proceed to
// the next level...
if (GetViewState() == ViewStateEditor) {
GetModel()->SetGameState(GameStatePaused);
if (mEditorState == EditorStateTest) {
GetModel()->DoLoadLevel("level_edit_temp.map");
mEditorState = EditorStateUnknown;
}
} else {
GetModel()->ReloadLevel();
GetModel()->SetGameState(GameStateRunning);
}
}
}
/** \brief Draws an entry in the form <name> .... <points>
*
* 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() {
DrawPageTitle ("Highscores");
SelectFont ("console.ttf size=23");
unsigned int entry_length = 32;
float char_width, height;
DrawGLStringMeasure ("M", &char_width, &height);
height = fabsf(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
y += 60;
std::list<Model::HighscoreEntry>::iterator highscore_iter = GetModel()->mHighscoreList.begin();
SetFontJustification(Engine::FontJustificationLeft);
unsigned int i = 0;
while (highscore_iter != GetModel()->mHighscoreList.end()) {
// Check whether we have to highlight an entry (such as when entering
// the name)
if (GetModel()->GetPlayerName() == highscore_iter->name
&& GetModel()->GetPoints() == highscore_iter->points) {
// we highlight the newest entry
SelectFont("console.ttf color=#e8d500 size=23");
} else {
SelectFont("console.ttf color=#ffffff size=23");
}
DrawHighscoreEntry ( x, y, entry_width, highscore_iter->name, highscore_iter->points);
// go down one line
y += height * 1.3;
i++;
highscore_iter++;
}
if (Engine::GUI::Button (1, "Back to Menu", screen_right * 0.5 - 250 * 0.5, y + 16, button_width, button_height)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
PopViewState();
}
}
void View::DrawUiOptions() {
DrawPageTitle ("Options");
SelectFont ("console.ttf size=23");
Engine::GUI::Label (10, "Effects Volume: ", screen_right * 0.5 - 150, 240);
float effects_volume = Engine::GetEffectsVolume();
if (Engine::GUI::VerticalSlider (1, 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 (11, "Music Volume: ", screen_right * 0.5 - 150, 300);
float music_volume = Engine::GetMusicVolume();
if (Engine::GUI::VerticalSlider (2, 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)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
PopViewState();
}
}
void View::DrawUiCredits() {
// DrawPageTitle ("Credits");
SelectFont ("console.ttf size=23");
static std::string credits_content (
"_Programming,\r\
_ Design, and Graphics\r\
Martin Felis\r\
\r\
_Level Design\r\
Martin Felis\r\
\r\
_Music\r\
DJad - Space Exploration\r\
\r\
_Sounds\r\
Martin Felis\r\
\r\
_Libraries\r\
libSDL\r\
SDL_mixer\r\
OGLFT\r\
freetype2\r\
boost\r\
libpng\r\
\r\
Aaron D. Gifford's sha2 code\r\
\r\
_Tools\r\
GIMP\r\
Blender\r\
CMake\r\
sfxr-sdl\r\
Audacity\r\
\r\
_Special Thanks\r\
to my wonderful wife Katrina\r\
\r\
\r\
\r\
\r\
\r\
_http://www.fysx.org\r\
\r\
\r\
\r\
\r\
\r\
\r\
\r\
\r\
:created with vim.\r\
::wq\r\
\r\
EOF\r\
");
static float page_duration = 4.;
static float page_draw_duration = page_duration;
if (page_draw_duration < 0.) {
mCreditsPageIndex ++;
page_draw_duration = page_duration;
}
stringstream credits_stream (credits_content);
std::string line;
float xpos = 270, ypos = 200.f;
float dy = 20.f;
unsigned int index = 0;
bool first_content_line = false;
while (getline (credits_stream, line, '\r')) {
if (line.size() == 0) {
index ++;
continue;
}
if (index == mCreditsPageIndex) {
if (line[0] == '_') {
SelectFont ("console.ttf size=23 color=#e8d500");
line = line.substr (1, line.size());
first_content_line = true;
} else if (line[0] == ':') {
SelectFont ("console.ttf size=23 color=#ffffff");
line = line.substr (1, line.size());
} else {
if (first_content_line) {
ypos += 20;
first_content_line = false;
}
SelectFont ("console.ttf size=23 color=#ffffff");
}
DrawGLString (xpos, ypos, line.c_str());
ypos += dy;
} else {
continue;
}
if (index == mCreditsPageIndex && line == std::string("EOF")) {
PopViewState();
}
}
page_draw_duration -= Engine::GetFrameDuration();
if (Engine::GUI::CheckKeyPressed(SDLK_ESCAPE)
|| Engine::GUI::CheckKeyPressed(SDLK_SPACE) ) {
PopViewState();
}
}
void View::DrawUiEnterPlayername() {
DrawPageTitle ("Asteroids");
SelectFont ("console.ttf size=23");
// Enter your name
std::string player_name = GetModel()->GetPlayerName();
SelectFont("console.ttf size=23");
Engine::GUI::Label (1, "Enter your name: ", screen_right * 0.5 - 220, 250);
std::string valid_chars ("abcdefghijklmnopqrstuvwxyzABCDEFGHIJKLMNOPQRSTUVWXYZ01234567890-_!.");
if (Engine::GUI::LineEditMasked (2, screen_right * 0.5 + 20, 238, player_name, 16, valid_chars)) {
GetModel()->SetPlayerName(player_name);
}
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)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
PopViewState();
}
}
void View::DrawUiEditor() {
// DrawPageTitle ("Editor");
SelectFont ("console.ttf size=23");
// The close button
if (Engine::GUI::Button (1, "X", screen_right - 48, 20, 32, button_height)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE)) {
if (mEditorState == EditorStateTest) {
mEditorState = EditorStateUnknown;
GetModel()->DoLoadLevel("level_edit_temp.map");
GetModel()->SetGameState(GameStatePaused);
return;
} else if (!Engine::GUI::CheckKeyPressed(SDLK_ESCAPE)){
PopViewState();
}
}
if (mEditorState != EditorStateTest) {
if (Engine::GUI::CheckButton (2, "Add", mEditorState == EditorStateAddEntity, 30, 20, 50, button_height)) {
mEditorState = EditorStateAddEntity;
Engine::LogDebug ("Editor state now Add");
return;
}
if (Engine::GUI::CheckButton (3, "Del", mEditorState == EditorStateDelEntity, 85, 20, 50, button_height)) {
mEditorState = EditorStateDelEntity;
Engine::LogDebug ("Editor state now Del");
return;
}
if (Engine::GUI::CheckButton (4, "Move", mEditorState == EditorStateMoveEntity, 140, 20, 65, button_height)) {
mEditorState = EditorStateMoveEntity;
Engine::LogDebug ("Editor state now Move");
return;
}
if (Engine::GUI::CheckButton (5, "Rot", mEditorState == EditorStateRotateEntity, 210, 20, 50, button_height)) {
mEditorState = EditorStateRotateEntity;
Engine::LogDebug ("Editor state now Move");
return;
}
if (Engine::GUI::CheckButton (6, "Spd", mEditorState == EditorStateEntityVelocity, 265, 20, 50, button_height)) {
mEditorState = EditorStateEntityVelocity;
Engine::LogDebug ("Editor state now Add");
return;
}
if (Engine::GUI::Button (7, "Save", 325, 20, 65, button_height)) {
mEditorState = EditorStateSave;
Engine::LogDebug ("Editor state now Save");
return;
}
if (Engine::GUI::Button (8, "Load", 400, 20, 65, button_height)) {
mEditorState = EditorStateLoad;
Engine::LogDebug ("Editor state now Load");
return;
}
// we only show the Test button when there is actually something to play
// (i.e. a player entity and at least one asteroid)
if (GetModel()->GetPlayerEntityId() != 0
&& GetModel()->mAsteroids.size() > 0) {
if (Engine::GUI::Button (9, "Test", 475, 20, 64, button_height)) {
mEditorState = EditorStateTest;
GetModel()->DoSaveLevel("level_edit_temp.map");
GetModel()->SetGameState(GameStateRunning);
}
}
}
std::ostringstream mouse_pos_str;
vector3d mouse_world_pos;
Controller* controller = GetController();
controller->GetMouseWorldPosition(mouse_world_pos);
mouse_pos_str << mouse_world_pos[0] << "," << mouse_world_pos[2];
if (mEditorState == EditorStateAddEntity) {
SelectFont ("console.ttf size=12");
// Engine::GUI::Label (9999, "Adding", 128, 16);
if (Engine::GUI::CheckKeyPressed(MouseButtonLeft)) {
// if there is no entity so far we create a player, otherwise we create
// an asteroid
Engine::EntityBase* entity = NULL;
if (GetModel()->GetPlayerEntityId() != Engine::NullEntityId) {
entity = Engine::CreateEntity(GameEntityTypeAsteroid);
entity->mPhysicState->SetVelocity (vector3d (0., 0., -1.));
entity->mPhysicState->SetAngleVelocity ( (rand()/float(RAND_MAX) - 0.5) * 120);
} else {
entity = Engine::CreateEntity(GameEntityTypeShip);
GetModel()->SetPlayerEntityId(entity->mId);
entity->mPhysicState->SetOrientation (vector3d (0., 90., 0.));
}
// Now we want to insert an Asteroid at the given position
entity->mPhysicState->SetPosition (vector3d (mouse_world_pos[0], 0., mouse_world_pos[2]));
}
}
if (mEditorState == EditorStateDelEntity) {
SelectFont ("console.ttf size=12");
// Engine::GUI::Label (9999, "Deleting", 128, 16);
if (Engine::GUI::CheckKeyPressed(MouseButtonLeft)) {
// Check if there is an entity near the given position
Engine::EntityBase* entity = Engine::GetEntityAt (mouse_world_pos);
if (entity && entity->mType == GameEntityTypeAsteroid) {
Engine::LogMessage ("Killing entity with id %u", entity->mId);
GetModel()->UnregisterEntity (entity->mId);
}
}
}
if (mEditorState == EditorStateMoveEntity) {
SelectFont ("console.ttf size=12");
// Engine::GUI::Label (9999, "Moving", 128, 16);
if (Engine::GUI::CheckKeyPress(MouseButtonLeft)) {
// Check if there is an entity near the given position
Engine::EntityBase* asteroid = Engine::GetEntityAt (mouse_world_pos);
if (asteroid) {
mEditorEntityId = asteroid->mId;
}
}
if (mEditorEntityId != 0) {
Engine::EntityBase* asteroid = Engine::GetEntity (mEditorEntityId);
asteroid->mPhysicState->SetPosition (mouse_world_pos);
}
if (!Engine::GUI::CheckKeyPress(MouseButtonLeft)) {
if (mEditorEntityId != 0) {
mEditorEntityId = 0;
}
}
}
if (mEditorState == EditorStateRotateEntity) {
SelectFont ("console.ttf size=12");
// static vector3d mouse_start_rotate (0., 0., 0.);
static float start_angle = 0.;
if (mEditorEntityId == 0) {
if (Engine::GUI::CheckKeyPress(MouseButtonLeft)) {
// Check if there is an entity near the given position
Engine::EntityBase* entity = Engine::GetEntityAt (mouse_world_pos);
if (entity) {
mEditorEntityId = entity->mId;
vector3d mouse_start_rotate = mouse_world_pos - entity->mPhysicState->GetPosition();
start_angle = atan2 (mouse_start_rotate[0], mouse_start_rotate[2]) * 180 / M_PI - entity->mPhysicState->mOrientation[1];
} else {
mEditorEntityId = 0;
}
}
}
if (mEditorEntityId != 0) {
Engine::EntityBase* entity = Engine::GetEntity (mEditorEntityId);
vector3d new_velocity = mouse_world_pos - entity->mPhysicState->GetPosition();
new_velocity[1] = 0.;
float angle = atan2 (new_velocity[0], new_velocity[2]) * 180. / M_PI;
entity->mPhysicState->SetOrientation (vector3d (0., angle - start_angle, 0.));
if (!Engine::GUI::CheckKeyPress(MouseButtonLeft)) {
mEditorEntityId = 0;
}
}
}
if (mEditorState == EditorStateEntityVelocity) {
SelectFont ("console.ttf size=12");
// Engine::GUI::Label (9999, "Velocity", 128, 16);
if (mEditorEntityId == 0) {
if (Engine::GUI::CheckKeyPress(MouseButtonLeft)) {
// Check if there is an entity near the given position
Engine::EntityBase* asteroid = Engine::GetEntityAt (mouse_world_pos);
if (asteroid) {
mEditorEntityId = asteroid->mId;
}
}
}
if (mEditorEntityId != 0) {
Engine::EntityBase* asteroid = Engine::GetEntity (mEditorEntityId);
vector3d new_velocity = mouse_world_pos - asteroid->mPhysicState->GetPosition();
new_velocity[1] = 0.;
asteroid->mPhysicState->SetVelocity (new_velocity);
if (!Engine::GUI::CheckKeyPress(MouseButtonLeft)) {
if (mEditorEntityId != 0) {
mEditorEntityId = 0;
}
}
}
}
if (mEditorState == EditorStateSave) {
glColor3f (0.2, 0.2, 0.2);
Engine::GUI::DrawRoundedBlock (140, 150, screen_right - 280, 280);
// Engine::GUI::Label (9999, "Saving", 128, 16);
SelectFont ("console.ttf size=23");
Engine::GUI::Label(51, "Filename: ", 145, 180);
std::string level_name = GetModel()->GetLevelName();
glColor3f (1., 1., 1.);
if (Engine::GUI::LineEdit(52, 150, 200, level_name, 31))
GetModel()->SetLevelName(level_name);
Engine::GUI::Label(53, "Author: ", 145, 250);
std::string level_author = GetModel()->GetLevelAuthor();
if (Engine::GUI::LineEdit(531, 150, 270, level_author, 31))
GetModel()->SetLevelAuthor(level_author);
Engine::GUI::Label(54, "Level Title: ", 145, 320);
std::string level_title = GetModel()->GetLevelTitle();
if (Engine::GUI::LineEdit(541, 150, 340, level_title, 31))
GetModel()->SetLevelTitle(level_title);
if (Engine::GUI::Button (55, "Save", 145, 380, button_width, button_height)) {
GetModel()->DoSaveLevel((level_name + std::string(".map")).c_str());
Engine::LogMessage ("Save");
mEditorState = EditorStateUnknown;
}
if (Engine::GUI::Button (56, "Cancel", screen_right - 140 - 5 - button_width, 380, button_width, button_height))
mEditorState = EditorStateUnknown;
}
if (mEditorState == EditorStateLoad) {
glColor3f (0.2, 0.2, 0.2);
Engine::GUI::DrawRoundedBlock (140, 150, screen_right - 280, 200);
// Engine::GUI::Label (9999, "Loading", 128, 16);
SelectFont ("console.ttf size=23");
Engine::GUI::Label(51, "Filename: ", 145, 180);
std::string level_name = GetModel()->GetLevelName();
glColor3f (1., 1., 1.);
if (Engine::GUI::LineEdit(52, 145, 210, level_name, 32))
GetModel()->SetLevelName(level_name);
if (Engine::FileExists (level_name + std::string(".map"))) {
if (Engine::GUI::Button (54, "Load", 145, 300, button_width, button_height)) {
GetModel()->DoLoadLevel((level_name + std::string(".map")).c_str());
Engine::LogMessage ("Load");
mEditorState = EditorStateUnknown;
}
}
if (Engine::GUI::Button (55, "Cancel", screen_right - 140 - 5 - button_width, 300, button_width, button_height))
mEditorState = EditorStateUnknown;
}
if (Engine::GUI::CheckKeyPressed(SDLK_ESCAPE)) {
if (mEditorState != EditorStateUnknown)
mEditorState = EditorStateUnknown;
}
if (Engine::GUI::CheckKeyPressed(MouseButtonRight)
|| Engine::GUI::CheckKeyPressed(SDLK_ESCAPE) ) {
mEditorState = EditorStateUnknown;
}
}
/*
* 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
}
}