2 Copyright (C) 2004-2011 Parallel Realities
3 Copyright (C) 2011-2015 Perpendicular Dimensions
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.
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.
14 See the GNU General Public License for more details.
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.
24 extern Graphics graphics;
28 memset(keyState, 0, sizeof keyState);
30 memset(joystickState, 0, sizeof joystickState);
34 mouseLeft = mouseRight = 0;
35 waitForButton = false;
40 lastKeyPressed[0] = 0;
52 highlightedWidget = NULL;
58 devNoMonsters = false;
63 char pakPath[PATH_MAX];
64 strlcpy(pakPath, PAKFULLPATH, sizeof(pakPath));
65 if (CFBundleGetMainBundle() != NULL) {
66 CFURLRef pakURL = CFBundleCopyResourceURL(CFBundleGetMainBundle(), CFSTR(PAKNAME), NULL, NULL);
69 CFURLGetFileSystemRepresentation(pakURL, true, (UInt8*)pakPath, sizeof(pakPath));
73 pak.setPakFile(pakPath);
75 pak.setPakFile(PAKFULLPATH);
83 memset(lastKeyEvents, ' ', 25);
84 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
85 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
90 void Engine::destroy()
92 debug(("engine: free widgets\n"));
95 debug(("engine: free databuffer\n"));
98 debug(("engine: free binarybuffer\n"));
99 delete[] binaryBuffer;
101 debug(("Clearing Define List...\n"));
105 void Engine::clearCheatVars()
107 memset(lastKeyEvents, ' ', 25);
108 cheatHealth = cheatExtras = cheatFuel = cheatLevels = false;
109 cheatBlood = cheatInvulnerable = cheatReload = cheatSpeed = cheatSkipLevel = false;
112 bool Engine::compareLastKeyInputs()
114 if (strstr(lastKeyEvents, "lockandload"))
123 void Engine::addKeyEvent()
125 if (strlen(lastKeyPressed) > 1)
132 for (int i = 0 ; i < 25 ; i++)
134 if (lastKeyEvents[i] == ' ')
143 for (int i = 0 ; i < 24 ; i++)
145 lastKeyEvents[i] = lastKeyEvents[i + 1];
151 lastKeyEvents[index] = lastKeyPressed[0];
153 compareLastKeyInputs();
156 void Engine::getInput()
158 SDL_GetMouseState(&mouseX, &mouseY);
160 while (SDL_PollEvent(&event))
171 case SDL_MOUSEBUTTONDOWN:
172 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
173 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
176 case SDL_MOUSEBUTTONUP:
177 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
178 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
185 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
187 lastButtonPressed = -1;
188 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
189 highlightedWidget->redraw();
190 waitForButton = false;
194 if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
196 lastButtonPressed = -2;
197 *highlightedWidget->value = -2;
198 highlightedWidget->redraw();
199 waitForButton = false;
208 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
210 *highlightedWidget->value = -*highlightedWidget->value;
214 *highlightedWidget->value = event.key.keysym.scancode;
217 lastButtonPressed = -1;
218 highlightedWidget->redraw();
219 waitForButton = false;
226 keyState[event.key.keysym.scancode] = 1;
227 strlcpy(lastKeyPressed, SDL_GetKeyName(event.key.keysym.scancode), sizeof lastKeyPressed);
232 keyState[event.key.keysym.scancode] = 0;
235 case SDL_JOYAXISMOTION:
236 if (event.jaxis.axis == 0)
238 joyX = event.jaxis.value;
240 else if (event.jaxis.axis == 1)
242 joyY = event.jaxis.value;
247 case SDL_JOYBUTTONDOWN:
251 lastButtonPressed = event.jbutton.button;
252 *highlightedWidget->value = lastButtonPressed;
253 highlightedWidget->redraw();
254 waitForButton = false;
259 joystickState[event.jbutton.button] = 1;
262 case SDL_JOYBUTTONUP:
263 joystickState[event.jbutton.button] = 0;
266 case SDL_WINDOWEVENT:
267 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
277 int Engine::getMouseX() const
282 int Engine::getMouseY() const
287 void Engine::setMouse(int x, int y)
289 SDL_WarpMouseInWindow(graphics.window, x, y);
292 bool Engine::userAccepts()
294 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]))
302 void Engine::flushInput()
304 while (SDL_PollEvent(&event)){}
307 void Engine::clearInput()
309 memset(keyState, 0, sizeof keyState);
311 mouseLeft = mouseRight = 0;
314 void Engine::setUserHome(const char *path)
316 strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
317 debug(("User Home = %s\n", path));
320 Pak *Engine::getPak()
326 Searches the pak file for the required data. When
327 it is found, the data is read into a character buffer.
328 In the case of music, the data music be written to a temporary directory
329 since SDL currently provides no means to load music directly from memory
331 bool Engine::unpack(const char *filename, int fileType)
335 if (fileType == PAK_DATA)
342 delete[] binaryBuffer;
346 if (fileType != PAK_DATA)
348 if (!pak.unpack(filename, &binaryBuffer))
355 if (!pak.unpack(filename, &dataBuffer))
361 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
363 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
366 printf("Fatal Error: SDL_RWops allocation failed\n");
371 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
373 char tempPath[PATH_MAX];
377 if (fileType == PAK_MUSIC)
379 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
380 fp = fopen(tempPath, "wb");
383 if (fileType == PAK_TAGS)
385 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
386 fp = fopen(tempPath, "wb");
389 if (fileType == PAK_FONT)
391 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
392 fp = fopen(tempPath, "wb");
397 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
401 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
403 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
409 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
414 bool Engine::loadData(const char *filename)
422 return unpack(filename, PAK_DATA);
426 fp = fopen(filename, "rb");
430 fseek(fp, 0, SEEK_END);
432 int fSize = ftell(fp);
436 dataBuffer = new unsigned char[fSize + 1];
438 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
441 dataBuffer[fSize] = 0;
445 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
450 void Engine::reportFontFailure()
452 printf("\nUnable to load font. The game cannot continue without it.\n");
453 printf("Please confirm that the game and all required SDL libraries are installed\n");
454 printf("The following information may be useful to you,\n\n");
455 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
456 printf("Location of TMP directory: %s\n", userHomeDirectory);
457 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
461 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
463 playerPosX = x - OFFSETX;
464 playerPosY = y - OFFSETY;
466 Math::limitInt(&playerPosX, limitLeft, limitRight);
467 Math::limitInt(&playerPosY, limitUp, limitDown);
470 int Engine::getFrameLoop() const
475 void Engine::doFrameLoop()
477 Math::wrapChar(&(++frameLoop), 0, 59);
480 void Engine::doTimeDifference()
482 timeDifference = (time2 - time1) / 10.0;
484 time2 = SDL_GetTicks();
487 float Engine::getTimeDifference() const
489 return timeDifference;
492 void Engine::resetTimeDifference()
494 time1 = time2 = SDL_GetTicks();
497 void Engine::setInfoMessage(const char *message, int priority, int type)
499 if (priority >= messagePriority)
501 strlcpy(this->message, message, sizeof this->message);
503 messagePriority = priority;
508 void Engine::deleteWidgets()
512 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
517 highlightedWidget = NULL;
520 void Engine::addWidget(Widget *widget)
522 widget->previous = (Widget*)widgetList.getTail();
523 widgetList.add(widget);
526 bool Engine::loadWidgets(const char *filename)
530 if (!loadData(filename))
533 char token[50], name[50], groupName[50], label[80], options[100], *line;
540 line = strtok((char*)dataBuffer, "\n");
544 sscanf(line, "%s", token);
546 if (strcmp(token, "END") == 0)
549 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
557 if (strcmp(token, widgetName[i]) == 0)
560 if (strcmp("-1", widgetName[i]) == 0)
566 widget->setProperties(name, groupName, label, options, x, y, min, max);
571 if ((line = strtok(NULL, "\n")) == NULL)
575 highlightedWidget = (Widget*)widgetList.getHead()->next;
580 Widget *Engine::getWidgetByName(const char *name)
582 Widget *widget = (Widget*)widgetList.getHead();
584 while (widget->next != NULL)
586 widget = (Widget*)widget->next;
588 if (strcmp(widget->name, name) == 0)
592 debug(("No such widget '%s'\n", name));
597 void Engine::showWidgetGroup(const char *groupName, bool show)
601 Widget *widget = (Widget*)widgetList.getHead();
603 while (widget->next != NULL)
605 widget = (Widget*)widget->next;
607 if (strcmp(widget->groupName, groupName) == 0)
609 widget->visible = show;
616 debug(("Group '%s' does not exist\n", groupName));
619 void Engine::enableWidgetGroup(const char *groupName, bool show)
623 Widget *widget = (Widget*)widgetList.getHead();
625 while (widget->next != NULL)
627 widget = (Widget*)widget->next;
629 if (strcmp(widget->groupName, groupName) == 0)
631 widget->enabled = show;
638 debug(("Group '%s' does not exist\n", groupName));
641 void Engine::showWidget(const char *name, bool show)
643 Widget *widget = getWidgetByName(name);
646 widget->visible = show;
651 void Engine::enableWidget(const char *name, bool enable)
653 Widget *widget = getWidgetByName(name);
656 widget->enabled = enable;
661 void Engine::setWidgetVariable(const char *name, int *variable)
663 Widget *widget = getWidgetByName(name);
665 widget->value = variable;
668 bool Engine::widgetChanged(const char *name)
670 Widget *widget = getWidgetByName(name);
672 return widget->changed;
677 void Engine::highlightWidget(int dir)
679 highlightedWidget->redraw();
685 if (highlightedWidget->next != NULL)
687 highlightedWidget = (Widget*)highlightedWidget->next;
691 highlightedWidget = (Widget*)widgetList.getHead()->next;
694 if (highlightedWidget->type == 4)
697 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
706 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
708 highlightedWidget = highlightedWidget->previous;
712 highlightedWidget = (Widget*)widgetList.getTail();
715 if (highlightedWidget->type == WG_LABEL)
718 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
723 highlightedWidget->redraw();
726 void Engine::highlightWidget(const char *name)
728 highlightedWidget = getWidgetByName(name);
731 int Engine::processWidgets()
735 if (keyState[SDL_SCANCODE_UP])
742 if (keyState[SDL_SCANCODE_DOWN])
749 if (keyState[SDL_SCANCODE_LEFT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
753 if (*highlightedWidget->value > highlightedWidget->min)
755 *highlightedWidget->value = *highlightedWidget->value - 1;
757 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
759 highlightedWidget->changed = true;
762 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
766 if (keyState[SDL_SCANCODE_RIGHT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
770 if (*highlightedWidget->value < highlightedWidget->max)
772 *highlightedWidget->value = *highlightedWidget->value + 1;
774 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
776 highlightedWidget->changed = true;
779 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
783 if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]))
785 if (highlightedWidget->value == NULL)
787 debug(("%s has not been implemented!\n", highlightedWidget->name));
791 if (highlightedWidget->type == WG_BUTTON)
793 *highlightedWidget->value = 1;
794 highlightedWidget->changed = true;
796 else if (highlightedWidget->type == WG_JOYPAD)
798 waitForButton = true;
802 if (*highlightedWidget->value > -1000)
804 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
807 else if (highlightedWidget->type == WG_KEYBOARD)
810 waitForButton = false;
812 *highlightedWidget->value = -*highlightedWidget->value;
827 char *strtok_r(char *s1, const char *s2, char **lasts)
836 while (*s1 && strchr(s2, *s1))
848 while(*s1 && !strchr(s2, *s1))
865 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
866 game at compile time and run time, so everything is syncronised. This technique has the advantage of
867 allowing the game's data to be human readable and easy to maintain.
869 bool Engine::loadDefines()
871 char string[2][1024];
873 if (!loadData("data/defines.h"))
876 char *token = strtok((char*)dataBuffer, "\n");
882 token = strtok(NULL, "\n");
886 if (!strstr(token, "/*"))
888 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
890 data->set(string[0], string[1], 1, 1);
891 defineList.add(data);
899 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
900 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
901 1) It makes the game data human readable and 2) It means if I change a #define in
902 the code, I don't have to change all the data entries too. You probably already
903 thought of that though... :)
905 int Engine::getValueOfDefine(const char *word)
909 Data *data = (Data*)defineList.getHead();
911 while (data->next != NULL)
913 data = (Data*)data->next;
915 if (strcmp(data->key, word) == 0)
917 rtn = atoi(data->value);
922 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
927 Does the opposite of the above(!)
929 char *Engine::getDefineOfValue(const char *prefix, int value)
933 Data *data = (Data*)defineList.getHead();
935 while (data->next != NULL)
937 data = (Data*)data->next;
939 if (strstr(data->key, prefix))
941 rtn = atoi(data->value);
950 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
955 I like this function. It receives a list of flags declared by their #define name... like
956 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
957 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
958 the values together and then returns them... phew! Makes data files human readable though :)
960 int Engine::getValueOfFlagTokens(const char *realLine)
962 if (strcmp(realLine, "0") == 0)
969 strlcpy(line, realLine, sizeof line);
973 char *word = strtok_r(line, "+", &store);
977 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
985 data = (Data*)defineList.getHead();
988 while (data->next != NULL)
990 data = (Data*)data->next;
992 if (strcmp(data->key, word) == 0)
995 sscanf(data->value, "%d", &value);
999 sscanf(data->value, "%*s %*d %*s %d", &value);
1011 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1012 #if IGNORE_FLAGTOKEN_ERRORS
1019 word = strtok_r(NULL, "+", &store);
1027 void Engine::delay(unsigned int frameLimit) {
1028 unsigned int ticks = SDL_GetTicks();
1030 if(frameLimit < ticks)
1033 if(frameLimit > ticks + 16)
1036 SDL_Delay(frameLimit - ticks);