1 /*
2 Copyright (C) 2004 by James Gregory
3 Part of the GalaxyHack project
4
5 This program is free software; you can redistribute it and/or modify
6 it under the terms of the GNU General Public License.
7 This program is distributed in the hope that it will be useful,
8 but WITHOUT ANY WARRANTY.
9
10 See the COPYING file for more details.
11 */
12
13 #include "Globals.h"
14 #include "Group.h"
15 #include "MainMenu.h"
16 #include "SetupBattle.h"
17 #include "PreBattle.h"
18 #include "RTS.h"
19 #include "Score.h"
20 #include "ForceSelect.h"
21 #include "LookupTables.h"
22 #include "Stuff.h"
23
24 #include <sstream>
25 #include <iostream>
26 #include <stdexcept>
27 #include <iterator>
28 #include <boost/filesystem/operations.hpp>
29
30 using std::cout;
31 using std::endl;
32 using std::ofstream;
33 using std::runtime_error;
34 using std::terminate;
35 using std::istringstream;
36 using std::istream_iterator;
37 using std::find;
38 using std::getline;
39
40 //Global just to this file:
41 GameState* game = 0;
42 int fpsState = 0;
43 string userHomePath;
44 int lastSwitchTime; //set in init
45
46 // Function declarations //////////////////////////////////
47 void FindHomePath();
48 bool DealWithArgs(int argc, char* argv[], vector<string>& preloadSides);
49 void LoadSettings(char* argv[]);
50 void SaveSettings();
51 void GameInit(char* argv[]);
52 bool GameMain();
53 void PollInput();
54 void DealWithEvent(SDL_Event& event);
55 void FPSCounter();
56 void GameShutdown(bool saveSettings = true);
57 void ExceptionShutdown(runtime_error e);
58
main(int argc,char * argv[])59 int main(int argc, char* argv[]) {
60 vector<string> preloadSides;
61
62 try {
63 if (!DealWithArgs(argc, argv, preloadSides))
64 return 0;
65
66 GameInit(argv);
67
68 if (preloadSides.size()) {
69 for (int i = 0; i != preloadSides.size(); ++i) {
70 sides.push_back(Side(preloadSides[i]));
71 sides[i].FilesToSDStruct();
72 sides[i].LoadData(false);
73 }
74 gsTo = GST_PreBattle;
75 PreBattle::pbState = PBS_PreBattle;
76 }
77 } catch(runtime_error e) {
78 const char* error = e.what();
79 WriteLog(error);
80 GameShutdown(false);
81 terminate();
82 }
83
84 try {
85 while (GameMain());
86 } catch(runtime_error e) {
87 ExceptionShutdown(e);
88 }
89
90 GameShutdown();
91
92 return 0;
93 }
94
FindHomePath()95 void FindHomePath() {
96 #ifndef WIN32
97 char *home = 0;
98
99 home = getenv("HOME");
100
101 if (home) {
102 userHomePath = home;
103 userHomePath += "/.galaxyhack/";
104 }
105 #endif
106 }
107
DealWithArgs(int argc,char * argv[],vector<string> & preloadSides)108 bool DealWithArgs(int argc, char* argv[], vector<string>& preloadSides) {
109 if (argc > 1) {
110 bool validArgs = 1;
111
112 for (int i = 1; i != argc; ++i) {
113 string theArg = argv[i];
114
115 if (theArg == "-driver" && argc > i + 1) {
116 globalSettings.videoDriver = argv[i+1];
117 cout << "Ignoring settings file and using video driver " + globalSettings.videoDriver << endl << endl;
118 ++i;
119 } else if (theArg == "-x" && argc > i + 2) {
120 preloadSides.push_back(argv[i+1]);
121 preloadSides.push_back(argv[i+2]);
122 i +=2;
123 } else if (theArg == "-b" && argc > i + 1) {
124 globalSettings.batch = true;
125 string framesString = argv[i+1];
126 string::const_iterator begin = framesString.begin();
127 string::const_iterator end = framesString.end();
128 globalSettings.maxFrames = IterToInt(begin, end);
129 #ifndef WIN32
130 cout << "Batch mode initiated, max frames set to " + globalSettings.maxFrames << endl << endl;
131 #endif
132 ++i;
133 } else if (theArg == "-u" && argc > i + 1) {
134 string speedString = argv[i+1];
135 string::const_iterator begin = speedString.begin();
136 string::const_iterator end = speedString.end();
137 worldUpdateInterval = IterToInt(begin, end);
138 #ifndef WIN32
139 cout << "World update interval set to" + worldUpdateInterval << endl << endl;
140 #endif
141 ++i;
142 } else if (theArg == "-r" && argc > i + 1) {
143 string randomString = argv[i+1];
144 string::const_iterator begin = randomString.begin();
145 string::const_iterator end = randomString.end();
146 globalSettings.randomSeed = IterToInt(begin, end);
147 #ifndef WIN32
148 cout << "Random seed set to" + globalSettings.randomSeed << endl << endl;
149 #endif
150 ++i;
151 } else
152 validArgs = 0;
153 }
154
155 if (validArgs == 0) {
156 cout << "Usage: ./galaxyhack [OPTIONS]" << endl << endl;
157
158 cout << "GalaxyHack - AI script based computer game" << endl << endl;
159
160 cout << "Options:" << endl;
161 cout << " -driver <videodriver>: use the specified video driver" << endl << endl;
162
163 cout << " \"x11\" is the default but runs rather slowly." << endl;
164 cout << " \"dga\" will make the game run faster, but requires root permissions (run rootperms.sh) and only works with certain video cards." << endl << endl;
165
166 cout << " -x <fleet1> <fleet2>: skip menu screen and immediately start a battle between the two fleets given" << endl;
167 cout << " -b <number of frames>: batch mode, see manual for details" << endl;
168 cout << " -u <number of frames>: world update interval, see manual for details" << endl;
169 cout << " -r <seed>: set the seed for the random number generator" << endl << endl;
170
171 cout << "A number of other options can be changed by editing ~/.galaxyhack/settings.dat" << endl << endl;
172
173 return false;
174 }
175 }
176
177 return true;
178 }
179
LoadSettings(char * argv[])180 void LoadSettings(char* argv[]) {
181 #ifndef WIN32
182 string settingsPath = userHomePath + "settings.dat";
183 if (!DoesFileExist(settingsPath))
184 settingsPath = "settings.dat";
185 #else
186 string settingsPath = "settings.dat";
187 #endif
188
189 if (!DoesFileExist(settingsPath)) {
190 #ifndef WIN32
191 string error = "Couldn't find settings.dat in $HOME/.galaxyhack or in present working directory";
192 #else
193 string error = "Couldn't find settings.dat";
194 #endif
195 throw runtime_error(error.c_str());
196 }
197
198 string inputStr;
199 FileToString(settingsPath, inputStr);
200 istringstream input(inputStr);
201
202 istream_iterator<char> iter = input;
203 istream_iterator<char> fileEnd = istream_iterator<char>();
204
205 #ifndef WIN32
206 cout << "Using " + settingsPath + ":" << endl << endl;
207 cout << inputStr;
208 #endif
209
210 //commander
211 iter = find(iter, fileEnd, ':');
212 input.ignore();
213 getline(input, globalSettings.commander);
214 iter = input;
215
216 //data path
217 iter = find(iter, fileEnd, ':');
218 input.ignore();
219 getline(input, globalSettings.bdp);
220 if (globalSettings.bdp == "pwd")
221 globalSettings.bdp = "";
222 else if (globalSettings.bdp[globalSettings.bdp.size() -1] != '/')
223 globalSettings.bdp += '/';
224 iter = input;
225
226 //video driver
227 iter = find(iter, fileEnd, ':');
228 input.ignore();
229 //may have been set on command line
230 if (globalSettings.videoDriver == "")
231 getline(input, globalSettings.videoDriver);
232 iter = input;
233
234 //fullscreen
235 iter = find(iter, fileEnd, ':');
236 ++iter;
237 globalSettings.fullScreen = static_cast<bool>(IterToInt(iter, fileEnd));
238
239 //screen resolution
240 iter = find(iter, fileEnd, ':');
241 ++iter;
242 globalSettings.screenWidth = IterToInt(iter, fileEnd);
243
244 iter = find(iter, fileEnd, ':');
245 ++iter;
246 globalSettings.screenHeight = IterToInt(iter, fileEnd);
247
248 //music
249 iter = find(iter, fileEnd, ':');
250 ++iter;
251 globalSettings.bMusic = static_cast<bool>(IterToInt(iter, fileEnd));
252
253 //disable sound
254 iter = find(iter, fileEnd, ':');
255 ++iter;
256 globalSettings.disableSound = static_cast<bool>(IterToInt(iter, fileEnd));
257
258 //default pics
259 iter = find(iter, fileEnd, ':');
260 input.ignore();
261 getline(input, globalSettings.defaultCSPic);
262 iter = input;
263
264 iter = find(iter, fileEnd, ':');
265 input.ignore();
266 getline(input, globalSettings.defaultFrPic);
267 iter = input;
268
269 iter = find(iter, fileEnd, ':');
270 input.ignore();
271 getline(input, globalSettings.defaultSSPic);
272 iter = input;
273
274 //remembered fleets
275 for (int i = 0; i != maxPlayers; ++i) {
276 iter = find(iter, fileEnd, ':');
277 input.ignore();
278 string tempStr;
279 getline(input, tempStr);
280 globalSettings.rememberFleets.push_back(tempStr);
281 iter = input;
282 }
283
284 iter = find(iter, fileEnd, ':');
285 ++iter;
286 globalSettings.howGreenIsGreen = IterToInt(iter, fileEnd);
287
288 oldGlobalSettings = globalSettings;
289
290 if (globalSettings.batch) {
291 globalSettings.disableSound = true;
292 globalSettings.dontWriteSound = true;
293 worldUpdateInterval = 0;
294 }
295 }
296
SaveSettings()297 void SaveSettings() {
298 #ifndef WIN32
299 namespace fs = boost::filesystem;
300 fs::path userHomePathPath(userHomePath);
301 if (!fs::exists(userHomePathPath))
302 fs::create_directory(userHomePathPath);
303 #endif
304 string settingsStr = userHomePath + "settings.dat";
305 ofstream output(settingsStr.c_str(), std::ios::trunc | std::ios::out);
306
307 output << "- there must be exactly one space between each colon and the setting that follows" << endl;
308 output << endl;
309
310 output << "Commander: " << globalSettings.commander << endl;
311 if (globalSettings.bdp.size())
312 output << "Base data path: " << globalSettings.bdp << endl << endl;
313 else
314 output << "Base data path: pwd" << endl << endl;
315
316 output << "Video driver: " << globalSettings.videoDriver << endl;
317
318 output << "Fullscreen: " << globalSettings.fullScreen << endl;
319 output << "Screen width: " << globalSettings.screenWidth << endl;
320 output << "Screen height: " << globalSettings.screenHeight << endl << endl;
321
322 output << "Music: " << globalSettings.bMusic << endl;
323
324 if (globalSettings.dontWriteSound)
325 output << "Disable sound: " << oldGlobalSettings.disableSound << endl << endl;
326 else
327 output << "Disable sound: " << globalSettings.disableSound << endl << endl;
328
329 output << "Default cap ship pic: " << globalSettings.defaultCSPic << endl;
330 output << "Default frigate pic: " << globalSettings.defaultFrPic << endl;
331 output << "Default small ship pic: " << globalSettings.defaultSSPic << endl << endl;
332
333 for (int i = 0; i != globalSettings.rememberFleets.size(); ++i)
334 output << "Fleet " << i + 1 << ": " << globalSettings.rememberFleets[i] << endl;
335 output << endl;
336
337 output << "How green is green: " << globalSettings.howGreenIsGreen << endl;
338 }
339
GameInit(char * argv[])340 void GameInit(char* argv[]) {
341 namespace fs = boost::filesystem;
342
343 FindHomePath();
344 LoadSettings(argv);
345
346 string tmpStr = "SDL_VIDEODRIVER=" + globalSettings.videoDriver;
347 char* sillyness = c_strNC(tmpStr);
348 putenv(sillyness);
349
350 JSDL.Init();
351 SetupLookupTables();
352 LoadStandardGraphics();
353
354 lastSwitchTime = SDL_GetTicks();
355 }
356
357
GameMain()358 bool GameMain() {
359 now = SDL_GetTicks();
360
361 int timePerFrame;
362 //1000ms/60fps = 16 ish, though this will get rounded to 20
363 if (worldUpdateInterval > 0)
364 timePerFrame = 16;
365 //1000ms/20fps = 50
366 else
367 timePerFrame = 50;
368 if (now - lastSwitchTime > timePerFrame) {
369 skipDisplayFrame = false;
370 lastSwitchTime = now;
371 } else
372 skipDisplayFrame = true;
373
374 PollInput();
375 UpdateWindows();
376
377 if (gsTo == gsCurrent)
378 game->Main();
379
380 else try {
381 SafeDelete(game);
382
383 if (gsTo == GST_Reload)
384 gsTo = gsCurrent;
385
386 switch (gsTo) {
387 case GST_MainMenu:
388 game = new MainMenu::MainMenu_State;
389 break;
390
391 case GST_SetupBattle:
392 game = new SetupBattle::SetupBattle_State;
393 break;
394
395 case GST_PreBattle:
396 game = new PreBattle::PreBattle_State;
397 break;
398
399 case GST_Battle:
400 game = new RTS::RTS_State;
401 break;
402
403 case GST_Score:
404 game = new Score::Score_State;
405 break;
406
407 case GST_ForceSelect:
408 game = new ForceSelect::ForceSelect_State;
409 break;
410
411 case GST_TheOS:
412 return false;
413 break;
414 }
415
416 //make sure menus have a starting event to force them to update screen before user
417 //input in spite of SDL_WaitEvent
418 SDL_Event event;
419 event.type = SDL_USEREVENT;
420 SDL_PushEvent(&event);
421
422 //make sure we don't get stuck in poll event loop before display is updated on fast computers
423 lastSwitchTime -= 1000;
424
425 gsCurrent = gsTo;
426 } catch(runtime_error e) {
427 if (gsTo != GST_MainMenu) {
428 sides.clear();
429 KillAllWindows();
430 const char* error = e.what();
431 WriteLog(error);
432 globalErrorString = error;
433
434 if (gsCurrent == GST_MainMenu)
435 gsCurrent = GST_TheOS;
436 gsTo = GST_MainMenu;
437 } else
438 throw runtime_error(e);
439 }
440
441 if (fpsState == 1)
442 FPSCounter();
443
444 if (!globalSettings.batch) {
445 static SDL_Rect mouseRect = {0, 0, genPictures[GENPIC_CURSOR]->w, genPictures[GENPIC_CURSOR]->h};
446
447 int mx, my;
448 SDL_GetMouseState(&mx, &my);
449 mouseRect.x = mx;
450 mouseRect.y = my;
451
452 JSDL.Blt(genPictures[GENPIC_CURSOR], mouseRect);
453 }
454
455 //this does two things:
456
457 //1. in windowed mode, it prevents it going so fast
458 //it actually slows down to a crawl, I think maybe
459 //because blits start having to wait for other ones to finish
460
461 //2. In full screen mode, it means if you have the game
462 //set to go more than 60fps it won't have a limit of the
463 //monitor refresh rate
464
465 if (!skipDisplayFrame)
466 JSDL.Flip();
467
468 return true;
469 }
470
PollInput()471 void PollInput() {
472 if (globalSettings.batch)
473 return;
474
475 SDL_Event event;
476
477 if (gsCurrent == GST_MainMenu || gsCurrent == GST_SetupBattle
478 || gsCurrent == GST_Score || gsCurrent == GST_ForceSelect) {
479 int result = SDL_WaitEvent(&event);
480 if (!result)
481 throw runtime_error("Error when waiting for event");
482 DealWithEvent(event);
483 return;
484 }
485
486 while (SDL_PollEvent(&event))
487 DealWithEvent(event);
488 }
489
DealWithEvent(SDL_Event & event)490 void DealWithEvent(SDL_Event& event) {
491 switch (event.type) {
492 case SDL_MOUSEBUTTONDOWN:
493 if (game && !WinMouseD(event.button.button, event.button.x, event.button.y))
494 game->MouseD(event.button.button, event.button.x, event.button.y);
495 break;
496
497 case SDL_MOUSEBUTTONUP:
498 if (game)
499 game->MouseU(event.button.button, event.button.x, event.button.y);
500 break;
501
502 case SDL_MOUSEMOTION:
503 WinMouseM(event.motion.state, event.motion.x, event.motion.y);
504 if (game)
505 game->MouseM(event.motion.state, event.motion.x, event.motion.y);
506 break;
507
508 case SDL_KEYDOWN:
509 if (WinKeyboard(event.key.keysym))
510 break;
511
512 switch(event.key.keysym.sym) {
513 case SDLK_F4:
514 CycleGroupInfoType();
515 break;
516
517 case SDLK_F8:
518 if (fpsState == false)
519 fpsState = true;
520 else
521 fpsState = false;
522 break;
523 }
524
525 if (game)
526 game->Keyboard(event.key.keysym);
527 break;
528
529 /* FIXME this doesn't work at all
530 case SDL_ACTIVEEVENT:
531 if (event.active.state & SDL_APPACTIVE && event.active.gain == 0) {
532 while (1) {
533 if (SDL_PollEvent(&event)) {
534 if (event.active.state == SDL_APPACTIVE && event.active.gain == 1)
535 break;
536 }
537 SDL_Delay(500);
538 }
539 }
540 break;
541 */
542
543 case SDL_QUIT:
544 gsTo = GST_TheOS;
545 break;
546
547 default:
548 break;
549 }
550 }
551
FPSCounter()552 void FPSCounter() {
553 static int fpsCounter = 0;
554 static int fpsSave = 0;
555 static unsigned int fpsTimer = SDL_GetTicks();
556
557 if (now - fpsTimer > 1000) {
558 fpsSave = fpsCounter;
559 fpsCounter = 0;
560 fpsTimer = now;
561 } else
562 ++fpsCounter;
563
564 char output[60];
565 sprintf(output, "FPS (sort of): %d", fpsSave);
566 normalFonts.BlitString(50, globalSettings.screenHeight - 20, 0, output);
567 }
568
GameShutdown(bool saveSettings)569 void GameShutdown(bool saveSettings) {
570 if (saveSettings)
571 SaveSettings();
572
573 ClearStandardGraphics();
574
575 JSDL.Shutdown();
576
577 //should already be deleted unless an exception has been thrown
578 SafeDelete(game);
579 }
580
ExceptionShutdown(runtime_error e)581 void ExceptionShutdown(runtime_error e) {
582 const string eWhat = e.what();
583
584 const string errorLog = "Exception shutdown due to: " + eWhat;
585 WriteLog(errorLog.c_str());
586
587 if (globalSettings.batch) {
588 GameShutdown();
589 terminate();
590 }
591
592 if (JSDL.screen->locked)
593 JSDL.UnlockBack();
594
595 const string errorWin = "Major problem:\n" + eWhat + "\n\nClick the left mouse button to exit";
596 CreateInfoString(errorWin);
597 list<GenWindow>::reverse_iterator iter = myWindows.rbegin();
598 iter->DrawSelf();
599
600 JSDL.ForceFlip();
601
602 while (1) {
603 SDL_Event event;
604 while (SDL_PollEvent(&event)) {
605 if (event.type == SDL_MOUSEBUTTONDOWN || event.type == SDL_KEYDOWN) {
606 GameShutdown();
607 terminate();
608 }
609 }
610 }
611 }
612
613