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