makefile: support a MinGW cross-compilation environment
[blobwars-mingw.git] / src / CEngine.cpp
bloba70efcc3b72293d7e28b13f8125c04e69b20950a
1 /*
2 Copyright (C) 2004 Parallel Realities
4 This program is free software; you can redistribute it and/or
5 modify it under the terms of the GNU General Public License
6 as published by the Free Software Foundation; either version 2
7 of the License, or (at your option) any later version.
9 This program is distributed in the hope that it will be useful,
10 but WITHOUT ANY WARRANTY; without even the implied warranty of
11 MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.
13 See the GNU General Public License for more details.
15 You should have received a copy of the GNU General Public License
16 along with this program; if not, write to the Free Software
17 Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307, USA.
21 #include "headers.h"
23 Engine::Engine()
25 for (int i = 0 ; i < 350 ; i++)
27 keyState[i] = 0;
30 for (int i = 0 ; i < 32 ; i++)
32 joystickState[i] = 0;
35 joyX = joyY = 0;
37 mouseLeft = mouseRight = 0;
38 waitForButton = false;
39 waitForKey = false;
41 allowJoypad = true;
43 strcpy(lastKeyPressed, "");
45 fullScreen = 0;
47 useAudio = 2;
49 practice = false;
51 allowQuit = false;
53 saveConfig = false;
55 highlightedWidget = NULL;
57 strcpy(message, "");
58 messageTime = -1;
60 // Development Stuff
61 devNoMonsters = false;
63 dataBuffer = NULL;
64 binaryBuffer = NULL;
65 #ifdef FRAMEWORK_SDL
66 char pakPath[PATH_MAX];
67 strncpy(pakPath, PAKFULLPATH, sizeof(pakPath));
68 if (CFBundleGetMainBundle() != NULL) {
69 CFURLRef pakURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(PAKNAME), NULL, NULL);
70 if (pakURL != NULL) {
71 CFShow(pakURL);
72 CFURLGetFileSystemRepresentation(pakURL, true, (UInt8*)pakPath, sizeof(pakPath));
73 CFRelease(pakURL);
76 pak.setPakFile(pakPath);
77 #else
78 pak.setPakFile(PAKFULLPATH);
79 #endif
81 // Timer
82 time1 = time2 = 0;
83 timeDifference = 0;
85 // Cheats
86 memset(lastKeyEvents, ' ', 25);
87 #if USEPAK
88 cheats = false;
89 #else
90 cheats = true;
91 cheats = false;
92 #endif
93 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
94 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
96 extremeAvailable = 0;
99 void Engine::destroy()
101 debug(("engine: free widgets\n"));
102 deleteWidgets();
104 debug(("engine: free databuffer\n"));
105 if (dataBuffer != NULL)
106 delete[] dataBuffer;
108 debug(("engine: free binarybuffer\n"));
109 if (binaryBuffer != NULL)
110 delete[] binaryBuffer;
112 debug(("Clearing Define List...\n"));
113 defineList.clear();
116 void Engine::clearCheatVars()
118 memset(lastKeyEvents, ' ', 25);
119 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
120 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
123 bool Engine::compareLastKeyInputs()
125 if (strstr(lastKeyEvents, "lockandload"))
127 cheats = true;
128 return true;
131 return false;
134 void Engine::addKeyEvent()
136 if (strlen(lastKeyPressed) > 1)
138 return;
141 int index = -1;
143 for (int i = 0 ; i < 25 ; i++)
145 if (lastKeyEvents[i] == ' ')
147 index = i;
148 break;
152 if (index == -1)
154 for (int i = 0 ; i < 25 ; i++)
156 lastKeyEvents[i] = lastKeyEvents[i + 1];
159 index = 24;
162 lastKeyEvents[index] = lastKeyPressed[0];
164 compareLastKeyInputs();
167 void Engine::getInput()
169 SDL_GetMouseState(&mouseX, &mouseY);
171 while (SDL_PollEvent(&event))
173 switch (event.type)
175 case SDL_QUIT:
176 if (allowQuit)
178 exit(0);
180 break;
182 case SDL_MOUSEBUTTONDOWN:
183 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
184 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
185 break;
187 case SDL_MOUSEBUTTONUP:
188 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
189 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
190 break;
192 case SDL_KEYDOWN:
194 if (waitForButton)
196 if (event.key.keysym.sym == SDLK_ESCAPE)
198 lastButtonPressed = -1;
199 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
200 highlightedWidget->redraw();
201 waitForButton = false;
202 allowJoypad = false;
205 if (event.key.keysym.sym == SDLK_BACKSPACE)
207 lastButtonPressed = -2;
208 *highlightedWidget->value = -2;
209 highlightedWidget->redraw();
210 waitForButton = false;
211 allowJoypad = false;
214 return;
217 if (waitForKey)
219 if (event.key.keysym.sym == SDLK_ESCAPE)
221 *highlightedWidget->value = -*highlightedWidget->value;
223 else
225 *highlightedWidget->value = event.key.keysym.sym;
228 lastButtonPressed = -1;
229 highlightedWidget->redraw();
230 waitForButton = false;
231 waitForKey = false;
232 allowJoypad = false;
234 return;
237 keyState[event.key.keysym.sym] = 1;
238 strcpy(lastKeyPressed, SDL_GetKeyName(event.key.keysym.sym));
239 addKeyEvent();
240 break;
242 case SDL_KEYUP:
243 keyState[event.key.keysym.sym] = 0;
244 break;
246 case SDL_JOYAXISMOTION:
247 if (event.jaxis.axis == 0)
249 joyX = event.jaxis.value;
251 else if (event.jaxis.axis == 1)
253 joyY = event.jaxis.value;
256 break;
258 case SDL_JOYBUTTONDOWN:
260 if (waitForButton)
262 lastButtonPressed = event.jbutton.button;
263 *highlightedWidget->value = lastButtonPressed;
264 highlightedWidget->redraw();
265 waitForButton = false;
266 allowJoypad = false;
267 return;
270 joystickState[event.jbutton.button] = 1;
271 break;
273 case SDL_JOYBUTTONUP:
274 joystickState[event.jbutton.button] = 0;
275 break;
277 default:
278 break;
283 int Engine::getMouseX()
285 return mouseX;
288 int Engine::getMouseY()
290 return mouseY;
293 void Engine::setMouse(int x, int y)
295 SDL_WarpMouse(x, y);
298 bool Engine::userAccepts()
300 if ((keyState[SDLK_SPACE]) || (keyState[SDLK_ESCAPE]) || (keyState[SDLK_LCTRL]) || (keyState[SDLK_RCTRL]) || (keyState[SDLK_RETURN]) || (keyState[SDLK_LCTRL]))
302 return true;
305 return false;
308 void Engine::flushInput()
310 while (SDL_PollEvent(&event)){}
313 void Engine::clearInput()
315 for (int i = 0 ; i < 350 ; i++)
316 keyState[i] = 0;
318 mouseLeft = mouseRight = 0;
321 void Engine::setUserHome(char *path)
323 strcpy(userHomeDirectory, path);
324 debug(("User Home = %s\n", path));
327 Pak *Engine::getPak()
329 return &pak;
333 Searches the pak file for the required data. When
334 it is found, the data is read into a character buffer.
335 In the case of music, the data music be written to a temporary directory
336 since SDL currently provides no means to load music directly from memory
338 bool Engine::unpack(char *filename, int fileType)
340 if (fileType == PAK_DATA)
342 if (dataBuffer != NULL)
343 delete[] dataBuffer;
345 dataBuffer = NULL;
347 else
349 if (binaryBuffer != NULL)
350 delete[] binaryBuffer;
352 binaryBuffer = NULL;
355 if (fileType != PAK_DATA)
357 if (!pak.unpack(filename, &binaryBuffer))
359 return false;
362 else
364 if (!pak.unpack(filename, &dataBuffer))
366 return false;
370 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
372 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
373 if (!sdlrw)
375 printf("Fatal Error: SDL_RWops allocation failed\n");
376 exit(1);
380 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT))
382 char tempPath[PATH_MAX];
384 FILE *fp = NULL;
386 if (fileType == PAK_MUSIC)
388 sprintf(tempPath, "%smusic.mod", userHomeDirectory);
389 fp = fopen(tempPath, "wb");
392 if (fileType == PAK_FONT)
394 sprintf(tempPath, "%sfont.ttf", userHomeDirectory);
395 fp = fopen(tempPath, "wb");
398 if (!fp)
400 return false;
403 fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp);
404 fclose(fp);
407 debug(("unpack() : Loaded %s (%d)\n", filename, pak.getUncompressedSize()));
409 return true;
412 bool Engine::loadData(char *filename)
414 if (dataBuffer != NULL)
416 delete[] dataBuffer;
417 dataBuffer = NULL;
420 #if USEPAK
421 return unpack(filename, PAK_DATA);
422 #endif
424 FILE *fp;
425 fp = fopen(filename, "rb");
426 if (fp == NULL)
427 return false;
429 fseek(fp, 0, SEEK_END);
431 int fSize = ftell(fp);
433 rewind(fp);
435 dataBuffer = new unsigned char[fSize];
437 fread(dataBuffer, 1, fSize, fp);
439 fclose(fp);
441 debug(("loadData() : Loaded %s (%d)\n", filename, fSize));
443 return true;
446 void Engine::reportFontFailure()
448 printf("\nUnable to load font. The game cannot continue without it.\n");
449 printf("Please confirm that the game and all required SDL libraries are installed\n");
450 printf("The following information may be useful to you,\n\n");
451 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
452 printf("Location of TMP directory: %s\n", userHomeDirectory);
453 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
454 exit(1);
457 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
459 playerPosX = x - OFFSETX;
460 playerPosY = y - OFFSETY;
462 Math::limitInt(&playerPosX, limitLeft, limitRight);
463 Math::limitInt(&playerPosY, limitUp, limitDown);
466 int Engine::getFrameLoop()
468 return frameLoop;
471 void Engine::doFrameLoop()
473 Math::wrapChar(&(++frameLoop), 0, 59);
476 void Engine::doTimeDifference()
478 timeDifference = (time2 - time1) / 10.0;
479 time1 = time2;
480 time2 = SDL_GetTicks();
483 float Engine::getTimeDifference()
485 return timeDifference;
488 void Engine::resetTimeDifference()
490 time1 = time2 = SDL_GetTicks();
493 void Engine::setInfoMessage(char *message, int priority, int type)
495 if (priority >= messagePriority)
497 strcpy(this->message, message);
498 messageTime = 180;
499 messagePriority = priority;
500 messageType = type;
504 void Engine::deleteWidgets()
506 Widget *widget;
508 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
509 widget->redraw();
511 widgetList.clear();
513 highlightedWidget = NULL;
516 void Engine::addWidget(Widget *widget)
518 widget->previous = (Widget*)widgetList.getTail();
519 widgetList.add(widget);
522 bool Engine::loadWidgets(char *filename)
524 deleteWidgets();
526 if (!loadData(filename))
527 return false;
529 char token[50], name[50], groupName[50], label[80], options[100], *line;
530 int x, y, min, max;
532 int i;
534 Widget *widget;
536 line = strtok((char*)dataBuffer, "\n");
538 while (true)
540 sscanf(line, "%s", token);
542 if (strcmp(token, "END") == 0)
543 break;
545 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
547 widget = new Widget;
549 i = 0;
551 while (true)
553 if (strcmp(token, widgetName[i]) == 0)
554 widget->type = i;
556 if (strcmp("-1", widgetName[i]) == 0)
557 break;
559 i++;
562 widget->setProperties(name, groupName, label, options, x, y, min, max);
564 addWidget(widget);
567 if ((line = strtok(NULL, "\n")) == NULL)
568 break;
571 highlightedWidget = (Widget*)widgetList.getHead()->next;
573 return true;
576 Widget *Engine::getWidgetByName(char *name)
578 Widget *widget = (Widget*)widgetList.getHead();
580 while (widget->next != NULL)
582 widget = (Widget*)widget->next;
584 if (strcmp(widget->name, name) == 0)
585 return widget;
588 debug(("No such widget '%s'\n", name));
590 return NULL;
593 void Engine::showWidgetGroup(char *groupName, bool show)
595 bool found = false;
597 Widget *widget = (Widget*)widgetList.getHead();
599 while (widget->next != NULL)
601 widget = (Widget*)widget->next;
603 if (strcmp(widget->groupName, groupName) == 0)
605 widget->visible = show;
606 widget->redraw();
607 found = true;
611 if (!found)
612 debug(("Group '%s' does not exist\n", groupName));
615 void Engine::enableWidgetGroup(char *groupName, bool show)
617 bool found = false;
619 Widget *widget = (Widget*)widgetList.getHead();
621 while (widget->next != NULL)
623 widget = (Widget*)widget->next;
625 if (strcmp(widget->groupName, groupName) == 0)
627 widget->enabled = show;
628 widget->redraw();
629 found = true;
633 if (!found)
634 debug(("Group '%s' does not exist\n", groupName));
637 void Engine::showWidget(char *name, bool show)
639 Widget *widget = getWidgetByName(name);
640 if (widget != NULL)
642 widget->visible = show;
643 widget->redraw();
647 void Engine::enableWidget(char *name, bool enable)
649 Widget *widget = getWidgetByName(name);
650 if (widget != NULL)
652 widget->enabled = enable;
653 widget->redraw();
657 void Engine::setWidgetVariable(char *name, int *variable)
659 Widget *widget = getWidgetByName(name);
660 if (widget != NULL)
661 widget->value = variable;
664 bool Engine::widgetChanged(char *name)
666 Widget *widget = getWidgetByName(name);
667 if (widget != NULL)
668 return widget->changed;
670 return false;
673 void Engine::highlightWidget(int dir)
675 highlightedWidget->redraw();
677 if (dir == 1)
679 while (true)
681 if (highlightedWidget->next != NULL)
683 highlightedWidget = (Widget*)highlightedWidget->next;
685 else
687 highlightedWidget = (Widget*)widgetList.getHead()->next;
690 if (highlightedWidget->type == 4)
691 continue;
693 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
694 break;
698 if (dir == -1)
700 while (true)
702 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
704 highlightedWidget = highlightedWidget->previous;
706 else
708 highlightedWidget = (Widget*)widgetList.getTail();
711 if (highlightedWidget->type == WG_LABEL)
712 continue;
714 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
715 break;
719 highlightedWidget->redraw();
722 void Engine::highlightWidget(char *name)
724 highlightedWidget = getWidgetByName(name);
727 int Engine::processWidgets()
729 int update = 0;
731 if (keyState[SDLK_UP])
733 highlightWidget(-1);
734 update = 1;
735 clearInput();
738 if (keyState[SDLK_DOWN])
740 highlightWidget(1);
741 update = 1;
742 clearInput();
745 if (keyState[SDLK_LEFT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
747 SDL_Delay(1);
749 if (*highlightedWidget->value > highlightedWidget->min)
751 *highlightedWidget->value = *highlightedWidget->value - 1;
752 update = 3;
753 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
754 update = 1;
755 highlightedWidget->changed = true;
758 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
759 clearInput();
762 if (keyState[SDLK_RIGHT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
764 SDL_Delay(1);
766 if (*highlightedWidget->value < highlightedWidget->max)
768 *highlightedWidget->value = *highlightedWidget->value + 1;
769 update = 3;
770 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
771 update = 1;
772 highlightedWidget->changed = true;
775 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
776 clearInput();
779 if ((keyState[SDLK_RETURN]) || (keyState[SDLK_SPACE]) || (keyState[SDLK_LCTRL]))
781 if (highlightedWidget->value == NULL)
783 debug(("%s has not been implemented!\n", highlightedWidget->name));
785 else
787 if (highlightedWidget->type == WG_BUTTON)
789 *highlightedWidget->value = 1;
790 highlightedWidget->changed = true;
792 else if (highlightedWidget->type == WG_JOYPAD)
794 waitForButton = true;
795 waitForKey = false;
796 allowJoypad = true;
798 if (*highlightedWidget->value > -1000)
800 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
803 else if (highlightedWidget->type == WG_KEYBOARD)
805 waitForKey = true;
806 waitForButton = false;
807 allowJoypad = false;
808 *highlightedWidget->value = -*highlightedWidget->value;
811 update = 2;
815 flushInput();
816 clearInput();
819 return update;
822 #if !UNIX
823 char *strtok_r(char *s1, const char *s2, char **lasts)
825 char *ret;
827 if (s1 == NULL)
829 s1 = *lasts;
832 while (*s1 && strchr(s2, *s1))
834 ++s1;
837 if (*s1 == '\0')
839 return NULL;
842 ret = s1;
844 while(*s1 && !strchr(s2, *s1))
846 ++s1;
849 if(*s1)
851 *s1++ = '\0';
854 *lasts = s1;
856 return ret;
858 #endif
861 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
862 game at compile time and run time, so everything is syncronised. This technique has the advantage of
863 allowing the game's data to be human readable and easy to maintain.
865 bool Engine::loadDefines()
867 char string[2][1024];
869 if (!loadData("data/defines.h"))
870 return false;
872 char *token = strtok((char*)dataBuffer, "\n");
874 Data *data;
876 while (true)
878 token = strtok(NULL, "\n");
879 if (!token)
880 break;
882 if (!strstr(token, "/*"))
884 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
885 data = new Data();
886 data->set(string[0], string[1], 1, 1);
887 defineList.add(data);
891 return true;
895 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
896 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
897 1) It makes the game data human readable and 2) It means if I change a #define in
898 the code, I don't have to change all the data entries too. You probably already
899 thought of that though... :)
901 int Engine::getValueOfDefine(char *word)
903 int rtn = 0;
905 Data *data = (Data*)defineList.getHead();
907 while (data->next != NULL)
909 data = (Data*)data->next;
911 if (strcmp(data->key, word) == 0)
913 rtn = atoi(data->value);
914 return rtn;
918 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
919 exit(1);
923 Does the opposite of the above(!)
925 char *Engine::getDefineOfValue(char *prefix, int value)
927 int rtn = 0;
929 Data *data = (Data*)defineList.getHead();
931 while (data->next != NULL)
933 data = (Data*)data->next;
935 if (strstr(data->key, prefix))
937 rtn = atoi(data->value);
939 if (rtn == value)
941 return data->key;
946 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
947 exit(1);
951 I like this function. It receives a list of flags declared by their #define name... like
952 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
953 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
954 the values together and then returns them... phew! Makes data files human readable though :)
956 int Engine::getValueOfFlagTokens(char *realLine)
958 if (strcmp(realLine, "0") == 0)
959 return 0;
961 char *store;
962 char line[1024];
963 bool found;
964 int value;
965 strcpy(line, realLine);
967 int flags = 0;
969 char *word = strtok_r(line, "+", &store);
971 if (!word)
973 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
974 exit(1);
977 Data *data;
979 while (true)
981 data = (Data*)defineList.getHead();
982 found = false;
984 while (data->next != NULL)
986 data = (Data*)data->next;
988 if (strcmp(data->key, word) == 0)
990 value = -1;
991 sscanf(data->value, "%d", &value);
993 if (value == -1)
995 sscanf(data->value, "%*s %*d %*s %d", &value);
996 value = 2 << value;
999 flags += value;
1000 found = true;
1001 break;
1005 if (!found)
1007 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1008 #if IGNORE_FLAGTOKEN_ERRORS
1009 break;
1010 #else
1011 exit(1);
1012 #endif
1015 word = strtok_r(NULL, "+", &store);
1016 if (!word)
1017 break;
1020 return flags;
1023 void Engine::delay(unsigned int frameLimit) {
1024 unsigned int ticks = SDL_GetTicks();
1026 if(frameLimit < ticks)
1027 return;
1029 if(frameLimit > ticks + 16)
1030 SDL_Delay(16);
1031 else
1032 SDL_Delay(frameLimit - ticks);