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 while (SDL_PollEvent(&event))
169 case SDL_MOUSEBUTTONDOWN:
170 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 1;
171 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 1;
174 case SDL_MOUSEBUTTONUP:
175 if (event.button.button == SDL_BUTTON_LEFT) mouseLeft = 0;
176 if (event.button.button == SDL_BUTTON_RIGHT) mouseRight = 0;
179 case SDL_MOUSEMOTION:
180 mouseX = event.motion.x;
181 mouseY = event.motion.y;
188 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
190 lastButtonPressed = -1;
191 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
192 highlightedWidget->redraw();
193 waitForButton = false;
197 if (event.key.keysym.scancode == SDL_SCANCODE_BACKSPACE)
199 lastButtonPressed = -2;
200 *highlightedWidget->value = -2;
201 highlightedWidget->redraw();
202 waitForButton = false;
211 if (event.key.keysym.scancode == SDL_SCANCODE_ESCAPE)
213 *highlightedWidget->value = -*highlightedWidget->value;
217 *highlightedWidget->value = event.key.keysym.scancode;
220 lastButtonPressed = -1;
221 highlightedWidget->redraw();
222 waitForButton = false;
229 keyState[event.key.keysym.scancode] = 1;
230 strlcpy(lastKeyPressed, SDL_GetKeyName(SDL_GetKeyFromScancode(event.key.keysym.scancode)), sizeof lastKeyPressed);
235 keyState[event.key.keysym.scancode] = 0;
238 case SDL_JOYAXISMOTION:
239 if (event.jaxis.axis == 0)
241 joyX = event.jaxis.value;
242 int joycurX = joyX < -25000 ? -1 : joyX > 25000 ? 1 : 0;
243 if (joycurX != joyprevX)
244 joykeyX = joyprevX = joycurX;
246 else if (event.jaxis.axis == 1)
248 joyY = event.jaxis.value;
249 int joycurY = joyY < -25000 ? -1 : joyY > 25000 ? 1 : 0;
250 if (joycurY != joyprevY)
251 joykeyY = joyprevY = joycurY;
255 case SDL_JOYBUTTONDOWN:
258 lastButtonPressed = event.jbutton.button;
259 *highlightedWidget->value = lastButtonPressed;
260 highlightedWidget->redraw();
261 waitForButton = false;
266 joystickState[event.jbutton.button] = 1;
270 case SDL_JOYBUTTONUP:
271 joystickState[event.jbutton.button] = 0;
275 case SDL_JOYHATMOTION:
276 switch (event.jhat.value) {
277 case SDL_HAT_CENTERED:
278 joyX = 0, joyY = 0; break;
280 joyX = -32768, joyY = 0; break;
282 joyX = -32768, joyY = -32768; break;
284 joyX = 0, joyY = -32768; break;
285 case SDL_HAT_RIGHTUP:
286 joyX = 32767, joyY = -32768; break;
288 joyX = 32767, joyY = 0; break;
289 case SDL_HAT_RIGHTDOWN:
290 joyX = 32767, joyY = 32767; break;
292 joyX = 0, joyY = 32767; break;
293 case SDL_HAT_LEFTDOWN:
294 joyX = -32768, joyY = 32767; break;
297 if (joyX != joyprevX)
298 joykeyX = joyprevX = joyX;
299 if (joyY != joyprevY)
300 joykeyY = joyprevY = joyY;
304 case SDL_WINDOWEVENT:
305 if (event.window.event == SDL_WINDOWEVENT_FOCUS_LOST)
315 int Engine::getMouseX() const
320 int Engine::getMouseY() const
325 void Engine::moveMouse(int dx, int dy)
329 Math::limitInt(&mouseX, 0, 640);
330 Math::limitInt(&mouseY, 0, 480);
333 bool Engine::userAccepts()
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)
343 void Engine::flushInput()
345 while (SDL_PollEvent(&event)){}
348 void Engine::clearInput()
350 memset(keyState, 0, sizeof keyState);
352 mouseLeft = mouseRight = 0;
353 joykeyX = joykeyY = 0;
357 void Engine::setUserHome(const char *path)
359 strlcpy(userHomeDirectory, path, sizeof userHomeDirectory);
360 debug(("User Home = %s\n", path));
363 Pak *Engine::getPak()
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
374 bool Engine::unpack(const char *filename, int fileType)
378 if (fileType == PAK_DATA)
385 delete[] binaryBuffer;
389 if (fileType != PAK_DATA)
391 if (!pak.unpack(filename, &binaryBuffer))
398 if (!pak.unpack(filename, &dataBuffer))
404 if ((fileType == PAK_IMG) || (fileType == PAK_SOUND))
406 sdlrw = SDL_RWFromMem(binaryBuffer, pak.getUncompressedSize());
409 printf("Fatal Error: SDL_RWops allocation failed\n");
414 if ((fileType == PAK_MUSIC) || (fileType == PAK_FONT) || (fileType == PAK_TAGS))
416 char tempPath[PATH_MAX];
420 if (fileType == PAK_MUSIC)
422 snprintf(tempPath, sizeof tempPath, "%smusic.mod", userHomeDirectory);
423 fp = fopen(tempPath, "wb");
425 else if (fileType == PAK_TAGS)
427 snprintf(tempPath, sizeof tempPath, "%smusic.tags", userHomeDirectory);
428 fp = fopen(tempPath, "wb");
430 else if (fileType == PAK_FONT)
432 snprintf(tempPath, sizeof tempPath, "%sfont.ttf", userHomeDirectory);
433 fp = fopen(tempPath, "wb");
438 printf("Fatal Error: could not open %s for writing: %s", tempPath, strerror(errno));
442 if (fwrite(binaryBuffer, 1, pak.getUncompressedSize(), fp) != pak.getUncompressedSize())
444 printf("Fatal Error: could not write to %s: %s", tempPath, strerror(errno));
450 debug(("unpack() : Loaded %s (%d), ret: %d\n", filename, pak.getUncompressedSize(), (int)ret));
455 bool Engine::loadData(const char *filename)
463 return unpack(filename, PAK_DATA);
467 fp = fopen(filename, "rb");
471 fseek(fp, 0, SEEK_END);
473 int fSize = ftell(fp);
477 dataBuffer = new unsigned char[fSize + 1];
479 if (fread(dataBuffer, 1, fSize, fp) != (size_t)fSize)
482 dataBuffer[fSize] = 0;
486 debug(("loadData() : Loaded %s (%d), ret: %d\n", filename, fSize, (int)ret));
491 void Engine::reportFontFailure()
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");
502 void Engine::setPlayerPosition(int x, int y, int limitLeft, int limitRight, int limitUp, int limitDown)
504 playerPosX = x - OFFSETX;
505 playerPosY = y - OFFSETY;
507 Math::limitInt(&playerPosX, limitLeft, limitRight);
508 Math::limitInt(&playerPosY, limitUp, limitDown);
511 int Engine::getFrameLoop() const
516 void Engine::doFrameLoop()
518 Math::wrapChar(&(++frameLoop), 0, 59);
521 void Engine::doTimeDifference()
523 timeDifference = (time2 - time1) / 10.0;
525 time2 = SDL_GetTicks();
528 float Engine::getTimeDifference() const
530 return timeDifference;
533 void Engine::resetTimeDifference()
535 time1 = time2 = SDL_GetTicks();
538 void Engine::setInfoMessage(const char *message, int priority, int type)
540 if (priority >= messagePriority)
542 strlcpy(this->message, message, sizeof this->message);
544 messagePriority = priority;
549 void Engine::deleteWidgets()
553 for (widget = (Widget*)widgetList.getHead()->next ; widget != NULL ; widget = (Widget*)widget->next)
558 highlightedWidget = NULL;
561 void Engine::addWidget(Widget *widget)
563 widget->previous = (Widget*)widgetList.getTail();
564 widgetList.add(widget);
567 bool Engine::loadWidgets(const char *filename)
571 if (!loadData(filename))
574 char token[50], name[50], groupName[50], label[80], options[100], *line;
581 line = strtok((char*)dataBuffer, "\n");
585 sscanf(line, "%s", token);
587 if (strcmp(token, "END") == 0)
590 sscanf(line, "%*s %s %s %*c %[^\"] %*c %*c %[^\"] %*c %d %d %d %d", name, groupName, label, options, &x, &y, &min, &max);
598 if (strcmp(token, widgetName[i]) == 0)
601 if (strcmp("-1", widgetName[i]) == 0)
607 widget->setProperties(name, groupName, label, options, x, y, min, max);
612 if ((line = strtok(NULL, "\n")) == NULL)
616 highlightedWidget = (Widget*)widgetList.getHead()->next;
621 Widget *Engine::getWidgetByName(const char *name)
623 Widget *widget = (Widget*)widgetList.getHead();
625 while (widget->next != NULL)
627 widget = (Widget*)widget->next;
629 if (strcmp(widget->name, name) == 0)
633 debug(("No such widget '%s'\n", name));
638 void Engine::showWidgetGroup(const char *groupName, bool show)
642 Widget *widget = (Widget*)widgetList.getHead();
644 while (widget->next != NULL)
646 widget = (Widget*)widget->next;
648 if (strcmp(widget->groupName, groupName) == 0)
650 widget->visible = show;
657 debug(("Group '%s' does not exist\n", groupName));
660 void Engine::enableWidgetGroup(const char *groupName, bool show)
664 Widget *widget = (Widget*)widgetList.getHead();
666 while (widget->next != NULL)
668 widget = (Widget*)widget->next;
670 if (strcmp(widget->groupName, groupName) == 0)
672 widget->enabled = show;
679 debug(("Group '%s' does not exist\n", groupName));
682 void Engine::showWidget(const char *name, bool show)
684 Widget *widget = getWidgetByName(name);
687 widget->visible = show;
692 void Engine::enableWidget(const char *name, bool enable)
694 Widget *widget = getWidgetByName(name);
697 widget->enabled = enable;
702 void Engine::setWidgetVariable(const char *name, int *variable)
704 Widget *widget = getWidgetByName(name);
706 widget->value = variable;
709 bool Engine::widgetChanged(const char *name)
711 Widget *widget = getWidgetByName(name);
713 return widget->changed;
718 void Engine::highlightWidget(int dir)
720 highlightedWidget->redraw();
726 if (highlightedWidget->next != NULL)
728 highlightedWidget = (Widget*)highlightedWidget->next;
732 highlightedWidget = (Widget*)widgetList.getHead()->next;
735 if (highlightedWidget->type == 4)
738 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
747 if ((highlightedWidget->previous != NULL) && (highlightedWidget->previous != (Widget*)widgetList.getHead()))
749 highlightedWidget = highlightedWidget->previous;
753 highlightedWidget = (Widget*)widgetList.getTail();
756 if (highlightedWidget->type == WG_LABEL)
759 if ((highlightedWidget->enabled) && (highlightedWidget->visible))
764 highlightedWidget->redraw();
767 void Engine::highlightWidget(const char *name)
769 highlightedWidget = getWidgetByName(name);
772 int Engine::processWidgets()
776 if (keyState[SDL_SCANCODE_UP] || joykeyY < 0)
783 if (keyState[SDL_SCANCODE_DOWN] || joykeyY > 0)
790 if ((keyState[SDL_SCANCODE_LEFT] || joykeyX < 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
794 if (*highlightedWidget->value > highlightedWidget->min)
796 *highlightedWidget->value = *highlightedWidget->value - 1;
798 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
800 highlightedWidget->changed = true;
803 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
807 if ((keyState[SDL_SCANCODE_RIGHT] || joykeyX > 0) && (highlightedWidget->type != WG_BUTTON && highlightedWidget->type != WG_JOYPAD))
811 if (*highlightedWidget->value < highlightedWidget->max)
813 *highlightedWidget->value = *highlightedWidget->value + 1;
815 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
817 highlightedWidget->changed = true;
820 if ((highlightedWidget->type == WG_RADIO) || (highlightedWidget->type == WG_SLIDER))
824 if ((keyState[SDL_SCANCODE_RETURN]) || (keyState[SDL_SCANCODE_SPACE]) || (keyState[SDL_SCANCODE_LCTRL]) || (joykeyFire && highlightedWidget->type != WG_JOYPAD))
826 if (highlightedWidget->value == NULL)
828 debug(("%s has not been implemented!\n", highlightedWidget->name));
832 if (highlightedWidget->type == WG_BUTTON)
834 *highlightedWidget->value = 1;
835 highlightedWidget->changed = true;
837 else if (highlightedWidget->type == WG_JOYPAD)
839 waitForButton = true;
843 if (*highlightedWidget->value > -1000)
845 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
848 else if (highlightedWidget->type == WG_KEYBOARD)
851 waitForButton = false;
853 *highlightedWidget->value = -*highlightedWidget->value;
864 if (joykeyX > 0 && highlightedWidget->type == WG_JOYPAD)
866 waitForButton = true;
870 if (*highlightedWidget->value > -1000)
872 *highlightedWidget->value = (-1000 - *highlightedWidget->value);
878 if (joykeyX < 0 && highlightedWidget->type == WG_JOYPAD)
882 lastButtonPressed = -1;
883 *highlightedWidget->value = abs(*highlightedWidget->value) - 1000;
887 lastButtonPressed = -2;
888 *highlightedWidget->value = -2;
891 waitForButton = false;
893 highlightedWidget->redraw();
901 char *strtok_r(char *s1, const char *s2, char **lasts)
910 while (*s1 && strchr(s2, *s1))
922 while(*s1 && !strchr(s2, *s1))
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.
943 bool Engine::loadDefines()
945 char string[2][1024];
947 if (!loadData("data/defines.h"))
950 strtok((char*)dataBuffer, "\n");
954 char *token = strtok(NULL, "\n");
958 if (!strstr(token, "/*"))
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);
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... :)
977 int Engine::getValueOfDefine(const char *word)
981 Data *data = (Data*)defineList.getHead();
983 while (data->next != NULL)
985 data = (Data*)data->next;
987 if (strcmp(data->key, word) == 0)
989 rtn = atoi(data->value);
994 printf("ERROR: getValueOfDefine() : %s is not defined!\n", word);
999 Does the opposite of the above(!)
1001 char *Engine::getDefineOfValue(const char *prefix, int value)
1005 Data *data = (Data*)defineList.getHead();
1007 while (data->next != NULL)
1009 data = (Data*)data->next;
1011 if (strstr(data->key, prefix))
1013 rtn = atoi(data->value);
1022 printf("ERROR: getDefineOfValue() : %s, %d is not defined!\n", prefix, value);
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 :)
1032 int Engine::getValueOfFlagTokens(const char *realLine)
1034 if (strcmp(realLine, "0") == 0)
1041 strlcpy(line, realLine, sizeof line);
1045 char *word = strtok_r(line, "+", &store);
1049 printf("ERROR: getValueOfFlagTokens() : NULL Pointer!\n");
1057 data = (Data*)defineList.getHead();
1060 while (data->next != NULL)
1062 data = (Data*)data->next;
1064 if (strcmp(data->key, word) == 0)
1067 sscanf(data->value, "%d", &value);
1071 sscanf(data->value, "%*s %*d %*s %d", &value);
1083 printf("ERROR: getValueOfFlagTokens() : Illegal Token '%s'\n", word);
1084 #if IGNORE_FLAGTOKEN_ERRORS
1091 word = strtok_r(NULL, "+", &store);
1099 void Engine::delay(unsigned int frameLimit) {
1100 unsigned int ticks = SDL_GetTicks();
1102 if(frameLimit < ticks)
1105 if(frameLimit > ticks + 16)
1108 SDL_Delay(frameLimit - ticks);