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;
245 int joycurX = joyX < -25000 ? -1 : joyX > 25000 ? 1 : 0;
246 if (joycurX != joyprevX)
247 joykeyX = joyprevX = joycurX;
249 else if (event.jaxis.axis == 1)
251 joyY = event.jaxis.value;
252 int joycurY = joyY < -25000 ? -1 : joyY > 25000 ? 1 : 0;
253 if (joycurY != joyprevY)
254 joykeyY = joyprevY = joycurY;
258 case SDL_JOYBUTTONDOWN:
261 lastButtonPressed = event.jbutton.button;
262 *highlightedWidget->value = lastButtonPressed;
263 highlightedWidget->redraw();
264 waitForButton = false;
269 joystickState[event.jbutton.button] = 1;
273 case SDL_JOYBUTTONUP:
274 joystickState[event.jbutton.button] = 0;
278 case SDL_WINDOWEVENT:
279 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
289 int Engine::getMouseX() const
294 int Engine::getMouseY() const
299 void Engine::setMouse(int x, int y)
301 SDL_WarpMouseInWindow(graphics.window, x, y);
304 bool Engine::userAccepts()
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)
314 void Engine::flushInput()
316 while (SDL_PollEvent(&event)){}
319 void Engine::clearInput()
321 memset(keyState, 0, sizeof keyState);
323 mouseLeft = mouseRight = 0;
324 joykeyX = joykeyY = 0;
328 void Engine::setUserHome(const char *path)
330 strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
331 debug(("User Home = %s\n", path));
334 Pak *Engine::getPak()
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
345 bool Engine::unpack(const char *filename, int fileType)
349 if (fileType == PAK_DATA)
356 delete[] binaryBuffer;
360 if (fileType != PAK_DATA)
362 if (!pak.unpack(filename, &binaryBuffer))
369 if (!pak.unpack(filename, &dataBuffer))
375 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
377 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
380 printf("Fatal Error: SDL_RWops allocation failed\n");
385 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
387 char tempPath[PATH_MAX];
391 if (fileType == PAK_MUSIC)
393 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
394 fp = fopen(tempPath, "wb");
397 if (fileType == PAK_TAGS)
399 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
400 fp = fopen(tempPath, "wb");
403 if (fileType == PAK_FONT)
405 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
406 fp = fopen(tempPath, "wb");
411 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
415 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
417 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
423 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
428 bool Engine::loadData(const char *filename)
436 return unpack(filename, PAK_DATA);
440 fp = fopen(filename, "rb");
444 fseek(fp, 0, SEEK_END);
446 int fSize = ftell(fp);
450 dataBuffer = new unsigned char[fSize + 1];
452 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
455 dataBuffer[fSize] = 0;
459 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
464 void Engine::reportFontFailure()
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");
475 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
477 playerPosX = x - OFFSETX;
478 playerPosY = y - OFFSETY;
480 Math::limitInt(&playerPosX, limitLeft, limitRight);
481 Math::limitInt(&playerPosY, limitUp, limitDown);
484 int Engine::getFrameLoop() const
489 void Engine::doFrameLoop()
491 Math::wrapChar(&(++frameLoop), 0, 59);
494 void Engine::doTimeDifference()
496 timeDifference = (time2 - time1) / 10.0;
498 time2 = SDL_GetTicks();
501 float Engine::getTimeDifference() const
503 return timeDifference;
506 void Engine::resetTimeDifference()
508 time1 = time2 = SDL_GetTicks();
511 void Engine::setInfoMessage(const char *message, int priority, int type)
513 if (priority >= messagePriority)
515 strlcpy(this->message, message, sizeof this->message);
517 messagePriority = priority;
522 void Engine::deleteWidgets()
526 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
531 highlightedWidget = NULL;
534 void Engine::addWidget(Widget *widget)
536 widget->previous = (Widget*)widgetList.getTail();
537 widgetList.add(widget);
540 bool Engine::loadWidgets(const char *filename)
544 if (!loadData(filename))
547 char token[50], name[50], groupName[50], label[80], options[100], *line;
554 line = strtok((char*)dataBuffer, "\n");
558 sscanf(line, "%s", token);
560 if (strcmp(token, "END") == 0)
563 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
571 if (strcmp(token, widgetName[i]) == 0)
574 if (strcmp("-1", widgetName[i]) == 0)
580 widget->setProperties(name, groupName, label, options, x, y, min, max);
585 if ((line = strtok(NULL, "\n")) == NULL)
589 highlightedWidget = (Widget*)widgetList.getHead()->next;
594 Widget *Engine::getWidgetByName(const char *name)
596 Widget *widget = (Widget*)widgetList.getHead();
598 while (widget->next != NULL)
600 widget = (Widget*)widget->next;
602 if (strcmp(widget->name, name) == 0)
606 debug(("No such widget '%s'\n", name));
611 void Engine::showWidgetGroup(const char *groupName, bool show)
615 Widget *widget = (Widget*)widgetList.getHead();
617 while (widget->next != NULL)
619 widget = (Widget*)widget->next;
621 if (strcmp(widget->groupName, groupName) == 0)
623 widget->visible = show;
630 debug(("Group '%s' does not exist\n", groupName));
633 void Engine::enableWidgetGroup(const char *groupName, bool show)
637 Widget *widget = (Widget*)widgetList.getHead();
639 while (widget->next != NULL)
641 widget = (Widget*)widget->next;
643 if (strcmp(widget->groupName, groupName) == 0)
645 widget->enabled = show;
652 debug(("Group '%s' does not exist\n", groupName));
655 void Engine::showWidget(const char *name, bool show)
657 Widget *widget = getWidgetByName(name);
660 widget->visible = show;
665 void Engine::enableWidget(const char *name, bool enable)
667 Widget *widget = getWidgetByName(name);
670 widget->enabled = enable;
675 void Engine::setWidgetVariable(const char *name, int *variable)
677 Widget *widget = getWidgetByName(name);
679 widget->value = variable;
682 bool Engine::widgetChanged(const char *name)
684 Widget *widget = getWidgetByName(name);
686 return widget->changed;
691 void Engine::highlightWidget(int dir)
693 highlightedWidget->redraw();
699 if (highlightedWidget->next != NULL)
701 highlightedWidget = (Widget*)highlightedWidget->next;
705 highlightedWidget = (Widget*)widgetList.getHead()->next;
708 if (highlightedWidget->type == 4)
711 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
720 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
722 highlightedWidget = highlightedWidget->previous;
726 highlightedWidget = (Widget*)widgetList.getTail();
729 if (highlightedWidget->type == WG_LABEL)
732 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
737 highlightedWidget->redraw();
740 void Engine::highlightWidget(const char *name)
742 highlightedWidget = getWidgetByName(name);
745 int Engine::processWidgets()
749 if (keyState[SDL_SCANCODE_UP] || joykeyY < 0)
756 if (keyState[SDL_SCANCODE_DOWN] || joykeyY > 0)
763 if ((keyState[SDL_SCANCODE_LEFT] || joykeyX < 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
767 if (*highlightedWidget->value > highlightedWidget->min)
769 *highlightedWidget->value = *highlightedWidget->value - 1;
771 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
773 highlightedWidget->changed = true;
776 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
780 if ((keyState[SDL_SCANCODE_RIGHT] || joykeyX > 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
784 if (*highlightedWidget->value < highlightedWidget->max)
786 *highlightedWidget->value = *highlightedWidget->value + 1;
788 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
790 highlightedWidget->changed = true;
793 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
797 if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]) || (joykeyFire && highlightedWidget->type != WG_JOYPAD))
799 if (highlightedWidget->value == NULL)
801 debug(("%s has not been implemented!\n", highlightedWidget->name));
805 if (highlightedWidget->type == WG_BUTTON)
807 *highlightedWidget->value = 1;
808 highlightedWidget->changed = true;
810 else if (highlightedWidget->type == WG_JOYPAD)
812 waitForButton = true;
816 if (*highlightedWidget->value > -1000)
818 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
821 else if (highlightedWidget->type == WG_KEYBOARD)
824 waitForButton = false;
826 *highlightedWidget->value = -*highlightedWidget->value;
837 if (joykeyX > 0 && highlightedWidget->type == WG_JOYPAD)
839 waitForButton = true;
843 if (*highlightedWidget->value > -1000)
845 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
851 if (joykeyX < 0 && highlightedWidget->type == WG_JOYPAD)
855 lastButtonPressed = -1;
856 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
860 lastButtonPressed = -2;
861 *highlightedWidget->value = -2;
864 waitForButton = false;
866 highlightedWidget->redraw();
874 char *strtok_r(char *s1, const char *s2, char **lasts)
883 while (*s1 && strchr(s2, *s1))
895 while(*s1 && !strchr(s2, *s1))
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.
916 bool Engine::loadDefines()
918 char string[2][1024];
920 if (!loadData("data/defines.h"))
923 char *token = strtok((char*)dataBuffer, "\n");
929 token = strtok(NULL, "\n");
933 if (!strstr(token, "/*"))
935 sscanf(token, "%*s %s %[^\n\r]", string[0], string[1]);
937 data->set(string[0], string[1], 1, 1);
938 defineList.add(data);
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... :)
952 int Engine::getValueOfDefine(const char *word)
956 Data *data = (Data*)defineList.getHead();
958 while (data->next != NULL)
960 data = (Data*)data->next;
962 if (strcmp(data->key, word) == 0)
964 rtn = atoi(data->value);
969 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
974 Does the opposite of the above(!)
976 char *Engine::getDefineOfValue(const char *prefix, int value)
980 Data *data = (Data*)defineList.getHead();
982 while (data->next != NULL)
984 data = (Data*)data->next;
986 if (strstr(data->key, prefix))
988 rtn = atoi(data->value);
997 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
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 :)
1007 int Engine::getValueOfFlagTokens(const char *realLine)
1009 if (strcmp(realLine, "0") == 0)
1016 strlcpy(line, realLine, sizeof line);
1020 char *word = strtok_r(line, "+", &store);
1024 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
1032 data = (Data*)defineList.getHead();
1035 while (data->next != NULL)
1037 data = (Data*)data->next;
1039 if (strcmp(data->key, word) == 0)
1042 sscanf(data->value, "%d", &value);
1046 sscanf(data->value, "%*s %*d %*s %d", &value);
1058 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1059 #if IGNORE_FLAGTOKEN_ERRORS
1066 word = strtok_r(NULL, "+", &store);
1074 void Engine::delay(unsigned int frameLimit) {
1075 unsigned int ticks = SDL_GetTicks();
1077 if(frameLimit < ticks)
1080 if(frameLimit > ticks + 16)
1083 SDL_Delay(frameLimit - ticks);