1 /*
2  * Shotgun Debugger
3  * Copyright 2005 Game Creation Society
4  *
5  * Programmed by Matt Sarnoff
6  * http://www.contrib.andrew.cmu.edu/~msarnoff
7  * http://www.gamecreation.org
8  *
9  * game.cpp - core functions, utilities, other miscellany
10  *
11  * This program is free software; you can redistribute it and/or
12  * modify it under the terms of the GNU General Public License
13  * as published by the Free Software Foundation; either version 2
14  * of the License, or (at your option) any later version.
15  *
16  * This program is distributed in the hope that it will be useful,
17  * but WITHOUT ANY WARRANTY; without even the implied warranty of
18  * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
19  * GNU General Public License for more details.
20  *
21  * You should have received a copy of the GNU General Public License
22  * along with this program; if not, write to the Free Software
23  * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
24  */
25 
26  #include "sdb.h"
27 
28 float SCREEN_TOP;
29 float SCREEN_CENTER;
30 float SCREEN_ASPECT;
31 //bool GFXMODE_ISACTIVE;
32 
33 MD2Loader modelLoader;
34 SDL_Surface *texdata[NUM_TEXTURES];
35 Model models[NUM_MODELS];
36 Mix_Chunk *snddata[NUM_SOUNDS];
37 Mix_Music *musicdata[NUM_SONGS];
38 
39 unsigned int *blurdata[NUM_BLUR_TEXTURES];
40 GLuint blurTextures[NUM_BLUR_TEXTURES];
41 
42 SDL_Event event;
43 Uint8 *keystate;
44 SDLMod modstate;
45 int mouse_dX;
46 int mouse_dY;
47 Uint8 mouseButtons;
48 
49 bool winLevel;
50 bool captured;
51 bool transitioning;
52 int systemExplode;
53 
54 Level currLevel;
55 Player P;
56 vector<Object *> currLevelObjs;
57 vector<AI *> currLevelEnemies;
58 Particle particles[NUM_PARTICLES];
59 int liveParticles;
60 Bullet bullets[NUM_BULLETS];
61 int liveBullets;
62 Laser lasers[NUM_LASERS];
63 int liveLasers;
64 Force forces[NUM_FORCES];
65 int liveForces;
66 Shockwave shockwaves[NUM_SHOCKWAVES];
67 int liveShockwaves;
68 float screenQuake;
69 float screenBlur;
70 RGBAColor screenBlurColor;
71 Trigger triggers[NUM_TRIGGERS];
72 char message[MESSAGE_LENGTH];
73 float messageTime;
74 
75 vector<GLdouble *> tessAllocatedVertices;
76 
77 //Timer timer;
78 
79 // returns a random float in the range [0.0, 1.0)
frand()80 float frand()
81 { return rand()%10000/10000.0; }
82 
83 // returns distance-squared between two points
distSquared(Vector2D a,Vector2D b)84 float distSquared(Vector2D a, Vector2D b)
85 { return (b.c[X]-a.c[X])*(b.c[X]-a.c[X]) + (b.c[Y]-a.c[Y])*(b.c[Y]-a.c[Y]); }
86 
87 // plain ol' distance formula, complete with square root
dist(Vector2D a,Vector2D b)88 float dist(Vector2D a, Vector2D b)
89 { return sqrt((b.c[X]-a.c[X])*(b.c[X]-a.c[X]) + (b.c[Y]-a.c[Y])*(b.c[Y]-a.c[Y])); }
90 
91 
92 // linear interpolation (t is between 0.0 and 1.0)
lerp(float a0,float a1,float t)93 float lerp(float a0, float a1, float t)
94 { return a0*(1-t)+a1*t; }
95 
96 // Grabs the next token from a line read from a file.
nextToken(string line,int & start,int & end)97 string nextToken(string line, int &start, int &end)
98 {
99   end = line.find_first_of(" \t", start);
100   if (end < 0 || end > line.length())
101     end = line.length();
102 
103   string token = line.substr(start, end-start);
104   start = line.find_first_not_of(" \t", end+1);
105   return token;
106 }
107 
108 // Takes a level number and makes the proper filename.
getLevelFileName(int levelNum)109 string getLevelFileName(int levelNum)
110 {
111   string s = LEVEL_PREFIX;
112   char n[4];
113   snprintf(n, 4, "%d", levelNum+1);
114   s += n;
115   return s;
116 }
117 
getLevelIntroFileName(int levelNum)118 string getLevelIntroFileName(int levelNum)
119 {
120   string s = LEVEL_INTRO_PREFIX;
121   char n[4];
122   snprintf(n, 4, "%d", levelNum+1);
123   s += n;
124   return s;
125 }
126 
127 
128 
129 // Load the configuration file. If it cannot find the file,
130 // the function exits, and the program uses the defaults.
loadFromFile(char * filename)131 void Configuration::loadFromFile(char *filename)
132 {
133   defaults();
134 
135   string dir = getenv("HOME");
136   dir += DATA_DIRECTORY;
137 
138   string file = dir + filename;
139 
140   ifstream cfgfile(file.c_str());
141   if (!cfgfile.is_open())
142   {
143     printf("Could not open configuration file %s, using default settings.\n", file.c_str());
144     return;
145   }
146 
147   int start, end;
148   string line, option, value, keyvalues[13];
149   while (getline(cfgfile, line, '\n'))
150   {
151     start = line.find_first_not_of(" \t\n");
152     end = 0;
153 
154     // Parse the line
155     if (line != "")
156     {
157       option = NEXT_TOK;
158 
159       if (option == "keybindings")
160       { for (int i = 0; i < 13; i++) keyvalues[i] = NEXT_TOK; }
161       else if (option != "level")
162         value = NEXT_TOK;
163 
164       // User config options
165       if (option == "xres"              && value != "") xres       = S2I(value);
166       if (option == "yres"              && value != "") yres       = S2I(value);
167       if (option == "depth"             && value != "") depth      = S2I(value);
168       if (option == "fullscreen"        && value != "") fullscreen = (bool)S2I(value);
169       if (option == "antialias"         && value != "") antialias  = (bool)S2I(value);
170       if (option == "blur"              && value != "") blur       = (bool)S2I(value);
171       if (option == "use_sound"         && value != "") use_sound  = (bool)S2I(value);
172       if (option == "sfx"               && value != "") sfx        = (bool)S2I(value);
173       if (option == "music"             && value != "") music      = (bool)S2I(value);
174       if (option == "mouse_threshold"   && value != "") mthresh    = S2I(value);
175       if (option == "mouse_sensitivity" && value != "") msense     = S2I(value);
176       if (option == "mouse_grab"        && value != "") mgrab      = S2I(value);
177       if (option == "framecap"          && value != "") framecap   = S2I(value);
178 
179       // Key bindings
180       if (option == "keybindings")
181       {
182         for (int i = 0; i < 13; i++)
183         {
184           if (keyvalues[i] != "")
185             keys[i] = (SDLKey)S2I(keyvalues[i]);
186         }
187       }
188 
189       // Debugging options
190       if (option == "show_fps"   && value != "") fps        = (bool)S2I(value);
191       if (option == "normals"    && value != "") normals    = (bool)S2I(value);
192       if (option == "paths"      && value != "") paths      = (bool)S2I(value);
193       if (option == "info"       && value != "") info       = (bool)S2I(value);
194       if (option == "culling"    && value != "") culling    = (bool)S2I(value);
195       if (option == "level")     defaultLevel = line.substr(start);
196 
197       // The Cheat(s)
198       if (option == CHEAT_INVUL   && value != "") cheat_invul   = (bool)S2I(value);
199       if (option == CHEAT_WEAPONS && value != "") cheat_weapons = (bool)S2I(value);
200       if (option == CHEAT_AMMO    && value != "") cheat_ammo    = (bool)S2I(value);
201       if (option == CHEAT_KEYS    && value != "") cheat_keys    = (bool)S2I(value);
202       if (option == CHEAT_NOAI    && value != "") cheat_noai    = (bool)S2I(value);
203       if (option == CHEAT_NOMASKS && value != "") cheat_nomasks = (bool)S2I(value);
204       if (option == CHEAT_SKIP    && value != "") cheat_skip    = (bool)S2I(value);
205       if (option == CHEAT_ZOOM    && value != "") cheat_zoom    = (bool)S2I(value);
206       if (option == CHEAT_ALL     && value != "")
207       {
208         bool val = (bool)S2I(value);
209         cheat_all = cheat_invul = cheat_weapons = cheat_ammo = cheat_keys = cheat_noai =
210         cheat_nomasks = cheat_skip = cheat_zoom = val;
211       }
212     }
213   }
214   cfgfile.close();
215 }
216 
217 // Writes current configuration data to a file. If the file
218 // cannot be opened for writing, the function exits.
writeToFile(char * filename)219 void Configuration::writeToFile(char *filename)
220 {
221   string dir = getenv("HOME");
222   dir += DATA_DIRECTORY;
223 
224   string file = dir + filename;
225   ofstream cfgfile(file.c_str());
226 
227   if (!cfgfile.is_open())
228   {
229     // try creating directory
230     mkdir(dir.c_str(), 0777);
231     cfgfile.clear();
232     cfgfile.open(file.c_str());
233     if (!cfgfile.is_open())
234     { printf("Could not open configuration file %s for writing.\n", file.c_str()); return; }
235   }
236   cfgfile.clear();
237 
238   cfgfile << "xres " << xres << endl;
239   cfgfile << "yres " << yres << endl;
240   cfgfile << "depth " << depth << endl;
241   cfgfile << "fullscreen " << fullscreen << endl;
242   cfgfile << "antialias " << antialias << endl;
243   cfgfile << "blur " << blur << endl;
244   cfgfile << "use_sound " << use_sound << endl;
245   cfgfile << "sfx " << sfx << endl;
246   cfgfile << "music " << music << endl;
247   cfgfile << "mouse_threshold " << mthresh << endl;
248   cfgfile << "mouse_sensitivity " << msense << endl;
249   cfgfile << "mouse_grab " << mgrab << endl;
250 
251   if (framecap >= 15) cfgfile << "framecap " << framecap << endl;
252 
253   cfgfile << "keybindings ";
254   for (int i = 0; i < 13; i++)
255   {
256     cfgfile << (int)keys[i];
257     if (i < 12)
258       cfgfile << " ";
259     else
260       cfgfile << endl;
261   }
262 
263   // Only output debugging options if they've been set
264 
265   if (fps) cfgfile << "show_fps " << fps << endl;
266   if (normals) cfgfile << "normals " << normals << endl;
267   if (paths) cfgfile << "paths " << paths << endl;
268   if (info) cfgfile << "info " << info << endl;
269   if (culling) cfgfile << "culling " << culling << endl;
270   if (defaultLevel != "") cfgfile << "level " << defaultLevel << endl;
271 
272   if (cheat_all)
273     cfgfile << CHEAT_ALL << " " << cheat_all << endl;
274   else
275   {
276     if (cheat_invul) cfgfile   << CHEAT_INVUL   << " " << cheat_invul << endl;
277     if (cheat_weapons) cfgfile << CHEAT_WEAPONS << " " << cheat_weapons << endl;
278     if (cheat_ammo) cfgfile    << CHEAT_AMMO << " " << cheat_ammo << endl;
279     if (cheat_keys) cfgfile    << CHEAT_KEYS << " " << cheat_keys << endl;
280     if (cheat_noai) cfgfile    << CHEAT_NOAI << " " << cheat_noai << endl;
281     if (cheat_nomasks) cfgfile << CHEAT_NOMASKS << " " << cheat_nomasks << endl;
282     if (cheat_skip) cfgfile    << CHEAT_SKIP << " " << cheat_skip << endl;
283     if (cheat_zoom) cfgfile    << CHEAT_ZOOM << " " << cheat_zoom << endl;
284   }
285   cfgfile.close();
286 }
287 
288 // Blend a color into this color. Specifying USE_ALPHA
289 // for amountOfNew will blend with the second color's alpha value,
290 // else the function will use the specified value.
blend(RGBAColor c,float amountOfNew)291 void RGBAColor::blend(RGBAColor c, float amountOfNew)
292 {
293   r = lerp(r, c.r, (amountOfNew == USE_ALPHA) ? c.a : amountOfNew);
294   g = lerp(g, c.g, (amountOfNew == USE_ALPHA) ? c.a : amountOfNew);
295   b = lerp(b, c.b, (amountOfNew == USE_ALPHA) ? c.a : amountOfNew);
296 }
297 
298 // Finds the intersection point of line segment ab with a line..
299 // Returns true and stores the intersection point in [ if there is an intersection.
300 // From the comp.graphics.algorithms FAQ, section 1.03
intersection(Vector2D a,Vector2D b,Vector2D * p)301 bool Line::intersection(Vector2D a, Vector2D b, Vector2D *p)
302 {
303   float r, s;
304 
305   r =  ((a.c[Y]-p1.c[Y])*(p2.c[X]-p1.c[X])-(a.c[X]-p1.c[X])*(p2.c[Y]-p1.c[Y])) /
306       ((b.c[X]-a.c[X])*(p2.c[Y]-p1.c[Y])-(b.c[Y]-a.c[Y])*(p2.c[X]-p1.c[X]));
307 
308   s =  ((a.c[Y]-p1.c[Y])*(b.c[X]-a.c[X])-(a.c[X]-p1.c[X])*(b.c[Y]-a.c[Y])) /
309       ((b.c[X]-a.c[X])*(p2.c[Y]-p1.c[Y])-(b.c[Y]-a.c[Y])*(p2.c[X]-p1.c[X]));
310 
311   if (r < 0 || r > 1 || s < 0 || s > 1)
312     return false;
313 
314   if (p != NULL)
315     p->set(a.c[X]+r*(b.c[X]-a.c[X]), a.c[Y]+r*(b.c[Y]-a.c[Y]));
316   return true;
317 }
318 
319 // Stretches a bounding box (if necessary) to accommodate a point.
addPoint(Vector2D point)320 void BoundingBox::addPoint(Vector2D point)
321 {
322   if (!pointsAdded)
323   { min = point; max = point; pointsAdded = true; }
324   else
325   {
326     if (point.c[X] < min.c[X])  min.c[X] = point.c[X];
327     if (point.c[Y] < min.c[Y])  min.c[Y] = point.c[Y];
328     if (point.c[X] > max.c[X])  max.c[X] = point.c[X];
329     if (point.c[Y] > max.c[Y])  max.c[Y] = point.c[Y];
330   }
331   center = (min+max)*0.5;
332 }
333 
334 // Calculate the size of the bounding box by finding the mininum and maximum x and y
335 // coordinates from a list of points.
calculate(vector<Vector2D> points)336 void BoundingBox::calculate(vector<Vector2D> points)
337 {
338   pointsAdded = false;
339   for (int i = 0; i < points.size(); i++)
340     addPoint(points[i]);
341 }
342 
343 // Returns true if the point is contained in this bounding box.
pointInBB(Vector2D point)344 bool BoundingBox::pointInBB(Vector2D point)
345 {
346   return (point.c[X] >= min.c[X] && point.c[X] <= max.c[X] &&
347           point.c[Y] >= min.c[Y] && point.c[Y] <= max.c[Y]);
348 }
349 
350 // Returns true if this bounding box intersects another
intersectBB(BoundingBox b2)351 bool BoundingBox::intersectBB(BoundingBox b2)
352 {
353   if (min.c[X] > b2.max.c[X]) return false;
354   if (max.c[X] < b2.min.c[X]) return false;
355   if (min.c[Y] > b2.max.c[Y]) return false;
356   if (max.c[Y] < b2.min.c[Y]) return false;
357   return true;
358 }
359 
rotate(float angle)360 void BoundingBox::rotate(float angle)
361 {
362   Vector2D dmax = max-center, dmin = min-center;
363   dmax.set(cos(angle)*dmax.c[X]-sin(angle)*dmax.c[Y], sin(angle)*dmax.c[X]+cos(angle)*dmax.c[Y]);
364   dmin.set(cos(angle)*dmin.c[X]-sin(angle)*dmin.c[Y], sin(angle)*dmin.c[X]+cos(angle)*dmin.c[Y]);
365   max = center + dmax;
366   min = center + dmin;
367 }
368 
369 // An adaptation of W. Randolph Franklin's pnpoly algorithm.
pointInPolygon(Vector2D point)370 int Surface::pointInPolygon(Vector2D point)//int npol, float *xp, float *yp, float x, float y)
371 {
372   int i, j, c = 0;
373   for (i = 0, j = vert.size()-1; i < vert.size(); j = i++) {
374     if ((((vert[i].c[Y]<=point.c[Y]) && (point.c[Y]<vert[j].c[Y])) ||
375           ((vert[j].c[Y]<=point.c[Y]) && (point.c[Y]<vert[i].c[Y]))) &&
376         (point.c[X] < (vert[j].c[X] - vert[i].c[X]) * (point.c[Y] - vert[i].c[Y]) / (vert[j].c[Y] - vert[i].c[Y]) + vert[i].c[X]))
377 
378       c = !c;
379   }
380   return c;
381 }
382 
383 // Checks to see if there are no walls intersecting the line between two points.
hasLineOfSight(Vector2D start,Vector2D end,bool includeDoors)384 bool hasLineOfSight(Vector2D start, Vector2D end, bool includeDoors)
385 {
386   Vector2D ray, intersect;
387   int cell;
388 
389   int startCX, startCY, endCX, endCY;
390   int minCX, minCY, maxCX, maxCY;
391   currLevel.cellNumber(start, &startCX, &startCY);
392   currLevel.cellNumber(end, &endCX, &endCY);
393 
394   if (startCX <= endCX)
395   { minCX = startCX; maxCX = endCX; }
396   else
397   { minCX = endCX; maxCX = startCX; }
398 
399   if (startCY <= endCY)
400   { minCY = startCY; maxCY = endCY; }
401   else
402   { minCY = endCY; maxCY = startCY; }
403 
404   for (int cy = minCY; cy <= maxCY; cy++)
405   {
406     for (int cx = minCX; cx <= maxCX; cx++)
407     {
408       ray = end - start;
409 
410       cell = cy*currLevel.gridCols + cx;
411 
412       if (currLevel.inGrid(start + ray))
413       {
414         int numWalls = currLevel.grid[cell].wall.size();
415         Wall *currWall;
416 
417         for (int i = 0; i < numWalls + ((includeDoors) ? 4*currLevel.grid[cell].door.size() : 0); i++)
418         {
419           if (i < numWalls)
420             currWall = &currLevel.wall[currLevel.grid[cell].wall[i]];
421           else
422             currWall = &currLevel.door[currLevel.grid[cell].door[(i-numWalls)/4]].sides[(i-numWalls)%4];
423 
424           if ((currWall->CollFlags() & COLLIDE_OBJECT) && currWall->height > 0 && currWall->intersection(start, start+ray, &intersect))
425             return false;
426         }
427       }
428     }
429   }
430   return true;
431 }
432 
circleSegmentIntersection(Vector2D c,float r,Vector2D p1,Vector2D p2,Vector2D * intersect)433 bool circleSegmentIntersection(Vector2D c, float r, Vector2D p1, Vector2D p2, Vector2D *intersect)
434 {
435   Vector2D dir = p2 - p1;
436   Vector2D diff = c - p1;
437   float t = (diff*dir) / (dir*dir);
438   if (t < 0.0f)
439       t = 0.0f;
440   if (t > 1.0f)
441       t = 1.0f;
442   Vector2D closest = p1 + (dir * t);
443   Vector2D d = c - closest;
444   intersect->set(closest.c[X], closest.c[Y]);
445   float distsqr = d*d;
446   return distsqr <= r * r;
447 }
448 
gameWelcomeMessage()449 void gameWelcomeMessage()
450 {
451   time_t rawtime;
452   tm *timeinfo;
453   time(&rawtime);
454   timeinfo = localtime(&rawtime);
455 
456   printf("%s %s\nBuilt %s %s by %s\n", GAME_TITLE, GAME_VERSION, __DATE__, __TIME__, GAME_AUTHOR);
457   printf("Started %s", asctime(timeinfo));
458 }
459 
gameInitSDL()460 void gameInitSDL()
461 {
462   printf("  Initializing SDL...");
463   if (SDL_Init(SDL_INIT_VIDEO | SDL_INIT_AUDIO) < 0)
464     errorMessage(1, "FLAGRANT SYSTEM ERROR", "Could not initialize SDL (%s)", SDL_GetError());
465 
466   atexit(SDL_Quit);
467 
468   SDL_WM_SetCaption(GAME_TITLE, GAME_TITLE);
469   printf("ok\n");
470 
471   SDL_ShowCursor(SDL_DISABLE);
472 
473   if (config.use_sound)
474   {
475     printf("  Initializing sound...");
476     if (Mix_OpenAudio(22050, MIX_DEFAULT_FORMAT, 2, 1024) < 0)
477     {
478       printf("Could not initialize sound mixer (%s)\nContinuing with no sound.\nTo reenable sound, set \"use_sound\" to 1 in %s%s%s\n", Mix_GetError(), getenv("HOME"), DATA_DIRECTORY, CONFIG_FILE);
479       config.use_sound = false;
480     }
481     else
482       printf("ok\n");
483   }
484 }
485 
gameInitOpenGL()486 void gameInitOpenGL()
487 {
488   printf("  Initializing OpenGL...");
489   SDL_GL_SetAttribute(SDL_GL_DOUBLEBUFFER, 1);
490 
491   glClearColor(0.0, 0.0, 0.0, 0.0);
492   glShadeModel(GL_SMOOTH);
493 
494   glEnable(GL_DEPTH_TEST);
495   glDepthFunc(GL_LEQUAL);
496 
497   glEnable(GL_ALPHA_TEST);
498   glAlphaFunc(GL_LEQUAL, 0);
499 
500   glEnable(GL_BLEND);
501   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
502 
503   glEnable(GL_TEXTURE_2D);
504   glPixelStorei(GL_UNPACK_ALIGNMENT, 1);
505 
506   GLfloat light_position[] = {0.0, 0.0, 1.0, 1.0};
507   glLightfv(GL_LIGHT0, GL_POSITION, light_position);
508 
509   printf("ok\n");
510 }
511 
gameSetVideoMode(int width,int height,int depth,int fullscreen)512 void gameSetVideoMode(int width, int height, int depth, int fullscreen)
513 {
514   printf("  Setting video mode %dx%dx%d...", width, height, depth);
515   if (!SDL_SetVideoMode(width, height, depth, SDL_OPENGL | SDL_HWSURFACE | fullscreen))
516     errorMessage(2, "FLAGRANT SYSTEM ERROR", "Could not set video mode: %s\nChange your video settings in %s%s%s", SDL_GetError(), getenv("HOME"), DATA_DIRECTORY, CONFIG_FILE);
517 
518   GFXMODE_ISACTIVE = true;
519 
520   glViewport(0, 0, width, height);
521   SCREEN_ASPECT = (float)width/(float)height;
522   gameSetPerspectiveMode();
523   printf("ok\n    Using %s %s %s\n", glGetString(GL_VENDOR), glGetString(GL_RENDERER),
524     glGetString(GL_VERSION));
525 }
526 
gameSetPerspectiveMode()527 void gameSetPerspectiveMode()
528 {
529   glMatrixMode(GL_PROJECTION);
530   glLoadIdentity();
531   gluPerspective(55.0, SCREEN_ASPECT, 0.1, 1000.0);
532   glMatrixMode(GL_MODELVIEW);
533 }
534 
gameSetOrthoMode(float r)535 void gameSetOrthoMode(float r)
536 {
537   glMatrixMode(GL_PROJECTION);
538   glPushMatrix();
539   glLoadIdentity();
540   SCREEN_TOP = r/SCREEN_ASPECT;
541   glOrtho(-r, r, -SCREEN_TOP, SCREEN_TOP, -1.0, 1.0);
542   glMatrixMode(GL_MODELVIEW);
543   glLoadIdentity();
544 }
545 
gameSetAntialiasing(bool set)546 void gameSetAntialiasing(bool set)
547 {
548   (set) ? glEnable(GL_LINE_SMOOTH) : glDisable(GL_LINE_SMOOTH);
549 }
550 
gameInitTessellator()551 void gameInitTessellator()
552 {
553   tess = gluNewTess();
554   tessAllocatedVertices.clear();
555   gluTessCallback(tess, GLU_TESS_BEGIN,  (GLvoid (CALLBACK *) ())tcbBegin);
556   gluTessCallback(tess, GLU_TESS_VERTEX, (GLvoid (CALLBACK *) ())glVertex3dv);
557   gluTessCallback(tess, GLU_TESS_END,    (GLvoid (CALLBACK *) ())tcbEnd);
558   gluTessCallback(tess, GLU_TESS_COMBINE,(GLvoid (CALLBACK *) ())tcbCombine);
559   gluTessCallback(tess, GLU_TESS_ERROR,  (GLvoid (CALLBACK *) ())tcbError);
560 }
561 
gameLoadSprites()562 void gameLoadSprites()
563 {
564   printf("  Loading sprites...");
565   // allocate texture space
566   glGenTextures(NUM_TEXTURES, textures);
567   int i;
568   for (i = 0; i < NUM_TEXTURES; i++)
569   {
570     switch(i)
571     {
572       case TEX_FONT:                texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/font3.png"); break;
573       case TEX_SHADOW:              texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/shadow.png"); break;
574       case TEX_LIGHT:               texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/light.png"); break;
575       case TEX_SCANLINES:           texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/scanlines.png"); break;
576       case TEX_MF_RIFLE:            texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/muzzleflash.png"); break;
577       case TEX_MF_SHOTGUN:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/muzzleflash2.png"); break;
578       case TEX_EXPLOSION1:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/explosion1.png"); break;
579       case TEX_EXPLOSION2:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/explosion2.png"); break;
580       case TEX_EXPLOSION3:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/explosion3.png"); break;
581       case TEX_EXPLOSION4:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/explosion4.png"); break;
582       case TEX_SMOKE:               texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/smoke.png"); break;
583       case TEX_BEAM1:               texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/beam1.png"); break;
584       case TEX_BEAM2:               texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/beam2.png"); break;
585       case TEX_LASER_GLOW:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/laserglow.png"); break;
586       case TEX_ESG_CHARGE:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/esgcharge.png"); break;
587       case TEX_BORDER:              texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/border.png"); break;
588       case TEX_TERMINAL_BG:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/scanline.png"); break;
589       case TEX_BIT:                 texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/bit.png"); break;
590       case TEX_BOX_NORMAL:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/box.png"); break;
591       case TEX_BARREL_NORMAL:       texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/barrel.png"); break;
592       case TEX_STEELBOX_NORMAL:     texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/steelbox.png"); break;
593       case TEX_PARTICLE:            texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle.png"); break;
594       case TEX_PARTICLE_BLOOD:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_blood.png"); break;
595       case TEX_PARTICLE_SLIME:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_slime.png"); break;
596       case TEX_PARTICLE_SPARK:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_pinkspark.png"); break;
597       case TEX_PARTICLE_ENERGY:     texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_energy.png"); break;
598       case TEX_PARTICLE_WOOD:       texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_wood.png"); break;
599       case TEX_PARTICLE_METAL:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_metal.png"); break;
600       case TEX_PARTICLE_GLOW:       texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/glowparticle.png"); break;
601       case TEX_HEPA_SPARK:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_hepa_spark.png"); break;
602       case TEX_LASER_SPARK1:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_laser_spark.png"); break;
603       case TEX_LASER_SPARK2:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/particle_laser_spark2.png"); break;
604       case TEX_ESG_SHOCKWAVE:       texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/esgshockwave.png"); break;
605       case TEX_EXPLOSIONPART:       texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/explosionpart.png"); break;
606       case TEX_POWERUP_HEALTH:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/health.png"); break;
607       case TEX_POWERUP_ENERGY_CLIP: texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/clip.png"); break;
608       case TEX_POWERUP_SHOTGUN_AMMO:texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/shotgunammo.png"); break;
609       case TEX_POWERUP_HEPA_CLIP:   texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/hepaclip.png"); break;
610       case TEX_POWERUP_LASER_CELL:  texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/lasercell.png"); break;
611       case TEX_POWERUP_ESG_BATTERY: texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/esgcell.png"); break;
612       case TEX_POWERUP_KEY1:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/key1.png"); break;
613       case TEX_POWERUP_KEY2:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/key2.png"); break;
614       case TEX_POWERUP_KEY3:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/key3.png"); break;
615       case TEX_WEAPON_RIFLE:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/riflepickup.png"); break;
616       case TEX_WEAPON_SHOTGUN:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/shotgunpickup.png"); break;
617       case TEX_WEAPON_HEPA:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/hepapickup.png"); break;
618       case TEX_WEAPON_LASER:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/laserpickup.png"); break;
619       case TEX_WEAPON_ESG:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/esgpickup.png"); break;
620       case TEX_RIFLE_SHELL:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/rifleshell.png"); break;
621       case TEX_SHOTGUN_SHELL:       texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/shotgunshell.png"); break;
622       case TEX_BLT_RIFLE:           texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/bullet.png"); break;
623       case TEX_BLT_SHOTGUN:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/pellet.png"); break;
624       case TEX_BLT_GRENADE:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/grenadelive.png"); break;
625       case TEX_BLT_SHRAPNEL:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/shrapnel.png"); break;
626       case TEX_BLT_BLASTER:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/blaster.png"); break;
627       case TEX_WPN_RIFLE:           texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/rifle.png"); break;
628       case TEX_WPN_SHOTGUN:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/shotgun.png"); break;
629       case TEX_WPN_HEPA:            texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/hepa.png"); break;
630       case TEX_WPN_LASER:           texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/laser.png"); break;
631       case TEX_WPN_ESG:             texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/esg.png"); break;
632       case TEX_WPN_GRENADE:         texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/grenade.png"); break;
633       case TEX_TITLE_BG:            texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/title.png"); break;
634       case TEX_BG2:                 texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/bg2.png"); break;
635       case TEX_HEX1:                texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/hex1.png"); break;
636       case TEX_HEX2:                texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/hex2.png"); break;
637 
638       case MTEX_PLAYER:             texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/playerskin.png"); break;
639       case MTEX_PLAYER2:            texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/playerskin2.png"); break;
640       case MTEX_BARREL:             texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/barrelskin.png"); break;
641       case MTEX_TURRET:             texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/turretskin.png"); break;
642       case MTEX_MIB:                texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/mibskin.png"); break;
643       case MTEX_UNARMED_GUARD:      texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/unarmedskin.png"); break;
644       case MTEX_ARMED_DRONE:        texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/armedskin.png"); break;
645       case MTEX_HUNTER:             texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/hunterskin.png"); break;
646       case MTEX_GLADIATOR:          texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/gladiatorskin.png"); break;
647       case MTEX_FADE:               texdata[i] = IMG_Load("/usr/local/share/sdb/sprites/skins/fade.png"); break;
648     }
649 
650     if (!texdata[i])
651       errorMessage(3, "FLAGRANT SYSTEM ERROR", "Could not load texture %d", i);
652 
653     glBindTexture(GL_TEXTURE_2D, textures[i]);
654     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_LINEAR);
655     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_LINEAR);
656     SDL_LockSurface(texdata[i]);
657     glTexImage2D(GL_TEXTURE_2D, 0, 4, texdata[i]->w, texdata[i]->h,
658               0, GL_RGBA, GL_UNSIGNED_BYTE, texdata[i]->pixels);
659     SDL_UnlockSurface(texdata[i]);
660   }
661 
662   // Generate blur textures
663   glGenTextures(NUM_BLUR_TEXTURES, blurTextures);
664   for (int j = 0; j < NUM_BLUR_TEXTURES; j++)
665   {
666     blurdata[j] = (unsigned int*)new GLuint[((BLUR_TEXTURE_WIDTH * BLUR_TEXTURE_HEIGHT)* 4 * sizeof(unsigned int))];
667     memset(blurdata[j], 0, ((BLUR_TEXTURE_WIDTH * BLUR_TEXTURE_HEIGHT)* 4 * sizeof(unsigned int)));
668 
669     glBindTexture(GL_TEXTURE_2D, blurTextures[j]);
670     glTexImage2D(GL_TEXTURE_2D, 0, 4, BLUR_TEXTURE_WIDTH, BLUR_TEXTURE_HEIGHT, 0, GL_RGBA, GL_UNSIGNED_BYTE, blurdata[j]);
671     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MAG_FILTER,GL_NEAREST);
672     glTexParameteri(GL_TEXTURE_2D,GL_TEXTURE_MIN_FILTER,GL_NEAREST);
673 
674     delete[] blurdata[j];
675   }
676 
677   printf("ok (loaded %d)\n", i);
678 }
679 
gameLoadModels()680 void gameLoadModels()
681 {
682   printf("  Loading models...");
683   int i;
684   for (i = 0; i < NUM_MODELS; i++)
685   {
686     switch(i)
687     {
688       case MDL_PLAYER_LEGS:   modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/player_legs" MD2_SUFFIX, MTEX_PLAYER);
689                               models[i].setTransforms(0.65, 90.0, 0.0, 90.0, 0.0, 0.0, 0.0); break;
690       case MDL_PLAYER_TORSO:  modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/player_torso" MD2_SUFFIX, MTEX_PLAYER);
691                               models[i].setTransforms(1.0, 90.0, 0.0, 90.0,  0.0, 0.0, 0.0); break;
692       case MDL_PLAYER_TORSO2: modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/player_torso" MD2_SUFFIX, MTEX_PLAYER2);
693                               models[i].setTransforms(1.0, 90.0, 0.0, 90.0,  0.0, 0.0, 0.0); break;
694       case MDL_BOX:           modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/box" MD2_SUFFIX, TEX_BOX_NORMAL);
695                               models[i].setTransforms(1.0, 90.0, 0.0, 0.0,   0.0, 0.0, 0.0); break;
696       case MDL_BARREL:        modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/barrel" MD2_SUFFIX, MTEX_BARREL);
697                               models[i].setTransforms(1.0, 90.0, 0.0, 0.0,   0.0, 0.0, 0.0); break;
698       case MDL_STEELBOX:      modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/box" MD2_SUFFIX, TEX_STEELBOX_NORMAL);
699                               models[i].setTransforms(1.0, 90.0, 0.0, 0.0,   0.0, 0.0, 0.0); break;
700       case MDL_TURRET:        modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/turret" MD2_SUFFIX, MTEX_TURRET);
701                               models[i].setTransforms(1.0, 90.0, 0.0, 90.0,   0.0, 0.0, 1.8); break;
702       case MDL_MIB:           modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/mib" MD2_SUFFIX, MTEX_MIB);
703                               models[i].setTransforms(1.0, 90.0, 0.0, 90.0,  0.0, 0.0, 2.3); break;
704       case MDL_UNARMED_GUARD: modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/unarmedguard" MD2_SUFFIX, MTEX_UNARMED_GUARD);
705                               models[i].setTransforms(0.15, 0.0, 0.0, 90.0,  0.0, 0.0, 11.0); break;
706       case MDL_ARMED_DRONE:   modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/armeddrone" MD2_SUFFIX, MTEX_ARMED_DRONE);
707                               models[i].setTransforms(1.0, 0.0, 0.0, 90.0,  0.0, 0.0, 1.5); break;
708       case MDL_HUNTER:        modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/hunter" MD2_SUFFIX, MTEX_HUNTER);
709                               models[i].setTransforms(0.25, 90.0, 0.0, 90.0,  0.0, 0.0, 11.0); break;
710       case MDL_GLADIATOR:     modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/gladiator" MD2_SUFFIX, MTEX_GLADIATOR);
711                               models[i].setTransforms(0.5, 90.0, 0.0, 90.0,  0.0, 0.0, 5.0); break;
712       case MDL_ZERO:          modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/zero" MD2_SUFFIX, MTEX_FADE);
713                               models[i].setTransforms(0.15, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0); break;
714       case MDL_ONE:           modelLoader.ImportMD2(&models[i], "/usr/local/share/sdb/models/one" MD2_SUFFIX, MTEX_FADE);
715                               models[i].setTransforms(0.15, 0.0, 0.0, 0.0,  0.0, 0.0, 0.0); break;
716     }
717   }
718   printf("ok (loaded %d)\n", i);
719 }
720 
gameLoadSounds()721 void gameLoadSounds()
722 {
723   printf("  Loading sounds...");
724 
725   int i;
726   for (i = 0; i < NUM_SOUNDS; i++)
727   {
728     switch(i)
729     {
730       case SND_WPN_RIFLE:         snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/rifle.wav"); break;
731       case SND_WPN_SHOTGUN:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/shotgun.wav"); break;
732       case SND_WPN_HEPA:          snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/hepa.wav"); break;
733       case SND_WPN_LASER:         snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/laser.wav"); break;
734       case SND_WPN_LASER2:        snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/lasercharged.wav"); break;
735       case SND_WPN_ESG:           snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/esg.wav"); break;
736       case SND_GRENADE_BOUNCE:    snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/gren-bounce.wav"); break;
737       case SND_DOOR_OPEN:         snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/dooropen.wav"); break;
738       case SND_DOOR_CLOSE:        snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/doorclose.wav"); break;
739       case SND_DOOR_UNLOCK:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/doorunlockopen.wav"); break;
740       case SND_EXPLOSION:         snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/explosion.wav"); break;
741       case SND_HEPA_EXPLOSION:    snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/hepa-explode.wav"); break;
742       case SND_GRENADE_EXPLOSION: snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/smallexplosion.wav"); break;
743       case SND_BULLET_HIT_HARD:   snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/bullet-on-hard.wav"); break;
744       case SND_BULLET_HIT_SOFT:   snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/bullet-on-soft.wav"); break;
745       case SND_PICKUP_HEALTH:     snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/pickuphealth.wav"); break;
746       case SND_PICKUP_ITEM:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/pickupitem.wav"); break;
747       case SND_JUMP:              snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/jump.wav"); break;
748       case SND_LAND:              snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/land.wav"); break;
749       case SND_RELOAD:            snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/reload.wav"); break;
750       case SND_CHANGE_WEAPON:     snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/changegun.wav"); break;
751       case SND_ROBOT_SEES:        snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/botseesyou.wav");  break;
752       case SND_ROBOT_DEATH:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/botexplode.wav");  break;
753       case SND_ROBOT_PARALYZE:    snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/botparalyze.wav");  break;
754       case SND_ROBOT_ALERT:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/botalert.wav");  break;
755 
756       case SND_EXPLODE:           snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/pexplode.wav");  break;
757       case SND_FALL:              snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/pfall.wav");  break;
758       case SND_HEAL:              snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/phealingsurface.wav");  break;
759       case SND_HURT1:             snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/phurt.wav");  break;
760       case SND_HURT2:             snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/phurt2.wav");  break;
761       case SND_HURTSURFACE:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/phurtsurface.wav");  break;
762       case SND_KILLED:            snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/pkilledbybullet.wav");  break;
763 
764       case SND_TERM_MOVE:         snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/termcursormove.wav");  break;
765       case SND_TERM_SELECT:       snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/termselect.wav");  break;
766       case SND_TERM_CLEAR:        snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/termclear.wav");  break;
767       case SND_LAUNCH:            snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/launch.wav");  break;
768       case SND_LAUNCH2:           snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/launch2.wav");  break;
769       case SND_BUTTON:            snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/button.wav");  break;
770       case SND_ALARM:             snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/alarm.wav");  break;
771       case SND_LASER_CHARGE:      snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/lasercharge.wav");  break;
772       case SND_LASER_CHARGE_LOOP: snddata[i] = Mix_LoadWAV("/usr/local/share/sdb/snd/laserchargeloop.wav");  break;
773 
774     }
775 
776     if (!snddata[i])
777       errorMessage(3, "FLAGRANT SYSTEM ERROR", "Could not load sound %d", i);
778   }
779   printf("ok (loaded %d)\n", i);
780 }
781 
gameLoadMusic()782 void gameLoadMusic()
783 {
784   printf("  Loading music...");
785 
786   int i;
787   for (i = 0; i < NUM_SONGS; i++)
788   {
789     switch(i)
790     {
791       case MUS_TITLE:           musicdata[i] = Mix_LoadMUS("/usr/local/share/sdb/snd/blackbubble2.ogg"); break;
792       case MUS_DD2:             musicdata[i] = Mix_LoadMUS("/usr/local/share/sdb/snd/dd2.ogg"); break;
793       case MUS_END:             musicdata[i] = Mix_LoadMUS("/usr/local/share/sdb/snd/loop13.ogg"); break;
794     }
795 
796     if (!musicdata[i])
797       errorMessage(3, "FLAGRANT SYSTEM ERROR", "Could not load song %d", i);
798   }
799   printf("ok (loaded %d)\n", i);
800 }
801 
802 // run through object list and allocate appropriate AI entities
loadAI()803 void loadAI()
804 {
805   if (!config.cheat_noai)
806   {
807     for (int i = 1; i < currLevelObjs.size(); i++)
808     {
809       switch(currLevelObjs[i]->Type())
810       {
811         case ENT_UNARMED_GUARD: case ENT_MIB:
812           currLevelEnemies.push_back(new KamikazeAI(i, 0, currLevelObjs[i]->State(), 30, 50, 60)); break;
813         case ENT_TURRET1:
814           currLevelEnemies.push_back(new TurretAI(i, 0, currLevelObjs[i]->State(), 0, 40, 60)); break;
815         case ENT_SLAVE_TURRET:
816           currLevelEnemies.push_back(new SlaveTurretAI(i, 0, currLevelObjs[i]->State(), 0, 40, 60)); break;
817         case ENT_ARMED_DRONE:
818           currLevelEnemies.push_back(new HunterAI(i, 0, currLevelObjs[i]->State(), 25, 40, 60,  0.5,1,  1,3,  0.1,0.3,  0.7,1, 15, 3, torad(-5))); break;
819         case ENT_HUNTER:
820           // float plm, float plr, float pdm, float pdr, float slm, float slr, float sdm, float sdr, float cdist, float aim
821           currLevelEnemies.push_back(new HunterAI(i, 0, currLevelObjs[i]->State(), 25, 30, 90,  0.75,1,  1,2,  0.1,0.3,  0.7,1, 10+frand()*5, 6, torad(15)*((rand()%2)?-1:1))); break;
822         case ENT_GLADIATOR:
823           currLevelEnemies.push_back(new HunterAI(i, 0, currLevelObjs[i]->State(), 25, 30, 40,  0.75,1,  1,2,  0.2,0,  1.0,3.0, 10+frand()*5, 0, torad(5))); break;
824 
825       }
826     }
827   }
828 }
829 
destroyAI()830 void destroyAI()
831 {
832   for (int i = 1; i < currLevelEnemies.size(); i++)
833     delete currLevelEnemies[i];
834 
835   currLevelEnemies.clear();
836 }
837 
838 // deallocates SDL_Surfaces that store texture data
gameDestroySprites()839 void gameDestroySprites()
840 {
841   for (int i = 0; i < NUM_TEXTURES; i++)
842     SDL_FreeSurface(texdata[i]);
843 
844   glDeleteTextures(NUM_TEXTURES, textures);
845   glDeleteTextures(NUM_BLUR_TEXTURES, blurTextures);
846 }
847 
gameDestroySounds()848 void gameDestroySounds()
849 {
850   for (int i = 0; i < NUM_SOUNDS; i++)
851     Mix_FreeChunk(snddata[i]);
852 }
853 
gameDestroyMusic()854 void gameDestroyMusic()
855 {
856   for (int i = 0; i < NUM_SONGS; i++)
857     Mix_FreeMusic(musicdata[i]);
858 }
859 
860 // reclaim allocated memory
gameShutdown()861 void gameShutdown()
862 {
863   printf("Shutting down...");
864   stopMusic();
865 
866   if (config.use_sound)
867   {
868     gameDestroyMusic();
869     gameDestroySounds();
870   }
871 
872   gameDestroySprites();
873   gluDeleteTess(tess);
874   for(vector<GLdouble *>::iterator i = tessAllocatedVertices.begin(); i != tessAllocatedVertices.end(); i++)
875     free(*i);
876   Mix_CloseAudio();
877   printf("ok\nEnd program\n");
878 }
879 
880 // Launch a particle (if there is a free slot available)
launchParticle(int typ,float x,float y,float height,float head,float alph,int sprite)881 void launchParticle(int typ, float x, float y, float height, float head, float alph, int sprite)
882 {
883   if (liveParticles < NUM_PARTICLES)
884   {
885     particles[liveParticles].launch(typ, x, y, height, head, alph, sprite);
886     liveParticles++;
887   }
888 }
889 
launchParticle(int typ,float x,float y,float height,float head,float alph,int sprite,float r,float g,float b)890 void launchParticle(int typ, float x, float y, float height, float head, float alph, int sprite, float r, float g, float b)
891 {
892   if (liveParticles < NUM_PARTICLES)
893   {
894     particles[liveParticles].launch(typ, x, y, height, head, alph, sprite, r, g, b);
895     liveParticles++;
896   }
897 }
898 
899 // Takes a particle out of the array.
900 // Achieved in constant time by overwriting the particle's data with that
901 // of the newest particle and decrementing liveParticles by one.
killParticle(int index)902 void killParticle(int index)
903 { particles[index] = particles[--liveParticles]; }
904 
905 // Launch a bullet (if there is a free slot available)
launchBullet(int own,int typ,float x,float y,float height,float head)906 void launchBullet(int own, int typ, float x, float y, float height, float head)
907 {
908   if (liveBullets < NUM_BULLETS)
909   {
910     bullets[liveBullets].launch(own, typ, x, y, height, head);
911     liveBullets++;
912   }
913 }
914 
killBullet(int index)915 void killBullet(int index)
916 {
917   bullets[index].kill(!bullets[index].Falling());
918   bullets[index] = bullets[--liveBullets];
919 }
920 
launchLaser(int typ,float x,float y,float height,float head)921 void launchLaser(int typ, float x, float y, float height, float head)
922 {
923   if (liveLasers < NUM_LASERS)
924   {
925     lasers[liveLasers].launch(typ, x, y, height, head);
926     liveLasers++;
927   }
928 }
929 
killLaser(int index)930 void killLaser(int index)
931 { lasers[index] = lasers[--liveLasers]; }
932 
933 // Add a force
addForce(int obj,float dur,float a,float b,bool mode)934 void addForce(int obj, float dur, float a, float b, bool mode)
935 {
936   if (liveForces < NUM_FORCES)
937   {
938     forces[liveForces].set(obj, dur, a, b, mode);
939     forces[liveForces].setActive(true);
940     liveForces++;
941   }
942 }
943 
944 // Adds a force and applies it for one frame
addInstantForce(int obj,float a,float b,bool mode)945 void addInstantForce(int obj, float a, float b, bool mode)
946 {
947   currLevelObjs[obj]->changeVel((mode == FORCE_RECT) ? Vector2D(a,b) : Vector2D(a*cos(b), a*sin(b)));
948 }
949 
950 // Removes a force
killForce(int index)951 void killForce(int index)
952 {
953   forces[index].setActive(false);
954   forces[index] = forces[--liveForces];
955 }
956 
957 // Add a shockwave
addShockwave(float x,float y,float ir,float irs,float otr,float ors,float dur,float frc,float hlth,bool ag)958 void addShockwave(float x, float y, float ir, float irs, float otr, float ors, float dur, float frc, float hlth, bool ag)
959 {
960   if (liveShockwaves < NUM_SHOCKWAVES)
961   {
962     shockwaves[liveShockwaves].set(x, y, ir, irs, otr, ors, dur, frc, hlth, ag);
963     shockwaves[liveShockwaves].setActive(true);
964     liveShockwaves++;
965   }
966 }
967 
968 // Removes a shockwave
killShockwave(int index)969 void killShockwave(int index)
970 {
971   shockwaves[index].setActive(false);
972   shockwaves[index] = shockwaves[--liveShockwaves];
973 }
974 
setScreenQuake(float amt)975 void setScreenQuake(float amt)
976 { screenQuake = amt; }
977 
addBlur(float amt,float r,float g,float b)978 void addBlur(float amt, float r, float g, float b)
979 {
980   screenBlur += amt;
981   screenBlurColor.set(r, g, b);
982 }
983 
setBlur(float amt,float r,float g,float b)984 void setBlur(float amt, float r, float g, float b)
985 {
986   screenBlur = amt;
987   screenBlurColor.set(r, g, b);
988 }
989 
setTrigger(int t)990 void setTrigger(int t)
991 {
992   //printf("Triggered %d\n", t);
993   if (t > 0 && t <= currLevel.maxTrigger)
994     triggers[t].hit = true;
995   else if (t == -1)
996     winLevel = true;
997   else if (t == -2)
998   {
999     transitioning = true;
1000     fadeOutMusic(2000);
1001     playSound(SND_LAUNCH, 3);
1002   }
1003   else if (t == -3)
1004   {
1005     PLAYER_OBJECT->kill(true);
1006     captured = true;
1007   }
1008   else if (t == -4)
1009   {
1010     setMessage("!!! CATASTROPHIC SYSTEM FAILURE !!!");
1011     fadeOutMusic(1000);
1012     systemExplode = true;
1013 
1014   }
1015 }
1016 
incrementTrigger(int t)1017 void incrementTrigger(int t)
1018 {
1019   if (t > 0 && t <= currLevel.maxTrigger)
1020   {
1021     triggers[t].hits++;
1022     //printf("Incremented %d; hits %d, hitsrequired %d\n", t, triggers[t].hits, triggers[t].hitsRequired);
1023 
1024     if (triggers[t].hits >= triggers[t].hitsRequired)
1025     { setTrigger(t); triggers[t].hits = 0; }
1026   }
1027 }
1028 
setMessage(const char * fmt,...)1029 void setMessage(const char *fmt, ...)
1030 {
1031   va_list ap;
1032   va_start(ap, fmt);
1033   vsnprintf(message, MESSAGE_LENGTH-1, fmt, ap);
1034 
1035   messageTime = MESSAGE_DELAY_TIME;
1036 }
1037 
playSound(int sound,int channel)1038 void playSound(int sound, int channel)
1039 {
1040   if (config.use_sound && config.sfx)
1041   {
1042     if (sound >= 0 && sound < NUM_SOUNDS)
1043       Mix_PlayChannel(channel, snddata[sound], 0);
1044   }
1045 }
1046 
playSoundLooped(int sound,int channel)1047 void playSoundLooped(int sound, int channel)
1048 {
1049   if (config.use_sound && config.sfx)
1050   {
1051 
1052     if (sound >= 0 && sound < NUM_SOUNDS)
1053       Mix_PlayChannel(channel, snddata[sound], INT_MAX);
1054   }
1055 }
1056 
1057 
stopSound(int channel)1058 void stopSound(int channel)
1059 {
1060   if (config.use_sound && config.sfx)
1061     Mix_HaltChannel(channel);
1062 }
1063 
startMusic(int song)1064 void startMusic(int song)
1065 {
1066   if (config.use_sound && config.music)
1067   {
1068     if (song >= 0 && song < NUM_SONGS)
1069       Mix_PlayMusic(musicdata[song], -1);
1070   }
1071 }
1072 
stopMusic()1073 void stopMusic()
1074 {
1075   if (config.use_sound && config.music)
1076   Mix_HaltMusic();
1077 }
1078 
fadeOutMusic(int ms)1079 void fadeOutMusic(int ms)
1080 {
1081   if (config.use_sound && config.music)
1082     Mix_FadeOutMusic(ms);
1083 }
1084 
fadeInMusic(int song,int ms)1085 void fadeInMusic(int song, int ms)
1086 {
1087   if (config.use_sound && config.music)
1088   {
1089     if (song >= 0 && song < NUM_SONGS)
1090       Mix_FadeInMusic(musicdata[song], -1, ms);
1091   }
1092 }
1093 
1094 
1095 // Main game loop.
playCurrentLevel(bool playInSequence)1096 int playCurrentLevel(bool playInSequence)
1097 {
1098   if (!currLevel.loaded)
1099   {
1100     printf("Crap! No level loaded!\n");
1101     return 0;
1102   }
1103   setMessage(""); messageTime = 0;
1104 
1105   glEnable(GL_BLEND);
1106   glShadeModel(GL_SMOOTH);
1107 
1108   glEnable(GL_DEPTH_TEST);
1109   glDepthFunc(GL_LEQUAL);
1110 
1111   glEnable(GL_ALPHA_TEST);
1112   glAlphaFunc(GL_EQUAL, 1.0);
1113 
1114   glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1115 
1116   bool loop = true;
1117 
1118   winLevel = captured = transitioning = false;
1119   Camera cam;
1120   BoundingBox viewRegion;
1121 
1122   // Load level entities
1123   currLevel.loadObjects();
1124   loadAI();
1125 
1126   gameSetPerspectiveMode();
1127   currLevel.start();
1128   int errnum;
1129   float mousePollTimer = 0;
1130   float healthAtFrameStart;
1131   int wpnAtFrameStart;
1132   float healthChange = 0;
1133   float damageEffectIntensity = 0;
1134   float damageEffectTimer = 0;
1135   liveParticles = liveBullets = liveShockwaves = liveLasers = 0;
1136 
1137   screenQuake = 0;
1138   screenBlur = 0;
1139   screenBlurColor.white();
1140 
1141   setMessage(currLevel.name.c_str());
1142 
1143   int blurQueueFront = 0;
1144   int blurQueueRear = 0;
1145   int blurQueueFrame = 0;
1146   float blurUpdateTimer = 0;
1147 
1148   // flush the keys
1149   keystate = SDL_GetKeyState(NULL);
1150   bool escPressed = keystate[SDLK_ESCAPE];
1151   bool paused = false;
1152   bool aborted = false;
1153   bool died = false;
1154 
1155   float blinker = 0;
1156   float weaponDisplayTimer = 0;
1157   float transitionTimer = 0;
1158   bool transitionJump = false;
1159   float systemExplodeTimer = 0.0;
1160   systemExplode = 0;
1161 
1162   P.resetCurrentLevelStats();
1163 
1164   TerminalWindow pauseMenu(15.0, 17.0, 6, 6, 2.0, 3.0, 3, 0.1, RGBAColor(0.5, 1.0, 0.76, 1.0), RGBAColor(0.5, 1.0, 0.76, 0.2));
1165   pauseMenu.setCursor(2, 3, 0, 3, ">", 3);
1166   pauseMenu.addString(1, "Abort?"); pauseMenu.addString(0, "");
1167   pauseMenu.addString(0, "  yes");  pauseMenu.addString(0, "  no");
1168   pauseMenu.addString(0, "");       pauseMenu.addString(0, "(sdb)");
1169   pauseMenu.deactivate();
1170 
1171   TerminalWindow deadMenu(15.0, 17.0, 6, 16, 2.0, 3.0, 3, 0.1, RGBAColor(0.5, 1.0, 0.76, 1.0), RGBAColor(0.5, 1.0, 0.76, 0.2));
1172   deadMenu.setCursor(2, 3, 0, 2, ">", 3);
1173   deadMenu.deactivate();
1174 
1175   int returnVal = LEVEL_QUIT;
1176 
1177   SDL_WarpMouse(config.xres/2, config.yres/2);
1178   printf("Start\n");
1179 
1180   while (loop && !winLevel)
1181   {
1182     timer.reset();
1183 
1184     // Error checking
1185     errnum = glGetError();
1186     if (errnum != GL_NO_ERROR)
1187       { errorMessage(9, "FLAGRANT OPENGL ERROR", "CODE %d: %s", errnum, gluErrorString(errnum)); }
1188 
1189     // Event checking loop
1190     while (SDL_PollEvent(&event))
1191     {
1192       switch (event.type)
1193       {
1194         case SDL_QUIT:
1195           loop = false;
1196           break;
1197       }
1198     }
1199 
1200     // Key checking loop
1201     keystate = SDL_GetKeyState(NULL);
1202     modstate = SDL_GetModState();
1203 
1204     wpnAtFrameStart = PLAYER_OBJECT->CurrWeapon();
1205 
1206     if (!(paused || died || transitioning))
1207     {
1208       mousePollTimer += timer.dT();
1209       if (mousePollTimer >= 0.05)
1210       {
1211         mouseButtons = SDL_GetRelativeMouseState(&mouse_dX, &mouse_dY); mousePollTimer = 0;
1212       }
1213 
1214       P.update();
1215 
1216       if (keystate[SDLK_ESCAPE])
1217       {
1218         if (!(escPressed || PLAYER_OBJECT->Dying()))
1219         {
1220           paused = true;
1221           if (config.mgrab)
1222             SDL_WM_GrabInput(SDL_GRAB_OFF);
1223           pauseMenu.activate(keystate);
1224           escPressed = true;
1225         }
1226       }
1227       else
1228         escPressed = false;
1229 
1230       if (config.cheat_skip && keystate[SDLK_F6])
1231         setTrigger(-1);
1232 
1233       SCREENSHOT_CHECK
1234 
1235       /*** Unmodifiable keybindings ***/
1236       // Select a weapon
1237       for (int i = 0; i < NUM_PLAYER_WEAPONS; i++)
1238         if (keystate[SDLK_1+i])
1239           PLAYER_OBJECT->selectWeapon(i);
1240 
1241       if (config.cheat_zoom)
1242       {
1243         if (keystate[SDLK_h])
1244           cam.change(0.0, 0.0, -((modstate & KMOD_LSHIFT) ? 500.0 : 50.0)*timer.dT(), 0.0);
1245         else if (keystate[SDLK_n])
1246           cam.change(0.0, 0.0, ((modstate & KMOD_LSHIFT) ? 500.0 : 50.0)*timer.dT(), 0.0);
1247         else if (keystate[SDLK_j])
1248           cam.setHeight(CAMERA_DEFAULT_HEIGHT);
1249       }
1250 
1251       if (keystate[SDLK_F2])
1252       {
1253         float foo1 = 0.0, foo2 = 0.0;
1254         configScreen(true, foo1, foo2);
1255         currLevel.setBackgroundColor();
1256         escPressed = keystate[SDLK_ESCAPE];
1257       }
1258 
1259       // Game logic
1260       healthAtFrameStart = PLAYER_OBJECT->Health();
1261 
1262       for (int i = 0; i <= currLevel.maxTrigger; i++)
1263         triggers[i].hit = false;
1264 
1265       if (systemExplode)
1266       {
1267         systemExplodeTimer += timer.dT();
1268         switch(systemExplode)
1269         {
1270           case 1:
1271             if (systemExplodeTimer > 0.0)
1272             { setTrigger(50); systemExplode++; setMessage("!!! CATASTROPHIC SYSTEM FAILURE !!!"); }
1273             break;
1274           case 2:
1275             if (systemExplodeTimer > 1.0)
1276             { setTrigger(51); systemExplode++; setMessage("!!! CATASTROPHIC SYSTEM FAILURE !!!"); }
1277             break;
1278           case 3:
1279             if (systemExplodeTimer > 2.0)
1280             { setTrigger(52); systemExplode++; setMessage("!!! CA&A&TR34#$ *** sDFd#$!@#34 !?#"); }
1281             break;
1282           case 4:
1283             if (systemExplodeTimer > 2.5)
1284             { setTrigger(53); systemExplode++; setMessage("!!@ #cAtE4%34#$ SYET#$3#$!SFIEA!!@"); }
1285             break;
1286           case 5:
1287             if (systemExplodeTimer > 3.0)
1288             { setTrigger(54); systemExplode++; setMessage("!!@ #cAt!@!~@4#$!@(%H*333333364A!"); }
1289             break;
1290           case 6:
1291             if (systemExplodeTimer > 3.5)
1292             { setTrigger(55); systemExplode++; setMessage("** (* ()*D()*io SF*(ioj45o25364A!"); }
1293             break;
1294           case 7:
1295             if (systemExplodeTimer > 4.0)
1296             { setTrigger(56); systemExplode++; setMessage("** F4AW T* YU6WU858I6EZR G)*(a89*"); }
1297             break;
1298           case 8:
1299             if (systemExplodeTimer > 4.5)
1300             { setTrigger(57); systemExplode++; setMessage("*********************************"); }
1301             break;
1302           case 9:
1303             if (systemExplodeTimer > 5.0)
1304             { setTrigger(-1); }
1305             break;
1306         }
1307       }
1308 
1309 
1310       // Reset collision flags
1311       for (int k = 0; k < currLevelObjs.size(); k++)
1312       {
1313         currLevelObjs[k]->resetCollided();
1314         currLevelObjs[k]->setObjectCollisionDetectOnOff(distSquared(currLevelObjs[k]->Pos(), PLAYER_OBJECT->Pos()) < OBJECT_CD_THRESHOLD);
1315       }
1316 
1317       for (int i = 0; i < currLevelObjs.size(); i++)
1318         currLevelObjs[i]->update();
1319 
1320       // Update objects
1321       for (int i = 0; i < currLevelEnemies.size(); i++)
1322         currLevelEnemies[i]->update();
1323 
1324       currLevel.updateDynamicGeometry();
1325 
1326       // Reveal masks
1327       for (vector<Surface>::iterator i = currLevel.mask.begin(); i != currLevel.mask.end(); i++)
1328       {
1329         if (config.cheat_nomasks)
1330           i->startFade(1);
1331         else
1332         {
1333           if (i->pointInBB(PLAYER_OBJECT->Pos()) && i->pointInPolygon(PLAYER_OBJECT->Pos()))
1334             i->startFade(1);
1335           else if (i->Reobscure())
1336             i->startFade(-1);
1337         }
1338       }
1339 
1340       for (int i = 0; i < liveBullets; i++)
1341       for (int j = 0; j < currLevelObjs.size(); j++)
1342       {
1343         if (currLevelObjs[j]->isActive() && !IS_POWERUP(currLevelObjs[j]->Type()))
1344         {
1345           if ((bullets[i].Owner() == j && bullets[i].Live()) || bullets[i].Owner() != j)
1346           {
1347             Vector2D intersect;
1348             if (circleSegmentIntersection(currLevelObjs[j]->Pos(),
1349               objType[currLevelObjs[j]->Type()].boundRadius,
1350               bullets[i].Pos(),
1351               bullets[i].Pos() + (bullets[i].Vel() * timer.dT()), &intersect))
1352             {
1353               currLevelObjs[j]->setDirectionAlert(atan2(bullets[i].PosY() - currLevelObjs[j]->PosY(),
1354                 bullets[i].PosX() - currLevelObjs[j]->PosX()), ALERT_SHOT);
1355 
1356               if (bullets[i].Type() == BLT_GRENADE)
1357               {
1358                 Vector2D diff = currLevelObjs[j]->Pos() - bullets[i].Pos();
1359                 diff.normalize();
1360                 Vector2D tmpVel = bullets[i].Vel();
1361                 tmpVel -= diff * (diff * bullets[i].Vel()) * 2;
1362                 bullets[i].setHeading(atan2(tmpVel.c[Y], tmpVel.c[X]));
1363               }
1364               else
1365               {
1366                 LAUNCH_MULTI_PARTICLES(5, PART_PARTICLE, intersect.c[X],intersect.c[Y],0,torad(rand()%360),1.0,0);
1367                 currLevelObjs[j]->changeForceVel(Vector2D(bltType[bullets[i].BType()].impactForce*cos(bullets[i].Heading()),
1368                 bltType[bullets[i].BType()].impactForce*sin(bullets[i].Heading())));
1369 
1370                 currLevelObjs[j]->damage(-bltType[bullets[i].BType()].damage, DAMAGE_BULLET);
1371 
1372                 killBullet(i);
1373                 i--;
1374               }
1375             }
1376           }
1377         }
1378       }
1379 
1380       // Update transient stuff
1381       for (int i = 0; i < liveBullets; i++)
1382       {
1383         bullets[i].update();
1384         if (bullets[i].toBeDestroyed())
1385         { killBullet(i); i--; }
1386       }
1387       for (int i = 0; i < liveLasers; i++)
1388       {
1389         lasers[i].update();
1390         if (lasers[i].toBeDestroyed())
1391         { killLaser(i); i--; }
1392       }
1393 
1394       for (int i = 0; i < liveForces; i++)
1395       {
1396         forces[i].apply();
1397         if (forces[i].toBeDestroyed())
1398         { killForce(i); i--; }
1399       }
1400 
1401       for (int i = 0; i < liveShockwaves; i++)
1402       {
1403         shockwaves[i].update();
1404         if (shockwaves[i].toBeDestroyed())
1405         { killShockwave(i); i--; }
1406       }
1407 
1408       // Update effects
1409 
1410       if (messageTime > 0)
1411         messageTime -= timer.dT();
1412       else if (messageTime < 0)
1413         messageTime = 0;
1414 
1415       if (!PLAYER_OBJECT->isActive())
1416       {
1417         died = true;
1418 
1419         if (captured)
1420           deadMenu.addString(1, "Captured!");
1421         else
1422           deadMenu.addString(1, "You have died.");
1423 
1424         deadMenu.addString(0, "");
1425         deadMenu.addString(0, "  restart level"); deadMenu.addString(0, "  return to menu");
1426         deadMenu.addString(0, "");                deadMenu.addString(0, "(sdb)");
1427         deadMenu.activate();
1428 
1429       }
1430 
1431       P.gameTime += timer.dT();
1432     }
1433     else if (paused)
1434     {
1435       setBlur(1.0, 0.0, 1.0, 0.0);
1436       switch(pauseMenu.update(keystate))
1437       {
1438         case 2: // abort
1439           PLAYER_OBJECT->setHealth(0);
1440           aborted = true;
1441         case 3:
1442           paused = false;
1443           pauseMenu.deactivate();
1444           setBlur(0.0, 0.0, 0.0, 0.0);
1445           if (config.mgrab)
1446             SDL_WM_GrabInput(SDL_GRAB_ON);
1447           break;
1448       }
1449     }
1450     else if (died)
1451     {
1452       if (aborted)
1453       { returnVal = LEVEL_QUIT; loop = false; }
1454       else
1455       {
1456         setBlur(1.0, 1.0, 0.0, 0.0);
1457         switch(deadMenu.update(keystate))
1458         {
1459           case 2: // restart
1460             returnVal = LEVEL_LOSE;
1461             loop = false;
1462             setBlur(0.0, 0.0, 0.0, 0.0);
1463             break;
1464           case 3:
1465             returnVal = LEVEL_QUIT;
1466             loop = false;
1467             aborted = true;
1468             setBlur(0.0, 0.0, 0.0, 0.0);
1469             break;
1470         }
1471       }
1472     }
1473     else if (transitioning)
1474     {
1475       transitionTimer += timer.dT();
1476 
1477       if (transitionTimer > 1.0 && !transitionJump)
1478       {
1479         LAUNCH_MULTI_PARTICLES((int)(2*(transitionTimer-1.0)), PART_PARTICLE_SPARK,
1480         PLAYER_OBJECT->PosX(), PLAYER_OBJECT->PosY(),0.5,torad(rand()%360),1.0,0);
1481       }
1482 
1483       if (transitionTimer > 2.0 && !transitionJump)
1484       {
1485         LAUNCH_MULTI_PARTICLES((int)(0.5*(transitionTimer-3.0)), PART_LASER_SPARK2,
1486         PLAYER_OBJECT->PosX(), PLAYER_OBJECT->PosY(),0.5,torad(rand()%360),1.0,0);
1487       }
1488 
1489       if (transitionTimer > 10.0)
1490       {
1491         cam.change(0.0, 0.0, (5000.0-MIN((transitionTimer-10)*2000, 4950))*timer.dT(), 0.0);
1492 
1493         if (!transitionJump)
1494         {
1495           for (int i = 0; i < liveParticles; i++)
1496             killParticle(i);
1497 
1498           LAUNCH_MULTI_PARTICLES(NUM_PARTICLES, PART_BIT,
1499           PLAYER_OBJECT->PosX()+(frand()*50-25), PLAYER_OBJECT->PosY()+(frand()*50-25), frand()*8000,0.0,1.0,0);
1500           transitionJump = true;
1501           playSound(SND_LAUNCH2, 3);
1502 
1503           setScreenQuake(0);
1504         }
1505 
1506         if (cam.Height() >= 6850)
1507         {
1508           fadeInMusic(MUS_DD2, 2000);
1509           setTrigger(-1);
1510         }
1511       }
1512 
1513 
1514 
1515       if (!transitionJump)
1516       {
1517         setBlur(1.0, 1.0, 1.0, 1.0);
1518         setScreenQuake(transitionTimer*0.5);
1519       }
1520       else
1521         setBlur(2.0, 0.0, 1.0, 0.0);
1522     }
1523 
1524     if (screenQuake > 0)
1525     {
1526       screenQuake -= SCREEN_QUAKE_DECAY * timer.dT();
1527       if (screenQuake <= 0) screenQuake = 0;
1528     }
1529 
1530     if (!(paused || died))
1531     {
1532       for (int i = 0; i < liveParticles; i++)
1533       {
1534         particles[i].update();
1535         if (particles[i].toBeDestroyed())
1536         { killParticle(i); i--; }
1537       }
1538 
1539       if (screenBlur > 0)
1540       {
1541         if (PLAYER_OBJECT->Health() > 0)
1542           screenBlur -= SCREEN_BLUR_DECAY * timer.dT();
1543 
1544         if (screenBlur <= 0) screenBlur = 0;
1545         blurUpdateTimer += timer.dT();
1546       }
1547     }
1548 
1549     if (PLAYER_OBJECT->Health() > 0)
1550     {
1551       blinker += timer.dT();
1552       if (blinker > 0.6)
1553         blinker = 0.0;
1554     }
1555     else
1556       blinker = 0.0;
1557 
1558     if (weaponDisplayTimer > 0)
1559     {
1560       weaponDisplayTimer -= timer.dT();
1561       if (weaponDisplayTimer <= 0.0)
1562         weaponDisplayTimer = 0.0;
1563     }
1564 
1565     // Draw scene
1566     // If we're going to blur, we make two passes over the scene;
1567     // first to render to the texture, then to render to the framebuffer
1568     for (int m = ((blurUpdateTimer < BLUR_DELAY && screenBlur && config.blur > 0) ? 1 : 0); m < 2; m++)
1569     {
1570       glClear(GL_COLOR_BUFFER_BIT | GL_DEPTH_BUFFER_BIT);
1571       glLoadIdentity();
1572 
1573       if (m == 0)
1574       {
1575         glViewport(0, 0, BLUR_TEXTURE_WIDTH, BLUR_TEXTURE_HEIGHT);
1576         blurUpdateTimer = 0;
1577       }
1578 
1579       // Do camera
1580       if (!config.cheat_zoom && transitionTimer < 10.0)
1581         cam.setHeight(P.cameraZoom());
1582 
1583       if (P.HeadingLock())
1584       {
1585         cam.setRotation(-todeg(PLAYER_OBJECT->LookAngle())+90);
1586         cam.setPos(PLAYER_OBJECT->PosX()+(cos(PLAYER_OBJECT->LookAngle())*15),
1587                 PLAYER_OBJECT->PosY()+(sin(PLAYER_OBJECT->LookAngle())*15));
1588       }
1589       else
1590       {
1591         cam.setRotation(0);
1592         cam.setPos(PLAYER_OBJECT->PosX(), PLAYER_OBJECT->PosY());
1593       }
1594       if (screenQuake)
1595         cam.change(frand()*screenQuake*2-screenQuake,frand()*screenQuake*2-screenQuake, 0, 0);
1596 
1597       cam.apply();
1598 
1599       viewRegion.reset();
1600       // fudged
1601       viewRegion.addPoint(cam.Pos()-Vector2D(cam.Height()*SCREEN_ASPECT, cam.Height()));
1602       viewRegion.addPoint(cam.Pos()+Vector2D(cam.Height()*SCREEN_ASPECT, cam.Height()));
1603 
1604       // Draw foreground. Make two passes (first normally, then second
1605       // with the depth buffer read-only) to ensure both proper depth-occlusion results
1606       // and proper blending.
1607       glEnable(GL_ALPHA_TEST);
1608       glAlphaFunc(GL_EQUAL, 1.0);
1609       drawScene(viewRegion);
1610       glAlphaFunc(GL_NOTEQUAL, 1.0);
1611       glDepthMask(GL_FALSE);
1612       drawScene(viewRegion);
1613       glDepthMask(GL_TRUE);
1614       glDisable(GL_ALPHA_TEST);
1615 
1616       // Blur
1617       if (m == 0)
1618       {
1619         glEnable(GL_TEXTURE_2D);
1620         glBindTexture(GL_TEXTURE_2D, blurTextures[blurQueueRear]);
1621         glCopyTexImage2D(GL_TEXTURE_2D, 0, GL_RGBA, 0, 0, BLUR_TEXTURE_WIDTH, BLUR_TEXTURE_HEIGHT, 0);
1622         // reset the viewport
1623         glViewport(0, 0, config.xres, config.yres);
1624 
1625         // cycle the queue
1626         blurQueueRear++;
1627         if (blurQueueRear == NUM_BLUR_TEXTURES)
1628         {
1629           blurQueueFront++;
1630           blurQueueRear = 0;
1631         }
1632 
1633         if (blurQueueRear == blurQueueFront)
1634           blurQueueFront++;
1635 
1636         if (blurQueueFront == NUM_BLUR_TEXTURES)
1637           blurQueueFront = 0;
1638       }
1639     }
1640     // Draw overlay
1641 
1642     gameSetOrthoMode(100);
1643     glDisable(GL_DEPTH_TEST);
1644 
1645     if (!transitionJump)
1646     {
1647       if (config.fps)
1648       {
1649         glColor3f(0.0, 1.0, 0.75);
1650         font.printf(CENTERED, SCREEN_TOP-2.0, 2.0, 1.6, "%03d FPS", timer.FPS());
1651       }
1652 
1653       if (config.cheating())
1654       {
1655         glColor3f(1.0, 0.0, 0.0);
1656         font.printf(-98.0, SCREEN_TOP-2.0, 3.0, 2.0, "* SYSTEM BACKDOOR ACTIVE *");
1657       }
1658 
1659       if (config.info)
1660         objectStats(0);
1661     }
1662 
1663     glEnable(GL_TEXTURE_2D);
1664 
1665     // Apply the blur effect
1666     if (screenBlur > 0 && config.blur)
1667     {
1668       screenBlurColor.a = 1.0/NUM_BLUR_TEXTURES;
1669       screenBlurColor.a *= MIN(1.0, screenBlur);
1670       screenBlurColor.apply();
1671 
1672       int blurIterator = blurQueueFront;
1673       for (int blur = 0; blur < NUM_BLUR_TEXTURES; blur++)
1674       {
1675         glBindTexture(GL_TEXTURE_2D, blurTextures[blurIterator]);
1676 
1677         if (PLAYER_OBJECT->Health() > 0)
1678           glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1679 
1680         glBegin(GL_QUADS);
1681           glTexCoord2f(0.0, 1.0);
1682           glVertex2f(-100, 100/SCREEN_ASPECT);
1683           glTexCoord2f(1.0, 1.0);
1684           glVertex2f( 100, 100/SCREEN_ASPECT);
1685           glTexCoord2f(1.0, 0.0);
1686           glVertex2f( 100,-100/SCREEN_ASPECT);
1687           glTexCoord2f(0.0, 0.0);
1688           glVertex2f(-100,-100/SCREEN_ASPECT);
1689         glEnd();
1690         glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1691 
1692         blurIterator++;
1693         if (blurIterator == NUM_BLUR_TEXTURES)
1694           blurIterator = 0;
1695       }
1696     }
1697 
1698 
1699     if (!transitionJump)
1700     {
1701       // Health LED display
1702       glBlendFunc(GL_SRC_ALPHA, GL_ONE);
1703       (PLAYER_OBJECT->Health() >= 26) ? glColor4f(0.2, 0.2, 1.0, 1.0) : glColor4f(1.0, 0.2, 0.2, 1.0);
1704       font.LEDprintf(-95.0, -SCREEN_TOP+6, 6.0, 6.0, "===");
1705       (PLAYER_OBJECT->Health() >= 26) ? glColor4f(0.3, 0.3, 1.0, 1.0) : glColor4f(1.0, 0.0, 0.0, (float)(blinker <= 0.3));
1706       if (PLAYER_OBJECT->Health() > 0.0)
1707         font.LEDprintf(-95.0, -SCREEN_TOP+6, 6.0, 6.0, "%3.f", ceil(PLAYER_OBJECT->Health()));
1708       else
1709         font.LEDprintf(-95.0, -SCREEN_TOP+6, 6.0, 6.0, "<<<");
1710 
1711       if (PLAYER_OBJECT->Augmented())
1712       {
1713         glColor4f(1.0, 1.0, 0.0, 1.0);
1714         if (PLAYER_OBJECT->Health() > 0.0)
1715           font.LEDprintf(-95.0, -SCREEN_TOP+6, 6.0, 6.0, "%3.f", ceil(PLAYER_OBJECT->Health()));
1716         else
1717           font.LEDprintf(-95.0, -SCREEN_TOP+6, 6.0, 6.0, "<<<");
1718 
1719         setBlur(0.5, 0.0, 1.0, 0.0);
1720       }
1721 
1722       // Weapons LED display
1723       glColor4f(0.2, 0.2, 1.0, 1.0);
1724 
1725       font.LEDprintf(64, -SCREEN_TOP+6, 6.0, 6.0, "======");
1726       if (PLAYER_OBJECT->Health() > 0 && PLAYER_OBJECT->Wpn()->Type() != WPN_NONE)
1727       {
1728         font.printf(64, -SCREEN_TOP+14.0, 2.8, 2.4, "%16s", wpnType[PLAYER_OBJECT->Wpn()->Type()].name);
1729 
1730         if (PLAYER_OBJECT->Wpn()->Type() != WPN_BLASTER)
1731         {
1732           if (!PLAYER_OBJECT->Wpn()->isReloading())
1733           {
1734             font.LEDprintf(64, -SCREEN_TOP+6, 6.0, 6.0, "%2d%s%3d",
1735             PLAYER_OBJECT->Wpn()->AmmoInClip(), (PLAYER_OBJECT->Wpn()->Ready()) ? ":" : " ", PLAYER_OBJECT->Wpn()->ReserveAmmo());
1736 
1737             if (PLAYER_OBJECT->Wpn()->Type() == WPN_LASER)
1738             {
1739               glColor4f(1.0, 1.0, 1.0, (PLAYER_OBJECT->Wpn()->getLaserCharge() < 1.0)?PLAYER_OBJECT->Wpn()->getLaserCharge():(float)(blinker <= 0.3));
1740               font.LEDprintf(64, -SCREEN_TOP+6, 6.0, 6.0, "%2d%s%3d",
1741               PLAYER_OBJECT->Wpn()->AmmoInClip(), (PLAYER_OBJECT->Wpn()->Ready()) ? ":" : " ", PLAYER_OBJECT->Wpn()->ReserveAmmo());
1742             }
1743           }
1744           else
1745           {
1746             glColor4f(0.2, 0.2, 1.0, (float)(blinker <= 0.3));
1747             font.LEDprintf(64, -SCREEN_TOP+6, 6.0, 6.0, "<< <<<");
1748           }
1749         }
1750       }
1751 
1752       // Obtained weapons display
1753       if (wpnAtFrameStart != PLAYER_OBJECT->CurrWeapon() && PLAYER_OBJECT->CurrWeapon() > 0)
1754         weaponDisplayTimer = 2.0;
1755 
1756       glColor4f(0.2, 0.2, 1.0, MIN(weaponDisplayTimer, 1.0));
1757       font.LEDprintf(50, -SCREEN_TOP+14.0, 2.0, 2.0, "======");
1758       font.LEDprintf(50, -SCREEN_TOP+14.0, 2.0, 2.0, "%c%c%c%c%c%c",PLAYER_OBJECT->weaponState(1),
1759       PLAYER_OBJECT->weaponState(2),PLAYER_OBJECT->weaponState(3),PLAYER_OBJECT->weaponState(4),
1760       PLAYER_OBJECT->weaponState(5),PLAYER_OBJECT->weaponState(6));
1761       glColor4f(0.5, 0.5, 1.0, MIN(weaponDisplayTimer, 1.0));
1762       if (PLAYER_OBJECT->CurrWeapon() > 0)
1763         font.LEDprintf(50+2.0*(PLAYER_OBJECT->CurrWeapon()-1), -SCREEN_TOP+14.0, 2.0, 2.0, "%d", PLAYER_OBJECT->CurrWeapon()+1);
1764 
1765       // Keys display
1766       glColor4f(0.4, 0.0, 1.0, 0.7);
1767       font.LEDprintf(-77.1, -SCREEN_TOP+3.5, 4.5, 4.5, ">");
1768       font.LEDprintf(-77.1, -SCREEN_TOP+3.5, 4.5, 4.5, "%c", PLAYER_OBJECT->keyState(3));
1769       glColor4f(0.6, 1.0, 0.0, 0.7);
1770       font.LEDprintf(-77, -SCREEN_TOP+6, 4.5, 4.5, ">");
1771       font.LEDprintf(-77, -SCREEN_TOP+6, 4.5, 4.5, "%c", PLAYER_OBJECT->keyState(2));
1772       glColor4f(1.0, 0.0, 0.0, 0.7);
1773       font.LEDprintf(-76.9, -SCREEN_TOP+8.5, 4.5, 4.5, ">");
1774       font.LEDprintf(-76.9, -SCREEN_TOP+8.5, 4.5, 4.5, "%c", PLAYER_OBJECT->keyState(1));
1775 
1776       // Timer display
1777       double foo;
1778       glColor4f(0.2, 0.2, 1.0, 1.0);
1779       font.LEDprintf(CENTERED, -SCREEN_TOP+6, 3.0, 3.0, "==:==;==");
1780       font.LEDprintf(CENTERED, -SCREEN_TOP+6, 3.0, 3.0, "%2d:%02d;%02d", (int)(P.gameTime/60),
1781       (int)fmod(P.gameTime, 60), (int)(modf(P.gameTime, &foo)*100));
1782 
1783 
1784       // Message display
1785       if (message != "" && messageTime > 0)
1786       {
1787         glColor4f(0.2, 0.2, 1.0, messageTime/MESSAGE_DELAY_TIME);
1788         font.printf(-95.0, -SCREEN_TOP+14.0, 3.7, 3.2, "%s", message);
1789       }
1790       glBlendFunc(GL_SRC_ALPHA, GL_ONE_MINUS_SRC_ALPHA);
1791 
1792 
1793       // Draw damage/healing effects
1794       if (healthAtFrameStart != PLAYER_OBJECT->Health())
1795       {
1796         healthChange = PLAYER_OBJECT->Health() - healthAtFrameStart;
1797         if (damageEffectTimer < 0.5)
1798         {
1799           damageEffectTimer = 1.0;
1800           if (healthChange < 0 && healthChange >= -5)
1801             damageEffectIntensity = 10;
1802           else if (healthChange < -5 && healthChange >= -15)
1803             damageEffectIntensity = 8;
1804           else if (healthChange < -15 && healthChange >= -30)
1805             damageEffectIntensity = 6;
1806           else if (healthChange < -30 && healthChange >= -60)
1807             damageEffectIntensity = 4;
1808           else if (healthChange < -30 && healthChange >= -60)
1809             damageEffectIntensity = 2;
1810           else if (healthChange < -60)
1811             damageEffectIntensity = 1;
1812 
1813           damageEffectIntensity *= PLAYER_OBJECT->Health()/100.0;
1814 
1815           if (healthChange > 0)
1816           damageEffectIntensity = 8;
1817         }
1818 
1819         if (PLAYER_OBJECT->Health() == 0 || PLAYER_OBJECT->Dying())
1820         { damageEffectTimer = 0.0; damageEffectIntensity = 0.0; }
1821 
1822         if (damageEffectIntensity < 0.25)
1823           damageEffectIntensity = 0.25;
1824       }
1825 
1826       if (PLAYER_OBJECT->Health()<= 0)
1827         healthChange = -100;
1828 
1829       if (damageEffectTimer > 0.0)
1830       {
1831         (healthChange < 0) ? glColor4f(1.0, 0.0, 0.0, damageEffectTimer) : glColor4f(0.04, 0.3, 1.0, damageEffectTimer);
1832 
1833         for (float i = 0; i < SCREEN_TOP; i += damageEffectIntensity)
1834         {
1835           glBegin(GL_LINES);
1836             glVertex2f(-100, i);
1837             glVertex2f(100, i);
1838             glVertex2f(-100, -i);
1839             glVertex2f(100, -i);
1840 
1841           glEnd();
1842         }
1843         damageEffectTimer -= timer.dT();
1844       }
1845     }
1846 
1847 
1848     if (transitionTimer > 0.0 && transitionTimer < 10.0)
1849     {
1850       glColor4f(0.0, 1.0, 0.0, 1.0);
1851       font.printf(CENTERED, 10, 5.4, 4.4, "NANO-ACTIVATION SEQUENCE COMMENCING IN");
1852 
1853       if (transitionTimer > 1.0)
1854         font.printf(CENTERED, 0, 7.4, 6.4, "%d", (int)(11-transitionTimer));
1855     }
1856 
1857 
1858     if (paused) pauseMenu.draw();
1859     if (died)   deadMenu.draw();
1860 
1861     // Return to perspective projection
1862     glEnable(GL_DEPTH_TEST);
1863     glMatrixMode(GL_PROJECTION);
1864     glPopMatrix();
1865     glMatrixMode(GL_MODELVIEW);
1866     SDL_GL_SwapBuffers();
1867 
1868     timer.update();
1869 
1870     if (config.framecap >= 15)
1871     {
1872       while (timer.dT() < 1.0/config.framecap)
1873         timer.update();
1874     }
1875   }
1876 
1877   if (aborted)
1878     returnVal = LEVEL_QUIT;
1879   else
1880   {
1881     if (PLAYER_OBJECT->Health() <= 0)
1882       returnVal = LEVEL_LOSE;
1883     else if (winLevel)
1884     {
1885       levelWinScreen(playInSequence);
1886       returnVal = LEVEL_WIN;
1887     }
1888   }
1889 
1890   // deallocate objects
1891   destroyAI();
1892   currLevelObjs.clear();
1893   currLevelEnemies.clear();
1894   return returnVal;
1895 }
1896 
drawScene(BoundingBox viewRegion)1897 void drawScene(BoundingBox viewRegion)
1898 {
1899   if (config.culling)
1900   {
1901     int cMinX,cMinY,cMaxX,cMaxY, oCX, oCY;
1902     currLevel.cellNumber(viewRegion.min, &cMinX, &cMinY);
1903     currLevel.cellNumber(viewRegion.max, &cMaxX, &cMaxY);
1904 
1905     for (int y = cMinY; y <= cMaxY; y++)
1906       for (int x = cMinX; x <= cMaxX; x++)
1907         if (x >= 0 && x < currLevel.gridCols && y >= 0 && y < currLevel.gridRows)
1908           currLevel.drawCell(y*currLevel.gridCols+x);
1909 
1910     drawObjectShadows(cMinX, cMinY, cMaxX, cMaxY);
1911     for (int i = 0; i < currLevelObjs.size(); i++)
1912     {
1913       currLevel.cellNumber(currLevelObjs[i]->Pos(), &oCX, &oCY);
1914       if (oCX >= cMinX && oCX <= cMaxX && oCY >= cMinY && oCY <= cMaxY)
1915         currLevelObjs[i]->draw();
1916     }
1917 
1918     for (int i = 0; i < liveParticles; i++)
1919     {
1920       currLevel.cellNumber(particles[i].Pos(), &oCX, &oCY);
1921       if (oCX >= cMinX && oCX <= cMaxX && oCY >= cMinY && oCY <= cMaxY)
1922         particles[i].draw();
1923     }
1924     for (int i = 0; i < liveBullets; i++)
1925     {
1926       currLevel.cellNumber(bullets[i].Pos(), &oCX, &oCY);
1927       if (oCX >= cMinX && oCX <= cMaxX && oCY >= cMinY && oCY <= cMaxY)
1928         bullets[i].draw();
1929     }
1930 
1931     for (int y = cMinY; y <= cMaxY; y++)
1932       for (int x = cMinX; x <= cMaxX; x++)
1933         if (x >= 0 && x < currLevel.gridCols && y >= 0 && y < currLevel.gridRows)
1934           currLevel.resetCell(y*currLevel.gridCols+x);
1935   }
1936   else
1937   {
1938     currLevel.drawStaticGeometryDL();
1939     currLevel.drawDynamicGeometry();
1940 
1941     drawObjectShadows(0, 0, 0, 0);
1942     for (int i = 0; i < currLevelObjs.size(); i++)
1943     {
1944       if (config.cheat_zoom || distSquared(currLevelObjs[i]->Pos(), PLAYER_OBJECT->Pos()) < OBJECT_DRAW_THRESHOLD)
1945         currLevelObjs[i]->draw();
1946     }
1947 
1948     for (int i = 0; i < liveParticles; i++)
1949       particles[i].draw();
1950 
1951     for (int i = 0; i < liveBullets; i++)
1952       bullets[i].draw();
1953 
1954     for (int i = 0; i < liveLasers; i++)
1955       lasers[i].draw();
1956 
1957   }
1958 
1959   if (config.paths)
1960     drawPaths();
1961 
1962   /*for (int i = 0; i < liveShockwaves; i++)
1963     shockwaves[i].draw();*/
1964 }
1965 
drawObjectShadows(int cMinX,int cMinY,int cMaxX,int cMaxY)1966 void drawObjectShadows(int cMinX, int cMinY, int cMaxX, int cMaxY)
1967 {
1968   if (config.culling)
1969   {
1970     int oCX, oCY;
1971     for (int i = 0; i < currLevelObjs.size(); i++)
1972     {
1973       currLevel.cellNumber(currLevelObjs[i]->Pos(), &oCX, &oCY);
1974       if (oCX >= cMinX && oCX <= cMaxX && oCY >= cMinY && oCY <= cMaxY)
1975         currLevelObjs[i]->drawShadow();
1976     }
1977     for (int i = 0; i < liveParticles; i++)
1978     {
1979       currLevel.cellNumber(particles[i].Pos(), &oCX, &oCY);
1980       if (oCX >= cMinX && oCX <= cMaxX && oCY >= cMinY && oCY <= cMaxY)
1981         particles[i].drawShadow();
1982     }
1983     for (int i = 0; i < liveBullets; i++)
1984     {
1985       currLevel.cellNumber(bullets[i].Pos(), &oCX, &oCY);
1986       if (oCX >= cMinX && oCX <= cMaxX && oCY >= cMinY && oCY <= cMaxY)
1987         bullets[i].drawShadow();
1988     }
1989   }
1990   else
1991   {
1992     for (int i = 0; i < currLevelObjs.size(); i++)
1993       currLevelObjs[i]->drawShadow();
1994 
1995     for (int i = 0; i < liveParticles; i++)
1996       particles[i].drawShadow();
1997 
1998     for (int i = 0; i < liveBullets; i++)
1999       bullets[i].drawShadow();
2000   }
2001 }
2002 
timerFunc()2003 double Timer::timerFunc()
2004 {
2005   #ifdef _WIN32
2006   LARGE_INTEGER count, freq;
2007   if (QueryPerformanceCounter(&count))
2008   {
2009     QueryPerformanceFrequency(&freq);
2010     return (double)count.QuadPart/freq.QuadPart;
2011   }
2012   else
2013     return SDL_GetTicks() / 1000.0;
2014   #else
2015   struct timeval tm;
2016   struct timezone tz;
2017   gettimeofday(&tm, &tz);
2018 
2019   // Convert a timeval to a double
2020   return (double)(tm.tv_sec) + ((double)(tm.tv_usec) * 0.000001);
2021   #endif
2022 }
2023 
update()2024 void Timer::update()
2025 {
2026   dt = timerFunc() - start;
2027   fpsCount++;
2028   fpsCache += dt;
2029 
2030   if (fpsCache >= 1.0)
2031   { fps = fpsCount; fpsCount = 0; fpsCache = 0.0; }
2032 }
2033 
tcbBegin(GLenum prim)2034 void CALLBACK tcbBegin(GLenum prim)
2035 { glBegin(prim); }
2036 
tcbEnd()2037 void CALLBACK tcbEnd() { glEnd(); }
2038 
tcbCombine(GLdouble c[3],void * d[4],GLfloat w[4],void ** out)2039 void CALLBACK tcbCombine (GLdouble c[3], void *d[4], GLfloat w[4], void **out)
2040 {
2041   GLdouble *nv = (GLdouble *) malloc(3*sizeof(GLdouble));
2042   tessAllocatedVertices.push_back(nv);
2043 
2044   nv[0] = c[0];
2045   nv[1] = c[1];
2046   nv[2] = c[2];
2047   *out = nv;
2048 }
2049 
2050 
tcbError(GLenum errnum)2051 void CALLBACK tcbError(GLenum errnum)
2052 {
2053   errorMessage(6, "TESSELLATION ERROR", "CODE %d: %s", errnum, gluErrorString(errnum));
2054 }
2055