]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/CEngine.cpp
Get rid of false positive warnings from cppcheck.
[quix0rs-blobwars.git] / src / CEngine.cpp
1 /*
2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
4
5 This program is free software; you can redistribute it and/or
6 modify it under the terms of the GNU General Public License
7 as published by the Free Software Foundation; either version 2
8 of the License, or (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.
13
14 See the GNU General Public License for more details.
15
16 You should have received a copy of the GNU General Public License
17 along with this program; if not, write to the Free Software
18 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA  02111-1307, USA.
19
20 */
21
22 #include "headers.h"
23 #include <errno.h>
24 extern Graphics graphics;
25
26 Engine::Engine()
27 {
28         memset(keyState, 0, sizeof keyState);
29
30         memset(joystickState, 0, sizeof joystickState);
31
32         joyX = joyY = 0;
33
34         mouseLeft = mouseRight = 0;
35         waitForButton = false;
36         waitForKey = false;
37         
38         allowJoypad = true;
39
40         lastKeyPressed[0] = 0;
41
42         fullScreen = 0;
43
44         useAudio = 2;
45         
46         practice = false;
47         
48         allowQuit = false;
49
50         saveConfig = false;
51
52         highlightedWidget = NULL;
53
54         message[0] = 0;
55         messageTime = -1;
56
57         // Development Stuff
58         devNoMonsters = false;
59
60         dataBuffer = NULL;
61         binaryBuffer = NULL;
62         #ifdef FRAMEWORK_SDL
63         char pakPath[PATH_MAX];
64         strlcpy(pakPath, PAKFULLPATH, sizeof(pakPath));
65         if (CFBundleGetMainBundle() != NULL) {
66                 CFURLRef pakURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(PAKNAME), NULL, NULL);
67                 if (pakURL != NULL) {
68                         CFShow(pakURL);
69                         CFURLGetFileSystemRepresentation(pakURL, true, (UInt8*)pakPath, sizeof(pakPath));
70                         CFRelease(pakURL);
71                 }
72         }
73         pak.setPakFile(pakPath);
74         #else
75         pak.setPakFile(PAKFULLPATH);
76         #endif
77
78         // Timer
79         time1 = time2 = 0;
80         timeDifference = 0;
81
82         // Cheats
83         memset(lastKeyEvents, ' ', 25);
84         cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
85         cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
86         
87         extremeAvailable = 0;
88 }
89
90 void Engine::destroy()
91 {
92         debug(("engine: free widgets\n"));
93         deleteWidgets();
94
95         debug(("engine: free databuffer\n"));
96         delete[] dataBuffer;
97
98         debug(("engine: free binarybuffer\n"));
99         delete[] binaryBuffer;
100
101         debug(("Clearing Define List...\n"));
102         defineList.clear();
103 }
104
105 void Engine::clearCheatVars()
106 {
107         memset(lastKeyEvents, ' ', 25);
108         cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
109         cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
110 }
111
112 bool Engine::compareLastKeyInputs()
113 {
114         if (strstr(lastKeyEvents, "LOCKANDLOAD"))
115         {
116                 cheats = true;
117                 return true;
118         }
119
120         return false;
121 }
122
123 void Engine::addKeyEvent()
124 {
125         if (strlen(lastKeyPressed) > 1)
126         {
127                 return;
128         }
129
130         int index = -1;
131
132         for (int i = 0 ; i < 25 ; i++)
133         {
134                 if (lastKeyEvents[i] == ' ')
135                 {
136                         index = i;
137                         break;
138                 }
139         }
140
141         if (index == -1)
142         {
143                 for (int i = 0 ; i < 24 ; i++)
144                 {
145                         lastKeyEvents[i] = lastKeyEvents[i + 1];
146                 }
147
148                 index = 24;
149         }
150
151         lastKeyEvents[index] = lastKeyPressed[0];
152
153         compareLastKeyInputs();
154 }
155
156 void Engine::getInput()
157 {
158         SDL_GetMouseState(&mouseX, &mouseY);
159
160         // Scale from window coordinates to graphics coordinates
161         int w, h;
162         SDL_GetWindowSize(graphics.window, &w, &h);
163         mouseX = mouseX * 640 / w;
164         mouseY = mouseY * 480 / h;
165
166         while (SDL_PollEvent(&event))
167         {
168                 switch (event.type)
169                 {
170                         case SDL_QUIT:
171                                 if (allowQuit)
172                                 {
173                                         exit(0);
174                                 }
175                                 break;
176
177                         case SDL_MOUSEBUTTONDOWN:
178                                 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
179                                 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
180                                 break;
181
182                         case SDL_MOUSEBUTTONUP:
183                                 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
184                                 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
185                                 break;
186
187                         case SDL_KEYDOWN:
188                                 
189                                 if (waitForButton)
190                                 {
191                                         if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
192                                         {
193                                                 lastButtonPressed = -1;
194                                                 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
195                                                 highlightedWidget->redraw();
196                                                 waitForButton = false;
197                                                 allowJoypad = false;
198                                         }
199                                         
200                                         if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
201                                         {
202                                                 lastButtonPressed = -2;
203                                                 *highlightedWidget->value = -2;
204                                                 highlightedWidget->redraw();
205                                                 waitForButton = false;
206                                                 allowJoypad = false;
207                                         }
208                                         
209                                         return;
210                                 }
211                                 
212                                 if (waitForKey)
213                                 {
214                                         if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
215                                         {
216                                                 *highlightedWidget->value = -*highlightedWidget->value;
217                                         }
218                                         else
219                                         {
220                                                 *highlightedWidget->value = event.key.keysym.scancode;
221                                         }
222                                         
223                                         lastButtonPressed = -1;
224                                         highlightedWidget->redraw();
225                                         waitForButton = false;
226                                         waitForKey = false;
227                                         allowJoypad = false;
228                                         
229                                         return;
230                                 }
231
232                                 keyState[event.key.keysym.scancode] = 1;
233                                 strlcpy(lastKeyPressed, SDL_GetKeyName(SDL_GetKeyFromScancode(event.key.keysym.scancode)), sizeof lastKeyPressed);
234                                 addKeyEvent();
235                                 break;
236
237                         case SDL_KEYUP:
238                                 keyState[event.key.keysym.scancode] = 0;
239                                 break;
240
241                         case SDL_JOYAXISMOTION:
242                                 if (event.jaxis.axis == 0)
243                                 {
244                                         joyX = event.jaxis.value;
245                                         int joycurX = joyX < -25000 ? -1 : joyX > 25000 ? 1 : 0;
246                                         if (joycurX != joyprevX)
247                                                 joykeyX = joyprevX = joycurX;
248                                 }
249                                 else if (event.jaxis.axis == 1)
250                                 {
251                                         joyY = event.jaxis.value;
252                                         int joycurY = joyY < -25000 ? -1 : joyY > 25000 ? 1 : 0;
253                                         if (joycurY != joyprevY)
254                                                 joykeyY = joyprevY = joycurY;
255                                 }
256                                 break;
257
258                         case SDL_JOYBUTTONDOWN:
259                                 if (waitForButton)
260                                 {
261                                         lastButtonPressed = event.jbutton.button;
262                                         *highlightedWidget->value = lastButtonPressed;
263                                         highlightedWidget->redraw();
264                                         waitForButton = false;
265                                         allowJoypad = false;
266                                         return;
267                                 }
268                                 
269                                 joystickState[event.jbutton.button] = 1;
270                                 joykeyFire = true;
271                                 break;
272
273                         case SDL_JOYBUTTONUP:
274                                 joystickState[event.jbutton.button] = 0;
275                                 joykeyFire = false;
276                                 break;
277
278                         case SDL_WINDOWEVENT:
279                                 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
280                                         paused = true;
281                                 break;
282
283                         default:
284                                 break;
285                 }
286         }
287 }
288
289 int Engine::getMouseX() const
290 {
291         return mouseX;
292 }
293
294 int Engine::getMouseY() const
295 {
296         return mouseY;
297 }
298
299 void Engine::setMouse(int x, int y)
300 {
301         SDL_WarpMouseInWindow(graphics.window, x, y);
302 }
303
304 bool Engine::userAccepts()
305 {
306         if ((keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_ESCAPE]) || (keyState[SDL_SCANCODE_LCTRL]) || (keyState[SDL_SCANCODE_RCTRL]) || (keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_LCTRL]) || joykeyFire)
307         {
308                 return true;
309         }
310
311         return false;
312 }
313
314 void Engine::flushInput()
315 {
316         while (SDL_PollEvent(&event)){}
317 }
318
319 void Engine::clearInput()
320 {
321         memset(keyState, 0, sizeof keyState);
322
323         mouseLeft = mouseRight = 0;
324         joykeyX = joykeyY = 0;
325         joykeyFire = false;
326 }
327
328 void Engine::setUserHome(const char *path)
329 {
330         strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
331         debug(("User Home = %s\n", path));
332 }
333
334 Pak *Engine::getPak()
335 {
336         return &pak;
337 }
338
339 /*
340 Searches the pak file for the required data. When
341 it is found, the data is read into a character buffer.
342 In the case of music, the data music be written to a temporary directory
343 since SDL currently provides no means to load music directly from memory
344 */
345 bool Engine::unpack(const char *filename, int fileType)
346 {
347         bool ret = true;
348
349         if (fileType == PAK_DATA)
350         {
351                 delete[] dataBuffer;
352                 dataBuffer = NULL;
353         }
354         else
355         {
356                 delete[] binaryBuffer;
357                 binaryBuffer = NULL;
358         }
359
360         if (fileType != PAK_DATA)
361         {
362                 if (!pak.unpack(filename, &binaryBuffer))
363                 {
364                         return false;
365                 }
366         }
367         else
368         {
369                 if (!pak.unpack(filename, &dataBuffer))
370                 {
371                         return false;
372                 }
373         }
374
375         if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
376         {
377                 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
378                 if (!sdlrw)
379                 {
380                         printf("Fatal Error: SDL_RWops allocation failed\n");
381                         exit(1);
382                 }
383         }
384
385         if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
386         {
387                 char tempPath[PATH_MAX];
388                 
389                 FILE *fp = NULL;
390
391                 if (fileType == PAK_MUSIC)
392                 {
393                         snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
394                         fp = fopen(tempPath, "wb");
395                 }
396                 else if (fileType == PAK_TAGS)
397                 {
398                         snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
399                         fp = fopen(tempPath, "wb");
400                 }
401                 else if (fileType == PAK_FONT)
402                 {
403                         snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
404                         fp = fopen(tempPath, "wb");
405                 }
406
407                 if (!fp)
408                 {
409                         printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
410                         return false;
411                 }
412
413                 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
414                 {
415                         printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
416                         ret = false;
417                 }
418                 fclose(fp);
419         }
420
421         debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
422
423         return ret;
424 }
425
426 bool Engine::loadData(const char *filename)
427 {
428         bool ret = true;
429
430         delete[] dataBuffer;
431         dataBuffer = NULL;
432         
433         #if USEPAK
434                 return unpack(filename, PAK_DATA);
435         #endif
436
437         FILE *fp;
438         fp = fopen(filename, "rb");
439         if (fp == NULL)
440                 return false;
441
442         fseek(fp, 0, SEEK_END);
443
444         int fSize = ftell(fp);
445
446         rewind(fp);
447
448         dataBuffer = new unsigned char[fSize + 1];
449
450         if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
451                 ret = false;
452
453         dataBuffer[fSize] = 0;
454
455         fclose(fp);
456
457         debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
458
459         return ret;
460 }
461
462 void Engine::reportFontFailure()
463 {
464         printf("\nUnable to load font. The game cannot continue without it.\n");
465         printf("Please confirm that the game and all required SDL libraries are installed\n");
466         printf("The following information may be useful to you,\n\n");
467         printf("Expected location of PAK file: %s\n", PAKFULLPATH);
468         printf("Location of TMP directory: %s\n", userHomeDirectory);
469         printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
470         exit(1);
471 }
472
473 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
474 {
475         playerPosX = x - OFFSETX;
476         playerPosY = y - OFFSETY;
477
478         Math::limitInt(&playerPosX, limitLeft, limitRight);
479         Math::limitInt(&playerPosY, limitUp, limitDown);
480 }
481
482 int Engine::getFrameLoop() const
483 {
484         return frameLoop;
485 }
486
487 void Engine::doFrameLoop()
488 {
489         Math::wrapChar(&(++frameLoop), 0, 59);
490 }
491
492 void Engine::doTimeDifference()
493 {
494         timeDifference = (time2 - time1) / 10.0;
495         time1 = time2;
496         time2 = SDL_GetTicks();
497 }
498
499 float Engine::getTimeDifference() const
500 {
501         return timeDifference;
502 }
503
504 void Engine::resetTimeDifference()
505 {
506         time1 = time2 = SDL_GetTicks();
507 }
508
509 void Engine::setInfoMessage(const char *message, int priority, int type)
510 {
511         if (priority >= messagePriority)
512         {
513                 strlcpy(this->message, message, sizeof this->message);
514                 messageTime = 180;
515                 messagePriority = priority;
516                 messageType = type;
517         }
518 }
519
520 void Engine::deleteWidgets()
521 {
522         Widget *widget;
523
524         for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
525                 widget->redraw();
526
527         widgetList.clear();
528
529         highlightedWidget = NULL;
530 }
531
532 void Engine::addWidget(Widget *widget)
533 {
534         widget->previous = (Widget*)widgetList.getTail();
535         widgetList.add(widget);
536 }
537
538 bool Engine::loadWidgets(const char *filename)
539 {
540         deleteWidgets();
541
542         if (!loadData(filename))
543                 return false;
544
545         char token[50], name[50], groupName[50], label[80], options[100], *line;
546         int x, y, min, max;
547
548         int i;
549
550         Widget *widget;
551
552         line = strtok((char*)dataBuffer, "\n");
553
554         while (true)
555         {
556                 sscanf(line, "%s", token);
557
558                 if (strcmp(token, "END") == 0)
559                         break;
560
561                 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
562
563                 widget = new Widget;
564
565                 i = 0;
566
567                 while (true)
568                 {
569                         if (strcmp(token, widgetName[i]) == 0)
570                                 widget->type = i;
571
572                         if (strcmp("-1", widgetName[i]) == 0)
573                                 break;
574
575                         i++;
576                 }
577
578                 widget->setProperties(name, groupName, label, options, x, y, min, max);
579
580                 addWidget(widget);
581
582
583                 if ((line = strtok(NULL, "\n")) == NULL)
584                         break;
585         }
586
587         highlightedWidget = (Widget*)widgetList.getHead()->next;
588
589         return true;
590 }
591
592 Widget *Engine::getWidgetByName(const char *name)
593 {
594         Widget *widget = (Widget*)widgetList.getHead();
595
596         while (widget->next != NULL)
597         {
598                 widget = (Widget*)widget->next;
599
600                 if (strcmp(widget->name, name) == 0)
601                         return widget;
602         }
603
604         debug(("No such widget '%s'\n", name));
605
606         return NULL;
607 }
608
609 void Engine::showWidgetGroup(const char *groupName, bool show)
610 {
611         bool found = false;
612
613         Widget *widget = (Widget*)widgetList.getHead();
614
615         while (widget->next != NULL)
616         {
617                 widget = (Widget*)widget->next;
618
619                 if (strcmp(widget->groupName, groupName) == 0)
620                 {
621                         widget->visible = show;
622                         widget->redraw();
623                         found = true;
624                 }
625         }
626
627         if (!found)
628                 debug(("Group '%s' does not exist\n", groupName));
629 }
630
631 void Engine::enableWidgetGroup(const char *groupName, bool show)
632 {
633         bool found = false;
634
635         Widget *widget = (Widget*)widgetList.getHead();
636
637         while (widget->next != NULL)
638         {
639                 widget = (Widget*)widget->next;
640
641                 if (strcmp(widget->groupName, groupName) == 0)
642                 {
643                         widget->enabled = show;
644                         widget->redraw();
645                         found = true;
646                 }
647         }
648
649         if (!found)
650                 debug(("Group '%s' does not exist\n", groupName));
651 }
652
653 void Engine::showWidget(const char *name, bool show)
654 {
655         Widget *widget = getWidgetByName(name);
656         if (widget != NULL)
657         {
658                 widget->visible = show;
659                 widget->redraw();
660         }
661 }
662
663 void Engine::enableWidget(const char *name, bool enable)
664 {
665         Widget *widget = getWidgetByName(name);
666         if (widget != NULL)
667         {
668                 widget->enabled = enable;
669                 widget->redraw();
670         }
671 }
672
673 void Engine::setWidgetVariable(const char *name, int *variable)
674 {
675         Widget *widget = getWidgetByName(name);
676         if (widget != NULL)
677                 widget->value = variable;
678 }
679
680 bool Engine::widgetChanged(const char *name)
681 {
682         Widget *widget = getWidgetByName(name);
683         if (widget != NULL)
684                 return widget->changed;
685
686         return false;
687 }
688
689 void Engine::highlightWidget(int dir)
690 {
691         highlightedWidget->redraw();
692
693         if (dir == 1)
694         {
695                 while (true)
696                 {
697                         if (highlightedWidget->next != NULL)
698                         {
699                                 highlightedWidget = (Widget*)highlightedWidget->next;
700                         }
701                         else
702                         {
703                                 highlightedWidget = (Widget*)widgetList.getHead()->next;
704                         }
705
706                         if (highlightedWidget->type == 4)
707                                 continue;
708
709                         if ((highlightedWidget->enabled) && (highlightedWidget->visible))
710                                 break;
711                 }
712         }
713
714         if (dir == -1)
715         {
716                 while (true)
717                 {
718                         if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
719                         {
720                                 highlightedWidget = highlightedWidget->previous;
721                         }
722                         else
723                         {
724                                 highlightedWidget = (Widget*)widgetList.getTail();
725                         }
726
727                         if (highlightedWidget->type == WG_LABEL)
728                                 continue;
729
730                         if ((highlightedWidget->enabled) && (highlightedWidget->visible))
731                                 break;
732                 }
733         }
734
735         highlightedWidget->redraw();
736 }
737
738 void Engine::highlightWidget(const char *name)
739 {
740         highlightedWidget = getWidgetByName(name);
741 }
742
743 int Engine::processWidgets()
744 {
745         int update = 0;
746
747         if (keyState[SDL_SCANCODE_UP] || joykeyY < 0)
748         {
749                 highlightWidget(-1);
750                 update = 1;
751                 clearInput();
752         }
753
754         if (keyState[SDL_SCANCODE_DOWN] || joykeyY > 0)
755         {
756                 highlightWidget(1);
757                 update = 1;
758                 clearInput();
759         }
760
761         if ((keyState[SDL_SCANCODE_LEFT] || joykeyX < 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
762         {
763                 SDL_Delay(1);
764
765                 if (*highlightedWidget->value > highlightedWidget->min)
766                 {
767                         *highlightedWidget->value = *highlightedWidget->value - 1;
768                         update = 3;
769                         if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
770                                 update = 1;
771                         highlightedWidget->changed = true;
772                 }
773
774                 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
775                         clearInput();
776         }
777
778         if ((keyState[SDL_SCANCODE_RIGHT] || joykeyX > 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
779         {
780                 SDL_Delay(1);
781
782                 if (*highlightedWidget->value < highlightedWidget->max)
783                 {
784                         *highlightedWidget->value = *highlightedWidget->value + 1;
785                         update = 3;
786                         if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
787                                 update = 1;
788                         highlightedWidget->changed = true;
789                 }
790
791                 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
792                         clearInput();
793         }
794
795         if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]) || (joykeyFire && highlightedWidget->type != WG_JOYPAD))
796         {
797                 if (highlightedWidget->value == NULL)
798                 {
799                         debug(("%s has not been implemented!\n", highlightedWidget->name));
800                 }
801                 else
802                 {
803                         if (highlightedWidget->type == WG_BUTTON)
804                         {
805                                 *highlightedWidget->value = 1;
806                                 highlightedWidget->changed = true;
807                         }
808                         else if (highlightedWidget->type == WG_JOYPAD)
809                         {
810                                 waitForButton = true;
811                                 waitForKey = false;
812                                 allowJoypad = true;
813                                 
814                                 if (*highlightedWidget->value > -1000)
815                                 {
816                                         *highlightedWidget->value = (-1000 - *highlightedWidget->value);
817                                 }
818                         }
819                         else if (highlightedWidget->type == WG_KEYBOARD)
820                         {
821                                 waitForKey = true;
822                                 waitForButton = false;
823                                 allowJoypad = false;
824                                 *highlightedWidget->value = -*highlightedWidget->value;
825                         }
826                         
827                         update = 2;
828                 }
829                 
830
831                 flushInput();
832                 clearInput();
833         }
834
835         if (joykeyX > 0 && highlightedWidget->type == WG_JOYPAD)
836         {
837                 waitForButton = true;
838                 waitForKey = false;
839                 allowJoypad = true;
840
841                 if (*highlightedWidget->value > -1000)
842                 {
843                         *highlightedWidget->value = (-1000 - *highlightedWidget->value);
844                 }
845
846                 clearInput();
847         }
848
849         if (joykeyX < 0 && highlightedWidget->type == WG_JOYPAD)
850         {
851                 if (waitForButton)
852                 {
853                         lastButtonPressed = -1;
854                         *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
855                 }
856                 else
857                 {
858                         lastButtonPressed = -2;
859                         *highlightedWidget->value = -2;
860                 }
861
862                 waitForButton = false;
863                 allowJoypad = false;
864                 highlightedWidget->redraw();
865                 clearInput();
866         }
867
868         return update;
869 }
870
871 #if !UNIX
872 char *strtok_r(char *s1, const char *s2, char **lasts)
873 {
874         char *ret;
875         
876         if (s1 == NULL)
877         {
878                 s1 = *lasts;
879         }
880         
881         while (*s1 && strchr(s2, *s1))
882         {
883                 ++s1;
884         }
885         
886         if (*s1 == '\0')
887         {
888                 return NULL;
889         }
890         
891         ret = s1;
892         
893         while(*s1 && !strchr(s2, *s1))
894         {
895                 ++s1;
896         }
897         
898         if(*s1)
899         {
900                 *s1++ = '\0';
901         }
902         
903         *lasts = s1;
904         
905         return ret;
906 }
907 #endif
908
909 /*
910 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
911 game at compile time and run time, so everything is syncronised. This technique has the advantage of
912 allowing the game's data to be human readable and easy to maintain.
913 */
914 bool Engine::loadDefines()
915 {
916         char string[2][1024];
917
918         if (!loadData("data/defines.h"))
919                 return false;
920
921         char *token = strtok((char*)dataBuffer, "\n");
922
923         Data *data;
924
925         while (true)
926         {
927                 token = strtok(NULL, "\n");
928                 if (!token)
929                         break;
930
931                 if (!strstr(token, "/*"))
932                 {
933                         sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
934                         data = new Data();
935                         data->set(string[0], string[1], 1, 1);
936                         defineList.add(data);
937                 }
938         }
939
940         return true;
941 }
942
943 /*
944 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
945 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
946 1) It makes the game data human readable and 2) It means if I change a #define in
947 the code, I don't have to change all the data entries too. You probably already
948 thought of that though... :)
949 */
950 int Engine::getValueOfDefine(const char *word)
951 {
952         int rtn = 0;
953
954         Data *data = (Data*)defineList.getHead();
955
956         while (data->next != NULL)
957         {
958                 data = (Data*)data->next;
959
960                 if (strcmp(data->key, word) == 0)
961                 {
962                         rtn = atoi(data->value);
963                         return rtn;
964                 }
965         }
966
967         printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
968         exit(1);
969 }
970
971 /*
972 Does the opposite of the above(!)
973 */
974 char *Engine::getDefineOfValue(const char *prefix, int value)
975 {
976         int rtn = 0;
977
978         Data *data = (Data*)defineList.getHead();
979
980         while (data->next != NULL)
981         {
982                 data = (Data*)data->next;
983
984                 if (strstr(data->key, prefix))
985                 {
986                         rtn = atoi(data->value);
987                         
988                         if (rtn == value)
989                         {
990                                 return data->key;
991                         }
992                 }
993         }
994
995         printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
996         exit(1);
997 }
998
999 /*
1000 I like this function. It receives a list of flags declared by their #define name... like
1001 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
1002 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
1003 the values together and then returns them... phew! Makes data files human readable though :)
1004 */
1005 int Engine::getValueOfFlagTokens(const char *realLine)
1006 {
1007         if (strcmp(realLine, "0") == 0)
1008                 return 0;
1009
1010         char *store;
1011         char line[1024];
1012         bool found;
1013         int value;
1014         strlcpy(line, realLine, sizeof line);
1015
1016         int flags = 0;
1017
1018         char *word = strtok_r(line, "+", &store);
1019
1020         if (!word)
1021         {
1022                 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
1023                 exit(1);
1024         }
1025
1026         Data *data;
1027
1028         while (true)
1029         {
1030                 data = (Data*)defineList.getHead();
1031                 found = false;
1032
1033                 while (data->next != NULL)
1034                 {
1035                         data = (Data*)data->next;
1036
1037                         if (strcmp(data->key, word) == 0)
1038                         {
1039                                 value = -1;
1040                                 sscanf(data->value, "%d", &value);
1041
1042                                 if (value == -1)
1043                                 {
1044                                         sscanf(data->value, "%*s %*d %*s %d", &value);
1045                                         value = 2 << value;
1046                                 }
1047
1048                                 flags += value;
1049                                 found = true;
1050                                 break;
1051                         }
1052                 }
1053
1054                 if (!found)
1055                 {
1056                         printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1057                         #if IGNORE_FLAGTOKEN_ERRORS
1058                                 break;
1059                         #else
1060                                 exit(1);
1061                         #endif
1062                 }
1063
1064                 word = strtok_r(NULL, "+", &store);
1065                 if (!word)
1066                         break;
1067         }
1068
1069         return flags;
1070 }
1071
1072 void Engine::delay(unsigned int frameLimit) {
1073         unsigned int ticks = SDL_GetTicks();
1074
1075         if(frameLimit < ticks)
1076                 return;
1077         
1078         if(frameLimit > ticks + 16)
1079                 SDL_Delay(16);
1080         else
1081                 SDL_Delay(frameLimit - ticks);
1082 }