/* ************************************************************************* *
OldSkoolGravityGame (OSGG) Lunar Lander-like game for linux.
Copyright (C) 2008 Jimmy Christensen ( dusted at dusted dot dk )
This program is free software: you can redistribute it and/or modify
it under the terms of the GNU General Public License as published by
the Free Software Foundation, either version 3 of the License, or
(at your option) any later version.
This program is distributed in the hope that it will be useful,
but WITHOUT ANY WARRANTY; without even the implied warranty of
MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
GNU General Public License for more details.
You should have received a copy of the GNU General Public License
along with this program. If not, see .
* ************************************************************************* */
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#include
#if defined(WIN32)
typedef unsigned int uint;
#include
#include
#elif defined(__FreeBSD__) || defined(__DragonFly__)
#include
#else
#include
#endif
#define VERSION "1.0"
#ifndef DATADIR
#define DATADIR "./"
#endif
using namespace std;
bool soundOn,intermediateSave, vsync,showfps;
#include "sound.hpp"
#include "text.hpp"
glTextClass* glText = NULL;
soundClass* soundMan = NULL;
bool showHelp=true;
bool booted=true;
#define THRUSTINCRATE 0.01
#define GRAVITY 0.002
#define TURNINCRATE 0.15
uint32_t swapbytes32(uint32_t value)
{
uint32_t result;
((uint8_t*) &result)[0] = ((uint8_t*) &value)[3];
((uint8_t*) &result)[1] = ((uint8_t*) &value)[2];
((uint8_t*) &result)[2] = ((uint8_t*) &value)[1];
((uint8_t*) &result)[3] = ((uint8_t*) &value)[0];
return result;
}
enum entTypes { entLine, entShip, entBase, entWp, entEnemy };
enum gameStateEnum { GameStateEditor, GameStatePlaying, GameStatePause, GameStateQuit, GameStateNewGame, GameStateGameOver, GameStateNextLevel, GameStateStartEditor };
uint gameState = GameStateEditor;
struct demoFrameStruct {
Uint8 left,right,up,shoot;
};
struct pStruct {
int x,y;
};
string levelFile, saveDemoFile;
struct glPstruct {
GLfloat x, y;
};
struct structVert {
GLfloat color[3];
struct glPstruct p;
int collision; // 0 = none, 1 = normal(damage), 2 = platform/Landing gear
};
typedef struct structVert vert;
typedef struct glPstruct gPs;
typedef struct pStruct pS;
typedef struct demoFrameStruct demoFrame;
vectordemoFrames;
struct structEnt {
struct glPstruct p; //position
GLfloat rotation; //In DEG
gPs vel; //Velocity, only used for enemies right now
gPs baseP; //Base (start) position, used for nme
int type; //type
int id;
bool base; // If it's a base, don't translate it every time..
};
typedef struct structEnt entity;
struct structDispInfo {
gPs mousePos;
bool mouseDrag; //Is user holding btn
bool mouseRightDrag;
bool fullScreen;
pS dispSize; //Window size in pixels
pS dispHalfSize;
gPs glSceneSize;
gPs camPos; //The is where the camera is.
GLfloat glZoom;
GLfloat glBaseZoom;
GLfloat glUnitsPrPixel;
GLdouble aspect;
pS screenRes; //The current resolution ) SDL_GetVideoInfo() );
GLfloat bgColor[3];
bool enableZoom;
};
struct structDispInfo dispInfo;
struct structGameRules {
int startLevel; //what level to start in
GLfloat maxLandingRotForce;
GLfloat maxLandingYel;
GLfloat maxLandingXvel;
int fuelConsumptionThrust;
int fuelConsumptionTurn;
int fuelMaxFuel;
int ammoMaxAmmo;
};
struct structGameRules gameRules;
struct gameInfoStruct {
bool recording; //recording demo?
bool playing; //playing a demo?
int demoFrame;
GLfloat thrust;
gPs velocity;
GLfloat speed; //Relative speed
GLfloat distance;
GLfloat rotationForce;
int fuel;
int reloadTime; //After this amount of fames (60th's of 1 sec) player can shoot again
int ammo;
gPs radarDest; //Destination for package, points to the base where package should be delivered
int destBase; // 0 = no package, id of base.
int numBases; // Number of bases on map, set by loadMap
int score;
int numMissions; //number of finished missions
int level;
int lives;
vector shipVerts;
vector baseVerts;
vector enemyVerts;
vector shipStaticVerts;
vector baseStaticVerts;
vector enemyStaticVerts;
vector nextObjective; //Where to deliver to next.
int currentObjective; //What right now?
};
struct gameInfoStruct gameInfo;
GLfloat abs2(GLfloat x)
{
if (x<0) {return -x;}
return x;
}
bool LinesCross(gPs aLineA, gPs aLineB, gPs bLineA, gPs bLineB)
{
//First line, first point
GLfloat x0 = aLineA.x;
GLfloat y0 = aLineA.y;
//First Line, second point
GLfloat x1 = aLineB.x;
GLfloat y1 = aLineB.y;
//Second Line, First point
GLfloat x2 = bLineA.x;
GLfloat y2 = bLineA.y;
//Second Line, Second point
GLfloat x3 = bLineB.x;
GLfloat y3 = bLineB.y;
GLfloat d=(x1-x0)*(y3-y2)-(y1-y0)*(x3-x2);
if (abs2(d)<0.001f) {return 0;}
GLfloat AB=((y0-y2)*(x3-x2)-(x0-x2)*(y3-y2))/d;
if (AB>0.0 && AB<1.0)
{
GLfloat CD=((y0-y2)*(x1-x0)-(x0-x2)*(y1-y0))/d;
if (CD>0.0 && CD<1.0)
{
return 1;
}
}
return 0;
}
struct structSpark {
gPs pa,pb, v; //Position and velocity
GLfloat color[4];
int life; //in frames
int age; //in frames
};
class classSparkler
{
private:
list sparks;
public:
void spawn(gPs p, GLfloat rotation, int degree, GLfloat velocity, int num);
void render();
};
classSparkler sparkler;
//Spawn sparks, used for the thruster and explotions
//gPs position, rotation (angle of emitting), degrees of freedom, gPs velocity, number of sparks, maxLife
void classSparkler::spawn(gPs p, GLfloat rotation, int degree, GLfloat velocity, int num)
{
struct structSpark tS;
for(int i = 0; i != num; i++)
{
GLfloat rotRad = ((rotation-(degree/2))+rand()%degree ) * 0.0174532925;
tS.life = rand()%60;
tS.age = 0;
tS.color[0] = 1;
tS.color[1] = float(rand()%1000) / 1000.0 ;
tS.color[2] = 0;
tS.color[3] = 1.0;
tS.pa.x = p.x;
tS.pa.y = p.y;
int r=rand()%5;
tS.pb.x = p.x + float(r) *cos( rotRad );
tS.pb.y = p.y + float(r) *sin( rotRad );
tS.v.x = velocity*cos( rotRad );
tS.v.y = velocity*sin( rotRad );
sparks.push_back(tS);
}
}
void classSparkler::render()
{
glBegin( GL_LINES );
for(list ::iterator it = sparks.begin(); it != sparks.end(); ++it)
{
//Move spark.
it->pa.x += it->v.x;
it->pa.y += it->v.y;
it->pb.x += it->v.x;
it->pb.y += it->v.y;
//change color
it->color[1] -= float(rand()%20/1000.0);
it->color[3] = (1.0/it->life)*(it->life-it->age);
glColor4f( it->color[0],it->color[1],it->color[2], it->color[3] );
glVertex2f(it->pa.x - dispInfo.camPos.x, it->pa.y - dispInfo.camPos.y);
glVertex2f(it->pb.x - dispInfo.camPos.x, it->pb.y - dispInfo.camPos.y);
it->age++;
if(it->age > it->life)
{
it = sparks.erase(it);
}
}
glEnd( );
}
struct structShot {
gPs pa,pb;
gPs v;
int age, life;
bool fromShip; //if 1, wont collide with ship
};
class classBullets {
private:
vector shots;
public:
void shoot(entity owner, gPs velocity);
void render();
bool col(vector target, bool isShip);
void envCol(vector< vector > polys);
};
void classBullets::envCol(vector< vector > polys)
{
for(vector< vector >::iterator PolyIt = polys.begin(); PolyIt != polys.end(); ++PolyIt)
{
classBullets::col(*PolyIt, 0);
}
}
bool classBullets::col(vector target, bool isShip)
{
gPs tLineA, tLineB;
for(vector::iterator it = shots.begin(); it != shots.end(); ++it)
{
if(it->fromShip != isShip)
{
for(int ii=0; ii < target.size(); ii++)
{
if(ii==0)
{
tLineB = target[ii].p;
} else {
tLineA = tLineB;
tLineB = target[ii].p;
if(LinesCross(tLineA, tLineB, it->pa, it->pb))
{
//Remove bullet.
it = shots.erase(it);
return(1);
}
}
}
}
}
return false;
}
void classBullets::shoot(entity owner, gPs velocity)
{
struct structShot ts;
ts.pb = owner.p;
ts.pa.x = owner.p.x + 6.0* cos( owner.rotation * 0.0174532925 );
ts.pa.y = owner.p.y + 6.0* sin( owner.rotation * 0.0174532925 );
ts.v.x = velocity.x + 1.0 * cos( owner.rotation * 0.0174532925 );
ts.v.y = velocity.y + 1.0 * sin( owner.rotation * 0.0174532925 );
ts.age = 0;
ts.life = 40;
if(owner.type == entShip)
{
ts.fromShip=1;
} else {
ts.fromShip=0;
}
shots.push_back(ts);
soundMan->add(sfxLaser);
}
void classBullets::render()
{
glBegin( GL_LINES );
for(vector::iterator it = shots.begin(); it != shots.end(); ++it)
{
it->pa.x += it->v.x;
it->pa.y += it->v.y;
it->pb.x += it->v.x;
it->pb.y += it->v.y;
glColor4f( 1.0, 0.5,0.0, 1.0 );
glVertex2f(it->pa.x - dispInfo.camPos.x, it->pa.y - dispInfo.camPos.y);
glVertex2f(it->pb.x - dispInfo.camPos.x, it->pb.y - dispInfo.camPos.y);
it->age++;
if(it->age > it->life)
{
it = shots.erase(it);
if(shots.size() < 1)
break;
}
}
glEnd( );
}
class classBullets bullets;
void setSeneSize()
{
glMatrixMode(GL_PROJECTION);
glLoadIdentity();
GLfloat glSceneRes = dispInfo.glZoom;;
dispInfo.glSceneSize.x = glSceneRes * dispInfo.aspect;
dispInfo.glSceneSize.y = glSceneRes;
dispInfo.glUnitsPrPixel = (glSceneRes / float(dispInfo.dispSize.y)) * 2.0;
glOrtho( -dispInfo.glSceneSize.x, dispInfo.glSceneSize.x, -dispInfo.glSceneSize.y, dispInfo.glSceneSize.y, 0,1);
glMatrixMode(GL_MODELVIEW);
glEnable(GL_BLEND);
glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
glEnable(GL_LINE_SMOOTH);
glEnable(GL_POINT_SMOOTH );
glLineWidth ( 1.0 );
}
void setRes(int x, int y)
{
SDL_GL_SetAttribute( SDL_GL_STENCIL_SIZE, 1 );
/** Try to enable vsync **/
if(vsync)
{
SDL_GL_SetAttribute(SDL_GL_SWAP_CONTROL, 1);
}
int more=0;
dispInfo.fullScreen ? more=SDL_FULLSCREEN : more=0;
/*SDL_Surface *screen =*/ SDL_SetVideoMode(x,y,32, SDL_OPENGL|SDL_RESIZABLE|more);
glDisable(GL_DEPTH_TEST);
dispInfo.aspect = float(x) / float(y);
dispInfo.dispSize.x = x;
dispInfo.dispSize.y = y;
dispInfo.dispHalfSize.x = float(x)/2.0;
dispInfo.dispHalfSize.y = float(y)/2.0;
glViewport(0,0,x,y);
glClearStencil(0); //stencil
setSeneSize();
glLineStipple (1, 0xCCCC);
glClearColor(dispInfo.bgColor[0],dispInfo.bgColor[1],dispInfo.bgColor[2],1.0f);
if(glText != NULL)
delete glText;
glText = new glTextClass;
glDisable( GL_TEXTURE_2D );
}
/* It almost work, except when two verticies are both outside the screen, but the line between them crosses into the screen. */
/*
void isOnScreen(vector >& polys, vector< vector >& onScreenPolys, gPs scrP, gPs scrN)
{
bool polyNotEmpty, lastVis; //is this going to be visible? was the last one ?
int numVert; //number of verticies, number of submitted (to the list to render)
vector tPoly;
onScreenPolys.clear();
//Loop through polys.
for(vector >::iterator polyIt = polys.begin(); polyIt != polys.end(); ++polyIt)
{
numVert=0;
lastVis=0;
tPoly.clear();
polyNotEmpty=0;
for(vector ::iterator vertIt = polyIt->begin(); vertIt != polyIt->end(); ++vertIt)
{
//Visible?
if(vertIt->p.x > scrN.x && vertIt->p.x < scrP.x && vertIt->p.y < scrP.y && vertIt->p.y > scrN.y)
{
polyNotEmpty=1;
//Was the one before me on ?
if(numVert!=0 && lastVis==0)
{
//Put it on
tPoly.push_back( polyIt->at(numVert-1) );
}
lastVis=1; //Telling it to the next.
tPoly.push_back( *vertIt );
} else {
if(lastVis)
{
lastVis=0;
tPoly.push_back( *vertIt );
polyNotEmpty=0;
onScreenPolys.push_back( tPoly );
tPoly.clear();
}
}
numVert++;
}
//That was one poly, check if any verts is there.
if(polyNotEmpty)
{
onScreenPolys.push_back( tPoly );
}
}
}*/
void renderPolys(vector< vector > polys)
{
int pc=0;
int pp=0;
for(vector >::iterator it = polys.begin(); it != polys.end(); ++it)
{
glBegin( GL_LINE_STRIP );
for(vector ::iterator itt = it->begin(); itt != it->end(); ++itt)
{
if(itt->color[0] == -1.0)
{
glColor4f(0,0,0,0);
} else {
glColor4f(itt->color[0], itt->color[1], itt->color[2], 1.0);
}
glVertex2f(itt->p.x-dispInfo.camPos.x, itt->p.y-dispInfo.camPos.y);
}
glEnd();
}
//Draw verts if in editor
if(gameState == GameStateEditor)
{
glPointSize( 3.0 );
for(vector >::iterator it = polys.begin(); it != polys.end(); ++it)
{
glColor4f(1,1,1,1);
glBegin( GL_POINTS );
for(vector ::iterator itt = it->begin(); itt != it->end(); ++itt)
{
glVertex2f(itt->p.x-dispInfo.camPos.x, itt->p.y-dispInfo.camPos.y);
}
glEnd();
}
}
}
//Move and rotate an entity according to ent.p and ent.rotation
void updateEntVerts(entity ent, vector& entverts, vector statV )
{
entverts = statV;
GLfloat tx, ty;
for(vector::iterator i = entverts.begin(); i != entverts.end(); ++i)
{
tx = i->p.x * cos( (ent.rotation-90.0) * 0.0174532925 ) - ( i->p.y *sin( (ent.rotation-90.0) * 0.0174532925 )) ;
ty = i->p.x * sin( (ent.rotation-90.0) * 0.0174532925 ) + ( i->p.y *cos( (ent.rotation-90.0) * 0.0174532925 ));
i->p.x=tx;
i->p.y=ty;
i->p.x += ent.p.x;
i->p.y += ent.p.y;
}
}
#define PI 3.14159265
void renderEntities(vector ents)
{
for(vector::iterator it = ents.begin(); it != ents.end(); ++it)
{
if(it->type==entShip)
{
updateEntVerts(*it, gameInfo.shipVerts, gameInfo.shipStaticVerts);
if(gameState == GameStateEditor)
{
glColor3f(1,0,1);
glBegin(GL_POINTS);
glVertex2f(it->p.x - dispInfo.camPos.x, it->p.y - dispInfo.camPos.y);
glEnd( );
glPointSize(1.0);
}
if(gameInfo.thrust > 0)
{
gPs tP = it->p;
tP.x += -1.3 * cos(it->rotation* 0.0174532925);
tP.y += -1.3 * sin(it->rotation* 0.0174532925);
sparkler.spawn(tP, it->rotation+180, 40, 0.7, 7);
}
glBegin(GL_LINE_STRIP);
for(vector::iterator shipIt = gameInfo.shipVerts.begin(); shipIt != gameInfo.shipVerts.end(); ++shipIt)
{
glColor3f( shipIt->color[0],shipIt->color[1],shipIt->color[2] );
glVertex2f( shipIt->p.x - dispInfo.camPos.x, shipIt->p.y - dispInfo.camPos.y );
}
glEnd( );
}
if(it->type==entBase)
{
updateEntVerts(*it, gameInfo.baseVerts, gameInfo.baseStaticVerts);
if(gameState == GameStateEditor)
{
glPointSize(2.0);
glColor3f(0,1,1);
glBegin(GL_POINTS);
glVertex2f(it->p.x - dispInfo.camPos.x, it->p.y - dispInfo.camPos.y);
glEnd( );
glPointSize(1.0);
}
glBegin(GL_LINE_STRIP);
for(vector::iterator baseIt = gameInfo.baseVerts.begin(); baseIt != gameInfo.baseVerts.end(); ++baseIt)
{
glColor3f( baseIt->color[0],baseIt->color[1],baseIt->color[2] );
glVertex2f( baseIt->p.x - dispInfo.camPos.x, baseIt->p.y - dispInfo.camPos.y );
}
glEnd( );
}
if(it->type==entEnemy)
{
updateEntVerts(*it, gameInfo.enemyVerts, gameInfo.enemyStaticVerts);
if(gameState == GameStateEditor)
{
glPointSize(2.0);
glColor3f(0,1,1);
glBegin(GL_POINTS);
glVertex2f(it->p.x - dispInfo.camPos.x, it->p.y - dispInfo.camPos.y);
glEnd( );
glPointSize(1.0);
}
glBegin(GL_LINE_STRIP);
for(vector::iterator It = gameInfo.enemyVerts.begin(); It != gameInfo.enemyVerts.end(); ++It)
{
glColor3f( It->color[0],It->color[1],It->color[2] );
glVertex2f( It->p.x - dispInfo.camPos.x, It->p.y - dispInfo.camPos.y );
}
glEnd( );
}
}
}
GLuint gridDL;
void genGrid()
{
GLfloat step = 4.95946, steps=1000;
GLfloat len=step*steps;
GLfloat f=0;
gridDL = glGenLists(1);
glNewList( gridDL, GL_COMPILE );
glColor3f(0.3,0.3,0.3);
glBegin(GL_LINES);
f = -( len/2.0 );
while(f <= (len)/2.0 )
{
glVertex2f(f, (len/2.0) );
glVertex2f(f, -(len/2.0) );
f+=step;
}
f = -( len/2.0 );
while(f <= (len)/2.0 )
{
glVertex2f( (len/2.0), f );
glVertex2f( -(len/2.0), f );
f+=step;
}
glEnd( );
glEndList();
}
void drawGrid()
{
glPushMatrix();
glLoadIdentity();
glTranslatef( -dispInfo.camPos.x, -dispInfo.camPos.y, 0 );
glCallList(gridDL);
glPopMatrix();
}
void saveMap(vector< vector >verts, vector ents, string FileName)
{
//Open file stream
ofstream save;
save.open( FileName.data() , ios::out | ios::trunc);
if(!save.is_open())
{
cout << "File open err."< >::iterator it = verts.begin(); it != verts.end(); ++it)
{
save << "StartPoly" << endl;
for(vector::iterator itt = it->begin(); itt != it->end(); ++itt)
{
save << "StartVert" << endl;
save << itt->p.x << endl;
save << itt->p.y << endl;
save << itt->color[0] << endl;
save << itt->color[1] << endl;
save << itt->color[2] << endl;
save << itt->collision << endl;
save << "EndVert" << endl;
}
save << "EndPoly" << endl;
}
//Save ents
for(vector::iterator it = ents.begin(); it != ents.end(); ++it)
{
{
save << "StartEntity" << endl;
save << it->p.x << endl;
save << it->p.y << endl;
save << it->type << endl;
save << it->rotation << endl;
save << it->id << endl;
save << "EndEntity" << endl;
}
}
//Save mission
if(gameInfo.nextObjective.size() > 1)
{
save << "StartMission" << endl;
for(int i = 0; i < gameInfo.nextObjective.size(); i++)
{
save << gameInfo.nextObjective[i] << endl;
}
save << "EndMission" << endl;
}
save.close();
}
void loadMap(vector< vector >& polys, vector& ents)
{
polys.clear();
ents.clear();
string line;
ifstream load;
vector tvs; //temp to store the poly as we read it
vert tv; //temp to store the vert as we read it
entity te; //temp to store the entity while we read it
gameInfo.numBases=0;
gameInfo.nextObjective.clear();
load.open(levelFile.data());
if(!load.is_open())
{
cout << "could not load '" << levelFile << "'" << endl;
return;
}
int parseState=0, dataNum=0;
while(!load.eof())
{
getline(load, line);
if(line == "StartPoly")
{
parseState=1;
} else if(line=="EndPoly")
{
parseState=0;
polys.push_back(tvs);
tvs.clear();
} else if(parseState==1)
{
if(line == "StartVert")
{
parseState=2;
dataNum=0;
}
} else if(parseState==2)
{
if(line == "EndVert")
{
tvs.push_back(tv);
parseState=1;
} else {
dataNum++;
switch(dataNum)
{
case 1:
tv.p.x = atof(line.data());
break;
case 2:
tv.p.y = atof(line.data());
break;
case 3:
tv.color[0] = atof(line.data());
break;
case 4:
tv.color[1] = atof(line.data());
break;
case 5:
tv.color[2] = atof(line.data());
break;
case 6:
tv.collision = atoi(line.data());
break;
default:
cout << "??" << line << endl;
break;
}
}
} else if(line =="StartEntity")
{
parseState=3;
dataNum=0;
} else if(parseState==3)
{
if(line=="EndEntity")
{
dataNum=0;
te.baseP = te.p;
ents.push_back(te);
parseState=0;
} else {
dataNum++;
switch(dataNum)
{
case 1:
te.p.x = atof(line.data());
break;
case 2:
te.p.y = atof(line.data());
break;
case 3:
te.type = atoi(line.data());
if(te.type==entBase)
{
gameInfo.numBases++;
}
break;
case 4:
te.rotation = atof(line.data());
break;
case 5:
te.id = atoi(line.data());
if(te.type==entBase && te.id != gameInfo.numBases)
{
cout << "error: base entity have id:" << te.id << " but numBases is:" << gameInfo.numBases << endl;
}
break;
default:
cout << ">?" << line << endl;
break;
}
}
} else if(line=="StartMission")
{
parseState=4;
} else if(parseState==4)
{
if(line=="EndMission")
{
parseState=0;
} else {
gameInfo.nextObjective.push_back( atoi(line.data()) );
}
}
}
}
void readEnt(string File, vector& verts)
{
string line;
ifstream f;
vert tv; //temp to store the vert as we read it
f.open(File.data());
if(!f.is_open())
{
cout << "Failed to open file" << endl;
return;
}
int parseState=0, dataNum=0;
while(!f.eof())
{
getline(f, line);
if(line == "StartVert")
{
parseState=1;
dataNum=0;
} else if(parseState==1)
{
if(line == "EndVert")
{
parseState=0;
verts.push_back(tv);
} else {
dataNum++;
switch(dataNum)
{
case 1:
tv.p.x = atof(line.data());
break;
case 2:
tv.p.y = atof(line.data());
case 3:
tv.color[0] = atof(line.data());
break;
case 4:
tv.color[1] = atof(line.data());
break;
case 5:
tv.color[2] = atof(line.data());
break;
case 6:
tv.collision = atoi(line.data());
break;
default:
cout << "??" << line << endl;
break;
}
}
}
}
}
bool boxCol(gPs posa, gPs posb, GLfloat dist)
{
if(posa.x < posb.x+dist && posa.x > posb.x-dist)
{
if(posa.y < posb.y+dist && posa.y > posb.y-dist)
{
return(1);
}
}
return(0);
}
bool PolyCol( vector PolyA, vector PolyB )
{
gPs paLineA, paLineB, pbLineA, pbLineB;
bool paFirst=1, pbFirst=1;
int prevColType=0;
int checks=0;
for( vector::iterator paVertIt = PolyA.begin(); paVertIt != PolyA.end(); ++paVertIt)
{
if(paVertIt->collision)
{
if(paFirst || prevColType == 2) //if the last one was a 2, this one is the start of the next line to collision detect
{
paFirst=0;
paLineB=paVertIt->p;
} else {
paLineA=paLineB;
paLineB=paVertIt->p;
pbFirst=1;
//Great now we have a line, lets check it against every line in the other poly.
for(vector::iterator pbVertIt = PolyB.begin(); pbVertIt != PolyB.end(); ++pbVertIt)
{
if(pbVertIt->collision)
{
if(pbFirst)
{
pbFirst=0;
pbLineB=pbVertIt->p;
} else {
pbLineA=pbLineB;
pbLineB=pbVertIt->p;
//Here we have two lines. paLineA, paLineB, and pbLineA, pbLineB.
checks++;
if(LinesCross(paLineA, paLineB, pbLineA, pbLineB))
{
return(1);
}
}
}
}
}
}
prevColType = paVertIt->collision;
}
return(0);
}
bool landCol(vector baseVerts, vector shipVerts)
{
gPs baseA, baseB;
gPs shipA, shipB;
int prevColType=0;
//Find points on ship
for(vector::iterator shipIt = shipVerts.begin(); shipIt != shipVerts.end(); ++shipIt)
{
if(shipIt->collision == 2)
{
if(prevColType == 0)
{
shipA = shipIt->p;
prevColType++;
} else {
shipB = shipIt->p;
prevColType++;
}
}
}
prevColType=0;
for(vector::iterator baseIt = baseVerts.begin(); baseIt != baseVerts.end(); ++baseIt)
{
//Find the line to check with
if(prevColType==2)
{
baseB = baseIt->p;
if(shipA.x > baseA.x && shipA.x < baseB.x) //Is left side inside
{
if(shipB.x > baseA.x && shipB.x < baseB.x) // Is right side inside
{
if(shipA.y < baseA.y && shipB.y < baseB.y) //Is left side under the line
{
if(shipB.y < baseA.y && shipB.y < baseB.y) //Is right side under the line
{
if(shipA.y > baseA.y-1.0 && shipB.y > baseA.y-1.0) //Check that left side is above the height
{
//check velocity:
//cout << "Impact:"<= gameRules.maxLandingYel) //If it's not smaller, it's not faster
{
GLfloat temp = gameInfo.velocity.x;
if(temp < 0)
temp *= -1.0;
if(temp <= gameRules.maxLandingXvel) //it have to be slower
{
temp = gameInfo.rotationForce;
if(temp < 0)
temp *= -1.0;
if(temp <= gameRules.maxLandingRotForce)
{
return(1);
}
}
}
}
}
}
}
}
}
prevColType = baseIt->collision;
if(prevColType==2)
baseA = baseIt->p;
}
return(0);
}
static char plInfo[] =
" -= Welcome to OSGG =-\n\n"
" Your objective: Land on platforms as shown on radar.\n\n"
" Game Controls:\n"
" Arrows keys: Thrust/turn\n"
" Mouse wheel: Zoom in/Out\n"
" h: show/hide this screen\n"
" t: Restart Level\n"
" s: Go to level editor\n"
" F1: Start recording demo\n"
" F2: Stop demo record (save to ./demo.bin)\n"
" F10: Toggle sound\n"
" F11: Toggle fullscreen\n"
" F12: Screenshot\n"
" Pause: Pause the game\n"
" ESC: Exit the program";
static char edInfo[] =
" -= The OSGG Level Editor =-\n\n"
" ESC = Cancel poly creation, or exit program\n"
" Left mouse button: Add/Edit/Drag\n"
" Right mouse button: Move around the board\n"
" Mouse wheel: Zoom in/Out\n"
" 0: Adding: Polygons\n"
" 1: Collision detection on for next verticies\n"
" 2: Collision detection off for the next verticies\n"
" 4: Next vertices are white - 5: Grey - 6: Invisible\n"
" 7: Red - 8: Green - 9: Blue\n"
" q: Adding: The ship (Player start position)\n"
" w: Adding: Bases\n"
" e: Adding: enemies\n"
" m: Adding: Mission waypoints.\n"
" n: Enable defined waypoints. (Press before saving)\n"
" g: save map - t: start new game from starting level\n"
" s: Back to game\n"
" del: remove polygon or entity\n"
" /* 3: Landing platform (not for levels, only verts.txt) *\n"
" * d: Write the first polygon to verts.txt (not for levels) */";
void renderHelp() {
if(showHelp) {
char* info;
char buf[1024];
if( gameState == GameStatePlaying || gameState == GameStatePause )
{
info = plInfo;
buf[0]=0;
} else if( gameState == GameStateEditor )
{
info = edInfo;
sprintf(buf, "File: %s", levelFile.c_str() );
} else {
return;
}
//Whoa, I was crazy back then (too)
GLfloat scale = dispInfo.glZoom/100.0;
glColor4f(0.05,0.05,0.05,0.8);
glBegin( GL_QUADS );
glVertex2f(-125.0*scale, 95.0*scale );
glVertex2f(125.0*scale, 95.0*scale );
glVertex2f(125.0*scale, -95.0*scale );
glVertex2f(-125.0*scale, -95.0*scale );
glEnd( );
glColor4f(1,1,1,1);
glText->write(info, FONT_DEFAULT, 50.0*scale, -120*scale, 85*scale );
glColor4f(1,0,0,1);
glText->write(buf, FONT_DEFAULT, 40.0*scale, -120*scale, -90*scale );
}
}
void renderRadar(vector ents, vector > Poly)
{
//The radar and everything should be the same size regardless of zoom
//assume
char txt[32];
GLfloat scale = dispInfo.glZoom/100.0;
GLfloat mapScale = scale / 10.0;
//translate into place.
glTranslatef(90.0*scale,-80.0*scale,0.0);
glEnable(GL_STENCIL_TEST);
glStencilFunc(GL_ALWAYS, 1, 1); // Always Passes, 1 Bit Plane, 1 As Mask
glStencilOp(GL_KEEP, GL_KEEP, GL_REPLACE); // We Set The Stencil Buffer To 1 Where We Draw Any Polygon
glColor4f(0,0,0, 0.8);
glBegin( GL_QUADS );
glVertex2f(15.0*scale, 15.0*scale );
glVertex2f(-15.0*scale, 15.0*scale );
glVertex2f(-15.0*scale, -15.0*scale );
glVertex2f(15.0*scale, -15.0*scale );
glEnd( );
glStencilFunc(GL_EQUAL, 1, 1);
glStencilOp(GL_KEEP, GL_KEEP, GL_KEEP);
glPushMatrix(); //Push matrix onto the stack
//Draw the map, scale it down.
for(vector< vector >::iterator PolyIt = Poly.begin(); PolyIt != Poly.end(); ++PolyIt)
{
glBegin( GL_LINE_STRIP );
for(vector::iterator vertIt = PolyIt->begin(); vertIt != PolyIt->end(); ++vertIt)
{
if(vertIt->color[0] == -1.0)
{
glColor4f(0,0,0,0);
} else {
glColor4f(1,1,1,1);
}
glVertex2f( (vertIt->p.x-dispInfo.camPos.x)*mapScale, (vertIt->p.y-dispInfo.camPos.y)*mapScale);
}
glEnd( );
}
//Draw destination (if any)
if(gameInfo.destBase)
{
glColor3f(1,1,0);
glEnable (GL_LINE_STIPPLE);
glBegin( GL_LINES );
glVertex2f(0,0);
glVertex2f( (gameInfo.radarDest.x-dispInfo.camPos.x)*mapScale, (gameInfo.radarDest.y-dispInfo.camPos.y)*mapScale );
glEnd( );
glDisable (GL_LINE_STIPPLE);
}
glPointSize( 3.0 );
for(vector::iterator entIt = ents.begin(); entIt != ents.end(); ++entIt)
{
glBegin(GL_POINTS);
if(entIt->type==entShip)
{
glColor3f(0,0,1);
} else if(entIt->type==entBase)
{
glColor3f(0,1,0);
} else if(entIt->type==entEnemy) {
glColor3f(1,0,0);
} else {
glColor3f(1,0,1);
}
glVertex2f((entIt->p.x-dispInfo.camPos.x)*mapScale,(entIt->p.y-dispInfo.camPos.y)*mapScale);
glEnd( );
}
glEnd( );
glPopMatrix();
glDisable(GL_STENCIL_TEST);
glColor3f( 0,1,0 );
//Draw box around
glBegin( GL_LINE_STRIP );
glVertex2f(15*scale, 15*scale );
glVertex2f(-15*scale, 15*scale );
glVertex2f(-15*scale, -15*scale );
glVertex2f(15*scale, -15*scale );
glVertex2f(15*scale, 15*scale );
glEnd( );
//draw destination distance
sprintf(txt, "> %0.1f u", gameInfo.distance );
glColor4f(0,1,0,1);
glText->write(txt, FONT_DEFAULT, 40.0*scale, -15*scale, 15*scale+(glText->getHeight(FONT_DEFAULT)*20.0*scale));
glTranslatef(-35*scale, 0, 0);
GLfloat S = 15.0*scale;
glColor4f(0,0,0, 0.8);
glBegin( GL_QUADS );
glVertex2f(S, S );
glVertex2f(-S, S );
glVertex2f(-S, -S );
glVertex2f(S, -S );
glEnd( );
//Draw velocities
gPs vel, maxL;
vel.x = (S/gameRules.maxLandingXvel) * (gameInfo.velocity.x/2.0);
vel.y = (S/gameRules.maxLandingYel) * (gameInfo.velocity.y/2.0);
vel.y *= -1;
if(vel.x > S)
{
vel.x = S;
} else if(vel.x < -S)
{
vel.x = -S;
}
if(vel.y > S)
{
vel.y = S;
} else if(vel.y < -S)
{
vel.y = -S;
}
glColor4f( 1,1,1,1 );
glBegin( GL_LINES );
glVertex2f( 0,0 );
glVertex2f( vel.x, 0 );
glVertex2f( 0,0 );
glVertex2f( 0, vel.y );
glVertex2f( 0,0 );
glVertex2f( vel.x,vel.y );
if(gameInfo.velocity.y > 0)
{
vel.y = (gameRules.maxLandingYel-gameInfo.velocity.y);
} else {
vel.y = (gameRules.maxLandingYel+gameInfo.velocity.y);
}
if(gameInfo.velocity.x > 0)
{
vel.x = (gameRules.maxLandingXvel-gameInfo.velocity.x);
} else {
vel.x = (gameRules.maxLandingXvel+gameInfo.velocity.x);
}
glColor3f(1,0,0);
glVertex2f( S/2.0 +(vel.x*scale), -S);
glVertex2f( S/2.0 +(vel.x*scale), S);
glVertex2f( -S/2.0 -(vel.x*scale), -S);
glVertex2f( -S/2.0 -(vel.x*scale), S);
glVertex2f(-S, S/2.0 + (vel.y*scale) );
glVertex2f( S, S/2.0 + (vel.y*scale) );
glVertex2f(-S, -S/2.0- (vel.y*scale) );
glVertex2f( S, -S/2.0- (vel.y*scale) );
//reuse for drawing fuel
vel.y = (double(S)/double(gameRules.fuelMaxFuel) * double(gameInfo.fuel))*2.0;
glColor3f( 1,1,0 );
glVertex2f(-14*scale, -S );
glVertex2f(-14*scale, -S+vel.y);
//and for drawing ammo
vel.y = (double(S)/double(gameRules.ammoMaxAmmo) * double(gameInfo.ammo))*2.0;
glColor3f( 1,0,1 );
glVertex2f(-13*scale, -S );
glVertex2f(-13*scale, -S+vel.y);
glEnd( );
glColor3f( 0, 1, 0);
glBegin( GL_LINE_STRIP );
glVertex2f(15*scale, 15*scale );
glVertex2f(-15*scale, 15*scale );
glVertex2f(-15*scale, -15*scale );
glVertex2f(15*scale, -15*scale );
glVertex2f(15*scale, 15*scale );
glEnd( );
//Draw speed
sprintf(txt, "%0.1f u/T", gameInfo.speed);
glText->write(txt, FONT_DEFAULT, 40.0*scale, -15*scale, 15*scale+(glText->getHeight(FONT_DEFAULT)*20.0*scale));
glLoadIdentity();
}
void setMission(int missionId, vector ents)
{
gameInfo.destBase = gameInfo.nextObjective[missionId];
//Set destination on radar.
for(vector::iterator searchIt = ents.begin(); searchIt != ents.end(); ++searchIt)
{
if(searchIt->id == gameInfo.destBase)
{
gameInfo.radarDest = searchIt->p;
}
}
}
void initGame(vector< vector >& polys, vector& ents)
{
loadMap(polys, ents);
gameInfo.currentObjective=0;
gameInfo.score=0;
gameInfo.destBase=0;
gameInfo.fuel = gameRules.fuelMaxFuel;
gameInfo.ammo = gameRules.ammoMaxAmmo;
if(gameInfo.nextObjective.size() > 1)
setMission(0,ents);
}
void advanceLevel(vector< vector >& polys, vector& ents)
{
ostringstream t;
t.clear();
cout << endl;
cout << "Completed" << endl;
cout << "File " << levelFile < >& polys, vector& ents)
{
gameRules.maxLandingRotForce = 1.0; //degrees, both ways
gameRules.maxLandingYel = -0.20; //downward
gameRules.maxLandingXvel= 0.15; //both sides
gameRules.fuelConsumptionThrust = 15;
gameRules.fuelConsumptionTurn = 4;
gameRules.fuelMaxFuel = 5500;
gameRules.ammoMaxAmmo = 1000;
if(dispInfo.enableZoom)
dispInfo.glBaseZoom = 18;
else
dispInfo.glBaseZoom = 28;
dispInfo.glZoom = dispInfo.glBaseZoom;
setSeneSize();
gameState = GameStatePlaying;
gameInfo.numMissions=0; //Amount of missions taken
gameInfo.level=gameRules.startLevel;
initGame(polys, ents);
}
void shipCrash(entity ship)
{
soundMan->add(sfxBoom);
sparkler.spawn(ship.p, 0, 360, 1, 100);
gameInfo.velocity.x=0;
gameInfo.velocity.y=0;
gameInfo.thrust=0;
gameInfo.rotationForce=0;
gameState = GameStateGameOver;
cout << endl;
cout << "Dead" << endl;
cout << "Level "<& entVect)
{
int t=gameInfo.score;
gameInfo = snap;
gameInfo.score=t;
for(vector::iterator it=entVect.begin(); it != entVect.end(); ++it)
{
if(it->type==entShip)
{
*it = snapShip;
}
}
gameInfo.velocity.x=0;
gameInfo.velocity.y=0;
gameInfo.thrust=0;
gameInfo.rotationForce=0;
}
void saveState(vector entVect)
{
snap = gameInfo;
for(vector::iterator it=entVect.begin(); it != entVect.end(); ++it)
{
if(it->type==entShip)
{
snapPos = it->p;
snapShip = *it;
}
}
}
bool screenShot()
{
FILE *fscreen;
char cName[256];
int i = 0;
bool found=0;
while(!found)
{
sprintf(cName, "screenshot_%i.tga",i);
fscreen = fopen(cName,"rb");
if(fscreen==NULL)
found=1;
else
fclose(fscreen);
i++;
}
int nS = dispInfo.dispSize.x * dispInfo.dispSize.y * 3;
GLubyte *px = new GLubyte[nS];
if(px == NULL)
{
cout << "Alloc err, screenshot failed." < argc)
{
cout << "Error: Specify level file." << endl;
return(0);
}
chosenLevel=1;
levelFile = argv[i];
} else if( strcmp(argv[i], "--playdemo") == 0 )
{
i++;
if(i > argc)
{
cout << "Error: Specify demo file to play." << endl;
return(0);
}
cout << "Loading demo '" << argv[i] << "'..."< argc)
{
cout << "Error: Specify name of demo to record." << endl;
return(0);
}
saveDemoFile = argv[i];
} else if( strcmp(argv[i], "--nosound") == 0 )
{
soundOn=0;
} else if( strcmp(argv[i], "--fullscreen") == 0)
{
dispInfo.fullScreen=1;
} else if( strcmp(argv[i], "--bgcolor") == 0)
{
i++;
if(i > argc)
{
cout << "Error: Specify background color, in hex, RRGGBB, 000000 is black, FF0000 is red." << endl;
return(0);
}
char rgb[5];
//Red
sprintf(rgb, "0x%c%c", argv[i][0], argv[i][1]);
dispInfo.bgColor[0] = 0.003921569*strtol(rgb, NULL, 16);
//Green
sprintf(rgb, "0x%c%c", argv[i][2], argv[i][3]);
dispInfo.bgColor[1] = 0.003921569*strtol(rgb, NULL, 16);
//Blue
sprintf(rgb, "0x%c%c", argv[i][4], argv[i][5]);
dispInfo.bgColor[2] = 0.003921569*strtol(rgb, NULL, 16);
} else if( strcmp(argv[i], "--edit" ) == 0)
{
gameState=GameStateStartEditor;
} else if( strcmp(argv[i], "--is") == 0 )
{
intermediateSave=1;
} else if( strcmp(argv[i], "--sleep") == 0)
{
vsync=0;
} else if( strcmp(argv[i], "--showfps") == 0)
{
showfps=1;
} else if( strcmp(argv[i], "--nozoom") == 0)
{
dispInfo.enableZoom = 0;
} else if(i!=1) {
cout << "Error: unknown argument '" << argv[i] << "'" << endl;
return(0);
}
}
if(!chosenLevel && chosenDemo)
{
cout << "You must choose in which level to play the demo." << endl;
return(0);
}
return(1);
}
int main(int argc, char **argv)
{
srand ( time(NULL) );
/* These can be overwritten by the call to parseCmdLine */
dispInfo.enableZoom=1;
soundOn=1;
intermediateSave=0;
levelFile = DATADIR"levels/0.level";
saveDemoFile = DATADIR"demo.bin";
dispInfo.fullScreen=0;
dispInfo.bgColor[0] = 0.0;
dispInfo.bgColor[1] = 0.0;
dispInfo.bgColor[2] = 0.0;
showfps=0;
vsync=1;
gameState = GameStateNewGame;
if(!parseCmdLine(argc, argv))
{
return(1);
}
SDL_Event event;
//Init sdl and screen
if(SDL_Init(SDL_INIT_VIDEO | SDL_INIT_TIMER | SDL_INIT_AUDIO) <0 )
{
cout << SDL_GetError() << endl;
}
SDL_WM_SetCaption("OldSkoolGravityGame " VERSION, "OSGG");
SDL_ShowCursor(SDL_DISABLE);
SDL_WM_SetIcon( IMG_Load( DATADIR"icon.png" ), 0 );
cout << "Old Skool Gravity Game " VERSION ". GPL v3"<current_w;
dispInfo.screenRes.y = SDL_GetVideoInfo()->current_h;
if(dispInfo.fullScreen)
{
setRes(dispInfo.screenRes.x,dispInfo.screenRes.y);
} else {
setRes(800,600);
}
genGrid();
soundMan = new soundClass;
soundMan->init();
/* for fps */
struct timeval timeStart,timeStop;
int renderTime;
int ticks=0, lastTicks=0, fps=0, lastFps=0;
/** Editor vars **/
vector editorWayPoints;
vector editorIntWayPoints;
vector ents; //entities
vector activeVerts;
vector< vector > polys;
int editMode=0; // 0 = Start Poly 1 = Insert Verts, 2 = Cancel Poly, 3 = SelectPoly, 4 = move poly
gPs* dragVert; //Pointer to the verticies position
GLfloat crossColor[3];
GLfloat lineColor[3] = { 1, 1, 1 };
gameInfo.numBases = 0;
int newType=entLine;
int newCol=1; //collision on new lines.
int editorDeletePoly=0; //0= none, 1= remove key pressed, 2=remove it;
/** GameVars **/
vector testVerts;
vert tempVert;
gPs collisionPoint;
bool crashed=0;
char score[256];
GLfloat scale;
readEnt(DATADIR "ship.txt", gameInfo.shipStaticVerts);
readEnt(DATADIR "base.txt", gameInfo.baseStaticVerts);
readEnt(DATADIR "enemy.txt", gameInfo.enemyStaticVerts);
//Enter Main loop
while(gameState != GameStateQuit)
{
if(!vsync)
{
gettimeofday(&timeStart, NULL);
}
while(SDL_PollEvent(&event))
{
switch(event.type)
{
case SDL_QUIT:
gameState = GameStateQuit;
break;
case SDL_KEYDOWN:
if(gameState==GameStateGameOver)
{
if(intermediateSave)
{
loadState(ents);
} else {
initGame(polys, ents);
}
gameState=GameStatePlaying;
}
switch(event.key.keysym.sym)
{
case SDLK_PAUSE:
(gameState==GameStatePause) ? gameState=GameStatePlaying : gameState=GameStatePause;
break;
case SDLK_ESCAPE:
if(showHelp)
{
if(gameState==GameStatePause) {
gameState=GameStatePlaying;
}
showHelp=false;
} else if(editMode==1)
{
editMode=2;
} else {
gameState = GameStateQuit;
}
break;
case SDLK_DELETE:
editorDeletePoly=1;
break;
case SDLK_1:
newCol=1;
cout << "Collision on the next lines." << endl;
break;
case SDLK_2:
newCol=0;
cout << "Collision off the next lines." << endl;
break;
case SDLK_3:
newCol=2;
cout << "Next Lines are platforms." << endl;
break;
case SDLK_4:
lineColor[0] = 1.0;
lineColor[1] = 1.0;
lineColor[2] = 1.0;
break;
case SDLK_5:
lineColor[0] = 0.4;
lineColor[1] = 0.4;
lineColor[2] = 0.4;
break;
case SDLK_6:
lineColor[0] = -1.0;
lineColor[1] = 1.0;
lineColor[2] = 1.0;
break;
case SDLK_7:
lineColor[0] = 1.0;
lineColor[1] = 0.0;
lineColor[2] = 0.0;
break;
case SDLK_8:
lineColor[0] = 0.0;
lineColor[1] = 1.0;
lineColor[2] = 0.0;
break;
case SDLK_9:
lineColor[0] = 0.0;
lineColor[1] = 0.0;
lineColor[2] = 1.0;
break;
case SDLK_0:
newType=entLine;
break;
case SDLK_q:
newType=entShip;
break;
case SDLK_w:
newType=entBase;
break;
case SDLK_m:
newType=entWp;
break;
case SDLK_n:
gameInfo.nextObjective = editorIntWayPoints;
break;
case SDLK_h:
if(showHelp && gameState==GameStatePause) {
gameState=GameStatePlaying;
} else if( gameState == GameStatePlaying )
{
gameState = GameStatePause;
}
showHelp = !showHelp;
break;
case SDLK_e:
newType=entEnemy;
break;
case SDLK_o:
loadState(ents);
break;
case SDLK_p:
saveState(ents);
break;
case SDLK_d:
saveMap(polys, ents, DATADIR "verts.txt");
break;
case SDLK_s:
if(gameState == GameStateEditor)
gameState = GameStatePlaying;
else
gameState = GameStateEditor;
break;
case SDLK_g:
saveMap(polys, ents, levelFile.data());
break;
case SDLK_l:
loadMap(polys, ents);
break;
case SDLK_t:
gameState=GameStateNewGame;
break;
case SDLK_F10:
soundOn ? soundOn=0:soundOn=1;
break;
case SDLK_F11:
dispInfo.fullScreen ? dispInfo.fullScreen=0 : dispInfo.fullScreen=1;
if(dispInfo.fullScreen)
{
setRes(dispInfo.screenRes.x,dispInfo.screenRes.y);
} else {
setRes(800,600);
}
break;
case SDLK_F1:
gameInfo.recording=1;
demoFrames.clear();
break;
case SDLK_F2:
saveDemo();
break;
case SDLK_F12:
screenShot();
break;
}
break;//case sdlkeydown
case SDL_VIDEORESIZE:
setRes(event.resize.w, event.resize.h);
break;
case SDL_MOUSEMOTION:
dispInfo.mousePos.x = (event.motion.x - dispInfo.dispHalfSize.x) * dispInfo.glUnitsPrPixel;
dispInfo.mousePos.y = (event.motion.y - dispInfo.dispHalfSize.y) * dispInfo.glUnitsPrPixel * -1;
dispInfo.mousePos.x += dispInfo.camPos.x;
dispInfo.mousePos.y += dispInfo.camPos.y;
break;
case SDL_MOUSEBUTTONDOWN:
if(event.button.button == SDL_BUTTON_LEFT)
{
dispInfo.mouseDrag=1;
}
else if(event.button.button == SDL_BUTTON_RIGHT)
{
dispInfo.mouseRightDrag=1;
}
else if(event.button.button == SDL_BUTTON_WHEELDOWN)
{
dispInfo.glBaseZoom += 2;
dispInfo.glZoom = dispInfo.glBaseZoom;
setSeneSize();
}
else if(event.button.button == SDL_BUTTON_WHEELUP)
{
dispInfo.glBaseZoom -= 2;
dispInfo.glZoom = dispInfo.glBaseZoom;
setSeneSize();
}
break;
case SDL_MOUSEBUTTONUP:
if(event.button.button == SDL_BUTTON_LEFT)
dispInfo.mouseDrag=0;
else if(event.button.button == SDL_BUTTON_RIGHT)
dispInfo.mouseRightDrag=0;
break;
}
}
glClear( GL_COLOR_BUFFER_BIT | GL_STENCIL_BUFFER_BIT );
glLoadIdentity();
soundMan->play();
switch(gameState)
{
case GameStateNewGame:
initNewGame(polys,ents);
if(booted) {
gameState = GameStatePause;
booted=false;
} else {
gameState=GameStatePlaying;
}
break;
case GameStateStartEditor:
initNewGame(polys, ents);
gameState=GameStateEditor;
break;
case GameStateNextLevel:
advanceLevel(polys, ents);
break;
case GameStatePause:
case GameStateGameOver: //This is the same as pause
renderPolys(polys);
renderEntities(ents);
renderRadar(ents,polys);
sparkler.render();
break;
case GameStatePlaying:
renderPolys(polys);
renderEntities(ents);
renderRadar(ents,polys);
sparkler.render();
//Check if any bullets hit the enviroment:
bullets.envCol(polys);
bullets.render();
//Render HUD.
gameInfo.score += 1;
glColor4f( 0,1,0,1 );
scale = dispInfo.glZoom/100.0;
sprintf(score, "%i.%i", gameInfo.score/60, int(((gameInfo.score%60)*2)));
glText->write(score, FONT_DEFAULT, 50.0*scale, 0.0, dispInfo.glSceneSize.y- (glText->getHeight(FONT_DEFAULT)*30.0)*scale );
if(showfps)
{
sprintf(score, "%i fps", lastFps);
glText->write(score, FONT_DEFAULT, 40.0*scale, -120*scale, dispInfo.glSceneSize.y- (glText->getHeight(FONT_DEFAULT)*30.0)*scale );
}
//Ents:
for(vector::iterator it = ents.begin(); it != ents.end(); ++it)
{
if(it->type == entShip)
{
Uint8* keyStates = SDL_GetKeyState( NULL );
//Record?
if(gameInfo.recording)
{
demoRec(keyStates);
//Write on screen
sprintf(score, "Recording: %zu KiB...", (demoFrames.size()*(sizeof(demoFrame)))/1024 );
glText->write(score, FONT_DEFAULT, 40.0*scale, 50*scale, dispInfo.glSceneSize.y- (glText->getHeight(FONT_DEFAULT)*30.0)*scale );
//Override input
}
else if(gameInfo.playing)
{
demoPlay(keyStates);
}
if(keyStates[SDLK_LEFT])
{
if(gameInfo.fuel > 0)
{
gameInfo.rotationForce += TURNINCRATE;
gameInfo.fuel -= gameRules.fuelConsumptionTurn;
}
} else if(keyStates[SDLK_RIGHT])
{
if(gameInfo.fuel > 0)
{
gameInfo.rotationForce -= TURNINCRATE;
gameInfo.fuel -= gameRules.fuelConsumptionTurn;
}
}
if(keyStates[SDLK_UP])
{
if(gameInfo.fuel > 0)
{
gameInfo.thrust = THRUSTINCRATE;
gameInfo.fuel -= gameRules.fuelConsumptionThrust;
} else {
gameInfo.thrust = 0;
}
} else
{
gameInfo.thrust = 0;
}
if(gameInfo.reloadTime != 0)
{
gameInfo.reloadTime--;
}
if(keyStates[SDLK_SPACE])
{
if(gameInfo.reloadTime == 0 && gameInfo.ammo >= 100)
{
gameInfo.ammo -= 100;
gameInfo.reloadTime = 25;
bullets.shoot(*it, gameInfo.velocity);
}
}
//Update the speed
gameInfo.speed = abs2(gameInfo.velocity.x*100)+abs2(gameInfo.velocity.y*100);
//Update distance to destination
gameInfo.distance = (sqrt( pow( dispInfo.camPos.x - gameInfo.radarDest.x, 2) + pow( dispInfo.camPos.y - gameInfo.radarDest.y, 2 ) ));
//Update velocities
gameInfo.velocity.x += gameInfo.thrust * cos( (it->rotation*0.0174532925) );
gameInfo.velocity.y += gameInfo.thrust * sin( (it->rotation*0.0174532925) );
gameInfo.velocity.y -= GRAVITY;
//Zoom in/out
if( dispInfo.enableZoom )
{
static GLfloat optimalZoom=0.0;
optimalZoom = dispInfo.glBaseZoom + (gameInfo.speed*1.25);
if( gameInfo.distance < 40 )
optimalZoom = dispInfo.glBaseZoom - (10-gameInfo.distance);
if( dispInfo.glZoom != optimalZoom )
{
if(optimalZoom > dispInfo.glZoom)
{
dispInfo.glZoom += (optimalZoom-dispInfo.glZoom)/25.0;
if( dispInfo.glZoom > optimalZoom )
dispInfo.glZoom = optimalZoom;
} else {
dispInfo.glZoom -= (dispInfo.glZoom-optimalZoom)/25.0;
if( dispInfo.glZoom < optimalZoom )
dispInfo.glZoom = optimalZoom;
}
setSeneSize();
}
}
if(gameInfo.rotationForce > 0)
{
gameInfo.rotationForce /= 1.01;
if(gameInfo.rotationForce < 0)
{
gameInfo.rotationForce = 0;
}
} else if(gameInfo.rotationForce < 0)
{
gameInfo.rotationForce /= 1.01;
if(gameInfo.rotationForce > 0)
{
gameInfo.rotationForce = 0;
}
}
//sound
if(gameInfo.thrust != 0)
{
soundMan->add(sfxNozzle);
}
//Collision with terrain
for(vector< vector >::iterator PolyIt = polys.begin(); PolyIt != polys.end(); ++PolyIt)
{
if(PolyCol(*PolyIt, gameInfo.shipVerts))
{
shipCrash(*it);
}
}
crashed=0;
//Collision with ents
for(vector::iterator entIt = ents.begin(); entIt != ents.end(); ++entIt)
{
//Collition with entities
if(entIt->type==entBase)
{
updateEntVerts(*entIt, gameInfo.baseVerts, gameInfo.baseStaticVerts);
crashed=PolyCol(gameInfo.baseVerts, gameInfo.shipVerts);
} else if(entIt->type==entEnemy)
{
updateEntVerts(*entIt, gameInfo.enemyVerts, gameInfo.enemyStaticVerts);
crashed=PolyCol(gameInfo.enemyVerts, gameInfo.shipVerts);
//check if this enemy is being hit
if(bullets.col(gameInfo.enemyVerts, 0))
{
soundMan->add(sfxBoom);
sparkler.spawn(entIt->p, 0, 360, 1, 100);
entIt = ents.erase(entIt);
// gameInfo.score -= 3000;
break;
}
}
if(crashed)
{
shipCrash(*it);
} else {
//Is he landing on a base?
if(entIt->type==entBase)
{
if(landCol(gameInfo.baseVerts, gameInfo.shipVerts))
{
//Save state
if(intermediateSave)
saveState(ents);
//Subtract 1 from score again
// gameInfo.score -= 1;
if(gameInfo.fuel < gameRules.fuelMaxFuel)
gameInfo.fuel += 15;
if(gameInfo.fuel > gameRules.fuelMaxFuel)
gameInfo.fuel = gameRules.fuelMaxFuel;
if(gameInfo.ammo < gameRules.ammoMaxAmmo)
gameInfo.ammo += 3;
if(gameInfo.ammo > gameRules.ammoMaxAmmo)
gameInfo.ammo = gameRules.ammoMaxAmmo;
it->rotation=90.0;
if(gameInfo.velocity.y < 0.0)
gameInfo.velocity.y=0;
gameInfo.velocity.x=0;
gameInfo.rotationForce=0;
if(gameInfo.numBases > 1)
{
//Is this the destination base?
if(gameInfo.destBase == entIt->id)
{
// gameInfo.score -= 1000;
gameInfo.numMissions++;
//Do he have more missions left?
gameInfo.currentObjective++;
if(gameInfo.currentObjective < gameInfo.nextObjective.size())
{
//Give it to him
setMission(gameInfo.currentObjective, ents);
} else {
gameState = GameStateNextLevel;
}
}
}
}
}
} //not crashing
}
it->rotation += gameInfo.rotationForce;
it->p.x += gameInfo.velocity.x;
it->p.y += gameInfo.velocity.y;
dispInfo.camPos = it->p;
} else
/** Update enemies **/
if(it->type == entEnemy)
{
//Only do this if they are in the screen
if(boxCol(dispInfo.camPos, it->p, dispInfo.glSceneSize.x+3) )
{
it->vel.y -= GRAVITY;
if(it->p.y <= it->baseP.y-0.3)
{
it->vel.y += 0.01;
sparkler.spawn(it->p, it->rotation+180, 40, 0.7, 5);
}
it->p.y += it->vel.y;
}
}
}
break;
/** END OF GAME **/
case GameStateEditor:
crossColor[0] = 1.0;
crossColor[1] = 1.0;
crossColor[2] = 1.0;
//Search all polygons for verticies to see if player wish to edit it
if(editMode != 4)
{
if(editMode==3)
editMode=0;
for(vector >::iterator it = polys.begin(); it != polys.end(); ++it)
{
for(vector ::iterator itt = it->begin(); itt != it->end(); ++itt)
{
if(boxCol(dispInfo.mousePos, itt->p, (dispInfo.glZoom+1)/100))
{
crossColor[0] = 1.0;
crossColor[1] = 0.0;
crossColor[2] = 0.0;
if(editorDeletePoly)
{
editorDeletePoly=2;
break;
}
if(dispInfo.mouseDrag)
{
editMode=4;
dragVert = &itt->p;
}
}
}
if(editorDeletePoly==2)
{
polys.erase(it);
break;
}
}
//Do the same for ents
for(vector::iterator it = ents.begin(); it != ents.end(); ++it)
{
if(boxCol(dispInfo.mousePos, it->p, (dispInfo.glZoom+1)/100))
{
if(newType==entWp)
{
crossColor[0] = 0.0;
crossColor[1] = 1.0;
crossColor[2] = 0.0;
if(dispInfo.mouseDrag)
{
editorWayPoints.push_back( it->p );
editorIntWayPoints.push_back(it->id );
dispInfo.mouseDrag = 0;
}
} else {
crossColor[0] = 1.0;
crossColor[1] = 0.0;
crossColor[2] = 1.0;
if(dispInfo.mouseDrag)
{
editMode=4;
dragVert = &it->p;
}
if(editorDeletePoly)
{
editorDeletePoly=2;
}
if(editorDeletePoly==2)
{
ents.erase(it);
break;
}
}
}
}
}
editorDeletePoly=0;
if(editMode==4)
{
crossColor[0] = 1.0;
crossColor[1] = 0.0;
crossColor[2] = 0.0;
if(!dispInfo.mouseDrag)
{
editMode=0;
} else {
*dragVert = dispInfo.mousePos;
}
}
//Add stuff
if(dispInfo.mouseDrag && (editMode == 0 || editMode ==1) )
{
editMode = 1;
dispInfo.mouseDrag = 0;
//Add line to poly
if(newType==entLine)
{
vert tVert;
tVert.color[0] = lineColor[0];
tVert.color[1] = lineColor[1];
tVert.color[2] = lineColor[2];
tVert.collision = newCol;
//Finished the poly
if(activeVerts.size() > 1 && boxCol(dispInfo.mousePos, activeVerts[0].p, (dispInfo.glZoom+1)/100))
{
editMode = 0;
tVert.p = activeVerts[0].p;
activeVerts.push_back(tVert);
polys.push_back(activeVerts);
activeVerts.clear();
} else {
tVert.p = dispInfo.mousePos;
activeVerts.push_back(tVert);
}
}
//Add ship starting point
if(newType==entShip)
{
//Search for an existing ship and remove that if found
for(vector::iterator it = ents.begin(); it != ents.end(); ++it)
{
if(it->type == entShip)
{
ents.erase(it);
break;
}
}
entity tEnt;
tEnt.p = dispInfo.mousePos;
tEnt.type=newType;
tEnt.rotation = 90.0;
tEnt.id=-1; // ship don't have
ents.push_back(tEnt);
editMode = 0;
}
//Add base entity
if(newType==entBase)
{
entity tEnt;
tEnt.p = dispInfo.mousePos;
tEnt.type=newType;
tEnt.rotation = 90.0;
gameInfo.numBases++;
tEnt.id = gameInfo.numBases;
ents.push_back(tEnt);
editMode = 0;
}
if(newType==entEnemy)
{
entity tEnt;
tEnt.p = dispInfo.mousePos;
tEnt.baseP = tEnt.p;
tEnt.type=newType;
tEnt.rotation = 90.0;
tEnt.id = -1;
ents.push_back(tEnt);
editMode = 0;
}
}
if(editMode==1)
{
crossColor[0] = 0.0;
crossColor[1] = 1.0;
crossColor[2] = 0.0;
}
if( editMode == 2 )
{
activeVerts.clear();
editMode = 0;
}
//Moving around
if( dispInfo.mouseRightDrag )
{
dispInfo.mouseRightDrag=0;
dispInfo.camPos.x += dispInfo.mousePos.x - dispInfo.camPos.x;
dispInfo.camPos.y += dispInfo.mousePos.y - dispInfo.camPos.y;
SDL_WarpMouse( dispInfo.dispHalfSize.x, dispInfo.dispHalfSize.y );
}
drawGrid();
if(activeVerts.size() > 1)
{
if( boxCol(dispInfo.mousePos, activeVerts[0].p, (dispInfo.glZoom+1)/100) )
{
crossColor[0] = 0.0;
crossColor[1] = 0.0;
crossColor[2] = 1.0;
}
}
if(crossColor[0] == 1 && crossColor[1] == 1 && crossColor[2] == 1)
{
crossColor[0] = lineColor[0];
crossColor[1] = lineColor[1];
crossColor[2] = lineColor[2];
}
glColor3f(crossColor[0], crossColor[1], crossColor[2]);
//Draw the cross
glBegin( GL_LINES );
// -
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x - 5.0, dispInfo.mousePos.y-dispInfo.camPos.y );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x - 1.0, dispInfo.mousePos.y-dispInfo.camPos.y );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x + 5.0, dispInfo.mousePos.y-dispInfo.camPos.y );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x + 1.0, dispInfo.mousePos.y-dispInfo.camPos.y );
// |
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x, dispInfo.mousePos.y-dispInfo.camPos.y + 5.0 );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x, dispInfo.mousePos.y-dispInfo.camPos.y + 1.0 );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x, dispInfo.mousePos.y-dispInfo.camPos.y - 5.0 );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x, dispInfo.mousePos.y-dispInfo.camPos.y - 1.0 );
glEnd( );
glColor3f( 1, 1,1);
glBegin( GL_POINTS );
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x, dispInfo.mousePos.y-dispInfo.camPos.y );
glEnd( );
glBegin( GL_POINTS );
glVertex2f( 0,0 );
glVertex2f( -2.5,2.5 );
glVertex2f( 2.5,2.5 );
glVertex2f( 2.5,-2.5);
glVertex2f( -2.5,-2.5);
glEnd( );
//Draw the line not placed yet
if( editMode == 1)
{
if(activeVerts.size() > 0)
{
glBegin( GL_LINES );
glColor3f(activeVerts.back().color[0], activeVerts.back().color[1], activeVerts.back().color[2]);
glVertex2f( activeVerts.back().p.x-dispInfo.camPos.x, activeVerts.back().p.y-dispInfo.camPos.y );
glColor3f( lineColor[0], lineColor[1], lineColor[2]);
glVertex2f( dispInfo.mousePos.x-dispInfo.camPos.x, dispInfo.mousePos.y-dispInfo.camPos.y );
glEnd() ;
}
}
//Render active verticies
glBegin( GL_LINE_STRIP | GL_POINTS );
for(vector::iterator it = activeVerts.begin(); it != activeVerts.end(); ++it)
{
glColor3f(it->color[0], it->color[1], it->color[2]);
glVertex2f(it->p.x-dispInfo.camPos.x, it->p.y-dispInfo.camPos.y);
}
glEnd( );
//Render mission path
glBegin( GL_LINE_STRIP );
glColor4f( 1,1,0,1 );
for(int i=0; i < editorWayPoints.size(); i++)
{
glVertex2f(editorWayPoints[i].x - dispInfo.camPos.x, editorWayPoints[i].y - dispInfo.camPos.y);
}
glEnd( );
//Render gameworld
renderPolys(polys);
renderEntities(ents);
break;
}
renderHelp();
/* Swap buffers, it should block to wait for vsync */
SDL_GL_SwapBuffers( );
if(!vsync)
{
gettimeofday(&timeStop, NULL);
/* each second, it resets, if we are in the middle of that, things get messy */
if(timeStop.tv_usec < timeStart.tv_usec)
timeStop.tv_usec += 1000000;
renderTime= timeStop.tv_usec - timeStart.tv_usec;
if(renderTime < 16666)
{
#ifdef WIN32
Sleep( 16 - renderTime/1000 );
#else
usleep(16666-renderTime); // around 60 fps
#endif
}
}
fps++;
ticks += SDL_GetTicks() - lastTicks;
lastTicks = SDL_GetTicks();
if(ticks >= 1000)
{
ticks = 0;
if(fps > 64 && lastFps > 64 && vsync)
{
vsync=0;
cout << "Vsync:Framerate too high (over 64 fps) falling back on sleep based limiting." << endl;
cout << "Enable vsync in your driver and set monitor refresh rate to 60 hz." << endl;
}
lastFps=fps;
fps=0;
}
}
SDL_ShowCursor(SDL_ENABLE);
return(0);
}