973 lines
20 KiB
C++
973 lines
20 KiB
C++
#include "SDL.h"
|
|
#include "SDL_opengl.h"
|
|
#include "SDL_keycode.h"
|
|
|
|
#include <GL/glu.h>
|
|
#include <AL/al.h>
|
|
#include <cmath>
|
|
|
|
#include "antigrav.h"
|
|
|
|
Game Game::instance;
|
|
|
|
const int Game::CONTROLS[MAX_LOCAL_PLAYERS][NUM_CONTROLS] = {
|
|
{SDLK_RIGHT, SDLK_LEFT, SDLK_UP},
|
|
{SDLK_d, SDLK_a, SDLK_w},
|
|
{SDLK_l, SDLK_j, SDLK_i},
|
|
{SDLK_KP_6, SDLK_KP_4, SDLK_KP_8}};
|
|
|
|
const char *Game::PLAYER_TEXTURES[MAX_PLAYERS] = {"", "racer1.png", "racer2.png", "racer3.png", "racer4.png", "racer5.png", "racer6.png", "racer7.png"};
|
|
const float Game::PLAYER_COLORS[MAX_PLAYERS][3] = {{1,0,0},{0,0,1},{0,1,0},{1,1,0}, {0.65, 0, 1}, {0.20, 0.64, 0.69}, {0.89, 0.63, 0.18}, {0.59, 0.56, 0.88}};
|
|
|
|
GLuint Game::signal;
|
|
GLuint Game::signalred;
|
|
GLuint Game::signalgreen;
|
|
ALuint Game::signalredbuffer;
|
|
ALuint Game::signalgreenbuffer;
|
|
|
|
Game::Game()
|
|
{
|
|
enable3d = true;
|
|
enable2d = false;
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
players[i].setActive(false);
|
|
players[i].setLocal(false);
|
|
}
|
|
}
|
|
|
|
Game::~Game()
|
|
{
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(alIsSource(playerSources[i]))
|
|
{
|
|
alDeleteSources(1, &playerSources[i]);
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < GLOBAL_SOURCES; i++)
|
|
{
|
|
if(alIsSource(globalSources[i]))
|
|
{
|
|
alDeleteSources(1, &globalSources[i]);
|
|
}
|
|
}
|
|
}
|
|
|
|
Game &Game::getInstance()
|
|
{
|
|
return instance;
|
|
}
|
|
|
|
Level &Game::getLevel()
|
|
{
|
|
return level;
|
|
}
|
|
|
|
int Game::init(SDL_Window *window)
|
|
{
|
|
this->window = window;
|
|
|
|
// Get viewport
|
|
glGetIntegerv(GL_VIEWPORT, masterViewport);
|
|
screenWidth = masterViewport[2];
|
|
screenHeight = masterViewport[3];
|
|
|
|
// Load resources
|
|
if(Craft::init() != 0) return 1;
|
|
if(level.init() != 0) return 1;
|
|
if(Font::getInstance().init() != 0) return 1;
|
|
if(Player::init() != 0) return 1;
|
|
if(Ring::init() != 0) return 1;
|
|
if(Background::init() != 0) return 1;
|
|
|
|
signal = m3dTexture::loadTexture("signal.png");
|
|
if(signal==0)
|
|
return -1;
|
|
|
|
signalred = m3dTexture::loadTexture("signalred.png");
|
|
if(signalred==0)
|
|
return -1;
|
|
|
|
signalgreen = m3dTexture::loadTexture("signalgreen.png");
|
|
if(signalgreen==0)
|
|
return -1;
|
|
|
|
/* signalredbuffer = alutCreateBufferWaveform(ALUT_WAVEFORM_SINE, 200.0, 0.0, 0.4);
|
|
signalgreenbuffer = alutCreateBufferWaveform(ALUT_WAVEFORM_SINE, 300.0, 0.0, 0.7);*/
|
|
signalredbuffer = loadWavBuffer("signalred.wav");
|
|
if(signalredbuffer == AL_NONE) return -1;
|
|
signalgreenbuffer = loadWavBuffer("signalgreen.wav");
|
|
if(signalgreenbuffer == AL_NONE) return -1;
|
|
|
|
playerTex[0] = players[0].getCraft().getMesh().getTexture(0);
|
|
for(int i = 1; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(playerTex[i].load(PLAYER_TEXTURES[i]) != 0)
|
|
{
|
|
return -1;
|
|
}
|
|
}
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
players[i].setTexture(&playerTex[i]);
|
|
players[i].setColor(PLAYER_COLORS[i]);
|
|
}
|
|
|
|
// set up player sources
|
|
alGenSources(MAX_PLAYERS, playerSources);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
{
|
|
fprintf(stderr, "Can't initialize OpenAL sources\n");
|
|
return -1;
|
|
}
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
players[i].setSource(playerSources[i]);
|
|
}
|
|
|
|
// set up global sources
|
|
alGenSources(GLOBAL_SOURCES, globalSources);
|
|
if(alGetError() != AL_NO_ERROR)
|
|
{
|
|
fprintf(stderr, "Can't initialize OpenAL sources\n");
|
|
return -1;
|
|
}
|
|
|
|
// set up permanent OpenGL state
|
|
// lighting
|
|
const GLfloat lightPos[] = {0,10,0,1};
|
|
const GLfloat lightAmb[] = {.5,.5,.5,1};
|
|
const GLfloat lightDif[] = {1,1,1,1};
|
|
|
|
glLightfv(GL_LIGHT0, GL_AMBIENT, lightAmb);
|
|
glLightfv(GL_LIGHT0, GL_DIFFUSE, lightDif);
|
|
glLightfv(GL_LIGHT0, GL_POSITION, lightPos);
|
|
glEnable(GL_LIGHT0);
|
|
|
|
// Depth func
|
|
glDepthFunc(GL_LEQUAL);
|
|
|
|
// Blend func
|
|
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
|
|
|
|
return 0;
|
|
}
|
|
|
|
static int sortStats(const void *s1, const void *s2) {
|
|
float t1 = ((PlayerStat*)s1)->time;
|
|
float t2 = ((PlayerStat*)s2)->time;
|
|
if(t1<t2) return -1;
|
|
if(t1>t2) return 1;
|
|
return 0;
|
|
}
|
|
|
|
int Game::gameLoop()
|
|
{
|
|
SDL_Event event;
|
|
Uint32 startTime;
|
|
float t;
|
|
bool loop;
|
|
|
|
// <temporary>
|
|
// generate level
|
|
level.generate(time(NULL));
|
|
// </temporary>
|
|
|
|
backg = Background();
|
|
|
|
// set up starting grid
|
|
int grid = 0;
|
|
activeplayers = 0;
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
playerFinishTime[i].plr = -1;
|
|
if(!players[i].isActive()) continue;
|
|
++activeplayers;
|
|
|
|
float xPos = 15.0 - grid * 0.75;
|
|
float yPos = 0.75 + (grid & 1) * 0.5;
|
|
grid++;
|
|
|
|
players[i].reset(xPos, level.getHeight(xPos) + yPos);
|
|
|
|
playerFinishTime[i].plr = i;
|
|
playerFinishTime[i].time = -1;
|
|
}
|
|
|
|
// Clear rings
|
|
Ring::resetAll();
|
|
|
|
// set up viewports
|
|
int temp = 0;
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(players[i].isActive() && players[i].isLocal()) temp++;
|
|
}
|
|
|
|
initViewports(temp);
|
|
|
|
// set up audio distance model
|
|
if(activeplayers > 1)
|
|
{
|
|
alDistanceModel(AL_NONE);
|
|
Player::setEngineVolume(0.2);
|
|
alDopplerFactor(0.0);
|
|
} else
|
|
{
|
|
alDistanceModel(AL_INVERSE_DISTANCE_CLAMPED);
|
|
Player::setEngineVolume(1.0);
|
|
alDopplerFactor(0.25);
|
|
}
|
|
|
|
// set up timer
|
|
fps = 0.0;
|
|
int numFrames = 0, skippedFrames = 0;
|
|
Uint32 frameTime = 0, fpsTimer = 0;
|
|
showFps = false;
|
|
|
|
// start main loop
|
|
state = WAITFORSTART;
|
|
stateTimer = 0;
|
|
loop = true;
|
|
startTime = SDL_GetTicks();
|
|
while(loop)
|
|
{
|
|
|
|
// Update timer and frame rate
|
|
frameTime = SDL_GetTicks() - startTime;
|
|
if(frameTime < MIN_FRAME_TIME && FRAME_LIMITER) { SDL_Delay(MIN_FRAME_TIME - frameTime); }
|
|
frameTime = (SDL_GetTicks() - startTime);
|
|
t = (frameTime) / 1000.0f;
|
|
|
|
fpsTimer += frameTime;
|
|
if(numFrames++ >= 5)
|
|
{
|
|
if(fpsTimer == 0) fpsTimer = 1; // avoid division by zero on smoking fast machines
|
|
fps = (float)(1000 * (numFrames - skippedFrames)) / fpsTimer;
|
|
updateRate = (float)(1000 * numFrames) / fpsTimer;
|
|
numFrames = 0;
|
|
fpsTimer = 0;
|
|
skippedFrames = 0;
|
|
}
|
|
|
|
startTime = SDL_GetTicks();
|
|
|
|
// Handle events
|
|
while(SDL_PollEvent(&event))
|
|
{
|
|
if(event.type == SDL_QUIT) loop = false;
|
|
else if(event.type == SDL_KEYDOWN)
|
|
{
|
|
if(event.key.keysym.sym == SDLK_ESCAPE) loop = false;
|
|
else if(event.key.keysym.sym == SDLK_F9) showFps = !showFps;
|
|
|
|
// update player controls
|
|
updateControls(event.key.keysym.sym, true);
|
|
if(state==WAITFORSTART) {
|
|
state = START;
|
|
stateTimer = 0;
|
|
stateVal = 0;
|
|
}
|
|
|
|
// debug-mode "secret" keys
|
|
#ifdef DEBUG
|
|
if(event.key.keysym.sym == SDLK_F10) m3dTexture::screenshot("antigrav-screenshot.png");
|
|
else if(event.key.keysym.sym == SDLK_F11) enable3d = !enable3d;
|
|
else if(event.key.keysym.sym == SDLK_F12) enable2d = !enable2d;
|
|
#endif
|
|
} else if(event.type == SDL_KEYUP)
|
|
{
|
|
// handle player controls
|
|
updateControls(event.key.keysym.sym, false);
|
|
|
|
} else if (event.type == SDL_CONTROLLERBUTTONUP) {
|
|
switch (event.cbutton.button) {
|
|
case SDL_CONTROLLER_BUTTON_A:
|
|
if (state == WAITFORSTART) {
|
|
state = START;
|
|
stateTimer = 0;
|
|
stateVal = 0;
|
|
}
|
|
break;
|
|
|
|
case SDL_CONTROLLER_BUTTON_B:
|
|
case SDL_CONTROLLER_BUTTON_BACK:
|
|
case SDL_CONTROLLER_BUTTON_START:
|
|
loop = false;
|
|
break;
|
|
}
|
|
|
|
} else if (event.type == SDL_CONTROLLERAXISMOTION) {
|
|
switch (event.caxis.axis) {
|
|
case SDL_CONTROLLER_AXIS_LEFTX:
|
|
updateControls(
|
|
mapControllerAxis(
|
|
event.caxis.which,
|
|
event.caxis.axis,
|
|
event.caxis.value
|
|
),
|
|
event.caxis.value < -12000 || event.caxis.value > 12000
|
|
);
|
|
break;
|
|
|
|
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
|
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
|
updateControls(
|
|
mapControllerAxis(
|
|
event.caxis.which,
|
|
event.caxis.axis,
|
|
event.caxis.value
|
|
),
|
|
event.caxis.value > 500
|
|
);
|
|
break;
|
|
}
|
|
|
|
}
|
|
}
|
|
|
|
stateTimer += t;
|
|
|
|
switch(state) {
|
|
case WAITFORSTART:
|
|
break;
|
|
case START:
|
|
if(stateTimer > 4.0) {
|
|
state = GAME;
|
|
stateTimer = 0;
|
|
}
|
|
break;
|
|
case GAME: {
|
|
// Check if all players have finished
|
|
int finp=0;
|
|
for(int p=0;p<8;p++)
|
|
if(players[p].isActive() && players[p].isFinished())
|
|
finp++;
|
|
if(finp==activeplayers) {
|
|
state = FINISHED;
|
|
stateTimer = 0;
|
|
qsort(playerFinishTime,MAX_PLAYERS,sizeof(PlayerStat),
|
|
sortStats);
|
|
}
|
|
// Update
|
|
updateWorld(t);
|
|
} break;
|
|
|
|
case FINISHED:
|
|
// Update
|
|
updateWorld(t);
|
|
if(stateTimer>=5.0)
|
|
loop = false;
|
|
}
|
|
|
|
// skip frames to maintain solid frame rate
|
|
if(frameTime > MAX_FRAME_TIME && FRAMESKIP)
|
|
{
|
|
skippedFrames++;
|
|
continue;
|
|
}
|
|
|
|
// Draw (all states)
|
|
resetListener();
|
|
drawFrame();
|
|
updateListener();
|
|
}
|
|
|
|
// Kill all audio
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
ALint state;
|
|
alGetSourcei(playerSources[i], AL_SOURCE_STATE, &state);
|
|
if(state == AL_PLAYING) alSourceStop(playerSources[i]);
|
|
}
|
|
|
|
for(int i = 0; i < GLOBAL_SOURCES; i++)
|
|
{
|
|
ALint state;
|
|
alGetSourcei(globalSources[i], AL_SOURCE_STATE, &state);
|
|
if(state == AL_PLAYING) alSourceStop(globalSources[i]);
|
|
}
|
|
|
|
return state!=FINISHED;
|
|
}
|
|
|
|
// returns true if key handled, false if not
|
|
bool Game::updateControls(int keysym, bool down)
|
|
{
|
|
int pl = -1;
|
|
int ctrl = -1;
|
|
if(state!=GAME) return false;
|
|
|
|
for(int i = 0; i < MAX_LOCAL_PLAYERS; i++)
|
|
{
|
|
for(int j = 0; j < NUM_CONTROLS; j++)
|
|
{
|
|
if(keysym == CONTROLS[i][j])
|
|
{
|
|
pl = i;
|
|
ctrl = j;
|
|
break;
|
|
}
|
|
|
|
if(pl != -1) break;
|
|
}
|
|
}
|
|
|
|
if(pl == -1 || ctrl == -1) return false;
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(!players[i].isLocal()) continue;
|
|
if(pl == 0)
|
|
{
|
|
if(!players[i].isFinished())
|
|
players[i].getCraft().setControl(ctrl, down);
|
|
else
|
|
players[i].getCraft().setControl(ctrl,false);
|
|
return true;
|
|
}
|
|
|
|
pl--;
|
|
}
|
|
|
|
return false;
|
|
}
|
|
|
|
void Game::updateWorld(float t)
|
|
{
|
|
// update crafts
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(!players[i].isActive()) continue;
|
|
if(players[i].update(t)) {
|
|
// Player finished
|
|
playerFinishTime[i].time = stateTimer;
|
|
}
|
|
}
|
|
|
|
// Update rings
|
|
Ring::updateAll(t);
|
|
|
|
// handle craft to craft collisions
|
|
for(int i = 0; i < MAX_PLAYERS - 1; i++)
|
|
{
|
|
if(!players[i].isActive()) continue;
|
|
Craft &craft1 = players[i].getCraft();
|
|
|
|
for(int j = i + 1; j < MAX_PLAYERS; j++)
|
|
{
|
|
if(!players[j].isActive()) continue;
|
|
Craft &craft2 = players[j].getCraft();
|
|
|
|
if(!craft1.collide(craft2)) craft2.collide(craft1);
|
|
}
|
|
}
|
|
|
|
// handle craft to level collisions and move crafts
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(!players[i].isActive()) continue;
|
|
|
|
players[i].getCraft().levelCollide();
|
|
players[i].getCraft().move();
|
|
}
|
|
|
|
}
|
|
|
|
SDL_Keycode Game::mapControllerAxis(SDL_JoystickID joy, Uint8 axis, Sint16 value)
|
|
{
|
|
int ctrl;
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++) {
|
|
if (!players[i].hasController(joy))
|
|
continue;
|
|
|
|
// Controls in order: right, left, thrust
|
|
switch(axis) {
|
|
case SDL_CONTROLLER_AXIS_LEFTX:
|
|
ctrl = value > 0 ? 0 : 1;
|
|
break;
|
|
|
|
case SDL_CONTROLLER_AXIS_TRIGGERLEFT:
|
|
case SDL_CONTROLLER_AXIS_TRIGGERRIGHT:
|
|
ctrl = 2;
|
|
break;
|
|
|
|
default:
|
|
return SDLK_ESCAPE;
|
|
}
|
|
|
|
return CONTROLS[i][ctrl];
|
|
}
|
|
|
|
return SDLK_ESCAPE;
|
|
}
|
|
|
|
int Game::getPlayerForController(SDL_JoystickID id)
|
|
{
|
|
for(int p = 0; p < MAX_LOCAL_PLAYERS; p++)
|
|
if (players[p].hasController(id))
|
|
return p;
|
|
return -1;
|
|
}
|
|
|
|
void Game::initViewports(int num)
|
|
{
|
|
numViewports = num;
|
|
if(numViewports > MAX_VIEWPORTS) numViewports = MAX_VIEWPORTS;
|
|
|
|
if(numViewports == 1)
|
|
{
|
|
viewports[0][2] = screenWidth;
|
|
viewports[0][3] = screenWidth / 16 * 9;
|
|
viewports[0][0] = 0;
|
|
viewports[0][1] = (screenHeight - viewports[0][3]) / 2;
|
|
|
|
for(int i = 0; i < 4; i++) masterViewport[i] = viewports[0][i];
|
|
return;
|
|
} else if(numViewports == 2)
|
|
{
|
|
for(int i = 0; i < 2; i++)
|
|
{
|
|
viewports[i][0] = 0;
|
|
viewports[i][1] = (1 - i) * screenHeight / 2;
|
|
viewports[i][2] = screenWidth;
|
|
viewports[i][3] = screenHeight / 2;
|
|
}
|
|
} else
|
|
{
|
|
for(int i = 0; i < 4; i++)
|
|
{
|
|
viewports[i][0] = (i & 1) * screenWidth / 2;
|
|
viewports[i][1] = (1 - i/2) * screenHeight / 2;
|
|
viewports[i][2] = screenWidth / 2;
|
|
viewports[i][3] = screenHeight / 2;
|
|
}
|
|
}
|
|
|
|
masterViewport[0] = 0;
|
|
masterViewport[1] = 0;
|
|
masterViewport[2] = screenWidth;
|
|
masterViewport[3] = screenHeight;
|
|
}
|
|
|
|
void Game::drawFrame()
|
|
{
|
|
glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
|
|
|
|
int vp = numViewports - 1;
|
|
for(int i = MAX_PLAYERS-1; i >= 0; i--)
|
|
{
|
|
if(!players[i].isActive() || !players[i].isLocal()) continue;
|
|
|
|
drawViewport(i, viewports[vp--]);
|
|
if(vp < 0) break;
|
|
}
|
|
|
|
drawHud();
|
|
|
|
SDL_GL_SwapWindow(window);
|
|
}
|
|
|
|
void Game::draw3d(const float *eye, const float *at, float fovDiag)
|
|
{
|
|
|
|
glDisable(GL_LIGHTING);
|
|
backg.draw();
|
|
|
|
glEnable(GL_LIGHTING);
|
|
glEnable(GL_DEPTH_TEST);
|
|
|
|
level.draw3d(eye, at, fovDiag);
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(!players[i].isActive()) continue;
|
|
|
|
players[i].getCraft().getMesh().setTexture(0, players[i].getTexture());
|
|
players[i].getCraft().draw3d();
|
|
}
|
|
|
|
Ring::drawAll();
|
|
}
|
|
|
|
void Game::draw2d()
|
|
{
|
|
glDisable(GL_DEPTH_TEST);
|
|
glDisable(GL_LIGHTING);
|
|
|
|
level.draw2d();
|
|
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(!players[i].isActive()) continue;
|
|
|
|
players[i].getCraft().draw2d();
|
|
}
|
|
}
|
|
|
|
void Game::drawViewport(int current, const GLint *viewport)
|
|
{
|
|
glViewport(viewport[0], viewport[1], viewport[2], viewport[3]);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
// set up projection and calculate diagoonal fov
|
|
const double fovY = RAD(45.0);
|
|
const double aspect = (GLdouble)viewport[2]/viewport[3];
|
|
gluPerspective(DEG(fovY), aspect, 0.1, 200.0);
|
|
|
|
double tfovY2 = tan(fovY / 2.0);
|
|
double tfovX2 = tfovY2 * aspect;
|
|
|
|
float fovDiag = (float)atan(sqrt( tfovX2 * tfovX2 + tfovY2 * tfovY2 ));
|
|
|
|
// Set up transformation
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
Craft &craft = players[current].getCraft();
|
|
|
|
// Set camera position
|
|
float eyeX, eyeY, eyeZ;
|
|
float centerX, centerY, centerZ;
|
|
eyeX = craft.getX();
|
|
if(eyeX < 10.0) eyeX = 10.0;
|
|
if(eyeX > level.getWidth() - 10.0) eyeX = level.getWidth() - 10.0;
|
|
|
|
eyeY = 6.0;
|
|
eyeZ = 7.0;
|
|
|
|
centerX = eyeX;
|
|
centerY = 4.0;
|
|
centerZ = 0.0;
|
|
|
|
gluLookAt(eyeX, eyeY, eyeZ, centerX, centerY, centerZ, centerX - eyeX, eyeZ - centerZ, centerY - eyeY);
|
|
|
|
// Update listener values
|
|
listenerPos[0] += eyeX;
|
|
listenerPos[1] += eyeY;
|
|
listenerPos[2] += eyeZ;
|
|
|
|
listenerVel[0] += craft.getVX();
|
|
|
|
listenerOri[0] += centerX - eyeX;
|
|
listenerOri[1] += centerY - eyeY;
|
|
listenerOri[2] += centerZ - eyeZ;
|
|
|
|
listenerOri[3] += centerX - eyeX;
|
|
listenerOri[4] += eyeZ - centerZ;
|
|
listenerOri[5] += centerY - eyeY;
|
|
|
|
listenerWeight += 1.0;
|
|
|
|
// Draw
|
|
const float at[3] = {centerX - eyeX, centerY - eyeY, centerZ - eyeZ};
|
|
const float eye[3] = {eyeX, eyeY, eyeZ};
|
|
|
|
if(enable3d) draw3d(eye, at, fovDiag);
|
|
if(enable2d) draw2d();
|
|
|
|
players[current].drawHud(viewport, activeplayers, current);
|
|
}
|
|
|
|
void Game::drawHud()
|
|
{
|
|
glViewport(masterViewport[0], masterViewport[1], masterViewport[2], masterViewport[3]);
|
|
|
|
glMatrixMode(GL_PROJECTION);
|
|
glLoadIdentity();
|
|
|
|
float width, height;
|
|
width = masterViewport[2] / 8.0;
|
|
height = width * masterViewport[3] / masterViewport[2];
|
|
glOrtho(0.0, width, height, 0.0, -1.0, 1.0);
|
|
|
|
glMatrixMode(GL_MODELVIEW);
|
|
glLoadIdentity();
|
|
|
|
glDisable(GL_LIGHTING);
|
|
glDisable(GL_DEPTH_TEST);
|
|
glEnable(GL_TEXTURE_2D);
|
|
glEnable(GL_BLEND);
|
|
|
|
// Draw signal lights
|
|
if(state == START || (state==GAME&&stateTimer<1.0)) {
|
|
float y=0.0, w=0.0;
|
|
|
|
if(state==START) {
|
|
glBindTexture(GL_TEXTURE_2D, signal);
|
|
if(stateTimer < 1.0) {
|
|
y = -10+stateTimer*10;
|
|
} else {
|
|
w = int(stateTimer)/3.0;
|
|
if(stateVal != int(stateTimer)) {
|
|
stateVal = int(stateTimer);
|
|
ALuint source = getSource();
|
|
if(alIsSource(source)) {
|
|
float vec[3] = {0,0,0};
|
|
alSourcefv(source, AL_VELOCITY, vec);
|
|
float pos[3] = {listenerPos[0],listenerPos[1],listenerPos[2]-1};
|
|
alSourcefv(source, AL_POSITION, pos);
|
|
alSourcei(source, AL_LOOPING, AL_FALSE);
|
|
alSourcei(source, AL_BUFFER, signalredbuffer);
|
|
alSourcePlay(source);
|
|
}
|
|
}
|
|
}
|
|
} else {
|
|
glBindTexture(GL_TEXTURE_2D, signalgreen);
|
|
y = stateTimer*-10.0;
|
|
if(stateVal!=-1) {
|
|
stateVal=-1;
|
|
ALuint source = getSource();
|
|
if(alIsSource(source)) {
|
|
float vec[3] = {0,0,0};
|
|
alSourcefv(source, AL_VELOCITY, vec);
|
|
float pos[3] = {listenerPos[0],listenerPos[1],listenerPos[2]-1};
|
|
alSourcefv(source, AL_POSITION, pos);
|
|
alSourcei(source, AL_LOOPING, AL_FALSE);
|
|
alSourcei(source, AL_BUFFER, signalgreenbuffer);
|
|
alSourcePlay(source);
|
|
}
|
|
}
|
|
}
|
|
|
|
// Unlit or green signals
|
|
glPushMatrix();
|
|
glTranslatef(width/2.0 - 10.0,y,0);
|
|
glScalef(10,10,1);
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glTexCoord2f(w, 0.0);
|
|
glVertex2f(2*w, 0);
|
|
|
|
glTexCoord2f(1, 0.0);
|
|
glVertex2f(2, 0);
|
|
|
|
glTexCoord2f(w, 1.0);
|
|
glVertex2f(2*w, 1);
|
|
|
|
glTexCoord2f(1, 1.0);
|
|
glVertex2f(2, 1);
|
|
glEnd();
|
|
|
|
if(w>0.0) {
|
|
// Red signals
|
|
glBindTexture(GL_TEXTURE_2D, signalred);
|
|
glBegin(GL_TRIANGLE_STRIP);
|
|
glTexCoord2f(0, 0.0);
|
|
glVertex2f(0, 0);
|
|
|
|
glTexCoord2f(w, 0.0);
|
|
glVertex2f(2*w, 0);
|
|
|
|
glTexCoord2f(0, 1.0);
|
|
glVertex2f(0, 1);
|
|
|
|
glTexCoord2f(w, 1.0);
|
|
glVertex2f(2*w, 1);
|
|
glEnd();
|
|
}
|
|
glPopMatrix();
|
|
}
|
|
|
|
// draw radar
|
|
if(state == GAME && stateTimer > 1.0)
|
|
{
|
|
float alpha = (stateTimer - 1.0);
|
|
if(alpha > 1.0) alpha = 1.0;
|
|
drawRadar(width, height, alpha);
|
|
}
|
|
|
|
// draw fps counter
|
|
if(showFps)
|
|
{
|
|
glPushMatrix();
|
|
glTranslatef(width - 10.0, 0.0, 0.0);
|
|
Font::getInstance().printf(" fps: %d\nrate: %d",
|
|
(int)fps, (int)updateRate);
|
|
glPopMatrix();
|
|
}
|
|
|
|
// Draw end game statistics
|
|
if(state == FINISHED)
|
|
drawStatistics(width,height);
|
|
|
|
glDisable(GL_BLEND);
|
|
glDisable(GL_TEXTURE_2D);
|
|
}
|
|
|
|
void Game::drawRadar(float width, float height, float alpha)
|
|
{
|
|
(void)height;
|
|
|
|
// draw radar
|
|
glPushMatrix();
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
const float maxHeight = 10.0;
|
|
glTranslatef(width/6.0, 4.5, 0.0);
|
|
glScalef((2.0/3.0 * width) / level.getWidth(), -4.0 / maxHeight, 1.0);
|
|
|
|
glColor4f(0.3, 0.3, 0.3, alpha);
|
|
glBegin(GL_TRIANGLE_STRIP); // gray background
|
|
glVertex2f(0.0, 0.0);
|
|
glVertex2f(level.getWidth(), 0.0);
|
|
glVertex2f(0.0, maxHeight);
|
|
glVertex2f(level.getWidth(), maxHeight);
|
|
glEnd();
|
|
|
|
glColor4f(1.0, 1.0, 1.0, alpha); // white frame
|
|
glBegin(GL_LINE_LOOP);
|
|
glVertex2f(0.0, 0.0);
|
|
glVertex2f(level.getWidth(), 0.0);
|
|
glVertex2f(level.getWidth(), maxHeight);
|
|
glVertex2f(0.0, maxHeight);
|
|
glEnd();
|
|
|
|
level.drawRadar();
|
|
|
|
glPointSize(5.0);
|
|
glBegin(GL_POINTS);
|
|
for(int i = 0; i < MAX_PLAYERS; i++)
|
|
{
|
|
if(!players[i].isActive()) continue;
|
|
|
|
float x = players[i].getCraft().getX();
|
|
float y = players[i].getCraft().getY();
|
|
if(x>0 && x<level.getWidth() && y>0 && y<maxHeight) {
|
|
players[i].bindColor(alpha);
|
|
glVertex2fv(players[i].getCraft().getPos().getData());
|
|
}
|
|
}
|
|
glEnd();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
glPopMatrix();
|
|
|
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
|
}
|
|
|
|
void Game::drawStatistics(float width, float height)
|
|
{
|
|
glPushMatrix();
|
|
glDisable(GL_TEXTURE_2D);
|
|
|
|
float boxw = width * (2.0/3.0);
|
|
float boxh = height * (2.0/3.0);
|
|
|
|
glTranslatef(width/2.0-boxw/2.0, height/2.0-boxh/2.0, 0.0);
|
|
|
|
glColor4f(0.3, 0.3, 0.3, 0.5);
|
|
glBegin(GL_TRIANGLE_STRIP); // gray background
|
|
glVertex2f(0.0, 0.0);
|
|
glVertex2f(boxw, 0.0);
|
|
glVertex2f(0.0, boxh);
|
|
glVertex2f(boxw, boxh);
|
|
glEnd();
|
|
|
|
glColor4f(1.0, 1.0, 1.0, 0.5); // white frame
|
|
glBegin(GL_LINE_LOOP);
|
|
glVertex2f(0.0, 0.0);
|
|
glVertex2f(boxw, 0.0);
|
|
glVertex2f(boxw, boxh);
|
|
glVertex2f(0.0, boxh);
|
|
glEnd();
|
|
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
Font &font = Font::getInstance();
|
|
glColor4f(1,1,1,1);
|
|
|
|
glPushMatrix();
|
|
glTranslatef(boxw/2-9,1,0);
|
|
|
|
glScalef(2,2,1);
|
|
font.drawString("Race finished");
|
|
glPopMatrix();
|
|
|
|
glTranslatef(10,8,0);
|
|
|
|
glDisable(GL_TEXTURE_2D);
|
|
glBegin(GL_LINES);
|
|
glVertex2f(0,1.5);
|
|
glVertex2f(boxw-12,1.5);
|
|
glVertex2f(38,0);
|
|
glVertex2f(38,boxh-10);
|
|
glEnd();
|
|
glEnable(GL_TEXTURE_2D);
|
|
|
|
font.drawString("Player");
|
|
glTranslatef(40,0,0);
|
|
font.drawString("Time");
|
|
glTranslatef(-40,4,0);
|
|
|
|
for(int p=0;p<MAX_PLAYERS;p++) {
|
|
if(playerFinishTime[p].plr!=-1) {
|
|
font.drawString(getPlayer(playerFinishTime[p].plr).getName());
|
|
glTranslatef(40,0,0);
|
|
font.printf("%.3f sec.",playerFinishTime[p].time);
|
|
glTranslatef(-40,2,0);
|
|
}
|
|
}
|
|
|
|
glPopMatrix();
|
|
|
|
glColor4f(1.0, 1.0, 1.0, 1.0);
|
|
}
|
|
|
|
Player &Game::getPlayer(int n)
|
|
{
|
|
return players[n];
|
|
}
|
|
|
|
void Game::resetListener()
|
|
{
|
|
listenerWeight = 0.0;
|
|
|
|
for(int i = 0; i < 3; i++)
|
|
{
|
|
listenerPos[i] = 0.0;
|
|
listenerVel[i] = 0.0;
|
|
listenerOri[i] = 0.0;
|
|
listenerOri[i + 3] = 0.0;
|
|
}
|
|
}
|
|
|
|
void Game::updateListener()
|
|
{
|
|
// Division by zero sanity check
|
|
if(listenerWeight == 0.0) return;
|
|
|
|
for(int i = 0; i < 3; i++)
|
|
{
|
|
listenerPos[i] /= listenerWeight;
|
|
listenerVel[i] /= listenerWeight;
|
|
listenerOri[i] /= listenerWeight;
|
|
listenerOri[i + 3] /= listenerWeight;
|
|
}
|
|
|
|
// Update OpenAL listener
|
|
alListenerfv(AL_POSITION, listenerPos);
|
|
alListenerfv(AL_VELOCITY, listenerVel);
|
|
alListenerfv(AL_ORIENTATION, listenerOri);
|
|
}
|
|
|
|
ALuint Game::getSource()
|
|
{
|
|
for(int i = 0; i < GLOBAL_SOURCES; i++)
|
|
{
|
|
ALint state;
|
|
alGetSourcei(globalSources[i], AL_SOURCE_STATE, &state);
|
|
if(state != AL_PLAYING) return globalSources[i];
|
|
}
|
|
|
|
return 0;
|
|
}
|
|
|