Initial commit

This commit is contained in:
Jakub Skokan 2016-07-12 08:47:20 +02:00
commit d6c7f1ae87
95 changed files with 31975 additions and 0 deletions

888
src/game.cpp Normal file
View file

@ -0,0 +1,888 @@
#include "SDL.h"
#include "SDL_opengl.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_KP6, SDLK_KP4, SDLK_KP8}};
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()
{
// 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);
}
}
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();
}
}
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_SwapBuffers();
}
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("Kilpa ohi");
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("Pelaaja");
glTranslatef(40,0,0);
font.drawString("Aika");
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 sek.",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;
}