1 /* Pushover
2 *
3 * Pushover is the legal property of its developers, whose
4 * names are listed in the COPYRIGHT file, which is included
5 * within the source distribution.
6 *
7 * This program is free software; you can redistribute it and/or
8 * modify it under the terms of the GNU General Public License
9 * as published by the Free Software Foundation; either version 2
10 * of the License, or (at your option) any later version.
11
12 * This program is distributed in the hope that it will be useful,
13 * but WITHOUT ANY WARRANTY; without even the implied warranty of
14 * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
15 * GNU General Public License for more details.
16
17 * You should have received a copy of the GNU General Public License
18 * along with this program; if not, write to the Free Software
19 * 51 Franklin Street, Fifth Floor, Boston, MA 02110-1335 USA
20 */
21
22 #include "graphicsn.h"
23 #include "textsections.h"
24 #include "levelplayer.h"
25 #include "levelset.h"
26 #include "ant.h"
27 #include "recorder.h"
28 #include "soundsys.h"
29 #include "screen.h"
30 #include "window.h"
31 #include "solvedmap.h"
32 #include "tools.h"
33
34 #include <SDL.h>
35
36 #include <vector>
37 #include <fstream>
38 #include <stdexcept>
39
40 #include <stdio.h>
41 #include <stdlib.h>
42 #include <sys/types.h>
43 #include <sys/stat.h>
44 #include <unistd.h>
45
46 #if defined(__AROS__)
47 #include <proto/dos.h>
48 #endif
49
check_record(const std::string & rec_path,levelsetList_c & levelsetList,unsigned int & count,unsigned int & failed,std::string checker (const ant_c & a,const levelData_c & l))50 static void check_record(const std::string & rec_path, levelsetList_c & levelsetList,
51 unsigned int & count, unsigned int & failed,
52 std::string checker(const ant_c & a, const levelData_c & l)) {
53 recorder_c rec;
54 rec.load(rec_path);
55
56 graphicsN_c gr("");
57 surface_c surf;
58 levelPlayer_c l(surf, gr);
59 levelsetList.getLevelset(rec.getLevelsetName()).loadLevel(l, rec.getLevelName(), "");
60 ant_c a(l, gr, surf);
61
62 while (!rec.endOfRecord()) {
63 a.setKeyStates(rec.getEvent());
64 l.performDoors();
65 l.performDominos(a);
66 }
67
68 // add a few more iterations at the end to make sure the ant has left the level
69 a.setKeyStates(0);
70 for (unsigned int j = 0; j < 100; j++)
71 {
72 l.performDoors();
73 l.performDominos(a);
74 }
75
76 const std::string error = checker(a, l);
77 if (!error.empty()) {
78 std::cout << rec_path << " " << error << std::endl;
79 failed++;
80 }
81 count++;
82 }
83
check(int argn,char * argv[],std::string checker (const ant_c & a,const levelData_c & l))84 void check(int argn, char * argv[], std::string checker(const ant_c & a, const levelData_c & l)) {
85 levelsetList_c levelsetList;
86 levelsetList.load("levels", "");
87
88 unsigned int count = 0;
89 unsigned int failed = 0;
90
91 for (int i = 0; i < argn; i++)
92 {
93 const std::string path(argv[i]);
94 struct stat st;
95 if (stat(path.c_str(), &st) != 0)
96 throw std::runtime_error("file or directory does not exist: " + path);
97 if (S_ISDIR(st.st_mode))
98 {
99 const std::vector<std::string> entries = directoryEntries(path);
100 for (std::vector<std::string>::const_iterator j = entries.begin(); j != entries.end(); j++) {
101 const std::string & filename = *j;
102 if (filename.size() > 0 && filename[0] != '.')
103 check_record(path + '/' + filename, levelsetList, count, failed, checker);
104 }
105 }
106 else
107 {
108 check_record(path, levelsetList, count, failed, checker);
109 }
110 }
111
112 std::cout << failed << " out of " << count << " tests failed\n";
113 }
114
checker1(const ant_c & a,const levelData_c & l)115 std::string checker1(const ant_c & a, const levelData_c & l) {
116
117 int i;
118 // we succeeded, when the ant has vanished, then it went out of the door
119 if (a.isVisible() == false && l.levelCompleted(i))
120 return "";
121 else
122 return "Level not Finished";
123 }
124
checker2(const ant_c & a,const levelData_c & l)125 std::string checker2(const ant_c & a, const levelData_c & l) {
126
127 int i;
128 // we succeeded, when the ant has vanished, then it went out of the door
129 if (a.isVisible() == true || !l.levelCompleted(i))
130 return "";
131 else
132 return "Level not Failed";
133 }
134
checker3(const ant_c & a,const levelData_c & l)135 std::string checker3(const ant_c & a, const levelData_c & l) {
136 // we succeeded, when the ant has vanished, then it went out of the door
137 for (int y = 0; y < 13; y++)
138 for (int x = 0; x < 20; x++)
139 if (l.getDominoType(x, y) >= levelData_c::DominoTypeCrash0 &&
140 l.getDominoType(x, y) <= levelData_c::DominoTypeCrash5) {
141 return "";
142 }
143
144 return "Crashes didn't happen";
145 }
146
getDataDir(void)147 static std::string getDataDir(void)
148 {
149 #if defined (__AROS__)
150 static char binaryPath[1024] = {0};
151 if (!*binaryPath)
152 NameFromLock(GetProgramDir(), binaryPath, sizeof(binaryPath));
153 return std::string(binaryPath);
154 #else
155 struct stat st;
156 return std::string((stat(PKGDATADIR, &st) == 0) ? PKGDATADIR : ".");
157 #endif
158 }
159
getLocaleDir(void)160 static std::string getLocaleDir(void)
161 {
162 #if defined (__AROS__)
163 static char localePath[1024] = {0};
164 if (!*localePath)
165 {
166 AddPart(localePath, getDataDir().c_str(), sizeof(localePath));
167 AddPart(localePath, "locale", sizeof(localePath));
168 }
169 return localePath;
170 #else
171 struct stat st;
172 return std::string((stat(LOCALEDIR, &st) == 0) ? LOCALEDIR : "locale");
173 #endif
174 }
175
getKeyMask(void)176 static unsigned int getKeyMask(void) {
177 unsigned int keyMask = 0;
178
179 Uint8 *keystate = SDL_GetKeyState(NULL);
180
181 if ( keystate[SDLK_UP] ) keyMask |= KEY_UP;
182 if ( keystate[SDLK_DOWN] ) keyMask |= KEY_DOWN;
183 if ( keystate[SDLK_LEFT] ) keyMask |= KEY_LEFT;
184 if ( keystate[SDLK_RIGHT] ) keyMask |= KEY_RIGHT;
185 if ( keystate[SDLK_SPACE] ) keyMask |= KEY_ACTION;
186
187 return keyMask;
188 }
189
190 // make a play tick
191 // return codes:
192 // 0 nothing happened
193 // 1 success
194 // 2 too slow
195 // 3 crashes
196 // 4 not all dominos fell
197 // 5 die
198 // 6 trigger not last to fall
199 // 7 trigger not flat on the ground
200 //
playTick(levelPlayer_c & l,ant_c & a)201 int playTick(levelPlayer_c & l, ant_c & a)
202 {
203 l.performDoors();
204 int res = l.performDominos(a);
205
206 l.updateBackground();
207 l.drawDominos();
208 a.draw();
209
210 if (l.triggerIsFalln() && !a.isVisible() && l.isExitDoorClosed() && (res == 0)) {
211
212 if (l.someTimeLeft())
213 {
214 return 1;
215 }
216 else
217 {
218 return 2;
219 }
220 }
221
222 if (!a.isLiving())
223 return 5;
224
225 return res;
226 }
227
228 // the states of the program
229 typedef enum {
230 ST_INIT, // initial transition state
231 ST_MAIN, // main menu
232 ST_PROFILE, // state for profile selection
233 ST_PROFILE_INIT, // profile selection at startup
234 ST_PROFILE_IN, // profile name input
235 ST_PROFILE_DEL, // select profile to delete
236 ST_LEVELCONF,// configure while playing
237 ST_CONFIG, // configure menu
238 ST_LEVELSET, // level set selection
239 ST_LEVEL, // level selection
240 ST_PREREPLAY,// prepare replay for running
241 ST_REPLAY, // replay currently running
242 ST_PREPLAY, // prepare level for playing
243 ST_PLAY, // currently playing
244 ST_FAILDELAY,// play a second more after failing
245 ST_SOLVED, // current level solved
246 ST_TIMEOUT, // took longer than level time
247 ST_FAILED, // current level failed (reason in failreason)
248 ST_HELP, // help dialog showing
249 ST_QUIT, // play exit dialog showing
250 ST_EXIT, // exiting program
251 ST_ABOUT,
252 } states_e;
253
loadAllLevels(const std::string & datadir,const std::string & userString)254 static levelsetList_c * loadAllLevels(const std::string & datadir, const std::string & userString)
255 {
256 levelsetList_c * levelsetList = new levelsetList_c();
257
258 levelsetList->load(datadir + "/levels", userString);
259 {
260 const std::string userleveldir(getHome() + "/.pushover/levels");
261 struct stat st;
262 if (stat(userleveldir.c_str(), &st) == 0)
263 levelsetList->load(userleveldir, userString);
264 }
265
266 return levelsetList;
267 }
268
269
main(int argc,char * argv[])270 int main(int argc, char * argv[]) {
271
272 // filter out the no graphic cases, they are special and will be treated
273 // separately
274 if (argc >= 3 && strcmp(argv[1], "-c") == 0) // the must complete tests
275 {
276 check(argc-2, argv+2, checker1);
277 return 0;
278 }
279
280 if (argc >= 3 && strcmp(argv[1], "-y") == 0) // the must complete tests
281 {
282 check(argc-2, argv+2, checker2);
283 return 0;
284 }
285
286 if (argc >= 3 && strcmp(argv[1], "-x") == 0) // the must crash tests
287 {
288 check(argc-2, argv+2, checker3);
289 return 0;
290 }
291
292 bool fullscreen = false;
293 if (argc >= 2 && strcmp(argv[1], "-f") == 0) fullscreen = true;
294
295 // setup internationalization
296 #if defined (__AROS__)
297 amigaLocale(LC_MESSAGES);
298 #endif
299 setlocale(LC_MESSAGES, "");
300 bindtextdomain("pushover", getLocaleDir().c_str());
301 bind_textdomain_codeset("pushover", "UTF-8");
302 textdomain("pushover");
303
304 // now off to all modes that use graphics
305 const std::string datadir = getDataDir();
306 std::string selectedMission; // the mission that was selected in menu
307
308 // initialize random number generator
309 srandFromTime();
310
311 // initialize SDL, graphics, timer, video mode, and level data structure
312 SDL_Init(SDL_INIT_TIMER);
313 SDL_Init(SDL_INIT_VIDEO);
314 SDL_EnableUNICODE(1);
315 atexit(SDL_Quit);
316 graphicsN_c gr(datadir);
317 screen_c screen(gr);
318 if (fullscreen) screen.toggleFullscreen();
319 gr.loadGraphics();
320 initText(datadir);
321 soundSystem_c::instance()->openSound(datadir);
322 levelPlayer_c l(screen, gr);
323 recorder_c rec;
324 ant_c a(l, gr, screen);
325 solvedMap_c solved;
326 std::string lwindowLevel = ""; // when it contains a valid levelname, the level is selected when the levelwindow is opened the next time
327
328 // prepare the list of levelsets
329 levelsetList_c * levelsetList = loadAllLevels(datadir, "");
330
331 states_e currentState = ST_INIT, nextState = ST_INIT;
332
333 if (argc == 3 && strcmp(argv[1], "-r") == 0)
334 {
335 // try to load the record and the level that belongs to it
336 // if it fails, fall back to main menu
337 try {
338 rec.load(argv[2]);
339 selectedMission = rec.getLevelsetName();
340 levelsetList->getLevelset(selectedMission).loadLevel(l, rec.getLevelName(), "");
341 a.initForLevel();
342 soundSystem_c::instance()->playMusic(datadir+"/themes/"+l.getTheme()+".ogg");
343
344 nextState = ST_REPLAY;
345 }
346 catch (...) {
347 nextState = ST_PROFILE_INIT;
348 }
349 }
350 else if (argc == 3)
351 {
352 // start with a given level set and level name
353 selectedMission = argv[1];
354 std::string levelName = argv[2];
355
356 // try to load the given levelset and level, if
357 // it fails fall back to main menu
358 try {
359
360 levelsetList->getLevelset(selectedMission).loadLevel(l, levelName, "");
361 a.initForLevel();
362 soundSystem_c::instance()->playMusic(datadir+"/themes/"+l.getTheme()+".ogg");
363
364 nextState = ST_PREPLAY;
365 }
366 catch (...) {
367 nextState = ST_PROFILE_INIT;
368 }
369 }
370 else if (argc == 2)
371 {
372 // start with a given file if the file exists,
373 // load it and play is, otherwise go to main menu
374 std::ifstream file(argv[1]);
375 if (file)
376 {
377 textsections_c sections(file, true);
378 l.load(sections, "");
379 selectedMission = "Original"; // TODO we need to find out which levelset this file belongs to
380 soundSystem_c::instance()->playMusic(datadir+"/themes/"+l.getTheme()+".ogg");
381
382 nextState = ST_PREPLAY;
383 }
384 else
385 {
386 nextState = ST_PROFILE_INIT;
387 screen.markAllDirty();
388 }
389 }
390 else
391 {
392 nextState = ST_PROFILE_INIT;
393 screen.markAllDirty();
394 }
395
396
397 // not we have initialized, lets get playing in the main state
398 bool exitProgram = false;
399 Uint32 ticks = SDL_GetTicks();
400
401 window_c * window = 0; // the currently visible window
402 unsigned int failReason = 0;
403 unsigned int failDelay = 0; // a counter to delay the fail window a bit after failing
404
405 if (nextState == ST_MAIN)
406 {
407 soundSystem_c::instance()->playMusic(datadir+"/themes/option.ogg");
408 }
409
410 try {
411
412 while (!exitProgram) {
413
414 // wait for the right amount of time for the next frame
415 ticks += 1000/18;
416 if (SDL_GetTicks() < ticks)
417 SDL_Delay(ticks-SDL_GetTicks());
418
419 while (true) {
420
421 if (nextState != currentState) {
422
423 // switch states, first leave the old one
424 switch (currentState) {
425
426 case ST_MAIN:
427 case ST_HELP:
428 case ST_QUIT:
429 case ST_LEVELSET:
430 case ST_LEVEL:
431 case ST_CONFIG:
432 case ST_LEVELCONF:
433 case ST_SOLVED:
434 case ST_FAILED:
435 case ST_ABOUT:
436 case ST_PROFILE:
437 case ST_PROFILE_INIT:
438 case ST_PROFILE_IN:
439 case ST_PROFILE_DEL:
440 case ST_TIMEOUT:
441 delete window;
442 window = 0;
443 l.drawDominos();
444 a.draw();
445 break;
446
447 case ST_PREREPLAY:
448 case ST_PREPLAY:
449 case ST_INIT:
450 case ST_REPLAY:
451 case ST_PLAY:
452 case ST_FAILDELAY:
453 case ST_EXIT:
454 break;
455
456 }
457
458 // now enter the new one
459 switch (nextState) {
460
461 case ST_MAIN: window = getMainWindow(screen, gr); break;
462 case ST_PROFILE_INIT:
463 case ST_PROFILE: window = getProfileWindow(solved, screen, gr); break;
464 case ST_PROFILE_IN: window = getProfileInputWindow(screen, gr); break;
465 case ST_PROFILE_DEL: window = getProfileSelector(solved, screen, gr); break;
466 case ST_LEVELSET: window = getMissionWindow(*levelsetList, screen, gr, selectedMission); break;
467 case ST_QUIT: window = getQuitWindow(screen, gr); break;
468 case ST_LEVELCONF:
469 case ST_CONFIG: window = getConfigWindow(screen, gr); break;
470 case ST_SOLVED: window = getSolvedWindow(screen, gr); break;
471 case ST_ABOUT: window = getAboutWindow(screen, gr); break;
472 case ST_FAILED: window = getFailedWindow(failReason, screen, gr); break;
473 case ST_TIMEOUT: window = getTimeoutWindow(screen, gr); break;
474
475
476 case ST_LEVEL:
477 window = getLevelWindow(levelsetList->getLevelset(selectedMission), solved, screen, gr, lwindowLevel);
478 lwindowLevel = "";
479 break;
480
481 case ST_HELP:
482 {
483 std::string text;
484 if (l.someTimeLeft())
485 text = _("Arrange dominos in a run so that trigger falls last. You have 1 push.");
486 else
487 text = l.getHint();
488
489 window = new helpWindow_c(text, screen, gr);
490 }
491 break;
492
493 case ST_PREREPLAY:
494 case ST_PREPLAY:
495 l.updateBackground();
496 l.drawDominos();
497 a.draw();
498 ticks = SDL_GetTicks(); // this might have taken some time so reinit the ticks
499 break;
500
501 case ST_PLAY:
502 ticks = SDL_GetTicks(); // the flip might have taken some time too long
503 rec.setLevel(selectedMission, l.getName());
504 rec.reset();
505 break;
506 case ST_INIT:
507 case ST_REPLAY:
508 case ST_FAILDELAY:
509 break;
510
511 case ST_EXIT:
512 exitProgram = true;
513 break;
514
515 }
516
517 currentState = nextState;
518 }
519
520 // if there is no event to process, leave the loop, otherwise handle the event and loop again
521 SDL_Event event;
522 if (!SDL_PollEvent(&event)) break;
523
524 // check for quit-event
525 if (event.type == SDL_QUIT)
526 {
527 // exit program, wherever we are
528 nextState = ST_EXIT;
529 break;
530 }
531
532 switch (currentState) {
533
534 case ST_MAIN:
535 if (!window) {
536 std::cout << "Oops no window\n";
537 nextState = ST_MAIN;
538 }
539 else
540 {
541 window->handleEvent(event);
542 if (window->isDone())
543 {
544 switch(dynamic_cast<listWindow_c*>(window)->getSelection())
545 {
546 case 0: nextState = ST_LEVELSET; break;// select level set
547 case 1: nextState = ST_CONFIG; break; // open config menu
548 case 2: nextState = ST_PROFILE; break; // open profile selector
549 case 3: nextState = ST_ABOUT; break; // about window
550 case 4: nextState = ST_EXIT; break; // exit program
551 }
552 }
553 }
554 break;
555
556 case ST_PROFILE:
557 case ST_PROFILE_INIT:
558 if (!window) {
559 std::cout << "Oops no window\n";
560 nextState = ST_MAIN;
561 }
562 else
563 {
564 window->handleEvent(event);
565 if (window->isDone())
566 {
567 unsigned int sel = dynamic_cast<listWindow_c*>(window)->getSelection();
568 if (sel < solved.getNumberOfUsers())
569 {
570 solved.selectUser(sel);
571 delete levelsetList;
572 levelsetList = loadAllLevels(datadir, solved.getUserString());
573
574 nextState = ST_MAIN;
575 }
576 else if (sel == solved.getNumberOfUsers())
577 {
578 nextState = ST_PROFILE_IN;
579 }
580 else if (sel == solved.getNumberOfUsers()+1)
581 {
582 nextState = ST_PROFILE_DEL;
583 }
584 else
585 {
586 nextState = ST_MAIN;
587 }
588 }
589 }
590 break;
591
592 case ST_PROFILE_DEL:
593 if (!window) {
594 std::cout << "Oops no window\n";
595 nextState = ST_MAIN;
596 }
597 else
598 {
599 window->handleEvent(event);
600 if (window->isDone())
601 {
602 size_t s = dynamic_cast<listWindow_c*>(window)->getSelection();
603
604 if (s+1 < solved.getNumberOfUsers())
605 {
606 // valid selection available
607
608 // if the currently selected profile is deleted, go to default
609 if (s+1 == solved.getCurrentUser())
610 {
611 solved.selectUser(0);
612 delete levelsetList;
613 levelsetList = loadAllLevels(datadir, solved.getUserString());
614 }
615
616 solved.deleteUser(s+1);
617 }
618
619 nextState = ST_PROFILE;
620 }
621 }
622 break;
623
624 case ST_PROFILE_IN:
625 if (!window) {
626 std::cout << "Oops no window\n";
627 nextState = ST_MAIN;
628 }
629 else
630 {
631 window->handleEvent(event);
632 if (window->isDone())
633 {
634 if (!dynamic_cast<InputWindow_c*>(window)->hasEscaped())
635 {
636 solved.addUser(dynamic_cast<InputWindow_c*>(window)->getText());
637 delete levelsetList;
638 levelsetList = loadAllLevels(datadir, solved.getUserString());
639 nextState = ST_MAIN;
640 }
641 else
642 {
643 nextState = ST_PROFILE;
644 }
645 }
646 }
647 break;
648
649 case ST_CONFIG:
650 case ST_LEVELCONF:
651 if (!window) {
652 std::cout << "Oops no window\n";
653 nextState = ST_MAIN;
654 }
655 else
656 {
657 window->handleEvent(event);
658 if (window->isDone())
659 {
660 switch(dynamic_cast<listWindow_c*>(window)->getSelection())
661 {
662 case 0: // toggle full screen
663 screen.toggleFullscreen();
664 screen.markAllDirty();
665 window->resetWindow();
666 break;
667
668 case 1: // toggle sound effects
669 soundSystem_c::instance()->toggleSound();
670 window->resetWindow();
671 break;
672
673 case 2: // toggle music
674 soundSystem_c::instance()->toggleMusic();
675 window->resetWindow();
676 break;
677
678 default:
679 if (currentState == ST_CONFIG)
680 nextState = ST_MAIN; // back to main menu
681 else
682 nextState = ST_QUIT; // back to level quit dialog
683
684 break;
685 }
686 }
687 }
688 break;
689
690 case ST_LEVELSET:
691 if (!window) {
692 std::cout << "Oops no window\n";
693 nextState = ST_MAIN;
694 }
695 else
696 {
697 window->handleEvent(event);
698 if (window->isDone())
699 {
700 unsigned int sel = dynamic_cast<listWindow_c*>(window)->getSelection();
701 if (sel >= levelsetList->getLevelsetNames().size())
702 nextState = ST_MAIN;
703 else
704 {
705 nextState = ST_LEVEL;
706 lwindowLevel = "";
707 selectedMission = levelsetList->getLevelsetNames()[sel];
708 }
709 }
710 }
711 break;
712
713 case ST_LEVEL:
714 if (!window) {
715 std::cout << "Oops no window\n";
716 nextState = ST_MAIN;
717 }
718 else
719 {
720 window->handleEvent(event);
721 if (window->isDone())
722 {
723 unsigned int sel = dynamic_cast<listWindow_c*>(window)->getSelection();
724 levelset_c ls = levelsetList->getLevelset(selectedMission);
725
726 if (sel >= ls.getLevelNames().size())
727 nextState = ST_LEVELSET;
728 else
729 {
730 nextState = ST_PREPLAY;
731 ls.loadLevel(l, ls.getLevelNames()[sel], solved.getUserString());
732 soundSystem_c::instance()->playMusic(datadir+"/themes/"+l.getTheme()+".ogg");
733 a.initForLevel();
734 }
735 }
736 }
737 break;
738
739 case ST_PLAY:
740 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) nextState = ST_QUIT;
741 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_F1) nextState = ST_HELP;
742 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == 'r') rec.save("man");
743 break;
744
745 case ST_PREPLAY:
746 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) nextState = ST_PLAY;
747 break;
748
749 case ST_PREREPLAY:
750 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE) nextState = ST_REPLAY;
751 break;
752
753 case ST_REPLAY:
754 if (event.type == SDL_KEYDOWN && event.key.keysym.sym == SDLK_ESCAPE)
755 {
756 rec.truncate();
757 nextState = ST_PLAY;
758 }
759 break;
760
761 case ST_HELP:
762 if (!window) {
763 std::cout << "Oops no window\n";
764 nextState = ST_MAIN;
765 }
766 else
767 {
768 window->handleEvent(event);
769 if (window->isDone())
770 nextState = ST_PLAY;
771 }
772 break;
773
774 case ST_ABOUT:
775 if (!window) {
776 std::cout << "Oops no window\n";
777 nextState = ST_MAIN;
778 }
779 else
780 {
781 window->handleEvent(event);
782 if (window->isDone())
783 nextState = ST_MAIN;
784 }
785 break;
786
787 case ST_QUIT:
788 if (!window) {
789 std::cout << "Oops no window\n";
790 nextState = ST_MAIN;
791 }
792 else
793 {
794 window->handleEvent(event);
795 if (window->isDone())
796 {
797 switch(dynamic_cast<listWindow_c*>(window)->getSelection())
798 {
799 case 3:
800 nextState = ST_LEVEL;
801 soundSystem_c::instance()->playMusic(datadir+"/themes/option.ogg");
802 lwindowLevel = l.getName();
803 break; // return to level list
804 case 1:
805 { // restart level
806 nextState = ST_PLAY;
807 levelset_c ls = levelsetList->getLevelset(selectedMission);
808 ls.loadLevel(l, l.getName(), solved.getUserString());
809 a.initForLevel();
810 }
811 break;
812 case 2: // configuration
813 nextState = ST_LEVELCONF;
814 break;
815 default:
816 case 0:
817 nextState = ST_PLAY;
818 break; // return to play
819 }
820 }
821 }
822 break;
823
824 case ST_SOLVED:
825 if (!window) {
826 std::cout << "Oops no window\n";
827 nextState = ST_MAIN;
828 }
829 else
830 {
831 window->handleEvent(event);
832 if (window->isDone())
833 {
834 switch(dynamic_cast<listWindow_c*>(window)->getSelection())
835 {
836 case 0:
837 nextState = ST_LEVEL;
838 lwindowLevel = "";
839 break; // select next level to play
840 }
841 }
842 }
843 break;
844
845 case ST_FAILED:
846 if (!window) {
847 std::cout << "Oops no window\n";
848 nextState = ST_MAIN;
849 }
850 else
851 {
852 window->handleEvent(event);
853 if (window->isDone())
854 {
855 switch(dynamic_cast<listWindow_c*>(window)->getSelection())
856 {
857 case 0: // try again
858 {
859 // find the current level
860 levelset_c ls = levelsetList->getLevelset(selectedMission);
861
862 std::vector<std::string> levels = ls.getLevelNames();
863
864 bool foundLevel = false;
865
866 for (unsigned int i = 0; i < levels.size(); i++)
867 {
868 if (levels[i] == l.getName())
869 {
870 ls.loadLevel(l, levels[i], solved.getUserString());
871 a.initForLevel();
872 foundLevel = true;
873 break;
874 }
875 }
876
877 if (!foundLevel)
878 {
879 nextState = ST_MAIN;
880 }
881 else
882 {
883 nextState = ST_PLAY;
884 }
885 }
886 break;
887 case 1:
888 nextState = ST_LEVEL;
889 lwindowLevel = l.getName();
890 soundSystem_c::instance()->playMusic(datadir+"/themes/option.ogg");
891 break; // back to level list
892 }
893 }
894 }
895 break;
896
897 case ST_TIMEOUT:
898 if (!window) {
899 std::cout << "Oops no window\n";
900 nextState = ST_MAIN;
901 }
902 else
903 {
904 window->handleEvent(event);
905 if (window->isDone())
906 {
907 switch(dynamic_cast<listWindow_c*>(window)->getSelection())
908 {
909 case 0: // try again
910 {
911 // find the current level
912 levelset_c ls = levelsetList->getLevelset(selectedMission);
913
914 std::vector<std::string> levels = ls.getLevelNames();
915
916 bool foundLevel = false;
917
918 for (unsigned int i = 0; i < levels.size(); i++)
919 {
920 if (levels[i] == l.getName())
921 {
922 ls.loadLevel(l, levels[i], solved.getUserString());
923 a.initForLevel();
924 foundLevel = true;
925 break;
926 }
927 }
928
929 if (!foundLevel)
930 {
931 nextState = ST_MAIN;
932 }
933 else
934 {
935 nextState = ST_PLAY;
936 }
937 }
938 break;
939 case 1: // continue to next level
940 nextState = ST_LEVEL;
941 lwindowLevel = "";
942 break; // select next level to play
943 }
944 }
945 }
946 break;
947
948 case ST_INIT:
949 case ST_FAILDELAY:
950 case ST_EXIT:
951 break;
952 }
953 }
954
955 // do the handling of the current state
956
957 switch (currentState) {
958
959 case ST_PREPLAY:
960 if (screen.flipAnimate()) nextState = ST_PLAY;
961 break;
962
963 case ST_PREREPLAY:
964 if (screen.flipAnimate()) nextState = ST_REPLAY;
965 break;
966
967 case ST_FAILDELAY:
968 if (failDelay > 0)
969 {
970 failDelay--;
971 a.setKeyStates(0);
972 playTick(l, a);
973 }
974 else
975 {
976 nextState = ST_FAILED;
977 }
978 break;
979
980 case ST_REPLAY:
981 if (rec.endOfRecord())
982 nextState = ST_PLAY;
983 else
984 {
985 a.setKeyStates(rec.getEvent());
986 if (playTick(l, a))
987 nextState = ST_MAIN;
988 break;
989 }
990 // intentionally fall through to ST_PLAY when
991 // the record has ended to allow the player to
992 // continue playing
993
994 case ST_PLAY:
995 {
996 unsigned int keyMask = getKeyMask();
997 rec.addEvent(keyMask);
998 a.setKeyStates(keyMask);
999 }
1000 failReason = playTick(l, a);
1001
1002 if (l.levelInactive())
1003 {
1004 switch (failReason) {
1005 case 1:
1006 rec.save("sol");
1007 solved.addLevel(l.getChecksum());
1008 nextState = ST_SOLVED;
1009 break;
1010 case 0:
1011 break;
1012 case 2:
1013 rec.save("time");
1014 solved.addLevel(l.getChecksumNoTime());
1015 nextState = ST_TIMEOUT;
1016 break;
1017 default:
1018 failDelay = 36;
1019 nextState = ST_FAILDELAY;
1020 break;
1021 }
1022 }
1023 break;
1024
1025 case ST_INIT:
1026 case ST_MAIN:
1027 case ST_PROFILE:
1028 case ST_PROFILE_INIT:
1029 case ST_PROFILE_IN:
1030 case ST_PROFILE_DEL:
1031 case ST_CONFIG:
1032 case ST_LEVELCONF:
1033 case ST_LEVELSET:
1034 case ST_LEVEL:
1035 case ST_SOLVED:
1036 case ST_FAILED:
1037 case ST_TIMEOUT:
1038 case ST_HELP:
1039 case ST_QUIT:
1040 case ST_EXIT:
1041 case ST_ABOUT:
1042 break;
1043 }
1044
1045 // flip the screen, but not when in the preplaymodes
1046 if (currentState != ST_PREPLAY && currentState != ST_PREREPLAY)
1047 {
1048 screen.flipDirty();
1049 screen.clearDirty();
1050 }
1051 }
1052 }
1053
1054 catch (...) {
1055
1056 if (currentState == ST_PLAY)
1057 rec.save("err");
1058
1059 }
1060
1061 return 0;
1062 }
1063