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 // Scale from window coordinates to graphics coordinates
162 SDL_GetWindowSize(graphics.window, &w, &h);
163 mouseX = mouseX * 640 / w;
164 mouseY = mouseY * 480 / h;
166 while (SDL_PollEvent(&event))
177 case SDL_MOUSEBUTTONDOWN:
178 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
179 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
182 case SDL_MOUSEBUTTONUP:
183 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
184 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
191 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
193 lastButtonPressed = -1;
194 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
195 highlightedWidget->redraw();
196 waitForButton = false;
200 if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
202 lastButtonPressed = -2;
203 *highlightedWidget->value = -2;
204 highlightedWidget->redraw();
205 waitForButton = false;
214 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
216 *highlightedWidget->value = -*highlightedWidget->value;
220 *highlightedWidget->value = event.key.keysym.scancode;
223 lastButtonPressed = -1;
224 highlightedWidget->redraw();
225 waitForButton = false;
232 keyState[event.key.keysym.scancode] = 1;
233 strlcpy(lastKeyPressed, SDL_GetKeyName(event.key.keysym.scancode), sizeof lastKeyPressed);
238 keyState[event.key.keysym.scancode] = 0;
241 case SDL_JOYAXISMOTION:
242 if (event.jaxis.axis == 0)
244 joyX = event.jaxis.value;
246 else if (event.jaxis.axis == 1)
248 joyY = event.jaxis.value;
253 case SDL_JOYBUTTONDOWN:
257 lastButtonPressed = event.jbutton.button;
258 *highlightedWidget->value = lastButtonPressed;
259 highlightedWidget->redraw();
260 waitForButton = false;
265 joystickState[event.jbutton.button] = 1;
268 case SDL_JOYBUTTONUP:
269 joystickState[event.jbutton.button] = 0;
272 case SDL_WINDOWEVENT:
273 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
283 int Engine::getMouseX() const
288 int Engine::getMouseY() const
293 void Engine::setMouse(int x, int y)
295 SDL_WarpMouseInWindow(graphics.window, x, y);
298 bool Engine::userAccepts()
300 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]))
308 void Engine::flushInput()
310 while (SDL_PollEvent(&event)){}
313 void Engine::clearInput()
315 memset(keyState, 0, sizeof keyState);
317 mouseLeft = mouseRight = 0;
320 void Engine::setUserHome(const char *path)
322 strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
323 debug(("User Home = %s\n", path));
326 Pak *Engine::getPak()
332 Searches the pak file for the required data. When
333 it is found, the data is read into a character buffer.
334 In the case of music, the data music be written to a temporary directory
335 since SDL currently provides no means to load music directly from memory
337 bool Engine::unpack(const char *filename, int fileType)
341 if (fileType == PAK_DATA)
348 delete[] binaryBuffer;
352 if (fileType != PAK_DATA)
354 if (!pak.unpack(filename, &binaryBuffer))
361 if (!pak.unpack(filename, &dataBuffer))
367 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
369 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
372 printf("Fatal Error: SDL_RWops allocation failed\n");
377 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
379 char tempPath[PATH_MAX];
383 if (fileType == PAK_MUSIC)
385 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
386 fp = fopen(tempPath, "wb");
389 if (fileType == PAK_TAGS)
391 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
392 fp = fopen(tempPath, "wb");
395 if (fileType == PAK_FONT)
397 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
398 fp = fopen(tempPath, "wb");
403 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
407 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
409 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
415 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
420 bool Engine::loadData(const char *filename)
428 return unpack(filename, PAK_DATA);
432 fp = fopen(filename, "rb");
436 fseek(fp, 0, SEEK_END);
438 int fSize = ftell(fp);
442 dataBuffer = new unsigned char[fSize + 1];
444 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
447 dataBuffer[fSize] = 0;
451 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
456 void Engine::reportFontFailure()
458 printf("\nUnable to load font. The game cannot continue without it.\n");
459 printf("Please confirm that the game and all required SDL libraries are installed\n");
460 printf("The following information may be useful to you,\n\n");
461 printf("Expected location of PAK file: %s\n", PAKFULLPATH);
462 printf("Location of TMP directory: %s\n", userHomeDirectory);
463 printf("\nAlso try checking http://www.parallelrealities.co.uk/blobWars.php for updates\n\n");
467 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
469 playerPosX = x - OFFSETX;
470 playerPosY = y - OFFSETY;
472 Math::limitInt(&playerPosX, limitLeft, limitRight);
473 Math::limitInt(&playerPosY, limitUp, limitDown);
476 int Engine::getFrameLoop() const
481 void Engine::doFrameLoop()
483 Math::wrapChar(&(++frameLoop), 0, 59);
486 void Engine::doTimeDifference()
488 timeDifference = (time2 - time1) / 10.0;
490 time2 = SDL_GetTicks();
493 float Engine::getTimeDifference() const
495 return timeDifference;
498 void Engine::resetTimeDifference()
500 time1 = time2 = SDL_GetTicks();
503 void Engine::setInfoMessage(const char *message, int priority, int type)
505 if (priority >= messagePriority)
507 strlcpy(this->message, message, sizeof this->message);
509 messagePriority = priority;
514 void Engine::deleteWidgets()
518 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
523 highlightedWidget = NULL;
526 void Engine::addWidget(Widget *widget)
528 widget->previous = (Widget*)widgetList.getTail();
529 widgetList.add(widget);
532 bool Engine::loadWidgets(const char *filename)
536 if (!loadData(filename))
539 char token[50], name[50], groupName[50], label[80], options[100], *line;
546 line = strtok((char*)dataBuffer, "\n");
550 sscanf(line, "%s", token);
552 if (strcmp(token, "END") == 0)
555 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
563 if (strcmp(token, widgetName[i]) == 0)
566 if (strcmp("-1", widgetName[i]) == 0)
572 widget->setProperties(name, groupName, label, options, x, y, min, max);
577 if ((line = strtok(NULL, "\n")) == NULL)
581 highlightedWidget = (Widget*)widgetList.getHead()->next;
586 Widget *Engine::getWidgetByName(const char *name)
588 Widget *widget = (Widget*)widgetList.getHead();
590 while (widget->next != NULL)
592 widget = (Widget*)widget->next;
594 if (strcmp(widget->name, name) == 0)
598 debug(("No such widget '%s'\n", name));
603 void Engine::showWidgetGroup(const char *groupName, bool show)
607 Widget *widget = (Widget*)widgetList.getHead();
609 while (widget->next != NULL)
611 widget = (Widget*)widget->next;
613 if (strcmp(widget->groupName, groupName) == 0)
615 widget->visible = show;
622 debug(("Group '%s' does not exist\n", groupName));
625 void Engine::enableWidgetGroup(const char *groupName, bool show)
629 Widget *widget = (Widget*)widgetList.getHead();
631 while (widget->next != NULL)
633 widget = (Widget*)widget->next;
635 if (strcmp(widget->groupName, groupName) == 0)
637 widget->enabled = show;
644 debug(("Group '%s' does not exist\n", groupName));
647 void Engine::showWidget(const char *name, bool show)
649 Widget *widget = getWidgetByName(name);
652 widget->visible = show;
657 void Engine::enableWidget(const char *name, bool enable)
659 Widget *widget = getWidgetByName(name);
662 widget->enabled = enable;
667 void Engine::setWidgetVariable(const char *name, int *variable)
669 Widget *widget = getWidgetByName(name);
671 widget->value = variable;
674 bool Engine::widgetChanged(const char *name)
676 Widget *widget = getWidgetByName(name);
678 return widget->changed;
683 void Engine::highlightWidget(int dir)
685 highlightedWidget->redraw();
691 if (highlightedWidget->next != NULL)
693 highlightedWidget = (Widget*)highlightedWidget->next;
697 highlightedWidget = (Widget*)widgetList.getHead()->next;
700 if (highlightedWidget->type == 4)
703 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
712 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
714 highlightedWidget = highlightedWidget->previous;
718 highlightedWidget = (Widget*)widgetList.getTail();
721 if (highlightedWidget->type == WG_LABEL)
724 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
729 highlightedWidget->redraw();
732 void Engine::highlightWidget(const char *name)
734 highlightedWidget = getWidgetByName(name);
737 int Engine::processWidgets()
741 if (keyState[SDL_SCANCODE_UP])
748 if (keyState[SDL_SCANCODE_DOWN])
755 if (keyState[SDL_SCANCODE_LEFT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
759 if (*highlightedWidget->value > highlightedWidget->min)
761 *highlightedWidget->value = *highlightedWidget->value - 1;
763 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
765 highlightedWidget->changed = true;
768 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
772 if (keyState[SDL_SCANCODE_RIGHT] && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
776 if (*highlightedWidget->value < highlightedWidget->max)
778 *highlightedWidget->value = *highlightedWidget->value + 1;
780 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
782 highlightedWidget->changed = true;
785 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
789 if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]))
791 if (highlightedWidget->value == NULL)
793 debug(("%s has not been implemented!\n", highlightedWidget->name));
797 if (highlightedWidget->type == WG_BUTTON)
799 *highlightedWidget->value = 1;
800 highlightedWidget->changed = true;
802 else if (highlightedWidget->type == WG_JOYPAD)
804 waitForButton = true;
808 if (*highlightedWidget->value > -1000)
810 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
813 else if (highlightedWidget->type == WG_KEYBOARD)
816 waitForButton = false;
818 *highlightedWidget->value = -*highlightedWidget->value;
833 char *strtok_r(char *s1, const char *s2, char **lasts)
842 while (*s1 && strchr(s2, *s1))
854 while(*s1 && !strchr(s2, *s1))
871 Loads key-value defines into a linked list, comments are ignored. The defines.h file is used by the
872 game at compile time and run time, so everything is syncronised. This technique has the advantage of
873 allowing the game's data to be human readable and easy to maintain.
875 bool Engine::loadDefines()
877 char string[2][1024];
879 if (!loadData("data/defines.h"))
882 char *token = strtok((char*)dataBuffer, "\n");
888 token = strtok(NULL, "\n");
892 if (!strstr(token, "/*"))
894 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
896 data->set(string[0], string[1], 1, 1);
897 defineList.add(data);
905 Returns the value of a #defined value... ACTIVE is declared as 1 so it will
906 traverse the list and return 1 when it encounters ACTIVE. This has two advantages.
907 1) It makes the game data human readable and 2) It means if I change a #define in
908 the code, I don't have to change all the data entries too. You probably already
909 thought of that though... :)
911 int Engine::getValueOfDefine(const char *word)
915 Data *data = (Data*)defineList.getHead();
917 while (data->next != NULL)
919 data = (Data*)data->next;
921 if (strcmp(data->key, word) == 0)
923 rtn = atoi(data->value);
928 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
933 Does the opposite of the above(!)
935 char *Engine::getDefineOfValue(const char *prefix, int value)
939 Data *data = (Data*)defineList.getHead();
941 while (data->next != NULL)
943 data = (Data*)data->next;
945 if (strstr(data->key, prefix))
947 rtn = atoi(data->value);
956 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
961 I like this function. It receives a list of flags declared by their #define name... like
962 the function above, delimited with plus signs. So ENT_FLIES+ENT_AIMS. It then works out the
963 flags (in a bit of a half arsed manner because of my lazy (2 << 25) declarations, adds all
964 the values together and then returns them... phew! Makes data files human readable though :)
966 int Engine::getValueOfFlagTokens(const char *realLine)
968 if (strcmp(realLine, "0") == 0)
975 strlcpy(line, realLine, sizeof line);
979 char *word = strtok_r(line, "+", &store);
983 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
991 data = (Data*)defineList.getHead();
994 while (data->next != NULL)
996 data = (Data*)data->next;
998 if (strcmp(data->key, word) == 0)
1001 sscanf(data->value, "%d", &value);
1005 sscanf(data->value, "%*s %*d %*s %d", &value);
1017 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1018 #if IGNORE_FLAGTOKEN_ERRORS
1025 word = strtok_r(NULL, "+", &store);
1033 void Engine::delay(unsigned int frameLimit) {
1034 unsigned int ticks = SDL_GetTicks();
1036 if(frameLimit < ticks)
1039 if(frameLimit > ticks + 16)
1042 SDL_Delay(frameLimit - ticks);