1 //-----------------------------------------------------------------------------
2 // Cake App
3 //-----------------------------------------------------------------------------
4
5 #include "app.h"
6 #include "alias.h"
7 #include "commands.h"
8 #include "console.h"
9 #include "definitions.h"
10 #include "framework.h"
11 #include "logfile.h"
12 #include "overlay.h"
13 #include "render.h"
14 #include "timer.h"
15 #include "vars.h"
16 #include "system.h"
17 #include "math.h"
18 #include "mem.h"
19 #include "sound.h"
20
21 #ifdef WIN32
22 #include <process.h> // for _getpid
23 #include <direct.h> // for _getcwd
24 #include <string.h> // for _strlwr
25 #include <time.h>
26 #else
27 #include <time.h>
28 #include <unistd.h> // for getcwd
29 #define _getcwd getcwd
30 #endif
31
32 LogFile* gLogFile = NULL; // Log file
33 Console* gConsole = NULL; // Console
34 FrameWork* gFramework = NULL; // FrameWork
35 Vars* gVars = NULL; // Variables
36 Alias* gAlias = NULL; // Aliases
37 Commands* gCommands = NULL; // Commands
38 Render* gRender = NULL; // Render
39 Overlay* gOver = NULL; // Overlay
40
41 #define FILENAME_LENGTH 1025 // Filenames length
42
43 Var r_path("r_path", "base;baseq3", VF_PERSISTENT | VF_LATCH | VF_SYSTEM);
44 Var r_mapsubdir("r_mapsubdir", "maps/", VF_PERSISTENT | VF_SYSTEM);
45 Var cg_drawFPS("cg_drawFPS", 0, VF_PERSISTENT);
46 Var cg_FPSrefreshrate("cg_FPSrefreshrate", 250.f, VF_PERSISTENT);
47 Var cg_drawtime("cg_drawtime", 0, VF_PERSISTENT);
48 Var cg_drawflush("cg_drawflush", 0);
49 Var cg_drawtris("cg_drawtris", 0);
50 Var cg_drawverts("cg_drawverts", 0);
51 Var cg_apicalls("cg_apicalls", 0);
52 Var cg_showpos("cg_showpos", 0);
53 Var cg_showrot("cg_showrot", 0);
54 Var r_debug("r_debug", 0);
55 Var sys_appstate("sys_appstate", INACTIVE, VF_SYSTEM);
56 Var sys_soundsupport("sys_soundsupport", 0, VF_SYSTEM);
57 Var r_targetlocationdist("r_targetlocationdist", 32768.f);
58
59 float lastfps = 0; // fps value for last fps refresh interval
60 float timelastfps = 0; // time of last fps display
61 float totallastfps = 1; // sum of frames fps during current fps refresh interval
62 unsigned int frameslastfps = 0; // number of frames during current fps refresh interval
63 char demomsg[64]; // message display when demo is recording/playing
64 int demofilenameindex = 0; // variable used for demo management
65
66 /**
67 * Return the length of a string without color codes
68 */
_strlen(const char * s)69 int _strlen(const char *s)
70 {
71 int i = 0, r, l = (int) strlen(s);
72 r = l;
73
74 while (l--)
75 {
76 if (s[i++] == '^') r-=2;
77 }
78
79 return r;
80 }
81
82 //-----------------------------------------------------------------------------
83 // Predefined commands
84 //-----------------------------------------------------------------------------
85
cmd_bsplist(int argc,char * argv[])86 void cmd_bsplist(int argc, char *argv[])
87 {
88 getPAKContent(NULL, ".bsp", argc>1?argv[1]:NULL);
89 }
90
cmd_wavlist(int argc,char * argv[])91 void cmd_wavlist(int argc, char *argv[])
92 {
93 getPAKContent(NULL, ".wav", argc>1?argv[1]:NULL);
94 }
95
cmd_md3list(int argc,char * argv[])96 void cmd_md3list(int argc, char *argv[])
97 {
98 getPAKContent(NULL, ".md3", argc>1?argv[1]:NULL);
99 }
100
cmd_listfiles(int argc,char * argv[])101 void cmd_listfiles(int argc, char *argv[])
102 {
103 if (argc > 1)
104 {
105 getPAKContent(NULL, argv[1], argc>2?argv[2]:NULL);
106 }
107 else gConsole->Insertln("usage: %s <string> [<string>]", argv[0]);
108 }
109
cmd_logpreviews(int argc,char * argv[])110 void cmd_logpreviews(int argc, char *argv[])
111 {
112 gConsole->Insertln("Writing previews into logfile...");
113 logPreviews();
114 }
115
App(void)116 App::App(void)
117 {
118 sys_appstate = INACTIVE;
119
120 _getcwd(app_path, PATH_LENGTH); // get current path
121 strcat(app_path, "\\");
122
123 world = NULL;
124
125 clients = NULL;
126 demos = NULL;
127 numclients = 0;
128 curr_client = 0;
129
130 memset(curr_map, '\0', 32*sizeof(char));
131 reloading = false;
132
133 if (!(gCommands = new Commands)) ThrowException(ALLOCATION_ERROR, "App:App.gCommands");
134 if (!(gVars = new Vars)) ThrowException(ALLOCATION_ERROR, "App:App.gVars");
135 if (!(gAlias = new Alias)) ThrowException(ALLOCATION_ERROR, "App:App.gAlias");
136 if (!(gLogFile = new LogFile)) ThrowException(ALLOCATION_ERROR, "App:App.gLogFile");
137 if (!(gRender = new Render)) ThrowException(ALLOCATION_ERROR, "App:App.gRender");
138 if (!(gConsole = new Console)) ThrowException(ALLOCATION_ERROR, "App:App.gConsole");
139 if (!(gFramework = new FrameWork)) ThrowException(ALLOCATION_ERROR, "App:App.gFramework");
140 if (!(gOver = new Overlay)) ThrowException(ALLOCATION_ERROR, "App:App.gOver");
141 gOver->Init();
142
143 gConsole->Insertln("<br/><b>^6----- Starting cake engine ------------------------------------</b>");
144
145 #ifdef _DEBUG
146 gConsole->Insertln("^1%s ^0debug version of %s (%s)", _VERSION_, __DATE__, __TIME__);
147 #else
148 gConsole->Insertln("^2%s ^0release version of %s (%s)", _VERSION_, __DATE__, __TIME__);
149 #endif
150
151 // Register variables
152 gVars->RegisterVar(r_path);
153 gVars->RegisterVar(r_mapsubdir);
154 gVars->RegisterVar(cg_drawFPS);
155 gVars->RegisterVar(cg_FPSrefreshrate);
156 gVars->RegisterVar(cg_drawtime);
157 gVars->RegisterVar(cg_drawflush);
158 gVars->RegisterVar(cg_drawtris);
159 gVars->RegisterVar(cg_drawverts);
160 gVars->RegisterVar(cg_apicalls);
161 gVars->RegisterVar(cg_showpos);
162 gVars->RegisterVar(cg_showrot);
163 gVars->RegisterVar(r_debug);
164 gVars->RegisterVar(sys_appstate);
165 gVars->RegisterVar(sys_soundsupport);
166 gVars->RegisterVar(r_targetlocationdist);
167
168 // Add commands
169 gCommands->AddCommand("bsplist", cmd_bsplist, "display the list of bsp map names (if an argument is present, only names containing this argument will be displayed)");
170 gCommands->AddCommand("wavlist", cmd_wavlist, "display the list of wav (music and sounds) files (if an argument is present, only names containing this argument will be displayed)");
171 gCommands->AddCommand("md3list", cmd_md3list, "display the list of md3 files (if an argument is present, only names containing this argument will be displayed)");
172 gCommands->AddCommand("listfiles", cmd_listfiles, "display the list of all files matching with a substring");
173 gCommands->AddCommand("logpreviews", cmd_logpreviews, "write the map list with corresponding levelshot into the logfile");
174 }
175
Init(void)176 void App::Init(void)
177 {
178 sys_appstate = INITIALIZING;
179
180 gCommands->AddToHistory = false;
181
182 // init the file environment
183 FileEnvironment::Init();
184 FileEnvironment::AddPath("./");
185
186 // Read the config file
187 ConfigFile(CONFIG_FILENAME);
188
189 Timer::Init();
190
191 #ifdef WIN32
192 {
193 // OS Info
194 OSVERSIONINFO vinfo;
195 vinfo.dwOSVersionInfoSize = sizeof(vinfo);
196
197 gLogFile->OpenFile();
198
199 if (!GetVersionEx (&vinfo))
200 gConsole->Insertln("^5WARNING: Couldn't get OS info");
201
202 gConsole->Insertln("<b>System infos :</b>");
203 gConsole->Insertln("\tWin32 platform");
204 gConsole->Insertln("\tBuild number : %d", vinfo.dwBuildNumber);
205 gConsole->Insertln("\tMajor version : %d", vinfo.dwMajorVersion);
206 gConsole->Insertln("\tMinor version : %d", vinfo.dwMinorVersion);
207 gConsole->Insertln("\tPlatform ID : %d", vinfo.dwPlatformId);
208 gConsole->Insertln("\tCSD version : %s", vinfo.szCSDVersion);
209 gConsole->Insertln("\tCurrent process ID : %d", _getpid());
210 gConsole->Insertln("\tCPU vendor name : %s", GetVendorString());
211 gConsole->Insertln("\tCPU speed : %d MHz", GetCpuSpeed());
212 gConsole->Insertln("\tCPU cache size : %d", GetCacheInfo());
213 if (GetCPUCaps(HAS_3DNOW)) gConsole->Insertln("\t3DNow! detected");
214 struct tm *newtime; time_t aclock; time(&aclock); newtime = localtime(&aclock);
215 gConsole->Insertln("\tSystem date and time : %s", asctime(newtime));
216
217 gLogFile->CloseFile();
218 }
219 #else
220 {
221 gConsole->Insertln("\tNot a Win32 Platform");
222 }
223 #endif
224
225 #if 0
226 { // in this case, last path is more important
227 // for instance, with "../base;../missionpack", the more
228 // important path is "../missionpack"
229 int i, j, l;
230 i = 0;
231 j = 0;
232 l = (int) strlen(r_path.svalue);
233 char buffer[256];
234 while (i < l)
235 {
236 memset(buffer, 0, 256);
237 j = 0;
238 while (i < l && r_path.svalue[i] != ';') buffer[j++] = r_path.svalue[i++];
239 if (buffer[j] != '/') buffer[j] = '/';
240 FileEnvironment::AddPath(buffer);
241 ++i;
242 }
243 }
244 #else
245 { // in this case, first path is more important
246 // for instance, with "../base;../missionpack", the more
247 // important path is "../base"
248 int i, j;
249 i = (int) strlen(r_path.svalue) - 1;
250 j = i;
251 char buffer[256];
252 while (i >= 0)
253 {
254 memset(buffer, 0, 256);
255 while (i >= 0 && r_path.svalue[i] != ';') --i;
256 if (i > 0) strncpy(buffer, &r_path.svalue[i+1], j-i);
257 else strncpy(buffer, r_path.svalue, j-i);
258 if (buffer[j-i] != '/') buffer[j-i] = '/';
259 FileEnvironment::AddPath(buffer);
260 j = --i;
261 }
262 }
263 #endif
264 FileEnvironment::dumpEnvironment();
265
266 // Initialize sound system
267 if (!initSoundSystem())
268 {
269 gConsole->Insertln("^1Could not initialize sound system!");
270 sys_soundsupport = 0;
271 }
272 else sys_soundsupport = 1;
273
274 // Loads packages
275 FileEnvironment::LoadPacks();
276
277 #ifdef _DEBUG
278 getPAKContent(NULL, ".bsp"); // print a list of bsp files in log file
279 cg_drawFPS = 1;
280 cg_drawtime = 1;
281 cg_showpos = 1;
282 cg_showrot = 1;
283 cg_drawflush = 1;
284 cg_drawtris = 1;
285 cg_drawverts = 1;
286 cg_apicalls = 1;
287 #endif
288
289 // Seed the random-number generator with current time so that
290 // the numbers will be different every time we run.
291 srand((unsigned)time(NULL));
292
293 InitializeShaders();
294
295 gCommands->AddToHistory = true;
296 }
297
~App(void)298 App::~App(void)
299 {
300 if (sys_appstate.ivalue != INACTIVE)
301 {
302 sys_appstate = INACTIVE;
303 gLogFile->Insert("^5WARNING: cake was not shut correctly\n");
304 }
305
306 // Creating new config file
307 /* FILE *fp;
308 if (!(fp = fopen(CONFIG_FILENAME, "w")))
309 {
310 gLogFile->Insert("^1ERROR: Unable to create config file for writing\n");
311 }
312 else
313 {
314 fclose(fp);
315 gVars->SaveToFile(CONFIG_FILENAME);
316 }
317 */
318
319 gVars->UnregisterVar(cg_showrot);
320 gVars->UnregisterVar(cg_showpos);
321 gVars->UnregisterVar(cg_drawtime);
322 gVars->UnregisterVar(cg_drawFPS);
323 gVars->UnregisterVar(cg_FPSrefreshrate);
324 gVars->UnregisterVar(cg_drawflush);
325 gVars->UnregisterVar(cg_drawtris);
326 gVars->UnregisterVar(cg_drawverts);
327 gVars->UnregisterVar(cg_apicalls);
328 gVars->UnregisterVar(r_debug);
329 gVars->UnregisterVar(r_mapsubdir);
330 gVars->UnregisterVar(r_path);
331 gVars->UnregisterVar(sys_appstate);
332 gVars->UnregisterVar(sys_soundsupport);
333 gVars->UnregisterVar(r_targetlocationdist);
334
335 gCommands->RemoveCommand("bsplist");
336 gCommands->RemoveCommand("wavlist");
337 gCommands->RemoveCommand("md3list");
338 gCommands->RemoveCommand("listfiles");
339 gCommands->RemoveCommand("logpreviews");
340
341 delete gOver; gOver = NULL;
342 delete gFramework; gFramework = NULL;
343 delete gConsole; gConsole = NULL;
344 delete gRender; gRender = NULL;
345 delete gVars; gVars = NULL;
346 delete gAlias; gAlias = NULL;
347 delete gCommands; gCommands = NULL;
348
349 #ifdef DEBUG_MEM
350 if (get_references()) log_mem_report();
351 else gLogFile->Insert("No memory leak detected.\n");
352 delete_memregister();
353 #endif
354
355 delete gLogFile; gLogFile = NULL;
356 }
357
Run(void)358 void App::Run(void)
359 {
360 sys_appstate = RUNNING;
361 }
362
Shut(void)363 void App::Shut(void)
364 {
365 sys_appstate = SHUTING;
366
367 UnloadMap();
368
369 if (clients) delete [] clients;
370 clients = NULL;
371 numclients = 0;
372
373 DestroyShaderList();
374
375 gFramework->Shut();
376 gConsole->Shut();
377 gRender->Shut();
378 gOver->Shut();
379
380 sys_appstate = INACTIVE;
381
382 gLogFile->Insert("<br/><b>^6----- cake engine shuting down --------------------------------</b>\n");
383 Timer::Refresh();
384 char time_text[64] = { '\0' };
385 int minutes = (int)Timer::fTime/60;
386 int seconds = (int)fmod(Timer::fTime,60);
387 sprintf(time_text, "%d:", minutes);
388 if (seconds < 10) strcat(time_text, "0");
389 sprintf(time_text, "%s%d", time_text, seconds);
390 gLogFile->Insert("Execution time: %s\n", time_text);
391
392 shutdownSoundSystem();
393 }
394
DrawWorld(void)395 void App::DrawWorld(void)
396 {
397 if (!gRender->GetWidth() || !gRender->GetHeight() ||
398 !world || sys_appstate.ivalue != RUNNING) return;
399
400 int i;
401
402 // Updating demo
403 for (i = 0; i < numclients; ++i)
404 {
405 if (demos[i].isPlaying)
406 {
407 float demopercent = demos[i].NewFrame();
408 sprintf(demomsg, "playing demo (%d %%)", (int)(demopercent));
409 }
410 else if (demos[i].isRecording)
411 {
412 demos[i].NewFrame();
413 sprintf(demomsg, "recording");
414 }
415 }
416
417 world->Render(clients, numclients);
418
419 for (i = 0; i < numclients; ++i)
420 {
421 if ((demos[i].isRecording && (long)(Timer::fTime*2)%2) || demos[i].isPlaying)
422 gOver->String(demomsg, gFramework->GetFont(NORMAL_CHAR),
423 (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*strlen(demomsg))/2.f,
424 (float) gRender->GetHeight()-SMALL_CHAR_HEIGHT);
425 }
426
427 // Draws the camera position
428 if (cg_showpos.ivalue && cg_showpos.ivalue <= numclients)
429 {
430 Client *client = &clients[cg_showpos.ivalue-1];
431
432 char pos_text[128] = { '\0' };
433 float x, y;
434 sprintf(pos_text, "pos (%.3f;%.3f;%.3f)",
435 client->cam.pos[0],
436 client->cam.pos[1],
437 client->cam.pos[2]);
438
439 x = (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(pos_text));
440 y = (float) gRender->GetHeight()-SMALL_CHAR_HEIGHT;
441
442 gOver->String(pos_text, gFramework->GetFont(NORMAL_CHAR), x, y);
443 }
444
445 // Draws the camera rotation
446 if (cg_showrot.ivalue && cg_showrot.ivalue <= numclients)
447 {
448 Client *client = &clients[cg_showrot.ivalue-1];
449
450 char rot_text[128];
451 float x, y;
452
453 // rotation
454 memset(rot_text, '\0', 128*sizeof(char));
455 sprintf(rot_text, "rot (%.3f;%.3f;%.3f)",
456 client->cam.rot[PITCH],
457 client->cam.rot[YAW],
458 client->cam.rot[ROLL]);
459
460 x = (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(rot_text));
461 y = (float) gRender->GetHeight()-SMALL_CHAR_HEIGHT;
462 if (cg_showpos.ivalue) y -= (float) (SMALL_CHAR_HEIGHT);
463
464 gOver->String(rot_text, gFramework->GetFont(NORMAL_CHAR), x, y);
465
466 // forward vector
467 memset(rot_text, '\0', 128*sizeof(char));
468 sprintf(rot_text, "forward (%.3f;%.3f;%.3f)",
469 client->forward[0],
470 client->forward[1],
471 client->forward[2]);
472
473 x = (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(rot_text));
474 y -= (float) (SMALL_CHAR_HEIGHT);
475
476 gOver->String(rot_text, gFramework->GetFont(NORMAL_CHAR), x, y);
477
478 // right
479 memset(rot_text, '\0', 128*sizeof(char));
480 sprintf(rot_text, "right (%.3f;%.3f;%.3f)",
481 client->right[0],
482 client->right[1],
483 client->right[2]);
484
485 x = (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(rot_text));
486 y -= (float) (SMALL_CHAR_HEIGHT);
487
488 gOver->String(rot_text, gFramework->GetFont(NORMAL_CHAR), x, y);
489
490 // up
491 memset(rot_text, '\0', 128*sizeof(char));
492 sprintf(rot_text, "up (%.3f;%.3f;%.3f)",
493 client->up[0],
494 client->up[1],
495 client->up[2]);
496
497 x = (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(rot_text));
498 y -= (float) (SMALL_CHAR_HEIGHT);
499
500 gOver->String(rot_text, gFramework->GetFont(NORMAL_CHAR), x, y);
501 }
502
503 gOver->Render(&gFramework->shaders);
504 }
505
DrawInterface(void)506 void App::DrawInterface(void)
507 {
508 // Draws FPS
509 if (cg_drawFPS.ivalue && totallastfps)
510 {
511 char fps_text[64] = { '\0' };
512 if (1000.f*(Timer::fTime-(float)timelastfps) > cg_FPSrefreshrate.fvalue)
513 {
514 lastfps = (float)frameslastfps/totallastfps;
515 timelastfps = (float) Timer::fTime;
516 totallastfps = 0;
517 frameslastfps = 0;
518 }
519 totallastfps += (float)Timer::frametime;
520 ++frameslastfps;
521 sprintf(fps_text, "^%d%4.2f^0 fps", lastfps<25?1:lastfps>30?0:5, lastfps);
522 gOver->String(fps_text, gFramework->GetFont(NORMAL_CHAR), (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(fps_text)), 0);
523 }
524
525 // Draw time
526 if (cg_drawtime.ivalue)
527 {
528 char time_text[64] = { '\0' };
529 float y = 0;
530 int minutes = (int)Timer::fTime/60;
531 int seconds = (int)fmod(Timer::fTime,60);
532 if (cg_drawFPS.ivalue) y += SMALL_CHAR_HEIGHT;
533 sprintf(time_text, "%d:", minutes);
534 if (seconds < 10) strcat(time_text, "0");
535 sprintf(time_text, "%s%d", time_text, seconds);
536 gOver->String(time_text, gFramework->GetFont(NORMAL_CHAR), (float) (gRender->GetWidth()-SMALL_CHAR_WIDTH*_strlen(time_text)), y);
537 }
538
539 // display rendering infos (bottom left)
540 if (cg_drawflush.ivalue)
541 {
542 char msg[64] = { '\0' };
543 sprintf(msg, "flush calls: %d", (int) gRender->num_flush);
544 gOver->String(msg, gFramework->GetFont(NORMAL_CHAR), 0, (float)gRender->GetHeight()-SMALL_CHAR_HEIGHT);
545 }
546
547 if (cg_drawtris.ivalue)
548 {
549 char msg[64] = { '\0' };
550 float y = (float)gRender->GetHeight()-SMALL_CHAR_HEIGHT;
551 if (cg_drawflush.ivalue) y -= SMALL_CHAR_HEIGHT;
552 sprintf(msg, "rendered tris: %d", (int) gRender->num_tris);
553 gOver->String(msg, gFramework->GetFont(NORMAL_CHAR), 0, y);
554 }
555
556 if (cg_drawverts.ivalue)
557 {
558 char msg[64] = { '\0' };
559 float y = (float)gRender->GetHeight()-SMALL_CHAR_HEIGHT;
560 if (cg_drawflush.ivalue) y -= SMALL_CHAR_HEIGHT;
561 if (cg_drawtris.ivalue) y -= SMALL_CHAR_HEIGHT;
562 sprintf(msg, "rendered verts: %d", (int) gRender->num_verts);
563 gOver->String(msg, gFramework->GetFont(NORMAL_CHAR), 0, y);
564 }
565
566 if (cg_apicalls.ivalue)
567 {
568 char msg[64] = { '\0' };
569 float y = (float)gRender->GetHeight()-SMALL_CHAR_HEIGHT;
570 if (cg_drawflush.ivalue) y -= SMALL_CHAR_HEIGHT;
571 if (cg_drawtris.ivalue) y -= SMALL_CHAR_HEIGHT;
572 if (cg_drawverts.ivalue) y -= SMALL_CHAR_HEIGHT;
573 sprintf(msg, "api calls: %d", (int) gRender->num_apicalls);
574 gOver->String(msg, gFramework->GetFont(NORMAL_CHAR), 0, y);
575 }
576
577 // target locations
578 /*
579 if (world)
580 {
581 EntityManager* ent = world->GetBSP()->GetEntities();
582 vec3_t vbuffer;
583 for (int i = 0; i < ent->num_target_location[0]; ++i)
584 {
585 VectorSub(ent->first_target_speaker[i].origin, gRender->current_camera->pos, vbuffer);
586 if (vbuffer[0]*vbuffer[0]+vbuffer[1]*vbuffer[1]+vbuffer[2]+vbuffer[2] < r_targetlocationdist.fvalue)
587 {
588 gOver->String(ent->first_target_location[i].message,
589 gFramework->GetFont(NORMAL_CHAR),
590 (gRender->GetWidth()-(float)(SMALL_CHAR_WIDTH*(int)strlen(ent->first_target_location[i].message))/2),
591 gRender->GetHeight()/2+(float)SMALL_CHAR_WIDTH);
592 }
593 }
594 }
595 */
596
597 gOver->Render(&gFramework->shaders);
598
599 gConsole->Update();
600 gConsole->Render();
601 }
602
GetFPS(void)603 double App::GetFPS(void)
604 {
605 return 1/Timer::frametime;
606 }
607
GetWorld(void)608 World* App::GetWorld(void)
609 {
610 return world;
611 }
612
GetCurrentMapName(void)613 const char* App::GetCurrentMapName(void)
614 {
615 return curr_map;
616 }
617
LoadMap(const char * map_name)618 int App::LoadMap(const char* map_name)
619 {
620 int reload = !stricmp(curr_map, map_name) && reloading;
621 reloading = false;
622
623 // Get a start position for client
624 if (!reload)
625 {
626 // Create new clients
627 if (!CreateClients(1))
628 {
629 gConsole->Insertln("^1Currently unable to create new clients. Loading aborted.");
630 return 0;
631 }
632 }
633
634 UnloadMap();
635
636 strncpy(curr_map, map_name, 32);
637
638 Timer::Refresh();
639 float startloadingtime = (float) Timer::fTime;
640
641 gConsole->Insertln("Loading map %s...", map_name);
642
643 world = new World;
644 if (!world) ThrowException(ALLOCATION_ERROR, "App::LoadMap.world");
645
646 // load a map, if one was specified:
647 // (objects must be created before shader loading because here we add the shader reference)
648 // TODO: objects should be loaded throug the world, the world should be an object factory
649 if (!world->AddMap(map_name))
650 {
651 delete world;
652 world = NULL;
653 gConsole->Insertln("Map %s not found.", map_name);
654 return 0;
655 }
656
657 gConsole->SetState(CLOSED);
658 gConsole->addToMessages = false;
659
660 world->Init();
661
662 // Get a start position for client
663 if (!reload)
664 {
665 if (world->GetNumStartPos())
666 {
667 // get a random start pos
668 world->SetStartPos(&clients[0], rand()%world->GetNumStartPos());
669 }
670 else gConsole->Insertln("^1ERROR: Couldn't find a spawnpoint.");
671 }
672 else
673 {
674 // If map is reloaded, clients stay at same place as they were before
675 // unloading the map. The internal cluster and leaf camera values are
676 // allways defined but are not valid any more for new map. Following
677 // function call will reinit these values. If they are not reinitialized
678 // screen (and current leaf/cluster) won't be updated until camera
679 // changes leaf/cluster.
680 for (int i = 0; i < numclients; ++i) clients[i].UpdateCam();
681 }
682
683 Timer::Refresh();
684 gConsole->Insertln("Map loaded successfully in %f sec.", (float)Timer::fTime-startloadingtime);
685 gConsole->addToMessages = true;
686
687 return 1;
688 }
689
ReloadMap(void)690 int App::ReloadMap(void)
691 {
692 if (strlen(curr_map))
693 {
694 reloading = true;
695 return LoadMap(curr_map);
696 }
697 else
698 {
699 gConsole->Insertln("^5No map running.");
700 return 0;
701 }
702 }
703
UnloadMap(void)704 void App::UnloadMap(void)
705 {
706 if (world)
707 {
708 gConsole->Insertln("Unloading map...");
709 world->Shut();
710 delete world;
711 world = NULL;
712
713 gConsole->Resize(-1, gRender->GetHeight());
714 }
715 }
716
CreateClients(int num)717 int App::CreateClients(int num)
718 {
719 if (!num) return 0;
720
721 // Check if already one demo is recording
722 for (int i = 0; i < numclients; ++i)
723 {
724 Demo *demo = &demos[i];
725 if (demos[i].isRecording || demos[i].isPlaying)
726 {
727 gConsole->Insertln("^1Unable to change number of clients when a demo is recording or playing.");
728 return 0;
729 }
730 }
731
732 if (clients) delete [] clients;
733 clients = new Client[num];
734 if (!clients) ThrowException(ALLOCATION_ERROR, "World::CreateClients.clients");
735 numclients = num;
736 curr_client = 0;
737 InitClients();
738
739 if (demos) delete [] demos;
740 demos = new Demo[num];
741 if (!demos) ThrowException(ALLOCATION_ERROR, "World::CreateClients.demos");
742
743 return 1;
744 }
745
GetClient(int num)746 Client* App::GetClient(int num)
747 {
748 if (!clients) return NULL;
749 if (num < 0 || num >= numclients) return NULL;
750 return &clients[num];
751 }
752
GetNumClients(void)753 int App::GetNumClients(void)
754 {
755 return numclients;
756 }
757
SetCurrentClient(int num)758 void App::SetCurrentClient(int num)
759 {
760 if (num < 0 || num >= numclients) return;
761 curr_client = num;
762
763 for (int i = 0; i < numclients; ++i) clients[i].isCurrent = false;
764 clients[num].isCurrent = true;
765 }
766
GetCurrentClient(void)767 Client* App::GetCurrentClient(void)
768 {
769 if (clients) return &clients[curr_client];
770 else return NULL;
771 }
772
GetCurrentClientIndex(void)773 int App::GetCurrentClientIndex(void)
774 {
775 if (!numclients) return -1;
776 return curr_client;
777 }
778
779 // Initialize the clients
InitClients(void)780 void App::InitClients(void)
781 {
782 if (!numclients) return;
783 int a = numclients, b = 1, c = numclients, i, j;
784
785 // Get the possible values
786 for (i = 1; i <= c; ++i)
787 {
788 for (j = i; j <= c; ++j)
789 {
790 if (c == i*j)
791 {
792 a = j;
793 b = i;
794 }
795 }
796 }
797
798 // Create the windows
799 int l, t, w, h;
800 w = gRender->GetWidth()/a;
801 h = gRender->GetHeight()/b;
802 l = 0, t = 0;
803
804 for (i = 0; i < numclients; ++i)
805 {
806 clients[i].Init(l, t, w, h);
807 l += w;
808 if (!((i+1) % a))
809 {
810 l = 0;
811 t += h;
812 }
813 }
814 curr_client = 0;
815
816 for (i = 0; i < numclients; ++i)
817 clients[i].isCurrent = (i==curr_client)?true:false;
818 }
819
RecordDemo(float framesinterval)820 void App::RecordDemo(float framesinterval)
821 {
822 if (!numclients)
823 {
824 gConsole->Insertln("^5WARNING: No available client !");
825 return;
826 }
827
828 // Check if already one demo is recording
829 for (int i = 0; i < numclients; ++i)
830 {
831 if (demos[i].isRecording)
832 {
833 gConsole->Insertln("^5Already recording a demo.");
834 return;
835 }
836 }
837
838 Demo* demo = &demos[curr_client];
839
840 if (demo->StartDemoRecording(&clients[curr_client], curr_map, framesinterval))
841 {
842 gConsole->Close();
843 gConsole->Insertln("Start recording...");
844 gConsole->Insertln("Stop recording by entering \"stopdemo <filename>\" or pressing ESC (default demo filename will be used).");
845 }
846 else ThrowException(DEFAULT_EXCEPTION, "Invalid demo state !");
847 }
848
StopDemo(const char * destfile)849 int App::StopDemo(const char *destfile)
850 {
851 if (!numclients)
852 {
853 gConsole->Insertln("^5WARNING: No available client !");
854 return 0;
855 }
856
857 Demo* demo = &demos[curr_client];
858
859 if (demo->isRecording)
860 {
861 char demofilename[32] = { '\0' };
862
863 // stop recording
864 if (destfile)
865 {
866 strncpy(demofilename, destfile, 31);
867 }
868 else
869 {
870 // stop recording demo and save to default file
871 int c, d, u;
872 do { // skip existing files
873 c = demofilenameindex/100;
874 d = (demofilenameindex-c*100)/10;
875 u = (demofilenameindex-c*100-d*10);
876 memset(demofilename, 0, 32);
877 sprintf(demofilename, "cake%d%d%d.demo", c, d, u);
878 ++demofilenameindex;
879 } while(fopen(demofilename, "r"));
880 }
881
882 if (demo->StopDemoRecording(demofilename))
883 gConsole->Insertln("Demo saved into \"%s\"", demofilename);
884 else
885 gConsole->Insertln("^1Demo saving abordted.");
886
887 return 1;
888 }
889 else if (demo->isPlaying)
890 {
891 // stop playing
892 demo->StopDemoPlaying();
893 gConsole->Insertln("Demo stopped.");
894 return 2;
895 }
896 else return 0;
897 }
898
PlayDemo(const char * demofile,bool loop,enum_InterpolationMode mode)899 void App::PlayDemo(const char *demofile, bool loop, enum_InterpolationMode mode)
900 {
901 Demo* curr_demo;
902
903 if (numclients)
904 {
905 curr_demo = &demos[curr_client];
906
907 if (curr_demo->isPlaying)
908 {
909 gConsole->Insertln("^1Demo is currently playing");
910 return;
911 }
912
913 if (curr_demo->isRecording)
914 {
915 gConsole->Insertln("^1Demo is currently recording");
916 return;
917 }
918 }
919
920 // if no world, we need to create a temporal demo until world is created
921 if (!world || !numclients)
922 {
923 curr_demo = new Demo;
924 if (!curr_demo) ThrowException(ALLOCATION_ERROR, "App::PlayDemo.curr_demo");
925 }
926
927 // Get the map to be loaded for demo running
928 char mapname[32] = { '\0' };
929 strcpy(mapname, curr_demo->LoadDemo(demofile, mode));
930
931 if (!strlen(mapname))
932 {
933 // Wrong mapname
934 gConsole->Insertln("Demo loading aborted.");
935 delete curr_demo;
936 return;
937 }
938
939 // if current map is not same as needed map
940 if (!world || stricmp(curr_map, mapname))
941 {
942 if (!world) delete curr_demo; // demo will be re-created
943 if (! LoadMap(mapname)) return;
944
945 // if is reloaded, demo is destroyed and re-created, then we
946 // need to reload the demo for default client
947 curr_demo = &demos[curr_client];
948 curr_demo->LoadDemo(demofile, mode);
949 }
950
951 gConsole->Insertln("Starting demo...");
952 curr_demo->StartDemoPlaying(&clients[curr_client], loop);
953 }
954