]> git.mxchange.org Git - quix0rs-blobwars.git/blob - src/CEngine.cpp
Make the game playable using only a gamepad.
[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(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
397                 if (fileType == PAK_TAGS)
398                 {
399                         snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
400                         fp = fopen(tempPath, "wb");
401                 }
402
403                 if (fileType == PAK_FONT)
404                 {
405                         snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
406                         fp = fopen(tempPath, "wb");
407                 }
408
409                 if (!fp)
410                 {
411                         printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
412                         return false;
413                 }
414
415                 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
416                 {
417                         printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
418                         ret = false;
419                 }
420                 fclose(fp);
421         }
422
423         debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
424
425         return ret;
426 }
427
428 bool Engine::loadData(const char *filename)
429 {
430         bool ret = true;
431
432         delete[] dataBuffer;
433         dataBuffer = NULL;
434         
435         #if USEPAK
436                 return unpack(filename, PAK_DATA);
437         #endif
438
439         FILE *fp;
440         fp = fopen(filename, "rb");
441         if (fp == NULL)
442                 return false;
443
444         fseek(fp, 0, SEEK_END);
445
446         int fSize = ftell(fp);
447
448         rewind(fp);
449
450         dataBuffer = new unsigned char[fSize + 1];
451
452         if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
453                 ret = false;
454
455         dataBuffer[fSize] = 0;
456
457         fclose(fp);
458
459         debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
460
461         return ret;
462 }
463
464 void Engine::reportFontFailure()
465 {
466         printf("\nUnable to load font. The game cannot continue without it.\n");
467         printf("Please confirm that the game and all required SDL libraries are installed\n");
468         printf("The following information may be useful to you,\n\n");
469         printf("Expected location of PAK file: %s\n", PAKFULLPATH);
470         printf("Location of TMP directory: %s\n", userHomeDirectory);
471         printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
472         exit(1);
473 }
474
475 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
476 {
477         playerPosX = x - OFFSETX;
478         playerPosY = y - OFFSETY;
479
480         Math::limitInt(&playerPosX, limitLeft, limitRight);
481         Math::limitInt(&playerPosY, limitUp, limitDown);
482 }
483
484 int Engine::getFrameLoop() const
485 {
486         return frameLoop;
487 }
488
489 void Engine::doFrameLoop()
490 {
491         Math::wrapChar(&(++frameLoop), 0, 59);
492 }
493
494 void Engine::doTimeDifference()
495 {
496         timeDifference = (time2 - time1) / 10.0;
497         time1 = time2;
498         time2 = SDL_GetTicks();
499 }
500
501 float Engine::getTimeDifference() const
502 {
503         return timeDifference;
504 }
505
506 void Engine::resetTimeDifference()
507 {
508         time1 = time2 = SDL_GetTicks();
509 }
510
511 void Engine::setInfoMessage(const char *message, int priority, int type)
512 {
513         if (priority >= messagePriority)
514         {
515                 strlcpy(this->message, message, sizeof this->message);
516                 messageTime = 180;
517                 messagePriority = priority;
518                 messageType = type;
519         }
520 }
521
522 void Engine::deleteWidgets()
523 {
524         Widget *widget;
525
526         for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
527                 widget->redraw();
528
529         widgetList.clear();
530
531         highlightedWidget = NULL;
532 }
533
534 void Engine::addWidget(Widget *widget)
535 {
536         widget->previous = (Widget*)widgetList.getTail();
537         widgetList.add(widget);
538 }
539
540 bool Engine::loadWidgets(const char *filename)
541 {
542         deleteWidgets();
543
544         if (!loadData(filename))
545                 return false;
546
547         char token[50], name[50], groupName[50], label[80], options[100], *line;
548         int x, y, min, max;
549
550         int i;
551
552         Widget *widget;
553
554         line = strtok((char*)dataBuffer, "\n");
555
556         while (true)
557         {
558                 sscanf(line, "%s", token);
559
560                 if (strcmp(token, "END") == 0)
561                         break;
562
563                 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
564
565                 widget = new Widget;
566
567                 i = 0;
568
569                 while (true)
570                 {
571                         if (strcmp(token, widgetName[i]) == 0)
572                                 widget->type = i;
573
574                         if (strcmp("-1", widgetName[i]) == 0)
575                                 break;
576
577                         i++;
578                 }
579
580                 widget->setProperties(name, groupName, label, options, x, y, min, max);
581
582                 addWidget(widget);
583
584
585                 if ((line = strtok(NULL, "\n")) == NULL)
586                         break;
587         }
588
589         highlightedWidget = (Widget*)widgetList.getHead()->next;
590
591         return true;
592 }
593
594 Widget *Engine::getWidgetByName(const char *name)
595 {
596         Widget *widget = (Widget*)widgetList.getHead();
597
598         while (widget->next != NULL)
599         {
600                 widget = (Widget*)widget->next;
601
602                 if (strcmp(widget->name, name) == 0)
603                         return widget;
604         }
605
606         debug(("No such widget '%s'\n", name));
607
608         return NULL;
609 }
610
611 void Engine::showWidgetGroup(const char *groupName, bool show)
612 {
613         bool found = false;
614
615         Widget *widget = (Widget*)widgetList.getHead();
616
617         while (widget->next != NULL)
618         {
619                 widget = (Widget*)widget->next;
620
621                 if (strcmp(widget->groupName, groupName) == 0)
622                 {
623                         widget->visible = show;
624                         widget->redraw();
625                         found = true;
626                 }
627         }
628
629         if (!found)
630                 debug(("Group '%s' does not exist\n", groupName));
631 }
632
633 void Engine::enableWidgetGroup(const char *groupName, bool show)
634 {
635         bool found = false;
636
637         Widget *widget = (Widget*)widgetList.getHead();
638
639         while (widget->next != NULL)
640         {
641                 widget = (Widget*)widget->next;
642
643                 if (strcmp(widget->groupName, groupName) == 0)
644                 {
645                         widget->enabled = show;
646                         widget->redraw();
647                         found = true;
648                 }
649         }
650
651         if (!found)
652                 debug(("Group '%s' does not exist\n", groupName));
653 }
654
655 void Engine::showWidget(const char *name, bool show)
656 {
657         Widget *widget = getWidgetByName(name);
658         if (widget != NULL)
659         {
660                 widget->visible = show;
661                 widget->redraw();
662         }
663 }
664
665 void Engine::enableWidget(const char *name, bool enable)
666 {
667         Widget *widget = getWidgetByName(name);
668         if (widget != NULL)
669         {
670                 widget->enabled = enable;
671                 widget->redraw();
672         }
673 }
674
675 void Engine::setWidgetVariable(const char *name, int *variable)
676 {
677         Widget *widget = getWidgetByName(name);
678         if (widget != NULL)
679                 widget->value = variable;
680 }
681
682 bool Engine::widgetChanged(const char *name)
683 {
684         Widget *widget = getWidgetByName(name);
685         if (widget != NULL)
686                 return widget->changed;
687
688         return false;
689 }
690
691 void Engine::highlightWidget(int dir)
692 {
693         highlightedWidget->redraw();
694
695         if (dir == 1)
696         {
697                 while (true)
698                 {
699                         if (highlightedWidget->next != NULL)
700                         {
701                                 highlightedWidget = (Widget*)highlightedWidget->next;
702                         }
703                         else
704                         {
705                                 highlightedWidget = (Widget*)widgetList.getHead()->next;
706                         }
707
708                         if (highlightedWidget->type == 4)
709                                 continue;
710
711                         if ((highlightedWidget->enabled) && (highlightedWidget->visible))
712                                 break;
713                 }
714         }
715
716         if (dir == -1)
717         {
718                 while (true)
719                 {
720                         if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
721                         {
722                                 highlightedWidget = highlightedWidget->previous;
723                         }
724                         else
725                         {
726                                 highlightedWidget = (Widget*)widgetList.getTail();
727                         }
728
729                         if (highlightedWidget->type == WG_LABEL)
730                                 continue;
731
732                         if ((highlightedWidget->enabled) && (highlightedWidget->visible))
733                                 break;
734                 }
735         }
736
737         highlightedWidget->redraw();
738 }
739
740 void Engine::highlightWidget(const char *name)
741 {
742         highlightedWidget = getWidgetByName(name);
743 }
744
745 int Engine::processWidgets()
746 {
747         int update = 0;
748
749         if (keyState[SDL_SCANCODE_UP] || joykeyY < 0)
750         {
751                 highlightWidget(-1);
752                 update = 1;
753                 clearInput();
754         }
755
756         if (keyState[SDL_SCANCODE_DOWN] || joykeyY > 0)
757         {
758                 highlightWidget(1);
759                 update = 1;
760                 clearInput();
761         }
762
763         if ((keyState[SDL_SCANCODE_LEFT] || joykeyX < 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
764         {
765                 SDL_Delay(1);
766
767                 if (*highlightedWidget->value > highlightedWidget->min)
768                 {
769                         *highlightedWidget->value = *highlightedWidget->value - 1;
770                         update = 3;
771                         if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
772                                 update = 1;
773                         highlightedWidget->changed = true;
774                 }
775
776                 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
777                         clearInput();
778         }
779
780         if ((keyState[SDL_SCANCODE_RIGHT] || joykeyX > 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
781         {
782                 SDL_Delay(1);
783
784                 if (*highlightedWidget->value < highlightedWidget->max)
785                 {
786                         *highlightedWidget->value = *highlightedWidget->value + 1;
787                         update = 3;
788                         if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
789                                 update = 1;
790                         highlightedWidget->changed = true;
791                 }
792
793                 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
794                         clearInput();
795         }
796
797         if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]) || (joykeyFire && highlightedWidget->type != WG_JOYPAD))
798         {
799                 if (highlightedWidget->value == NULL)
800                 {
801                         debug(("%s has not been implemented!\n", highlightedWidget->name));
802                 }
803                 else
804                 {
805                         if (highlightedWidget->type == WG_BUTTON)
806                         {
807                                 *highlightedWidget->value = 1;
808                                 highlightedWidget->changed = true;
809                         }
810                         else if (highlightedWidget->type == WG_JOYPAD)
811                         {
812                                 waitForButton = true;
813                                 waitForKey = false;
814                                 allowJoypad = true;
815                                 
816                                 if (*highlightedWidget->value > -1000)
817                                 {
818                                         *highlightedWidget->value = (-1000 - *highlightedWidget->value);
819                                 }
820                         }
821                         else if (highlightedWidget->type == WG_KEYBOARD)
822                         {
823                                 waitForKey = true;
824                                 waitForButton = false;
825                                 allowJoypad = false;
826                                 *highlightedWidget->value = -*highlightedWidget->value;
827                         }
828                         
829                         update = 2;
830                 }
831                 
832
833                 flushInput();
834                 clearInput();
835         }
836
837         if (joykeyX > 0 && highlightedWidget->type == WG_JOYPAD)
838         {
839                 waitForButton = true;
840                 waitForKey = false;
841                 allowJoypad = true;
842
843                 if (*highlightedWidget->value > -1000)
844                 {
845                         *highlightedWidget->value = (-1000 - *highlightedWidget->value);
846                 }
847
848                 clearInput();
849         }
850
851         if (joykeyX < 0 && highlightedWidget->type == WG_JOYPAD)
852         {
853                 if (waitForButton)
854                 {
855                         lastButtonPressed = -1;
856                         *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
857                 }
858                 else
859                 {
860                         lastButtonPressed = -2;
861                         *highlightedWidget->value = -2;
862                 }
863
864                 waitForButton = false;
865                 allowJoypad = false;
866                 highlightedWidget->redraw();
867                 clearInput();
868         }
869
870         return update;
871 }
872
873 #if !UNIX
874 char *strtok_r(char *s1, const char *s2, char **lasts)
875 {
876         char *ret;
877         
878         if (s1 == NULL)
879         {
880                 s1 = *lasts;
881         }
882         
883         while (*s1 && strchr(s2, *s1))
884         {
885                 ++s1;
886         }
887         
888         if (*s1 == '\0')
889         {
890                 return NULL;
891         }
892         
893         ret = s1;
894         
895         while(*s1 && !strchr(s2, *s1))
896         {
897                 ++s1;
898         }
899         
900         if(*s1)
901         {
902                 *s1++ = '\0';
903         }
904         
905         *lasts = s1;
906         
907         return ret;
908 }
909 #endif
910
911 /*
912 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
913 game at compile time and run time, so everything is syncronised. This technique has the advantage of
914 allowing the game's data to be human readable and easy to maintain.
915 */
916 bool Engine::loadDefines()
917 {
918         char string[2][1024];
919
920         if (!loadData("data/defines.h"))
921                 return false;
922
923         char *token = strtok((char*)dataBuffer, "\n");
924
925         Data *data;
926
927         while (true)
928         {
929                 token = strtok(NULL, "\n");
930                 if (!token)
931                         break;
932
933                 if (!strstr(token, "/*"))
934                 {
935                         sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
936                         data = new Data();
937                         data->set(string[0], string[1], 1, 1);
938                         defineList.add(data);
939                 }
940         }
941
942         return true;
943 }
944
945 /*
946 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
947 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
948 1) It makes the game data human readable and 2) It means if I change a #define in
949 the code, I don't have to change all the data entries too. You probably already
950 thought of that though... :)
951 */
952 int Engine::getValueOfDefine(const char *word)
953 {
954         int rtn = 0;
955
956         Data *data = (Data*)defineList.getHead();
957
958         while (data->next != NULL)
959         {
960                 data = (Data*)data->next;
961
962                 if (strcmp(data->key, word) == 0)
963                 {
964                         rtn = atoi(data->value);
965                         return rtn;
966                 }
967         }
968
969         printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
970         exit(1);
971 }
972
973 /*
974 Does the opposite of the above(!)
975 */
976 char *Engine::getDefineOfValue(const char *prefix, int value)
977 {
978         int rtn = 0;
979
980         Data *data = (Data*)defineList.getHead();
981
982         while (data->next != NULL)
983         {
984                 data = (Data*)data->next;
985
986                 if (strstr(data->key, prefix))
987                 {
988                         rtn = atoi(data->value);
989                         
990                         if (rtn == value)
991                         {
992                                 return data->key;
993                         }
994                 }
995         }
996
997         printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
998         exit(1);
999 }
1000
1001 /*
1002 I like this function. It receives a list of flags declared by their #define name... like
1003 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
1004 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
1005 the values together and then returns them... phew! Makes data files human readable though :)
1006 */
1007 int Engine::getValueOfFlagTokens(const char *realLine)
1008 {
1009         if (strcmp(realLine, "0") == 0)
1010                 return 0;
1011
1012         char *store;
1013         char line[1024];
1014         bool found;
1015         int value;
1016         strlcpy(line, realLine, sizeof line);
1017
1018         int flags = 0;
1019
1020         char *word = strtok_r(line, "+", &store);
1021
1022         if (!word)
1023         {
1024                 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
1025                 exit(1);
1026         }
1027
1028         Data *data;
1029
1030         while (true)
1031         {
1032                 data = (Data*)defineList.getHead();
1033                 found = false;
1034
1035                 while (data->next != NULL)
1036                 {
1037                         data = (Data*)data->next;
1038
1039                         if (strcmp(data->key, word) == 0)
1040                         {
1041                                 value = -1;
1042                                 sscanf(data->value, "%d", &value);
1043
1044                                 if (value == -1)
1045                                 {
1046                                         sscanf(data->value, "%*s %*d %*s %d", &value);
1047                                         value = 2 << value;
1048                                 }
1049
1050                                 flags += value;
1051                                 found = true;
1052                                 break;
1053                         }
1054                 }
1055
1056                 if (!found)
1057                 {
1058                         printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1059                         #if IGNORE_FLAGTOKEN_ERRORS
1060                                 break;
1061                         #else
1062                                 exit(1);
1063                         #endif
1064                 }
1065
1066                 word = strtok_r(NULL, "+", &store);
1067                 if (!word)
1068                         break;
1069         }
1070
1071         return flags;
1072 }
1073
1074 void Engine::delay(unsigned int frameLimit) {
1075         unsigned int ticks = SDL_GetTicks();
1076
1077         if(frameLimit < ticks)
1078                 return;
1079         
1080         if(frameLimit > ticks + 16)
1081                 SDL_Delay(16);
1082         else
1083                 SDL_Delay(frameLimit - ticks);
1084 }