1 /*
2 * Kuklomenos
3 * Copyright (C) 2008-2009 Martin Bays <mbays@sdf.lonestar.org>
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 as published by
7 * the Free Software Foundation, either version 3 of the License, or
8 * (at your option) any later version.
9 *
10 * This program is distributed in the hope that it will be useful,
11 * but WITHOUT ANY WARRANTY; without even the implied warranty of
12 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
13 * GNU General Public License for more details.
14 *
15 * You should have received a copy of the GNU General Public License
16 * along with this program. If not, see http://www.gnu.org/licenses/.
17 */
18
19 #include <config.h>
20
21 #ifdef HAVE_LIBCURL
22 #define HIGH_SCORE_REPORTING 1
23 #endif
24
25 #include <cstdlib>
26 #include <cstdio>
27 #include <ctime>
28 #include <stack>
29 #include <SDL/SDL.h>
30 #include <SDL_gfxPrimitivesDirty.h>
31 #include <string>
32 #include <sstream>
33
34 #include "state.h"
35 #include "geom.h"
36 #include "clock.h"
37 #include "overlay.h"
38 #include "data.h"
39 #include "settings.h"
40 #include "conffile.h"
41 #include "random.h"
42 #include "ai.h"
43 #include "menu.h"
44 #include "keybindings.h"
45 #include "sound.h"
46 #include "background.h"
47
48 #ifdef HIGH_SCORE_REPORTING
49 # include "highScore.h"
50 #endif
51
52 #ifndef __APPLE__
53 # include "config.h"
54 #endif
55
56
57 SDL_Surface* screen = NULL;
58 std::stack<Menu*> menuStack;
59
60 enum EventsReturn
61 {
62 ER_NONE,
63 ER_MISC,
64 ER_QUIT,
65 ER_SURRENDER,
66 ER_RESTART,
67 ER_SCREENSHOT,
68 ER_NEWBACKGROUND,
69 ER_NOTIMETAKEN,
70 ER_MENU
71 };
72
setVideoMode()73 bool setVideoMode()
74 {
75 SDL_Surface* ret = SDL_SetVideoMode(settings.width, settings.height,
76 settings.bpp, settings.videoFlags);
77
78 if (!ret)
79 {
80 settings.width = screen->w;
81 settings.height = screen->h;
82 settings.bpp = screen->format->BitsPerPixel;
83 return false;
84 }
85
86 screen = ret;
87 screenGeom = ScreenGeom(settings.width, settings.height);
88
89 setBackground(screen);
90 setDirty(screen, background);
91
92 return true;
93 }
94
handleCommand(command c,GameState * gameState,GameClock & gameClock)95 EventsReturn handleCommand(command c, GameState* gameState, GameClock& gameClock)
96 {
97 switch (c)
98 {
99 case C_QUIT:
100 if (menuStack.empty())
101 return ER_QUIT;
102 else
103 while (!menuStack.empty())
104 menuStack.pop();
105 break;
106 case C_PAUSE:
107 gameClock.paused = !gameClock.paused;
108 break;
109 case C_MENU:
110 if (menuStack.empty())
111 menuStack.push(&topMenu);
112 else
113 menuStack.pop();
114 break;
115 case C_STARTGAME:
116 if (!menuStack.empty())
117 menuStack.pop();
118 else if (gameState->end || gameState->ai)
119 return ER_RESTART;
120 break;
121 #ifdef SOUND
122 case C_SOUND:
123 settings.sound = !settings.sound;
124 break;
125 #endif
126 case C_ZOOM:
127 settings.zoomEnabled = !settings.zoomEnabled;
128 break;
129 case C_ROTATE:
130 settings.rotatingView = !settings.rotatingView;
131 break;
132 case C_INCAA:
133 settings.useAA =
134 (settings.useAA == AA_NO) ? AA_YES :
135 AA_FORCE;
136 break;
137 case C_DECAA:
138 settings.useAA =
139 (settings.useAA == AA_FORCE) ? AA_YES :
140 AA_NO;
141 break;
142 case C_GRID:
143 settings.showGrid = !settings.showGrid;
144 break;
145 case C_DECFPS:
146 settings.fps = std::max(1, settings.fps-1);
147 break;
148 case C_INCFPS:
149 settings.fps = std::min(100, settings.fps+1);
150 break;
151 case C_DECRATE:
152 if (settings.debug || gameState->end || gameState->ai)
153 {
154 gameClock.rate = 4*gameClock.rate/5;
155 const int diff =
156 gameClock.rate - rateOfSpeed(gameState->speed);
157 if (abs(diff) < 100)
158 gameClock.rate = rateOfSpeed(gameState->speed);
159 }
160 break;
161 case C_RESETRATE:
162 gameClock.rate = rateOfSpeed(gameState->speed);
163 break;
164 case C_INCRATE:
165 if (settings.debug || gameState->end || gameState->ai)
166 {
167 gameClock.rate = std::max(5*gameClock.rate/4,
168 gameClock.rate+1);
169 const int diff =
170 gameClock.rate - rateOfSpeed(gameState->speed);
171 if (abs(diff) < 100)
172 gameClock.rate = rateOfSpeed(gameState->speed);
173 }
174 break;
175 case C_INVULN:
176 if (settings.debug)
177 settings.invuln = !settings.invuln;
178 break;
179 case C_WIN:
180 if (settings.debug)
181 gameState->end = END_WIN;
182 break;
183
184 #ifdef HIGH_SCORE_REPORTING
185 case C_REPORTHS:
186 if (gameState->end || gameClock.paused)
187 {
188 reportHighScore(screen, gameState->speed);
189 }
190 return ER_NOTIMETAKEN;
191 #endif
192 case C_SCREENSHOT:
193 return ER_SCREENSHOT;
194
195 default:
196
197 if (!menuStack.empty())
198 {
199 int dir =
200 (c == C_M_LEFT || c == C_LEFT) ? 0 :
201 (c == C_M_UP || c == C_DEZOOM) ? 1 :
202 (c == C_M_RIGHT || c == C_RIGHT) ? 2 :
203 (c == C_M_DOWN || c == C_DEAIM) ? 3 :
204 -1;
205
206 if (dir < 0)
207 break;
208
209 Menu* submenu = menuStack.top()->menus[dir];
210 MenuLeaf* leaf = menuStack.top()->leaves[dir];
211
212 if (submenu)
213 {
214 menuStack.push(submenu);
215 }
216 else if (leaf)
217 {
218 LeafReturn lr = leaf->act();
219 switch (lr)
220 {
221 case LR_QUIT:
222 return ER_QUIT;
223 case LR_SURRENDER:
224 while (!menuStack.empty())
225 menuStack.pop();
226 return ER_SURRENDER;
227 case LR_NEWSPEED:
228 if (gameState->ai)
229 {
230 // update speed on the fly, so the player
231 // can see what they're getting themself
232 // into...
233 gameClock.rate = rateOfSpeed(settings.speed);
234 }
235 break;
236 case LR_SETRES:
237 setVideoMode();
238 break;
239 case LR_EXITMENU:
240 menuStack.pop();
241 break;
242 case LR_SAVESETTINGS:
243 config.importSettings(settings);
244 config.write();
245 menuStack.pop();
246 break;
247 case LR_NEWBACKGROUND:
248 return ER_NEWBACKGROUND;
249 break;
250 default: ;
251 }
252 }
253 }
254 }
255 return ER_MISC;
256 }
257
process_events(GameState * gameState,GameClock & gameClock)258 EventsReturn process_events(GameState* gameState, GameClock& gameClock)
259 {
260 SDL_Event event;
261 while (SDL_PollEvent(&event))
262 {
263 if (event.type == SDL_QUIT)
264 return ER_QUIT;
265 else if (event.type == SDL_VIDEORESIZE)
266 {
267 settings.width = event.resize.w;
268 settings.height = event.resize.h;
269 setVideoMode();
270 return ER_MISC;
271 }
272 else if (event.type == SDL_KEYDOWN)
273 {
274 Key key(event.key.keysym);
275
276 if (settings.commandToBind != C_NONE)
277 {
278 if (key.sym >= SDLK_NUMLOCK && key.sym <= SDLK_COMPOSE)
279 // modifier - ignore
280 return ER_NONE;
281
282 settings.keybindings[settings.commandToBind] = key;
283
284 settings.commandToBind = C_NONE;
285 return ER_MISC;
286 }
287
288 for (command c = C_FIRST; c <= C_LASTDEBUG; c = command(c+1))
289 if (settings.keybindings.get(c) == key)
290 return handleCommand(c, gameState, gameClock);
291 }
292 }
293 return ER_NONE;
294 }
295
drawInfo(SDL_Surface * surface,GameState * gameState,GameClock & gameClock,float observedFPS)296 void drawInfo(SDL_Surface* surface, GameState* gameState,
297 GameClock& gameClock, float observedFPS)
298 {
299 int shownFPS = int(round(observedFPS));
300 char fpsStr[5+10+9];
301 snprintf(fpsStr, 5+10+9, "fps: %d/%d%s", shownFPS,
302 settings.fps, gameClock.paused ? " [Paused]" : "");
303
304 char ratingStr[8+20+7+3];
305 snprintf(ratingStr, 8+20+7+3, "rating: %.1f %s (%s)",
306 gameState->rating, ratingString((int)(gameState->rating)),
307 speedStringShort(gameState->speed));
308
309 char rateStr[6+5+10];
310 snprintf(rateStr, 6+5+10, "speed: %d.%d%s", gameClock.rate/1000,
311 (gameClock.rate%1000)/100, settings.debug ? " [*DEBUG*]" : "");
312
313 // if not enough room, try short version; if still too long don't display:
314 if ((int)strlen(fpsStr) > screenGeom.infoMaxLength)
315 snprintf(fpsStr, 5+10+9, "F: %d%s", shownFPS,
316 gameClock.paused ? " [P]" : "" );
317 if ((int)strlen(fpsStr) > screenGeom.infoMaxLength)
318 *fpsStr = '\0';
319
320 if ((int)strlen(ratingStr) > screenGeom.infoMaxLength)
321 snprintf(ratingStr, 8+20+7+5, "R: %.1f (%s)", gameState->rating,
322 speedStringShort(gameState->speed));
323 if ((int)strlen(ratingStr) > screenGeom.infoMaxLength)
324 *ratingStr = '\0';
325
326 if ((int)strlen(rateStr) > screenGeom.infoMaxLength)
327 snprintf(rateStr, 6+5+9, "S: %d.%d%s", gameClock.rate/1000,
328 (gameClock.rate%1000)/100, settings.debug ? " [D]" : "");
329 if ((int)strlen(rateStr) > screenGeom.infoMaxLength)
330 *rateStr = '\0';
331
332 gfxPrimitivesSetFont(fontSmall,7,13);
333
334 int line = 0;
335 if (settings.showFPS)
336 stringColor(surface,
337 screenGeom.info.x, screenGeom.info.y+15*line++,
338 fpsStr, 0xffffffff);
339
340
341 if (screenGeom.infoMaxLines > line)
342 {
343 stringColor(surface,
344 screenGeom.info.x, screenGeom.info.y+15*line++,
345 ratingStr, 0xffffffff);
346 }
347
348 if ( (settings.debug || gameClock.rate != rateOfSpeed(gameState->speed))
349 && screenGeom.infoMaxLines > line)
350 stringColor(surface,
351 screenGeom.info.x, screenGeom.info.y+15*line++,
352 rateStr, 0xffffffff);
353 }
354
drawSplash(SDL_Surface * surface)355 void drawSplash(SDL_Surface* surface)
356 {
357 int x = screenGeom.centre.x - 19*10/2;
358 int y = screenGeom.centre.y - screenGeom.rad/3;
359
360 // fade over time, and dim if menu up
361 const Uint32 titleColour = 0xff0000ff -
362 std::min(0xa0, (int)(SDL_GetTicks()/500) +
363 (menuStack.empty() ? 0 : 0x50));
364 const Uint32 instructColour = 0xffffffa0 -
365 std::min(0x60, (int)(SDL_GetTicks()/500) +
366 (menuStack.empty() ? 0 : 0x40));
367
368 gfxPrimitivesSetFont(fontBig, 10, 20);
369
370 if (x > 0)
371 stringColor(surface, x, y, "K U K L O M E N O S", titleColour);
372 else
373 {
374 // Not enough room for that
375 x = screenGeom.centre.x - 10*10/2;
376 stringColor(surface, x, y, "KUKLOMENOS", titleColour);
377 }
378
379 static Overlay splashInstructOverlay(0.35);
380 static bool setText = false;
381 if (!setText)
382 {
383 splashInstructOverlay.push_back(
384 string(" ") + settings.keybindings[C_STARTGAME].getString() +
385 string(" to start "));
386 splashInstructOverlay.push_back(
387 string(" ") + settings.keybindings[C_MENU].getString() +
388 string(" for menu "));
389 setText = true;
390 }
391
392 splashInstructOverlay.colour = instructColour;
393
394 splashInstructOverlay.draw(surface);
395 }
396
397
initialize_system()398 void initialize_system()
399 {
400 /* Initialize SDL */
401 if ( SDL_Init(SDL_INIT_EVERYTHING) < 0 ) {
402 fprintf(stderr,
403 "Couldn't initialize SDL: %s\n", SDL_GetError());
404 exit(1);
405 }
406 atexit(SDL_Quit); /* Clean up on exit */
407
408 SDL_EnableKeyRepeat(SDL_DEFAULT_REPEAT_DELAY, SDL_DEFAULT_REPEAT_INTERVAL);
409
410 // set random seed
411 srand(time(NULL));
412 }
413
initialize_video()414 void initialize_video()
415 {
416 /* Initialize the display */
417 if (settings.width != 0 && settings.height != 0)
418 {
419 // try the mode we've been given
420 screen = SDL_SetVideoMode(settings.width, settings.height,
421 settings.bpp, settings.videoFlags);
422 if ( screen == NULL ) {
423 fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n",
424 settings.width, settings.height, settings.bpp,
425 SDL_GetError());
426 }
427 }
428
429 if (screen == NULL)
430 {
431 // try a mode SDL tells us shold work:
432 SDL_Rect** modes = SDL_ListModes(NULL, settings.videoFlags);
433 if (modes == NULL)
434 {
435 fprintf(stderr,
436 "SDL reports no modes available\n");
437 exit(1);
438 }
439 if (modes == (SDL_Rect**)(-1))
440 {
441 // "All modes available"
442 settings.width = 1024;
443 settings.height = 768;
444 }
445 else
446 {
447 // use the first (i.e. biggest) mode which is no bigger than
448 // 1024x768
449 SDL_Rect* bestMode;
450 do
451 {
452 bestMode = *modes;
453 if (bestMode->w <= 1024 && bestMode->h <= 768)
454 break;
455 } while (++modes);
456 settings.width = bestMode->w;
457 settings.height = bestMode->h;
458 }
459
460 screen = SDL_SetVideoMode(settings.width, settings.height,
461 settings.bpp, settings.videoFlags);
462 if ( screen == NULL ) {
463 fprintf(stderr, "Couldn't set %dx%dx%d video mode: %s\n",
464 settings.width, settings.height, settings.bpp,
465 SDL_GetError());
466
467 // one last try... let SDL use any bpp:
468 screen = SDL_SetVideoMode(settings.width, settings.height,
469 settings.bpp, settings.videoFlags | SDL_ANYFORMAT);
470 if ( screen == NULL )
471 {
472 fprintf(stderr, "Couldn't set %dx%d video mode: %s\n",
473 settings.width, settings.height, SDL_GetError());
474 // give up
475 exit(1);
476 }
477 }
478 }
479
480 /* Show some info */
481 printf("Set %dx%dx%d mode\n",
482 screen->w, screen->h, screen->format->BitsPerPixel);
483 printf("Video surface located in %s memory.\n",
484 (screen->flags&SDL_HWSURFACE) ? "video" : "system");
485
486 SDL_WM_SetCaption( "Kuklomenos", NULL );
487 SDL_ShowCursor(SDL_DISABLE);
488
489 if ( !initFont() )
490 {
491 fprintf(stderr, "Failed to load font data file\n");
492 exit(1);
493 }
494
495 screenGeom = ScreenGeom(settings.width, settings.height);
496
497 setBackground(screen);
498 setDirty(screen, background);
499 }
500
haveInput()501 bool haveInput()
502 {
503 for (command c = C_FIRST; c <= C_LASTACTION; c = command(c+1))
504 if (settings.keybindings.get(c).isPressed())
505 return true;
506 return false;
507 }
508
run_game()509 void run_game()
510 {
511 if (settings.requestedRating == 0 &&
512 (!settings.debug || settings.wizard))
513 config.shouldUpdateRating = true;
514
515 // set up a game for the AI to play...
516 GameState* gameState = new GameState(settings.speed);
517 gameState->ai = new BasicAI(gameState);
518 GameClock gameClock(rateOfSpeed(settings.speed));
519
520 // main loop
521 Uint32 lastStateUpdate = 0, beforeDelay = 0;
522 Uint32 ticksBefore, ticksAfter;
523 Uint32 loopTicks = SDL_GetTicks();
524 Uint32 AIEndTick = 0;
525 bool splash = true;
526 int timeTillNextFrame = 0;
527 int delayTime, actualDelayed, updateTime;
528 float avFrameTime = 1000/settings.fps;
529 int fpsRegulatorTenthTicks = 0;
530 const int avFrames = 10; // number of frames to average over
531 EventsReturn eventsReturn = ER_NONE;
532 bool wantScreenshot = false;
533 bool wantVideo = false;
534 int videoFrame = 1;
535 bool quit = false;
536 bool ended = false;
537 bool forceFrame = false;
538
539 Overlay victoryOverlay(-0.2);
540 Overlay infoOverlay(0.2, 0xffffffff);
541
542 const int MIN_INPUT_STEP = 30;
543 const int MIN_GAME_STEP = 30;
544
545 while ( !quit ) {
546 forceFrame = false;
547 while (timeTillNextFrame > 0)
548 {
549 delayTime = std::min(timeTillNextFrame, MIN_INPUT_STEP);
550 beforeDelay = SDL_GetTicks();
551 SDL_Delay(delayTime);
552 actualDelayed = SDL_GetTicks() - beforeDelay;
553 timeTillNextFrame -= actualDelayed;
554
555 eventsReturn = process_events(gameState, gameClock);
556
557 switch (eventsReturn)
558 {
559 case ER_RESTART:
560 {
561 GameState* newGameState =
562 new GameState(settings.speed);
563 delete gameState;
564 gameState = newGameState;
565 gameClock = GameClock(rateOfSpeed(settings.speed));
566 }
567 ended = false;
568 victoryOverlay.clear();
569 infoOverlay.clear();
570 splash = false;
571 drawBackground(screen);
572 lastStateUpdate = SDL_GetTicks();
573 break;
574 case ER_QUIT:
575 quit = true;
576 break;
577 case ER_SURRENDER:
578 if (!ended)
579 gameState->end = END_DEAD;
580 break;
581 case ER_SCREENSHOT:
582 wantScreenshot = true;
583 break;
584 case ER_NEWBACKGROUND:
585 if (!background)
586 {
587 setBackground(screen);
588 setDirty(screen, background);
589 }
590 else
591 drawBackground(screen);
592 forceFrame = true;
593 break;
594 case ER_NOTIMETAKEN:
595 // pretend the time spent in process_events() didn't
596 // actually happen:
597 lastStateUpdate = SDL_GetTicks();
598 forceFrame = true;
599 break;
600 case ER_MISC:
601 forceFrame = true;
602 break;
603 default: ;
604 }
605
606 updateTime = std::max(1,
607 gameClock.scale(SDL_GetTicks() - lastStateUpdate));
608 lastStateUpdate = SDL_GetTicks();
609 if ( !gameClock.paused &&
610 ( (!settings.stopMotion || haveInput()) &&
611 menuStack.empty() ) || ended || gameState->ai )
612 {
613 while (updateTime > 0)
614 {
615 const int stepTime =
616 std::min( MIN_GAME_STEP, updateTime );
617 gameState->update(stepTime, !menuStack.empty());
618 gameClock.updatePreScaled(stepTime);
619 updateTime -= stepTime;
620 }
621 }
622 }
623
624 ticksBefore = SDL_GetTicks();
625 if (!gameClock.paused || forceFrame)
626 {
627 gameState->draw(screen);
628 drawInfo(screen, gameState, gameClock, 1000.0/avFrameTime);
629 victoryOverlay.draw(screen, menuStack.empty() ? 0xff : 0xa0);
630 infoOverlay.draw(screen, menuStack.empty() ? 0xff : 0xa0);
631 if (!menuStack.empty())
632 drawMenu(screen, *menuStack.top());
633 if (splash)
634 drawSplash(screen);
635 SDL_Flip(screen);
636
637 if (wantScreenshot)
638 {
639 if (SDL_SaveBMP(screen, "screenshot.bmp") != 0)
640 fprintf(stderr, "Screenshot failed.\n");
641 wantScreenshot = false;
642 }
643 if (wantVideo)
644 {
645 char fname[16];
646 snprintf(fname, 16, "frame%.5d.bmp", videoFrame);
647 SDL_SaveBMP(screen, fname);
648 videoFrame++;
649 }
650
651 // blank over what we've drawn:
652 blankDirty();
653 }
654 ticksAfter = SDL_GetTicks();
655
656 const int renderingTicks = ticksAfter - ticksBefore;
657
658 // Add in a manual tweak to the delay, to ensure we approximate the
659 // requested fps (where possible):
660 if (10000/avFrameTime < settings.fps * 10 &&
661 1000/settings.fps - renderingTicks + fpsRegulatorTenthTicks/10
662 > 1)
663 fpsRegulatorTenthTicks -= 1;
664 else if (10000/avFrameTime > settings.fps * 10)
665 fpsRegulatorTenthTicks += 1;
666
667 timeTillNextFrame =
668 std::max(1, (1000/settings.fps) - renderingTicks +
669 fpsRegulatorTenthTicks/10 + (rani(10) <
670 fpsRegulatorTenthTicks%10));
671
672 if (!ended && gameState->end && !gameState->ai)
673 {
674 // Game over, man
675 ended = true;
676 switch (gameState->end)
677 {
678 case END_DEAD:
679 case END_EXTRACTED:
680 victoryOverlay.push_back("Failure.");
681 victoryOverlay.colour = 0xff4444ff;
682 break;
683 case END_WIN:
684 victoryOverlay.push_back("Victory!");
685 victoryOverlay.colour = 0x88ff00ff;
686 break;
687 default: ;
688 }
689
690 // give a hint if appropriate
691 const char* hint = gameState->getHint();
692 if (hint)
693 infoOverlay.push_back(hint);
694
695 if (config.shouldUpdateRating)
696 {
697 int old = (int)(gameState->rating);
698
699 if (gameState->end == END_DEAD ||
700 gameState->end == END_EXTRACTED)
701 {
702 gameState->rating -= 0.1;
703 if (gameState->rating < 1)
704 gameState->rating = 1.0;
705
706 }
707 else if (gameState->end == END_WIN)
708 {
709 gameState->rating += 0.4;
710 if (gameState->rating < 5)
711 // extra boost, so clawing your way back up from the
712 // depths you sink to while first learning the game
713 // isn't too arduous:
714 gameState->rating += 0.2*(5 - (int)gameState->rating);
715 }
716
717 // deal with float inaccuracy - sometimes, 0.6+0.4 is just
718 // less that 1.0...
719 if ((gameState->rating - (int)(gameState->rating)) > 0.95)
720 gameState->rating = (int)(gameState->rating) + 1;
721
722 if ((int)(gameState->rating) != old)
723 {
724 ostringstream s;
725 s << "New rating: \"" <<
726 ratingString( (int)(gameState->rating) ) << "\".";
727 infoOverlay.push_back(s.str());
728 }
729 if (config.shouldUpdateRating)
730 {
731 // update ratings. Ratings are always kept ordered: lowest
732 // speed highest rating down to highest speed lowest
733 // rating.
734 bool newHigh = false;
735 for (int speed=0; speed<3; speed++)
736 if ( (speed <= gameState->speed &&
737 config.rating[speed] < gameState->rating)
738 || (speed >= gameState->speed &&
739 config.rating[speed] > gameState->rating) )
740 {
741 config.rating[speed] = gameState->rating;
742 if (config.highestRating[speed] < gameState->rating)
743 {
744 newHigh = true;
745 config.highestRating[speed] = gameState->rating;
746 }
747 }
748 if (newHigh)
749 {
750 infoOverlay.push_back("New high score!");
751 #ifdef HIGH_SCORE_REPORTING
752 infoOverlay.push_back(settings.keybindings[C_REPORTHS].getString()
753 + string(" to report to server"));
754 #endif
755 }
756 }
757 }
758 }
759
760 else if (gameState->end && gameState->ai)
761 {
762 if (!ended)
763 {
764 ended = true;
765 AIEndTick = SDL_GetTicks();
766 }
767 else if (SDL_GetTicks() - AIEndTick > 5000)
768 {
769 GameState* newGameState =
770 new GameState(settings.speed);
771 delete gameState;
772 gameState = newGameState;
773 gameState->ai = new BasicAI(gameState);
774 gameClock = GameClock(rateOfSpeed(settings.speed));
775 drawBackground(screen);
776 ended = false;
777 }
778 }
779
780
781 const int loopTime = SDL_GetTicks() - loopTicks;
782 avFrameTime += (loopTime-avFrameTime)/avFrames;
783 loopTicks = SDL_GetTicks();
784 }
785
786 if (!ended && gameState->extracted > 0)
787 {
788 // game is forfeit - reduce rating
789 gameState->rating -= 0.1;
790 if (gameState->rating < 1)
791 gameState->rating = 1.0;
792 }
793
794 config.write();
795
796 delete gameState->ai;
797 delete gameState;
798
799 SDL_Quit();
800 }
801
802 #ifndef __APPLE__
main(int argc,char ** argv)803 int main(int argc, char** argv)
804 {
805 load_settings(argc, argv);
806 initialize_system();
807 initialize_video();
808 run_game();
809
810 return 0;
811 }
812 #endif /* __APPLE__ */
813