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