/* ************************************************************************* * 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); }